Merge branch 'develop' of https://github.com/NodeBB/NodeBB into develop

This commit is contained in:
Barış Soner Uşaklı
2023-10-12 10:26:06 -04:00
102 changed files with 395 additions and 6 deletions

View File

@@ -49,6 +49,16 @@ flagsApi.update = async (caller, data) => {
flagsApi.delete = async (_, { flagId }) => await flags.purge([flagId]);
flagsApi.rescind = async ({ uid }, { flagId }) => {
const { type, targetId } = await flags.get(flagId);
const exists = await flags.exists(type, targetId, uid);
if (!exists) {
throw new Error('[[error:no-flag]]');
}
await flags.rescindReport(type, targetId, uid);
};
flagsApi.appendNote = async (caller, data) => {
const allowed = await user.isPrivileged(caller.uid);
if (!allowed) {

View File

@@ -48,6 +48,12 @@ exports.handleErrors = async function handleErrors(err, req, res, next) { // esl
res.status(403).type('text/plain').send(err.message);
},
};
const notFoundHandler = () => {
const controllers = require('.');
controllers['404'].handle404(req, res);
};
const defaultHandler = async function () {
if (res.headersSent) {
return;
@@ -87,6 +93,8 @@ exports.handleErrors = async function handleErrors(err, req, res, next) { // esl
try {
if (data.cases.hasOwnProperty(err.code)) {
data.cases[err.code](err, req, res, defaultHandler);
} else if (err.message.startsWith('[[error:no-') && err.message !== '[[error:no-privileges]]') {
notFoundHandler();
} else {
await defaultHandler();
}

View File

@@ -13,7 +13,7 @@ Flags.create = async (req, res) => {
};
Flags.get = async (req, res) => {
helpers.formatApiResponse(200, res, await api.flags.get(req, req.params.flagId));
helpers.formatApiResponse(200, res, await api.flags.get(req, req.params));
};
Flags.update = async (req, res) => {
@@ -32,6 +32,11 @@ Flags.delete = async (req, res) => {
helpers.formatApiResponse(200, res);
};
Flags.rescind = async (req, res) => {
await api.flags.rescind(req, { flagId: req.params.flagId });
helpers.formatApiResponse(200, res);
};
Flags.appendNote = async (req, res) => {
const { note, datetime } = req.body;
const payload = await api.flags.appendNote(req, {

View File

@@ -109,7 +109,7 @@ Flags.get = async function (flagId) {
Flags.getReports(flagId),
]);
if (!base) {
return;
throw new Error('[[error:no-flag]]');
}
const flagObj = {
state: 'open',
@@ -417,7 +417,10 @@ Flags.create = async function (type, id, uid, reason, timestamp, forceFlag = fal
const flagId = await Flags.getFlagIdByTarget(type, id);
await Promise.all([
Flags.addReport(flagId, type, id, uid, reason, timestamp),
Flags.update(flagId, uid, { state: 'open' }),
Flags.update(flagId, uid, {
state: 'open',
report: 'added',
}),
]);
return await Flags.get(flagId);
@@ -542,6 +545,7 @@ Flags.getReports = async function (flagId) {
return reports;
};
// Not meant to be called directly, call Flags.create() instead.
Flags.addReport = async function (flagId, type, id, uid, reason, timestamp) {
await db.sortedSetAddBulk([
[`flags:byReporter:${uid}`, timestamp, flagId],
@@ -553,6 +557,45 @@ Flags.addReport = async function (flagId, type, id, uid, reason, timestamp) {
plugins.hooks.fire('action:flags.addReport', { flagId, type, id, uid, reason, timestamp });
};
Flags.rescindReport = async (type, id, uid) => {
const exists = await Flags.exists(type, id, uid);
if (!exists) {
return true;
}
const flagId = await db.sortedSetScore('flags:hash', [type, id, uid].join(':'));
const reports = await db.getSortedSetMembers(`flag:${flagId}:reports`);
let reason;
reports.forEach((payload) => {
if (!reason) {
const [payloadUid, payloadReason] = payload.split(';');
if (parseInt(payloadUid, 10) === parseInt(uid, 10)) {
reason = payloadReason;
}
}
});
if (!reason) {
throw new Error('[[error:cant-locate-flag-report]]');
}
await db.sortedSetRemoveBulk([
[`flags:byReporter:${uid}`, flagId],
[`flag:${flagId}:reports`, [uid, reason].join(';')],
['flags:hash', [type, id, uid].join(':')],
]);
// If there are no more reports, consider the flag resolved
const reportCount = await db.sortedSetCard(`flag:${flagId}:reports`);
if (reportCount < 1) {
await Flags.update(flagId, uid, {
state: 'resolved',
report: 'rescinded',
});
}
};
Flags.exists = async function (type, id, uid) {
return await db.isSortedSetMember('flags:hash', [type, id, uid].join(':'));
};
@@ -766,6 +809,9 @@ Flags.getHistory = async function (flagId) {
if (changeset.hasOwnProperty('state')) {
changeset.state = changeset.state === undefined ? '' : `[[flags:state-${changeset.state}]]`;
}
if (changeset.hasOwnProperty('report')) {
changeset.report = `[[flags:report-${changeset.report}]]`;
}
return {
uid: entry.value[0],

View File

@@ -12,10 +12,13 @@ module.exports = function () {
setupApiRoute(router, 'post', '/', [...middlewares], controllers.write.flags.create);
// Note: access control provided by middleware.assert.flag
setupApiRoute(router, 'get', '/:flagId', [...middlewares, middleware.assert.flag], controllers.write.flags.get);
setupApiRoute(router, 'put', '/:flagId', [...middlewares, middleware.assert.flag], controllers.write.flags.update);
setupApiRoute(router, 'delete', '/:flagId', [...middlewares, middleware.assert.flag], controllers.write.flags.delete);
setupApiRoute(router, 'delete', '/:flagId/report', middlewares, controllers.write.flags.rescind);
setupApiRoute(router, 'post', '/:flagId/notes', [...middlewares, middleware.assert.flag], controllers.write.flags.appendNote);
setupApiRoute(router, 'delete', '/:flagId/notes/:datetime', [...middlewares, middleware.assert.flag], controllers.write.flags.deleteNote);