Skip to content

Current System Audit

Phase: 0 Legacy Audit Status: initial evidence-backed pass Rule: legacy source is read-only reference; no legacy file was edited for this audit.

Scope

This audit covers the current HOCTAPAZ repo under /Users/velikho/Desktop/WORKING/HOCTAPAZ as the legacy source of truth before any Go source implementation. It focuses on backend framework, route topology, database schema, auth/tenant behavior, frontend API calls, storage, worker queues, DOCX import, question bank, exam, and attempt flows.

Repo Inventory

Evidence:

  • Root workspace uses pnpm@10.27.0, Turbo scripts, and package-level scripts in package.json.
  • Backend package is apps/api/package.json; frontend package is apps/web/package.json.
  • Backend entrypoints are apps/api/src/main.ts and apps/api/src/worker.ts.
  • Prisma schema is apps/api/prisma/schema.prisma.
  • Existing local dependency compose file is docker-compose.yml.

Observed structure:

  • apps/api: NestJS/Express backend, Prisma, BullMQ workers, S3-compatible storage, Swagger.
  • apps/web: Next 15 frontend, React 19, direct calls to /api/....
  • packages/shared: shared TS contracts, Zod schemas, feature maintenance routing and Vietnamese product labels.
  • packages/eslint-config, packages/tsconfig: shared tool config.
  • apps/mcp-teacher-server: separate MCP teacher server package.
  • docs, output, artifacts, work, tmp: existing legacy docs/artifacts/output. They are not source-of-truth for behavior unless cross-checked.

Backend Framework

Evidence:

  • apps/api/package.json depends on @nestjs/common, @nestjs/core, @nestjs/platform-express, @nestjs/swagger, express, @prisma/client, bullmq, ioredis, @aws-sdk/client-s3, openai, and document/media libraries.
  • apps/api/src/main.ts creates NestFactory.create(AppModule, { bodyParser: false }), installs Express JSON/urlencoded parsers, sets global prefix api, adds global ValidationPipe, and registers Swagger at /docs.
  • apps/api/src/app.module.ts registers all controllers and providers in one Nest module.

Current behavior:

  • REST API base path is /api.
  • Compatibility alias rewrites /api/v1/... to /api/....
  • SePay webhook aliases can be rewritten to /api/hooks/sepay-payment.
  • Swagger docs are exposed at /docs.
  • Body size defaults to 25mb via API_BODY_LIMIT or API_JSON_BODY_LIMIT.
  • CORS allows configured origins plus local/dev HOCTAPAZ hosts.

Migration note:

  • Go API Gateway must preserve /api path compatibility for the current frontend and may expose /v1 internally behind adapters.
  • Swagger/OpenAPI contracts must capture the envelope shape and current response semantics before frontend cutover.

Runtime Roles And Workers

Evidence:

  • apps/api/src/app.module.ts:150 sets enableWorkers = process.env.HOCTAPAZ_PROCESS_ROLE !== "api".
  • apps/api/src/app.module.ts:286-294 conditionally registers AlgorithmExamImportWorker, QuestionClassificationWorker, QuestionDuplicateResolutionWorker, QuestionPermanentDeleteWorker, and AzCreditBillingCycleWorker.
  • apps/api/src/worker.ts:6-11 starts a Nest application context with default role worker.
  • apps/api/src/modules/exam-paper-templates/algorithm-exam-import.queue.ts:4-65 defines BullMQ queue exam-import-algorithm using REDIS_URL or redis://localhost:6388.

Current behavior:

  • API and workers are same Nest module with different process roles.
  • Background jobs use BullMQ/Redis.
  • Some realtime import updates use Redis pub/sub, not only DB polling.

Migration note:

  • Go services need explicit separation between HTTP deployment and worker deployment.
  • API Gateway should not run domain workers.

Auth And Tenant Flow

Evidence:

  • Auth routes are in apps/api/src/modules/auth/auth.controller.ts:48-245.
  • AuthService login/register/refresh/logout are in apps/api/src/modules/auth/auth.service.ts:58-142.
  • Access/refresh token payload generation is in apps/api/src/modules/auth/auth.service.ts:686-737.
  • JWT guard reads Bearer token or cookie hoctapaz.accessToken in apps/api/src/common/auth.guard.ts:46-100.
  • Tenant guard reads X-Organization-Id or query organizationId in apps/api/src/common/tenant.guard.ts:20-45.
  • User profile and refresh token tables are in Prisma models User, RefreshToken, TeacherProfile, StudentProfile, ParentProfile, TeacherKyc.

Current behavior:

  • Login normalizes email and delegates credential checks to IdentityService.verifyUser.
  • Access token includes sub, type=access, role, accountCode, fullName, email, isGuest, guestSourceLinkId, defaultOrganizationId, and optional impersonation fields.
  • Refresh token includes sub, type=refresh, jti, expires after 7 days, and is persisted via RefreshToken.
  • Guard enforces roles via @Roles(...).
  • Tenant guard resolves or validates organization membership; ADMIN can force requested organization.
  • Frontend stores hoctapaz.accessToken and hoctapaz.organizationId in localStorage/cookies.

Migration note:

  • auth-service must preserve token payload compatibility at gateway boundary or gateway must adapt legacy/new payloads.
  • user-service and school-service need a clear boundary for profile data vs organization membership.
  • Current JWT secret default dev-secret-change-me must not be carried into production defaults.

Request Context And Health

Evidence:

  • RequestContextMiddleware sets X-Request-Id and X-Correlation-Id, logging JSON request fields in apps/api/src/common/request-context.middleware.ts:14-37.
  • HealthController exposes /api/health/live, /api/health/ready, and /api/metrics in apps/api/src/modules/health/health.controller.ts:15-48.
  • checkSystemDependencies probes Prisma/Postgres, Redis, and storage in apps/api/src/modules/health/system-health.util.ts:15-44.

Migration note:

  • Go services must expose /healthz and /readyz; gateway should also retain /api/health/live and /api/health/ready compatibility until frontend/ops cutover.
  • Correlation ID propagation is a platform requirement.

Frontend API Coupling

Evidence:

  • apps/web/lib/api-config.ts:19-51 resolves API base URL from NEXT_PUBLIC_API_URL, NEXT_PUBLIC_WEB_URL, server-side API vars, or http://localhost:4001.
  • apps/web/lib/api.ts:13-58 calls ${API_URL}/api${path}, sends X-Request-Id, X-Organization-Id, and Bearer token.
  • apps/web/lib/client-api.ts:50-81 mirrors client-side headers and direct /api fetches.
  • Many feature components call /api/... directly, including import workspace, wallet, question bank, exams, admin, storage, and auth.

Current behavior:

  • Frontend is tightly coupled to legacy /api routes.
  • The gateway must be a compatibility layer first, not a breaking frontend rewrite.

Database And Persistence

Evidence:

  • apps/api/prisma/schema.prisma:1-7 configures Prisma Client against PostgreSQL using DATABASE_URL.
  • apps/api/src/prisma/prisma.service.ts:5-21 extends PrismaClient and connects on module init.
  • Prisma schema contains 79 models and 45 enums.
  • Migrations are under apps/api/prisma/migrations/.

Current behavior:

  • One legacy database contains auth, users, organizations, classrooms, courses, documents, media, question bank, imports, exams, attempts, notifications, audit, wallet, support, AI settings, and outbox data.
  • Query boundaries are service-code boundaries, not database ownership boundaries.

Migration note:

  • New Go platform must split by service database. A migrator must read legacy DB but never mutate it by default.

Storage And Media

Evidence:

  • apps/api/src/modules/storage/storage.service.ts:65-88 configures S3-compatible storage with default bucket hoctapaz-local, endpoint http://localhost:9000, and env-driven credentials.
  • apps/api/src/modules/storage/storage.service.ts:90-122 validates MIME/size and creates presigned upload URLs.
  • apps/api/src/modules/storage/storage.controller.ts:44-99 exposes /api/storage/presigned-upload, /api/storage/media-assets, and media content endpoints.
  • docker-compose.yml:8-34 defines MinIO and bucket initialization for legacy local infrastructure.

Current behavior:

  • Files live in S3-compatible storage; metadata lives in Prisma MediaAsset and related document/import tables.
  • Media content can be served with image variants for formula/trim previews.

Migration note:

  • document-service should own MediaAsset-like metadata and object keys.
  • DOCX import must emit media references instead of embedding storage assumptions directly into question-bank tables.

DOCX Import Flow

Evidence:

  • Main import controller is apps/api/src/modules/exam-paper-templates/exam-paper-templates.controller.ts:150-1056.
  • DOCX Fast job creation and enqueue flow is apps/api/src/modules/exam-paper-templates/exam-paper-templates.controller.ts:210-307.
  • MathType -> Word Equation conversion endpoint is apps/api/src/modules/exam-paper-templates/exam-paper-templates.controller.ts:347-453.
  • OCR document conversion endpoints are apps/api/src/modules/exam-paper-templates/exam-paper-templates.controller.ts:485-607.
  • Teacher library/history/SSE stream is apps/api/src/modules/exam-paper-templates/exam-paper-templates.controller.ts:702-865.
  • Review/update/approve/reprocess/reject endpoints are apps/api/src/modules/exam-paper-templates/exam-paper-templates.controller.ts:867-1055.
  • BullMQ actions include pipeline, fast-lane, heavy-lane, docx-fast, convert-formulas, approve, and ocr-document in apps/api/src/modules/exam-paper-templates/algorithm-exam-import.queue.ts:16-33.
  • Worker dispatch is in apps/api/src/modules/exam-paper-templates/algorithm-exam-import.worker.ts:129-227.
  • DOCX Fast calls external Go Formula DOCX /v1/import/docx/simple in apps/api/src/modules/exam-paper-templates/go-formula-docx-import.service.ts:618-683.
  • DOCX Fast returns timing and warnings in apps/api/src/modules/exam-paper-templates/go-formula-docx-import.service.ts:759-817.

Current behavior:

  • Legacy already has a Go Formula DOCX dependency for fast import, configured by GO_FORMULA_DOCX_URL or GO_FORMULA_DOCX_BASE_URL.
  • Parse result is stored on ExamImportJob.parseResultJson and draft/review metadata in packagingJson.
  • SSE streams initial snapshots plus heartbeat and job updates.
  • Exact workflow names in source include docx-fast, mathtype-equation, ocr-document, MathType -> Word Equation, OCR Tài Liệu, and Import Đề Thi Nhanh (VIP).

Migration note:

  • docx-import-service should be the first deep domain service. It must preserve warnings, media, formulas, table data, source locations, progress, and latency.
  • The new service should absorb or wrap the existing Go Formula DOCX behavior rather than silently changing parser semantics.

Question Bank Flow

Evidence:

  • QuestionsController list/create/update/delete and AI routes are in apps/api/src/modules/questions/questions.controller.ts:81-147, 649-835, and 1000-1060.
  • QuestionTypesController, folders, and groups are in apps/api/src/modules/questions/question-admin.controllers.ts:23-240.
  • createQuestion validates question type contract, scoring rule, rich content, options, and versions in apps/api/src/modules/app-data/app-data.questions-write.ts:345-535.
  • Soft delete vs hard delete behavior is in apps/api/src/modules/app-data/app-data.questions-write.ts:880-1045.
  • List/filter pagination is in apps/api/src/modules/app-data/app-data.questions-read.ts:68-287.
  • Shared question types are SINGLE_CHOICE, MULTIPLE_CHOICE, TRUE_FALSE, TRUE_FALSE_GROUP, SHORT_ANSWER, SHORT_NUMERIC_ANSWER, ESSAY in packages/shared/src/index.ts:54-62.

Current behavior:

  • Question CRUD is scoped by organization and owner unless admin/all-org mode applies.
  • Questions have QuestionVersion, options, tags, custom type definitions, rich content JSON, source metadata, and scoring rules.
  • Used questions are archived by normal delete; hard delete explicitly removes exam/attempt/course references and can delete historical attempt rows.
  • AI classification jobs expose list/detail/errors/events/cancel/apply endpoints.

Migration note:

  • question-bank-service must preserve current question type definitions and the manual override behavior from import/editor workflows.
  • Hard-delete semantics need explicit product approval before reproducing, because it can delete attempt-related rows.

Exam And Attempt Flow

Evidence:

  • Exam routes are in apps/api/src/modules/exams/exams.controller.ts:27-225 and export/event routes in 315-404.
  • Attempt routes are in apps/api/src/modules/attempts/attempts.controller.ts:14-70.
  • createExam persists scheduling, shuffle, result mode, delivery mode, access password hash, and metadata in apps/api/src/modules/app-data/app-data.exams-authoring.ts:143-206.
  • Draft-only change rule is enforced for adding/removing questions in apps/api/src/modules/app-data/app-data.exams-authoring.ts:516-641 and 746-770.
  • Publishing refreshes ExamQuestion.questionSnapshotJson in apps/api/src/modules/app-data/app-data.exams-authoring.ts:772-818.
  • Starting an attempt creates ExamAttemptQuestion.questionSnapshotJson and option order snapshots in apps/api/src/modules/app-data/app-data.exams-attempts.ts:81-314.
  • Saving answers uses optimistic versioning and logs SAVE_ANSWER events in apps/api/src/modules/app-data/app-data.exams-attempts.ts:395-487.
  • Submitting grades against attempt snapshots in apps/api/src/modules/app-data/app-data.exams-attempts.ts:490-628.

Current behavior:

  • Only students can start attempts.
  • Exams must be PUBLISHED, online, open, not closed, and either assigned/shared by valid link.
  • Attempts snapshot question content and option order to avoid later question-bank edits changing active/historical attempts.
  • Result visibility depends on exam result mode.

Migration note:

  • attempt-service must not directly depend on question-bank-service internal schema; snapshot/API/event contract is mandatory.
  • exam-service owns exam authoring and publish snapshot; attempt-service owns attempt snapshots, answer saves, grading, and events.

Classroom, Course, Document, Notification, Admin

Evidence:

  • Classroom route group has 45 routes in controller decorators.
  • Course route group has 33 routes in controller decorators.
  • Documents route group has teacher/admin document routes in apps/api/src/modules/documents/documents.controller.ts.
  • Notifications are in apps/api/src/modules/notifications/notifications.controller.ts.
  • Admin operations, users, dashboard, AI settings, wallet, and content admin are in apps/api/src/modules/admin and wallet controllers.
  • Feature maintenance definitions map product areas to route/API prefixes in packages/shared/src/feature-maintenance.ts.

Current behavior:

  • These domains are implemented inside the same Nest module and same Prisma database.
  • Admin flows write audit logs in multiple places.

Migration note:

  • Service boundaries should follow ownership first, not route prefix only. Some route prefixes combine multiple domain concerns.

Open Questions

  • Exact production legacy database name is not encoded in source; DATABASE_URL controls it.
  • Complete field-level migration transforms require a second pass per model and should be executed before writing migration scripts.
  • Current test coverage quality per domain was not fully audited in this pass.
  • Some admin/wallet/support flows are out of the first strangler path but still need mapping before final cutover.

Go-platform documentation is generated from repository Markdown.