batchCooking/apps/api/src/modules/house/house.routes.ts
Nicolas 7d5a6c05bb API: administrateur de foyer, code d'invitation, créer/rejoindre/quitter/supprimer (step 2/8)
- House.adminId / House.inviteCode (migration Prisma)
- house.service: createHouse, joinHouse, leaveCurrentHouse (transfert
  d'admin ou suppression du foyer si dernier membre), deleteHouse
  (admin-only), removeMember (admin-only)
- house.routes: POST /house, POST /house/join, POST /house/leave,
  DELETE /house/current, DELETE /house/members/:memberId
- GET/PATCH /house/current renvoient désormais membres + admin + code
2026-08-17 10:38:16 +02:00

110 lines
3.4 KiB
TypeScript

import { HttpError } from "@batch-cooking/error-tools";
import { wrapAsyncHandler } from "@batch-cooking/express-tools";
import {
ErrorCode,
createHouseSchema,
joinHouseSchema,
renameHouseSchema,
} from "@batch-cooking/shared";
import { Router } from "express";
import { type AuthLocals, requireAuth } from "../../middlewares/require-auth.js";
import {
createHouse,
deleteHouse,
getCurrentHouse,
joinHouse,
leaveCurrentHouse,
removeMember,
renameHouse,
} from "./house.service.js";
/** Router mounted at `/house` in app.ts. Every route requires a session — a household is per-user (via their profile), never public. */
export const houseRouter = Router();
houseRouter.get(
"/current",
requireAuth,
wrapAsyncHandler<unknown, AuthLocals>(async (_req, res) => {
const house = await getCurrentHouse(res.locals.userProfile.houseId);
res.status(200).json(house);
}),
);
/** The household step of the profile journey (onboarding wizard and the `/parametres/foyer` settings page both call this) — renaming, open to any member. */
houseRouter.patch(
"/current",
requireAuth,
wrapAsyncHandler<unknown, AuthLocals>(async (req, res) => {
const input = renameHouseSchema.parse(req.body);
const house = await renameHouse(res.locals.userProfile.houseId, input.name);
res.status(200).json(house);
}),
);
/** Creates a new household for a profile that doesn't have one yet — the "create" half of the optional household step. */
houseRouter.post(
"/",
requireAuth,
wrapAsyncHandler<unknown, AuthLocals>(async (req, res) => {
const input = createHouseSchema.parse(req.body);
const house = await createHouse(
res.locals.userProfile.id,
res.locals.userProfile.houseId,
input.name,
);
res.status(201).json(house);
}),
);
/** Joins an existing household by invite code — the "join" half of the optional household step. */
houseRouter.post(
"/join",
requireAuth,
wrapAsyncHandler<unknown, AuthLocals>(async (req, res) => {
const input = joinHouseSchema.parse(req.body);
const house = await joinHouse(
res.locals.userProfile.id,
res.locals.userProfile.houseId,
input.inviteCode,
);
res.status(200).json(house);
}),
);
/** Removes the caller from their own household — see `deleteHouse` below for removing the household itself. */
houseRouter.post(
"/leave",
requireAuth,
wrapAsyncHandler<unknown, AuthLocals>(async (_req, res) => {
await leaveCurrentHouse(res.locals.userProfile.id, res.locals.userProfile.houseId);
res.status(204).end();
}),
);
/** Deletes the household entirely — every member loses it. Admin-only, see `house.service.ts`. */
houseRouter.delete(
"/current",
requireAuth,
wrapAsyncHandler<unknown, AuthLocals>(async (_req, res) => {
await deleteHouse(res.locals.userProfile.id, res.locals.userProfile.houseId);
res.status(204).end();
}),
);
/** Removes one specific member from the caller's household. Admin-only, see `house.service.ts`. */
houseRouter.delete(
"/members/:memberId",
requireAuth,
wrapAsyncHandler<unknown, AuthLocals>(async (req, res) => {
const memberId = Number(req.params.memberId);
if (!Number.isInteger(memberId)) {
throw new HttpError(400, ErrorCode.VALIDATION_ERROR, "memberId must be an integer");
}
const house = await removeMember(
res.locals.userProfile.id,
res.locals.userProfile.houseId,
memberId,
);
res.status(200).json(house);
}),
);