Public web-only release: SvxPortal front-end + auth backend, install templates and INSTALL.md
Stripped from the internal repo: per-box deploy trees, Go services (nodelist/astmeta/proxy/svxdr), deploy/sync scripts and all secrets. Config templates for nginx, PHP-FPM, Redis and tmpfiles live in install/ with CHANGEME placeholders. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require __DIR__ . '/lib.php';
|
||||
|
||||
$j = json_in();
|
||||
$callsign = base_call(trim((string)($j['callsign'] ?? '')));
|
||||
$email = norm_email((string)($j['email'] ?? ''));
|
||||
|
||||
// Rate limit: 3 attempts per 15 minutes per IP
|
||||
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
|
||||
rate_limit("forgot:{$ip}", 3, 900);
|
||||
|
||||
if ($callsign === '' || $email === '') {
|
||||
json_out(['ok' => false, 'error' => 'Callsign and email are required.'], 400);
|
||||
}
|
||||
|
||||
// Find account by callsign
|
||||
$uuid = redis()->get(call_index_key($callsign));
|
||||
if (!is_string($uuid) || $uuid === '') {
|
||||
// Don't reveal if account exists
|
||||
json_out(['ok' => true, 'message' => 'If the account exists, a reset link has been sent.']);
|
||||
}
|
||||
|
||||
$acct = acct_get($uuid);
|
||||
$acct_email = norm_email($acct['email'] ?? '');
|
||||
|
||||
// Verify email matches
|
||||
if ($acct_email !== $email) {
|
||||
json_out(['ok' => true, 'message' => 'If the account exists, a reset link has been sent.']);
|
||||
}
|
||||
|
||||
// Generate reset token
|
||||
$tid = token_id();
|
||||
$secret = token_secret();
|
||||
$hash = token_hash($secret);
|
||||
|
||||
$k = rkey("token:reset:$tid");
|
||||
redis()->hMSet($k, ['uid' => $uuid, 'hash' => $hash]);
|
||||
redis()->expire($k, 60 * 60); // 1 hour
|
||||
|
||||
$base = rtrim(cfg()['base_url'], '/');
|
||||
$link = $base . '/auth/reset_password.php?tid=' . urlencode($tid) . '&token=' . urlencode($secret);
|
||||
|
||||
$name = $acct['firstname'] ?? $callsign;
|
||||
$body = "Hello {$name},\n\n";
|
||||
$body .= "A password reset was requested for your SvxPortal account ({$callsign}).\n\n";
|
||||
$body .= "Click the link below to set a new password:\n\n";
|
||||
$body .= "{$link}\n\n";
|
||||
$body .= "This link is valid for 1 hour.\n\n";
|
||||
$body .= "If you did not request this, you can ignore this email.\n\n";
|
||||
$body .= "73,\nSvxPortal";
|
||||
|
||||
mail_send($email, "Password reset - SvxPortal", $body);
|
||||
|
||||
json_out(['ok' => true, 'message' => 'If the account exists, a reset link has been sent.']);
|
||||
Reference in New Issue
Block a user