Console Super-Admin (plateforme)

Console d'administration plateforme — distincte du back-office d'une institution. Elle sert à gérer l'ensemble des tenants (institutions) hébergés sur Kairos MFI.

Accès

  • URL : https://app.kairos-mfi.com/super-admin/login
  • Authentification : compte SuperAdmin (email + mot de passe, 2FA TOTP optionnel), totalement séparé des comptes back-office des tenants.
  • Fallback pour scripts / CI : header x-super-admin-key (variable SUPER_ADMIN_KEY).

Le super-admin n'est rattaché à aucun tenant (pas de tenantId dans son JWT, type: 'super-admin').

Créer le premier super-admin

Les tables vivent dans le schéma public (SuperAdmin, SuperAdminAuditLog). Si elles n'existent pas encore, appliquer migrations/2026-06-24-super-admin-tables.sql.

Créer le compte (sur le VPS) :

# 1. Générer le hash bcrypt (chemin pnpm du module bcryptjs)
docker compose -f docker-compose.prod.yml --env-file .env.prod exec api \
  node -e "console.log(require('/app/node_modules/.pnpm/bcryptjs@2.4.3/node_modules/bcryptjs').hashSync('MonMotDePasse', 12))"

# 2. Insérer via un FICHIER (⚠️ ne PAS passer le hash en argument bash :
#    les $ du hash bcrypt seraient interprétés comme des variables shell).
cat > /tmp/sa.sql <<'SQL'
INSERT INTO public."SuperAdmin"
  (id, email, "passwordHash", "firstName", "lastName", "isActive", "createdAt", "updatedAt")
VALUES
  (gen_random_uuid()::text, 'admin@kairos-mfi.com', 'COLLER_LE_HASH', 'Super', 'Admin', true, now(), now());
SQL
docker compose -f docker-compose.prod.yml --env-file .env.prod exec -T postgres \
  psql -U kairos -d kairos_mfi < /tmp/sa.sql
rm /tmp/sa.sql

Piège classique : ne jamais mettre un hash bcrypt ($2a$12$...) directement dans une commande psql -c "..." entre guillemets doubles. Bash interprète $2a, $12, $5 comme des variables et corrompt le hash. Toujours passer par un heredoc quoté (<<'SQL') ou un fichier.

Fonctionnalités

Tenants

  • Liste des institutions avec compteurs (membres, comptes, prêts).
  • Fiche tenant : infos, plan (STARTER / PROFESSIONAL / ENTERPRISE), statut actif/inactif, usage (membres, comptes, prêts, SMS, stockage).
  • Provisionner une nouvelle institution (clone du schéma template + directeur initial).
  • Activer / désactiver / supprimer un tenant (suppression avec confirmation par le slug).

Gestion des comptes utilisateurs d'un tenant

Sur la fiche tenant, section Back-office accounts :

Action Endpoint Effet
Lister GET /v1/super-admin/tenants/:id/users Users back-office du tenant (rôle, statut, 2FA, dernière connexion)
Reset mdp POST .../users/:userId/reset-password Génère un mdp temporaire 10 car., force mustChangePassword, le retourne une fois
Activer/désactiver POST .../users/:userId/set-active { isActive }. Refuse de désactiver le dernier DIRECTOR actif
Désactiver 2FA POST .../users/:userId/disable-2fa Lève le TOTP (dépannage perte de device)

Toutes ces actions sont auditées dans SuperAdminAuditLog avec l'id du super-admin, l'action, le tenant ciblé et l'ip.

Techniquement : le super-admin résout le tenant → son schéma Postgres, puis obtient un PrismaClient scopé via TenantPrismaService.forTenant() (cf. super-admin-tenant-ops.service.ts).

Feature flags par tenant

Sur la fiche tenant, section Features : des toggles activent / désactivent par institution :

Feature Clé Effet si désactivé
Mobile Money mobile_money /v1/mobile-money/* → 403
Reporting réglementaire regulatory /v1/regulatory/* → 403
Portail membre member_portal (UI back-office masquée — cf. note)
Agent terrain field_agent /v1/field-agent/* → 403
Webhooks sortants webhooks /v1/webhooks/* → 403

Stockage : colonne Tenant.features (JSONB). null ou clé absente = activé (rétro-compat). Seul false désactive.

Enforcement : décorateur @RequiresFeature('...') sur les controllers concernés + FeatureGuard global qui lit req.tenant.features (posé par le TenantMiddleware). Après un changement, le cache du TenantService est invalidé → effet immédiat.

Note portail membre : les routes /v1/portal/* sont bypassées par le TenantMiddleware (le tenant est résolu depuis le JWT membre, pas req.tenant). Le flag member_portal gate donc l'UI et la visibilité, mais l'enforcement backend strict des routes portail nécessitera un hook dédié dans une itération ultérieure.

Endpoints :

  • GET /v1/super-admin/tenants/:id/features{ catalog, features }
  • PATCH /v1/super-admin/tenants/:id/features → merge { key: boolean }

Monitoring & incidents (/super-admin/monitoring)

Vue plateforme de la santé et des incidents, toutes institutions confondues :

  • Infra : latence DB.
  • Tableau par institution : users actifs / total, dernière connexion (max sur les users du tenant), nb d'événements d'audit sur 7 jours, nb de notifications échouées sur 7 jours.
  • Rafraîchissement auto toutes les 60 s.

Sur la fiche tenant, carte Activity & incidents :

  • Notifications en échec (canal, template, destinataire, message d'erreur, date) — le vrai signal « incident ».
  • 25 derniers événements d'audit du tenant (action, entité, date).

Endpoints :

  • GET /v1/super-admin/monitoring/health{ infra, tenants[], generatedAt }
  • GET /v1/super-admin/tenants/:id/activity{ tenant, recentAudit[], recentFailedNotifications[] }

Implémentation : agrégation cross-schéma via $queryRawUnsafe, chaque requête protégée par try/catch (un schéma absent ne casse pas la vue), cache mémoire 30 s. Pour les erreurs applicatives détaillées (stack traces), la source de vérité reste Sentry (API + Web).

Audit log super-admin

GET /v1/super-admin/audit — journal de toutes les opérations plateforme (login, création tenant, activation/désactivation, reset mdp user, etc.).

Sécurité

  • 2FA fortement recommandé sur les comptes super-admin (option dans Profil → 2FA setup).
  • Le mot de passe super-admin doit être unique (ne pas réutiliser celui d'un compte back-office ou du SMTP).
  • La clé statique SUPER_ADMIN_KEY est un secret : à faire tourner avant chaque mise en production et à ne jamais commiter.

À venir

  • Feature flags par tenant — activer/désactiver Mobile Money, reporting réglementaire, portail membre, app agent par institution.
  • Incidents & logs centralisés — santé par tenant, dernières connexions, audit cross-tenant, intégration Sentry.