feat(activation): wire notifications into enrollment/auth, real password-reset delivery
- integration-routes.js: accepts student.contact_email, calls notifyForEnrollment on a genuinely new enrollment only (never on an idempotent replay — inherits Phase 5B's proven enrollment-level idempotency, no separate notification dedup needed). - admin-routes.js: create-student/convert/reset-password now enqueue the corresponding notification using the SAME token already minted synchronously (never mints twice, which would silently invalidate the token just handed back in the response). Fixed a real bug: request-password-reset would mint a reset token for a never-activated student, which makes no sense and would confuse them — now falls through to the generic anti-enumeration response. - admin-programs-routes.js: manual enrollment triggers the same notification decision as the website path. - student-auth-routes.js: /activate records a metric; /change-password and /reset-password both queue a password_changed security notice; /request-password-reset now actually delivers email (production still never returns the token — unchanged anti-enumeration/isProduction gating, just now with a real send behind it). - rbac.js: requireStudentRelationshipAccess/checkStudentRelationshipAccess — the notification visibility rule (cohort-shared or an enrollment they created), reusable beyond just enrollments. - server.js: wires notification-routes.js and starts the notification worker in main(). - docker-compose.dev.yml: PUBLIC_APP_ORIGIN, EMAIL_FROM_NAME, and a parametrized COOKIE_SECURE (was hard-commented-out; needed so e2e-phase5c.js can actually prove production gating).
This commit is contained in:
@@ -73,10 +73,21 @@ services:
|
||||
# POST /api/integrations/enrollments refuses every request (fails closed), which is fine
|
||||
# for anyone not exercising that boundary locally.
|
||||
INTEGRATION_SHARED_SECRET: ${INTEGRATION_SHARED_SECRET:-}
|
||||
# Phase 5C — activation/reset email links are built from this origin (see
|
||||
# docs/PHASE5C_ENROLLMENT_COMMUNICATIONS.md §7). The Gateway's own dev URL, since that's
|
||||
# what a browser actually hits locally. EMAIL_PROVIDER is deliberately left unset here —
|
||||
# it defaults to the dev provider (email/dev-provider.js), which never makes a real
|
||||
# network call no matter what recipient it's given.
|
||||
PUBLIC_APP_ORIGIN: ${PUBLIC_APP_ORIGIN:-http://localhost:4200}
|
||||
EMAIL_FROM_NAME: ${EMAIL_FROM_NAME:-RootNode Academy}
|
||||
PLATFORM_API_PORT: "4300"
|
||||
# No ADMIN_PASSWORD anymore (Phase 3A) — instructor accounts live in Postgres, created via
|
||||
# `npm run bootstrap-owner` for the first owner and the admin portal after that.
|
||||
# COOKIE_SECURE: "1" # set once served over real HTTPS
|
||||
# Empty by default — unset/empty means the exact same dev behavior as before this was
|
||||
# parametrized (dev_reset_token echoed, dev-preview route reachable). Phase 5C's
|
||||
# e2e-phase5c.js sets this to "1" via .env for one test to prove production gating for
|
||||
# real, then unsets it again — see that test's own comment.
|
||||
COOKIE_SECURE: ${COOKIE_SECURE:-}
|
||||
# TRUST_PROXY: "1" # set once fronted by a trusted reverse proxy
|
||||
depends_on:
|
||||
postgres:
|
||||
|
||||
Reference in New Issue
Block a user