feat: BackupService real pg_dump, OrganisationService region stats, SystemConfigService overrides
- BackupService: DB-persisted metadata (BackupRecord/BackupConfig entities + V16 Flyway migration), real pg_dump execution via ProcessBuilder, soft-delete on deleteBackup, pg_restore manual guidance - OrganisationService: repartitionRegion now queries Adresse entities (was Map.of() stub) - SystemConfigService: in-memory config overrides via AtomicReference (no DB dependency) - SystemMetricsService: null-guard on MemoryMXBean in getSystemStatus() (fixes test NPE) - Souscription workflow: SouscriptionService, SouscriptionResource, FormuleAbonnementRepository, V11 Flyway migration, admin REST clients - Flyway V8-V15: notes membres, types référence, type orga constraint, seed roles, première connexion, Wave checkout URL, Wave telephone column length fix - .gitignore: added uploads/ and .claude/ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -26,7 +26,9 @@ quarkus.http.cors.origins=*
|
||||
quarkus.oidc.tenant-enabled=true
|
||||
quarkus.oidc.auth-server-url=http://localhost:8180/realms/unionflow
|
||||
quarkus.oidc.client-id=unionflow-server
|
||||
quarkus.oidc.token.audience=unionflow-mobile
|
||||
# Validation audience : seuls les tokens destinés à unionflow-server sont acceptés
|
||||
# Nécessite un audience mapper dans Keycloak : unionflow-mobile client → scope → audience mapper → unionflow-server
|
||||
quarkus.oidc.token.audience=unionflow-server
|
||||
quarkus.oidc.credentials.secret=unionflow-secret-2025
|
||||
quarkus.oidc.tls.verification=none
|
||||
|
||||
@@ -47,3 +49,10 @@ quarkus.log.category."io.quarkus.security".level=INFO
|
||||
# Wave — mock pour dev (pas de clé API requise)
|
||||
wave.mock.enabled=true
|
||||
wave.redirect.base.url=http://localhost:8085
|
||||
|
||||
# OIDC client "admin-service" — service account pour appels admin vers lions-user-manager
|
||||
quarkus.oidc-client.admin-service.auth-server-url=http://localhost:8180/realms/unionflow
|
||||
quarkus.oidc-client.admin-service.client-id=unionflow-server
|
||||
quarkus.oidc-client.admin-service.credentials.secret=unionflow-secret-2025
|
||||
quarkus.oidc-client.admin-service.grant.type=client
|
||||
quarkus.oidc-client.admin-service.tls.verification=none
|
||||
|
||||
@@ -32,6 +32,9 @@ quarkus.oidc.auth-server-url=${KEYCLOAK_AUTH_SERVER_URL:https://security.lions.d
|
||||
quarkus.oidc.client-id=unionflow-server
|
||||
quarkus.oidc.credentials.secret=${KEYCLOAK_CLIENT_SECRET}
|
||||
quarkus.oidc.tls.verification=required
|
||||
# Validation audience : seuls les tokens destinés à unionflow-server sont acceptés
|
||||
# Nécessite un audience mapper dans Keycloak : unionflow-mobile client → scope → audience mapper → unionflow-server
|
||||
quarkus.oidc.token.audience=unionflow-server
|
||||
|
||||
# OpenAPI — serveur prod
|
||||
quarkus.smallrye-openapi.servers=https://api.lions.dev/unionflow
|
||||
@@ -50,6 +53,12 @@ quarkus.log.category."org.jboss.resteasy".level=WARN
|
||||
# REST Client lions-user-manager
|
||||
quarkus.rest-client.lions-user-manager-api.url=${LIONS_USER_MANAGER_URL:http://lions-user-manager:8081}
|
||||
|
||||
# OIDC client "admin-service" — service account pour appels admin vers lions-user-manager
|
||||
quarkus.oidc-client.admin-service.auth-server-url=${KEYCLOAK_AUTH_SERVER_URL:https://security.lions.dev/realms/unionflow}
|
||||
quarkus.oidc-client.admin-service.client-id=unionflow-server
|
||||
quarkus.oidc-client.admin-service.credentials.secret=${KEYCLOAK_CLIENT_SECRET}
|
||||
quarkus.oidc-client.admin-service.grant.type=client
|
||||
|
||||
# Wave Money — Production
|
||||
wave.environment=production
|
||||
|
||||
|
||||
@@ -6,6 +6,13 @@
|
||||
quarkus.application.name=unionflow-server
|
||||
quarkus.application.version=1.0.0
|
||||
|
||||
# Backup configuration
|
||||
unionflow.backup.directory=${BACKUP_DIR:/tmp/unionflow-backups}
|
||||
|
||||
# Jackson — sérialisation des dates en ISO string (pas en tableau [year, month, day])
|
||||
quarkus.jackson.write-dates-as-timestamps=false
|
||||
quarkus.jackson.serialization-inclusion=non_null
|
||||
|
||||
# Configuration HTTP
|
||||
quarkus.http.port=8085
|
||||
quarkus.http.host=0.0.0.0
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
-- V10__Drop_Organisation_Type_Check_Constraint.sql
|
||||
-- La contrainte chk_organisation_type était basée sur un enum hardcodé (V1) :
|
||||
-- ('ASSOCIATION', 'COOPERATIVE', 'LIONS_CLUB', 'ENTREPRISE', 'ONG', 'FONDATION', 'SYNDICAT', 'AUTRE')
|
||||
-- Les types d'organisation sont désormais dynamiques via la table types_reference.
|
||||
-- Cette contrainte bloque tout INSERT/UPDATE avec un type non prévu initialement (ex: MUTUELLE).
|
||||
|
||||
ALTER TABLE organisations DROP CONSTRAINT IF EXISTS chk_organisation_type;
|
||||
279
src/main/resources/db/migration/V11__Souscription_Workflow.sql
Normal file
279
src/main/resources/db/migration/V11__Souscription_Workflow.sql
Normal file
@@ -0,0 +1,279 @@
|
||||
-- =============================================================================
|
||||
-- V11 — Workflow de souscription/onboarding UnionFlow
|
||||
-- =============================================================================
|
||||
-- Auteur : UnionFlow Team
|
||||
-- Date : 2026-03-30
|
||||
-- Objectif:
|
||||
-- 0. Renommer les tables singulières créées par V1 vers les noms pluriels attendus
|
||||
-- par les entités JPA (FormuleAbonnement → formules_abonnement, etc.)
|
||||
-- 1. Ajouter les colonnes manquantes à formules_abonnement (entité refactorisée)
|
||||
-- 2. Ajouter la colonne `plage` à formules_abonnement (PETITE/MOYENNE/GRANDE/TRES_GRANDE)
|
||||
-- 3. Supprimer le code unique sur `code` seul (nouvelle contrainte sur code+plage)
|
||||
-- 4. Migrer les anciennes valeurs TypeFormule (STARTER→BASIC, CRYSTAL supprimé)
|
||||
-- 5. Vider et re-seeder avec la matrice tarifaire 4×3 (12 formules)
|
||||
-- 6. Ajouter les colonnes du workflow de validation à souscriptions_organisation
|
||||
-- 7. Ajouter EN_ATTENTE au statut (déjà en DB comme VARCHAR, pas de contrainte CHECK)
|
||||
-- =============================================================================
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 0. Renommer les tables singulières → plurielles (alignement entités JPA)
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
-- formule_abonnement → formules_abonnement (FormuleAbonnement entity @Table)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'formule_abonnement')
|
||||
AND NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'formules_abonnement') THEN
|
||||
ALTER TABLE formule_abonnement RENAME TO formules_abonnement;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- souscription_organisation → souscriptions_organisation (SouscriptionOrganisation entity @Table)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'souscription_organisation')
|
||||
AND NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'souscriptions_organisation') THEN
|
||||
ALTER TABLE souscription_organisation RENAME TO souscriptions_organisation;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Mise à jour des noms de contraintes FK après le renommage
|
||||
-- (PostgreSQL met à jour automatiquement les FK qui référencent la table renommée)
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 0b. Ajouter les colonnes manquantes à formules_abonnement (entité refactorisée)
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
-- Colonne libelle (libellé de la formule — nullable temporairement, NOT NULL après seed)
|
||||
ALTER TABLE formules_abonnement ADD COLUMN IF NOT EXISTS libelle VARCHAR(100);
|
||||
|
||||
-- Colonne plage (taille organisation cible)
|
||||
ALTER TABLE formules_abonnement ADD COLUMN IF NOT EXISTS plage VARCHAR(20);
|
||||
|
||||
-- Nombre max de membres (NULL = illimité)
|
||||
ALTER TABLE formules_abonnement ADD COLUMN IF NOT EXISTS max_membres INTEGER;
|
||||
|
||||
-- Stockage max en Mo (défaut 1 Go)
|
||||
ALTER TABLE formules_abonnement ADD COLUMN IF NOT EXISTS max_stockage_mo INTEGER DEFAULT 1024;
|
||||
|
||||
-- Ordre d'affichage dans le catalogue
|
||||
ALTER TABLE formules_abonnement ADD COLUMN IF NOT EXISTS ordre_affichage INTEGER DEFAULT 0;
|
||||
|
||||
-- La colonne `nom` de V1 n'est pas dans l'entité JPA — rendre nullable pour compatibilité
|
||||
ALTER TABLE formules_abonnement ALTER COLUMN nom DROP NOT NULL;
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 0c. Supprimer les contraintes CHECK Hibernate obsolètes
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
-- Hibernate génère automatiquement des CHECK constraints pour les enums @Enumerated(STRING).
|
||||
-- Les valeurs ont changé (STARTER→BASIC, CRYSTAL supprimé) : on supprime ces contraintes
|
||||
-- avant toute modification de données.
|
||||
ALTER TABLE formules_abonnement DROP CONSTRAINT IF EXISTS formules_abonnement_code_check;
|
||||
ALTER TABLE souscriptions_organisation DROP CONSTRAINT IF EXISTS souscriptions_organisation_statut_check;
|
||||
ALTER TABLE souscriptions_organisation DROP CONSTRAINT IF EXISTS souscriptions_organisation_type_periode_check;
|
||||
ALTER TABLE souscriptions_organisation DROP CONSTRAINT IF EXISTS souscriptions_organisation_statut_validation_check;
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 1. Préparer formules_abonnement
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
-- Ajouter la colonne plage si elle n'existe pas encore
|
||||
ALTER TABLE formules_abonnement ADD COLUMN IF NOT EXISTS plage VARCHAR(20);
|
||||
|
||||
-- Supprimer toutes les contraintes unicité sur code seul (V1 ou Hibernate)
|
||||
DO $$
|
||||
DECLARE r RECORD;
|
||||
BEGIN
|
||||
FOR r IN
|
||||
SELECT conname FROM pg_constraint
|
||||
WHERE conrelid = 'formules_abonnement'::regclass
|
||||
AND contype = 'u'
|
||||
AND conname NOT IN ('idx_formule_code_plage')
|
||||
AND array_length(conkey, 1) = 1
|
||||
AND conkey[1] = (
|
||||
SELECT attnum FROM pg_attribute
|
||||
WHERE attrelid = 'formules_abonnement'::regclass AND attname = 'code'
|
||||
)
|
||||
LOOP
|
||||
EXECUTE 'ALTER TABLE formules_abonnement DROP CONSTRAINT ' || quote_ident(r.conname);
|
||||
END LOOP;
|
||||
END $$;
|
||||
|
||||
-- Supprimer les index uniques sur code seul s'ils existent
|
||||
DROP INDEX IF EXISTS idx_formule_code;
|
||||
DROP INDEX IF EXISTS formule_abonnement_code_key;
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 2. Migrer les anciennes données TypeFormule
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
UPDATE formules_abonnement SET code = 'BASIC' WHERE code = 'STARTER';
|
||||
DELETE FROM formules_abonnement WHERE code = 'CRYSTAL';
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 3. Vider et re-seeder avec la nouvelle matrice 4 plages × 3 formules
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
DELETE FROM formules_abonnement;
|
||||
|
||||
INSERT INTO formules_abonnement (
|
||||
id, version, actif, cree_par, modifie_par, date_creation, date_modification,
|
||||
code, libelle, description, plage, max_membres, max_stockage_mo,
|
||||
prix_mensuel, prix_annuel, ordre_affichage
|
||||
) VALUES
|
||||
|
||||
-- ── PETITE (1–100 membres) ──────────────────────────────────────────────────
|
||||
(gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(),
|
||||
'BASIC', 'Basic — Petites structures',
|
||||
'Idéal pour les petites associations de moins de 100 membres',
|
||||
'PETITE', 100, 1024, 3000, 28800, 1),
|
||||
|
||||
(gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(),
|
||||
'STANDARD', 'Standard — Petites structures',
|
||||
'Pour les associations actives de moins de 100 membres',
|
||||
'PETITE', 100, 5120, 6000, 57600, 2),
|
||||
|
||||
(gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(),
|
||||
'PREMIUM', 'Premium — Petites structures',
|
||||
'Fonctionnalités avancées pour petites structures ambitieuses',
|
||||
'PETITE', 100, 10240, 10000, 96000, 3),
|
||||
|
||||
-- ── MOYENNE (101–500 membres) ────────────────────────────────────────────────
|
||||
(gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(),
|
||||
'BASIC', 'Basic — Moyennes structures',
|
||||
'Gestion complète pour moyennes structures',
|
||||
'MOYENNE', 500, 2048, 8000, 76800, 4),
|
||||
|
||||
(gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(),
|
||||
'STANDARD', 'Standard — Moyennes structures',
|
||||
'Fonctionnalités étendues pour organisations en croissance',
|
||||
'MOYENNE', 500, 10240, 15000, 144000, 5),
|
||||
|
||||
(gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(),
|
||||
'PREMIUM', 'Premium — Moyennes structures',
|
||||
'Suite complète pour organisations actives',
|
||||
'MOYENNE', 500, 20480, 25000, 240000, 6),
|
||||
|
||||
-- ── GRANDE (501–2 000 membres) ────────────────────────────────────────────────
|
||||
(gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(),
|
||||
'BASIC', 'Basic — Grandes structures',
|
||||
'Solution économique pour grandes organisations',
|
||||
'GRANDE', 2000, 5120, 20000, 192000, 7),
|
||||
|
||||
(gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(),
|
||||
'STANDARD', 'Standard — Grandes structures',
|
||||
'Gestion avancée pour grandes structures',
|
||||
'GRANDE', 2000, 20480, 35000, 336000, 8),
|
||||
|
||||
(gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(),
|
||||
'PREMIUM', 'Premium — Grandes structures',
|
||||
'Fonctionnalités entreprise pour grandes organisations',
|
||||
'GRANDE', 2000, 51200, 60000, 576000, 9),
|
||||
|
||||
-- ── TRES_GRANDE (2 000+ membres) ─────────────────────────────────────────────
|
||||
(gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(),
|
||||
'BASIC', 'Basic — Très grandes structures',
|
||||
'Gestion de base pour très grandes organisations',
|
||||
'TRES_GRANDE', NULL, 10240, 50000, 480000, 10),
|
||||
|
||||
(gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(),
|
||||
'STANDARD', 'Standard — Très grandes structures',
|
||||
'Suite complète pour très grandes organisations',
|
||||
'TRES_GRANDE', NULL, 51200, 80000, 768000, 11),
|
||||
|
||||
(gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(),
|
||||
'PREMIUM', 'Premium — Très grandes structures',
|
||||
'Solution enterprise multi-sites avec analytics avancé',
|
||||
'TRES_GRANDE', NULL, 102400, 120000, 1152000, 12);
|
||||
|
||||
|
||||
-- Appliquer NOT NULL sur plage après le seed
|
||||
DO $$
|
||||
BEGIN
|
||||
-- PostgreSQL ne supporte pas ADD COLUMN ... NOT NULL ... en une seule commande
|
||||
-- quand la table est déjà peuplée. On pose la contrainte séparément.
|
||||
ALTER TABLE formules_abonnement ALTER COLUMN plage SET NOT NULL;
|
||||
EXCEPTION
|
||||
WHEN others THEN
|
||||
RAISE NOTICE 'Contrainte NOT NULL sur plage déjà présente ou erreur: %', SQLERRM;
|
||||
END $$;
|
||||
|
||||
-- Créer l'index unique sur la combinaison code+plage
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_formule_code_plage
|
||||
ON formules_abonnement (code, plage);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_formule_plage
|
||||
ON formules_abonnement (plage);
|
||||
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 4. Colonnes workflow de validation dans souscriptions_organisation
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
ALTER TABLE souscriptions_organisation
|
||||
ADD COLUMN IF NOT EXISTS plage VARCHAR(20);
|
||||
|
||||
ALTER TABLE souscriptions_organisation
|
||||
ADD COLUMN IF NOT EXISTS type_organisation VARCHAR(30);
|
||||
|
||||
ALTER TABLE souscriptions_organisation
|
||||
ADD COLUMN IF NOT EXISTS coefficient_applique NUMERIC(4, 2);
|
||||
|
||||
ALTER TABLE souscriptions_organisation
|
||||
ADD COLUMN IF NOT EXISTS statut_validation VARCHAR(40);
|
||||
|
||||
ALTER TABLE souscriptions_organisation
|
||||
ADD COLUMN IF NOT EXISTS montant_total NUMERIC(12, 2);
|
||||
|
||||
ALTER TABLE souscriptions_organisation
|
||||
ADD COLUMN IF NOT EXISTS date_validation DATE;
|
||||
|
||||
ALTER TABLE souscriptions_organisation
|
||||
ADD COLUMN IF NOT EXISTS validated_by_id UUID;
|
||||
|
||||
ALTER TABLE souscriptions_organisation
|
||||
ADD COLUMN IF NOT EXISTS commentaire_rejet VARCHAR(500);
|
||||
|
||||
ALTER TABLE souscriptions_organisation
|
||||
ADD COLUMN IF NOT EXISTS mot_de_passe_temporaire VARCHAR(100);
|
||||
|
||||
-- Backfill des lignes existantes avant d'ajouter les contraintes NOT NULL
|
||||
UPDATE souscriptions_organisation
|
||||
SET plage = 'PETITE',
|
||||
type_organisation = 'ASSOCIATION',
|
||||
coefficient_applique = 1.0,
|
||||
statut_validation = 'VALIDEE'
|
||||
WHERE plage IS NULL;
|
||||
|
||||
-- Appliquer NOT NULL sur statut_validation uniquement (les autres sont optionnels)
|
||||
DO $$
|
||||
BEGIN
|
||||
ALTER TABLE souscriptions_organisation
|
||||
ALTER COLUMN statut_validation SET NOT NULL;
|
||||
EXCEPTION
|
||||
WHEN others THEN
|
||||
RAISE NOTICE 'Contrainte NOT NULL sur statut_validation: %', SQLERRM;
|
||||
END $$;
|
||||
|
||||
-- Valeur par défaut pour les nouvelles lignes
|
||||
ALTER TABLE souscriptions_organisation
|
||||
ALTER COLUMN statut_validation SET DEFAULT 'EN_ATTENTE_PAIEMENT';
|
||||
|
||||
-- Index pour les requêtes SuperAdmin sur le workflow
|
||||
CREATE INDEX IF NOT EXISTS idx_souscription_statut_validation
|
||||
ON souscriptions_organisation (statut_validation);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_souscription_plage
|
||||
ON souscriptions_organisation (plage);
|
||||
|
||||
-- Mise à jour du statut global pour les souscriptions existantes actives
|
||||
-- (l'ancienne valeur ACTIVE reste cohérente avec VALIDEE côté validation)
|
||||
UPDATE souscriptions_organisation
|
||||
SET statut = 'ACTIVE'
|
||||
WHERE statut IS NULL OR statut NOT IN ('ACTIVE', 'EXPIREE', 'SUSPENDUE', 'RESILIEE', 'EN_ATTENTE');
|
||||
|
||||
|
||||
-- =============================================================================
|
||||
-- Fin de V11
|
||||
-- =============================================================================
|
||||
@@ -0,0 +1,36 @@
|
||||
-- V12 : Alignement colonnes utilisateurs avec l'entité Membre (refactorisée depuis V1)
|
||||
-- V1 a créé les colonnes avec d'anciens noms ; ce script corrige l'écart.
|
||||
--
|
||||
-- 1. Renommage statut → statut_compte (Membre.java @Column(name="statut_compte"))
|
||||
-- 2. Ajout telephone_wave VARCHAR(20) (Membre.java @Column(name="telephone_wave"))
|
||||
-- Format E.164 international (+[1-9][0-9]{6,14}) — Wave CI/SN/ML/BF/CM...
|
||||
|
||||
-- 1. Renommer statut → statut_compte si pas encore fait
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'utilisateurs' AND column_name = 'statut'
|
||||
) AND NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'utilisateurs' AND column_name = 'statut_compte'
|
||||
) THEN
|
||||
ALTER TABLE utilisateurs RENAME COLUMN statut TO statut_compte;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- 2. Ajouter telephone_wave si la colonne n'existe pas encore
|
||||
ALTER TABLE utilisateurs ADD COLUMN IF NOT EXISTS telephone_wave VARCHAR(20);
|
||||
|
||||
-- Si la colonne existe déjà avec une taille inférieure, l'élargir
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'utilisateurs'
|
||||
AND column_name = 'telephone_wave'
|
||||
AND character_maximum_length < 20
|
||||
) THEN
|
||||
ALTER TABLE utilisateurs ALTER COLUMN telephone_wave TYPE VARCHAR(20);
|
||||
END IF;
|
||||
END $$;
|
||||
56
src/main/resources/db/migration/V13__Seed_Standard_Roles.sql
Normal file
56
src/main/resources/db/migration/V13__Seed_Standard_Roles.sql
Normal file
@@ -0,0 +1,56 @@
|
||||
-- ============================================================================
|
||||
-- V13 — Initialisation des rôles standard UnionFlow
|
||||
-- Codes en MAJUSCULES correspondant aux cases du switch mobile (via toLowerCase())
|
||||
-- ============================================================================
|
||||
|
||||
-- Ajouter les colonnes manquantes à roles (entité Role refactorisée depuis V1)
|
||||
-- V1 a créé : nom, description, niveau_acces, est_systeme
|
||||
-- Entité attend : code, libelle, description, niveau_hierarchique, type_role, organisation_id
|
||||
|
||||
ALTER TABLE roles ADD COLUMN IF NOT EXISTS code VARCHAR(50);
|
||||
ALTER TABLE roles ADD COLUMN IF NOT EXISTS libelle VARCHAR(100);
|
||||
ALTER TABLE roles ADD COLUMN IF NOT EXISTS niveau_hierarchique INTEGER DEFAULT 100;
|
||||
ALTER TABLE roles ADD COLUMN IF NOT EXISTS type_role VARCHAR(50);
|
||||
ALTER TABLE roles ADD COLUMN IF NOT EXISTS organisation_id UUID;
|
||||
|
||||
-- Contrainte UNIQUE sur code (si pas déjà présente)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint
|
||||
WHERE conname = 'roles_code_key' AND conrelid = 'roles'::regclass
|
||||
) THEN
|
||||
ALTER TABLE roles ADD CONSTRAINT roles_code_key UNIQUE (code);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_role_code ON roles (code);
|
||||
CREATE INDEX IF NOT EXISTS idx_role_niveau ON roles (niveau_hierarchique);
|
||||
|
||||
-- La colonne `nom` de V1 n'est pas dans l'entité Role — rendre nullable
|
||||
ALTER TABLE roles ALTER COLUMN nom DROP NOT NULL;
|
||||
|
||||
INSERT INTO roles (id, nom, code, libelle, description, niveau_hierarchique, type_role, actif, date_creation, date_modification, cree_par, modifie_par, version)
|
||||
SELECT
|
||||
gen_random_uuid(),
|
||||
v.libelle,
|
||||
v.code,
|
||||
v.libelle,
|
||||
v.description,
|
||||
v.niveau_hierarchique,
|
||||
'SYSTEME',
|
||||
true,
|
||||
NOW(),
|
||||
NOW(),
|
||||
'system',
|
||||
'system',
|
||||
0
|
||||
FROM (VALUES
|
||||
('SUPERADMIN', 'Super Administrateur', 'Accès total à la plateforme', 10),
|
||||
('ORGADMIN', 'Administrateur Organisation', 'Administrateur d''une organisation', 20),
|
||||
('MODERATOR', 'Modérateur', 'Modérateur des contenus et activités', 30),
|
||||
('ACTIVEMEMBER', 'Membre Actif', 'Membre actif à jour de ses cotisations', 40),
|
||||
('SIMPLEMEMBER', 'Membre Simple', 'Membre standard sans droits étendus', 50),
|
||||
('VISITOR', 'Visiteur', 'Accès en lecture seule', 60)
|
||||
) AS v(code, libelle, description, niveau_hierarchique)
|
||||
WHERE NOT EXISTS (SELECT 1 FROM roles WHERE roles.code = v.code);
|
||||
@@ -0,0 +1,10 @@
|
||||
-- ============================================================================
|
||||
-- V14 — Ajout du flag premiere_connexion sur les utilisateurs
|
||||
-- Permet de forcer le changement de mot de passe au premier login.
|
||||
-- ============================================================================
|
||||
|
||||
ALTER TABLE utilisateurs
|
||||
ADD COLUMN IF NOT EXISTS premiere_connexion BOOLEAN NOT NULL DEFAULT TRUE;
|
||||
|
||||
-- Les comptes existants (SuperAdmin + tout compte déjà actif) ont déjà leur mot de passe défini.
|
||||
UPDATE utilisateurs SET premiere_connexion = FALSE WHERE statut_compte = 'ACTIF';
|
||||
@@ -0,0 +1,3 @@
|
||||
-- V15 : Stockage de l'URL Wave Checkout pour récupération en cas d'interruption
|
||||
ALTER TABLE souscriptions_organisation
|
||||
ADD COLUMN IF NOT EXISTS wave_checkout_url VARCHAR(1024);
|
||||
@@ -0,0 +1,59 @@
|
||||
-- V16: Tables for backup tracking (BackupRecord + BackupConfig entities)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS backup_records (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
date_creation TIMESTAMP,
|
||||
date_modification TIMESTAMP,
|
||||
cree_par VARCHAR(255),
|
||||
modifie_par VARCHAR(255),
|
||||
version BIGINT DEFAULT 0,
|
||||
actif BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
name VARCHAR(200) NOT NULL,
|
||||
description VARCHAR(500),
|
||||
type VARCHAR(50) NOT NULL,
|
||||
size_bytes BIGINT,
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'IN_PROGRESS',
|
||||
completed_at TIMESTAMP,
|
||||
created_by VARCHAR(200),
|
||||
includes_database BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
includes_files BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
includes_configuration BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
file_path VARCHAR(500),
|
||||
error_message TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_backup_records_status ON backup_records (status);
|
||||
CREATE INDEX IF NOT EXISTS idx_backup_records_type ON backup_records (type);
|
||||
CREATE INDEX IF NOT EXISTS idx_backup_records_created_at ON backup_records (date_creation DESC);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS backup_config (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
date_creation TIMESTAMP,
|
||||
date_modification TIMESTAMP,
|
||||
cree_par VARCHAR(255),
|
||||
modifie_par VARCHAR(255),
|
||||
version BIGINT DEFAULT 0,
|
||||
actif BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
auto_backup_enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
frequency VARCHAR(20) NOT NULL DEFAULT 'DAILY',
|
||||
retention_days INTEGER NOT NULL DEFAULT 30,
|
||||
backup_time VARCHAR(10) NOT NULL DEFAULT '02:00',
|
||||
include_database BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
include_files BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
include_configuration BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
backup_directory VARCHAR(500)
|
||||
);
|
||||
|
||||
-- Seed default config row (only if table is empty)
|
||||
INSERT INTO backup_config (
|
||||
id, date_creation, date_modification, version, actif,
|
||||
auto_backup_enabled, frequency, retention_days, backup_time,
|
||||
include_database, include_files, include_configuration
|
||||
)
|
||||
SELECT
|
||||
gen_random_uuid(), NOW(), NOW(), 0, TRUE,
|
||||
TRUE, 'DAILY', 30, '02:00',
|
||||
TRUE, FALSE, TRUE
|
||||
WHERE NOT EXISTS (SELECT 1 FROM backup_config);
|
||||
@@ -0,0 +1,5 @@
|
||||
-- ============================================================================
|
||||
-- V8 : Ajout du champ notes (biographie) dans la table utilisateurs
|
||||
-- ============================================================================
|
||||
|
||||
ALTER TABLE utilisateurs ADD COLUMN IF NOT EXISTS notes VARCHAR(1000);
|
||||
@@ -0,0 +1,9 @@
|
||||
-- V9__Fix_TypesReference_Categorie_Nullable.sql
|
||||
-- The entity TypeReference was redesigned to use 'domaine' instead of 'categorie'.
|
||||
-- V1 created types_reference with categorie VARCHAR(50) NOT NULL, but the entity
|
||||
-- no longer includes this column, causing INSERT to fail with NOT NULL violation.
|
||||
-- Also drop the old unique constraint on (categorie, code) which references the old design.
|
||||
|
||||
ALTER TABLE types_reference ALTER COLUMN categorie DROP NOT NULL;
|
||||
|
||||
ALTER TABLE types_reference DROP CONSTRAINT IF EXISTS uk_type_ref;
|
||||
@@ -20,15 +20,24 @@
|
||||
"quickLoginCheckMilliSeconds": 1000,
|
||||
"maxDeltaTimeSeconds": 43200,
|
||||
"failureFactor": 30,
|
||||
"defaultRoles": ["offline_access", "uma_authorization", "default-roles-unionflow"],
|
||||
"requiredCredentials": ["password"],
|
||||
"defaultRoles": [
|
||||
"offline_access",
|
||||
"uma_authorization",
|
||||
"default-roles-unionflow"
|
||||
],
|
||||
"requiredCredentials": [
|
||||
"password"
|
||||
],
|
||||
"otpPolicyType": "totp",
|
||||
"otpPolicyAlgorithm": "HmacSHA1",
|
||||
"otpPolicyInitialCounter": 0,
|
||||
"otpPolicyDigits": 6,
|
||||
"otpPolicyLookAheadWindow": 1,
|
||||
"otpPolicyPeriod": 30,
|
||||
"supportedLocales": ["fr", "en"],
|
||||
"supportedLocales": [
|
||||
"fr",
|
||||
"en"
|
||||
],
|
||||
"defaultLocale": "fr",
|
||||
"internationalizationEnabled": true,
|
||||
"clients": [
|
||||
@@ -38,9 +47,14 @@
|
||||
"description": "Client pour l'API serveur UnionFlow",
|
||||
"enabled": true,
|
||||
"clientAuthenticatorType": "client-secret",
|
||||
"secret": "dev-secret",
|
||||
"redirectUris": ["http://localhost:8080/*"],
|
||||
"webOrigins": ["http://localhost:8080", "http://localhost:3000"],
|
||||
"secret": "unionflow-secret-2025",
|
||||
"redirectUris": [
|
||||
"http://localhost:8080/*"
|
||||
],
|
||||
"webOrigins": [
|
||||
"http://localhost:8080",
|
||||
"http://localhost:3000"
|
||||
],
|
||||
"protocol": "openid-connect",
|
||||
"attributes": {
|
||||
"saml.assertion.signature": "false",
|
||||
@@ -118,8 +132,21 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"defaultClientScopes": ["web-origins", "role_list", "profile", "roles", "email"],
|
||||
"optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
|
||||
"defaultClientScopes": [
|
||||
"web-origins",
|
||||
"role_list",
|
||||
"profile",
|
||||
"roles",
|
||||
"email"
|
||||
],
|
||||
"optionalClientScopes": [
|
||||
"address",
|
||||
"phone",
|
||||
"offline_access",
|
||||
"microprofile-jwt"
|
||||
],
|
||||
"serviceAccountsEnabled": true,
|
||||
"directAccessGrantsEnabled": true
|
||||
},
|
||||
{
|
||||
"clientId": "unionflow-mobile",
|
||||
@@ -127,15 +154,31 @@
|
||||
"description": "Client pour l'application mobile UnionFlow",
|
||||
"enabled": true,
|
||||
"publicClient": true,
|
||||
"redirectUris": ["unionflow://callback", "http://localhost:3000/callback"],
|
||||
"webOrigins": ["*"],
|
||||
"redirectUris": [
|
||||
"unionflow://callback",
|
||||
"http://localhost:3000/callback"
|
||||
],
|
||||
"webOrigins": [
|
||||
"*"
|
||||
],
|
||||
"protocol": "openid-connect",
|
||||
"attributes": {
|
||||
"pkce.code.challenge.method": "S256"
|
||||
},
|
||||
"fullScopeAllowed": true,
|
||||
"defaultClientScopes": ["web-origins", "role_list", "profile", "roles", "email"],
|
||||
"optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
|
||||
"defaultClientScopes": [
|
||||
"web-origins",
|
||||
"role_list",
|
||||
"profile",
|
||||
"roles",
|
||||
"email"
|
||||
],
|
||||
"optionalClientScopes": [
|
||||
"address",
|
||||
"phone",
|
||||
"offline_access",
|
||||
"microprofile-jwt"
|
||||
]
|
||||
}
|
||||
],
|
||||
"roles": {
|
||||
@@ -206,7 +249,10 @@
|
||||
"temporary": false
|
||||
}
|
||||
],
|
||||
"realmRoles": ["ADMIN", "PRESIDENT"],
|
||||
"realmRoles": [
|
||||
"ADMIN",
|
||||
"PRESIDENT"
|
||||
],
|
||||
"clientRoles": {}
|
||||
},
|
||||
{
|
||||
@@ -223,7 +269,10 @@
|
||||
"temporary": false
|
||||
}
|
||||
],
|
||||
"realmRoles": ["PRESIDENT", "MEMBRE"],
|
||||
"realmRoles": [
|
||||
"PRESIDENT",
|
||||
"MEMBRE"
|
||||
],
|
||||
"clientRoles": {}
|
||||
},
|
||||
{
|
||||
@@ -240,7 +289,11 @@
|
||||
"temporary": false
|
||||
}
|
||||
],
|
||||
"realmRoles": ["SECRETAIRE", "GESTIONNAIRE_MEMBRE", "MEMBRE"],
|
||||
"realmRoles": [
|
||||
"SECRETAIRE",
|
||||
"GESTIONNAIRE_MEMBRE",
|
||||
"MEMBRE"
|
||||
],
|
||||
"clientRoles": {}
|
||||
},
|
||||
{
|
||||
@@ -257,7 +310,10 @@
|
||||
"temporary": false
|
||||
}
|
||||
],
|
||||
"realmRoles": ["TRESORIER", "MEMBRE"],
|
||||
"realmRoles": [
|
||||
"TRESORIER",
|
||||
"MEMBRE"
|
||||
],
|
||||
"clientRoles": {}
|
||||
},
|
||||
{
|
||||
@@ -274,7 +330,9 @@
|
||||
"temporary": false
|
||||
}
|
||||
],
|
||||
"realmRoles": ["MEMBRE"],
|
||||
"realmRoles": [
|
||||
"MEMBRE"
|
||||
],
|
||||
"clientRoles": {}
|
||||
}
|
||||
],
|
||||
@@ -282,26 +340,37 @@
|
||||
{
|
||||
"name": "Administration",
|
||||
"path": "/Administration",
|
||||
"realmRoles": ["ADMIN"],
|
||||
"realmRoles": [
|
||||
"ADMIN"
|
||||
],
|
||||
"subGroups": []
|
||||
},
|
||||
{
|
||||
"name": "Bureau",
|
||||
"path": "/Bureau",
|
||||
"realmRoles": ["PRESIDENT", "SECRETAIRE", "TRESORIER"],
|
||||
"realmRoles": [
|
||||
"PRESIDENT",
|
||||
"SECRETAIRE",
|
||||
"TRESORIER"
|
||||
],
|
||||
"subGroups": []
|
||||
},
|
||||
{
|
||||
"name": "Gestionnaires",
|
||||
"path": "/Gestionnaires",
|
||||
"realmRoles": ["GESTIONNAIRE_MEMBRE", "ORGANISATEUR_EVENEMENT"],
|
||||
"realmRoles": [
|
||||
"GESTIONNAIRE_MEMBRE",
|
||||
"ORGANISATEUR_EVENEMENT"
|
||||
],
|
||||
"subGroups": []
|
||||
},
|
||||
{
|
||||
"name": "Membres",
|
||||
"path": "/Membres",
|
||||
"realmRoles": ["MEMBRE"],
|
||||
"realmRoles": [
|
||||
"MEMBRE"
|
||||
],
|
||||
"subGroups": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user