false, 'error' => 'Callsign, first name, email and password are required.'], 400); } if (strlen($callsign) < 3 || strlen($callsign) > 10) { json_out(['ok' => false, 'error' => 'Callsign must be 3-10 characters.'], 400); } if (!preg_match('/^[A-Z0-9]+$/', $callsign)) { json_out(['ok' => false, 'error' => 'Callsign must contain only letters and numbers.'], 400); } if ($pass1 !== $pass2) { json_out(['ok' => false, 'error' => 'Passwords do not match.'], 400); } if (strlen($pass1) < 6) { json_out(['ok' => false, 'error' => 'Password must be at least 6 characters.'], 400); } if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { json_out(['ok' => false, 'error' => 'Invalid email address.'], 400); } // Global callsign ban blocks registration if (is_call_banned($callsign)) { json_out(['ok' => false, 'error' => 'This callsign is not permitted to register.'], 403); } // Check duplicates $r = redis(); $existing_cs = $r->get(call_index_key($callsign)); if (is_string($existing_cs) && $existing_cs !== '') { json_out(['ok' => false, 'error' => 'Callsign or email already registered.'], 409); } $existing_em = $r->get(email_index_key($email)); if (is_string($existing_em) && $existing_em !== '') { json_out(['ok' => false, 'error' => 'Callsign or email already registered.'], 409); } // Create account $uuid = uuidv4(); $now = (string)time(); $r->hMSet(acct_key($uuid), [ 'callsign' => $callsign, 'firstname' => $firstname, 'lastname' => $lastname, 'shortname' => $firstname, 'email' => $email, 'city' => $city, 'state' => $state, 'country' => $country, 'password_hash' => password_hash($pass1, PASSWORD_DEFAULT), 'email_verified' => '0', 'acct_flags' => '1', 'created_at' => $now, ]); // Create indexes $r->set(call_index_key($callsign), $uuid); $r->set(email_index_key($email), $uuid); // Generate email verification token $tid = token_id(); $secret = token_secret(); $hash = token_hash($secret); $k = rkey("token:verify:$tid"); $r->hMSet($k, ['uid' => $uuid, 'hash' => $hash]); $r->expire($k, 60 * 60 * 24); // Send verification email $base = rtrim(cfg()['base_url'], '/'); $link = $base . '/auth/verify.php?tid=' . urlencode($tid) . '&token=' . urlencode($secret); $body = "Hello {$firstname},\n\n"; $body .= "Thank you for registering on SvxPortal with callsign {$callsign}.\n\n"; $body .= "Please verify your email address by clicking the link below:\n\n"; $body .= "{$link}\n\n"; $body .= "This link is valid for 24 hours.\n\n"; $body .= "After email verification, an administrator will review and activate your account.\n\n"; $body .= "73,\nSvxPortal"; $mail_sent = mail_send($email, "Verify your email - SvxPortal", $body); json_out(['ok' => true, 'callsign' => $callsign, 'email_sent' => $mail_sent]);