diff --git a/package-lock.json b/package-lock.json index 0cbfd17..e5ebcff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@ajayyy/lru-diskcache": "^1.1.9", "@types/request": "^2.48.6", "better-sqlite3": "^7.4.1", + "cron": "^1.8.2", "express": "^4.17.1", "express-promise-router": "^4.1.0", "express-rate-limit": "^5.3.0", @@ -22,6 +23,7 @@ }, "devDependencies": { "@types/better-sqlite3": "^5.4.3", + "@types/cron": "^1.7.3", "@types/express": "^4.17.13", "@types/express-rate-limit": "^5.1.3", "@types/mocha": "^8.2.3", @@ -371,6 +373,16 @@ "@types/node": "*" } }, + "node_modules/@types/cron": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@types/cron/-/cron-1.7.3.tgz", + "integrity": "sha512-iPmUXyIJG1Js+ldPYhOQcYU3kCAQ2FWrSkm1FJPoii2eYSn6wEW6onPukNTT0bfiflexNSRPl6KWmAIqS+36YA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "moment": ">=2.14.0" + } + }, "node_modules/@types/express": { "version": "4.17.13", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", @@ -1537,6 +1549,14 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/cron": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/cron/-/cron-1.8.2.tgz", + "integrity": "sha512-Gk2c4y6xKEO8FSAUTklqtfSr7oTq0CiPQeLBG5Fl0qoXpZyMcj1SG59YL+hqq04bu6/IuEA7lMkYDAplQNKkyg==", + "dependencies": { + "moment-timezone": "^0.5.x" + } + }, "node_modules/cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", @@ -3474,6 +3494,25 @@ "node": ">= 8" } }, + "node_modules/moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.33", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.33.tgz", + "integrity": "sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==", + "dependencies": { + "moment": ">= 2.9.0" + }, + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -5856,6 +5895,16 @@ "@types/node": "*" } }, + "@types/cron": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@types/cron/-/cron-1.7.3.tgz", + "integrity": "sha512-iPmUXyIJG1Js+ldPYhOQcYU3kCAQ2FWrSkm1FJPoii2eYSn6wEW6onPukNTT0bfiflexNSRPl6KWmAIqS+36YA==", + "dev": true, + "requires": { + "@types/node": "*", + "moment": ">=2.14.0" + } + }, "@types/express": { "version": "4.17.13", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", @@ -6776,6 +6825,14 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "cron": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/cron/-/cron-1.8.2.tgz", + "integrity": "sha512-Gk2c4y6xKEO8FSAUTklqtfSr7oTq0CiPQeLBG5Fl0qoXpZyMcj1SG59YL+hqq04bu6/IuEA7lMkYDAplQNKkyg==", + "requires": { + "moment-timezone": "^0.5.x" + } + }, "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", @@ -8229,6 +8286,19 @@ } } }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, + "moment-timezone": { + "version": "0.5.33", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.33.tgz", + "integrity": "sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==", + "requires": { + "moment": ">= 2.9.0" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", diff --git a/src/cronjob/downvoteSegmentArchiveJob.ts b/src/cronjob/downvoteSegmentArchiveJob.ts index a619b47..6fa4f71 100644 --- a/src/cronjob/downvoteSegmentArchiveJob.ts +++ b/src/cronjob/downvoteSegmentArchiveJob.ts @@ -56,8 +56,12 @@ export const archiveDownvoteSegment = async (dayLimit: number, voteLimit: number }; const DownvoteSegmentArchiveJob = new CronJob( - jobConfig?.schedule || new Date(1), + jobConfig?.schedule || "0 0 * * * 0", () => archiveDownvoteSegment(jobConfig?.timeThresholdInDays, jobConfig?.voteThreshold) ); +if (serverConfig?.crons?.enabled && jobConfig && !jobConfig.schedule) { + Logger.error("Invalid cron schedule for downvoteSegmentArchive"); +} + export default DownvoteSegmentArchiveJob; diff --git a/src/routes/postSkipSegments.ts b/src/routes/postSkipSegments.ts index d160218..5131055 100644 --- a/src/routes/postSkipSegments.ts +++ b/src/routes/postSkipSegments.ts @@ -338,11 +338,13 @@ export async function postSkipSegments(req: Request, res: Response): Promise p + (i !== 0 ? ", " : "") + c, ""); - return res.status(400).send(`No valid ${fields} field(s) provided`); + const formattedFields = invalidFields.reduce((p, c, i) => p + (i !== 0 ? ", " : "") + c, ""); + const formattedErrors = errors.reduce((p, c, i) => p + (i !== 0 ? ". " : " ") + c, ""); + return res.status(400).send(`No valid ${formattedFields} field(s) provided.${formattedErrors}`); } //hash the userID diff --git a/src/routes/setUsername.ts b/src/routes/setUsername.ts index 7ba454b..9361216 100644 --- a/src/routes/setUsername.ts +++ b/src/routes/setUsername.ts @@ -62,7 +62,7 @@ export async function setUsername(req: Request, res: Response): Promise 0) { + if (row?.userName !== undefined) { //already exists, update this row oldUserName = row.userName; await db.prepare("run", `UPDATE "userNames" SET "userName" = ?, "locked" = ? WHERE "userID" = ?`, [userName, locked, userID]); diff --git a/src/routes/voteOnSponsorTime.ts b/src/routes/voteOnSponsorTime.ts index a177385..5e204fb 100644 --- a/src/routes/voteOnSponsorTime.ts +++ b/src/routes/voteOnSponsorTime.ts @@ -256,6 +256,10 @@ export async function voteOnSponsorTime(req: Request, res: Response): Promise 0 && voteTypeEnum === voteTypes.normal) { // Unide and Lock this submission await db.prepare("run", 'UPDATE "sponsorTimes" SET locked = 1, hidden = 0 WHERE "UUID" = ?', [UUID]); - } else if (isVIP && incrementAmount < 0 && voteTypeEnum === voteTypes.normal) { + } else if (isVIP && incrementAmount <= 0 && voteTypeEnum === voteTypes.normal) { // Unlock if a VIP downvotes it await db.prepare("run", 'UPDATE "sponsorTimes" SET locked = 0 WHERE "UUID" = ?', [UUID]); } diff --git a/test/cases/lockCategoriesRecords.ts b/test/cases/lockCategoriesRecords.ts index d7f682e..4de53a2 100644 --- a/test/cases/lockCategoriesRecords.ts +++ b/test/cases/lockCategoriesRecords.ts @@ -5,12 +5,6 @@ import {db} from "../../src/databases/databases"; import assert from "assert"; import {LockCategory} from "../../src/types/segments.model"; -const deepStringEqual = (a: string[], b: string[]): boolean => { - if (a.length !== b.length) return false; - a.forEach((e) => { if (!b.includes(e)) return false; }); - return true; -}; - describe("lockCategoriesRecords", () => { before(async () => { const insertVipUserQuery = 'INSERT INTO "vipUsers" ("userID") VALUES (?)'; @@ -571,7 +565,7 @@ describe("lockCategoriesRecords", () => { .then(async res => { assert.strictEqual(res.status, 200); const data = await res.json(); - assert.ok(deepStringEqual(data.categories, expected.categories)); + assert.deepStrictEqual(data.categories, expected.categories); done(); }) .catch(err => done(err));