This commit is contained in:
seb
2026-10-12 04:05:54 +02:00
parent a2e446af47
commit 4429294d8b
4 changed files with 13 additions and 6 deletions

View File

@@ -82,11 +82,11 @@ Applet executed successfully
| Status | Description | | Status | Description |
|--------|-------------| |--------|-------------|
| 400 | No company selected, invalid path, not a .applet.js file, invalid input, or invalid applet metadata — `{"error":"…"}` | | 400 | No company selected, invalid path, not a .applet.js file, invalid input, invalid applet metadata, or applet runtime error — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` | | 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized, or cannot execute read-only database paths — `{"error":"…"}` | | 403 | Not authorized, or cannot execute read-only database paths — `{"error":"…"}` |
| 404 | Applet not found — `{"error":"…"}` | | 404 | Applet not found — `{"error":"…"}` |
| 500 | The applet threw during execution — `{"error":"…"}` | | 500 | Sandbox worker harness failure — `{"error":"…"}` |
| 504 | The applet exceeded the execution timeout and was terminated — `{"error":"…"}` | | 504 | The applet exceeded the execution timeout and was terminated — `{"error":"…"}` |
--- ---

View File

@@ -23,6 +23,10 @@ function captureResponseBody(res) {
res.json = (payload) => { res.json = (payload) => {
let out = payload; let out = payload;
if (res.statusCode >= 500 && payload && typeof payload === 'object' && 'error' in payload) { if (res.statusCode >= 500 && payload && typeof payload === 'object' && 'error' in payload) {
if (!res.locals?.error && typeof payload.error === 'string') {
res.locals = res.locals || {};
res.locals.error = new Error(payload.error);
}
out = { ...payload, error: 'Internal server error' }; out = { ...payload, error: 'Internal server error' };
} }
body = out; body = out;

View File

@@ -43,7 +43,8 @@ function appletExecErrorStatus(error) {
if (message.includes('is not a file')) return 400; if (message.includes('is not a file')) return 400;
if (message.includes('Applet metadata') || message.includes('Applet input schema') || message.includes('Applet output schema')) return 400; if (message.includes('Applet metadata') || message.includes('Applet input schema') || message.includes('Applet output schema')) return 400;
if (message.includes('Sandbox exceeded')) return 504; if (message.includes('Sandbox exceeded')) return 504;
return 500; if (message.includes('Sandbox worker did not report a result payload')) return 500;
return 400;
} }
// #endregion // #endregion
@@ -163,7 +164,7 @@ export function register(app, basePath = '') {
* truncationNote: * truncationNote:
* type: string * type: string
* 400: * 400:
* description: No company selected, invalid path, not a .applet.js file, invalid input, or invalid applet metadata * description: No company selected, invalid path, not a .applet.js file, invalid input, invalid applet metadata, or applet runtime error
* content: * content:
* application/json: * application/json:
* schema: * schema:
@@ -187,7 +188,7 @@ export function register(app, basePath = '') {
* schema: * schema:
* $ref: '#/components/schemas/Error' * $ref: '#/components/schemas/Error'
* 500: * 500:
* description: The applet threw during execution * description: Sandbox worker harness failure
* content: * content:
* application/json: * application/json:
* schema: * schema:
@@ -241,6 +242,7 @@ export function register(app, basePath = '') {
return res.status(404).json({ error: 'File not found' }); return res.status(404).json({ error: 'File not found' });
} }
} catch (error) { } catch (error) {
res.locals.error = error;
const status = vfsFileErrorStatus(error); const status = vfsFileErrorStatus(error);
if (status === 500) console.error('Execute vfs applet failed:', error); if (status === 500) console.error('Execute vfs applet failed:', error);
return res.status(status).json({ error: error.message }); return res.status(status).json({ error: error.message });
@@ -258,6 +260,7 @@ export function register(app, basePath = '') {
emit('vfs', { companyId: selected.companyId }); emit('vfs', { companyId: selected.companyId });
return res.json(result); return res.json(result);
} catch (error) { } catch (error) {
res.locals.error = error;
const status = appletExecErrorStatus(error); const status = appletExecErrorStatus(error);
if (status === 500) console.error('Execute vfs applet failed:', error); if (status === 500) console.error('Execute vfs applet failed:', error);
return res.status(status).json({ error: error.message }); return res.status(status).json({ error: error.message });

View File

@@ -1,4 +1,4 @@
// @applet-input-schema {"type":"object","properties":{"sku":{"type":"string","title":"Article SKU","default":"","description":"Exact SKU to inspect. Leave empty for a single-row manifest summary."}}} // @applet-input-schema {"type":"object","properties":{"sku":{"type":"string","title":"Article SKU","description":"Exact SKU to inspect (e.g. \"42000009\")."}},"required":["sku"]}
// @applet-output-schema key:string;value:string // @applet-output-schema key:string;value:string
// //
// Article lookup — the raw `GET /article/{sku}` payload as a key/value sheet. // Article lookup — the raw `GET /article/{sku}` payload as a key/value sheet.