diff options
| author | seth <[email protected]> | 2024-10-12 14:28:54 -0400 |
|---|---|---|
| committer | seth <[email protected]> | 2024-10-12 14:59:04 -0400 |
| commit | 6a2d9e752fab27b59da4f194b0ef6daf7e8b6d81 (patch) | |
| tree | ed8f9f07861a0a4463dcd910baa349b6cc6656aa /src | |
| parent | 08912b439bd61088dd849b9342a81341fa9e4a23 (diff) | |
port to deno
Diffstat (limited to 'src')
| -rw-r--r-- | src/consts.ts | 4 | ||||
| -rw-r--r-- | src/env.ts | 6 | ||||
| -rw-r--r-- | src/index.ts | 93 | ||||
| -rw-r--r-- | src/schemas.ts | 34 | ||||
| -rw-r--r-- | src/teawie.ts | 98 |
5 files changed, 0 insertions, 235 deletions
diff --git a/src/consts.ts b/src/consts.ts deleted file mode 100644 index 0e31b83..0000000 --- a/src/consts.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { version } from "../package.json"; - -export const VERSION = version; -export const USER_AGENT = `teawieAPI/${version}`; diff --git a/src/env.ts b/src/env.ts deleted file mode 100644 index 2ee3db7..0000000 --- a/src/env.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type Bindings = { - REDIRECT_ROOT?: string; - TEAWIE_API: KVNamespace; -}; - -export type Variables = Record<string, never>; diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index 82eaad9..0000000 --- a/src/index.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { logger } from "hono/logger"; -import { prettyJSON } from "hono/pretty-json"; -import { swaggerUI } from "@hono/swagger-ui"; -import { OpenAPIHono, createRoute } from "@hono/zod-openapi"; -import { VERSION } from "./consts"; -import { Bindings, Variables } from "./env"; -import { - ListTeawiesParams, - ListTeawiesResponse, - RandomTeawiesResponse, -} from "./schemas"; -import { imageUrls } from "./teawie"; - -const app = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>(); - -app.use("*", logger()); -app.use("*", prettyJSON()); - -app.get("/", (c) => - c.redirect(c.env.REDIRECT_ROOT ?? "https://github.com/getchoo/teawieAPI"), -); - -app.get("/swagger", swaggerUI({ url: "/doc" })); - -app.doc("/doc", { - openapi: "3.0.0", - info: { - version: VERSION, - title: "teawieAPI", - }, -}); - -app.openapi( - createRoute({ - method: "get", - path: "/list_teawies", - request: { - params: ListTeawiesParams, - }, - responses: { - 200: { - content: { - "application/json": { - schema: ListTeawiesResponse, - }, - }, - description: "List known Teawie URLS", - }, - }, - }), - async (c) => { - const { limit } = c.req.query(); - const urls = await imageUrls(c.env.TEAWIE_API); - - return c.json( - { - urls: urls.splice(0, parseInt(limit ?? "5")), - }, - 200, - ); - }, -); - -app.openapi( - createRoute({ - method: "get", - path: "/random_teawie", - responses: { - 200: { - content: { - "application/json": { - schema: RandomTeawiesResponse, - }, - }, - description: "A random URL to a picture of Teawie", - }, - }, - }), - async (c) => - imageUrls(c.env.TEAWIE_API).then((urls) => - c.json({ - url: urls[Math.floor(Math.random() * urls.length)], - }), - ), -); - -app.onError((error, c) => { - console.error(error); - - return c.json({ error: error.message }, 500); -}); - -export default app; diff --git a/src/schemas.ts b/src/schemas.ts deleted file mode 100644 index 669674a..0000000 --- a/src/schemas.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { z } from "@hono/zod-openapi"; - -const ErrorResponse = z.string().optional().openapi({ - description: "Error message reported by server", -}); - -const TeawieURLResponse = z.object({ - url: z.string().url().optional().openapi({ - description: "URL to Teawie", - }), - - error: ErrorResponse, -}); - -export const ListTeawiesParams = z.object({ - limit: z - .string() - .optional() - .default("5") - .refine((data) => { - const parsed = parseInt(data); - return !isNaN(parsed); - }) - .openapi({ - description: "Maximum number of Teawie URLs to be returned", - }), -}); - -export const ListTeawiesResponse = z.object({ - urls: z.array(z.string().url()).optional(), - error: ErrorResponse, -}); - -export const RandomTeawiesResponse = TeawieURLResponse; diff --git a/src/teawie.ts b/src/teawie.ts deleted file mode 100644 index 65d6617..0000000 --- a/src/teawie.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { USER_AGENT } from "./consts"; -import { Endpoints } from "@octokit/types"; - -type repositoryPathContentsResponse = - Endpoints["GET /repos/{owner}/{repo}/contents/{path}"]["response"]; - -const GITHUB_API = "https://api.github.com"; - -// Teawie repository owner and name -const REPO_OWNER = "SympathyTea"; -const REPO_NAME = "Teawie-Archive"; - -// Subdirectories of the above repository containing files we want -const SUBDIRS = [ - "teawie-media/Original Teawies", - "teawie-media/Teawie Variants", - "teawie-media/Teawie in Places", - "teawie-media/Unfinished Teawies", -]; - -// File extensions we consider to be images -const IMAGE_EXTENSIONS = ["gif", "jpg", "jpeg", "png", "svg", "webp"]; - -const contentsOf = ( - path: string, -): Promise<repositoryPathContentsResponse["data"]> => - fetch(`${GITHUB_API}/repos/${REPO_OWNER}/${REPO_NAME}/contents/${path}`, { - headers: { - accept: "application/vnd.github+json", - "user-agent": USER_AGENT, - }, - }) - .then((response) => { - if (!response.ok) { - throw new Error( - `HTTP Error ${response.status}: ${response.statusText}`, - ); - } - - return response.json(); - }) - .then((json) => { - return json as repositoryPathContentsResponse["data"]; - }); - -const imageUrlsIn = ( - files: repositoryPathContentsResponse["data"], -): string[] => { - // NOTE: This is done because the response may only contain data - // for a single file's path - const filesArray = Array.isArray(files) ? files : [files]; - - return ( - filesArray - // Find *files* that are (probably) images and have a download URL - .filter( - (file) => - !Array.isArray(file) && - file.download_url && - file.type == "file" && - IMAGE_EXTENSIONS.includes(file.name.split(".").at(-1) ?? ""), - ) - .map((file) => { - // Should this happen? No - // Could it? I don't know - // But let's be safe :steamhappy: - if (!file.download_url) { - throw new Error( - `Could not find download URL for file "${file.name}"`, - ); - } - - return file.download_url; - }) - ); -}; - -export const imageUrls = async (kv: KVNamespace): Promise<string[]> => { - const cached = await kv.get("urls"); - if (cached) { - console.trace("Found Teawie URLs in cache!"); - return JSON.parse(cached); - } - - console.warn("Couldn't find Teawie URLs in cache! Fetching fresh ones"); - const fresh = await Promise.all(SUBDIRS.map(contentsOf)).then((responses) => { - // See the note above - const flatResponses = responses.flatMap((response) => - Array.isArray(response) ? response : [response], - ); - - return imageUrlsIn(flatResponses); - }); - - await kv.put("urls", JSON.stringify(fresh), { expirationTtl: 60 * 60 }); - - return fresh; -}; |
