From aa96d79e473d9770370f51ecc0d4db17d1f0cb34 Mon Sep 17 00:00:00 2001 From: maix0 Date: Sat, 14 Jun 2025 03:16:48 +0200 Subject: [PATCH] update: icon store --- src/user-icons/package-lock.json | 142 ++++++++++++++++++++++++ src/user-icons/package.json | 3 + src/user-icons/src/app.ts | 69 ++++++++---- src/user-icons/src/plugins/support.ts | 20 ---- src/user-icons/src/routes/get/index.ts | 16 +++ src/user-icons/src/routes/icon/index.ts | 17 --- src/user-icons/src/routes/root.ts | 9 +- src/user-icons/src/routes/set/index.ts | 23 ++++ 8 files changed, 235 insertions(+), 64 deletions(-) delete mode 100644 src/user-icons/src/plugins/support.ts create mode 100644 src/user-icons/src/routes/get/index.ts delete mode 100644 src/user-icons/src/routes/icon/index.ts create mode 100644 src/user-icons/src/routes/set/index.ts diff --git a/src/user-icons/package-lock.json b/src/user-icons/package-lock.json index 6dba909..43ad2bd 100644 --- a/src/user-icons/package-lock.json +++ b/src/user-icons/package-lock.json @@ -10,11 +10,14 @@ "license": "ISC", "dependencies": { "@fastify/autoload": "^6.3.1", + "@fastify/formbody": "^8.0.2", + "@fastify/multipart": "^9.0.3", "@fastify/sensible": "^6.0.0", "@fastify/static": "^8.2.0", "fastify": "^5.0.0", "fastify-cli": "^7.4.0", "fastify-plugin": "^5.0.0", + "fastify-raw-body": "^5.0.0", "sharp": "^0.34.2" }, "devDependencies": { @@ -112,6 +115,12 @@ ], "license": "MIT" }, + "node_modules/@fastify/busboy": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.1.tgz", + "integrity": "sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==", + "license": "MIT" + }, "node_modules/@fastify/deepmerge": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-2.0.2.tgz", @@ -163,6 +172,26 @@ "fast-json-stringify": "^6.0.0" } }, + "node_modules/@fastify/formbody": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@fastify/formbody/-/formbody-8.0.2.tgz", + "integrity": "sha512-84v5J2KrkXzjgBpYnaNRPqwgMsmY7ZDjuj0YVuMR3NXCJRCgKEZy/taSP1wUYGn0onfxJpLyRGDLa+NMaDJtnA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "fast-querystring": "^1.1.2", + "fastify-plugin": "^5.0.0" + } + }, "node_modules/@fastify/forwarded": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.0.tgz", @@ -188,6 +217,45 @@ "dequal": "^2.0.3" } }, + "node_modules/@fastify/multipart": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@fastify/multipart/-/multipart-9.0.3.tgz", + "integrity": "sha512-pJogxQCrT12/6I5Fh6jr3narwcymA0pv4B0jbC7c6Bl9wnrxomEUnV0d26w6gUls7gSXmhG8JGRMmHFIPsxt1g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^3.0.0", + "@fastify/deepmerge": "^2.0.0", + "@fastify/error": "^4.0.0", + "fastify-plugin": "^5.0.0", + "secure-json-parse": "^3.0.0" + } + }, + "node_modules/@fastify/multipart/node_modules/secure-json-parse": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-3.0.2.tgz", + "integrity": "sha512-H6nS2o8bWfpFEV6U38sOSjS7bTbdgbCGU9wEM6W14P5H0QOsz94KCusifV44GpHDTu2nqZbuDNhTzu+mjDSw1w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/@fastify/proxy-addr": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.0.0.tgz", @@ -1020,6 +1088,15 @@ "balanced-match": "^1.0.0" } }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/c8": { "version": "10.1.3", "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.3.tgz", @@ -1552,6 +1629,29 @@ "integrity": "sha512-HCxs+YnRaWzCl+cWRYFnHmeRFyR5GVnJTAaCJQiYzQSDwK9MgJdyAsuL3nh0EWRCYMgQ5MeziymvmAhUHYHDUQ==", "license": "MIT" }, + "node_modules/fastify-raw-body": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/fastify-raw-body/-/fastify-raw-body-5.0.0.tgz", + "integrity": "sha512-2qfoaQ3BQDhZ1gtbkKZd6n0kKxJISJGM6u/skD9ljdWItAscjXrtZ1lnjr7PavmXX9j4EyCPmBDiIsLn07d5vA==", + "license": "MIT", + "dependencies": { + "fastify-plugin": "^5.0.0", + "raw-body": "^3.0.0", + "secure-json-parse": "^2.4.0" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "url": "https://github.com/Eomm/fastify-raw-body?sponsor=1" + } + }, + "node_modules/fastify-raw-body/node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", + "license": "BSD-3-Clause" + }, "node_modules/fastify-tsconfig": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/fastify-tsconfig/-/fastify-tsconfig-3.0.0.tgz", @@ -1721,6 +1821,18 @@ "node": ">= 0.8" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -2343,6 +2455,21 @@ "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", "license": "MIT" }, + "node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -2490,6 +2617,12 @@ "node": ">=10" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/secure-json-parse": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.0.0.tgz", @@ -2929,6 +3062,15 @@ "dev": true, "license": "MIT" }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/src/user-icons/package.json b/src/user-icons/package.json index 386a873..c422004 100644 --- a/src/user-icons/package.json +++ b/src/user-icons/package.json @@ -20,11 +20,14 @@ "license": "ISC", "dependencies": { "@fastify/autoload": "^6.3.1", + "@fastify/formbody": "^8.0.2", + "@fastify/multipart": "^9.0.3", "@fastify/sensible": "^6.0.0", "@fastify/static": "^8.2.0", "fastify": "^5.0.0", "fastify-cli": "^7.4.0", "fastify-plugin": "^5.0.0", + "fastify-raw-body": "^5.0.0", "sharp": "^0.34.2" }, "devDependencies": { diff --git a/src/user-icons/src/app.ts b/src/user-icons/src/app.ts index 74d9ff0..7d5b1b3 100644 --- a/src/user-icons/src/app.ts +++ b/src/user-icons/src/app.ts @@ -2,44 +2,69 @@ import * as path from 'node:path' import AutoLoad, { AutoloadPluginOptions } from '@fastify/autoload' import { FastifyPluginAsync } from 'fastify' import { fileURLToPath } from 'node:url' +import fastifyFormBody from '@fastify/formbody' +import fastifyMultipart from '@fastify/multipart' +import { mkdir } from 'node:fs/promises' +import fp from 'fastify-plugin' const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) export type AppOptions = { - // Place your custom options for app below here. + // Place your custom options for app below here. } & Partial // Pass --options via CLI arguments in command to enable these options. const options: AppOptions = { } +// When using .decorate you have to specify added properties for Typescript +declare module 'fastify' { + export interface FastifyInstance { + image_store: string; + } +} + const app: FastifyPluginAsync = async ( - fastify, - opts + fastify, + opts ): Promise => { - // Place here your custom code! + // Place here your custom code! - // Do not touch the following lines + void fastify.register(fastifyFormBody, {}) + void fastify.register(fastifyMultipart, {}) - // This loads all plugins defined in plugins - // those should be support plugins that are reused - // through your application - // eslint-disable-next-line no-void - void fastify.register(AutoLoad, { - dir: path.join(__dirname, 'plugins'), - options: opts, - forceESM: true - }) + // The use of fastify-plugin is required to be able + // to export the decorators to the outer scope + void fastify.register(fp(async (fastify) => { + const image_store = process.env.USER_ICONS_STORE ?? "/tmp/icons"; + fastify.decorate('image_store', image_store) + await mkdir(fastify.image_store, { recursive: true }) + })) + + + + // Do not touch the following lines + + // This loads all plugins defined in plugins + // those should be support plugins that are reused + // through your application + // eslint-disable-next-line no-void + void fastify.register(AutoLoad, { + dir: path.join(__dirname, 'plugins'), + options: opts, + forceESM: true + }) + + // This loads all plugins defined in routes + // define your routes in one of these + // eslint-disable-next-line no-void + void fastify.register(AutoLoad, { + dir: path.join(__dirname, 'routes'), + options: opts, + forceESM: true + }) - // This loads all plugins defined in routes - // define your routes in one of these - // eslint-disable-next-line no-void - void fastify.register(AutoLoad, { - dir: path.join(__dirname, 'routes'), - options: opts, - forceESM: true - }) } export default app diff --git a/src/user-icons/src/plugins/support.ts b/src/user-icons/src/plugins/support.ts deleted file mode 100644 index 94bae4f..0000000 --- a/src/user-icons/src/plugins/support.ts +++ /dev/null @@ -1,20 +0,0 @@ -import fp from 'fastify-plugin' - -export interface SupportPluginOptions { - // Specify Support plugin options here -} - -// The use of fastify-plugin is required to be able -// to export the decorators to the outer scope -export default fp(async (fastify, opts) => { - fastify.decorate('someSupport', function () { - return 'hugs' - }) -}) - -// When using .decorate you have to specify added properties for Typescript -declare module 'fastify' { - export interface FastifyInstance { - someSupport(): string; - } -} diff --git a/src/user-icons/src/routes/get/index.ts b/src/user-icons/src/routes/get/index.ts new file mode 100644 index 0000000..3711bb0 --- /dev/null +++ b/src/user-icons/src/routes/get/index.ts @@ -0,0 +1,16 @@ +import { FastifyPluginAsync } from 'fastify' +import fastifyStatic from '@fastify/static' + +const example: FastifyPluginAsync = async (fastify, opts): Promise => { + fastify.register(fastifyStatic, { + root: fastify.getDecorator('image_store')!, + prefix: '/', + }) + fastify.get('/:userid', async (req, res) => { + const filename = (req.params as any)['userid'] + '.png'; + await res.sendFile(filename) + }) +} + +export default example + diff --git a/src/user-icons/src/routes/icon/index.ts b/src/user-icons/src/routes/icon/index.ts deleted file mode 100644 index 35c5c31..0000000 --- a/src/user-icons/src/routes/icon/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { fastifyStatic } from '@fastify/static' -import process from 'node:process' -import { FastifyPluginAsync } from 'fastify' - -const example: FastifyPluginAsync = async (fastify, opts): Promise => { - console.log("HELLO ????") - fastify.post('/set/:userid', async function(request, reply) { - console.log(request.params) - }) - - fastify.register(fastifyStatic, { - root: process.env.USER_ICONS_STORE ?? "/tmp/icons", - prefix: '/get', - }) -} - -export default example diff --git a/src/user-icons/src/routes/root.ts b/src/user-icons/src/routes/root.ts index 27918c7..ebccaed 100644 --- a/src/user-icons/src/routes/root.ts +++ b/src/user-icons/src/routes/root.ts @@ -1,9 +1,8 @@ import { FastifyPluginAsync } from 'fastify' -const root: FastifyPluginAsync = async (fastify, opts): Promise => { - fastify.get('/', async function (request, reply) { - return { root: true } - }) + +const example: FastifyPluginAsync = async (fastify, opts): Promise => { + fastify.get('*', async (req, res) => res.code(403)) } -export default root +export default example diff --git a/src/user-icons/src/routes/set/index.ts b/src/user-icons/src/routes/set/index.ts new file mode 100644 index 0000000..17be1e9 --- /dev/null +++ b/src/user-icons/src/routes/set/index.ts @@ -0,0 +1,23 @@ +import { FastifyPluginAsync } from 'fastify' +import { join } from 'node:path' +import { open } from 'node:fs/promises' +import fastifyRawBody from 'fastify-raw-body' + +const example: FastifyPluginAsync = async (fastify, opts): Promise => { + // fastify.register(DeadgeInternalApi, {}) + await fastify.register(fastifyRawBody, { encoding: false }); + fastify.post('/:userid', { config: { rawBody: true, encoding: false } }, async function(request, reply) { + const userid: string | undefined = (request.params as any)['userid']; + if (userid === undefined) { + return await reply.code(403); + } + const image_store: string = fastify.getDecorator('image_store') + const image_path = join(image_store, userid + ".png") + let image_file = await open(image_path, "w", 0o666) + await image_file.write(request.rawBody as Buffer); + await image_file.close() + }) +} + +export default example +