From d5ed8736aa5f90980086a5a2fb1d911ccf2a33be Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 11 Feb 2022 16:16:28 -0500 Subject: [PATCH] feat: deleting a user upload dissociates from posts, and vice versa --- src/posts/uploads.js | 12 +++++++++--- src/user/uploads.js | 7 +++++++ test/posts/uploads.js | 10 ++++++++++ test/user/uploads.js | 18 ++++++++++++++++++ 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/posts/uploads.js b/src/posts/uploads.js index bed2e5be2b..b7916f8ad1 100644 --- a/src/posts/uploads.js +++ b/src/posts/uploads.js @@ -9,6 +9,7 @@ const validator = require('validator'); const db = require('../database'); const image = require('../image'); +const user = require('../user'); const topics = require('../topics'); const file = require('../file'); const meta = require('../meta'); @@ -123,14 +124,19 @@ module.exports = function (Posts) { db.sortedSetRemoveBulk(bulkRemove), ]; + await Promise.all(promises); + if (!meta.config.preserveOrphanedUploads) { const deletePaths = (await Promise.all( filePaths.map(async filePath => (await Posts.uploads.isOrphan(filePath) ? filePath : false)) )).filter(Boolean); - promises.push(Posts.uploads.deleteFromDisk(deletePaths)); - } - await Promise.all(promises); + const uploaderUids = (await db.getObjectsFields(deletePaths.map(path => `upload:${md5(path)}`, ['uid']))).map(o => (o ? o.uid || null : null)); + await Promise.all(uploaderUids.map((uid, idx) => ( + uid && isFinite(uid) ? user.deleteUpload(uid, uid, deletePaths[idx]) : null + )).filter(Boolean)); + await Posts.uploads.deleteFromDisk(deletePaths); + } }; Posts.uploads.dissociateAll = async (pid) => { diff --git a/src/user/uploads.js b/src/user/uploads.js index 0867e9b982..14c7a67b34 100644 --- a/src/user/uploads.js +++ b/src/user/uploads.js @@ -6,6 +6,7 @@ const winston = require('winston'); const crypto = require('crypto'); const db = require('../database'); +const posts = require('../posts'); const file = require('../file'); const batch = require('../batch'); @@ -66,6 +67,12 @@ module.exports = function (User) { db.delete(`upload:${md5(uploadNames[idx])}`), ]); })); + + // Dissociate the upload from pids, if any + const pids = await db.getSortedSetsMembers(uploadNames.map(relativePath => `upload:${md5(relativePath)}:pids`)); + await Promise.all(pids.map(async (pids, idx) => Promise.all( + pids.map(async pid => posts.uploads.dissociate(pid, uploadNames[idx])) + ))); }, { batch: 50 }); }; diff --git a/test/posts/uploads.js b/test/posts/uploads.js index 7370a1c90f..8067e4cd15 100644 --- a/test/posts/uploads.js +++ b/test/posts/uploads.js @@ -207,6 +207,16 @@ describe('upload methods', () => { done(); }); }); + + it('should remove the image\'s user association, if present', async () => { + _recreateFiles(); + await posts.uploads.associate(pid, 'files/wut.txt'); + await user.associateUpload(uid, 'files/wut.txt'); + await posts.uploads.dissociate(pid, 'files/wut.txt'); + + const userUploads = await db.getSortedSetMembers(`uid:${uid}:uploads`); + assert.strictEqual(userUploads.includes('files/wut.txt'), false); + }); }); describe('.dissociateAll()', () => { diff --git a/test/user/uploads.js b/test/user/uploads.js index 5d86afb633..eee135c4de 100644 --- a/test/user/uploads.js +++ b/test/user/uploads.js @@ -9,6 +9,8 @@ const nconf = require('nconf'); const db = require('../mocks/databasemock'); const user = require('../../src/user'); +const topics = require('../../src/topics'); +const categories = require('../../src/categories'); const file = require('../../src/file'); const utils = require('../../public/src/utils'); @@ -144,5 +146,21 @@ describe('uploads.js', () => { assert.strictEqual(e.message, '[[error:invalid-path]]'); } }); + + it('should remove the post association as well, if present', async () => { + const { cid } = await categories.create({ name: utils.generateUUID() }); + const { postData } = await topics.post({ + uid, + cid, + title: utils.generateUUID(), + content: `[an upload](/assets/uploads/${relativePath})`, + }); + + assert.deepStrictEqual(await db.getSortedSetMembers(`upload:${md5(relativePath)}:pids`), [postData.pid.toString()]); + + await user.deleteUpload(uid, uid, relativePath); + + assert.strictEqual(await db.exists(`upload:${md5(relativePath)}:pids`), false); + }); }); });