Files
temetro/backend
Khalid Abdi 2d47abcc42 feat: patient wallet — real Signing, encrypted share relay, import-from-app
Backend
- clinic Ed25519 signing key (services/signing.ts, routes/signing.ts,
  clinic_signing_keys table) — Settings → Signing is now real
- @noble wallet-crypto (lib/wallet-crypto.ts): ed25519 identity, base58check
  wallet numbers, sealed-box (x25519 + xchacha20poly1305)
- /wallet Socket.io relay namespace (challenge-signed device auth) forwarding
  only ciphertext; emitToWallet helper
- import-from-app flow (routes/patients-wallet.ts, services/wallet-share.ts):
  request-share → patient approval → decrypt + verify → review draft → commit
- temporary shares: patients.share_expires_at + 5-min auto-delete sweep; revoke

Frontend
- SigningPanel wired to live key/fingerprint/rotate + shared-records list
- "Import from a patient app" dialog (lib/signing.ts, import-from-wallet-dialog)
  reusing the draft-review path; temporary badge on the patient list

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 18:19:57 +03:00
..
2026-06-17 20:50:09 +03:00

temetro backend

TypeScript + Express + Postgres API for temetro. Authentication is powered by Better Auth (email/password with verification, password reset, and multi-tenant organizations with role-based access control). Patient records are scoped to an organization (a clinic) and exposed over a small REST API that mirrors the frontend's Patient shape exactly.

Quick start (Docker)

docker compose up            # db + backend + frontend — no setup needed

No .env or manual secret generation is required: on first start the backend generates any missing secrets (BETTER_AUTH_SECRET, AI_CREDENTIALS_KEY) and persists them to a Docker volume, so they stay stable across restarts. Create a .env (from .env.example) only if you want to override something — your own auth secret, SMTP, etc.

Run only the API + database: docker compose up db backend. Browse the DB with Adminer: docker compose --profile tools up adminerhttp://localhost:8080.

Migrations are applied automatically on container start (node dist/migrate.js).

Local development (without Docker)

npm install
cp .env.example .env          # point DATABASE_URL at a local Postgres (localhost:5432)
npm run db:migrate            # apply migrations (drizzle-kit)
npm run dev                   # tsx watch on http://localhost:4000

Auth & schema workflow

The Better Auth tables are generated into src/db/schema/auth.ts from src/auth.ts. Re-run after changing auth config/plugins, then regenerate the SQL migration:

npm run auth:generate   # better-auth CLI -> src/db/schema/auth.ts
npm run db:generate     # drizzle-kit -> ./drizzle/*.sql
npm run db:migrate      # apply

API

All patient routes require a signed-in user (Better Auth session cookie) and an active organization; access is gated by the caller's clinic role.

Method Path Permission Notes
GET /api/patients patient:read list patients in the active clinic
GET /api/patients/:fileNumber patient:read one patient (404 if absent)
POST /api/patients patient:write create (409 if file number exists)
PUT /api/patients/:fileNumber patient:write replace the full record
DELETE /api/patients/:fileNumber patient:delete remove a patient

Other org-scoped resources follow the same pattern (CRUD, role-gated):

Resource Base path Permission Notes
Appointments /api/appointments appointment:* list / create / update / delete
Prescriptions /api/prescriptions prescription:* prescriber defaults to the signed-in user
Tasks /api/tasks task:* PATCH /:id for partial updates / the done toggle
Notes /api/notes — (author-scoped) private to the signed-in author
Activity GET /api/activity — (any member) audit feed of record changes
Analytics GET /api/analytics — (any member) computed clinic aggregates
Conversations /api/conversations — (participant-scoped) staff messaging; real-time over Socket.io
Notifications /api/notifications — (per-recipient) auto-generated; read-all + per-id read

Real-time messaging and live notifications are delivered over Socket.io, attached to the same HTTP server; the handshake is authenticated with the Better Auth session cookie.

Auth endpoints (sign up / in / out, verify email, reset password, organizations & invitations) are served by Better Auth under /api/auth/*.

Roles

Role Patient access Prescriptions Lab results Clinic management
owner read / write / delete read / write / delete read / write full
admin read / write / delete read / write / delete read / write members, invitations, settings
doctor / member (clinician) read / write read / write / delete read / write
reception read / write (demographics)
pharmacy read read / write
lab read read / write

Environment

See .env.example. If SMTP_HOST is unset, verification / reset / invitation emails are printed to the server console instead of being sent — no setup required for local development.