diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..dd5b5d5 --- /dev/null +++ b/.env.sample @@ -0,0 +1,4 @@ +# Appwrite +APPWRITE_PROJECT_ID = "5f5f3d5d5f5f3" +APPWRITE_ENDPOINT = "https://appwrite.io/v1" +APPWRITE_KEY = "G" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 26b002a..a900b7f 100644 --- a/.gitignore +++ b/.gitignore @@ -30,7 +30,7 @@ yarn-debug.log* yarn-error.log* # env files (can opt-in for commiting if needed) -.env* +.env.local # vercel .vercel diff --git a/app/api/auth/login/route.ts b/app/api/auth/login/route.ts new file mode 100644 index 0000000..c0fa1e7 --- /dev/null +++ b/app/api/auth/login/route.ts @@ -0,0 +1,61 @@ +import type { NextRequest } from "next/server"; +import { AppwriteException } from "node-appwrite"; + +import { ClientAW, RootAW } from "@/appwrite_configs/config"; +import { SetAuthCookie } from "@/helpers/sessionCookieFunctions"; +import { loginSchema } from "@/validations/auth/loginSchema"; + +import { errorHandler, successHandler } from "../../handler"; + +export async function POST(req: NextRequest) { + try { + let body = {}; + try { + body = (await req.json()) || {}; + } catch { + throw new AppwriteException("No body passed", 400); + } + + const validation = await loginSchema.safeParse(body); + if (!validation.success) { + throw new AppwriteException(validation.error.errors[0].message, 400); + } + const validatedData = validation.data; + + // Register user with validated data + const { account: acccountRoot } = await RootAW(); + + + // creating sesssion + const session = await acccountRoot.createEmailPasswordSession( + validatedData.email, + validatedData.password + ); + + + + const sessionKey = session.secret; + + // setting the cookie + await SetAuthCookie(sessionKey); + + + const {account:SavedUserAccount} = await ClientAW(); + + const user = await SavedUserAccount.get(); + + + // return the user + return successHandler({ + user: { + id: user.$id, + email: user.email, + name: user.name, + registrationDate: user.registration, + verified: user.emailVerification, + } + }); + } catch (e) { + return errorHandler(e); + } +} diff --git a/app/api/auth/oauth/github/login/failure/route.ts b/app/api/auth/oauth/github/login/failure/route.ts new file mode 100644 index 0000000..b2a1dd2 --- /dev/null +++ b/app/api/auth/oauth/github/login/failure/route.ts @@ -0,0 +1,7 @@ +import { AppwriteException } from "node-appwrite"; + +import { errorHandler } from "@/app/api/handler"; + +export function GET() { + return errorHandler(new AppwriteException("Github login failed", 400)); +} diff --git a/app/api/auth/oauth/github/login/route.ts b/app/api/auth/oauth/github/login/route.ts new file mode 100644 index 0000000..8bf129b --- /dev/null +++ b/app/api/auth/oauth/github/login/route.ts @@ -0,0 +1,21 @@ +import { NextResponse } from "next/server"; +import * as sdk from "node-appwrite"; + +import { errorHandler } from "@/app/api/handler"; +import { ClientAW } from "@/appwrite_configs/config"; + +export async function GET() { + try { + const { account } = await ClientAW(false); + const result = await account.createOAuth2Token( + sdk.OAuthProvider.Github, // provider + "http://localhost:3000/api/auth/oauth/github/login/success", // success (optional) + "http://localhost:3000/api/auth/oauth/github/login/failure", // failure (optional) + [] // scopes (optional) + ); + + return NextResponse.redirect(result); + } catch (e) { + return errorHandler(e); + } +} diff --git a/app/api/auth/oauth/github/login/success/route.ts b/app/api/auth/oauth/github/login/success/route.ts new file mode 100644 index 0000000..4277448 --- /dev/null +++ b/app/api/auth/oauth/github/login/success/route.ts @@ -0,0 +1,51 @@ +import { NextRequest } from "next/server"; +import { AppwriteException } from "node-appwrite"; + +import { errorHandler, successHandler } from "@/app/api/handler"; +import { ClientAW, RootAW } from "@/appwrite_configs/config"; +import { SetAuthCookie } from "@/helpers/sessionCookieFunctions"; + +export async function GET(req: NextRequest) { + try { + const queryStore = req.nextUrl.searchParams; + + const isPresent = queryStore.has("secret") && queryStore.has("userId"); + + if (!isPresent) { + return errorHandler( + new AppwriteException("userId or secret not present", 400) + ); + } + + const secret = queryStore.get("secret") as string; + const userId = queryStore.get("userId") as string; + + const { account: accountRoot } = await RootAW(); + const session = await accountRoot.createSession(userId, secret); + + const sessionKey = session.secret; + + // setting the cookie + await SetAuthCookie(sessionKey); + + const { account: SavedUserAccount } = await ClientAW(); + + const user = await SavedUserAccount.get(); + + return successHandler( + { + user: { + id: user.$id, + email: user.email, + name: user.name, + registrationDate: user.registration, + verified: user.emailVerification, + }, + }, + "Github login successful", + 200 + ); + } catch (e) { + return errorHandler(e); + } +} diff --git a/app/api/auth/oauth/github/register/failure/route.tsx b/app/api/auth/oauth/github/register/failure/route.tsx new file mode 100644 index 0000000..c963542 --- /dev/null +++ b/app/api/auth/oauth/github/register/failure/route.tsx @@ -0,0 +1,7 @@ +import { AppwriteException } from "node-appwrite"; + +import { errorHandler } from "@/app/api/handler"; + +export function GET() { + return errorHandler(new AppwriteException("Github registration failed", 400)); +} diff --git a/app/api/auth/oauth/github/register/route.ts b/app/api/auth/oauth/github/register/route.ts new file mode 100644 index 0000000..a0bde15 --- /dev/null +++ b/app/api/auth/oauth/github/register/route.ts @@ -0,0 +1,21 @@ +import { NextResponse } from "next/server"; +import * as sdk from "node-appwrite"; + +import { errorHandler } from "@/app/api/handler"; +import { ClientAW } from "@/appwrite_configs/config"; + +export async function GET() { + try { + const { account } = await ClientAW(false); + const result = await account.createOAuth2Token( + sdk.OAuthProvider.Github, // provider + "http://localhost:3000/api/auth/oauth/github/register/success", // success (optional) + "http://localhost:3000/api/auth/oauth/github/register/failure", // failure (optional) + [] // scopes (optional) + ); + + return NextResponse.redirect(result); + } catch (e) { + return errorHandler(e); + } +} diff --git a/app/api/auth/oauth/github/register/success/route.ts b/app/api/auth/oauth/github/register/success/route.ts new file mode 100644 index 0000000..4277448 --- /dev/null +++ b/app/api/auth/oauth/github/register/success/route.ts @@ -0,0 +1,51 @@ +import { NextRequest } from "next/server"; +import { AppwriteException } from "node-appwrite"; + +import { errorHandler, successHandler } from "@/app/api/handler"; +import { ClientAW, RootAW } from "@/appwrite_configs/config"; +import { SetAuthCookie } from "@/helpers/sessionCookieFunctions"; + +export async function GET(req: NextRequest) { + try { + const queryStore = req.nextUrl.searchParams; + + const isPresent = queryStore.has("secret") && queryStore.has("userId"); + + if (!isPresent) { + return errorHandler( + new AppwriteException("userId or secret not present", 400) + ); + } + + const secret = queryStore.get("secret") as string; + const userId = queryStore.get("userId") as string; + + const { account: accountRoot } = await RootAW(); + const session = await accountRoot.createSession(userId, secret); + + const sessionKey = session.secret; + + // setting the cookie + await SetAuthCookie(sessionKey); + + const { account: SavedUserAccount } = await ClientAW(); + + const user = await SavedUserAccount.get(); + + return successHandler( + { + user: { + id: user.$id, + email: user.email, + name: user.name, + registrationDate: user.registration, + verified: user.emailVerification, + }, + }, + "Github login successful", + 200 + ); + } catch (e) { + return errorHandler(e); + } +} diff --git a/app/api/auth/register/route.ts b/app/api/auth/register/route.ts new file mode 100644 index 0000000..bf9df54 --- /dev/null +++ b/app/api/auth/register/route.ts @@ -0,0 +1,65 @@ +import type { NextRequest } from "next/server"; +import { AppwriteException, ID } from "node-appwrite"; + +import { ClientAW, RootAW } from "@/appwrite_configs/config"; +import { SetAuthCookie } from "@/helpers/sessionCookieFunctions"; +import { registerSchema } from "@/validations/auth/registerSchema"; + +import { errorHandler, successHandler } from "../../handler"; + +export async function POST(req: NextRequest) { + try { + let body = {}; + try{ + body = await req.json() || {}; + }catch{ + throw new AppwriteException("No body passed", 400); + } + + const validation = await registerSchema.safeParse(body); + if (!validation.success) { + throw new AppwriteException(validation.error.errors[0].message, 400); + } + const validatedData = validation.data; + + // Register user with validated data + const { account } = await ClientAW(false); + const { account: acccountRoot } = await RootAW(); + + // creating account + const user = await account.create( + ID.unique(), + validatedData.email, + validatedData.password, + validatedData.name + ); + + // creating sesssion + const session = await acccountRoot.createEmailPasswordSession( + validatedData.email, + validatedData.password + ); + const sessionKey = session.secret; + + // setting the cookie + await SetAuthCookie(sessionKey); + + const { account: SavedUserAccount } = await ClientAW(); + // sending email + await SavedUserAccount.createVerification("http://localhost:3000/api/user/email/verify"); + + // return the user + return successHandler({ + user: { + id: user.$id, + email: user.email, + name: user.name, + verified: user.emailVerification, + registrationDate: user.registration, + } + }); + } catch (e) { + return errorHandler(e); + } +} + diff --git a/app/api/handler.ts b/app/api/handler.ts new file mode 100644 index 0000000..2d8c29e --- /dev/null +++ b/app/api/handler.ts @@ -0,0 +1,62 @@ +import { NextResponse } from "next/server"; +import { AppwriteException } from "node-appwrite"; + +export const errorHandler = (error: unknown) => { + if (error instanceof AppwriteException) { + return NextResponse.json( + { + success: false, + mode: process.env.NODE_ENV as string, + status: error.code, + message: error.message, + }, + { + status: error.code || 500, + } + ); + } + + if (error instanceof Error) { + return NextResponse.json( + { + success: false, + mode: process.env.NODE_ENV as string, + status: 500, + message: error.message, + }, + { + status: 500, + } + ); + } + + return NextResponse.json( + { + success: false, + mode: process.env.NODE_ENV as string, + status: 500, + message: "An unknown error occurred", + }, + { + status: 500, + } + ); +}; + +export const successHandler = ( + data: object | string, + message?: string, + code?: number +) => { + return NextResponse.json( + { + success: true, + message: message || "Successful", + mode: process.env.NODE_ENV as string, + data: data, + }, + { + status: code || 200, + } + ); +}; diff --git a/app/api/user/email/route.ts b/app/api/user/email/route.ts new file mode 100644 index 0000000..177afcd --- /dev/null +++ b/app/api/user/email/route.ts @@ -0,0 +1,17 @@ +import { ClientAW } from "@/appwrite_configs/config"; + +import { errorHandler, successHandler } from "../../handler"; + +export async function GET() { + try { + const { account } = await ClientAW(true); + const user= await account.get(); + return successHandler({ + id: user.$id, + email: user.email, + verified: user.emailVerification, + },"Email Info",200); + } catch (e) { + return errorHandler(e); + } +} diff --git a/app/api/user/email/verify/resend/route.ts b/app/api/user/email/verify/resend/route.ts new file mode 100644 index 0000000..e65c410 --- /dev/null +++ b/app/api/user/email/verify/resend/route.ts @@ -0,0 +1,45 @@ +import { NextRequest } from "next/server"; +import { AppwriteException } from "node-appwrite"; + +import { errorHandler, successHandler } from "@/app/api/handler"; +import { ClientAW } from "@/appwrite_configs/config"; + +export async function POST(req: NextRequest) { + try { + let body = { + userId: null, + }; + try { + body = await req.json(); + } catch { + throw new AppwriteException("No body passed", 400); + } + + const { userId } = body; + if (!userId) { + throw new AppwriteException("No userId passed", 400); + } + + const { account } = await ClientAW(true); + + const user = await account.get(); + + if (user.emailVerification) { + throw new AppwriteException("Email already verified", 400); + } + + if (user.$id !== userId) { + throw new AppwriteException("User Id does not match", 400); + } + + await account.createVerification("http://localhost:3000/api/user/email/verify"); + + return successHandler( + {}, + "Email verification mail sent successfully", + 201 + ); + } catch (e) { + return errorHandler(e); + } +} diff --git a/app/api/user/email/verify/route.ts b/app/api/user/email/verify/route.ts new file mode 100644 index 0000000..ad26e6b --- /dev/null +++ b/app/api/user/email/verify/route.ts @@ -0,0 +1,40 @@ +import type { NextRequest } from "next/server"; +import { AppwriteException } from "node-appwrite"; + +import { errorHandler, successHandler } from "@/app/api/handler"; +import { ClientAW} from "@/appwrite_configs/config"; + +export async function GET(req: NextRequest) { + try { + const queryStore = req.nextUrl.searchParams; + + const isPresent = queryStore.has("secret") && queryStore.has("userId"); + + if (!isPresent) { + return errorHandler( + new AppwriteException("userId or secret not present", 400) + ); + } + + const secret = queryStore.get("secret") as string; + const userId = queryStore.get("userId") as string; + + const {account} = await ClientAW(false); + + await account.updateVerification(userId, secret); + + + + return successHandler( + { + id: userId, + + verified: true, + }, + "Email verified successfully", + 201 + ); + } catch (e) { + return errorHandler(e); + } +} diff --git a/app/api/user/me/route.ts b/app/api/user/me/route.ts new file mode 100644 index 0000000..753517f --- /dev/null +++ b/app/api/user/me/route.ts @@ -0,0 +1,19 @@ +import { ClientAW } from "@/appwrite_configs/config"; + +import { errorHandler, successHandler } from "../../handler"; + +export async function GET() { + try { + const { account } = await ClientAW(true); + const user= await account.get(); + return successHandler({ + id: user.$id, + email: user.email, + name: user.name, + registrationDate: user.registration, + verified: user.emailVerification, + },"User details fetched successfully",200); + } catch (e) { + return errorHandler(e); + } +} diff --git a/app/api/user/update/name/route.ts b/app/api/user/update/name/route.ts new file mode 100644 index 0000000..b188ac8 --- /dev/null +++ b/app/api/user/update/name/route.ts @@ -0,0 +1,33 @@ +import { NextRequest } from "next/server"; +import { AppwriteException } from "node-appwrite"; + +import { errorHandler, successHandler } from "@/app/api/handler"; +import { ClientAW } from "@/appwrite_configs/config"; +import { updateNameSchema } from "@/validations/user/updateName"; + +export async function POST(req: NextRequest) { + try { + let body = {}; + try { + body = (await req.json()) || {}; + } catch { + throw new AppwriteException("No body passed", 400); + } + + const validation = updateNameSchema.safeParse(body); + if (!validation.success) { + throw new AppwriteException(validation.error.errors[0].message, 400); + } + + const { account } = await ClientAW(true); + + const validatedData = validation.data; + await account.updateName( + validatedData.name + ); + + return successHandler({}, "Name updated successfully", 201); + } catch (e) { + return errorHandler(e); + } +} diff --git a/app/api/user/update/password/route.ts b/app/api/user/update/password/route.ts new file mode 100644 index 0000000..804cced --- /dev/null +++ b/app/api/user/update/password/route.ts @@ -0,0 +1,34 @@ +import { NextRequest } from "next/server"; +import { AppwriteException } from "node-appwrite"; + +import { errorHandler, successHandler } from "@/app/api/handler"; +import { ClientAW } from "@/appwrite_configs/config"; +import { updatePasswordSchema } from "@/validations/user/updatePassword"; + +export async function POST(req: NextRequest) { + try { + let body = {}; + try { + body = (await req.json()) || {}; + } catch { + throw new AppwriteException("No body passed", 400); + } + + const validation = updatePasswordSchema.safeParse(body); + if (!validation.success) { + throw new AppwriteException(validation.error.errors[0].message, 400); + } + + const { account } = await ClientAW(true); + + const validatedData = validation.data; + await account.updatePassword( + validatedData.newPassword, + validatedData.currentPassword + ); + + return successHandler({}, "Password updated successfully", 201); + } catch (e) { + return errorHandler(e); + } +} diff --git a/appwrite_configs/config.ts b/appwrite_configs/config.ts new file mode 100644 index 0000000..8a5ddb8 --- /dev/null +++ b/appwrite_configs/config.ts @@ -0,0 +1,63 @@ +import * as sdk from "node-appwrite"; + +import { env } from "@/env"; +import { RetrieveAuthCookie } from "@/helpers/sessionCookieFunctions"; + +export const ClientAW = async (autoSetSession = true) => { + // if "autoSetSession" is true, then you just use this function out of the box it will automatically set the session but if you want to set the session manually then set "autoSetSession" to false + // also if set to true and the session is not set, it will throw an error + const client = new sdk.Client() + .setEndpoint(env.appwrite.endpoint) // Your API Endpoint + .setProject(env.appwrite.projectId) // Your project ID + .setSession(""); // The user session to authenticate with + + if (autoSetSession) { + const sessionKey = await RetrieveAuthCookie(); + client.setSession(sessionKey); + } + + const database = new sdk.Databases(client); + const account = new sdk.Account(client); + const storage = new sdk.Storage(client); + const functions = new sdk.Functions(client); + const teams = new sdk.Teams(client); + const locale = new sdk.Locale(client); + const avatars = new sdk.Avatars(client); + + return { + client, + database, + account, + storage, + functions, + teams, + locale, + avatars, + }; +}; + +export const RootAW = async () => { + const client = new sdk.Client() + .setEndpoint(env.appwrite.endpoint) // Your API Endpoint + .setProject(env.appwrite.projectId) // Your project ID + .setKey(env.appwrite.key); // The user session to authenticate with + + const database = new sdk.Databases(client); + const account = new sdk.Account(client); + const storage = new sdk.Storage(client); + const functions = new sdk.Functions(client); + const teams = new sdk.Teams(client); + const locale = new sdk.Locale(client); + const avatars = new sdk.Avatars(client); + + return { + client, + database, + account, + storage, + functions, + teams, + locale, + avatars, + }; +}; diff --git a/env.ts b/env.ts new file mode 100644 index 0000000..f419194 --- /dev/null +++ b/env.ts @@ -0,0 +1,17 @@ +export const env = { + appwrite:{ + projectId:String(process.env.APPWRITE_PROJECT_ID), + endpoint:String(process.env.APPWRITE_ENDPOINT), + key:String(process.env.APPWRITE_KEY) + }, + auth:{ + cookieName:"auth", + cookieSecret:String(process.env.COOKIE_SECRET) || "secret", + cookieMaxAge:60 * 60 * 8, + cookieSecure:process.env.NODE_ENV === "production", + cookieSameSite:"lax" as "lax" | "strict" | "none", + cookiePath:"/", + cookieHttpOnly:true, + + } +} \ No newline at end of file diff --git a/helpers/sessionCookieFunctions.ts b/helpers/sessionCookieFunctions.ts new file mode 100644 index 0000000..7b79a33 --- /dev/null +++ b/helpers/sessionCookieFunctions.ts @@ -0,0 +1,33 @@ +import { ReadonlyRequestCookies } from "next/dist/server/web/spec-extension/adapters/request-cookies"; +import { cookies } from "next/headers"; +import { AppwriteException } from "node-appwrite"; + +import { env } from "@/env"; + +export const EnsureAuthCookie = async (cookieStore: ReadonlyRequestCookies) => { + const isPresent = cookieStore.has(env.auth.cookieName); + if (!isPresent) { + throw new AppwriteException("login please!", 401); + // throw new AppwriteException("Cookie not present", 401); + } +}; + +export const RetrieveAuthCookie = async ():Promise => { + const cookieStore = await cookies(); + await EnsureAuthCookie(cookieStore); + + const authToken = cookieStore.get(env.auth.cookieName); + return authToken?.value || ""; +}; + + +export const SetAuthCookie = async (cookieValue: string) => { + const cookieStore = await cookies(); + cookieStore.set(env.auth.cookieName, cookieValue, { + httpOnly:env.auth.cookieHttpOnly, + secure:env.auth.cookieSecure, + sameSite:env.auth.cookieSameSite , + path:env.auth.cookiePath, + maxAge:env.auth.cookieMaxAge + }) +} \ No newline at end of file diff --git a/middleware.ts b/middleware.ts new file mode 100644 index 0000000..ca698e9 --- /dev/null +++ b/middleware.ts @@ -0,0 +1,7 @@ +import { NextResponse } from "next/server"; + +export default async function Middleware(){ + + return NextResponse.next(); + +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 97ec64a..75d0485 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,10 +20,12 @@ "clsx": "^2.1.1", "lucide-react": "^0.454.0", "next": "15.0.1", + "node-appwrite": "^14.1.0", "react": "^18.3.1", "react-dom": "^18.3.1", "tailwind-merge": "^2.5.4", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "zod": "^3.23.8" }, "devDependencies": { "@types/node": "^20", @@ -4737,6 +4739,21 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/node-appwrite": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-14.1.0.tgz", + "integrity": "sha512-kuKAZrdaAcGYOMUXtxNb1j+uIy+FIMiiU1dFkgwTXLsMLeLvC6HJ8/FH/kN9JyrWR2a2zcGN7gWfyQgWYoLMTA==", + "license": "BSD-3-Clause", + "dependencies": { + "node-fetch-native-with-agent": "1.7.2" + } + }, + "node_modules/node-fetch-native-with-agent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-fetch-native-with-agent/-/node-fetch-native-with-agent-1.7.2.tgz", + "integrity": "sha512-5MaOOCuJEvcckoz7/tjdx1M6OusOY6Xc5f459IaruGStWnKzlI1qpNgaAwmn4LmFYcsSlj+jBMk84wmmRxfk5g==", + "license": "MIT" + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -6647,6 +6664,15 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index 0a73464..185ad99 100644 --- a/package.json +++ b/package.json @@ -21,10 +21,12 @@ "clsx": "^2.1.1", "lucide-react": "^0.454.0", "next": "15.0.1", + "node-appwrite": "^14.1.0", "react": "^18.3.1", "react-dom": "^18.3.1", "tailwind-merge": "^2.5.4", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "zod": "^3.23.8" }, "devDependencies": { "@types/node": "^20", diff --git a/validations/auth/loginSchema.ts b/validations/auth/loginSchema.ts new file mode 100644 index 0000000..0e86109 --- /dev/null +++ b/validations/auth/loginSchema.ts @@ -0,0 +1,16 @@ +import { z } from "zod"; + +export const loginSchema = z.object({ + email: z + .string({ + required_error: "Email is required", + invalid_type_error: "Email must be a string", + }) + .email({ message: "Invalid email" }), + password: z + .string({ + required_error: "Password is required", + invalid_type_error: "Password must be a string", + }) + .min(6, { message: "Password must be at least 6 characters long" }) +}); diff --git a/validations/auth/registerSchema.ts b/validations/auth/registerSchema.ts new file mode 100644 index 0000000..cf0e414 --- /dev/null +++ b/validations/auth/registerSchema.ts @@ -0,0 +1,22 @@ +import { z } from "zod"; + +export const registerSchema = z.object({ + email: z + .string({ + required_error: "Email is required", + invalid_type_error: "Email must be a string", + }) + .email({ message: "Invalid email" }), + password: z + .string({ + required_error: "Password is required", + invalid_type_error: "Password must be a string", + }) + .min(6, { message: "Password must be at least 6 characters long" }), + name: z + .string({ + required_error: "Name is required", + invalid_type_error: "Name must be a string", + }) + .min(3, { message: "Name must be at least 3 characters long" }), +}); diff --git a/validations/user/updateName.ts b/validations/user/updateName.ts new file mode 100644 index 0000000..25026dd --- /dev/null +++ b/validations/user/updateName.ts @@ -0,0 +1,10 @@ +import { z } from "zod"; + +export const updateNameSchema = z.object({ + name: z + .string({ + required_error: "Name is required", + invalid_type_error: "Name must be a string", + }) + .min(3, { message: "Name must be at least 3 characters long" }), +}); \ No newline at end of file diff --git a/validations/user/updatePassword.ts b/validations/user/updatePassword.ts new file mode 100644 index 0000000..46bd17b --- /dev/null +++ b/validations/user/updatePassword.ts @@ -0,0 +1,26 @@ +import {z} from "zod"; + + +export const updatePasswordSchema = z.object({ + currentPassword: z + .string({ + required_error: "Current password is required", + invalid_type_error: "Current password must be a string", + }) + .min(6, {message: "Current password must be at least 6 characters long"}), + newPassword: z + .string({ + required_error: "New password is required", + invalid_type_error: "New password must be a string", + }) + .min(6, {message: "New password must be at least 6 characters long"}), + confirmPassword: z + .string({ + required_error: "Confirm password is required", + invalid_type_error: "Confirm password must be a string", + }) + .min(6, {message: "Confirm password must be at least 6 characters long"}), +}).refine(data => data.newPassword === data.confirmPassword, { + message: "New password and confirm password must match", + path: ["confirmPassword"], +}); \ No newline at end of file