diff --git a/cyt-utils/index.js b/cyt-utils/index.js index 7c44e7cb..f5bde9fe 100644 --- a/cyt-utils/index.js +++ b/cyt-utils/index.js @@ -5,4 +5,5 @@ module.exports = { responseLoggerMiddleware: require('./logging/middleware/ResLoggerMiddleware'), i18nextInitializer: require('./i18n/i18nextInitializer'), i18nextMiddleware: require('./i18n/i18nextMiddleware'), + fieldChecker: require('./validation/fieldChecker'), } diff --git a/cyt-utils/package-lock.json b/cyt-utils/package-lock.json index a2bb9db9..95d7d9ba 100644 --- a/cyt-utils/package-lock.json +++ b/cyt-utils/package-lock.json @@ -1,12 +1,12 @@ { - "name": "@arquisoft/cyt-utils", - "version": "1.0.0", + "name": "cyt-utils", + "version": "2.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@arquisoft/cyt-utils", - "version": "1.0.0", + "name": "cyt-utils", + "version": "2.2.0", "license": "ISC", "dependencies": { "@elastic/ecs-winston-format": "^1.5.3", diff --git a/cyt-utils/package.json b/cyt-utils/package.json index 62d7dc52..8204130e 100644 --- a/cyt-utils/package.json +++ b/cyt-utils/package.json @@ -1,6 +1,6 @@ { "name": "cyt-utils", - "version": "2.2.0", + "version": "2.4.0", "description": "A variety of utilities for the project", "main": "index.js", "scripts": { diff --git a/cyt-utils/validation/fieldChecker.js b/cyt-utils/validation/fieldChecker.js new file mode 100644 index 00000000..00537482 --- /dev/null +++ b/cyt-utils/validation/fieldChecker.js @@ -0,0 +1,7 @@ +const fieldChecker = (fields, obj) => { + for (let field of fields) + if (!(field in obj)) return field; + return null; +} + +module.exports = fieldChecker; \ No newline at end of file diff --git a/gatewayservice/middleware/AuthTokenMiddleware.js b/gatewayservice/middleware/AuthTokenMiddleware.js index 259a4cf1..26293884 100644 --- a/gatewayservice/middleware/AuthTokenMiddleware.js +++ b/gatewayservice/middleware/AuthTokenMiddleware.js @@ -2,13 +2,9 @@ module.exports = (i18next) => (req, res, next) => { const { userIdToken } = req let userId - if("userId" in req.params) { - userId = req.params.userId - } else if ("userId" in req.body) { - userId = req.body.userId - } else { - userId = req.query.userId - } + if("userId" in req.params) userId = req.params.userId + else if ("userId" in req.body) userId = req.body.userId + else userId = req.query.userId if(!userId) return next({status: 400, error: `${i18next.t("error_missing_field")} userId`}) if (userIdToken && userIdToken !== userId) return next({ status: 401, error: i18next.t("error_save_property") }) diff --git a/gatewayservice/routes/jordiRoutes.js b/gatewayservice/routes/jordiRoutes.js index 0f7b9a27..160619ab 100644 --- a/gatewayservice/routes/jordiRoutes.js +++ b/gatewayservice/routes/jordiRoutes.js @@ -51,8 +51,6 @@ module.exports = (app, axios) => { ); }); - // TODO - Check n is a number -> error 400 - // TODO - If no category is found -> error 404 app.get("/game/questions/:category/:n", async (req, res, next) => { axios .get(`${questionServiceUrl}/questions/${req.params.category}/${req.params.n}`) diff --git a/gatewayservice/routes/usersRoutes.js b/gatewayservice/routes/usersRoutes.js index 5aeab92d..8d594174 100644 --- a/gatewayservice/routes/usersRoutes.js +++ b/gatewayservice/routes/usersRoutes.js @@ -21,7 +21,7 @@ module.exports = (app, axios, authTokenMiddleware) => { let e = error.error || i18next.t("error_login_service_unable") if (error.code && error.code.includes("ECONNREFUSED")) e = { error: i18next.t("error_service_unavailable") } - res.status(200).json({ message: response.data.message, error: e }) + res.json({ message: response.data.message, error: e }) }) }) .catch(() => next({ error: i18next.t("error_adding_user") })); diff --git a/gatewayservice/utils/FieldChecker.js b/gatewayservice/utils/FieldChecker.js index 1ab952bd..a27dff2d 100644 --- a/gatewayservice/utils/FieldChecker.js +++ b/gatewayservice/utils/FieldChecker.js @@ -1,3 +1,5 @@ + +// TODO - Move to npm package const checkFieldsOn = (fields, obj) => { for (let field of fields) if (!(field in obj)) return field; diff --git a/jordi/locals/en.json b/jordi/locals/en.json index 2073b4a9..9a602a0e 100644 --- a/jordi/locals/en.json +++ b/jordi/locals/en.json @@ -4,6 +4,8 @@ "error_invalid_n": "'n' must be a number", "error_group_not_found": "Group not found", "error_group_exists": "Already existing groups detected", + "error_question_not_found": "Question not found", + "error_category_not_found": "Category not found", "group_questions_generated": "Questions generated successfully: ", "all_questions_generated": "All questions generated successfully", diff --git a/jordi/locals/es.json b/jordi/locals/es.json index 49c6d3bb..7a406e72 100644 --- a/jordi/locals/es.json +++ b/jordi/locals/es.json @@ -4,6 +4,8 @@ "error_invalid_n": "'n' debe ser un numero", "error_group_not_found": "Grupo no encontrado", "error_group_exists": "Already existing groups detected", + "error_question_not_found": "Pregunta no encontrada", + "error_category_not_found": "Categoria no encontrada", "group_questions_generated": "Preguntas generadas correctamente: ", "all_questions_generated": "Todas las preguntas generadas correctamente", diff --git a/jordi/repositories/questionRepository.js b/jordi/repositories/questionRepository.js index c60d241d..0fa446f1 100644 --- a/jordi/repositories/questionRepository.js +++ b/jordi/repositories/questionRepository.js @@ -29,6 +29,17 @@ module.exports = { } }, + checkCategory: async function (category) { + try { + await this.checkUp(); + return await this.mongoose.connection + .collection(this.collectionName) + .findOne({ categories: category }); + } catch (e) { + throw e.message; + } + }, + getQuestions: async function (category, n = 10) { try { await this.checkUp(); diff --git a/jordi/routes/jordi-ask.js b/jordi/routes/jordi-ask.js index 80d75132..db8624e4 100644 --- a/jordi/routes/jordi-ask.js +++ b/jordi/routes/jordi-ask.js @@ -13,16 +13,19 @@ module.exports = function (app, questionsRepository) { questionsRepository .findQuestionById(req.params.id) - .then(result => res.json(result)) + .then(result => { + if(!result) return next({status: 404, error: i18next.t("error_question_not_found")}) + res.json(result) + }) .catch(err => next(err)); }) - // TODO: Should return 404 if category not found - // TODO: Check n is a number -> error 400 app.get('/questions/:category/:n', async (req, res, next) => { const {category, n} = req.params; if(isNaN(n)) return next({status: 400, error: i18next.t("error_invalid_n")}) + const c = await questionsRepository.checkCategory(category); + if(!c) return next({status: 404, error: i18next.t("error_category_not_found")}) questionsRepository.getQuestions(category, n) .then(result => { diff --git a/jordi/routes/jordi-think.js b/jordi/routes/jordi-think.js index e90b33e9..b09dc7af 100644 --- a/jordi/routes/jordi-think.js +++ b/jordi/routes/jordi-think.js @@ -8,26 +8,19 @@ module.exports = function (app, questionsRepository, groupsRepository) { app.get("/gen/:groupId", async (req, res, next) => { try { - const { groupId } = req.params; - let group = await groupsRepository.findGroups({ groupId: groupId }); - if (!group[0]) return next({ status: 404, error: i18next.t("error_group_not_found") }); group = group[0]; - const generator = new WikidataGenerator(group); - const questions = await generator.generate() await questionsRepository.deleteQuestions(groupId) await questionsRepository.insertQuestions(questions) res.json({ message: i18next.t("group_questions_generated") + groupId }); console.log("Questions generated successfully: " + groupId) - } catch (error) { - next(error); - } + } catch (error) { next(error); } }); @@ -35,35 +28,25 @@ module.exports = function (app, questionsRepository, groupsRepository) { try { await script(groupsRepository, questionsRepository, WikidataGenerator); res.json({ message: i18next.t("all_questions_generated") }); - - } catch (error) { - next(error); - } + } catch (error) { next(error); } }); app.get("/groups", async (req, res, next) => { try { const groups = await groupsRepository.findGroups({}); res.json(groups); - } catch (error) { - next(error); - } + } catch (error) { next(error); } }); app.post("/addGroups", async (req, res, next) => { try { - const groups = req.body; - for (let group of groups) { + for (let group of groups) await groupsRepository.insertGroup(group); - } res.json({ message: i18next.t("groups_added") }) - - } catch (error) { - next(error); - } + } catch (error) { next(error); } }); app.get("/removeGroup/:groupId", async (req, res, next) => { @@ -71,18 +54,14 @@ module.exports = function (app, questionsRepository, groupsRepository) { const { groupId } = req.params; await groupsRepository.removeGroups({ groupId: groupId }); res.json({ message: i18next.t("group_removed") }) - } catch (error) { - next(error); - } + } catch (error) { next(error); } }); app.get("/removeAllGroups", async (req, res, next) => { try { await groupsRepository.removeGroups(); res.json({ message: i18next.t("all_groups_removed") }); - } catch (error) { - next(error); - } + } catch (error) { next(error); } }); } diff --git a/package.json b/package.json index 1f448a72..8d3b613f 100644 --- a/package.json +++ b/package.json @@ -1,27 +1,11 @@ { "scripts": { "mongo": "docker run -d -p 27017:27017 --name=my-mongo mongo:latest", - "postgres": "docker run -d --name my_postgres -e POSTGRES_PASSWORD=jordishhh -p 5432:5432 postgres:latest", "webapp": "cd webapp && npm start", "gateway": "cd gatewayservice && npm start", "auth": "cd users/authservice && npm start", "user": "cd users/userservice && npm start", - "think": "cd jordi/jordi-think && npm start", - "ask": "cd jordi/jordi-ask && npm start", - "ranking": "cd ranking && npm start" - }, - "dependencies": { - "axios": "^1.6.8", - "bcrypt": "^5.1.1", - "es6": "^0.0.7", - "express": "^4.19.2", - "mongodb-memory-server": "^9.1.8", - "mongoose": "^8.3.0", - "node-cron": "^3.0.3", - "supertest": "^6.3.4" - }, - "devDependencies": { - "jest": "^29.7.0", - "typescript": "^5.4.3" + "jordi": "cd jordi && npm start", + "history": "cd userhistory && npm start" } } diff --git a/userhistory/history-service.js b/userhistory/history-service.js index 890e99c8..85ae1ef8 100644 --- a/userhistory/history-service.js +++ b/userhistory/history-service.js @@ -62,5 +62,4 @@ server.on('close', () => { cron.getTasks().forEach(task => task.stop()) }); - module.exports = server \ No newline at end of file diff --git a/userhistory/routes/historyRoutes.js b/userhistory/routes/historyRoutes.js index 3672a52b..2f4233b1 100644 --- a/userhistory/routes/historyRoutes.js +++ b/userhistory/routes/historyRoutes.js @@ -1,3 +1,5 @@ + +// TODO - move to npm package const checkFieldsOn = (fields, obj) => { for (let field of fields) if (!(field in obj)) return field; @@ -7,7 +9,6 @@ const checkFieldsOn = (fields, obj) => { module.exports = (app, saveRepository) => { const i18next = app.get("i18next"); - // TODO - Add error mapping app.post("/create", (req, res, next) => { const result = checkFieldsOn(["userId", "category"], req.body) if(result) return next({status: 400, error: `${i18next.t("error_missing_field")} ${result}`}) @@ -23,7 +24,6 @@ module.exports = (app, saveRepository) => { .catch(error => next(error)) }) - // TODO - Add error mapping // TODO - Gateway should check if the user is owner of the save app.post("/add/:id", (req, res, next) => { const { id } = req.params; @@ -47,7 +47,6 @@ module.exports = (app, saveRepository) => { .catch((e) => next(e)); }); - // TODO - Add error mapping app.get("/get/:userId", (req, res, next) => { const { userId } = req.params; if (!saveRepository.isValidObjectId(userId)) return next({ status: 400, error: i18next.t("error_invalid_userId")}) @@ -64,7 +63,6 @@ module.exports = (app, saveRepository) => { .catch((e) => next(e)); }); - // TODO - Add error mapping app.get("/get/:userId/:id", (req, res, next) => { const { userId, id } = req.params; if (!saveRepository.isValidObjectId(userId)) return next({status: 400, error: i18next.t("error_invalid_userId")}) @@ -92,7 +90,7 @@ module.exports = (app, saveRepository) => { saveRepository .getRanking(Number(n), order) - .then((result) => res.status(200).json(result)) + .then((result) => res.json(result)) .catch((error) => next(error)); }); }; diff --git a/users/authservice/routes/authRoutes.js b/users/authservice/routes/authRoutes.js index 899d85aa..84e12ebf 100644 --- a/users/authservice/routes/authRoutes.js +++ b/users/authservice/routes/authRoutes.js @@ -4,6 +4,7 @@ const jwt = require('jsonwebtoken'); // TODO - Move to GH secret const JWT_SECRET = process.env.SECRET || "a-very-secret-string" +// TODO - Move to npm package const checkFieldsOn = (fields, obj) => { for (let field of fields) if (!(field in obj)) return field; diff --git a/users/userservice/.env b/users/userservice/.env index fb9856f9..32bfd7b1 100644 --- a/users/userservice/.env +++ b/users/userservice/.env @@ -1 +1,2 @@ -MONGODB_URI="mongodb://localhost:27017/userdb" \ No newline at end of file +MONGODB_URI="mongodb://localhost:27017/userdb" +ADMIN_PASSWORD="admin" \ No newline at end of file diff --git a/users/userservice/locals/en.json b/users/userservice/locals/en.json index c0578832..e64a166b 100644 --- a/users/userservice/locals/en.json +++ b/users/userservice/locals/en.json @@ -1,5 +1,6 @@ { "translation": { + "user_created": "User created successfully", "error_invalid_id": "Invalid id format", "error_user_exists": "Username already exists", "error_user_not_found": "User not found", diff --git a/users/userservice/locals/es.json b/users/userservice/locals/es.json index 0d019a05..c8a6aa98 100644 --- a/users/userservice/locals/es.json +++ b/users/userservice/locals/es.json @@ -1,5 +1,6 @@ { "translation": { + "user_created": "Usuario creado correctamente", "error_invalid_id": "Formato de ID inválido", "error_user_exists": "El usuario ya existe", "error_user_not_found": "Usuario no encontrado", diff --git a/users/userservice/middleware/DataMiddleware.js b/users/userservice/middleware/DataMiddleware.js index 2b6ac1b6..852d9407 100644 --- a/users/userservice/middleware/DataMiddleware.js +++ b/users/userservice/middleware/DataMiddleware.js @@ -1,3 +1,5 @@ + +// TODO - Move to npm package const checkFieldsOn = (fields, obj) => { for (const field of fields) if (!obj[field]) return field; diff --git a/users/userservice/repositories/userRepository.js b/users/userservice/repositories/userRepository.js index 05f3df45..8baba503 100644 --- a/users/userservice/repositories/userRepository.js +++ b/users/userservice/repositories/userRepository.js @@ -1,10 +1,17 @@ +const bcrypt = require('bcrypt'); + module.exports = { mongoose: null, uri: null, + i18next: null, User: require("../user-model"), - init: function (mongoose, uri) { + init: function (mongoose, uri, i18next) { this.mongoose = mongoose; this.uri = uri; + this.i18next = i18next; + + this.initAdminUser(); + }, checkUp: async function () { if (this.mongoose.connection.readyState !== 1) { @@ -19,7 +26,7 @@ module.exports = { password: password, }); await user.save(); - return { message: "User created successfully" }; + return { message: this.i18next.t("user_created") }; } catch (error) { throw error.message; } @@ -35,5 +42,26 @@ module.exports = { }, checkValidId: function (id) { return !(!id || !this.mongoose.isValidObjectId(id)); + }, + initAdminUser: function () { + + const adminPassword = process.env.ADMIN_PASSWORD || "admin"; + + this.getUser({ username: "admin" }).then((user) => { + if (!user) { + bcrypt.hash(adminPassword, 10, async (err, hashedPassword) => { + if (err) { + console.error("Error hashing admin password", err); + return; + } + try { + await this.insertUser("admin", hashedPassword); + console.log("Admin user created successfully"); + } catch (error) { + console.error("Error creating admin user", error); + } + }); + } + }); } }; diff --git a/users/userservice/user-service.js b/users/userservice/user-service.js index 4ce7a8ab..b591be3c 100644 --- a/users/userservice/user-service.js +++ b/users/userservice/user-service.js @@ -43,7 +43,7 @@ const dataMiddleware = require('./middleware/DataMiddleware')(i18next) app.use("/adduser", dataMiddleware) // Initialize the repository -userRepository.init(mongoose, mongoUri); +userRepository.init(mongoose, mongoUri, i18next); // Routes require('./routes/usersRoutes')(app, userRepository)