mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-27 00:56:13 +01:00
feat: more work on topic thumbs refactor
- addThumb and deleteThumb are now protected routes (duh) - new getThumbs route GET /api/v3/topics/<tid>/thumbs - Updated `assert.path` middleware to better handle if relative paths are received with upload_url - Slight refactor of thumbs lib to use validator to differentiate between tid and UUID
This commit is contained in:
@@ -4,6 +4,7 @@ const validator = require('validator');
|
|||||||
|
|
||||||
const api = require('../../api');
|
const api = require('../../api');
|
||||||
const topics = require('../../topics');
|
const topics = require('../../topics');
|
||||||
|
const privileges = require('../../privileges');
|
||||||
|
|
||||||
const helpers = require('../helpers');
|
const helpers = require('../helpers');
|
||||||
const uploadsController = require('../uploads');
|
const uploadsController = require('../uploads');
|
||||||
@@ -89,6 +90,10 @@ Topics.deleteTags = async (req, res) => {
|
|||||||
helpers.formatApiResponse(200, res);
|
helpers.formatApiResponse(200, res);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Topics.getThumbs = async (req, res) => {
|
||||||
|
helpers.formatApiResponse(200, res, await topics.thumbs.get(req.params.tid));
|
||||||
|
};
|
||||||
|
|
||||||
Topics.addThumb = async (req, res) => {
|
Topics.addThumb = async (req, res) => {
|
||||||
// req.params.tid could be either a tid (pushing a new thumb to an existing topic) or a post UUID (a new topic being composed)
|
// req.params.tid could be either a tid (pushing a new thumb to an existing topic) or a post UUID (a new topic being composed)
|
||||||
const id = req.params.tid;
|
const id = req.params.tid;
|
||||||
@@ -98,23 +103,34 @@ Topics.addThumb = async (req, res) => {
|
|||||||
if (!isUUID && (isNaN(parseInt(id, 10)) || !await topics.exists(req.params.tid))) {
|
if (!isUUID && (isNaN(parseInt(id, 10)) || !await topics.exists(req.params.tid))) {
|
||||||
return helpers.formatApiResponse(404, res, new Error('[[error:no-topic]]'));
|
return helpers.formatApiResponse(404, res, new Error('[[error:no-topic]]'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// While drafts are not protected, tids are
|
||||||
|
if (!isUUID && !await privileges.topics.canEdit(req.params.tid, req.user.uid)) {
|
||||||
|
return helpers.formatApiResponse(403, res, new Error('[[error:no-privileges]]'));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* todo test:
|
* todo test:
|
||||||
* - uuid
|
* - uuid
|
||||||
* - tid
|
* - tid
|
||||||
* - number but not tid
|
* - number but not tid
|
||||||
* - random garbage
|
* - random garbage
|
||||||
|
* - wrong caller uid (unpriv)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const files = await uploadsController.uploadThumb(req, res); // response is handled here, fix this?
|
const files = await uploadsController.uploadThumb(req, res); // response is handled here, fix this?
|
||||||
|
|
||||||
// Add uploaded files to topic zset
|
// Add uploaded files to topic zset
|
||||||
await Promise.all(files.map(async (fileObj) => {
|
await Promise.all(files.map(async (fileObj) => {
|
||||||
await topics.thumbs.associate(id, fileObj.path, isUUID);
|
await topics.thumbs.associate(id, fileObj.path);
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
Topics.deleteThumb = async (req, res) => {
|
Topics.deleteThumb = async (req, res) => {
|
||||||
|
if (!await privileges.topics.canEdit(req.params.tid, req.user.uid)) {
|
||||||
|
return helpers.formatApiResponse(403, res, new Error('[[error:no-privileges]]'));
|
||||||
|
}
|
||||||
|
|
||||||
await topics.thumbs.delete(req.params.tid, req.body.path);
|
await topics.thumbs.delete(req.params.tid, req.body.path);
|
||||||
helpers.formatApiResponse(200, res, await topics.thumbs.get(req.params.tid));
|
helpers.formatApiResponse(200, res, await topics.thumbs.get(req.params.tid));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -58,6 +58,11 @@ Assert.path = helpers.try(async (req, res, next) => {
|
|||||||
req.body.path = new URL(req.body.path).pathname;
|
req.body.path = new URL(req.body.path).pathname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Strip upload_url if found
|
||||||
|
if (req.body.path.startsWith(nconf.get('upload_url'))) {
|
||||||
|
req.body.path = req.body.path.slice(nconf.get('upload_url').length);
|
||||||
|
}
|
||||||
|
|
||||||
const pathToFile = path.join(nconf.get('upload_path'), req.body.path);
|
const pathToFile = path.join(nconf.get('upload_path'), req.body.path);
|
||||||
res.locals.cleanedPath = pathToFile;
|
res.locals.cleanedPath = pathToFile;
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ module.exports = function () {
|
|||||||
setupApiRoute(router, 'put', '/:tid/tags', [...middlewares, middleware.checkRequired.bind(null, ['tags']), middleware.assert.topic], controllers.write.topics.addTags);
|
setupApiRoute(router, 'put', '/:tid/tags', [...middlewares, middleware.checkRequired.bind(null, ['tags']), middleware.assert.topic], controllers.write.topics.addTags);
|
||||||
setupApiRoute(router, 'delete', '/:tid/tags', [...middlewares, middleware.assert.topic], controllers.write.topics.deleteTags);
|
setupApiRoute(router, 'delete', '/:tid/tags', [...middlewares, middleware.assert.topic], controllers.write.topics.deleteTags);
|
||||||
|
|
||||||
|
setupApiRoute(router, 'get', '/:tid/thumbs', [], controllers.write.topics.getThumbs);
|
||||||
setupApiRoute(router, 'post', '/:tid/thumbs', [multipartMiddleware, middleware.validateFiles, ...middlewares], controllers.write.topics.addThumb);
|
setupApiRoute(router, 'post', '/:tid/thumbs', [multipartMiddleware, middleware.validateFiles, ...middlewares], controllers.write.topics.addThumb);
|
||||||
setupApiRoute(router, 'delete', '/:tid/thumbs', [...middlewares, middleware.assert.topic, middleware.assert.path], controllers.write.topics.deleteThumb);
|
setupApiRoute(router, 'delete', '/:tid/thumbs', [...middlewares, middleware.assert.topic, middleware.assert.path], controllers.write.topics.deleteThumb);
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var nconf = require('nconf');
|
const nconf = require('nconf');
|
||||||
var path = require('path');
|
const path = require('path');
|
||||||
|
const validator = require('validator');
|
||||||
|
|
||||||
const db = require('../database');
|
const db = require('../database');
|
||||||
var file = require('../file');
|
const file = require('../file');
|
||||||
var plugins = require('../plugins');
|
const plugins = require('../plugins');
|
||||||
|
|
||||||
const Thumbs = {};
|
const Thumbs = {};
|
||||||
module.exports = Thumbs;
|
module.exports = Thumbs;
|
||||||
@@ -25,7 +26,7 @@ Thumbs.get = async function (tids) {
|
|||||||
singular = true;
|
singular = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sets = tids.map(tid => `topic:${tid}:thumbs`);
|
const sets = tids.map(tid => `${validator.isUUID(String(tid)) ? 'draft' : 'topic'}:${tid}:thumbs`);
|
||||||
const thumbs = await db.getSortedSetsMembers(sets);
|
const thumbs = await db.getSortedSetsMembers(sets);
|
||||||
let response = thumbs.map(thumbSet => thumbSet.map(thumb => ({
|
let response = thumbs.map(thumbSet => thumbSet.map(thumb => ({
|
||||||
url: path.join(nconf.get('upload_url'), thumb),
|
url: path.join(nconf.get('upload_url'), thumb),
|
||||||
@@ -35,8 +36,9 @@ Thumbs.get = async function (tids) {
|
|||||||
return singular ? response.pop() : response;
|
return singular ? response.pop() : response;
|
||||||
};
|
};
|
||||||
|
|
||||||
Thumbs.associate = async function (id, path, isDraft) {
|
Thumbs.associate = async function (id, path) {
|
||||||
// Associates a newly uploaded file as a thumb to the passed-in tid
|
// Associates a newly uploaded file as a thumb to the passed-in draft or topic
|
||||||
|
const isDraft = validator.isUUID(String(id));
|
||||||
const set = `${isDraft ? 'draft' : 'topic'}:${id}:thumbs`;
|
const set = `${isDraft ? 'draft' : 'topic'}:${id}:thumbs`;
|
||||||
const numThumbs = await db.sortedSetCard(set);
|
const numThumbs = await db.sortedSetCard(set);
|
||||||
path = path.replace(nconf.get('upload_path'), '');
|
path = path.replace(nconf.get('upload_path'), '');
|
||||||
@@ -47,7 +49,7 @@ Thumbs.commit = async function (uuid, tid) {
|
|||||||
// Converts the draft thumb zset to the topic zset (combines thumbs if applicable)
|
// Converts the draft thumb zset to the topic zset (combines thumbs if applicable)
|
||||||
const set = `draft:${uuid}:thumbs`;
|
const set = `draft:${uuid}:thumbs`;
|
||||||
const thumbs = await db.getSortedSetRange(set, 0, -1);
|
const thumbs = await db.getSortedSetRange(set, 0, -1);
|
||||||
await Promise.all(thumbs.map(async path => await Thumbs.associate(tid, path, false)));
|
await Promise.all(thumbs.map(async path => await Thumbs.associate(tid, path)));
|
||||||
await db.delete(set);
|
await db.delete(set);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user