From 976bde156378d5d5c2e5954525555ef116da07d1 Mon Sep 17 00:00:00 2001 From: Guillermo Croppi Date: Thu, 19 Dec 2024 20:26:41 -0300 Subject: [PATCH] Ready for 0.3-dev --- controllers/challengeController.js | 110 ++++++++++++++++++++++++++ controllers/faqController.js | 69 ++++++++++++++++ controllers/initiativeController.js | 69 ++++++++++++++++ controllers/utilsController.js | 41 ++++++++-- helpers/utilsHelper.js | 59 ++++++++++++++ middlewares/jwt.js | 21 ++++- migrations/00008-create-dimensions.js | 4 + migrations/00012-create-faq.js | 39 +++++++++ migrations/00013-create-config.js | 42 ++++++++++ migrations/00014-create-members.js | 59 ++++++++++++++ migrations/00015-create-challenges.js | 55 +++++++++++++ models/challenge.js | 58 ++++++++++++++ models/config.js | 26 ++++++ models/faq.js | 26 ++++++ models/member.js | 48 +++++++++++ models/user.js | 5 +- package-lock.json | 18 +++++ package.json | 1 + routes/challenge.js | 38 +++++++++ routes/faq.js | 58 ++++++++++++++ routes/index.js | 8 +- routes/initiative.js | 45 +++++++++++ routes/utils.js | 11 ++- 23 files changed, 900 insertions(+), 10 deletions(-) create mode 100644 controllers/challengeController.js create mode 100644 controllers/faqController.js create mode 100644 helpers/utilsHelper.js create mode 100644 migrations/00012-create-faq.js create mode 100644 migrations/00013-create-config.js create mode 100644 migrations/00014-create-members.js create mode 100644 migrations/00015-create-challenges.js create mode 100644 models/challenge.js create mode 100644 models/config.js create mode 100644 models/faq.js create mode 100644 models/member.js create mode 100644 routes/challenge.js create mode 100644 routes/faq.js create mode 100644 routes/initiative.js diff --git a/controllers/challengeController.js b/controllers/challengeController.js new file mode 100644 index 0000000..c694bc7 --- /dev/null +++ b/controllers/challengeController.js @@ -0,0 +1,110 @@ +const models = require('../models'); +const msg = require('../utils/messages'); + +exports.create = async (req, res) => { + try { + const { + dimensionId, + subdivisionId, + needsAndChallenges, + proposal, + inWords + } = req.body; + + // create a new Challenge entry + const blogEntry = await models.Challenge.create({ + dimensionId, + subdivisionId, + needsAndChallenges, + proposal, + inWords + }); + + return res.status(201).json(blogEntry); + } catch (error) { + console.error(error); + return res.status(500).json({ message: msg.error.default }); + } +} + +exports.stats = async (req, res) => { + try { + const cityId = parseInt(req.query.cityId) || null; + // count how many challenges per dimension + const countChallengesPerDimension = await models.Challenge.count({ + attributes: ['dimensionId'], + include: [ + { + model: models.Subdivision, + as: 'subdivision', + where: cityId ? { cityId: cityId } : {} + } + ], + group: ['dimensionId'] + }); + + // count how many challenges per city + const countChallengesInCity = await models.Challenge.count({ + attributes: ['subdivision.cityId'], + include: [ + { + model: models.Subdivision, + as: 'subdivision', + where: cityId ? { cityId: cityId } : {} + } + ], + group: ['subdivision.cityId'], + }); + + // { + // "countChallengesPerDimension": [ + // { + // "dimensionId": 1, + // "count": 2 + // }, + // { + // "dimensionId": 4, + // "count": 2 + // }, + // { + // "dimensionId": 2, + // "count": 1 + // }, + // { + // "dimensionId": 3, + // "count": 1 + // }, + // { + // "dimensionId": 6, + // "count": 1 + // }, + // { + // "dimensionId": 5, + // "count": 1 + // }, + // { + // "dimensionId": 7, + // "count": 1 + // } + // ], + // "countChallengesInCity": [ + // { + // "cityId": 1, + // "count": 5 + // }, + // { + // "cityId": 2, + // "count": 4 + // } + // ] + // } + + return res.status(200).json({ + countChallengesPerDimension, + countChallengesInCity + }); + } catch (error) { + console.error(error); + return res.status(500).json({ message: msg.error.default }); + } +} \ No newline at end of file diff --git a/controllers/faqController.js b/controllers/faqController.js new file mode 100644 index 0000000..b3fac27 --- /dev/null +++ b/controllers/faqController.js @@ -0,0 +1,69 @@ +const models = require('../models'); + +exports.getAll = async (req, res) => { + try { + const faqs = await models.Faq.findAll({ + order: [['order', 'ASC']] + }); + return res.status(200).json(faqs); + } catch (error) { + console.error(error); + return res.status(500).json({ message: 'An error occurred' }); + } +} + +exports.getOne = async (req, res) => { + try { + const id = req.params.id; + const faq = await models.Faq.findOne({ where: { id } }); + return res.status(200).json(faq); + } catch (error) { + console.error(error); + return res.status(500).json({ message: 'An error occurred' }); + } +} + +exports.create = async (req, res) => { + try { + const { order, question, answer } = req.body; + const faq = await models.Faq.create({ order, question, answer }); + return res.status(201).json(faq); + } catch (error) { + console.error(error); + return res.status(500).json({ message: 'An error occurred' }); + } +} + +exports.edit = async (req, res) => { + try { + const id = req.params.id; + const { order, question, answer } = req.body; + const faq = await models.Faq.findOne({ where: { id } }); + if (!faq) { + return res.status(404).json({ message: 'FAQ not found' }); + } + faq.order = order; + faq.question = question; + faq.answer = answer; + await faq.save(); + return res.status(200).json(faq); + } catch (error) { + console.error(error); + return res.status(500).json({ message: 'An error occurred' }); + } +} + +exports.delete = async (req, res) => { + try { + const id = req.params.id; + const faq = await models.Faq.findOne({ where: { id } }); + if (!faq) { + return res.status(404).json({ message: 'FAQ not found' }); + } + await faq.destroy(); + return res.status(200).json({ message: 'FAQ deleted' }); + } catch (error) { + console.error(error); + return res.status(500).json({ message: 'An error occurred' }); + } +} diff --git a/controllers/initiativeController.js b/controllers/initiativeController.js index da819bb..e51dfcd 100644 --- a/controllers/initiativeController.js +++ b/controllers/initiativeController.js @@ -51,6 +51,75 @@ exports.getAll = async (req, res) => { exports.create = async (req, res) => { try { + const { + name, + description, + needsAndOffers, + dimensionIds, + subdivisionId + } = req.body; + const userId = req.user.id; + + // get the user + const user = await models.User.findOne({ + where: { id: userId }, + include: [ + { + model: models.Subdivision, + as: 'subdivision', + } + ] + }); + + if(!user) { + return res.status(404).json({ message: 'Usuario no encontrado' }); + } + + // get the dimensions + const dimensions = await models.Dimension.findAll({ + where: { + id: dimensionIds, + } + }); + + // TODO: check if the subdivisionId exists and the city of the subdivision is the same as the user's city + + const t = await models.sequelize.transaction(); + try { + // create the contact + const contact = { + fullname: req.body.contact.fullname, + email: req.body.contact.email, + phone: req.body.contact.phone, + keepPrivate: req.body.contact.keepPrivate, + } + + const newContact = await models.InitiativeContact.create(contact, { transaction: t }); + + // create the initiative + const initiative = { + name, + description, + needsAndOffers, + contactId: newContact.id, + subdivisionId: subdivisionId, + authorId: user.id, + } + + const newInitiative = await models.Initiative.create(initiative, { transaction: t }); + + // add the dimensions + await newInitiative.addDimensions(dimensions, { transaction: t }); + + await t.commit(); + + } catch(error){ + console.error(error); + await t.rollback(); + return res.status(500).json({ message: 'Error al crear la iniciativa' }); + } + + return res.status(201).json({ message: 'Iniciativa creada' }); } catch (error) { console.error(error); diff --git a/controllers/utilsController.js b/controllers/utilsController.js index f6efeea..1207362 100644 --- a/controllers/utilsController.js +++ b/controllers/utilsController.js @@ -1,6 +1,21 @@ +const { faker } = require('@faker-js/faker'); const models = require('../models'); const dayjs = require('dayjs'); const msg = require('../utils/messages'); +const UtilsHelper = require('../helpers/utilsHelper'); + +exports.getConfigs = async (req, res) => { + try { + const keys = req.query.keys ? req.query.keys.split(',') : null; + + const configs = await UtilsHelper.getConfigs(keys); + + return res.status(200).json(configs); + } catch (error) { + console.error(error); + res.status(500).json({ message: msg.error.default }); + } +} exports.getSubdivisions = async (req, res) => { try { @@ -23,6 +38,20 @@ exports.getSubdivisions = async (req, res) => { } } +exports.getDimensions = async (req, res) => { + try { + // get all dimensions + const dimensions = await models.Dimension.findAll({ + attributes: ['id', 'name'], + }); + + return res.status(200).json(dimensions); + } catch (error) { + console.error(error); + res.status(500).json({ message: msg.error.default }); + } +} + exports.somethingForUsers = async (req, res) => { try { console.log('req.user', req.user); @@ -40,17 +69,17 @@ exports.generateBlogPosts = async (req, res) => { const sections = await models.BlogSection.findAll(); const authors = await models.User.findAll(); - for(let i = 0; i < 100; i++) { + for(let i = 0; i < 50; i++) { const category = categories[Math.floor(Math.random() * categories.length)]; const section = sections[Math.floor(Math.random() * sections.length)]; const author = authors[Math.floor(Math.random() * authors.length)]; await models.BlogEntry.create({ - title: `Post ${i + 1}`, - subtitle: `This is the subtitle for post ${i + 1}`, - text: `This is the content for post ${i + 1}`, - imageUrl: 'https://placecats.com/300/200', - slug: `post-${i + 1}`, + title: faker.lorem.words({min: 8, max: 16}), + subtitle: faker.lorem.sentence(16), + text: faker.lorem.paragraphs({min: 12, max: 24}), + imageUrl: faker.image.urlPicsumPhotos({ width: 1024, height: 768, grayscale: false, blur: 0 }), + slug: faker.lorem.slug(5), authorId: author.id, categoryId: category.id, sectionId: section.id, diff --git a/helpers/utilsHelper.js b/helpers/utilsHelper.js new file mode 100644 index 0000000..5976f0e --- /dev/null +++ b/helpers/utilsHelper.js @@ -0,0 +1,59 @@ +const models = require('../models'); + +exports.getConfigs = async (keys) => { + try { + const configs = await models.Config.findAll({ + where: keys ? { key: keys } : null, + }); + + // for every config, check the type and parse the value + configs.forEach(config => { + switch (config.type) { + case 'string': + config.value = config.value; + break; + case 'number': + config.value = parseInt(config.value, 10); + break; + case 'boolean': + config.value = config.value === 'true'; + break; + case 'json': + config.value = JSON.parse(config.value); + break; + default: + config.value = config.value; + break; + } + }); + + const formattedConfigs = {}; + configs.forEach(config => { + formattedConfigs[config.key] = config.value; + }); + + return formattedConfigs; + } catch (error) { + throw error; + } +} + +exports.saveConfig = async (key, value) => { + try { + const config = await models.Config.findOne({ + where: { key: key } + }); + + if (!config) { + throw new Error('Config not found'); + } + + // make sure to save the value as a string + config.value = value.toString(); + await config.save(); + + return config; + } catch (error) { + throw error; + } +} diff --git a/middlewares/jwt.js b/middlewares/jwt.js index ccad37d..ae0258e 100644 --- a/middlewares/jwt.js +++ b/middlewares/jwt.js @@ -1,6 +1,8 @@ const JwtStrategy = require('passport-jwt').Strategy; const ExtractJwt = require('passport-jwt').ExtractJwt; const User = require('../models').User; +const Subdivision = require('../models').Subdivision; +const City = require('../models').City; const opts = { jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), @@ -11,7 +13,24 @@ module.exports = passport => { passport.use( new JwtStrategy(opts, (jwt_payload, done) => { console.log(jwt_payload) - User.findByPk(jwt_payload.id) + User.findOne({ + attributes: ['id', 'firstName', 'lastName', 'role', 'subdivisionId'], + include: { + model: Subdivision, + as: 'subdivision', + attributes: ['id', 'name'], + include: [ + { + model: City, + as: 'city', + attributes: ['id', 'name'], + } + ] + }, + where: { + id: jwt_payload.id + } + }) .then(user => { if (user) return done(null, user); return done(null, false); diff --git a/migrations/00008-create-dimensions.js b/migrations/00008-create-dimensions.js index 502f3a9..685d4c4 100644 --- a/migrations/00008-create-dimensions.js +++ b/migrations/00008-create-dimensions.js @@ -9,6 +9,10 @@ module.exports = { primaryKey: true, type: Sequelize.DataTypes.INTEGER }, + key: { + type: Sequelize.DataTypes.STRING, + allowNull: false + }, name: { type: Sequelize.DataTypes.STRING, allowNull: false diff --git a/migrations/00012-create-faq.js b/migrations/00012-create-faq.js new file mode 100644 index 0000000..f03488d --- /dev/null +++ b/migrations/00012-create-faq.js @@ -0,0 +1,39 @@ +const {Sequelize} = require('sequelize') + +module.exports = { + async up({context: queryInterface}) { + await queryInterface.createTable('Faqs', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.DataTypes.INTEGER + }, + order: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false + }, + question: { + type: Sequelize.DataTypes.STRING, + allowNull: false + }, + answer: { + type: Sequelize.DataTypes.TEXT, + allowNull: false + }, + createdAt: { + type: Sequelize.DataTypes.DATE, + defaultValue: Sequelize.fn('now'), + allowNull: false, + }, + updatedAt: { + type: Sequelize.DataTypes.DATE, + defaultValue: Sequelize.fn('now'), + allowNull: false, + } + }); + }, + async down({context: queryInterface}) { + await queryInterface.dropTable('Faqs'); + } +}; \ No newline at end of file diff --git a/migrations/00013-create-config.js b/migrations/00013-create-config.js new file mode 100644 index 0000000..eeef6a1 --- /dev/null +++ b/migrations/00013-create-config.js @@ -0,0 +1,42 @@ +const {Sequelize} = require('sequelize') + +module.exports = { + async up({context: queryInterface}) { + await queryInterface.createTable('Configs', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.DataTypes.INTEGER + }, + key: { + type: Sequelize.DataTypes.STRING, + allowNull: false + }, + type: { + type: Sequelize.DataTypes.STRING, + allowNull: false + }, + value: { + type: Sequelize.DataTypes.TEXT, + allowNull: true + }, + createdAt: { + type: Sequelize.DataTypes.DATE, + defaultValue: Sequelize.fn('now'), + allowNull: false, + }, + updatedAt: { + type: Sequelize.DataTypes.DATE, + defaultValue: Sequelize.fn('now'), + allowNull: false, + } + }); + await queryInterface.bulkInsert('Configs', [ + { key: 'tos', type: 'string', value: 'Próximamente' }, + ]); + }, + async down({context: queryInterface}) { + await queryInterface.dropTable('Configs'); + } +}; \ No newline at end of file diff --git a/migrations/00014-create-members.js b/migrations/00014-create-members.js new file mode 100644 index 0000000..54f5121 --- /dev/null +++ b/migrations/00014-create-members.js @@ -0,0 +1,59 @@ +const {Sequelize} = require('sequelize') + +module.exports = { + async up({context: queryInterface}) { + await queryInterface.createTable('Members', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.DataTypes.INTEGER + }, + fullname: { + type: Sequelize.DataTypes.STRING, + allowNull: false + }, + team: { + type: Sequelize.DataTypes.STRING, + allowNull: false + }, + bio: { + type: Sequelize.DataTypes.TEXT, + allowNull: false + }, + imageUrl: { + type: Sequelize.DataTypes.STRING(510), + allowNull: true + }, + linkedin: { + type: Sequelize.DataTypes.STRING(510), + allowNull: true + }, + twitter: { + type: Sequelize.DataTypes.STRING(510), + allowNull: true + }, + instagram: { + type: Sequelize.DataTypes.STRING(510), + allowNull: true + }, + tiktok: { + type: Sequelize.DataTypes.STRING(510), + allowNull: true + }, + createdAt: { + type: Sequelize.DataTypes.DATE, + defaultValue: Sequelize.fn('now'), + allowNull: false, + }, + updatedAt: { + type: Sequelize.DataTypes.DATE, + defaultValue: Sequelize.fn('now'), + allowNull: false, + } + }); + }, + async down({context: queryInterface}) { + await queryInterface.dropTable('Members'); + } +}; \ No newline at end of file diff --git a/migrations/00015-create-challenges.js b/migrations/00015-create-challenges.js new file mode 100644 index 0000000..9e57b5a --- /dev/null +++ b/migrations/00015-create-challenges.js @@ -0,0 +1,55 @@ +const { Sequelize } = require('sequelize') + +module.exports = { + async up({context: queryInterface}) { + await queryInterface.createTable('Challenges', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.DataTypes.INTEGER + }, + dimensionId: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + references: { + model: 'Dimensions', + key: 'id' + } + }, + subdivisionId: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + references: { + model: 'Subdivisions', + key: 'id' + } + }, + needsAndChallenges: { + type: Sequelize.DataTypes.TEXT, + allowNull: false, + }, + proposal: { + type: Sequelize.DataTypes.TEXT, + allowNull: false, + }, + inWords: { + type: Sequelize.DataTypes.TEXT, + allowNull: false, + }, + createdAt: { + type: Sequelize.DataTypes.DATE, + defaultValue: Sequelize.fn('now'), + allowNull: false, + }, + updatedAt: { + type: Sequelize.DataTypes.DATE, + defaultValue: Sequelize.fn('now'), + allowNull: false, + } + }); + }, + async down({context: queryInterface}) { + await queryInterface.dropTable('Challenges'); + } +}; \ No newline at end of file diff --git a/models/challenge.js b/models/challenge.js new file mode 100644 index 0000000..6472ce3 --- /dev/null +++ b/models/challenge.js @@ -0,0 +1,58 @@ +// 'use strict'; +const { Model, DataTypes } = require('sequelize'); + + +module.exports = (sequelize) => { + class Challenge extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + // define association here + Challenge.belongsTo(models.Dimension,{ + foreignKey: 'dimensionId', + targetKey: 'id', + as: 'dimension', + }); + Challenge.belongsTo(models.Subdivision, { + foreignKey: { + name: 'subdivisionId', + allowNull: false, + }, + as: 'subdivision', + }); + } + } + + Challenge.init({ + dimensionId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + subdivisionId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + needsAndChallenges: { + type: DataTypes.TEXT, + allowNull: false, + }, + proposal: { + type: DataTypes.TEXT, + allowNull: false, + }, + inWords: { + type: DataTypes.TEXT, + allowNull: false, + }, + }, { + sequelize, + timestamps: true, + modelName: 'Challenge', + tableName: 'Challenges', + }); + + return Challenge; +}; \ No newline at end of file diff --git a/models/config.js b/models/config.js new file mode 100644 index 0000000..95fe25d --- /dev/null +++ b/models/config.js @@ -0,0 +1,26 @@ +const { Model } = require('sequelize'); + +module.exports = (sequelize, DataTypes) => { + class Config extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + // define association here + } + } + Config.init({ + key: DataTypes.STRING, + type: DataTypes.STRING, + value: DataTypes.TEXT + }, { + sequelize, + timestamps: true, + modelName: 'Config', + tableName: 'Configs', + }); + + return Config; +}; \ No newline at end of file diff --git a/models/faq.js b/models/faq.js new file mode 100644 index 0000000..7bba442 --- /dev/null +++ b/models/faq.js @@ -0,0 +1,26 @@ +const { Model } = require('sequelize'); + +module.exports = (sequelize, DataTypes) => { + class Faq extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + // define association here + } + } + Faq.init({ + name: DataTypes.STRING, + order: DataTypes.NUMBER, + question: DataTypes.STRING, + answer: DataTypes.TEXT + }, { + sequelize, + timestamps: true, + modelName: 'Faq', + tableName: 'Faqs', + }); + return Faq; +}; \ No newline at end of file diff --git a/models/member.js b/models/member.js new file mode 100644 index 0000000..ee1e1ae --- /dev/null +++ b/models/member.js @@ -0,0 +1,48 @@ +const { Model } = require('sequelize'); + +module.exports = (sequelize, DataTypes) => { + class Member extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + // define association here + } + } + Member.init({ + fullname: DataTypes.STRING, + team: DataTypes.STRING, + bio: DataTypes.TEXT, + imageUrl: { + type: DataTypes.STRING(510), + validate: { + isUrl: true + }, + allowNull: true + }, + linkedin: { + type: DataTypes.STRING(510), + allowNull: true + }, + twitter: { + type: DataTypes.STRING(510), + allowNull: true + }, + instagram: { + type: DataTypes.STRING(510), + allowNull: true + }, + tiktok: { + type: DataTypes.STRING(510), + allowNull: true + }, + }, { + sequelize, + timestamps: true, + modelName: 'Member', + tableName: 'Members', + }); + return Member; +}; \ No newline at end of file diff --git a/models/user.js b/models/user.js index 8f2b3ea..ba09d00 100644 --- a/models/user.js +++ b/models/user.js @@ -21,7 +21,10 @@ module.exports = (sequelize) => { */ static associate(models) { // define association here - User.belongsTo(models.Subdivision); + User.belongsTo(models.Subdivision, { + foreignKey: 'subdivisionId', + as: 'subdivision', + }); User.hasMany(models.Initiative, { foreignKey: 'authorId', sourceKey: 'id', diff --git a/package-lock.json b/package-lock.json index b116ff8..dc832c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,10 +25,28 @@ "umzug": "^3.8.2" }, "devDependencies": { + "@faker-js/faker": "^9.3.0", "nodemon": "^3.1.7", "sequelize-cli": "^6.6.2" } }, + "node_modules/@faker-js/faker": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.3.0.tgz", + "integrity": "sha512-r0tJ3ZOkMd9xsu3VRfqlFR6cz0V/jFYRswAIpC+m/DIfAUXq7g8N7wTAlhSANySXYGKzGryfDXwtwsY8TxEIDw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/fakerjs" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0.0", + "npm": ">=9.0.0" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", diff --git a/package.json b/package.json index a1e3a18..295c119 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "umzug": "^3.8.2" }, "devDependencies": { + "@faker-js/faker": "^9.3.0", "nodemon": "^3.1.7", "sequelize-cli": "^6.6.2" } diff --git a/routes/challenge.js b/routes/challenge.js new file mode 100644 index 0000000..c7af3da --- /dev/null +++ b/routes/challenge.js @@ -0,0 +1,38 @@ +const express = require('express'); +const { check } = require('express-validator'); + +const validate = require('../middlewares/validate'); +const authorize = require('../middlewares/authorize'); +const requiresAnon = require('../middlewares/requiresAnon'); +const ChallengeController = require('../controllers/challengeController'); +const msg = require('../utils/messages'); + +// initialize router +const router = express.Router(); + +// ----------------------------------------------- +// BASE /challenges +// ----------------------------------------------- +// POST / +// GET /stats +// ----------------------------------------------- + +router.post('/', + [ + check('dimensionId').isInt().withMessage(msg.validationError.integer), + check('subdivisionId').isInt().withMessage(msg.validationError.integer), + check('needsAndChallenges').isString().withMessage(msg.validationError.string), + check('proposal').isString().withMessage(msg.validationError.string), + check('inWords').isString().withMessage(msg.validationError.string), + ], + validate, + ChallengeController.create +); + +router.get('/stats', + ChallengeController.stats +); + +// ----------------------------------------------- + +module.exports = router; diff --git a/routes/faq.js b/routes/faq.js new file mode 100644 index 0000000..7c178cc --- /dev/null +++ b/routes/faq.js @@ -0,0 +1,58 @@ +const express = require('express'); +const { check, query } = require('express-validator'); + +const validate = require('../middlewares/validate'); +const authorize = require('../middlewares/authorize'); +const FaqController = require('../controllers/faqController'); + +// initialize router +const router = express.Router(); + +// ----------------------------------------------- +// BASE /faq +// ----------------------------------------------- +// GET /faq +// GET /faq/:id +// POST /faq +// PUT /faq/:id +// DELETE /faq/:id +// ----------------------------------------------- + +// initialize router + +router.get('', + FaqController.getAll +); + +router.get('/:id', + FaqController.getOne +); + +router.post('', + authorize('admin'), + [ + check('order').isInt().withMessage('Order is required'), + check('question').not().isEmpty().withMessage('Question is required'), + check('answer').not().isEmpty().withMessage('Answer is required'), + ], + validate, + FaqController.create +); + +router.put('/:id', + authorize('admin'), + [ + check('order').isInt().withMessage('Order is required'), + check('question').not().isEmpty().withMessage('Question is required'), + check('answer').not().isEmpty().withMessage('Answer is required'), + ], + validate, + FaqController.edit +); + +router.delete('/:id', + authorize('admin'), + FaqController.delete +); + +module.exports = router; diff --git a/routes/index.js b/routes/index.js index 4385810..9640ed0 100644 --- a/routes/index.js +++ b/routes/index.js @@ -2,6 +2,9 @@ const testRoutes = require('./test'); const authRoutes = require('./auth'); const utilsRoutes = require('./utils'); const blogRoutes = require('./blog'); +const initiativeRoutes = require('./initiative'); +const challengeRoutes = require('./challenge'); +const faqRoutes = require('./faq'); const authenticate = require('../middlewares/authenticate'); module.exports = app => { @@ -14,8 +17,11 @@ module.exports = app => { app.use('/auth', authRoutes); app.use('/blog', blogRoutes); app.use('/utils', utilsRoutes); - // app.use('/users', userRoutes); + app.use('/faq', faqRoutes); + app.use('/initiatives', initiativeRoutes); + app.use('/challenges', challengeRoutes); app.use('/test', testRoutes); }; + \ No newline at end of file diff --git a/routes/initiative.js b/routes/initiative.js new file mode 100644 index 0000000..da74344 --- /dev/null +++ b/routes/initiative.js @@ -0,0 +1,45 @@ +const express = require('express'); +const { check } = require('express-validator'); + +const validate = require('../middlewares/validate'); +const authorize = require('../middlewares/authorize'); +const requiresAnon = require('../middlewares/requiresAnon'); +const InitiativeController = require('../controllers/initiativeController'); +const msg = require('../utils/messages'); + +// initialize router +const router = express.Router(); + +// ----------------------------------------------- +// BASE /challenges +// ----------------------------------------------- +// POST / +// GET /stats +// ----------------------------------------------- + +router.post('/', + authorize(), + [ + check('name').isString().withMessage(msg.validationError.string), + check('description').isString().withMessage(msg.validationError.string), + check('needsAndOffers').isString().withMessage(msg.validationError.string), + check('dimensionIds').isArray().withMessage(msg.validationError.defaultMessage), + check('dimensionIds.*').isInt().withMessage(msg.validationError.integer), + check('subdivisionId').isInt().withMessage(msg.validationError.integer), + check('contact').isObject().withMessage(msg.validationError.defaultMessage), + check('contact.fullname').isString().withMessage(msg.validationError.string), + check('contact.email').isEmail().withMessage(msg.validationError.email), + check('contact.phone').optional().isString().withMessage(msg.validationError.string), + check('contact.keepPrivate').isBoolean().withMessage(msg.validationError.boolean), + ], + validate, + InitiativeController.create +); + +// router.get('/stats', +// InitiativeController.stats +// ); + +// ----------------------------------------------- + +module.exports = router; diff --git a/routes/utils.js b/routes/utils.js index a2a49e6..3968e3e 100644 --- a/routes/utils.js +++ b/routes/utils.js @@ -13,13 +13,22 @@ const router = express.Router(); // ----------------------------------------------- // BASE /utils // ----------------------------------------------- -// POST /utils/subdivisions +// GET /utils/subdivisions +// GET /utils/configs // ----------------------------------------------- router.get('/subdivisions', UtilsController.getSubdivisions ); +router.get('/dimensions', + UtilsController.getDimensions +); + +router.get('/configs', + UtilsController.getConfigs +); + // -----------------------------------------------