mirror of
https://github.com/temetro/temetro.git
synced 2026-08-21 15:36:47 +00:00
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:
@@ -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);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user