Diff -- /var/www/vhosts/3dshawn.com/admin.3dshawn.com/index.cgi
Diff
/var/www/vhosts/3dshawn.com/admin.3dshawn.com/index.cgi
added on local at 2026-06-22 00:51:51
Added
+0
lines
Removed
-0
lines
Context
152
unchanged
Blobs
from 14c0f9c86cb5
to 14c0f9c86cb5
to 14c0f9c86cb5
Unified diff
Naive LCS-based line diff. Additions in emerald, deletions in rose. Both sides decompressed live from BLOB_STORE.| 1 | 1 | #!/usr/bin/perl |
| 2 | 2 | #====================================================================== |
| 3 | 3 | # Meta-Admin -- /index.cgi: portfolio overview placeholder. |
| 4 | 4 | # |
| 5 | 5 | # Chunk A renders the chrome + a list of every site_connections row so |
| 6 | 6 | # the operator console is verifiably wired. Chunks B/C/D wire the KPI |
| 7 | 7 | # tiles to live aggregate queries. |
| 8 | 8 | #====================================================================== |
| 9 | 9 | use strict; |
| 10 | 10 | use warnings; |
| 11 | 11 | use lib '/var/www/vhosts/3dshawn.com/admin.3dshawn.com'; |
| 12 | 12 | use MODS::Login; |
| 13 | 13 | use MODS::DBConnect; |
| 14 | 14 | use MODS::MetaAdmin::Config; |
| 15 | 15 | use MODS::MetaAdmin::Wrapper; |
| 16 | 16 | |
| 17 | 17 | my $auth = MODS::Login->new; |
| 18 | 18 | my $cfg = MODS::MetaAdmin::Config->new; |
| 19 | 19 | my $wrap = MODS::MetaAdmin::Wrapper->new; |
| 20 | 20 | |
| 21 | 21 | my $userinfo = $auth->login_verify; |
| 22 | 22 | unless ($userinfo) { |
| 23 | 23 | print "Status: 302 Found\nLocation: /login.cgi\n\n"; exit; |
| 24 | 24 | } |
| 25 | 25 | |
| 26 | 26 | my $db = MODS::DBConnect->new; |
| 27 | 27 | my $dbh = $db->db_connect(); |
| 28 | 28 | my @sites = $db->db_readwrite_multiple($dbh, q~ |
| 29 | 29 | SELECT slug, display_name, display_url, brand_color, db_name, vhost_path, is_active, |
| 30 | 30 | last_test_at, last_test_result |
| 31 | 31 | FROM site_connections |
| 32 | 32 | ORDER BY sort_order, id |
| 33 | 33 | ~, __FILE__, __LINE__); |
| 34 | 34 | $db->db_disconnect($dbh); |
| 35 | 35 | |
| 36 | 36 | my $n_sites = scalar @sites; |
| 37 | 37 | my $n_active = scalar grep { $_->{is_active} } @sites; |
| 38 | 38 | |
| 39 | 39 | sub _esc { |
| 40 | 40 | my $s = shift; $s //= ''; |
| 41 | 41 | $s =~ s/&/&/g; $s =~ s/</</g; $s =~ s/>/>/g; |
| 42 | 42 | $s =~ s/"/"/g; |
| 43 | 43 | return $s; |
| 44 | 44 | } |
| 45 | 45 | |
| 46 | 46 | sub _brand_letters { |
| 47 | 47 | my ($s) = @_; |
| 48 | 48 | my $slug = $s->{slug} || ''; |
| 49 | 49 | my %map = ( |
| 50 | 50 | webstls => 'WS', |
| 51 | 51 | ptmatrix => 'PT', |
| 52 | 52 | affsoft => 'AS', |
| 53 | 53 | shopcart => 'SC', |
| 54 | 54 | repricer => 'RP', |
| 55 | 55 | abforge => 'AB', |
| 56 | 56 | contactforge => 'CF', |
| 57 | 57 | ); |
| 58 | 58 | return $map{$slug} || uc(substr($s->{display_name} || '?', 0, 1)); |
| 59 | 59 | } |
| 60 | 60 | my $body = ''; |
| 61 | 61 | |
| 62 | 62 | # ---- Page head ---- |
| 63 | 63 | $body .= q~ |
| 64 | 64 | <div class="page-head"> |
| 65 | 65 | <div class="left"> |
| 66 | 66 | <span class="page-eyebrow"><span class="dot"></span> Overview</span> |
| 67 | 67 | <h1 class="page-title">All your sites, one screen.</h1> |
| 68 | 68 | <p class="page-subtitle">Read-only roll-up across the portfolio. Configure each connection in <a href="/sites.cgi">Sites</a>; aggregates come online in the next chunk.</p> |
| 69 | 69 | </div> |
| 70 | 70 | </div> |
| 71 | 71 | ~; |
| 72 | 72 | |
| 73 | 73 | # ---- KPI tiles ---- |
| 74 | 74 | my $inactive = $n_sites - $n_active; |
| 75 | 75 | $body .= qq~ |
| 76 | 76 | <div class="dash-grid"> |
| 77 | 77 | <div class="module glow-cyan"> |
| 78 | 78 | <div class="module-head"><div class="left"><div class="module-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/></svg></div><div class="module-title">Sites configured</div></div></div> |
| 79 | 79 | <div class="module-body kpi kpi-cyan"> |
| 80 | 80 | <div class="num">$n_sites</div> |
| 81 | 81 | <div class="lbl">in portfolio</div> |
| 82 | 82 | <div class="sub">$n_active active · $inactive inactive</div> |
| 83 | 83 | </div> |
| 84 | 84 | </div> |
| 85 | 85 | <div class="module glow-magenta"> |
| 86 | 86 | <div class="module-head"><div class="left"><div class="module-icon" style="color:var(--neon-magenta);background:rgba(255,16,240,0.08);border-color:rgba(255,16,240,0.25)"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7H14a3.5 3.5 0 0 1 0 7H6"/></svg></div><div class="module-title">MTD revenue</div></div></div> |
| 87 | 87 | <div class="module-body kpi kpi-magenta"> |
| 88 | 88 | <div class="num">--</div> |
| 89 | 89 | <div class="lbl">across all sites</div> |
| 90 | 90 | <div class="sub">Wired in chunk C</div> |
| 91 | 91 | </div> |
| 92 | 92 | </div> |
| 93 | 93 | <div class="module glow-lime"> |
| 94 | 94 | <div class="module-head"><div class="left"><div class="module-icon" style="color:var(--neon-lime);background:rgba(170,255,0,0.08);border-color:rgba(170,255,0,0.25)"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg></div><div class="module-title">Open support</div></div></div> |
| 95 | 95 | <div class="module-body kpi kpi-lime"> |
| 96 | 96 | <div class="num">--</div> |
| 97 | 97 | <div class="lbl">unread threads</div> |
| 98 | 98 | <div class="sub">Wired in chunk C</div> |
| 99 | 99 | </div> |
| 100 | 100 | </div> |
| 101 | 101 | <div class="module glow-violet"> |
| 102 | 102 | <div class="module-head"><div class="left"><div class="module-icon" style="color:var(--neon-violet);background:rgba(179,0,255,0.08);border-color:rgba(179,0,255,0.25)"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="2" width="20" height="8" rx="2"/><rect x="2" y="14" width="20" height="8" rx="2"/></svg></div><div class="module-title">Disk used</div></div></div> |
| 103 | 103 | <div class="module-body kpi kpi-violet"> |
| 104 | 104 | <div class="num">--</div> |
| 105 | 105 | <div class="lbl">on this server</div> |
| 106 | 106 | <div class="sub">Wired in chunk D</div> |
| 107 | 107 | </div> |
| 108 | 108 | </div> |
| 109 | 109 | </div> |
| 110 | 110 | ~; |
| 111 | 111 | |
| 112 | 112 | # ---- Sites table ---- |
| 113 | 113 | $body .= q~ |
| 114 | 114 | <div class="module"> |
| 115 | 115 | <div class="module-head"><div class="left"><div class="module-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 12L12 3l9 9"/><path d="M5 10v10h14V10"/></svg></div><div class="module-title">Sites in this portfolio</div></div></div> |
| 116 | 116 | <div class="module-body" style="padding:0"> |
| 117 | 117 | <table class="tbl"> |
| 118 | 118 | <thead><tr><th></th><th>Site</th><th>URL</th><th>DB</th><th>Vhost path</th><th>Status</th></tr></thead> |
| 119 | 119 | <tbody> |
| 120 | 120 | ~; |
| 121 | 121 | foreach my $s (@sites) { |
| 122 | 122 | my $url = _esc($s->{display_url}); |
| 123 | 123 | my $name = _esc($s->{display_name}); |
| 124 | 124 | my $dbn = _esc($s->{db_name}); |
| 125 | 125 | my $vp = _esc($s->{vhost_path} || '-'); |
| 126 | 126 | my $color = _esc($s->{brand_color} || '#807aa8'); |
| 127 | 127 | my $ltr = _brand_letters($s); |
| 128 | 128 | my $pill = $s->{is_active} |
| 129 | 129 | ? '<span class="pill pill-ok">Active</span>' |
| 130 | 130 | : '<span class="pill pill-bad">Off</span>'; |
| 131 | 131 | $body .= qq~<tr> |
| 132 | 132 | <td><span class="brand-icon" style="--site-glow:$color" data-letter="$ltr"></span></td> |
| 133 | 133 | <td><strong>$name</strong></td> |
| 134 | 134 | <td><a href="$url" target="_blank">$url</a></td> |
| 135 | 135 | <td class="mono dim">$dbn</td> |
| 136 | 136 | <td class="mono dim" style="font-size:11px">$vp</td> |
| 137 | 137 | <td>$pill</td> |
| 138 | 138 | </tr>~; |
| 139 | 139 | } |
| 140 | 140 | $body .= q~ |
| 141 | 141 | </tbody> |
| 142 | 142 | </table> |
| 143 | 143 | </div> |
| 144 | 144 | </div> |
| 145 | 145 | ~; |
| 146 | 146 | |
| 147 | 147 | $wrap->render( |
| 148 | 148 | title => 'Overview', |
| 149 | 149 | page_key => 'home', |
| 150 | 150 | body => $body, |
| 151 | 151 | userinfo => $userinfo, |
| 152 | 152 | ); |