Diff -- /var/www/vhosts/3dshawn.com/admin.3dshawn.com/sessions.cgi
Diff

/var/www/vhosts/3dshawn.com/admin.3dshawn.com/sessions.cgi

added on local at 2026-07-01 14:22:45

Added
+208
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to 5215d3470358
Restore this content →
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#======================================================================
3# Meta-Admin -- /sessions.cgi
4#
5# Per-user session_minutes_override editor across the portfolio.
6# Each site row lets the operator view + set the override for users
7# on that site without having to log into the site's own admin.
8#
9# Read path = MODS::MetaAdmin::SiteConn->query_many (SELECT only).
10# Write path = MODS::MetaAdmin::SiteConn->set_session_override
11# (carved-out UPDATE on one column).
12#======================================================================
13use strict;
14use warnings;
15use lib '/var/www/vhosts/3dshawn.com/admin.3dshawn.com';
16use CGI;
17use MODS::Login;
18use MODS::MetaAdmin::Config;
19use MODS::MetaAdmin::Wrapper;
20use MODS::MetaAdmin::SiteConn;
21
22my $q = CGI->new;
23my $form = $q->Vars;
24my $auth = MODS::Login->new;
25my $u = $auth->login_verify;
26
27my $entry = ($ENV{SCRIPT_NAME} || '') =~ m{/([^/]+)\.cgi$} ? $1 : 'sessions';
28
29if ($entry eq 'sessions_action') {
30 unless ($u) { print "Status: 302 Found\nLocation: /login.cgi\n\n"; exit; }
31 _handle_action($q, $form);
32 exit;
33}
34
35unless ($u) { print "Status: 302 Found\nLocation: /login.cgi\n\n"; exit; }
36
37my $sc = MODS::MetaAdmin::SiteConn->new;
38my $sites = $sc->list_sites(active => 1);
39
40sub _esc {
41 my $s = shift; $s //= '';
42 $s =~ s/&/&amp;/g; $s =~ s/</&lt;/g; $s =~ s/>/&gt;/g; $s =~ s/"/&quot;/g;
43 return $s;
44}
45
46sub _brand_letters {
47 my ($s) = @_;
48 my %map = (webstls=>'WS',ptmatrix=>'PT',affsoft=>'AS',shopcart=>'SC',
49 repricer=>'RP',abforge=>'AB',contactforge=>'CF');
50 return $map{$s->{slug}} || uc(substr($s->{display_name} || '?', 0, 1));
51}
52
53sub _human_minutes {
54 my ($m) = @_;
55 return '<span class="dim">--</span>' unless defined $m && $m > 0;
56 if ($m >= 60*24*30) { return sprintf('%.1f mo', $m/(60*24*30)); }
57 if ($m >= 60*24) { return sprintf('%.0f d', $m/(60*24)); }
58 if ($m >= 60) { return sprintf('%.0f h', $m/60); }
59 return "$m m";
60}
61
62my $flash = '';
63if ($form->{saved}) {
64 my $n = _esc($form->{name} || '');
65 my $m = _esc($form->{mins} || '');
66 $flash = qq~<div class="banner success">Set $n -&gt; $m minutes override.</div>~;
67}
68if ($form->{cleared}) {
69 my $n = _esc($form->{name} || '');
70 $flash = qq~<div class="banner success">Cleared override for $n (site default applies).</div>~;
71}
72if ($form->{err}) {
73 my $e = _esc($form->{err});
74 $flash = qq~<div class="banner danger">$e</div>~;
75}
76
77my $body = '';
78$body .= q~
79<div class="page-head"><div class="left">
80 <span class="page-eyebrow"><span class="dot"></span> Sessions</span>
81 <h1 class="page-title">Per-user session overrides</h1>
82 <p class="page-subtitle">Override the site's default session length on a per-account basis. Lift your own session to months so testing the portfolio doesn't require re-authing. The site setting still applies to every other user.</p>
83</div></div>
84~;
85$body .= $flash;
86
87foreach my $s (@$sites) {
88 my $slug = $s->{slug};
89 my $name = _esc($s->{display_name});
90 my $color = _esc($s->{brand_color} || '#92400e');
91 my $ltr = _brand_letters($s);
92
93 # Pull users + their override on this site
94 my $users = $sc->query_many($slug, q~
95 SELECT id, email, display_name, session_minutes_override
96 FROM users
97 ORDER BY (session_minutes_override IS NULL), id
98 LIMIT 50
99 ~);
100 my $err = $sc->last_error;
101
102 $body .= qq~<div class="module" style="margin-bottom:16px">
103 <div class="module-head">
104 <div class="left">
105 <span class="brand-icon" style="--site-glow:$color" data-letter="$ltr"></span>
106 <div class="module-title" style="margin-left:10px">$name</div>
107 </div>
108 </div>
109 <div class="module-body" style="padding:0">~;
110
111 if ($err) {
112 $body .= qq~<div class="dim" style="padding:14px 18px">Read error: ~ . _esc($err) . qq~</div>~;
113 } elsif (!@$users) {
114 $body .= qq~<div class="dim" style="padding:14px 18px">No users yet.</div>~;
115 } else {
116 $body .= qq~<table class="tbl">
117 <thead><tr><th>User</th><th>Email</th><th style="text-align:right">Override</th><th style="text-align:right;width:380px">Actions</th></tr></thead>
118 <tbody>~;
119 foreach my $r (@$users) {
120 my $uid = int($r->{id});
121 my $em = _esc($r->{email} || '');
122 my $dn = _esc($r->{display_name} || '');
123 my $ovr = $r->{session_minutes_override};
124 my $ovr_h = _human_minutes($ovr);
125 my $cur = defined $ovr ? int($ovr) : '';
126
127 $body .= qq~<tr>
128 <td><strong>$dn</strong> <span class="dim mono" style="font-size:11px">#$uid</span></td>
129 <td class="dim" style="font-size:12px">$em</td>
130 <td class="mono" style="text-align:right"><strong>$ovr_h</strong></td>
131 <td style="text-align:right;white-space:nowrap">
132 <form method="POST" action="/sessions_action.cgi" style="display:inline-flex;gap:6px;align-items:center">
133 <input type="hidden" name="op" value="set">
134 <input type="hidden" name="slug" value="$slug">
135 <input type="hidden" name="user_id" value="$uid">
136 <input type="hidden" name="name" value="$em">
137 <input type="number" name="minutes" value="$cur" min="1" max="2628000" placeholder="min"
138 style="width:90px;padding:6px 8px;font-size:12px">
139 <button type="submit" class="btn btn-sm btn-primary">Set</button>
140 </form>
141 <button type="button" class="btn btn-sm" onclick="document.getElementById('p_${slug}_${uid}').value=87600; this.parentElement.querySelector('form').requestSubmit();" title="60 days">60d</button>
142 <form method="POST" action="/sessions_action.cgi" style="display:inline" onsubmit="return confirm('Clear override? Site default takes over.')">
143 <input type="hidden" name="op" value="clear">
144 <input type="hidden" name="slug" value="$slug">
145 <input type="hidden" name="user_id" value="$uid">
146 <input type="hidden" name="name" value="$em">
147 <button type="submit" class="btn btn-sm btn-danger">Clear</button>
148 </form>
149 </td>
150 </tr>~;
151 }
152 $body .= q~</tbody></table>~;
153 }
154 $body .= q~</div></div>~;
155}
156
157MODS::MetaAdmin::Wrapper->new->render(title => 'Sessions', page_key => 'sessions', body => $body, userinfo => $u);
158
159#======================================================================
160# sessions_action entry: POST handler
161# op=set -- set users.session_minutes_override on the target site
162# op=clear -- clear it (site default applies)
163#======================================================================
164sub _act_q {
165 my $s = shift; $s //= '';
166 $s =~ s/([^A-Za-z0-9_.\-])/sprintf('%%%02X', ord($1))/ge;
167 return $s;
168}
169
170sub _handle_action {
171 my ($q, $form) = @_;
172
173 my $op = $form->{op} || '';
174 my $slug = $form->{slug} || '';
175 my $user_id = int($form->{user_id} || 0);
176 my $name = $form->{name} || '';
177 my $mins = int($form->{minutes} || 0);
178
179 my $sc = MODS::MetaAdmin::SiteConn->new;
180
181 if ($op eq 'set') {
182 unless ($user_id && $mins > 0 && length $slug) {
183 print "Status: 302 Found\nLocation: /sessions.cgi?err=" . _act_q('missing fields') . "\n\n"; return;
184 }
185 my $ok = $sc->set_session_override($slug, $user_id, $mins);
186 if ($ok) {
187 print "Status: 302 Found\nLocation: /sessions.cgi?saved=1&name=" . _act_q($name) . "&mins=$mins\n\n";
188 } else {
189 my $e = $sc->last_error || 'update failed';
190 print "Status: 302 Found\nLocation: /sessions.cgi?err=" . _act_q($e) . "\n\n";
191 }
192 return;
193 }
194 elsif ($op eq 'clear') {
195 unless ($user_id && length $slug) {
196 print "Status: 302 Found\nLocation: /sessions.cgi?err=" . _act_q('missing fields') . "\n\n"; return;
197 }
198 my $ok = $sc->clear_session_override($slug, $user_id);
199 if ($ok) {
200 print "Status: 302 Found\nLocation: /sessions.cgi?cleared=1&name=" . _act_q($name) . "\n\n";
201 } else {
202 my $e = $sc->last_error || 'clear failed';
203 print "Status: 302 Found\nLocation: /sessions.cgi?err=" . _act_q($e) . "\n\n";
204 }
205 return;
206 }
207 print "Status: 302 Found\nLocation: /sessions.cgi?err=" . _act_q('unknown op') . "\n\n";
208}
Keyboard: j next diff k previous diff g top G bottom r restore c compile-check ? help