modified on local at 2026-07-12 23:13:00
| 1 | 1 | #!/usr/bin/perl |
| 2 | 2 | #====================================================================== |
| 3 | 3 | # DriftSense -- /logout.cgi |
| 4 | 4 | # |
| 5 | 5 | # Ends the operator session. DriftSense doesn't have a full auth flow |
| 6 | 6 | # yet (single-operator install, browser is scoped by the vhost's basic- |
| 7 | 7 | # auth or dev-mode "Operator" default), so this is intentionally minimal: |
| 8 | 8 | # - clears any DriftSense cookies |
| 9 | 9 | # - shows a "signed out" landing with a link back to /dashboard.cgi |
| 10 | 10 | # |
| 11 | 11 | # When we wire real sessions (WATCHTOWER_SESSIONS table), invalidate the |
| 12 | 12 | # session_id here before rendering. |
| 13 | 13 | #====================================================================== |
| 14 | 14 | use strict; |
| 15 | 15 | use warnings; |
| 16 | 16 | use CGI (); |
| 17 | 17 | use MODS::Config; use MODS::Template; use MODS::PageWrapper; |
| 18 | 18 | |
| 19 | 19 | my $cgi = CGI->new; |
| 20 | 20 | my $tpl = MODS::Template->new; |
| 21 | 21 | $|=1; |
| 22 | 22 | |
| 23 | 23 | # Clear the (currently-notional) DriftSense cookies. Belt-and-suspenders -- |
| 24 | 24 | # name each one we've ever set; missing ones are no-ops on the browser side. |
| 25 | 25 | print "Content-Type: text/html; charset=utf-8\n"; |
| 26 | 26 | print "Cache-Control: no-store, no-cache, must-revalidate, max-age=0\n"; |
| 27 | 27 | print "Pragma: no-cache\nExpires: 0\n"; |
| 28 | 28 | foreach my $c (qw(ds_session ds_op_id ds_op_name)) { |
| 29 | 29 | print "Set-Cookie: $c=; Path=/; Max-Age=0; HttpOnly; SameSite=Lax\n"; |
| 30 | 30 | } |
| 31 | 31 | print "\n"; |
| 32 | 32 | |
| 33 | 33 | my $body = <<'BODY'; |
| 34 | 34 | <div style="max-width:520px;margin:8vh auto;text-align:center"> |
| 35 | 35 | <div style="width:64px;height:64px;margin:0 auto 20px;border-radius:16px;background:linear-gradient(135deg,var(--accent),var(--accent-deep));display:grid;place-items:center;box-shadow:0 8px 30px var(--accent-glow)"> |
| 36 | 36 | <svg viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="#030710" stroke-width="2.2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg> |
| 37 | 37 | </div> |
| 38 | 38 | <h1 style="font-size:22px;font-weight:700;margin:0 0 8px;letter-spacing:-.02em">You're signed out.</h1> |
| 39 | 39 | <p style="color:var(--text-dim);font-size:14px;margin:0 0 24px">Session ended cleanly. All DriftSense cookies cleared from this browser.</p> |
| 40 | 40 | <div style="display:flex;gap:10px;justify-content:center"> |
| 41 | 41 | <a href="/dashboard.cgi" class="tr-chip is-active" style="border:none;padding:9px 22px">Sign back in</a> |
| 42 | 42 | <a href="/" class="tr-chip">Watchtower home</a> |
| 43 | 43 | </div> |
| 44 | 44 | <p class="dim" style="font-size:11.5px;margin-top:36px"> |
| 45 | 45 | DriftSense wave 6+ uses a shared operator seat — wave 7+ will introduce per-user sessions with real auth. |
| 46 | 46 | </p> |
| 47 | 47 | </div> |
| 48 | 48 | BODY |
| 49 | 49 | |
| 50 | 50 | my @body = $tpl->template('page_wrapper.html', { |
| 51 | 51 | page_title => 'Signed out', |
| 52 | 52 | brand_name => 'DriftSense', |
| 53 | 53 | brand_tagline => 'Silent safety net for code + schema drift', |
| 54 | 54 | asset_version => time(), |
| 55 | 55 | page_body => $body, |
| 56 | 56 | user_name => 'Operator', |
| 57 | 57 | user_email => '', |
| 58 | 58 | user_initials => '?', |
| 59 | 59 | sites => [], has_sites => 0, sites_ct => 0, |
| 60 | 60 | stable_releases => [], has_stables => 0, stables_ct => 0, |
| 61 | 61 | (map { ("active_$_" => 0) } qw(dashboard file_changes schema_changes table_locks |
| 62 | 62 | named_releases alerts export purge_log sites databases |
| 63 | 63 | file_monitors servers containers settings search |
| 64 | 64 | portfolio_drift)), |
| 65 | 65 | }); |
| 66 | 66 | print @body; |
| 67 | 67 | exit; |