Diff -- /var/www/vhosts/3dshawn.com/repricer.3dshawn.com/admin_companies.cgi

O Operator
Diff

/var/www/vhosts/3dshawn.com/repricer.3dshawn.com/admin_companies.cgi

added on local at 2026-07-11 18:33:25

Added
+236
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to 9d90985fb2ea
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# RePricer - SUPER-ADMIN: list customer storefronts/workspaces (list)
4# + per-company detail (detail) dispatched on SCRIPT_NAME.
5#
6# admin_companies.cgi -> list view (rich columns)
7# admin_company.cgi?id=<id> -> detail view
8#
9# Consolidated: former /admin_company.cgi is now a wrapper that does
10# this file; SCRIPT_NAME routes execution.
11#======================================================================
12use strict; use warnings;
13use lib '/var/www/vhosts/3dshawn.com/repricer.3dshawn.com';
14use CGI;
15use MODS::Template;
16use MODS::DBConnect;
17use MODS::Login;
18use MODS::RePricer::Config;
19use MODS::RePricer::Wrapper;
20use MODS::RePricer::Admin;
21
22my $q = CGI->new;
23my $form = $q->Vars;
24my $auth = MODS::Login->new;
25my $wrap = MODS::RePricer::Wrapper->new;
26my $tfile = MODS::Template->new;
27my $db = MODS::DBConnect->new;
28my $cfg = MODS::RePricer::Config->new;
29my $admin = MODS::RePricer::Admin->new;
30my $DB = $cfg->settings('database_name');
31
32my $userinfo = $auth->login_verify();
33if (!$userinfo) { print "Status: 302 Found\nLocation: /login.cgi\n\n"; exit; }
34$admin->require_admin($userinfo);
35require MODS::RePricer::Permissions;
36MODS::RePricer::Permissions->new->require_feature($userinfo, 'admin_view_users');
37
38my $entry = ($ENV{SCRIPT_NAME} || '') =~ m{/([^/]+)\.cgi$} ? $1 : 'admin_companies';
39
40if ($entry eq 'admin_company') {
41 _co_detail();
42 exit;
43}
44
45_co_list();
46exit;
47
48#======================================================================
49# LIST view (admin_companies.cgi)
50#======================================================================
51sub _co_list {
52 my $sq = $form->{q} || '';
53 $sq =~ s/[^A-Za-z0-9_\-\.\s\@]//g;
54
55 my $dbh = $db->db_connect();
56
57 my $where = '1=1';
58 $where .= " AND (s.name LIKE '%$sq%' OR s.subdomain LIKE '%$sq%' OR u.email LIKE '%$sq%')" if $sq;
59
60 my @rows = $db->db_readwrite_multiple($dbh, qq~
61 SELECT s.id, s.name, s.subdomain, s.status, s.created_at,
62 UNIX_TIMESTAMP(s.created_at) AS created_at_epoch,
63 s.user_id, u.email AS owner_email, u.display_name AS owner_name,
64 u.plan_tier AS owner_plan, u.account_status AS owner_status
65 FROM `${DB}`.storefronts s
66 LEFT JOIN `${DB}`.users u ON u.id = s.user_id
67 WHERE $where
68 ORDER BY s.created_at DESC
69 LIMIT 500
70 ~, $ENV{SCRIPT_NAME}, __LINE__);
71
72 my @palette = ('#5aa9ff','#34d399','#fbbf24','#a78bfa','#f87171','#22c55e','#06b6d4','#ec4899','#f59e0b');
73
74 foreach my $r (@rows) {
75 $r->{name} = _co_h($r->{name} || '(unnamed)');
76 $r->{owner_email} = _co_h($r->{owner_email} || '');
77 $r->{owner_name} = _co_h($r->{owner_name} || $r->{owner_email});
78 $r->{subdomain} = _co_h($r->{subdomain});
79 $r->{status} = _co_h($r->{status});
80 my $_ce = int($r->{created_at_epoch} || 0);
81 my $_cs = $r->{created_at} || '';
82 $r->{created_at} = ($_ce > 0 && length $_cs)
83 ? qq~<span class="ts" data-ts="$_ce" data-fmt="datetime">$_cs</span>~
84 : ($_cs || '-');
85 $r->{plan_tier} = ucfirst($r->{owner_plan} || 'free');
86 $r->{brand_color} = $palette[ ($r->{id} || 0) % scalar(@palette) ];
87 $r->{users_n} = 1;
88 $r->{trial_ends_at} = '-';
89 }
90
91 $db->db_disconnect($dbh);
92
93 my $tvars = {
94 rows => \@rows,
95 has_rows => scalar(@rows) ? 1 : 0,
96 row_count => scalar(@rows),
97 q => _co_h($sq),
98 };
99
100 print "Content-Type: text/html; charset=utf-8\nCache-Control: no-store, no-cache, must-revalidate, max-age=0\nPragma: no-cache\nExpires: 0\n\n";
101 my $body = join('', $tfile->template('repricer_admin_companies.html', $tvars, $userinfo));
102 $wrap->render({
103 userinfo => $userinfo,
104 page_key => 'admin_companies',
105 title => 'All Companies',
106 body => $body,
107 });
108}
109
110#======================================================================
111# DETAIL view (admin_company.cgi?id=<id>) -- ex-/admin_company.cgi
112#======================================================================
113sub _co_detail {
114 my $sid = $form->{id} || 0;
115 $sid =~ s/[^0-9]//g;
116 unless ($sid) {
117 print "Status: 302 Found\nLocation: /admin_companies.cgi\n\n"; return;
118 }
119
120 my $dbh = $db->db_connect();
121
122 my $sf = $db->db_readwrite($dbh, qq~
123 SELECT s.*, u.email AS owner_email, u.display_name AS owner_name,
124 u.id AS owner_user_id, u.plan_tier AS owner_plan,
125 u.account_status AS owner_status, u.last_login_at AS owner_last_login,
126 u.created_at AS owner_created_at, u.avatar_color AS owner_avatar,
127 UNIX_TIMESTAMP(s.created_at) AS sf_created_at_epoch,
128 UNIX_TIMESTAMP(u.last_login_at) AS owner_last_login_epoch,
129 UNIX_TIMESTAMP(u.created_at) AS owner_created_at_epoch
130 FROM `${DB}`.storefronts s
131 LEFT JOIN `${DB}`.users u ON u.id = s.user_id
132 WHERE s.id='$sid' LIMIT 1
133 ~, $ENV{SCRIPT_NAME}, __LINE__);
134
135 unless ($sf && $sf->{id}) {
136 $db->db_disconnect($dbh);
137 print "Status: 302 Found\nLocation: /admin_companies.cgi\n\n"; return;
138 }
139
140 my ($orders_total, $orders_30, $orders_count, @recent_orders) = (0,0,0);
141 my $tbl = $db->db_readwrite($dbh, qq~SHOW TABLES LIKE 'orders'~, $ENV{SCRIPT_NAME}, __LINE__);
142 if ($tbl && %$tbl) {
143 my $all = $db->db_readwrite($dbh, qq~
144 SELECT COALESCE(SUM(total_amount_cents),0) AS total, COUNT(*) AS n
145 FROM `${DB}`.orders WHERE storefront_id='$sid' AND status='paid'
146 ~, $ENV{SCRIPT_NAME}, __LINE__);
147 $orders_total = $all && $all->{total} ? $all->{total} + 0 : 0;
148 $orders_count = $all && $all->{n} ? $all->{n} + 0 : 0;
149 my $r30 = $db->db_readwrite($dbh, qq~
150 SELECT COALESCE(SUM(total_amount_cents),0) AS total
151 FROM `${DB}`.orders WHERE storefront_id='$sid' AND status='paid'
152 AND created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
153 ~, $ENV{SCRIPT_NAME}, __LINE__);
154 $orders_30 = $r30 && $r30->{total} ? $r30->{total} + 0 : 0;
155 @recent_orders = $db->db_readwrite_multiple($dbh, qq~
156 SELECT id, buyer_email, total_amount_cents, status, created_at,
157 UNIX_TIMESTAMP(created_at) AS created_at_epoch
158 FROM `${DB}`.orders WHERE storefront_id='$sid'
159 ORDER BY created_at DESC LIMIT 10
160 ~, $ENV{SCRIPT_NAME}, __LINE__);
161 foreach my $o (@recent_orders) {
162 $o->{total_disp} = sprintf('$%.2f', ($o->{total_amount_cents}||0)/100);
163 $o->{buyer_email} = _co_h($o->{buyer_email});
164 $o->{status} = _co_h($o->{status});
165 my $_ep = int($o->{created_at_epoch} || 0);
166 my $_s = $o->{created_at} || '';
167 $o->{created_at} = ($_ep > 0 && length $_s)
168 ? qq~<span class="ts" data-ts="$_ep" data-fmt="datetime">$_s</span>~
169 : $_s;
170 }
171 }
172
173 $db->db_disconnect($dbh);
174
175 my $tvars = {
176 sid => $sf->{id},
177 sf_name => _co_h($sf->{name} || '(unnamed)'),
178 sf_subdomain => _co_h($sf->{subdomain} || ''),
179 sf_status => _co_h($sf->{status} || 'draft'),
180 sf_custom_domain => _co_h($sf->{custom_domain} || ''),
181 sf_created_at => do {
182 my $ep = int($sf->{sf_created_at_epoch} || 0);
183 my $s = $sf->{created_at} || '';
184 ($ep > 0 && length $s)
185 ? qq~<span class="ts" data-ts="$ep" data-fmt="datetime">$s</span>~
186 : ($s || '-');
187 },
188 sf_ssl_status => _co_h($sf->{ssl_status} || 'none'),
189
190 owner_user_id => $sf->{owner_user_id} || 0,
191 owner_email => _co_h($sf->{owner_email} || ''),
192 owner_name => _co_h($sf->{owner_name} || $sf->{owner_email} || ''),
193 owner_plan => ucfirst($sf->{owner_plan} || 'free'),
194 owner_status => _co_h($sf->{owner_status} || ''),
195 owner_last_login => do {
196 my $ep = int($sf->{owner_last_login_epoch} || 0);
197 my $s = $sf->{owner_last_login} || '';
198 ($ep > 0 && length $s)
199 ? qq~<span class="ts" data-ts="$ep" data-fmt="datetime">$s</span>~
200 : ($s || 'never');
201 },
202 owner_created_at => do {
203 my $ep = int($sf->{owner_created_at_epoch} || 0);
204 my $s = $sf->{owner_created_at} || '';
205 ($ep > 0 && length $s)
206 ? qq~<span class="ts" data-ts="$ep" data-fmt="datetime">$s</span>~
207 : ($s || '-');
208 },
209 owner_avatar => $sf->{owner_avatar} || '#5aa9ff',
210
211 orders_count => $orders_count,
212 orders_total_disp => sprintf('%.2f', $orders_total/100),
213 orders_30_disp => sprintf('%.2f', $orders_30/100),
214 recent_orders => \@recent_orders,
215 has_orders => scalar(@recent_orders) ? 1 : 0,
216 };
217
218 print "Content-Type: text/html; charset=utf-8\nCache-Control: no-store\n\n";
219 my $body = join('', $tfile->template('repricer_admin_company.html', $tvars, $userinfo));
220 $wrap->render({
221 userinfo => $userinfo,
222 page_key => 'admin_companies',
223 title => 'Admin - ' . ($sf->{name} || 'Storefront'),
224 body => $body,
225 });
226}
227
228sub _co_h {
229 my $s = shift;
230 return '' unless defined $s;
231 $s =~ s/&/&amp;/g;
232 $s =~ s/</&lt;/g;
233 $s =~ s/>/&gt;/g;
234 $s =~ s/"/&quot;/g;
235 return $s;
236}