@riffyh/server 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +9 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -2
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,6 @@ import { swagger } from "@elysiajs/swagger";
|
|
|
4
4
|
import { cors } from "@elysiajs/cors";
|
|
5
5
|
import { toon } from "@toon-tools/elysia";
|
|
6
6
|
import { defineCacheInstance } from "@rayriffy/filesystem";
|
|
7
|
-
import sharp from "sharp";
|
|
8
7
|
import { galleryModel, listingResultModel } from "@riffyh/commons";
|
|
9
8
|
import debug from "debug";
|
|
10
9
|
import path from "node:path";
|
|
@@ -60,6 +59,7 @@ try {
|
|
|
60
59
|
}
|
|
61
60
|
log(`loaded configuration with ${config.dataSources.length} data sources`);
|
|
62
61
|
const dataSourceKeys = t.Union(config.dataSources.map((o) => t.Literal(o.key)));
|
|
62
|
+
const isStoreExist = config.dataSources.find((dataSource) => dataSource.key === "store");
|
|
63
63
|
const server = new Elysia().use(swagger({ exclude: ["/_image"] })).use(toon()).use(cors()).get("/", ({ redirect }) => redirect("/swagger")).get("/health", () => "healthy").get("/dataSources", () => config.dataSources.map((o) => ({
|
|
64
64
|
key: o.key,
|
|
65
65
|
name: o.name,
|
|
@@ -69,6 +69,10 @@ const server = new Elysia().use(swagger({ exclude: ["/_image"] })).use(toon()).u
|
|
|
69
69
|
name: t.String(),
|
|
70
70
|
iconUrl: t.String()
|
|
71
71
|
})) }).get("/gallery", async ({ query }) => {
|
|
72
|
+
if (isStoreExist) {
|
|
73
|
+
const gallery = await config.dataSources.find((o) => o.key === "store").getGallery({ id: `${query.dataSource};${query.id}` }).catch(() => null);
|
|
74
|
+
if (gallery !== null) return gallery;
|
|
75
|
+
}
|
|
72
76
|
const dataSource = config.dataSources.find((o) => o.key === query.dataSource);
|
|
73
77
|
if (dataSource === void 0) throw new Error(`data source ${query.dataSource} not found`);
|
|
74
78
|
return dataSource.getGallery({ id: query.id });
|
|
@@ -120,7 +124,10 @@ const server = new Elysia().use(swagger({ exclude: ["/_image"] })).use(toon()).u
|
|
|
120
124
|
} });
|
|
121
125
|
const dataSource = config.dataSources.find((o) => o.key === query.dataSource);
|
|
122
126
|
if (dataSource === void 0) throw new Error(`data source ${query.dataSource} not found`);
|
|
123
|
-
const
|
|
127
|
+
const fetchedImage = new Bun.Image(await dataSource.getImage({ url: query.url })).resize(query.type === "cover" ? 640 : 1280);
|
|
128
|
+
if (query.format === "webp") fetchedImage.webp({ quality: 72 });
|
|
129
|
+
else if (query.format === "jpeg") fetchedImage.jpeg({ quality: 72 });
|
|
130
|
+
const resizedImage = await fetchedImage.buffer();
|
|
124
131
|
await cache.write(cacheKeys, resizedImage, 864e5);
|
|
125
132
|
return new Response(resizedImage, { headers: {
|
|
126
133
|
"Content-Type": `image/${query.format}`,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/bytebin.ts","../src/index.ts"],"sourcesContent":["import { secretbox, randomBytes } from \"tweetnacl\";\nimport { decodeUTF8, encodeUTF8, encodeBase64, decodeBase64 } from \"tweetnacl-util\";\n\nexport const upload = async (content: string, key: string): Promise<string> => {\n const nonce = randomBytes(secretbox.nonceLength);\n const keyUint8Array = decodeBase64(key);\n const messageUint8 = decodeUTF8(content);\n const box = secretbox(messageUint8, nonce, keyUint8Array);\n\n const fullMessage = new Uint8Array(nonce.length + box.length);\n fullMessage.set(nonce);\n fullMessage.set(box, nonce.length);\n\n const base64FullMessage = encodeBase64(fullMessage);\n const response = await fetch(\"https://api.pastes.dev/post\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"text/plain\",\n \"User-Agent\": \"RiffyH (github.com/rayriffy/rayriffy-h)\",\n },\n body: base64FullMessage,\n });\n\n if (!response.ok) throw new Error(\"failed to upload content\");\n\n const data = response.json() as Promise<{ key: string }>;\n return (await data).key;\n};\n\nexport const download = async (code: string, key: string): Promise<string> => {\n const response = await fetch(`https://api.pastes.dev/${code}`);\n\n if (!response.ok) throw new Error(`failed to download content with code ${code}`);\n\n const encryptedContent = await response.text();\n\n const keyUint8Array = decodeBase64(key);\n const messageWithNonceAsUint8Array = decodeBase64(encryptedContent);\n const nonce = messageWithNonceAsUint8Array.slice(0, secretbox.nonceLength);\n const message = messageWithNonceAsUint8Array.slice(\n secretbox.nonceLength,\n encryptedContent.length,\n );\n\n const decrypted = secretbox.open(message, nonce, keyUint8Array);\n\n if (!decrypted) throw new Error(\"could not decrypt message\");\n\n return encodeUTF8(decrypted);\n};\n","#!/usr/bin/env bun\n\nimport { t, Elysia } from \"elysia\";\nimport { swagger } from \"@elysiajs/swagger\";\nimport { cors } from \"@elysiajs/cors\";\nimport { toon } from \"@toon-tools/elysia\";\n\nimport { defineCacheInstance } from \"@rayriffy/filesystem\";\nimport sharp from \"sharp\";\nimport { galleryModel, listingResultModel, type Config } from \"@riffyh/commons\";\nimport debug from \"debug\";\nimport path from \"node:path\";\nimport { download, upload } from \"./bytebin\";\nimport { secretbox, randomBytes } from \"tweetnacl\";\nimport { encodeBase64, decodeBase64 } from \"tweetnacl-util\";\n\nconst log = debug(\"riffyh:server\");\nconst cache = defineCacheInstance();\n\nlog(\"warming up server...\");\n\nconst configFile = process.env.RIFFYH_CONFIG_PATH || \"./riffyh.config.ts\";\nconst configPath = path.resolve(configFile);\nlog(`resolving config file at ${configPath}...`);\nconst config: Config = await import(configPath).then((o) => o.default);\n\ntry {\n if (\n typeof config.secretboxKey !== \"string\" ||\n decodeBase64(config.secretboxKey).length !== secretbox.keyLength\n )\n throw new Error(\"key length mismatch\");\n // oxlint-disable-next-line no-unused-vars\n} catch (_) {\n const generatedKey = encodeBase64(randomBytes(secretbox.keyLength));\n console.error(\n `unable to parse secret key. please either generate key via https://tweetnacl.js.org/#/secretbox or copy following key to configuration: ${generatedKey}`,\n );\n process.exit(1);\n}\n\nlog(`loaded configuration with ${config.dataSources.length} data sources`);\n\nconst dataSourceKeys = t.Union(config.dataSources.map((o) => t.Literal(o.key)));\n\nconst server = new Elysia()\n .use(\n swagger({\n exclude: [\"/_image\"],\n }),\n )\n .use(toon())\n .use(cors())\n .get(\"/\", ({ redirect }) => redirect(\"/swagger\"))\n .get(\"/health\", () => \"healthy\")\n .get(\n \"/dataSources\",\n () =>\n config.dataSources.map((o) => ({\n key: o.key,\n name: o.name,\n iconUrl: o.iconUrl,\n })),\n {\n response: t.Array(\n t.Object({\n key: t.String(),\n name: t.String(),\n iconUrl: t.String(),\n }),\n ),\n },\n )\n .get(\n \"/gallery\",\n async ({ query }) => {\n const dataSource = config.dataSources.find((o) => o.key === query.dataSource);\n if (dataSource === undefined) throw new Error(`data source ${query.dataSource} not found`);\n\n return dataSource.getGallery({\n id: query.id,\n });\n },\n {\n query: t.Object({\n id: t.String(),\n dataSource: dataSourceKeys,\n }),\n response: galleryModel,\n },\n )\n .get(\n \"/listing\",\n async ({ query }) => {\n const dataSource = config.dataSources.find((o) => o.key === query.dataSource);\n if (dataSource === undefined) throw new Error(`data source ${query.dataSource} not found`);\n\n return dataSource.getListing({\n searchQuery: query.query || null,\n page: query.page,\n });\n },\n {\n query: t.Object({\n query: t.Optional(t.String()),\n page: t.Number(),\n dataSource: dataSourceKeys,\n }),\n response: listingResultModel,\n },\n )\n .get(\n \"/tag\",\n async ({ query }) => {\n const dataSource = config.dataSources.find((o) => o.key === query.dataSource);\n if (dataSource === undefined) throw new Error(`data source ${query.dataSource} not found`);\n\n return dataSource.getTagListing({\n id: query.id,\n page: query.page,\n });\n },\n {\n query: t.Object({\n id: t.String(),\n page: t.Number(),\n dataSource: dataSourceKeys,\n }),\n response: listingResultModel,\n },\n )\n .post(\"/collection/export\", ({ body }) => upload(body, config.secretboxKey), {\n body: t.String(),\n })\n .get(\"/collection/import\", async ({ query }) => download(query.key, config.secretboxKey), {\n query: t.Object({\n key: t.String(),\n }),\n })\n .get(\n \"/image\",\n async ({ query }) => {\n const cacheKeys = [query.dataSource, query.url, query.format, query.type];\n\n const cachedImage = await cache.read<Buffer>(cacheKeys);\n if (cachedImage !== null)\n return new Response(Buffer.from(cachedImage.data), {\n headers: {\n \"Content-Type\": `image/${query.format}`,\n \"Cache-Control\": \"public, max-age=86400000\",\n },\n });\n\n const dataSource = config.dataSources.find((o) => o.key === query.dataSource);\n if (dataSource === undefined) throw new Error(`data source ${query.dataSource} not found`);\n\n const fetchedImage = await dataSource.getImage({\n url: query.url,\n });\n const resizedImage = await sharp(fetchedImage)\n .resize({\n width: query.type === \"cover\" ? 640 : 1280,\n })\n .toFormat(query.format, {\n quality: 72,\n })\n .toBuffer();\n await cache.write(\n cacheKeys,\n resizedImage,\n 86_400_000, // 1 month\n );\n\n return new Response(resizedImage, {\n headers: {\n \"Content-Type\": `image/${query.format}`,\n \"Cache-Control\": \"public, max-age=86400\",\n \"CDN-Cache-Control\": \"public, max-age=2592000\",\n \"Cloudflare-CDN-Cache-Control\": \"public, max-age=2592000\",\n },\n });\n },\n {\n query: t.Object({\n url: t.String(),\n format: t.Union([t.Literal(\"webp\"), t.Literal(\"jpeg\")]),\n type: t.Union([t.Literal(\"cover\"), t.Literal(\"page\")]),\n dataSource: dataSourceKeys,\n }),\n },\n )\n .listen({\n hostname: config.hostname ?? \"0.0.0.0\",\n port: config.port ?? 3000,\n });\n\nexport type Server = typeof server;\n\nconsole.log(`🦊 Elysia is running at http://${server.server?.hostname}:${server.server?.port}`);\n"],"mappings":";;;;;;;;;;;;;AAGA,MAAa,SAAS,OAAO,SAAiB,QAAiC;CAC7E,MAAM,QAAQ,YAAY,UAAU,WAAW;CAC/C,MAAM,gBAAgB,aAAa,GAAG;CAEtC,MAAM,MAAM,UADS,WAAW,OACC,GAAG,OAAO,aAAa;CAExD,MAAM,cAAc,IAAI,WAAW,MAAM,SAAS,IAAI,MAAM;CAC5D,YAAY,IAAI,KAAK;CACrB,YAAY,IAAI,KAAK,MAAM,MAAM;CAEjC,MAAM,oBAAoB,aAAa,WAAW;CAClD,MAAM,WAAW,MAAM,MAAM,+BAA+B;EAC1D,QAAQ;EACR,SAAS;GACP,gBAAgB;GAChB,cAAc;EAChB;EACA,MAAM;CACR,CAAC;CAED,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,0BAA0B;CAG5D,QAAQ,MADK,SAAS,KACL,EAAA,CAAG;AACtB;AAEA,MAAa,WAAW,OAAO,MAAc,QAAiC;CAC5E,MAAM,WAAW,MAAM,MAAM,0BAA0B,MAAM;CAE7D,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,wCAAwC,MAAM;CAEhF,MAAM,mBAAmB,MAAM,SAAS,KAAK;CAE7C,MAAM,gBAAgB,aAAa,GAAG;CACtC,MAAM,+BAA+B,aAAa,gBAAgB;CAClE,MAAM,QAAQ,6BAA6B,MAAM,GAAG,UAAU,WAAW;CACzE,MAAM,UAAU,6BAA6B,MAC3C,UAAU,aACV,iBAAiB,MACnB;CAEA,MAAM,YAAY,UAAU,KAAK,SAAS,OAAO,aAAa;CAE9D,IAAI,CAAC,WAAW,MAAM,IAAI,MAAM,2BAA2B;CAE3D,OAAO,WAAW,SAAS;AAC7B;;;ACjCA,MAAM,MAAM,MAAM,eAAe;AACjC,MAAM,QAAQ,oBAAoB;AAElC,IAAI,sBAAsB;AAE1B,MAAM,aAAa,QAAQ,IAAI,sBAAsB;AACrD,MAAM,aAAa,KAAK,QAAQ,UAAU;AAC1C,IAAI,4BAA4B,WAAW,IAAI;AAC/C,MAAM,SAAiB,MAAM,OAAO,WAAW,CAAC,MAAM,MAAM,EAAE,OAAO;AAErE,IAAI;CACF,IACE,OAAO,OAAO,iBAAiB,YAC/B,aAAa,OAAO,YAAY,CAAC,CAAC,WAAW,UAAU,WAEvD,MAAM,IAAI,MAAM,qBAAqB;AAEzC,SAAS,GAAG;CACV,MAAM,eAAe,aAAa,YAAY,UAAU,SAAS,CAAC;CAClE,QAAQ,MACN,2IAA2I,cAC7I;CACA,QAAQ,KAAK,CAAC;AAChB;AAEA,IAAI,6BAA6B,OAAO,YAAY,OAAO,cAAc;AAEzE,MAAM,iBAAiB,EAAE,MAAM,OAAO,YAAY,KAAK,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;AAE9E,MAAM,SAAS,IAAI,OAAO,CAAC,CACxB,IACC,QAAQ,EACN,SAAS,CAAC,SAAS,EACrB,CAAC,CACH,CAAC,CACA,IAAI,KAAK,CAAC,CAAC,CACX,IAAI,KAAK,CAAC,CAAC,CACX,IAAI,MAAM,EAAE,eAAe,SAAS,UAAU,CAAC,CAAC,CAChD,IAAI,iBAAiB,SAAS,CAAC,CAC/B,IACC,sBAEE,OAAO,YAAY,KAAK,OAAO;CAC7B,KAAK,EAAE;CACP,MAAM,EAAE;CACR,SAAS,EAAE;AACb,EAAE,GACJ,EACE,UAAU,EAAE,MACV,EAAE,OAAO;CACP,KAAK,EAAE,OAAO;CACd,MAAM,EAAE,OAAO;CACf,SAAS,EAAE,OAAO;AACpB,CAAC,CACH,EACF,CACF,CAAC,CACA,IACC,YACA,OAAO,EAAE,YAAY;CACnB,MAAM,aAAa,OAAO,YAAY,MAAM,MAAM,EAAE,QAAQ,MAAM,UAAU;CAC5E,IAAI,eAAe,KAAA,GAAW,MAAM,IAAI,MAAM,eAAe,MAAM,WAAW,WAAW;CAEzF,OAAO,WAAW,WAAW,EAC3B,IAAI,MAAM,GACZ,CAAC;AACH,GACA;CACE,OAAO,EAAE,OAAO;EACd,IAAI,EAAE,OAAO;EACb,YAAY;CACd,CAAC;CACD,UAAU;AACZ,CACF,CAAC,CACA,IACC,YACA,OAAO,EAAE,YAAY;CACnB,MAAM,aAAa,OAAO,YAAY,MAAM,MAAM,EAAE,QAAQ,MAAM,UAAU;CAC5E,IAAI,eAAe,KAAA,GAAW,MAAM,IAAI,MAAM,eAAe,MAAM,WAAW,WAAW;CAEzF,OAAO,WAAW,WAAW;EAC3B,aAAa,MAAM,SAAS;EAC5B,MAAM,MAAM;CACd,CAAC;AACH,GACA;CACE,OAAO,EAAE,OAAO;EACd,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC;EAC5B,MAAM,EAAE,OAAO;EACf,YAAY;CACd,CAAC;CACD,UAAU;AACZ,CACF,CAAC,CACA,IACC,QACA,OAAO,EAAE,YAAY;CACnB,MAAM,aAAa,OAAO,YAAY,MAAM,MAAM,EAAE,QAAQ,MAAM,UAAU;CAC5E,IAAI,eAAe,KAAA,GAAW,MAAM,IAAI,MAAM,eAAe,MAAM,WAAW,WAAW;CAEzF,OAAO,WAAW,cAAc;EAC9B,IAAI,MAAM;EACV,MAAM,MAAM;CACd,CAAC;AACH,GACA;CACE,OAAO,EAAE,OAAO;EACd,IAAI,EAAE,OAAO;EACb,MAAM,EAAE,OAAO;EACf,YAAY;CACd,CAAC;CACD,UAAU;AACZ,CACF,CAAC,CACA,KAAK,uBAAuB,EAAE,WAAW,OAAO,MAAM,OAAO,YAAY,GAAG,EAC3E,MAAM,EAAE,OAAO,EACjB,CAAC,CAAC,CACD,IAAI,sBAAsB,OAAO,EAAE,YAAY,SAAS,MAAM,KAAK,OAAO,YAAY,GAAG,EACxF,OAAO,EAAE,OAAO,EACd,KAAK,EAAE,OAAO,EAChB,CAAC,EACH,CAAC,CAAC,CACD,IACC,UACA,OAAO,EAAE,YAAY;CACnB,MAAM,YAAY;EAAC,MAAM;EAAY,MAAM;EAAK,MAAM;EAAQ,MAAM;CAAI;CAExE,MAAM,cAAc,MAAM,MAAM,KAAa,SAAS;CACtD,IAAI,gBAAgB,MAClB,OAAO,IAAI,SAAS,OAAO,KAAK,YAAY,IAAI,GAAG,EACjD,SAAS;EACP,gBAAgB,SAAS,MAAM;EAC/B,iBAAiB;CACnB,EACF,CAAC;CAEH,MAAM,aAAa,OAAO,YAAY,MAAM,MAAM,EAAE,QAAQ,MAAM,UAAU;CAC5E,IAAI,eAAe,KAAA,GAAW,MAAM,IAAI,MAAM,eAAe,MAAM,WAAW,WAAW;CAKzF,MAAM,eAAe,MAAM,MAAM,MAHN,WAAW,SAAS,EAC7C,KAAK,MAAM,IACb,CAAC,CAC4C,CAAC,CAC3C,OAAO,EACN,OAAO,MAAM,SAAS,UAAU,MAAM,KACxC,CAAC,CAAC,CACD,SAAS,MAAM,QAAQ,EACtB,SAAS,GACX,CAAC,CAAC,CACD,SAAS;CACZ,MAAM,MAAM,MACV,WACA,cACA,KACF;CAEA,OAAO,IAAI,SAAS,cAAc,EAChC,SAAS;EACP,gBAAgB,SAAS,MAAM;EAC/B,iBAAiB;EACjB,qBAAqB;EACrB,gCAAgC;CAClC,EACF,CAAC;AACH,GACA,EACE,OAAO,EAAE,OAAO;CACd,KAAK,EAAE,OAAO;CACd,QAAQ,EAAE,MAAM,CAAC,EAAE,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,CAAC,CAAC;CACtD,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,MAAM,CAAC,CAAC;CACrD,YAAY;AACd,CAAC,EACH,CACF,CAAC,CACA,OAAO;CACN,UAAU,OAAO,YAAY;CAC7B,MAAM,OAAO,QAAQ;AACvB,CAAC;AAIH,QAAQ,IAAI,kCAAkC,OAAO,QAAQ,SAAS,GAAG,OAAO,QAAQ,MAAM"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/bytebin.ts","../src/index.ts"],"sourcesContent":["import { secretbox, randomBytes } from \"tweetnacl\";\nimport { decodeUTF8, encodeUTF8, encodeBase64, decodeBase64 } from \"tweetnacl-util\";\n\nexport const upload = async (content: string, key: string): Promise<string> => {\n const nonce = randomBytes(secretbox.nonceLength);\n const keyUint8Array = decodeBase64(key);\n const messageUint8 = decodeUTF8(content);\n const box = secretbox(messageUint8, nonce, keyUint8Array);\n\n const fullMessage = new Uint8Array(nonce.length + box.length);\n fullMessage.set(nonce);\n fullMessage.set(box, nonce.length);\n\n const base64FullMessage = encodeBase64(fullMessage);\n const response = await fetch(\"https://api.pastes.dev/post\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"text/plain\",\n \"User-Agent\": \"RiffyH (github.com/rayriffy/rayriffy-h)\",\n },\n body: base64FullMessage,\n });\n\n if (!response.ok) throw new Error(\"failed to upload content\");\n\n const data = response.json() as Promise<{ key: string }>;\n return (await data).key;\n};\n\nexport const download = async (code: string, key: string): Promise<string> => {\n const response = await fetch(`https://api.pastes.dev/${code}`);\n\n if (!response.ok) throw new Error(`failed to download content with code ${code}`);\n\n const encryptedContent = await response.text();\n\n const keyUint8Array = decodeBase64(key);\n const messageWithNonceAsUint8Array = decodeBase64(encryptedContent);\n const nonce = messageWithNonceAsUint8Array.slice(0, secretbox.nonceLength);\n const message = messageWithNonceAsUint8Array.slice(\n secretbox.nonceLength,\n encryptedContent.length,\n );\n\n const decrypted = secretbox.open(message, nonce, keyUint8Array);\n\n if (!decrypted) throw new Error(\"could not decrypt message\");\n\n return encodeUTF8(decrypted);\n};\n","#!/usr/bin/env bun\n\nimport { t, Elysia } from \"elysia\";\nimport { swagger } from \"@elysiajs/swagger\";\nimport { cors } from \"@elysiajs/cors\";\nimport { toon } from \"@toon-tools/elysia\";\n\nimport { defineCacheInstance } from \"@rayriffy/filesystem\";\nimport { galleryModel, listingResultModel, type Config } from \"@riffyh/commons\";\nimport debug from \"debug\";\nimport path from \"node:path\";\nimport { download, upload } from \"./bytebin\";\nimport { secretbox, randomBytes } from \"tweetnacl\";\nimport { encodeBase64, decodeBase64 } from \"tweetnacl-util\";\n\nconst log = debug(\"riffyh:server\");\nconst cache = defineCacheInstance();\n\nlog(\"warming up server...\");\n\nconst configFile = process.env.RIFFYH_CONFIG_PATH || \"./riffyh.config.ts\";\nconst configPath = path.resolve(configFile);\nlog(`resolving config file at ${configPath}...`);\nconst config: Config = await import(configPath).then((o) => o.default);\n\ntry {\n if (\n typeof config.secretboxKey !== \"string\" ||\n decodeBase64(config.secretboxKey).length !== secretbox.keyLength\n )\n throw new Error(\"key length mismatch\");\n // oxlint-disable-next-line no-unused-vars\n} catch (_) {\n const generatedKey = encodeBase64(randomBytes(secretbox.keyLength));\n console.error(\n `unable to parse secret key. please either generate key via https://tweetnacl.js.org/#/secretbox or copy following key to configuration: ${generatedKey}`,\n );\n process.exit(1);\n}\n\nlog(`loaded configuration with ${config.dataSources.length} data sources`);\n\nconst dataSourceKeys = t.Union(config.dataSources.map((o) => t.Literal(o.key)));\nconst isStoreExist = config.dataSources.find((dataSource) => dataSource.key === \"store\");\n\nconst server = new Elysia()\n .use(\n swagger({\n exclude: [\"/_image\"],\n }),\n )\n .use(toon())\n .use(cors())\n .get(\"/\", ({ redirect }) => redirect(\"/swagger\"))\n .get(\"/health\", () => \"healthy\")\n .get(\n \"/dataSources\",\n () =>\n config.dataSources.map((o) => ({\n key: o.key,\n name: o.name,\n iconUrl: o.iconUrl,\n })),\n {\n response: t.Array(\n t.Object({\n key: t.String(),\n name: t.String(),\n iconUrl: t.String(),\n }),\n ),\n },\n )\n .get(\n \"/gallery\",\n async ({ query }) => {\n if (isStoreExist) {\n const storeDataSource = config.dataSources.find((o) => o.key === \"store\")!;\n const gallery = await storeDataSource\n .getGallery({\n id: `${query.dataSource};${query.id}`,\n })\n .catch(() => null);\n\n if (gallery !== null) return gallery;\n }\n\n const dataSource = config.dataSources.find((o) => o.key === query.dataSource);\n if (dataSource === undefined) throw new Error(`data source ${query.dataSource} not found`);\n\n return dataSource.getGallery({\n id: query.id,\n });\n },\n {\n query: t.Object({\n id: t.String(),\n dataSource: dataSourceKeys,\n }),\n response: galleryModel,\n },\n )\n .get(\n \"/listing\",\n async ({ query }) => {\n const dataSource = config.dataSources.find((o) => o.key === query.dataSource);\n if (dataSource === undefined) throw new Error(`data source ${query.dataSource} not found`);\n\n return dataSource.getListing({\n searchQuery: query.query || null,\n page: query.page,\n });\n },\n {\n query: t.Object({\n query: t.Optional(t.String()),\n page: t.Number(),\n dataSource: dataSourceKeys,\n }),\n response: listingResultModel,\n },\n )\n .get(\n \"/tag\",\n async ({ query }) => {\n const dataSource = config.dataSources.find((o) => o.key === query.dataSource);\n if (dataSource === undefined) throw new Error(`data source ${query.dataSource} not found`);\n\n return dataSource.getTagListing({\n id: query.id,\n page: query.page,\n });\n },\n {\n query: t.Object({\n id: t.String(),\n page: t.Number(),\n dataSource: dataSourceKeys,\n }),\n response: listingResultModel,\n },\n )\n .post(\"/collection/export\", ({ body }) => upload(body, config.secretboxKey), {\n body: t.String(),\n })\n .get(\"/collection/import\", async ({ query }) => download(query.key, config.secretboxKey), {\n query: t.Object({\n key: t.String(),\n }),\n })\n .get(\n \"/image\",\n async ({ query }) => {\n const cacheKeys = [query.dataSource, query.url, query.format, query.type];\n\n const cachedImage = await cache.read<Buffer>(cacheKeys);\n if (cachedImage !== null)\n return new Response(Buffer.from(cachedImage.data), {\n headers: {\n \"Content-Type\": `image/${query.format}`,\n \"Cache-Control\": \"public, max-age=86400000\",\n },\n });\n\n const dataSource = config.dataSources.find((o) => o.key === query.dataSource);\n if (dataSource === undefined) throw new Error(`data source ${query.dataSource} not found`);\n\n const fetchedImage = new Bun.Image(\n await dataSource.getImage({\n url: query.url,\n }),\n ).resize(query.type === \"cover\" ? 640 : 1280);\n\n // if query.format is webp, then convert to webp\n if (query.format === \"webp\")\n fetchedImage.webp({\n quality: 72,\n });\n else if (query.format === \"jpeg\")\n fetchedImage.jpeg({\n quality: 72,\n });\n\n const resizedImage = await fetchedImage.buffer();\n await cache.write(\n cacheKeys,\n resizedImage,\n 86_400_000, // 1 month\n );\n\n return new Response(resizedImage, {\n headers: {\n \"Content-Type\": `image/${query.format}`,\n \"Cache-Control\": \"public, max-age=86400\",\n \"CDN-Cache-Control\": \"public, max-age=2592000\",\n \"Cloudflare-CDN-Cache-Control\": \"public, max-age=2592000\",\n },\n });\n },\n {\n query: t.Object({\n url: t.String(),\n format: t.Union([t.Literal(\"webp\"), t.Literal(\"jpeg\")]),\n type: t.Union([t.Literal(\"cover\"), t.Literal(\"page\")]),\n dataSource: dataSourceKeys,\n }),\n },\n )\n .listen({\n hostname: config.hostname ?? \"0.0.0.0\",\n port: config.port ?? 3000,\n });\n\nexport type Server = typeof server;\n\nconsole.log(`🦊 Elysia is running at http://${server.server?.hostname}:${server.server?.port}`);\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riffyh/server",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/rayriffy/rayriffy-h.git"
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
"@toon-tools/elysia": "1.1.0",
|
|
24
24
|
"debug": "4.4.3",
|
|
25
25
|
"elysia": "1.4.29",
|
|
26
|
-
"sharp": "0.35.2",
|
|
27
26
|
"tweetnacl": "1.0.3",
|
|
28
27
|
"tweetnacl-util": "0.15.1",
|
|
29
28
|
"@riffyh/commons": "2.2.0"
|