Skip to main content

admin

Owns the operator surface: listing and acting on accounts, reviewing doctor applications, correcting a doctor's specializations, intervening in appointments, dashboard counts, and reading the audit log.

Every route is administrator-only, declared once at the controller level. The module owns no table of its own — each mutation pairs one domain write with one audit insert.

What is audited, and what is not​

This is the module's most important distinction, and it is a deliberate design choice rather than an omission.

Audited — every action that changed something:

ActionRecorded as
Activate an accountACCOUNT_ACTIVATED
Suspend an accountACCOUNT_SUSPENDED, with the reason
Deactivate an accountACCOUNT_DEACTIVATED, with the reason
Approve a doctorDOCTOR_APPROVED
Reject a doctorDOCTOR_REJECTED, with the reason
Correct specializationsDOCTOR_SPECIALIZATION_UPDATED
Cancel an appointmentAPPOINTMENT_CANCELLED, with the reason

Not audited — listing accounts, reading the review queue, listing appointments, the dashboard, and reading the audit log itself. The log records what an administrator changed, not what they looked at. A log of every page view would be large, would say very little, and would make the entries that matter harder to find.

Account status​

Three states, and the endpoint is a setter rather than a state machine: any of active, suspended or deactivated may be set from any other. There is no transition matrix, because there is no transition an administrator should be forbidden from making.

Two rules apply:

  • Administrator accounts cannot be changed here. Attempting it is a 400.
  • A reason is required for anything but activation. Suspending or deactivating without one is refused; activating needs no justification. Activating an account also clears the previous suspension reason, so a reactivated account does not carry a stale explanation.

Suspension takes effect on the account's next request, because the authentication guard re-reads the status every time rather than trusting the token.

Doctor approval​

The review queue shows pending applications oldest first. Approving sets the state, records the review time, and clears any previous rejection reason, so a doctor who was once rejected and is later approved does not carry the old explanation.

Rejecting requires a reason, which the doctor sees on their own profile.

Neither decision is guarded by the doctor's current state, so calling approve on an already-approved doctor succeeds and writes a second audit entry. The queue only offers pending applications, so the interface never presents that, but the API permits it.

A rejection is not revisited: nothing returns a rejected profile to the queue. The doctor may edit their profile, but editing does not reset the approval state, so a rejected application stays rejected unless an administrator approves it.

Correcting specializations​

The only way a doctor's specialization list changes after submission. Doctors choose from an application-defined list and cannot invent a value, so an administrator who spots a mistake corrects it here.

It is the one doctor-profile mutation that sends no notification.

Appointment intervention​

Administrators can list appointments across all accounts — capped at the most recent 200, with no pagination — and cancel one.

Cancellation delegates to the same service the patient and doctor routes use, so it releases the slot and notifies both parties identically. The only difference is that cancelledBy is recorded as ADMIN.

The refusal to cancel an already-completed appointment is enforced by that shared service, not here.

Dashboard counts​

Four numbers: patients, doctors, doctors awaiting review, and completed consultations. Plus a breakdown of appointments by state, which is pre-filled with zeros so an empty deployment shows zeroes rather than missing keys.

Two things worth knowing about the counts:

  • Patient and doctor counts include suspended and deactivated accounts. They count accounts, not active users.
  • Completed consultations are counted from the consultation sessions, a different table from appointments.

Reading the audit log​

Filterable by actor and by action, newest first, capped at 200 entries with no pagination and no date filter. Each entry names the actor, the action, the kind of record acted on, the record's id, the reason where one was given, and when.

The actor's display name is resolved from their doctor profile, then their patient profile, then their email — which is why an administrator appearing as an actor shows as an email address: administrators have neither profile.

Rules and invariants​

  • Every route is administrator-only.
  • The audit log is insert-only. No update or delete path exists anywhere in the API.
  • Account status cannot be changed for an administrator account.
  • Actions that change something record why, where a reason is meaningful.

Data model slice​

ModelUse
UserReads and writes status and statusReason.
DoctorProfileWrites approvalState, rejectionReason, reviewedAt, specializations.
AuditLogInsert-only. targetType is a plain string — 'User', 'DoctorProfile' or 'Appointment' — rather than an enumeration.
AppointmentRead for the cross-account listing; the write itself goes through the appointments module.
ConsultationSessionCounted, for the completed-consultations figure.

Relevant enumeration: AuditAction. Two of its values — NOTE_DRAFT_GENERATED and RECORD_SUMMARY_GENERATED — are written by records, not here.