diff --git a/src/app.ts b/src/app.ts index 3ce004f..71a85bd 100644 --- a/src/app.ts +++ b/src/app.ts @@ -31,6 +31,7 @@ import dumpDatabase, {redirectLink} from './routes/dumpDatabase'; import {endpoint as getSegmentInfo} from './routes/getSegmentInfo'; import {postClearCache} from './routes/postClearCache'; import { addUnlistedVideo } from './routes/addUnlistedVideo'; +import {postPurgeAllSegments} from './routes/postPurgeAllSegments'; export function createServer(callback: () => void) { // Create a service (the app object is just a callback). @@ -139,9 +140,12 @@ function setupRoutes(app: Express) { app.get('/api/segmentInfo', getSegmentInfo); //clear cache as VIP - app.post('/api/clearCache', postClearCache) + app.post('/api/clearCache', postClearCache); - app.post('/api/unlistedVideo', addUnlistedVideo) + //purge all segments for VIP + app.post('/api/purgeAllSegments', postPurgeAllSegments); + + app.post('/api/unlistedVideo', addUnlistedVideo); if (config.postgres) { app.get('/database', (req, res) => dumpDatabase(req, res, true)); diff --git a/src/routes/postPurgeAllSegments.ts b/src/routes/postPurgeAllSegments.ts new file mode 100644 index 0000000..9a05f4b --- /dev/null +++ b/src/routes/postPurgeAllSegments.ts @@ -0,0 +1,41 @@ +import {Logger} from '../utils/logger'; +import {getHash} from '../utils/getHash'; +import {isUserVIP} from '../utils/isUserVIP'; +import {Request, Response} from 'express'; +import {HashedUserID, UserID} from '../types/user.model'; +import {VideoID} from "../types/segments.model"; +import {db} from '../databases/databases'; + +export async function postPurgeAllSegments(req: Request, res: Response): Promise { + const userID = req.body.userID as UserID; + const videoID = req.body.videoID as VideoID; + + if (userID == undefined) { + //invalid request + res.sendStatus(400); + return; + } + + //hash the userID + const hashedUserID: HashedUserID = getHash(userID); + + try { + let vipState = await isUserVIP(hashedUserID); + if (!vipState) { + res.status(403).json({ + message: 'Must be a VIP to perform this action.', + }); + return; + } + + await db.prepare('run', `UPDATE "sponsorTimes" SET "hidden" = 1 WHERE "videoID" = ?`, [videoID]); + + } catch (err) { + Logger.error(err); + res.sendStatus(500); + + return; + } + + res.sendStatus(200); +} diff --git a/test/cases/postPurgeAllSegments.ts b/test/cases/postPurgeAllSegments.ts new file mode 100644 index 0000000..b9eef50 --- /dev/null +++ b/test/cases/postPurgeAllSegments.ts @@ -0,0 +1,82 @@ +import fetch from 'node-fetch'; +import {Done, getbaseURL} from '../utils'; +import {db} from '../../src/databases/databases'; +import {getHash} from '../../src/utils/getHash'; +import {IDatabase} from '../../src/databases/IDatabase'; + +async function dbSponsorTimesAdd(db: IDatabase, videoID: string, startTime: number, endTime: number, UUID: string, category: string) { + const votes = 0, + userID = 0, + timeSubmitted = 0, + views = 0, + shadowHidden = 0, + hidden = 0, + hashedVideoID = `hash_${UUID}`; + await db.prepare("run", `INSERT INTO + "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "UUID", + "userID", "timeSubmitted", "views", "category", "shadowHidden", "hashedVideoID", "hidden") + VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + [videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden, hashedVideoID, hidden]); +} + +async function dbSponsorTimesCompareExpect(db: IDatabase, videoId: string, expectdHidden: number) { + let seg = await db.prepare('get', `SELECT "hidden", "UUID" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoId]); + for (let i = 0, len = seg.length; i < len; i++) { + if (seg.hidden !== expectdHidden) { + return `${seg.UUID} hidden expected to be ${expectdHidden} but found ${seg.hidden}`; + } + } + return; +} + +describe('postPurgeAllSegments', function () { + const privateVipUserID = 'VIPUser-purgeAll'; + const route = '/api/purgeAllSegments'; + const vipUserID = getHash(privateVipUserID); + const baseURL = getbaseURL(); + + before(async function () { + // startTime and endTime get set in beforeEach for consistency + await dbSponsorTimesAdd(db, 'vsegpurge01', 0, 1, 'vsegpurgetest01uuid01', 'intro'); + await dbSponsorTimesAdd(db, 'vsegpurge01', 0, 2, 'vsegpurgetest01uuid02', 'sponsor'); + await dbSponsorTimesAdd(db, 'vsegpurge01', 0, 3, 'vsegpurgetest01uuid03', 'interaction'); + await dbSponsorTimesAdd(db, 'vsegpurge01', 0, 4, 'vsegpurgetest01uuid04', 'outro'); + await dbSponsorTimesAdd(db, 'vseg-not-purged01', 0, 5, 'vsegpurgetest01uuid05', 'outro'); + await db.prepare("run", `INSERT INTO "vipUsers" ("userID") VALUES (?)`, [vipUserID]); + }); + + it('Reject non-VIP user', function (done: Done) { + fetch(`${baseURL}${route}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + videoID: 'vsegpurge01', + userID: 'segshift_randomuser001', + }), + }) + .then(async res => { + done(res.status === 403 ? undefined : res.status); + }) + .catch(err => done(err)); + }); + + it('Purge all segments success', function (done: Done) { + fetch(`${baseURL}${route}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + videoID: 'vsegpurge01', + userID: privateVipUserID, + }), + }) + .then(async res => { + if (res.status !== 200) return done(`Status code was ${res.status}`); + done(await dbSponsorTimesCompareExpect(db, 'vsegpurge01', 1) || await dbSponsorTimesCompareExpect(db, 'vseg-not-purged01', 0)); + }) + .catch(err => done(err)); + }); +});