Add ability to add manually choose who can submit chapters

This commit is contained in:
Ajay
2022-07-06 00:11:45 -04:00
parent 47f460bb2c
commit c2b0ecd6f6
14 changed files with 292 additions and 22 deletions

72
src/routes/addFeature.ts Normal file
View File

@@ -0,0 +1,72 @@
import { getHashCache } from "../utils/getHashCache";
import { db } from "../databases/databases";
import { config } from "../config";
import { Request, Response } from "express";
import { isUserVIP } from "../utils/isUserVIP";
import { Feature, HashedUserID } from "../types/user.model";
import { Logger } from "../utils/logger";
import { QueryCacher } from "../utils/queryCacher";
interface AddFeatureRequest extends Request {
body: {
userID: HashedUserID;
adminUserID: string;
feature: string;
enabled: string;
}
}
const allowedFeatures = {
vip: [
Feature.ChapterSubmitter
],
admin: [
Feature.ChapterSubmitter
]
}
export async function addFeature(req: AddFeatureRequest, res: Response): Promise<Response> {
const { body: { userID, adminUserID } } = req;
const feature = parseInt(req.body.feature) as Feature;
const enabled = req.body?.enabled !== "false";
if (!userID || !adminUserID) {
// invalid request
return res.sendStatus(400);
}
// hash the userID
const adminUserIDInput = await getHashCache(adminUserID);
const isAdmin = adminUserIDInput !== config.adminUserID;
const isVIP = (await isUserVIP(userID)) || isAdmin;
if (!isAdmin && !isVIP) {
// not authorized
return res.sendStatus(403);
}
try {
const currentAllowedFeatures = isAdmin ? allowedFeatures.admin : allowedFeatures.vip;
if (currentAllowedFeatures.includes(feature)) {
if (enabled) {
const featureAdded = await db.prepare("get", 'SELECT "feature" from "userFeatures" WHERE "userID" = ? AND "feature" = ?', [userID, feature]);
if (!featureAdded) {
await db.prepare("run", 'INSERT INTO "userFeatures" ("userID", "feature", "issuerUserID", "timeSubmitted") VALUES(?, ?, ?, ?)'
, [userID, feature, adminUserID, Date.now()]);
}
} else {
await db.prepare("run", 'DELETE FROM "userFeatures" WHERE "userID" = ? AND "feature" = ?', [userID, feature]);
}
QueryCacher.clearFeatureCache(userID, feature);
} else {
return res.status(400).send("Invalid feature");
}
return res.sendStatus(200);
} catch (e) {
Logger.error(e as string);
return res.sendStatus(500);
}
}

View File

@@ -7,6 +7,7 @@ import { HashedUserID, UserID } from "../types/user.model";
import { getReputation } from "../utils/reputation";
import { SegmentUUID } from "../types/segments.model";
import { config } from "../config";
import { canSubmitChapter } from "../utils/permissions";
const maxRewardTime = config.maxRewardTimePerSegmentInSeconds;
async function dbGetSubmittedSegmentSummary(userID: HashedUserID): Promise<{ minutesSaved: number, segmentCount: number }> {
@@ -105,10 +106,6 @@ async function dbGetBanned(userID: HashedUserID): Promise<boolean> {
}
}
async function dbCanSubmitChapter(userID: HashedUserID): Promise<boolean> {
return (await isUserVIP(userID)) || (await getReputation(userID)) > config.minReputationToSubmitChapter;
}
type cases = Record<string, any>
const executeIfFunction = (f: any) =>
@@ -133,7 +130,7 @@ const dbGetValue = (userID: HashedUserID, property: string): Promise<string|Segm
reputation: () => getReputation(userID),
vip: () => isUserVIP(userID),
lastSegmentID: () => dbGetLastSegmentForUser(userID),
canSubmitChapter: () => dbCanSubmitChapter(userID)
canSubmitChapter: () => canSubmitChapter(userID)
})("")(property);
};

View File

@@ -21,6 +21,7 @@ import { parseUserAgent } from "../utils/userAgent";
import { getService } from "../utils/getService";
import axios from "axios";
import { vote } from "./voteOnSponsorTime";
import { canSubmitChapter } from "../utils/permissions";
type CheckResult = {
pass: boolean,
@@ -200,7 +201,8 @@ async function checkUserActiveWarning(userID: string): Promise<CheckResult> {
return CHECK_PASS;
}
function checkInvalidFields(videoID: VideoID, userID: UserID, segments: IncomingSegment[]): CheckResult {
async function checkInvalidFields(videoID: VideoID, userID: UserID, hashedUserID: HashedUserID
, segments: IncomingSegment[]): Promise<CheckResult> {
const invalidFields = [];
const errors = [];
if (typeof videoID !== "string" || videoID?.length == 0) {
@@ -227,6 +229,10 @@ function checkInvalidFields(videoID: VideoID, userID: UserID, segments: Incoming
|| (segmentPair.description.length !== 0 && segmentPair.actionType !== ActionType.Chapter)) {
invalidFields.push("segment description");
}
if (segmentPair.actionType === ActionType.Chapter && !(await canSubmitChapter(hashedUserID))) {
invalidFields.push("permission to submit chapters");
}
}
if (invalidFields.length !== 0) {
@@ -478,14 +484,14 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
// eslint-disable-next-line prefer-const
let { videoID, userID: paramUserID, service, videoDuration, videoDurationParam, segments, userAgent } = preprocessInput(req);
const invalidCheckResult = checkInvalidFields(videoID, paramUserID, segments);
//hash the userID
const userID = await getHashCache(paramUserID || "");
const invalidCheckResult = await checkInvalidFields(videoID, paramUserID, userID, segments);
if (!invalidCheckResult.pass) {
return res.status(invalidCheckResult.errorCode).send(invalidCheckResult.errorMessage);
}
//hash the userID
const userID = await getHashCache(paramUserID);
const userWarningCheckResult = await checkUserActiveWarning(userID);
if (!userWarningCheckResult.pass) {
Logger.warn(`Caught a submission for a warned user. userID: '${userID}', videoID: '${videoID}', category: '${segments.reduce<string>((prev, val) => `${prev} ${val.category}`, "")}', times: ${segments.reduce<string>((prev, val) => `${prev} ${val.segment}`, "")}`);