Add SQLite database and repositories, remove old routes

Introduces a new SQLite database setup using better-sqlite3, with initialization scripts and repository modules for users, permissions, and licenses. Removes legacy user and register routes and related test files. Updates dependencies to include better-sqlite3, bcryptjs, and ftp-srv.
This commit is contained in:
Gilles Lazures 2026-01-27 05:29:40 +01:00
parent c296f53ba1
commit 1e78bd68cf
13 changed files with 1174 additions and 54 deletions

3
.gitignore vendored
View File

@ -131,4 +131,5 @@ dist
.pnp.*
#logs
logs
logs
/data

5
modules/database.js Normal file
View File

@ -0,0 +1,5 @@
const path = require("node:path")
const Database = require("better-sqlite3")
const databasePath = path.join(process.cwd(), "data", "database.db")
module.exports = new Database(databasePath)

View File

@ -0,0 +1,83 @@
const fs = require("node:fs")
const path = require("node:path")
const Database = require("better-sqlite3")
const databasePath = path.join(process.cwd(), "data", "database.db")
if (!fs.existsSync(path.parse(databasePath).dir)) {
fs.mkdirSync(path.parse(databasePath).dir, { recursive: true })
}
const db = new Database(databasePath)
function initializeDatabase() {
db.exec("PRAGMA foreign_keys = ON")
db.exec(`
CREATE TABLE IF NOT EXISTS accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
firstName TEXT,
lastName TEXT,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL,
createdAt TEXT DEFAULT CURRENT_TIMESTAMP
)
`)
db.exec(`
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
owner INTEGER NOT NULL,
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(owner)
REFERENCES accounts(id)
ON DELETE CASCADE
)
`)
db.exec(`
CREATE TABLE IF NOT EXISTS permissions (
id INTEGER PRIMARY KEY,
userId INTEGER NOT NULL,
productId INTEGER NOT NULL,
channel TEXT DEFAULT '*',
permission TEXT NOT NULL,
FOREIGN KEY(userId)
REFERENCES accounts(id)
ON DELETE CASCADE,
FOREIGN KEY(productId)
REFERENCES products(id)
ON DELETE CASCADE
UNIQUE(
userId,
channel,
productId,
permission
)
)
`)
db.exec(`
CREATE TABLE IF NOT EXISTS licenses (
id INTEGER PRIMARY KEY,
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
)
`)
}
module.exports = {
initializeDatabase
}

864
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -17,16 +17,19 @@
"scripts": {
"start:dev": "nodemon .",
"start": "node .",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"homepage": "https://gitea.azures.fr/azures04/Base-REST-API",
"readme": "https://gitea.azures.fr/azures04/Base-REST-API/src/branch/main/README.md",
"dependencies": {
"bcryptjs": "^3.0.3",
"better-sqlite3": "^12.6.2",
"colors": "^1.4.0",
"cors": "^2.8.5",
"dotenv": "^17.2.3",
"express": "^5.2.1",
"ftp-srv": "^4.6.3",
"helmet": "^8.1.0",
"hpp": "^0.2.3",
"path-to-regexp": "^8.3.0",

View File

@ -0,0 +1,75 @@
const database = require("../modules/database")
function create(key, productId, targetChannel) {
const query = `
INSERT INTO licenses (
key,
productId,
targetChannel
)
VALUES (
?,
?,
?
)
`
const statement = database.prepare(query)
return statement.run(key, productId, targetChannel)
}
function findByKey(key) {
const query = `
SELECT id, key, productId, targetChannel, userId, usedAt
FROM licenses
WHERE key = ?
`
const statement = database.prepare(query)
return statement.get(key)
}
function activate(licenseId, userId) {
const query = `
UPDATE licenses
SET userId = ?, usedAt = CURRENT_TIMESTAMP
WHERE id = ? AND userId IS NULL
`
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)
}
function findByProduct(productId) {
const query = `
SELECT * FROM licenses
WHERE productId = ?
`
const statement = database.prepare(query)
return statement.all(productId)
}
function remove(id) {
const query = `
DELETE FROM licenses
WHERE id = ?
`
const statement = database.prepare(query)
return statement.run(id)
}
module.exports = {
create,
remove,
activate,
findByKey,
deactivate,
findByProduct,
}

View File

@ -0,0 +1,61 @@
const database = require("../modules/database")
function grant(userId, productId, channel, permissionKey) {
const query = `
INSERT INTO permissions (
userId,
productId,
channel,
permissionKey
)
VALUES (
?,
?,
?,
?
)
`
const statement = database.prepare(query)
return statement.run(userId, productId, channel, permissionKey)
}
function revoke(userId, productId, channel, permissionKey) {
const query = `
DELETE FROM permissions
WHERE userId = ?
AND productId = ?
AND channel = ?
AND permissionKey = ?
`
const statement = database.prepare(query)
return statement.run(userId, productId, channel, permissionKey)
}
function findByUserAndProduct(userId, productId) {
const query = `
SELECT channel, permissionKey
FROM permissions
WHERE userId = ?
AND productId = ?
`
const statement = database.prepare(query)
return statement.all(userId, productId)
}
function revokeAllOnProduct(userId, productId) {
const query = `
DELETE FROM permissions
WHERE userId = ?
AND productId = ?
`
const statement = database.prepare(query)
return statement.run(userId, productId)
}
module.exports = {
grant,
revoke,
findByUserAndProduct,
revokeAllOnProduct
};

View File

@ -0,0 +1,85 @@
const database = require("../modules/database")
function register(firstName = null, lastName = null, username, hashedPassword) {
const query = `
INSERT INTO accounts (
firstName,
lastName,
username,
password
)
VALUES (
?,
?,
?,
?
)
`
const statement = database.prepare(query)
return statement.run(firstName, lastName, username, hashedPassword)
}
function remove(id) {
const query = `
DELETE FROM accounts
WHERE id = ?
`
const statement = database.prepare(query)
return statement.run(id)
}
function changePassword(id, hashedPassword) {
const query = `
UPDATE accounts
SET password = ?
WHERE id = ?
`
const statement = database.prepare(query)
return statement.run(id, hashedPassword)
}
function findByUsername(identifier) {
const query = `
SELECT id, username, password
FROM accounts
WHERE username = ?
`
const statement = database.prepare(query)
return statement.get(identifier)
}
function findById(identifier) {
const query = `
SELECT id, firstName, lastName, username, createdAt
FROM accounts
WHERE id = ?
`
const statement = database.prepare(query)
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,
}

View File

@ -1,15 +0,0 @@
const express = require("express")
const router = express.Router()
const registerService = require("../services/register")
router.post("/", async (req, res) => {
const { email, username, password } = req.body
const registerResult = registerService.register({ email, username, password })
return res.status(200).json({
code: 200,
message: "User successfully registered",
data: registerResult
})
})
module.exports = router

View File

@ -1,17 +0,0 @@
const express = require("express")
const DefaultError = require("../../errors/DefaultError")
const router = express.Router()
router.get("", async (req, res) => {
const bearer = req.headers.authorization
if (bearer == "Bearer token") {
return res.status(200).json({
id: req.params.id,
username: "johndoe"
})
} else {
throw new DefaultError(403, "Invalid token", "", "InvalidTokenException")
}
})
module.exports = router

3
test.js Normal file
View File

@ -0,0 +1,3 @@
const dg = require("./modules/databaseGlobals")
dg.initializeDatabase()

View File

@ -1,8 +0,0 @@
POST http://localhost:3000/register HTTP/1.1
Content-Type: application/json
{
"email": "johndoe@exemple.com",
"username": "johndoe",
"password": "Password123"
}

View File

@ -1,2 +0,0 @@
GET http://localhost:3000/users/johndoe HTTP/1.1
authorization: Bearer token