Diff -- /var/www/vhosts/webstls.com/httpdocs/profile.cgi
Diff

/var/www/vhosts/webstls.com/httpdocs/profile.cgi

added on WebSTLs (webstls.com) at 2026-07-01 22:26:45

Added
+190
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to ac2ac353bced
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# WebSTLs - Profile (account & identity)
4#
5# Account identity surface: display name, email, default currency,
6# timezone, password, 2FA, sessions, resend email verification, close
7# account. Matches the PTMatrix /profile.cgi convention.
8#
9# Split off from the legacy /settings.cgi (which combined account +
10# notifications + module visibility in a single tabbed page). The
11# notifications / module visibility portion now lives at
12# /preferences.cgi.
13#======================================================================
14use strict;
15use warnings;
16
17use lib '/var/www/vhosts/webstls.com/httpdocs';
18use CGI;
19use MODS::Template;
20use MODS::DBConnect;
21use MODS::Login;
22use MODS::WebSTLs::Config;
23use MODS::WebSTLs::Wrapper;
24use MODS::WebSTLs::Billing;
25
26my $q = CGI->new;
27my $form = $q->Vars;
28my $auth = MODS::Login->new;
29my $wrap = MODS::WebSTLs::Wrapper->new;
30my $tfile = MODS::Template->new;
31my $db = MODS::DBConnect->new;
32my $cfg = MODS::WebSTLs::Config->new;
33my $bill = MODS::WebSTLs::Billing->new;
34my $DB = $cfg->settings('database_name');
35
36$|=1;
37my $userinfo = $auth->login_verify();
38if (!$userinfo) {
39 print "Status: 302 Found\nLocation: /login.cgi\n\n";
40 exit;
41}
42my $uid = $userinfo->{user_id};
43$uid =~ s/[^0-9]//g;
44
45my $dbh = $db->db_connect();
46
47# ---- POST handlers -------------------------------------------------
48my $saved_msg = '';
49
50sub _esc {
51 my $s = shift;
52 $s //= '';
53 $s =~ s/\\/\\\\/g;
54 $s =~ s/'/''/g;
55 return $s;
56}
57
58if (($form->{action} || '') eq 'save_account') {
59 my $name = $form->{display_name} // '';
60 my $email = $form->{email} // '';
61 my $cur = uc($form->{default_currency} // 'USD');
62 my $tz = $form->{timezone} // 'UTC';
63
64 # Trim + cap lengths. The schema already enforces these but doing
65 # it here gives us a clean error path before the UPDATE.
66 $name =~ s/^\s+|\s+$//g;
67 $email =~ s/^\s+|\s+$//g;
68 $cur = substr($cur, 0, 3);
69 $tz = substr($tz, 0, 64);
70
71 if ($name eq '' || $email !~ /\@/) {
72 $saved_msg = 'Please provide a display name and a valid email address.';
73 } else {
74 $db->db_readwrite($dbh, qq~
75 UPDATE `${DB}`.users
76 SET display_name='~ . _esc($name) . qq~',
77 email='~ . _esc($email) . qq~',
78 default_currency='~ . _esc($cur) . qq~',
79 timezone='~ . _esc($tz) . qq~'
80 WHERE id='$uid'
81 ~, $ENV{SCRIPT_NAME}, __LINE__);
82 $saved_msg = 'Profile saved.';
83 }
84}
85elsif (($form->{action} || '') eq 'resend_verification') {
86 # Re-mint a verification token and email the link. We require the
87 # account to NOT already be verified -- there's no reason to ship
88 # another email after the user already confirmed.
89 require MODS::WebSTLs::EmailVerify;
90 my $u_check = $db->db_readwrite($dbh, qq~
91 SELECT id, email, display_name, email_verified_at
92 FROM `${DB}`.users WHERE id='$uid' LIMIT 1
93 ~, $ENV{SCRIPT_NAME}, __LINE__);
94 if ($u_check && $u_check->{id} && !$u_check->{email_verified_at}) {
95 my $r = MODS::WebSTLs::EmailVerify->send_for_user(
96 $db, $dbh, $DB,
97 { id => $u_check->{id}, email => $u_check->{email}, display_name => $u_check->{display_name} },
98 );
99 $saved_msg = ($r && $r->{ok})
100 ? 'Verification email sent. Check your inbox (and spam folder).'
101 : 'Could not send verification email: ' . ($r->{error} || 'unknown error');
102 } else {
103 $saved_msg = 'Your email is already verified.';
104 }
105}
106
107# ---- Load current state --------------------------------------------
108my $u = $db->db_readwrite($dbh, qq~
109 SELECT display_name, email, default_currency, timezone,
110 two_factor_enabled,
111 email_verified_at,
112 DATE_FORMAT(email_verified_at, '%b %e, %Y') AS verified_on
113 FROM `${DB}`.users
114 WHERE id='$uid'
115~, $ENV{SCRIPT_NAME}, __LINE__) || {};
116
117# Build the dropdown lists with `selected` flags so the template can
118# render plain HTML without any client-side guessing.
119my @CURRENCIES = qw(USD EUR GBP CAD AUD JPY);
120my @TIMEZONES = (
121 'UTC',
122 'America/Los_Angeles',
123 'America/Denver',
124 'America/Chicago',
125 'America/New_York',
126 'Europe/London',
127 'Europe/Berlin',
128 'Europe/Paris',
129 'Asia/Tokyo',
130 'Australia/Sydney',
131);
132my (@cur_opts, @tz_opts);
133my $cur_now = $u->{default_currency} || 'USD';
134my $tz_now = $u->{timezone} || 'UTC';
135foreach my $c (@CURRENCIES) {
136 push @cur_opts, { value => $c, selected_attr => ($c eq $cur_now ? ' selected' : '') };
137}
138foreach my $t (@TIMEZONES) {
139 push @tz_opts, { value => $t, selected_attr => ($t eq $tz_now ? ' selected' : '') };
140}
141
142# Active sessions (creator side -- buyer sessions live in their own table).
143my $active_sessions = 0;
144my $r_sess = $db->db_readwrite($dbh, qq~
145 SELECT COUNT(*) AS n FROM `${DB}`.user_sessions
146 WHERE user_id='$uid'
147 AND revoked_at IS NULL
148 AND expires_at > NOW()
149~, $ENV{SCRIPT_NAME}, __LINE__);
150$active_sessions = $r_sess->{n} if $r_sess && defined $r_sess->{n};
151
152$db->db_disconnect($dbh);
153
154# Helper to HTML-escape values that go into <input value="...">.
155sub _h_attr {
156 my $s = shift;
157 $s //= '';
158 $s =~ s/&/&amp;/g;
159 $s =~ s/</&lt;/g;
160 $s =~ s/>/&gt;/g;
161 $s =~ s/"/&quot;/g;
162 return $s;
163}
164
165my $tvars = {
166 user_id => $uid,
167 saved_msg => $saved_msg,
168 has_saved_msg => $saved_msg ? 1 : 0,
169
170 display_name => _h_attr($u->{display_name}),
171 email => _h_attr($u->{email}),
172 currency_options => \@cur_opts,
173 timezone_options => \@tz_opts,
174
175 email_verified => $u->{email_verified_at} ? 1 : 0,
176 email_verified_label => $u->{email_verified_at} ? ('Verified ' . ($u->{verified_on} || '')) : 'Not verified yet',
177 two_factor_enabled => $u->{two_factor_enabled} ? 1 : 0,
178 active_session_count => $active_sessions,
179};
180
181print "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";
182
183my $body = join('', $tfile->template('webstls_profile.html', $tvars, $userinfo));
184
185$wrap->render({
186 userinfo => $userinfo,
187 page_key => 'my_profile',
188 title => 'Profile',
189 body => $body,
190});
Keyboard: j next diff k previous diff g top G bottom r restore c compile-check ? help