gancio-plugin-cleaner/index.js
snt 16ea47432a do not delete images, same image can be used by another event.
order results by start_date, end_date can be null
2025-07-10 15:20:33 +00:00

162 lines
6 KiB
JavaScript

/**
* 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.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: [['start_datetime', 'ASC']],
include: [{ model: Event, as:'child' }],
limit: plugin.settings.events_number,
})
if (!events.length) { return }
plugin.log.info("[Cleaner Plugin] Found %s past events and related resources.", events.length)
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 + " - From: " + start_date + " - To: " + end_date)
plugin.log.debug("[Cleaner Plugin] %s", JSON.stringify(e, null, "\t"))
if (e.media && e.media.length && !e.recurrent && !e.parentId && !e.child.length) {
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())
}
} else {
plugin.log.debug("[Cleaner Plugin] skip cleaning media file")
}
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