Files
guanwang/scripts/purge-media-trash.sh

29 lines
1.5 KiB
Bash
Executable File

#!/bin/sh
set -eu
retention_days="${MEDIA_TRASH_RETENTION_DAYS:-30}"
case "$retention_days" in *[!0-9]*|'') echo "MEDIA_TRASH_RETENTION_DAYS 必须是非负整数" >&2; exit 2 ;; esac
docker compose exec -T -e RETENTION_DAYS="$retention_days" web node - <<'NODE'
const Database = require('better-sqlite3')
const fs = require('node:fs')
const path = require('node:path')
const db = new Database('/app/data/db/huiyu.sqlite')
const cutoff = new Date(Date.now() - Number(process.env.RETENTION_DAYS) * 86400000).toISOString()
const rows = db.prepare("SELECT id, asset_key, original_path, derived_path, thumbnail_path FROM media_assets WHERE status = 'trash' AND deleted_at <= ?").all(cutoff)
const root = path.resolve('/app/data/uploads/trash')
const remove = relative => {
const target = path.resolve(root, relative)
if (!target.startsWith(root + path.sep)) throw new Error('unsafe media path')
try { fs.unlinkSync(target) } catch (error) { if (error.code !== 'ENOENT') throw error }
}
const finish = db.transaction(row => {
for (const relative of [row.original_path, row.derived_path, row.thumbnail_path]) remove(relative)
db.prepare("UPDATE media_assets SET status = 'deleted', updated_at = ? WHERE id = ?").run(new Date().toISOString(), row.id)
db.prepare("INSERT INTO audit_logs (action, entity_type, entity_key, metadata_json, created_at) VALUES ('media.purge', 'media', ?, '{}', ?)").run(row.asset_key, new Date().toISOString())
})
for (const row of rows) finish(row)
db.close()
console.log(`已物理清理 ${rows.length} 个回收站素材`)
NODE