CareTrack Architecture

Full Stack Architecture Documentation

This document describes the CareTrack website and medical records dashboard as implemented in this repository: frontend routes, backend APIs, Firebase schemas, security rules, runtime behavior, deployment scripts, and the purpose of each major subsystem.

Generated May 19, 2026
Application CareTrack MRMS
Primary Stack Next.js, React, Express, Firebase
Firebase Project caretrack-badc5

1. Overview

CareTrack is a staff-only medical records management system for clinics. The public page explains the product and sends authorized staff to the login flow. The protected admin dashboard handles staff directory management, patient registration, diagnosis tracking, schedules, reports, notifications, profile images, and role-based access.

Current primary application: the Next.js App Router pages in app/, especially the React admin app at app/admin/[[...slug]]/AdminApp.jsx.
Compatibility layer: static pages in admin/ and scripts in public/assets/js/ still exist and contain a Firestore-based data layer.
Capability Purpose Main implementation
Public landing page Explain CareTrack and link staff to login. app/page.jsx, index.html
Staff authentication Sign in using Firebase Auth, validate staff registration, sync custom claims. app/auth/[[...slug]]/page.jsx, public/assets/js/caretrack-auth.js
Admin dashboard Single-page protected staff dashboard for operational workflows. app/admin/[[...slug]]/AdminApp.jsx
Staff account management Create/disable staff accounts and keep Firebase Auth custom claims synchronized. Firebase callable functions in functions/src/controllers/*.js
Clinical data storage Store patients, diagnoses, schedules, notifications, and staff registration data. Firebase Realtime Database, with Firestore rules/scripts retained for legacy static flows.
Image storage Store patient and doctor profile photos. Firebase Storage paths under clinics/{clinic}/...
Offline shell Cache application shell/assets and show a connectivity overlay. sw.js, public/assets/js/caretrack-runtime.js

2. System Map

Browser Public page, auth page, admin SPA, runtime script, service worker.
Next/Express Serves pages, static assets, health endpoints, security headers.
Firebase Client SDK Auth, RTDB reads/writes, callable functions, Storage uploads.
Firebase Backend Auth users/claims, Realtime Database, Storage, Cloud Functions.
Layer Technology Purpose Key files
Frontend application Next.js 16, React 19 Public marketing page, login screen, protected admin single-page experience. app/page.jsx, app/auth/[[...slug]]/page.jsx, app/admin/[[...slug]]/AdminApp.jsx
Custom web server Express 5 + Next request handler Runs the app on VPS/PM2, handles API CORS, security headers, static assets, and health endpoints. server.js, ecosystem.config.cjs
Backend functions Firebase Functions v2 callable functions Privileged staff lifecycle operations that require Admin SDK access. functions/src/routes/*.js, functions/src/controllers/*.js
Auth Firebase Authentication Email/password staff login, user records, and custom role claims. app/lib/firebase-client.js, functions/src/data/firebase.js
Primary data store Firebase Realtime Database Registration, staff profiles, patients, diagnoses, schedules, notifications, reports, and settings. rtdrules.json, functions/src/data/registration.js, AdminApp.jsx
Legacy/secondary data store Cloud Firestore Static admin script data layer and rules/index definitions for clinic-scoped collections. firestore.rules, firestore.indexes.json, public/assets/js/caretrack-app.js
File storage Firebase Storage Doctor, patient, and report files with clinic-aware paths. storage.rules, upload helper in AdminApp.jsx
Offline/runtime Service Worker + browser runtime script Precache key routes/assets, health checks, theme toggle, offline overlay. sw.js, public/assets/js/caretrack-runtime.js
Deployment Vercel, Firebase CLI, PM2, Nginx Frontend hosting, Firebase Functions/rules, and VPS server deployment. vercel.json, firebase.json, deploy.bat, scripts/*.sh

3. Runtime Topologies

Local and VPS runtime

The app can run through the custom Express server with npm run dev or npm start. The server prepares a Next app, applies compression, adds security headers, serves static assets, exposes health/API endpoints, and passes all remaining requests to Next.

Browser -> Express server.js -> Next request handler -> app/ routes
Browser -> /api/* -> Express API middleware -> JSON response or 404
Browser -> Firebase SDK -> Auth / RTDB / Storage / callable Functions

Vercel runtime

vercel.json declares the project as a Next.js app using npm run build. Next route handlers serve /api/health, /robots.txt, /sitemap.xml, and /sw.js when deployed as a standard Next app.

Firebase Functions runtime

Cloud Functions are exported from functions/src/server.js, which sets maxInstances: 10 and exports callable functions from functions/src/app/next.js. These functions use the Firebase Admin SDK for Auth user and custom-claim operations.

The frontend currently calls Firebase callable functions directly through the Firebase Client SDK. The Express /api routes are health/test utilities, not the main clinical API surface.

4. Frontend Routes

Next.js routes

Route File Purpose Access
/ app/page.jsx Public product/landing page with staff login link, feature summary, and structured data. Public
/auth, /auth/* app/auth/[[...slug]]/page.jsx Email/password staff login, remember-me persistence, password reset, registration check, claim sync. Public until login
/admin, /admin/* app/admin/[[...slug]]/page.jsx -> AdminApp.jsx Protected dashboard and all staff workflows. Client-side slug router maps pages inside the React app. Authenticated active staff
/api/health app/api/health/route.js Next health endpoint returning app/service/timestamp JSON. Public
/robots.txt app/robots.txt/route.js Reads and returns root robots.txt with static caching. Public
/sitemap.xml app/sitemap.xml/route.js Reads and returns root sitemap.xml. Public
/sw.js app/sw.js/route.js Reads and returns root service worker with Service-Worker-Allowed: /. Public

Admin SPA page keys

Page key URL Purpose Allowed roles
dashboard/adminStats, staff/patient entry points, critical count.admin, doctor, receptionist
doctors/admin/doctorsStaff directory: doctors and receptionists.admin, doctor, receptionist
doctor-form/admin/doctor-form?id=...Create/update doctor profile and account.admin
doctor-detail/admin/doctor-detail?id=...View doctor profile, schedule, assigned patients.admin, doctor, receptionist
patients/admin/patientsSearch, view, register, edit, and delete patient records.admin, doctor, receptionist
patient-form/admin/patient-form?id=...Register/update patient demographics, contact, assignment, photo.admin, doctor, receptionist
patient-profile/admin/patient-profile?id=...Patient detail and linked diagnosis notes.admin, doctor, receptionist
diagnoses/admin/diagnosesDiagnosis list and delete actions.admin, doctor
diagnosis-form/admin/diagnosis-form?patientId=...Add diagnosis, clinical findings, ICD code, severity, and status.admin, doctor
reports/admin/reportsReport view derived from diagnosis records in the React app.admin, doctor
schedules/admin/schedulesDoctor availability, rooms, and assigned patient summary.admin, doctor, receptionist
settings/admin/settingsClinic settings form. Current React implementation displays local save toast only.admin
access-denied/admin/access-deniedPermission-denied screen.Special public fallback

Static compatibility pages

The admin/*.html and auth/index.html files load shared scripts from public/assets/js/. They are still precached by sw.js and tested by Playwright, but the Next React app is the newer route surface.

admin/admin.html admin/doctors.html admin/patients.html admin/diagnoses.html admin/reports.html admin/schedules.html admin/settings.html admin/audit-logs.html admin/*-form.html admin/*-profile.html

5. Auth and Access

CareTrack uses Firebase Authentication for staff accounts. A Firebase Auth account alone is not enough: the login flow also requires a matching Realtime Database registration record and an active status. Custom claims then drive frontend navigation and security rules.

Login flow

  1. Staff submits email/password on /auth.
  2. Client signs in using signInWithEmailAndPassword.
  3. Client searches RTDB registration paths for the signed-in UID.
  4. If no registration exists, or the registration is disabled, the client signs out.
  5. Client writes lastLoginAt and lastLoginEmail when allowed.
  6. Client calls syncOwnAccessClaims to mirror registration into Firebase Auth custom claims.
  7. Client refreshes the ID token and redirects to the requested admin route.

Custom claim schema

Claim Type Allowed values Purpose
rolestringadmin, doctor, receptionistPrimary authorization role used by UI, Firestore, RTDB, and Storage rules.
staffTypestringadmin, doctor, receptionistNormalized staff category. Aliases like administrator/frontdesk/clinical are converted.
clinicIdstringUsually defaultScopes clinic-aware reads/writes and Storage paths.
statusstringactive, disabledHuman-readable account state.
activebooleantrue, falseBoolean account state required by security rules.

Role purpose

Role Primary purpose Typical abilities
admin Clinic system owner/manager. Create/disable staff, edit doctors, delete records, access settings, view audit logs, manage all clinical records.
doctor Clinical user who diagnoses assigned patients. View staff/patients/schedules, create diagnoses for assigned patients, view reports.
receptionist Front-desk user handling patient registration and scheduling visibility. View staff/patients/schedules, create or update patient demographic/assignment data.

Registration lookup paths

registration/admin/{uid}
registration/doctors/{uid}
registration/receptionist/{uid}
registration/clinicks/doctor/{uid}
registration/clinicks/reception/{uid}
access/users/{uid}      # client-side legacy lookup only

6. API Catalogue

HTTP endpoints

Endpoint Method Auth Purpose Response / behavior Source
/api/health GET Public Health check for web runtime. Next returns { ok, app, service: "web", via: "next", timestamp }. Express returns { ok, app, service: "vps-api", uptime, timestamp }. app/api/health/route.js, server.js
/_caretrack_health, /__caretrack_health GET Public Runtime/service-worker connectivity checks. Next rewrites to /api/health. Express responds with { ok: true, app: "caretrack", via: "express" }. next.config.js, server.js, sw.js
/api/echo POST Public with API CORS Utility/test endpoint that echoes body and query. { ok, receivedAt, body, query }. JSON and URL-encoded bodies limited to 1 MB. server.js
/api/* OPTIONS Public with origin allowlist CORS preflight for Express API routes. Returns 204 with allowed methods GET,POST,OPTIONS and headers Authorization,Content-Type. server.js
/api/* Other unmatched Public with API CORS Catch-all API miss. 404 JSON: { ok: false, error: "API route not found.", path }. server.js
/sw.js GET Public Service worker script. JavaScript with Cache-Control: no-cache and Service-Worker-Allowed: /. app/sw.js/route.js, server.js
/robots.txt GET Public SEO robots policy. Disallows staff/admin/API/private routes and points to sitemap. robots.txt, app/robots.txt/route.js
/sitemap.xml GET Public SEO sitemap. Contains only the public homepage. sitemap.xml, app/sitemap.xml/route.js

Firebase callable functions

These are called with httpsCallable(functions, name). They are not REST routes. All privileged functions rely on Firebase Auth context and the Admin SDK.

Function Required auth Input Purpose and side effects Return
syncOwnAccessClaims Any signed-in staff with active registration None Finds the caller's RTDB registration, validates active status, mirrors role/staffType/clinicId/active into Firebase Auth custom claims, updates last login, saves staffProfiles/{uid}. { uid, role, staffType, clinicId, active, registrationPath }
syncAccessClaims Admin { uid } Admin-triggered claim repair for another staff user based on registration data. { uid, role, staffType, clinicId, active }
createStaffUser Admin { email, displayName, temporaryPassword, staffType|role, active, clinicId, department } Creates or updates Firebase Auth user, removes old registrations, writes normalized registration, mirrors claims, writes staffProfiles/{uid}. Temporary password required for new users and must be at least 8 chars. { uid, email, role, staffType, active, registrationPath }
setStaffRole Admin { uid, email?, displayName?, staffType|role, active?, clinicId?, department? } Changes role/staff type and active state, updates Auth display name/disabled flag, rewrites registration path, mirrors claims, updates staff profile. { uid, email, role, staffType, active, registrationPath }
disableStaffUser Admin { uid, clinicId? } Disables a staff account in Firebase Auth/custom claims, marks registration disabled, updates staff profile. Admins cannot disable their own account. { uid, active: false }
listStaffUsers Admin Optional { clinicId } from client. Controller currently lists all registrations and client filters by clinic. Reads known registration groups and returns deduplicated staff records sorted by display name/email/uid. { users: [...] }

API CORS allowlist

Express API CORS allows credentials for origins from API_ALLOWED_ORIGINS or the defaults: https://caretrack.website, https://www.caretrack.website, https://project-xbo8a.vercel.app, http://localhost:3000, and http://localhost:4173.

7. Data Schemas

Current React admin screens read and write Realtime Database paths. The static admin script still reads and writes Firestore collections under clinics/{clinic}. Both schema families are documented because both exist in the codebase.

7.1 Firebase Auth user schema

Field Type Purpose Writer
uidstringCanonical staff identity and record key.Firebase Auth
emailstringLogin email and staff contact email.createStaffUser
displayNamestringName shown in the app and copied into registration/profile records.createStaffUser, setStaffRole
disabledbooleanFirebase Auth account enable/disable flag. Inverse of active.updateAuthUser
emailVerifiedbooleanCreated as false for new staff accounts.createAuthUser
customClaimsobjectRole, staff type, clinic, active state.mirrorClaims

7.2 Realtime Database schemas

The React admin app uses root-level RTDB collections plus a clinicId field for filtering. Doctor records are stored under registration paths, while patient/diagnosis/schedule/notification/report/settings records are root collections.

Registration: registration/admin/{uid}, registration/doctors/{uid}, registration/receptionist/{uid}

Also reads legacy paths registration/clinicks/doctor/{uid} and registration/clinicks/reception/{uid}.

FieldTypeRequiredPurpose
uidstringYesFirebase Auth UID and RTDB key.
emailstringAdmin path yes; other paths usually yesStaff email/login identifier.
displayNamestringNoName shown in UI.
activebooleanYesControls access and claim generation.
statusstringNoactive or disabled.
rolestringYesAuthorization role.
staffTypestringYesNormalized staff type.
clinicIdstringYesClinic scope. Defaults to default.
department, departmentIdstringNoStaff department or front desk group.
createdBy, updatedBystringNoActor UID for lifecycle tracking.
createdAt, updatedAtRTDB timestampNoServer-side lifecycle timestamps.
lastLoginAt, lastLoginEmailtimestamp/stringNoUpdated during login/claim sync.
disabledAttimestampNoSet when staff is disabled.

Doctor profile: registration/doctors/{uid}

The current React admin app stores doctor profile fields on the same RTDB registration record as access fields. It also reads legacy doctor records from registration/clinicks/doctor/{uid}.

FieldTypePurpose
uid, idstringDoctor UID/document key.
fullName, displayNamestringDoctor name.
email, phonestringContact and Auth identity.
genderstringMale, Female, Other; used for default photo choice.
specialty, departmentstringClinical specialty and department.
room, officeRoomstringClinic room display.
statusstringAvailable, Busy, or Off Duty.
availabilitystringHuman-readable days such as Mon-Fri.
startTime, endTimeHH:mm stringWorking hours.
assignedPatientsnumberDisplay count when stored; actual assignment can be derived from patients.
photoUrl, photoPathstringFirebase Storage profile image URL/path.
role, staffType, active, clinicIdmixedAccess fields inherited from registration schema.
createdAt, createdBy, updatedAt, updatedBymixedLifecycle metadata from saveRecord.

Staff profile mirror: staffProfiles/{uid}

FieldTypePurpose
uid, email, displayNamestringStaff identity mirror.
role, staffType, active, statusmixedCurrent access state.
department, departmentIdstringStaff department.
clinicIdstringClinic scope.
registrationPathstringRTDB registration source path.
lastLoginAttimestampMost recent claim-sync login time.
updatedAt, updatedBymixedLifecycle metadata.

Patients: patients/{patientRecordId}

FieldTypePurpose
id, uid, patientIdstringInternal record key and public patient ID such as PT-123456.
firstName, lastName, fullName, namestringPatient name. Current forms write first/last name.
dateOfBirth, age, gendermixedDemographic data.
phone, email, addressstringContact information.
emergencyContactName, emergencyContactPhonestringAllowed by Firestore rules/static model; not shown in the current React patient form.
assignedDoctorId, assignedDoctorNamestringDoctor assignment; drives diagnosis permissions and notifications.
departmentstringCopied from assigned doctor.
statusstringStable, Monitoring, or Critical.
lastDiagnosis, lastUpdatedstringSummary fields updated after diagnosis save.
registrationNotesstringOptional notes allowed by rules/static model.
photoUrl, photoPathstringPatient profile image.
registeredBy, registeredByNamestringStaff member that created or owns registration.
clinicIdstringClinic scope used by client-side filtering.
createdAt, createdBy, updatedAt, updatedBymixedLifecycle metadata.

Diagnoses: diagnoses/{diagnosisId}

FieldTypePurpose
idstringRTDB key.
patientIdstringInternal patient record ID.
patientPublicIdstringHuman-readable patient ID.
patientNamestringPatient display name copied at diagnosis time.
assignedDoctorId, assignedDoctorNamestringDiagnosing or assigned doctor.
department, specialtystringClinical department/specialty context.
descriptionstringDisease or diagnosis text.
icdCodestringOptional ICD code.
severitystringLow, Medium, High, Critical.
diagnosisDatedate stringClinical diagnosis date.
statusstringOpen, Monitoring, Resolved.
patientStatusstringPatient-level status to summarize back onto patient record.
clinicalFindingsarray<string>Checked clinical symptoms/findings.
findingsTextstringComma-separated finding summary.
clinicalNotes, notes, criticalNotesstringDiagnosis notes. Render helper checks all three names.
followUpRequired, finding_* booleanLegacy static Firestore form fields.
clinicId, createdAt, createdBy, updatedAt, updatedBymixedScope and lifecycle metadata.

Schedules: schedules/{scheduleId}

FieldTypePurpose
idstringSchedule key.
doctorId, assignedDoctorIdstringDoctor reference.
doctorNamestringDoctor display name.
department, specialtystringClinical context.
daystringWeekday or availability text.
startTime, endTimeHH:mm stringAvailability window.
roomstringRoom/location.
statusstringAvailable, Busy, etc.
clinicId, createdAt, createdBy, updatedAt, updatedBymixedScope and lifecycle metadata.

Notifications: notifications/{notificationId}

FieldTypePurpose
idstringNotification key, usually {patientRecordId}_{doctorId}_assignment.
typestringCurrently patient-assignment.
title, messagestringNotification display text.
recipientUid, recipientNamestringDoctor/staff recipient.
patientRecordId, patientId, patientNamestringPatient target data.
departmentstringDepartment context.
read, readAtboolean/timestampRead state.
clinicIdstringClinic scope.
createdAt, createdBy, createdByNamemixedCreation metadata.

Reports: reports/{reportId}

The current React reports page renders diagnosis records as reports. The static script can create report metadata records.

FieldTypePurpose
idstringReport key.
reportNamestringReport title, such as Patient Diagnosis Report.
subjectstringPatient/clinic/department subject.
createdBy, createdAt, createdAtTextmixedCreator and time.
statusstringReport state such as Ready.
clinicId, updatedAt, updatedBymixedScope and lifecycle metadata.

Settings: settings/{settingId}

FieldTypePurpose
clinicNamestringClinic display name.
clinicEmailstringClinic contact email in static settings model.
emergencyPhonestringEmergency contact number in static settings model.
department, departmentsstringDefault department or comma-separated department list.
specialtiesstringComma-separated specialty list.
severityLabelsstringDiagnosis severity labels.
sessionTimeoutnumber/stringStatic script session timeout setting display.
clinicId, updatedAt, updatedBymixedScope and lifecycle metadata if persisted.

Access mirror: access/users/{uid}

Client code checks this legacy path during registration lookup. RTDB rules allow reads by the user or admin and deny writes. It is not written by the current functions.

7.3 Firestore schemas

Firestore rules define a clinic-scoped document tree under clinics/{clinic}. The static admin script uses this shape via helpers like collection(db, "clinics", state.clinicId, name).

Collection path Main fields Purpose Access summary
clinics/{clinic}/staffProfiles/{uid} uid, email, displayName, role, staffType, active, status, department Clinic-scoped staff profile mirror. Clinic staff can read; admins can create/update/delete.
clinics/{clinic}/doctors/{doctorId} fullName, email, phone, specialty, department, room, status, availability, photoUrl Firestore doctor directory for static admin pages. Clinic staff can read; admins can write.
clinics/{clinic}/patients/{patientId} firstName, lastName, dateOfBirth, age, gender, patientId, phone, email, address, assignedDoctorId, assignedDoctorName, status Patient demographic and assignment records. Clinic staff can read; admin/doctor/receptionist can create; doctors/receptionists can update only allowed demographic/assignment/status fields; admins can delete.
clinics/{clinic}/diagnoses/{diagnosisId} patientId, patientName, assignedDoctorId, assignedDoctorName, icdCode, description, severity, diagnosisDate, clinicalNotes Clinical diagnosis records linked to patients. Admins/doctors read and create/update; admins delete.
clinics/{clinic}/schedules/{scheduleId} doctorId, doctorName, department, day, startTime, endTime, room, status Doctor schedule records. Clinic staff read; admins write.
clinics/{clinic}/reports/{reportId} reportName, subject, createdBy, status, createdAt Report metadata. Admins/doctors read/create; admins update/delete.
clinics/{clinic}/settings/{settingsId} clinicName, clinicEmail, departments, specialties, severityLabels, sessionTimeout Clinic settings for static admin pages. Clinic staff read; admins write.
clinics/{clinic}/auditLogs/{auditId} action, entity, entityId, details, userId, userName, role, device, timestamp Immutable audit events from static Firestore flows. Admins read; clinic staff can create only for their own UID/role; no updates/deletes.

7.4 Firestore composite indexes

Collection groupFieldsPurpose
doctorsdepartment ASC, status ASC, updatedAt DESCFilter staff by department/status with newest updates first.
patientsassignedDoctorId ASC, status ASC, updatedAt DESCDoctor-specific patient lists.
patientsdepartment ASC, status ASC, updatedAt DESCDepartment patient queues.
diagnosespatientId ASC, severity ASC, diagnosisDate DESCPatient diagnosis history by severity/date.
diagnosesassignedDoctorId ASC, severity ASC, diagnosisDate DESCDoctor diagnosis worklist.
schedulesdoctorId ASC, day ASC, startTime ASCOrdered doctor schedules.
reportsstatus ASC, createdAt DESCReport listing by status/newest.
auditLogsaction ASC, entity ASC, timestamp DESCAdmin audit filtering.

7.5 Firebase Storage schemas

PathPurposeWrite rulesRead rules
clinics/{clinic}/patients/{patientId}/profile.{ext} Patient profile photo uploaded from patient form. Admin, doctor, or receptionist in same clinic. Any active staff in same clinic.
clinics/{clinic}/doctors/{doctorId}/profile.{ext} Doctor profile photo uploaded from doctor form. Admin in same clinic. Any active staff in same clinic.
clinics/{clinic}/reports/{allPaths} Report files, when used. Admin or doctor in same clinic. Admin or doctor in same clinic.

8. Data Workflows

Staff creation

  1. Admin opens staff or doctor form.
  2. Client calls createStaffUser with email, display name, temporary password, staff type, department, active flag, and clinic ID.
  3. Function creates/updates Firebase Auth user and removes older registration paths for that UID.
  4. Function writes the normalized registration under the correct path and mirrors custom claims.
  5. For doctors, the frontend then writes profile fields into registration/doctors/{uid}.

Patient registration

  1. Staff chooses or generates a patient record key and public patient ID.
  2. Optional profile image uploads to clinics/{clinicId}/patients/{patientId}/profile.{ext}.
  3. Frontend writes patients/{patientRecordId} with assignment, demographics, image URL/path, clinic ID, and lifecycle metadata.
  4. If assigned doctor changed, frontend writes a patient assignment notification under notifications/{patientId}_{doctorId}_assignment.

Diagnosis creation

  1. Admin or assigned doctor opens diagnosis form.
  2. Client validates that the doctor can diagnose the selected patient.
  3. Client writes a diagnosis record under diagnoses/{diagnosisId}.
  4. Client updates the patient summary fields lastDiagnosis, status, updatedAt, and updatedBy.

Notification handling

Doctors see notifications targeted to their UID. The app also derives assignment notifications from patient records if no explicit notification exists. Marking a notification as read updates or creates read: true and readAt.

Reports

The React reports page filters and renders diagnosis records as report rows. Static pages can create Firestore report metadata and render a printable patient diagnosis report.

9. Security Model

Frontend access checks

The admin app checks Firebase Auth state, registration status, custom claims, and per-page role lists. Unauthorized users are redirected to /auth or shown the access-denied page.

Firestore rules summary

ResourceReadCreateUpdateDelete
Clinic rootClinic staffAdminAdminAdmin
Staff profilesClinic staffAdminAdminAdmin
DoctorsClinic staffAdminAdminAdmin
PatientsClinic staffAdmin, doctor, receptionistAdmin or limited doctor/receptionist fieldsAdmin
DiagnosesAdmin, doctorAdmin, doctorAdmin, doctorAdmin
SchedulesClinic staffAdminAdminAdmin
ReportsAdmin, doctorAdmin, doctorAdminAdmin
SettingsClinic staffAdminAdminAdmin
Audit logsAdminClinic staff for their own UID/roleDeniedDenied

Realtime Database rules summary

PathReadWriteValidation
registration/admin/{uid}Self or adminAdmin active, or self preserving core identity/access fieldsRequires uid, email, active, role, staffType, clinicId
registration/doctors/{uid}Self or admin; group read admin onlyAdmin active, or self preserving core fieldsRequires uid, active, role, staffType, clinicId
registration/receptionist/{uid}Self or admin; group read active staffAdmin active, or self preserving core fieldsRequires uid, active, role, staffType, clinicId
registration/clinicks/doctor/{uid}, registration/clinicks/reception/{uid}Self or admin; group read active staffAdmin active, or self preserving core fieldsNo explicit required child list in current rules.
patients/{patientId}Active staffActive admin, doctor, or receptionistNo per-field validation.
diagnoses/{diagnosisId}Active admin or doctorActive admin or doctorNo per-field validation.
schedules/{scheduleId}Active staffActive adminNo per-field validation.
notifications/{notificationId}Active staffActive admin, doctor, or receptionistNo per-field validation.
reports/{reportId}Active admin or doctorActive admin or doctorNo per-field validation.
settings/{settingId}Active staffActive adminNo per-field validation.
staffProfiles/{uid}Self or adminSelf onlyNo per-field validation.
access/users/{uid}Self or adminDeniedRead-only legacy mirror.

Storage rules summary

Storage requires active staff claims and same-clinic matching. Patient files are writable by admins, doctors, and receptionists. Doctor files are writable only by admins. Report files are readable/writable by admins and doctors.

Server security headers

Both server.js and next.config.js define security headers including X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy, and a Content Security Policy allowing Firebase, Google APIs, app assets, and Storage image sources.

10. Offline Runtime

CareTrack includes a service worker and runtime script to improve perceived reliability and provide visible connection status.

ComponentPurposeDetails
sw.js Application shell cache and fetch strategy. Uses cache version v2.5, precaches public/auth/admin routes and assets, stale-while-revalidates assets, network-first navigations, special handling for health checks.
caretrack-runtime.js Browser runtime helper. Registers service worker, checks connectivity through /__caretrack_health, shows offline overlay, manages light/dark theme toggle, emits runtime events.
/__caretrack_health Connectivity probe. Service worker can intercept and return JSON with X-CareTrack-SW: 1; runtime falls back to direct homepage fetch.

The service worker ignores non-GET requests and lets Firebase hosts pass through to the network. It only caches same-origin assets and app shell pages.

11. Build and Deployment

NPM scripts

ScriptCommandPurpose
devnode server.js --devRun custom Express/Next server in development mode.
buildnext buildBuild Next app.
startnode server.jsRun custom server, using production build when present.
test:e2eplaywright testRun Playwright end-to-end tests.
deploy:functionsfirebase deploy --only functionsDeploy callable Cloud Functions.
deploy:rulesfirebase deploy --only database,storageDeploy RTDB and Storage rules.
deploy:hostingfirebase deploy --only hostingDeploy Firebase Hosting target.
deploy:vercel:prodvercel --prodDeploy Next frontend to Vercel.

Deployment targets

TargetFilesPurpose
Vercelvercel.jsonBuild and host the Next.js frontend.
Firebasefirebase.json, .firebasercFunctions, Realtime Database rules, Storage rules, and optional hosting from repo root.
VPS/PM2server.js, ecosystem.config.cjs, scripts/setup-vps.sh, scripts/deploy-vps.shRun the custom Express/Next server on port 4173 behind Nginx.
Windows deployment launcherdeploy.batBuild validation, then opens separate deployment windows for Vercel, Firebase Functions, rules, GitHub push, and VPS deploy.

Firebase configuration

projectId: caretrack-badc5
databaseURL: https://caretrack-badc5-default-rtdb.firebaseio.com
storageBucket: caretrack-badc5.firebasestorage.app
defaultClinicId: default

12. Testing

Playwright tests in tests/e2e/caretrack.spec.js validate core shell/runtime behavior:

  • Static assets and sw.js are served.
  • Public page shows the real logo and registers the service worker.
  • Health checks are driven through the service worker.
  • Connectivity overlay appears/disappears when the browser context goes offline/online.
  • Auth page loads without the old loader path.
  • Protected static admin pages redirect unauthenticated staff to login.
  • Access denied page renders without requiring a session.

Run with npm run test:e2e after starting the app according to playwright.config.js.

13. Architecture Notes

RTDB and Firestore both exist. The current React admin app is RTDB-first, while the static admin script is Firestore-first. Keep this in mind before changing schemas or rules.
Firestore rules/indexes are present but not wired in firebase.json. The configured deploy:rules script deploys Realtime Database and Storage rules only.
Functions audit writer is currently a no-op. functions/src/data/db.js defines writeAudit, but the body intentionally discards its arguments. Static Firestore flows still write audit documents.
RTDB clinical collections are broad at the rules level. Root patients, schedules, and notifications are readable by active staff. The React app filters by clinicId in client code.
Settings persistence differs by app surface. The current React settings page shows a local success toast only. The static Firestore settings form can save a settings/system document.
Claim freshness matters. Role changes and enable/disable operations update custom claims, but clients may need token refresh or sign-out/sign-in to pick up claim changes.

14. Source File Map

AreaFiles
Next routes/layoutsapp/layout.jsx, app/page.jsx, app/auth/[[...slug]]/page.jsx, app/admin/[[...slug]]/AdminApp.jsx, app/api/health/route.js
Firebase client SDKapp/lib/firebase-client.js, public/assets/js/firebase-config.js
Static compatibility UIadmin/*.html, auth/index.html, public/assets/js/caretrack-app.js, public/assets/js/caretrack-auth.js
Runtime/offlinesw.js, app/sw.js/route.js, public/assets/js/caretrack-runtime.js
Custom serverserver.js, next.config.js, ecosystem.config.cjs
Firebase Functionsfunctions/src/server.js, functions/src/app.js, functions/src/app/next.js, functions/src/routes/*.js, functions/src/controllers/*.js, functions/src/data/*.js, functions/src/middleware/auth.js
Rules and indexesrtdrules.json, firestore.rules, firestore.indexes.json, storage.rules
Deploymentfirebase.json, .firebaserc, vercel.json, deploy.bat, scripts/setup-vps.sh, scripts/deploy-vps.sh
Teststests/e2e/caretrack.spec.js, playwright.config.js