Bloc A - Code mort: - Suppression Studio (components, views, features) - Suppression gamification + services mock (projectService, storageService, gamificationService) - Mise à jour Sidebar, Navbar, locales Bloc B - Frontend: - Suppression modal.tsx deprecated, Modal.stories (doublon Dialog) - Feature flags: PLAYLIST_SEARCH, PLAYLIST_RECOMMENDATIONS, ROLE_MANAGEMENT = true - Suppression 19 tests orphelins, retrait exclusions vitest.config Bloc C - Backend: - Extraction routes_auth.go depuis router.go Bloc D - Rust: - Suppression security_legacy.rs (code mort, patterns déjà dans security/)
215 lines
9 KiB
PL/PgSQL
215 lines
9 KiB
PL/PgSQL
-- 052_audit_triggers.sql
|
|
-- Database Triggers for Audit Logging (BE-DB-014: Add database triggers for audit logging)
|
|
-- Create triggers to auto-log changes to audit_logs table
|
|
|
|
-- === HELPER FUNCTION ===
|
|
-- Function to create audit log entries from trigger context
|
|
CREATE OR REPLACE FUNCTION public.audit_trigger_function()
|
|
RETURNS TRIGGER AS $$
|
|
DECLARE
|
|
v_user_id UUID;
|
|
v_action TEXT;
|
|
v_old_data JSONB;
|
|
v_new_data JSONB;
|
|
BEGIN
|
|
-- Determine action type
|
|
-- FIX: Use JSONB extraction to safely access columns that may not exist
|
|
-- This prevents errors when tables don't have creator_id or user_id columns
|
|
IF TG_OP = 'INSERT' THEN
|
|
v_action := 'create';
|
|
v_new_data := to_jsonb(NEW);
|
|
v_old_data := NULL;
|
|
-- Extract user_id from JSONB (safe - no direct column access)
|
|
IF v_new_data ? 'creator_id' AND v_new_data->>'creator_id' IS NOT NULL AND v_new_data->>'creator_id' != 'null' THEN
|
|
v_user_id := (v_new_data->>'creator_id')::UUID;
|
|
ELSIF v_new_data ? 'user_id' AND v_new_data->>'user_id' IS NOT NULL AND v_new_data->>'user_id' != 'null' THEN
|
|
v_user_id := (v_new_data->>'user_id')::UUID;
|
|
ELSE
|
|
v_user_id := NULL;
|
|
END IF;
|
|
ELSIF TG_OP = 'UPDATE' THEN
|
|
v_action := 'update';
|
|
v_old_data := to_jsonb(OLD);
|
|
v_new_data := to_jsonb(NEW);
|
|
-- Extract user_id from JSONB
|
|
IF v_new_data ? 'creator_id' AND v_new_data->>'creator_id' IS NOT NULL AND v_new_data->>'creator_id' != 'null' THEN
|
|
v_user_id := (v_new_data->>'creator_id')::UUID;
|
|
ELSIF v_new_data ? 'user_id' AND v_new_data->>'user_id' IS NOT NULL AND v_new_data->>'user_id' != 'null' THEN
|
|
v_user_id := (v_new_data->>'user_id')::UUID;
|
|
ELSE
|
|
v_user_id := NULL;
|
|
END IF;
|
|
ELSIF TG_OP = 'DELETE' THEN
|
|
v_action := 'delete';
|
|
v_old_data := to_jsonb(OLD);
|
|
v_new_data := NULL;
|
|
-- Extract user_id from JSONB
|
|
IF v_old_data ? 'creator_id' AND v_old_data->>'creator_id' IS NOT NULL AND v_old_data->>'creator_id' != 'null' THEN
|
|
v_user_id := (v_old_data->>'creator_id')::UUID;
|
|
ELSIF v_old_data ? 'user_id' AND v_old_data->>'user_id' IS NOT NULL AND v_old_data->>'user_id' != 'null' THEN
|
|
v_user_id := (v_old_data->>'user_id')::UUID;
|
|
ELSE
|
|
v_user_id := NULL;
|
|
END IF;
|
|
END IF;
|
|
|
|
-- Insert audit log entry
|
|
INSERT INTO public.audit_logs (
|
|
id,
|
|
user_id,
|
|
action,
|
|
resource,
|
|
resource_id,
|
|
metadata,
|
|
timestamp
|
|
) VALUES (
|
|
gen_random_uuid(),
|
|
v_user_id,
|
|
v_action,
|
|
TG_TABLE_NAME,
|
|
CASE
|
|
WHEN TG_OP = 'DELETE' THEN (OLD.id)::UUID
|
|
ELSE (NEW.id)::UUID
|
|
END,
|
|
jsonb_build_object(
|
|
'operation', TG_OP,
|
|
'old_data', v_old_data,
|
|
'new_data', v_new_data,
|
|
'table', TG_TABLE_NAME
|
|
),
|
|
NOW()
|
|
);
|
|
|
|
-- Return appropriate record
|
|
IF TG_OP = 'DELETE' THEN
|
|
RETURN OLD;
|
|
ELSE
|
|
RETURN NEW;
|
|
END IF;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
COMMENT ON FUNCTION public.audit_trigger_function() IS 'Generic audit trigger function that logs INSERT, UPDATE, and DELETE operations to audit_logs table';
|
|
|
|
-- === TRIGGERS FOR USERS TABLE ===
|
|
DROP TRIGGER IF EXISTS audit_users_trigger ON public.users;
|
|
CREATE TRIGGER audit_users_trigger
|
|
AFTER INSERT OR UPDATE OR DELETE ON public.users
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION public.audit_trigger_function();
|
|
|
|
COMMENT ON TRIGGER audit_users_trigger ON public.users IS 'Audit trigger for users table - logs all create, update, and delete operations';
|
|
|
|
-- === TRIGGERS FOR TRACKS TABLE ===
|
|
DROP TRIGGER IF EXISTS audit_tracks_trigger ON public.tracks;
|
|
CREATE TRIGGER audit_tracks_trigger
|
|
AFTER INSERT OR UPDATE OR DELETE ON public.tracks
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION public.audit_trigger_function();
|
|
|
|
COMMENT ON TRIGGER audit_tracks_trigger ON public.tracks IS 'Audit trigger for tracks table - logs all create, update, and delete operations';
|
|
|
|
-- === TRIGGERS FOR PLAYLISTS TABLE ===
|
|
DROP TRIGGER IF EXISTS audit_playlists_trigger ON public.playlists;
|
|
CREATE TRIGGER audit_playlists_trigger
|
|
AFTER INSERT OR UPDATE OR DELETE ON public.playlists
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION public.audit_trigger_function();
|
|
|
|
COMMENT ON TRIGGER audit_playlists_trigger ON public.playlists IS 'Audit trigger for playlists table - logs all create, update, and delete operations';
|
|
|
|
-- === TRIGGERS FOR PLAYLIST_TRACKS TABLE ===
|
|
DROP TRIGGER IF EXISTS audit_playlist_tracks_trigger ON public.playlist_tracks;
|
|
CREATE TRIGGER audit_playlist_tracks_trigger
|
|
AFTER INSERT OR UPDATE OR DELETE ON public.playlist_tracks
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION public.audit_trigger_function();
|
|
|
|
COMMENT ON TRIGGER audit_playlist_tracks_trigger ON public.playlist_tracks IS 'Audit trigger for playlist_tracks table - logs all create, update, and delete operations';
|
|
|
|
-- === TRIGGERS FOR TRACK_LIKES TABLE ===
|
|
DROP TRIGGER IF EXISTS audit_track_likes_trigger ON public.track_likes;
|
|
CREATE TRIGGER audit_track_likes_trigger
|
|
AFTER INSERT OR UPDATE OR DELETE ON public.track_likes
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION public.audit_trigger_function();
|
|
|
|
COMMENT ON TRIGGER audit_track_likes_trigger ON public.track_likes IS 'Audit trigger for track_likes table - logs all create, update, and delete operations';
|
|
|
|
-- === TRIGGERS FOR TRACK_COMMENTS TABLE ===
|
|
DROP TRIGGER IF EXISTS audit_track_comments_trigger ON public.track_comments;
|
|
CREATE TRIGGER audit_track_comments_trigger
|
|
AFTER INSERT OR UPDATE OR DELETE ON public.track_comments
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION public.audit_trigger_function();
|
|
|
|
COMMENT ON TRIGGER audit_track_comments_trigger ON public.track_comments IS 'Audit trigger for track_comments table - logs all create, update, and delete operations';
|
|
|
|
-- === TRIGGERS FOR MARKETPLACE_PRODUCTS TABLE ===
|
|
-- Note: marketplace_products table may not exist yet
|
|
DO $$
|
|
BEGIN
|
|
IF EXISTS (
|
|
SELECT 1 FROM information_schema.tables
|
|
WHERE table_schema = 'public'
|
|
AND table_name = 'marketplace_products'
|
|
) THEN
|
|
DROP TRIGGER IF EXISTS audit_marketplace_products_trigger ON public.marketplace_products;
|
|
CREATE TRIGGER audit_marketplace_products_trigger
|
|
AFTER INSERT OR UPDATE OR DELETE ON public.marketplace_products
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION public.audit_trigger_function();
|
|
|
|
COMMENT ON TRIGGER audit_marketplace_products_trigger ON public.marketplace_products IS 'Audit trigger for marketplace_products table - logs all create, update, and delete operations';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- === TRIGGERS FOR MARKETPLACE_ORDERS TABLE ===
|
|
-- Note: marketplace_orders table may not exist yet
|
|
DO $$
|
|
BEGIN
|
|
IF EXISTS (
|
|
SELECT 1 FROM information_schema.tables
|
|
WHERE table_schema = 'public'
|
|
AND table_name = 'marketplace_orders'
|
|
) THEN
|
|
DROP TRIGGER IF EXISTS audit_marketplace_orders_trigger ON public.marketplace_orders;
|
|
CREATE TRIGGER audit_marketplace_orders_trigger
|
|
AFTER INSERT OR UPDATE OR DELETE ON public.marketplace_orders
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION public.audit_trigger_function();
|
|
|
|
COMMENT ON TRIGGER audit_marketplace_orders_trigger ON public.marketplace_orders IS 'Audit trigger for marketplace_orders table - logs all create, update, and delete operations';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- === TRIGGERS FOR FOLLOWS TABLE ===
|
|
DROP TRIGGER IF EXISTS audit_follows_trigger ON public.follows;
|
|
CREATE TRIGGER audit_follows_trigger
|
|
AFTER INSERT OR UPDATE OR DELETE ON public.follows
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION public.audit_trigger_function();
|
|
|
|
COMMENT ON TRIGGER audit_follows_trigger ON public.follows IS 'Audit trigger for follows table - logs all create, update, and delete operations';
|
|
|
|
-- === TRIGGERS FOR USER_BLOCKS TABLE ===
|
|
DROP TRIGGER IF EXISTS audit_user_blocks_trigger ON public.user_blocks;
|
|
CREATE TRIGGER audit_user_blocks_trigger
|
|
AFTER INSERT OR UPDATE OR DELETE ON public.user_blocks
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION public.audit_trigger_function();
|
|
|
|
COMMENT ON TRIGGER audit_user_blocks_trigger ON public.user_blocks IS 'Audit trigger for user_blocks table - logs all create, update, and delete operations';
|
|
|
|
-- === TRIGGERS FOR PLAYLIST_COLLABORATORS TABLE ===
|
|
DROP TRIGGER IF EXISTS audit_playlist_collaborators_trigger ON public.playlist_collaborators;
|
|
CREATE TRIGGER audit_playlist_collaborators_trigger
|
|
AFTER INSERT OR UPDATE OR DELETE ON public.playlist_collaborators
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION public.audit_trigger_function();
|
|
|
|
COMMENT ON TRIGGER audit_playlist_collaborators_trigger ON public.playlist_collaborators IS 'Audit trigger for playlist_collaborators table - logs all create, update, and delete operations';
|
|
|
|
-- Note: Triggers are set to AFTER to ensure data integrity and allow access to both OLD and NEW records
|
|
-- The function captures the full state of records (old and new) in JSONB format for detailed audit trails
|
|
|