Restore
Restore
Restore file to captured state
Every captured change carries its SHA-256-addressed content in BLOB_STORE. Restoring writes that content back to the original path — current file gets backed up to <path>.drift_restore_backup_<epoch> before overwrite.
What will change
Preview -- nothing has been written yet.
| Target file | /var/www/vhosts/3dshawn.com/site1/file_monitors.cgi |
| Site | DriftSense self-monitor on local |
| Kind | local |
| Captured at | 2026-07-10 18:57:29 |
| Captured SHA | f0a44a1113a6c6e97a9dbc36220a70852c3cdb19166afd64447abd1cbc9234ce |
Current state on disk
Live check just now. If SHAs match, the restore is a no-op.
| File present | yes |
| Size | 2883 bytes |
| Current SHA | 6db9f3b7ebe4 |
| Same as captured? | no -- restore will change it |
What restore will change — live diff
Left column shows current on-disk content; right shows what restore will write. +3 additions, -20 deletions, 37 unchanged context lines.
| 1 | 1 | #!/usr/bin/perl |
| 2 | 2 | use strict; use warnings; use CGI (); |
| 3 | 3 | use MODS::Config; use MODS::DBConnect; use MODS::Template; use MODS::PageWrapper; |
| 4 | 4 | my $cgi = CGI->new; my $db = MODS::DBConnect->new; my $tpl = MODS::Template->new; |
| 5 | 5 | $|=1; |
| 6 | 6 | my $dbh = $db->db_connect or die "DB connect failed\n"; |
| 7 | 7 | |
| 8 | 8 | if (($ENV{REQUEST_METHOD} || '') eq 'POST' && $cgi->param('save')) { |
| 9 | 9 | my $q_name = $dbh->quote($cgi->param('scan_name') || ''); |
| 10 | 10 | my $q_path = $dbh->quote($cgi->param('scan_path') || ''); |
| 11 | 11 | my $q_ig = $dbh->quote($cgi->param('ignore_list') || '.git/,node_modules/,*.log,*.tmp,.DS_Store'); |
| 12 | 12 | my $q_ft = $dbh->quote($cgi->param('file_type_filter') || ''); |
| 13 | 13 | my $status = $cgi->param('status') ? 1 : 0; |
| 14 | my $ret = int($cgi->param('retention_days') || 0); | |
| 15 | my $ret_expr = $ret > 0 ? $ret : 'NULL'; | |
| 16 | 14 | $db->db_readwrite($dbh, qq~ |
| 17 | 15 | INSERT INTO FILE_MONITOR_SETTINGS |
| 18 | (scan_name, scan_path, ignore_list, file_type_filter, status, server_id, retention_days) | |
| 19 | VALUES ($q_name, $q_path, $q_ig, $q_ft, $status, 1, $ret_expr) | |
| 16 | (scan_name, scan_path, ignore_list, file_type_filter, status, server_id) | |
| 17 | VALUES ($q_name, $q_path, $q_ig, $q_ft, $status, 1) | |
| 20 | 18 | ~, __FILE__, __LINE__); |
| 21 | 19 | print "Status: 302 Found\nLocation: /file_monitors.cgi?saved=1\n\n"; exit; |
| 22 | } | |
| 23 | ||
| 24 | # POST-update: change per-monitor retention_days | |
| 25 | if (($ENV{REQUEST_METHOD} || '') eq 'POST' && $cgi->param('update_retention')) { | |
| 26 | my $mid = int($cgi->param('mid') || 0); | |
| 27 | my $ret = int($cgi->param('retention_days') || 0); | |
| 28 | my $ret_expr = $ret > 0 ? $ret : 'NULL'; | |
| 29 | $db->db_readwrite($dbh, | |
| 30 | "UPDATE FILE_MONITOR_SETTINGS SET retention_days = $ret_expr WHERE file_monitor_list_id = $mid", | |
| 31 | __FILE__, __LINE__); | |
| 32 | print "Status: 302 Found\nLocation: /file_monitors.cgi?updated=1\n\n"; exit; | |
| 33 | 20 | } |
| 34 | 21 | |
| 35 | 22 | print "Content-Type: text/html; charset=utf-8\nCache-Control: no-cache\n\n"; |
| 36 | 23 | my @rows = $db->db_readwrite_multiple($dbh, q~ |
| 37 | 24 | SELECT file_monitor_list_id AS id, scan_name, scan_path, ignore_list, file_type_filter, |
| 38 | status, retention_days, | |
| 39 | DATE_FORMAT(last_scanned, '%b %d %H:%i') AS last_h | |
| 25 | status, DATE_FORMAT(last_scanned, '%b %d %H:%i') AS last_h | |
| 40 | 26 | FROM FILE_MONITOR_SETTINGS ORDER BY file_monitor_list_id ASC |
| 41 | 27 | ~, __FILE__, __LINE__); |
| 42 | 28 | foreach my $r (@rows) { |
| 43 | 29 | $r->{status_pill} = $r->{status} ? 'pill-ok' : 'pill-mute'; |
| 44 | 30 | $r->{status_lbl} = $r->{status} ? 'ACTIVE' : 'DISABLED'; |
| 45 | 31 | $r->{ignore_preview} = $r->{ignore_list} ? substr($r->{ignore_list}, 0, 50) . (length($r->{ignore_list}) > 50 ? '...' : '') : ''; |
| 46 | $r->{retention_h} = defined($r->{retention_days}) && $r->{retention_days} > 0 | |
| 47 | ? "$r->{retention_days} d" : '(global)'; | |
| 48 | $r->{retention_days} //= 0; | |
| 49 | 32 | } |
| 50 | 33 | $db->db_disconnect($dbh); |
| 51 | 34 | |
| 52 | 35 | my @body = $tpl->template('file_monitors.html', |
| 53 | 36 | { rows => \@rows, has_rows => scalar(@rows) ? 1 : 0, total => scalar @rows }); |
| 54 | 37 | MODS::PageWrapper->new->wrapper( |
| 55 | 38 | page_title => 'File monitors', page_key => 'file_monitors', |
| 56 | 39 | body_html => join('', @body), userinfo => { display_name => 'Operator' }, |
| 57 | 40 | ); |
Confirm restore
Backs current content to <target>.drift_restore_backup_<epoch>, writes the captured content, verifies SHA post-write.