const UserService = require('../services/UserService'); const AuthService = require('../services/AuthService'); const RegistrationService = require('../services/RegistrationService'); const { validateRegistration, validateLogin } = require('../validators/userValidators'); const telegrambot = require('../telegram/telegrambot'); const tools = require('../tools/general'); const server_constants = require('../tools/server_constants'); const shared_consts = require('../tools/shared_nodejs'); class UserController { constructor() { this.userService = new UserService(); this.authService = new AuthService(); this.registrationService = new RegistrationService(); } /** * Register new user * POST /users */ async register(req, res) { try { tools.mylog('POST /users - Registration'); // Validate input const validationError = validateRegistration(req.body); if (validationError) { await tools.snooze(5000); return res.status(400).send({ code: validationError.code, msg: validationError.message }); } const userData = this._extractUserData(req.body); userData.ipaddr = tools.getiPAddressUser(req); // Check security (IP bans, block words, etc.) const securityCheck = await this._performSecurityChecks(userData, req); if (securityCheck.blocked) { return res.status(securityCheck.status).send({ code: securityCheck.code, msg: securityCheck.message }); } // Process registration const result = await this.registrationService.registerUser(userData, req); if (result.error) { return res.status(400).send({ code: result.code, msg: result.message }); } // Send response with tokens res .header('x-auth', result.token) .header('x-refrtok', result.refreshToken) .send(result.user); } catch (error) { console.error('Error in registration:', error.message); res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: error.message }); } } /** * User login * POST /users/login */ async login(req, res) { try { const { username, password, idapp, keyappid } = req.body; // Validate API key if (keyappid !== process.env.KEY_APP_ID) { return res.status(400).send({ code: server_constants.RIS_CODE_ERR }); } // Validate input const validationError = validateLogin(req.body); if (validationError) { return res.status(400).send({ code: validationError.code, msg: validationError.message }); } // Attempt login const result = await this.authService.authenticate( idapp, username, password, req ); if (result.error) { return res.status(result.status).send({ code: result.code, msg: result.message }); } // Send response with tokens res .header('x-auth', result.token) .header('x-refrtok', result.refreshToken) .send({ usertosend: result.user, code: server_constants.RIS_CODE_OK, subsExistonDb: result.subsExistonDb }); } catch (error) { console.error('Error in login:', error.message); res.status(400).send({ code: server_constants.RIS_CODE_LOGIN_ERR_GENERIC, msgerr: error.message }); } } /** * Get user profile * POST /users/profile */ async getProfile(req, res) { try { const usernameOrig = req.user?.username || ''; const perm = req.user?.perm || tools.Perm.PERM_NONE; const { username, idapp, idnotif } = req.body; // Mark notification as read if present if (idnotif) { await this.userService.markNotificationAsRead(idapp, usernameOrig, idnotif); } // Get user profile const profile = await this.userService.getUserProfile( idapp, username, usernameOrig, perm ); res.send(profile); } catch (error) { console.error('Error in getProfile:', error.message); res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: error.message }); } } /** * Update user saldo (balance) * POST /users/updatesaldo */ async updateSaldo(req, res) { try { const username = req.user.username; const { idapp, circuitId, groupname, lastdr } = req.body; const result = await this.userService.updateUserBalance( idapp, username, circuitId, groupname, lastdr ); res.send({ ris: result }); } catch (error) { console.error('Error in updateSaldo:', error.message); res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: error.message }); } } /** * Get user's friends * POST /users/friends */ async getFriends(req, res) { try { const username = req.user.username; const { idapp } = req.body; const friends = await this.userService.getUserFriends(idapp, username); res.send(friends); } catch (error) { console.error('Error in getFriends:', error.message); res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: error.message }); } } /** * Execute friend command (add, remove, etc.) * POST /users/friends/cmd */ async executeFriendCommand(req, res) { try { const usernameLogged = req.user.username; const { idapp, usernameOrig, usernameDest, cmd, value } = req.body; // Security check if (!this._canExecuteFriendCommand(req.user, usernameOrig, usernameDest, cmd)) { return res.status(server_constants.RIS_CODE_ERR_UNAUTHORIZED).send({ code: server_constants.RIS_CODE_ERR_UNAUTHORIZED, msg: '' }); } const result = await this.userService.executeFriendCommand( req, idapp, usernameOrig, usernameDest, cmd, value ); res.send(result); } catch (error) { console.error('Error in executeFriendCommand:', error.message); res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: error.message }); } } /** * Get user's groups * POST /users/groups */ async getGroups(req, res) { try { const username = req.user.username; const { idapp } = req.body; const groups = await this.userService.getUserGroups(idapp, username, req); res.send(groups); } catch (error) { console.error('Error in getGroups:', error.message); res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: error.message }); } } /** * Get user's circuits * POST /users/circuits */ async getCircuits(req, res) { try { const username = req.user.username; const { idapp, nummovTodownload } = req.body; const circuits = await this.userService.getUserCircuits( idapp, username, req.user, nummovTodownload ); res.send(circuits); } catch (error) { console.error('Error in getCircuits:', error.message); res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: error.message }); } } /** * Refresh authentication token * POST /users/newtok */ async refreshToken(req, res) { try { const { refreshToken } = req.body; if (!refreshToken) { return res.status(400).send({ error: 'Refresh token mancante' }); } const result = await this.authService.refreshToken(refreshToken, req); if (result.error) { return res.status(result.status).send({ error: result.message }); } res.status(200).send({ token: result.token, refreshToken: result.refreshToken }); } catch (error) { console.error('Error in refreshToken:', error.message); res.status(500).send({ error: 'Errore interno del server' }); } } /** * Logout user (remove token) * DELETE /users/me/token */ async logout(req, res) { try { await this.authService.removeToken(req.user, req.token); res.status(200).send(); } catch (error) { console.error('Error in logout:', error.message); res.status(400).send(); } } /** * Check if username exists * GET /users/:idapp/:username */ async checkUsername(req, res) { try { const { username, idapp } = req.params; const exists = await this.userService.checkUsernameExists(idapp, username); if (!exists) { return res.status(404).send(); } res.status(200).send(); } catch (error) { console.error('Error in checkUsername:', error.message); res.status(400).send(); } } /** * Update user permissions * POST /users/setperm */ async setPermissions(req, res) { try { const { idapp, username, perm } = req.body; await this.userService.setUserPermissions(req.user._id, { idapp, username, perm }); res.status(200).send(); } catch (error) { console.error('Error in setPermissions:', error.message); res.status(400).send(); } } /** * Database operations (admin only) * POST /users/dbop */ async executeDbOperation(req, res) { try { const { mydata, idapp } = req.body; // Check permissions if (!this._hasAdminPermissions(req.user)) { return res.status(404).send({ code: server_constants.RIS_CODE_ERR_UNAUTHORIZED }); } const result = await this.userService.executeDbOperation( idapp, mydata, req, res ); res.send({ code: server_constants.RIS_CODE_OK, data: result }); } catch (error) { console.error('Error in executeDbOperation:', error.message); res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: error.message }); } } /** * Get map information * POST /users/infomap */ async getMapInfo(req, res) { try { const { idapp } = req.body; const mapData = await this.userService.getMapInformation(idapp); res.send({ code: server_constants.RIS_CODE_OK, ris: mapData }); } catch (error) { console.error('Error in getMapInfo:', error.message); res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: error.message }); } } // ===== PRIVATE HELPER METHODS ===== _extractUserData(body) { const fields = [ 'email', 'password', 'username', 'group', 'name', 'surname', 'idapp', 'keyappid', 'lang', 'profile', 'aportador_solidario' ]; const userData = {}; fields.forEach(field => { if (body[field] !== undefined) { userData[field] = body[field]; } }); // Normalize data if (userData.email) userData.email = userData.email.toLowerCase().trim(); if (userData.username) userData.username = userData.username.trim(); if (userData.name) userData.name = userData.name.trim(); if (userData.surname) userData.surname = userData.surname.trim(); return userData; } async _performSecurityChecks(userData, req) { const { User } = require('../models/user'); // Check for blocked words if (tools.blockwords(userData.username) || tools.blockwords(userData.name) || tools.blockwords(userData.surname)) { await tools.snooze(5000); return { blocked: true, status: 404, code: server_constants.RIS_CODE_ERR }; } // Check IP ban const lastRecord = await User.getLastRec(userData.idapp); if (lastRecord && process.env.LOCALE !== '1') { if (lastRecord.ipaddr === userData.ipaddr && lastRecord.date_reg) { const isTooRecent = tools.isdiffSecDateLess(lastRecord.date_reg, 3); if (isTooRecent) { const msg = `${userData.ipaddr}: [${userData.username}] ${userData.name} ${userData.surname}`; tools.writeIPToBan(msg); await telegrambot.sendMsgTelegramToTheAdmin(userData.idapp, '‼️ BAN: ' + msg, true); await tools.snooze(5000); return { blocked: true, status: 400, code: server_constants.RIS_CODE_BANIP }; } } } return { blocked: false }; } _canExecuteFriendCommand(user, usernameOrig, usernameDest, cmd) { const { User } = require('../models/user'); if (User.isAdmin(user.perm) || User.isManager(user.perm)) { return true; } const allowedCommands = [ shared_consts.FRIENDSCMD.SETFRIEND, shared_consts.FRIENDSCMD.SETHANDSHAKE ]; if (allowedCommands.includes(cmd)) { return usernameOrig === user.username || usernameDest === user.username; } return false; } _hasAdminPermissions(user) { const { User } = require('../models/user'); return User.isCollaboratore(user.perm); } } module.exports = UserController;