From f3fffa56c9fa836edc9b37ff8e4b0ac7e5454715 Mon Sep 17 00:00:00 2001 From: Ajay Date: Thu, 8 Feb 2024 15:47:25 -0500 Subject: [PATCH] Don't allow downvoting locked segments --- src/routes/postBranding.ts | 20 ++++++++++++++++- test/cases/postBranding.ts | 45 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/routes/postBranding.ts b/src/routes/postBranding.ts index b56102c..8afd00e 100644 --- a/src/routes/postBranding.ts +++ b/src/routes/postBranding.ts @@ -70,13 +70,20 @@ export async function postBranding(req: Request, res: Response) { return; } + let errorCode = 0; + await Promise.all([(async () => { if (title) { // ignore original submissions from banned users - hiding those would cause issues if (title.original && isBanned) return; const existingUUID = (await db.prepare("get", `SELECT "UUID" from "titles" where "videoID" = ? AND "title" = ?`, [videoID, title.title]))?.UUID; + const existingIsLocked = !!existingUUID && (await db.prepare("get", `SELECT "locked" from "titleVotes" where "UUID" = ?`, [existingUUID]))?.locked; if (existingUUID != undefined && isBanned) return; // ignore votes on existing details from banned users + if (downvote && existingIsLocked && !isVip) { + errorCode = 403; + return; + } const UUID = existingUUID || crypto.randomUUID(); await handleExistingVotes(BrandingType.Title, videoID, hashedUserID, UUID, hashedIP, voteType); @@ -117,7 +124,12 @@ export async function postBranding(req: Request, res: Response) { ? (await db.prepare("get", `SELECT "UUID" from "thumbnails" where "videoID" = ? AND "original" = 1`, [videoID]))?.UUID : (await db.prepare("get", `SELECT "thumbnails"."UUID" from "thumbnailTimestamps" JOIN "thumbnails" ON "thumbnails"."UUID" = "thumbnailTimestamps"."UUID" WHERE "thumbnailTimestamps"."timestamp" = ? AND "thumbnails"."videoID" = ?`, [(thumbnail as TimeThumbnailSubmission).timestamp, videoID]))?.UUID; + const existingIsLocked = !!existingUUID && (await db.prepare("get", `SELECT "locked" from "titleVotes" where "UUID" = ?`, [existingUUID]))?.locked; if (existingUUID != undefined && isBanned) return; // ignore votes on existing details from banned users + if (downvote && existingIsLocked && !isVip) { + errorCode = 403; + return; + } const UUID = existingUUID || crypto.randomUUID(); await handleExistingVotes(BrandingType.Thumbnail, videoID, hashedUserID, UUID, hashedIP, voteType); @@ -152,7 +164,13 @@ export async function postBranding(req: Request, res: Response) { })()]); QueryCacher.clearBrandingCache({ videoID, hashedVideoID, service }); - res.status(200).send("OK"); + + if (errorCode) { + res.status(errorCode).send(); + } else { + res.status(200).send("OK"); + } + lock.unlock(); } catch (e) { Logger.error(e as string); diff --git a/test/cases/postBranding.ts b/test/cases/postBranding.ts index 7e67cc6..f4d606d 100644 --- a/test/cases/postBranding.ts +++ b/test/cases/postBranding.ts @@ -358,6 +358,51 @@ describe("postBranding", () => { } }); + it("Fail to downvote locked title and thumbnail", async () => { + const videoID = "postBrandRemoved1"; + const title = { + title: "Some other title", + original: false + }; + const thumbnail = { + timestamp: 12.34, + original: false + }; + + const res = await postBranding({ + title, + thumbnail, + userID: userID6, + service: Service.YouTube, + videoID, + downvote: true + }); + + assert.strictEqual(res.status, 403); + const dbTitles = await queryTitleByVideo(videoID, true); + for (const dbTitle of dbTitles) { + if (dbTitle.title === title.title) { + const dbTitleVotes = await queryTitleVotesByUUID(dbTitle.UUID); + assert.strictEqual(dbTitleVotes.votes, 0); + assert.strictEqual(dbTitleVotes.downvotes, 0); + assert.strictEqual(dbTitleVotes.locked, 1); + assert.strictEqual(dbTitleVotes.shadowHidden, 0); + } + } + + const dbThumbnails = await queryThumbnailByVideo(videoID, true); + for (const dbThumbnail of dbThumbnails) { + if (dbThumbnail.timestamp === thumbnail.timestamp) { + const dbThumbnailVotes = await queryThumbnailVotesByUUID(dbThumbnail.UUID); + + assert.strictEqual(dbThumbnailVotes.votes, 0); + assert.strictEqual(dbThumbnailVotes.downvotes, 0); + assert.strictEqual(dbThumbnailVotes.locked, 1); + assert.strictEqual(dbThumbnailVotes.shadowHidden, 0); + } + } + }); + it("Upvote after downvoting title and thumbnail", async () => { const videoID = "postBrand5"; const title = {