293 lines
9.0 KiB
TypeScript
293 lines
9.0 KiB
TypeScript
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" });
|
|
}
|
|
} |