From b4681c72ea7350beaede98d6dfc0eae8c0b933cc Mon Sep 17 00:00:00 2001 From: zkldi Date: Sat, 26 Jun 2021 00:43:32 +0100 Subject: [PATCH] change CDNRetrieve to CDNRedirect --- server/src/lib/cdn/cdn.ts | 29 ++++++++++++++++--- server/src/lib/setup/config.ts | 7 +++-- .../api/v1/users/_userID/banner/router.ts | 6 ++-- .../router/api/v1/users/_userID/pfp/router.ts | 6 ++-- .../src/server/router/ir/usc/router.test.ts | 4 +-- server/src/test-utils/resets.ts | 4 +-- 6 files changed, 40 insertions(+), 16 deletions(-) diff --git a/server/src/lib/cdn/cdn.ts b/server/src/lib/cdn/cdn.ts index c4cc81a8c..ff20fc66a 100644 --- a/server/src/lib/cdn/cdn.ts +++ b/server/src/lib/cdn/cdn.ts @@ -1,9 +1,10 @@ import fs from "fs"; import path from "path"; -import { KTCDN_ROOT } from "../setup/config"; +import { CDN_ROOT, CDN_URL } from "../setup/config"; import CreateLogCtx from "../logger/logger"; import { promisify } from "util"; import mkdirp from "mkdirp"; +import { Response } from "express"; const readFilePromise = promisify(fs.readFile); const writeFilePromise = promisify(fs.writeFile); @@ -18,18 +19,38 @@ const logger = CreateLogCtx(__filename); * Path directory traversal *is* possible, and *will* ruin your day. */ function CDNRoot(fileLoc: string) { - return path.join(KTCDN_ROOT, fileLoc); + return path.join(CDN_ROOT, fileLoc); } /** * Retrieves the data of the file at the given CDN location. + * + * This is used for quick development setups, where a cdn server isn't available. + * As in, this ruins the purpose of a CDN! make sure you have one running. */ -export function CDNRetrieve(fileLoc: string) { - logger.debug(`Retrieving path ${fileLoc}.`); +function CDNRetrieve(fileLoc: string) { + logger.debug(`Retrieving path ${fileLoc} locally.`); return readFilePromise(CDNRoot(fileLoc)); } +/** + * Redirects the response to the CDN server at the given path. + * + * If no CDN_URL is set, then this falls back to fetching from the CDN ROOT via. FS. + */ +export function CDNRedirect(res: Response, fileLoc: string) { + if (fileLoc[0] !== "/") { + throw new Error(`Invalid fileLoc - did not start with /.`); + } + + if (CDN_URL) { + return res.redirect(`${CDN_URL}${fileLoc}`); + } else { + return CDNRetrieve(fileLoc); + } +} + /** * Flag for fs.open to write a file but NOT overwrite it if it already exists. */ diff --git a/server/src/lib/setup/config.ts b/server/src/lib/setup/config.ts index 379ddc05f..594973f72 100644 --- a/server/src/lib/setup/config.ts +++ b/server/src/lib/setup/config.ts @@ -4,7 +4,7 @@ import JSON5 from "json5"; import fs from "fs"; import p from "prudence"; import { FormatPrError } from "../../utils/prudence"; -import { integer, Game, StaticConfig } from "tachi-common"; +import { integer, StaticConfig } from "tachi-common"; dotenv.config(); // imports things like NODE_ENV from a local .env file if one is present. // reads from $pwd/conf.json5 @@ -36,6 +36,7 @@ export interface TachiConfig { ARC_API_URL: string; ARC_AUTH_TOKEN: string; CDN_ROOT: string; + CDN_URL: string | null; TYPE: "ktchi" | "btchi" | "omni"; PORT: integer; TYPE_INFO: StaticConfig.ServerConfig; @@ -52,6 +53,7 @@ const err = p(config, { ARC_API_URL: isValidURL, ARC_AUTH_TOKEN: "string", CDN_ROOT: "string", + CDN_URL: "?string", PORT: p.isPositiveInteger, TYPE: p.isIn("ktchi", "btchi", "omni"), }); @@ -79,7 +81,8 @@ export const FLO_API_URL = tachiConfig.FLO_API_URL; export const EAG_API_URL = tachiConfig.EAG_API_URL; export const ARC_API_URL = tachiConfig.ARC_API_URL; export const ARC_AUTH_TOKEN = tachiConfig.ARC_AUTH_TOKEN; -export const KTCDN_ROOT = tachiConfig.CDN_ROOT; +export const CDN_ROOT = tachiConfig.CDN_ROOT; +export const CDN_URL = tachiConfig.CDN_URL; export const CONF_INFO = tachiConfig.TYPE_INFO; export const PORT = tachiConfig.PORT; export const CONFIG = tachiConfig; diff --git a/server/src/server/router/api/v1/users/_userID/banner/router.ts b/server/src/server/router/api/v1/users/_userID/banner/router.ts index b0ca5a009..c561bc833 100644 --- a/server/src/server/router/api/v1/users/_userID/banner/router.ts +++ b/server/src/server/router/api/v1/users/_userID/banner/router.ts @@ -1,6 +1,6 @@ import { Router } from "express"; import db from "../../../../../../../external/mongo/db"; -import { CDNStoreOrOverwrite, CDNRetrieve, CDNDelete } from "../../../../../../../lib/cdn/cdn"; +import { CDNStoreOrOverwrite, CDNRedirect, CDNDelete } from "../../../../../../../lib/cdn/cdn"; import { GetProfileBannerURL } from "../../../../../../../lib/cdn/url-format"; import { ONE_MEGABYTE } from "../../../../../../../lib/constants/filesize"; import { SYMBOL_TachiData } from "../../../../../../../lib/constants/tachi"; @@ -86,14 +86,14 @@ router.get("/", async (req, res) => { if (!user.customBanner) { res.setHeader("Content-Type", "image/png"); - const buf = await CDNRetrieve("/users/default/banner.png"); + const buf = await CDNRedirect("/users/default/banner.png"); return res.send(buf); } // this might be a png or a jpg. Could we sniff this out somehow? // alternatively - could we convert jpgs to pngs on upload? // this isn't my area of expertise - zkldi - const buf = await CDNRetrieve(GetProfileBannerURL(user.id)); + const buf = await CDNRedirect(GetProfileBannerURL(user.id)); return res.send(buf); }); diff --git a/server/src/server/router/api/v1/users/_userID/pfp/router.ts b/server/src/server/router/api/v1/users/_userID/pfp/router.ts index dd14ca290..23927af9b 100644 --- a/server/src/server/router/api/v1/users/_userID/pfp/router.ts +++ b/server/src/server/router/api/v1/users/_userID/pfp/router.ts @@ -1,6 +1,6 @@ import { Router } from "express"; import db from "../../../../../../../external/mongo/db"; -import { CDNStoreOrOverwrite, CDNRetrieve, CDNDelete } from "../../../../../../../lib/cdn/cdn"; +import { CDNStoreOrOverwrite, CDNRedirect, CDNDelete } from "../../../../../../../lib/cdn/cdn"; import { GetProfilePictureURL } from "../../../../../../../lib/cdn/url-format"; import { ONE_MEGABYTE } from "../../../../../../../lib/constants/filesize"; import { SYMBOL_TachiData } from "../../../../../../../lib/constants/tachi"; @@ -84,14 +84,14 @@ router.get("/", async (req, res) => { if (!user.customPfp) { res.setHeader("Content-Type", "image/png"); - const buf = await CDNRetrieve("/users/default/pfp.png"); + const buf = await CDNRedirect("/users/default/pfp.png"); return res.send(buf); } // this might be a png or a jpg. Could we sniff this out somehow? // alternatively - could we convert jpgs to pngs on upload? // this isn't my area of expertise - zkldi - const buf = await CDNRetrieve(GetProfilePictureURL(user.id)); + const buf = await CDNRedirect(GetProfilePictureURL(user.id)); return res.send(buf); }); diff --git a/server/src/server/router/ir/usc/router.test.ts b/server/src/server/router/ir/usc/router.test.ts index 5fd3f92cd..9371fed1c 100644 --- a/server/src/server/router/ir/usc/router.test.ts +++ b/server/src/server/router/ir/usc/router.test.ts @@ -6,7 +6,7 @@ import ResetDBState, { ResetCDN } from "../../../../test-utils/resets"; import deepmerge from "deepmerge"; import { PBScoreDocument, ScoreDocument } from "tachi-common"; import { GetKTDataBuffer } from "../../../../test-utils/test-data"; -import { CDNRetrieve } from "../../../../lib/cdn/cdn"; +import { CDNRedirect } from "../../../../lib/cdn/cdn"; async function InsertFakeUSCAuth() { await db["api-tokens"].insert({ @@ -374,7 +374,7 @@ t.test("POST /replays", (t) => { body: null, }); - const stored = await CDNRetrieve("/uscir/replays/MOCK_IDENTIFIER"); + const stored = await CDNRedirect("/uscir/replays/MOCK_IDENTIFIER"); t.strictSame(stored, replayFile, "Should store the same file exactly."); diff --git a/server/src/test-utils/resets.ts b/server/src/test-utils/resets.ts index 574961f53..304ce121d 100644 --- a/server/src/test-utils/resets.ts +++ b/server/src/test-utils/resets.ts @@ -7,7 +7,7 @@ import path from "path"; import CreateLogCtx from "../lib/logger/logger"; // im installing an entire library for rm rf... import rimraf from "rimraf"; -import { KTCDN_ROOT, MONGO_CONNECTION_URL } from "../lib/setup/config"; +import { CDN_ROOT, MONGO_CONNECTION_URL } from "../lib/setup/config"; import { SetIndexes } from "../external/mongo/indexes"; const logger = CreateLogCtx(__filename); @@ -77,7 +77,7 @@ export default async function ResetDBState() { export function ResetCDN() { return new Promise((resolve, reject) => - rimraf(KTCDN_ROOT, (err) => { + rimraf(CDN_ROOT, (err) => { if (err) { reject(err); }