diff --git a/package.json b/package.json index f40ba35a..5404e764 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@planetscale/database": "^1.8.0", "@prisma/client": "^5.0.0", "@typescript-eslint/eslint-plugin": "^6.0.0", + "hono": "^3.3.4", "itty-router": "^4.0.14", "lucia": "^2.0.0", "prettier": "^3.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d6ca3824..89d3b90c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ dependencies: "@typescript-eslint/eslint-plugin": specifier: ^6.0.0 version: 6.0.0(@typescript-eslint/parser@6.0.0)(eslint@8.45.0)(typescript@5.1.6) + hono: + specifier: ^3.3.4 + version: 3.3.4 itty-router: specifier: ^4.0.14 version: 4.0.14 @@ -3045,6 +3048,14 @@ packages: } engines: { node: ">=8" } + /hono@3.3.4: + resolution: + { + integrity: sha512-lqvcsQrXS0bSydj/MkOXi3opAwrlauIjZefhXMMRj9prABfBWdrYHSrWk/xfxgxNPs4yuEMh2Z5zV0L2b1Jmdw==, + } + engines: { node: ">=16.0.0" } + dev: false + /html-to-text@9.0.3: resolution: { diff --git a/src/handler.ts b/src/handler.ts deleted file mode 100644 index fd4ced12..00000000 --- a/src/handler.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Router } from "itty-router"; -import { errorHandler } from "@/middleware/errorHandler"; -import { responseHeaders } from "@/lib/responseHeaders"; -import { getContributors } from "@/routes/discord/contributors"; -import { index } from "@/routes"; -import { getGenerator } from "@/routes/oc-generators/getGenerator"; -import { getGenerators } from "@/routes/oc-generators/getGenerators"; -import { getAssetSearch, getRecentAssets } from "@/routes/search/assetSearch"; -import { downloadFile } from "@/routes/download/downloadFile"; -import { getUserByUsername } from "@/routes/user/getUserByUsername"; -import { getUserBySearch } from "@/routes/user/getUsersBySearch"; -import { allGames } from "@/routes/games/allGames"; -import { getAssetFromId } from "@/routes/asset/getAssetFromId"; -import { createNotFoundResponse } from "@/lib/helpers/responses/notFoundResponse"; -// import { login } from "@/routes/auth/login"; -// import { logout } from "@/routes/auth/logout"; -// import { signup } from "@/routes/auth/signup"; - -const router = Router(); - -router - .get("/", errorHandler(index)) - .get("/games", errorHandler(allGames)) - .get("/user/:name", errorHandler(getUserByUsername)) - .get("/recent", errorHandler(getRecentAssets)) - .get("/asset/:id", errorHandler(getAssetFromId)) - .get("/user/s/:name", errorHandler(getUserBySearch)) - .get("/search/assets", errorHandler(getAssetSearch)) - .get("/download/:id", errorHandler(downloadFile)) - .get("/oc-generators", errorHandler(getGenerators)) - .get("/oc-generator/:gameId", errorHandler(getGenerator)) - .get("/discord/contributors", errorHandler(getContributors)) - // .post("/auth/login", errorHandler(login)) - // .post("/auth/logout", errorHandler(logout)) - // .post("/auth/signup", errorHandler(signup)) - .all("*", (): Response => { - return createNotFoundResponse("Route doesn't exist", responseHeaders); - }); - -addEventListener("fetch", (event: FetchEvent) => { - event.respondWith(router.handle(event.request)); -}); - -export { router }; diff --git a/src/index.ts b/src/index.ts index 93b077a1..a519a14c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,38 @@ -import { router } from "@/handler"; +import { Hono } from "hono"; +import { Env } from "./worker-configuration"; +import assetRoute from "./routes/asset/assetRoute"; +import discordRoute from "./routes/discord/discordRoute"; +import ocGeneratorRoute from "./routes/oc-generators/ocGeneratorRoutes"; +import searchRoute from "./routes/search/searchRoute"; +import gamesRoute from "./routes/games/gamesRoute"; +import userRoute from "./routes/user/userRoute"; -export default { - fetch: router.handle, -}; +interface Bindings extends Env { + [key: string]: unknown; +} + +const app = new Hono<{ Bindings: Bindings }>(); + +app.get("/status", (c) => { + c.status(200); + return c.json({ status: "ok" }); +}); +app.get("/", (c) => { + c.status(200); + return c.json({ status: "ok", routes: app.routes }); +}); +app.route("/asset", assetRoute); +app.route("/discord", discordRoute); +app.route("/oc-generators", ocGeneratorRoute); +app.route("/search", searchRoute); +app.route("/games", gamesRoute); +app.route("/user", userRoute); +app.all("*", (c) => { + c.status(404); + return c.json({ status: "not found" }); +}); + +// https://hono.dev/api/hono#showroutes +app.showRoutes(); + +export default app; diff --git a/src/lib/helpers/getQueryParams.ts b/src/lib/helpers/getQueryParams.ts deleted file mode 100644 index 50f2d8ae..00000000 --- a/src/lib/helpers/getQueryParams.ts +++ /dev/null @@ -1,4 +0,0 @@ -export function getQueryParam(url, param) { - const value = url.searchParams.get(param); - return value ? value.split(",") : []; -} diff --git a/src/lib/helpers/responses/notFoundResponse.ts b/src/lib/helpers/responses/notFoundResponse.ts index 14768c3c..4818a796 100644 --- a/src/lib/helpers/responses/notFoundResponse.ts +++ b/src/lib/helpers/responses/notFoundResponse.ts @@ -1,14 +1,14 @@ // helper function to create a 404 Not Found response export function createNotFoundResponse(errorMessage, responseHeaders) { - const responseBody = { - success: false, - status: "error", - error: "404 Not Found", - message: errorMessage, - }; - - return new Response(JSON.stringify(responseBody), { - status: 404, - headers: responseHeaders, - }); + return new Response( + JSON.stringify({ + success: false, + status: "error", + error: errorMessage, + }), + { + status: 404, + headers: responseHeaders, + } + ); } diff --git a/src/lib/planetscale.ts b/src/lib/planetscale.ts index 67499bef..b69fa9e9 100644 --- a/src/lib/planetscale.ts +++ b/src/lib/planetscale.ts @@ -1,7 +1,8 @@ import { connect } from "@planetscale/database"; +import { Env } from "../worker-configuration"; // useful wrapper for planetscale connection -export function getConnection(env) { +export function getConnection(env: Env) { const config = { // this can be set with "wrangler secret put" or through the planetscale integration on cf dashboard host: env.DATABASE_HOST, diff --git a/src/lib/query.ts b/src/lib/query.ts index 95cb13a1..a5b9a638 100644 --- a/src/lib/query.ts +++ b/src/lib/query.ts @@ -5,28 +5,33 @@ type queryParameter = string | number; export const getSearchResults = async ( query: string, - game: string[], - asset: string[], - tags: string[], - env: Env + gameArray: string[], + assetArray: string[], + tagsArray: string[], + c ): Promise => { let sqlQuery = `SELECT * FROM assets WHERE 1=1`; const parameters = []; sqlQuery = addQueryToSqlQuery(query, sqlQuery, parameters); - sqlQuery = addGameToSqlQuery(game, sqlQuery, parameters); - sqlQuery = addAssetToSqlQuery(asset, sqlQuery, parameters); - sqlQuery = addTagsToSqlQuery(tags, sqlQuery, parameters); + sqlQuery = addGameToSqlQuery(gameArray, sqlQuery, parameters); + sqlQuery = addAssetToSqlQuery(assetArray, sqlQuery, parameters); + sqlQuery = addTagsToSqlQuery(tagsArray, sqlQuery, parameters); sqlQuery += ` ORDER BY uploaded_date DESC`; sqlQuery = limitResults(sqlQuery); - if (!query && !game.length && !asset.length && !tags.length) { + if ( + !query && + !gameArray.length && + !assetArray.length && + !tagsArray.length + ) { sqlQuery = `SELECT * FROM assets ORDER BY uploaded_date DESC LIMIT 30`; } - const db = await getConnection(env); + const db = await getConnection(c.env); return await db .execute(sqlQuery, parameters) @@ -46,42 +51,42 @@ const addQueryToSqlQuery = ( }; const addGameToSqlQuery = ( - game: string[], + gameArray: string[], sqlQuery: string, parameters: queryParameter[] ): string => { - if (game.length) { - sqlQuery += ` AND game IN (${game.map(() => "?").join(",")})`; - parameters.push(...game); + if (gameArray.length) { + sqlQuery += ` AND game IN (${gameArray.map(() => "?").join(",")})`; + parameters.push(...gameArray); } return sqlQuery; }; const addAssetToSqlQuery = ( - asset: string[], + assetArray: string[], sqlQuery: string, parameters: queryParameter[] ): string => { - if (asset.length) { - sqlQuery += ` AND asset_category IN (${asset + if (assetArray.length) { + sqlQuery += ` AND asset_category IN (${assetArray .map(() => "?") .join(",")})`; - parameters.push(...asset); + parameters.push(...assetArray); } return sqlQuery; }; const addTagsToSqlQuery = ( - tags: string[], + tagsArray: string[], sqlQuery: string, parameters: queryParameter[] ): string => { - if (tags.length) { - sqlQuery += ` AND tags IN (${tags + if (tagsArray.length) { + sqlQuery += ` AND tags IN (${tagsArray .map(() => "?") .join(",") .toUpperCase()})`; - parameters.push(...tags); + parameters.push(...tagsArray); } return sqlQuery; }; diff --git a/src/lib/types/auth.ts b/src/lib/types/auth.ts index cf1a9975..647ff9f0 100644 --- a/src/lib/types/auth.ts +++ b/src/lib/types/auth.ts @@ -2,10 +2,7 @@ export interface LoginBody { username: string; password: string; } - -export interface RegisterBody { - username: string; +export interface RegisterBody extends LoginBody { email: string; - password: string; passwordConfirm: string; } diff --git a/src/middleware/errorHandler.ts b/src/middleware/errorHandler.ts deleted file mode 100644 index 1b5540a2..00000000 --- a/src/middleware/errorHandler.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { responseHeaders } from "@/lib/responseHeaders"; - -// generic error handler wrapper -export const errorHandler = - (handler: (request: Request, env: Env) => Promise) => - async (request: Request, env: Env): Promise => { - try { - return await handler(request, env); - } catch (error) { - console.error(error); - return new Response( - JSON.stringify({ - success: false, - status: "error", - error: "500 Internal Server Error", - }), - { - status: 500, - headers: responseHeaders, - } - ); - } - }; diff --git a/src/routes/asset/assetRoute.ts b/src/routes/asset/assetRoute.ts new file mode 100644 index 00000000..35456011 --- /dev/null +++ b/src/routes/asset/assetRoute.ts @@ -0,0 +1,15 @@ +import { Hono } from "hono"; +import { getAssetFromId } from "./getAssetFromId"; +import { downloadAsset } from "./downloadAsset"; + +const assetRoute = new Hono(); + +assetRoute.get("/:id", async (c) => { + return getAssetFromId(c); +}); + +assetRoute.get("/download/:assetId", async (c) => { + return downloadAsset(c); +}); + +export default assetRoute; diff --git a/src/routes/download/downloadFile.ts b/src/routes/asset/downloadAsset.ts similarity index 70% rename from src/routes/download/downloadFile.ts rename to src/routes/asset/downloadAsset.ts index 0a9278c2..81d66fe1 100644 --- a/src/routes/download/downloadFile.ts +++ b/src/routes/asset/downloadAsset.ts @@ -3,19 +3,13 @@ import type { Asset } from "@/lib/types/asset"; import { getConnection } from "@/lib/planetscale"; import { createNotFoundResponse } from "@/lib/helpers/responses/notFoundResponse"; -export const downloadFile = async ( - request: Request, - env: Env -): Promise => { - const url = new URL(request.url); - const id = url.pathname.split("/")[2]; +export const downloadAsset = async (c) => { + const { assetId } = c.req.param(); - if (!id || isNaN(parseInt(id))) throw new Error("No ID provided"); - - const db = await getConnection(env); + const db = await getConnection(c.env); const row = await db - .execute("SELECT * FROM assets WHERE id = ?", [id]) + .execute("SELECT * FROM assets WHERE id = ?", [assetId]) .then((row) => row.rows[0] as Asset | undefined); if (!row) @@ -30,7 +24,7 @@ export const downloadFile = async ( await db.execute( "UPDATE assets SET download_count = download_count + 1 WHERE id = ?", - [id] + [assetId] ); return new Response(blob, { diff --git a/src/routes/asset/getAssetFromId.ts b/src/routes/asset/getAssetFromId.ts index e8346d9b..eb711b9e 100644 --- a/src/routes/asset/getAssetFromId.ts +++ b/src/routes/asset/getAssetFromId.ts @@ -3,22 +3,15 @@ import type { Asset } from "@/lib/types/asset"; import { getConnection } from "@/lib/planetscale"; import { createNotFoundResponse } from "@/lib/helpers/responses/notFoundResponse"; -export const getAssetFromId = async ( - request: Request, - env: Env -): Promise => { - const url = new URL(request.url); - const id = url.pathname.split("/")[2]; - - if (!id || isNaN(parseInt(id))) throw new Error("No ID provided"); - - const cacheKey = new Request(url.toString(), request); +export const getAssetFromId = async (c) => { + const { id } = c.req.param(); + const cacheKey = new Request(c.req.url.toString(), c.req); const cache = caches.default; let response = await cache.match(cacheKey); if (response) return response; - const db = await getConnection(env); + const db = await getConnection(c.env); const row = await db .execute("SELECT * FROM assets WHERE id = ?", [id]) @@ -62,16 +55,15 @@ export const getAssetFromId = async ( }; }); - response = new Response( - JSON.stringify({ + response = c.json( + { success: true, status: "ok", asset, similarAssets, - }), - { - headers: responseHeaders, - } + }, + 200, + responseHeaders ); response.headers.set("Cache-Control", "s-maxage=604800"); diff --git a/src/routes/auth/login.ts b/src/routes/auth/login.ts index c633bcb1..92dded1d 100644 --- a/src/routes/auth/login.ts +++ b/src/routes/auth/login.ts @@ -1,6 +1,8 @@ import { auth } from "@/lib/auth/lucia"; import type { LoginBody } from "@/lib/types/auth"; import { responseHeaders } from "@/lib/responseHeaders"; +// import { Hono } from "hono"; +// import { deleteCookie, getCookie, setCookie } from "hono/cookie"; import "lucia/polyfill/node"; // required for old nodejs versions export const login = async (request: Request): Promise => { diff --git a/src/routes/auth/logout.ts b/src/routes/auth/logout.ts index 0aa80e82..98257cf8 100644 --- a/src/routes/auth/logout.ts +++ b/src/routes/auth/logout.ts @@ -1,4 +1,6 @@ import { auth } from "@/lib/auth/lucia"; +// import { Hono } from "hono"; +// import { deleteCookie, getCookie, setCookie } from "hono/cookie"; export async function logout(request: Request): Promise { const authRequest = auth.handleRequest(request); diff --git a/src/routes/auth/signup.ts b/src/routes/auth/signup.ts index da4b3ade..52422cf4 100644 --- a/src/routes/auth/signup.ts +++ b/src/routes/auth/signup.ts @@ -1,5 +1,7 @@ import { auth } from "@/lib/auth/lucia"; import type { RegisterBody } from "@/lib/types/auth"; +// import { Hono } from "hono"; +// import { deleteCookie, getCookie, setCookie } from "hono/cookie"; import { responseHeaders } from "@/lib/responseHeaders"; export const signup = async (request: Request): Promise => { diff --git a/src/routes/auth/validate.ts b/src/routes/auth/validate.ts index 66fe1088..c0e0b07f 100644 --- a/src/routes/auth/validate.ts +++ b/src/routes/auth/validate.ts @@ -1,5 +1,7 @@ import { auth } from "@/lib/auth/lucia"; import { responseHeaders } from "@/lib/responseHeaders"; +// import { Hono } from "hono"; +// import { deleteCookie, getCookie, setCookie } from "hono/cookie"; export const validate = async (request: Request): Promise => { const authRequest = auth.handleRequest(request); @@ -21,6 +23,8 @@ export const validate = async (request: Request): Promise => { ); } + if (session.state === "idle") auth.invalidateSession(session.sessionId); + return new Response( JSON.stringify({ success: true, diff --git a/src/routes/discord/contributors.ts b/src/routes/discord/contributors.ts index 843da63a..a72cad6b 100644 --- a/src/routes/discord/contributors.ts +++ b/src/routes/discord/contributors.ts @@ -2,10 +2,7 @@ import { responseHeaders } from "@/lib/responseHeaders"; import { roles, guildId } from "@/lib/discord"; import type { Contributor, GuildMember } from "@/lib/types/discord"; -export const getContributors = async ( - request: Request, - env: Env -): Promise => { +export const contributors = async (c) => { const members: Contributor[] = []; let after: string | null = null; @@ -18,7 +15,7 @@ export const getContributors = async ( }`, { headers: { - Authorization: `Bot ${env.DISCORD_TOKEN}`, + Authorization: `Bot ${c.env.DISCORD_TOKEN}`, }, } ); @@ -57,16 +54,13 @@ export const getContributors = async ( after = guildMembers[guildMembers.length - 1]?.user?.id; } - return new Response( - JSON.stringify({ + return c.json( + { success: true, status: "ok", - path: `/discord/contributors`, contributors: members, - }), - { - status: 200, - headers: responseHeaders, - } + }, + 200, + responseHeaders ); }; diff --git a/src/routes/discord/discordRoute.ts b/src/routes/discord/discordRoute.ts new file mode 100644 index 00000000..fbe37d06 --- /dev/null +++ b/src/routes/discord/discordRoute.ts @@ -0,0 +1,10 @@ +import { Hono } from "hono"; +import { contributors } from "./contributors"; + +const discordRoute = new Hono(); + +discordRoute.get("/contributors", async (c) => { + return contributors(c); +}); + +export default discordRoute; diff --git a/src/routes/games/allGames.ts b/src/routes/games/allGames.ts index 6444e5c5..93b60c93 100644 --- a/src/routes/games/allGames.ts +++ b/src/routes/games/allGames.ts @@ -2,20 +2,15 @@ import { responseHeaders } from "@/lib/responseHeaders"; import { Game } from "@/lib/types/game"; import { listBucket } from "@/lib/listBucket"; -export const allGames = async ( - request: Request, - env: Env -): Promise => { - const url = new URL(request.url); - - const cacheKey = new Request(url.toString(), request); +export const getAllGames = async (c) => { + const cacheKey = new Request(c.req.url.toString(), c.req); const cache = caches.default; let response = await cache.match(cacheKey); if (response) return response; // TODO: fix getting data from old D1 database but using Planetscale DB - const row: D1Result = await env.database + const row: D1Result = await c.env.database .prepare(`SELECT * FROM games`) .run(); @@ -23,7 +18,7 @@ export const allGames = async ( row.results.map(async (result) => ({ name: result.name, id: result.id, - assetCategories: await listBucket(env.bucket, { + assetCategories: await listBucket(c.env.bucket, { prefix: `assets/${result.name}/`, delimiter: "/", }).then((data) => @@ -36,16 +31,14 @@ export const allGames = async ( })) ); - response = new Response( - JSON.stringify({ + response = c.json( + { success: true, status: "ok", results: gameList, - }), - { - status: 200, - headers: responseHeaders, - } + }, + 200, + responseHeaders ); response.headers.set("Cache-Control", "s-maxage=1200"); diff --git a/src/routes/games/gamesRoute.ts b/src/routes/games/gamesRoute.ts new file mode 100644 index 00000000..cc18fa3e --- /dev/null +++ b/src/routes/games/gamesRoute.ts @@ -0,0 +1,10 @@ +import { Hono } from "hono"; +import { getAllGames } from "./allGames"; + +const gamesRoute = new Hono(); + +gamesRoute.get("/all", async (c) => { + return getAllGames(c); +}); + +export default gamesRoute; diff --git a/src/routes/index.ts b/src/routes/index.ts deleted file mode 100644 index c65f4d31..00000000 --- a/src/routes/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { responseHeaders } from "@/lib/responseHeaders"; - -const routes: string[] = [ - "/search[?query=''&tags=''&game=''&asset='']", - "/user/[username]", - "/user/s/[search]", - "/recent", - "/asset/[assetId]", - "/download/[assetId]", - "/discord/contributors", - "/games", - "/oc-generators", - "/oc-generator/[gameId]", -]; - -export const index = async (): Promise => { - return new Response( - JSON.stringify({ - success: true, - status: "ok", - path: "/", - description: - "Please read the API documentation on the site/github repository if you're unsure how to use this API.", - routes, - }), - { - headers: responseHeaders, - } - ); -}; diff --git a/src/routes/oc-generators/getGenerator.ts b/src/routes/oc-generators/getGenerator.ts index cabd9aed..86e173b9 100644 --- a/src/routes/oc-generators/getGenerator.ts +++ b/src/routes/oc-generators/getGenerator.ts @@ -2,21 +2,16 @@ import { responseHeaders } from "@/lib/responseHeaders"; import { listBucket } from "@/lib/listBucket"; import { createNotFoundResponse } from "@/lib/helpers/responses/notFoundResponse"; -export const getGenerator = async ( - request: Request, - env: Env -): Promise => { - const url = new URL(request.url); - const gameId = url.pathname.split("/")[2]; - - const cacheKey = new Request(url.toString(), request); +export const getGeneratorFromName = async (c) => { + const { gameName } = c.req.param(); + const cacheKey = new Request(c.req.url.toString(), c.req); const cache = caches.default; let response = await cache.match(cacheKey); if (response) return response; - const files = await listBucket(env.bucket, { - prefix: `oc-generators/${gameId}/list.json`, + const files = await listBucket(c.env.bucket, { + prefix: `oc-generators/${gameName}/list.json`, }); if (files.objects.length === 0) @@ -28,18 +23,13 @@ export const getGenerator = async ( const generatorData = await data.json(); - response = new Response( - JSON.stringify({ - success: true, + response = c.json( + { status: "ok", - uploaded: files.objects[0].uploaded, - key: files.objects[0].key, data: generatorData, - }), - { - status: 200, - headers: responseHeaders, - } + }, + 200, + responseHeaders ); response.headers.set("Cache-Control", "s-maxage=604800"); // the content of this file is unlikely to change, so caching is fine diff --git a/src/routes/oc-generators/getGenerators.ts b/src/routes/oc-generators/getGenerators.ts index 6fc478d7..96f5938d 100644 --- a/src/routes/oc-generators/getGenerators.ts +++ b/src/routes/oc-generators/getGenerators.ts @@ -1,13 +1,8 @@ import { responseHeaders } from "@/lib/responseHeaders"; import { listBucket } from "@/lib/listBucket"; -export const getGenerators = async ( - request: Request, - env: Env -): Promise => { - const url = new URL(request.url); - - const cacheKey = new Request(url.toString(), request); +export const getGenerators = async (c) => { + const cacheKey = new Request(c.req.url.toString(), c.req); const cache = caches.default; let response = await cache.match(cacheKey); @@ -15,7 +10,7 @@ export const getGenerators = async ( // listing all files inside of oc-generators subfolder, as they can't be manually inputted // by users but instead stored on the oc-generators repo - const files = await listBucket(env.bucket, { + const files = await listBucket(c.env.bucket, { prefix: "oc-generators/", delimiter: "/", }); @@ -31,15 +26,13 @@ export const getGenerators = async ( }; }); - response = new Response( - JSON.stringify({ - success: true, - status: "ok", - results: results, - }), + response = c.json( { - headers: responseHeaders, - } + status: "ok", + data: results, + }, + 200, + responseHeaders ); response.headers.set("Cache-Control", "s-maxage=28800"); diff --git a/src/routes/oc-generators/ocGeneratorRoutes.ts b/src/routes/oc-generators/ocGeneratorRoutes.ts new file mode 100644 index 00000000..7e763295 --- /dev/null +++ b/src/routes/oc-generators/ocGeneratorRoutes.ts @@ -0,0 +1,15 @@ +import { Hono } from "hono"; +import { getGeneratorFromName } from "./getGenerator"; +import { getGenerators } from "./getGenerators"; + +const ocGeneratorRoute = new Hono(); + +ocGeneratorRoute.get("/", async (c) => { + return getGenerators(c); +}); + +ocGeneratorRoute.get("/:gameName", async (c) => { + return getGeneratorFromName(c); +}); + +export default ocGeneratorRoute; diff --git a/src/routes/search/assetSearch.ts b/src/routes/search/assetSearch.ts index 7a952f39..9a3e2875 100644 --- a/src/routes/search/assetSearch.ts +++ b/src/routes/search/assetSearch.ts @@ -2,51 +2,44 @@ import { responseHeaders } from "@/lib/responseHeaders"; import type { Asset } from "@/lib/types/asset"; import { getSearchResults } from "@/lib/query"; import { getConnection } from "@/lib/planetscale"; -import { getQueryParam } from "@/lib/helpers/getQueryParams"; - -export const getAssetSearch = async ( - request: Request, - env: Env -): Promise => { - const url = new URL(request.url); - const paramNames = ["query", "game", "asset", "tags"]; - - const params = {}; - for (const paramName of paramNames) { - params[paramName] = getQueryParam(url, paramName); - } - - // TODO: fix this cuz idk what's going on here - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const { query, game, asset, tags } = params; - - const cacheKey = new Request(url.toString(), request); - const cache = caches.default; +export const getAssetSearch = async (c) => { + const queryParams = c.req.query(); + // console.log(queryParams); + const { query, game, asset, tags } = queryParams; + + // Convert game and asset parameters to arrays + const gameArray = game ? game.split(",") : []; + const assetArray = asset ? asset.split(",") : []; + const tagsArray = tags ? tags.split(",") : []; + + const cacheKey = new Request(c.req.url.toString(), c.req); + const cache = caches.default; let response = await cache.match(cacheKey); if (response) return response; - const results = (await getSearchResults(query, game, asset, tags, env)).map( - (results) => { - return { - id: results.id, - name: results.name, - game: results.game, - asset_category: results.asset_category, - url: results.url, - tags: results.tags, - status: results.status, - uploaded_by: results.uploaded_by, - uploaded_date: results.uploaded_date, - file_size: results.file_size, - }; - } - ); + // console.log(query, gameArray, assetArray, tags); - response = new Response( - JSON.stringify({ + const results = ( + await getSearchResults(query, gameArray, assetArray, tagsArray, c) + ).map((results) => { + return { + id: results.id, + name: results.name, + game: results.game, + asset_category: results.asset_category, + url: results.url, + tags: results.tags, + status: results.status, + uploaded_by: results.uploaded_by, + uploaded_date: results.uploaded_date, + file_size: results.file_size, + }; + }); + + response = c.json( + { success: true, status: "ok", path: "/search/assets", @@ -55,10 +48,9 @@ export const getAssetSearch = async ( asset, tags, results, - }), - { - headers: responseHeaders, - } + }, + 200, + responseHeaders ); response.headers.set("Cache-Control", "s-maxage=3600"); @@ -67,19 +59,13 @@ export const getAssetSearch = async ( return response; }; -export const getRecentAssets = async ( - request: Request, - env: Env -): Promise => { - const url = new URL(request.url); - - const cacheKey = new Request(url.toString(), request); +export const recentAssets = async (c) => { + const cacheKey = new Request(c.req.url.toString(), c.req); const cache = caches.default; let response = await cache.match(cacheKey); - if (response) return response; - const db = await getConnection(env); + const db = await getConnection(c.env); const row = await db .execute( @@ -87,9 +73,7 @@ export const getRecentAssets = async ( ) .then((row) => row.rows as Asset[] | undefined); - if (!row) { - throw new Error("No results found"); - } + if (!row) throw new Error("No results found"); const results = row.map((asset) => { return { @@ -106,17 +90,15 @@ export const getRecentAssets = async ( }; }); - response = new Response( - JSON.stringify({ + response = c.json( + { success: true, status: "ok", - path: "/search", + path: "/assets/recent", results, - }), - { - status: 200, - headers: responseHeaders, - } + }, + 200, + responseHeaders ); response.headers.set("Cache-Control", "s-maxage=3600"); diff --git a/src/routes/search/searchRoute.ts b/src/routes/search/searchRoute.ts new file mode 100644 index 00000000..3adbfed1 --- /dev/null +++ b/src/routes/search/searchRoute.ts @@ -0,0 +1,15 @@ +import { Hono } from "hono"; +import { getAssetSearch } from "./assetSearch"; +import { recentAssets } from "./assetSearch"; + +const searchRoute = new Hono(); + +searchRoute.get("/assets", async (c) => { + return getAssetSearch(c); +}); + +searchRoute.get("/recent", async (c) => { + return recentAssets(c); +}); + +export default searchRoute; diff --git a/src/routes/user/getUserByUsername.ts b/src/routes/user/getUserByUsername.ts index dc09588c..83d4d35d 100644 --- a/src/routes/user/getUserByUsername.ts +++ b/src/routes/user/getUserByUsername.ts @@ -4,22 +4,15 @@ import type { Asset } from "@/lib/types/asset"; import { getConnection } from "@/lib/planetscale"; import { createNotFoundResponse } from "@/lib/helpers/responses/notFoundResponse"; -export const getUserByUsername = async ( - request: Request, - env: Env -): Promise => { - const url = new URL(request.url); - const name = url.pathname.split("/")[2]; - - if (!name) throw new Error("No username provided"); - - const cacheKey = new Request(url.toString(), request); +export const getUserByUsername = async (c) => { + const { name } = c.req.param(); + const cacheKey = new Request(c.req.url.toString(), c.req); const cache = caches.default; let response = await cache.match(cacheKey); if (response) return response; - const db = await getConnection(env); + const db = await getConnection(c.env); const row = await db .execute("SELECT * FROM User WHERE username = ?", [name]) @@ -64,17 +57,15 @@ export const getUserByUsername = async ( if (!row) return createNotFoundResponse("User not found", responseHeaders); - response = new Response( - JSON.stringify({ + response = c.json( + { success: true, status: "ok", user, uploadedAssets, - }), - { - status: 200, - headers: responseHeaders, - } + }, + 200, + responseHeaders ); response.headers.set("Cache-Control", "s-maxage=300"); diff --git a/src/routes/user/getUsersBySearch.ts b/src/routes/user/getUsersBySearch.ts index 2a336d7c..72f8b036 100644 --- a/src/routes/user/getUsersBySearch.ts +++ b/src/routes/user/getUsersBySearch.ts @@ -3,25 +3,17 @@ import type { User } from "@/lib/types/user"; import { createNotFoundResponse } from "@/lib/helpers/responses/notFoundResponse"; import { getConnection } from "@/lib/planetscale"; -export const getUserBySearch = async ( - request: Request, - env: Env -): Promise => { - const url = new URL(request.url); - const name = url.pathname.split("/")[3]; - - if (!name) throw new Error("No username provided"); - - const cacheKey = new Request(url.toString(), request); +export const getUsersBySearch = async (c) => { + const cacheKey = new Request(c.req.url.toString(), c.req); const cache = caches.default; let response = await cache.match(cacheKey); - if (response) return response; - const db = await getConnection(env); + const { query } = c.req.param(); + const db = await getConnection(c.env); const row = await db - .execute("SELECT * FROM User WHERE username LIKE ?", [name]) + .execute("SELECT * FROM User WHERE username LIKE ?", [query]) .then((row) => row.rows as User[] | undefined); if (!row) return createNotFoundResponse("User not found", responseHeaders); @@ -41,19 +33,19 @@ export const getUserBySearch = async ( }); results.sort((a, b) => - a.username === name ? -1 : b.username === name ? 1 : 0 + a.username === query ? -1 : b.username === query ? 1 : 0 ); - response = new Response( - JSON.stringify({ + response = c.json( + { success: true, status: "ok", - results: results, - }), - { - status: 200, - headers: responseHeaders, - } + path: "/users/s/:query", + query, + results, + }, + 200, + responseHeaders ); response.headers.set("Cache-Control", "s-maxage=60"); diff --git a/src/routes/user/userRoute.ts b/src/routes/user/userRoute.ts new file mode 100644 index 00000000..cdbf7e80 --- /dev/null +++ b/src/routes/user/userRoute.ts @@ -0,0 +1,15 @@ +import { Hono } from "hono"; +import { getUsersBySearch } from "./getUsersBySearch"; +import { getUserByUsername } from "./getUserByUsername"; + +const userRoute = new Hono(); + +userRoute.get("/u/:name", async (c) => { + return getUserByUsername(c); +}); + +userRoute.get("/s/:query", async (c) => { + return getUsersBySearch(c); +}); + +export default userRoute; diff --git a/src/worker-configuration.d.ts b/src/worker-configuration.d.ts index 7064f5ee..0533788b 100644 --- a/src/worker-configuration.d.ts +++ b/src/worker-configuration.d.ts @@ -1,6 +1,9 @@ -interface Env { +export interface Env { DISCORD_TOKEN: string; bucket: R2Bucket; database: D1Database; DATABASE_URL: string; + DATABASE_PASSWORD: string; + DATABASE_USERNAME: string; + DATABASE_HOST: string; }