Skip to content

Admin Service API

Phase: 9 Status: P9-003 foundation plus P9-030 maintenance guard adapter

Scope

admin-service owns the native foundation for feature maintenance state and admin audit history.

This slice does not cut over public /api/* routes. The gateway continues to proxy legacy admin routes until auth/RBAC adapters and response compatibility are explicitly enabled.

Legacy Evidence

  • Feature registry and exact labels: node-platform/packages/shared/src/feature-maintenance.ts.
  • Public/admin feature routes: node-platform/apps/api/src/modules/admin/feature-maintenance.controller.ts.
  • Feature state merge/cache/update behavior: node-platform/apps/api/src/modules/admin/feature-maintenance.service.ts.
  • Global maintenance guard and AI classification GET bypass: node-platform/apps/api/src/common/feature-maintenance.guard.ts.
  • Guard registration: node-platform/apps/api/src/app.module.ts.
  • Legacy models: node-platform/apps/api/prisma/schema.prisma models FeatureMaintenance and AuditLog.
  • Admin operations audit listing: node-platform/apps/api/src/modules/admin/operations.controller.ts.
  • Admin maintenance UI callers: node-platform/apps/web/app/admin/maintenance/page.tsx.
  • Admin operations audit UI shape: node-platform/apps/web/app/admin/operations/page.tsx.

Native Routes

MethodPathPurpose
GET/v1Service marker.
GET/v1/feature-maintenance/publicPublic active feature payload for guard/frontend adapters.
GET/v1/feature-maintenance/guard/{key}Guard decision for gateway/BFF adapters, including allowed/blocked state and read-only AI classification job bypass.
GET/v1/admin/feature-maintenanceAdmin list of all feature maintenance states.
PATCH/v1/admin/feature-maintenance/{key}Admin update for enabled and message.
GET/v1/admin/operations/source-mapRead-only owner map for future admin operations/dashboard metrics and commands.
GET/v1/admin/operations/source-snapshotsService-local probe of configured owner APIs from the operations source map.
POST/v1/admin/audit-logsInternal/admin audit event writer.
GET/v1/admin/audit-logsAdmin audit list with filters.
POST/v1/admin/notification-broadcastsInternal explicit-recipient notification broadcast producer.
POST/v1/support/notification-eventsInternal support ticket notification producer with already-resolved recipients.

Compatibility Rules

  • Feature definitions are native data in Go, copied from the legacy registry.
  • Preserve exact Vietnamese labels such as Trợ Lý AI, AI Phân Loại Ngân Hàng Câu Hỏi, Import Đề Thi Nhanh (VIP), MathType -> Word Equation, OCR Tài Liệu, and Mẫu Word.
  • Feature update accepts only enabled and optional nullable message.
  • Message values are trimmed; empty messages are stored as null.
  • Public status returns { items, activeFeatureKeys, generatedAt }.
  • Guard decision returns the same registry metadata plus enabled, allowed, blocked, optional reason=feature_maintenance, optional bypassReason=readonly_ai_classification_job, and optional inspected method/path.
  • Admin list returns every registry item, defaulting unconfigured rows to enabled=false.
  • Read-only AI classification history/progress GETs must remain inspectable during maintenance: /api/questions/ai-classify/jobs and /api/questions/ai-classify/jobs/:id/....
  • Audit logs preserve organizationId, actorId, action, entity, entityId, metadata, and createdAt.
  • Audit listing preserves action, entity, limit, and skip filters; limit defaults to 100 and clamps to 1..500, skip defaults to 0.
  • When AUDIT_SERVICE_URL or AUDIT_SERVICE_BASE_URL is set, new admin audit logs are also projected to audit-service /v1/audit-events best-effort with sourceService=admin-service, sourceEventId=<admin audit log id>, category=ADMIN, severity=INFO, retentionPolicy=admin-operations, and createdAt -> occurredAt. Projection failures are ignored so the current admin compatibility write remains available.
  • GET /v1/admin/operations/source-map is a planning/read-model contract only. It maps health, queue failures/depths, import jobs, outbox events and commands, AI provider status at ai-classifier-service /v1/ai/providers/status, wallet review summaries, support queues, identity/profile counts, organization roster/member counts, classroom/exam counts, attempt metrics, document totals/pending reviews, and audit history to owner services and APIs. It does not aggregate live metrics and does not move public admin operations routes. queue.failures now points to docx-import-service GET /v1/admin/import/docx/queue-summary; import.jobs points to docx-import-service GET /v1/import/docx/jobs; queue.depths uses docx-import-service GET /v1/admin/import/docx/queue-summary and ai-classifier-service GET /v1/admin/ai-classifier/queue-summary. operations.queue-retry points to docx-import-service POST /v1/import/docx/docx-fast-jobs/{id}/reprocess and ai-classifier-service POST /v1/ai-classifier/jobs/{id}/retry. organization.roster now points to the school-service owner summary GET /v1/admin/organizations/roster-summary; IAM still owns permission-bearing organization membership. learning.classroom-counts points to classroom-service GET /v1/admin/classrooms/summary. learning.exam-counts points to exam-service GET /v1/admin/exams/summary. learning.attempt-metrics points to attempt-service GET /v1/admin/attempts/summary for local attempt rows; derived result/weak-topic/dashboard rollups still belong to analytics-service. identity.counts points to IAM GET /v1/admin/identity/summary and profile-service GET /v1/admin/profiles/summary; IAM owns access-control counts while profile-service owns display/read-model counts. support.queues points to notification-service GET /v1/admin/support/notification-summary; it summarizes support notification/event/delivery state only and does not move support ticket storage into admin-service or notification-service.
  • GET /v1/admin/operations/source-snapshots uses the source map as the required key list and only probes owner endpoints configured in ADMIN_OPERATIONS_SOURCE_TARGETS (key=url,key=url). Unconfigured owners return status=unconfigured; failing owner endpoints return status=degraded or status=unavailable. This route is still a service-local read-model step, not dashboard parity and not public /api/admin/overview or /api/admin/operations* cutover. Configured owner probes run concurrently with a maximum of eight in flight, preserve source-map order, and report per-owner latency/status independently.
  • SVC-023 owner-source and route/browser evidence is recorded in Admin Owner Source And Route Proof. The proof keeps the default gateway table legacy-proxied and treats configured owner snapshots as health/status evidence only; admin-service does not synthesize owner totals.
  • POST /v1/admin/notification-broadcasts accepts only already-resolved recipientIds plus clientRequestId, title, and content. It writes a local admin audit log and, when NOTIFICATION_SERVICE_URL or NOTIFICATION_SERVICE_BASE_URL is configured, best-effort emits notification-service POST /v1/events/notification with sourceService=admin-service, sourceEventId=admin:broadcast:{clientRequestId}, type=admin.notification.broadcast.v1, and preferenceType=ADMIN_BROADCAST unless a custom notification type is supplied. It does not implement legacy ALL or ROLES recipient discovery; IAM/profile/gateway resolvers must supply concrete recipient ids first.
  • POST /v1/support/notification-events accepts only already-resolved recipientIds plus ticketId, clientRequestId, title, and content. It writes a local support audit log using eventType such as support.ticket.created or support.ticket.reply and, when NOTIFICATION_SERVICE_URL or NOTIFICATION_SERVICE_BASE_URL is configured, best-effort emits notification-service POST /v1/events/notification with sourceService=admin-service, sourceEventId=support:ticket:{ticketId}:notification:{clientRequestId}, type=support.ticket.notification.v1, entity=SupportTicket, and preferenceType=SUPPORT_TICKET. It preserves legacy support notification inbox types such as SUPPORT_TICKET_CREATED, SUPPORT_TICKET_REPLY, and SUPPORT_TICKET_STATUS through the input type field. It does not store support tickets, discover admins/requesters, or write notification rows directly.

Deferred Routes

  • /api/admin/operations/health, queues, metrics, import jobs, outbox, and retry/clear commands.
  • /api/admin/users* public compatibility wrapping. Teacher KYC review storage and owner-side native list/detail/approve/reject now live in profile-service at /v1/admin/users/kyc/teacher*; admin-service must not copy KYC storage or document hashes.
  • /api/admin/ai-settings*.
  • /api/admin/inbox* target search plus ALL/ROLES recipient discovery.
  • /api/admin/wallet*, /api/wallet*, and /api/hooks*.
  • /api/support* public route cutover and support ticket persistence.
  • Admin dashboard aggregation.

Gateway Route Rehearsal

The default gateway route table keeps /api/feature-maintenance*, /api/admin/feature-maintenance*, /api/admin/operations*, and broad /api/admin* traffic on legacy.

The native guard route is service-local for gateway/BFF adapter development: GET /v1/feature-maintenance/guard/{key}. Passing method=GET&path=/api/questions/ai-classify/jobs/... returns allowed=true with bypassReason=readonly_ai_classification_job for assessment.questionBankAiClassification, preserving the legacy read-only AI classification job bypass while default /api/* routing remains legacy.

Use deploy/gateway/routes.feature-maintenance-native-example.json for Compose or deploy/gateway/routes.feature-maintenance-native-localhost-example.json for local go run rehearsal. The non-default table rewrites only:

  • GET /api/feature-maintenance/public to /v1/feature-maintenance/public without gateway auth.
  • GET /api/admin/feature-maintenance to /v1/admin/feature-maintenance with gateway auth and ADMIN role.
  • PATCH /api/admin/feature-maintenance/{key} to /v1/admin/feature-maintenance/{key} with gateway auth and ADMIN role.

Broad feature-maintenance siblings, nested admin feature-maintenance paths, AI classification job reads, operations/audit, users, AI settings, wallet, support, dashboard, broad admin, and fallback routes remain legacy-proxied until maintenance guard, RBAC, audit, and response compatibility adapters are explicit.

The admin audit route-table rehearsal uses deploy/gateway/routes.admin-audit-native-example.json for Compose or deploy/gateway/routes.admin-audit-native-localhost-example.json for local go run. The non-default table rewrites only:

  • GET /api/admin/operations/audit to /v1/admin/audit-logs with gateway auth and ADMIN role.

Audit-service has a separate compatibility-preview rehearsal through deploy/gateway/routes.audit-admin-compat-native-example.json, deploy/gateway/routes.audit-admin-compat-native-localhost-example.json, and make test-audit-admin-compat-routes. That rehearsal targets audit-service /v1/admin/operations/audit-compat and does not promote the default public route.

This audit rehearsal intentionally does not require organization at the gateway because the legacy AdminOperationsController is guarded by global admin role only. Admin operations health, queues, retry/clear commands, metrics, import jobs, outbox, AI settings, feature-maintenance routes not in the table, broad admin, and fallback routes remain legacy-proxied until the remaining operational controls and tenant behavior are explicit.

The owner-source status rehearsal uses deploy/gateway/routes.admin-owner-source-native-example.json for Compose or deploy/gateway/routes.admin-owner-source-native-localhost-example.json for local development. It rewrites only these ADMIN-protected exact reads:

  • GET /api/admin/operations/source-map to /v1/admin/operations/source-map.
  • GET /api/admin/operations/source-snapshots to /v1/admin/operations/source-snapshots.

The admin-web operations panel may display owner status, HTTP code, and latency from the snapshot route through Gateway. It keeps local workflow fixtures when the rehearsal is unavailable and does not aggregate or synthesize dashboard metrics. The default route table remains legacy-proxied.

Rollback

  • Keep /api/feature-maintenance*, /api/admin/feature-maintenance*, and /api/admin/operations* as legacy_proxy gateway routes.
  • Disable native adapters by removing route-table entries or switching them back to legacy_proxy.
  • Drop service-owned feature_maintenance and audit_logs only for local test reset; do not delete legacy FeatureMaintenance or AuditLog.

Validation Queries

sql
SELECT key, enabled, updated_at
FROM feature_maintenance
ORDER BY key;

SELECT action, entity, entity_id, actor_id, created_at
FROM audit_logs
ORDER BY created_at DESC
LIMIT 20;

Go-platform documentation is generated from repository Markdown.