1<?php
2require_once __DIR__ . '/../includes/bootstrap.php';
3if ($USER) go('/');
4if (cfg('allow_reg','1') !== '1') $disabled = true;
5
6$errs = [];
7
8
9
10
11if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
12 $captcha = captcha_generate();
13} else {
14
15
16 $captcha = ['q' => ''];
17}
18
19if ($_SERVER['REQUEST_METHOD'] === 'POST' && !isset($disabled)) {
20 if (!csrf_ok()) {
21 $errs[] = 'Invalid request. Please refresh and try again.';
22 $captcha = captcha_generate();
23 } else {
24 $name = sanitise(post('username'));
25 $email = sanitise(post('email'));
26 $pass = post('password');
27 $pass2 = post('password2');
28 $cap = post('captcha');
29
30
31 if (!captcha_verify($cap)) {
32 $errs[] = 'Incorrect answer to the security question. Please try again.';
33 $captcha = captcha_generate();
34 }
35
36
37 if (!$errs) {
38 if (strlen($name) < 3 || strlen($name) > 30) $errs[] = 'Username must be 3โ30 characters.';
39 if (!preg_match('/^[a-zA-Z0-9_\-]+$/', $name)) $errs[] = 'Username: letters, numbers, _ and - only.';
40 if (!filter_var($email, FILTER_VALIDATE_EMAIL)) $errs[] = 'Invalid email address.';
41 if (strlen($pass) < 8) $errs[] = 'Password must be at least 8 characters.';
42 if ($pass !== $pass2) $errs[] = 'Passwords do not match.';
43 }
44
45 if (!$errs) {
46 if (DB::row('SELECT id FROM users WHERE username=? OR email=?', [$name, $email])) {
47 $errs[] = 'Username or email is already taken.';
48 $captcha = captcha_generate();
49 } else {
50 $id = DB::insert(
51 'INSERT INTO users (username, email, password) VALUES (?, ?, ?)',
52 [$name, $email, password_hash($pass, PASSWORD_BCRYPT, ['cost' => 12])]
53 );
54 addon_hook('after_user_registered', ['user_id'=>$id,'username'=>$name,'email'=>$email]);
55 login_user($id);
56 go('/');
57 }
58 }
59
60
61
62 if ($errs && empty(array_filter($errs, fn($e) => str_contains($e, 'security')))) {
63 if (!isset($captcha['q']) || $captcha['q'] === '') {
64 $captcha = captcha_generate();
65 }
66 }
67 }
68}
69?><!DOCTYPE html>
70<html lang="en">
71<head>
72 <meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
73 <title>Sign Up โ <?= e(cfg('site_name','Nexus Forum')) ?></title>
74 <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
75 <link rel="stylesheet" href="<?= asset('css/main.css') ?>">
76</head>
77<body class="auth-body">
78<div class="auth-page">
79 <div class="auth-card">
80 <a href="<?= u('/') ?>" class="auth-logo">
81 <div class="logo-mark"><?= e(substr(cfg('site_name','N'),0,1)) ?></div>
82 <span><?= e(cfg('site_name','Nexus Forum')) ?></span>
83 </a>
84 <h1>Create an account</h1>
85 <p class="auth-sub">Join the community</p>
86
87 <?php if (!empty($errs)): ?>
88 <div class="alert err"><?= implode('<br>', array_map('e', $errs)) ?></div>
89 <?php endif; ?>
90
91 <?php if (isset($disabled)): ?>
92 <div class="alert warn">Registration is currently disabled.</div>
93 <?php else: ?>
94 <form method="POST" autocomplete="off">
95 <?= csrf_input() ?>
96 <div class="fg">
97 <label>Username</label>
98 <input type="text" name="username" class="fi" required autofocus
99 value="<?= e($_POST['username'] ?? '') ?>"
100 placeholder="your_username" minlength="3" maxlength="30">
101 <span class="hint">Letters, numbers, _ and - only</span>
102 </div>
103 <div class="fg">
104 <label>Email</label>
105 <input type="email" name="email" class="fi" required
106 value="<?= e($_POST['email'] ?? '') ?>" placeholder="you@example.com">
107 </div>
108 <div class="fg">
109 <label>Password</label>
110 <div class="pw-row">
111 <input type="password" id="pw1" name="password" class="fi" required
112 placeholder="Min. 8 characters" minlength="8">
113 <button type="button" class="pw-eye" onclick="togglePwd('pw1')">๐</button>
114 </div>
115 <div class="pw-bar"><div class="pw-fill" id="pwFill"></div></div>
116 <span class="hint" id="pwHint"></span>
117 </div>
118 <div class="fg">
119 <label>Confirm Password</label>
120 <div class="pw-row">
121 <input type="password" id="pw2" name="password2" class="fi" required
122 placeholder="Repeat password">
123 <button type="button" class="pw-eye" onclick="togglePwd('pw2')">๐</button>
124 </div>
125 </div>
126
127 <!-- Math Captcha -->
128 <div class="captcha-box">
129 <div class="captcha-label">
130 ๐ Security check โ What is
131 <strong class="captcha-q"><?= e($captcha['q']) ?></strong>
132 </div>
133 <input type="number" name="captcha" class="fi captcha-input"
134 required placeholder="Your answer" autocomplete="off">
135 <span class="hint">Solve this simple math problem to continue</span>
136 </div>
137
138 <button type="submit" class="btn-primary btn-block" style="margin-top:16px">
139 Create Account
140 </button>
141 </form>
142 <?php endif; ?>
143 <p class="auth-foot">Have an account? <a href="<?= u('auth/login.php') ?>">Sign in โ</a></p>
144 </div>
145 <p class="auth-back"><a href="<?= u('/') ?>">โ Back to forum</a></p>
146</div>
147<script>
148function togglePwd(id) {
149 var e = document.getElementById(id);
150 e.type = e.type === 'password' ? 'text' : 'password';
151}
152var pw = document.getElementById('pw1');
153if (pw) pw.addEventListener('input', function() {
154 var p=this.value, f=document.getElementById('pwFill'), h=document.getElementById('pwHint'), s=0;
155 if (p.length >= 8) s++;
156 if (p.length >= 12) s++;
157 if (/[A-Z]/.test(p)) s++;
158 if (/[0-9]/.test(p)) s++;
159 if (/[^A-Za-z0-9]/.test(p)) s++;
160 f.style.width = (s / 5 * 100) + '%';
161 var cols = ['','#ef4444','#f59e0b','#f59e0b','#22c55e','#22c55e'];
162 f.style.background = cols[s] || '#22c55e';
163 h.textContent = ['','Weak','Fair','Good','Strong','Very strong'][s] || '';
164 h.style.color = f.style.background;
165});
166</script>
167</body>
168</html>