import { echo } from "@atums/echo"; import { errorMessages, httpStatus, successMessages, } from "#environment/constants"; import { sessionManager } from "#lib/auth"; import { cassandra } from "#lib/database"; import { isValidEmail, isValidUsername } from "#lib/validation"; import type { ExtendedRequest, LoginRequest, LoginResponse, RouteDef, UserRow, } from "#types/server"; const routeDef: RouteDef = { method: "POST", accepts: "application/json", returns: "application/json", needsBody: "json", }; async function handler( request: ExtendedRequest, requestBody: unknown, ): Promise { try { const { identifier, password } = requestBody as LoginRequest; const { force } = request.query; if (force !== "true" && force !== "1") { const existingSession = await sessionManager.getSession(request); if (existingSession) { const response: LoginResponse = { code: httpStatus.CONFLICT, success: false, error: errorMessages.USER_ALREADY_LOGGED_IN, }; return Response.json(response, { status: httpStatus.CONFLICT }); } } if (!identifier || !password) { const response: LoginResponse = { code: httpStatus.BAD_REQUEST, success: false, error: errorMessages.MISSING_REQUIRED_FIELDS, }; return Response.json(response, { status: httpStatus.BAD_REQUEST }); } const isEmail = isValidEmail(identifier).valid; const isUsername = isValidUsername(identifier).valid; if (!isEmail && !isUsername) { const response: LoginResponse = { code: httpStatus.BAD_REQUEST, success: false, error: "Invalid identifier format - must be a valid username or email", }; return Response.json(response, { status: httpStatus.BAD_REQUEST }); } let userQuery: string; let queryParams: string[]; if (isEmail) { userQuery = ` SELECT id, username, display_name, email, password, is_verified, created_at, updated_at FROM users WHERE email = ? LIMIT 1 `; queryParams = [identifier.trim().toLowerCase()]; } else { userQuery = ` SELECT id, username, display_name, email, password, is_verified, created_at, updated_at FROM users WHERE username = ? LIMIT 1 `; queryParams = [identifier.trim()]; } const userResult = (await cassandra.execute(userQuery, queryParams)) as { rows: UserRow[]; }; if (!userResult?.rows || !Array.isArray(userResult.rows)) { const response: LoginResponse = { code: httpStatus.INTERNAL_SERVER_ERROR, success: false, error: errorMessages.DATABASE_QUERY_FAILED, }; return Response.json(response, { status: httpStatus.INTERNAL_SERVER_ERROR, }); } if (userResult.rows.length === 0) { const response: LoginResponse = { code: httpStatus.UNAUTHORIZED, success: false, error: errorMessages.INVALID_CREDENTIALS, }; return Response.json(response, { status: httpStatus.UNAUTHORIZED }); } const user = userResult.rows[0]; if (!user) { const response: LoginResponse = { code: httpStatus.UNAUTHORIZED, success: false, error: errorMessages.INVALID_CREDENTIALS, }; return Response.json(response, { status: httpStatus.UNAUTHORIZED }); } const isPasswordValid = await Bun.password.verify(password, user.password); if (!isPasswordValid) { const response: LoginResponse = { code: httpStatus.UNAUTHORIZED, success: false, error: errorMessages.INVALID_CREDENTIALS, }; return Response.json(response, { status: httpStatus.UNAUTHORIZED }); } const userAgent = request.headers.get("User-Agent") || "Unknown"; const sessionPayload = { id: user.id, username: user.username, email: user.email, isVerified: user.is_verified, displayName: user.display_name, createdAt: user.created_at.toISOString(), updatedAt: user.updated_at.toISOString(), }; const sessionCookie = await sessionManager.createSession( sessionPayload, userAgent, ); const responseUser: LoginResponse["user"] = { id: user.id, username: user.username, displayName: user.display_name, email: user.email, isVerified: user.is_verified, createdAt: user.created_at.toISOString(), }; const response: LoginResponse = { code: httpStatus.OK, success: true, message: successMessages.LOGIN_SUCCESSFUL, user: responseUser, }; return Response.json(response, { status: httpStatus.OK, headers: { "Set-Cookie": sessionCookie, }, }); } catch (error) { echo.error({ message: "Error during user login", error, }); const response: LoginResponse = { code: httpStatus.INTERNAL_SERVER_ERROR, success: false, error: errorMessages.INTERNAL_SERVER_ERROR, }; return Response.json(response, { status: httpStatus.INTERNAL_SERVER_ERROR, }); } } export { handler, routeDef };