import { FastifyReply, FastifyRequest } from 'fastify'; import * as userService from './user.service'; import {queryOne} from '../../shared/database'; import jwt from "jsonwebtoken"; import { getConfig } from '../../shared/config'; const { values } = getConfig(); const jwtSecret = values.server?.jwt_secret; interface UserIdParams { id: string; } interface CreateUserBody { username: string; profilePictureUrl?: string; password?: string; } interface UpdateUserBody { username?: string; profilePictureUrl?: string | null; password?: string | null; } interface LoginBody { userId: number; password?: string; } interface DBRunResult { changes: number; lastID: number; } export async function getMe(req: any, reply: any) { const userId = req.user?.id; if (!userId) { return reply.code(401).send({ error: "Unauthorized" }); } const user = await queryOne( `SELECT username, profile_picture_url FROM User WHERE id = ?`, [userId], 'userdata' ); if (!user) { return reply.code(404).send({ error: "User not found" }); } return reply.send({ username: user.username, avatar: user.profile_picture_url }); } export async function login(req: FastifyRequest, reply: FastifyReply) { const { userId, password } = req.body as LoginBody; if (!userId || typeof userId !== "number" || userId <= 0) { return reply.code(400).send({ error: "Invalid userId provided" }); } const user = await userService.getUserById(userId); if (!user) { return reply.code(404).send({ error: "User not found in local database" }); } // Si el usuario tiene contraseƱa, debe proporcionarla if (user.has_password) { if (!password) { return reply.code(401).send({ error: "Password required", requiresPassword: true }); } const isValid = await userService.verifyPassword(userId, password); if (!isValid) { return reply.code(401).send({ error: "Incorrect password" }); } } const token = jwt.sign( { id: userId }, jwtSecret, { expiresIn: "7d" } ); return reply.code(200).send({ success: true, token }); } export async function getAllUsers(req: FastifyRequest, reply: FastifyReply) { try { const users: any = await userService.getAllUsers(); return { users }; } catch (err) { console.error("Get All Users Error:", (err as Error).message); return reply.code(500).send({ error: "Failed to retrieve user list" }); } } export async function createUser(req: FastifyRequest, reply: FastifyReply) { try { const { username, profilePictureUrl, password } = req.body as CreateUserBody; if (!username) { return reply.code(400).send({ error: "Missing required field: username" }); } const result: any = await userService.createUser(username, profilePictureUrl, password); return reply.code(201).send({ success: true, userId: result.lastID, username }); } catch (err) { if ((err as Error).message.includes('SQLITE_CONSTRAINT')) { return reply.code(409).send({ error: "Username already exists." }); } console.error("Create User Error:", (err as Error).message); return reply.code(500).send({ error: "Failed to create user" }); } } export async function getUser(req: FastifyRequest, reply: FastifyReply) { try { const { id } = req.params as UserIdParams; const userId = parseInt(id, 10); const user: any = await userService.getUserById(userId); if (!user) { return reply.code(404).send({ error: "User not found" }); } return { user }; } catch (err) { console.error("Get User Error:", (err as Error).message); return reply.code(500).send({ error: "Failed to retrieve user" }); } } export async function updateUser(req: FastifyRequest, reply: FastifyReply) { try { const { id } = req.params as UserIdParams; const userId = parseInt(id, 10); const updates = req.body as UpdateUserBody; if (Object.keys(updates).length === 0) { return reply.code(400).send({ error: "No update fields provided" }); } const result: DBRunResult = await userService.updateUser(userId, updates); if (result && result.changes > 0) { return { success: true, message: "User updated successfully" }; } else { return reply.code(404).send({ error: "User not found or nothing to update" }); } } catch (err) { if ((err as Error).message.includes('SQLITE_CONSTRAINT')) { return reply.code(409).send({ error: "Username already exists or is invalid." }); } console.error("Update User Error:", (err as Error).message); return reply.code(500).send({ error: "Failed to update user" }); } } export async function deleteUser(req: FastifyRequest, reply: FastifyReply) { try { const { id } = req.params as { id: string }; const userId = parseInt(id, 10); const body = (req.body ?? {}) as { password?: string }; const password = body.password; if (!userId || isNaN(userId)) { return reply.code(400).send({ error: "Invalid user id" }); } const user = await userService.getUserById(userId); if (!user) { return reply.code(404).send({ error: "User not found" }); } if (user.has_password) { if (!password) { return reply.code(401).send({ error: "Password required" }); } const isValid = await userService.verifyPassword(userId, password); if (!isValid) { return reply.code(401).send({ error: "Incorrect password" }); } } const result = await userService.deleteUser(userId); if (result.changes > 0) { return reply.send({ success: true }); } return reply.code(500).send({ error: "Failed to delete user" }); } catch (err) { console.error("Delete User Error:", err); return reply.code(500).send({ error: "Failed to delete user" }); } } export async function getIntegrationStatus(req: FastifyRequest, reply: FastifyReply) { try { const { id } = req.params as { id: string }; const userId = parseInt(id, 10); if (!userId || isNaN(userId)) { return reply.code(400).send({ error: "Invalid user id" }); } const integration = await userService.getAniListIntegration(userId); return reply.code(200).send(integration); } catch (err) { console.error("Get Integration Status Error:", (err as Error).message); return reply.code(500).send({ error: "Failed to check integration status" }); } } export async function disconnectAniList(req: FastifyRequest, reply: FastifyReply) { try { const { id } = req.params as { id: string }; const userId = parseInt(id, 10); if (!userId || isNaN(userId)) { return reply.code(400).send({ error: "Invalid user id" }); } const result = await userService.removeAniListIntegration(userId); if (result.changes === 0) { return reply.code(404).send({ error: "AniList integration not found" }); } return reply.send({ success: true }); } catch (err) { console.error("Disconnect AniList Error:", err); return reply.code(500).send({ error: "Failed to disconnect AniList" }); } } export async function changePassword(req: FastifyRequest, reply: FastifyReply) { try { const { id } = req.params as { id: string }; const { currentPassword, newPassword } = req.body as { currentPassword?: string; newPassword: string | null; }; const userId = parseInt(id, 10); if (!userId || isNaN(userId)) { return reply.code(400).send({ error: "Invalid user id" }); } const user = await userService.getUserById(userId); if (!user) { return reply.code(404).send({ error: "User not found" }); } if (user.has_password) { if (!currentPassword) { return reply.code(401).send({ error: "Current password required" }); } const isValid = await userService.verifyPassword(userId, currentPassword); if (!isValid) { return reply.code(401).send({ error: "Current password is incorrect" }); } } await userService.updateUser(userId, { password: newPassword }); return reply.send({ success: true, message: newPassword ? "Password updated successfully" : "Password removed successfully" }); } catch (err) { console.error("Change Password Error:", err); return reply.code(500).send({ error: "Failed to change password" }); } }