diff --git a/app/api.js b/app/api.js index d4e793cc..d6e0c6f2 100644 --- a/app/api.js +++ b/app/api.js @@ -16,6 +16,9 @@ const api = express.Router() // login api.post('/login', userController.login) +api.post('/user/recover', userController.forgotPassword) +api.post('/user/check_recover_code', userController.checkRecoverCode) +api.post('/user/recover_password', userController.updatePasswordWithRecoverCode) api.route('/user') // register @@ -51,7 +54,7 @@ api.get('/event/unconfirmed', isAuth, isAdmin, eventController.getUnconfirmed) // add event notification api.post('/event/notification', eventController.addNotification) -api.delete('/event/del_notification/:code', eventController.delNotification) +api.delete('/event/notification/:code', eventController.delNotification) api.get('/settings', settingsController.getAdminSettings) api.post('/settings', settingsController.setAdminSetting) diff --git a/app/config.js b/app/config.js index a09c9615..7fd55154 100644 --- a/app/config.js +++ b/app/config.js @@ -1,32 +1,13 @@ /* backend configuration */ -let db = {} -let apiurl - -if (process.env.NODE_ENV === 'production') { - db = { - host: process.env.DB_HOST, - username: process.env.DB_USER, - password: process.env.DB_PASS, - database: process.env.DB_NAME, - dialect: 'postgres' - } - apiurl = process.env.BASE_URL + '/api' -} else { - db = { - dialect: 'sqlite', - storage: './db.sqlite' - } - apiurl = 'http://localhost:9000' -} +const env = process.env.NODE_ENV || 'development' +const db = require('./config/config.json')[env] module.exports = { - locale: 'it', - + locale: process.env.LOCALE || 'it', title: process.env.TITLE || 'GANCIO', description: process.env.DESCRIPTION || 'A calendar for radical communities', - baseurl: process.env.BASE_URL || 'http://localhost:8080', - apiurl, + apiurl: env === 'production' ? process.env.BASE_URL + '/api' : 'http://localhost:9000', db, admin: process.env.ADMIN_EMAIL, diff --git a/app/config/config.json b/app/config/config.json new file mode 100644 index 00000000..5a68a3e1 --- /dev/null +++ b/app/config/config.json @@ -0,0 +1,21 @@ +{ + "development": { + "storage": "/home/les/dev/hacklab/eventi/db.sqlite", + "dialect": "sqlite", + "logging": false + }, + "test": { + "username": "root", + "password": null, + "database": "database_test", + "host": "127.0.0.1", + "dialect": "mysql" + }, + "production": { + "username": "root", + "password": null, + "database": "database_production", + "host": "127.0.0.1", + "dialect": "mysql" + } +} diff --git a/app/controller/event.js b/app/controller/event.js index 933118ce..66d187b1 100644 --- a/app/controller/event.js +++ b/app/controller/event.js @@ -111,9 +111,12 @@ const eventController = { async addNotification (req, res) { try { - const notification = req.body - notification.remove_code = crypto.randomBytes(16).toString('hex') - await Notification.create(req.body) + const notification = { + email: req.body.email, + type: 'mail', + remove_code: crypto.randomBytes(16).toString('hex') + } + await Notification.create(notification) res.sendStatus(200) } catch (e) { res.sendStatus(404) @@ -126,7 +129,7 @@ const eventController = { const notification = await Notification.findOne({ where: { remove_code: { [Op.eq]: remove_code } } }) await notification.destroy() } catch (e) { - return res.send('Error') + return res.status(404).send('Error') } res.send('Ok, notification removed') }, diff --git a/app/controller/export.js b/app/controller/export.js index 00ece085..3f54ecf2 100644 --- a/app/controller/export.js +++ b/app/controller/export.js @@ -21,8 +21,7 @@ const exportController = { wherePlace.name = places.split(',') } const events = await Event.findAll({ - // order: [['start_datetime', 'ASC']], - where: { start_datetime: { [Op.gte]: yesterday } }, + where: { is_visible: true, start_datetime: { [Op.gte]: yesterday } }, include: [Comment, { model: Tag, where: whereTag diff --git a/app/controller/user.js b/app/controller/user.js index 19d396e1..466dc9dd 100644 --- a/app/controller/user.js +++ b/app/controller/user.js @@ -10,6 +10,7 @@ const mail = require('../mail') const { Op } = require('sequelize') const fs = require('fs') const path = require('path') +const crypto = require('crypto') const userController = { async login (req, res) { @@ -201,6 +202,36 @@ const userController = { } }, + async forgotPassword (req, res) { + const email = req.body.email + const user = await User.findOne({ where: { email: { [Op.eq]: email } } }) + if (!user) return res.sendStatus(200) + + user.recover_code = crypto.randomBytes(16).toString('hex') + mail.send(user.email, 'recover', { user, config }) + await user.save() + res.sendStatus(200) + }, + + async checkRecoverCode (req, res) { + const recover_code = req.body.recover_code + if (!recover_code) return res.sendStatus(400) + const user = await User.findOne({ where: { recover_code: { [Op.eq]: recover_code } } }) + if (!user) return res.sendStatus(400) + res.json(user) + }, + + async updatePasswordWithRecoverCode (req, res) { + const recover_code = req.body.recover_code + if (!recover_code) return res.sendStatus(400) + const password = req.body.password + const user = await User.findOne({ where: { recover_code: { [Op.eq]: recover_code } } }) + if (!user) return res.sendStatus(400) + user.password = password + await user.save() + res.sendStatus(200) + }, + async current (req, res) { res.json(req.user) }, @@ -236,7 +267,7 @@ const userController = { } const user = await User.create(req.body) try { - mail.send(user.email, 'register', { user }) + mail.send([user.email, config.admin], 'register', { user, config }) } catch (e) { return res.status(400).json(e) } diff --git a/app/cron.js b/app/cron.js index c0d27abe..3259beff 100644 --- a/app/cron.js +++ b/app/cron.js @@ -1,6 +1,7 @@ const mail = require('./mail') const bot = require('./controller/bot') const settingsController = require('./controller/settings') +const config = require('./config.js') const { Event, Notification, EventNotification, User, Place, Tag } = require('./model') @@ -8,36 +9,28 @@ let settings async function sendNotification (notification, event, eventNotification) { const promises = [] - try { - switch (notification.type) { - case 'mail': - const p = mail.send(notification.email, 'event', { event }) - promises.push(p) - break - 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 }))) - break - case 'mastodon': - // instance publish - if (settings.mastodon_auth.instance && settings.mastodon_auth.access_token) { - const b = bot.post(settings.mastodon_auth, event) - promises.push(b) - } - // user publish - 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() - }) - promises.push(b) - } - break - } - } catch (e) { - console.log('CATCH!', e) - return false + switch (notification.type) { + case 'mail': + return mail.send(notification.email, 'event', { event, config, notification }) + case 'admin_email': + const admins = await User.findAll({ where: { is_admin: true } }) + const admin_emails = admins.map(admin => admin.email) + return mail.send(admin_emails, 'event', { event, to_confirm: true, notification }) + case 'mastodon': + // instance publish + if (settings.mastodon_auth.instance && settings.mastodon_auth.access_token) { + const b = bot.post(settings.mastodon_auth, event) + promises.push(b) + } + // user publish + 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() + }) + promises.push(b) + } + break } return Promise.all(promises) } @@ -54,8 +47,8 @@ async function loop () { await sendNotification(notification, event, e) e.status = 'sent' return e.save() - } catch (e) { - console.error(e) + } catch (err) { + console.error(err) e.status = 'error' return e.save() } diff --git a/app/db.js b/app/db.js index 3d526686..16479cdb 100644 --- a/app/db.js +++ b/app/db.js @@ -1,8 +1,4 @@ const Sequelize = require('sequelize') const conf = require('./config.js') const db = new Sequelize(conf.db) - -// db.sync({ force: true }) -// db.sync() - module.exports = db diff --git a/app/emails/event/html.pug b/app/emails/event/html.pug index 72b0151d..dfaa099f 100644 --- a/app/emails/event/html.pug +++ b/app/emails/event/html.pug @@ -13,6 +13,6 @@ br hr if to_confirm p Puoi confermare questo evento qui -//- else - p Puoi eliminare queste notifiche qui +else + p Puoi eliminare queste notifiche qui #{config.title} - #{config.description} diff --git a/app/emails/recover/html.pug b/app/emails/recover/html.pug new file mode 100644 index 00000000..fd8cb11a --- /dev/null +++ b/app/emails/recover/html.pug @@ -0,0 +1,3 @@ +p= t('recover_email') + +#{t('press here')} \ No newline at end of file diff --git a/app/emails/recover/subject.pug b/app/emails/recover/subject.pug new file mode 100644 index 00000000..6d0067a6 --- /dev/null +++ b/app/emails/recover/subject.pug @@ -0,0 +1 @@ += `[Gancio] Richiesta password recovery` diff --git a/app/emails/register/html.pug b/app/emails/register/html.pug index 87d894b7..de38b8ed 100644 --- a/app/emails/register/html.pug +++ b/app/emails/register/html.pug @@ -1,4 +1,6 @@ p= t('registration_email') -hr +--- +small #{config.title} +br small #{config.baseurl} \ No newline at end of file diff --git a/app/migrations/20190316230454-notification_status.js b/app/migrations/20190316230454-notification_status.js index 789275c5..1d689aca 100644 --- a/app/migrations/20190316230454-notification_status.js +++ b/app/migrations/20190316230454-notification_status.js @@ -1,26 +1,10 @@ -'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' }) + // 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/migrations/20190321163240-recover_code.js b/app/migrations/20190321163240-recover_code.js new file mode 100644 index 00000000..54aab425 --- /dev/null +++ b/app/migrations/20190321163240-recover_code.js @@ -0,0 +1,10 @@ +module.exports = { + up: (queryInterface, Sequelize) => { + return queryInterface.addColumn('users', 'recover_code', + { type: Sequelize.STRING }) + }, + + down: (queryInterface, Sequelize) => { + return queryInterface.removeColumn('users', 'recover_code') + } +} diff --git a/app/models/event.js b/app/models/event.js index ed894783..c0e3de2b 100644 --- a/app/models/event.js +++ b/app/models/event.js @@ -34,9 +34,6 @@ const Notification = db.define('notification', { } }) -Notification.findOrCreate({ where: { type: 'mastodon', filters: { is_visible: true } } }) -Notification.findOrCreate({ where: { type: 'admin_email', filters: { is_visible: false } } }) - const Place = db.define('place', { name: { type: Sequelize.STRING, unique: true, index: true }, address: { type: Sequelize.STRING } @@ -66,4 +63,10 @@ Event.belongsTo(Place) User.hasMany(Event) Place.hasMany(Event) +async function init () { + await Notification.findOrCreate({ where: { type: 'mastodon', filters: { is_visible: true } } }) + await Notification.findOrCreate({ where: { type: 'admin_email', filters: { is_visible: false } } }) +} + +init() module.exports = { Event, Comment, Tag, Place, Notification, EventNotification } diff --git a/app/models/user.js b/app/models/user.js index 3af9a95a..2fcfbac5 100644 --- a/app/models/user.js +++ b/app/models/user.js @@ -11,6 +11,7 @@ const User = db.define('user', { }, description: Sequelize.TEXT, password: Sequelize.STRING, + recover_code: Sequelize.STRING, is_admin: Sequelize.BOOLEAN, is_active: Sequelize.BOOLEAN, mastodon_auth: Sequelize.JSON diff --git a/app/server.js b/app/server.js index 7d75173b..a6340b1b 100644 --- a/app/server.js +++ b/app/server.js @@ -4,9 +4,12 @@ const bodyParser = require('body-parser') const api = require('./api') const cors = require('cors') const path = require('path') +const morgan = require('morgan') const config = require('./config') +const db = require('./db') const port = process.env.PORT || 9000 +app.use(morgan('dev')) app.set('views', path.join(__dirname, 'views')) app.use(bodyParser.urlencoded({ extended: false })) app.use(bodyParser.json()) @@ -19,5 +22,7 @@ app.use('/css', express.static(path.join(__dirname, '..', 'client', 'dist', 'css app.use('/js', express.static(path.join(__dirname, '..', 'client', 'dist', 'js'))) app.use('*', express.static(path.join(__dirname, '..', 'client', 'dist', 'index.html'))) -app.listen(port) -console.log(`[${config.title}] Started ${process.env.NODE_ENV} mode at ${config.baseurl} (api @ ${config.apiurl})`, config) +app.listen(port, () => { + console.log(`[${config.title}] Started ${process.env.NODE_ENV} mode at ${config.baseurl} (api @ ${config.apiurl})\n\nCONFIG`, config) + db.sync() +}) diff --git a/app/views/feed/rss.pug b/app/views/feed/rss.pug index 8a0ed94c..ae57d6f7 100644 --- a/app/views/feed/rss.pug +++ b/app/views/feed/rss.pug @@ -16,8 +16,8 @@ rss(version='2.0', xmlns:atom=' - | + | #{moment(event.start_datetime).format("dddd, D MMMM HH:mm")}
+ | |
!{event.description}
| ]]> pubDate= new Date(event.createdAt).toUTCString() diff --git a/client/src/api.js b/client/src/api.js index 18718402..de4deb0c 100644 --- a/client/src/api.js +++ b/client/src/api.js @@ -48,6 +48,11 @@ export default { login: (email, password) => post('/login', { email, password }), register: user => post('/user', user), + // password recovery + forgotPassword: email => post('/user/recover', { email }), + checkRecoverCode: recover_code => post('/user/check_recover_code', { recover_code }), + recoverPassword: (recover_code, password) => post('/user/recover_password', { recover_code, password }), + getAllEvents: (month, year) => get(`/event/${year}/${month}/`), getUnconfirmedEvents: () => get('/event/unconfirmed'), @@ -55,6 +60,7 @@ export default { unconfirmEvent: id => get(`/event/unconfirm/${id}`), addNotification: notification => post('/event/notification', notification), + delNotification: code => del(`/event/notification/${code}`), addEvent: event => post('/user/event', event), updateEvent: event => put('/user/event', event), diff --git a/client/src/assets/main.css b/client/src/assets/main.css index 9badc857..e36e9153 100644 --- a/client/src/assets/main.css +++ b/client/src/assets/main.css @@ -26,5 +26,5 @@ pre { } a, a:hover { - color: orange; + color: red; } \ No newline at end of file diff --git a/client/src/components/About.vue b/client/src/components/About.vue index 991697fc..81805c9e 100644 --- a/client/src/components/About.vue +++ b/client/src/components/About.vue @@ -12,9 +12,9 @@ Dentro gancio puoi trovare e inserire eventi. Gancio, come tutto
cisti.org è uno strumento antisessista, antirazzista, antifascista e anticapitalista, riflettici quando - stai pubblicando un evento. + pubblichi un evento. - h5 Ok, ma cosa vuol dire? + h5 Ok, ma cosa vuol dire gancio? blockquote. Se vieni a Torino e dici: "ehi, ci diamo un gancio alle 8?" nessuno si presenterà con i guantoni per fare a mazzate. Darsi un gancio vuol dire beccarsi alle ore X in un posto Y diff --git a/client/src/components/DelNotification.vue b/client/src/components/DelNotification.vue new file mode 100644 index 00000000..4869df83 --- /dev/null +++ b/client/src/components/DelNotification.vue @@ -0,0 +1,39 @@ + + diff --git a/client/src/components/Event.vue b/client/src/components/Event.vue index 482bef36..035a5eb7 100644 --- a/client/src/components/Event.vue +++ b/client/src/components/Event.vue @@ -14,8 +14,6 @@ import { mapState, mapActions } from 'vuex'; import api from '@/api' import filters from '@/filters' -console.log(process.env) - export default { props: ['event'], computed: { diff --git a/client/src/components/Export.vue b/client/src/components/Export.vue index f5f86628..20b9391c 100644 --- a/client/src/components/Export.vue +++ b/client/src/components/Export.vue @@ -11,12 +11,12 @@ el-tab-pane.pt-1(label='email' name='email') p(v-html='$t(`export_email_explanation`)') - b-form + el-form(@submit.native.prevent='add_notification') //- el-switch(v-model='notification.notify_on_add' :active-text="$t('notify_on_insert')") //- br //- el-switch.mt-2(v-model='notification.send_notification' :active-text="$t('send_notification')") - el-input.mt-2(v-model='notification.email' :placeholder="$t('Insert your address')") - el-button.mt-2.float-right(type='success' @click='add_notification') {{$t('Send')}} + el-input.mt-2(v-model='notification.email' :placeholder="$t('Insert your address')" ref='email') + el-button.mt-2.float-right(native-type= 'submit' type='success' @click='add_notification') {{$t('Send')}} el-tab-pane.pt-1(label='feed rss' name='feed') span(v-html='$t(`export_feed_explanation`)') @@ -59,6 +59,7 @@ import filters from '../filters' import Calendar from '@/components/Calendar' import {intersection} from 'lodash' import api from '@/api' +import { Message } from 'element-ui' export default { name: 'Export', @@ -83,8 +84,13 @@ export default { }, methods: { async add_notification () { + if (!this.notification.email){ + Message({message:'Inserisci una mail', type: 'error'}) + return this.$refs.email.focus() + } await api.addNotification({ ...this.notification, filters: this.filters}) this.$refs.modal.hide() + Message({message: this.$t('email_notification_activated'), type: 'success'}) }, loadLink () { const tags = this.filters.tags.join(',') diff --git a/client/src/components/Login.vue b/client/src/components/Login.vue index 4dbfd2a0..5aca03f7 100644 --- a/client/src/components/Login.vue +++ b/client/src/components/Login.vue @@ -1,15 +1,16 @@ diff --git a/client/src/components/newEvent.vue b/client/src/components/newEvent.vue index b06d497d..f626b83f 100644 --- a/client/src/components/newEvent.vue +++ b/client/src/components/newEvent.vue @@ -1,8 +1,8 @@