-
Notifications
You must be signed in to change notification settings - Fork 200
fix: allow valid GitHub usernames with hyphens and numbers #616
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -39,8 +39,8 @@ const SignUp: React.FC = () => { | |||||||||||||||||||||||||||||||||||
| if (name === "username") { | ||||||||||||||||||||||||||||||||||||
| if (!value.trim()) { | ||||||||||||||||||||||||||||||||||||
| errorMessage = "Username is required"; | ||||||||||||||||||||||||||||||||||||
| } else if (!/^[A-Za-z\s]+$/.test(value)) { | ||||||||||||||||||||||||||||||||||||
| errorMessage = "Only letters are allowed"; | ||||||||||||||||||||||||||||||||||||
| } else if (!/^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/.test(value)) { | ||||||||||||||||||||||||||||||||||||
| errorMessage = "Enter a valid GitHub username"; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| if (name === "email") { | ||||||||||||||||||||||||||||||||||||
|
|
@@ -53,8 +53,11 @@ const SignUp: React.FC = () => { | |||||||||||||||||||||||||||||||||||
| if (name === "password") { | ||||||||||||||||||||||||||||||||||||
| if (!value.trim()) { | ||||||||||||||||||||||||||||||||||||
| errorMessage = "Password is required"; | ||||||||||||||||||||||||||||||||||||
| } else if (!/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/.test(value)) { | ||||||||||||||||||||||||||||||||||||
| errorMessage = "Password must be 8+ characters with letters and numbers"; | ||||||||||||||||||||||||||||||||||||
| } else if ( | ||||||||||||||||||||||||||||||||||||
| !/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/.test(value) | ||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||
| errorMessage = | ||||||||||||||||||||||||||||||||||||
| "Password must be 8+ characters with letters and numbers"; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| setErrors((prev) => ({ ...prev, [name]: errorMessage })); | ||||||||||||||||||||||||||||||||||||
|
|
@@ -64,36 +67,46 @@ const SignUp: React.FC = () => { | |||||||||||||||||||||||||||||||||||
| e.preventDefault(); | ||||||||||||||||||||||||||||||||||||
| const usernameError = !formData.username.trim() | ||||||||||||||||||||||||||||||||||||
| ? "Username is required" | ||||||||||||||||||||||||||||||||||||
| : !/^[A-Za-z\s]+$/.test(formData.username) | ||||||||||||||||||||||||||||||||||||
| ? "Only letters are allowed" | ||||||||||||||||||||||||||||||||||||
| : ""; | ||||||||||||||||||||||||||||||||||||
| : !/^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/.test(formData.username) | ||||||||||||||||||||||||||||||||||||
| ? "Enter a valid GitHub username" | ||||||||||||||||||||||||||||||||||||
| : ""; | ||||||||||||||||||||||||||||||||||||
| const emailError = !formData.email.trim() | ||||||||||||||||||||||||||||||||||||
| ? "Email is required" | ||||||||||||||||||||||||||||||||||||
| : !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email.trim()) | ||||||||||||||||||||||||||||||||||||
| ? "Enter a valid email" | ||||||||||||||||||||||||||||||||||||
| : ""; | ||||||||||||||||||||||||||||||||||||
| ? "Enter a valid email" | ||||||||||||||||||||||||||||||||||||
| : ""; | ||||||||||||||||||||||||||||||||||||
| const passwordError = !formData.password.trim() | ||||||||||||||||||||||||||||||||||||
| ? "Password is required" | ||||||||||||||||||||||||||||||||||||
| : !/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/.test(formData.password) | ||||||||||||||||||||||||||||||||||||
| ? "Password must be 8+ characters with letters and numbers" | ||||||||||||||||||||||||||||||||||||
| : ""; | ||||||||||||||||||||||||||||||||||||
| : !/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/.test( | ||||||||||||||||||||||||||||||||||||
| formData.password, | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| ? "Password must be 8+ characters with letters and numbers" | ||||||||||||||||||||||||||||||||||||
| : ""; | ||||||||||||||||||||||||||||||||||||
| if (usernameError || emailError || passwordError) { | ||||||||||||||||||||||||||||||||||||
| setErrors({ username: usernameError, email: emailError, password: passwordError }); | ||||||||||||||||||||||||||||||||||||
| setErrors({ | ||||||||||||||||||||||||||||||||||||
| username: usernameError, | ||||||||||||||||||||||||||||||||||||
| email: emailError, | ||||||||||||||||||||||||||||||||||||
| password: passwordError, | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| setIsLoading(true); | ||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||
| const response = await axios.post(`${backendUrl}/api/auth/signup`, | ||||||||||||||||||||||||||||||||||||
| formData // Include cookies for session | ||||||||||||||||||||||||||||||||||||
| const response = await axios.post( | ||||||||||||||||||||||||||||||||||||
| `${backendUrl}/api/auth/signup`, | ||||||||||||||||||||||||||||||||||||
| formData, // Include cookies for session | ||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||
| setMessage(response.data.message); // Show success message from backend | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // Navigate to login page after successful signup | ||||||||||||||||||||||||||||||||||||
| if (response.data.message === 'User created successfully') { | ||||||||||||||||||||||||||||||||||||
| if (response.data.message === "User created successfully") { | ||||||||||||||||||||||||||||||||||||
| navigate("/login"); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } catch (error: any) { | ||||||||||||||||||||||||||||||||||||
| setMessage(error.response?.data?.message || "Something went wrong. Please try again."); | ||||||||||||||||||||||||||||||||||||
| setMessage( | ||||||||||||||||||||||||||||||||||||
| error.response?.data?.message || | ||||||||||||||||||||||||||||||||||||
| "Something went wrong. Please try again.", | ||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
105
to
+109
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify there are no explicit `any` catch parameters in TS/TSX files.
rg -nP --type=ts --type=tsx 'catch\s*\(\s*[A-Za-z_$][A-Za-z0-9_$]*\s*:\s*any\s*\)'Repository: GitMetricsLab/github_tracker Length of output: 100 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the exact lines around the reported catch
echo "---- src/pages/Signup/Signup.tsx (around 100-115) ----"
nl -ba src/pages/Signup/Signup.tsx | sed -n '90,125p'
# Search for explicit `any` in catch parameters across the repo
echo "---- Searching for `catch (..: any)` in .ts/.tsx ----"
rg -n --glob='*.ts' --glob='*.tsx' -P 'catch\s*\(\s*[^)]*:\s*any\s*\)'Repository: GitMetricsLab/github_tracker Length of output: 188 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "---- src/pages/Signup/Signup.tsx (around 100-115) ----"
cat -n src/pages/Signup/Signup.tsx | sed -n '90,125p'
echo "---- Searching for `catch (..: any)` in .ts/.tsx ----"
rg -n --glob='*.ts' --glob='*.tsx' -P 'catch\s*\(\s*[^)]*:\s*any\s*\)'Repository: GitMetricsLab/github_tracker Length of output: 1711 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "---- src/pages/Signup/Signup.tsx (around 100-115) ----"
cat -n src/pages/Signup/Signup.tsx | sed -n '100,112p'
echo "---- Searching for explicit typed `any` in catch parameters ----"
rg -n --glob='*.ts' --glob='*.tsx' -P 'catch\s*\(\s*[^)]*:\s*any\s*\)' .
echo "---- Searching ESLint for no-explicit-any rule ----"
rg -n "no-explicit-any" .eslintrc* package.json eslint.* --hidden || true
echo "---- Searching tsconfig for useUnknownInCatchVariables ----"
rg -n "useUnknownInCatchVariables" tsconfig*.json . --hidden || trueRepository: GitMetricsLab/github_tracker Length of output: 919 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "---- repo root ----"
ls -a
echo "---- Searching for explicit typed any in catch parameters (.ts/.tsx) ----"
rg -n --glob='*.ts' --glob='*.tsx' -P 'catch\s*\(\s*[^)]*:\s*any\s*\)' .
echo "---- Searching for ESLint rule no-explicit-any in repo ----"
rg -n "no-explicit-any" . || true
echo "---- Searching for TS compiler option useUnknownInCatchVariables in tsconfig ----"
rg -n "useUnknownInCatchVariables" tsconfig*.json . || true
echo "---- Searching for `@typescript-eslint` in package.json ----"
cat package.json | head -n 80Repository: GitMetricsLab/github_tracker Length of output: 2796 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "---- src/pages/Signup/Signup.tsx imports (top) ----"
sed -n '1,60p' src/pages/Signup/Signup.tsx
echo "---- eslint.config.js (relevant excerpts) ----"
sed -n '1,200p' eslint.config.js
echo "---- tsconfig.json (relevant excerpts) ----"
cat tsconfig.json
echo "---- tsconfig.app.json (relevant excerpts) ----"
cat tsconfig.app.json
echo "---- tsconfig.node.json (relevant excerpts) ----"
cat tsconfig.node.jsonRepository: GitMetricsLab/github_tracker Length of output: 4439 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "---- Running eslint on src/pages/Signup/Signup.tsx ----"
npx eslint src/pages/Signup/Signup.tsxRepository: GitMetricsLab/github_tracker Length of output: 306 Replace
Proposed fix+import type { AxiosError } from "axios";
...
- } catch (error: any) {
- setMessage(
- error.response?.data?.message ||
- "Something went wrong. Please try again.",
- );
+ } catch (error: unknown) {
+ const axiosError = error as AxiosError<{ message?: string }>;
+ setMessage(
+ axiosError.response?.data?.message ??
+ "Something went wrong. Please try again.",
+ );
} finally {📝 Committable suggestion
Suggested change
🧰 Tools🪛 ESLint[error] 105-105: Unexpected any. Specify a different type. ( 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
| } finally { | ||||||||||||||||||||||||||||||||||||
| setIsLoading(false); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
@@ -138,10 +151,22 @@ const SignUp: React.FC = () => { | |||||||||||||||||||||||||||||||||||
| className="text-center mb-10" | ||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||
| <div className="inline-flex items-center justify-center w-20 h-20 bg-white rounded-3xl mb-6 shadow-2xl transform hover:scale-105 transition-transform duration-300 overflow-hidden"> | ||||||||||||||||||||||||||||||||||||
| <img src="/crl-icon.png" alt="Logo" className="w-14 h-14 object-contain" /> | ||||||||||||||||||||||||||||||||||||
| <img | ||||||||||||||||||||||||||||||||||||
| src="/crl-icon.png" | ||||||||||||||||||||||||||||||||||||
| alt="Logo" | ||||||||||||||||||||||||||||||||||||
| className="w-14 h-14 object-contain" | ||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||
| <h1 className={`text-4xl font-bold mb-2 ${mode === "dark" ? "text-white" : "text-black"}`}>GitHubTracker</h1> | ||||||||||||||||||||||||||||||||||||
| <p className={`text-lg font-medium ${mode === "dark" ? "text-slate-300" : "text-gray-700"}`}>Join your GitHub journey</p> | ||||||||||||||||||||||||||||||||||||
| <h1 | ||||||||||||||||||||||||||||||||||||
| className={`text-4xl font-bold mb-2 ${mode === "dark" ? "text-white" : "text-black"}`} | ||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||
| GitHubTracker | ||||||||||||||||||||||||||||||||||||
| </h1> | ||||||||||||||||||||||||||||||||||||
| <p | ||||||||||||||||||||||||||||||||||||
| className={`text-lg font-medium ${mode === "dark" ? "text-slate-300" : "text-gray-700"}`} | ||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||
| Join your GitHub journey | ||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||
| </motion.div> | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| <motion.div | ||||||||||||||||||||||||||||||||||||
|
|
@@ -154,7 +179,6 @@ const SignUp: React.FC = () => { | |||||||||||||||||||||||||||||||||||
| : "bg-white border-gray-200 text-black" | ||||||||||||||||||||||||||||||||||||
| }`} | ||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| <h2 | ||||||||||||||||||||||||||||||||||||
| className={`text-2xl font-bold text-center mb-8 ${ | ||||||||||||||||||||||||||||||||||||
| mode === "dark" ? "text-white" : "text-gray-800" | ||||||||||||||||||||||||||||||||||||
|
|
@@ -169,39 +193,72 @@ const SignUp: React.FC = () => { | |||||||||||||||||||||||||||||||||||
| <div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none"> | ||||||||||||||||||||||||||||||||||||
| <User className="h-5 w-5 text-gray-400" /> | ||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||
| <input type="text" name="username" placeholder="Enter your username" value={formData.username} onChange={handleChange} required | ||||||||||||||||||||||||||||||||||||
| <input | ||||||||||||||||||||||||||||||||||||
| type="text" | ||||||||||||||||||||||||||||||||||||
| name="username" | ||||||||||||||||||||||||||||||||||||
| placeholder="Enter your username" | ||||||||||||||||||||||||||||||||||||
| value={formData.username} | ||||||||||||||||||||||||||||||||||||
| onChange={handleChange} | ||||||||||||||||||||||||||||||||||||
| required | ||||||||||||||||||||||||||||||||||||
| className={`w-full pl-12 pr-4 py-4 rounded-2xl border focus:outline-none focus:ring-2 focus:ring-gray-400 focus:border-transparent transition-all duration-300 ${mode === "dark" ? "bg-white/10 border-white/20 text-white placeholder-gray-400" : "bg-gray-100 border-gray-300 text-black placeholder-gray-400"}`} | ||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||
| {errors.username && <p className="text-red-500 text-sm mt-2">{errors.username}</p>} | ||||||||||||||||||||||||||||||||||||
| {errors.username && ( | ||||||||||||||||||||||||||||||||||||
| <p className="text-red-500 text-sm mt-2">{errors.username}</p> | ||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| <div> | ||||||||||||||||||||||||||||||||||||
| <div className="relative"> | ||||||||||||||||||||||||||||||||||||
| <div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none"> | ||||||||||||||||||||||||||||||||||||
| <Mail className="h-5 w-5 text-gray-400" /> | ||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||
| <input type="email" name="email" placeholder="Enter your email" value={formData.email} onChange={handleChange} required | ||||||||||||||||||||||||||||||||||||
| <input | ||||||||||||||||||||||||||||||||||||
| type="email" | ||||||||||||||||||||||||||||||||||||
| name="email" | ||||||||||||||||||||||||||||||||||||
| placeholder="Enter your email" | ||||||||||||||||||||||||||||||||||||
| value={formData.email} | ||||||||||||||||||||||||||||||||||||
| onChange={handleChange} | ||||||||||||||||||||||||||||||||||||
| required | ||||||||||||||||||||||||||||||||||||
| className={`w-full pl-12 pr-4 py-4 rounded-2xl border focus:outline-none focus:ring-2 focus:ring-gray-400 focus:border-transparent transition-all duration-300 ${mode === "dark" ? "bg-white/10 border-white/20 text-white placeholder-gray-400" : "bg-gray-100 border-gray-300 text-black placeholder-gray-400"}`} | ||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||
| {errors.email && <p className="text-red-500 text-sm mt-2">{errors.email}</p>} | ||||||||||||||||||||||||||||||||||||
| {errors.email && ( | ||||||||||||||||||||||||||||||||||||
| <p className="text-red-500 text-sm mt-2">{errors.email}</p> | ||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| <div> | ||||||||||||||||||||||||||||||||||||
| <div className="relative"> | ||||||||||||||||||||||||||||||||||||
| <div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none"> | ||||||||||||||||||||||||||||||||||||
| <Lock className="h-5 w-5 text-gray-400" /> | ||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||
| <input type={showPassword ? "text" : "password"} name="password" placeholder="Enter your password" value={formData.password} onChange={handleChange} required | ||||||||||||||||||||||||||||||||||||
| <input | ||||||||||||||||||||||||||||||||||||
| type={showPassword ? "text" : "password"} | ||||||||||||||||||||||||||||||||||||
| name="password" | ||||||||||||||||||||||||||||||||||||
| placeholder="Enter your password" | ||||||||||||||||||||||||||||||||||||
| value={formData.password} | ||||||||||||||||||||||||||||||||||||
| onChange={handleChange} | ||||||||||||||||||||||||||||||||||||
| required | ||||||||||||||||||||||||||||||||||||
| className={`w-full pl-12 pr-12 py-4 rounded-2xl border focus:outline-none focus:ring-2 focus:ring-gray-400 focus:border-transparent transition-all duration-300 ${mode === "dark" ? "bg-white/10 border-white/20 text-white placeholder-gray-400" : "bg-gray-100 border-gray-300 text-black placeholder-gray-400"}`} | ||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||
| <button type="button" onClick={() => setShowPassword(!showPassword)} aria-label={showPassword ? "Hide password" : "Show password"} aria-pressed={showPassword} | ||||||||||||||||||||||||||||||||||||
| className={`absolute inset-y-0 right-0 pr-4 flex items-center transition-colors duration-200 ${mode === "dark" ? "text-slate-400 hover:text-white" : "text-gray-500 hover:text-gray-800"}`}> | ||||||||||||||||||||||||||||||||||||
| {showPassword ? <EyeOff className="h-5 w-5" /> : <Eye className="h-5 w-5" />} | ||||||||||||||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||||||||||||||
| type="button" | ||||||||||||||||||||||||||||||||||||
| onClick={() => setShowPassword(!showPassword)} | ||||||||||||||||||||||||||||||||||||
| aria-label={showPassword ? "Hide password" : "Show password"} | ||||||||||||||||||||||||||||||||||||
| aria-pressed={showPassword} | ||||||||||||||||||||||||||||||||||||
| className={`absolute inset-y-0 right-0 pr-4 flex items-center transition-colors duration-200 ${mode === "dark" ? "text-slate-400 hover:text-white" : "text-gray-500 hover:text-gray-800"}`} | ||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||
| {showPassword ? ( | ||||||||||||||||||||||||||||||||||||
| <EyeOff className="h-5 w-5" /> | ||||||||||||||||||||||||||||||||||||
| ) : ( | ||||||||||||||||||||||||||||||||||||
| <Eye className="h-5 w-5" /> | ||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||
| {errors.password && <p className="text-red-500 text-sm mt-2">{errors.password}</p>} | ||||||||||||||||||||||||||||||||||||
| {errors.password && ( | ||||||||||||||||||||||||||||||||||||
| <p className="text-red-500 text-sm mt-2">{errors.password}</p> | ||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||||||||||||||
|
|
@@ -214,15 +271,20 @@ const SignUp: React.FC = () => { | |||||||||||||||||||||||||||||||||||
| </form> | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| {message && ( | ||||||||||||||||||||||||||||||||||||
| <div className={`text-center mt-6 p-3 rounded-xl ${message.includes("successfully") ? "text-green-600 bg-green-100" : "text-red-600 bg-red-100"}`}> | ||||||||||||||||||||||||||||||||||||
| <div | ||||||||||||||||||||||||||||||||||||
| className={`text-center mt-6 p-3 rounded-xl ${message.includes("successfully") ? "text-green-600 bg-green-100" : "text-red-600 bg-red-100"}`} | ||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||
| {message} | ||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| <div className="text-center mt-8"> | ||||||||||||||||||||||||||||||||||||
| <p className={mode === "dark" ? "text-gray-300" : "text-gray-600"}> | ||||||||||||||||||||||||||||||||||||
| Already have an account?{" "} | ||||||||||||||||||||||||||||||||||||
| <Link to="/login" className={`font-medium hover:underline transition-colors duration-300 ${mode === "dark" ? "text-white" : "text-black"}`}> | ||||||||||||||||||||||||||||||||||||
| <Link | ||||||||||||||||||||||||||||||||||||
| to="/login" | ||||||||||||||||||||||||||||||||||||
| className={`font-medium hover:underline transition-colors duration-300 ${mode === "dark" ? "text-white" : "text-black"}`} | ||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||
| Sign in here | ||||||||||||||||||||||||||||||||||||
| </Link> | ||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Username regex still accepts invalid GitHub usernames.
Line 42 and Line 70 allow consecutive hyphens and unlimited length, which are not GitHub-compatible. This will still pass some invalid usernames client-side.
Proposed fix
Also applies to: 70-72
🤖 Prompt for AI Agents