Diff -- /var/www/vhosts/3dshawn.com/repricer.3dshawn.com/account_close.cgi
Diff
/var/www/vhosts/3dshawn.com/repricer.3dshawn.com/account_close.cgi
added on local at 2026-07-01 21:46:50
Added
+102
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to 81c41a042f87
to 81c41a042f87
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 -- account self-close (30-day grace). | |
| 4 | # | |
| 5 | # GET /account_close.cgi confirmation page | |
| 6 | # POST act=request stamp users.account_deletion_requested_at | |
| 7 | # POST act=cancel clear it (within grace window) | |
| 8 | # | |
| 9 | # Actual purge happens in _subscription_worker.pl when | |
| 10 | # account_deletion_requested_at <= NOW() - INTERVAL 30 DAY. | |
| 11 | #====================================================================== | |
| 12 | use strict; | |
| 13 | use warnings; | |
| 14 | use lib '/var/www/vhosts/3dshawn.com/repricer.3dshawn.com'; | |
| 15 | use CGI; | |
| 16 | use MODS::DBConnect; | |
| 17 | use MODS::Login; | |
| 18 | use MODS::RePricer::Config; | |
| 19 | use MODS::RePricer::Wrapper; | |
| 20 | ||
| 21 | $|=1; | |
| 22 | my $q = CGI->new; | |
| 23 | my $form = $q->Vars; | |
| 24 | my $auth = MODS::Login->new; | |
| 25 | my $wrap = MODS::RePricer::Wrapper->new; | |
| 26 | my $db = MODS::DBConnect->new; | |
| 27 | my $cfg = MODS::RePricer::Config->new; | |
| 28 | my $DB = $cfg->settings('database_name'); | |
| 29 | ||
| 30 | my $userinfo = $auth->login_verify(); | |
| 31 | unless ($userinfo) { print "Status: 302 Found\nLocation: /login.cgi\n\n"; exit; } | |
| 32 | my $uid = $userinfo->{user_id}; $uid =~ s/[^0-9]//g; | |
| 33 | ||
| 34 | sub _h { my $s = shift // ''; $s =~ s/&/&/g; $s =~ s/</</g; $s =~ s/>/>/g; $s =~ s/"/"/g; $s } | |
| 35 | ||
| 36 | my $dbh = $db->db_connect(); | |
| 37 | my $flash = ''; | |
| 38 | if (($ENV{REQUEST_METHOD} || '') eq 'POST') { | |
| 39 | my $act = $form->{act} || ''; | |
| 40 | if ($act eq 'request') { | |
| 41 | $db->db_readwrite($dbh, qq~ | |
| 42 | UPDATE `${DB}`.users SET account_deletion_requested_at=NOW() WHERE id='$uid' | |
| 43 | ~, $ENV{SCRIPT_NAME}, __LINE__); | |
| 44 | $flash = 'requested'; | |
| 45 | } elsif ($act eq 'cancel') { | |
| 46 | $db->db_readwrite($dbh, qq~ | |
| 47 | UPDATE `${DB}`.users SET account_deletion_requested_at=NULL WHERE id='$uid' | |
| 48 | ~, $ENV{SCRIPT_NAME}, __LINE__); | |
| 49 | $flash = 'cancelled'; | |
| 50 | } | |
| 51 | } | |
| 52 | ||
| 53 | my $u = $db->db_readwrite($dbh, qq~ | |
| 54 | SELECT account_deletion_requested_at, | |
| 55 | TIMESTAMPDIFF(DAY, account_deletion_requested_at, NOW()) AS days_since | |
| 56 | FROM `${DB}`.users WHERE id='$uid' | |
| 57 | ~, $ENV{SCRIPT_NAME}, __LINE__); | |
| 58 | $db->db_disconnect($dbh); | |
| 59 | ||
| 60 | my $requested = $u->{account_deletion_requested_at} // ''; | |
| 61 | my $days_left = $u->{days_since} ? (30 - $u->{days_since}) : 30; | |
| 62 | $days_left = 0 if $days_left < 0; | |
| 63 | ||
| 64 | my $body = '<div style="max-width:680px;margin:0 auto;padding:24px 28px;line-height:1.7;color:#e6ecf6">'; | |
| 65 | $body .= '<h1 style="font-size:26px;color:#fff;margin:0 0 6px">Close my account</h1>'; | |
| 66 | ||
| 67 | if ($flash eq 'requested') { | |
| 68 | $body .= '<div style="background:#fbbf2422;border-left:3px solid #fbbf24;color:#fbbf24;padding:12px 16px;border-radius:8px;margin:14px 0">Deletion requested. You have 30 days to change your mind by clicking "Cancel deletion" below.</div>'; | |
| 69 | } elsif ($flash eq 'cancelled') { | |
| 70 | $body .= '<div style="background:#10b98122;border-left:3px solid #10b981;color:#10b981;padding:12px 16px;border-radius:8px;margin:14px 0">Deletion request cancelled. Your account is active.</div>'; | |
| 71 | } | |
| 72 | ||
| 73 | if ($requested) { | |
| 74 | $body .= '<section style="background:#0b1226;border:1px solid #fbbf24;border-radius:12px;padding:18px 22px;margin:14px 0">'; | |
| 75 | $body .= ' <div style="color:#fbbf24;font-size:16px;font-weight:600;margin-bottom:8px">Account deletion is scheduled</div>'; | |
| 76 | $body .= " <div style=\"color:#7e92b6\">Requested at " . _h($requested) . ". <b style=\"color:#fff\">$days_left days remaining</b> before the purge runs. Repricing is paused for this account until you cancel or the purge completes.</div>"; | |
| 77 | $body .= ' <form method="POST" style="margin-top:14px">'; | |
| 78 | $body .= ' <input type="hidden" name="act" value="cancel">'; | |
| 79 | $body .= ' <button type="submit" style="background:#064e3b;color:#a7f3d0;border:0;padding:10px 20px;border-radius:8px;font-weight:600;cursor:pointer">Cancel deletion — keep my account</button>'; | |
| 80 | $body .= ' </form>'; | |
| 81 | $body .= '</section>'; | |
| 82 | } else { | |
| 83 | $body .= '<p style="color:#7e92b6">Closing your account is a 2-step process so accidents are reversible:</p>'; | |
| 84 | $body .= '<ol style="color:#e6ecf6;padding-left:22px">'; | |
| 85 | $body .= ' <li>You request deletion below. We immediately pause all repricing on your account.</li>'; | |
| 86 | $body .= ' <li>30 days later, a worker permanently purges your row + tokens + products + price history. Until then, you can cancel from this same page.</li>'; | |
| 87 | $body .= '</ol>'; | |
| 88 | $body .= '<p style="color:#7e92b6">Stripe invoice records are retained for 7 years for tax / legal compliance, separate from your account row. Per <a href="/privacy.cgi" style="color:#14b8a6">privacy policy</a>.</p>'; | |
| 89 | $body .= '<p style="color:#7e92b6">If you have an active paid subscription, <a href="/billing.cgi" style="color:#14b8a6">cancel it first</a> — otherwise you'll keep getting charged through your current cycle.</p>'; | |
| 90 | $body .= '<form method="POST" style="margin-top:18px" onsubmit="return confirm(\'Request deletion? Your data is reversibly hidden for 30 days, then permanently purged.\')">'; | |
| 91 | $body .= ' <input type="hidden" name="act" value="request">'; | |
| 92 | $body .= ' <button type="submit" style="background:#ef4444;color:#fff;border:0;padding:10px 20px;border-radius:8px;font-weight:600;cursor:pointer">Request account deletion</button>'; | |
| 93 | $body .= ' <a href="/profile.cgi" style="color:#7e92b6;font-size:13px;text-decoration:none">Cancel — back to profile</a>'; | |
| 94 | $body .= '</form>'; | |
| 95 | } | |
| 96 | ||
| 97 | $body .= '<div style="margin-top:18px;color:#7e92b6;font-size:12px"><a href="/account_export.cgi" style="color:#14b8a6">Download your data first</a> (recommended).</div>'; | |
| 98 | $body .= '</div>'; | |
| 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 | $wrap->render({ userinfo => $userinfo, title => 'Close my account', body => $body }); | |
| 102 | exit; |