generated from azures04/Base-REST-API
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:
parent
c296f53ba1
commit
1e78bd68cf
3
.gitignore
vendored
3
.gitignore
vendored
@ -131,4 +131,5 @@ dist
|
|||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
#logs
|
#logs
|
||||||
logs
|
logs
|
||||||
|
/data
|
||||||
|
|||||||
5
modules/database.js
Normal file
5
modules/database.js
Normal 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)
|
||||||
83
modules/databaseGlobals.js
Normal file
83
modules/databaseGlobals.js
Normal 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
864
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -17,16 +17,19 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"start:dev": "nodemon .",
|
"start:dev": "nodemon .",
|
||||||
"start": "node .",
|
"start": "node .",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"lint:fix": "eslint . --fix"
|
"lint:fix": "eslint . --fix"
|
||||||
},
|
},
|
||||||
"homepage": "https://gitea.azures.fr/azures04/Base-REST-API",
|
"homepage": "https://gitea.azures.fr/azures04/Base-REST-API",
|
||||||
"readme": "https://gitea.azures.fr/azures04/Base-REST-API/src/branch/main/README.md",
|
"readme": "https://gitea.azures.fr/azures04/Base-REST-API/src/branch/main/README.md",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"bcryptjs": "^3.0.3",
|
||||||
|
"better-sqlite3": "^12.6.2",
|
||||||
"colors": "^1.4.0",
|
"colors": "^1.4.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
|
"ftp-srv": "^4.6.3",
|
||||||
"helmet": "^8.1.0",
|
"helmet": "^8.1.0",
|
||||||
"hpp": "^0.2.3",
|
"hpp": "^0.2.3",
|
||||||
"path-to-regexp": "^8.3.0",
|
"path-to-regexp": "^8.3.0",
|
||||||
|
|||||||
75
repositories/licenceRepository.js
Normal file
75
repositories/licenceRepository.js
Normal 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,
|
||||||
|
}
|
||||||
61
repositories/permissionsRepository.js
Normal file
61
repositories/permissionsRepository.js
Normal 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
|
||||||
|
};
|
||||||
85
repositories/userRepository.js
Normal file
85
repositories/userRepository.js
Normal 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,
|
||||||
|
}
|
||||||
@ -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
|
|
||||||
@ -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
3
test.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
const dg = require("./modules/databaseGlobals")
|
||||||
|
|
||||||
|
dg.initializeDatabase()
|
||||||
@ -1,8 +0,0 @@
|
|||||||
POST http://localhost:3000/register HTTP/1.1
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{
|
|
||||||
"email": "johndoe@exemple.com",
|
|
||||||
"username": "johndoe",
|
|
||||||
"password": "Password123"
|
|
||||||
}
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
GET http://localhost:3000/users/johndoe HTTP/1.1
|
|
||||||
authorization: Bearer token
|
|
||||||
Loading…
x
Reference in New Issue
Block a user