push de la fleme
This commit is contained in:
parent
66e402cbf6
commit
f7fcc0d051
2
.env.exemple
Normal file
2
.env.exemple
Normal file
@ -0,0 +1,2 @@
|
||||
HASH_SECRET=''
|
||||
JWT_SECRET=''
|
135
.gitignore
vendored
135
.gitignore
vendored
@ -1,132 +1,5 @@
|
||||
# ---> Node
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
node_modules
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
.idea
|
||||
pnpm-lock.yaml
|
||||
yarn.lock
|
26
app.js
Normal file
26
app.js
Normal file
@ -0,0 +1,26 @@
|
||||
const express = require('express')
|
||||
const cors = require('cors')
|
||||
const app = express()
|
||||
const { Logger } = require('tslog');
|
||||
|
||||
const logger = new Logger({ name: "App" });
|
||||
|
||||
app.use(express.urlencoded({ extended: true }))
|
||||
app.use(express.json())
|
||||
app.use(cors())
|
||||
|
||||
// ROUTES
|
||||
|
||||
const AuthRoutes = require("./controllers/routes/auth")
|
||||
const ImageRoutes = require("./controllers/routes/thread")
|
||||
const EventRoutes = require("./controllers/routes/event")
|
||||
|
||||
app.use('/auth', AuthRoutes)
|
||||
app.use('/image', ImageRoutes)
|
||||
app.use('/event', EventRoutes)
|
||||
|
||||
app.listen(3333)
|
||||
logger.info('Server is running !')
|
||||
app.on('APP_STARTED', ()=>{
|
||||
|
||||
})
|
163
arkit.svg
Normal file
163
arkit.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 32 KiB |
15
biome.json
Normal file
15
biome.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.6.4/schema.json",
|
||||
"organizeImports": {
|
||||
"enabled": true
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true,
|
||||
"performance": {
|
||||
"recommended": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
343
controllers/AuthController.js
Normal file
343
controllers/AuthController.js
Normal file
@ -0,0 +1,343 @@
|
||||
const {
|
||||
RegisterService,
|
||||
LoginService,
|
||||
getAllUsersService,
|
||||
getUserFromIdService,
|
||||
editUserService,
|
||||
deleteUserService
|
||||
} = require("../services/UserService");
|
||||
const {JwtVerify} = require("../services/JwtService");
|
||||
const { Logger } = require('tslog');
|
||||
|
||||
const logger = new Logger({ name: "Auth Controller" });
|
||||
|
||||
/**
|
||||
* Registers a user.
|
||||
*
|
||||
* @async
|
||||
* @param {Object} req - The request object.
|
||||
* @param {Object} res - The response object.
|
||||
* @return {Object} The response object containing the result of the registration.
|
||||
* If successful, it will contain the registered user's data.
|
||||
* If there is an error, it will contain an error name and message.
|
||||
*/
|
||||
async function registerUser(req, res) {
|
||||
const body = req.body;
|
||||
if (!body) {
|
||||
logger.warn(`Invalid input data (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({ error: 'Invalid input data' });
|
||||
}
|
||||
if (!body.password || !body.username || !body.firstName || !body.lastName || !body.displayName) {
|
||||
logger.warn(`Field(s) missing (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({ error: 'Field(s) missing' });
|
||||
}
|
||||
// sanitize data
|
||||
let gdpr = false
|
||||
if (body.gdpr === true) {gdpr = true}
|
||||
const sanitizeData= {
|
||||
username: `${body.username}`,
|
||||
displayName: `${body.displayName}`,
|
||||
gdpr: gdpr,
|
||||
password: `${body.password}`,
|
||||
firstName: `${body.firstName}`,
|
||||
lastName: `${body.lastName}`,
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the result of the registration service.
|
||||
*
|
||||
* @typedef {Object} RegisterServiceResult
|
||||
* @property {Promise} promise - The promise that resolves when the service registration is complete.
|
||||
*/
|
||||
const RegisterServiceResult = await RegisterService(sanitizeData)
|
||||
|
||||
if (RegisterServiceResult.error === "gdprNotApproved") {
|
||||
logger.warn(`GDPR not approved (${req.ip})`);
|
||||
return res
|
||||
.status(400)
|
||||
.json({
|
||||
error: RegisterServiceResult.error,
|
||||
message: "GDPR not accepted."
|
||||
});
|
||||
}
|
||||
if (RegisterServiceResult.error === "exist") {
|
||||
logger.warn(`The user already exists (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({
|
||||
error: RegisterServiceResult.error,
|
||||
message: "The user already exists."
|
||||
});
|
||||
}
|
||||
|
||||
// SUCCESS
|
||||
logger.info(`User registered successfully (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(201)
|
||||
.json(RegisterServiceResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs in a user with the provided username and password.
|
||||
*
|
||||
* @param {Object} req - The request object containing the body data.
|
||||
* @param {Object} res - The response object to send the result.
|
||||
* @returns {Promise<void>} - A promise that resolves once the login process is complete.
|
||||
*/
|
||||
async function loginUser(req, res) {
|
||||
|
||||
const body = req.body;
|
||||
if (!body) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({ error: 'Invalid input data' });
|
||||
}
|
||||
if (!body.password || !body.username) {
|
||||
logger.warn(`Field(s) missing (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({ error: 'Field(s) missing' });
|
||||
}
|
||||
|
||||
const loginData = {
|
||||
username: `${body.username}`,
|
||||
password: `${body.password}`
|
||||
};
|
||||
console.log(body)
|
||||
const LoginServiceResult = await LoginService(loginData);
|
||||
console.log(LoginServiceResult)
|
||||
|
||||
if (LoginServiceResult.error === "userNotFound") {
|
||||
console.log('POOL')
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({
|
||||
error: LoginServiceResult.error,
|
||||
message: "User not found."
|
||||
});
|
||||
}
|
||||
if (LoginServiceResult.error === "invalidPassword") {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(401)
|
||||
.json({
|
||||
error: LoginServiceResult.error,
|
||||
message: "Invalid password."
|
||||
});
|
||||
}
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json(LoginServiceResult);
|
||||
}
|
||||
|
||||
//TODO - To test
|
||||
async function getAllUsers(req, res) {
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader.split(' ')[1];
|
||||
const payload = await JwtVerify(bearerToken);
|
||||
const sourceUser = await getUserFromIdService(payload.sub)
|
||||
if (!sourceUser) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({ error: 'You dont exist anymore' });
|
||||
}
|
||||
if (!sourceUser.isAdmin) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(403)
|
||||
.json({ error: 'Unauthorized' });
|
||||
}
|
||||
const AllUserResponse = await getAllUsersService()
|
||||
if (!AllUserResponse.users) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(500)
|
||||
.json({ error: 'Internal server error' });
|
||||
}
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json(AllUserResponse);
|
||||
}
|
||||
|
||||
//TODO - To test
|
||||
/**
|
||||
* Get user from the database based on the provided user ID and return it as a response.
|
||||
*
|
||||
* @async
|
||||
* @param {object} req - The request object containing the user ID as a parameter.
|
||||
* @param {object} res - The response object to be used for sending the user data or error.
|
||||
* @return {Promise<void>} - A Promise that resolves when the user data is sent to the client or an error occurred.
|
||||
*/
|
||||
async function getUser(req, res) {
|
||||
const userId = req.params.userId;
|
||||
const dbUser = await getUserFromIdService(userId);
|
||||
if (!dbUser) {
|
||||
logger.warn(`User not found (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({ error: 'User not found' });
|
||||
}
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json(dbUser);
|
||||
}
|
||||
|
||||
//TODO - To test
|
||||
async function editUser(req, res) {
|
||||
const body = req.body;
|
||||
if (!body) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({ error: 'Field(s) missing' });
|
||||
}
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader.split(' ')[1];
|
||||
const payload = await JwtVerify(bearerToken);
|
||||
const sourceUser = await getUserFromIdService(payload.sub)
|
||||
|
||||
/**
|
||||
* Represents the user ID that is the target for a specific operation.
|
||||
*
|
||||
* @type {string|number}
|
||||
*/
|
||||
const targetUserId = body.targetId | payload.sub
|
||||
|
||||
if (!sourceUser) {
|
||||
logger.warn(`Unauthorized access attempt (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({ error: 'You dont exist anymore' });
|
||||
}
|
||||
if (sourceUser.isAdmin || sourceUser.id === payload.sub) {
|
||||
if (sourceUser.isAdmin) {
|
||||
logger.info(`EDIT :> Source user is an admin (${sourceUser.displayName})`)
|
||||
} else {
|
||||
logger.info(`EDIT :> Source user modify itself (${sourceUser.displayName})`)
|
||||
}
|
||||
|
||||
// biome-ignore lint/style/useConst: <explanation>
|
||||
let modifiedData= {}
|
||||
if (body.firstName) modifiedData.firstName = `${body.firstName}`;
|
||||
if (body.lastName) modifiedData.lastName = `${body.lastName}`;
|
||||
if (body.displayName) modifiedData.displayName = `${body.displayName}`;
|
||||
if (body.password) modifiedData.password = `${body.password}`;
|
||||
|
||||
//Call service
|
||||
const EditUserServiceResult = await editUserService(`${targetUserId}`, modifiedData);
|
||||
if (EditUserServiceResult.error === 'userNotFound') {
|
||||
logger.warn(`User not found (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({ error: 'User not found' });
|
||||
}
|
||||
if (EditUserServiceResult.error !== 'none') {
|
||||
logger.error(`Error occurred during user edit (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(500)
|
||||
.json({ error: 'Internal server error' });
|
||||
}
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json(EditUserServiceResult);
|
||||
}
|
||||
//Not itself or
|
||||
logger.warn(`Unauthorized access attempt, not self or admin (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(403)
|
||||
.json({ error: 'Unauthorized' });
|
||||
|
||||
|
||||
}
|
||||
|
||||
//TODO - To test
|
||||
async function deleteUser(req, res) {
|
||||
const body = req.body;
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader.split(' ')[1];
|
||||
const payload = await JwtVerify(bearerToken);
|
||||
const sourceUser = await getUserFromIdService(payload.sub)
|
||||
const targetUserId = body.targetId | payload.sub
|
||||
if (!sourceUser) {
|
||||
logger.warn(`Unauthorized access attempt (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({ error: 'You dont exist anymore' });
|
||||
}
|
||||
if (sourceUser.isAdmin || sourceUser.id === payload.sub) {
|
||||
const deleteUserServiceResult = await deleteUserService(`${targetUserId}`);
|
||||
if (!deleteUserServiceResult) {
|
||||
logger.error(`Error occurred during user delete (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(500)
|
||||
.json({ error: 'Internal server error' });
|
||||
}
|
||||
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json({ message: 'User deleted successfully' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the user's information.
|
||||
*
|
||||
* @param {object} req - The incoming request object.
|
||||
* @param {object} res - The outgoing response object.
|
||||
*/
|
||||
async function getSelf(req, res) {
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader.split(' ')[1];
|
||||
const payload = await JwtVerify(bearerToken);
|
||||
console.log(payload)
|
||||
const dbUser = await getUserFromIdService(payload.sub)
|
||||
if (!dbUser) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({ error: 'User not found' });
|
||||
}
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json({
|
||||
username: dbUser.username,
|
||||
displayName: dbUser.displayName,
|
||||
firstName: dbUser.firstName,
|
||||
lastName: dbUser.lastName
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
registerUser,
|
||||
loginUser,
|
||||
getAllUsers,
|
||||
getUser,
|
||||
editUser,
|
||||
deleteUser,
|
||||
getSelf
|
||||
}
|
180
controllers/EventController.js
Normal file
180
controllers/EventController.js
Normal file
@ -0,0 +1,180 @@
|
||||
const {
|
||||
getUserFromIdService
|
||||
} = require("../services/UserService");
|
||||
|
||||
const {
|
||||
JwtVerify
|
||||
} = require("../services/JwtService");
|
||||
|
||||
const {
|
||||
getAllEventsService,
|
||||
getEventFromIdService, alterUserSubscribedEventStateService, getUserSubscribedEventService
|
||||
} = require("../services/EventService");
|
||||
|
||||
//TODO - To test
|
||||
/**
|
||||
* Retrieves all events.
|
||||
*
|
||||
* @param {Object} req - The request object.
|
||||
* @param {Object} res - The response object.
|
||||
* @return {Object} The response object containing all events or an error message if events not found.
|
||||
*/
|
||||
async function getAllEvents(req, res) {
|
||||
const events = await getAllEventsService('public');
|
||||
if (!events) {
|
||||
return res.status(404).json({ message: "Events not found" });
|
||||
}
|
||||
return res.status(200).json(events);
|
||||
}
|
||||
|
||||
//TODO - To test
|
||||
/**
|
||||
* Retrieves an event by ID.
|
||||
*
|
||||
* @param {object} req - The request object.
|
||||
* @param {object} res - The response object.
|
||||
*
|
||||
* @return {object} Returns a response with the retrieved event.
|
||||
*
|
||||
* @throws {Error} Throws an error if the event ID is missing or if the event is not found.
|
||||
*/
|
||||
async function getEvent(req, res) {
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader.split(' ')[1];
|
||||
const payload = await JwtVerify(bearerToken);
|
||||
const sourceUser = await getUserFromIdService(payload.sub)
|
||||
const targetId = req.params.id;
|
||||
if (!targetId) {
|
||||
return res.status(400).json({ message: "Event ID is missing" });
|
||||
}
|
||||
const result = await getEventFromIdService(targetId)
|
||||
if (!result) {
|
||||
return res.status(404).json({ message: "Event not found" });
|
||||
}
|
||||
return res.status(200).json(result);
|
||||
}
|
||||
|
||||
//TODO Owner user, admin user ===
|
||||
async function editEvent(req, res) {
|
||||
const body = req.body;
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader.split(' ')[1];
|
||||
const payload = await JwtVerify(bearerToken);
|
||||
const sourceUser = await getUserFromIdService(payload.sub)
|
||||
const targetId = body.targetId || sourceUser.id;
|
||||
const eventTargetId = req.params.id
|
||||
if (targetId !== sourceUser.id && !sourceUser.isAdmin) {
|
||||
res.status(403).json({ message: "Unauthorized request" });
|
||||
}
|
||||
if (!eventTargetId) {
|
||||
res.status(400).json({ message: "Event target ID is missing" });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//TODO Owner user, admin user ===
|
||||
async function deleteEvent(req, res) {
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader.split(' ')[1];
|
||||
const payload = await JwtVerify(bearerToken);
|
||||
const sourceUser = await getUserFromIdService(payload.sub)
|
||||
}
|
||||
|
||||
//TODO Event creation by logged user ===
|
||||
async function createNewEvent(req, res) {
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader.split(' ')[1];
|
||||
const payload = await JwtVerify(bearerToken);
|
||||
const sourceUser = await getUserFromIdService(payload.sub)
|
||||
}
|
||||
|
||||
//TODO - To test
|
||||
/**
|
||||
* Retrieves the subscribed event for the specified user.
|
||||
*
|
||||
* @param {Object} req - The request object.
|
||||
* @param {Object} res - The response object.
|
||||
* @returns {Object} The subscribed event information.
|
||||
*/
|
||||
async function getSubscribedEvent(req, res) {
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader.split(' ')[1];
|
||||
const payload = await JwtVerify(bearerToken);
|
||||
const sourceUser = await getUserFromIdService(payload.sub)
|
||||
const targetId = body.targetId || sourceUser.id;
|
||||
if (targetId !== sourceUser.id && !sourceUser.isAdmin) {
|
||||
res.status(403).json({ message: "Unauthorized request" });
|
||||
}
|
||||
const subscribedEventResult = await getUserSubscribedEventService(targetId);
|
||||
if (subscribedEventResult.error === 'noSubscribedEventFound') {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({
|
||||
error: 'noSubscribedEventFound',
|
||||
message: 'No subscribed event found'
|
||||
});
|
||||
}
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json(subscribedEventResult);
|
||||
}
|
||||
|
||||
//TODO - To test
|
||||
/**
|
||||
* Alter the subscription state of an event for a user.
|
||||
* @param {Object} req
|
||||
* - The request object.
|
||||
* @param {Object} req.body
|
||||
* - The body of the request containing the desired subscription state.
|
||||
* @param {string} req.headers.authorization
|
||||
* - The authorization header containing the bearer token.
|
||||
* @param {string} req.params.id
|
||||
* - The ID of the target event.
|
||||
* @param {Object} res
|
||||
* - The response object.
|
||||
* @returns {Object} The response object.
|
||||
*/
|
||||
async function alterSubscribedEventState(req, res) {
|
||||
const body = req.body
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader.split(' ')[1];
|
||||
const payload = await JwtVerify(bearerToken);
|
||||
const sourceUser = await getUserFromIdService(payload.sub)
|
||||
const eventTargetId = req.params.id;
|
||||
const userTargetId = body.userId || sourceUser.id
|
||||
const wantedState = body.subscribed === true
|
||||
if (!eventTargetId) {
|
||||
return res.status(400).json({ message: "Event target ID is missing" });
|
||||
}
|
||||
if (userTargetId !== sourceUser.id && !sourceUser.isAdmin) {
|
||||
return res.status(403).json({ message: "Unauthorized request" });
|
||||
}
|
||||
|
||||
const alterEventSubStateResult = await alterUserSubscribedEventStateService(
|
||||
userTargetId,
|
||||
eventTargetId,
|
||||
wantedState
|
||||
)
|
||||
|
||||
if (alterEventSubStateResult.error === 'none') {
|
||||
return res.status(200).json({
|
||||
message: "Event subscription state altered"
|
||||
});
|
||||
}
|
||||
return res.status(400).json({
|
||||
error: alterEventSubStateResult.error,
|
||||
message: "Event subscription state not altered"
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getAllEvent: getAllEvents,
|
||||
getEvent,
|
||||
editEvent,
|
||||
deleteEvent,
|
||||
createNewEvent,
|
||||
getSubscribedEvent,
|
||||
alterSubscribedEventState
|
||||
}
|
22
controllers/ImageController.js
Normal file
22
controllers/ImageController.js
Normal file
@ -0,0 +1,22 @@
|
||||
//TODO Logged user, size limit, format type
|
||||
async function addNewImage(req, res) {
|
||||
}
|
||||
|
||||
//TODO Admin user
|
||||
async function GetAllImages(req, res) {
|
||||
}
|
||||
|
||||
//TODO Logged and non logged
|
||||
async function getImage(req, res) {
|
||||
}
|
||||
|
||||
//TODO Owner user and admin user
|
||||
async function deleteImage(req, res) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
addNewImage,
|
||||
GetAllImages,
|
||||
getImage,
|
||||
deleteImage
|
||||
}
|
281
controllers/ThreadController.js
Normal file
281
controllers/ThreadController.js
Normal file
@ -0,0 +1,281 @@
|
||||
const {JwtVerify} = require("../services/JwtService");
|
||||
const {
|
||||
CreateThreadService,
|
||||
GetAllThreadService,
|
||||
GetThreadByIdService,
|
||||
UpdateThreadService,
|
||||
DeleteThreadService,
|
||||
GetUserThreadService
|
||||
} = require("../services/ThreadService");
|
||||
const req = require("express/lib/request");
|
||||
|
||||
/**
|
||||
* Retrieves the payload of a JWT token from the request headers.
|
||||
* @param {Object} req - The HTTP request object.
|
||||
* @return {Promise<Object|boolean>} - The payload of the JWT token if it exists, otherwise false.
|
||||
*/
|
||||
async function getJwtPayload(req) {
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader.split(' ')[1];
|
||||
const jwtPayload = await JwtVerify(bearerToken);
|
||||
if (jwtPayload) {
|
||||
return jwtPayload;
|
||||
}
|
||||
console.log(`AUTH :> Invalid jwt (${req.ip})`)
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* CreateThreadController - Controller function for creating a thread
|
||||
*
|
||||
* @param {Object} req - The request object
|
||||
* @param {Object} res - The response object
|
||||
*
|
||||
* @return {Promise} - A Promise that resolves to the response object with appropriate status and JSON payload
|
||||
*/
|
||||
async function CreateThreadController(req, res) {
|
||||
console.log(`CTRL :> Thread creation (${req.ip})`)
|
||||
const payload = await getJwtPayload(req)
|
||||
if (payload) {
|
||||
const body = req.body;
|
||||
if (!body) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({ error: 'Invalid input data' });
|
||||
}
|
||||
if (!body.title || !body.subTitle || !body.base64Banner || !body.desc || !body.price) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({ error: 'Field(s) missing' });
|
||||
}
|
||||
const userId = payload.sub
|
||||
const sanitizedData = {
|
||||
userId: `${userId}`,
|
||||
title: `${body.title}`,
|
||||
subTitle: `${body.subTitle}`,
|
||||
base64Banner: `${body.base64Banner}`,
|
||||
desc: `${body.desc}`,
|
||||
price: Number.parseFloat(body.price)
|
||||
}
|
||||
const CreateThreadServiceResult = await CreateThreadService(sanitizedData)
|
||||
|
||||
if (!CreateThreadServiceResult) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(500)
|
||||
.json({ error: 'Failed to create thread' });
|
||||
}
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json({ success: 'Thread created successfully' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a thread by its ID.
|
||||
* @async
|
||||
* @function GetThreadByIdController
|
||||
* @param {Object} req - The request object containing the parameters and body.
|
||||
* @param {Object} res - The response object used to send the response.
|
||||
* @return {Promise<void>} - A promise that resolves once the thread is retrieved and the response is sent.
|
||||
*/
|
||||
async function GetThreadByIdController(req, res) {
|
||||
const payload = await getJwtPayload(req)
|
||||
const body = req.body;
|
||||
if (payload) {
|
||||
if (!req.params.id) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({ error: 'Field(s) missing' });
|
||||
}
|
||||
const threadId = req.params.id;
|
||||
const GetThreadByIdServiceResult = await GetThreadByIdService(threadId);
|
||||
if (GetThreadByIdServiceResult.error === 'ThreadNotFound') {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({ message: `${GetThreadByIdServiceResult.message}` });
|
||||
}
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json(GetThreadByIdServiceResult);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all threads for a user.
|
||||
*
|
||||
* @param {Object} req - The request object.
|
||||
* @param {Object} req.ip - The IP address of the requesting client.
|
||||
* @param {Object} res - The response object.
|
||||
* @returns {Promise} A promise that resolves with the JSON response containing all threads, or rejects with an error.
|
||||
*/
|
||||
async function GetAllThreadController(req, res) {
|
||||
console.log(`CTRL :> Query all threads (${req.ip})`)
|
||||
const payload = await getJwtPayload(req)
|
||||
if (payload) {
|
||||
const ServiceResult = await GetAllThreadService({userId: payload.sub})
|
||||
if (!ServiceResult) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json({ error: 'No threads found' });
|
||||
}
|
||||
if (ServiceResult.error) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(500)
|
||||
.json({ error: ServiceResult.message });
|
||||
}
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json(ServiceResult);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a user's thread from the server.
|
||||
*
|
||||
* @param {Object} req - The request object.
|
||||
* @param {Object} res - The response object.
|
||||
* @return {Object} The response object containing the retrieved user thread.
|
||||
* @throws {Error} If an error occurs while retrieving the user thread.
|
||||
*/
|
||||
async function GetUserThreadController(req, res) {
|
||||
const payload = await getJwtPayload(req)
|
||||
const GetUserThreadServiceResult = await GetUserThreadService(payload.sub);
|
||||
if (GetUserThreadServiceResult.error === 'ThreadNotFound') {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json({ error: 'No threads found' });
|
||||
}
|
||||
if (GetUserThreadServiceResult.error) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(500)
|
||||
.json({ error: GetUserThreadServiceResult.message });
|
||||
}
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json(GetUserThreadServiceResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* UpdateThreadController is an asynchronous function that handles the logic for updating a thread.
|
||||
*
|
||||
* @param {Object} req - The request object.
|
||||
* @param {Object} res - The response object.
|
||||
* @return {Object} - The response object.
|
||||
*/
|
||||
async function UpdateThreadController(req, res) {
|
||||
const payload = await getJwtPayload(req)
|
||||
if (payload) {
|
||||
const body = req.body;
|
||||
if (!body) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({ error: 'Invalid input data' });
|
||||
}
|
||||
if (!req.params.id) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({ error: 'Missing identifier' });
|
||||
}
|
||||
const threadId = req.params.id;
|
||||
if (!body) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({ error: 'Invalid input data' });
|
||||
}
|
||||
if (!body.title || !body.subTitle || !body.base64Banner || !body.desc || !body.price || !body.userId) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({ error: 'Field(s) missing' });
|
||||
}
|
||||
const sanitizedData = {
|
||||
title: `${body.title}`,
|
||||
subTitle: `${body.subTitle}`,
|
||||
base64Banner: `${body.base64Banner}`,
|
||||
desc: `${body.desc}`,
|
||||
price: Number.parseFloat(body.price)
|
||||
}
|
||||
const UpdateThreadServiceResult = await UpdateThreadService(threadId, sanitizedData);
|
||||
|
||||
if (UpdateThreadServiceResult.error === 'ThreadNotFound') {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({ message: `${UpdateThreadServiceResult.message}` });
|
||||
}
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json({ message: 'Thread updated successfully' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a thread.
|
||||
* @param {Object} req - The request object.
|
||||
* @param {Object} res - The response object.
|
||||
* @return {Promise} - A promise that resolves with the response.
|
||||
*/
|
||||
async function DeleteThreadController(req, res) {
|
||||
const payload = await getJwtPayload(req)
|
||||
if (payload) {
|
||||
const body = req.body;
|
||||
if (!body) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({ error: 'Invalid input data' });
|
||||
}
|
||||
if (!req.params.id) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({ error: 'Field(s) missing' });
|
||||
}
|
||||
|
||||
const threadId = `${body.id}`;
|
||||
const DeleteThreadServiceResult = await DeleteThreadService(threadId);
|
||||
|
||||
if (!DeleteThreadServiceResult) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(500)
|
||||
.json({ error: 'Failed to delete thread' });
|
||||
}
|
||||
if (DeleteThreadServiceResult.error === 'ThreadNotFound') {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({ message: `${DeleteThreadServiceResult.message}` });
|
||||
}
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json({ message: 'Thread deleted successfully' });
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
CreateThreadController,
|
||||
GetThreadByIdController,
|
||||
GetAllThreadController,
|
||||
GetUserThreadController,
|
||||
UpdateThreadController,
|
||||
DeleteThreadController
|
||||
}
|
23
controllers/routes/auth/index.js
Normal file
23
controllers/routes/auth/index.js
Normal file
@ -0,0 +1,23 @@
|
||||
const express = require("express");
|
||||
const router = express.Router()
|
||||
|
||||
const {
|
||||
getSelf,
|
||||
loginUser,
|
||||
registerUser,
|
||||
getUser,
|
||||
editUser,
|
||||
deleteUser
|
||||
} = require("../../AuthController");
|
||||
const {validateJWT} = require("../../../middlewares/AuthorizationMiddleware");
|
||||
|
||||
router.route("/login").post(loginUser)
|
||||
router.route("/register").post(registerUser)
|
||||
|
||||
router.route("/me").get(validateJWT, getSelf)
|
||||
|
||||
router.route("/:id").get(validateJWT, getUser)
|
||||
router.route("/:id").patch(validateJWT, editUser)
|
||||
router.route("/:id").delete(validateJWT, deleteUser)
|
||||
|
||||
module.exports = router
|
24
controllers/routes/event/index.js
Normal file
24
controllers/routes/event/index.js
Normal file
@ -0,0 +1,24 @@
|
||||
const express = require("express");
|
||||
const router = express.Router()
|
||||
|
||||
const {
|
||||
getSubscribedEvent,
|
||||
getEvent,
|
||||
getAllEvent,
|
||||
alterSubscribedEventState,
|
||||
createNewEvent,
|
||||
deleteEvent,
|
||||
editEvent
|
||||
} = require("../../EventController");
|
||||
const {validateJWT} = require("../../../middlewares/AuthorizationMiddleware");
|
||||
|
||||
router.route("/all").get(getAllEvent)
|
||||
router.route("/subscribed").get(validateJWT, getSubscribedEvent)
|
||||
router.route("/subscribed").post(validateJWT, alterSubscribedEventState)
|
||||
router.route("/new").post(validateJWT, createNewEvent)
|
||||
|
||||
router.route("/:id").get(validateJWT, getEvent)
|
||||
router.route("/:id").patch(validateJWT, editEvent)
|
||||
router.route("/:id").delete(validateJWT, deleteEvent)
|
||||
|
||||
module.exports = router
|
13
controllers/routes/image/index.js
Normal file
13
controllers/routes/image/index.js
Normal file
@ -0,0 +1,13 @@
|
||||
const express = require("express");
|
||||
const router = express.Router()
|
||||
|
||||
const {} = require("../../EventController");
|
||||
const {isAdmin, validateJWT} = require("../../../middlewares/AuthorizationMiddleware");
|
||||
|
||||
router.route("/all").get(isAdmin)
|
||||
router.route("/new").post(validateJWT)
|
||||
|
||||
router.route("/:id").get(validateJWT)
|
||||
router.route("/:id").delete(validateJWT)
|
||||
|
||||
module.exports = router
|
28
controllers/routes/thread/index.js
Normal file
28
controllers/routes/thread/index.js
Normal file
@ -0,0 +1,28 @@
|
||||
const express = require("express");
|
||||
const {
|
||||
CreateThreadController,
|
||||
GetThreadByIdController,
|
||||
GetAllThreadController,
|
||||
UpdateThreadController,
|
||||
DeleteThreadController,
|
||||
GetUserThreadController
|
||||
} = require("../../ThreadController");
|
||||
const {validateJWT} = require("../../../middlewares/AuthorizationMiddleware");
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
// CREATE
|
||||
router.route("/new").post(validateJWT, CreateThreadController)
|
||||
|
||||
// READ
|
||||
router.route("/all").get(validateJWT, GetAllThreadController)
|
||||
router.route("/user").get(validateJWT, GetUserThreadController)
|
||||
router.route("/:id").get(validateJWT, GetThreadByIdController)
|
||||
|
||||
// UPDATE
|
||||
router.route("/:id").patch(validateJWT, UpdateThreadController)
|
||||
|
||||
// DELETE
|
||||
router.route("/:id").delete(validateJWT, DeleteThreadController)
|
||||
|
||||
module.exports = router
|
49
middlewares/AuthorizationMiddleware.js
Normal file
49
middlewares/AuthorizationMiddleware.js
Normal file
@ -0,0 +1,49 @@
|
||||
const {JwtVerify} = require("../services/JwtService");
|
||||
const {getUserFromId} = require("../services/UserService");
|
||||
const {log} = require("../utils/logging");
|
||||
const UNAUTHORIZED = 401;
|
||||
const FORBIDDEN = 403;
|
||||
const UNAUTH_MESSAGE = 'Missing Authorization Header';
|
||||
const INVALID_TOKEN_MESSAGE = 'Invalid or expired token.';
|
||||
|
||||
async function validateJWT(req, res, next) {
|
||||
log('MIDDLEWARE', 'JWT', `Vérification du jwt... (${req.ip})`)
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader) {
|
||||
res.status(UNAUTHORIZED).json({message: UNAUTH_MESSAGE});
|
||||
return;
|
||||
}
|
||||
|
||||
const bearerToken = authHeader.split(' ')[1];
|
||||
const isTokenValid = await JwtVerify(bearerToken);
|
||||
|
||||
if (isTokenValid !== false) {
|
||||
log('MIDDLEWARE', 'JWT', `Token valide. (${req.ip})`)
|
||||
next();
|
||||
} else {
|
||||
log('MIDDLEWARE', 'CHECK', `Token invalide (${req.ip})`)
|
||||
res.status(FORBIDDEN).json({message: INVALID_TOKEN_MESSAGE});
|
||||
}
|
||||
}
|
||||
|
||||
async function isAdmin(req, res, next) {
|
||||
log('MIDDLEWARE', 'ROLE', `Vérification du role... (${req.ip})`)
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader.split(' ')[1];
|
||||
const payload = await JwtVerify(bearerToken);
|
||||
const dbUser = await getUserFromId(payload.sub)
|
||||
if (!dbUser || !dbUser.isAdmin) {
|
||||
log('MIDDLEWARE', 'ROLE', `Non admin ou éxistant. (${req.ip})`)
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(403)
|
||||
.json({ error: 'Unauthorized' });
|
||||
}
|
||||
log('MIDDLEWARE', 'ROLE', `Accès admin validé. (${req.ip})`)
|
||||
next();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
validateJWT,
|
||||
isAdmin
|
||||
};
|
37
models/Event.js
Normal file
37
models/Event.js
Normal file
@ -0,0 +1,37 @@
|
||||
const { v4: uuid, parse } = require('uuid');
|
||||
|
||||
class Event {
|
||||
members = [];
|
||||
constructor(title, subTitle, base64Banner, desc, date, were, maxMembers, authorId, id, members) {
|
||||
if (!id || parse(id)) {
|
||||
this.id = uuid(undefined, undefined, undefined);
|
||||
} else {
|
||||
this.id = id;
|
||||
}
|
||||
if (members) {
|
||||
this.members = members;
|
||||
}
|
||||
this.title = title;
|
||||
this.subTitle = subTitle;
|
||||
this.base64Banner = base64Banner;
|
||||
this.desc = desc;
|
||||
this.date = date;
|
||||
this.were = were;
|
||||
this.maxMembers = maxMembers;
|
||||
this.authorId = authorId;
|
||||
this.members.push(`${authorId}`)
|
||||
}
|
||||
addMember(memberId) {
|
||||
this.members.push(`${memberId}`);
|
||||
}
|
||||
getMembers() {
|
||||
return this.members;
|
||||
}
|
||||
getMemberCount() {
|
||||
return this.members.length;
|
||||
}
|
||||
removeMember(id) {
|
||||
this.members = this.members.filter(member => member !== id);
|
||||
}
|
||||
}
|
||||
module.exports = Event;
|
44
models/User.js
Normal file
44
models/User.js
Normal file
@ -0,0 +1,44 @@
|
||||
const { v4: uuid, parse } = require('uuid');
|
||||
|
||||
/**
|
||||
* Represents a User object.
|
||||
*
|
||||
* @class User
|
||||
*/
|
||||
class User {
|
||||
firstName;
|
||||
lastName;
|
||||
/**
|
||||
* Creates a new user object.
|
||||
*
|
||||
* @constructor
|
||||
* @param {string} username - The username of the user.
|
||||
* @param {string} displayName - The display name of the user.
|
||||
* @param {string} passwordHash - The password hash of the user.
|
||||
* @param {string} [id] - The optional unique identifier of the user. If not provided or not a valid UUID, a new UUID will be generated.
|
||||
* @return {void}
|
||||
*/
|
||||
constructor(username, displayName, passwordHash, gdpr, id) {
|
||||
if (!id || parse(id)) {
|
||||
this.id = uuid(undefined, undefined, undefined);
|
||||
} else {
|
||||
this.id = id;
|
||||
}
|
||||
console.log(this.id)
|
||||
this.username = username;
|
||||
this.displayName = displayName;
|
||||
this.gdpr = gdpr;
|
||||
this.passwordHash = passwordHash;
|
||||
this.isAdmin = false;
|
||||
this.isDisabled = false
|
||||
}
|
||||
|
||||
setFirstName(firstName) {
|
||||
this.firstName = firstName
|
||||
}
|
||||
|
||||
setLastName(lastName) {
|
||||
this.lastName = lastName
|
||||
}
|
||||
}
|
||||
module.exports = User;
|
26
package.json
Normal file
26
package.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "brief-04-back",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"dev": "nodemon --enable-source-maps",
|
||||
"schema": "arkit -f app.js"
|
||||
},
|
||||
"author": "Mathis HERRIOT",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@node-rs/argon2": "^1.8.0",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.19.2",
|
||||
"jose": "^5.2.3",
|
||||
"mongodb": "^6.5.0",
|
||||
"tslog": "^4.9.2",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^1.6.4",
|
||||
"arkit": "^1.6.4",
|
||||
"nodemon": "^3.1.0"
|
||||
}
|
||||
}
|
18
services/CredentialService.js
Normal file
18
services/CredentialService.js
Normal file
@ -0,0 +1,18 @@
|
||||
const Argon2id = require("@node-rs/argon2");
|
||||
|
||||
/**
|
||||
* Generates a hash from a given password using Argon2id algorithm.
|
||||
*
|
||||
* @param {string} password - The password to generate a hash for.
|
||||
* @return {Promise<string>} - The generated hash.
|
||||
*/
|
||||
async function getHashFromPassword(password) {
|
||||
return await Argon2id.hash(password,{
|
||||
secret: Buffer.from(`${process.env.HASH_SECRET}`),
|
||||
algorithm: 2
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getHashFromPassword
|
||||
}
|
193
services/EventService.js
Normal file
193
services/EventService.js
Normal file
@ -0,0 +1,193 @@
|
||||
const {getDatabase} = require("./MongodbService");
|
||||
let Db = null
|
||||
getDatabase("brief04").then((value)=>{Db = value})
|
||||
|
||||
const { Logger } = require('tslog');
|
||||
|
||||
const logger = new Logger({ name: "Auth Controller" });
|
||||
|
||||
//TODO Better return error integration ===
|
||||
/**
|
||||
* Retrieves an event from the database using the specified ID.
|
||||
* @param {string} id - The ID of the event to retrieve.
|
||||
* @return {Promise<Object>} - A Promise that resolves to an object representing the event.
|
||||
*/
|
||||
async function getEventFromIdService(id) {
|
||||
return await Db.collection("events").findOne({id: id});
|
||||
}
|
||||
|
||||
//TODO - To test
|
||||
/**
|
||||
* Retrieves all events from the database.
|
||||
*
|
||||
* @param {string} sourceId - The source ID to query the events from.
|
||||
* @return {object} - An object containing:
|
||||
* - updatedAt : The timestamp when the events were last updated.
|
||||
* - events : An array of sanitized event objects. Each event object contains the following properties:
|
||||
* - id : The event ID.
|
||||
* - title : The event title.
|
||||
* - subTitle : The event subtitle.
|
||||
* - base64Banner : The base64 encoded banner image of the event.
|
||||
* - desc : The event description.
|
||||
* - date : The event date.
|
||||
* - were : The event location.
|
||||
* - maxMembers : The maximum number of members allowed for the event.
|
||||
* - authorId : The ID of the event author.
|
||||
* - members : The number of members currently participating in the event.
|
||||
* - length : The number of events returned.
|
||||
*/
|
||||
async function getAllEventsService(sourceId) {
|
||||
logger.info(`EVENT :> Query all threads (${sourceId})`)
|
||||
let eventsArray = []
|
||||
eventsArray = await Db.collection("events").find().toArray();
|
||||
if (!eventsArray) {
|
||||
logger.error('No event found.')
|
||||
return null
|
||||
}
|
||||
const sanitizedEvents = eventsArray.map((event)=>{
|
||||
return {
|
||||
id: event.id,
|
||||
title: event.title,
|
||||
subTitle: event.subTitle,
|
||||
base64Banner: event.base64Banner,
|
||||
desc: event.desc,
|
||||
date: event.date,
|
||||
were: event.were,
|
||||
maxMembers: event.maxMembers,
|
||||
authorId: event.authorId,
|
||||
members: event.members
|
||||
}
|
||||
})
|
||||
console.log(` -> Returned ${sanitizedEvents.length} event(s)`)
|
||||
return {
|
||||
updatedAt: Date.now(),
|
||||
events: sanitizedEvents,
|
||||
length: sanitizedEvents.length
|
||||
}
|
||||
}
|
||||
|
||||
//TODO - To test
|
||||
/**
|
||||
* Retrieves the subscribed event(s) for a given user.
|
||||
*
|
||||
* @param {string} targetId - The id of the target user.
|
||||
*
|
||||
* @return {Promise<Object>}
|
||||
* - A promise that resolves to an object containing information about the
|
||||
* subscribed event(s).
|
||||
* - If there are subscribed event(s), the resolved object will include the
|
||||
* following properties:
|
||||
* - iat: The current timestamp in milliseconds.
|
||||
* - subscribedEvent: An array of the subscribed event(s) retrieved from the database.
|
||||
* - length: The number of subscribed event(s).
|
||||
* - If no subscribed event is found for the given targetId, the resolved object will
|
||||
* include the following error property:
|
||||
* - error: "noSubscribedEventFound"
|
||||
*/
|
||||
async function getUserSubscribedEventService(targetId) {
|
||||
const subscribedEvent = await Db.collection("events").find({
|
||||
members: {
|
||||
$eltMatch: {
|
||||
$eq: {targetId}
|
||||
}}}).toArray();
|
||||
if (!subscribedEvent) {
|
||||
logger.error(`No subscribed event found for USERID:${targetId}`)
|
||||
return {
|
||||
error: "noSubscribedEventFound"
|
||||
}
|
||||
}
|
||||
return {
|
||||
iat: Date.now(),
|
||||
subscribedEvent: subscribedEvent,
|
||||
length: subscribedEvent.length
|
||||
}
|
||||
}
|
||||
|
||||
//TODO - To test
|
||||
/**
|
||||
* Modify the subscription state of a user for an event.
|
||||
*
|
||||
* @param {string} userId - The ID of the user.
|
||||
* @param {string} eventId - The ID of the event.
|
||||
* @param {boolean} state - The desired subscription state (true for subscribed, false for unsubscribed).
|
||||
*
|
||||
* @return {object} - The result of the operation.
|
||||
* - If the user is not found, { error: "userNotFound" } is returned.
|
||||
* - If the event is not found, { error: "eventNotFound" } is returned.
|
||||
* - If the user is already subscribed to the event and the state is true, { error: "alreadySubscribed" } is returned.
|
||||
* - If the user is not subscribed to the event and the state is false, { error: "notSubscribed" } is returned.
|
||||
* - If the event update fails, { error: "updateFailed" } is returned.
|
||||
* - If the operation is successful, { error: "none" } is returned.
|
||||
*/
|
||||
async function alterUserSubscribedEventStateService(userId, eventId, state){
|
||||
const user = await Db.collection("users").findOne({id: userId});
|
||||
if (!user) {
|
||||
logger.error(`User not found (${userId})`);
|
||||
return { error: "userNotFound" };
|
||||
}
|
||||
const event = await Db.collection("events").findOne({id: eventId});
|
||||
if (!event) {
|
||||
logger.error(`Event not found (${eventId})`);
|
||||
return { error: "eventNotFound" };
|
||||
}
|
||||
const isUserSubscribed = event.members.includes(userId);
|
||||
if (state === true && isUserSubscribed) {
|
||||
logger.error(`User already subscribed to the event (${userId}, ${eventId})`);
|
||||
return { error: "alreadySubscribed" };
|
||||
}
|
||||
if (state === false && !isUserSubscribed) {
|
||||
logger.error(`User is not subscribed to the event (${userId}, ${eventId})`);
|
||||
return { error: "notSubscribed" };
|
||||
}
|
||||
if (state === true) {
|
||||
event.members.push(userId);
|
||||
} else if (state === false) {
|
||||
event.members = event.members.filter(memberId => memberId !== userId);
|
||||
}
|
||||
const updatedEventResult = await Db.collection("events").updateOne({id: eventId}, {$set: event});
|
||||
if (updatedEventResult.modifiedCount === 0) {
|
||||
logger.error(`Failed to update event (${eventId})`);
|
||||
return { error: "updateFailed" };
|
||||
}
|
||||
logger.info(`User ${state === true ? "subscribed" : "unsubscribed"} successfully to the event. (${userId}, ${eventId})`);
|
||||
return { error: "none" };
|
||||
}
|
||||
|
||||
//TODO - To test
|
||||
/**
|
||||
* Edits an event in the database.
|
||||
*
|
||||
* @param {string} eventId
|
||||
* - The ID of the event to be edited.
|
||||
* @param {object} sanitizedData
|
||||
* - The sanitized data used to update the event.
|
||||
*
|
||||
* @return {object}
|
||||
* - An object with an "error" property.
|
||||
* - If the event is not found, the "error" property will be "eventNotFound".
|
||||
* - If the event is edited successfully, the "error" property will be "none".
|
||||
*/
|
||||
async function editEventService(eventId, sanitizedData) {
|
||||
try {
|
||||
const updatedEventResult = await Db.collection("events").updateOne({id: eventId}, {$set: sanitizedData});
|
||||
if (updatedEventResult.modifiedCount === 0) {
|
||||
logger.info(`EDIT :> Event not found (${eventId})`)
|
||||
return { error: "eventNotFound" };
|
||||
}
|
||||
logger.info(`EDIT :> Event edited successfully (${eventId})`);
|
||||
return { error: "none" };
|
||||
} catch (e) {
|
||||
logger.error(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//TODO Delete event - Owner || Admin ===
|
||||
|
||||
module.exports = {
|
||||
getEventFromIdService,
|
||||
getAllEventsService,
|
||||
getUserSubscribedEventService,
|
||||
alterUserSubscribedEventStateService,
|
||||
editEventService
|
||||
}
|
47
services/JwtService.js
Normal file
47
services/JwtService.js
Normal file
@ -0,0 +1,47 @@
|
||||
const Jose = require("jose")
|
||||
const { Logger } = require('tslog')
|
||||
|
||||
const logger = new Logger({ name: "JwtService" });
|
||||
|
||||
/**
|
||||
* Validates a JWT and returns the result.
|
||||
*
|
||||
* @param {string} jwt - The JWT to be validated.
|
||||
* @return {Promise<boolean|object>} A promise that resolves to the validation result. It returns `false` if the JWT is invalid, otherwise it returns the decoded payload as an object.
|
||||
*/
|
||||
async function JwtVerifyService(jwt) {
|
||||
try {
|
||||
const result = await Jose.jwtVerify(
|
||||
jwt,
|
||||
new TextEncoder()
|
||||
.encode(`${process.env.JWT_SECRET}`),
|
||||
{
|
||||
})
|
||||
return result.payload;
|
||||
} catch (error) {
|
||||
logger.error(error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a JWT with the given payload, algorithm, expiration time, and audience.
|
||||
*
|
||||
* @param {object} payload - The payload of the JWT.
|
||||
* @param {string} alg - The algorithm to use for signing the JWT.
|
||||
* @param {string} expTime - The expiration time of the JWT in format "4h || 7d || 1w".
|
||||
* @param {string|string[]} audience - The audience(s) of the JWT.
|
||||
*
|
||||
* @returns {Promise<string>} - A promise that resolves to the signed JWT.
|
||||
*/
|
||||
async function JwtSignService(payload, alg, expTime, audience) {
|
||||
return await new Jose.SignJWT(payload)
|
||||
.setProtectedHeader({alg})
|
||||
.setIssuedAt(new Date())
|
||||
.setIssuer('Brief 03 - Mathis HERRIOT')
|
||||
.setAudience(audience)
|
||||
.setExpirationTime(expTime)
|
||||
.sign(new TextEncoder().encode(`${process.env.JWT_SECRET}`))
|
||||
}
|
||||
|
||||
module.exports = {JwtVerify: JwtVerifyService, JwtSign: JwtSignService}
|
28
services/MongodbService.js
Normal file
28
services/MongodbService.js
Normal file
@ -0,0 +1,28 @@
|
||||
const {MongoClient} = require('mongodb')
|
||||
const { Logger } = require('tslog')
|
||||
|
||||
/**
|
||||
* Establishes a connection to a MongoDB server using the MongoClient.
|
||||
*
|
||||
* @returns {MongoClient} A Promise that resolves with a MongoDB client object connected to the server.
|
||||
* The client object can be used to perform database operations.
|
||||
* @throws {Error} If an error occurs while attempting to connect to the MongoDB server.
|
||||
*/
|
||||
async function connect() {
|
||||
try {
|
||||
return await MongoClient.connect("mongodb://127.0.0.1:27017/")
|
||||
} catch (err) {
|
||||
throw new Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
async function getDatabase(name) {
|
||||
const {connect} = require("./MongodbService")
|
||||
const client = await connect()
|
||||
//console.log(client)
|
||||
const db = client.db(name);
|
||||
return db;
|
||||
|
||||
}
|
||||
|
||||
module.exports = {connect, getDatabase}
|
197
services/ThreadService.js
Normal file
197
services/ThreadService.js
Normal file
@ -0,0 +1,197 @@
|
||||
const {getDatabase} = require("./MongodbService");
|
||||
const Thread = require("../models/Event");
|
||||
let Db = null
|
||||
getDatabase("brief04").then((value)=>{Db = value})
|
||||
|
||||
async function getThreadFromId(id) {
|
||||
return await Db.collection("threads").findOne({id: id});
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously creates a thread service.
|
||||
*
|
||||
* @param {Object} sanitizedData - The sanitized data object containing necessary information for creating the thread service.
|
||||
* @param {string} sanitizedData.userId
|
||||
* @param {string} sanitizedData.title
|
||||
* @param {string} sanitizedData.subTitle
|
||||
* @param {string} sanitizedData.base64Banner
|
||||
* @param {string} sanitizedData.desc
|
||||
* @param {number} sanitizedData.price
|
||||
*
|
||||
* @return {Promise} A Promise that resolves to the created thread service.
|
||||
*/
|
||||
async function CreateThreadService(sanitizedData) {
|
||||
console.log(`SERV :> Create thread (${sanitizedData.title})`)
|
||||
const NewThread = new Thread(sanitizedData.title, sanitizedData.subTitle, sanitizedData.base64Banner, sanitizedData.desc, sanitizedData.price, sanitizedData.userId)
|
||||
const dbResult = await Db.collection("threads").insertOne(NewThread);
|
||||
if (!dbResult.acknowledged) {
|
||||
console.log(" -> FAIL")
|
||||
return null
|
||||
}
|
||||
console.log(` -> ${NewThread.id} = Success`)
|
||||
return NewThread.id;
|
||||
}
|
||||
|
||||
// TODO OK
|
||||
/**
|
||||
* Retrieve a thread by its ID.
|
||||
*
|
||||
* @param {string} threadId - The ID of the thread to retrieve.
|
||||
* @return {Promise<Object>} The thread object if found, otherwise an error object.
|
||||
* The error object has the following properties:
|
||||
* - error: "ThreadNotFound"
|
||||
* - message: "Thread not found"
|
||||
*/
|
||||
async function GetThreadByIdService(threadId) {
|
||||
console.log(`SERV :> Get thread (${threadId})`)
|
||||
const targetThread = await getThreadFromId(threadId);
|
||||
if (!targetThread) {
|
||||
console.log(` -> Thread not found (${threadId})`)
|
||||
return {
|
||||
error: "ThreadNotFound",
|
||||
message: "Thread not found"
|
||||
}
|
||||
}
|
||||
console.log(` -> Thread found (${threadId})`)
|
||||
return {
|
||||
id: targetThread.id,
|
||||
title: targetThread.title,
|
||||
subTitle: targetThread.subTitle,
|
||||
base64Banner: targetThread.base64Banner,
|
||||
desc: targetThread.desc,
|
||||
price: targetThread.price,
|
||||
userId: targetThread.userId
|
||||
};
|
||||
}
|
||||
|
||||
// TODO OK
|
||||
/**
|
||||
* Retrieves all threads for a given user.
|
||||
*
|
||||
* @param {object} sanitizedData - The sanitized data object containing user ID.
|
||||
*
|
||||
* @return {object} - An object containing the updated timestamp, an array of sanitized threads, and the length of the array.
|
||||
* If the user is not found, an error object with the corresponding error code and message is returned.
|
||||
*/
|
||||
async function GetAllThreadService(sanitizedData) {
|
||||
console.log(`SERV :> Query all threads (${sanitizedData.userId})`)
|
||||
const sourceUser = Db.collection('users').findOne({id: sanitizedData.userId})
|
||||
if (!sourceUser) {
|
||||
console.log(` -> User not found (${sanitizedData.userId})`)
|
||||
return {
|
||||
error: "UserNotFound",
|
||||
message: "User not found"
|
||||
}
|
||||
}
|
||||
let threadsArray = []
|
||||
threadsArray = await Db.collection("threads").find().toArray();
|
||||
const sanitizedThreads = threadsArray.map((thread)=>{
|
||||
return {
|
||||
id: thread.id,
|
||||
title: thread.title,
|
||||
subTitle: thread.subTitle,
|
||||
base64Banner: thread.base64Banner,
|
||||
desc: thread.desc,
|
||||
price: thread.price,
|
||||
userId: thread.userId
|
||||
}
|
||||
})
|
||||
console.log(` -> Returned ${sanitizedThreads.length} thread(s)`)
|
||||
return {
|
||||
updatedAt: Date.now(),
|
||||
threads: sanitizedThreads,
|
||||
length: sanitizedThreads.length
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the user thread(s) for the given userId.
|
||||
*
|
||||
* @param {string} userId - The ID of the user.
|
||||
* @return {Promise<Object>} - A promise that resolves to an object containing the user threads, or an error object if no threads are found.
|
||||
*/
|
||||
async function GetUserThreadService(userId) {
|
||||
console.log(`SERV :> Get user thread(s) (${userId})`)
|
||||
const userThreads = await Db.collection("threads").find({ userId: userId }).toArray();
|
||||
if (!userThreads) {
|
||||
console.log(` -> Thread not found (${userId})`)
|
||||
return {
|
||||
error: "ThreadNotFound",
|
||||
message: "Thread not found"
|
||||
}
|
||||
}
|
||||
console.log(` -> ${userThreads.length} thread(s) found.`)
|
||||
const cleanUserThreads = userThreads.map((thread) => {
|
||||
return {
|
||||
id: thread.id,
|
||||
title: thread.title,
|
||||
subTitle: thread.subTitle,
|
||||
base64Banner: thread.base64Banner,
|
||||
desc: thread.desc,
|
||||
price: thread.price,
|
||||
userId: thread.userId
|
||||
};
|
||||
});
|
||||
console.log(cleanUserThreads)
|
||||
return {
|
||||
iat: Date.now(),
|
||||
threads : cleanUserThreads,
|
||||
length: cleanUserThreads.length
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a thread in the database with the given threadId and sanitized data.
|
||||
*
|
||||
* @param {string} threadId - The unique identifier of the thread to update.
|
||||
* @param {object} sanitizedData - The sanitized data to update the thread with.
|
||||
* @return {object} - An object indicating the result of the update operation.
|
||||
* If the thread is not found, an error object with the error type and message is returned.
|
||||
* If the thread is found and successfully updated, an object with an "error" property set to "none" is returned.
|
||||
*/
|
||||
async function UpdateThreadService(threadId, sanitizedData) {
|
||||
const updatedThread = await Db.collection("threads").findOneAndUpdate({ id: threadId }, { $set: sanitizedData }, { returnOriginal: false });
|
||||
console.log(updatedThread)
|
||||
if (!updatedThread) {
|
||||
console.log(` -> Thread not found (${threadId})`);
|
||||
return {
|
||||
error: "ThreadNotFound",
|
||||
message: "Thread not found"
|
||||
};
|
||||
}
|
||||
console.log(` -> Thread updated (${threadId})`);
|
||||
return {
|
||||
error: "none"
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a thread with the given thread ID from the database.
|
||||
*
|
||||
* @param {string} threadId - The ID of the thread to delete.
|
||||
* @return {Promise<{ error: string } | { error: string, message: string }>} - A promise that resolves to an object with either an "error" property set to "none" if the thread was deleted successfully, or an "error" property set to "ThreadNotFound" with a "message" property set to "Thread not found" if the thread was not found.
|
||||
*/
|
||||
async function DeleteThreadService(threadId) {
|
||||
console.log(`SERV :> Delete thread (${threadId})`);
|
||||
const deletedThread = await Db.collection("threads").findOneAndDelete({ id: threadId });
|
||||
if (!deletedThread) {
|
||||
console.log(` -> Thread not found (${threadId})`);
|
||||
return {
|
||||
error: "ThreadNotFound",
|
||||
message: "Thread not found"
|
||||
};
|
||||
}
|
||||
console.log(` -> Thread deleted (${threadId})`);
|
||||
return {
|
||||
error: "none"
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
CreateThreadService,
|
||||
GetThreadByIdService,
|
||||
GetAllThreadService,
|
||||
GetUserThreadService,
|
||||
UpdateThreadService,
|
||||
DeleteThreadService
|
||||
}
|
229
services/UserService.js
Normal file
229
services/UserService.js
Normal file
@ -0,0 +1,229 @@
|
||||
const Argon2id = require("@node-rs/argon2")
|
||||
const {getDatabase} = require("./MongodbService");
|
||||
const {getHashFromPassword} = require("./CredentialService")
|
||||
const {JwtSign} = require("./JwtService");
|
||||
const User = require("../models/User");
|
||||
const { Logger } = require('tslog')
|
||||
|
||||
const logger = new Logger({ name: "UserService" });
|
||||
|
||||
let Db = null
|
||||
getDatabase("brief04").then((value)=>{Db = value})
|
||||
|
||||
/**
|
||||
* Retrieves a user from the database based on the provided username.
|
||||
*
|
||||
* @param {string} username - The username of the user to retrieve.
|
||||
*
|
||||
* @return {Promise<object>} - A promise that resolves with the user object if found,
|
||||
* or null if not found.
|
||||
*/
|
||||
async function getUserFromUsername(username) {
|
||||
const dbUser = await Db.collection("users").findOne({username: `${username}`})
|
||||
if (dbUser === undefined) return null;
|
||||
return dbUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a user from the database based on the provided id.
|
||||
*
|
||||
* @param {string} id - The id of the user.
|
||||
* @returns {Promise<object|null>} - A Promise that resolves with the user object if found,
|
||||
* or null if no user is found.
|
||||
*/
|
||||
async function getUserFromIdService(id) {
|
||||
return await Db.collection("users").findOne({id: id});
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new user by creating a UserService object, generating a JWT token, and inserting the user into the database.
|
||||
*
|
||||
* @param {Object} sanitizedData - The sanitized user data.
|
||||
* @param {string} sanitizedData.username - The username of the new user.
|
||||
* @param {string} sanitizedData.displayName - The display namcoe of the new user.
|
||||
* @param {string} sanitizedData.firstName
|
||||
* @param {string} sanitizedData.lastName
|
||||
* @param {string} sanitizedData.password - The password of the new user.
|
||||
* @param {boolean} sanitizedData.gdpr - Indicates whether the new user has accepted GDPR.
|
||||
*
|
||||
* @returns {Object} - An object containing the registered user's data and JWT token.
|
||||
* @returns {string} error - The error name, if any. "none" if registration was successful.
|
||||
* @returns {string|null} jwt - The JWT token for the registered user. Null if registration was not successful.
|
||||
* @returns {Object|null} user - The registered user's data. Null if registration was not successful.
|
||||
* @returns {string|null} user.id - The ID of the registered user. Null if registration was not successful.
|
||||
* @returns {string|null} user.username - The username of the registered user. Null if registration was not successful.
|
||||
* @returns {string|null} user.displayName - The display name of the registered user. Null if registration was not successful.
|
||||
*/
|
||||
async function RegisterService(sanitizedData) {
|
||||
if (sanitizedData.password.length < 6) {
|
||||
logger.info(`REGISTER :> Invalid password (${sanitizedData.username})`)
|
||||
return { error: "invalidPassword" };
|
||||
}
|
||||
const passwordHash = await getHashFromPassword(sanitizedData.password)
|
||||
|
||||
// Does the new user has accepted GDPR ?
|
||||
if (sanitizedData.gdpr !== true) {
|
||||
logger.info(`REGISTER :> GDPR not validated (${sanitizedData.username})`)
|
||||
return { error: "gdprNotApproved" }
|
||||
}
|
||||
|
||||
// Check if exist and return
|
||||
|
||||
const dbUserIfExist = await getUserFromUsername(sanitizedData.username)
|
||||
if (dbUserIfExist) {
|
||||
logger.info(`REGISTER :> User exist (${dbUserIfExist.username})\n ID:${dbUserIfExist.id}`)
|
||||
return { error: "exist" }
|
||||
}
|
||||
|
||||
const currentDate = new Date();
|
||||
|
||||
// New UserService (class)
|
||||
|
||||
const NewUser = new User(sanitizedData.username, sanitizedData.displayName, passwordHash, currentDate);
|
||||
NewUser.setFirstName(sanitizedData.firstName);
|
||||
NewUser.setLastName(sanitizedData.lastName);
|
||||
|
||||
// JWT
|
||||
|
||||
const alg = 'HS512'
|
||||
const token = await JwtSign({
|
||||
sub: NewUser.id
|
||||
}, alg,
|
||||
'1d',
|
||||
'user')
|
||||
|
||||
const userData = {
|
||||
error: "none",
|
||||
jwt: token,
|
||||
user: {
|
||||
id: NewUser.id,
|
||||
username: NewUser.username,
|
||||
displayName: NewUser.displayName,
|
||||
firstName: NewUser.firstName,
|
||||
lastName: NewUser.lastName
|
||||
}};
|
||||
logger.info(userData)
|
||||
await Db.collection("users").insertOne(NewUser);
|
||||
logger.info(`REGISTER :> Inserted new user (${NewUser.username})`)
|
||||
return userData
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the login process by verifying the provided credentials.
|
||||
* @param {Object} sanitizedData - The sanitized user login data.
|
||||
* @param {string} sanitizedData.username - The username provided by the user.
|
||||
* @param {string} sanitizedData.password - The password provided by the user.
|
||||
* @returns {Object} - The login result object.
|
||||
* @returns {string} result.error - The error code if there is an error during the login process.
|
||||
* @returns {string} result.jwt - The JSON Web Token (JWT) generated upon successful login.
|
||||
* @returns {Object} result.user - The user information.
|
||||
* @returns {number} result.user.id - The ID of the user.
|
||||
* @returns {string} result.user.username - The username of the user.
|
||||
* @returns {string} result.user.displayName - The display name of the user.
|
||||
*/
|
||||
async function LoginService(sanitizedData) {
|
||||
//const passwordHash = await getHashFromPassword(sanitizedData.password);
|
||||
const dbUser = await getUserFromUsername(sanitizedData.username);
|
||||
if (!dbUser) {
|
||||
console.log(`LoginService :> User does not exist (${sanitizedData.username})`);
|
||||
return { error: "userNotFound" };
|
||||
}
|
||||
if (sanitizedData.password.length < 6) {
|
||||
console.log('X')
|
||||
console.log(`LoginService :> Invalid password (${sanitizedData.username})`);
|
||||
return { error: "invalidPassword" };
|
||||
}
|
||||
const isPasswordValid = await Argon2id.verify(
|
||||
Buffer.from(dbUser.passwordHash),
|
||||
Buffer.from(sanitizedData.password),
|
||||
{
|
||||
secret: Buffer.from(`${process.env.HASH_SECRET}`),
|
||||
algorithm: 2
|
||||
});
|
||||
if (!isPasswordValid) {
|
||||
console.log(isPasswordValid)
|
||||
console.log(`LoginService :> Invalid password (${sanitizedData.username})`);
|
||||
return { error: "invalidPassword" };
|
||||
}
|
||||
// biome-ignore lint/style/useConst: <explanation>
|
||||
let userData = {
|
||||
error: "none",
|
||||
jwt: null,
|
||||
user: {
|
||||
id: dbUser.id,
|
||||
username: dbUser.username,
|
||||
displayName: dbUser.displayName,
|
||||
}
|
||||
};
|
||||
|
||||
const alg = 'HS512';
|
||||
userData.jwt = await JwtSign({sub: dbUser.id}, alg, '1d', 'user')
|
||||
|
||||
|
||||
console.log("USERDATA :>");
|
||||
console.log(userData);
|
||||
return userData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all users from the database.
|
||||
*
|
||||
* @async
|
||||
* @function getAllUsersService
|
||||
* @returns {Promise<{iat: number, users: Array<user>, length: number}>} - The response object containing the users array and its length.
|
||||
*/
|
||||
async function getAllUsersService() {
|
||||
const users = await Db.collection("users").find().toArray();
|
||||
logger.info(`Query ${users.length} user(s)`)
|
||||
return {
|
||||
iat: Date.now(),
|
||||
users: users,
|
||||
length: users.length
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits a user in the database.
|
||||
*
|
||||
* @param {string} targetId - The ID of the user to be edited.
|
||||
* @param {object} sanitizedData - The sanitized data to update the user with.
|
||||
* @returns {object} - An object indicating the result of the operation.
|
||||
* If the user is not found, the error property will be a string "userNotFound".
|
||||
* Otherwise, the error property will be a string "none".
|
||||
*/
|
||||
async function editUserService(targetId, sanitizedData) {
|
||||
const updatedUserResult = await Db.collection("users").updateOne({id: targetId}, {$set: sanitizedData});
|
||||
if (updatedUserResult.modifiedCount === 0) {
|
||||
logger.info(`EDIT :> User not found (${targetId})`);
|
||||
return { error: "userNotFound" };
|
||||
}
|
||||
|
||||
logger.info(`EDIT :> User updated (${targetId})`);
|
||||
return { error: "none" };
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a user from the database.
|
||||
*
|
||||
* @param {string} targetId - The ID of the user to be deleted.
|
||||
* @return {Promise<boolean>} - A promise that resolves to true if the user is successfully deleted, or false if an error occurs.
|
||||
*/
|
||||
async function deleteUserService(targetId) {
|
||||
logger.info(`Deleting user ${targetId}`)
|
||||
try {
|
||||
await Db.collection("users").deleteOne({id: targetId});
|
||||
return true
|
||||
} catch (e) {
|
||||
logger.warn(e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
RegisterService,
|
||||
LoginService,
|
||||
getAllUsersService,
|
||||
getUserFromIdService,
|
||||
editUserService,
|
||||
deleteUserService
|
||||
}
|
4
types/requests/IReqLoginData.ts
Normal file
4
types/requests/IReqLoginData.ts
Normal file
@ -0,0 +1,4 @@
|
||||
interface IReqLoginData {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
8
types/requests/IReqRegisterData.ts
Normal file
8
types/requests/IReqRegisterData.ts
Normal file
@ -0,0 +1,8 @@
|
||||
interface IReqRegisterData {
|
||||
username: string;
|
||||
displayName: string;
|
||||
password: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
gdpr: boolean;
|
||||
}
|
11
types/responses/IResLoginData.ts
Normal file
11
types/responses/IResLoginData.ts
Normal file
@ -0,0 +1,11 @@
|
||||
interface IResLoginData {
|
||||
error: string;
|
||||
jwt: string;
|
||||
user: User;
|
||||
}
|
||||
|
||||
interface User {
|
||||
id: string;
|
||||
username: string;
|
||||
displayName: string;
|
||||
}
|
13
types/responses/IResRegisterData.ts
Normal file
13
types/responses/IResRegisterData.ts
Normal file
@ -0,0 +1,13 @@
|
||||
interface IResRegisterData {
|
||||
error: string;
|
||||
jwt: string;
|
||||
user: User;
|
||||
}
|
||||
|
||||
interface User {
|
||||
id: string;
|
||||
username: string;
|
||||
displayName: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
}
|
14
utils/logging.js
Normal file
14
utils/logging.js
Normal file
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Logs a message with a specific category and state.
|
||||
*
|
||||
* @param {string} category - MIDDLEWARE | CONTROLLER | SERVICE
|
||||
* @param {string} state - REQ | RES | ERR | WARN | INFO
|
||||
* @param {string} message - The message to be logged.
|
||||
* @return {undefined} - No return value.
|
||||
*/
|
||||
function newLog(category, state, message) {
|
||||
console.log(`[${category}] ${state} :> \n ${message}`);
|
||||
}
|
||||
module.exports = {
|
||||
log: newLog
|
||||
}
|
Reference in New Issue
Block a user