Fixed bundling into an exe not working as intended.

This commit is contained in:
2025-12-14 13:16:35 -05:00
parent 76c9eb38f6
commit 5d8441bf27
124 changed files with 279314 additions and 103 deletions

View File

@@ -0,0 +1,247 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMe = getMe;
exports.login = login;
exports.getAllUsers = getAllUsers;
exports.createUser = createUser;
exports.getUser = getUser;
exports.updateUser = updateUser;
exports.deleteUser = deleteUser;
exports.getIntegrationStatus = getIntegrationStatus;
exports.disconnectAniList = disconnectAniList;
exports.changePassword = changePassword;
const userService = __importStar(require("./user.service"));
const database_1 = require("../../shared/database");
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
async function getMe(req, reply) {
const userId = req.user?.id;
if (!userId) {
return reply.code(401).send({ error: "Unauthorized" });
}
const user = await (0, database_1.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
});
}
async function login(req, reply) {
const { userId, password } = req.body;
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 = jsonwebtoken_1.default.sign({ id: userId }, process.env.JWT_SECRET, { expiresIn: "7d" });
return reply.code(200).send({
success: true,
token
});
}
async function getAllUsers(req, reply) {
try {
const users = await userService.getAllUsers();
return { users };
}
catch (err) {
console.error("Get All Users Error:", err.message);
return reply.code(500).send({ error: "Failed to retrieve user list" });
}
}
async function createUser(req, reply) {
try {
const { username, profilePictureUrl, password } = req.body;
if (!username) {
return reply.code(400).send({ error: "Missing required field: username" });
}
const result = await userService.createUser(username, profilePictureUrl, password);
return reply.code(201).send({
success: true,
userId: result.lastID,
username
});
}
catch (err) {
if (err.message.includes('SQLITE_CONSTRAINT')) {
return reply.code(409).send({ error: "Username already exists." });
}
console.error("Create User Error:", err.message);
return reply.code(500).send({ error: "Failed to create user" });
}
}
async function getUser(req, reply) {
try {
const { id } = req.params;
const userId = parseInt(id, 10);
const user = 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.message);
return reply.code(500).send({ error: "Failed to retrieve user" });
}
}
async function updateUser(req, reply) {
try {
const { id } = req.params;
const userId = parseInt(id, 10);
const updates = req.body;
if (Object.keys(updates).length === 0) {
return reply.code(400).send({ error: "No update fields provided" });
}
const result = 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.message.includes('SQLITE_CONSTRAINT')) {
return reply.code(409).send({ error: "Username already exists or is invalid." });
}
console.error("Update User Error:", err.message);
return reply.code(500).send({ error: "Failed to update user" });
}
}
async function deleteUser(req, reply) {
try {
const { id } = req.params;
const userId = parseInt(id, 10);
if (!userId || isNaN(userId)) {
return reply.code(400).send({ error: "Invalid user id" });
}
const result = await userService.deleteUser(userId);
if (result && result.changes > 0) {
return { success: true, message: "User deleted successfully" };
}
else {
return reply.code(404).send({ error: "User not found" });
}
}
catch (err) {
console.error("Delete User Error:", err.message);
return reply.code(500).send({ error: "Failed to delete user" });
}
}
async function getIntegrationStatus(req, reply) {
try {
const { id } = req.params;
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.message);
return reply.code(500).send({ error: "Failed to check integration status" });
}
}
async function disconnectAniList(req, reply) {
try {
const { id } = req.params;
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" });
}
}
async function changePassword(req, reply) {
try {
const { id } = req.params;
const { currentPassword, newPassword } = req.body;
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" });
}
// Si el usuario tiene contraseña actual, debe proporcionar la contraseña actual
if (user.has_password && currentPassword) {
const isValid = await userService.verifyPassword(userId, currentPassword);
if (!isValid) {
return reply.code(401).send({ error: "Current password is incorrect" });
}
}
// Actualizar la contraseña (null para eliminarla, string para establecerla)
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" });
}
}

View File

@@ -0,0 +1,49 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const controller = __importStar(require("./user.controller"));
async function userRoutes(fastify) {
fastify.get('/me', controller.getMe);
fastify.post("/login", controller.login);
fastify.get('/users', controller.getAllUsers);
fastify.post('/users', { bodyLimit: 1024 * 1024 * 50 }, controller.createUser);
fastify.get('/users/:id', controller.getUser);
fastify.put('/users/:id', { bodyLimit: 1024 * 1024 * 50 }, controller.updateUser);
fastify.delete('/users/:id', controller.deleteUser);
fastify.get('/users/:id/integration', controller.getIntegrationStatus);
fastify.delete('/users/:id/integration', controller.disconnectAniList);
fastify.put('/users/:id/password', controller.changePassword);
}
exports.default = userRoutes;

View File

@@ -0,0 +1,144 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.userExists = userExists;
exports.createUser = createUser;
exports.updateUser = updateUser;
exports.deleteUser = deleteUser;
exports.getAllUsers = getAllUsers;
exports.getUserById = getUserById;
exports.verifyPassword = verifyPassword;
exports.getAniListIntegration = getAniListIntegration;
exports.removeAniListIntegration = removeAniListIntegration;
const database_1 = require("../../shared/database");
const bcrypt_1 = __importDefault(require("bcrypt"));
const USER_DB_NAME = 'userdata';
const SALT_ROUNDS = 10;
async function userExists(id) {
const sql = 'SELECT 1 FROM User WHERE id = ?';
const row = await (0, database_1.queryOne)(sql, [id], USER_DB_NAME);
return !!row;
}
async function createUser(username, profilePictureUrl, password) {
let passwordHash = null;
if (password && password.trim()) {
passwordHash = await bcrypt_1.default.hash(password.trim(), SALT_ROUNDS);
}
const sql = `
INSERT INTO User (username, profile_picture_url, password_hash)
VALUES (?, ?, ?)
`;
const params = [username, profilePictureUrl || null, passwordHash];
const result = await (0, database_1.run)(sql, params, USER_DB_NAME);
return { lastID: result.lastID };
}
async function updateUser(userId, updates) {
const fields = [];
const values = [];
if (updates.username !== undefined) {
fields.push('username = ?');
values.push(updates.username);
}
if (updates.profilePictureUrl !== undefined) {
fields.push('profile_picture_url = ?');
values.push(updates.profilePictureUrl);
}
if (updates.password !== undefined) {
if (updates.password === null || updates.password === '') {
// Eliminar contraseña
fields.push('password_hash = ?');
values.push(null);
}
else {
// Actualizar contraseña
const hash = await bcrypt_1.default.hash(updates.password.trim(), SALT_ROUNDS);
fields.push('password_hash = ?');
values.push(hash);
}
}
if (fields.length === 0) {
return { changes: 0, lastID: userId };
}
const setClause = fields.join(', ');
const sql = `UPDATE User SET ${setClause} WHERE id = ?`;
values.push(userId);
return await (0, database_1.run)(sql, values, USER_DB_NAME);
}
async function deleteUser(userId) {
await (0, database_1.run)(`DELETE FROM ListEntry WHERE user_id = ?`, [userId], USER_DB_NAME);
await (0, database_1.run)(`DELETE FROM UserIntegration WHERE user_id = ?`, [userId], USER_DB_NAME);
await (0, database_1.run)(`DELETE FROM favorites WHERE user_id = ?`, [userId], 'favorites');
const result = await (0, database_1.run)(`DELETE FROM User WHERE id = ?`, [userId], USER_DB_NAME);
return result;
}
async function getAllUsers() {
const sql = `
SELECT
id,
username,
profile_picture_url,
CASE WHEN password_hash IS NOT NULL THEN 1 ELSE 0 END as has_password
FROM User
ORDER BY id
`;
const users = await (0, database_1.queryAll)(sql, [], USER_DB_NAME);
return users.map((user) => ({
id: user.id,
username: user.username,
profile_picture_url: user.profile_picture_url || null,
has_password: !!user.has_password
}));
}
async function getUserById(id) {
const sql = `
SELECT
id,
username,
profile_picture_url,
CASE WHEN password_hash IS NOT NULL THEN 1 ELSE 0 END as has_password
FROM User
WHERE id = ?
`;
const user = await (0, database_1.queryOne)(sql, [id], USER_DB_NAME);
if (!user)
return null;
return {
id: user.id,
username: user.username,
profile_picture_url: user.profile_picture_url || null,
has_password: !!user.has_password
};
}
async function verifyPassword(userId, password) {
const sql = 'SELECT password_hash FROM User WHERE id = ?';
const user = await (0, database_1.queryOne)(sql, [userId], USER_DB_NAME);
if (!user || !user.password_hash) {
return false;
}
return await bcrypt_1.default.compare(password, user.password_hash);
}
async function getAniListIntegration(userId) {
const sql = `
SELECT anilist_user_id, expires_at
FROM UserIntegration
WHERE user_id = ? AND platform = ?
`;
const row = await (0, database_1.queryOne)(sql, [userId, "AniList"], USER_DB_NAME);
if (!row) {
return { connected: false };
}
return {
connected: true,
anilistUserId: row.anilist_user_id,
expiresAt: row.expires_at
};
}
async function removeAniListIntegration(userId) {
const sql = `
DELETE FROM UserIntegration
WHERE user_id = ? AND platform = ?
`;
return (0, database_1.run)(sql, [userId, "AniList"], USER_DB_NAME);
}