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