From f8c1340b4147f3b553ad595b1f46e981bea2e154 Mon Sep 17 00:00:00 2001 From: azures04 Date: Tue, 27 Jan 2026 05:49:50 +0100 Subject: [PATCH] Refactor license and permission management, add services Renamed licenceRepository.js to licenseRepository.js and updated its API to use status instead of userId for licenses. Moved permission checking logic from userRepository to permissionsRepository. Added new service layers for license, permissions, and user management, implementing error handling and business logic. Removed the old register.js service and cleaned up test.js. Updated database schema to remove userId from licenses. --- modules/databaseGlobals.js | 5 -- ...enceRepository.js => licenseRepository.js} | 32 +++----- repositories/permissionsRepository.js | 21 ++++- repositories/userRepository.js | 17 ---- services/licenseService.js | 77 +++++++++++++++++++ services/permissionsService.js | 72 +++++++++++++++++ services/register.js | 19 ----- services/userService.js | 66 ++++++++++++++++ test.js | 3 - 9 files changed, 243 insertions(+), 69 deletions(-) rename repositories/{licenceRepository.js => licenseRepository.js} (62%) create mode 100644 services/licenseService.js create mode 100644 services/permissionsService.js delete mode 100644 services/register.js create mode 100644 services/userService.js diff --git a/modules/databaseGlobals.js b/modules/databaseGlobals.js index ec87549..8af8308 100644 --- a/modules/databaseGlobals.js +++ b/modules/databaseGlobals.js @@ -64,16 +64,11 @@ function initializeDatabase() { key TEXT NOT NULL UNIQUE, productId INTEGER NOT NULL, targetIhannel TEXT NOT NULL, - userId INTEGER DEFAULT NULL, usedAt TEXT, FOREIGN KEY(productId) REFERENCES products(id) ON DELETE CASCADE, - - FOREIGN KEY(userId) - REFERENCES accounts(id) - ON DELETE SET NULL ) `) } diff --git a/repositories/licenceRepository.js b/repositories/licenseRepository.js similarity index 62% rename from repositories/licenceRepository.js rename to repositories/licenseRepository.js index 5075ef8..68aafd8 100644 --- a/repositories/licenceRepository.js +++ b/repositories/licenseRepository.js @@ -5,13 +5,10 @@ function create(key, productId, targetChannel) { INSERT INTO licenses ( key, productId, - targetChannel - ) - VALUES ( - ?, - ?, - ? + targetChannel, + status ) + VALUES (?, ?, ?, 'active') ` const statement = database.prepare(query) return statement.run(key, productId, targetChannel) @@ -19,7 +16,7 @@ function create(key, productId, targetChannel) { function findByKey(key) { const query = ` - SELECT id, key, productId, targetChannel, userId, usedAt + SELECT id, key, productId, targetChannel, status, createdAt FROM licenses WHERE key = ? ` @@ -27,24 +24,14 @@ function findByKey(key) { return statement.get(key) } -function activate(licenseId, userId) { +function updateStatus(id, status) { const query = ` UPDATE licenses - SET userId = ?, usedAt = CURRENT_TIMESTAMP - WHERE id = ? AND userId IS NULL + SET status = ? + WHERE id = ? ` const statement = database.prepare(query) - return statement.run(userId, licenseId) -} - -function deactivate(licenseId, userId) { - const query = ` - UPDATE licenses - SET userId = ?, usedAt = NULL - WHERE id = ? AND userId IS NULL - ` - const statement = database.prepare(query) - return statement.run(userId, licenseId) + return statement.run(status, id) } function findByProduct(productId) { @@ -68,8 +55,7 @@ function remove(id) { module.exports = { create, remove, - activate, findByKey, - deactivate, + updateStatus, findByProduct, } \ No newline at end of file diff --git a/repositories/permissionsRepository.js b/repositories/permissionsRepository.js index 6156eb3..cd5ba24 100644 --- a/repositories/permissionsRepository.js +++ b/repositories/permissionsRepository.js @@ -53,9 +53,26 @@ function revokeAllOnProduct(userId, productId) { return statement.run(userId, productId) } +function hasPermission(userId, productName, channel, permission) { + const query = ` + SELECT 1 FROM permissions p + JOIN products pr ON p.product_id = pr.id + WHERE p.user_id = ? + AND pr.name = ? + AND (p.channel = ? OR p.channel = '*') + AND p.permission_key = ? + ` + + const stmt = db.prepare(query) + const result = stmt.get(userId, productName, channel, permission) + + return !!result +} + module.exports = { grant, revoke, + hasPermission, + revokeAllOnProduct, findByUserAndProduct, - revokeAllOnProduct -}; \ No newline at end of file +} \ No newline at end of file diff --git a/repositories/userRepository.js b/repositories/userRepository.js index 4b507d8..2f8f004 100644 --- a/repositories/userRepository.js +++ b/repositories/userRepository.js @@ -59,27 +59,10 @@ function findById(identifier) { return statement.get(identifier) } -function hasPermission(userId, productName, channel, permission) { - const query = ` - SELECT 1 FROM permissions p - JOIN products pr ON p.product_id = pr.id - WHERE p.user_id = ? - AND pr.name = ? - AND (p.channel = ? OR p.channel = '*') - AND p.permission_key = ? - ` - - const stmt = db.prepare(query) - const result = stmt.get(userId, productName, channel, permission) - - return !!result -} - module.exports = { remove, register, findById, - hasPermission, findByUsername, changePassword, } \ No newline at end of file diff --git a/services/licenseService.js b/services/licenseService.js new file mode 100644 index 0000000..530254c --- /dev/null +++ b/services/licenseService.js @@ -0,0 +1,77 @@ +const licenceRepository = require("../repositories/licenceRepository") +const logger = require("../modules/logger") +const { DefaultError } = require("../errors/errors") + +async function create({ key, productId, targetChannel }) { + const existing = licenceRepository.findByKey(key) + if (existing) { + throw new DefaultError(409, "License key already exists.") + } + + try { + return licenceRepository.create(key, productId, targetChannel) + } catch (error) { + logger.error(error, ["Service", "yellow", "LICENSE", "green"]) + throw new DefaultError(500, "Please contact the maintainer") + } +} + +function getLicenseByKey(key) { + try { + const license = licenceRepository.findByKey(key) + if (!license) { + throw new DefaultError(404, "License not found.") + } + return license + } catch (error) { + if (error instanceof DefaultError) throw error + logger.error(error, ["Service", "yellow", "LICENSE", "green"]) + throw new DefaultError(500, "Please contact the maintainer") + } +} + +async function updateStatus(id, status) { + try { + const result = licenceRepository.updateStatus(id, status) + if (result.changes === 0) { + throw new DefaultError(404, "License not found, status update failed.") + } + return result + } catch (error) { + if (error instanceof DefaultError) throw error + logger.error(error, ["Service", "yellow", "LICENSE", "green"]) + throw new DefaultError(500, "Please contact the maintainer") + } +} + +function listByProduct(productId) { + try { + const licenses = licenceRepository.findByProduct(productId) + return licenses + } catch (error) { + logger.error(error, ["Service", "yellow", "LICENSE", "green"]) + throw new DefaultError(500, "Please contact the maintainer") + } +} + +async function remove(id) { + try { + const result = licenceRepository.remove(id) + if (result.changes === 0) { + throw new DefaultError(404, "License not found.") + } + return result + } catch (error) { + if (error instanceof DefaultError) throw error + logger.error(error, ["Service", "yellow", "LICENSE", "green"]) + throw new DefaultError(500, "Please contact the maintainer") + } +} + +module.exports = { + create, + getLicenseByKey, + updateStatus, + listByProduct, + remove +} \ No newline at end of file diff --git a/services/permissionsService.js b/services/permissionsService.js new file mode 100644 index 0000000..b8a1cda --- /dev/null +++ b/services/permissionsService.js @@ -0,0 +1,72 @@ +const permissionsRepository = require("../repositories/permissionsRepository") +const userRepository = require("../repositories/userRepository") +const logger = require("../modules/logger") +const { DefaultError } = require("../errors/errors") + +async function grant({ userId, productId, channel, permissionKey }) { + const user = userRepository.findById(userId) + if (!user) { + throw new DefaultError(404, "User not found.") + } + + try { + return permissionsRepository.grant(userId, productId, channel, permissionKey) + } catch (error) { + logger.error(error, ["Service", "yellow", "PERMISSION", "green"]) + throw new DefaultError(500, "Please contact the maintainer") + } +} + +async function revoke({ userId, productId, channel, permissionKey }) { + try { + const result = permissionsRepository.revoke(userId, productId, channel, permissionKey) + if (result.changes === 0) { + throw new DefaultError(404, "Permission not found or already revoked.") + } + return result + } catch (error) { + if (error instanceof DefaultError) throw error + logger.error(error, ["Service", "yellow", "PERMISSION", "green"]) + throw new DefaultError(500, "Please contact the maintainer") + } +} + +function findByUserAndProduct(userId, productId) { + try { + const permissions = permissionsRepository.findByUserAndProduct(userId, productId) + if (!permissions || permissions.length === 0) { + throw new DefaultError(404, "No permissions found for this user and product.") + } + return permissions + } catch (error) { + if (error instanceof DefaultError) throw error + logger.error(error, ["Service", "yellow", "PERMISSION", "green"]) + throw new DefaultError(500, "Please contact the maintainer") + } +} + +async function revokeAllOnProduct(userId, productId) { + try { + return permissionsRepository.revokeAllOnProduct(userId, productId) + } catch (error) { + logger.error(error, ["Service", "yellow", "PERMISSION", "green"]) + throw new DefaultError(500, "Please contact the maintainer") + } +} + +function checkPermission(userId, productName, channel, permission) { + try { + return permissionsRepository.hasPermission(userId, productName, channel, permission) + } catch (error) { + logger.error(error, ["Service", "yellow", "PERMISSION", "green"]) + throw new DefaultError(500, "Please contact the maintainer") + } +} + +module.exports = { + grant, + revoke, + checkPermission, + revokeAllOnProduct, + findByUserAndProduct +} \ No newline at end of file diff --git a/services/register.js b/services/register.js deleted file mode 100644 index 1b8b245..0000000 --- a/services/register.js +++ /dev/null @@ -1,19 +0,0 @@ -const crypto = require("node:crypto") -const DefaultError = require("../errors/DefaultError") - -function register({ email, username }) { - const canRegister = true - if (canRegister === true) { - return { - id: crypto.randomUUID(), - username: username, - email: email - } - } else { - throw new DefaultError(418, "I'm a teapot", "", "TeaPotExeception") - } -} - -module.exports = { - register -} \ No newline at end of file diff --git a/services/userService.js b/services/userService.js new file mode 100644 index 0000000..4d540ca --- /dev/null +++ b/services/userService.js @@ -0,0 +1,66 @@ +const userRepository = require("../repositories/userRepository") +const bcrypt = require("bcryptjs") +const logger = require("../modules/logger") +const { DefaultError } = require("../errors/errors") + +async function register({ firstName = null, lastName = null, username, password }) { + const isUserExists = userRepository.findByUsername(username) + if (isUserExists) { + throw new DefaultError(409, "Username taken.") + } + try { + const hashedPassword = await bcrypt.hash(password, 4) + return userRepository.register(firstName, lastName, username, hashedPassword) + } catch (error) { + logger.error(error, ["Service", "yellow", "USER", "green"]) + throw new DefaultError(500, "Please contact the maintainer") + } +} + +async function remove(id) { + const user = userRepository.findById(id) + if (!user) { + throw new DefaultError(404, "User not found.") + } + try { + return userRepository.remove(id) + } catch (error) { + logger.error(error, ["Service", "yellow", "USER", "green"]) + throw new DefaultError(500, "Please contact the maintainer") + } +} + +async function changePassword(id, newPassword) { + const user = userRepository.findById(id) + if (!user) { + throw new DefaultError(404, "User not found.") + } + try { + const hashedPassword = await bcrypt.hash(newPassword, 4) + return userRepository.changePassword(id, hashedPassword) + } catch (error) { + logger.error(error, ["Service", "yellow", "USER", "green"]) + throw new DefaultError(500, "Please contact the maintainer") + } +} + +function findById(id) { + try { + const user = userRepository.findById(id) + if (!user) { + throw new DefaultError(404, "User not found.") + } + return user + } catch (error) { + if (error instanceof DefaultError) throw error + logger.error(error, ["Service", "yellow", "USER", "green"]) + throw new DefaultError(500, "Please contact the maintainer") + } +} + +module.exports = { + remove, + findById, + register, + changePassword, +} \ No newline at end of file diff --git a/test.js b/test.js index 8b851d5..e69de29 100644 --- a/test.js +++ b/test.js @@ -1,3 +0,0 @@ -const dg = require("./modules/databaseGlobals") - -dg.initializeDatabase() \ No newline at end of file