pg_sql_triggers 1.0.0 → 1.1.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.
- checksums.yaml +4 -4
- data/.erb_lint.yml +47 -0
- data/.rubocop.yml +4 -1
- data/CHANGELOG.md +112 -1
- data/COVERAGE.md +58 -0
- data/Goal.md +450 -123
- data/README.md +53 -215
- data/app/controllers/pg_sql_triggers/application_controller.rb +46 -0
- data/app/controllers/pg_sql_triggers/dashboard_controller.rb +4 -1
- data/app/controllers/pg_sql_triggers/generator_controller.rb +76 -8
- data/app/controllers/pg_sql_triggers/migrations_controller.rb +18 -0
- data/app/models/pg_sql_triggers/trigger_registry.rb +93 -12
- data/app/views/layouts/pg_sql_triggers/application.html.erb +34 -1
- data/app/views/pg_sql_triggers/dashboard/index.html.erb +70 -30
- data/app/views/pg_sql_triggers/generator/new.html.erb +22 -4
- data/app/views/pg_sql_triggers/generator/preview.html.erb +244 -16
- data/app/views/pg_sql_triggers/shared/_confirmation_modal.html.erb +221 -0
- data/app/views/pg_sql_triggers/shared/_kill_switch_status.html.erb +40 -0
- data/app/views/pg_sql_triggers/tables/index.html.erb +0 -2
- data/app/views/pg_sql_triggers/tables/show.html.erb +3 -4
- data/config/initializers/pg_sql_triggers.rb +69 -0
- data/db/migrate/20251222000001_create_pg_sql_triggers_tables.rb +3 -1
- data/db/migrate/20251229071916_add_timing_to_pg_sql_triggers_registry.rb +8 -0
- data/docs/README.md +66 -0
- data/docs/api-reference.md +681 -0
- data/docs/configuration.md +541 -0
- data/docs/getting-started.md +135 -0
- data/docs/kill-switch.md +586 -0
- data/docs/screenshots/.gitkeep +1 -0
- data/docs/screenshots/Generate Trigger.png +0 -0
- data/docs/screenshots/Triggers Page.png +0 -0
- data/docs/screenshots/kill error.png +0 -0
- data/docs/screenshots/kill modal for migration down.png +0 -0
- data/docs/usage-guide.md +493 -0
- data/docs/web-ui.md +353 -0
- data/lib/generators/pg_sql_triggers/templates/create_pg_sql_triggers_tables.rb +3 -1
- data/lib/generators/pg_sql_triggers/templates/initializer.rb +44 -2
- data/lib/pg_sql_triggers/drift/db_queries.rb +116 -0
- data/lib/pg_sql_triggers/drift/detector.rb +187 -0
- data/lib/pg_sql_triggers/drift/reporter.rb +179 -0
- data/lib/pg_sql_triggers/drift.rb +14 -11
- data/lib/pg_sql_triggers/dsl/trigger_definition.rb +15 -1
- data/lib/pg_sql_triggers/generator/form.rb +3 -1
- data/lib/pg_sql_triggers/generator/service.rb +82 -26
- data/lib/pg_sql_triggers/migration.rb +1 -1
- data/lib/pg_sql_triggers/migrator/pre_apply_comparator.rb +344 -0
- data/lib/pg_sql_triggers/migrator/pre_apply_diff_reporter.rb +143 -0
- data/lib/pg_sql_triggers/migrator/safety_validator.rb +258 -0
- data/lib/pg_sql_triggers/migrator.rb +85 -3
- data/lib/pg_sql_triggers/registry/manager.rb +100 -13
- data/lib/pg_sql_triggers/sql/kill_switch.rb +300 -0
- data/lib/pg_sql_triggers/testing/dry_run.rb +5 -7
- data/lib/pg_sql_triggers/testing/function_tester.rb +66 -24
- data/lib/pg_sql_triggers/testing/safe_executor.rb +23 -11
- data/lib/pg_sql_triggers/testing/syntax_validator.rb +24 -1
- data/lib/pg_sql_triggers/version.rb +1 -1
- data/lib/pg_sql_triggers.rb +24 -0
- data/lib/tasks/trigger_migrations.rake +33 -0
- data/scripts/generate_coverage_report.rb +129 -0
- metadata +45 -5
data/docs/web-ui.md
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
# Web UI Documentation
|
|
2
|
+
|
|
3
|
+
The PgSqlTriggers web interface provides a visual dashboard for managing triggers, migrations, and monitoring drift status.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Accessing the Web UI](#accessing-the-web-ui)
|
|
8
|
+
- [Dashboard Overview](#dashboard-overview)
|
|
9
|
+
- [Managing Triggers](#managing-triggers)
|
|
10
|
+
- [Migration Management](#migration-management)
|
|
11
|
+
- [SQL Capsules](#sql-capsules)
|
|
12
|
+
- [Permissions and Safety](#permissions-and-safety)
|
|
13
|
+
|
|
14
|
+
## Accessing the Web UI
|
|
15
|
+
|
|
16
|
+
By default, the web UI is mounted at:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
http://localhost:3000/pg_sql_triggers
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
You can customize the mount path in your routes:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
# config/routes.rb
|
|
26
|
+
Rails.application.routes.draw do
|
|
27
|
+
mount PgSqlTriggers::Engine, at: "/admin/triggers" # Custom path
|
|
28
|
+
end
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Dashboard Overview
|
|
32
|
+
|
|
33
|
+
The dashboard provides a comprehensive view of your trigger ecosystem.
|
|
34
|
+
|
|
35
|
+
### Main Features
|
|
36
|
+
|
|
37
|
+
1. **Trigger List**: View all triggers with their current status
|
|
38
|
+
2. **Drift Detection**: Visual indicators for drift states
|
|
39
|
+
3. **Migration Status**: See pending and applied migrations
|
|
40
|
+
4. **Quick Actions**: Enable/disable triggers, run migrations
|
|
41
|
+
5. **Kill Switch Status**: Production environment indicator
|
|
42
|
+
|
|
43
|
+

|
|
44
|
+
|
|
45
|
+
### Status Indicators
|
|
46
|
+
|
|
47
|
+
- **✓ Green**: Managed & In Sync
|
|
48
|
+
- **⚠ Yellow**: Drifted or Manual Override
|
|
49
|
+
- **✗ Red**: Dropped or Error
|
|
50
|
+
- **○ Gray**: Disabled
|
|
51
|
+
- **? Purple**: Unknown
|
|
52
|
+
|
|
53
|
+
## Managing Triggers
|
|
54
|
+
|
|
55
|
+
### Viewing Trigger Details
|
|
56
|
+
|
|
57
|
+
Click on any trigger to view:
|
|
58
|
+
- Current status and drift state
|
|
59
|
+
- Table and function information
|
|
60
|
+
- Version history
|
|
61
|
+
- Enabled/disabled state
|
|
62
|
+
- Environment configuration
|
|
63
|
+
|
|
64
|
+
### Enabling/Disabling Triggers
|
|
65
|
+
|
|
66
|
+
#### Enable a Trigger
|
|
67
|
+
|
|
68
|
+
1. Navigate to the trigger in the dashboard
|
|
69
|
+
2. Click the "Enable" button
|
|
70
|
+
3. In production environments, enter the confirmation text when prompted
|
|
71
|
+
4. Confirm the action
|
|
72
|
+
|
|
73
|
+
#### Disable a Trigger
|
|
74
|
+
|
|
75
|
+
1. Navigate to the trigger in the dashboard
|
|
76
|
+
2. Click the "Disable" button
|
|
77
|
+
3. In production environments, enter the confirmation text when prompted
|
|
78
|
+
4. Confirm the action
|
|
79
|
+
|
|
80
|
+
### Viewing Drift Status
|
|
81
|
+
|
|
82
|
+
The dashboard automatically shows drift status for each trigger:
|
|
83
|
+
|
|
84
|
+
- **In Sync**: Green checkmark, no action needed
|
|
85
|
+
- **Drifted**: Yellow warning, shows differences between DSL and database
|
|
86
|
+
- **Manual Override**: Yellow warning, indicates changes made outside PgSqlTriggers
|
|
87
|
+
- **Dropped**: Red X, trigger removed from database but still in registry
|
|
88
|
+
|
|
89
|
+
### Trigger Actions
|
|
90
|
+
|
|
91
|
+
Available actions depend on trigger state and your permissions:
|
|
92
|
+
|
|
93
|
+
- **Enable/Disable**: Toggle trigger activation
|
|
94
|
+
- **Apply**: Apply generated trigger definition
|
|
95
|
+
- **Drop**: Remove trigger from database (Admin only)
|
|
96
|
+
- **View SQL**: See the trigger's SQL definition
|
|
97
|
+
- **View Diff**: Compare DSL vs database state
|
|
98
|
+
|
|
99
|
+
## Migration Management
|
|
100
|
+
|
|
101
|
+
The Web UI provides full migration management capabilities.
|
|
102
|
+
|
|
103
|
+
### Migration Status View
|
|
104
|
+
|
|
105
|
+
Navigate to the "Migrations" tab to see:
|
|
106
|
+
|
|
107
|
+
- **Pending Migrations**: Not yet applied (down state)
|
|
108
|
+
- **Applied Migrations**: Successfully run (up state)
|
|
109
|
+
- **Migration Details**: Timestamp, name, and status
|
|
110
|
+
|
|
111
|
+
### Applying Migrations
|
|
112
|
+
|
|
113
|
+
#### Apply All Pending Migrations
|
|
114
|
+
|
|
115
|
+
1. Click "Apply All Pending Migrations" button
|
|
116
|
+
2. Review the list of migrations to be applied
|
|
117
|
+
3. In production, enter confirmation text: `EXECUTE UI_MIGRATION_UP`
|
|
118
|
+
4. Confirm the action
|
|
119
|
+
5. Wait for completion and review results
|
|
120
|
+
|
|
121
|
+
#### Apply Individual Migration
|
|
122
|
+
|
|
123
|
+
1. Find the migration in the status table
|
|
124
|
+
2. Click the "Up" button next to the migration
|
|
125
|
+
3. In production, enter confirmation text
|
|
126
|
+
4. Confirm the action
|
|
127
|
+
|
|
128
|
+
### Rolling Back Migrations
|
|
129
|
+
|
|
130
|
+
#### Rollback Last Migration
|
|
131
|
+
|
|
132
|
+
1. Click "Rollback Last Migration" button
|
|
133
|
+
2. Review which migration will be rolled back
|
|
134
|
+
3. In production, enter confirmation text: `EXECUTE UI_MIGRATION_DOWN`
|
|
135
|
+
4. Confirm the action
|
|
136
|
+
|
|
137
|
+
#### Rollback Individual Migration
|
|
138
|
+
|
|
139
|
+
1. Find the migration in the status table
|
|
140
|
+
2. Click the "Down" button next to the migration
|
|
141
|
+
3. In production, enter confirmation text
|
|
142
|
+
4. Confirm the action
|
|
143
|
+
|
|
144
|
+
### Redo Migrations
|
|
145
|
+
|
|
146
|
+
Redo (rollback and re-apply) a migration:
|
|
147
|
+
|
|
148
|
+
1. Click "Redo Last Migration" for the most recent migration
|
|
149
|
+
2. Or click "Redo" button next to a specific migration
|
|
150
|
+
3. In production, enter confirmation text: `EXECUTE UI_MIGRATION_REDO`
|
|
151
|
+
4. Confirm the action
|
|
152
|
+
|
|
153
|
+
### Migration Feedback
|
|
154
|
+
|
|
155
|
+
After each migration action:
|
|
156
|
+
- **Success**: Green flash message with details
|
|
157
|
+
- **Error**: Red flash message with error details
|
|
158
|
+
- **Warnings**: Yellow flash message if issues occurred
|
|
159
|
+
|
|
160
|
+
## SQL Capsules
|
|
161
|
+
|
|
162
|
+
SQL Capsules provide emergency escape hatches for executing SQL directly.
|
|
163
|
+
|
|
164
|
+
### When to Use SQL Capsules
|
|
165
|
+
|
|
166
|
+
Use SQL Capsules for:
|
|
167
|
+
- Emergency fixes in production
|
|
168
|
+
- Quick database queries
|
|
169
|
+
- Testing SQL functions
|
|
170
|
+
- Debugging trigger behavior
|
|
171
|
+
|
|
172
|
+
### Executing SQL
|
|
173
|
+
|
|
174
|
+
1. Navigate to "SQL Capsules" tab
|
|
175
|
+
2. Enter your SQL query:
|
|
176
|
+
```sql
|
|
177
|
+
SELECT * FROM pg_sql_triggers_registry;
|
|
178
|
+
```
|
|
179
|
+
3. Click "Execute"
|
|
180
|
+
4. In production, enter confirmation text: `EXECUTE SQL`
|
|
181
|
+
5. Review results in the output panel
|
|
182
|
+
|
|
183
|
+
### Safety Features
|
|
184
|
+
|
|
185
|
+
- **Production Protection**: Requires confirmation in protected environments
|
|
186
|
+
- **Read-Only Mode**: Optional configuration for limiting to SELECT queries
|
|
187
|
+
- **Query Logging**: All SQL execution is logged
|
|
188
|
+
- **Permission Checks**: Requires Admin permission level
|
|
189
|
+
|
|
190
|
+
### Example SQL Capsules
|
|
191
|
+
|
|
192
|
+
#### View All Triggers
|
|
193
|
+
```sql
|
|
194
|
+
SELECT
|
|
195
|
+
trigger_name,
|
|
196
|
+
event_object_table,
|
|
197
|
+
action_timing,
|
|
198
|
+
event_manipulation
|
|
199
|
+
FROM information_schema.triggers
|
|
200
|
+
WHERE trigger_schema = 'public';
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
#### Check Function Definitions
|
|
204
|
+
```sql
|
|
205
|
+
SELECT
|
|
206
|
+
routine_name,
|
|
207
|
+
routine_type
|
|
208
|
+
FROM information_schema.routines
|
|
209
|
+
WHERE routine_schema = 'public'
|
|
210
|
+
AND routine_name LIKE '%trigger%';
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
#### Verify Trigger State
|
|
214
|
+
```sql
|
|
215
|
+
SELECT * FROM pg_sql_triggers_registry
|
|
216
|
+
WHERE trigger_name = 'users_email_validation';
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Permissions and Safety
|
|
220
|
+
|
|
221
|
+
### Permission Levels
|
|
222
|
+
|
|
223
|
+
The Web UI enforces three permission levels:
|
|
224
|
+
|
|
225
|
+
#### Viewer (Read-Only)
|
|
226
|
+
- View triggers and their status
|
|
227
|
+
- View drift information
|
|
228
|
+
- Check migration status
|
|
229
|
+
- View SQL definitions
|
|
230
|
+
|
|
231
|
+
Cannot:
|
|
232
|
+
- Enable/disable triggers
|
|
233
|
+
- Run migrations
|
|
234
|
+
- Execute SQL
|
|
235
|
+
- Drop triggers
|
|
236
|
+
|
|
237
|
+
#### Operator
|
|
238
|
+
- All Viewer permissions
|
|
239
|
+
- Enable/disable triggers
|
|
240
|
+
- Apply generated triggers
|
|
241
|
+
- Run migrations (up/down/redo)
|
|
242
|
+
|
|
243
|
+
Cannot:
|
|
244
|
+
- Drop triggers
|
|
245
|
+
- Execute arbitrary SQL
|
|
246
|
+
|
|
247
|
+
#### Admin (Full Access)
|
|
248
|
+
- All Operator permissions
|
|
249
|
+
- Drop triggers
|
|
250
|
+
- Execute SQL via capsules
|
|
251
|
+
- Modify registry directly
|
|
252
|
+
|
|
253
|
+
### Kill Switch Protection
|
|
254
|
+
|
|
255
|
+
In protected environments (production, staging), the Web UI enforces additional safety:
|
|
256
|
+
|
|
257
|
+
1. **Status Indicator**: Kill switch badge shows protection status
|
|
258
|
+
2. **Confirmation Required**: Dangerous operations require typed confirmation
|
|
259
|
+
3. **Warning Banners**: Visual alerts for production environment
|
|
260
|
+
4. **Audit Logging**: All protected operations are logged
|
|
261
|
+
|
|
262
|
+
### Configuring Permissions
|
|
263
|
+
|
|
264
|
+
Set up custom permission checking in the initializer:
|
|
265
|
+
|
|
266
|
+
```ruby
|
|
267
|
+
# config/initializers/pg_sql_triggers.rb
|
|
268
|
+
PgSqlTriggers.configure do |config|
|
|
269
|
+
config.permission_checker = ->(actor, action, environment) {
|
|
270
|
+
user = User.find(actor[:id])
|
|
271
|
+
|
|
272
|
+
case action
|
|
273
|
+
when :view
|
|
274
|
+
user.present?
|
|
275
|
+
when :operate
|
|
276
|
+
user.admin? || user.operator?
|
|
277
|
+
when :admin
|
|
278
|
+
user.admin?
|
|
279
|
+
else
|
|
280
|
+
false
|
|
281
|
+
end
|
|
282
|
+
}
|
|
283
|
+
end
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Screenshots
|
|
287
|
+
|
|
288
|
+
### Main Dashboard
|
|
289
|
+

|
|
290
|
+
|
|
291
|
+
### Trigger Generator
|
|
292
|
+
|
|
293
|
+
The trigger generator provides a comprehensive form for creating triggers:
|
|
294
|
+
|
|
295
|
+
1. **Basic Information**: Trigger name, table name, function name, and function body
|
|
296
|
+
2. **Trigger Events**: Select timing (BEFORE/AFTER) and events (INSERT, UPDATE, DELETE, TRUNCATE)
|
|
297
|
+
3. **Configuration**: Version, environments, WHEN condition, and enabled state
|
|
298
|
+
4. **Preview**: Review generated DSL and migration code with timing and condition information
|
|
299
|
+
|
|
300
|
+
The preview page displays:
|
|
301
|
+
- Generated DSL code with timing
|
|
302
|
+
- Trigger configuration summary (timing, events, table, function, condition)
|
|
303
|
+
- PL/pgSQL function body (editable)
|
|
304
|
+
- SQL validation results
|
|
305
|
+
|
|
306
|
+

|
|
307
|
+
|
|
308
|
+
### Migration Management
|
|
309
|
+

|
|
310
|
+
|
|
311
|
+
### Kill Switch Protection
|
|
312
|
+

|
|
313
|
+
|
|
314
|
+
### SQL Capsules
|
|
315
|
+

|
|
316
|
+
|
|
317
|
+
## Tips and Best Practices
|
|
318
|
+
|
|
319
|
+
1. **Check Status Regularly**: Monitor drift detection to catch unexpected changes
|
|
320
|
+
2. **Use Confirmations**: Don't bypass production confirmations without understanding the impact
|
|
321
|
+
3. **Test in Development**: Always test UI actions in development before production
|
|
322
|
+
4. **Review Logs**: Check application logs after important operations
|
|
323
|
+
5. **Document Changes**: Add comments when making manual changes via SQL Capsules
|
|
324
|
+
|
|
325
|
+
## Troubleshooting
|
|
326
|
+
|
|
327
|
+
### UI Not Accessible
|
|
328
|
+
|
|
329
|
+
Check that:
|
|
330
|
+
1. The engine is mounted in routes
|
|
331
|
+
2. Your database migrations are up to date
|
|
332
|
+
3. The registry table exists
|
|
333
|
+
|
|
334
|
+
### Permission Denied
|
|
335
|
+
|
|
336
|
+
Verify:
|
|
337
|
+
1. Your permission checker is configured correctly
|
|
338
|
+
2. Your user has the required permission level
|
|
339
|
+
3. The kill switch isn't blocking your operation
|
|
340
|
+
|
|
341
|
+
### Migration Failures
|
|
342
|
+
|
|
343
|
+
If migrations fail:
|
|
344
|
+
1. Check the error message in the flash notification
|
|
345
|
+
2. Review the migration SQL in `db/triggers/`
|
|
346
|
+
3. Test the SQL in a console first
|
|
347
|
+
4. Check database logs for detailed error information
|
|
348
|
+
|
|
349
|
+
## Next Steps
|
|
350
|
+
|
|
351
|
+
- [Kill Switch Documentation](kill-switch.md) - Understand production safety
|
|
352
|
+
- [Configuration](configuration.md) - Customize UI behavior
|
|
353
|
+
- [API Reference](api-reference.md) - Programmatic access
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
class CreatePgSqlTriggersTables < ActiveRecord::Migration[6.
|
|
3
|
+
class CreatePgSqlTriggersTables < ActiveRecord::Migration[6.1]
|
|
4
4
|
def change
|
|
5
5
|
# Registry table - source of truth for all triggers
|
|
6
6
|
create_table :pg_sql_triggers_registry do |t|
|
|
@@ -14,6 +14,7 @@ class CreatePgSqlTriggersTables < ActiveRecord::Migration[6.0]
|
|
|
14
14
|
t.text :definition # Stored DSL or SQL definition
|
|
15
15
|
t.text :function_body # The actual function body
|
|
16
16
|
t.text :condition # Optional WHEN clause condition
|
|
17
|
+
t.string :timing, default: "before", null: false # Trigger timing: before or after
|
|
17
18
|
t.datetime :installed_at
|
|
18
19
|
t.datetime :last_verified_at
|
|
19
20
|
|
|
@@ -25,6 +26,7 @@ class CreatePgSqlTriggersTables < ActiveRecord::Migration[6.0]
|
|
|
25
26
|
add_index :pg_sql_triggers_registry, :enabled
|
|
26
27
|
add_index :pg_sql_triggers_registry, :source
|
|
27
28
|
add_index :pg_sql_triggers_registry, :environment
|
|
29
|
+
add_index :pg_sql_triggers_registry, :timing
|
|
28
30
|
|
|
29
31
|
# Trigger migrations table - tracks which trigger migrations have been run
|
|
30
32
|
create_table :trigger_migrations do |t|
|
|
@@ -1,10 +1,44 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
PgSqlTriggers.configure do |config|
|
|
4
|
-
#
|
|
5
|
-
#
|
|
4
|
+
# ========== Kill Switch Configuration ==========
|
|
5
|
+
# The Kill Switch is a safety mechanism that prevents accidental destructive operations
|
|
6
|
+
# in protected environments (production, staging, etc.)
|
|
7
|
+
|
|
8
|
+
# Enable or disable the kill switch globally
|
|
9
|
+
# Default: true (recommended for safety)
|
|
6
10
|
config.kill_switch_enabled = true
|
|
7
11
|
|
|
12
|
+
# Specify which environments should be protected by the kill switch
|
|
13
|
+
# Default: %i[production staging]
|
|
14
|
+
config.kill_switch_environments = %i[production staging]
|
|
15
|
+
|
|
16
|
+
# Require confirmation text for kill switch overrides
|
|
17
|
+
# When true, users must type a specific confirmation text to proceed
|
|
18
|
+
# Default: true (recommended for maximum safety)
|
|
19
|
+
config.kill_switch_confirmation_required = true
|
|
20
|
+
|
|
21
|
+
# Custom confirmation pattern generator
|
|
22
|
+
# Takes an operation symbol and returns the required confirmation text
|
|
23
|
+
# Default: "EXECUTE <OPERATION_NAME>"
|
|
24
|
+
config.kill_switch_confirmation_pattern = ->(operation) { "EXECUTE #{operation.to_s.upcase}" }
|
|
25
|
+
|
|
26
|
+
# Logger for kill switch events
|
|
27
|
+
# Default: Rails.logger
|
|
28
|
+
config.kill_switch_logger = Rails.logger
|
|
29
|
+
|
|
30
|
+
# Enable audit trail for kill switch events (optional enhancement)
|
|
31
|
+
# When enabled, all kill switch events are logged to a database table
|
|
32
|
+
# Default: false (can be enabled later)
|
|
33
|
+
# config.kill_switch_audit_trail_enabled = false
|
|
34
|
+
|
|
35
|
+
# Time-window auto-lock configuration (optional enhancement)
|
|
36
|
+
# Automatically enable kill switch during specific time windows
|
|
37
|
+
# Default: false
|
|
38
|
+
# config.kill_switch_auto_lock_enabled = false
|
|
39
|
+
# config.kill_switch_auto_lock_window = 30.minutes
|
|
40
|
+
# config.kill_switch_auto_lock_after = -> { Time.current.hour.between?(22, 6) } # Night hours
|
|
41
|
+
|
|
8
42
|
# Set the default environment detection
|
|
9
43
|
# By default, uses Rails.env
|
|
10
44
|
config.default_environment = -> { Rails.env }
|
|
@@ -24,4 +58,12 @@ PgSqlTriggers.configure do |config|
|
|
|
24
58
|
# Add additional tables you want to exclude:
|
|
25
59
|
# config.excluded_tables = %w[audit_logs temporary_data]
|
|
26
60
|
config.excluded_tables = []
|
|
61
|
+
|
|
62
|
+
# ========== Migration Safety Configuration ==========
|
|
63
|
+
# Prevent unsafe DROP + CREATE operations in migrations
|
|
64
|
+
# When false (default), migrations with DROP + CREATE patterns will be blocked
|
|
65
|
+
# Set to true to allow unsafe operations (not recommended)
|
|
66
|
+
# You can also override per-migration with ALLOW_UNSAFE_MIGRATIONS=true environment variable
|
|
67
|
+
# Default: false (recommended for safety)
|
|
68
|
+
config.allow_unsafe_migrations = false
|
|
27
69
|
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PgSqlTriggers
|
|
4
|
+
module Drift
|
|
5
|
+
module DbQueries
|
|
6
|
+
class << self
|
|
7
|
+
# Fetch all triggers from database
|
|
8
|
+
def all_triggers
|
|
9
|
+
sql = <<~SQL.squish
|
|
10
|
+
SELECT
|
|
11
|
+
t.oid AS trigger_oid,
|
|
12
|
+
t.tgname AS trigger_name,
|
|
13
|
+
c.relname AS table_name,
|
|
14
|
+
n.nspname AS schema_name,
|
|
15
|
+
p.proname AS function_name,
|
|
16
|
+
pg_get_triggerdef(t.oid) AS trigger_definition,
|
|
17
|
+
pg_get_functiondef(p.oid) AS function_definition,
|
|
18
|
+
t.tgenabled AS enabled,
|
|
19
|
+
t.tgisinternal AS is_internal
|
|
20
|
+
FROM pg_trigger t
|
|
21
|
+
JOIN pg_class c ON t.tgrelid = c.oid
|
|
22
|
+
JOIN pg_namespace n ON c.relnamespace = n.oid
|
|
23
|
+
JOIN pg_proc p ON t.tgfoid = p.oid
|
|
24
|
+
WHERE NOT t.tgisinternal
|
|
25
|
+
AND n.nspname = 'public'
|
|
26
|
+
AND t.tgname NOT LIKE 'RI_%'
|
|
27
|
+
ORDER BY c.relname, t.tgname;
|
|
28
|
+
SQL
|
|
29
|
+
|
|
30
|
+
execute_query(sql)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Fetch single trigger
|
|
34
|
+
def find_trigger(trigger_name)
|
|
35
|
+
sql = <<~SQL.squish
|
|
36
|
+
SELECT
|
|
37
|
+
t.oid AS trigger_oid,
|
|
38
|
+
t.tgname AS trigger_name,
|
|
39
|
+
c.relname AS table_name,
|
|
40
|
+
n.nspname AS schema_name,
|
|
41
|
+
p.proname AS function_name,
|
|
42
|
+
pg_get_triggerdef(t.oid) AS trigger_definition,
|
|
43
|
+
pg_get_functiondef(p.oid) AS function_definition,
|
|
44
|
+
t.tgenabled AS enabled,
|
|
45
|
+
t.tgisinternal AS is_internal
|
|
46
|
+
FROM pg_trigger t
|
|
47
|
+
JOIN pg_class c ON t.tgrelid = c.oid
|
|
48
|
+
JOIN pg_namespace n ON c.relnamespace = n.oid
|
|
49
|
+
JOIN pg_proc p ON t.tgfoid = p.oid
|
|
50
|
+
WHERE t.tgname = $1
|
|
51
|
+
AND NOT t.tgisinternal
|
|
52
|
+
AND n.nspname = 'public';
|
|
53
|
+
SQL
|
|
54
|
+
|
|
55
|
+
result = execute_query(sql, [trigger_name])
|
|
56
|
+
result.first
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Fetch triggers for a specific table
|
|
60
|
+
def find_triggers_for_table(table_name)
|
|
61
|
+
sql = <<~SQL.squish
|
|
62
|
+
SELECT
|
|
63
|
+
t.oid AS trigger_oid,
|
|
64
|
+
t.tgname AS trigger_name,
|
|
65
|
+
c.relname AS table_name,
|
|
66
|
+
n.nspname AS schema_name,
|
|
67
|
+
p.proname AS function_name,
|
|
68
|
+
pg_get_triggerdef(t.oid) AS trigger_definition,
|
|
69
|
+
pg_get_functiondef(p.oid) AS function_definition,
|
|
70
|
+
t.tgenabled AS enabled,
|
|
71
|
+
t.tgisinternal AS is_internal
|
|
72
|
+
FROM pg_trigger t
|
|
73
|
+
JOIN pg_class c ON t.tgrelid = c.oid
|
|
74
|
+
JOIN pg_namespace n ON c.relnamespace = n.oid
|
|
75
|
+
JOIN pg_proc p ON t.tgfoid = p.oid
|
|
76
|
+
WHERE c.relname = $1
|
|
77
|
+
AND NOT t.tgisinternal
|
|
78
|
+
AND n.nspname = 'public'
|
|
79
|
+
AND t.tgname NOT LIKE 'RI_%'
|
|
80
|
+
ORDER BY t.tgname;
|
|
81
|
+
SQL
|
|
82
|
+
|
|
83
|
+
execute_query(sql, [table_name])
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Fetch function body by function name
|
|
87
|
+
def find_function(function_name)
|
|
88
|
+
sql = <<~SQL.squish
|
|
89
|
+
SELECT
|
|
90
|
+
p.proname AS function_name,
|
|
91
|
+
pg_get_functiondef(p.oid) AS function_definition
|
|
92
|
+
FROM pg_proc p
|
|
93
|
+
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
94
|
+
WHERE p.proname = $1
|
|
95
|
+
AND n.nspname = 'public';
|
|
96
|
+
SQL
|
|
97
|
+
|
|
98
|
+
result = execute_query(sql, [function_name])
|
|
99
|
+
result.first
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
def execute_query(sql, params = [])
|
|
105
|
+
if params.any?
|
|
106
|
+
# Use ActiveRecord's connection to execute parameterized queries
|
|
107
|
+
result = ActiveRecord::Base.connection.exec_query(sql, "SQL", params)
|
|
108
|
+
result.to_a
|
|
109
|
+
else
|
|
110
|
+
ActiveRecord::Base.connection.execute(sql).to_a
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|