This commit is contained in:
les
2019-09-11 19:12:24 +02:00
parent 93baf01a55
commit 2fe956d117
65 changed files with 762 additions and 721 deletions

View File

@@ -2,8 +2,8 @@ const { Op } = require('sequelize')
const { user: User } = require('./models')
const Auth = {
async fillUser(req, res, next) {
if (!req.user) return next()
async fillUser (req, res, next) {
if (!req.user) { return next() }
req.user = await User.findOne({
where: { id: { [Op.eq]: req.user.id }, is_active: true }
}).catch(e => {
@@ -12,7 +12,7 @@ const Auth = {
})
next()
},
async isAuth(req, res, next) {
async isAuth (req, res, next) {
if (!req.user) {
return res
.status(403)
@@ -29,15 +29,15 @@ const Auth = {
}
next()
},
isAdmin(req, res, next) {
isAdmin (req, res, next) {
if (!req.user) {
return res
.status(403)
.send({ message: 'Failed to authenticate token ' })
}
if (req.user.is_admin && req.user.is_active) return next()
if (req.user.is_admin && req.user.is_active) { return next() }
return res.status(403).send({ message: 'Admin needed' })
},
}
}

View File

@@ -9,7 +9,7 @@ const federation = require('../../federation/helpers')
const eventController = {
async addComment(req, res) {
async addComment (req, res) {
// comment could be added to an event or to another comment
let event = await Event.findOne({ where: { activitypub_id: { [Op.eq]: req.body.id } } })
if (!event) {
@@ -21,11 +21,11 @@ const eventController = {
res.json(comment)
},
async getMeta(req, res) {
async getMeta (req, res) {
const places = await Place.findAll({
order: [[Sequelize.literal('weigth'), 'DESC']],
attributes: {
include: [[Sequelize.fn('count', Sequelize.col('events.placeId')) , 'weigth']],
include: [[Sequelize.fn('count', Sequelize.col('events.placeId')), 'weigth']],
exclude: ['weigth', 'createdAt', 'updatedAt']
},
include: [{ model: Event, attributes: [] }],
@@ -36,25 +36,25 @@ const eventController = {
order: [['weigth', 'DESC']],
attributes: {
exclude: ['createdAt', 'updatedAt']
},
}
})
res.json({ tags, places })
},
async getNotifications(event) {
function match(event, filters) {
async getNotifications (event) {
function match (event, filters) {
// matches if no filter specified
if (!filters) return true
if (!filters) { return true }
// check for visibility
if (typeof filters.is_visible !== 'undefined' && filters.is_visible !== event.is_visible) return false
if (typeof filters.is_visible !== 'undefined' && filters.is_visible !== event.is_visible) { return false }
if (!filters.tags && !filters.places) return true
if (!filters.tags.length && !filters.places.length) return true
if (!filters.tags && !filters.places) { return true }
if (!filters.tags.length && !filters.places.length) { return true }
if (filters.tags.length) {
const m = lodash.intersection(event.tags.map(t => t.tag), filters.tags)
if (m.length > 0) return true
if (m.length > 0) { return true }
}
if (filters.places.length) {
if (filters.places.find(p => p === event.place.name)) {
@@ -68,7 +68,7 @@ const eventController = {
return notifications.filter(notification => match(event, notification.filters))
},
async updateTag(req, res) {
async updateTag (req, res) {
const tag = await Tag.findByPk(req.body.tag)
if (tag) {
res.json(await tag.update(req.body))
@@ -77,7 +77,7 @@ const eventController = {
}
},
async updatePlace(req, res) {
async updatePlace (req, res) {
const place = await Place.findByPk(req.body.id)
await place.update(req.body)
res.json(place)
@@ -85,12 +85,12 @@ const eventController = {
// TODO retrieve next/prev event also
// select id, start_datetime, title from events where start_datetime > (select start_datetime from events where id=89) order by start_datetime limit 20;
async get(req, res) {
async get (req, res) {
const is_admin = req.user && req.user.is_admin
const id = req.params.event_id
let event = await Event.findByPk(id, {
const event = await Event.findByPk(id, {
plain: true,
attributes: {
attributes: {
exclude: ['createdAt', 'updatedAt']
},
include: [
@@ -109,29 +109,29 @@ const eventController = {
}
},
async confirm(req, res) {
async confirm (req, res) {
const id = Number(req.params.event_id)
const event = await Event.findByPk(id)
if (!event) return res.sendStatus(404)
if (!event) { return res.sendStatus(404) }
try {
event.is_visible = true
await event.save()
res.sendStatus(200)
// send notification
//notifier.notifyEvent(event.id)
//federation.sendEvent(event, req.user)
// notifier.notifyEvent(event.id)
// federation.sendEvent(event, req.user)
} catch (e) {
res.sendStatus(404)
}
},
async unconfirm(req, res) {
async unconfirm (req, res) {
const id = Number(req.params.event_id)
const event = await Event.findByPk(id)
if (!event) return sendStatus(404)
if (!event) { return sendStatus(404) }
try {
event.is_visible = false
@@ -142,7 +142,7 @@ const eventController = {
}
},
async getUnconfirmed(req, res) {
async getUnconfirmed (req, res) {
const events = await Event.findAll({
where: {
is_visible: false
@@ -153,7 +153,7 @@ const eventController = {
res.json(events)
},
async addNotification(req, res) {
async addNotification (req, res) {
try {
const notification = {
filters: { is_visible: true },
@@ -168,7 +168,7 @@ const eventController = {
}
},
async delNotification(req, res) {
async delNotification (req, res) {
const remove_code = req.params.code
try {
const notification = await Notification.findOne({ where: { remove_code: { [Op.eq]: remove_code } } })
@@ -179,7 +179,7 @@ const eventController = {
res.sendStatus(200)
},
async getAll(req, res) {
async getAll (req, res) {
// this is due how v-calendar shows dates
const start = moment()
.year(req.params.year)
@@ -193,7 +193,7 @@ const eventController = {
.endOf('month')
const shownDays = end.diff(start, 'days')
if (shownDays <= 35) end = end.add(1, 'week')
if (shownDays <= 35) { end = end.add(1, 'week') }
end = end.endOf('week')
let events = await Event.findAll({
@@ -202,10 +202,10 @@ const eventController = {
is_visible: true,
[Op.or]: [
// return all recurrent events
{recurrent: { [Op.ne]: null }},
{ recurrent: { [Op.ne]: null } },
// and events in specified range
{ start_datetime: { [Op.between]: [start.unix(), end.unix()] }}
{ start_datetime: { [Op.between]: [start.unix(), end.unix()] } }
]
},
attributes: { exclude: ['createdAt', 'updatedAt', 'placeId' ] },
@@ -223,10 +223,10 @@ const eventController = {
})
// build singular events from a recurrent pattern
function createEventsFromRecurrent(e, dueTo=null) {
function createEventsFromRecurrent (e, dueTo = null) {
const events = []
const recurrent = JSON.parse(e.recurrent)
if (!recurrent.frequency) return false
if (!recurrent.frequency) { return false }
let cursor = moment(start).startOf('week')
const start_date = moment.unix(e.start_datetime)
@@ -236,18 +236,18 @@ const eventController = {
const type = recurrent.type
// default frequency is '1d' => each day
const toAdd = { n: 1, unit: 'day'}
const toAdd = { n: 1, unit: 'day' }
cursor.set('hour', start_date.hour()).set('minute', start_date.minutes())
// each week or 2 (search for the first specified day)
if (frequency === '1w' || frequency === '2w') {
cursor.add(days[0]-1, 'day')
cursor.add(days[0] - 1, 'day')
if (frequency === '2w') {
const nWeeks = cursor.diff(e.start_datetime, 'w')%2
if (!nWeeks) cursor.add(1, 'week')
const nWeeks = cursor.diff(e.start_datetime, 'w') % 2
if (!nWeeks) { cursor.add(1, 'week') }
}
toAdd.n = Number(frequency[0])
toAdd.unit = 'week';
toAdd.unit = 'week'
// cursor.set('hour', start_date.hour()).set('minute', start_date.minutes())
}
@@ -263,37 +263,35 @@ const eventController = {
}
}
// add event at specified frequency
// add event at specified frequency
while (true) {
let first_event_of_week = cursor.clone()
const first_event_of_week = cursor.clone()
days.forEach(d => {
if (type === 'ordinal') {
cursor.date(d)
} else {
cursor.day(d-1)
cursor.day(d - 1)
}
if (cursor.isAfter(dueTo) || cursor.isBefore(start)) return
if (cursor.isAfter(dueTo) || cursor.isBefore(start)) { return }
e.start_datetime = cursor.unix()
e.end_datetime = e.start_datetime+duration
events.push( Object.assign({}, e) )
})
if (cursor.isAfter(dueTo)) break
e.end_datetime = e.start_datetime + duration
events.push(Object.assign({}, e))
})
if (cursor.isAfter(dueTo)) { break }
cursor = first_event_of_week.add(toAdd.n, toAdd.unit)
}
return events
}
let allEvents = events.filter(e => !e.recurrent)
events.filter(e => e.recurrent).forEach(e => {
const events = createEventsFromRecurrent(e, end)
if (events)
allEvents = allEvents.concat(events)
if (events) { allEvents = allEvents.concat(events) }
})
// allEvents.sort((a,b) => a.start_datetime-b.start_datetime)
res.json(allEvents.sort((a,b) => a.start_datetime-b.start_datetime))
res.json(allEvents.sort((a, b) => a.start_datetime - b.start_datetime))
}
}

View File

@@ -5,7 +5,7 @@ const ics = require('ics')
const exportController = {
async export(req, res) {
async export (req, res) {
const type = req.params.type
const tags = req.query.tags
const places = req.query.places
@@ -40,12 +40,12 @@ const exportController = {
}
},
feed(res, events) {
feed (res, events) {
res.type('application/rss+xml; charset=UTF-8')
res.render('feed/rss.pug', { events, config: process.env, moment })
},
ics(res, events) {
ics (res, events) {
const eventsMap = events.map(e => {
const tmpStart = moment.unix(e.start_datetime)
const tmpEnd = moment.unix(e.end_datetime)

View File

@@ -11,10 +11,10 @@ const settingsController = require('./settings')
const federation = require('../../federation/helpers')
const userController = {
async login(req, res) {
async login (req, res) {
// find the user
const user = await User.findOne({ where: {
[Op.or]: [
[Op.or]: [
{ email: req.body.email },
{ username: req.body.email }
]
@@ -44,7 +44,7 @@ const userController = {
}
},
async delEvent(req, res) {
async delEvent (req, res) {
const event = await Event.findByPk(req.params.id)
// check if event is mine (or user is admin)
if (event && (req.user.is_admin || req.user.id === event.userId)) {
@@ -68,7 +68,7 @@ const userController = {
},
// ADD EVENT
async addEvent(req, res) {
async addEvent (req, res) {
const body = req.body
const eventDetails = {
@@ -88,7 +88,7 @@ const userController = {
eventDetails.image_path = req.file.filename
}
let event = await Event.create(eventDetails)
const event = await Event.create(eventDetails)
// create place if needed
let place
@@ -105,7 +105,7 @@ const userController = {
if (body.tags) {
await Tag.bulkCreate(body.tags.map(t => ({ tag: t })), { ignoreDuplicates: true })
const tags = await Tag.findAll({ where: { tag: { [Op.in]: body.tags } } })
await Promise.all(tags.map(t => t.update({weigth: Number(t.weigth)+1})))
await Promise.all(tags.map(t => t.update({ weigth: Number(t.weigth) + 1 })))
await event.addTags(tags)
event.tags = tags
}
@@ -118,17 +118,15 @@ const userController = {
// send response to client
res.json(event)
if (req.user)
federation.sendEvent(event, req.user)
if (req.user) { federation.sendEvent(event, req.user) }
// res.sendStatus(200)
// send notification (mastodon/email/confirmation)
// notifier.notifyEvent(event.id)
},
async updateEvent(req, res) {
async updateEvent (req, res) {
const body = req.body
const event = await Event.findByPk(body.id)
if (!req.user.is_admin && event.userId !== req.user.id) {
@@ -168,10 +166,10 @@ const userController = {
res.json(newEvent)
},
async forgotPassword(req, res) {
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)
if (!user) { return res.sendStatus(200) }
user.recover_code = crypto.randomBytes(16).toString('hex')
mail.send(user.email, 'recover', { user, config })
@@ -180,25 +178,25 @@ const userController = {
res.sendStatus(200)
},
async checkRecoverCode(req, res) {
async checkRecoverCode (req, res) {
const recover_code = req.body.recover_code
if (!recover_code) return res.sendStatus(400)
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)
if (!user) { return res.sendStatus(400) }
try {
await user.update({ recover_code: ''})
await user.update({ recover_code: '' })
res.sendStatus(200)
} catch (e) {
res.sendStatus(400)
}
},
async updatePasswordWithRecoverCode(req, res) {
async updatePasswordWithRecoverCode (req, res) {
const recover_code = req.body.recover_code
const password = req.body.password
if (!recover_code || !password) return res.sendStatus(400)
if (!recover_code || !password) { return res.sendStatus(400) }
const user = await User.findOne({ where: { recover_code: { [Op.eq]: recover_code } } })
if (!user) return res.sendStatus(400)
if (!user) { return res.sendStatus(400) }
user.recover_code = ''
user.password = password
try {
@@ -209,32 +207,31 @@ const userController = {
}
},
current(req, res) {
current (req, res) {
if (req.user) { res.json(req.user) } else { res.sendStatus(404) }
},
async getAll(req, res) {
async getAll (req, res) {
const users = await User.findAll({
order: [['createdAt', 'DESC']]
})
res.json(users)
},
async update(req, res) {
async update (req, res) {
// user to modify
user = await User.findByPk(req.body.id)
if (!user) return res.status(404).json({ success: false, message: 'User not found!' })
if (!user) { return res.status(404).json({ success: false, message: 'User not found!' }) }
if (req.body.id !== req.user.id && !req.user.is_admin) {
return res.status(400).json({ succes: false, message: 'Not allowed' })
}
// ensure username to not change if not empty
req.body.username = user.username ? user.username : req.body.username
req.body.username = user.username ? user.username : req.body.username
if (!req.body.password)
delete req.body.password
if (!req.body.password) { delete req.body.password }
await user.update(req.body)
@@ -244,9 +241,8 @@ const userController = {
res.json(user)
},
async register(req, res) {
if (!settingsController.settings.allow_registration) return res.sendStatus(404)
async register (req, res) {
if (!settingsController.settings.allow_registration) { return res.sendStatus(404) }
const n_users = await User.count()
try {
// the first registered user will be an active admin
@@ -276,7 +272,7 @@ const userController = {
}
},
async create(req, res) {
async create (req, res) {
try {
req.body.is_active = true
req.body.recover_code = crypto.randomBytes(16).toString('hex')
@@ -288,7 +284,7 @@ const userController = {
}
},
async remove(req, res) {
async remove (req, res) {
try {
const user = await User.findByPk(req.params.id)
user.destroy()

View File

@@ -14,6 +14,8 @@ const settingsController = require('./controller/settings')
const storage = require('./storage')
const upload = multer({ storage })
const debug = require('debug')('api')
const api = express.Router()
api.use(cookieParser())
api.use(bodyParser.urlencoded({ extended: false }))
@@ -24,10 +26,10 @@ const jwt = expressJwt({
credentialsRequired: false,
getToken: function fromHeaderOrQuerystring (req) {
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
return req.headers.authorization.split(' ')[1];
return req.headers.authorization.split(' ')[1]
} else if (req.cookies && req.cookies['auth._token.local']) {
const [ prefix, token ] = req.cookies['auth._token.local'].split(' ')
if (prefix === 'Bearer') return token
if (prefix === 'Bearer') { return token }
}
}
})
@@ -47,10 +49,10 @@ api.post('/user', jwt, isAuth, isAdmin, userController.create)
// update user
api.put('/user', jwt, isAuth, userController.update)
//delete user
// delete user
api.delete('/user/:id', jwt, isAuth, isAdmin, userController.remove)
//
//
// api.delete('/user', userController.remove)
// get all users
@@ -64,7 +66,7 @@ api.put('/place', jwt, isAuth, isAdmin, eventController.updatePlace)
// add event
api.post('/user/event', jwt, fillUser, upload.single('image'), userController.addEvent)
// update event
api.put('/user/event', jwt, isAuth, upload.single('image'), userController.updateEvent)
@@ -100,4 +102,16 @@ api.get('/export/:type', exportController.export)
api.get('/event/:month/:year', eventController.getAll)
// Handle 404
api.use((req, res) => {
debug('404 Page not found: %s', req.path)
res.status(404).send('404: Page not Found')
})
// Handle 500
api.use((error, req, res, next) => {
debug(error)
res.status(500).send('500: Internal Server Error')
})
module.exports = api

View File

@@ -7,7 +7,7 @@ const debug = require('debug')('email')
moment.locale('it')
const mail = {
send(addresses, template, locals) {
send (addresses, template, locals) {
debug(`Send ${template} email to ${addresses}`)
const email = new Email({
views: { root: path.join(__dirname, '..', 'emails') },
@@ -30,7 +30,7 @@ const mail = {
updateFiles: false,
defaultLocale: settings.locale,
locale: settings.locale,
locales: ['it', 'es'],
locales: ['it', 'es']
},
transport: config.smtp
})

View File

@@ -1,10 +1,10 @@
'use strict'
module.exports = (sequelize, DataTypes) => {
module.exports = (sequelize, DataTypes) => {
const comment = sequelize.define('comment', {
activitypub_id: {
type: DataTypes.STRING(18),
index: true,
unique: true,
unique: true
},
data: DataTypes.JSON
}, {})
@@ -12,4 +12,4 @@
comment.belongsTo(models.event)
}
return comment
};
}

View File

@@ -6,7 +6,7 @@ module.exports = (sequelize, DataTypes) => {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
autoIncrement: true
},
title: DataTypes.STRING,
slug: DataTypes.STRING,
@@ -36,8 +36,8 @@ module.exports = (sequelize, DataTypes) => {
event.hasMany(models.comment)
}
//
event.prototype.toAP = function (username=config.admin, follower) {
//
event.prototype.toAP = function (username = config.admin, follower) {
const tags = this.tags && this.tags.map(t => '#' + t.tag).join(' ')
const content = `<b><a href='${config.baseurl}/event/${this.id}'>${this.title}</a></b><br/>
📍${this.place.name}<br/>
@@ -45,7 +45,7 @@ module.exports = (sequelize, DataTypes) => {
${this.description.length > 200 ? this.description.substr(0, 200) + '...' : this.description}<br/>
${tags} <br/>`
let attachment = []
const attachment = []
if (this.image_path) {
attachment.push({
type: 'Document',
@@ -62,19 +62,22 @@ module.exports = (sequelize, DataTypes) => {
// actor: `${config.baseurl}/federation/u/${username}`,
// url: `${config.baseurl}/federation/m/${this.id}`,
// object: {
attachment,
tag: this.tags.map(tag => ({
type: 'Hashtag',
name: '#' + tag.tag
})),
id: `${config.baseurl}/federation/m/${this.id}`,
type: 'Note',
published: this.createdAt,
attributedTo: `${config.baseurl}/federation/u/${username}`,
to: 'https://www.w3.org/ns/activitystreams#Public',
cc: follower ? follower: [],
content
}
type: 'Note',
id: `${config.baseurl}/federation/m/${this.id}`,
url: `${config.baseurl}/federation/m/${this.id}`,
attachment,
tag: this.tags.map(tag => ({
type: 'Hashtag',
name: '#' + tag.tag
})),
published: this.createdAt,
attributedTo: `${config.baseurl}/federation/u/${username}`,
to: ['https://www.w3.org/ns/activitystreams#Public'],
cc: follower || [],
content,
summary: null,
sensitive: false,
// }
}
}

View File

@@ -7,7 +7,7 @@ const consola = require('consola')
const db = {}
const sequelize = new Sequelize(config.db)
sequelize.authenticate().catch( e => {
sequelize.authenticate().catch(e => {
consola.error('Error connecting to DB: ', String(e))
process.exit(-1)
})
@@ -21,15 +21,14 @@ fs
const model = sequelize.import(path.join(__dirname, file))
db[model.name] = model
})
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db)
}
})
db.sequelize = sequelize
db.Sequelize = Sequelize
module.exports = db
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db)
}
})
db.sequelize = sequelize
db.Sequelize = Sequelize
module.exports = db

View File

@@ -3,7 +3,8 @@ module.exports = (sequelize, DataTypes) => {
const place = sequelize.define('place', {
name: {
type: DataTypes.STRING,
unique: true, index: true,
unique: true,
index: true,
allowNull: false
},
address: DataTypes.STRING

View File

@@ -15,4 +15,4 @@ module.exports = (sequelize, DataTypes) => {
}
return tag
};
}

View File

@@ -18,7 +18,7 @@ module.exports = (sequelize, DataTypes) => {
settings: DataTypes.JSON,
email: {
type: DataTypes.STRING,
unique: { msg: 'error.email_taken' },
unique: { msg: 'error.email_taken' },
index: true,
allowNull: false
},
@@ -46,7 +46,7 @@ module.exports = (sequelize, DataTypes) => {
}
user.prototype.comparePassword = async function (pwd) {
if (!this.password) return false
if (!this.password) { return false }
const ret = await bcrypt.compare(pwd, this.password)
return ret
}
@@ -78,4 +78,4 @@ module.exports = (sequelize, DataTypes) => {
})
return user
};
}

View File

@@ -13,7 +13,7 @@ try {
}
const DiskStorage = {
_handleFile(req, file, cb) {
_handleFile (req, file, cb) {
const filename = crypto.randomBytes(16).toString('hex') + '.jpg'
const finalPath = path.resolve(config.upload_path, filename)
const thumbPath = path.resolve(config.upload_path, 'thumb', filename)
@@ -36,7 +36,7 @@ const DiskStorage = {
})
})
},
_removeFile(req, file, cb) {
_removeFile (req, file, cb) {
delete file.destination
delete file.filename
delete file.path