parent
9702f93cf9
commit
c5989fa8b4
23 changed files with 232 additions and 85 deletions
|
@ -1,3 +1,6 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"extends": "standard"
|
"extends": "standard",
|
||||||
|
"rules": {
|
||||||
|
"camelcase": 0
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,6 +3,8 @@ const { fillUser, isAuth, isAdmin } = require('./auth')
|
||||||
const eventController = require('./controller/event')
|
const eventController = require('./controller/event')
|
||||||
const exportController = require('./controller/export')
|
const exportController = require('./controller/export')
|
||||||
const userController = require('./controller/user')
|
const userController = require('./controller/user')
|
||||||
|
const settingsController = require('./controller/settings')
|
||||||
|
|
||||||
// const botController = require('./controller/bot')
|
// const botController = require('./controller/bot')
|
||||||
const multer = require('multer')
|
const multer = require('multer')
|
||||||
|
|
||||||
|
@ -51,6 +53,9 @@ api.get('/event/unconfirmed', isAuth, isAdmin, eventController.getUnconfirmed)
|
||||||
api.post('/event/notification', eventController.addNotification)
|
api.post('/event/notification', eventController.addNotification)
|
||||||
api.delete('/event/del_notification/:code', eventController.delNotification)
|
api.delete('/event/del_notification/:code', eventController.delNotification)
|
||||||
|
|
||||||
|
api.get('/settings', settingsController.getAdminSettings)
|
||||||
|
api.post('/settings', settingsController.setAdminSetting)
|
||||||
|
|
||||||
// get event
|
// get event
|
||||||
api.get('/event/:event_id', eventController.get)
|
api.get('/event/:event_id', eventController.get)
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
const { User, Event, Comment, Tag } = require('../model')
|
// const { User, Event, Comment, Tag } = require('../model')
|
||||||
const config = require('../config')
|
const config = require('../config')
|
||||||
const Mastodon = require('mastodon-api')
|
const Mastodon = require('mastodon-api')
|
||||||
const Sequelize = require('sequelize')
|
// const Sequelize = require('sequelize')
|
||||||
const Op = Sequelize.Op
|
// const Op = Sequelize.Op
|
||||||
|
const fs = require('fs')
|
||||||
|
const path = require('path')
|
||||||
const moment = require('moment')
|
const moment = require('moment')
|
||||||
moment.locale('it')
|
moment.locale('it')
|
||||||
|
|
||||||
const botController = {
|
const botController = {
|
||||||
bots: [],
|
bots: [],
|
||||||
// async initialize () {
|
// async initialize () {
|
||||||
|
@ -30,12 +33,20 @@ const botController = {
|
||||||
// listener.on('error', botController.error)
|
// listener.on('error', botController.error)
|
||||||
// botController.bots.push({ email: user.email, bot })
|
// botController.bots.push({ email: user.email, bot })
|
||||||
// },
|
// },
|
||||||
post (user, event) {
|
async post (mastodon_auth, event) {
|
||||||
const { client_id, client_secret, access_token } = user.mastodon_auth
|
const { access_token, instance } = mastodon_auth
|
||||||
const bot = new Mastodon({ access_token, api_url: `https://${user.mastodon_instance}/api/v1/` })
|
const bot = new Mastodon({ access_token, api_url: `https://${instance}/api/v1/` })
|
||||||
const status = `${event.title} @ ${event.place.name} ${moment(event.start_datetime).format('ddd, D MMMM HH:mm')} -
|
const status = `${event.title} @ ${event.place.name} ${moment(event.start_datetime).format('ddd, D MMMM HH:mm')} -
|
||||||
${event.description} - ${event.tags.map(t => '#' + t.tag).join(' ')} ${config.baseurl}/event/${event.id}`
|
${event.description} - ${event.tags.map(t => '#' + t.tag).join(' ')} ${config.baseurl}/event/${event.id}`
|
||||||
return bot.post('/statuses', { status, visibility: 'private' })
|
|
||||||
|
let media
|
||||||
|
if (event.image_path) {
|
||||||
|
const file = path.join(__dirname, '..', '..', event.image_path)
|
||||||
|
if (fs.statSync(file)) {
|
||||||
|
media = await bot.post('media', { file: fs.createReadStream(file) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bot.post('statuses', { status, visibility: 'direct', media_ids: media ? [media.data.id] : [] })
|
||||||
}
|
}
|
||||||
// async message (msg) {
|
// async message (msg) {
|
||||||
// console.log(msg)
|
// console.log(msg)
|
||||||
|
|
|
@ -19,7 +19,6 @@ const eventController = {
|
||||||
},
|
},
|
||||||
|
|
||||||
async getMeta (req, res) {
|
async getMeta (req, res) {
|
||||||
console.log('GET META')
|
|
||||||
const places = await Place.findAll()
|
const places = await Place.findAll()
|
||||||
const tags = await Tag.findAll()
|
const tags = await Tag.findAll()
|
||||||
res.json({ tags, places })
|
res.json({ tags, places })
|
||||||
|
@ -28,6 +27,7 @@ const eventController = {
|
||||||
async getNotifications (event) {
|
async getNotifications (event) {
|
||||||
function match (event, filters) {
|
function match (event, filters) {
|
||||||
// matches if no filter specified
|
// matches if no filter specified
|
||||||
|
if (!filters) return true
|
||||||
if (!filters.tags.length && !filters.places.length) return true
|
if (!filters.tags.length && !filters.places.length) return true
|
||||||
if (filters.tags.length) {
|
if (filters.tags.length) {
|
||||||
const m = lodash.intersection(event.tags.map(t => t.tag), filters.tags)
|
const m = lodash.intersection(event.tags.map(t => t.tag), filters.tags)
|
||||||
|
@ -47,7 +47,6 @@ const eventController = {
|
||||||
|
|
||||||
async updateTag (req, res) {
|
async updateTag (req, res) {
|
||||||
const tag = await Tag.findByPk(req.body.tag)
|
const tag = await Tag.findByPk(req.body.tag)
|
||||||
console.log(tag)
|
|
||||||
if (tag) {
|
if (tag) {
|
||||||
res.json(await tag.update(req.body))
|
res.json(await tag.update(req.body))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -5,6 +5,7 @@ const moment = require('moment')
|
||||||
const ics = require('ics')
|
const ics = require('ics')
|
||||||
|
|
||||||
const exportController = {
|
const exportController = {
|
||||||
|
|
||||||
async export (req, res) {
|
async export (req, res) {
|
||||||
console.log('type ', req.params.type)
|
console.log('type ', req.params.type)
|
||||||
const type = req.params.type
|
const type = req.params.type
|
||||||
|
@ -34,6 +35,7 @@ const exportController = {
|
||||||
return exportController.ics(res, events)
|
return exportController.ics(res, events)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async feed (res, events) {
|
async feed (res, events) {
|
||||||
res.type('application/rss+xml; charset=UTF-8')
|
res.type('application/rss+xml; charset=UTF-8')
|
||||||
res.render('feed/rss.pug', { events, config, moment })
|
res.render('feed/rss.pug', { events, config, moment })
|
||||||
|
|
27
app/controller/settings.js
Normal file
27
app/controller/settings.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
const { Settings } = require('../model')
|
||||||
|
|
||||||
|
const settingsController = {
|
||||||
|
async setAdminSetting (key, value) {
|
||||||
|
await Settings.findOrCreate({ where: { key },
|
||||||
|
defaults: { value } })
|
||||||
|
.spread((settings, created) => {
|
||||||
|
if (!created) return settings.update({ value })
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
async getAdminSettings (req, res) {
|
||||||
|
const settings = await settingsController.settings()
|
||||||
|
res.json(settings)
|
||||||
|
},
|
||||||
|
|
||||||
|
async settings () {
|
||||||
|
const settings = await Settings.findAll()
|
||||||
|
const map = {}
|
||||||
|
settings.forEach(setting => {
|
||||||
|
map[setting.key] = setting.value
|
||||||
|
})
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = settingsController
|
|
@ -2,7 +2,8 @@ const jwt = require('jsonwebtoken')
|
||||||
const Mastodon = require('mastodon-api')
|
const Mastodon = require('mastodon-api')
|
||||||
|
|
||||||
const User = require('../models/user')
|
const User = require('../models/user')
|
||||||
const { Event, Tag, Place, Notification } = require('../models/event')
|
const { Event, Tag, Place } = require('../models/event')
|
||||||
|
const settingsController = require('./settings')
|
||||||
const eventController = require('./event')
|
const eventController = require('./event')
|
||||||
const config = require('../config')
|
const config = require('../config')
|
||||||
const mail = require('../mail')
|
const mail = require('../mail')
|
||||||
|
@ -57,7 +58,7 @@ const userController = {
|
||||||
async addEvent (req, res) {
|
async addEvent (req, res) {
|
||||||
const body = req.body
|
const body = req.body
|
||||||
|
|
||||||
// remove description tag and create anchor tag
|
// remove description tag and create anchor tags
|
||||||
const description = body.description
|
const description = body.description
|
||||||
.replace(/(<([^>]+)>)/ig, '')
|
.replace(/(<([^>]+)>)/ig, '')
|
||||||
.replace(/(https?:\/\/[^\s]+)/g, '<a href="$1">$1</a>')
|
.replace(/(https?:\/\/[^\s]+)/g, '<a href="$1">$1</a>')
|
||||||
|
@ -68,28 +69,27 @@ const userController = {
|
||||||
multidate: body.multidate,
|
multidate: body.multidate,
|
||||||
start_datetime: body.start_datetime,
|
start_datetime: body.start_datetime,
|
||||||
end_datetime: body.end_datetime,
|
end_datetime: body.end_datetime,
|
||||||
is_visible: req.user ? true : false
|
is_visible: !!req.user
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.file) {
|
if (req.file) {
|
||||||
eventDetails.image_path = req.file.path
|
eventDetails.image_path = req.file.path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let event = await Event.create(eventDetails)
|
||||||
|
|
||||||
// create place
|
// create place
|
||||||
let place
|
let place
|
||||||
try {
|
try {
|
||||||
place = await Place.findOrCreate({ where: { name: body.place_name },
|
place = await Place.findOrCreate({ where: { name: body.place_name },
|
||||||
defaults: { address: body.place_address } })
|
defaults: { address: body.place_address } })
|
||||||
.spread((place, created) => place)
|
.spread((place, created) => place)
|
||||||
|
await event.setPlace(place)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
console.error(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
let event = await Event.create(eventDetails)
|
|
||||||
await event.setPlace(place)
|
|
||||||
|
|
||||||
// create/assign tags
|
// create/assign tags
|
||||||
console.log(body.tags)
|
|
||||||
if (body.tags) {
|
if (body.tags) {
|
||||||
await Tag.bulkCreate(body.tags.map(t => ({ tag: t })), { ignoreDuplicates: true })
|
await Tag.bulkCreate(body.tags.map(t => ({ tag: t })), { ignoreDuplicates: true })
|
||||||
const tags = await Tag.findAll({ where: { tag: { [Op.in]: body.tags } } })
|
const tags = await Tag.findAll({ where: { tag: { [Op.in]: body.tags } } })
|
||||||
|
@ -98,22 +98,11 @@ const userController = {
|
||||||
if (req.user) await req.user.addEvent(event)
|
if (req.user) await req.user.addEvent(event)
|
||||||
event = await Event.findByPk(event.id, { include: [User, Tag, Place] })
|
event = await Event.findByPk(event.id, { include: [User, Tag, Place] })
|
||||||
|
|
||||||
// check if bot exists
|
|
||||||
if (req.user && req.user.mastodon_auth) {
|
|
||||||
const post = await bot.post(req.user, event)
|
|
||||||
event.activitypub_id = post.id
|
|
||||||
event.save()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
// insert notifications
|
// insert notifications
|
||||||
const notifications = await eventController.getNotifications(event)
|
const notifications = await eventController.getNotifications(event)
|
||||||
await event.setNotifications(notifications)
|
await event.setNotifications(notifications)
|
||||||
} else {
|
|
||||||
const notification = await Notification.create({ type: 'admin_email' })
|
|
||||||
await event.setNotification(notification)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.json(event)
|
return res.json(event)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -139,7 +128,6 @@ const userController = {
|
||||||
}
|
}
|
||||||
await event.setPlace(place)
|
await event.setPlace(place)
|
||||||
await event.setTags([])
|
await event.setTags([])
|
||||||
console.log(body.tags)
|
|
||||||
if (body.tags) {
|
if (body.tags) {
|
||||||
await Tag.bulkCreate(body.tags.map(t => ({ tag: t })), { ignoreDuplicates: true })
|
await Tag.bulkCreate(body.tags.map(t => ({ tag: t })), { ignoreDuplicates: true })
|
||||||
const tags = await Tag.findAll({ where: { tag: { [Op.eq]: body.tags } } })
|
const tags = await Tag.findAll({ where: { tag: { [Op.eq]: body.tags } } })
|
||||||
|
@ -157,26 +145,47 @@ const userController = {
|
||||||
|
|
||||||
async getAuthURL (req, res) {
|
async getAuthURL (req, res) {
|
||||||
const instance = req.body.instance
|
const instance = req.body.instance
|
||||||
const { client_id, client_secret } = await Mastodon.createOAuthApp(`https://${instance}/api/v1/apps`, 'eventi', 'read write', `${config.baseurl}/settings`)
|
const is_admin = req.body.admin && req.user.is_admin
|
||||||
const url = await Mastodon.getAuthorizationUrl(client_id, client_secret, `https://${instance}`, 'read write', `${config.baseurl}/settings`)
|
const callback = `${config.baseurl}/${is_admin ? 'admin/oauth' : 'settings'}`
|
||||||
console.log(req.user)
|
const { client_id, client_secret } = await Mastodon.createOAuthApp(`https://${instance}/api/v1/apps`,
|
||||||
req.user.mastodon_instance = instance
|
config.title, 'read write', callback)
|
||||||
req.user.mastodon_auth = { client_id, client_secret }
|
const url = await Mastodon.getAuthorizationUrl(client_id, client_secret,
|
||||||
await req.user.save()
|
`https://${instance}`, 'read write', callback)
|
||||||
|
|
||||||
|
if (is_admin) {
|
||||||
|
await settingsController.setAdminSetting('mastodon_auth', { client_id, client_secret, instance })
|
||||||
|
} else {
|
||||||
|
req.user.mastodon_auth = { client_id, client_secret, instance }
|
||||||
|
await req.user.save()
|
||||||
|
}
|
||||||
res.json(url)
|
res.json(url)
|
||||||
},
|
},
|
||||||
|
|
||||||
async code (req, res) {
|
async code (req, res) {
|
||||||
const code = req.body.code
|
const { code, is_admin } = req.body
|
||||||
const { client_id, client_secret } = req.user.mastodon_auth
|
let client_id, client_secret, instance
|
||||||
const instance = req.user.mastodon_instance
|
const callback = `${config.baseurl}/${is_admin ? 'admin/oauth' : 'settings'}`
|
||||||
|
|
||||||
|
if (is_admin) {
|
||||||
|
const settings = await settingsController.settings();
|
||||||
|
({ client_id, client_secret, instance } = settings.mastodon_auth)
|
||||||
|
} else {
|
||||||
|
({ client_id, client_secret, instance } = req.user.mastodon_auth)
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const token = await Mastodon.getAccessToken(client_id, client_secret, code, `https://${instance}`, `${config.baseurl}/settings`)
|
const token = await Mastodon.getAccessToken(client_id, client_secret, code,
|
||||||
const mastodon_auth = { client_id, client_secret, access_token: token }
|
`https://${instance}`, callback)
|
||||||
req.user.mastodon_auth = mastodon_auth
|
const mastodon_auth = { client_id, client_secret, access_token: token, instance }
|
||||||
await req.user.save()
|
if (is_admin) {
|
||||||
await bot.add(req.user, token)
|
await settingsController.setAdminSetting('mastodon_auth', mastodon_auth)
|
||||||
res.json(req.user)
|
res.json(instance)
|
||||||
|
} else {
|
||||||
|
req.user.mastodon_auth = mastodon_auth
|
||||||
|
await req.user.save()
|
||||||
|
// await bot.add(req.user, token)
|
||||||
|
res.json(req.user)
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.json(e)
|
res.json(e)
|
||||||
}
|
}
|
||||||
|
@ -204,13 +213,18 @@ const userController = {
|
||||||
},
|
},
|
||||||
|
|
||||||
async register (req, res) {
|
async register (req, res) {
|
||||||
|
const n_users = await User.count()
|
||||||
try {
|
try {
|
||||||
req.body.is_active = false
|
if (n_users === 0) {
|
||||||
|
// admin will be the first registered user
|
||||||
|
req.body.is_active = req.body.is_admin = true
|
||||||
|
} else {
|
||||||
|
req.body.is_active = false
|
||||||
|
}
|
||||||
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, 'register', { user })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
|
||||||
return res.status(400).json(e)
|
return res.status(400).json(e)
|
||||||
}
|
}
|
||||||
const payload = { email: user.email }
|
const payload = { email: user.email }
|
||||||
|
|
59
app/cron.js
59
app/cron.js
|
@ -1,26 +1,57 @@
|
||||||
const mail = require('./mail')
|
const mail = require('./mail')
|
||||||
const { Event, Notification, EventNotification, User, Place, Tag } = require('./model')
|
const bot = require('./controller/bot')
|
||||||
|
const settingsController = require('./controller/settings')
|
||||||
|
|
||||||
|
const { Event, Notification, EventNotification,
|
||||||
|
User, Place, Tag } = require('./model')
|
||||||
|
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 'mail_admin':
|
||||||
|
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) {
|
||||||
|
const b = bot.post(settings.mastodon_auth, event)
|
||||||
|
promises.push(b)
|
||||||
|
}
|
||||||
|
// user publish
|
||||||
|
if (event.user && event.user.mastodon_auth) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
return Promise.all(promises)
|
||||||
|
}
|
||||||
|
|
||||||
async function loop () {
|
async function loop () {
|
||||||
|
settings = await settingsController.settings()
|
||||||
// get all event notification in queue
|
// get all event notification in queue
|
||||||
const eventNotifications = await EventNotification.findAll()
|
const eventNotifications = await EventNotification.findAll()
|
||||||
const promises = eventNotifications.map(async e => {
|
const promises = eventNotifications.map(async e => {
|
||||||
const event = await Event.findByPk(e.eventId, { include: [User, Place, Tag] })
|
const event = await Event.findByPk(e.eventId, { include: [User, Place, Tag] })
|
||||||
if (!event.place) return
|
if (!event.place) return
|
||||||
const notification = await Notification.findByPk(e.notificationId)
|
const notification = await Notification.findByPk(e.notificationId)
|
||||||
try {
|
await sendNotification(notification, event, e)
|
||||||
if (notification.type === 'mail') {
|
e.destroy()
|
||||||
await mail.send(notification.email, 'event', { event })
|
|
||||||
} else if (notification.type === 'mail_admin') {
|
|
||||||
const admins = await User.findAll({ where: { is_admin: true } })
|
|
||||||
await Promise.all(admins.map(admin =>
|
|
||||||
mail.send(admin.email, 'event', { event, to_confirm: true, notification })))
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log('CATCH!', e)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return e.destroy()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
const Sequelize = require('sequelize')
|
const Sequelize = require('sequelize')
|
||||||
const conf = require('./config.js')
|
const conf = require('./config.js')
|
||||||
console.error(conf.db)
|
|
||||||
const db = new Sequelize(conf.db)
|
const db = new Sequelize(conf.db)
|
||||||
|
|
||||||
// db.sync({ force: true })
|
// db.sync({ force: true })
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
{
|
{
|
||||||
"registration_email": "registration_email"
|
"registration_email": "registration_email"
|
||||||
}
|
}
|
||||||
|
|
12
app/model.js
12
app/model.js
|
@ -1,4 +1,14 @@
|
||||||
const User = require('./models/user')
|
const User = require('./models/user')
|
||||||
const { Event, Comment, Tag, Place, Notification, EventNotification } = require('./models/event')
|
const { Event, Comment, Tag, Place, Notification, EventNotification } = require('./models/event')
|
||||||
|
const Settings = require('./models/settings')
|
||||||
|
|
||||||
module.exports = { User, Event, Comment, Tag, Place, Notification, EventNotification }
|
module.exports = {
|
||||||
|
User,
|
||||||
|
Event,
|
||||||
|
Comment,
|
||||||
|
Tag,
|
||||||
|
Place,
|
||||||
|
Notification,
|
||||||
|
EventNotification,
|
||||||
|
Settings
|
||||||
|
}
|
||||||
|
|
|
@ -30,10 +30,13 @@ const Notification = db.define('notification', {
|
||||||
remove_code: Sequelize.STRING,
|
remove_code: Sequelize.STRING,
|
||||||
type: {
|
type: {
|
||||||
type: Sequelize.ENUM,
|
type: Sequelize.ENUM,
|
||||||
values: ['mail', 'admin_mail', 'activity_pub']
|
values: ['mail', 'admin_mail', 'mastodon']
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Notification.findOrCreate({ where: { type: 'mastodon' } })
|
||||||
|
Notification.findOrCreate({ where: { type: 'admin_email' } })
|
||||||
|
|
||||||
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 }
|
||||||
|
|
9
app/models/settings.js
Normal file
9
app/models/settings.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
const db = require('../db')
|
||||||
|
const Sequelize = require('sequelize')
|
||||||
|
|
||||||
|
const Settings = db.define('settings', {
|
||||||
|
key: { type: Sequelize.STRING, primaryKey: true, index: true },
|
||||||
|
value: Sequelize.JSON
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports = Settings
|
|
@ -13,7 +13,6 @@ const User = db.define('user', {
|
||||||
password: Sequelize.STRING,
|
password: Sequelize.STRING,
|
||||||
is_admin: Sequelize.BOOLEAN,
|
is_admin: Sequelize.BOOLEAN,
|
||||||
is_active: Sequelize.BOOLEAN,
|
is_active: Sequelize.BOOLEAN,
|
||||||
mastodon_instance: Sequelize.STRING,
|
|
||||||
mastodon_auth: Sequelize.JSON
|
mastodon_auth: Sequelize.JSON
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@ 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())
|
||||||
app.use(cors())
|
app.use(cors())
|
||||||
app.use('/static', express.static(path.join(__dirname, 'uploads')))
|
app.use('/static', express.static(path.join(__dirname, '..', 'uploads')))
|
||||||
app.use('/uploads', express.static('uploads'))
|
app.use('/uploads', express.static(path.join(__dirname, '..', 'uploads')))
|
||||||
app.use('/api', api)
|
app.use('/api', api)
|
||||||
app.use('/', express.static(path.join(__dirname, '..', 'client', 'dist')))
|
app.use('/', express.static(path.join(__dirname, '..', 'client', 'dist')))
|
||||||
app.use('/css', express.static(path.join(__dirname, '..', 'client', 'dist', 'css')))
|
app.use('/css', express.static(path.join(__dirname, '..', 'client', 'dist', 'css')))
|
||||||
|
|
|
@ -23,10 +23,10 @@ DiskStorage.prototype._handleFile = function _handleFile (req, file, cb) {
|
||||||
that.getDestination(req, file, function (err, destination) {
|
that.getDestination(req, file, function (err, destination) {
|
||||||
if (err) return cb(err)
|
if (err) return cb(err)
|
||||||
|
|
||||||
const filename = crypto.randomBytes(16).toString('hex') + '.webp'
|
const filename = crypto.randomBytes(16).toString('hex') + '.jpg'
|
||||||
const finalPath = path.join(destination, filename)
|
const finalPath = path.join(destination, filename)
|
||||||
const outStream = fs.createWriteStream(finalPath)
|
const outStream = fs.createWriteStream(finalPath)
|
||||||
const resizer = sharp().resize(800).webp()
|
const resizer = sharp().resize(800).jpeg({ quality: 80 })
|
||||||
|
|
||||||
file.stream.pipe(resizer).pipe(outStream)
|
file.stream.pipe(resizer).pipe(outStream)
|
||||||
outStream.on('error', cb)
|
outStream.on('error', cb)
|
||||||
|
|
|
@ -60,5 +60,7 @@ export default {
|
||||||
getAuthURL: mastodonInstance => post('/user/getauthurl', mastodonInstance),
|
getAuthURL: mastodonInstance => post('/user/getauthurl', mastodonInstance),
|
||||||
setCode: code => post('/user/code', code),
|
setCode: code => post('/user/code', code),
|
||||||
getKnowLocations: () => get('/locations'),
|
getKnowLocations: () => get('/locations'),
|
||||||
getKnowTags: () => get('/tags')
|
getKnowTags: () => get('/tags'),
|
||||||
|
getAdminSettings: () => get('/settings')
|
||||||
|
// setAdminSetting: (key, value) => post('/settings', { key, value })
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
b-modal(hide-footer @hidden='$router.replace("/")' :title='$t("Admin")' :visible='true' size='lg')
|
b-modal(hide-footer @hidden='$router.replace("/")' :title='$t("Admin")' :visible='true' size='lg')
|
||||||
el-tabs
|
el-tabs(tabPosition='left' v-model='tab')
|
||||||
//- USERS
|
//- USERS
|
||||||
el-tab-pane.pt-1
|
el-tab-pane.pt-1
|
||||||
template(slot='label')
|
template(slot='label')
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
template(slot='label')
|
template(slot='label')
|
||||||
v-icon(name='tag')
|
v-icon(name='tag')
|
||||||
span {{$t('Tags')}}
|
span {{$t('Tags')}}
|
||||||
p Select a tag to change it's color
|
p {{$t('admin_tag_explanation')}}
|
||||||
el-tag(v-if='tag.tag' :color='tag.color || "grey"' size='mini') {{tag.tag}}
|
el-tag(v-if='tag.tag' :color='tag.color || "grey"' size='mini') {{tag.tag}}
|
||||||
el-form(:inline='true' label-width='120px')
|
el-form(:inline='true' label-width='120px')
|
||||||
el-form-item(:label="$t('Color')")
|
el-form-item(:label="$t('Color')")
|
||||||
|
@ -71,7 +71,12 @@
|
||||||
template(slot='label')
|
template(slot='label')
|
||||||
v-icon(name='tools')
|
v-icon(name='tools')
|
||||||
span {{$t('Settings')}}
|
span {{$t('Settings')}}
|
||||||
|
el-form(inline)
|
||||||
|
span {{$t('admin_mastodon_explanation')}}
|
||||||
|
el-input(v-model="mastodon_instance")
|
||||||
|
span(slot='prepend') {{$t('Mastodon instance')}}
|
||||||
|
el-button(slot='append' @click='associate' variant='success' type='success') {{$t('Associate')}}
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
|
@ -95,17 +100,26 @@ export default {
|
||||||
place: {name: '', address: '' },
|
place: {name: '', address: '' },
|
||||||
tag: {name: '', color: ''},
|
tag: {name: '', color: ''},
|
||||||
events: [],
|
events: [],
|
||||||
loading: false
|
loading: false,
|
||||||
|
mastodon_instance: '',
|
||||||
|
settings: {},
|
||||||
|
tab: "0",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted () {
|
async mounted () {
|
||||||
|
const code = this.$route.query.code
|
||||||
|
if (code) {
|
||||||
|
this.tab = "4"
|
||||||
|
const instance = await api.setCode({code, is_admin: true})
|
||||||
|
}
|
||||||
this.users = await api.getUsers()
|
this.users = await api.getUsers()
|
||||||
this.events = await api.getUnconfirmedEvents()
|
this.events = await api.getUnconfirmedEvents()
|
||||||
|
this.settings = await api.getAdminSettings()
|
||||||
|
this.mastodon_instance = this.settings.mastodon_auth && this.settings.mastodon_auth.instance
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['tags', 'places']),
|
...mapState(['tags', 'places']),
|
||||||
paginatedEvents () {
|
paginatedEvents () {
|
||||||
console.log(this.events)
|
|
||||||
return this.events.slice((this.eventPage-1) * this.perPage,
|
return this.events.slice((this.eventPage-1) * this.perPage,
|
||||||
this.eventPage * this.perPage)
|
this.eventPage * this.perPage)
|
||||||
},
|
},
|
||||||
|
@ -146,8 +160,12 @@ export default {
|
||||||
preview (id) {
|
preview (id) {
|
||||||
this.$router.push(`/event/${id}`)
|
this.$router.push(`/event/${id}`)
|
||||||
},
|
},
|
||||||
|
async associate () {
|
||||||
|
if (!this.mastodon_instance) return
|
||||||
|
const url = await api.getAuthURL({instance: this.mastodon_instance, admin: true})
|
||||||
|
setTimeout( () => window.location.href=url, 100);
|
||||||
|
},
|
||||||
async confirm (id) {
|
async confirm (id) {
|
||||||
console.log('dentro confirm', id)
|
|
||||||
try {
|
try {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
await api.confirmEvent(id)
|
await api.confirmEvent(id)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template lang='pug'>
|
<template lang='pug'>
|
||||||
b-modal(hide-footer @hidden='$router.replace("/")'
|
b-modal(hide-footer @hidden='$router.replace("/")' ref='modal'
|
||||||
:title="$t('Register')" :visible='true' @shown='$refs.email.focus()')
|
:title="$t('Register')" :visible='true' @shown='$refs.email.focus()')
|
||||||
el-form
|
el-form
|
||||||
p(v-html="$t('register_explanation')")
|
p(v-html="$t('register_explanation')")
|
||||||
|
@ -35,11 +35,18 @@ export default {
|
||||||
async register () {
|
async register () {
|
||||||
try {
|
try {
|
||||||
const user = await api.register(this.user)
|
const user = await api.register(this.user)
|
||||||
this.$router.go(-1)
|
this.$refs.modal.hide()
|
||||||
Message({
|
if (!user.is_admin) {
|
||||||
message: this.$t('registration_complete'),
|
Message({
|
||||||
type: 'success'
|
message: this.$t('registration_complete'),
|
||||||
})
|
type: 'success'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Message({
|
||||||
|
message: this.$t('admin_registration_complete'),
|
||||||
|
type: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
b-modal(:title="$t('Settings')" hide-footer @hide='$router.go(-1)' :visible='true')
|
b-modal(:title="$t('Settings')" hide-footer @hide='$router.go(-1)' :visible='true')
|
||||||
el-form(inline)
|
el-form(inline)
|
||||||
el-input(v-model="mastodon_instance")
|
el-input(v-model="mastodon_instance" type='success')
|
||||||
span(slot='prepend') Mastodon instance
|
span(slot='prepend') Mastodon instance
|
||||||
el-button(slot='append' @click='associate' type='success') Associate
|
el-button(slot='append' @click='associate' type='success') Associate
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,10 @@ const it = {
|
||||||
Admin: 'Amministra',
|
Admin: 'Amministra',
|
||||||
Today: 'Oggi',
|
Today: 'Oggi',
|
||||||
Export: 'Esporta',
|
Export: 'Esporta',
|
||||||
|
'Mastodon instance': 'Istanza Mastodon',
|
||||||
|
'admin_mastodon_explanation': 'Puoi associare un account mastodon a questa istanza di gancio, ogni evento verrà pubblicato lì',
|
||||||
|
Associate: 'Associa',
|
||||||
|
admin_tag_explanation: `Seleziona un'etichetta per cambiarne il colore`,
|
||||||
event_confirmed: 'Evento confermato!',
|
event_confirmed: 'Evento confermato!',
|
||||||
notify_on_insert: `Notifica all'inserimento`,
|
notify_on_insert: `Notifica all'inserimento`,
|
||||||
'event_confirm_explanation': 'Puoi approvare gli eventi inseriti da utenti non registrati',
|
'event_confirm_explanation': 'Puoi approvare gli eventi inseriti da utenti non registrati',
|
||||||
|
|
|
@ -46,6 +46,10 @@ export default new Router({
|
||||||
{
|
{
|
||||||
path: '/export',
|
path: '/export',
|
||||||
components: { modal: Export }
|
components: { modal: Export }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/admin/oauth',
|
||||||
|
components: { modal: Admin }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
|
@ -13,13 +13,13 @@ services:
|
||||||
- ./postgres:/var/lib/postgresql/data
|
- ./postgres:/var/lib/postgresql/data
|
||||||
|
|
||||||
app:
|
app:
|
||||||
|
env_file: .env
|
||||||
build: .
|
build: .
|
||||||
ports:
|
ports:
|
||||||
- '12300:12300'
|
- '12300:12300'
|
||||||
volumes:
|
volumes:
|
||||||
- ./app/uploads:/usr/src/app/app/uploads
|
- ./app/uploads:/usr/src/app/app/uploads
|
||||||
|
|
||||||
env_file: .env
|
|
||||||
environment:
|
environment:
|
||||||
PORT: 12300
|
PORT: 12300
|
||||||
DB_HOST: db
|
DB_HOST: db
|
||||||
|
|
Loading…
Reference in a new issue