pg_sql_triggers 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +144 -0
  3. data/COVERAGE.md +26 -19
  4. data/Goal.md +276 -155
  5. data/README.md +27 -1
  6. data/app/assets/javascripts/pg_sql_triggers/trigger_actions.js +50 -0
  7. data/app/controllers/concerns/pg_sql_triggers/error_handling.rb +56 -0
  8. data/app/controllers/concerns/pg_sql_triggers/kill_switch_protection.rb +66 -0
  9. data/app/controllers/concerns/pg_sql_triggers/permission_checking.rb +117 -0
  10. data/app/controllers/pg_sql_triggers/application_controller.rb +10 -62
  11. data/app/controllers/pg_sql_triggers/audit_logs_controller.rb +102 -0
  12. data/app/controllers/pg_sql_triggers/dashboard_controller.rb +4 -9
  13. data/app/controllers/pg_sql_triggers/tables_controller.rb +30 -4
  14. data/app/controllers/pg_sql_triggers/triggers_controller.rb +3 -21
  15. data/app/helpers/pg_sql_triggers/permissions_helper.rb +43 -0
  16. data/app/models/pg_sql_triggers/audit_log.rb +106 -0
  17. data/app/models/pg_sql_triggers/trigger_registry.rb +178 -9
  18. data/app/views/layouts/pg_sql_triggers/application.html.erb +26 -6
  19. data/app/views/pg_sql_triggers/audit_logs/index.html.erb +177 -0
  20. data/app/views/pg_sql_triggers/dashboard/index.html.erb +33 -8
  21. data/app/views/pg_sql_triggers/tables/index.html.erb +76 -3
  22. data/app/views/pg_sql_triggers/tables/show.html.erb +17 -4
  23. data/app/views/pg_sql_triggers/triggers/_drop_modal.html.erb +16 -7
  24. data/app/views/pg_sql_triggers/triggers/_re_execute_modal.html.erb +16 -7
  25. data/app/views/pg_sql_triggers/triggers/show.html.erb +26 -6
  26. data/config/routes.rb +2 -0
  27. data/db/migrate/20260103000001_create_pg_sql_triggers_audit_log.rb +28 -0
  28. data/docs/README.md +15 -5
  29. data/docs/api-reference.md +191 -0
  30. data/docs/audit-trail.md +413 -0
  31. data/docs/configuration.md +6 -6
  32. data/docs/permissions.md +369 -0
  33. data/docs/troubleshooting.md +486 -0
  34. data/docs/ui-guide.md +211 -0
  35. data/docs/web-ui.md +257 -34
  36. data/lib/pg_sql_triggers/errors.rb +245 -0
  37. data/lib/pg_sql_triggers/generator/service.rb +32 -0
  38. data/lib/pg_sql_triggers/permissions/checker.rb +9 -2
  39. data/lib/pg_sql_triggers/registry.rb +141 -8
  40. data/lib/pg_sql_triggers/sql/kill_switch.rb +33 -5
  41. data/lib/pg_sql_triggers/testing/function_tester.rb +2 -0
  42. data/lib/pg_sql_triggers/version.rb +1 -1
  43. data/lib/pg_sql_triggers.rb +3 -6
  44. metadata +29 -6
  45. data/docs/screenshots/.gitkeep +0 -1
  46. data/docs/screenshots/Generate Trigger.png +0 -0
  47. data/docs/screenshots/Triggers Page.png +0 -0
  48. data/docs/screenshots/kill error.png +0 -0
  49. data/docs/screenshots/kill modal for migration down.png +0 -0
@@ -9,6 +9,7 @@ Complete reference for using PgSqlTriggers programmatically from the Rails conso
9
9
  - [Kill Switch API](#kill-switch-api)
10
10
  - [DSL API](#dsl-api)
11
11
  - [TriggerRegistry Model](#triggerregistry-model)
12
+ - [Audit Log API](#audit-log-api)
12
13
 
13
14
  ## Registry API
14
15
 
@@ -97,6 +98,76 @@ end
97
98
 
98
99
  **Returns**: Hash with drift categories
99
100
 
101
+ ### `PgSqlTriggers::Registry.drifted`
102
+
103
+ Returns all triggers that have drifted from their expected state.
104
+
105
+ ```ruby
106
+ drifted_triggers = PgSqlTriggers::Registry.drifted
107
+ # => [
108
+ # { state: "drifted", trigger_name: "users_email_validation", ... },
109
+ # ...
110
+ # ]
111
+
112
+ drifted_triggers.each do |trigger|
113
+ puts "Drifted: #{trigger[:trigger_name]}"
114
+ end
115
+ ```
116
+
117
+ **Returns**: Array of drift result hashes for drifted triggers
118
+
119
+ ### `PgSqlTriggers::Registry.in_sync`
120
+
121
+ Returns all triggers that are in sync with their expected state.
122
+
123
+ ```ruby
124
+ in_sync_triggers = PgSqlTriggers::Registry.in_sync
125
+ # => [
126
+ # { state: "in_sync", trigger_name: "billing_trigger", ... },
127
+ # ...
128
+ # ]
129
+
130
+ puts "In sync triggers: #{in_sync_triggers.count}"
131
+ ```
132
+
133
+ **Returns**: Array of drift result hashes for in-sync triggers
134
+
135
+ ### `PgSqlTriggers::Registry.unknown_triggers`
136
+
137
+ Returns all unknown (external) triggers not managed by this gem.
138
+
139
+ ```ruby
140
+ unknown = PgSqlTriggers::Registry.unknown_triggers
141
+ # => [
142
+ # { state: "unknown", trigger_name: "external_trigger", ... },
143
+ # ...
144
+ # ]
145
+
146
+ unknown.each do |trigger|
147
+ puts "External trigger: #{trigger[:trigger_name]}"
148
+ end
149
+ ```
150
+
151
+ **Returns**: Array of drift result hashes for unknown triggers
152
+
153
+ ### `PgSqlTriggers::Registry.dropped`
154
+
155
+ Returns all triggers that have been dropped from the database.
156
+
157
+ ```ruby
158
+ dropped_triggers = PgSqlTriggers::Registry.dropped
159
+ # => [
160
+ # { state: "dropped", trigger_name: "old_trigger", ... },
161
+ # ...
162
+ # ]
163
+
164
+ dropped_triggers.each do |trigger|
165
+ puts "Dropped: #{trigger[:trigger_name]}"
166
+ end
167
+ ```
168
+
169
+ **Returns**: Array of drift result hashes for dropped triggers
170
+
100
171
  ### `PgSqlTriggers::Registry.validate!`
101
172
 
102
173
  Validates all triggers and raises an error if any are invalid.
@@ -836,6 +907,14 @@ puts "Total triggers: #{triggers.count}"
836
907
  drift = PgSqlTriggers::Registry.diff
837
908
  puts "Drifted triggers: #{drift[:drifted].count}"
838
909
 
910
+ # Alternative: Use dedicated query methods
911
+ drifted = PgSqlTriggers::Registry.drifted
912
+ in_sync = PgSqlTriggers::Registry.in_sync
913
+ unknown = PgSqlTriggers::Registry.unknown_triggers
914
+ dropped = PgSqlTriggers::Registry.dropped
915
+
916
+ puts "Drifted: #{drifted.count}, In Sync: #{in_sync.count}, Unknown: #{unknown.count}, Dropped: #{dropped.count}"
917
+
839
918
  # 5. Enable specific trigger
840
919
  trigger = PgSqlTriggers::TriggerRegistry.find_by(trigger_name: "users_email_validation")
841
920
  trigger.enable!(confirmation: "EXECUTE TRIGGER_ENABLE") if trigger
@@ -922,6 +1001,118 @@ rescue StandardError => e
922
1001
  end
923
1002
  ```
924
1003
 
1004
+ ## Audit Log API
1005
+
1006
+ The Audit Log API provides methods for querying and managing audit log entries.
1007
+
1008
+ ### `PgSqlTriggers::AuditLog`
1009
+
1010
+ The `AuditLog` model provides methods for querying audit log entries.
1011
+
1012
+ #### Class Methods
1013
+
1014
+ ##### `AuditLog.for_trigger_name(trigger_name)`
1015
+
1016
+ Get audit log entries for a specific trigger.
1017
+
1018
+ **Parameters:**
1019
+ - `trigger_name` (String): The trigger name to query
1020
+
1021
+ **Returns:** `ActiveRecord::Relation` - Audit log entries for the trigger, ordered by most recent first
1022
+
1023
+ **Example:**
1024
+ ```ruby
1025
+ # Get all audit log entries for a specific trigger
1026
+ entries = PgSqlTriggers::AuditLog.for_trigger_name("users_email_validation")
1027
+ entries.each do |entry|
1028
+ puts "#{entry.operation}: #{entry.status} at #{entry.created_at}"
1029
+ end
1030
+ ```
1031
+
1032
+ ##### `AuditLog.log_success(...)`
1033
+
1034
+ Log a successful operation to the audit log.
1035
+
1036
+ **Parameters:**
1037
+ - `operation:` (Symbol, String): The operation being performed (e.g., `:trigger_enable`)
1038
+ - `trigger_name:` (String, nil): The trigger name (if applicable)
1039
+ - `actor:` (Hash): Information about who performed the action (e.g., `{ type: "UI", id: "123" }`)
1040
+ - `environment:` (String, nil): The environment
1041
+ - `reason:` (String, nil): Reason for the operation
1042
+ - `confirmation_text:` (String, nil): Confirmation text used
1043
+ - `before_state:` (Hash, nil): State before operation
1044
+ - `after_state:` (Hash, nil): State after operation
1045
+ - `diff:` (String, nil): Diff information
1046
+
1047
+ **Returns:** `AuditLog` instance or `nil` if logging fails
1048
+
1049
+ **Example:**
1050
+ ```ruby
1051
+ PgSqlTriggers::AuditLog.log_success(
1052
+ operation: :trigger_enable,
1053
+ trigger_name: "users_email_validation",
1054
+ actor: { type: "Console", id: "admin@example.com" },
1055
+ environment: "production",
1056
+ before_state: { enabled: false },
1057
+ after_state: { enabled: true }
1058
+ )
1059
+ ```
1060
+
1061
+ ##### `AuditLog.log_failure(...)`
1062
+
1063
+ Log a failed operation to the audit log.
1064
+
1065
+ **Parameters:**
1066
+ - `operation:` (Symbol, String): The operation being performed
1067
+ - `trigger_name:` (String, nil): The trigger name (if applicable)
1068
+ - `actor:` (Hash): Information about who performed the action
1069
+ - `environment:` (String, nil): The environment
1070
+ - `error_message:` (String): Error message (required)
1071
+ - `reason:` (String, nil): Reason for the operation (if provided before failure)
1072
+ - `confirmation_text:` (String, nil): Confirmation text used
1073
+ - `before_state:` (Hash, nil): State before operation
1074
+
1075
+ **Returns:** `AuditLog` instance or `nil` if logging fails
1076
+
1077
+ **Example:**
1078
+ ```ruby
1079
+ PgSqlTriggers::AuditLog.log_failure(
1080
+ operation: :trigger_enable,
1081
+ trigger_name: "users_email_validation",
1082
+ actor: { type: "UI", id: "user_123" },
1083
+ environment: "production",
1084
+ error_message: "Trigger does not exist in database",
1085
+ before_state: { enabled: false }
1086
+ )
1087
+ ```
1088
+
1089
+ #### Scopes
1090
+
1091
+ The `AuditLog` model provides several useful scopes:
1092
+
1093
+ - `AuditLog.for_trigger(trigger_name)` - Filter by trigger name
1094
+ - `AuditLog.for_operation(operation)` - Filter by operation type
1095
+ - `AuditLog.for_environment(env)` - Filter by environment
1096
+ - `AuditLog.successful` - Only successful operations
1097
+ - `AuditLog.failed` - Only failed operations
1098
+ - `AuditLog.recent` - Order by most recent first
1099
+
1100
+ **Example:**
1101
+ ```ruby
1102
+ # Get all failed operations in production
1103
+ failed_ops = PgSqlTriggers::AuditLog
1104
+ .failed
1105
+ .for_environment("production")
1106
+ .recent
1107
+ .limit(10)
1108
+
1109
+ # Get all enable operations for a trigger
1110
+ enable_ops = PgSqlTriggers::AuditLog
1111
+ .for_trigger("users_email_validation")
1112
+ .for_operation("trigger_enable")
1113
+ .recent
1114
+ ```
1115
+
925
1116
  ## Next Steps
926
1117
 
927
1118
  - [Usage Guide](usage-guide.md) - Learn the DSL and migration system
@@ -0,0 +1,413 @@
1
+ # Audit Trail Guide
2
+
3
+ PgSqlTriggers provides comprehensive audit logging for all trigger operations. This guide explains how to view, filter, and export audit logs.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Overview](#overview)
8
+ - [Accessing Audit Logs](#accessing-audit-logs)
9
+ - [Viewing Audit Logs](#viewing-audit-logs)
10
+ - [Filtering Logs](#filtering-logs)
11
+ - [Exporting Logs](#exporting-logs)
12
+ - [Console API](#console-api)
13
+ - [Logged Operations](#logged-operations)
14
+ - [Audit Log Structure](#audit-log-structure)
15
+
16
+ ## Overview
17
+
18
+ The audit trail captures:
19
+
20
+ - **Who**: Actor information (user type and ID)
21
+ - **What**: Operation performed (enable, disable, drop, re-execute, etc.)
22
+ - **When**: Timestamp of the operation
23
+ - **Where**: Environment where operation occurred
24
+ - **Result**: Success or failure status
25
+ - **Context**: Before/after state, reasons, confirmation text, error messages
26
+
27
+ All operations are automatically logged, providing a complete audit trail for compliance and debugging.
28
+
29
+ ## Accessing Audit Logs
30
+
31
+ ### Via Web UI
32
+
33
+ Navigate to the Audit Log page:
34
+
35
+ 1. Go to the PgSqlTriggers dashboard
36
+ 2. Click "Audit Log" in the navigation menu
37
+ 3. URL: `http://localhost:3000/pg_sql_triggers/audit_logs`
38
+
39
+ ### Via Console API
40
+
41
+ ```ruby
42
+ # Get all audit logs
43
+ PgSqlTriggers::AuditLog.all
44
+
45
+ # Get logs for a specific trigger
46
+ PgSqlTriggers::AuditLog.for_trigger_name("users_email_validation")
47
+
48
+ # Get recent logs
49
+ PgSqlTriggers::AuditLog.recent.limit(100)
50
+ ```
51
+
52
+ ## Viewing Audit Logs
53
+
54
+ ### Audit Log Table
55
+
56
+ The audit log table displays:
57
+
58
+ - **Timestamp**: When the operation occurred
59
+ - **Operation**: Type of operation (enable, disable, drop, etc.)
60
+ - **Trigger Name**: Affected trigger (if applicable)
61
+ - **Actor**: Who performed the operation (type and ID)
62
+ - **Environment**: Environment where operation occurred
63
+ - **Status**: Success or failure
64
+ - **Reason**: Reason provided (for drop/re-execute operations)
65
+
66
+ ### Viewing Details
67
+
68
+ Click on any audit log entry to view detailed information:
69
+
70
+ - **Before State**: State before the operation (enabled/disabled, function body, etc.)
71
+ - **After State**: State after the operation
72
+ - **Diff**: Changes made (for re-execute operations)
73
+ - **Confirmation Text**: Confirmation text used (if applicable)
74
+ - **Error Message**: Error details (for failed operations)
75
+
76
+ ## Filtering Logs
77
+
78
+ ### Filter Options
79
+
80
+ Filter audit logs by:
81
+
82
+ - **Trigger Name**: Filter by specific trigger
83
+ - **Operation**: Filter by operation type (enable, disable, drop, etc.)
84
+ - **Status**: Filter by success or failure
85
+ - **Environment**: Filter by environment (production, staging, etc.)
86
+ - **Sort Order**: Newest first or oldest first
87
+
88
+ ### Using Filters in UI
89
+
90
+ 1. Navigate to Audit Log page
91
+ 2. Use filter dropdowns at the top
92
+ 3. Click "Apply Filters"
93
+ 4. Use "Clear" to reset filters
94
+
95
+ ### Filtering via Console API
96
+
97
+ ```ruby
98
+ # Filter by trigger
99
+ PgSqlTriggers::AuditLog.for_trigger("users_email_validation")
100
+
101
+ # Filter by operation
102
+ PgSqlTriggers::AuditLog.for_operation("trigger_enable")
103
+
104
+ # Filter by environment
105
+ PgSqlTriggers::AuditLog.for_environment("production")
106
+
107
+ # Filter by status
108
+ PgSqlTriggers::AuditLog.successful
109
+ PgSqlTriggers::AuditLog.failed
110
+
111
+ # Combine filters
112
+ PgSqlTriggers::AuditLog
113
+ .for_trigger("users_email_validation")
114
+ .for_operation("trigger_enable")
115
+ .successful
116
+ .recent
117
+ .limit(50)
118
+ ```
119
+
120
+ ### Advanced Filtering
121
+
122
+ ```ruby
123
+ # Get failed operations in production
124
+ PgSqlTriggers::AuditLog
125
+ .for_environment("production")
126
+ .failed
127
+ .recent
128
+
129
+ # Get all drop operations
130
+ PgSqlTriggers::AuditLog
131
+ .for_operation("trigger_drop")
132
+ .includes(:trigger_name)
133
+
134
+ # Get operations by specific actor
135
+ PgSqlTriggers::AuditLog
136
+ .where("actor->>'type' = ? AND actor->>'id' = ?", "User", "123")
137
+ ```
138
+
139
+ ## Exporting Logs
140
+
141
+ ### CSV Export via UI
142
+
143
+ 1. Apply any desired filters
144
+ 2. Click "Export CSV" button
145
+ 3. CSV file downloads with all visible entries
146
+ 4. Filters are preserved in the export
147
+
148
+ ### CSV Export Format
149
+
150
+ The CSV includes all columns:
151
+
152
+ - Timestamp
153
+ - Operation
154
+ - Trigger Name
155
+ - Actor Type
156
+ - Actor ID
157
+ - Environment
158
+ - Status
159
+ - Reason
160
+ - Error Message (for failures)
161
+ - Created At
162
+
163
+ ### Programmatic Export
164
+
165
+ ```ruby
166
+ # Export to CSV
167
+ require 'csv'
168
+
169
+ logs = PgSqlTriggers::AuditLog.recent.limit(1000)
170
+
171
+ CSV.open("audit_logs.csv", "w") do |csv|
172
+ csv << ["Timestamp", "Operation", "Trigger", "Actor", "Environment", "Status", "Reason"]
173
+
174
+ logs.each do |log|
175
+ csv << [
176
+ log.created_at,
177
+ log.operation,
178
+ log.trigger_name,
179
+ "#{log.actor['type']}:#{log.actor['id']}",
180
+ log.environment,
181
+ log.status,
182
+ log.reason
183
+ ]
184
+ end
185
+ end
186
+ ```
187
+
188
+ ## Console API
189
+
190
+ ### Querying Audit Logs
191
+
192
+ ```ruby
193
+ # Get logs for a trigger
194
+ PgSqlTriggers::AuditLog.for_trigger_name("users_email_validation")
195
+
196
+ # Returns: ActiveRecord::Relation ordered by most recent first
197
+ ```
198
+
199
+ ### Logging Operations
200
+
201
+ Operations are automatically logged, but you can also log manually:
202
+
203
+ ```ruby
204
+ # Log a successful operation
205
+ PgSqlTriggers::AuditLog.log_success(
206
+ operation: :trigger_enable,
207
+ trigger_name: "users_email_validation",
208
+ actor: { type: "Console", id: "admin@example.com" },
209
+ environment: "production",
210
+ before_state: { enabled: false },
211
+ after_state: { enabled: true }
212
+ )
213
+
214
+ # Log a failed operation
215
+ PgSqlTriggers::AuditLog.log_failure(
216
+ operation: :trigger_drop,
217
+ trigger_name: "old_trigger",
218
+ actor: { type: "UI", id: "user_123" },
219
+ environment: "production",
220
+ error_message: "Trigger not found",
221
+ reason: "Cleanup"
222
+ )
223
+ ```
224
+
225
+ ## Logged Operations
226
+
227
+ The following operations are automatically logged:
228
+
229
+ ### Trigger Operations
230
+
231
+ - **`trigger_enable`**: Trigger enabled
232
+ - **`trigger_disable`**: Trigger disabled
233
+ - **`trigger_drop`**: Trigger dropped from database
234
+ - **`trigger_re_execute`**: Trigger re-executed (drop and recreate)
235
+
236
+ ### Migration Operations
237
+
238
+ - **`migration_up`**: Migration applied (up)
239
+ - **`migration_down`**: Migration rolled back (down)
240
+
241
+ ### SQL Capsule Operations
242
+
243
+ - **`sql_capsule_execute`**: SQL capsule executed
244
+ - **`sql_capsule_dry_run`**: SQL capsule dry-run performed
245
+
246
+ ### Generator Operations
247
+
248
+ - **`trigger_generate`**: Trigger generated via UI
249
+
250
+ ## Audit Log Structure
251
+
252
+ ### Database Schema
253
+
254
+ The audit log table (`pg_sql_triggers_audit_log`) contains:
255
+
256
+ | Column | Type | Description |
257
+ |--------|------|-------------|
258
+ | `id` | integer | Primary key |
259
+ | `trigger_name` | string | Trigger name (nullable) |
260
+ | `operation` | string | Operation type |
261
+ | `actor` | jsonb | Actor information (type, id) |
262
+ | `environment` | string | Environment name |
263
+ | `status` | string | "success" or "failure" |
264
+ | `reason` | text | Reason for operation (nullable) |
265
+ | `confirmation_text` | text | Confirmation text used (nullable) |
266
+ | `before_state` | jsonb | State before operation (nullable) |
267
+ | `after_state` | jsonb | State after operation (nullable) |
268
+ | `diff` | text | Diff information (nullable) |
269
+ | `error_message` | text | Error message (for failures) |
270
+ | `created_at` | timestamp | When operation occurred |
271
+ | `updated_at` | timestamp | Last update time |
272
+
273
+ ### Actor Format
274
+
275
+ Actor information is stored as JSON:
276
+
277
+ ```json
278
+ {
279
+ "type": "User",
280
+ "id": "123"
281
+ }
282
+ ```
283
+
284
+ Or for console operations:
285
+
286
+ ```json
287
+ {
288
+ "type": "Console",
289
+ "id": "admin@example.com"
290
+ }
291
+ ```
292
+
293
+ ### State Format
294
+
295
+ Before and after states are stored as JSON:
296
+
297
+ ```json
298
+ {
299
+ "enabled": true,
300
+ "function_body": "CREATE FUNCTION...",
301
+ "version": 1,
302
+ "drift_state": "in_sync"
303
+ }
304
+ ```
305
+
306
+ ## Use Cases
307
+
308
+ ### Compliance Auditing
309
+
310
+ Track who performed what operations for compliance:
311
+
312
+ ```ruby
313
+ # Get all admin operations in production
314
+ PgSqlTriggers::AuditLog
315
+ .for_environment("production")
316
+ .where("actor->>'type' = ?", "Admin")
317
+ .recent
318
+ ```
319
+
320
+ ### Debugging Failed Operations
321
+
322
+ Find and analyze failed operations:
323
+
324
+ ```ruby
325
+ # Get recent failures
326
+ failures = PgSqlTriggers::AuditLog.failed.recent.limit(50)
327
+
328
+ failures.each do |log|
329
+ puts "#{log.created_at}: #{log.operation} on #{log.trigger_name}"
330
+ puts "Error: #{log.error_message}"
331
+ puts "Actor: #{log.actor}"
332
+ puts "---"
333
+ end
334
+ ```
335
+
336
+ ### Trigger History
337
+
338
+ View complete history of a trigger:
339
+
340
+ ```ruby
341
+ history = PgSqlTriggers::AuditLog.for_trigger_name("users_email_validation")
342
+
343
+ history.each do |log|
344
+ puts "#{log.created_at}: #{log.operation} - #{log.status}"
345
+ if log.reason
346
+ puts " Reason: #{log.reason}"
347
+ end
348
+ end
349
+ ```
350
+
351
+ ### Operation Analysis
352
+
353
+ Analyze operation patterns:
354
+
355
+ ```ruby
356
+ # Count operations by type
357
+ PgSqlTriggers::AuditLog
358
+ .group(:operation)
359
+ .count
360
+
361
+ # Count failures by operation
362
+ PgSqlTriggers::AuditLog
363
+ .failed
364
+ .group(:operation)
365
+ .count
366
+
367
+ # Operations by environment
368
+ PgSqlTriggers::AuditLog
369
+ .group(:environment)
370
+ .count
371
+ ```
372
+
373
+ ## Best Practices
374
+
375
+ 1. **Regular Reviews**: Review audit logs regularly for anomalies
376
+ 2. **Retention Policy**: Implement a retention policy for old logs
377
+ 3. **Monitoring**: Set up alerts for failed operations
378
+ 4. **Backup**: Include audit logs in your backup strategy
379
+ 5. **Analysis**: Use audit logs to understand usage patterns
380
+
381
+ ## Troubleshooting
382
+
383
+ ### Audit logs not appearing
384
+
385
+ **Problem**: Operations are not being logged.
386
+
387
+ **Solution**:
388
+ - Check that migrations are run (`rails db:migrate`)
389
+ - Verify the audit log table exists
390
+ - Check Rails logs for audit logging errors
391
+
392
+ ### Performance issues with large logs
393
+
394
+ **Problem**: Audit log queries are slow.
395
+
396
+ **Solution**:
397
+ - Add indexes on frequently queried columns
398
+ - Implement pagination
399
+ - Archive old logs regularly
400
+ - Use filtering to reduce result set size
401
+
402
+ ### Missing actor information
403
+
404
+ **Problem**: Actor shows as "unknown".
405
+
406
+ **Solution**: Ensure `current_actor` method is properly implemented in your controllers.
407
+
408
+ ## Related Documentation
409
+
410
+ - [Web UI Guide](web-ui.md#audit-log) - Using the audit log UI
411
+ - [API Reference](api-reference.md#audit-log-api) - Console API methods
412
+ - [Configuration Reference](configuration.md) - Configuration options
413
+
@@ -208,12 +208,12 @@ config.permission_checker = ->(actor, action, environment) {
208
208
  return false unless user
209
209
 
210
210
  case action
211
- when :view
212
- user.present?
213
- when :operate
214
- user.operator? || user.admin?
215
- when :admin
216
- user.admin?
211
+ when :view_triggers, :view_diffs
212
+ user.present? # Viewer level
213
+ when :enable_trigger, :disable_trigger, :apply_trigger, :generate_trigger, :test_trigger, :dry_run_sql
214
+ user.operator? || user.admin? # Operator level
215
+ when :drop_trigger, :execute_sql, :override_drift
216
+ user.admin? # Admin level
217
217
  else
218
218
  false
219
219
  end