Diff -- /var/www/vhosts/3dshawn.com/site1/reset.cgi
Diff

/var/www/vhosts/3dshawn.com/site1/reset.cgi

added on local at 2026-07-13 16:57:45

Added
+163
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to 47438119a79d
Restore this content →
Unified diff
Naive LCS-based line diff. Additions in emerald, deletions in rose. Both sides decompressed live from BLOB_STORE.
1#!/usr/bin/perl
2#======================================================================
3# DriftSense -- /reset.cgi
4#
5# Admin-only "start fresh" flow. Wipes ALL captured data + logs while
6# preserving all config (monitors / servers / alert rules / watchlist /
7# scan globals). After the wipe, kicks the file + agent + schema
8# scanners so a new baseline is captured immediately.
9#
10# Safety:
11# * GET /reset.cgi -> preview page (what will be wiped)
12# * POST /reset.cgi confirm=WIPE -> perform the wipe
13# * Automatic mysqldump backup to site_backups/DriftSense_db_<stamp>-reset.sql.gz
14# before ANY row is removed.
15#======================================================================
16use strict;
17use warnings;
18use CGI ();
19use POSIX ();
20use MODS::Config; use MODS::DBConnect; use MODS::Template; use MODS::PageWrapper;
21
22my $cgi = CGI->new;
23my $db = MODS::DBConnect->new;
24my $tpl = MODS::Template->new;
25$|=1;
26
27my $dbh = $db->db_connect or die "DB connect failed\n";
28
29my $method = $ENV{REQUEST_METHOD} || 'GET';
30my $confirm = $cgi->param('confirm') // '';
31
32# Tables to WIPE (all captured data + audit logs)
33my @WIPE_TABLES = qw(
34 FILE_CHANGES BLOB_STORE SCHEMA_CHANGE CONFIG_DRIFT_BASELINES
35 TABLE_DATA_CHANGES LOCKED_TABLE_LOGS FAILED_SCAN_LOGS AGENT_UPLOADS
36 NAMED_RELEASES NAMED_RELEASE_PINS NAMED_RELEASE_SCOPES PIN_DRIFT_LOG
37 ALERT_CURSOR ALERT_DELIVERIES BLOB_INTEGRITY_LOG PURGE_LOG
38 RESTORE_LOG STABLE_PROMOTE_LOG
39);
40
41# Tables to KEEP (config)
42my @KEEP_TABLES = qw(
43 FILE_MONITOR_SETTINGS SERVERS ALERT_RULES DATABASE_MONITOR_SETTINGS
44 CONTAINER_TARGETS SCAN_GLOBALS MONITOR_SETTING WATCH_LIST TABLE_INFO
45);
46
47# ---- POST: perform the wipe ----------------------------------------
48if ($method eq 'POST' && $confirm eq 'WIPE') {
49 my $stamp = POSIX::strftime('%Y%m%d-%H%M%S', localtime);
50 my $result = { steps => [] };
51
52 # Row counts BEFORE wipe
53 my %before;
54 foreach my $t (@WIPE_TABLES) {
55 my $r = $db->db_readwrite($dbh, "SELECT COUNT(*) AS n FROM $t", __FILE__, __LINE__);
56 $before{$t} = ($r && $r->{n}) || 0;
57 }
58 my $total_before = 0; $total_before += $_ for values %before;
59 push @{$result->{steps}}, "Counted rows across " . scalar(@WIPE_TABLES) . " tables: $total_before to wipe";
60
61 # 1. Auto-backup via mysqldump. Password via MYSQL_PWD env var so the
62 # cmdline doesn't leak it into ps output AND so we don't have to shell-
63 # escape it (an earlier attempt using quotemeta() -- which is REGEX
64 # escape not shell escape -- silently broke every backup with a
65 # special char in the password).
66 my $backup_path = "/var/www/vhosts/3dshawn.com/site1/site_backups/DriftSense_db_${stamp}-reset.sql.gz";
67 my $cfg = MODS::Config->new;
68 my $dbn = $cfg->settings('db_name') || 'drift_sense';
69 my $dbu = $cfg->settings('db_user') || 'drift_sense';
70 my $dbp = $cfg->settings('db_password') || '';
71 # Locate mysqldump (PATH under Apache CGI is minimal)
72 my $md = -x '/usr/bin/mysqldump' ? '/usr/bin/mysqldump'
73 : -x '/usr/local/bin/mysqldump' ? '/usr/local/bin/mysqldump'
74 : 'mysqldump';
75 local $ENV{MYSQL_PWD} = $dbp;
76 my $safe_dbn = $dbn; $safe_dbn =~ s/[^A-Za-z0-9_]//g;
77 my $safe_dbu = $dbu; $safe_dbu =~ s/[^A-Za-z0-9_]//g;
78 my $safe_bak = $backup_path; $safe_bak =~ s/'/'\\''/g;
79 my $cmd = "$md -u '$safe_dbu' --single-transaction --routines --triggers --add-drop-table '$safe_dbn' 2>/tmp/ds_reset_dump.err | gzip -c > '$safe_bak'";
80 my $rc = system('/bin/bash', '-c', $cmd);
81 if ($rc == 0 && -s $backup_path) {
82 my $sz = int((-s $backup_path) / 1024);
83 push @{$result->{steps}}, "Backup written: $backup_path (${sz} KB)";
84 $result->{backup_path} = $backup_path;
85 } else {
86 my $err = '';
87 if (open(my $eh, '<', '/tmp/ds_reset_dump.err')) { local $/; $err = <$eh>; close $eh; }
88 $err =~ s/\s+/ /g; $err = substr($err, 0, 200);
89 push @{$result->{steps}}, "WARNING: backup command failed (rc=$rc; err=$err). Proceeding anyway per operator's confirmation.";
90 }
91 unlink '/tmp/ds_reset_dump.err';
92
93 # 2. TRUNCATE all captured-data + log tables
94 $db->db_readwrite($dbh, "SET FOREIGN_KEY_CHECKS = 0", __FILE__, __LINE__);
95 foreach my $t (@WIPE_TABLES) {
96 eval { $db->db_readwrite($dbh, "TRUNCATE TABLE $t", __FILE__, __LINE__) };
97 }
98 $db->db_readwrite($dbh, "SET FOREIGN_KEY_CHECKS = 1", __FILE__, __LINE__);
99 push @{$result->{steps}}, "Truncated " . scalar(@WIPE_TABLES) . " data + log tables";
100
101 # 3. Kick fresh scans for all active monitors + schema. Scanners
102 # detect empty %prev per monitor -> baseline_mode=1 -> captures
103 # flagged is_baseline=1 -> dashboard hides them. That's the whole
104 # point of "start fresh".
105 my $dir = '/var/www/vhosts/3dshawn.com/site1';
106 foreach my $s ('_file_scan.pl', '_agent_scan.pl', '_schema_scan.pl') {
107 next unless -f "$dir/$s";
108 my $spawn = "setsid /usr/bin/nohup /usr/bin/perl -I$dir $dir/$s "
109 . "</dev/null >/dev/null 2>&1 &";
110 system($spawn);
111 }
112 push @{$result->{steps}}, "Kicked fresh scans: _file_scan.pl + _agent_scan.pl + _schema_scan.pl";
113
114 $db->db_disconnect($dbh);
115
116 print "Content-Type: text/html; charset=utf-8\nCache-Control: no-cache\n\n";
117 my $tvars = {
118 did_reset => 1,
119 steps => [ map { { text => $_ } } @{$result->{steps}} ],
120 total_wiped => $total_before,
121 backup_path => $result->{backup_path} // '',
122 has_backup => $result->{backup_path} ? 1 : 0,
123 };
124 my @body = $tpl->template('reset.html', $tvars);
125 MODS::PageWrapper->new->wrapper(
126 page_title => 'Reset', page_key => 'reset',
127 body_html => join('', @body), userinfo => { display_name => 'Operator' },
128 );
129 exit;
130}
131
132# ---- GET: preview page ---------------------------------------------
133print "Content-Type: text/html; charset=utf-8\nCache-Control: no-cache\n\n";
134
135# Current row counts for the preview
136my %counts;
137my $grand_total = 0;
138foreach my $t (@WIPE_TABLES) {
139 my $r = $db->db_readwrite($dbh, "SELECT COUNT(*) AS n FROM $t", __FILE__, __LINE__);
140 my $n = ($r && $r->{n}) || 0;
141 $counts{$t} = $n;
142 $grand_total += $n;
143}
144
145my @wipe_rows = map { { name => $_, count => $counts{$_} } }
146 sort { $counts{$b} <=> $counts{$a} || $a cmp $b } @WIPE_TABLES;
147my @keep_rows = map { { name => $_ } } sort @KEEP_TABLES;
148
149$db->db_disconnect($dbh);
150
151my $tvars = {
152 did_reset => 0,
153 wipe_tables => \@wipe_rows,
154 keep_tables => \@keep_rows,
155 grand_total => $grand_total,
156 wipe_ct => scalar(@WIPE_TABLES),
157 keep_ct => scalar(@KEEP_TABLES),
158};
159my @body = $tpl->template('reset.html', $tvars);
160MODS::PageWrapper->new->wrapper(
161 page_title => 'Reset', page_key => 'reset',
162 body_html => join('', @body), userinfo => { display_name => 'Operator' },
163);
Keyboard: j next diff k previous diff g top G bottom r restore c compile-check ? help