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