mirror of
https://github.com/SrIzan10/stack.git
synced 2026-06-06 01:06:54 +00:00
feat: zod form validation
This commit is contained in:
@@ -28,7 +28,8 @@
|
||||
"react-dom": "^18",
|
||||
"sonner": "^1.4.41",
|
||||
"tailwind-merge": "^2.2.2",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"zod": "^3.23.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20",
|
||||
|
||||
@@ -5,9 +5,16 @@ import Link from "next/link";
|
||||
import { useFormState } from "react-dom";
|
||||
import { login } from "@/lib/auth/actions";
|
||||
import SubmitButton from "@/components/app/SubmitButton/SubmitButton";
|
||||
import { useEffect } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
export default function Page() {
|
||||
const [, formAction] = useFormState(login, null);
|
||||
const [formData, formAction] = useFormState(login, null);
|
||||
useEffect(() => {
|
||||
if (formData?.error) {
|
||||
toast.error(formData.error)
|
||||
}
|
||||
}, [formData])
|
||||
|
||||
return (
|
||||
<div className="flex items-center p-4 lg:p-8">
|
||||
|
||||
@@ -6,6 +6,7 @@ import { redirect } from "next/navigation";
|
||||
import prisma from "../db";
|
||||
import { Argon2id } from "oslo/password";
|
||||
import { generateId } from "lucia";
|
||||
import { accountSchema } from "./zod";
|
||||
|
||||
export async function logout() {
|
||||
const { session } = await validateRequest();
|
||||
@@ -16,25 +17,13 @@ export async function logout() {
|
||||
}
|
||||
|
||||
export async function login(prev: any, data: FormData) {
|
||||
const username = data.get("username");
|
||||
if (
|
||||
typeof username !== "string" ||
|
||||
username.length < 3 ||
|
||||
username.length > 31 ||
|
||||
!/^[a-z0-9_-]+$/.test(username)
|
||||
) {
|
||||
const checkSchema = await accountSchema.safeParseAsync(Object.fromEntries(data.entries()))
|
||||
if (!checkSchema.success)
|
||||
return {
|
||||
error: "Invalid username",
|
||||
error: `From ${checkSchema.error.errors[0].path[0]}: ${checkSchema.error.errors[0].message}`,
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
const password = data.get("password");
|
||||
if (typeof password !== "string" || password.length < 6 || password.length > 255) {
|
||||
return {
|
||||
error: "Invalid password",
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
const { username, password } = checkSchema.data;
|
||||
|
||||
const existingUser = await prisma.user.findUnique({
|
||||
where: {
|
||||
@@ -72,35 +61,13 @@ export async function login(prev: any, data: FormData) {
|
||||
}
|
||||
|
||||
export async function signup(prev: any, formData: FormData): Promise<ActionResult> {
|
||||
"use server";
|
||||
const username = formData.get("username");
|
||||
console.log(username)
|
||||
// username must be between 4 ~ 31 characters, and only consists of lowercase letters, 0-9, -, and _
|
||||
// keep in mind some database (e.g. mysql) are case insensitive
|
||||
if (
|
||||
typeof username !== "string" ||
|
||||
username.length < 3 ||
|
||||
username.length > 31 ||
|
||||
!/^[a-z0-9_-]+$/.test(username)
|
||||
) {
|
||||
const checkSchema = await accountSchema.safeParseAsync(Object.fromEntries(formData.entries()))
|
||||
if (!checkSchema.success)
|
||||
return {
|
||||
error: "Invalid username",
|
||||
error: `From ${checkSchema.error.errors[0].path[0]}: ${checkSchema.error.errors[0].message}`,
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
if (await prisma.user.findUnique({ where: { username: username } })) {
|
||||
return {
|
||||
error: "Username is already taken",
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
const password = formData.get("password");
|
||||
if (typeof password !== "string" || password.length < 6 || password.length > 255) {
|
||||
return {
|
||||
error: "Invalid password",
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
const { username, password } = checkSchema.data;
|
||||
|
||||
const hashedPassword = await new Argon2id().hash(password);
|
||||
const userId = generateId(15);
|
||||
|
||||
8
src/lib/auth/zod.ts
Normal file
8
src/lib/auth/zod.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
'use server'
|
||||
|
||||
import { z } from 'zod'
|
||||
|
||||
export const accountSchema = z.object({
|
||||
username: z.string().min(3, { message: 'Mininum 3 characters' }).max(31, { message: 'Maximum 31 characters' }).regex(/^[a-z0-9_-]+$/, { message: 'Only characters from a-z, 0-9, underscores and dashes' }),
|
||||
password: z.string().min(6, { message: 'Minimum 6 characters' }).max(255, { message: 'Maximum 255 characters' }),
|
||||
})
|
||||
@@ -3392,3 +3392,8 @@ yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
|
||||
zod@^3.23.6:
|
||||
version "3.23.6"
|
||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.6.tgz#c08a977e2255dab1fdba933651584a05fcbf19e1"
|
||||
integrity sha512-RTHJlZhsRbuA8Hmp/iNL7jnfc4nZishjsanDAfEY1QpDQZCahUp3xDzl+zfweE9BklxMUcgBgS1b7Lvie/ZVwA==
|
||||
|
||||
Reference in New Issue
Block a user