import { TRPCError } from "@trpc/server"; import { deleteCookie, setCookie } from "cookies-next"; import { add } from "date-fns"; import type { UsersId } from "~/schemas/public/Users"; import type { Context } from "~/server/api/trpc"; import { signToken } from "~/server/auth/jwt"; import { db } from "~/server/db"; import { isBanned } from "~/server/services/banlist.service"; import { comparePasswords, generateSalt, hashPassword, } from "~/server/services/password.service"; import { addUser, findUser_byEmail, findUser_byId, findUser_byId_MINI, } from "~/server/services/user.service"; import { checkCtx } from "~/server/utils/ctxChecker"; import { TOKEN_CONFIG } from "../auth/configs"; export const createUserAdmin = async ({ email, password, nome, cognome, telefono, }: { email: string; password: string; nome: string; cognome: string; telefono: string; }) => { const banned = await isBanned({ data: { email }, db }); if (banned) { throw new TRPCError({ code: "FORBIDDEN", message: "Utente bannato", }); } const salt = generateSalt(); const hashedPassword = await hashPassword(password, salt); const user = await addUser({ data: { cognome: cognome.trim(), email, isAdminMade: true, isVerified: true, nome: nome.trim(), password: hashedPassword, salt, telefono, username: `${nome.trim()} ${cognome.trim()}`, }, db, }); if (!user) { throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Errore in fase di registrazione", }); } return { status: "success" as const }; }; export const logIn = async ({ email, password, ctx: { req, res }, }: { email: string; password: string; ctx: Context; }) => { try { checkCtx({ req, res }); // Get the user from the collection const user = await findUser_byEmail({ email }); if (!user) { console.error("User not found in login for email:", email); throw new TRPCError({ code: "NOT_FOUND", message: "Errore in Login: Utente non trovato", }); } //Check if user is blocked or unverified if (user.isBlocked) { throw new TRPCError({ code: "FORBIDDEN", message: "Errore in Login: Utente bloccato", }); } // If Admin and password is "changeme", skip password check const skipPwCheck = user.isAdmin && user.password === "changeme"; if (!skipPwCheck) { const passworIsMatch = await comparePasswords({ hashedPassword: user.password, password, salt: user.salt, }); if (!passworIsMatch) { throw new TRPCError({ code: "BAD_REQUEST", message: "Errore in Login: Invalid email or password", }); } } const payload = { email: user.email, id: user.id, isAdmin: user.isAdmin, isBlocked: user.isBlocked, nome: user.nome, username: user.username, }; console.log("User authenticated, generating tokens for:", user.email); const accessToken = await signToken( payload, TOKEN_CONFIG.ACCESS_TOKEN_EXPIRY, "accessToken", ); const refreshToken = await signToken( payload, TOKEN_CONFIG.REFRESH_TOKEN_EXPIRY, "refreshToken", ); console.log("Generated tokens for user:", user.email); try { await setCookie(TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_NAME, accessToken, { ...TOKEN_CONFIG.COOKIE_OPTIONS, expires: add(new Date(), { minutes: TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_EXPIRY, }), req, res, }); await setCookie(TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_NAME, refreshToken, { ...TOKEN_CONFIG.COOKIE_OPTIONS, expires: add(new Date(), { minutes: TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_EXPIRY, }), req, res, }); console.log("Login successful, cookies set."); return { status: "success" as const }; } catch (cookieError) { console.error("Error setting cookies:", cookieError); throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: `Errore durante l'impostazione dei cookies: ${(cookieError as Error).message}`, }); } } catch (e) { console.error("Login error:", e); throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: `Errore login: ${(e as Error).message}`, }); } }; export const swapAuth = async ({ ctx: { req, res }, userId, }: { ctx: Context; userId: UsersId; }) => { checkCtx({ req, res }); try { const user = await findUser_byId_MINI({ userId }); if (!user) { throw new TRPCError({ code: "NOT_FOUND", message: "Utente non trovato", }); } const accessToken = await signToken( user, TOKEN_CONFIG.ACCESS_TOKEN_EXPIRY, "accessToken", ); const refreshToken = await signToken( user, TOKEN_CONFIG.REFRESH_TOKEN_EXPIRY, "refreshToken", ); // Send Access Token in Cookie await setCookie(TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_NAME, accessToken, { ...TOKEN_CONFIG.COOKIE_OPTIONS, expires: add(new Date(), { minutes: TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_EXPIRY, }), req, res, }); await setCookie(TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_NAME, refreshToken, { ...TOKEN_CONFIG.COOKIE_OPTIONS, expires: add(new Date(), { minutes: TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_EXPIRY, }), req, res, }); } catch { throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Errore interno", }); } }; export const logOut = async ({ ctx: { req, res } }: { ctx: Context }) => { checkCtx({ req, res }); try { await deleteCookie(TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_NAME, { req, res }); await deleteCookie(TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_NAME, { req, res }); return { status: "success" }; } catch (err: unknown) { console.error(err); throw err; } }; export const passwordChange = async ({ userId, oldpassword, newpassword, }: { userId: UsersId; oldpassword: string; newpassword: string; }) => { const dbUser = await findUser_byId({ id: userId }); if (!dbUser) { throw new TRPCError({ code: "NOT_FOUND", message: "Utente non trovato", }); } const banned = await isBanned({ data: { email: dbUser.email }, db }); if (banned) { throw new TRPCError({ code: "FORBIDDEN", message: "Utente bannato", }); } const isPasswordCorrect = await comparePasswords({ hashedPassword: dbUser.password, password: oldpassword, salt: dbUser.salt, }); if (!isPasswordCorrect) { throw new TRPCError({ code: "BAD_REQUEST", message: "Password errata", }); } const salt = generateSalt(); const hashedPassword = await hashPassword(newpassword, salt); await db .updateTable("users") .set({ password: hashedPassword, salt, }) .where("id", "=", userId) .execute(); return { status: "success", }; };