From 7111b4578b109cf627954382e569dd4a5e3a9120 Mon Sep 17 00:00:00 2001 From: lesion Date: Mon, 18 Mar 2019 01:42:42 +0100 Subject: [PATCH] fix #27, fix #25, fix #26, fix #20, fix #19 --- app/api.js | 1 + app/controller/event.js | 12 ++++ app/controller/user.js | 5 +- app/cron.js | 19 ++++-- app/emails/confirm/html.pug | 4 ++ app/emails/register/html.pug | 6 +- app/locales/en.json | 3 - app/locales/es.json | 3 - app/locales/it.json | 3 - app/locales/zh.json | 3 - .../20190316230454-notification_status.js | 26 +++++++++ app/models/event.js | 10 +++- app/models/index.js | 37 ++++++++++++ client/.eslintrc.js | 5 +- client/src/api.js | 12 +++- client/src/components/About.vue | 25 ++++++++ client/src/components/Admin.vue | 28 ++++++--- client/src/components/EventDetail.vue | 16 +++++ client/src/components/Login.vue | 19 ++++-- client/src/components/Nav.vue | 6 +- client/src/components/Register.vue | 6 +- client/src/components/Settings.vue | 2 +- client/src/components/newEvent.vue | 58 +++++++++++-------- client/src/locale/it.js | 2 + client/src/main.js | 5 +- client/src/router.js | 5 ++ client/src/store.js | 3 +- locales/en.json | 4 ++ locales/es.json | 4 ++ locales/it.json | 4 ++ locales/zh.json | 4 ++ 31 files changed, 270 insertions(+), 70 deletions(-) create mode 100644 app/emails/confirm/html.pug delete mode 100644 app/locales/en.json delete mode 100644 app/locales/es.json delete mode 100644 app/locales/it.json delete mode 100644 app/locales/zh.json create mode 100644 app/migrations/20190316230454-notification_status.js create mode 100644 app/models/index.js create mode 100644 client/src/components/About.vue create mode 100644 locales/en.json create mode 100644 locales/es.json create mode 100644 locales/it.json create mode 100644 locales/zh.json diff --git a/app/api.js b/app/api.js index 261f7052..d4e793cc 100644 --- a/app/api.js +++ b/app/api.js @@ -61,6 +61,7 @@ api.get('/event/:event_id', eventController.get) // confirm event api.get('/event/confirm/:event_id', isAuth, isAdmin, eventController.confirm) +api.get('/event/unconfirm/:event_id', isAuth, isAdmin, eventController.unconfirm) // export events (rss/ics) api.get('/export/:type', exportController.export) diff --git a/app/controller/event.js b/app/controller/event.js index bb9f8d57..d7d9f2a4 100644 --- a/app/controller/event.js +++ b/app/controller/event.js @@ -82,6 +82,18 @@ const eventController = { } }, + async unconfirm (req, res) { + const id = req.params.event_id + const event = await Event.findByPk(id) + + try { + await event.update({ is_visible: false }) + res.send(200) + } catch (e) { + res.send(404) + } + }, + async getUnconfirmed (req, res) { const events = await Event.findAll({ where: { diff --git a/app/controller/user.js b/app/controller/user.js index bbda197f..ca4cc343 100644 --- a/app/controller/user.js +++ b/app/controller/user.js @@ -198,6 +198,9 @@ const userController = { async update (req, res) { const user = await User.findByPk(req.body.id) if (user) { + if (!user.is_active && req.body.is_active) { + await mail.send(user.email, 'confirm', { user, config }) + } await user.update(req.body) res.json(user) } else { @@ -209,7 +212,7 @@ const userController = { const n_users = await User.count() try { if (n_users === 0) { - // admin will be the first registered user + // the first registered user will be an active admin req.body.is_active = req.body.is_admin = true } else { req.body.is_active = false diff --git a/app/cron.js b/app/cron.js index ff3f4dd0..77656bbc 100644 --- a/app/cron.js +++ b/app/cron.js @@ -14,7 +14,7 @@ async function sendNotification (notification, event, eventNotification) { const p = mail.send(notification.email, 'event', { event }) promises.push(p) break - case 'mail_admin': + case 'admin_email': const admins = await User.findAll({ where: { is_admin: true } }) promises.push(admins.map(admin => mail.send(admin.email, 'event', { event, to_confirm: true, notification }))) @@ -26,7 +26,7 @@ async function sendNotification (notification, event, eventNotification) { promises.push(b) } // user publish - if (event.user && event.user.mastodon_auth) { + if (event.user && event.user.mastodon_auth && event.user.mastodon_auth.access_token) { const b = bot.post(event.user.mastodon_auth, event).then(ret => { event.activitypub_id = ret.id return event.save() @@ -45,17 +45,24 @@ async function sendNotification (notification, event, eventNotification) { async function loop () { settings = await settingsController.settings() // get all event notification in queue - const eventNotifications = await EventNotification.findAll() + const eventNotifications = await EventNotification.findAll({ where: { status: 'new' } }) const promises = eventNotifications.map(async e => { const event = await Event.findByPk(e.eventId, { include: [User, Place, Tag] }) if (!event.place) return const notification = await Notification.findByPk(e.notificationId) - await sendNotification(notification, event, e) - e.destroy() + try { + await sendNotification(notification, event, e) + e.status = 'sent' + e.save() + } catch (e) { + console.error(e) + e.status = 'error' + return e.save() + } }) return Promise.all(promises) } -setInterval(loop, 20000) +setInterval(loop, 260000) loop() diff --git a/app/emails/confirm/html.pug b/app/emails/confirm/html.pug new file mode 100644 index 00000000..e0f26aa1 --- /dev/null +++ b/app/emails/confirm/html.pug @@ -0,0 +1,4 @@ +p= t('confirm_email') + +hr +small #{config.baseurl} diff --git a/app/emails/register/html.pug b/app/emails/register/html.pug index fca72152..87d894b7 100644 --- a/app/emails/register/html.pug +++ b/app/emails/register/html.pug @@ -1,6 +1,4 @@ -h4 Gancio - p= t('registration_email') -small -- -small https://cisti.org \ No newline at end of file +hr +small #{config.baseurl} \ No newline at end of file diff --git a/app/locales/en.json b/app/locales/en.json deleted file mode 100644 index f31adcfc..00000000 --- a/app/locales/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "registration_email": "Ciao, la tua registrazione sarà confermata nei prossimi giorni. Riceverai una conferma non temere." -} diff --git a/app/locales/es.json b/app/locales/es.json deleted file mode 100644 index 9cd985b3..00000000 --- a/app/locales/es.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "registration_email": "registration_email" -} diff --git a/app/locales/it.json b/app/locales/it.json deleted file mode 100644 index f31adcfc..00000000 --- a/app/locales/it.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "registration_email": "Ciao, la tua registrazione sarà confermata nei prossimi giorni. Riceverai una conferma non temere." -} diff --git a/app/locales/zh.json b/app/locales/zh.json deleted file mode 100644 index f01ae3b9..00000000 --- a/app/locales/zh.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "registration_email": "registration_email" -} \ No newline at end of file diff --git a/app/migrations/20190316230454-notification_status.js b/app/migrations/20190316230454-notification_status.js new file mode 100644 index 00000000..789275c5 --- /dev/null +++ b/app/migrations/20190316230454-notification_status.js @@ -0,0 +1,26 @@ +'use strict'; + +module.exports = { + up: (queryInterface, Sequelize) => { + /* + Add altering commands here. + Return a promise to correctly handle asynchronicity. + + Example: + return queryInterface.createTable('users', { id: Sequelize.INTEGER }); + */ + return queryInterface.addColumn('EventNotifications', 'status', + { type: Sequelize.ENUM, values: ['new', 'sent', 'error'], index: true, defaultValue: 'new' }) + }, + + down: (queryInterface, Sequelize) => { + /* + Add reverting commands here. + Return a promise to correctly handle asynchronicity. + + Example: + return queryInterface.dropTable('users'); + */ + return queryInterface.removeColumn('EventNotifications', 'status') + } +}; diff --git a/app/models/event.js b/app/models/event.js index 6646e4c3..80ea88d8 100644 --- a/app/models/event.js +++ b/app/models/event.js @@ -48,7 +48,15 @@ Event.hasMany(Comment) Event.belongsToMany(Tag, { through: 'tagEvent' }) Tag.belongsToMany(Event, { through: 'tagEvent' }) -const EventNotification = db.define('EventNotification') +const EventNotification = db.define('EventNotification', { + status: { + type: Sequelize.ENUM, + values: ['new', 'sent', 'error'], + defaultValue: 'new', + index: true + } +}) + Event.belongsToMany(Notification, { through: EventNotification }) Notification.belongsToMany(Event, { through: EventNotification }) diff --git a/app/models/index.js b/app/models/index.js new file mode 100644 index 00000000..c1a3d6d5 --- /dev/null +++ b/app/models/index.js @@ -0,0 +1,37 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const Sequelize = require('sequelize'); +const basename = path.basename(__filename); +const env = process.env.NODE_ENV || 'development'; +const config = require(__dirname + '/../config/config.json')[env]; +const db = {}; + +let sequelize; +if (config.use_env_variable) { + sequelize = new Sequelize(process.env[config.use_env_variable], config); +} else { + sequelize = new Sequelize(config.database, config.username, config.password, config); +} + +fs + .readdirSync(__dirname) + .filter(file => { + return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'); + }) + .forEach(file => { + const model = sequelize['import'](path.join(__dirname, file)); + db[model.name] = model; + }); + +Object.keys(db).forEach(modelName => { + if (db[modelName].associate) { + db[modelName].associate(db); + } +}); + +db.sequelize = sequelize; +db.Sequelize = Sequelize; + +module.exports = db; diff --git a/client/.eslintrc.js b/client/.eslintrc.js index 2f16b25f..d410c471 100644 --- a/client/.eslintrc.js +++ b/client/.eslintrc.js @@ -1,3 +1,6 @@ module.exports = { - 'extends': 'standard' + 'extends': 'standard', + "rules": { + "camelcase": 0 + } } diff --git a/client/src/api.js b/client/src/api.js index d2908963..18718402 100644 --- a/client/src/api.js +++ b/client/src/api.js @@ -18,6 +18,8 @@ function get (path) { store.commit('logout') return false } + throw e.response && e.response.data && + e.response.data.errors && e.response.data.errors[0].message }) } @@ -29,6 +31,8 @@ function post (path, data) { store.commit('logout') return false } + throw e.response && e.response.data && + e.response.data.errors && e.response.data.errors[0].message }) } function put (path, data) { @@ -43,12 +47,18 @@ function del (path) { export default { login: (email, password) => post('/login', { email, password }), register: user => post('/user', user), + getAllEvents: (month, year) => get(`/event/${year}/${month}/`), getUnconfirmedEvents: () => get('/event/unconfirmed'), + confirmEvent: id => get(`/event/confirm/${id}`), + unconfirmEvent: id => get(`/event/unconfirm/${id}`), + addNotification: notification => post('/event/notification', notification), + addEvent: event => post('/user/event', event), updateEvent: event => put('/user/event', event), + updatePlace: place => put('/place', place), delEvent: eventId => del(`/user/event/${eventId}`), getEvent: eventId => get(`/event/${eventId}`), @@ -59,8 +69,6 @@ export default { updateUser: user => put('/user', user), getAuthURL: mastodonInstance => post('/user/getauthurl', mastodonInstance), setCode: code => post('/user/code', code), - getKnowLocations: () => get('/locations'), - getKnowTags: () => get('/tags'), getAdminSettings: () => get('/settings') // setAdminSetting: (key, value) => post('/settings', { key, value }) } diff --git a/client/src/components/About.vue b/client/src/components/About.vue new file mode 100644 index 00000000..e5b5bdde --- /dev/null +++ b/client/src/components/About.vue @@ -0,0 +1,25 @@ + \ No newline at end of file diff --git a/client/src/components/Admin.vue b/client/src/components/Admin.vue index 75c7596b..0acc1f1e 100644 --- a/client/src/components/Admin.vue +++ b/client/src/components/Admin.vue @@ -1,16 +1,26 @@