experimental: misc ergonomic checking

This commit is contained in:
zkldi
2022-05-06 04:16:23 +01:00
parent ba50d17846
commit 3f4a582ad7
13 changed files with 94 additions and 61 deletions
@@ -43,7 +43,7 @@ enum STATUS_CODES {
const ValidateUSCRequest: RequestHandler = async (req, res, next) => {
const token = req.header("Authorization");
if (!token) {
if (token === undefined) {
return res.status(200).json({
statusCode: STATUS_CODES.BAD_REQ,
description: "No auth token provided.",
@@ -224,7 +224,7 @@ router.get("/charts/:chartHash/leaderboard", RetrieveChart, async (req, res) =>
try {
n = AssertStrAsPositiveNonZeroInt(req.query.n, "Invalid 'N' param.");
} catch (err) {
} catch (_err) {
return res.status(200).json({
statusCode: STATUS_CODES.BAD_REQ,
description: `Invalid 'n' param - expected a positive non-zero integer less than or equal to ${USCIR_MAX_LEADERBOARD_N}.`,
@@ -265,7 +265,7 @@ router.get("/charts/:chartHash/leaderboard", RetrieveChart, async (req, res) =>
});
});
const PR_USCIRChartDoc = {
const PR_USCIR_CHART_DOC = {
chartHash: "string",
artist: "string",
title: "string",
@@ -286,7 +286,7 @@ router.post("/scores", RequirePermissions("submit_score"), async (req, res) => {
const chartErr = p(
req.body.chart,
PR_USCIRChartDoc,
PR_USCIR_CHART_DOC,
{},
{
throwOnNonObject: false,
@@ -333,14 +333,19 @@ router.post("/scores", RequirePermissions("submit_score"), async (req, res) => {
// If the import failed, AND the import failure WAS NOT that the chart didnt exist
// report that error instead.
if (importDoc.errors[0]?.type && importDoc.errors[0].type !== "KTDataNotFound") {
logger.info(`USC Import Failed ${importDoc.errors[0].message}`, {
importDoc,
userID,
});
logger.info(
`USC Import Failed ${importDoc.errors[0].message ?? "with null error message?"}`,
{
importDoc,
userID,
}
);
return res.status(200).json({
statusCode: STATUS_CODES.BAD_REQ,
description: `${importDoc.errors[0].type} ${importDoc.errors[0].message}`,
description: `${importDoc.errors[0].type} ${
importDoc.errors[0].message ?? "No Error Message"
}`,
});
}
@@ -355,7 +360,7 @@ router.post("/scores", RequirePermissions("submit_score"), async (req, res) => {
}
// If the chartDoc exists, any error is a failure here.
if (importDoc.errors[0]) {
if (importDoc.errors.length !== 0) {
logger.info(`USC Import Failed ${importDoc.errors[0].message}`, {
importDoc,
userID,
@@ -382,7 +387,7 @@ router.post("/scores", RequirePermissions("submit_score"), async (req, res) => {
description: "Successfully imported score.",
body,
});
} catch (err) {
} catch (_err) {
return res.status(200).json({
statusCode: STATUS_CODES.SERVER_ERROR,
description: "An internal server error has occured.",
@@ -28,7 +28,9 @@ export interface USCClientScore {
gaugeOpt: integer;
mirror: boolean;
random: boolean;
autoFlags: integer; // ???
// ??? - Not sure what these are.
autoFlags: integer;
};
windows: {
perfect: number;
@@ -169,7 +169,7 @@ export async function CreatePOSTScoresResponseBody(
const originalScore = (await db.scores.findOne({
scoreID,
})) as ScoreDocument<"usc:Controller" | "usc:Keyboard">;
})) as ScoreDocument<"usc:Controller" | "usc:Keyboard"> | null;
if (!originalScore) {
logger.severe(
+11 -8
View File
@@ -1,16 +1,17 @@
import { RequestLoggerMiddleware } from "./middleware/request-logger";
import mainRouter from "./router/router";
import connectRedis from "connect-redis";
import express from "express";
import type { Express } from "express";
import "express-async-errors";
import expressSession from "express-session";
import { RedisClient } from "external/redis/redis";
import helmet from "helmet";
import { SYMBOL_TACHI_API_AUTH } from "lib/constants/tachi";
import CreateLogCtx from "lib/logger/logger";
import { Environment, ServerConfig, TachiConfig } from "lib/setup/config";
import type { Express } from "express";
import "express-async-errors";
import type { integer } from "tachi-common";
import { RequestLoggerMiddleware } from "./middleware/request-logger";
import mainRouter from "./router/router";
import { IsNonEmptyString } from "utils/misc";
const logger = CreateLogCtx(__filename);
@@ -39,13 +40,15 @@ const userSessionMiddleware = expressSession({
saveUninitialized: false,
cookie: {
secure: Environment.nodeEnv === "production" || ServerConfig.ENABLE_SERVER_HTTPS,
sameSite: "strict", // Very important. Without this, we're vulnerable to CSRF!
// Very important. Without this, we're vulnerable to CSRF!
sameSite: "strict",
},
});
const app: Express = express();
if (Environment.nodeEnv !== "production" && ServerConfig.CLIENT_DEV_SERVER) {
if (Environment.nodeEnv !== "production" && IsNonEmptyString(ServerConfig.CLIENT_DEV_SERVER)) {
logger.warn(`Enabling CORS requests from ${ServerConfig.CLIENT_DEV_SERVER}.`, {
bootInfo: true,
});
@@ -63,7 +66,7 @@ if (Environment.nodeEnv !== "production" && ServerConfig.CLIENT_DEV_SERVER) {
});
// hack to allow all OPTIONS requests. Remember that this setting should not be on in production!
if (ServerConfig.OPTIONS_ALWAYS_SUCCEEDS) {
if (ServerConfig.OPTIONS_ALWAYS_SUCCEEDS === true) {
app.options("*", (req, res) => res.send());
}
} else {
@@ -117,7 +120,7 @@ app.use("/", mainRouter);
// In dev, this is a pain to setup, so we can just run it locally.
if (
ServerConfig.CDN_CONFIG.SAVE_LOCATION.TYPE === "LOCAL_FILESYSTEM" &&
ServerConfig.CDN_CONFIG.SAVE_LOCATION.SERVE_OWN_CDN
ServerConfig.CDN_CONFIG.SAVE_LOCATION.SERVE_OWN_CDN === true
) {
if (Environment.nodeEnv === "production") {
logger.warn(
+33 -28
View File
@@ -9,43 +9,48 @@ export function RequireAuthPerms(
perms: APIPermissions | Array<APIPermissions>,
method: "DELETE" | "GET" | "PATCH" | "POST" | "PUT" = "GET"
) {
t.test(`Testing permissions for ${method} ${url} [${perms}]`, async (t) => {
const m = method.toLowerCase() as Lowercase<typeof method>;
return t.test(
`Testing permissions for ${method} ${url} [${
Array.isArray(perms) ? perms.join(", ") : perms
}]`,
async (t) => {
const m = method.toLowerCase() as Lowercase<typeof method>;
const res = await mockApi[m](url);
const res = await mockApi[m](url);
// 401 if no auth given
t.equal(res.statusCode, 401);
// 401 if no auth given
t.equal(res.statusCode, 401);
await db["api-tokens"].insert({
identifier: "temp_auth_perms",
permissions: {},
token: "temp_auth",
userID: 1,
fromAPIClient: null,
});
await db["api-tokens"].insert({
identifier: "temp_auth_perms",
permissions: {},
token: "temp_auth",
userID: 1,
fromAPIClient: null,
});
const resAuth = await mockApi[m](url).set("Authorization", "Bearer temp_auth");
const resAuth = await mockApi[m](url).set("Authorization", "Bearer temp_auth");
t.equal(resAuth.statusCode, 403);
t.equal(resAuth.statusCode, 403);
const prm = Array.isArray(perms) ? perms : [perms];
const prm = Array.isArray(perms) ? perms : [perms];
await db["api-tokens"].insert({
identifier: "temp_auth_perms2",
permissions: Object.fromEntries(prm.map((e) => [e, true])),
token: "temp_auth2",
userID: 1,
fromAPIClient: null,
});
await db["api-tokens"].insert({
identifier: "temp_auth_perms2",
permissions: Object.fromEntries(prm.map((e) => [e, true])),
token: "temp_auth2",
userID: 1,
fromAPIClient: null,
});
const resAuthed = await mockApi[m](url).set("Authorization", "Bearer temp_auth2");
const resAuthed = await mockApi[m](url).set("Authorization", "Bearer temp_auth2");
t.not(resAuthed.statusCode, 401);
t.not(resAuthed.statusCode, 403);
t.not(resAuthed.statusCode, 401);
t.not(resAuthed.statusCode, 403);
await ResetDBState();
await ResetDBState();
t.end();
});
t.end();
}
);
}
+1 -1
View File
@@ -23,7 +23,7 @@ export function isApproximately(
export function prAssert(
t: Tap.Test,
obj: Record<string, unknown> | unknown,
obj: unknown,
schema: PrudenceSchema,
message = "Unnamed Prudence Assertion"
) {
+1 -1
View File
@@ -6,7 +6,7 @@ import { CloseRedisPubSub } from "external/redis/redis-IPC";
import { CloseScoreImportQueue } from "lib/score-import/worker/queue";
export async function CleanUpAfterTests() {
if (process.env.TAP_SNAPSHOT) {
if (process.env.TAP_SNAPSHOT !== "" && process.env.TAP_SNAPSHOT !== undefined) {
WriteSnapshotData();
}
+10 -1
View File
@@ -11,7 +11,16 @@ logger.verbose("Connecting to Supertest...");
const mockApi = supertest(connection);
export function CloseServerConnection() {
return connection.close();
return new Promise<void>((resolve, reject) => {
connection.close((err) => {
if (err) {
reject(err);
return;
}
resolve();
});
});
}
export default mockApi;
+4 -2
View File
@@ -1,6 +1,8 @@
export function MockMulterFile(buffer: Buffer, originalname: string) {
return {
const mockFile: Express.Multer.File = {
originalname,
buffer,
} as Express.Multer.File;
};
return mockFile;
}
+2 -2
View File
@@ -80,7 +80,7 @@ export async function UpdateClassIfGreater(
});
if (isGreater === null) {
EmitWebhookEvent({
void EmitWebhookEvent({
type: "class-update/v1",
content: { userID, new: classVal, old: null, set: classSet, game, playtype },
});
@@ -88,7 +88,7 @@ export async function UpdateClassIfGreater(
return null;
}
EmitWebhookEvent({
void EmitWebhookEvent({
type: "class-update/v1",
content: {
userID,
+1 -1
View File
@@ -195,7 +195,7 @@ export async function GetRecentlyAchievedGoals(
goalID: { $in: goalSubs.map((e) => e.goalID) },
});
if (goals.length !== goals.length) {
if (goals.length !== goalSubs.length) {
logger.error(
`Found ${goals.length} goals when looking for parents of ${goalSubs.length} subscriptions. This mismatch implies a state desync.`
);
+4 -1
View File
@@ -11,7 +11,10 @@ export function ParseEA3SoftID(ver: string) {
return {
model: a[0],
dest: a[1], // region
// region
dest: a[1],
spec: a[2],
rev: a[3],
ext: a[4],
+7 -3
View File
@@ -135,11 +135,11 @@ export function IsValidURL(string: string) {
}
export function Sleep(ms: number) {
return new Promise<void>((resolve) =>
return new Promise<void>((resolve) => {
setTimeout(() => {
resolve();
}, ms)
);
}, ms);
});
}
export function GetTimeXHoursAgo(hours: integer) {
@@ -228,3 +228,7 @@ export function IsSupported(game: Game) {
export function ArrayDiff<T>(left: Array<T>, right: Array<T>) {
return right.filter((e) => !left.includes(e));
}
export function IsNonEmptyString(maybeStr: string | null | undefined): maybeStr is string {
return Boolean(maybeStr);
}