admin settings fix #12, fix #13 image in mastodon

This commit is contained in:
lesion 2019-03-12 01:16:52 +01:00
parent 9702f93cf9
commit c5989fa8b4
23 changed files with 232 additions and 85 deletions

View file

@ -1,3 +1,6 @@
module.exports = { module.exports = {
"extends": "standard" "extends": "standard",
"rules": {
"camelcase": 0
}
}; };

View file

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

View file

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

View file

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

View file

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

View 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

View file

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

View file

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

View file

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

View file

@ -1,3 +1,3 @@
{ {
"registration_email": "registration_email" "registration_email": "registration_email"
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -46,6 +46,10 @@ export default new Router({
{ {
path: '/export', path: '/export',
components: { modal: Export } components: { modal: Export }
},
{
path: '/admin/oauth',
components: { modal: Admin }
} }
] ]
}) })

View file

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