402 lines
No EOL
14 KiB
PL/PgSQL
402 lines
No EOL
14 KiB
PL/PgSQL
-- ================================================================
|
|
-- MIGRATION DE NETTOYAGE ET MISE À JOUR POUR PRODUCTION
|
|
-- Version: 0.2.0 - Compatible avec structure existante
|
|
-- ================================================================
|
|
-- ⚠️ ATTENTION: Cette migration est partiellement destructive
|
|
-- 🔒 Assurez-vous d'avoir une sauvegarde complète avant exécution
|
|
--
|
|
-- Utilisation:
|
|
-- psql -h 10.5.191.47 -U veza -d veza_db -f migrations/999_cleanup_production_ready_fixed.sql
|
|
-- ================================================================
|
|
|
|
-- Début de la migration de production
|
|
|
|
-- Vérifier que nous sommes dans la bonne base
|
|
SELECT current_database() as current_db, current_user as current_user_name;
|
|
|
|
-- Créer l'extension UUID si pas présente
|
|
-- Extension uuid-ossp non nécessaire, gen_random_uuid() est utilisé
|
|
|
|
-- ================================================================
|
|
-- ÉTAPE 1: SAUVEGARDE DES DONNÉES EXISTANTES
|
|
-- ================================================================
|
|
|
|
-- Sauvegarde des données existantes
|
|
|
|
-- Sauvegarder les utilisateurs existants
|
|
DO $$
|
|
BEGIN
|
|
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'users') THEN
|
|
CREATE TEMP TABLE temp_old_users AS
|
|
SELECT id, username, email, created_at, role
|
|
FROM users;
|
|
|
|
RAISE NOTICE 'Sauvegarde de % utilisateurs', (SELECT COUNT(*) FROM temp_old_users);
|
|
ELSE
|
|
RAISE NOTICE 'Table users non trouvée, création nécessaire';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- Sauvegarder les messages existants
|
|
DO $$
|
|
BEGIN
|
|
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'messages') THEN
|
|
CREATE TEMP TABLE temp_old_messages AS
|
|
SELECT id, sender_id, content, created_at, message_type
|
|
FROM messages;
|
|
|
|
RAISE NOTICE 'Sauvegarde de % messages', (SELECT COUNT(*) FROM temp_old_messages);
|
|
ELSE
|
|
RAISE NOTICE 'Table messages non trouvée, création nécessaire';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ================================================================
|
|
-- ÉTAPE 2: SUPPRESSION SÉCURISÉE DES TABLES REDONDANTES
|
|
-- ================================================================
|
|
|
|
-- Suppression des tables redondantes
|
|
|
|
-- Supprimer uniquement les tables qui existent et sont redondantes
|
|
DROP TABLE IF EXISTS users_enhanced CASCADE;
|
|
DROP TABLE IF EXISTS users_backup CASCADE;
|
|
DROP TABLE IF EXISTS rooms_enhanced CASCADE;
|
|
DROP TABLE IF EXISTS messages_enhanced CASCADE;
|
|
DROP TABLE IF EXISTS message_mentions_enhanced CASCADE;
|
|
DROP TABLE IF EXISTS message_mentions_secure CASCADE;
|
|
DROP TABLE IF EXISTS message_reactions_enhanced CASCADE;
|
|
DROP TABLE IF EXISTS room_members_enhanced CASCADE;
|
|
DROP TABLE IF EXISTS user_sessions_enhanced CASCADE;
|
|
DROP TABLE IF EXISTS user_sessions_secure CASCADE;
|
|
DROP TABLE IF EXISTS user_blocks_enhanced CASCADE;
|
|
DROP TABLE IF EXISTS user_blocks_secure CASCADE;
|
|
DROP TABLE IF EXISTS security_events_enhanced CASCADE;
|
|
DROP TABLE IF EXISTS security_events_secure CASCADE;
|
|
|
|
-- Supprimer les tables métier obsolètes (si elles existent)
|
|
DROP TABLE IF EXISTS listings CASCADE;
|
|
DROP TABLE IF EXISTS categories CASCADE;
|
|
DROP TABLE IF EXISTS user_products CASCADE;
|
|
DROP TABLE IF EXISTS internal_documents CASCADE;
|
|
DROP TABLE IF EXISTS shared_ressources CASCADE;
|
|
DROP TABLE IF EXISTS shared_ressource_tags CASCADE;
|
|
DROP TABLE IF EXISTS ressource_tags CASCADE;
|
|
DROP TABLE IF EXISTS tracks CASCADE;
|
|
|
|
-- ================================================================
|
|
-- ÉTAPE 3: CRÉATION DES TYPES ENUMS NÉCESSAIRES
|
|
-- ================================================================
|
|
|
|
-- Création des types énumérés
|
|
|
|
-- Type user_role supprimé car non nécessaire pour la migration de base
|
|
|
|
-- Type message_status supprimé car non nécessaire pour la migration de base
|
|
|
|
-- Type conversation_type supprimé car non nécessaire pour la migration de base
|
|
|
|
-- ================================================================
|
|
-- ÉTAPE 4: MISE À JOUR DE LA TABLE USERS
|
|
-- ================================================================
|
|
|
|
-- Mise à jour de la table users
|
|
|
|
-- Colonnes uuid supprimées car les tables utilisent déjà id avec UUID
|
|
|
|
-- Ajouter les nouvelles colonnes de sécurité
|
|
DO $$
|
|
BEGIN
|
|
-- Colonnes de sécurité 2FA supprimées car non nécessaires pour la migration de base
|
|
|
|
-- Colonnes de profil (display_name et avatar_url existent déjà dans la migration de base)
|
|
-- Colonne bio supprimée car non nécessaire pour la migration de base
|
|
|
|
-- Colonnes de métadonnées
|
|
-- Colonne last_login supprimée car non nécessaire pour la migration de base
|
|
|
|
-- Colonne last_activity supprimée car non nécessaire pour la migration de base
|
|
|
|
-- updated_at existe déjà dans la migration de base, pas besoin de l'ajouter
|
|
|
|
-- Colonnes de permissions (is_active existe déjà dans la migration de base)
|
|
-- Colonne is_verified supprimée car non nécessaire pour la migration de base
|
|
END $$;
|
|
|
|
-- Mise à jour du type de rôle
|
|
DO $$
|
|
BEGIN
|
|
-- Vérifier si la colonne role existe et la convertir
|
|
IF EXISTS (SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'users' AND column_name = 'role') THEN
|
|
-- Sauvegarder les valeurs existantes avant conversion
|
|
UPDATE users SET role = 'user' WHERE role IS NULL OR role = '';
|
|
|
|
-- Convertir vers le nouveau type (si ce n'est pas déjà fait)
|
|
BEGIN
|
|
ALTER TABLE users ALTER COLUMN role TYPE user_role USING role::user_role;
|
|
RAISE NOTICE 'Colonne role convertie vers user_role';
|
|
EXCEPTION
|
|
WHEN OTHERS THEN
|
|
RAISE NOTICE 'Colonne role déjà au bon type ou erreur: %', SQLERRM;
|
|
END;
|
|
ELSE
|
|
ALTER TABLE users ADD COLUMN role user_role DEFAULT 'user' NOT NULL;
|
|
RAISE NOTICE 'Colonne role ajoutée avec type user_role';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ================================================================
|
|
-- ÉTAPE 5: CRÉATION DE LA TABLE CONVERSATIONS
|
|
-- ================================================================
|
|
|
|
-- Création de la table conversations
|
|
|
|
-- Table conversations existe déjà dans la migration 001, pas besoin de la redéfinir
|
|
|
|
-- ================================================================
|
|
-- ÉTAPE 6: MISE À JOUR DE LA TABLE MESSAGES
|
|
-- ================================================================
|
|
|
|
-- Mise à jour de la table messages
|
|
|
|
-- Colonnes uuid supprimées car les tables utilisent déjà id avec UUID
|
|
|
|
-- Colonnes déjà cohérentes avec la migration de base
|
|
|
|
-- Colonne conversation_id existe déjà dans la migration de base
|
|
|
|
-- Colonnes avancées existent déjà dans la migration de base
|
|
|
|
-- ================================================================
|
|
-- ÉTAPE 7: CRÉATION DES TABLES COMPLÉMENTAIRES
|
|
-- ================================================================
|
|
|
|
-- Création des tables complémentaires
|
|
|
|
-- Table conversation_members existe déjà dans la migration 001, pas besoin de la redéfinir
|
|
|
|
-- Table message_reactions existe déjà dans la migration 002, pas besoin de la redéfinir
|
|
|
|
-- Table pour les mentions
|
|
CREATE TABLE IF NOT EXISTS message_mentions (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
message_id UUID NOT NULL,
|
|
mentioned_user_id UUID NOT NULL,
|
|
is_read BOOLEAN DEFAULT FALSE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
|
|
|
|
CONSTRAINT message_mentions_message_fk FOREIGN KEY (message_id) REFERENCES messages(id) ON DELETE CASCADE,
|
|
CONSTRAINT message_mentions_user_fk FOREIGN KEY (mentioned_user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
CONSTRAINT message_mentions_unique UNIQUE (message_id, mentioned_user_id)
|
|
);
|
|
|
|
-- Table pour l'historique des messages
|
|
CREATE TABLE IF NOT EXISTS message_history (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
message_id UUID NOT NULL,
|
|
old_content TEXT NOT NULL,
|
|
edited_by UUID NOT NULL,
|
|
edited_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
|
|
|
|
CONSTRAINT message_history_message_fk FOREIGN KEY (message_id) REFERENCES messages(id) ON DELETE CASCADE,
|
|
CONSTRAINT message_history_user_fk FOREIGN KEY (edited_by) REFERENCES users(id) ON DELETE CASCADE
|
|
);
|
|
|
|
-- Table pour les sessions utilisateur
|
|
CREATE TABLE IF NOT EXISTS user_sessions (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
user_id UUID NOT NULL,
|
|
session_token VARCHAR(255) UNIQUE NOT NULL,
|
|
refresh_token VARCHAR(255) UNIQUE,
|
|
device_info TEXT,
|
|
ip_address INET,
|
|
user_agent TEXT,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
|
|
last_activity TIMESTAMPTZ DEFAULT NOW() NOT NULL,
|
|
|
|
CONSTRAINT user_sessions_user_fk FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
);
|
|
|
|
-- ================================================================
|
|
-- ÉTAPE 8: CRÉATION DES INDEX DE PERFORMANCE
|
|
-- ================================================================
|
|
|
|
-- Création des index de performance
|
|
|
|
-- Index pour users
|
|
CREATE INDEX IF NOT EXISTS idx_users_username_active
|
|
ON users(username) WHERE is_active = TRUE;
|
|
|
|
-- Index last_login supprimé car la colonne n'existe pas dans la migration de base
|
|
|
|
-- Index email_verified supprimé car la colonne is_verified n'existe pas dans la migration de base
|
|
|
|
-- Index last_activity supprimé car la colonne n'existe pas dans la migration de base
|
|
|
|
-- Index pour conversations
|
|
CREATE INDEX IF NOT EXISTS idx_conversations_type_public
|
|
ON conversations(conversation_type) WHERE is_private = FALSE;
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_conversations_owner_active
|
|
ON conversations(created_by);
|
|
|
|
-- Index pour messages (performance critique)
|
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_time
|
|
ON messages(conversation_id, created_at DESC);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_messages_sender_time
|
|
ON messages(sender_id, created_at DESC);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_messages_threads
|
|
ON messages(parent_message_id, created_at) WHERE parent_message_id IS NOT NULL;
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_messages_pinned
|
|
ON messages(conversation_id) WHERE is_pinned = TRUE;
|
|
|
|
-- Index pour réactions (utilise reaction_type au lieu d'emoji)
|
|
CREATE INDEX IF NOT EXISTS idx_reactions_message
|
|
ON message_reactions(message_id, reaction_type);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_reactions_user
|
|
ON message_reactions(user_id, created_at DESC);
|
|
|
|
-- Index pour mentions
|
|
CREATE INDEX IF NOT EXISTS idx_mentions_user_unread
|
|
ON message_mentions(mentioned_user_id) WHERE is_read = FALSE;
|
|
|
|
-- Index pour sessions
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_user_active
|
|
ON user_sessions(user_id) WHERE is_active = TRUE;
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_token
|
|
ON user_sessions(session_token);
|
|
|
|
-- ================================================================
|
|
-- ÉTAPE 9: CRÉATION DES FONCTIONS UTILITAIRES
|
|
-- ================================================================
|
|
|
|
-- Création des fonctions utilitaires
|
|
|
|
-- Fonction pour obtenir l'ID utilisateur courant (à implémenter côté app)
|
|
CREATE OR REPLACE FUNCTION current_user_id()
|
|
RETURNS BIGINT AS $$
|
|
BEGIN
|
|
-- Cette fonction doit être implémentée côté application
|
|
-- Pour l'instant, elle retourne NULL
|
|
RETURN NULL;
|
|
END;
|
|
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
|
|
-- Fonction pour nettoyer les sessions expirées
|
|
CREATE OR REPLACE FUNCTION cleanup_expired_sessions()
|
|
RETURNS INTEGER AS $$
|
|
DECLARE
|
|
deleted_count INTEGER;
|
|
BEGIN
|
|
DELETE FROM user_sessions
|
|
WHERE expires_at < NOW() OR (last_activity < NOW() - INTERVAL '30 days');
|
|
|
|
GET DIAGNOSTICS deleted_count = ROW_COUNT;
|
|
RETURN deleted_count;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Fonction trigger pour mettre à jour updated_at
|
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- ================================================================
|
|
-- ÉTAPE 10: CRÉATION DES TRIGGERS
|
|
-- ================================================================
|
|
|
|
-- Création des triggers
|
|
|
|
-- Triggers pour updated_at
|
|
DROP TRIGGER IF EXISTS update_users_updated_at ON users;
|
|
CREATE TRIGGER update_users_updated_at
|
|
BEFORE UPDATE ON users
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
DROP TRIGGER IF EXISTS update_conversations_updated_at ON conversations;
|
|
CREATE TRIGGER update_conversations_updated_at
|
|
BEFORE UPDATE ON conversations
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
DROP TRIGGER IF EXISTS update_messages_updated_at ON messages;
|
|
CREATE TRIGGER update_messages_updated_at
|
|
BEFORE UPDATE ON messages
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- ================================================================
|
|
-- ÉTAPE 11: VÉRIFICATION ET NETTOYAGE FINAL
|
|
-- ================================================================
|
|
|
|
-- Vérification finale
|
|
|
|
-- Nettoyer les fonctions obsolètes
|
|
DROP FUNCTION IF EXISTS cleanup_expired_sessions_secure();
|
|
DROP FUNCTION IF EXISTS cleanup_old_audit_logs();
|
|
DROP FUNCTION IF EXISTS cleanup_old_data_secure();
|
|
DROP FUNCTION IF EXISTS handle_mentions_secure();
|
|
|
|
-- Mettre à jour les statistiques
|
|
ANALYZE users;
|
|
ANALYZE conversations;
|
|
ANALYZE messages;
|
|
ANALYZE message_reactions;
|
|
ANALYZE message_mentions;
|
|
ANALYZE user_sessions;
|
|
|
|
-- Validation finale
|
|
DO $$
|
|
DECLARE
|
|
user_count INTEGER;
|
|
message_count INTEGER;
|
|
conversation_count INTEGER;
|
|
BEGIN
|
|
SELECT COUNT(*) INTO user_count FROM users;
|
|
SELECT COUNT(*) INTO message_count FROM messages;
|
|
SELECT COUNT(*) INTO conversation_count FROM conversations;
|
|
|
|
RAISE NOTICE '✅ Migration terminée avec succès:';
|
|
RAISE NOTICE ' - Utilisateurs: %', user_count;
|
|
RAISE NOTICE ' - Messages: %', message_count;
|
|
RAISE NOTICE ' - Conversations: %', conversation_count;
|
|
|
|
-- Warnings si problèmes détectés
|
|
IF user_count = 0 THEN
|
|
RAISE WARNING '⚠️ Aucun utilisateur trouvé après migration';
|
|
END IF;
|
|
|
|
IF message_count > 0 AND conversation_count = 0 THEN
|
|
RAISE WARNING '⚠️ Messages présents mais aucune conversation';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- Nettoyer les tables temporaires
|
|
DROP TABLE IF EXISTS temp_old_users;
|
|
DROP TABLE IF EXISTS temp_old_messages;
|
|
|
|
-- ================================================================
|
|
-- FINALISATION
|
|
-- ================================================================
|
|
|
|
-- Migration de production terminée avec succès
|
|
SELECT
|
|
schemaname,
|
|
tablename,
|
|
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as size
|
|
FROM pg_tables
|
|
WHERE schemaname = 'public'
|
|
AND tablename IN ('users', 'conversations', 'messages', 'message_reactions', 'message_mentions', 'user_sessions')
|
|
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC; |