diff --git a/adapters/satori/package.json b/adapters/satori/package.json index 9760cc96..ef139da3 100644 --- a/adapters/satori/package.json +++ b/adapters/satori/package.json @@ -1,7 +1,7 @@ { "name": "@satorijs/adapter-satori", "description": "Satori Adapter for Satorijs", - "version": "1.3.1", + "version": "1.4.0", "type": "module", "exports": { ".": { diff --git a/adapters/satori/src/bot.ts b/adapters/satori/src/bot.ts index 0940fc40..8896dd4f 100644 --- a/adapters/satori/src/bot.ts +++ b/adapters/satori/src/bot.ts @@ -1,4 +1,4 @@ -import { Bot, camelCase, Context, h, HTTP, snakeCase, Universal } from '@satorijs/core' +import { Bot, camelCase, Context, Dict, h, HTTP, snakeCase, Universal, valueMap } from '@satorijs/core' export function transformKey(source: any, callback: (key: string) => string) { if (!source || typeof source !== 'object') return source @@ -9,12 +9,40 @@ export function transformKey(source: any, callback: (key: string) => string) { })) } +function serialize(data: any, path: string, blobs: Dict) { + if (!data || typeof data !== 'object') return data + if (data instanceof Blob) { + blobs[path] = data + return null + } + if (Array.isArray(data)) { + return data.map((value, index) => serialize(value, `${path}.${index}`, blobs)) + } + return valueMap(data, (value, key) => { + return serialize(value, `${path}.${key}`, blobs) + }) +} + function createInternal(bot: SatoriBot, prefix = '') { return new Proxy(() => {}, { apply(target, thisArg, args) { const key = snakeCase(prefix.slice(1)) bot.logger.debug('[request.internal]', key, args) - return bot.http.post('/v1/internal/' + key, args) + const blobs: Dict = Object.create(null) + const data = serialize(args, '$', blobs) + if (!Object.keys(blobs).length) { + return bot.http.post('/v1/internal/' + key, args) + } + const form = new FormData() + form.append('$', new Blob([JSON.stringify(data)], { type: 'application/json' })) + for (const [key, value] of Object.entries(blobs)) { + if (value instanceof File) { + form.append(key, value, value.name) + } else { + form.append(key, value) + } + } + return bot.http.post('/v1/internal/' + key, form) }, get(target, key, receiver) { if (typeof key === 'symbol' || key in target) { diff --git a/packages/server/package.json b/packages/server/package.json index ef671fc4..31bd5aa2 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@satorijs/plugin-server", "description": "Basic API server for Satori protocol", - "version": "2.7.3", + "version": "2.8.0", "type": "module", "exports": { ".": { diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 6d53f272..f2370fde 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -1,4 +1,4 @@ -import { Binary, camelCase, Context, makeArray, sanitize, Schema, Service, Session, snakeCase, Time, Universal } from '@satorijs/core' +import { Binary, camelCase, Context, Dict, makeArray, sanitize, Schema, Service, Session, snakeCase, Time, Universal, valueMap } from '@satorijs/core' import {} from '@cordisjs/plugin-server' import WebSocket from 'ws' import { Readable } from 'node:stream' @@ -33,6 +33,17 @@ function transformKey(source: any, callback: (key: string) => string) { })) } +function deserialize(data: any, path: string, blobs: Dict) { + if (path in blobs) return blobs[path] + if (!data || typeof data !== 'object') return data + if (Array.isArray(data)) { + return data.map((value, index) => deserialize(value, `${path}.${index}`, blobs)) + } + return valueMap(data, (value, key) => { + return deserialize(value, `${path}.${key}`, blobs) + }) +} + class SatoriServer extends Service { static inject = ['server', 'http'] @@ -122,7 +133,21 @@ class SatoriServer extends Service { return } try { - const result = await bot.internal[name](...koa.request.body) + let args = koa.request.body + if (koa.request.files) { + const blobs: Dict = Object.create(null) + const { $, ...files } = koa.request.files + const [json] = await Promise.all([ + readFile(makeArray($)[0].filepath, 'utf8'), + Promise.all(Object.entries(files).map(async ([key, value]) => { + value = makeArray(value)[0] + const buffer = await readFile(value.filepath) + return [key, new File([buffer], value.originalFilename!, { type: value.mimetype! })] + })), + ]) + args = deserialize(JSON.parse(json), '$', blobs) + } + const result = await bot.internal[name](...args) koa.body = result koa.status = 200 } catch (error) { @@ -171,6 +196,9 @@ class SatoriServer extends Service { if (!ctx.http.isError(error) || !error.response) throw error koa.status = error.response.status koa.body = error.response.data + for (const [key, value] of error.response.headers) { + koa.set(key, value) + } } } })