-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfix-audit-function-final.sql
More file actions
152 lines (138 loc) · 5.54 KB
/
Copy pathfix-audit-function-final.sql
File metadata and controls
152 lines (138 loc) · 5.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
-- Final fix for audit function with corrected variable naming
-- This fixes the "column reference ambiguous" error
-- Create the corrected audit log function
CREATE OR REPLACE FUNCTION create_audit_log()
RETURNS TRIGGER AS $$
DECLARE
changed_fields TEXT[];
old_data JSONB;
new_data JSONB;
BEGIN
-- Determine the action and set data accordingly
IF TG_OP = 'INSERT' THEN
old_data := NULL;
new_data := to_jsonb(NEW);
ELSIF TG_OP = 'UPDATE' THEN
old_data := to_jsonb(OLD);
new_data := to_jsonb(NEW);
-- Calculate changed fields
SELECT array_agg(key) INTO changed_fields
FROM jsonb_each(old_data) o
FULL OUTER JOIN jsonb_each(new_data) n USING (key)
WHERE o.value IS DISTINCT FROM n.value;
ELSIF TG_OP = 'DELETE' THEN
old_data := to_jsonb(OLD);
new_data := NULL;
END IF;
-- Insert audit log entry with corrected organization_id logic
INSERT INTO audit_logs (
organization_id,
user_id,
table_name,
record_id,
action,
old_data,
new_data,
changed_fields,
ip_address,
user_agent,
session_id,
request_id
) VALUES (
COALESCE(
CASE
-- FIXED: For organizations table, use 'id' field as the organization_id
WHEN TG_TABLE_NAME = 'organizations' THEN
COALESCE((NEW.id)::UUID, (OLD.id)::UUID)
-- For other tables that have organization_id field, use that
WHEN TG_TABLE_NAME IN ('memberships', 'projects', 'items', 'categories', 'attachments', 'custom_fields', 'subscriptions', 'invoices', 'payments', 'usage_tracking', 'organization_invitations', 'activities', 'feature_flag_overrides', 'api_keys') THEN
COALESCE((NEW.organization_id)::UUID, (OLD.organization_id)::UUID)
ELSE NULL
END,
NULL
),
auth.uid(),
TG_TABLE_NAME,
COALESCE((NEW.id)::UUID, (OLD.id)::UUID),
TG_OP,
old_data,
new_data,
changed_fields,
current_setting('request.ip', true)::inet,
current_setting('request.user_agent', true),
current_setting('request.session_id', true)::uuid,
current_setting('request.request_id', true)::uuid
);
-- Return the appropriate record based on operation
IF TG_OP = 'DELETE' THEN
RETURN OLD;
ELSE
RETURN NEW;
END IF;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Create audit triggers only for tables that exist
-- Check each table before creating the trigger
DO $$
BEGIN
-- Organizations table (definitely exists)
IF EXISTS (SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'organizations') THEN
DROP TRIGGER IF EXISTS audit_organizations ON organizations;
CREATE TRIGGER audit_organizations
AFTER INSERT OR UPDATE OR DELETE ON organizations
FOR EACH ROW EXECUTE FUNCTION create_audit_log();
RAISE NOTICE 'Created audit trigger for organizations';
END IF;
-- Users table
IF EXISTS (SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'users') THEN
DROP TRIGGER IF EXISTS audit_users ON users;
CREATE TRIGGER audit_users
AFTER INSERT OR UPDATE OR DELETE ON users
FOR EACH ROW EXECUTE FUNCTION create_audit_log();
RAISE NOTICE 'Created audit trigger for users';
END IF;
-- Memberships table
IF EXISTS (SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'memberships') THEN
DROP TRIGGER IF EXISTS audit_memberships ON memberships;
CREATE TRIGGER audit_memberships
AFTER INSERT OR UPDATE OR DELETE ON memberships
FOR EACH ROW EXECUTE FUNCTION create_audit_log();
RAISE NOTICE 'Created audit trigger for memberships';
END IF;
END $$;
-- Create triggers for remaining tables that exist (fixed variable naming)
DO $$
DECLARE
current_table_name TEXT;
tables_to_audit TEXT[] := ARRAY['projects', 'items', 'categories', 'attachments', 'custom_fields', 'subscriptions', 'invoices', 'payments', 'usage_tracking', 'organization_invitations', 'activities', 'feature_flag_overrides', 'api_keys', 'audit_logs'];
BEGIN
FOREACH current_table_name IN ARRAY tables_to_audit
LOOP
-- Fixed: Use different variable name to avoid conflict
IF EXISTS (SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = current_table_name) THEN
EXECUTE format('DROP TRIGGER IF EXISTS audit_%I ON %I', current_table_name, current_table_name);
EXECUTE format('CREATE TRIGGER audit_%I AFTER INSERT OR UPDATE OR DELETE ON %I FOR EACH ROW EXECUTE FUNCTION create_audit_log()', current_table_name, current_table_name);
RAISE NOTICE 'Created audit trigger for %', current_table_name;
ELSE
RAISE NOTICE 'Skipped audit trigger for % (table does not exist)', current_table_name;
END IF;
END LOOP;
END $$;
-- Test that organization creation still works
DO $$
DECLARE
test_org_id UUID;
BEGIN
BEGIN
INSERT INTO public.organizations (name, slug)
VALUES ('Audit Test Final', 'audit-test-final-' || extract(epoch from now()))
RETURNING id INTO test_org_id;
RAISE NOTICE 'SUCCESS: Organization creation works with corrected audit function! Org ID: %', test_org_id;
-- Clean up
DELETE FROM public.organizations WHERE id = test_org_id;
RAISE NOTICE 'SUCCESS: Test organization cleaned up';
EXCEPTION WHEN OTHERS THEN
RAISE NOTICE 'ERROR: Organization creation failed: %', SQLERRM;
END;
END $$;
SELECT 'AUDIT FUNCTION PROPERLY RESTORED - FINAL VERSION' as status;