From 08fd3da48f4c14b121f80194fdb09e80ba356ac7 Mon Sep 17 00:00:00 2001 From: Saumya-249 Date: Thu, 15 Jan 2026 23:18:38 +0530 Subject: [PATCH 01/17] Update Register page with the modified frontend and backend --- savebook/app/(auth)/register/page.js | 238 +++++++++++------------- savebook/app/api/auth/register/route.js | 65 ++++--- savebook/context/auth/AuthState.js | 2 +- savebook/package-lock.json | 24 +-- savebook/package.json | 2 +- 5 files changed, 157 insertions(+), 174 deletions(-) diff --git a/savebook/app/(auth)/register/page.js b/savebook/app/(auth)/register/page.js index f793b78..30dff6f 100644 --- a/savebook/app/(auth)/register/page.js +++ b/savebook/app/(auth)/register/page.js @@ -1,36 +1,16 @@ -"use client" -import { useAuth } from '@/context/auth/authContext'; -import { useRouter } from 'next/navigation'; -import React, { useState, useEffect } from 'react'; -import toast from 'react-hot-toast'; -import Link from 'next/link'; +"use client"; +import React, { useState, useEffect } from "react"; +import { useRouter } from "next/navigation"; +import Link from "next/link"; +import toast from "react-hot-toast"; +import { useAuth } from "@/context/auth/authContext"; import { Eye, EyeOff } from 'lucide-react'; // Signup Form Component -const SignupForm = () => { - const { register, isAuthenticated } = useAuth(); - const [credentials, setCredentials] = useState({ - username: '', - password: '', - confirmPassword: '' - }); - const [errors, setErrors] = useState({ - username: '', - password: '', - confirmPassword: '' - }); - const [isLoading, setIsLoading] = useState(false); - const [showPassword, setShowPassword] = useState(false); - const [showConfirmPassword, setShowConfirmPassword] = useState(false); - const router = useRouter(); - - // Handle redirection based on authentication status - useEffect(() => { - if (isAuthenticated) { - router.push("/"); - } - }, [isAuthenticated, router]); +export default async function SignupPage() { + const { register, isAuthenticated } = useAuth(); + const router = useRouter(); const handleSubmit = async (e) => { e.preventDefault(); @@ -141,11 +121,48 @@ const SignupForm = () => { setErrors({ ...errors, [e.target.name]: '' }); }; - // Show loading state while checking authentication - if (isAuthenticated === undefined) { - return ; + const newErrors = {}; + if (!credentials.username.trim()) { + newErrors.username = "Username is required"; + } + if (!credentials.password) { + newErrors.password = "Password is required"; + } else if (credentials.password.length < 6) { + newErrors.password = "Password must be at least 6 characters"; + } + if (!credentials.confirmPassword) { + newErrors.confirmPassword = "Confirm password is required"; + } else if (credentials.password !== credentials.confirmPassword) { + newErrors.confirmPassword = "Passwords do not match"; + } + + if (Object.keys(newErrors).length > 0) { + setErrors(newErrors); + return; + } + + setIsLoading(true); + try { + const result = await register(credentials.username, credentials.password, credentials.confirmPassword); + + if (result?.success) { + setSuccessMessage("Account created successfully! πŸŽ‰ Redirecting..."); + setErrorMessage(""); //clear any previous error + setTimeout(() => router.push("/login"), 1500); + } else { + setSuccessMessage(""); // clear success + setErrorMessage(result?.message || "Registration failed"); + } + } catch (error) { + setSuccessMessage(""); + setErrorMessage("Something went wrong"); + } finally { + setIsLoading(false); } + }; + // Skeleton loader + if (isAuthenticated === undefined) { return (
{/* Username Field */} @@ -243,11 +260,57 @@ const SignupForm = () => { )} - {/* Submit Button */} + + {/* Username */} +
+ + + {errors.username && ( +

{errors.username}

+ )} + + {usernameMessage && !errors.username && ( +

{usernameMessage}

+ )} +
+ + {/* Password */} +
+ + + {errors.password &&

{errors.password}

} +
{/* Login link */}
@@ -280,102 +346,6 @@ const SignupForm = () => {
- ); -} - -// Signup Skeleton Component for loading state -const SignupFormSkeleton = () => { - return ( -
- {/* Username Field Skeleton */} -
-
-
-
- - {/* Password Field Skeleton */} -
-
-
-
- - {/* Confirm Password Field Skeleton */} -
-
-
-
- - {/* Button Skeleton */} -
- - {/* Sign up link Skeleton */} -
-
-
-
- ); -}; - -// Main component -export default function Signup() { - const { isAuthenticated } = useAuth(); - - // Show loading while checking initial auth state - if (isAuthenticated === undefined) { - return ( -
-
- {/* Header Skeleton */} -
-
-
-
-
- - {/* Form Skeleton */} -
- -
-
-
- ); - } - - // // Redirect if already authenticated - // if (isAuthenticated) { - // return ( - //
- //
- //
- //

Redirecting...

- //
- //
- // ); - // } - - return ( -
-
- {/* Header */} -
-
- - - -
-

- Create Account -

-

- Join us and get started -

-
- - {/* Signup Form */} -
- -
-
-
- ); + + ); } \ No newline at end of file diff --git a/savebook/app/api/auth/register/route.js b/savebook/app/api/auth/register/route.js index 1dc57da..5c61a45 100644 --- a/savebook/app/api/auth/register/route.js +++ b/savebook/app/api/auth/register/route.js @@ -4,44 +4,57 @@ import dbConnect from "@/lib/db/mongodb"; export async function POST(request) { try { - await dbConnect(); - - const { username, password } = await request.json(); + const { username, password} = await request.json(); + + //Validate required fields + if(!username || !password){ + return NextResponse.json( + { success: false, message: "All fields are required" }, + { status: 400} + ); + } - // βœ… Input validation - if ( - !username || - !password || - typeof username !== "string" || - typeof password !== "string" || - password.length < 6 - ) { + // Password strength check + if (password.length < 6) { return NextResponse.json( - { success: false, message: "Invalid input" }, + { success: false, message: "Password must be at least 6 characters" }, { status: 400 } ); } - // βœ… Prevent username enumeration - const existingUser = await User.findOne({ username }); - - if (existingUser) { + // Check if user exists + let user = await User.findOne({ username }); + if (user) { return NextResponse.json( - { success: false, message: "Unable to create account" }, + { success: false, message: "Invalid input" }, { status: 400 } ); } + + // Try creating user + try { + const newUser = await User.create({ username, password }); + console.log("User created:", newUser._id); - // βœ… Create user - await User.create({ - username, - password - }); + return NextResponse.json( + { success: true, message: "Account created successfully" }, + { status: 201 } + ); + } catch (err) { + // Handle duplicate key error explicitly + if (err.code === 11000) { + return NextResponse.json( + { success: false, message: "Username already taken" }, + { status: 400 } + ); + } - return NextResponse.json( - { success: true, message: "Account created successfully" }, - { status: 201 } - ); + console.error("User creation error:", err.message); + return NextResponse.json( + { success: false, message: err.message || "Failed to create user" }, + { status: 500 } + ); + } } catch (error) { console.error("Register error:", error); return NextResponse.json( diff --git a/savebook/context/auth/AuthState.js b/savebook/context/auth/AuthState.js index dcdc85d..68aea5f 100644 --- a/savebook/context/auth/AuthState.js +++ b/savebook/context/auth/AuthState.js @@ -90,7 +90,7 @@ const AuthProvider = ({ children }) => { headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ username,password }), + body: JSON.stringify({ username,password}), credentials: 'include' }); diff --git a/savebook/package-lock.json b/savebook/package-lock.json index a4cf780..f6521ca 100644 --- a/savebook/package-lock.json +++ b/savebook/package-lock.json @@ -13,7 +13,7 @@ "cloudinary": "^2.8.0", "framer-motion": "^12.24.7", "jose": "^6.1.3", - "jsonwebtoken": "^9.0.2", + "jsonwebtoken": "^9.0.3", "lucide-react": "^0.562.0", "mongoose": "^8.19.1", "next": "^15.5.7", @@ -4483,12 +4483,12 @@ } }, "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", "license": "MIT", "dependencies": { - "jws": "^3.2.2", + "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", @@ -4521,9 +4521,9 @@ } }, "node_modules/jwa": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", - "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", "license": "MIT", "dependencies": { "buffer-equal-constant-time": "^1.0.1", @@ -4532,12 +4532,12 @@ } }, "node_modules/jws": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", - "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", "license": "MIT", "dependencies": { - "jwa": "^1.4.2", + "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, diff --git a/savebook/package.json b/savebook/package.json index 66eab19..7114296 100644 --- a/savebook/package.json +++ b/savebook/package.json @@ -14,7 +14,7 @@ "cloudinary": "^2.8.0", "framer-motion": "^12.24.7", "jose": "^6.1.3", - "jsonwebtoken": "^9.0.2", + "jsonwebtoken": "^9.0.3", "lucide-react": "^0.562.0", "mongoose": "^8.19.1", "next": "^15.5.7", From 85e44c44c03e3e86f21d7fd68ad0c75c9b89911c Mon Sep 17 00:00:00 2001 From: Saumya-249 Date: Sat, 17 Jan 2026 00:58:00 +0530 Subject: [PATCH 02/17] Updated login page and update profile page, fix: removal of console logs, restored .env --- savebook/app/(auth)/login/page.js | 380 +++++------- savebook/app/api/auth/login/route.js | 50 +- savebook/app/api/auth/register/route.js | 16 +- savebook/app/api/auth/update-profile/route.js | 93 ++- savebook/app/api/auth/user/route.js | 36 ++ savebook/app/profile/page.js | 559 ++++++++++-------- savebook/context/auth/AuthState.js | 62 +- savebook/lib/models/User.js | 15 + savebook/lib/utils/jwtAuth.js | 4 + savebook/middleware.js | 57 ++ 10 files changed, 733 insertions(+), 539 deletions(-) create mode 100644 savebook/middleware.js diff --git a/savebook/app/(auth)/login/page.js b/savebook/app/(auth)/login/page.js index d495c24..7388e8f 100644 --- a/savebook/app/(auth)/login/page.js +++ b/savebook/app/(auth)/login/page.js @@ -3,265 +3,193 @@ import { useAuth } from "@/context/auth/authContext"; import { useRouter } from "next/navigation"; import React, { useState, useEffect } from "react"; -import toast from "react-hot-toast"; import Link from "next/link"; -import { Eye, EyeOff } from "lucide-react"; - -/* ========================= - Login Form -========================= */ const LoginForm = () => { const { login, isAuthenticated, loading } = useAuth(); - const router = useRouter(); - - const [credentials, setCredentials] = useState({ - username: "", - password: "", - }); - - const [isLoading, setIsLoading] = useState(false); - const [hasRedirected, setHasRedirected] = useState(false); + const [credentials, setCredentials] = useState({ username: "", password: "" }); + const [rememberMe, setRememberMe] = useState(false); + const [errors, setErrors] = useState({}); + const [errorMessage, setErrorMessage] = useState(""); + const [successMessage, setSuccessMessage] = useState(""); const [showPassword, setShowPassword] = useState(false); + const [hasRedirected, setHasRedirected] = useState(false); + const router = useRouter(); - // Recovery Codes - const [recoveryCodes, setRecoveryCodes] = useState(null); - const [showRecoveryModal, setShowRecoveryModal] = useState(false); - - /* ------------------------- - Redirect after login - ------------------------- */ + // Redirect if authenticated useEffect(() => { - if ( - isAuthenticated && - !loading && - !hasRedirected && - !showRecoveryModal - ) { + if (isAuthenticated && !loading && !hasRedirected) { setHasRedirected(true); router.push("/notes"); } - }, [isAuthenticated, loading, hasRedirected, showRecoveryModal, router]); + }, [isAuthenticated, loading, hasRedirected, router]); + + const onChange = (e) => { + setCredentials({ ...credentials, [e.target.name]: e.target.value }); + }; - /* ------------------------- - Submit - ------------------------- */ const handleSubmit = async (e) => { e.preventDefault(); - if (isLoading || isAuthenticated) return; + setErrors({}); + setErrorMessage(""); + setSuccessMessage(""); - setIsLoading(true); + const newErrors = {}; + if (!credentials.username.trim()) { + newErrors.username = "Username is required"; + } + if (!credentials.password) { + newErrors.password = "Password is required"; + } + if (Object.keys(newErrors).length > 0) { + setErrors(newErrors); + return; + } try { - const result = await login( - credentials.username, - credentials.password - ); + const result = await login(credentials.username, credentials.password, rememberMe); if (result.success) { - toast.success("Welcome back! πŸŽ‰"); - - // First login show recovery codes - if (result.recoveryCodes && result.recoveryCodes.length > 0) { - setRecoveryCodes(result.recoveryCodes); - setShowRecoveryModal(true); - } + setSuccessMessage("Logging in..."); + setErrorMessage(""); + // redirect handled by useEffect } else { - toast.error(result.message || "Invalid credentials"); + setSuccessMessage(""); + setErrorMessage(result.message); } - } catch (err) { - toast.error("Something went wrong"); - } finally { - setIsLoading(false); + } catch (error) { + // Removed console.error + setSuccessMessage(""); + setErrorMessage("Something went wrong. Please try again."); } }; - /* ------------------------- - Helpers - ------------------------- */ - const onchange = (e) => - setCredentials({ ...credentials, [e.target.name]: e.target.value }); - - const handleCopy = async () => { - await navigator.clipboard.writeText(recoveryCodes.join("\n")); - toast.success("Recovery codes copied"); - }; - - const handleDownload = () => { - const blob = new Blob([recoveryCodes.join("\n")], { - type: "text/plain", - }); - const url = URL.createObjectURL(blob); - - const a = document.createElement("a"); - a.href = url; - a.download = "savebook-recovery-codes.txt"; - a.click(); - - URL.revokeObjectURL(url); - }; - - if (loading) return ; - return ( - <> - {/* ========== LOGIN FORM ========== */} -
- {/* Username */} -
-