Перейти к содержимому

01 -- Architecture Overview: Admin Service

The platform currently supports two user roles — Customer and Employee — each with dedicated JWT authentication infrastructure, Passport strategies, and guards. This task introduces a full Admin Service layer to enable platform operators to manage users and projects through a privileged administrative interface. The Admin Service implements passwordless authentication (corporate email plus 6-digit verification code), a separate admin JWT token infrastructure (access + refresh), project management CRUD (list all, edit any, soft delete), and user management (list, block/unblock). Prisma schema changes are required to add ADMIN to the RoleType enum, introduce isBlocked on the User model, add deletedAt to the Project model for soft deletes, and make passwordHash/passwordSalt nullable to support the passwordless admin flow.


#PathTypeActionPurpose
1apps/auth-apiAppModifiedHost admin auth endpoints (/admin/auth/*), extend app.module.ts to import admin-auth feature, add Swagger tag in main.ts
2apps/core-apiAppModifiedPrisma schema changes (ADMIN enum, isBlocked, deletedAt, nullable password fields), new migration, seed ADMIN role record
3apps/admin-apiAppCreatedNew NX app hosting admin CRUD endpoints (/admin/projects/*, /admin/users/*)
4apps/user-apiAppReference OnlyExisting user microservice; no direct changes, consumed via shared Prisma client
5apps/project-apiAppReference OnlyExisting project microservice; soft-delete filter applied through its provider lib
6libs/apis/sharedLibModifiedAdd AdminAccessTokenGuard, AdminRefreshTokenGuard, ADMIN to Role constants, token extraction helper for admin tokens
7libs/apis/providers/auth-api/features/tokenLibModifiedAdd TokenAdminService for admin access/refresh token generation and validation
8libs/apis/providers/auth-api/features/employee-authLibModifiedAdd isBlocked check to EmployeeAccessTokenStrategy.validate()
9libs/apis/providers/auth-api/features/customer-authLibModifiedAdd isBlocked check to CustomerAccessTokenStrategy.validate()
10libs/apis/providers/email-sender-api/features/senderLibModifiedAdd admin_email_verification HTML template and sendAdminVerificationCode() method
11libs/apis/providers/project-api/features/projectLibModifiedAdd deletedAt IS NULL filter to all existing project queries
12libs/apis/providers/user-api/features/userLibModifiedAdd updateIsBlocked(userId, isBlocked) method to UserService
13libs/apis/configs/auth-api/jwt-adminLibCreatedJWT admin configuration module: env vars JWT_ADMIN_ACCESS_SECRET, JWT_ADMIN_REFRESH_SECRET, expiration settings
14libs/apis/providers/auth-api/features/admin-authLibCreatedPasswordless auth flow: controller, service, AdminAccessTokenStrategy, AdminRefreshTokenStrategy, register/login/verify/refresh
15libs/apis/providers/admin-api/features/admin-projectLibCreatedAdmin project CRUD: list all (with pagination + filters), get by ID, edit any project, soft delete
16libs/apis/providers/admin-api/features/admin-userLibCreatedAdmin user management: list (with pagination + filters), get by ID, block, unblock
17libs/apis/providers/admin-api/data-accessLibCreatedDTOs (AdminProjectDto, AdminUserDto, pagination DTOs) and constants for admin endpoints

Context. The admin service requires 8 CRUD endpoints for project and user management. The existing auth-api app handles authentication, while user-api and project-api handle their respective domains. We need to decide where the admin CRUD controllers live.

Decision. Create a new admin-api NX application dedicated to admin CRUD operations. Admin authentication endpoints (/admin/auth/*) remain in auth-api since they are part of the auth domain. The admin-api app hosts project management (/admin/projects/*) and user management (/admin/users/*) controllers.

Alternatives considered.

  1. Add everything to auth-api under /admin prefix. Simpler setup but conflates authentication concerns with resource management. The auth-api app would grow into a monolith, violating the existing microservice boundary conventions.
  2. Split across user-api and project-api with admin guards. Follows domain boundaries but scatters admin logic across multiple apps, making it harder to reason about admin capabilities as a unit and to apply uniform admin middleware.
  3. Single admin-api for both auth and CRUD. Clean separation but breaks the established pattern where all authentication flows live in auth-api.

Consequences.

  • Positive: Clear separation of concerns; admin CRUD is isolated and independently deployable; auth-api stays focused on authentication.
  • Positive: Follows the existing monorepo convention where each app maps to a bounded context.
  • Negative: One additional app to build, configure (Swagger, env, Dockerfile), and deploy. Increases infrastructure surface slightly.
  • Negative: Admin auth and admin CRUD are in different apps, requiring shared JWT validation infrastructure through the shared lib.

Context. Admin users authenticate without passwords. The system must verify admin identity through a secondary channel. The existing codebase already has infrastructure for 6-digit code generation (random-code.helper.ts), Redis-based code storage with TTL, cooldown enforcement, and SMTP email delivery via @nestjs-modules/mailer.

Decision. Use email-based 6-digit verification codes for passwordless admin auth. Both registration and login follow a two-step flow: (1) submit email, receive code; (2) submit code, receive JWT tokens. Codes are stored in Redis with a 900-second TTL and a 120-second resend cooldown, reusing the existing verification infrastructure patterns.

Alternatives considered.

  1. Magic link (email with signed URL). Simpler UX (single click) but requires configuring a frontend callback URL, does not work well in API-only/Swagger testing scenarios, and introduces URL expiration management that differs from the existing code-based pattern.
  2. OAuth/SSO with corporate identity provider. Strongest security but adds external IdP dependency (Google Workspace, Okta, etc.), significant integration complexity, and is premature given the current scale and the absence of an existing SSO provider.
  3. Password-based auth identical to Customer/Employee. Simplest to implement (reuse existing flows) but the spec explicitly mandates passwordless, and admin accounts are managed differently — no self-service password reset, no password storage liability.

Consequences.

  • Positive: Reuses existing code generation, Redis TTL, cooldown, and email sending infrastructure with minimal new code.
  • Positive: No password storage for admin accounts, reducing credential-related attack surface.
  • Negative: Every login requires email access, which may be slower than password-based auth for frequent admin sessions. Mitigated by long-lived refresh tokens.
  • Negative: Dependency on SMTP availability for every auth attempt.

Context. Admin users need the ability to remove projects from public visibility without permanently destroying data. The system must support potential future restoration and audit trails. The Project model currently has no deletion mechanism.

Decision. Add a nullable deletedAt (DateTime?) column to the Project model. A soft-deleted project has deletedAt set to the current timestamp. All existing project queries in ProjectService add a WHERE deletedAt IS NULL filter. The admin soft-delete endpoint sets deletedAt via a Prisma update, not a delete.

Alternatives considered.

  1. Separate archive table. Move deleted projects to a ProjectArchive table. Provides clean separation but requires duplicating the schema, managing data migration between tables, and updating all relations (e.g., WalriderThread.projectId foreign key).
  2. Status flag (status: DELETED). The ProjectStatus enum already exists; adding a DELETED value is simple. However, this conflates lifecycle status (DRAFT, IN_PROGRESS, COMPLETED) with visibility/deletion semantics. A project could be COMPLETED and also soft-deleted; a single enum cannot represent both states.
  3. Prisma middleware / global filter. Apply deletedAt IS NULL automatically via Prisma middleware or $extends. Convenient but implicit, making it easy to forget about soft deletes, harder to debug, and the existing codebase does not use Prisma middleware.

Consequences.

  • Positive: Simple, well-understood pattern. Single new nullable column; no schema duplication.
  • Positive: Supports future restoration by setting deletedAt back to null.
  • Positive: Compatible with the existing ProjectStatus enum without semantic overloading.
  • Negative: Every existing query that returns projects must be updated to filter deletedAt IS NULL. Missed filters could leak soft-deleted data.
  • Negative: Soft-deleted records accumulate in the same table, potentially affecting query performance at scale. Mitigated by indexing deletedAt.

Context. The system already uses separate JWT secret pairs for Customer (JWT_CUSTOMER_ACCESS_SECRET, JWT_CUSTOMER_REFRESH_SECRET) and Employee (JWT_EMPLOYEE_ACCESS_SECRET, JWT_EMPLOYEE_REFRESH_SECRET). Admin tokens need their own signing configuration.

Decision. Create a dedicated jwt-admin config library (libs/apis/configs/auth-api/jwt-admin) with its own secret pair (JWT_ADMIN_ACCESS_SECRET, JWT_ADMIN_REFRESH_SECRET) and separate expiration values. Admin Passport strategies validate exclusively against admin secrets.

Alternatives considered.

  1. Reuse Employee JWT secrets with a role claim. Fewer env vars but a compromised Employee secret would grant admin access. Role claims in the token payload could be tampered with if the same signing key is shared.
  2. Single shared secret with audience/issuer claims. Reduces secret management overhead but weakens the security boundary; all token types become interchangeable if claims validation is missed.

Consequences.

  • Positive: Cryptographic isolation between admin and non-admin tokens. A compromised Customer or Employee secret cannot forge admin tokens.
  • Positive: Consistent with the existing multi-secret pattern, reducing cognitive overhead for developers familiar with the codebase.
  • Negative: Two additional secrets to generate, store, and rotate per environment.

Context. The new isBlocked field on User must be enforced so that blocked users cannot access protected endpoints. This check needs to be applied to all three user types (Customer, Employee, Admin).

Decision. Add the isBlocked check inside each Passport strategy’s validate() method (CustomerAccessTokenStrategy, EmployeeAccessTokenStrategy, AdminAccessTokenStrategy). If isBlocked is true, the strategy throws UnauthorizedException, preventing token validation from succeeding.

Alternatives considered.

  1. Dedicated IsBlockedGuard applied globally or per-route. Separates concerns but adds another database query per request (the strategy already fetches the user). Also requires ensuring guard ordering relative to auth guards.
  2. Redis-based blocklist checked in middleware. Fastest check but introduces cache invalidation complexity. Blocking/unblocking a user would require updating both the database and the Redis blocklist atomically.

Consequences.

  • Positive: No additional database query; the strategy already loads the user, so the isBlocked check is effectively free.
  • Positive: Blocked users are rejected at the earliest possible point in the request lifecycle (token validation).
  • Negative: The check is duplicated across three strategies. Mitigated by the fact that each strategy is a separate class by design in this codebase, and the check is a single conditional.

  1. Admin domain restriction is sufficient for registration authorization. Only users with email addresses matching domains listed in ADMIN_ALLOWED_DOMAINS can register as admins. There is no additional invitation flow, approval queue, or superadmin confirmation step required.

  2. All admins have equal privileges. There is no admin hierarchy, permission levels, or role-based access control within the admin tier. Every admin can perform all admin operations (manage any project, block/unblock any user).

  3. Existing Token and Session models are reused for admin tokens. Admin access/refresh tokens are stored in the same tokens and sessions tables used by Customer and Employee. The userId foreign key is sufficient to associate tokens with the admin user, and the UserOnRole join table distinguishes the role.

  4. The passwordHash and passwordSalt fields can be made nullable without breaking existing auth flows. Existing Customer and Employee registration flows always populate these fields, so existing user records will not have null values. Prisma validation and application-level checks in Customer/Employee strategies will continue to work as before.

  5. Soft-deleted projects are excluded from all non-admin queries by default. The project-api service’s ProjectService will filter out deletedAt IS NOT NULL records in all list and detail queries. Only the admin project endpoints will have the ability to query soft-deleted projects (for audit or restore purposes in future iterations).

  6. Admin verification codes use the same TTL (900s) and resend cooldown (120s) as existing email verification. These values are consistent with the established patterns in the codebase and are not configurable per-role.

  7. The admin-api app connects to the same PostgreSQL database (via Prisma) and the same Redis instance as the other apps. No separate data store is provisioned for admin operations.

  8. RabbitMQ is used for inter-service communication where needed (e.g., email sending), but admin CRUD operations are synchronous request-response over HTTP against the shared database. There is no event-driven saga for admin actions in this iteration.


#FileDescription
1architecture/01_overview.mdThis document. Task restatement, scope table, ADRs, assumptions, and file index.
2architecture/02_api_endpoints.mdFull specification of all 13 admin API endpoints: routes, HTTP methods, request/response schemas, status codes, auth requirements.
3architecture/03_prisma_schema.mdPrisma schema changes: ADMIN enum value, isBlocked on User, nullable password fields, deletedAt on Project, migration plan, seed data.
4architecture/04_auth_flow.mdDetailed passwordless authentication flow for admin: registration sequence, login sequence, code verification, token issuance, refresh logic.
5architecture/05_jwt_infrastructure.mdAdmin JWT configuration: env variables, secret management, token payload structure, expiration policy, Passport strategy wiring.
6architecture/06_guards_and_strategies.mdAdmin guards (AdminAccessTokenGuard, AdminRefreshTokenGuard), strategy implementations, isBlocked enforcement across all user types.
7architecture/07_project_management.mdAdmin project CRUD: list all with pagination/filters, edit any project, soft delete/restore logic, deletedAt filtering in existing queries.
8architecture/08_user_management.mdAdmin user management: list with pagination/filters, block/unblock flow, isBlocked field behavior, cascade effects on active sessions.
9architecture/09_libs_structure.mdNX library creation and modification plan: new lib generators, module wiring, barrel exports, dependency graph between libs.
10architecture/10_email_templates.mdAdmin email verification template specification: HTML structure, template variables, localization (en/ru), integration with EmailSenderService.
11architecture/11_testing_strategy.mdTesting plan: unit tests for services/strategies, e2e tests for admin endpoints, test fixtures, mocking strategy for Redis/Prisma/SMTP.