Reviewed: 2026-07-27
Application: Project Amazon PH Academy v2
Source of truth: src/app/, src/app/actions/, src/usecases/, src/ports/, src/composition/container.ts, and prisma/schema.prisma.
This document replaces the original Day 0 API design as the current-state reference. The original design described planned ports, routes, and models that were never added. Do not infer a route or method from a design section that is absent from the source tree.
| Concern | Current implementation |
|---|---|
| Framework | Next.js 16 App Router, React server components by default |
| Language | TypeScript strict |
| Database | PostgreSQL through Prisma 7 and @prisma/adapter-pg |
| Composition | src/composition/container.ts, cached production container plus request scope |
| Authentication | jose JWT in HttpOnly cookies, Argon2id password hashing |
| Payment | IPaymentGateway and PayMongoAdapter |
EmailSender and ResendEmailSender, React Email templates |
|
CertificateRenderer and React PDF adapter |
|
| Rate limiting | RateLimiter, Upstash production adapter, in-memory test adapter |
| Observability | Pino logger, Sentry client/server/edge configuration, Web Vitals |
| Styling | CSS Modules and AMPH design tokens, no Tailwind utility classes |
| Testing | Vitest, architecture tests, integration tests, Playwright E2E |
src/app -> src/usecases -> src/ports <- src/infra
-> src/domain
src/composition wires concrete infra adapters to the ports
src/domain and src/ports do not import app, infra, or framework code
The ESLint boundary tests enforce the direction. src/composition/container.ts is the production wiring file. src/composition/container.test.ts supplies in-memory and fake adapters for tests.
| Path | File |
|---|---|
/ |
src/app/page.tsx |
/pricing |
src/app/pricing/page.tsx |
/courses |
src/app/courses/page.tsx |
/courses/[slug] |
src/app/courses/[slug]/page.tsx |
/signup |
src/app/signup/page.tsx |
/login |
src/app/login/page.tsx |
/admin-login |
src/app/admin-login/page.tsx |
/verify-email |
src/app/verify-email/page.tsx |
/verify-email/sent |
src/app/verify-email/sent/page.tsx |
/reset-password |
src/app/reset-password/page.tsx |
/reset-password/[token] |
src/app/reset-password/[token]/page.tsx |
/checkout |
src/app/checkout/page.tsx |
/checkout/success |
src/app/checkout/success/page.tsx |
/checkout/failed |
src/app/checkout/failed/page.tsx |
/certificates/[hash] |
src/app/certificates/[hash]/page.tsx |
/dashboard/profile/courses/[slug]/lessons/[lessonId]/courses/[slug]/lessons/[lessonId]/quiz/tools/tools/bid-elevator/tools/str-triage/tools/campaign-builder/tools/listing-audit/tools/keyword-research/resources— download center (STORY-098): guides, templates, automation tools, handouts, cheat sheets, grouped by category and gated byCourseAccessTier
All pages under /admin inherit requireAdmin() from src/app/admin/layout.tsx.
/admin/admin/users,/admin/users/[id]/admin/courses,/admin/courses/new,/admin/courses/[id],/admin/courses/[id]/edit/admin/courses/[id]/modules/new/admin/courses/[id]/modules/[moduleId],/edit/admin/courses/[id]/modules/[moduleId]/lessons/new/admin/courses/[id]/modules/[moduleId]/lessons/[lessonId],/edit/admin/payments,/admin/payments/[id]/admin/refunds,/admin/refunds/[orderId]/admin/simulators,/admin/simulators/new,/admin/simulators/[id]/edit/admin/live-classes,/admin/live-classes/new,/admin/live-classes/[id]/edit/admin/resources,/admin/resources/new,/admin/resources/[id]/edit— download-center CRUD (STORY-098)/admin/discount-codes,/new,/[id]/edit/admin/badges,/new,/[slug]/edit/admin/audit-log/admin/settings,/admin/settings/2fa-setup
There is no src/app/admin/settings/email-templates page in the current tree. Email-template entity and use-case code is backend-only until a UI story is implemented.
| Method and path | Purpose |
|---|---|
POST /api/auth/signup |
Signup and redirect response with session cookie |
POST /api/auth/login |
Login and redirect response with session cookie |
POST /api/auth/admin-login |
Login with an admin-role check |
POST /api/auth/logout |
Clear the session cookie |
GET /api/health |
Liveness response and version; no database probe |
GET, POST /api/cron/live-class-reminders |
Cron health check and protected reminder execution |
POST /api/quizzes/[quizId]/attempt |
Quiz attempt submission |
GET /api/resources/[id]/download |
Re-checks access, records the download, 302-redirects to the resource's fileUrl (relative paths resolved against the request origin) |
POST /api/webhooks/paymongo |
Signature-verified PayMongo webhook processing |
POST /actions/verifyEmail |
Email verification action route |
GET /admin/audit-log/export |
CSV audit-log export |
GET /certificates/[hash]/pdf |
Certificate PDF response |
There is no public REST API version. Mutations use server actions except for webhooks, third-party callbacks, health, cron, quiz submission, and PDF or CSV responses.
Files under src/app/actions/ currently cover:
- Authentication: signup, login, logout, password reset, verification, resend verification, two-factor setup.
- Checkout and access: checkout, enrollment, course access.
- Curriculum administration: course, module, lesson create/update/delete/reorder/archive actions.
- Payment operations: refund request, refund processing, certificate revocation.
- Admin resources: users and impersonation, discount codes, badges, simulator scenarios, live classes, download-center resources.
- Simulator lifecycle: start attempt, save decision, submit, grade, compose feedback, and four tool-specific wrappers.
- Audit and operations: list/export audit logs, live-class reminder invocation.
Each action should parse untrusted input, obtain the request container, call a use case, and return a discriminated result. The exact input and error unions are defined beside each action and use case; this document intentionally does not duplicate those types.
src/usecases/ contains flat application classes plus src/usecases/auth/ for the password and email token flows.
| Group | Representative classes |
|---|---|
| Authentication | SignUp, Login, Logout, VerifyEmail, ResendVerification, RequestPasswordReset, ResetPassword |
| Checkout and access | CreatePaymentIntent, ApplyDiscountCode, EnrollStudent, CheckCourseAccess, AuthorizeLessonAccess, ProcessRefund (moved to refund/: RequestRefund) |
| Curriculum | ListCatalogCourses, GetCatalogCourse, ListCourses, GetCourse, CreateCourse, UpdateCourse, ArchiveCourse, CreateModule, UpdateModule, DeleteModule, ReorderModules, CreateLesson, UpdateLesson, DeleteLesson, ReorderLessons, RebuildCourseCurriculum |
| Learning | RecordQuizAttempt, AwardXP, AwardBadge, ListUserBadges (moved to progress/: RecordStreakVisit, MarkLessonComplete) |
| Certificates | IssueCertificate, RenderCertificatePdf, VerifyCertificate, RevokeCertificate |
| Simulator infrastructure | StartSimulatorAttempt, SaveSimulatorDecision, SubmitSimulatorAttempt, GradeSimulatorAttempt, ComposeAttemptFeedback |
| Simulator administration | AdminListScenarios, GetSimulatorScenario, CreateSimulatorScenario, UpdateSimulatorScenario, ArchiveSimulatorScenario |
| Admin operations | ListUsers, GetUserDetail, ImpersonateUser, GetAdminDashboardStats, payment and refund admin classes, audit-log classes, live-class classes, discount-code classes, badge classes |
| Email and reminders | SendLiveClassReminders (moved to email/: ListEmailTemplates, GetEmailTemplate, UpdateEmailTemplate) |
| Two-factor authentication | EnableTwoFactor, ConfirmTwoFactor, DisableTwoFactor |
| Download center (STORY-098) | CreateResource, UpdateResource, DeleteResource, AdminListResources, AdminGetResource, ListAvailableResources, RecordResourceDownload, PurgeResource, UploadFile, DeleteFile (STORY-098.5) |
src/ports/repositories/ contains ports for users, sessions, courses, modules, lessons, orders, enrollments, discount codes, quizzes and attempts, XP and progress, badges and awards, certificates, audit logs, webhook events, simulator scenarios and attempts, score policies, feedback, live classes, pricing tiers, email verification, password reset, sent reminders, user streaks, email templates, and download-center resources (IResourceRepository, STORY-098).
IFileStorage(STORY-098.5) — generic upload/delete; production adapter isVercelBlobFileStorage, dev fallback isLocalFileStorage(does not persist in production)IPaymentGatewayEmailSender,EmailVerificationRenderer,LiveClassReminderRendererIAccessPolicyCertificateRenderer,IMdxContentRendererJwtService,PasswordHasher,RateLimiter,TotpService,CertificateHashGeneratorSimulator,SimulatorRegistryClock,IdGenerator,ContentIdGenerator,Logger
The production container uses Prisma, PayMongo, Resend, Argon2, jose, otpauth, Upstash, React PDF, and Next MDX adapters. The test container uses in-memory or fake implementations.
Known adapter gaps are tracked in docs/audit-2026-07-27-completeness-review.md. In particular, Prisma badge mutation methods are stubs, and the admin seed script does not use the shared Prisma 7 adapter.
- JWTs are signed with HS256 using
JWT_SECRET. - Session cookies are HttpOnly, SameSite=Lax, and use
amph_sessionfor HTTP or__Secure-amph_sessionfor HTTPS. - Login and signup route handlers set cookies on the redirect response.
getSessionUserId()verifies the cookie andgetSessionUser()reloads the user row.requireAuth()redirects unauthenticated requests to/login.requireAdmin()redirects non-admin users to/dashboard?error=forbidden.
Current limitation: request guards do not consult the sessions table or lockedUntil, so session deletion and account lockout do not revoke an already-issued JWT. Role changes are re-read from Postgres and do take effect on the next guarded request.
POST /api/webhooks/paymongo obtains the production container, verifies the PayMongo signature, records a durable WebhookEvent, and processes the event through the configured order, enrollment, and audit ports. The old documentation claim that the route creates in-memory repositories is obsolete.
The current repository contains no separate public Checkout, Payment, Refund, or Receipt Prisma models from the original target design. Payment state is represented by Order and related fields. Confirm model names in prisma/schema.prisma before writing integrations.
GET /api/cron/live-class-reminders reports whether CRON_SECRET is configured. POST requires x-cron-secret, then invokes SendLiveClassReminders. SentReminder persistence makes sends idempotent. Vercel configuration currently runs the job once daily at 0 8 * * *.
Domain and use-case failures use the Result type from src/domain/shared/Result.ts. Route handlers and server actions map those results to redirects, JSON, or page states. Programmer invariant violations can throw; external and business failures should remain discriminated results.
For the current verification commands and known Windows test limitations, see the completeness audit and README.md.