Add notes API: clinic + author-scoped CRUD

New `note` table (src/db/schema/notes.ts) referencing organization + user,
with org+author scoping so a doctor only sees their own notes within the active
clinic. Adds:

- src/types/note.ts + src/lib/note-validation.ts (zod)
- src/services/notes.ts (CRUD, treats non-uuid ids as not-found)
- src/routes/notes.ts mounted at /api/notes, gated requireAuth → requireOrg
- schema barrel + generated migration drizzle/0001_*.sql (applied on startup
  by the runtime migrator)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalid Abdi
2026-06-04 19:48:39 +03:00
parent 8bb341ea27
commit 7d6641ef59
10 changed files with 1698 additions and 0 deletions
+78
View File
@@ -0,0 +1,78 @@
import { Router } from "express";
import { HttpError } from "../lib/http-error.js";
import { noteInputSchema } from "../lib/note-validation.js";
import { requireAuth, requireOrg } from "../middleware/auth.js";
import * as service from "../services/notes.js";
export const notesRouter = Router();
// Notes are scoped to the active clinic AND the signed-in author. Any clinic
// member may manage their own notes, so no extra RBAC permission is required.
notesRouter.use(requireAuth, requireOrg);
notesRouter.get("/", async (req, res, next) => {
try {
res.json(await service.listNotes(req.organizationId!, req.user!.id));
} catch (err) {
next(err);
}
});
notesRouter.post("/", async (req, res, next) => {
try {
const input = noteInputSchema.parse(req.body);
const created = await service.createNote(
req.organizationId!,
req.user!.id,
input,
);
res.status(201).json(created);
} catch (err) {
next(err);
}
});
notesRouter.get("/:id", async (req, res, next) => {
try {
const note = await service.getNote(
req.organizationId!,
req.user!.id,
req.params.id as string,
);
if (!note) throw new HttpError(404, "Note not found.");
res.json(note);
} catch (err) {
next(err);
}
});
notesRouter.put("/:id", async (req, res, next) => {
try {
const input = noteInputSchema.parse(req.body);
const updated = await service.updateNote(
req.organizationId!,
req.user!.id,
req.params.id as string,
input,
);
if (!updated) throw new HttpError(404, "Note not found.");
res.json(updated);
} catch (err) {
next(err);
}
});
notesRouter.delete("/:id", async (req, res, next) => {
try {
const ok = await service.deleteNote(
req.organizationId!,
req.user!.id,
req.params.id as string,
);
if (!ok) throw new HttpError(404, "Note not found.");
res.status(204).end();
} catch (err) {
next(err);
}
});