@substrat-run/vertical-host 0.147.0 → 0.149.0
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.d.ts +17 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +120 -14
- package/dist/index.js.map +1 -1
- package/dist/invocation-log.d.ts +49 -1
- package/dist/invocation-log.d.ts.map +1 -1
- package/dist/invocation-log.js +80 -10
- package/dist/invocation-log.js.map +1 -1
- package/dist/outbound-calls.d.ts +94 -0
- package/dist/outbound-calls.d.ts.map +1 -0
- package/dist/outbound-calls.js +282 -0
- package/dist/outbound-calls.js.map +1 -0
- package/dist/peer-client.d.ts.map +1 -1
- package/dist/peer-client.js +6 -2
- package/dist/peer-client.js.map +1 -1
- package/dist/upload-link.d.ts +86 -0
- package/dist/upload-link.d.ts.map +1 -0
- package/dist/upload-link.js +267 -0
- package/dist/upload-link.js.map +1 -0
- package/package.json +7 -7
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { PROBLEM_CONTENT_TYPE, UPLOAD_LINK_IDEMPOTENCY_HEADER, UPLOAD_LINK_PATH_PREFIX, isUploadLinkIdempotencyKey, isUploadLinkSecretShape, substratError, uploadLinkFilename, } from '@substrat-run/contracts';
|
|
2
|
+
import { UPLOAD_LINK_GONE, UPLOAD_LINK_REPLAY_MISMATCH, UPLOAD_LINK_TOO_LARGE, UPLOAD_LINK_TYPE_REFUSED, UPLOAD_LINK_WRONG_TARGET, normalizeUploadMediaType, uploadContentTypeAllowed, } from '@substrat-run/kernel';
|
|
3
|
+
import { problemOf, messageOf } from './errors.js';
|
|
4
|
+
/**
|
|
5
|
+
* Where `mountUploadLinkRedeem` listens — the one path, not an option: `uploadLinkPath` mints
|
|
6
|
+
* URLs under it, and `invocationLog` redacts the secret segment under it.
|
|
7
|
+
*/
|
|
8
|
+
export const UPLOAD_LINK_REDEEM_PATH = `${UPLOAD_LINK_PATH_PREFIX}/:entityType/:entityId/:secret`;
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// The media type. A client's `Content-Type` is a claim; the bytes get a say where they can.
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
const GENERIC_TYPES = new Set(['application/octet-stream', 'application/x-www-form-urlencoded', 'binary/octet-stream']);
|
|
13
|
+
const EXTENSION_TYPES = {
|
|
14
|
+
pdf: 'application/pdf',
|
|
15
|
+
png: 'image/png',
|
|
16
|
+
jpg: 'image/jpeg',
|
|
17
|
+
jpeg: 'image/jpeg',
|
|
18
|
+
gif: 'image/gif',
|
|
19
|
+
txt: 'text/plain',
|
|
20
|
+
csv: 'text/csv',
|
|
21
|
+
json: 'application/json',
|
|
22
|
+
xml: 'application/xml',
|
|
23
|
+
html: 'text/html',
|
|
24
|
+
zip: 'application/zip',
|
|
25
|
+
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
26
|
+
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
27
|
+
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
28
|
+
};
|
|
29
|
+
const startsWith = (bytes, magic) => bytes.length >= magic.length && magic.every((b, i) => bytes[i] === b);
|
|
30
|
+
/** The few types whose first bytes say what they are. Anything else is `null`: not sniffable, not suspect. */
|
|
31
|
+
export function sniffMediaType(bytes) {
|
|
32
|
+
if (startsWith(bytes, [0x25, 0x50, 0x44, 0x46, 0x2d]))
|
|
33
|
+
return 'application/pdf'; // %PDF-
|
|
34
|
+
if (startsWith(bytes, [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))
|
|
35
|
+
return 'image/png';
|
|
36
|
+
if (startsWith(bytes, [0xff, 0xd8, 0xff]))
|
|
37
|
+
return 'image/jpeg';
|
|
38
|
+
if (startsWith(bytes, [0x47, 0x49, 0x46, 0x38]))
|
|
39
|
+
return 'image/gif'; // GIF8
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const SNIFFABLE = new Set(['application/pdf', 'image/png', 'image/jpeg', 'image/gif']);
|
|
43
|
+
/**
|
|
44
|
+
* The type the platform will record. Order: the request's own `Content-Type` unless it is
|
|
45
|
+
* generic (`curl --upload-file` sends none), else the file name's extension, else what the
|
|
46
|
+
* bytes say, else `application/octet-stream`. A claim about a type the bytes can prove — PDF,
|
|
47
|
+
* PNG, JPEG, GIF — that the bytes contradict is refused (`null`), as is a sniffable file under
|
|
48
|
+
* another claim: a `.pdf` that is a PNG is not what the link's allowlist was written for.
|
|
49
|
+
*/
|
|
50
|
+
export function resolveUploadMediaType(header, filename, bytes) {
|
|
51
|
+
const fromHeader = header ? normalizeUploadMediaType(header) : '';
|
|
52
|
+
const ext = filename.includes('.') ? filename.slice(filename.lastIndexOf('.') + 1).toLowerCase() : '';
|
|
53
|
+
const claimed = fromHeader && !GENERIC_TYPES.has(fromHeader) ? fromHeader : (EXTENSION_TYPES[ext] ?? null);
|
|
54
|
+
const sniffed = sniffMediaType(bytes);
|
|
55
|
+
if (claimed === null)
|
|
56
|
+
return sniffed ?? 'application/octet-stream';
|
|
57
|
+
if (SNIFFABLE.has(claimed))
|
|
58
|
+
return sniffed === claimed ? claimed : null;
|
|
59
|
+
return sniffed !== null ? null : claimed;
|
|
60
|
+
}
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// The file name.
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
function dispositionFilename(header) {
|
|
65
|
+
if (!header)
|
|
66
|
+
return undefined;
|
|
67
|
+
const star = /filename\*\s*=\s*UTF-8''([^;]+)/i.exec(header);
|
|
68
|
+
if (star) {
|
|
69
|
+
try {
|
|
70
|
+
return decodeURIComponent(star[1].trim());
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const plain = /filename\s*=\s*(?:"([^"]*)"|([^;]+))/i.exec(header);
|
|
77
|
+
return (plain?.[1] ?? plain?.[2])?.trim();
|
|
78
|
+
}
|
|
79
|
+
/** A leaf name, never a path: what the uploader called it, cleaned, or `undefined` if nothing usable. */
|
|
80
|
+
function cleanFilename(raw) {
|
|
81
|
+
if (raw === undefined)
|
|
82
|
+
return undefined;
|
|
83
|
+
const leaf = raw.split(/[\\/]/).pop() ?? '';
|
|
84
|
+
// eslint-disable-next-line no-control-regex
|
|
85
|
+
const cleaned = leaf.replace(/[\u0000-\u001f\u007f]/g, '').trim().slice(0, 255);
|
|
86
|
+
return uploadLinkFilename.safeParse(cleaned).success ? cleaned : undefined;
|
|
87
|
+
}
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
// The body, read under a ceiling.
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
class TooLarge extends Error {
|
|
92
|
+
}
|
|
93
|
+
/** Read the request body, refusing past `max` bytes as they arrive — not after buffering them all. */
|
|
94
|
+
async function readBody(request, max) {
|
|
95
|
+
const declared = request.headers.get('content-length');
|
|
96
|
+
if (declared !== null && Number(declared) > max)
|
|
97
|
+
throw new TooLarge();
|
|
98
|
+
if (!request.body)
|
|
99
|
+
return new Uint8Array(0);
|
|
100
|
+
const reader = request.body.getReader();
|
|
101
|
+
const chunks = [];
|
|
102
|
+
let total = 0;
|
|
103
|
+
for (;;) {
|
|
104
|
+
const { done, value } = await reader.read();
|
|
105
|
+
if (done)
|
|
106
|
+
break;
|
|
107
|
+
total += value.byteLength;
|
|
108
|
+
if (total > max) {
|
|
109
|
+
await reader.cancel().catch(() => { });
|
|
110
|
+
throw new TooLarge();
|
|
111
|
+
}
|
|
112
|
+
chunks.push(value);
|
|
113
|
+
}
|
|
114
|
+
const out = new Uint8Array(total);
|
|
115
|
+
let at = 0;
|
|
116
|
+
for (const chunk of chunks) {
|
|
117
|
+
out.set(chunk, at);
|
|
118
|
+
at += chunk.byteLength;
|
|
119
|
+
}
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// The answers.
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
const reasonOf = (err) => {
|
|
126
|
+
const ext = err?.extensions;
|
|
127
|
+
return typeof ext?.reason === 'string' ? ext.reason : undefined;
|
|
128
|
+
};
|
|
129
|
+
const STATUS_BY_REASON = {
|
|
130
|
+
[UPLOAD_LINK_GONE]: 410,
|
|
131
|
+
[UPLOAD_LINK_TOO_LARGE]: 413,
|
|
132
|
+
[UPLOAD_LINK_TYPE_REFUSED]: 415,
|
|
133
|
+
[UPLOAD_LINK_WRONG_TARGET]: 404,
|
|
134
|
+
[UPLOAD_LINK_REPLAY_MISMATCH]: 422,
|
|
135
|
+
};
|
|
136
|
+
const CORS_HEADERS = [
|
|
137
|
+
'access-control-allow-origin',
|
|
138
|
+
'access-control-allow-credentials',
|
|
139
|
+
'access-control-expose-headers',
|
|
140
|
+
'vary',
|
|
141
|
+
];
|
|
142
|
+
function refuse(c, err) {
|
|
143
|
+
const reason = reasonOf(err);
|
|
144
|
+
const status = reason !== undefined ? STATUS_BY_REASON[reason] : undefined;
|
|
145
|
+
if (status === undefined) {
|
|
146
|
+
// Not ours to reclassify: the platform's ordinary vocabulary (403 for a lost key, 404 for an unknown secret).
|
|
147
|
+
const { status: decided, body } = problemOf({ status: errorStatus(err), message: messageOf(err) }, err, c.req.path.replace(/\/[^/]+$/, '/…'));
|
|
148
|
+
return c.body(JSON.stringify(body), decided, { 'content-type': PROBLEM_CONTENT_TYPE });
|
|
149
|
+
}
|
|
150
|
+
const body = {
|
|
151
|
+
type: 'about:blank',
|
|
152
|
+
title: reason,
|
|
153
|
+
status,
|
|
154
|
+
detail: messageOf(err),
|
|
155
|
+
reason,
|
|
156
|
+
};
|
|
157
|
+
return c.body(JSON.stringify(body), status, { 'content-type': PROBLEM_CONTENT_TYPE });
|
|
158
|
+
}
|
|
159
|
+
const STATUS_BY_CODE = {
|
|
160
|
+
unauthenticated: 401,
|
|
161
|
+
permission_denied: 403,
|
|
162
|
+
forbidden: 403,
|
|
163
|
+
not_found: 404,
|
|
164
|
+
conflict: 409,
|
|
165
|
+
validation_failed: 400,
|
|
166
|
+
unavailable: 503,
|
|
167
|
+
};
|
|
168
|
+
function errorStatus(err) {
|
|
169
|
+
const code = err?.code;
|
|
170
|
+
return (code && STATUS_BY_CODE[code]) || 500;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* The `Idempotency-Key` a request carries, or `undefined` for none: the bare token, or one
|
|
174
|
+
* structured-field string (`"…"`, the IETF draft's spelling) with its quotes taken off. Anything
|
|
175
|
+
* else is a 400 — never a key matched loosely.
|
|
176
|
+
*/
|
|
177
|
+
function idempotencyKeyOf(header) {
|
|
178
|
+
if (header === undefined)
|
|
179
|
+
return undefined;
|
|
180
|
+
const key = header.length >= 2 && header.startsWith('"') && header.endsWith('"') ? header.slice(1, -1) : header;
|
|
181
|
+
if (!isUploadLinkIdempotencyKey(key)) {
|
|
182
|
+
throw substratError('validation_failed', 'An Idempotency-Key is 16 to 255 visible ASCII characters, with no space, quote or backslash, such as a UUID.');
|
|
183
|
+
}
|
|
184
|
+
return key;
|
|
185
|
+
}
|
|
186
|
+
/** The 404 every secret that names nothing gets — the scope's answer, and this route's for a malformed one. */
|
|
187
|
+
const unknownLink = () => substratError('not_found', 'This upload link is not valid.');
|
|
188
|
+
/**
|
|
189
|
+
* Mount `PUT` and `POST` on `UPLOAD_LINK_REDEEM_PATH`: redeem one upload link. See the module
|
|
190
|
+
* header for the answers and the hygiene.
|
|
191
|
+
*/
|
|
192
|
+
export function mountUploadLinkRedeem(app, options) {
|
|
193
|
+
const handler = async (c) => {
|
|
194
|
+
// Before anything can fail: nothing here may be cached or handed onwards, and an outer
|
|
195
|
+
// blanket CORS layer must not make the answer readable by a page that learned the link.
|
|
196
|
+
c.header('Cache-Control', 'no-store');
|
|
197
|
+
c.header('Referrer-Policy', 'no-referrer');
|
|
198
|
+
c.header('X-Content-Type-Options', 'nosniff');
|
|
199
|
+
for (const name of CORS_HEADERS)
|
|
200
|
+
c.header(name, undefined);
|
|
201
|
+
try {
|
|
202
|
+
const entityType = c.req.param('entityType');
|
|
203
|
+
const entityId = c.req.param('entityId');
|
|
204
|
+
const secret = c.req.param('secret');
|
|
205
|
+
// A secret that cannot be one the kernel minted costs the scope nothing: the same 404 an
|
|
206
|
+
// unknown one gets, before the host, the scope or its Durable Object is touched.
|
|
207
|
+
if (!entityType || !entityId || !isUploadLinkSecretShape(secret))
|
|
208
|
+
throw unknownLink();
|
|
209
|
+
// #2315: the redeemer's own id for this redemption, judged before the scope is asked, so a
|
|
210
|
+
// malformed one is the same 400 whatever the link's state.
|
|
211
|
+
const idempotencyKey = idempotencyKeyOf(c.req.header(UPLOAD_LINK_IDEMPOTENCY_HEADER));
|
|
212
|
+
const [host, node] = await Promise.all([options.host(c), options.node(c)]);
|
|
213
|
+
if (!host.inspectUploadLink || !host.redeemUploadLink) {
|
|
214
|
+
throw substratError('unavailable', 'this scope host serves no upload links');
|
|
215
|
+
}
|
|
216
|
+
// The link first, so a refusal costs no body: unknown or minted for another target (404,
|
|
217
|
+
// judged before its state), used / expired / revoked (410), and what the body will be
|
|
218
|
+
// judged against.
|
|
219
|
+
const view = await host.inspectUploadLink(node.tenantId, node.scopeId, secret, { entityType, entityId }, idempotencyKey);
|
|
220
|
+
const filename = view.filename ??
|
|
221
|
+
cleanFilename(c.req.query('filename')) ??
|
|
222
|
+
cleanFilename(dispositionFilename(c.req.header('content-disposition'))) ??
|
|
223
|
+
'upload';
|
|
224
|
+
let body;
|
|
225
|
+
try {
|
|
226
|
+
body = await readBody(c.req.raw, view.maxBytes);
|
|
227
|
+
}
|
|
228
|
+
catch (err) {
|
|
229
|
+
if (err instanceof TooLarge) {
|
|
230
|
+
throw substratError('validation_failed', `The file is larger than the ${view.maxBytes} bytes this link admits.`, {
|
|
231
|
+
reason: UPLOAD_LINK_TOO_LARGE,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
throw err;
|
|
235
|
+
}
|
|
236
|
+
if (body.byteLength === 0)
|
|
237
|
+
throw substratError('validation_failed', 'An empty file cannot be uploaded.');
|
|
238
|
+
const contentType = resolveUploadMediaType(c.req.header('content-type'), filename, body);
|
|
239
|
+
if (contentType === null || !uploadContentTypeAllowed(view.contentTypes, contentType)) {
|
|
240
|
+
throw substratError('validation_failed', 'A file of that type is not admitted by this link.', {
|
|
241
|
+
reason: UPLOAD_LINK_TYPE_REFUSED,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
const record = await host.redeemUploadLink(node.tenantId, node.scopeId, secret, {
|
|
245
|
+
entity: view.entity,
|
|
246
|
+
filename,
|
|
247
|
+
contentType,
|
|
248
|
+
body,
|
|
249
|
+
...(idempotencyKey !== undefined ? { idempotencyKey } : {}),
|
|
250
|
+
});
|
|
251
|
+
// 200 for a replay of the redemption that spent the link, 201 for a file stored now.
|
|
252
|
+
return c.body(JSON.stringify({
|
|
253
|
+
id: record.id,
|
|
254
|
+
filename: record.filename,
|
|
255
|
+
contentType: record.contentType,
|
|
256
|
+
size: record.size,
|
|
257
|
+
sha256: record.sha256,
|
|
258
|
+
}), record.replayed ? 200 : 201, { 'content-type': 'application/json' });
|
|
259
|
+
}
|
|
260
|
+
catch (err) {
|
|
261
|
+
return refuse(c, err);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
app.put(UPLOAD_LINK_REDEEM_PATH, handler);
|
|
265
|
+
app.post(UPLOAD_LINK_REDEEM_PATH, handler);
|
|
266
|
+
}
|
|
267
|
+
//# sourceMappingURL=upload-link.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-link.js","sourceRoot":"","sources":["../src/upload-link.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@substrat-run/vertical-host",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.149.0",
|
|
4
4
|
"description": "The platform's /internal/* surface + error envelope, authored once — mount it in a vertical worker instead of re-copying the control-plane contract",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"repository": {
|
|
@@ -29,18 +29,18 @@
|
|
|
29
29
|
"ai": "7.0.136",
|
|
30
30
|
"hono": "^4.13.13",
|
|
31
31
|
"workers-ai-provider": "^4.0.0",
|
|
32
|
-
"@substrat-run/contracts": "^0.
|
|
33
|
-
"@substrat-run/model-providers": "^0.5.
|
|
34
|
-
"@substrat-run/kernel": "^0.
|
|
32
|
+
"@substrat-run/contracts": "^0.149.0",
|
|
33
|
+
"@substrat-run/model-providers": "^0.5.39",
|
|
34
|
+
"@substrat-run/kernel": "^0.149.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/better-sqlite3": "^9.6.0",
|
|
38
38
|
"better-sqlite3": "^13.0.3",
|
|
39
39
|
"typescript": "^7.0.0",
|
|
40
40
|
"vitest": "^5.0.3",
|
|
41
|
-
"@substrat-run/
|
|
42
|
-
"@substrat-run/vertical-auth": "^0.24.
|
|
43
|
-
"@substrat-run/
|
|
41
|
+
"@substrat-run/contract-tests": "^0.149.0",
|
|
42
|
+
"@substrat-run/vertical-auth": "^0.24.4",
|
|
43
|
+
"@substrat-run/adapter-sqlite": "^0.149.0"
|
|
44
44
|
},
|
|
45
45
|
"publishConfig": {
|
|
46
46
|
"access": "public"
|