Skip to content

Integrations

The backend integrates with several external or cross-cutting systems, but Redis is intentionally used only above the database boundary (token revocation, throttling, queues). PostgreSQL remains the single source of truth.

src/redis/redis.module.ts provides a REDIS_CLIENT token and a TokenStore abstraction.

export const REDIS_CLIENT = 'SIM_REDIS_CLIENT';
@Global()
@Module({
providers: [
{ provide: REDIS_CLIENT, useFactory: (config) => new Redis(REDIS_URL) || null },
{ provide: TokenStore, useFactory: (_, client) => client ? new RedisTokenStore(client) : new InMemoryTokenStore() },
],
exports: [TokenStore, REDIS_CLIENT],
})
  • In development, REDIS_URL absent falls back to InMemoryTokenStore.
  • In production, REDIS_URL is required.
  • Keys: sim:auth:jti:${jti} with EX TTL.
  • revoked fails open on Redis outage.
  • Used by JwtAuthGuard to deny revoked access tokens.
  • src/jobs/jobs.queue.ts — BullMQ Queue
  • src/jobs/jobs.worker.ts — BullMQ Worker
  • src/health/indicators.ts — Redis health ping

src/s3/s3.module.ts exports a global S3_CLIENT provider.

export const S3_CLIENT = 'SIM_S3_CLIENT';
new S3Client({
endpoint: s3.endpoint,
region: s3.region,
forcePathStyle: true, // required for MinIO
credentials: { accessKeyId, secretAccessKey },
});
export const S3_DEFAULTS = {
endpoint: 'http://127.0.0.1:9000',
region: 'us-east-1',
bucket: 'sim',
accessKeyId: 'sim',
secretAccessKey: 'sim-admin',
};

src/uploads/uploads.service.ts:

  • validates category and MIME type
  • generates <categorie>/<userId>-<uuid>.<ext>
  • auto-creates bucket if missing
  • returns object bytes for GET /api/v1/uploads?key=...
  • validates KEY_PATTERN before GetObject to prevent path traversal

Object keys are stored in PostgreSQL; only the key, not the file content, lives in the database.

src/mailer wraps Resend. In development, emails are logged rather than sent because RESEND_API_KEY defaults to empty. Password-reset emails are the primary user-facing email flow.

src/pdf provides PdfService.render(html, filename) using headless Chromium. The Dockerfile installs Chrome libraries and sets PUPPETEER_EXECUTABLE_PATH.

PDF services per module:

  • src/residence/contrat-pdf.service.ts — 25-article French lease
  • src/residence/echeance-pdf.service.ts — rent receipt
  • src/residence/paiement-pdf.service.ts — generic resident payment receipt
  • src/facturation/facture-pdf.service.ts — A4 invoice + 58/80mm ticket
  • src/rh/bulletin-pdf.service.ts — payslip
  • src/rapports/rapports-pdf.service.ts — report sheets
  • src/audit/audit-pdf.service.ts — audit journal report
  • src/dashboard/dashboard-pdf.service.ts — dashboard tiles

Pure SVG chart generation lives in src/audit/audit-charts.ts.

src/metrics exposes GET /metrics as a public, version-neutral scrape endpoint.

MetricsMiddleware records:

  • http_requests_total{method, route, status}
  • http_request_duration_seconds{method, route, status}

Additional counters:

  • sim_jobs_total{job, outcome}
  • sim_jobs_queue_depth{status}

Default Node metrics are disabled under NODE_ENV=test.

src/jobs/jobs.queue.ts creates a bullmq Queue only when REDIS_URL is set. src/jobs/jobs.worker.ts consumes sim-jobs with concurrency: 1. Jobs are added with attempts: 3 and fixed 5-second backoff.

The service seam in src/jobs/jobs.service.ts wraps each job with metrics logging and rethrows errors so HTTP triggers surface real failures.

src/health exposes public routes:

Route Purpose
/api/v1/health/live Always { status: 'ok' }
/api/v1/health/ready PostgreSQL + Redis readiness
/api/v1/health Aggregate

PgHealthIndicator runs select 1 on the real Kysely instance. RedisHealthIndicator pings ioredis or reports mode: 'in-memory' when Redis is absent.