diff --git a/api/openapi.yaml b/api/openapi.yaml index 950fe1f61275948686ca48aa1bb6fa5471813966..c66554ca9e238aa152d7aa465a86ac92042045ed 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -30,8 +30,8 @@ paths: post: tags: - graphql - summary: Execute GraphQL query - description: Execute GraphQL query + summary: Execué une requête GraphQL + description: Exécute une requête GraphQL operationId: executeGraphQLQuery requestBody: required: true @@ -47,8 +47,8 @@ paths: security: - bearerAuth: [ ] responses: - '200': - description: Successful operation + "200": + description: Succès. La requête a été exécutée avec succès. content: application/json: schema: @@ -60,6 +60,12 @@ paths: type: array items: type: object + "400": + description: Requete invalide + "401": + description: Authentification requise + "500": + description: Erreur serveur interne. Veuillez réessayer plus tard. /ad: post: tags: @@ -82,8 +88,14 @@ paths: application/json: schema: $ref: '#/components/schemas/Ad' - "405": - description: Invalid input + "400": + description: Requête invalide. Veuillez vérifier vos paramètres de requête. + "401": + description: Authentification requise. + "403": + description: Accès interdit. L'utilisateur n'est pas autorisé à effectuer cette action. + "500": + description: Erreur serveur interne. Veuillez réessayer plus tard. x-swagger-router-controller: Ad get: tags: @@ -100,6 +112,14 @@ paths: application/json: schema: $ref: '#/components/schemas/AdsList' + "400": + description: Requête invalide. Veuillez vérifier vos paramètres de requête. + "401": + description: Authentification requise. + "403": + description: Accès interdit. L'utilisateur n'est pas autorisé à accéder à cette ressource. + "404": + description: Non trouvé. Aucune annonce n'a été trouvée. "500": description: Erreur serveur interne. Veuillez réessayer plus tard. x-swagger-router-controller: Ad @@ -134,6 +154,12 @@ paths: responses: '200': description: Succès. Les photos ont été ajoutées avec succès. + "400": + description: Requête invalide. Veuillez vérifier vos paramètres de requête. + "401": + description: Authentification requise. + "403": + description: Accès interdit. L'utilisateur n'est pas autorisé à effectuer cette action. '404': description: Annonce non trouvée. '500': @@ -165,10 +191,16 @@ paths: responses: '200': description: Succès. Renvoie les détails de l'annonce mise à jour. - '403': - description: Accès interdit. L'utilisateur n'est pas autorisé à effectuer cette action. - '404': - description: Non trouvé. L'annonce avec cet ID n'a pas été trouvée. + "400": + description: Requête invalide. Veuillez vérifier vos paramètres de requête. + "401": + description: Authentification requise. + "403": + description: Accès interdit. L'utilisateur n'est pas autorisé à accéder à cette ressource. + "404": + description: Non trouvé. Aucune annonce n'a été trouvée. + "500": + description: Erreur serveur interne. Veuillez réessayer plus tard. x-swagger-router-controller: Ad delete: tags: @@ -188,14 +220,15 @@ paths: responses: '200': description: Succès. L'annonce a été supprimée avec succès. - '404': - description: Annonce non trouvée. - '403': - description: Utilisateur non autorisé. - '401': - description: - Authentification requise. - '500': + "400": + description: Requête invalide. Veuillez vérifier vos paramètres de requête. + "401": + description: Authentification requise. + "403": + description: Accès interdit. L'utilisateur n'est pas autorisé à accéder à cette ressource. + "404": + description: Non trouvé. Aucune annonce n'a été trouvée. + "500": description: Erreur serveur interne. Veuillez réessayer plus tard. x-swagger-router-controller: Ad get: @@ -220,8 +253,12 @@ paths: application/json: schema: $ref: '#/components/schemas/Ad' + "400": + description: Requête invalide. Veuillez vérifier vos paramètres de requête. + "401": + description: Authentification requise. "403": - description: Accès interdit. L'utilisateur n'est pas autorisé à accéder à cette annonce. + description: Accès interdit. L'utilisateur n'est pas autorisé à accéder à cette ressource. "404": description: Non trouvé. L'annonce avec l'ID spécifié n'a pas été trouvée ou n'est pas disponible. "500": @@ -253,7 +290,15 @@ paths: responses: '200': description: Succès. La question a été posée avec succès. - '500': + "400": + description: Requête invalide. Veuillez vérifier vos paramètres de requête. + "401": + description: Authentification requise. + "403": + description: Accès interdit. L'utilisateur n'est pas autorisé à accéder à cette ressource. + "404": + description: Non trouvé. Aucune annonce n'a été trouvée. + "500": description: Erreur serveur interne. Veuillez réessayer plus tard. x-swagger-router-controller: Ad @@ -293,40 +338,54 @@ paths: responses: '200': description: Succès. La réponse a été enregistrée avec succès. - '500': + "400": + description: Requête invalide. Veuillez vérifier vos paramètres de requête. + "401": + description: Authentification requise. + "403": + description: Accès interdit. L'utilisateur n'est pas autorisé à accéder à cette ressource. + "404": + description: Non trouvé. Aucune annonce n'a été trouvée. + "500": description: Erreur serveur interne. Veuillez réessayer plus tard. x-swagger-router-controller: Ad /user: post: tags: - user - summary: Create user - description: This can only be done by the logged in user. + summary: Créer un nouvel utilisateur + description: Crée un nouvel utilisateur operationId: createUser requestBody: - description: Created user object + description: Utilisateur à créer content: application/json: schema: $ref: '#/components/schemas/User' responses: - default: - description: successful operation + "200": + description: Opération réussie content: application/json: schema: $ref: '#/components/schemas/User' + "400": + description: Erreur de saisie. Veuillez vérifier vos informations d'identification. + "409": + description: Conflit. L'utilisateur existe déjà. + "500": + description: Erreur serveur interne. Veuillez réessayer plus tard. x-swagger-router-controller: User /user/login: post: tags: - user - summary: Logs user into the system + summary: Authentifier un utilisateur description: "" operationId: loginUser requestBody: - description: User credentials for login + description: Les informations d'identification de l'utilisateur content: application/json: schema: @@ -341,17 +400,17 @@ paths: - password responses: "200": - description: successful operation + description: Succès. L'utilisateur est authentifié avec succès. headers: X-Rate-Limit: - description: calls per hour allowed by the user + description: nombre de requêtes autorisées par minute style: simple explode: false schema: type: integer format: int32 X-Expires-After: - description: date in UTC when token expires + description: date et heure d'expiration du jeton (format UTC) style: simple explode: false schema: @@ -365,24 +424,34 @@ paths: token: type: string "400": - description: Invalid username/password supplied + description: Erreur de saisie. Veuillez vérifier vos informations d'identification. + "404": + description: Non trouvé. L'utilisateur avec le nom d'utilisateur spécifié n'a pas été trouvé. + "500": + description: Erreur serveur interne. Veuillez réessayer plus tard. x-swagger-router-controller: User - /user/logout: post: tags: - user - summary: Logs out current logged in user session + summary: Déconnecter un utilisateur description: "" operationId: logoutUser security: - bearerAuth: [ ] responses: - default: - description: successful operation + "200": + description: Opération réussie + "401": + description: Authentification requise. + "403": + description: Accès interdit. L'utilisateur n'est pas autorisé à accéder à cette ressource. + "404": + description: Non trouvé. L'utilisateur avec le nom d'utilisateur spécifié n'a pas été trouvé. + "500": + description: Erreur serveur interne. Veuillez réessayer plus tard. x-swagger-router-controller: User components: - schemas: Ad: type: object @@ -451,7 +520,7 @@ components: properties: username: type: string - example: theUser + example: "theUser" password: type: string example: "12345" @@ -499,7 +568,7 @@ components: name: '##default' requestBodies: UserArray: - description: List of user object + description: Liste des utilisateurs content: application/json: schema: @@ -510,24 +579,3 @@ components: bearerAuth: type: http scheme: bearer - oAuthSample: - type: oauth2 - description: This API uses OAuth 2 with the implicit grant flow. - flows: - implicit: - authorizationUrl: https://api.example.com/oauth2/authorize - scopes: - read_pets: read your pets - write_pets: modify pets in your account - real-estate-ads_auth: - type: oauth2 - flows: - implicit: - authorizationUrl: https://real-estate-ads3.swagger.io/oauth/authorize - scopes: - write:pets: modify pets in your account - read:pets: read your pets - api_key: - type: apiKey - name: api_key - in: header diff --git a/controllers/Ad.js b/controllers/Ad.js index 9e7f8df96b26b4c1cc0ad5514671432342ba0e8e..2e8dc97e2cd76e15eff6915e7b7f1cf31cfe712b 100644 --- a/controllers/Ad.js +++ b/controllers/Ad.js @@ -1,27 +1,21 @@ const express = require('express'); -const multer = require('multer'); const router = express.Router(); const AdService = require('../service/AdService'); const utils = require('../utils/writer.js'); const upload = require('../multer-config'); +const {extractToken} = require("../utils/writer"); -const extractToken = (req, res, next) => { - const authorizationHeader = req.headers.authorization; - - if (authorizationHeader && authorizationHeader.startsWith('Bearer ')) { - req.token = authorizationHeader.slice(7); - next(); - } else { - res.status(401).json({message: 'Authentication is required'}); - } -}; router.post('/', extractToken, upload.array('photos'), async (req, res) => { try { const response = await AdService.addAdWithPhotos(req.body, req.files, req.token); - utils.writeJson(res, response); + if (response.status === undefined) { + res.json(response); + } else { + res.status(response.status).json(response); + } } catch (error) { res.status(500).json({message: error.message}); } @@ -30,7 +24,11 @@ router.post('/', extractToken, upload.array('photos'), async (req, res) => { router.get('/', extractToken, async (req, res) => { try { const response = await AdService.getAds(req.token); - utils.writeJson(res, response); + if (response.status === undefined) { + res.json(response); + } else { + res.status(response.status).json(response); + } } catch (error) { res.status(500).json({message: error.message}); } @@ -38,7 +36,11 @@ router.get('/', extractToken, async (req, res) => { router.post('/:id/photos', extractToken, upload.array('photos'), async (req, res) => { try { const response = await AdService.addPhotosToAd(req.params.id, req.files, req.token); - utils.writeJson(res, response); + if (response.status === undefined) { + res.json(response); + } else { + res.status(response.status).json(response); + } } catch (error) { res.status(500).json({message: error.message}); } @@ -47,7 +49,11 @@ router.post('/:id/photos', extractToken, upload.array('photos'), async (req, res router.put('/:id', extractToken, upload.array('photos'), async (req, res) => { try { const response = await AdService.updateAd(req.params.id, req.body, req.files, req.token); - utils.writeJson(res, response); + if (response.status === undefined) { + res.json(response); + } else { + res.status(response.status).json(response); + } } catch (error) { res.status(500).json({message: error.message}); } @@ -56,7 +62,11 @@ router.put('/:id', extractToken, upload.array('photos'), async (req, res) => { router.delete('/:id', extractToken, async (req, res) => { try { const response = await AdService.deleteAd(req.params.id, req.token); - utils.writeJson(res, response); + if (response.status === undefined) { + res.json(response); + } else { + res.status(response.status).json(response); + } } catch (error) { res.status(500).json({message: error.message}); } @@ -76,7 +86,11 @@ router.put('/:id/ask', extractToken, async (req, res) => { const {question} = req.body; try { const response = await AdService.askQuestion(adtId, question, req.token); - utils.writeJson(res, response); + if (response.status === undefined) { + res.json(response); + } else { + res.status(response.status).json(response); + } } catch (error) { res.status(500).json({message: error.message}); } @@ -88,7 +102,11 @@ router.put('/:id/question/:questionId/answer', extractToken, async (req, res) => const {answer} = req.body; try { const response = await AdService.answerQuestion(adtId, questionId, answer, req.token); - utils.writeJson(res, response); + if (response.status === undefined) { + res.json(response); + } else { + res.status(response.status).json(response); + } } catch (error) { res.status(500).json({message: error.message}); } diff --git a/controllers/User.js b/controllers/User.js index 07c68847a567dd50547960f95501b08da689488a..37b60759ac8666b84d84733d876527b94b6846ef 100644 --- a/controllers/User.js +++ b/controllers/User.js @@ -1,69 +1,51 @@ const express = require('express'); -const passport = require('../passeport-config'); -const jwt = require('jsonwebtoken'); -const utils = require('../utils/writer.js'); - -const {typeDefs, resolvers, schema} = require('../schemas/User&AdSchema'); const User = require('../service/UserService'); const {default: expressPlayground} = require("graphql-playground-middleware-express"); -const {createHandler} = require("graphql-http/lib/use/express"); -const {extractUserFromToken} = require("../service/UserService"); - -const extractToken = (req, res, next) => { - const authorizationHeader = req.headers.authorization; - - if (authorizationHeader && authorizationHeader.startsWith('Bearer ')) { - req.token = authorizationHeader.slice(7); - next(); - } else { - res.status(401).json({ message: 'Authentication is required.' }); - } -}; -const secretKey = process.env.SECRET_KEY; +const {extractToken} = require("../utils/writer"); +const {response} = require("express"); const authRouter = express.Router(); authRouter.post('/', async (req, res) => { try { - const {username, password, isAgent} = req.body; - user = await User.createUser(username, password, isAgent); - console.log("user", user) - utils.writeJson(res, {message: 'Utilisateur créé avec succès', user}); + const response = await User.createUser(req.body); + if (response.status === undefined) { + res.json(response); + } else { + res.status(response.status).json(response); + } } catch (error) { - utils.writeJson(res, {error: error.message}); + res.status(response.status).json(response); } }); authRouter.post('/login', async (req, res, next) => { try { - passport.authenticate('local', (err, user, info) => { - if (err) { - return res.status(500).send(err); // Internal Server Error - } - if (!user) { - return res.status(401).send('Invalid username or password.'); // Unauthorized - } - const token = jwt.sign({username: user.username}, secretKey, {expiresIn: '1h'}); - return res.status(200).json({token}); - })(req, res, next); + const {username, password} = req.body; + const response = await User.authenticateLocal(username, password); + console.log(response); + if (response.status === undefined) { + res.json(response); + } else { + res.status(response.status).json(response); + } } catch (error) { - console.error("Erreur lors de la tentative de connexion :", error); - return res.status(500).send(error); + const errorStatus = error.status || 500; + return res.status(errorStatus).send(error.message || error); } }); authRouter.post('/logout', extractToken, async (req, res) => { try { - // Perform logout logic if needed - const decoded = await extractUserFromToken(req.token, secretKey); - if (!decoded) { - return res.status(401).send('Invalid token.'); + const response = await User.logoutUser(req.token); + if (response.status === undefined) { + res.json(response); + } else { + res.status(response.status).json(response); } - return res.status(200).send('Logout successful.'); } catch (error) { - console.error("Erreur lors de la déconnexion de l'utilisateur :", error); - return res.status(500).send(error); + res.status(500).json({message: error.message}); } }); diff --git a/data-access/connection.js b/data-access/connection.js index 41005d4a9f9a66b9a81323ca81de7e199e4f00c0..40428d3d2680bde9d41d71983ad8c089bf57f7c7 100644 --- a/data-access/connection.js +++ b/data-access/connection.js @@ -3,7 +3,7 @@ require('dotenv').config(); const dbName = process.env.DB_NAME; const host = process.env.HOST; -const url = `mongodb://${host}:27017/${dbName}`; +const url = `${host}/${dbName}`; const connectToDatabase = async () => { try { diff --git a/index.js b/index.js index ba99466d899401374a9f31ad15825a1eb1710aef..94faaa815e0f7a90db9ed15349443e43f29eefbe 100644 --- a/index.js +++ b/index.js @@ -5,7 +5,6 @@ const swaggerUi = require('swagger-ui-express'); const swaggerJsdoc = require('swagger-jsdoc'); const swaggerParser = require('swagger-parser'); -const {makeExecutableSchema} = require('@graphql-tools/schema'); const {ApolloServer} = require('apollo-server-express'); const YAML = require('yamljs'); @@ -73,12 +72,12 @@ startServer().then(() => { connectToDatabase() .then(() => { app.listen(serverPort, () => { - console.log(`Your server is listening on port ${serverPort} (http://localhost:${serverPort})`); - console.log('Swagger-ui is available on http://localhost:%d/docs', serverPort); - console.log('GraphQL Playground is available on http://localhost:%d/graphql-playground', serverPort); + console.log(`Votre serveur écoute sur le port ${serverPort} (http://localhost:${serverPort})`); + console.log('Swagger-ui est disponible sur http://localhost:%d/docs', serverPort); + console.log('GraphQL Playground est disponible sur http://localhost:%d/graphql-playground', serverPort); }); }) .catch(error => { - console.error('Error connecting to the database:', error); + console.error('Erreur de connexion à la base de données:', error); }); }); diff --git a/models/Ad.js b/models/Ad.js index 22c3eef007ab9cc9e41594a4075dcef9ab234000..cb1e2f8b8047f1569a8eaf062adb59e82b93ae19 100644 --- a/models/Ad.js +++ b/models/Ad.js @@ -1,6 +1,5 @@ var mongoose = require('mongoose'); var Schema = mongoose.Schema; -var passportLocalMongoose = require('passport-local-mongoose'); const AdSchema = new Schema({ title: {type: String, required: true}, diff --git a/package-lock.json b/package-lock.json index 3655a8cb7cb45f7314c188f08ccd9c1cd295625b..dbb2f4d015e09d994bcd89f03f823693af0dace1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -459,6 +459,12 @@ "@types/node": "*" } }, + "node_modules/@types/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-b698BLJ6kPVd6uhHsY7wlebZdrWPXYied883PDSzpJZYOP97EOn/oGdLCH3jJf157srkFReIZY5v0H1s8Dozrg==", + "dev": true + }, "node_modules/@types/cors": { "version": "2.8.12", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", @@ -2598,6 +2604,22 @@ "resolved": "https://registry.npmjs.org/lodash.zipobject/-/lodash.zipobject-4.1.3.tgz", "integrity": "sha512-A9SzX4hMKWS25MyalwcOnNoplyHbkNVsjidhTp8ru0Sj23wY9GWBKS8gAIGDSAqeWjIjvE4KBEl24XXAs+v4wQ==" }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/loglevel": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz", @@ -2615,21 +2637,6 @@ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/loupe": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", @@ -3568,6 +3575,15 @@ "node": ">=8.10.0" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", diff --git a/schemas/User&AdSchema.js b/schemas/User&AdSchema.js index fe7589a72750a353e9d9933ba319841f54f9306a..0cfe8a6c89aaa58f55b43fa28ae3d94a1af98af2 100644 --- a/schemas/User&AdSchema.js +++ b/schemas/User&AdSchema.js @@ -2,8 +2,6 @@ const {makeExecutableSchema} = require("@graphql-tools/schema"); const UserModel = require("../models/User"); -const axios = require('axios'); - const { addAdWithPhotos, updateAd, @@ -14,6 +12,12 @@ const { answerQuestion, } = require("../service/AdService"); +const { + createUser, + authenticateLocal, + logoutUser +} = require("../service/UserService"); + const typeDefs = ` type Ad { _id: ID @@ -74,7 +78,6 @@ const typeDefs = ` type User { _id: ID! username: String! - password: String isAgent: Boolean } @@ -115,9 +118,11 @@ const resolvers = { }, getUser: async (parent, {id, username}) => { if (id) { - return await UserModel.findById(id); + user = await UserModel.findById(id); + return user; } else if (username) { - return await UserModel.findOne({username}); + user = await UserModel.findOne({username}); + return user; } else { return null; } @@ -205,44 +210,41 @@ const resolvers = { return result; }, createUser: async (_, {input}) => { - try { - const response = await axios.post('http://localhost:8080/user', input); - if (response.data.exports) { - throw new Error(response.data.exports.error); - } - return response.data.user; - } catch (error) { - console.error('Erreur lors de la création de l\'utilisateur:', error); - throw new Error('Erreur lors de la création de l\'utilisateur. Vérifiez les détails dans les journaux.'); + const result = await createUser(input); + + if (result.status) { + throw new Error(result.message); } + + return result; }, loginUser: async (_, {username, password}) => { try { - const response = await axios.post('http://localhost:8080/user/login', {username, password}); + const response = await authenticateLocal(username, password); - if (response.status === 200) { - return {token: response.data.token}; + if (response.status === undefined) { + return {token: response.token}; } else { throw new Error('Invalid username or password.'); } } catch (error) { - console.error('Erreur lors de la tentative de connexion:', error); - throw new Error('Erreur lors de la tentative de connexion. Vérifiez les détails dans les journaux.'); + throw new Error('Invalid username or password.', error); } }, - logoutUser: async () => { - try { - const response = await axios.post('http://localhost:8080/user/logout'); + logoutUser: async (_, {}, context) => { + const tokenHeader = context.headers.authorization; + if (!tokenHeader || !tokenHeader.startsWith('Bearer ')) { + throw new Error('L\'en-tête d\'autorisation est manquant ou a un format invalide'); + } - if (response.status === 200) { - return 'Logout successful.'; - } else { - throw new Error('Logout failed.'); - } - } catch (error) { - console.error('Erreur lors de la déconnexion de l\'utilisateur:', error); - throw new Error('Erreur lors de la déconnexion de l\'utilisateur. Vérifiez les détails dans les journaux.'); + const token = context.headers.authorization.replace('Bearer ', ''); + const result = await logoutUser(token); + + if (result.status) { + throw new Error(result.message); } + + return result; }, }, }; diff --git a/service/AdService.js b/service/AdService.js index 3dff6d3bd2b71a7d83e7f8939a35c8acfd8f3310..0ffe51da6cdbba79e97a65d6a5c661779b075a4b 100644 --- a/service/AdService.js +++ b/service/AdService.js @@ -6,13 +6,11 @@ const User = require('../models/User'); const {extractUserFromToken} = require("./UserService"); -const secretKey = process.env.SECRET_KEY; - // Service for adding an ad exports.addAdWithPhotos = async function (body, photos, token) { try { const {title, propertyType, publicationStatus, propertyStatus, description, price, availabilityDate} = body; - const decoded = await extractUserFromToken(token, secretKey); + const decoded = await extractUserFromToken(token); const user = await User.findOne({username: decoded.username}); if (user.isAgent === false) { @@ -48,7 +46,7 @@ exports.addAdWithPhotos = async function (body, photos, token) { // Service for getting ads exports.getAds = async function (token) { try { - const decoded = await extractUserFromToken(token, secretKey); + const decoded = await extractUserFromToken(token); let ads; const user = await User.findOne({username: decoded.username}); if (user && user.isAgent) { @@ -65,7 +63,7 @@ exports.getAds = async function (token) { // Service for adding photos to an ad exports.addPhotosToAd = async function (adId, photos, token) { try { - const decoded = await extractUserFromToken(token, secretKey); + const decoded = await extractUserFromToken(token); const user = await User.findOne({username: decoded.username}); const ad = await Ad.findById(adId); if (!ad) { @@ -99,7 +97,7 @@ exports.addPhotosToAd = async function (adId, photos, token) { // Service for updating an ad with photos exports.updateAd = async function (adId, reqBody, files, token) { try { - const decoded = await extractUserFromToken(token, secretKey); + const decoded = await extractUserFromToken(token); const user = await User.findOne({username: decoded.username}); const ad = await Ad.findById(adId); @@ -145,7 +143,7 @@ exports.updateAd = async function (adId, reqBody, files, token) { // Service for deleting an ad exports.deleteAd = async function (adId, token) { try { - const decoded = await extractUserFromToken(token, secretKey); + const decoded = await extractUserFromToken(token); const user = await User.findOne({username: decoded.username}); const ad = await Ad.findById(adId); @@ -176,7 +174,7 @@ exports.deleteAd = async function (adId, token) { // Service for getting an ad by id exports.getAdById = async function (adId, token) { try { - const decoded = await extractUserFromToken(token, secretKey); + const decoded = await extractUserFromToken(token); const user = await User.findOne({username: decoded.username}); const adDetails = await Ad.findById(adId); @@ -204,7 +202,7 @@ exports.getAdById = async function (adId, token) { // Service for asking a question on an ad exports.askQuestion = async function (adId, question, token) { try { - const decoded = await extractUserFromToken(token, secretKey); + const decoded = await extractUserFromToken(token); const user = await User.findOne({username: decoded.username}); const ad = await Ad.findById(adId); @@ -228,7 +226,7 @@ exports.askQuestion = async function (adId, question, token) { // Service for answering a question on an ad exports.answerQuestion = async function (adId, questionId, answer, token) { try { - const decoded = await extractUserFromToken(token, secretKey); + const decoded = await extractUserFromToken(token); const user = await User.findOne({username: decoded.username}); const ad = await Ad.findById(adId); diff --git a/service/UserService.js b/service/UserService.js index e81e3a5d01c60a5f5788c0f45caf4513d98b1ca4..e5fd40ab29edc9e389e6325ec58dfbf45fbbed13 100644 --- a/service/UserService.js +++ b/service/UserService.js @@ -4,9 +4,19 @@ const passport = require('../passeport-config'); const User = require('../models/User'); const jwt = require('jsonwebtoken'); +const secretKey = process.env.SECRET_KEY; +const expiresIn = process.env.EXPIRES_IN; passport.use(User.createStrategy()); +// Serialize user +function createUserResponse(user) { + return { + _id: user._id, + username: user.username, + }; +} + /** * Create user * This can only be done by the logged in user. @@ -14,33 +24,42 @@ passport.use(User.createStrategy()); * body User Created user object (optional) * returns User **/ -exports.createUser = async function (username, password, isAgent) { +exports.createUser = async function (body) { + const {username, password, isAgent} = body; const existingUser = await User.findOne({username}); if (existingUser) { - throw new Error('This user already exists.'); + return {status: 400, message: 'Le nom d\'utilisateur existe déjà'}; } const user = await User.register(new User({username: username, isAgent: isAgent}), password); - console.log("user", user) - return user; + const newUser = createUserResponse(user); + return newUser; }; -/** - * Logs user into the system - * - * username String The user name for login (optional) - * password String The password for login in clear text (optional) - * returns String - **/ -exports.isAuthenticated = function (req, res, next) { - if (req.isAuthenticated()) { - return next(); - } - res.status(401).json({message: 'Authentification requise'}); +// Generate JWT token +function generateAuthToken(user) { + return jwt.sign({username: user.username}, secretKey, {expiresIn: expiresIn}); } -exports.extractUserFromToken = function (token, secretKey) { +// Authenticate user +exports.authenticateLocal = function (username, password) { + return new Promise((resolve, reject) => { + passport.authenticate('local', (err, user, info) => { + if (err) { + reject({status: 500, message: err}); + } + if (!user) { + reject({status: 404, message: 'Invalid username or password'}); + } + const token = generateAuthToken(user); + resolve({token}); + })({body: {username, password}}); + }); +}; + +// Extract token from request +exports.extractUserFromToken = function (token) { return new Promise(async (resolve, reject) => { try { const decoded = await jwt.verify(token, secretKey); @@ -63,3 +82,13 @@ exports.extractUserFromToken = function (token, secretKey) { } }); }; + +// Authenticate user +exports.logoutUser = async function (token) { + try { + await exports.extractUserFromToken(token); + return JSON.stringify('Déconnexion réussie'); + } catch (error) { + throw error; + } +}; diff --git a/utils/writer.js b/utils/writer.js index fa55a0bea3cfe0bd77407c64990e866461e2e497..7f61ccd2b2f1e4ec907e45cba895511a95b0b70c 100644 --- a/utils/writer.js +++ b/utils/writer.js @@ -39,3 +39,14 @@ var writeJson = exports.writeJson = function (response, arg1, arg2) { response.writeHead(code, {'Content-Type': 'application/json'}); response.end(payload); } + +exports.extractToken = function (req, res, next) { + const authorizationHeader = req.headers.authorization; + + if (authorizationHeader && authorizationHeader.startsWith('Bearer ')) { + req.token = authorizationHeader.slice(7); + next(); + } else { + res.status(401).json({message: 'Authentication is required.'}); + } +}; \ No newline at end of file