From c4874466696fd33b3e4309750982b439e9887556 Mon Sep 17 00:00:00 2001 From: Marcel <65048232+dromzeh@users.noreply.github.com> Date: Wed, 7 Jun 2023 17:26:51 +0100 Subject: [PATCH] feat: seperate routes into `./routes/` (Fixes #5) --- package-lock.json | 90 ++--- package.json | 8 +- src/handler.js | 31 ++ src/index.js | 7 + src/index.mjs | 7 - src/lib/listBucket.js | 6 + ...responseHeaders.mjs => responseHeaders.js} | 0 .../{errorHandler.mjs => errorHandler.js} | 5 +- src/routes.mjs | 356 ------------------ src/routes/games.js | 209 ++++++++++ src/routes/index.js | 24 ++ src/routes/ocGenerators.js | 62 +++ wrangler.toml | 2 +- 13 files changed, 390 insertions(+), 417 deletions(-) create mode 100644 src/handler.js create mode 100644 src/index.js delete mode 100644 src/index.mjs create mode 100644 src/lib/listBucket.js rename src/lib/{responseHeaders.mjs => responseHeaders.js} (100%) rename src/middleware/{errorHandler.mjs => errorHandler.js} (88%) delete mode 100644 src/routes.mjs create mode 100644 src/routes/games.js create mode 100644 src/routes/index.js create mode 100644 src/routes/ocGenerators.js diff --git a/package-lock.json b/package-lock.json index 86486ae7..80b49565 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,15 +8,15 @@ "name": "cdn-worker", "version": "0.0.0", "dependencies": { - "itty-router": "^4.0.6", + "itty-router": "^4.0.9", "prettier": "^2.8.8" }, "devDependencies": { "@cloudflare/workers-types": "^4.20230518.0", - "eslint": "^8.41.0", + "eslint": "^8.42.0", "eslint-config-google": "^0.14.0", - "typescript": "^5.0.4", - "wrangler": "3.0.1" + "typescript": "^5.1.3", + "wrangler": "3.1.0" } }, "node_modules/@cloudflare/kv-asset-handler": { @@ -536,18 +536,18 @@ } }, "node_modules/@eslint/js": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz", - "integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", + "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -1094,16 +1094,16 @@ } }, "node_modules/eslint": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz", - "integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", + "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.4.0", "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.41.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint/js": "8.42.0", + "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -1638,9 +1638,9 @@ "dev": true }, "node_modules/itty-router": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/itty-router/-/itty-router-4.0.6.tgz", - "integrity": "sha512-bzfBY19gJS/GQK7Wvw/Wj03t6KFRo/JOvtzmlNtEv0UJqqWWbBT/0PIf6j9YbpIUwwA3OpeZHjJN05+YWaAVvQ==" + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/itty-router/-/itty-router-4.0.9.tgz", + "integrity": "sha512-al8PIAJEWuWZcg4iwLcLiF7R9njsIQxrT27ik2Vfp1Mi5CBEVr1BDKbA1xpOyqkRbj9cCBQiTRpLIKnNO2YKlQ==" }, "node_modules/js-yaml": { "version": "4.1.0", @@ -2530,16 +2530,16 @@ } }, "node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", + "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=12.20" + "node": ">=14.17" } }, "node_modules/undici": { @@ -2614,9 +2614,9 @@ } }, "node_modules/wrangler": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.0.1.tgz", - "integrity": "sha512-YamXlRjkMO/V3Fvq7IC9H9GDWIbNGc4IV3l1Z5q45XYTWxUYbkwXyiTAfpmqhyl5wx+XEPKe3k/ubqmW+r63yQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.1.0.tgz", + "integrity": "sha512-oqVBJZoOQqSKxhgaPt4LcmtBf0FssIz/4F1VgjWOomeGQ3kN9pg3swPO0Zkf0aAphDodG9rekjrtccvKW7bSsA==", "dev": true, "dependencies": { "@cloudflare/kv-asset-handler": "^0.2.0", @@ -2970,15 +2970,15 @@ } }, "@eslint/js": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz", - "integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", + "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", "dev": true }, "@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", @@ -3376,16 +3376,16 @@ "dev": true }, "eslint": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz", - "integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", + "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.4.0", "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.41.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint/js": "8.42.0", + "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -3784,9 +3784,9 @@ "dev": true }, "itty-router": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/itty-router/-/itty-router-4.0.6.tgz", - "integrity": "sha512-bzfBY19gJS/GQK7Wvw/Wj03t6KFRo/JOvtzmlNtEv0UJqqWWbBT/0PIf6j9YbpIUwwA3OpeZHjJN05+YWaAVvQ==" + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/itty-router/-/itty-router-4.0.9.tgz", + "integrity": "sha512-al8PIAJEWuWZcg4iwLcLiF7R9njsIQxrT27ik2Vfp1Mi5CBEVr1BDKbA1xpOyqkRbj9cCBQiTRpLIKnNO2YKlQ==" }, "js-yaml": { "version": "4.1.0", @@ -4415,9 +4415,9 @@ "dev": true }, "typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", + "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", "dev": true }, "undici": { @@ -4473,9 +4473,9 @@ } }, "wrangler": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.0.1.tgz", - "integrity": "sha512-YamXlRjkMO/V3Fvq7IC9H9GDWIbNGc4IV3l1Z5q45XYTWxUYbkwXyiTAfpmqhyl5wx+XEPKe3k/ubqmW+r63yQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.1.0.tgz", + "integrity": "sha512-oqVBJZoOQqSKxhgaPt4LcmtBf0FssIz/4F1VgjWOomeGQ3kN9pg3swPO0Zkf0aAphDodG9rekjrtccvKW7bSsA==", "dev": true, "requires": { "@cloudflare/kv-asset-handler": "^0.2.0", diff --git a/package.json b/package.json index 26b3799f..cce1b907 100644 --- a/package.json +++ b/package.json @@ -6,14 +6,14 @@ }, "devDependencies": { "@cloudflare/workers-types": "^4.20230518.0", - "eslint": "^8.41.0", + "eslint": "^8.42.0", "eslint-config-google": "^0.14.0", - "typescript": "^5.0.4", - "wrangler": "3.0.1" + "typescript": "^5.1.3", + "wrangler": "3.1.0" }, "private": true, "dependencies": { - "itty-router": "^4.0.6", + "itty-router": "^4.0.9", "prettier": "^2.8.8" } } diff --git a/src/handler.js b/src/handler.js new file mode 100644 index 00000000..f9a1ae7a --- /dev/null +++ b/src/handler.js @@ -0,0 +1,31 @@ +// handler.js + +import { Router } from "itty-router"; +import { getGames, getGameId, getAsset } from "./routes/games.js"; +import { getGenerators, getGeneratorGameId } from "./routes/ocGenerators.js"; +import { errorHandler } from "./middleware/errorHandler.js"; +import { responseHeaders } from "./lib/responseHeaders.js"; +import { index } from "./routes/index.js"; + +const router = Router(); + +router + .get("/", errorHandler(index)) + .get("/games", errorHandler(getGames)) + .get("/game/:gameId", errorHandler(getGameId)) + .get("/game/:gameId/:asset", errorHandler(getAsset)) + .get("/oc-generators", errorHandler(getGenerators)) + .get("/oc-generator/:gameId", errorHandler(getGeneratorGameId)) + .all("*", () => new Response(JSON.stringify({ + success: false, + status: "error", + error: "404 Not Found", + }), { + headers: responseHeaders, + })); + +addEventListener("fetch", (event) => { + event.respondWith(router.handle(event.request)); +}); + +export { router }; diff --git a/src/index.js b/src/index.js new file mode 100644 index 00000000..feac59ee --- /dev/null +++ b/src/index.js @@ -0,0 +1,7 @@ +// src/index.js + +import { router } from "./handler.js"; + +export default { + fetch: router.handle, +}; diff --git a/src/index.mjs b/src/index.mjs deleted file mode 100644 index 42d3b8f3..00000000 --- a/src/index.mjs +++ /dev/null @@ -1,7 +0,0 @@ -// src/index.mjs - -import { router } from "./routes.mjs"; - -export default { - fetch: router.handle, -}; diff --git a/src/lib/listBucket.js b/src/lib/listBucket.js new file mode 100644 index 00000000..1c7ca747 --- /dev/null +++ b/src/lib/listBucket.js @@ -0,0 +1,6 @@ +// lib/listBucket.js + +export const listBucket = async (bucket, options) => { + const files = await bucket.list(options); + return files; +}; diff --git a/src/lib/responseHeaders.mjs b/src/lib/responseHeaders.js similarity index 100% rename from src/lib/responseHeaders.mjs rename to src/lib/responseHeaders.js diff --git a/src/middleware/errorHandler.mjs b/src/middleware/errorHandler.js similarity index 88% rename from src/middleware/errorHandler.mjs rename to src/middleware/errorHandler.js index 96b71bb2..599ec0a4 100644 --- a/src/middleware/errorHandler.mjs +++ b/src/middleware/errorHandler.js @@ -1,6 +1,3 @@ -// src/middleware/errorHandler.mjs - -// error handler export const errorHandler = (handler) => async (request, env) => { try { return await handler(request, env); @@ -17,4 +14,4 @@ export const errorHandler = (handler) => async (request, env) => { } ); } -}; +}; \ No newline at end of file diff --git a/src/routes.mjs b/src/routes.mjs deleted file mode 100644 index 7553764b..00000000 --- a/src/routes.mjs +++ /dev/null @@ -1,356 +0,0 @@ -// src/routes.mjs - -import { Router } from "itty-router"; -import { responseHeaders } from "./lib/responseHeaders.mjs"; -import { errorHandler } from "./middleware/errorHandler.mjs"; - -const router = Router(); - -const listBucket = async (env, options) => { - const files = await env.bucket.list(options); - return files; -}; - -const unwantedPrefixes = ["other/", "locales/", "oc-generator/"]; - -// TODO: stop hardcoding routes -const routes = [ - "https://api.wanderer.moe/games", - "https://api.wanderer.moe/game/{gameId}", - "https://api.wanderer.moe/game/{gameId}/{asset}", - "https://api.wanderer.moe/oc-generators", - "https://api.wanderer.moe/oc-generator/{gameId}", -]; - -// index route -// gets all routes and their paths -router.get( - "/", - errorHandler(async (request, env) => { - return new Response( - JSON.stringify({ - success: true, - status: "ok", - path: "/", - routes, - }), - { - headers: responseHeaders, - } - ); - }) -); - -// oc generator index route -// gets all oc generators -router.get( - "/oc-generators", - errorHandler(async (request, env) => { - const files = await listBucket(env, { - prefix: "oc-generator/", - delimiter: "/", - }); - - const locations = files.delimitedPrefixes.map((file) => ({ - name: file.replace("oc-generator/", "").replace("/", ""), - path: `https://api.wanderer.moe/oc-generator/${file - .replace("oc-generator/", "") - .replace("/", "")}`, - })); - - return new Response( - JSON.stringify({ - success: true, - status: "ok", - path: "/oc-generators", - locations, - }), - { - headers: responseHeaders, - } - ); - }) -); - -// oc generator :gameId route -// gets oc generator for a game -router.get( - "/oc-generator/:gameId", - errorHandler(async (request, env) => { - const { gameId } = request.params; - - const files = await listBucket(env, { - prefix: `oc-generator/${gameId}/list.json`, - }); - - if (files.objects.length === 0) { - return new Response( - JSON.stringify({ - success: false, - status: "error", - error: "404 Not Found", - }), - { - headers: responseHeaders, - } - ); - } - - return new Response( - JSON.stringify({ - success: true, - status: "ok", - path: `/oc-generator/${gameId}`, - game: gameId, - json: `https://cdn.wanderer.moe/oc-generator/${gameId}/list.json`, - }), - { - headers: responseHeaders, - } - ); - }) -); - -// games index route -// gets all games -router.get( - "/games", - errorHandler(async (request, env) => { - const files = await listBucket(env, { - prefix: "", - delimiter: "/", - }); - - const rootLocations = files.delimitedPrefixes - .filter((game) => !unwantedPrefixes.includes(game)) - .map(async (game) => { - const gameFiles = await listBucket(env, { - prefix: `${game}`, - delimiter: "/", - }); - - const tags = gameFiles.delimitedPrefixes.some((subfolder) => - subfolder.includes("sheets") - ) - ? ["Has Sheets"] - : []; - - const subfolders = await Promise.all( - gameFiles.delimitedPrefixes.map(async (subfolder) => { - const subfolderFiles = await listBucket(env, { - prefix: `${subfolder}`, - }); - const lastUploaded = subfolderFiles.objects.reduce( - (prev, current) => { - const prevDate = new Date(prev.uploaded); - const currentDate = new Date(current.uploaded); - return prevDate > currentDate ? prev : current; - }, - { uploaded: 0 } - ); - return { - name: subfolder.replace(game, "").replace("/", ""), - path: `https://api.wanderer.moe/game/${subfolder}`, - fileCount: subfolderFiles.objects.length, - lastUploaded: lastUploaded.uploaded, - }; - }) - ); - - const totalFiles = subfolders.reduce( - (total, subfolder) => total + subfolder.fileCount, - 0 - ); - - const lastUploaded = subfolders.reduce( - (prev, current) => { - const prevDate = new Date(prev.lastUploaded); - const currentDate = new Date(current.lastUploaded); - return prevDate > currentDate ? prev : current; - }, - { lastUploaded: 0 } - ); - - return { - name: game.replace("/", ""), - path: `https://api.wanderer.moe/game/${game}`, - tags, - totalFiles, - lastUploaded: lastUploaded.lastUploaded, - subfolders, - }; - }); - - const games = await Promise.all(rootLocations); - - return new Response( - JSON.stringify({ - success: true, - status: "ok", - path: "/games", - games, - }), - { - headers: responseHeaders, - } - ); - }) -); - -// game :gameId route -// gets asset categories for a game -router.get( - "/game/:gameId", - errorHandler(async (request, env) => { - const { gameId } = request.params; - - const files = await listBucket(env, { - prefix: `${gameId}/`, - delimiter: "/", - }); - - const locations = files.delimitedPrefixes.map(async (file) => { - const subfolderFiles = await listBucket(env, { - prefix: `${file}`, - }); - const fileCount = subfolderFiles.objects.length; - const lastUploaded = subfolderFiles.objects.reduce( - (prev, current) => { - const prevDate = new Date(prev.uploaded); - const currentDate = new Date(current.uploaded); - return prevDate > currentDate ? prev : current; - }, - { uploaded: 0 } - ); - return { - name: file.replace(`${gameId}/`, "").replace("/", ""), - path: `https://api.wanderer.moe/game/${gameId}/${file - .replace(`${gameId}/`, "") - .replace("/", "")}`, - fileCount, - lastUploaded: lastUploaded.uploaded, - }; - }); - - const locationsWithFileCount = await Promise.all(locations); - - const totalFiles = locationsWithFileCount.reduce( - (total, location) => total + location.fileCount, - 0 - ); - - if (files.objects.length === 0) { - return new Response( - JSON.stringify({ - success: false, - status: "error", - path: `/game/${gameId}`, - error: "404 Not Found", - }), - { - headers: responseHeaders, - } - ); - } - - const lastUploaded = locationsWithFileCount.reduce( - (prev, current) => { - const prevDate = new Date(prev.lastUploaded); - const currentDate = new Date(current.lastUploaded); - return prevDate > currentDate ? prev : current; - }, - { lastUploaded: 0 } - ); - - return new Response( - JSON.stringify({ - success: true, - status: "ok", - path: `/game/${gameId}`, - game: gameId, - totalFiles, - lastUploaded: lastUploaded.lastUploaded, - locations: locationsWithFileCount, - }), - { - headers: responseHeaders, - } - ); - }) -); - -// game :gameId :asset route -// gets all assets for a game -router.get( - "/game/:gameId/:asset", - errorHandler(async (request, env) => { - const { gameId, asset } = request.params; - - const files = await listBucket(env, { - prefix: `${gameId}/${asset}/`, - }); - - if (files.objects.length === 0) { - return new Response( - JSON.stringify({ - success: false, - status: "error", - path: `/game/${gameId}/${asset}`, - error: "404 Not Found", - }), - { - headers: responseHeaders, - } - ); - } - - const images = files.objects.map((file) => ({ - name: file.key.split("/").pop().replace(".png", ""), - nameWithExtension: file.key.split("/").pop(), - path: `https://cdn.wanderer.moe/${file.key}`, - uploaded: file.uploaded, - size: file.size, - })); - - const lastUploaded = images.sort((a, b) => b.uploaded - a.uploaded)[0]; - - return new Response( - JSON.stringify({ - success: true, - status: "ok", - path: `/game/${gameId}/${asset}`, - game: gameId, - asset, - lastUploaded, - images, - }), - { - headers: responseHeaders, - } - ); - }) -); - -// other routes that don't exist, 404 -router.all( - "*", - errorHandler(() => { - return new Response( - JSON.stringify({ - success: false, - status: "error", - error: "404 Not Found", - }), - { - headers: responseHeaders, - } - ); - }) -); - -// event listener for fetch events -addEventListener("fetch", (event) => { - event.respondWith(router.handle(event.request)); -}); - -export { router }; diff --git a/src/routes/games.js b/src/routes/games.js new file mode 100644 index 00000000..ff15f9b5 --- /dev/null +++ b/src/routes/games.js @@ -0,0 +1,209 @@ +import { responseHeaders } from "../lib/responseHeaders.js"; +import { listBucket } from "../lib/listBucket.js"; + +const unwantedPrefixes = ["other/", "locales/", "oc-generator/"]; + +export const getGames = async (request, env) => { + const files = await listBucket(env.bucket, { + prefix: "", + delimiter: "/", + }); + + const rootLocations = files.delimitedPrefixes + .filter((game) => !unwantedPrefixes.includes(game)) + .map(async (game) => { + const gameFiles = await listBucket(env.bucket, { + prefix: `${game}`, + delimiter: "/", + }); + + const tags = gameFiles.delimitedPrefixes.some((subfolder) => + subfolder.includes("sheets") + ) + ? ["Has Sheets"] + : []; + + const subfolders = await Promise.all( + gameFiles.delimitedPrefixes.map(async (subfolder) => { + const subfolderFiles = await listBucket(env.bucket, { + prefix: `${subfolder}`, + }); + const lastUploaded = subfolderFiles.objects.reduce( + (prev, current) => { + const prevDate = new Date(prev.uploaded); + const currentDate = new Date(current.uploaded); + return prevDate > currentDate ? prev : current; + }, + { uploaded: 0 } + ); + return { + name: subfolder.replace(game, "").replace("/", ""), + path: `https://api.wanderer.moe/game/${subfolder}`, + fileCount: subfolderFiles.objects.length, + lastUploaded: lastUploaded.uploaded, + }; + }) + ); + + const totalFiles = subfolders.reduce( + (total, subfolder) => total + subfolder.fileCount, + 0 + ); + + const lastUploaded = subfolders.reduce( + (prev, current) => { + const prevDate = new Date(prev.lastUploaded); + const currentDate = new Date(current.lastUploaded); + return prevDate > currentDate ? prev : current; + }, + { lastUploaded: 0 } + ); + + return { + name: game.replace("/", ""), + path: `https://api.wanderer.moe/game/${game}`, + tags, + totalFiles, + lastUploaded: lastUploaded.lastUploaded, + subfolders, + }; + }); + + const games = await Promise.all(rootLocations); + + return new Response( + JSON.stringify({ + success: true, + status: "ok", + path: "/games", + games, + }), + { + headers: responseHeaders, + } + ); +}; + +export const getGameId = async (request, env) => { + const { gameId } = request.params; + + const files = await listBucket(env.bucket, { + prefix: `${gameId}/`, + delimiter: "/", + }); + + const locations = files.delimitedPrefixes.map(async (file) => { + const subfolderFiles = await listBucket(env.bucket, { + prefix: `${file}`, + }); + const fileCount = subfolderFiles.objects.length; + const lastUploaded = subfolderFiles.objects.reduce( + (prev, current) => { + const prevDate = new Date(prev.uploaded); + const currentDate = new Date(current.uploaded); + return prevDate > currentDate ? prev : current; + }, + { uploaded: 0 } + ); + return { + name: file.replace(`${gameId}/`, "").replace("/", ""), + path: `https://api.wanderer.moe/game/${gameId}/${file + .replace(`${gameId}/`, "") + .replace("/", "")}`, + fileCount, + lastUploaded: lastUploaded.uploaded, + }; + }); + + const locationsWithFileCount = await Promise.all(locations); + + const totalFiles = locationsWithFileCount.reduce( + (total, location) => total + location.fileCount, + 0 + ); + + if (files.objects.length === 0) { + return new Response( + JSON.stringify({ + success: false, + status: "error", + path: `/game/${gameId}`, + error: "404 Not Found", + }), + { + headers: responseHeaders, + } + ); + } + + const lastUploaded = locationsWithFileCount.reduce( + (prev, current) => { + const prevDate = new Date(prev.lastUploaded); + const currentDate = new Date(current.lastUploaded); + return prevDate > currentDate ? prev : current; + }, + { lastUploaded: 0 } + ); + + return new Response( + JSON.stringify({ + success: true, + status: "ok", + path: `/game/${gameId}`, + game: gameId, + totalFiles, + lastUploaded: lastUploaded.lastUploaded, + locations: locationsWithFileCount, + }), + { + headers: responseHeaders, + } + ); +}; + +export const getAsset = async (request, env) => { + const { gameId, asset } = request.params; + + const files = await listBucket(env.bucket, { + prefix: `${gameId}/${asset}/`, + }); + + if (files.objects.length === 0) { + return new Response( + JSON.stringify({ + success: false, + status: "error", + path: `/game/${gameId}/${asset}`, + error: "404 Not Found", + }), + { + headers: responseHeaders, + } + ); + } + + const images = files.objects.map((file) => ({ + name: file.key.split("/").pop().replace(".png", ""), + nameWithExtension: file.key.split("/").pop(), + path: `https://cdn.wanderer.moe/${file.key}`, + uploaded: file.uploaded, + size: file.size, + })); + + const lastUploaded = images.sort((a, b) => b.uploaded - a.uploaded)[0]; + + return new Response( + JSON.stringify({ + success: true, + status: "ok", + path: `/game/${gameId}/${asset}`, + game: gameId, + asset, + lastUploaded, + images, + }), + { + headers: responseHeaders, + } + ); +}; diff --git a/src/routes/index.js b/src/routes/index.js new file mode 100644 index 00000000..76ff596c --- /dev/null +++ b/src/routes/index.js @@ -0,0 +1,24 @@ +import { responseHeaders } from "../lib/responseHeaders.js"; + +// TODO: stop hardcoding routes +const routes = [ + "https://api.wanderer.moe/games", + "https://api.wanderer.moe/game/{gameId}", + "https://api.wanderer.moe/game/{gameId}/{asset}", + "https://api.wanderer.moe/oc-generators", + "https://api.wanderer.moe/oc-generator/{gameId}", +]; + +export const index = async (request, env) => { + return new Response( + JSON.stringify({ + success: true, + status: "ok", + path: "/", + routes, + }), + { + headers: responseHeaders, + } + ); +}; diff --git a/src/routes/ocGenerators.js b/src/routes/ocGenerators.js new file mode 100644 index 00000000..10e5995e --- /dev/null +++ b/src/routes/ocGenerators.js @@ -0,0 +1,62 @@ +import { responseHeaders } from "../lib/responseHeaders.js"; +import { listBucket } from "../lib/listBucket.js"; + +export const getGenerators = async (request, env) => { + const files = await listBucket(env, { + prefix: "oc-generator/", + delimiter: "/", + }); + + const locations = files.delimitedPrefixes.map((file) => ({ + name: file.replace("oc-generator/", "").replace("/", ""), + path: `https://api.wanderer.moe/oc-generator/${file + .replace("oc-generator/", "") + .replace("/", "")}`, + })); + + return new Response( + JSON.stringify({ + success: true, + status: "ok", + path: "/oc-generators", + locations, + }), + { + headers: responseHeaders, + } + ); +}; + +export const getGeneratorGameId = async (request, env) => { + const { gameId } = request.params; + + const files = await listBucket(env, { + prefix: `oc-generator/${gameId}/list.json`, + }); + + if (files.objects.length === 0) { + return new Response( + JSON.stringify({ + success: false, + status: "error", + error: "404 Not Found", + }), + { + headers: responseHeaders, + } + ); + } + + return new Response( + JSON.stringify({ + success: true, + status: "ok", + path: `/oc-generator/${gameId}`, + game: gameId, + json: `https://cdn.wanderer.moe/oc-generator/${gameId}/list.json`, + }), + { + headers: responseHeaders, + } + ); +}; diff --git a/wrangler.toml b/wrangler.toml index 9a5f8787..c858ba4d 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -1,5 +1,5 @@ name = "api-wanderer-moe" -main = "./src/index.mjs" +main = "./src/index.js" compatibility_date = "2023-04-24" account_id = "ba4e8f7f9ffbc23dba6acd0d9bd3ef46"