114 lines
3.1 KiB
TypeScript
114 lines
3.1 KiB
TypeScript
"use server";
|
|
|
|
import { compare, hash } from "bcryptjs";
|
|
import { redirect } from "next/navigation";
|
|
import { Prisma, type User } from "@prisma/client";
|
|
import { prisma } from "./prisma";
|
|
import { clearSession, setSession } from "./session";
|
|
import { getAppSettings } from "./settings";
|
|
|
|
function normalizeUsername(value: FormDataEntryValue | null) {
|
|
return String(value || "")
|
|
.trim()
|
|
.toLowerCase()
|
|
.replace(/[^a-z0-9_-]/g, "");
|
|
}
|
|
|
|
export async function registerUser(formData: FormData) {
|
|
const settings = await getAppSettings();
|
|
if (settings.registrationMode === "DISABLED") {
|
|
redirect("/register?error=closed");
|
|
}
|
|
|
|
const username = normalizeUsername(formData.get("username"));
|
|
const password = String(formData.get("password") || "");
|
|
const inviteCode = String(formData.get("inviteCode") || "").trim();
|
|
|
|
if (!username || password.length < 10) {
|
|
redirect("/register?error=invalid");
|
|
}
|
|
|
|
const invite =
|
|
settings.registrationMode === "INVITE_ONLY"
|
|
? await prisma.invite.findUnique({ where: { code: inviteCode } })
|
|
: null;
|
|
|
|
if (
|
|
settings.registrationMode === "INVITE_ONLY" &&
|
|
(!invite || invite.status !== "ACTIVE" || (invite.expiresAt && invite.expiresAt.getTime() < Date.now()))
|
|
) {
|
|
redirect("/register?error=invite");
|
|
}
|
|
|
|
const passwordHash = await hash(password, 12);
|
|
let user: User;
|
|
|
|
try {
|
|
user = await prisma.$transaction(async (tx) => {
|
|
const created = await tx.user.create({
|
|
data: {
|
|
username,
|
|
displayName: username,
|
|
passwordHash,
|
|
ownedRooms: {
|
|
create: {
|
|
slug: `@${username}`,
|
|
name: `${username}'s room`,
|
|
isPersonal: true,
|
|
visibility: "FRIENDS"
|
|
}
|
|
}
|
|
}
|
|
});
|
|
if (invite) {
|
|
await tx.invite.update({
|
|
where: { id: invite.id },
|
|
data: { status: "USED", usedById: created.id, usedAt: new Date() }
|
|
});
|
|
if (invite.roomId) {
|
|
await tx.roomMember.upsert({
|
|
where: { roomId_userId: { roomId: invite.roomId, userId: created.id } },
|
|
update: {},
|
|
create: { roomId: invite.roomId, userId: created.id }
|
|
});
|
|
}
|
|
}
|
|
return created;
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") {
|
|
redirect("/register?error=username");
|
|
}
|
|
throw error;
|
|
}
|
|
|
|
await setSession(user.id);
|
|
redirect("/dashboard");
|
|
}
|
|
|
|
export async function loginUser(formData: FormData) {
|
|
const username = normalizeUsername(formData.get("username"));
|
|
const password = String(formData.get("password") || "");
|
|
|
|
const user = await prisma.user.findUnique({ where: { username } });
|
|
if (!user) {
|
|
redirect("/login?error=credentials");
|
|
}
|
|
if (user.disabledAt) {
|
|
redirect("/login?error=disabled");
|
|
}
|
|
|
|
const ok = await compare(password, user.passwordHash);
|
|
if (!ok) {
|
|
redirect("/login?error=credentials");
|
|
}
|
|
|
|
await setSession(user.id);
|
|
redirect("/dashboard");
|
|
}
|
|
|
|
export async function logoutUser() {
|
|
await clearSession();
|
|
redirect("/login");
|
|
}
|