import KoaRouter from "@koa/router"; import type { BackendDbInterface } from "../db/types/DbInterface.js"; import { convertError } from "./middleware/convertError.js"; import { jsonBody } from "./middleware/jsonBody.js"; import { createabode, createapikey, createnote, createresident, createuser, loginuser, updateabode, updateresident, updatenote, updateuser, } from "../schema/validators.js"; import { authenticate } from "./middleware/authenticate.js"; import { InvalidAbodeError, NotFoundAbodeError } from "../db/types/errors.js"; export function apirouter(db: BackendDbInterface): KoaRouter { const router = new KoaRouter(); router.use(convertError); router.post("/auth/login", jsonBody({ validate: loginuser }), async (ctx) => { const user = await db.getUserByLogin(ctx.request.body); const token = await db.createSession(user.uid); ctx.cookies.set("abode_session", token); ctx.body = user; }); router.post("/auth/logout", authenticate(db), async (ctx) => { if (ctx.session!.source !== "session") throw new InvalidAbodeError(); ctx.cookies.set("abode_session", "", { expires: new Date("1970-01-01") }); ctx.status = 204; }); router.get("/auth/self", authenticate(db), async (ctx) => { ctx.body = ctx.user!; }); router.post("/auth/clear-sessions", authenticate(db), async (ctx) => { await db.deleteSessionsByUser(ctx.user!.uid); ctx.status = 204; }); router.use("/users", authenticate(db)); router.get("/users", async (ctx) => { ctx.body = await db.listUsers(); }); router.post("/users", jsonBody({ validate: createuser }), async (ctx) => { ctx.body = await db.createUser(ctx.request.body); }); router.get("/users/by-email", async (ctx) => { if (typeof ctx.query.email !== "string") throw new InvalidAbodeError(); ctx.body = await db.getUserByEmail(ctx.query.email); }); router.get("/users/:uid", async (ctx) => { ctx.body = await db.getUserById(ctx.params.uid); }); router.patch( "/users/:uid", jsonBody({ validate: updateuser, includeParams: ["uid"] }), async (ctx) => { ctx.body = await db.updateUser(ctx.request.body); } ); router.delete("/users/:uid", async (ctx) => { await db.deleteUserById(ctx.params.uid); ctx.status = 204; }); router.get("/users/:uid/residents", async (ctx) => { ctx.body = await db.listResidentsByUserId(ctx.params.uid); }); router.get("/users/:uid/abodes", async (ctx) => { ctx.body = await db.listAbodesByUserId(ctx.params.uid); }); router.get("/users/:uid/apikeys", async (ctx) => { ctx.body = await db.listApikeysByUser(ctx.params.uid); }); router.post( "/users/:uid/apikeys", jsonBody({ validate: createapikey, includeParams: ["uid"] }), async (ctx) => { const [apikey, token] = await db.createApikey(ctx.request.body); ctx.body = { apikey, token }; } ); router.get("/users/:uid/apikeys/:kid", async (ctx) => { const apikey = await db.getApikeyById(ctx.params.kid); if (apikey.uid !== ctx.params.uid) throw new NotFoundAbodeError(); ctx.body = apikey; }); router.delete("/users/:uid/apikeys/:kid", async (ctx) => { const apikey = await db.getApikeyById(ctx.params.kid); if (apikey.uid !== ctx.params.uid) throw new NotFoundAbodeError(); await db.deleteApikeyById(ctx.params.kid); ctx.status = 204; }); router.post("/users/:uid/auth/clear-sessions", async (ctx) => { await db.deleteSessionsByUser(ctx.params.uid); ctx.status = 204; }); router.get("/users/:uid/notes", async (ctx) => { ctx.body = await db.listNotesByUserId(ctx.params.uid); }); router.use("/abodes", authenticate(db)); router.get("/abodes", async (ctx) => { ctx.body = await db.listAbodes(); }); router.post("/abodes", jsonBody({ validate: createabode }), async (ctx) => { ctx.body = await db.createAbode(ctx.request.body, { uid: ctx.user!.uid }); }); router.get("/abodes/:aid", async (ctx) => { ctx.body = await db.getAbodeById(ctx.params.aid); }); router.patch( "/abodes/:aid", jsonBody({ validate: updateabode, includeParams: ["aid"] }), async (ctx) => { ctx.body = await db.updateAbode(ctx.request.body, { uid: ctx.user!.uid }); } ); router.delete("/abodes/:aid", async (ctx) => { await db.deleteAbodeById(ctx.params.aid); ctx.status = 204; }); router.get("/abodes/:aid/residents", async (ctx) => { ctx.body = await db.listResidentsByAbodeId(ctx.params.aid); }); router.get("/abodes/:aid/users", async (ctx) => { ctx.body = await db.listUsersByAbodeId(ctx.params.aid); }); router.get("/abodes/:aid/notes", async (ctx) => { ctx.body = await db.listNotesByAbodeId(ctx.params.aid); }); router.post( "/abodes/:aid/notes", jsonBody({ validate: createnote, includeParams: ["aid"] }), async (ctx) => { ctx.body = await db.createNote(ctx.request.body, { uid: ctx.user!.uid }); } ); router.use("/residents", authenticate(db)); router.get("/residents", async (ctx) => { ctx.body = await db.listResidents(); }); router.post( "/residents", jsonBody({ validate: createresident }), async (ctx) => { ctx.body = await db.createResident(ctx.request.body, { uid: ctx.user!.uid, }); } ); router.get("/residents/:uid/:aid", async (ctx) => { ctx.body = await db.getResidentById(ctx.params.uid, ctx.params.aid); }); router.patch( "/residents/:uid/:aid", jsonBody({ validate: updateresident, includeParams: ["uid", "aid"] }), async (ctx) => { ctx.body = await db.updateResident(ctx.request.body, { uid: ctx.user!.uid, }); } ); router.delete("/residents/:uid/:aid", async (ctx) => { await db.deleteResidentById(ctx.params.uid, ctx.params.aid); ctx.status = 204; }); router.use("/apikeys", authenticate(db)); router.get("/apikeys/:kid", async (ctx) => { ctx.body = await db.getApikeyById(ctx.params.kid); }); router.delete("/apikeys/:kid", async (ctx) => { await db.deleteApikeyById(ctx.params.kid); ctx.status = 204; }); router.use("/notes", authenticate(db)); router.get("/notes", async (ctx) => { ctx.body = await db.listNotes(); }); router.get("/notes/:nid", async (ctx) => { ctx.body = await db.getNoteById(ctx.params.nid); }); router.patch( "/notes/:nid", jsonBody({ validate: updatenote, includeParams: ["nid"] }), async (ctx) => { ctx.body = await db.updateNote(ctx.request.body, { uid: ctx.user!.uid }); } ); router.delete("/notes/:nid", async (ctx) => { await db.deleteNoteById(ctx.params.nid); ctx.status = 204; }); return router; }