diff --git a/backend/package-lock.json b/backend/package-lock.json index 507edf0..04e8836 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -12,9 +12,9 @@ "cors": "^2.8.6", "dotenv": "^16.6.1", "express": "^4.22.2", - "next": "*", - "react": "*", - "react-dom": "*", + "next": "latest", + "react": "latest", + "react-dom": "latest", "socket.io": "^4.7.2" }, "devDependencies": { diff --git a/frontend/app/api/auth/signup/route.ts b/frontend/app/api/auth/signup/route.ts index 4f9313c..5d794a9 100644 --- a/frontend/app/api/auth/signup/route.ts +++ b/frontend/app/api/auth/signup/route.ts @@ -2,8 +2,7 @@ import { createClient } from "@supabase/supabase-js"; import { NextRequest, NextResponse } from "next/server"; import { randomUUID } from "crypto"; - -const USERNAME_REGEX = /^[a-zA-Z0-9_]{3,20}$/; +import { signupSchema } from "../../../../lib/validations/auth"; export const runtime = "nodejs"; @@ -22,58 +21,29 @@ type ProjectRow = { export async function POST(req: NextRequest) { try { const payload = await req.json(); - const { email, password, name, username, mobile, description, members, due, tags } = payload || {}; - - // Input validation - if (!email || !password || !name || !username) { - return NextResponse.json( - { error: "Missing required fields: email, password, name, username are required." }, - { status: 400 } - ); - } - - // Username format validation - if (!USERNAME_REGEX.test(username)) { - return NextResponse.json( - { - error: - "Username can only contain letters, numbers, and underscores.", - }, - { status: 400 } - ); - } - - // Email format validation - const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; - if (!emailRegex.test(email)) { - return NextResponse.json( - { error: "Invalid email format." }, - { status: 400 } - ); - } - - // Password strength - const STRONG_PASSWORD_REGEX = - /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&]).{8,}$/; - - if (!STRONG_PASSWORD_REGEX.test(password)) { + const validation = signupSchema.safeParse(payload); + if (!validation.success) { return NextResponse.json( { - error: - "Password must be at least 8 characters and include uppercase, lowercase, number, and special character.", + error: validation.error.issues[0].message, }, { status: 400 } ); } - // Username validation - if (username.length < 3 || username.length > 20) { - return NextResponse.json( - { error: "Username must be between 3-20 characters." }, - { status: 400 } - ); - } - + const { + email, + password, + name, + username, + mobile, + description, + members, + due, + tags, + } = validation.data; + + // Environment variables check if (!process.env.SUPABASE_URL || !process.env.SUPABASE_SERVICE_KEY) { console.error("Missing Supabase environment variables"); diff --git a/frontend/lib/validations/auth.ts b/frontend/lib/validations/auth.ts new file mode 100644 index 0000000..d3abb3d --- /dev/null +++ b/frontend/lib/validations/auth.ts @@ -0,0 +1,35 @@ +import { z } from "zod"; + +export const signupSchema = z.object({ + name: z + .string() + .min(2, "Name must be at least 2 characters"), + + username: z + .string() + .min(3, "Username must be at least 3 characters") + .max(20, "Username must be under 20 characters") + .regex( + /^[a-zA-Z0-9_]+$/, + "Username can only contain letters, numbers, and underscores" + ), + + email: z.string().email("Invalid email address"), + + password: z + .string() + .min(8, "Password must be at least 8 characters") + .regex(/[A-Z]/, "Password must contain an uppercase letter") + .regex(/[a-z]/, "Password must contain a lowercase letter") + .regex(/[0-9]/, "Password must contain a number"), + + mobile: z.string().optional(), + + description: z.string().optional(), + + members: z.union([z.string(), z.number()]).optional(), + + due: z.string().optional(), + + tags: z.any().optional(), +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 9cacf96..e9f31d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,8 @@ "@supabase/supabase-js": "^2.103.0", "lucide": "^1.8.0", "react": "^19.2.4", - "react-dom": "^19.2.4" + "react-dom": "^19.2.4", + "zod": "^4.4.3" } }, "frontend": { @@ -4001,14 +4002,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "frontend/node_modules/zod": { - "version": "4.3.6", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "frontend/node_modules/zod-validation-error": { "version": "4.0.2", "dev": true, @@ -6507,6 +6500,15 @@ "engines": { "node": ">=0.4.0" } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index 2d9b12e..860b2bd 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@supabase/supabase-js": "^2.103.0", "lucide": "^1.8.0", "react": "^19.2.4", - "react-dom": "^19.2.4" + "react-dom": "^19.2.4", + "zod": "^4.4.3" } }