import { CanActivate, ExecutionContext, Injectable, ServiceUnavailableException, SetMetadata, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { MaintenanceStateService } from './maintenance-state.service'; const MAINTENANCE_EXEMPT_KEY = 'maintenanceExempt'; /** * Marks routes that stay reachable while an in-app restore runs: the health * probes (monitors must keep seeing the instance) and the restore status * endpoint the maintenance screen polls. */ export const MaintenanceExempt = (): MethodDecorator & ClassDecorator => SetMetadata(MAINTENANCE_EXEMPT_KEY, true); /** * Global first-line guard (registered before SetupModule and AuthModule via * module order): while the backup sidecar restores the database, every * non-exempt route answers 503 `maintenance_mode` (issue #103) — nothing * may read or write mid-restore state. */ @Injectable() export class MaintenanceGuard implements CanActivate { constructor( private readonly reflector: Reflector, private readonly state: MaintenanceStateService, ) {} canActivate(context: ExecutionContext): boolean { const exempt = this.reflector.getAllAndOverride(MAINTENANCE_EXEMPT_KEY, [ context.getHandler(), context.getClass(), ]); if (exempt) return true; if (this.state.isActive()) { throw new ServiceUnavailableException({ code: 'maintenance_mode' }); } return true; } }