Fixed bundling into an exe not working as intended.
This commit is contained in:
247
electron/api/user/user.controller.js
Normal file
247
electron/api/user/user.controller.js
Normal 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" });
|
||||
}
|
||||
}
|
||||
49
electron/api/user/user.routes.js
Normal file
49
electron/api/user/user.routes.js
Normal 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;
|
||||
144
electron/api/user/user.service.js
Normal file
144
electron/api/user/user.service.js
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user