@@ -16,6 +16,9 @@ const api = express.Router()
|
|||||||
|
|
||||||
// login
|
// login
|
||||||
api.post('/login', userController.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')
|
api.route('/user')
|
||||||
// register
|
// register
|
||||||
@@ -51,7 +54,7 @@ api.get('/event/unconfirmed', isAuth, isAdmin, eventController.getUnconfirmed)
|
|||||||
|
|
||||||
// add event notification
|
// add event notification
|
||||||
api.post('/event/notification', eventController.addNotification)
|
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.get('/settings', settingsController.getAdminSettings)
|
||||||
api.post('/settings', settingsController.setAdminSetting)
|
api.post('/settings', settingsController.setAdminSetting)
|
||||||
|
|||||||
@@ -1,32 +1,13 @@
|
|||||||
/* backend configuration */
|
/* backend configuration */
|
||||||
let db = {}
|
const env = process.env.NODE_ENV || 'development'
|
||||||
let apiurl
|
const db = require('./config/config.json')[env]
|
||||||
|
|
||||||
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'
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
locale: 'it',
|
locale: process.env.LOCALE || 'it',
|
||||||
|
|
||||||
title: process.env.TITLE || 'GANCIO',
|
title: process.env.TITLE || 'GANCIO',
|
||||||
description: process.env.DESCRIPTION || 'A calendar for radical communities',
|
description: process.env.DESCRIPTION || 'A calendar for radical communities',
|
||||||
|
|
||||||
baseurl: process.env.BASE_URL || 'http://localhost:8080',
|
baseurl: process.env.BASE_URL || 'http://localhost:8080',
|
||||||
apiurl,
|
apiurl: env === 'production' ? process.env.BASE_URL + '/api' : 'http://localhost:9000',
|
||||||
db,
|
db,
|
||||||
admin: process.env.ADMIN_EMAIL,
|
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) {
|
async addNotification (req, res) {
|
||||||
try {
|
try {
|
||||||
const notification = req.body
|
const notification = {
|
||||||
notification.remove_code = crypto.randomBytes(16).toString('hex')
|
email: req.body.email,
|
||||||
await Notification.create(req.body)
|
type: 'mail',
|
||||||
|
remove_code: crypto.randomBytes(16).toString('hex')
|
||||||
|
}
|
||||||
|
await Notification.create(notification)
|
||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.sendStatus(404)
|
res.sendStatus(404)
|
||||||
@@ -126,7 +129,7 @@ const eventController = {
|
|||||||
const notification = await Notification.findOne({ where: { remove_code: { [Op.eq]: remove_code } } })
|
const notification = await Notification.findOne({ where: { remove_code: { [Op.eq]: remove_code } } })
|
||||||
await notification.destroy()
|
await notification.destroy()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return res.send('Error')
|
return res.status(404).send('Error')
|
||||||
}
|
}
|
||||||
res.send('Ok, notification removed')
|
res.send('Ok, notification removed')
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ const exportController = {
|
|||||||
wherePlace.name = places.split(',')
|
wherePlace.name = places.split(',')
|
||||||
}
|
}
|
||||||
const events = await Event.findAll({
|
const events = await Event.findAll({
|
||||||
// order: [['start_datetime', 'ASC']],
|
where: { is_visible: true, start_datetime: { [Op.gte]: yesterday } },
|
||||||
where: { start_datetime: { [Op.gte]: yesterday } },
|
|
||||||
include: [Comment, {
|
include: [Comment, {
|
||||||
model: Tag,
|
model: Tag,
|
||||||
where: whereTag
|
where: whereTag
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const mail = require('../mail')
|
|||||||
const { Op } = require('sequelize')
|
const { Op } = require('sequelize')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const crypto = require('crypto')
|
||||||
|
|
||||||
const userController = {
|
const userController = {
|
||||||
async login (req, res) {
|
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) {
|
async current (req, res) {
|
||||||
res.json(req.user)
|
res.json(req.user)
|
||||||
},
|
},
|
||||||
@@ -236,7 +267,7 @@ const userController = {
|
|||||||
}
|
}
|
||||||
const user = await User.create(req.body)
|
const user = await User.create(req.body)
|
||||||
try {
|
try {
|
||||||
mail.send(user.email, 'register', { user })
|
mail.send([user.email, config.admin], 'register', { user, config })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return res.status(400).json(e)
|
return res.status(400).json(e)
|
||||||
}
|
}
|
||||||
|
|||||||
57
app/cron.js
57
app/cron.js
@@ -1,6 +1,7 @@
|
|||||||
const mail = require('./mail')
|
const mail = require('./mail')
|
||||||
const bot = require('./controller/bot')
|
const bot = require('./controller/bot')
|
||||||
const settingsController = require('./controller/settings')
|
const settingsController = require('./controller/settings')
|
||||||
|
const config = require('./config.js')
|
||||||
|
|
||||||
const { Event, Notification, EventNotification,
|
const { Event, Notification, EventNotification,
|
||||||
User, Place, Tag } = require('./model')
|
User, Place, Tag } = require('./model')
|
||||||
@@ -8,36 +9,28 @@ let settings
|
|||||||
|
|
||||||
async function sendNotification (notification, event, eventNotification) {
|
async function sendNotification (notification, event, eventNotification) {
|
||||||
const promises = []
|
const promises = []
|
||||||
try {
|
switch (notification.type) {
|
||||||
switch (notification.type) {
|
case 'mail':
|
||||||
case 'mail':
|
return mail.send(notification.email, 'event', { event, config, notification })
|
||||||
const p = mail.send(notification.email, 'event', { event })
|
case 'admin_email':
|
||||||
promises.push(p)
|
const admins = await User.findAll({ where: { is_admin: true } })
|
||||||
break
|
const admin_emails = admins.map(admin => admin.email)
|
||||||
case 'admin_email':
|
return mail.send(admin_emails, 'event', { event, to_confirm: true, notification })
|
||||||
const admins = await User.findAll({ where: { is_admin: true } })
|
case 'mastodon':
|
||||||
promises.push(admins.map(admin =>
|
// instance publish
|
||||||
mail.send(admin.email, 'event', { event, to_confirm: true, notification })))
|
if (settings.mastodon_auth.instance && settings.mastodon_auth.access_token) {
|
||||||
break
|
const b = bot.post(settings.mastodon_auth, event)
|
||||||
case 'mastodon':
|
promises.push(b)
|
||||||
// instance publish
|
}
|
||||||
if (settings.mastodon_auth.instance && settings.mastodon_auth.access_token) {
|
// user publish
|
||||||
const b = bot.post(settings.mastodon_auth, event)
|
if (event.user && event.user.mastodon_auth && event.user.mastodon_auth.access_token) {
|
||||||
promises.push(b)
|
const b = bot.post(event.user.mastodon_auth, event).then(ret => {
|
||||||
}
|
event.activitypub_id = ret.id
|
||||||
// user publish
|
return event.save()
|
||||||
if (event.user && event.user.mastodon_auth && event.user.mastodon_auth.access_token) {
|
})
|
||||||
const b = bot.post(event.user.mastodon_auth, event).then(ret => {
|
promises.push(b)
|
||||||
event.activitypub_id = ret.id
|
}
|
||||||
return event.save()
|
break
|
||||||
})
|
|
||||||
promises.push(b)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log('CATCH!', e)
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
}
|
}
|
||||||
@@ -54,8 +47,8 @@ async function loop () {
|
|||||||
await sendNotification(notification, event, e)
|
await sendNotification(notification, event, e)
|
||||||
e.status = 'sent'
|
e.status = 'sent'
|
||||||
return e.save()
|
return e.save()
|
||||||
} catch (e) {
|
} catch (err) {
|
||||||
console.error(e)
|
console.error(err)
|
||||||
e.status = 'error'
|
e.status = 'error'
|
||||||
return e.save()
|
return e.save()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
const Sequelize = require('sequelize')
|
const Sequelize = require('sequelize')
|
||||||
const conf = require('./config.js')
|
const conf = require('./config.js')
|
||||||
const db = new Sequelize(conf.db)
|
const db = new Sequelize(conf.db)
|
||||||
|
|
||||||
// db.sync({ force: true })
|
|
||||||
// db.sync()
|
|
||||||
|
|
||||||
module.exports = db
|
module.exports = db
|
||||||
|
|||||||
@@ -13,6 +13,6 @@ br
|
|||||||
hr
|
hr
|
||||||
if to_confirm
|
if to_confirm
|
||||||
p Puoi confermare questo evento <a href="#{config.baseurl}/admin/confirm/#{event.id}">qui</a>
|
p Puoi confermare questo evento <a href="#{config.baseurl}/admin/confirm/#{event.id}">qui</a>
|
||||||
//- else
|
else
|
||||||
p Puoi eliminare queste notifiche <a href="#{confir.apiurl}/api/del_notification/#{notification.remove_code}">qui</a>
|
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>
|
<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')
|
p= t('registration_email')
|
||||||
|
|
||||||
hr
|
---
|
||||||
|
small #{config.title}
|
||||||
|
br
|
||||||
small #{config.baseurl}
|
small #{config.baseurl}
|
||||||
@@ -1,26 +1,10 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
up: (queryInterface, Sequelize) => {
|
up: (queryInterface, Sequelize) => {
|
||||||
/*
|
// return queryInterface.addColumn('EventNotifications', 'status',
|
||||||
Add altering commands here.
|
// { type: Sequelize.ENUM, values: ['new', 'sent', 'error'], index: true, defaultValue: 'new' })
|
||||||
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) => {
|
down: (queryInterface, Sequelize) => {
|
||||||
/*
|
|
||||||
Add reverting commands here.
|
|
||||||
Return a promise to correctly handle asynchronicity.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
return queryInterface.dropTable('users');
|
|
||||||
*/
|
|
||||||
return queryInterface.removeColumn('EventNotifications', 'status')
|
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', {
|
const Place = db.define('place', {
|
||||||
name: { type: Sequelize.STRING, unique: true, index: true },
|
name: { type: Sequelize.STRING, unique: true, index: true },
|
||||||
address: { type: Sequelize.STRING }
|
address: { type: Sequelize.STRING }
|
||||||
@@ -66,4 +63,10 @@ Event.belongsTo(Place)
|
|||||||
User.hasMany(Event)
|
User.hasMany(Event)
|
||||||
Place.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 }
|
module.exports = { Event, Comment, Tag, Place, Notification, EventNotification }
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const User = db.define('user', {
|
|||||||
},
|
},
|
||||||
description: Sequelize.TEXT,
|
description: Sequelize.TEXT,
|
||||||
password: Sequelize.STRING,
|
password: Sequelize.STRING,
|
||||||
|
recover_code: Sequelize.STRING,
|
||||||
is_admin: Sequelize.BOOLEAN,
|
is_admin: Sequelize.BOOLEAN,
|
||||||
is_active: Sequelize.BOOLEAN,
|
is_active: Sequelize.BOOLEAN,
|
||||||
mastodon_auth: Sequelize.JSON
|
mastodon_auth: Sequelize.JSON
|
||||||
|
|||||||
@@ -4,9 +4,12 @@ const bodyParser = require('body-parser')
|
|||||||
const api = require('./api')
|
const api = require('./api')
|
||||||
const cors = require('cors')
|
const cors = require('cors')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const morgan = require('morgan')
|
||||||
const config = require('./config')
|
const config = require('./config')
|
||||||
|
const db = require('./db')
|
||||||
const port = process.env.PORT || 9000
|
const port = process.env.PORT || 9000
|
||||||
|
|
||||||
|
app.use(morgan('dev'))
|
||||||
app.set('views', path.join(__dirname, 'views'))
|
app.set('views', path.join(__dirname, 'views'))
|
||||||
app.use(bodyParser.urlencoded({ extended: false }))
|
app.use(bodyParser.urlencoded({ extended: false }))
|
||||||
app.use(bodyParser.json())
|
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('/js', express.static(path.join(__dirname, '..', 'client', 'dist', 'js')))
|
||||||
app.use('*', express.static(path.join(__dirname, '..', 'client', 'dist', 'index.html')))
|
app.use('*', express.static(path.join(__dirname, '..', 'client', 'dist', 'index.html')))
|
||||||
|
|
||||||
app.listen(port)
|
app.listen(port, () => {
|
||||||
console.log(`[${config.title}] Started ${process.env.NODE_ENV} mode at ${config.baseurl} (api @ ${config.apiurl})`, config)
|
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[
|
| <![CDATA[
|
||||||
| <h4>#{event.title}</h4>
|
| <h4>#{event.title}</h4>
|
||||||
| <strong>#{event.place.name} - #{event.place.address}</strong>
|
| <strong>#{event.place.name} - #{event.place.address}</strong>
|
||||||
| #{moment(event.start_datetime).format("ddd, D MMMM HH:mm")}<br/>
|
| #{moment(event.start_datetime).format("dddd, D MMMM HH:mm")}<br/>
|
||||||
| <img src="#{config.apiurl}/#{event.image_path}"/>
|
| <img src="#{config.apiurl}/uploads/#{event.image_path}"/>
|
||||||
| <pre>!{event.description}</pre>
|
| <pre>!{event.description}</pre>
|
||||||
| ]]>
|
| ]]>
|
||||||
pubDate= new Date(event.createdAt).toUTCString()
|
pubDate= new Date(event.createdAt).toUTCString()
|
||||||
|
|||||||
@@ -48,6 +48,11 @@ export default {
|
|||||||
login: (email, password) => post('/login', { email, password }),
|
login: (email, password) => post('/login', { email, password }),
|
||||||
register: user => post('/user', user),
|
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}/`),
|
getAllEvents: (month, year) => get(`/event/${year}/${month}/`),
|
||||||
getUnconfirmedEvents: () => get('/event/unconfirmed'),
|
getUnconfirmedEvents: () => get('/event/unconfirmed'),
|
||||||
|
|
||||||
@@ -55,6 +60,7 @@ export default {
|
|||||||
unconfirmEvent: id => get(`/event/unconfirm/${id}`),
|
unconfirmEvent: id => get(`/event/unconfirm/${id}`),
|
||||||
|
|
||||||
addNotification: notification => post('/event/notification', notification),
|
addNotification: notification => post('/event/notification', notification),
|
||||||
|
delNotification: code => del(`/event/notification/${code}`),
|
||||||
|
|
||||||
addEvent: event => post('/user/event', event),
|
addEvent: event => post('/user/event', event),
|
||||||
updateEvent: event => put('/user/event', event),
|
updateEvent: event => put('/user/event', event),
|
||||||
|
|||||||
@@ -26,5 +26,5 @@ pre {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a, a:hover {
|
a, a:hover {
|
||||||
color: orange;
|
color: red;
|
||||||
}
|
}
|
||||||
@@ -12,9 +12,9 @@
|
|||||||
Dentro gancio puoi trovare e inserire eventi.
|
Dentro gancio puoi trovare e inserire eventi.
|
||||||
Gancio, come tutto <a href='https://cisti.org'>cisti.org</a> è uno strumento
|
Gancio, come tutto <a href='https://cisti.org'>cisti.org</a> è uno strumento
|
||||||
antisessista, antirazzista, antifascista e anticapitalista, riflettici quando
|
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.
|
blockquote.
|
||||||
Se vieni a Torino e dici: "ehi, ci diamo un gancio alle 8?" nessuno si presenterà con i guantoni per fare a mazzate.
|
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
|
Darsi un gancio vuol dire beccarsi alle ore X in un posto Y
|
||||||
|
|||||||
39
client/src/components/DelNotification.vue
Normal file
39
client/src/components/DelNotification.vue
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
b-modal(hide-footer @hidden='$router.replace("/")' :title='$t("Remove notification")'
|
||||||
|
:visible='true' size='sm' ref='modal')
|
||||||
|
div( v-loading='loading')
|
||||||
|
p Vuoi eliminare le notifiche?
|
||||||
|
el-button.float-right(type='success' @click='remove') {{$t('Yes')}}
|
||||||
|
el-button.float-right.mr-1(type='danger' @click='$refs.modal.hide()') {{$t('No')}}
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import api from '@/api'
|
||||||
|
import { Message } from 'element-ui';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DelNotification',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
code: '',
|
||||||
|
loading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.code = this.$route.params.code
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async remove () {
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
await api.delNotification(this.code)
|
||||||
|
Message({message: this.$t('Email notification removed'), type: 'success'})
|
||||||
|
} catch(e) {
|
||||||
|
Message({message: this.$t('Error removing email notification'), type: 'error'})
|
||||||
|
}
|
||||||
|
this.$refs.modal.hide()
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -14,8 +14,6 @@ import { mapState, mapActions } from 'vuex';
|
|||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
import filters from '@/filters'
|
import filters from '@/filters'
|
||||||
|
|
||||||
console.log(process.env)
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['event'],
|
props: ['event'],
|
||||||
computed: {
|
computed: {
|
||||||
|
|||||||
@@ -11,12 +11,12 @@
|
|||||||
|
|
||||||
el-tab-pane.pt-1(label='email' name='email')
|
el-tab-pane.pt-1(label='email' name='email')
|
||||||
p(v-html='$t(`export_email_explanation`)')
|
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')")
|
//- el-switch(v-model='notification.notify_on_add' :active-text="$t('notify_on_insert')")
|
||||||
//- br
|
//- br
|
||||||
//- el-switch.mt-2(v-model='notification.send_notification' :active-text="$t('send_notification')")
|
//- 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-input.mt-2(v-model='notification.email' :placeholder="$t('Insert your address')" ref='email')
|
||||||
el-button.mt-2.float-right(type='success' @click='add_notification') {{$t('Send')}}
|
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')
|
el-tab-pane.pt-1(label='feed rss' name='feed')
|
||||||
span(v-html='$t(`export_feed_explanation`)')
|
span(v-html='$t(`export_feed_explanation`)')
|
||||||
@@ -59,6 +59,7 @@ import filters from '../filters'
|
|||||||
import Calendar from '@/components/Calendar'
|
import Calendar from '@/components/Calendar'
|
||||||
import {intersection} from 'lodash'
|
import {intersection} from 'lodash'
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
|
import { Message } from 'element-ui'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Export',
|
name: 'Export',
|
||||||
@@ -83,8 +84,13 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async add_notification () {
|
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})
|
await api.addNotification({ ...this.notification, filters: this.filters})
|
||||||
this.$refs.modal.hide()
|
this.$refs.modal.hide()
|
||||||
|
Message({message: this.$t('email_notification_activated'), type: 'success'})
|
||||||
},
|
},
|
||||||
loadLink () {
|
loadLink () {
|
||||||
const tags = this.filters.tags.join(',')
|
const tags = this.filters.tags.join(',')
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
<template lang='pug'>
|
<template lang='pug'>
|
||||||
b-modal(@shown="$refs.email.focus()" :title='$t("Login")' hide-footer
|
b-modal(@shown="$refs.email.focus()" :title='$t("Login")' hide-footer
|
||||||
@hidden='$router.replace("/")' :visible='true' ref='modal')
|
@hidden='$router.replace("/")' :visible='true' ref='modal')
|
||||||
el-form
|
el-form(v-loading='loading')
|
||||||
p {{$t('login_explanation')}}
|
p(v-html="$t('login_explanation')")
|
||||||
el-input.mb-2(v-model='email' type='email' :placeholder='$t("Email")' autocomplete='email' ref='email')
|
el-input.mb-2(v-model='email' type='email' :placeholder='$t("Email")' autocomplete='email' ref='email')
|
||||||
v-icon(name='user' slot='prepend')
|
v-icon(name='user' slot='prepend')
|
||||||
el-input.mb-2(v-model='password' type='password' :placeholder='$t("Password")')
|
el-input.mb-1(v-model='password' @keyup.enter.native="submit" type='password' :placeholder='$t("Password")')
|
||||||
v-icon(name="lock" slot='prepend')
|
v-icon(name="lock" slot='prepend')
|
||||||
el-button(plain type="success" icon='el-icon-arrow-right' @click='submit') {{$t('Login')}}
|
el-button.mr-1(plain type="success" @click='submit') {{$t('Login')}}
|
||||||
router-link.float-right(to='/register')
|
router-link(to='/register')
|
||||||
a {{$t('Not registered?')}}
|
el-button.mt-1(plain type="primary") {{$t('Not registered?')}}
|
||||||
|
a.float-right(href='#' @click='forgot') {{$t('Forgot password?')}}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -24,22 +25,37 @@ export default {
|
|||||||
return {
|
return {
|
||||||
password: '',
|
password: '',
|
||||||
email: '',
|
email: '',
|
||||||
|
loading: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(['login']),
|
...mapActions(['login']),
|
||||||
|
async forgot () {
|
||||||
|
if (!this.email) {
|
||||||
|
Message({ message: this.$t('Insert your email'), type: 'error' })
|
||||||
|
this.$refs.email.focus()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.loading = true
|
||||||
|
await api.forgotPassword(this.email)
|
||||||
|
this.loading = false
|
||||||
|
Message({ message: this.$t('Check your email!'), type: 'success' })
|
||||||
|
},
|
||||||
async submit (e) {
|
async submit (e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
try {
|
try {
|
||||||
|
this.loading = true
|
||||||
const user = await api.login(this.email, this.password)
|
const user = await api.login(this.email, this.password)
|
||||||
|
this.loading = false
|
||||||
if (!user) {
|
if (!user) {
|
||||||
Message({ message: this.$t('login error'), type: 'error' })
|
Message({ message: this.$t('Login error'), type: 'error' })
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.login(user)
|
this.login(user)
|
||||||
Message({ message: this.$t('Logged'), type: 'success' })
|
Message({ message: this.$t('Logged'), type: 'success' })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Message({ message: this.$t('login error'), type: 'error' })
|
Message({ message: this.$t('Login error'), type: 'error' })
|
||||||
|
this.loading = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.email = this.password = ''
|
this.email = this.password = ''
|
||||||
|
|||||||
56
client/src/components/Recover.vue
Normal file
56
client/src/components/Recover.vue
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<template lang='pug'>
|
||||||
|
b-modal(@shown="$refs.password.focus()" :title='$t("Password reset")' hide-footer
|
||||||
|
@hidden='$router.replace("/")' :visible='true' ref='modal')
|
||||||
|
div(v-loading='loading')
|
||||||
|
p(v-html="$t('recover_explanation')")
|
||||||
|
span(v-if='user.email') {{user.email}}
|
||||||
|
el-input.mb-2(v-model='password' type='password' :placeholder='$t("New password")' ref='password')
|
||||||
|
v-icon(name='lock' slot='prepend')
|
||||||
|
el-button.mr-1(plain type="success" @click='change') {{$t('Change')}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import api from '@/api'
|
||||||
|
import { mapActions } from 'vuex'
|
||||||
|
import { Message } from 'element-ui'
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Recover',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
password: '',
|
||||||
|
loading: true,
|
||||||
|
user: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async mounted () {
|
||||||
|
try {
|
||||||
|
this.user = await api.checkRecoverCode(this.$route.params.recover_code)
|
||||||
|
console.log(this.user)
|
||||||
|
} catch (e) {
|
||||||
|
this.$refs.modal.hide()
|
||||||
|
Message({message: this.$t('error_recover_code'), type: 'error'})
|
||||||
|
}
|
||||||
|
this.loading = false
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async change () {
|
||||||
|
if (!this.password) return
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
await api.recoverPassword(this.$route.params.recover_code, this.password)
|
||||||
|
} catch (e) {
|
||||||
|
this.loading = false
|
||||||
|
Message({ message: this.$t('password_not_changed'), type: 'error'})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.loading = false
|
||||||
|
// this.$refs.modal.hide()
|
||||||
|
Message({ message: this.$t('password_changed'), type: 'success'})
|
||||||
|
this.$router.replace('/login')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
b-modal(ref='modal' @hidden='$router.replace("/")' size='lg' :visible='true'
|
b-modal(ref='modal' @hidden='$router.replace("/")' size='lg' :visible='true'
|
||||||
:title="edit?$t('Edit event'):$t('New event')" hide-footer)
|
:title="edit?$t('Edit event'):$t('New event')" hide-footer)
|
||||||
b-container
|
el-form
|
||||||
el-tabs.mb-2(v-model='activeTab' v-loading='sending' @tab-click.native='changeTab')
|
el-tabs.mb-2(v-model='activeTab' v-loading='sending')
|
||||||
|
|
||||||
//- NOT LOGGED EVENT
|
//- NOT LOGGED EVENT
|
||||||
el-tab-pane(v-if='!logged')
|
el-tab-pane(v-if='!logged')
|
||||||
@@ -36,12 +36,13 @@
|
|||||||
:picker-options="{start: '00:00', step: '00:30', end: '24:00'}")
|
:picker-options="{start: '00:00', step: '00:30', end: '24:00'}")
|
||||||
el-button.float-right(@click='next' :disabled='!couldProceed') {{$t('Next')}}
|
el-button.float-right(@click='next' :disabled='!couldProceed') {{$t('Next')}}
|
||||||
|
|
||||||
|
//- WHAT
|
||||||
el-tab-pane
|
el-tab-pane
|
||||||
span(slot='label') {{$t('What')}} <v-icon name='file-alt'/>
|
span(slot='label') {{$t('What')}} <v-icon name='file-alt'/>
|
||||||
span {{$t('what_explanation')}}
|
span {{$t('what_explanation')}}
|
||||||
el-input.mb-3(v-model='event.title' ref='title')
|
el-input.mb-3(v-model='event.title' ref='title')
|
||||||
span {{$t('description_explanation')}}
|
span {{$t('description_explanation')}}
|
||||||
el-input.mb-3(v-model='event.description' type='textarea' :rows='3')
|
el-input.mb-3(v-model='event.description' type='textarea' :rows='9')
|
||||||
span {{$t('tag_explanation')}}
|
span {{$t('tag_explanation')}}
|
||||||
br
|
br
|
||||||
el-select(v-model='event.tags' multiple filterable allow-create
|
el-select(v-model='event.tags' multiple filterable allow-create
|
||||||
@@ -143,9 +144,6 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(['addEvent', 'updateEvent', 'updateMeta']),
|
...mapActions(['addEvent', 'updateEvent', 'updateMeta']),
|
||||||
changeTab (tab) {
|
|
||||||
if (this.activeTab === "2") this.$refs.title.focus()
|
|
||||||
},
|
|
||||||
next () {
|
next () {
|
||||||
this.activeTab = String(Number(this.activeTab)+1)
|
this.activeTab = String(Number(this.activeTab)+1)
|
||||||
if (this.activeTab === "2") {
|
if (this.activeTab === "2") {
|
||||||
@@ -171,9 +169,9 @@ export default {
|
|||||||
const [ end_hour, end_minute ] = this.time.end.split(':')
|
const [ end_hour, end_minute ] = this.time.end.split(':')
|
||||||
if (this.event.multidate) {
|
if (this.event.multidate) {
|
||||||
start_datetime = moment(this.date.start)
|
start_datetime = moment(this.date.start)
|
||||||
.hour(start_hour).minute(start_minute)
|
.set('hour', start_hour).set('minute', start_minute)
|
||||||
end_datetime = moment(this.date.end)
|
end_datetime = moment(this.date.end)
|
||||||
.hour(end_hour).minute(end_minute)
|
.set('hour', end_hour).set('minute', end_minute)
|
||||||
} else {
|
} else {
|
||||||
console.log(this.date)
|
console.log(this.date)
|
||||||
start_datetime = moment(this.date).set('hour', start_hour).set('minute', start_minute)
|
start_datetime = moment(this.date).set('hour', start_hour).set('minute', start_minute)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const it = {
|
|||||||
<li>Per il computer fisso/portatile consigliamo Feedbro, da installare all'interno <a href="https://addons.mozilla.org/en-GB/firefox/addon/feedbroreader/">di Firefox </a>o <a href="https://chrome.google.com/webstore/detail/feedbro/mefgmmbdailogpfhfblcnnjfmnpnmdfa">di Chrome</a> e compatibile con tutti i principali sistemi operativi.</li>
|
<li>Per il computer fisso/portatile consigliamo Feedbro, da installare all'interno <a href="https://addons.mozilla.org/en-GB/firefox/addon/feedbroreader/">di Firefox </a>o <a href="https://chrome.google.com/webstore/detail/feedbro/mefgmmbdailogpfhfblcnnjfmnpnmdfa">di Chrome</a> e compatibile con tutti i principali sistemi operativi.</li>
|
||||||
<br/>
|
<br/>
|
||||||
Aggiungendo questo link al tuo lettore di feed, rimarrai aggiornata.`,
|
Aggiungendo questo link al tuo lettore di feed, rimarrai aggiornata.`,
|
||||||
export_email_explanation: `Puoi ricevere via mail gli eventi che ti interessano [WIP]`,
|
export_email_explanation: `Puoi ricevere via mail gli eventi che ti interessano.`,
|
||||||
export_list_explanation: `Se hai un sito web e vuoi mostrare una lista di eventi, puoi usare il seguente codice [WIP]`,
|
export_list_explanation: `Se hai un sito web e vuoi mostrare una lista di eventi, puoi usare il seguente codice [WIP]`,
|
||||||
export_calendar_explanation: `Se hai un sito web e vuoi mostrare un calendario di eventi, puoi usare il seguente codice [WIP]`,
|
export_calendar_explanation: `Se hai un sito web e vuoi mostrare un calendario di eventi, puoi usare il seguente codice [WIP]`,
|
||||||
export_ical_explanation: `I computer e gli smartphone sono comunemente attrezzati con un'applicazione per gestire un calendario. A questi programmi solitamente è possibile far importare un calendario remoto.`,
|
export_ical_explanation: `I computer e gli smartphone sono comunemente attrezzati con un'applicazione per gestire un calendario. A questi programmi solitamente è possibile far importare un calendario remoto.`,
|
||||||
@@ -51,6 +51,8 @@ const it = {
|
|||||||
Deactivate: 'Disattiva',
|
Deactivate: 'Disattiva',
|
||||||
Activate: 'Attiva',
|
Activate: 'Attiva',
|
||||||
'Remove Admin': 'Rimuovi Admin',
|
'Remove Admin': 'Rimuovi Admin',
|
||||||
|
'Remove notification': 'Rimuovi notifiche',
|
||||||
|
Yes: 'Sì',
|
||||||
Users: 'Utenti',
|
Users: 'Utenti',
|
||||||
Places: 'Luoghi',
|
Places: 'Luoghi',
|
||||||
Tags: 'Etichette',
|
Tags: 'Etichette',
|
||||||
@@ -70,6 +72,7 @@ const it = {
|
|||||||
Admin: 'Amministra',
|
Admin: 'Amministra',
|
||||||
Today: 'Oggi',
|
Today: 'Oggi',
|
||||||
Export: 'Esporta',
|
Export: 'Esporta',
|
||||||
|
'Forgot password?': 'Hai dimenticato la password?',
|
||||||
'Mastodon instance': 'Istanza Mastodon',
|
'Mastodon instance': 'Istanza Mastodon',
|
||||||
'admin_mastodon_explanation': 'Puoi associare un account mastodon a questa istanza di gancio, ogni evento verrà pubblicato lì',
|
'admin_mastodon_explanation': 'Puoi associare un account mastodon a questa istanza di gancio, ogni evento verrà pubblicato lì',
|
||||||
Associate: 'Associa',
|
Associate: 'Associa',
|
||||||
@@ -91,7 +94,16 @@ const it = {
|
|||||||
'Not registered?': `Non sei registrata?`,
|
'Not registered?': `Non sei registrata?`,
|
||||||
new_event_added: `Evento aggiunto.`,
|
new_event_added: `Evento aggiunto.`,
|
||||||
new_anon_event_added: `Evento inserito, verrà confermato quanto prima.`,
|
new_anon_event_added: `Evento inserito, verrà confermato quanto prima.`,
|
||||||
login_explanation: ``
|
login_explanation: `Entrando puoi pubblicare nuovi eventi.`,
|
||||||
|
recover_explanation: `Inserisci una nuova password:`,
|
||||||
|
'New password': 'Nuova password',
|
||||||
|
'Password reset': 'Password reset',
|
||||||
|
password_changed: 'Password cambiata con successo',
|
||||||
|
password_not_changed: 'Errore nella modifica della password!',
|
||||||
|
'Insert your email': 'Inserisci la tua email',
|
||||||
|
error_recover_code: `Ops, c'è stato un problema, hai richiesto più reset password?`,
|
||||||
|
email_notification_activated: 'Notifiche via mail attivate. Per disattivarle puoi usare il link in fondo ad ogni email che ricevi.'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default it
|
export default it
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import Register from './components/Register'
|
|||||||
import Export from './components/Export'
|
import Export from './components/Export'
|
||||||
import Admin from './components/Admin'
|
import Admin from './components/Admin'
|
||||||
import About from './components/About'
|
import About from './components/About'
|
||||||
|
import DelNotification from './components/DelNotification'
|
||||||
|
import Recover from './components/Recover'
|
||||||
|
|
||||||
Vue.use(Router)
|
Vue.use(Router)
|
||||||
|
|
||||||
@@ -55,6 +57,14 @@ export default new Router({
|
|||||||
{
|
{
|
||||||
path: '/about',
|
path: '/about',
|
||||||
components: { modal: About }
|
components: { modal: About }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/del_notification/:code',
|
||||||
|
components: { modal: DelNotification }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/recover/:recover_code',
|
||||||
|
components: { modal: Recover }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
"registration_email": "Ciao, la tua registrazione sarà confermata nei prossimi giorni. Riceverai una conferma non temere.",
|
"registration_email": "Ciao, la tua registrazione sarà confermata nei prossimi giorni. Riceverai una conferma non temere.",
|
||||||
"confirm_email": "Ti abbiamo attivato il tuo utente su gancio. Ora puoi aggiungere eventi."
|
"confirm_email": "Abbiamo attivato il tuo utente su gancio. Ora puoi aggiungere eventi.",
|
||||||
|
"recover_email": "Qualcuno ha richiesto un reset della password su gancio, se non sei stata tu, ignora pure questa email altrimenti visita questo link:",
|
||||||
|
"press here": "Premi qui"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user