/** * Clean past events from gancio */ const path = require('path') const fs = require('fs/promises') const config = require('../../config') const notifier = require('../../../gancio_source/server/notifier') const { Op } = require('sequelize') const { Event, Resource, Tag, Place, Notification, APUser, EventNotification, Message, User } = require('../../../gancio_source/server/api/models/models') const plugin = { configuration: { name: 'Cleaner', author: 'snt', url: 'https://git.criptomart.net/snt/gancio', description: 'Cleans events from a specified time before now, this is a beta release of this plugin.', settings: { refresh_time: { type: 'NUMBER', hint: 'Search for past events each n hours', required: true, description: '1 hour?' }, deadline_time: { type: 'NUMBER', hint: 'Clean all events non recurrent with end time n hours ago.', required: true, description: '720 hours? events ended after this number of hours ago will be deleted.' }, events_number: { type: 'NUMBER', hint: 'Limit the number of events to be deleted each call.', required: true, description: '4? Do not set too much, it can overload notifications.' }, } }, gancio: null, // { helpers, log, settings } log: null, settings: null, db: null, interval: null, ETag: null, load(gancio, settings) { plugin.gancio = gancio // contains all gancio settings, including all plugins settings plugin.log = gancio.log // just the logger plugin.db = gancio.db plugin.settings = settings // this plugin settings plugin.apiBaseUrl = gancio.settings.baseurl + '/api' // TODO: could use the TaskManager? plugin.interval = setInterval(this._tick, settings.refresh_time*1000*60*60) plugin.log.debug("[Cleaner Plugin] loaded with params: refresh: %s -- deadline: %s -- number: %s", settings.refresh_time, settings.deadline_time, settings.events_number) // this._tick() }, unload () { plugin.log.debug('[Cleaner Plugin] Clear interval an unload plugin') clearInterval(plugin.interval) }, onTest () { plugin._tick() }, async _tick () { // Avoid running multiple ticks at the same time which could cause race conditions on the database if (plugin._isTickRunning) { plugin.log.warn('[Cleaner Plugin] _tick already in progress, skipping') return } plugin.log.debug('[Cleaner Plugin] _tick started, locking it') plugin._isTickRunning = true try { if (!plugin.settings?.refresh_time) { plugin.log.debug('[Cleaner Plugin] refresh time not set, default to 1 hour') plugin.settings.refresh_time = 1 plugin.interval = setInterval(this._tick, settings.refresh_time*1000*60*60) } if (!plugin.settings?.deadline_time) { plugin.log.debug('[Cleaner Plugin] deadline time not set, default to 4 weeks') plugin.settings.deadline_time = 720 } if (!plugin.settings?.events_number) { plugin.log.debug('[Cleaner Plugin] events cleared not set, default to 1') plugin.settings.events_number = 1 } try { plugin.log.debug(`[Cleaner Plugin] Begin searching`) const now = Math.floor(Date.now()) const cut_datetime = now - plugin.settings?.deadline_time*60*60*1000 const date_obj = new Date(cut_datetime) plugin.log.debug("now %s -- deadline %s", now, cut_datetime) plugin.log.info("[Cleaner Plugin] Removing old events before " + date_obj) events = await plugin.db.models.event.findAll({ where: { recurrent: null, start_datetime: { [Op.lt]: cut_datetime/1000 }, [Op.or]: [ {end_datetime: { [Op.lt]: cut_datetime/1000 } }, { end_datetime: null } ] }, order: [['end_datetime', 'ASC']], include: [{ model: Event, as:'child' }], limit: plugin.settings.events_number, }) plugin.log.warn("[Cleaner Plugin] Found %s past events and related resources.", events.length) if (!events.length) { return } for (e of events) { end_date = new Date(e.end_datetime * 1000) start_date = new Date(e.start_datetime * 1000) plugin.log.info("[Cleaner Plugin] Cleaning: " + e.title + " - Rec: " + e.recurrent + " - From: " + start_date + " - To: " + end_date + " - place: " + e.placeId) plugin.log.debug("[Cleaner Plugin] %s", JSON.stringify(e, null, "\t")) if (e.media && e.media.length && !e.recurrent && !e.parentId) { try { const old_path = path.join(config.upload_path, e.media[0].url) const old_thumb_path = path.join(config.upload_path, 'thumb', e.media[0].url) await fs.unlink(old_thumb_path) await fs.unlink(old_path) plugin.log.debug("[Cleaner Plugin] removing file: " + old_path) } catch (excep) { plugin.log.error("[Cleaner Plugin] error removing file %s", excep.toString()) } } try { // notify local events before destroying notifications if (!e.ap_id) { await notifier.notifyEvent("Delete", e.id) } // remove related resources await Resource.destroy({ where: { eventId: e.id }}) // remove notifications await EventNotification.destroy({ where: { eventId: e.id }}) // remove event await e.destroy() } catch (excep) { console.error(excep) plugin.log.error("[Cleaner Plugin] error destroying %s", excep.toString() ) } }; } catch (e) { plugin.log.error(`[Cleaner Plugin] Error: ${String(e)}`) } } catch (e) { plugin.log.error(`[Cleaner Plugin] Uncaught error in _tick: ${String(e)}`) } finally { plugin.log.debug('[Cleaner Plugin] _tick finished, unlocking it') plugin._isTickRunning = false } } } module.exports = plugin