gancio-upstream/server/api/controller/collection.js

258 lines
7.3 KiB
JavaScript
Raw Normal View History

const { Collection, Filter, Event, Tag, Place } = require('../models/models')
2022-05-20 13:04:07 +02:00
const log = require('../../log')
const { DateTime } = require('luxon')
2023-06-18 22:17:11 +02:00
const { col: Col, queryParamToBool } = require('../../helpers')
2022-05-25 10:55:39 +02:00
const { Op, Sequelize } = require('sequelize')
2022-05-20 13:04:07 +02:00
2022-06-18 01:10:27 +02:00
const collectionController = {
2022-05-20 13:04:07 +02:00
// get all collections
2022-05-20 13:04:07 +02:00
async getAll (req, res) {
2023-06-18 22:17:11 +02:00
const withFilters = queryParamToBool(req.query.withFilters)
const pin = req.query.pin
2022-06-18 01:10:27 +02:00
let collections
2022-05-25 10:55:39 +02:00
if (withFilters) {
collections = await Collection.findAll({ include: [ Filter ] })
2022-05-25 10:55:39 +02:00
} else {
if (pin) {
collections = await Collection.findAll({ where: { isTop: true }})
} else {
collections = await Collection.findAll()
}
2022-05-25 10:55:39 +02:00
}
2022-06-18 01:10:27 +02:00
return res.json(collections)
2022-05-20 13:04:07 +02:00
},
async _getVisible () {
return Collection.findAll({ attributes: ['name', 'id'], where: { isTop: true }, raw: true })
},
async togglePin (req, res) {
const id = req.params.id
try {
const collection = await Collection.findByPk(id)
if (!collection) { return req.sendStatus(404) }
await collection.update({ isTop: !collection.isTop })
res.json(!collection.isTop)
} catch (e) {
log.error(e)
res.sendStatus(404)
}
},
2022-05-20 13:04:07 +02:00
async getEvents (req, res) {
2024-02-28 15:10:56 +01:00
const settings = res.locals.settings
2024-01-11 12:58:29 +01:00
const exportController = require('./export')
const format = req.params?.format ?? 'json'
2022-05-20 13:04:07 +02:00
const name = req.params.name
2024-05-20 12:37:17 +02:00
const limit = req.query?.max
const start = req.query?.start_at ?? DateTime.local().toUnixInteger()
const reverse = queryParamToBool(req.query.reverse)
const older = queryParamToBool(req.query.older)
const show_recurrent = settings.allow_recurrent_event && queryParamToBool(req.query.show_recurrent, settings.recurrent_event_visible)
2023-10-30 09:36:18 +01:00
try {
const events = await collectionController._getEvents({ name, start, reverse, older, limit, show_recurrent })
log.debug(`[COLLECTION] (${name}) events: ${events?.length}`)
switch (format) {
case 'rss':
return exportController.feed(req, res, events,
`${res.locals.settings.title} - Collection @${name}`,
`${res.locals.settings.baseurl}/feed/rss/collection/${name}`)
case 'ics':
return exportController.ics(req, res, events)
default:
return res.json(events)
}
2023-10-30 09:36:18 +01:00
} catch (e) {
log.warn('[COLLECTION] getEvents: %s', String(e))
2023-10-30 09:36:18 +01:00
return res.sendStatus(404)
}
},
// return events from collection
async _getEvents ({
name, start=DateTime.local().toUnixInteger(), end,
show_recurrent=false,
2024-05-20 12:37:17 +02:00
limit, include_description=false,
older, reverse }) {
2022-05-20 13:04:07 +02:00
// get the collection from specified name
2022-06-18 01:10:27 +02:00
const collection = await Collection.findOne({ where: { name } })
if (!collection) {
2024-02-07 10:43:12 +01:00
log.warn(`[COLLECTION] "%s" not found`, name)
return []
2022-05-25 10:55:39 +02:00
}
2022-05-20 13:04:07 +02:00
// and all related filters
2023-10-30 09:36:18 +01:00
const filters = await Filter.findAll({ where: { collectionId: collection.id } })
2023-12-28 00:47:31 +01:00
// collection is empty if there are no filters
2022-06-18 01:10:27 +02:00
if (!filters.length) {
log.debug('[COLLECTION] This collection has no filter!')
2023-10-30 09:36:18 +01:00
return []
2022-06-18 01:10:27 +02:00
}
2023-12-28 00:47:31 +01:00
// init stardard filter
2022-05-20 13:04:07 +02:00
const where = {
// do not include parent recurrent event
recurrent: null,
// confirmed event only
is_visible: true,
2023-12-28 00:47:31 +01:00
[Op.or]: {
start_datetime: { [older ? Op.lte : Op.gte]: start },
end_datetime: { [older ? Op.lte : Op.gte]: start }
2023-12-28 00:47:31 +01:00
}
}
2022-05-20 13:04:07 +02:00
// include recurrent events?
if (!show_recurrent) {
where.parentId = null
}
2023-12-28 00:47:31 +01:00
if (end) {
where.start_datetime = { [older ? Op.gte : Op.lte]: end }
2022-05-20 13:04:07 +02:00
}
const replacements = []
const conditions = []
const negatedConditions = []
// collections are a set of filters to match
2022-05-20 13:04:07 +02:00
filters.forEach(f => {
2023-12-28 00:47:31 +01:00
let tmpConditions = []
2023-12-28 00:47:31 +01:00
2022-05-20 13:04:07 +02:00
if (f.tags && f.tags.length) {
2022-06-18 01:10:27 +02:00
const tags = Sequelize.fn('EXISTS', Sequelize.literal(`SELECT 1 FROM event_tags WHERE ${Col('event_tags.eventId')}=event.id AND ${Col('tagTag')} in (?)`))
2022-05-20 13:04:07 +02:00
replacements.push(f.tags)
tmpConditions.push(tags)
2023-12-28 00:47:31 +01:00
}
if (f.places && f.places.length) {
tmpConditions.push({ placeId: f.places.map(p => p.id) })
2022-05-20 13:04:07 +02:00
}
2023-12-28 00:47:31 +01:00
if (f.actors && f.actors.length) {
tmpConditions.push({ apUserApId: f.actors.map(a => a.ap_id)})
2023-12-28 00:47:31 +01:00
}
if (!tmpConditions.length) return
if (f.negate) {
negatedConditions.push(tmpConditions.length === 1 ? tmpConditions[0] : { [Op.and]: tmpConditions })
} else {
conditions.push(tmpConditions.length === 1 ? tmpConditions[0] : { [Op.and]: tmpConditions })
}
2022-05-20 13:04:07 +02:00
})
where[Op.and] = {
...(negatedConditions.length > 0 && { [Op.not]: negatedConditions}),
...(conditions.length > 0 && { [Op.or]: conditions })
}
2022-05-20 13:04:07 +02:00
const events = await Event.findAll({
where,
attributes: {
2024-01-08 23:51:57 +01:00
exclude: ['likes', 'boost', 'userId', 'is_visible', 'createdAt', 'resources', 'ap_id', ...(!include_description ? ['description'] : [])]
2022-05-20 13:04:07 +02:00
},
order: [['start_datetime', reverse ? 'DESC' : 'ASC']],
2022-05-20 13:04:07 +02:00
include: [
{
model: Tag,
2022-06-18 01:10:27 +02:00
// order: [Sequelize.literal('(SELECT COUNT("tagTag") FROM event_tags WHERE tagTag = tag) DESC')],
2022-05-20 13:04:07 +02:00
attributes: ['tag'],
through: { attributes: [] }
},
{ model: Place, required: true, attributes: ['id', 'name', 'address'] },
2022-05-20 13:04:07 +02:00
],
2024-05-20 12:37:17 +02:00
...( limit > 0 && { limit }),
2022-05-20 13:04:07 +02:00
replacements
}).catch(e => {
log.error('[EVENT]', e)
return []
})
2023-10-30 09:36:18 +01:00
return events.map(e => {
2022-05-20 13:04:07 +02:00
e = e.get()
e.tags = e.tags ? e.tags.map(t => t && t.tag) : []
return e
})
},
async add (req, res) {
2022-06-18 01:10:27 +02:00
const collectionDetail = {
2022-05-20 13:04:07 +02:00
name: req.body.name,
isActor: true,
isTop: true
}
// TODO: validation
log.info(`Create collection: ${req.body.name}`)
try {
const collection = await Collection.create(collectionDetail)
res.json(collection)
} catch (e) {
log.error(`Create collection failed ${e}`)
res.status(400).send(e)
}
2022-05-20 13:04:07 +02:00
},
async remove (req, res) {
2022-06-18 01:10:27 +02:00
const collection_id = req.params.id
log.info('Remove collection', collection_id)
2022-05-20 13:04:07 +02:00
try {
2022-06-18 01:10:27 +02:00
const collection = await Collection.findByPk(collection_id)
await collection.destroy()
2022-05-20 13:04:07 +02:00
res.sendStatus(200)
} catch (e) {
log.error('Remove collection failed:' + String(e))
2022-05-20 13:04:07 +02:00
res.sendStatus(404)
}
},
async getFilters (req, res) {
2022-06-18 01:10:27 +02:00
const collectionId = req.params.collection_id
const filters = await Filter.findAll({ where: { collectionId } })
2022-05-20 13:04:07 +02:00
return res.json(filters)
2023-06-17 09:30:44 +02:00
},
2022-05-20 13:04:07 +02:00
async addFilter (req, res) {
const { collectionId, tags, places, actors, negate } = req.body
2023-06-17 09:30:44 +02:00
2022-05-20 13:04:07 +02:00
try {
const filter = await Filter.create({ collectionId, tags, places, actors, negate })
2022-05-20 13:04:07 +02:00
return res.json(filter)
} catch (e) {
log.error(String(e))
return res.sendStatus(400)
2022-05-20 13:04:07 +02:00
}
},
async removeFilter (req, res) {
const filter_id = req.params.id
log.info(`Remove filter ${filter_id}`)
2022-05-20 13:04:07 +02:00
try {
const filter = await Filter.findByPk(filter_id)
if (!filter) {
return res.sendStatus(404)
}
2022-05-20 13:04:07 +02:00
await filter.destroy()
res.sendStatus(200)
} catch (e) {
log.error('Remove filter failed:', e)
res.sendStatus(404)
}
},
}
2023-06-17 09:30:44 +02:00
module.exports = collectionController