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

O Operator
Diff

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

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

Added
+0
lines
Removed
-0
lines
Context
208
unchanged
Blobs
from 5215d3470358
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.
11#!/usr/bin/perl
22#======================================================================
33# Meta-Admin -- /sessions.cgi
44#
55# Per-user session_minutes_override editor across the portfolio.
66# Each site row lets the operator view + set the override for users
77# on that site without having to log into the site's own admin.
88#
99# Read path = MODS::MetaAdmin::SiteConn->query_many (SELECT only).
1010# Write path = MODS::MetaAdmin::SiteConn->set_session_override
1111# (carved-out UPDATE on one column).
1212#======================================================================
1313use strict;
1414use warnings;
1515use lib '/var/www/vhosts/3dshawn.com/admin.3dshawn.com';
1616use CGI;
1717use MODS::Login;
1818use MODS::MetaAdmin::Config;
1919use MODS::MetaAdmin::Wrapper;
2020use MODS::MetaAdmin::SiteConn;
2121
2222my $q = CGI->new;
2323my $form = $q->Vars;
2424my $auth = MODS::Login->new;
2525my $u = $auth->login_verify;
2626
2727my $entry = ($ENV{SCRIPT_NAME} || '') =~ m{/([^/]+)\.cgi$} ? $1 : 'sessions';
2828
2929if ($entry eq 'sessions_action') {
3030 unless ($u) { print "Status: 302 Found\nLocation: /login.cgi\n\n"; exit; }
3131 _handle_action($q, $form);
3232 exit;
3333}
3434
3535unless ($u) { print "Status: 302 Found\nLocation: /login.cgi\n\n"; exit; }
3636
3737my $sc = MODS::MetaAdmin::SiteConn->new;
3838my $sites = $sc->list_sites(active => 1);
3939
4040sub _esc {
4141 my $s = shift; $s //= '';
4242 $s =~ s/&/&amp;/g; $s =~ s/</&lt;/g; $s =~ s/>/&gt;/g; $s =~ s/"/&quot;/g;
4343 return $s;
4444}
4545
4646sub _brand_letters {
4747 my ($s) = @_;
4848 my %map = (webstls=>'WS',ptmatrix=>'PT',affsoft=>'AS',shopcart=>'SC',
4949 repricer=>'RP',abforge=>'AB',contactforge=>'CF');
5050 return $map{$s->{slug}} || uc(substr($s->{display_name} || '?', 0, 1));
5151}
5252
5353sub _human_minutes {
5454 my ($m) = @_;
5555 return '<span class="dim">--</span>' unless defined $m && $m > 0;
5656 if ($m >= 60*24*30) { return sprintf('%.1f mo', $m/(60*24*30)); }
5757 if ($m >= 60*24) { return sprintf('%.0f d', $m/(60*24)); }
5858 if ($m >= 60) { return sprintf('%.0f h', $m/60); }
5959 return "$m m";
6060}
6161
6262my $flash = '';
6363if ($form->{saved}) {
6464 my $n = _esc($form->{name} || '');
6565 my $m = _esc($form->{mins} || '');
6666 $flash = qq~<div class="banner success">Set $n -&gt; $m minutes override.</div>~;
6767}
6868if ($form->{cleared}) {
6969 my $n = _esc($form->{name} || '');
7070 $flash = qq~<div class="banner success">Cleared override for $n (site default applies).</div>~;
7171}
7272if ($form->{err}) {
7373 my $e = _esc($form->{err});
7474 $flash = qq~<div class="banner danger">$e</div>~;
7575}
7676
7777my $body = '';
7878$body .= q~
7979<div class="page-head"><div class="left">
8080 <span class="page-eyebrow"><span class="dot"></span> Sessions</span>
8181 <h1 class="page-title">Per-user session overrides</h1>
8282 <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>
8383</div></div>
8484~;
8585$body .= $flash;
8686
8787foreach my $s (@$sites) {
8888 my $slug = $s->{slug};
8989 my $name = _esc($s->{display_name});
9090 my $color = _esc($s->{brand_color} || '#92400e');
9191 my $ltr = _brand_letters($s);
9292
9393 # Pull users + their override on this site
9494 my $users = $sc->query_many($slug, q~
9595 SELECT id, email, display_name, session_minutes_override
9696 FROM users
9797 ORDER BY (session_minutes_override IS NULL), id
9898 LIMIT 50
9999 ~);
100100 my $err = $sc->last_error;
101101
102102 $body .= qq~<div class="module" style="margin-bottom:16px">
103103 <div class="module-head">
104104 <div class="left">
105105 <span class="brand-icon" style="--site-glow:$color" data-letter="$ltr"></span>
106106 <div class="module-title" style="margin-left:10px">$name</div>
107107 </div>
108108 </div>
109109 <div class="module-body" style="padding:0">~;
110110
111111 if ($err) {
112112 $body .= qq~<div class="dim" style="padding:14px 18px">Read error: ~ . _esc($err) . qq~</div>~;
113113 } elsif (!@$users) {
114114 $body .= qq~<div class="dim" style="padding:14px 18px">No users yet.</div>~;
115115 } else {
116116 $body .= qq~<table class="tbl">
117117 <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>
118118 <tbody>~;
119119 foreach my $r (@$users) {
120120 my $uid = int($r->{id});
121121 my $em = _esc($r->{email} || '');
122122 my $dn = _esc($r->{display_name} || '');
123123 my $ovr = $r->{session_minutes_override};
124124 my $ovr_h = _human_minutes($ovr);
125125 my $cur = defined $ovr ? int($ovr) : '';
126126
127127 $body .= qq~<tr>
128128 <td><strong>$dn</strong> <span class="dim mono" style="font-size:11px">#$uid</span></td>
129129 <td class="dim" style="font-size:12px">$em</td>
130130 <td class="mono" style="text-align:right"><strong>$ovr_h</strong></td>
131131 <td style="text-align:right;white-space:nowrap">
132132 <form method="POST" action="/sessions_action.cgi" style="display:inline-flex;gap:6px;align-items:center">
133133 <input type="hidden" name="op" value="set">
134134 <input type="hidden" name="slug" value="$slug">
135135 <input type="hidden" name="user_id" value="$uid">
136136 <input type="hidden" name="name" value="$em">
137137 <input type="number" name="minutes" value="$cur" min="1" max="2628000" placeholder="min"
138138 style="width:90px;padding:6px 8px;font-size:12px">
139139 <button type="submit" class="btn btn-sm btn-primary">Set</button>
140140 </form>
141141 <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>
142142 <form method="POST" action="/sessions_action.cgi" style="display:inline" onsubmit="return confirm('Clear override? Site default takes over.')">
143143 <input type="hidden" name="op" value="clear">
144144 <input type="hidden" name="slug" value="$slug">
145145 <input type="hidden" name="user_id" value="$uid">
146146 <input type="hidden" name="name" value="$em">
147147 <button type="submit" class="btn btn-sm btn-danger">Clear</button>
148148 </form>
149149 </td>
150150 </tr>~;
151151 }
152152 $body .= q~</tbody></table>~;
153153 }
154154 $body .= q~</div></div>~;
155155}
156156
157157MODS::MetaAdmin::Wrapper->new->render(title => 'Sessions', page_key => 'sessions', body => $body, userinfo => $u);
158158
159159#======================================================================
160160# sessions_action entry: POST handler
161161# op=set -- set users.session_minutes_override on the target site
162162# op=clear -- clear it (site default applies)
163163#======================================================================
164164sub _act_q {
165165 my $s = shift; $s //= '';
166166 $s =~ s/([^A-Za-z0-9_.\-])/sprintf('%%%02X', ord($1))/ge;
167167 return $s;
168168}
169169
170170sub _handle_action {
171171 my ($q, $form) = @_;
172172
173173 my $op = $form->{op} || '';
174174 my $slug = $form->{slug} || '';
175175 my $user_id = int($form->{user_id} || 0);
176176 my $name = $form->{name} || '';
177177 my $mins = int($form->{minutes} || 0);
178178
179179 my $sc = MODS::MetaAdmin::SiteConn->new;
180180
181181 if ($op eq 'set') {
182182 unless ($user_id && $mins > 0 && length $slug) {
183183 print "Status: 302 Found\nLocation: /sessions.cgi?err=" . _act_q('missing fields') . "\n\n"; return;
184184 }
185185 my $ok = $sc->set_session_override($slug, $user_id, $mins);
186186 if ($ok) {
187187 print "Status: 302 Found\nLocation: /sessions.cgi?saved=1&name=" . _act_q($name) . "&mins=$mins\n\n";
188188 } else {
189189 my $e = $sc->last_error || 'update failed';
190190 print "Status: 302 Found\nLocation: /sessions.cgi?err=" . _act_q($e) . "\n\n";
191191 }
192192 return;
193193 }
194194 elsif ($op eq 'clear') {
195195 unless ($user_id && length $slug) {
196196 print "Status: 302 Found\nLocation: /sessions.cgi?err=" . _act_q('missing fields') . "\n\n"; return;
197197 }
198198 my $ok = $sc->clear_session_override($slug, $user_id);
199199 if ($ok) {
200200 print "Status: 302 Found\nLocation: /sessions.cgi?cleared=1&name=" . _act_q($name) . "\n\n";
201201 } else {
202202 my $e = $sc->last_error || 'clear failed';
203203 print "Status: 302 Found\nLocation: /sessions.cgi?err=" . _act_q($e) . "\n\n";
204204 }
205205 return;
206206 }
207207 print "Status: 302 Found\nLocation: /sessions.cgi?err=" . _act_q('unknown op') . "\n\n";
208208}