270 lines
No EOL
9.6 KiB
PL/PgSQL
270 lines
No EOL
9.6 KiB
PL/PgSQL
-- Migration 003: Schéma amélioré avec sécurité renforcée et séparation DM/salons
|
|
|
|
-- Extension pour UUID
|
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
|
|
|
-- ================================================
|
|
-- UTILISATEURS AVEC SÉCURITÉ RENFORCÉE
|
|
-- ================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS users_enhanced (
|
|
id SERIAL PRIMARY KEY,
|
|
username VARCHAR(50) NOT NULL UNIQUE,
|
|
email VARCHAR(255) NOT NULL UNIQUE,
|
|
password_hash VARCHAR(255) NOT NULL,
|
|
|
|
-- Rôles et permissions
|
|
role VARCHAR(20) NOT NULL DEFAULT 'user' CHECK (role IN ('admin', 'moderator', 'user', 'guest')),
|
|
|
|
-- Statut et sécurité
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
is_banned BOOLEAN NOT NULL DEFAULT false,
|
|
is_verified BOOLEAN NOT NULL DEFAULT false,
|
|
|
|
-- Statut de présence
|
|
status VARCHAR(20) DEFAULT 'offline' CHECK (status IN ('online', 'away', 'busy', 'invisible', 'offline')),
|
|
status_message VARCHAR(100),
|
|
|
|
-- Modération
|
|
reputation_score INTEGER DEFAULT 100,
|
|
|
|
-- Métadonnées
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ================================================
|
|
-- SALONS AVEC GESTION AVANCÉE
|
|
-- ================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS rooms_enhanced (
|
|
id VARCHAR(100) PRIMARY KEY,
|
|
name VARCHAR(100) NOT NULL,
|
|
description TEXT,
|
|
|
|
-- Propriétaire
|
|
owner_id INTEGER NOT NULL REFERENCES users_enhanced(id) ON DELETE CASCADE,
|
|
|
|
-- Configuration
|
|
is_public BOOLEAN NOT NULL DEFAULT true,
|
|
is_archived BOOLEAN DEFAULT false,
|
|
max_members INTEGER DEFAULT 1000,
|
|
|
|
-- Métadonnées
|
|
member_count INTEGER DEFAULT 0,
|
|
message_count INTEGER DEFAULT 0,
|
|
|
|
-- Timestamps
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ================================================
|
|
-- MESSAGES UNIFIÉS
|
|
-- ================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS messages_enhanced (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
|
|
-- Type et contenu
|
|
message_type VARCHAR(20) NOT NULL CHECK (message_type IN ('room_message', 'direct_message', 'system_message')),
|
|
content TEXT NOT NULL CHECK (LENGTH(content) <= 4000),
|
|
|
|
-- Auteur
|
|
author_id INTEGER NOT NULL REFERENCES users_enhanced(id) ON DELETE CASCADE,
|
|
author_username VARCHAR(50) NOT NULL,
|
|
|
|
-- Destination (exclusion mutuelle)
|
|
room_id VARCHAR(100) REFERENCES rooms_enhanced(id) ON DELETE CASCADE,
|
|
recipient_id INTEGER REFERENCES users_enhanced(id) ON DELETE CASCADE,
|
|
recipient_username VARCHAR(50),
|
|
|
|
-- Threading
|
|
parent_message_id BIGINT REFERENCES messages_enhanced(id) ON DELETE SET NULL,
|
|
thread_count INTEGER DEFAULT 0,
|
|
|
|
-- Statut
|
|
status VARCHAR(20) DEFAULT 'sent' CHECK (status IN ('sent', 'delivered', 'read', 'edited', 'deleted')),
|
|
is_pinned BOOLEAN DEFAULT false,
|
|
is_edited BOOLEAN DEFAULT false,
|
|
original_content TEXT,
|
|
|
|
-- Modération
|
|
is_flagged BOOLEAN DEFAULT false,
|
|
moderation_notes TEXT,
|
|
|
|
-- Timestamps
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ,
|
|
|
|
-- Contraintes logiques
|
|
CONSTRAINT message_destination_check CHECK (
|
|
(message_type = 'room_message' AND room_id IS NOT NULL AND recipient_id IS NULL) OR
|
|
(message_type = 'direct_message' AND room_id IS NULL AND recipient_id IS NOT NULL) OR
|
|
(message_type = 'system_message')
|
|
)
|
|
);
|
|
|
|
-- ================================================
|
|
-- RÉACTIONS AUX MESSAGES
|
|
-- ================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS message_reactions_enhanced (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
message_id BIGINT NOT NULL REFERENCES messages_enhanced(id) ON DELETE CASCADE,
|
|
user_id INTEGER NOT NULL REFERENCES users_enhanced(id) ON DELETE CASCADE,
|
|
emoji VARCHAR(100) NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
UNIQUE (message_id, user_id, emoji)
|
|
);
|
|
|
|
-- ================================================
|
|
-- MENTIONS DANS LES MESSAGES
|
|
-- ================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS message_mentions_enhanced (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
message_id BIGINT NOT NULL REFERENCES messages_enhanced(id) ON DELETE CASCADE,
|
|
user_id INTEGER NOT NULL REFERENCES users_enhanced(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
is_read BOOLEAN DEFAULT false,
|
|
|
|
UNIQUE (message_id, user_id)
|
|
);
|
|
|
|
-- ================================================
|
|
-- MEMBRES DES SALONS
|
|
-- ================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS room_members_enhanced (
|
|
room_id VARCHAR(100) NOT NULL REFERENCES rooms_enhanced(id) ON DELETE CASCADE,
|
|
user_id INTEGER NOT NULL REFERENCES users_enhanced(id) ON DELETE CASCADE,
|
|
|
|
role VARCHAR(20) DEFAULT 'member' CHECK (role IN ('owner', 'admin', 'moderator', 'member')),
|
|
joined_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
last_read_message_id BIGINT,
|
|
|
|
PRIMARY KEY (room_id, user_id)
|
|
);
|
|
|
|
-- ================================================
|
|
-- BLOCAGES UTILISATEURS
|
|
-- ================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS user_blocks_enhanced (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
blocker_id INTEGER NOT NULL REFERENCES users_enhanced(id) ON DELETE CASCADE,
|
|
blocked_id INTEGER NOT NULL REFERENCES users_enhanced(id) ON DELETE CASCADE,
|
|
reason VARCHAR(500),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
UNIQUE (blocker_id, blocked_id),
|
|
CONSTRAINT no_self_block CHECK (blocker_id != blocked_id)
|
|
);
|
|
|
|
-- ================================================
|
|
-- SESSIONS SÉCURISÉES
|
|
-- ================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS user_sessions_enhanced (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id INTEGER NOT NULL REFERENCES users_enhanced(id) ON DELETE CASCADE,
|
|
|
|
token_hash VARCHAR(128) NOT NULL UNIQUE,
|
|
ip_address INET,
|
|
user_agent TEXT,
|
|
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
last_activity TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
is_active BOOLEAN DEFAULT true
|
|
);
|
|
|
|
-- ================================================
|
|
-- LOGS DE SÉCURITÉ
|
|
-- ================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS security_events_enhanced (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
|
|
event_type VARCHAR(50) NOT NULL,
|
|
severity VARCHAR(20) DEFAULT 'info' CHECK (severity IN ('debug', 'info', 'warning', 'error', 'critical')),
|
|
|
|
user_id INTEGER REFERENCES users_enhanced(id) ON DELETE SET NULL,
|
|
ip_address INET,
|
|
user_agent TEXT,
|
|
|
|
details JSONB DEFAULT '{}'::jsonb,
|
|
success BOOLEAN,
|
|
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ================================================
|
|
-- INDEX POUR PERFORMANCE
|
|
-- ================================================
|
|
|
|
-- Messages
|
|
CREATE INDEX IF NOT EXISTS idx_messages_room_enhanced ON messages_enhanced (room_id, created_at DESC) WHERE message_type = 'room_message' AND status != 'deleted';
|
|
CREATE INDEX IF NOT EXISTS idx_messages_dm_enhanced ON messages_enhanced (author_id, recipient_id, created_at DESC) WHERE message_type = 'direct_message' AND status != 'deleted';
|
|
CREATE INDEX IF NOT EXISTS idx_messages_dm_reverse_enhanced ON messages_enhanced (recipient_id, author_id, created_at DESC) WHERE message_type = 'direct_message' AND status != 'deleted';
|
|
|
|
-- Réactions
|
|
CREATE INDEX IF NOT EXISTS idx_reactions_message_enhanced ON message_reactions_enhanced (message_id);
|
|
CREATE INDEX IF NOT EXISTS idx_reactions_user_enhanced ON message_reactions_enhanced (user_id);
|
|
|
|
-- Mentions
|
|
CREATE INDEX IF NOT EXISTS idx_mentions_user_enhanced ON message_mentions_enhanced (user_id, is_read);
|
|
|
|
-- Sessions
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_user_enhanced ON user_sessions_enhanced (user_id, is_active);
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_token_enhanced ON user_sessions_enhanced (token_hash);
|
|
|
|
-- ================================================
|
|
-- TRIGGERS
|
|
-- ================================================
|
|
|
|
-- Fonction 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;
|
|
|
|
-- Appliquer aux tables principales
|
|
CREATE TRIGGER update_users_enhanced_updated_at
|
|
BEFORE UPDATE ON users_enhanced
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
CREATE TRIGGER update_rooms_enhanced_updated_at
|
|
BEFORE UPDATE ON rooms_enhanced
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- ================================================
|
|
-- FONCTIONS UTILITAIRES
|
|
-- ================================================
|
|
|
|
-- Fonction pour nettoyer les sessions expirées
|
|
CREATE OR REPLACE FUNCTION cleanup_expired_sessions()
|
|
RETURNS void AS $$
|
|
BEGIN
|
|
DELETE FROM user_sessions_enhanced
|
|
WHERE expires_at < NOW() AND is_active = false;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- ================================================
|
|
-- COMMENTAIRES
|
|
-- ================================================
|
|
|
|
COMMENT ON TABLE users_enhanced IS 'Utilisateurs avec sécurité renforcée';
|
|
COMMENT ON TABLE messages_enhanced IS 'Messages unifiés avec séparation logique DM/salons';
|
|
COMMENT ON TABLE message_reactions_enhanced IS 'Réactions aux messages';
|
|
COMMENT ON TABLE message_mentions_enhanced IS 'Mentions d''utilisateurs';
|
|
COMMENT ON TABLE user_blocks_enhanced IS 'Blocages entre utilisateurs';
|
|
COMMENT ON TABLE user_sessions_enhanced IS 'Sessions sécurisées';
|
|
COMMENT ON TABLE security_events_enhanced IS 'Journal de sécurité'; |