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

O Operator
Diff

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

added on local at 2026-07-11 18:38:29

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