diff --git a/.vscode/settings.json b/.vscode/settings.json index 5beb24bcfe..b3d3d85722 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -110,6 +110,7 @@ "telepresence", "tolerations", "uids", + "ungzip", "upsert", "upserted", "urlencode", diff --git a/runtimes/nodejs/package-lock.json b/runtimes/nodejs/package-lock.json index 36edc72a63..6e2048ed7f 100644 --- a/runtimes/nodejs/package-lock.json +++ b/runtimes/nodejs/package-lock.json @@ -13,6 +13,7 @@ "@aws-sdk/s3-request-presigner": "^3.231.0", "@kubernetes/client-node": "^0.18.0", "@lafjs/cloud": "^1.0.0-beta.12", + "@types/pako": "^2.0.2", "axios": "^1.4.0", "chalk": "^4.1.2", "chatgpt": "^5.2.5", @@ -33,6 +34,7 @@ "multer": "^1.4.5-lts.1", "node-modules-utils": "^0.8.2", "nodemailer": "^6.6.3", + "pako": "^2.1.0", "validator": "^13.7.0", "ws": "^8.11.0" }, @@ -2792,6 +2794,11 @@ "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" }, + "node_modules/@types/pako": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.2.tgz", + "integrity": "sha512-AtTbzIwhvLMTEUPudP3hxUwNK50DoX3amfVJmmL7WQH5iF3Kfqs8pG1tStsewHqmh75ULmjjldKn/B70D6DNcQ==" + }, "node_modules/@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", @@ -5195,6 +5202,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -8561,6 +8573,11 @@ "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" }, + "@types/pako": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.2.tgz", + "integrity": "sha512-AtTbzIwhvLMTEUPudP3hxUwNK50DoX3amfVJmmL7WQH5iF3Kfqs8pG1tStsewHqmh75ULmjjldKn/B70D6DNcQ==" + }, "@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", @@ -10441,6 +10458,11 @@ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.1.tgz", "integrity": "sha512-yqz2Wi4fiFRpMmK0L2pGAU49naSUaP23fFIQL2Y6YT+qDGPoFwpvgQM/wzc6F8JoenUkIlAFa4Ql7NguXBxI7w==" }, + "pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, "parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", diff --git a/runtimes/nodejs/package.json b/runtimes/nodejs/package.json index 3d2b3bd3ff..9f4ef5016e 100644 --- a/runtimes/nodejs/package.json +++ b/runtimes/nodejs/package.json @@ -30,6 +30,7 @@ "@aws-sdk/s3-request-presigner": "^3.231.0", "@kubernetes/client-node": "^0.18.0", "@lafjs/cloud": "^1.0.0-beta.12", + "@types/pako": "^2.0.2", "axios": "^1.4.0", "chalk": "^4.1.2", "chatgpt": "^5.2.5", @@ -50,6 +51,7 @@ "multer": "^1.4.5-lts.1", "node-modules-utils": "^0.8.2", "nodemailer": "^6.6.3", + "pako": "^2.1.0", "validator": "^13.7.0", "ws": "^8.11.0" }, diff --git a/runtimes/nodejs/src/handler/invoke.ts b/runtimes/nodejs/src/handler/invoke.ts index 1a76f937d1..9554d7b3a0 100644 --- a/runtimes/nodejs/src/handler/invoke.ts +++ b/runtimes/nodejs/src/handler/invoke.ts @@ -10,6 +10,8 @@ import { FunctionContext, ICloudFunctionData, } from '../support/engine' +import pako from 'pako' +import { base64ToUint8Array, uint8ArrayToBase64 } from '../support/utils' export async function handleInvokeFunction(req: IRequest, res: Response) { const ctx: FunctionContext = { @@ -26,12 +28,6 @@ export async function handleInvokeFunction(req: IRequest, res: Response) { } let useInterceptor = true - - // intercept the request, skip websocket request - if (true === req.method.startsWith('WebSocket:')) { - useInterceptor = false - } - if (!FunctionCache.get(INTERCEPTOR_FUNCTION_NAME)) { useInterceptor = false } @@ -126,19 +122,31 @@ async function invokeDebug( .send('permission denied: invalid develop token') } - // get func_data from header - const funcStr = ctx.request.get('x-laf-func-data') - if (!funcStr) { - return ctx.response.status(400).send('x-laf-func-data is required') - } - // parse func_data let funcData: ICloudFunctionData - try { - const decoded = decodeURIComponent(funcStr) - funcData = JSON.parse(decoded) - } catch (error) { - return ctx.response.status(400).send('x-laf-func-data is invalid') + + // get func_data from header `x-laf-debug-data` + if (ctx.request.get('x-laf-debug-data')) { + const funcStr = ctx.request.get('x-laf-debug-data') + try { + // decode base64 string + const compressed = base64ToUint8Array(funcStr) + const restored = pako.ungzip(compressed, { to: 'string' }) + funcData = JSON.parse(restored) + } catch (error) { + return ctx.response.status(400).send('x-laf-debug-data is invalid') + } + } else if (ctx.request.get('x-laf-func-data')) { + // reserve 'x-laf-func-data' check to keep compatible to old clients (laf-web, laf-cli) + const funcStr = ctx.request.get('x-laf-func-data') + try { + const decoded = decodeURIComponent(funcStr) + funcData = JSON.parse(decoded) + } catch (error) { + return ctx.response.status(400).send('x-laf-func-data is invalid') + } + } else { + return ctx.response.status(400).send('x-laf-debug-data is required') } const requestId = ctx.requestId @@ -160,14 +168,23 @@ async function invokeDebug( ctx.__function_name = funcName const result = await func.execute(ctx, useInterceptor, debugConsole) - // set logs to response header if (result.error) { debugConsole.error(result.error) } - const logs = encodeURIComponent(debugConsole.getLogs()) - ctx.response.set('x-laf-func-logs', logs) - ctx.response.set('x-laf-func-time-usage', result.time_usage.toString()) + + const logs = debugConsole.getLogs() + if (ctx.request.get('x-laf-debug-data')) { + const compressed = pako.gzip(logs) + const base64Encoded = uint8ArrayToBase64(compressed) + ctx.response.set('x-laf-debug-logs', base64Encoded) + } else if (ctx.request.get('x-laf-func-data')) { + // keep compatible for old version clients(laf web & laf cli) + const encoded = encodeURIComponent(logs) + ctx.response.set('x-laf-func-logs', encoded) + } + + ctx.response.set('x-laf-debug-time-usage', result.time_usage.toString()) if (result.error) { return ctx.response.status(500).send({ diff --git a/runtimes/nodejs/src/support/utils.ts b/runtimes/nodejs/src/support/utils.ts index a28a3c620d..6fddd7cfbf 100644 --- a/runtimes/nodejs/src/support/utils.ts +++ b/runtimes/nodejs/src/support/utils.ts @@ -106,3 +106,12 @@ export function sleep(ms: number) { export function md5(content: string) { return crypto.createHash('md5').update(content).digest('hex') } + +export function uint8ArrayToBase64(buffer: Uint8Array) { + return Buffer.from(buffer).toString('base64') +} + +export function base64ToUint8Array(base64: string) { + const buffer = Buffer.from(base64, 'base64') + return new Uint8Array(buffer) +} \ No newline at end of file diff --git a/runtimes/nodejs/src/support/ws.ts b/runtimes/nodejs/src/support/ws.ts index e01292e081..7798427d2d 100644 --- a/runtimes/nodejs/src/support/ws.ts +++ b/runtimes/nodejs/src/support/ws.ts @@ -79,7 +79,6 @@ async function handleWebSocketEvent( } const module = FunctionModule.get(WEBSOCKET_FUNCTION_NAME) - console.log(module) const handler = module.default || module.main || module if(typeof handler === 'function') { await handler(param) diff --git a/server/src/gateway/website-task.service.ts b/server/src/gateway/website-task.service.ts index e7129c7d38..fdf10f05d7 100644 --- a/server/src/gateway/website-task.service.ts +++ b/server/src/gateway/website-task.service.ts @@ -96,7 +96,7 @@ export class WebsiteTaskService { bucketName: site.bucketName, }) - assert(bucketDomain, 'bucket domain not found') + assert(bucketDomain, `bucket domain not found: ${site.bucketName}`) // create website custom certificate if custom domain is set // Warning: create certificate before ingress, otherwise apisix ingress will not work diff --git a/server/src/storage/bucket-task.service.ts b/server/src/storage/bucket-task.service.ts index 8b554d996e..14764d6115 100644 --- a/server/src/storage/bucket-task.service.ts +++ b/server/src/storage/bucket-task.service.ts @@ -155,13 +155,6 @@ export class BucketTaskService { this.logger.debug('minio bucket deleted:', doc.name) } - // delete bucket domain - const domain = await this.bucketDomainService.findOne(doc) - if (domain) { - await this.bucketDomainService.deleteOne(doc) - this.logger.debug('bucket domain deleted:', domain) - } - // delete bucket website if exists const websiteRes = await db .collection('WebsiteHosting') @@ -174,6 +167,13 @@ export class BucketTaskService { this.logger.log('website state set to Deleted for bucket: ' + doc.name) } + // delete bucket domain + const domain = await this.bucketDomainService.findOne(doc) + if (domain) { + await this.bucketDomainService.deleteOne(doc) + this.logger.debug('bucket domain deleted:', domain) + } + // update phase to `Deleted` const updated = await db .collection('StorageBucket')