feat: move all user profile pics to folder, closes #12449 (#12450)

* feat: move all user profile pics to folder

get rid of glob delete and just delete the uid-{uid} folder when deleting user images

* when exporting user uploads add all profile uploads

* uid check
This commit is contained in:
Barış Soner Uşaklı
2024-03-28 16:50:56 -04:00
committed by GitHub
parent aef3ea18cf
commit 8f9ac5c17a
5 changed files with 99 additions and 17 deletions

View File

@@ -18,8 +18,7 @@ const plugins = require('../plugins');
const events = require('../events');
const translator = require('../translator');
const sockets = require('../socket.io');
// const api = require('.');
const utils = require('../utils');
const usersAPI = module.exports;
@@ -686,6 +685,9 @@ usersAPI.generateExport = async (caller, { uid, type }) => {
if (!validTypes.includes(type)) {
throw new Error('[[error:invalid-data]]');
}
if (!utils.isNumber(uid) || !(parseInt(uid, 10) > 0)) {
throw new Error('[[error:invalid-uid]]');
}
const count = await db.incrObjectField('locks', `export:${uid}${type}`);
if (count > 1) {
throw new Error('[[error:already-exporting]]');

View File

@@ -0,0 +1,86 @@
'use strict';
const fs = require('fs');
const nconf = require('nconf');
const path = require('path');
const { mkdirp } = require('mkdirp');
const db = require('../../database');
const batch = require('../../batch');
module.exports = {
name: 'Create user upload folders',
timestamp: Date.UTC(2024, 4, 28),
method: async function () {
const { progress } = this;
const folder = path.join(nconf.get('upload_path'), 'profile');
const userPicRegex = /^\d+-profile/;
const files = (await fs.promises.readdir(folder, { withFileTypes: true }))
.filter(item => !item.isDirectory() && String(item.name).match(userPicRegex))
.map(item => item.name);
progress.total = files.length;
await batch.processArray(files, async (files) => {
progress.incr(files.length);
await Promise.all(files.map(async (file) => {
const uid = file.split('-')[0];
if (parseInt(uid, 10) > 0) {
await mkdirp(path.join(folder, `uid-${uid}`));
await fs.promises.rename(
path.join(folder, file),
path.join(folder, `uid-${uid}`, file),
);
}
}));
}, {
batch: 500,
});
await batch.processSortedSet('users:joindate', async (uids) => {
progress.incr(uids.length);
const usersData = await db.getObjects(uids.map(uid => `user:${uid}`));
const bulkSet = [];
usersData.forEach((userData) => {
const setObj = {};
if (userData && userData.picture &&
userData.picture.includes(`/uploads/profile/${userData.uid}-`) &&
!userData.picture.includes(`/uploads/profile/uid-${userData.uid}/${userData.uid}-`)) {
setObj.picture = userData.picture.replace(
`/uploads/profile/${userData.uid}-`,
`/uploads/profile/uid-${userData.uid}/${userData.uid}-`
);
}
if (userData && userData.uploadedpicture &&
userData.uploadedpicture.includes(`/uploads/profile/${userData.uid}-`) &&
!userData.uploadedpicture.includes(`/uploads/profile/uid-${userData.uid}/${userData.uid}-`)) {
setObj.uploadedpicture = userData.uploadedpicture.replace(
`/uploads/profile/${userData.uid}-`,
`/uploads/profile/uid-${userData.uid}/${userData.uid}-`
);
}
if (userData && userData['cover:url'] &&
userData['cover:url'].includes(`/uploads/profile/${userData.uid}-`) &&
!userData['cover:url'].includes(`/uploads/profile/uid-${userData.uid}/${userData.uid}-`)) {
setObj['cover:url'] = userData['cover:url'].replace(
`/uploads/profile/${userData.uid}-`,
`/uploads/profile/uid-${userData.uid}/${userData.uid}-`
);
}
if (Object.keys(setObj).length) {
bulkSet.push([`user:${userData.uid}`, setObj]);
}
});
await db.setObjectBulk(bulkSet);
}, {
batch: 500,
progress: progress,
});
},
};

View File

@@ -227,7 +227,7 @@ module.exports = function (User) {
}
async function deleteImages(uid) {
const folder = path.join(nconf.get('upload_path'), 'profile');
await rimraf(`${uid}-profile{avatar,cover}*`, { glob: { cwd: folder } });
const folder = path.join(nconf.get('upload_path'), 'profile', `uid-${uid}`);
await rimraf(folder);
}
};

View File

@@ -74,14 +74,8 @@ process.on('message', async (msg) => {
winston.verbose(`[user/export/uploads] Collating uploads for uid ${targetUid}`);
await user.collateUploads(targetUid, archive);
const uploadedPicture = await user.getUserField(targetUid, 'uploadedpicture');
if (uploadedPicture) {
const filePath = uploadedPicture.replace(nconf.get('upload_url'), '');
archive.file(path.join(nconf.get('upload_path'), filePath), {
name: path.basename(filePath),
});
}
const profileUploadPath = path.join(nconf.get('upload_path'), `profile/uid-${targetUid}`);
archive.directory(profileUploadPath, 'profile');
archive.finalize();
}
});

View File

@@ -50,7 +50,7 @@ module.exports = function (User) {
const extension = file.typeToExtension(image.mimeFromBase64(data.imageData));
const filename = `${data.uid}-profilecover-${Date.now()}${extension}`;
const uploadData = await image.uploadImage(filename, 'profile', picture);
const uploadData = await image.uploadImage(filename, `profile/uid-${data.uid}`, picture);
await deleteCurrentPicture(data.uid, 'cover:url');
await User.setUserField(data.uid, 'cover:url', uploadData.url);
@@ -96,7 +96,7 @@ module.exports = function (User) {
});
const filename = generateProfileImageFilename(data.uid, extension);
const uploadedImage = await image.uploadImage(filename, 'profile', {
const uploadedImage = await image.uploadImage(filename, `profile/uid-${data.uid}`, {
uid: data.uid,
path: newPath,
name: 'profileAvatar',
@@ -140,7 +140,7 @@ module.exports = function (User) {
});
const filename = generateProfileImageFilename(data.uid, extension);
const uploadedImage = await image.uploadImage(filename, 'profile', picture);
const uploadedImage = await image.uploadImage(filename, `profile/uid-${data.uid}`, picture);
await deleteCurrentPicture(data.uid, 'uploadedpicture');
await User.updateProfile(data.callerUid, {
@@ -224,10 +224,10 @@ module.exports = function (User) {
async function getPicturePath(uid, field) {
const value = await User.getUserField(uid, field);
if (!value || !value.startsWith(`${nconf.get('relative_path')}/assets/uploads/profile/`)) {
if (!value || !value.startsWith(`${nconf.get('relative_path')}/assets/uploads/profile/uid-${uid}`)) {
return false;
}
const filename = value.split('/').pop();
return path.join(nconf.get('upload_path'), 'profile', filename);
return path.join(nconf.get('upload_path'), `profile/uid-${uid}`, filename);
}
};