2025-12-24 14:47:38 +00:00
-- 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
2025-12-26 15:21:03 +00:00
-- 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
2025-12-24 14:47:38 +00:00
IF TG_OP = ' INSERT ' THEN
v_action : = ' create ' ;
v_new_data : = to_jsonb ( NEW ) ;
v_old_data : = NULL ;
2025-12-26 15:21:03 +00:00
-- 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 ;
2025-12-24 14:47:38 +00:00
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 ) ;
2025-12-26 15:21:03 +00:00
-- 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 ;
2025-12-24 14:47:38 +00:00
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 ;
2025-12-26 15:21:03 +00:00
-- 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 ;
2025-12-24 14:47:38 +00:00
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 ===
2025-12-26 09:35:01 +00:00
-- 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 $ $ ;
2025-12-24 14:47:38 +00:00
-- === TRIGGERS FOR MARKETPLACE_ORDERS TABLE ===
2025-12-26 09:35:01 +00:00
-- 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 $ $ ;
2025-12-24 14:47:38 +00:00
-- === 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