diff --git a/backend/src/controllers/auth.js b/backend/src/controllers/auth.js
index 83fe035..74ea314 100644
--- a/backend/src/controllers/auth.js
+++ b/backend/src/controllers/auth.js
@@ -660,6 +660,50 @@ async function resetPassword(req, res, next) {
}
}
+// Check username/email availability (for real-time validation)
+async function checkAvailability(req, res, next) {
+ try {
+ const { username, email } = req.query;
+
+ if (!username && !email) {
+ return res.status(400).json({
+ success: false,
+ error: 'Username or email is required',
+ });
+ }
+
+ const result = {
+ usernameAvailable: true,
+ emailAvailable: true,
+ };
+
+ // Check username availability
+ if (username) {
+ const existingUsername = await prisma.user.findUnique({
+ where: { username },
+ select: { id: true },
+ });
+ result.usernameAvailable = !existingUsername;
+ }
+
+ // Check email availability
+ if (email) {
+ const existingEmail = await prisma.user.findUnique({
+ where: { email },
+ select: { id: true },
+ });
+ result.emailAvailable = !existingEmail;
+ }
+
+ res.json({
+ success: true,
+ data: result,
+ });
+ } catch (error) {
+ next(error);
+ }
+}
+
module.exports = {
register,
login,
@@ -668,4 +712,5 @@ module.exports = {
resendVerification,
requestPasswordReset,
resetPassword,
+ checkAvailability,
};
diff --git a/backend/src/routes/auth.js b/backend/src/routes/auth.js
index a524a49..f535cb7 100644
--- a/backend/src/routes/auth.js
+++ b/backend/src/routes/auth.js
@@ -6,7 +6,8 @@ const {
verifyEmailByCode,
resendVerification,
requestPasswordReset,
- resetPassword
+ resetPassword,
+ checkAvailability
} = require('../controllers/auth');
const {
registerValidation,
@@ -39,4 +40,7 @@ router.post('/request-password-reset', emailLimiter, requestPasswordReset);
// POST /api/auth/reset-password - Reset password with token
router.post('/reset-password', passwordResetValidation, resetPassword);
+// GET /api/auth/check-availability?username=xxx&email=xxx - Check username/email availability
+router.get('/check-availability', checkAvailability);
+
module.exports = router;
diff --git a/frontend/src/pages/RegisterPage.jsx b/frontend/src/pages/RegisterPage.jsx
index 19657cc..6f872e7 100644
--- a/frontend/src/pages/RegisterPage.jsx
+++ b/frontend/src/pages/RegisterPage.jsx
@@ -1,7 +1,7 @@
import { useState, useEffect, useRef } from 'react';
import { useNavigate, Link } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';
-import { wsdcAPI } from '../services/api';
+import { wsdcAPI, authAPI } from '../services/api';
import { Video, Mail, Lock, User, Hash, ArrowRight, ArrowLeft, Loader2, CheckCircle, XCircle, AlertCircle } from 'lucide-react';
import PasswordStrengthIndicator from '../components/common/PasswordStrengthIndicator';
import FormInput from '../components/common/FormInput';
@@ -36,6 +36,12 @@ const RegisterPage = () => {
const [turnstileToken, setTurnstileToken] = useState('');
const turnstileRef = useRef(null);
+ // Availability validation state
+ const [usernameAvailable, setUsernameAvailable] = useState(null);
+ const [emailAvailable, setEmailAvailable] = useState(null);
+ const [checkingUsername, setCheckingUsername] = useState(false);
+ const [checkingEmail, setCheckingEmail] = useState(false);
+
const { register } = useAuth();
const navigate = useNavigate();
@@ -133,6 +139,50 @@ const RegisterPage = () => {
};
}, [step]);
+ // Check username availability
+ useEffect(() => {
+ const checkUsername = async () => {
+ if (formData.username.length >= 3) {
+ setCheckingUsername(true);
+ try {
+ const result = await authAPI.checkAvailability(formData.username, null);
+ setUsernameAvailable(result.usernameAvailable);
+ } catch (error) {
+ console.error('Error checking username:', error);
+ } finally {
+ setCheckingUsername(false);
+ }
+ } else {
+ setUsernameAvailable(null);
+ }
+ };
+
+ const timeoutId = setTimeout(checkUsername, 500); // Debounce 500ms
+ return () => clearTimeout(timeoutId);
+ }, [formData.username]);
+
+ // Check email availability
+ useEffect(() => {
+ const checkEmail = async () => {
+ if (formData.email && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
+ setCheckingEmail(true);
+ try {
+ const result = await authAPI.checkAvailability(null, formData.email);
+ setEmailAvailable(result.emailAvailable);
+ } catch (error) {
+ console.error('Error checking email:', error);
+ } finally {
+ setCheckingEmail(false);
+ }
+ } else {
+ setEmailAvailable(null);
+ }
+ };
+
+ const timeoutId = setTimeout(checkEmail, 500); // Debounce 500ms
+ return () => clearTimeout(timeoutId);
+ }, [formData.email]);
+
// Handle WSDC ID confirmation and continue to registration
const handleWsdcContinue = () => {
if (!wsdcPreview) {
@@ -414,27 +464,71 @@ const RegisterPage = () => {
required
/>
-