feat: initial commit

This commit is contained in:
2026-06-29 23:07:14 +00:00
commit 3b9a6bc85c
152 changed files with 12558 additions and 0 deletions
+200
View File
@@ -0,0 +1,200 @@
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,
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("/user/:uid/auth/clear-sessions", async (ctx) => {
await db.deleteSessionsByUser(ctx.params.uid);
ctx.status = 204;
});
router.get("/user/: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({ 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;
}