@@ -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)
|
||||
|
||||
@@ -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
21
app/config/config.json
Normal 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"
|
||||
}
|
||||
}
|
||||
@@ -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')
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
57
app/cron.js
57
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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
3
app/emails/recover/html.pug
Normal file
3
app/emails/recover/html.pug
Normal file
@@ -0,0 +1,3 @@
|
||||
p= t('recover_email')
|
||||
|
||||
<a href="#{config.baseurl}/recover/#{user.recover_code}">#{t('press here')}</a>
|
||||
1
app/emails/recover/subject.pug
Normal file
1
app/emails/recover/subject.pug
Normal file
@@ -0,0 +1 @@
|
||||
= `[Gancio] Richiesta password recovery`
|
||||
@@ -1,4 +1,6 @@
|
||||
p= t('registration_email')
|
||||
|
||||
hr
|
||||
---
|
||||
small #{config.title}
|
||||
br
|
||||
small #{config.baseurl}
|
||||
@@ -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')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
10
app/migrations/20190321163240-recover_code.js
Normal file
10
app/migrations/20190321163240-recover_code.js
Normal 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')
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user