veza/veza-chat-server/migrations/999_cleanup_production_ready_fixed.sql
2025-12-03 20:33:26 +01:00

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;