Diff -- /var/www/vhosts/3dshawn.com/site1/file_changes.cgi
Diff
/var/www/vhosts/3dshawn.com/site1/file_changes.cgi
added on local at 2026-07-10 22:19:06
Added
+108
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to 359054848baa
to 359054848baa
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 | use strict; | |
| 3 | use warnings; | |
| 4 | use CGI (); | |
| 5 | use MODS::Config; | |
| 6 | use MODS::DBConnect; | |
| 7 | use MODS::Template; | |
| 8 | use MODS::PageWrapper; | |
| 9 | use MODS::Charts; | |
| 10 | use POSIX (); | |
| 11 | ||
| 12 | my $cgi = CGI->new; | |
| 13 | my $db = MODS::DBConnect->new; | |
| 14 | my $tpl = MODS::Template->new; | |
| 15 | ||
| 16 | $|=1; print "Content-Type: text/html; charset=utf-8\nCache-Control: no-cache\n\n"; | |
| 17 | ||
| 18 | my $dbh = $db->db_connect or die "DB connect failed\n"; | |
| 19 | ||
| 20 | my $days = int($cgi->param('days') || 7); $days = 1 if $days < 1; $days = 365 if $days > 365; | |
| 21 | my $q = $cgi->param('q') || ''; | |
| 22 | my $server_filter = int($cgi->param('server') || 0); | |
| 23 | ||
| 24 | my $where = "date_time >= DATE_SUB(NOW(), INTERVAL $days DAY)"; | |
| 25 | $where .= " AND file_name LIKE " . $dbh->quote('%' . $q . '%') if length $q; | |
| 26 | $where .= " AND server_id = $server_filter" if $server_filter; | |
| 27 | ||
| 28 | my @rows = $db->db_readwrite_multiple($dbh, qq~ | |
| 29 | SELECT fc.file_changes_id AS id, fc.file_name, fc.status, fc.date_time, | |
| 30 | fc.blob_sha, fc.diff_blob_sha, fc.is_ts_only, fc.uid, fc.gid, fc.permissions, | |
| 31 | s.server_name | |
| 32 | FROM FILE_CHANGES fc | |
| 33 | LEFT JOIN SERVERS s ON s.server_id = fc.server_id | |
| 34 | WHERE $where | |
| 35 | ORDER BY fc.date_time DESC | |
| 36 | LIMIT 200 | |
| 37 | ~, __FILE__, __LINE__); | |
| 38 | # --- Per-file 30-day sparkline data. One query, then bucket in Perl. --- | |
| 39 | my %spark; # file_name => arrayref [30 daily counts, oldest -> newest] | |
| 40 | { | |
| 41 | my @dedupe_files; | |
| 42 | my %seen; | |
| 43 | foreach my $r (@rows) { | |
| 44 | next unless defined $r->{file_name}; | |
| 45 | next if $seen{$r->{file_name}}++; | |
| 46 | push @dedupe_files, $r->{file_name}; | |
| 47 | } | |
| 48 | if (@dedupe_files) { | |
| 49 | my @quoted = map { $dbh->quote($_) } @dedupe_files; | |
| 50 | my $in_list = join(',', @quoted); | |
| 51 | my @day_rows = $db->db_readwrite_multiple($dbh, qq~ | |
| 52 | SELECT file_name, | |
| 53 | DATE_FORMAT(date_time, '%Y-%m-%d') AS d, | |
| 54 | COUNT(*) AS n | |
| 55 | FROM FILE_CHANGES | |
| 56 | WHERE file_name IN ($in_list) | |
| 57 | AND date_time >= DATE_SUB(NOW(), INTERVAL 30 DAY) | |
| 58 | GROUP BY file_name, d | |
| 59 | ~, __FILE__, __LINE__); | |
| 60 | my %by_file; | |
| 61 | foreach my $dr (@day_rows) { | |
| 62 | $by_file{$dr->{file_name}}{$dr->{d}} = $dr->{n}; | |
| 63 | } | |
| 64 | my $now = time; | |
| 65 | my @day_keys; | |
| 66 | for (my $i = 29; $i >= 0; $i--) { | |
| 67 | push @day_keys, POSIX::strftime('%Y-%m-%d', localtime($now - $i * 86400)); | |
| 68 | } | |
| 69 | foreach my $fname (@dedupe_files) { | |
| 70 | my @series; | |
| 71 | for my $k (@day_keys) { | |
| 72 | push @series, ($by_file{$fname}{$k} || 0); | |
| 73 | } | |
| 74 | $spark{$fname} = \@series; | |
| 75 | } | |
| 76 | } | |
| 77 | } | |
| 78 | ||
| 79 | foreach my $r (@rows) { | |
| 80 | $r->{status_pill} = $r->{status} && $r->{status} eq 'added' ? 'pill-ok' | |
| 81 | : $r->{status} && $r->{status} eq 'modified' ? 'pill-warn' | |
| 82 | : $r->{status} && $r->{status} eq 'deleted' ? 'pill-bad' | |
| 83 | : 'pill-mute'; | |
| 84 | $r->{status} //= 'unknown'; | |
| 85 | $r->{file_name_h} = $r->{file_name} // ''; | |
| 86 | $r->{file_name_h} = length($r->{file_name_h}) > 90 ? '...' . substr($r->{file_name_h}, -87) : $r->{file_name_h}; | |
| 87 | $r->{blob_short} = $r->{blob_sha} ? substr($r->{blob_sha}, 0, 8) : '-'; | |
| 88 | $r->{diff_url} = "/diff.cgi?kind=file&id=$r->{id}"; | |
| 89 | my $series = $spark{$r->{file_name} // ''}; | |
| 90 | $r->{sparkline_svg} = $series | |
| 91 | ? MODS::Charts::sparkline(values => $series, width => 88, height => 20, color => '#14b8a6') | |
| 92 | : ''; | |
| 93 | } | |
| 94 | my $total = scalar @rows; | |
| 95 | ||
| 96 | $db->db_disconnect($dbh); | |
| 97 | ||
| 98 | my $tvars = { | |
| 99 | rows => \@rows, has_rows => $total ? 1 : 0, total => $total, | |
| 100 | days => $days, q => $q, | |
| 101 | }; | |
| 102 | my @body = $tpl->template('file_changes.html', $tvars); | |
| 103 | ||
| 104 | MODS::PageWrapper->new->wrapper( | |
| 105 | page_title => 'File changes', page_key => 'file_changes', | |
| 106 | body_html => join('', @body), | |
| 107 | userinfo => { display_name => 'Operator' }, | |
| 108 | ); |