Fills in all 7 previously-unimplemented note methods in SqliteInterface and ApiInterface, adds cast/query helpers for notes, and fixes the apirouter (missing updatenote validator, two /user/ → /users/ typos that were also bypassing auth middleware). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
202 lines
6.5 KiB
TypeScript
202 lines
6.5 KiB
TypeScript
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;
|
|
}
|