fix #17 fix #36 remove notification, rss fix&more

This commit is contained in:
lesion
2019-03-21 22:20:30 +01:00
parent ba0004a89f
commit 58f0d8c9fe
30 changed files with 302 additions and 123 deletions

View File

@@ -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)

View File

@@ -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,

21
app/config/config.json Normal file
View File

@@ -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"
}
}

View File

@@ -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')
},

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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()
}

View File

@@ -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

View File

@@ -13,6 +13,6 @@ br
hr
if to_confirm
p Puoi confermare questo evento <a href="#{config.baseurl}/admin/confirm/#{event.id}">qui</a>
//- else
p Puoi eliminare queste notifiche <a href="#{confir.apiurl}/api/del_notification/#{notification.remove_code}">qui</a>
else
p Puoi eliminare queste notifiche <a href="#{config.baseurl}/del_notification/#{notification.remove_code}">qui</a>
<a href="#{config.baseurl}">#{config.title} - #{config.description}</a>

View File

@@ -0,0 +1,3 @@
p= t('recover_email')
<a href="#{config.baseurl}/recover/#{user.recover_code}">#{t('press here')}</a>

View File

@@ -0,0 +1 @@
= `[Gancio] Richiesta password recovery`

View File

@@ -1,4 +1,6 @@
p= t('registration_email')
hr
---
small #{config.title}
br
small #{config.baseurl}

View File

@@ -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')
}
};
}

View File

@@ -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')
}
}

View File

@@ -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 }

View File

@@ -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

View File

@@ -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()
})

View File

@@ -16,8 +16,8 @@ rss(version='2.0', xmlns:atom='<a href="http://www.w3.org/2005/Atom" rel="nofoll
| <![CDATA[
| <h4>#{event.title}</h4>
| <strong>#{event.place.name} - #{event.place.address}</strong>
| #{moment(event.start_datetime).format("ddd, D MMMM HH:mm")}<br/>
| <img src="#{config.apiurl}/#{event.image_path}"/>
| #{moment(event.start_datetime).format("dddd, D MMMM HH:mm")}<br/>
| <img src="#{config.apiurl}/uploads/#{event.image_path}"/>
| <pre>!{event.description}</pre>
| ]]>
pubDate= new Date(event.createdAt).toUTCString()