Organize/lib/actions/auth.ts

91 lines
2.4 KiB
TypeScript

"use server";
import bcrypt from "bcryptjs";
import { AuthError } from "next-auth";
import { prisma } from "@/lib/db";
import { signIn, signOut } from "@/auth";
import { LoginSchema, SignupSchema } from "@/lib/validation/auth";
import { Role } from "@/lib/generated/prisma/enums";
export type SignupState = {
error?: string;
} | undefined;
export type LoginState = {
error?: string;
} | undefined;
export async function login(
_prevState: LoginState,
formData: FormData
): Promise<LoginState> {
const parsed = LoginSchema.safeParse({
email: formData.get("email"),
password: formData.get("password"),
});
if (!parsed.success) {
return { error: parsed.error.issues[0]?.message ?? "Invalid input." };
}
try {
await signIn("credentials", {
email: parsed.data.email,
password: parsed.data.password,
redirectTo: "/",
});
} catch (error) {
if (error instanceof AuthError) {
return { error: "Incorrect email or password." };
}
throw error;
}
}
export async function signup(
_prevState: SignupState,
formData: FormData
): Promise<SignupState> {
const parsed = SignupSchema.safeParse({
name: formData.get("name"),
email: formData.get("email"),
password: formData.get("password"),
});
if (!parsed.success) {
return { error: parsed.error.issues[0]?.message ?? "Invalid input." };
}
const { name, email, password } = parsed.data;
const existing = await prisma.user.findUnique({ where: { email } });
if (existing) {
return { error: "An account with that email already exists." };
}
const passwordHash = await bcrypt.hash(password, 12);
// The very first account on the whole site becomes the administrator;
// everyone after that gets the default "USER" role.
const isFirstUser = (await prisma.user.count()) === 0;
await prisma.user.create({
data: { name, email, passwordHash, role: isFirstUser ? Role.ADMIN : Role.USER },
});
try {
// Signs the user in and redirects to "/" on success. NextAuth throws a
// framework-handled redirect internally, so nothing after this runs.
await signIn("credentials", { email, password, redirectTo: "/" });
} catch (error) {
if (error instanceof AuthError) {
return { error: "Account created, but sign-in failed. Try logging in." };
}
throw error;
}
}
export async function logout() {
await signOut({ redirectTo: "/login" });
}