portal: public Patient Portal kiosk (self-service booking + results)

New chrome-less (portal) route group with /portal/[clinic], a card-style choice
of "Book an appointment" vs "View my results", and step flows wired to a new
public backend router (src/routes/portal.ts):
- GET  /api/portal/:clinic            -> clinic name
- POST /api/portal/:clinic/appointments  (verifies name+file#, books a confirmed
  appointment that appears on the doctor's Appointments page)
- GET  /api/portal/:clinic/results    (minimal, safe status — no clinical values)

Excluded /portal from the auth proxy redirect so the kiosk loads without a
session. PHI exposure is intentionally minimal (see docs).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalid Abdi
2026-06-20 18:49:39 +03:00
parent e656eae362
commit 516de6ad60
8 changed files with 676 additions and 1 deletions
+3
View File
@@ -23,6 +23,7 @@ import { meetingsRouter } from "./routes/meetings.js";
import { notesRouter } from "./routes/notes.js";
import { notificationsRouter } from "./routes/notifications.js";
import { patientsRouter } from "./routes/patients.js";
import { portalRouter } from "./routes/portal.js";
import { prescriptionsRouter } from "./routes/prescriptions.js";
import { settingsRouter } from "./routes/settings.js";
import { staffRouter } from "./routes/staff.js";
@@ -84,6 +85,7 @@ app.use("/api/settings", settingsRouter);
app.use("/api/ai", aiRouter);
app.use("/api/chat", chatRouter);
app.use("/api/integrations", integrationsRouter);
app.use("/api/portal", portalRouter);
app.use(notFound);
app.use(errorHandler);
@@ -112,4 +114,5 @@ server.listen(env.PORT, () => {
console.log(` • ai: /api/ai (config + import)`);
console.log(` • chat: /api/chat (LLM agent)`);
console.log(` • integr.: /api/integrations (FHIR / e-Rx / claims)`);
console.log(` • portal: /api/portal (public clinic kiosk)`);
});