diff --git a/config.sample.js b/config.sample.js index f14d468..d27172e 100644 --- a/config.sample.js +++ b/config.sample.js @@ -153,7 +153,17 @@ module.exports = { Cloudflare will not cache files bigger than 512MB. NOTE: Set to falsy value (false, null, etc.) to disable. */ - zipMaxTotalSize: '512MB' + zipMaxTotalSize: '512MB', + + /* + If you want to make it automatically calls Cloudflare's API to purge cache on file delete, + fill your api key, email and your site's zone id below, then set "purgeCache" to true. + This will only purge cache of the deleted file and its associated thumb. + */ + apiKey: '', + email: '', + zoneId: '', + purgeCache: false }, /* diff --git a/controllers/albumsController.js b/controllers/albumsController.js index 2a510f5..37845fa 100644 --- a/controllers/albumsController.js +++ b/controllers/albumsController.js @@ -22,9 +22,7 @@ class ZipEmitter extends EventEmitter { constructor (identifier) { super() this.identifier = identifier - this.once('done', () => { - albumsController.zipEmitters.delete(this.identifier) - }) + this.once('done', () => albumsController.zipEmitters.delete(this.identifier)) } } diff --git a/controllers/uploadController.js b/controllers/uploadController.js index f451395..2cd994a 100644 --- a/controllers/uploadController.js +++ b/controllers/uploadController.js @@ -448,38 +448,10 @@ uploadsController.processFilesForDisplay = async (req, res, files, existingFiles } uploadsController.delete = async (req, res) => { - // TODO: Wrap utils.bulkDeleteFiles() instead - const user = await utils.authorize(req, res) - if (!user) { return } - - const id = req.body.id - if (id === undefined || id === '') { - return res.json({ success: false, description: 'No file specified.' }) - } - - const file = await db.table('files') - .where('id', id) - .where(function () { - if (user.username !== 'root') { - this.where('userid', user.id) - } - }) - .first() - - const deleteFile = await utils.deleteFile(file.name).catch(console.error) - if (!deleteFile) { return } - - await db.table('files') - .where('id', id) - .del() - - if (file.albumid) { - await db.table('albums') - .where('id', file.albumid) - .update('editedAt', Math.floor(Date.now() / 1000)) - } - - return res.json({ success: true }) + req.body.field = 'id' + req.body.values = [req.body.id] + delete req.body.id + return uploadsController.bulkDelete(req, res) } uploadsController.bulkDelete = async (req, res) => { diff --git a/controllers/utilsController.js b/controllers/utilsController.js index 24b0822..2f3a2bc 100644 --- a/controllers/utilsController.js +++ b/controllers/utilsController.js @@ -4,12 +4,14 @@ const ffmpeg = require('fluent-ffmpeg') const fs = require('fs') const gm = require('gm') const path = require('path') +const snekfetch = require('snekfetch') const units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] const utilsController = {} const uploadsDir = path.join(__dirname, '..', config.uploads.folder) const thumbsDir = path.join(uploadsDir, 'thumbs') +const cloudflareAuth = config.cloudflare.apiKey && config.cloudflare.email && config.cloudflare.zoneId utilsController.imageExtensions = ['.webp', '.jpg', '.jpeg', '.bmp', '.gif', '.png'] utilsController.videoExtensions = ['.webm', '.mp4', '.wmv', '.avi', '.mov', '.mkv'] @@ -179,7 +181,40 @@ utilsController.bulkDeleteFiles = async (field, values, user) => { })) } + if (config.cloudflare.purgeCache) { + // purgeCloudflareCache() is an async function, but let us not wait for it + const names = files.filter(file => !failed.includes(file[field])).map(file => file.name) + utilsController.purgeCloudflareCache(names) + } + return failed } +utilsController.purgeCloudflareCache = async names => { + if (!cloudflareAuth) { return } + + const thumbs = [] + names = names.map(name => { + const url = `${config.domain}/${name}` + const extname = path.extname(name).toLowerCase() + if (utilsController.mayGenerateThumb(extname)) { + thumbs.push(`${config.domain}/thumbs/${name.slice(0, -extname.length)}.png`) + } + return url + }) + + const purge = await snekfetch + .post(`https://api.cloudflare.com/client/v4/zones/${config.cloudflare.zoneId}/purge_cache`) + .set({ + 'X-Auth-Email': config.cloudflare.email, + 'X-Auth-Key': config.cloudflare.apiKey + }) + .send({ files: names.concat(thumbs) }) + .catch(error => error) + + if (purge.body && !purge.body.success) { + purge.body.errors.forEach(error => console.error(`CF: ${error.code}: ${error.message}`)) + } +} + module.exports = utilsController diff --git a/package.json b/package.json index 1088f57..8e44ecf 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "multer": "^1.3.0", "nunjucks": "^3.1.2", "randomstring": "^1.1.5", + "snekfetch": "^3.6.4", "sqlite3": "^4.0.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 996db55..4c408c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2440,6 +2440,10 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" +snekfetch@^3.6.4: + version "3.6.4" + resolved "https://registry.yarnpkg.com/snekfetch/-/snekfetch-3.6.4.tgz#d13e80a616d892f3d38daae4289f4d258a645120" + source-map-resolve@^0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.1.tgz#7ad0f593f2281598e854df80f19aae4b92d7a11a"