diff --git a/.env.exemple b/.env.exemple
new file mode 100644
index 0000000..2ed0415
--- /dev/null
+++ b/.env.exemple
@@ -0,0 +1,2 @@
+HASH_SECRET=''
+JWT_SECRET=''
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index ceaea36..efeef5a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
\ No newline at end of file
diff --git a/app.js b/app.js
new file mode 100644
index 0000000..131fd90
--- /dev/null
+++ b/app.js
@@ -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', ()=>{
+
+})
diff --git a/arkit.svg b/arkit.svg
new file mode 100644
index 0000000..fe4e3d9
--- /dev/null
+++ b/arkit.svg
@@ -0,0 +1,163 @@
+
\ No newline at end of file
diff --git a/biome.json b/biome.json
new file mode 100644
index 0000000..4282cf8
--- /dev/null
+++ b/biome.json
@@ -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
+ }
+ }
+ }
+}
diff --git a/controllers/AuthController.js b/controllers/AuthController.js
new file mode 100644
index 0000000..1cba7a2
--- /dev/null
+++ b/controllers/AuthController.js
@@ -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} - 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} - 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:
+ 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
+}
\ No newline at end of file
diff --git a/controllers/EventController.js b/controllers/EventController.js
new file mode 100644
index 0000000..703d006
--- /dev/null
+++ b/controllers/EventController.js
@@ -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
+}
\ No newline at end of file
diff --git a/controllers/ImageController.js b/controllers/ImageController.js
new file mode 100644
index 0000000..b4c1365
--- /dev/null
+++ b/controllers/ImageController.js
@@ -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
+}
\ No newline at end of file
diff --git a/controllers/ThreadController.js b/controllers/ThreadController.js
new file mode 100644
index 0000000..ffd6ea2
--- /dev/null
+++ b/controllers/ThreadController.js
@@ -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