pipework 0.66.0 → 0.68.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/CHANGELOG.md +12 -0
- package/REFERENCE.md +89 -2
- package/dist/core/config/namespace.d.ts +1 -0
- package/dist/core/config/namespace.d.ts.map +1 -1
- package/dist/core/config/schema.d.ts +3 -0
- package/dist/core/config/schema.d.ts.map +1 -1
- package/dist/core/config/schema.js +2 -0
- package/dist/core/config/schema.js.map +1 -1
- package/dist/data/migrate/internal-definitions.d.ts.map +1 -1
- package/dist/data/migrate/internal-definitions.js +4 -0
- package/dist/data/migrate/internal-definitions.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/infra/aws/sigv4.d.ts +46 -0
- package/dist/infra/aws/sigv4.d.ts.map +1 -0
- package/dist/infra/aws/sigv4.js +94 -0
- package/dist/infra/aws/sigv4.js.map +1 -0
- package/dist/infra/email/index.d.ts +1 -1
- package/dist/infra/email/index.d.ts.map +1 -1
- package/dist/infra/email/index.js +1 -1
- package/dist/infra/email/index.js.map +1 -1
- package/dist/infra/email/providers.d.ts +1 -1
- package/dist/infra/email/providers.d.ts.map +1 -1
- package/dist/infra/email/providers.js +1 -1
- package/dist/infra/email/providers.js.map +1 -1
- package/dist/infra/storage/backend.d.ts +32 -0
- package/dist/infra/storage/backend.d.ts.map +1 -0
- package/dist/infra/storage/backend.js +2 -0
- package/dist/infra/storage/backend.js.map +1 -0
- package/dist/infra/storage/handle.d.ts +110 -0
- package/dist/infra/storage/handle.d.ts.map +1 -0
- package/dist/infra/storage/handle.js +317 -0
- package/dist/infra/storage/handle.js.map +1 -0
- package/dist/infra/storage/index.d.ts +8 -0
- package/dist/infra/storage/index.d.ts.map +1 -0
- package/dist/infra/storage/index.js +4 -0
- package/dist/infra/storage/index.js.map +1 -0
- package/dist/infra/storage/local.d.ts +16 -0
- package/dist/infra/storage/local.d.ts.map +1 -0
- package/dist/infra/storage/local.js +95 -0
- package/dist/infra/storage/local.js.map +1 -0
- package/dist/infra/storage/namespace.d.ts +13 -0
- package/dist/infra/storage/namespace.d.ts.map +1 -0
- package/dist/infra/storage/namespace.js +13 -0
- package/dist/infra/storage/namespace.js.map +1 -0
- package/dist/infra/storage/s3.d.ts +34 -0
- package/dist/infra/storage/s3.d.ts.map +1 -0
- package/dist/infra/storage/s3.js +220 -0
- package/dist/infra/storage/s3.js.map +1 -0
- package/dist/infra/storage/schema.d.ts +43 -0
- package/dist/infra/storage/schema.d.ts.map +1 -0
- package/dist/infra/storage/schema.js +23 -0
- package/dist/infra/storage/schema.js.map +1 -0
- package/dist/request/di/builder.d.ts +28 -1
- package/dist/request/di/builder.d.ts.map +1 -1
- package/dist/request/di/builder.js +49 -0
- package/dist/request/di/builder.js.map +1 -1
- package/dist/request/di/index.d.ts +1 -1
- package/dist/request/di/index.d.ts.map +1 -1
- package/dist/request/di/types.d.ts +8 -0
- package/dist/request/di/types.d.ts.map +1 -1
- package/dist/request/http/file.d.ts +62 -0
- package/dist/request/http/file.d.ts.map +1 -0
- package/dist/request/http/file.js +120 -0
- package/dist/request/http/file.js.map +1 -0
- package/dist/request/http/index.d.ts +1 -0
- package/dist/request/http/index.d.ts.map +1 -1
- package/dist/request/http/index.js.map +1 -1
- package/dist/request/http/namespace.d.ts +5 -2
- package/dist/request/http/namespace.d.ts.map +1 -1
- package/dist/request/http/namespace.js +3 -2
- package/dist/request/http/namespace.js.map +1 -1
- package/dist/request/http/server.d.ts.map +1 -1
- package/dist/request/http/server.js +49 -3
- package/dist/request/http/server.js.map +1 -1
- package/dist/request/http/types.d.ts +3 -1
- package/dist/request/http/types.d.ts.map +1 -1
- package/dist/request/openapi/generate.d.ts.map +1 -1
- package/dist/request/openapi/generate.js +7 -1
- package/dist/request/openapi/generate.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { createReadStream, createWriteStream } from 'node:fs';
|
|
2
|
+
import { copyFile, mkdir, readdir, rename, rm, stat } from 'node:fs/promises';
|
|
3
|
+
import { dirname, join, resolve, sep } from 'node:path';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
import { pipeline } from 'node:stream/promises';
|
|
6
|
+
import { Readable } from 'node:stream';
|
|
7
|
+
/**
|
|
8
|
+
* Files on local disk, for development and tests. Refused by `storage.create` when
|
|
9
|
+
* its `environment` is `production`. There are no signed URLs: `signedUrl` throws
|
|
10
|
+
* here, so serve a file by streaming `open()` through `http.file()`.
|
|
11
|
+
*
|
|
12
|
+
* In a test, give each file its own directory (`mkdtemp`) and remove it in `afterAll`;
|
|
13
|
+
* rows written through the test harness roll back with its transaction, and the
|
|
14
|
+
* leftover blobs are then plain files in that directory.
|
|
15
|
+
*/
|
|
16
|
+
export function local(config) {
|
|
17
|
+
const root = resolve(config.dir);
|
|
18
|
+
function pathOf(key) {
|
|
19
|
+
const full = resolve(root, ...key.split('/'));
|
|
20
|
+
if (full !== root && !full.startsWith(root + sep)) {
|
|
21
|
+
throw new Error(`[pipework] storage local: key "${key}" escapes the storage directory.\n`);
|
|
22
|
+
}
|
|
23
|
+
return full;
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
name: 'local',
|
|
27
|
+
async put(key, body) {
|
|
28
|
+
const target = pathOf(key);
|
|
29
|
+
await mkdir(dirname(target), { recursive: true });
|
|
30
|
+
const partial = `${target}.${randomUUID()}.partial`;
|
|
31
|
+
try {
|
|
32
|
+
await pipeline(Readable.from(body, { objectMode: false }), createWriteStream(partial));
|
|
33
|
+
await rename(partial, target);
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
await rm(partial, { force: true });
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
async exists(key) {
|
|
41
|
+
try {
|
|
42
|
+
return (await stat(pathOf(key))).isFile();
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
if (err.code === 'ENOENT')
|
|
46
|
+
return false;
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
async copy(from, to) {
|
|
51
|
+
const target = pathOf(to);
|
|
52
|
+
await mkdir(dirname(target), { recursive: true });
|
|
53
|
+
await copyFile(pathOf(from), target);
|
|
54
|
+
},
|
|
55
|
+
async remove(key) {
|
|
56
|
+
await rm(pathOf(key), { force: true });
|
|
57
|
+
},
|
|
58
|
+
async read(key) {
|
|
59
|
+
const path = pathOf(key);
|
|
60
|
+
await stat(path);
|
|
61
|
+
return createReadStream(path);
|
|
62
|
+
},
|
|
63
|
+
async *list(prefix) {
|
|
64
|
+
const walk = async function* (dir) {
|
|
65
|
+
let entries;
|
|
66
|
+
try {
|
|
67
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
if (err.code === 'ENOENT')
|
|
71
|
+
return;
|
|
72
|
+
throw err;
|
|
73
|
+
}
|
|
74
|
+
for (const entry of entries.sort((a, b) => (a.name < b.name ? -1 : 1))) {
|
|
75
|
+
const full = join(dir, entry.name);
|
|
76
|
+
if (entry.isDirectory())
|
|
77
|
+
yield* walk(full);
|
|
78
|
+
else if (entry.isFile() && !entry.name.endsWith('.partial'))
|
|
79
|
+
yield full;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
for await (const file of walk(root)) {
|
|
83
|
+
const key = file.slice(root.length + 1).split(sep).join('/');
|
|
84
|
+
if (!key.startsWith(prefix))
|
|
85
|
+
continue;
|
|
86
|
+
yield { key, lastModified: (await stat(file)).mtime };
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
signedUrl() {
|
|
90
|
+
return Promise.reject(new Error('[pipework] The local storage backend has no signed URLs.\n\n' +
|
|
91
|
+
' Stream the file with storage.open(id) and send it with http.file(), or use storage.s3(...).\n'));
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=local.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local.js","sourceRoot":"","sources":["../../../src/infra/storage/local.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createStorage } from './handle.js';
|
|
2
|
+
import { s3 } from './s3.js';
|
|
3
|
+
import { local } from './local.js';
|
|
4
|
+
/** Storage namespace — tenant-scoped, content-addressed file storage with signed URLs and retention. */
|
|
5
|
+
export declare const storage: {
|
|
6
|
+
/** Creates a storage handle over a backend. */
|
|
7
|
+
readonly create: typeof createStorage;
|
|
8
|
+
/** S3-compatible backend (AWS S3, MinIO, R2): SigV4 over `fetch`, multipart streaming upload, optional server-side encryption. */
|
|
9
|
+
readonly s3: typeof s3;
|
|
10
|
+
/** Local-disk backend for development and tests; refused in production. */
|
|
11
|
+
readonly local: typeof local;
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=namespace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"namespace.d.ts","sourceRoot":"","sources":["../../../src/infra/storage/namespace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAElC,wGAAwG;AACxG,eAAO,MAAM,OAAO;IAClB,+CAA+C;;IAE/C,kIAAkI;;IAElI,2EAA2E;;CAEnE,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createStorage } from './handle.js';
|
|
2
|
+
import { s3 } from './s3.js';
|
|
3
|
+
import { local } from './local.js';
|
|
4
|
+
/** Storage namespace — tenant-scoped, content-addressed file storage with signed URLs and retention. */
|
|
5
|
+
export const storage = {
|
|
6
|
+
/** Creates a storage handle over a backend. */
|
|
7
|
+
create: createStorage,
|
|
8
|
+
/** S3-compatible backend (AWS S3, MinIO, R2): SigV4 over `fetch`, multipart streaming upload, optional server-side encryption. */
|
|
9
|
+
s3,
|
|
10
|
+
/** Local-disk backend for development and tests; refused in production. */
|
|
11
|
+
local,
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=namespace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"namespace.js","sourceRoot":"","sources":["../../../src/infra/storage/namespace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAElC,wGAAwG;AACxG,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,+CAA+C;IAC/C,MAAM,EAAE,aAAa;IACrB,kIAAkI;IAClI,EAAE;IACF,2EAA2E;IAC3E,KAAK;CACG,CAAA"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type AwsCredentials } from '../aws/sigv4.js';
|
|
2
|
+
import type { StorageBackend } from './backend.js';
|
|
3
|
+
export type S3Encryption = {
|
|
4
|
+
readonly type: 'AES256';
|
|
5
|
+
} | {
|
|
6
|
+
readonly type: 'aws:kms';
|
|
7
|
+
readonly keyId?: string;
|
|
8
|
+
};
|
|
9
|
+
export interface S3Config {
|
|
10
|
+
readonly bucket: string;
|
|
11
|
+
readonly region: string;
|
|
12
|
+
/** Base URL of an S3-compatible server, e.g. `http://127.0.0.1:9000`. Default `https://s3.<region>.amazonaws.com`. */
|
|
13
|
+
readonly endpoint?: string;
|
|
14
|
+
/** Address the bucket in the path (`<endpoint>/<bucket>/<key>`) rather than the host. Default: true when `endpoint` is given, false otherwise. */
|
|
15
|
+
readonly forcePathStyle?: boolean;
|
|
16
|
+
/** Called for every request, so short-lived credentials can be refreshed by the app. */
|
|
17
|
+
readonly credentials: () => Promise<AwsCredentials>;
|
|
18
|
+
/** Prepended to every key, e.g. `verity/`. */
|
|
19
|
+
readonly prefix?: string;
|
|
20
|
+
/** Server-side encryption, sent with every write (PutObject, CreateMultipartUpload, CopyObject). */
|
|
21
|
+
readonly encryption?: S3Encryption;
|
|
22
|
+
}
|
|
23
|
+
/** Size of each multipart upload part; a body that fits in one is sent with a single PutObject. */
|
|
24
|
+
export declare const PART_SIZE: number;
|
|
25
|
+
/** The headers that ask S3 to encrypt a written object. */
|
|
26
|
+
export declare function encryptionHeaders(encryption: S3Encryption | undefined): Record<string, string>;
|
|
27
|
+
/**
|
|
28
|
+
* S3-compatible storage (AWS S3, MinIO, R2, ...) over `fetch` with SigV4; no AWS SDK.
|
|
29
|
+
* Bodies are uploaded in 8 MiB parts (multipart) unless the whole body fits in one,
|
|
30
|
+
* and the signature does not cover the payload (`UNSIGNED-PAYLOAD`), so use `https`
|
|
31
|
+
* endpoints in production. A failed upload aborts its multipart upload.
|
|
32
|
+
*/
|
|
33
|
+
export declare function s3(config: S3Config): StorageBackend;
|
|
34
|
+
//# sourceMappingURL=s3.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../../../src/infra/storage/s3.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAExE,OAAO,KAAK,EAA0C,cAAc,EAAE,MAAM,cAAc,CAAA;AAE1F,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GAC3B;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAEzD,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,sHAAsH;IACtH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,kJAAkJ;IAClJ,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAA;IACjC,wFAAwF;IACxF,QAAQ,CAAC,WAAW,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,CAAA;IACnD,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,oGAAoG;IACpG,QAAQ,CAAC,UAAU,CAAC,EAAE,YAAY,CAAA;CACnC;AAED,mGAAmG;AACnG,eAAO,MAAM,SAAS,QAAkB,CAAA;AAGxC,2DAA2D;AAC3D,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,YAAY,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAO9F;AAuBD;;;;;GAKG;AACH,wBAAgB,EAAE,CAAC,MAAM,EAAE,QAAQ,GAAG,cAAc,CAwLnD"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { Readable } from 'node:stream';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { signV4, presignV4 } from '../aws/sigv4.js';
|
|
4
|
+
import { contentDisposition } from '../../request/http/file.js';
|
|
5
|
+
/** Size of each multipart upload part; a body that fits in one is sent with a single PutObject. */
|
|
6
|
+
export const PART_SIZE = 8 * 1024 * 1024;
|
|
7
|
+
const EMPTY_SHA256 = createHash('sha256').update('').digest('hex');
|
|
8
|
+
/** The headers that ask S3 to encrypt a written object. */
|
|
9
|
+
export function encryptionHeaders(encryption) {
|
|
10
|
+
if (encryption === undefined)
|
|
11
|
+
return {};
|
|
12
|
+
if (encryption.type === 'AES256')
|
|
13
|
+
return { 'x-amz-server-side-encryption': 'AES256' };
|
|
14
|
+
return {
|
|
15
|
+
'x-amz-server-side-encryption': 'aws:kms',
|
|
16
|
+
...(encryption.keyId !== undefined ? { 'x-amz-server-side-encryption-aws-kms-key-id': encryption.keyId } : {}),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function encodePath(key) {
|
|
20
|
+
return key.split('/').map(seg => encodeURIComponent(seg).replace(/[!'()*]/g, c => `%${c.charCodeAt(0).toString(16).toUpperCase()}`)).join('/');
|
|
21
|
+
}
|
|
22
|
+
function unescapeXml(s) {
|
|
23
|
+
return s.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, '&');
|
|
24
|
+
}
|
|
25
|
+
function xmlTag(xml, tag) {
|
|
26
|
+
const m = new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`).exec(xml);
|
|
27
|
+
return m === null ? null : unescapeXml(m[1]);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* S3-compatible storage (AWS S3, MinIO, R2, ...) over `fetch` with SigV4; no AWS SDK.
|
|
31
|
+
* Bodies are uploaded in 8 MiB parts (multipart) unless the whole body fits in one,
|
|
32
|
+
* and the signature does not cover the payload (`UNSIGNED-PAYLOAD`), so use `https`
|
|
33
|
+
* endpoints in production. A failed upload aborts its multipart upload.
|
|
34
|
+
*/
|
|
35
|
+
export function s3(config) {
|
|
36
|
+
const endpoint = (config.endpoint ?? `https://s3.${config.region}.amazonaws.com`).replace(/\/+$/, '');
|
|
37
|
+
const pathStyle = config.forcePathStyle ?? config.endpoint !== undefined;
|
|
38
|
+
const prefix = config.prefix ?? '';
|
|
39
|
+
const write = encryptionHeaders(config.encryption);
|
|
40
|
+
function urlFor(key, query = {}) {
|
|
41
|
+
const base = new URL(endpoint);
|
|
42
|
+
const objectPath = key === undefined ? '' : `/${encodePath(prefix + key)}`;
|
|
43
|
+
let origin = base.origin;
|
|
44
|
+
let path;
|
|
45
|
+
if (pathStyle)
|
|
46
|
+
path = `${base.pathname.replace(/\/+$/, '')}/${encodeURIComponent(config.bucket)}${objectPath}`;
|
|
47
|
+
else {
|
|
48
|
+
origin = `${base.protocol}//${config.bucket}.${base.host}`;
|
|
49
|
+
path = `${base.pathname.replace(/\/+$/, '')}${objectPath === '' ? '/' : objectPath}`;
|
|
50
|
+
}
|
|
51
|
+
const qs = Object.entries(query).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join('&');
|
|
52
|
+
return `${origin}${path}${qs === '' ? '' : `?${qs}`}`;
|
|
53
|
+
}
|
|
54
|
+
async function call(c) {
|
|
55
|
+
const credentials = await config.credentials();
|
|
56
|
+
const body = c.body ?? Buffer.alloc(0);
|
|
57
|
+
const payloadHash = c.body === undefined ? EMPTY_SHA256 : 'UNSIGNED-PAYLOAD';
|
|
58
|
+
const url = urlFor(c.key, c.query);
|
|
59
|
+
const headers = signV4({
|
|
60
|
+
method: c.method, url, region: config.region, service: 's3', credentials, body, payloadHash,
|
|
61
|
+
headers: { ...c.headers, 'x-amz-content-sha256': payloadHash },
|
|
62
|
+
});
|
|
63
|
+
return fetch(url, {
|
|
64
|
+
method: c.method, headers,
|
|
65
|
+
...(c.body !== undefined ? { body: new Uint8Array(c.body.buffer, c.body.byteOffset, c.body.byteLength) } : {}),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async function ok(op, res, allowed = []) {
|
|
69
|
+
const text = await res.text();
|
|
70
|
+
if ((res.status >= 200 && res.status < 300 && !(text.startsWith('<?xml') && /<Error>/.test(text))) || allowed.includes(res.status))
|
|
71
|
+
return text;
|
|
72
|
+
const code = xmlTag(text, 'Code');
|
|
73
|
+
const message = xmlTag(text, 'Message');
|
|
74
|
+
throw new Error(`[pipework] storage s3 ${op} failed: HTTP ${res.status}${code !== null ? ` ${code}` : ''}${message !== null ? `: ${message}` : ''}.\n`);
|
|
75
|
+
}
|
|
76
|
+
async function putWhole(key, body) {
|
|
77
|
+
await ok('PutObject', await call({ method: 'PUT', key, body, headers: { 'content-type': 'application/octet-stream', ...write } }));
|
|
78
|
+
}
|
|
79
|
+
async function putMultipart(key, first, rest, pending) {
|
|
80
|
+
const created = await ok('CreateMultipartUpload', await call({
|
|
81
|
+
method: 'POST', key, query: { uploads: '' }, headers: { 'content-type': 'application/octet-stream', ...write },
|
|
82
|
+
}));
|
|
83
|
+
const uploadId = xmlTag(created, 'UploadId');
|
|
84
|
+
if (uploadId === null)
|
|
85
|
+
throw new Error('[pipework] storage s3 CreateMultipartUpload returned no UploadId.\n');
|
|
86
|
+
const etags = [];
|
|
87
|
+
try {
|
|
88
|
+
const sendPart = async (part) => {
|
|
89
|
+
const number = etags.length + 1;
|
|
90
|
+
const res = await call({ method: 'PUT', key, query: { partNumber: String(number), uploadId }, body: part });
|
|
91
|
+
await ok('UploadPart', res);
|
|
92
|
+
const etag = res.headers.get('etag');
|
|
93
|
+
if (etag === null)
|
|
94
|
+
throw new Error('[pipework] storage s3 UploadPart returned no ETag.\n');
|
|
95
|
+
etags.push(etag);
|
|
96
|
+
};
|
|
97
|
+
await sendPart(first);
|
|
98
|
+
// `pending` holds what the first read left over beyond one part.
|
|
99
|
+
let buffered = pending;
|
|
100
|
+
let size = buffered.reduce((n, b) => n + b.length, 0);
|
|
101
|
+
for (;;) {
|
|
102
|
+
while (size >= PART_SIZE) {
|
|
103
|
+
const all = Buffer.concat(buffered);
|
|
104
|
+
await sendPart(all.subarray(0, PART_SIZE));
|
|
105
|
+
const remainder = all.subarray(PART_SIZE);
|
|
106
|
+
buffered = remainder.length > 0 ? [remainder] : [];
|
|
107
|
+
size = remainder.length;
|
|
108
|
+
}
|
|
109
|
+
const next = await rest.next();
|
|
110
|
+
if (next.done === true)
|
|
111
|
+
break;
|
|
112
|
+
buffered.push(next.value);
|
|
113
|
+
size += next.value.length;
|
|
114
|
+
}
|
|
115
|
+
if (size > 0)
|
|
116
|
+
await sendPart(Buffer.concat(buffered));
|
|
117
|
+
const xml = '<CompleteMultipartUpload>' +
|
|
118
|
+
etags.map((e, i) => `<Part><PartNumber>${i + 1}</PartNumber><ETag>${e}</ETag></Part>`).join('') +
|
|
119
|
+
'</CompleteMultipartUpload>';
|
|
120
|
+
const payload = Buffer.from(xml, 'utf8');
|
|
121
|
+
await ok('CompleteMultipartUpload', await call({ method: 'POST', key, query: { uploadId }, body: payload, headers: { 'content-type': 'application/xml' } }));
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
try {
|
|
125
|
+
await ok('AbortMultipartUpload', await call({ method: 'DELETE', key, query: { uploadId } }), [404]);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// the original failure is the one to report
|
|
129
|
+
}
|
|
130
|
+
throw err;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
name: 's3',
|
|
135
|
+
async put(key, body) {
|
|
136
|
+
const iterator = (async function* () {
|
|
137
|
+
for await (const chunk of body)
|
|
138
|
+
yield Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
139
|
+
})();
|
|
140
|
+
const chunks = [];
|
|
141
|
+
let size = 0;
|
|
142
|
+
try {
|
|
143
|
+
while (size < PART_SIZE) {
|
|
144
|
+
const next = await iterator.next();
|
|
145
|
+
if (next.done === true) {
|
|
146
|
+
await putWhole(key, Buffer.concat(chunks));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
chunks.push(next.value);
|
|
150
|
+
size += next.value.length;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
await iterator.return(undefined).catch(() => undefined);
|
|
155
|
+
throw err;
|
|
156
|
+
}
|
|
157
|
+
const all = Buffer.concat(chunks);
|
|
158
|
+
const rest = all.length > PART_SIZE ? [all.subarray(PART_SIZE)] : [];
|
|
159
|
+
try {
|
|
160
|
+
await putMultipart(key, all.subarray(0, PART_SIZE), iterator, rest);
|
|
161
|
+
}
|
|
162
|
+
finally {
|
|
163
|
+
await iterator.return(undefined).catch(() => undefined);
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
async exists(key) {
|
|
167
|
+
const res = await call({ method: 'HEAD', key });
|
|
168
|
+
await res.arrayBuffer();
|
|
169
|
+
if (res.status === 404)
|
|
170
|
+
return false;
|
|
171
|
+
if (res.status >= 200 && res.status < 300)
|
|
172
|
+
return true;
|
|
173
|
+
throw new Error(`[pipework] storage s3 HeadObject failed: HTTP ${res.status}.\n`);
|
|
174
|
+
},
|
|
175
|
+
async copy(from, to) {
|
|
176
|
+
const source = `/${encodePath(config.bucket)}/${encodePath(prefix + from)}`;
|
|
177
|
+
await ok('CopyObject', await call({ method: 'PUT', key: to, headers: { 'x-amz-copy-source': source, ...write } }));
|
|
178
|
+
},
|
|
179
|
+
async remove(key) {
|
|
180
|
+
await ok('DeleteObject', await call({ method: 'DELETE', key }), [404]);
|
|
181
|
+
},
|
|
182
|
+
async read(key) {
|
|
183
|
+
const res = await call({ method: 'GET', key });
|
|
184
|
+
if (res.status < 200 || res.status >= 300 || res.body === null) {
|
|
185
|
+
await ok('GetObject', res);
|
|
186
|
+
throw new Error('[pipework] storage s3 GetObject returned no body.\n');
|
|
187
|
+
}
|
|
188
|
+
return Readable.fromWeb(res.body);
|
|
189
|
+
},
|
|
190
|
+
async *list(listPrefix) {
|
|
191
|
+
let token;
|
|
192
|
+
do {
|
|
193
|
+
const query = { 'list-type': '2', prefix: prefix + listPrefix };
|
|
194
|
+
if (token !== undefined)
|
|
195
|
+
query['continuation-token'] = token;
|
|
196
|
+
const xml = await ok('ListObjectsV2', await call({ method: 'GET', query }));
|
|
197
|
+
for (const m of xml.matchAll(/<Contents>([\s\S]*?)<\/Contents>/g)) {
|
|
198
|
+
const key = xmlTag(m[1], 'Key');
|
|
199
|
+
const modified = xmlTag(m[1], 'LastModified');
|
|
200
|
+
if (key === null || modified === null || !key.startsWith(prefix))
|
|
201
|
+
continue;
|
|
202
|
+
yield { key: key.slice(prefix.length), lastModified: new Date(modified) };
|
|
203
|
+
}
|
|
204
|
+
token = xmlTag(xml, 'IsTruncated') === 'true' ? (xmlTag(xml, 'NextContinuationToken') ?? undefined) : undefined;
|
|
205
|
+
} while (token !== undefined);
|
|
206
|
+
},
|
|
207
|
+
async signedUrl(key, options) {
|
|
208
|
+
const credentials = await config.credentials();
|
|
209
|
+
return presignV4({
|
|
210
|
+
method: 'GET',
|
|
211
|
+
url: urlFor(key, {
|
|
212
|
+
'response-content-disposition': contentDisposition(options.disposition, options.filename),
|
|
213
|
+
'response-content-type': options.mediaType,
|
|
214
|
+
}),
|
|
215
|
+
region: config.region, service: 's3', credentials, expiresInSeconds: options.expiresInSeconds,
|
|
216
|
+
});
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=s3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3.js","sourceRoot":"","sources":["../../../src/infra/storage/s3.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAuB,MAAM,iBAAiB,CAAA;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAsB/D,mGAAmG;AACnG,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAA;AACxC,MAAM,YAAY,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAElE,2DAA2D;AAC3D,MAAM,UAAU,iBAAiB,CAAC,UAAoC;IACpE,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,EAAE,CAAA;IACvC,IAAI,UAAU,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,8BAA8B,EAAE,QAAQ,EAAE,CAAA;IACrF,OAAO;QACL,8BAA8B,EAAE,SAAS;QACzC,GAAG,CAAC,UAAU,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,6CAA6C,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC/G,CAAA;AACH,CAAC;AAED,SAAS,UAAU,CAAC,GAAW;IAC7B,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAChJ,CAAC;AAED,SAAS,WAAW,CAAC,CAAS;IAC5B,OAAO,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAA;AAC7H,CAAC;AAED,SAAS,MAAM,CAAC,GAAW,EAAE,GAAW;IACtC,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,IAAI,GAAG,kBAAkB,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC/D,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,CAAA;AAC/C,CAAC;AAUD;;;;;GAKG;AACH,MAAM,UAAU,EAAE,CAAC,MAAgB;IACjC,MAAM,QAAQ,GAAG,CAAC,MAAM,CAAC,QAAQ,IAAI,cAAc,MAAM,CAAC,MAAM,gBAAgB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IACrG,MAAM,SAAS,GAAG,MAAM,CAAC,cAAc,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAA;IACxE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAA;IAClC,MAAM,KAAK,GAAG,iBAAiB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;IAElD,SAAS,MAAM,CAAC,GAAuB,EAAE,QAAgC,EAAE;QACzE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAA;QAC9B,MAAM,UAAU,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE,CAAA;QAC1E,IAAI,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;QACxB,IAAI,IAAY,CAAA;QAChB,IAAI,SAAS;YAAE,IAAI,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,UAAU,EAAE,CAAA;aACzG,CAAC;YACJ,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,KAAK,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAA;YAC1D,IAAI,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,UAAU,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,CAAA;QACtF,CAAC;QACD,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAC/G,OAAO,GAAG,MAAM,GAAG,IAAI,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAA;IACvD,CAAC;IAED,KAAK,UAAU,IAAI,CAAC,CAAO;QACzB,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,WAAW,EAAE,CAAA;QAC9C,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QACtC,MAAM,WAAW,GAAG,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,kBAAkB,CAAA;QAC5E,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,CAAA;QAClC,MAAM,OAAO,GAAG,MAAM,CAAC;YACrB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW;YAC3F,OAAO,EAAE,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,sBAAsB,EAAE,WAAW,EAAE;SAC/D,CAAC,CAAA;QACF,OAAO,KAAK,CAAC,GAAG,EAAE;YAChB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,OAAO;YACzB,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC/G,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,UAAU,EAAE,CAAC,EAAU,EAAE,GAAa,EAAE,UAA6B,EAAE;QAC1E,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;QAC7B,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAA;QAC/I,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QACjC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;QACvC,MAAM,IAAI,KAAK,CACb,yBAAyB,EAAE,iBAAiB,GAAG,CAAC,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CACvI,CAAA;IACH,CAAC;IAED,KAAK,UAAU,QAAQ,CAAC,GAAW,EAAE,IAAY;QAC/C,MAAM,EAAE,CAAC,WAAW,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,GAAG,KAAK,EAAE,EAAE,CAAC,CAAC,CAAA;IACpI,CAAC;IAED,KAAK,UAAU,YAAY,CAAC,GAAW,EAAE,KAAa,EAAE,IAA2B,EAAE,OAAiB;QACpG,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,uBAAuB,EAAE,MAAM,IAAI,CAAC;YAC3D,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,GAAG,KAAK,EAAE;SAC/G,CAAC,CAAC,CAAA;QACH,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;QAC5C,IAAI,QAAQ,KAAK,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAA;QAC7G,MAAM,KAAK,GAAa,EAAE,CAAA;QAC1B,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,KAAK,EAAE,IAAY,EAAiB,EAAE;gBACrD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAA;gBAC/B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;gBAC3G,MAAM,EAAE,CAAC,YAAY,EAAE,GAAG,CAAC,CAAA;gBAC3B,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;gBACpC,IAAI,IAAI,KAAK,IAAI;oBAAE,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAA;gBAC1F,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAClB,CAAC,CAAA;YACD,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAA;YACrB,iEAAiE;YACjE,IAAI,QAAQ,GAAG,OAAO,CAAA;YACtB,IAAI,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;YACrD,SAAS,CAAC;gBACR,OAAO,IAAI,IAAI,SAAS,EAAE,CAAC;oBACzB,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;oBACnC,MAAM,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAA;oBAC1C,MAAM,SAAS,GAAG,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;oBACzC,QAAQ,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;oBAClD,IAAI,GAAG,SAAS,CAAC,MAAM,CAAA;gBACzB,CAAC;gBACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAA;gBAC9B,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI;oBAAE,MAAK;gBAC7B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;gBACzB,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAA;YAC3B,CAAC;YACD,IAAI,IAAI,GAAG,CAAC;gBAAE,MAAM,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAA;YACrD,MAAM,GAAG,GAAG,2BAA2B;gBACrC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,qBAAqB,CAAC,GAAG,CAAC,sBAAsB,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC/F,4BAA4B,CAAA;YAC9B,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;YACxC,MAAM,EAAE,CAAC,yBAAyB,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC,CAAA;QAC9J,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC,sBAAsB,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;YACrG,CAAC;YAAC,MAAM,CAAC;gBACP,4CAA4C;YAC9C,CAAC;YACD,MAAM,GAAG,CAAA;QACX,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI,EAAE,IAAI;QAEV,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI;YACjB,MAAM,QAAQ,GAAG,CAAC,KAAK,SAAS,CAAC;gBAC/B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,IAAI;oBAAE,MAAM,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YAC3F,CAAC,CAAC,EAAE,CAAA;YACJ,MAAM,MAAM,GAAa,EAAE,CAAA;YAC3B,IAAI,IAAI,GAAG,CAAC,CAAA;YACZ,IAAI,CAAC;gBACH,OAAO,IAAI,GAAG,SAAS,EAAE,CAAC;oBACxB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;oBAClC,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;wBACvB,MAAM,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;wBAC1C,OAAM;oBACR,CAAC;oBACD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;oBACvB,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAA;gBAC3B,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;gBACvD,MAAM,GAAG,CAAA;YACX,CAAC;YACD,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YACjC,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;YACpE,IAAI,CAAC;gBACH,MAAM,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAA;YACrE,CAAC;oBAAS,CAAC;gBACT,MAAM,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;YACzD,CAAC;QACH,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,GAAG;YACd,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;YAC/C,MAAM,GAAG,CAAC,WAAW,EAAE,CAAA;YACvB,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;gBAAE,OAAO,KAAK,CAAA;YACpC,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG;gBAAE,OAAO,IAAI,CAAA;YACtD,MAAM,IAAI,KAAK,CAAC,iDAAiD,GAAG,CAAC,MAAM,KAAK,CAAC,CAAA;QACnF,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE;YACjB,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAA;YAC3E,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,mBAAmB,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,EAAE,CAAC,CAAC,CAAA;QACpH,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,GAAG;YACd,MAAM,EAAE,CAAC,cAAc,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;QACxE,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,GAAG;YACZ,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAA;YAC9C,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;gBAC/D,MAAM,EAAE,CAAC,WAAW,EAAE,GAAG,CAAC,CAAA;gBAC1B,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAA;YACxE,CAAC;YACD,OAAO,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAa,CAAC,CAAA;QAC5C,CAAC;QAED,KAAK,CAAC,CAAC,IAAI,CAAC,UAAU;YACpB,IAAI,KAAyB,CAAA;YAC7B,GAAG,CAAC;gBACF,MAAM,KAAK,GAA2B,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,EAAE,CAAA;gBACvF,IAAI,KAAK,KAAK,SAAS;oBAAE,KAAK,CAAC,oBAAoB,CAAC,GAAG,KAAK,CAAA;gBAC5D,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,eAAe,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;gBAC3E,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,mCAAmC,CAAC,EAAE,CAAC;oBAClE,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAE,EAAE,KAAK,CAAC,CAAA;oBAChC,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAE,EAAE,cAAc,CAAC,CAAA;oBAC9C,IAAI,GAAG,KAAK,IAAI,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC;wBAAE,SAAQ;oBAC1E,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAA;gBAC3E,CAAC;gBACD,KAAK,GAAG,MAAM,CAAC,GAAG,EAAE,aAAa,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,uBAAuB,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;YACjH,CAAC,QAAQ,KAAK,KAAK,SAAS,EAAC;QAC/B,CAAC;QAED,KAAK,CAAC,SAAS,CAAC,GAAW,EAAE,OAAgC;YAC3D,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,WAAW,EAAE,CAAA;YAC9C,OAAO,SAAS,CAAC;gBACf,MAAM,EAAE,KAAK;gBACb,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE;oBACf,8BAA8B,EAAE,kBAAkB,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,QAAQ,CAAC;oBACzF,uBAAuB,EAAE,OAAO,CAAC,SAAS;iBAC3C,CAAC;gBACF,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;aAC9F,CAAC,CAAA;QACJ,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export declare const storageFile: import("../../data/domain/types.js").DefinedTable<{
|
|
2
|
+
id: import("../../data/domain/types.js").FieldDescriptor<string, {
|
|
3
|
+
defaultRandom: true;
|
|
4
|
+
hasDefault: true;
|
|
5
|
+
} & {
|
|
6
|
+
primaryKey: true;
|
|
7
|
+
}> & import("../../data/domain/field.js").FieldModifiers<string, {
|
|
8
|
+
defaultRandom: true;
|
|
9
|
+
hasDefault: true;
|
|
10
|
+
} & {
|
|
11
|
+
primaryKey: true;
|
|
12
|
+
}>;
|
|
13
|
+
tenantId: import("../../data/domain/types.js").FieldDescriptor<string, {}> & import("../../data/domain/field.js").FieldModifiers<string, {}>;
|
|
14
|
+
sha256: import("../../data/domain/types.js").FieldDescriptor<string, {}> & import("../../data/domain/field.js").FieldModifiers<string, {}>;
|
|
15
|
+
size: import("../../data/domain/types.js").FieldDescriptor<bigint, {}> & import("../../data/domain/field.js").FieldModifiers<bigint, {}>;
|
|
16
|
+
mediaType: import("../../data/domain/types.js").FieldDescriptor<string, {}> & import("../../data/domain/field.js").FieldModifiers<string, {}>;
|
|
17
|
+
filename: import("../../data/domain/types.js").FieldDescriptor<string | null, {
|
|
18
|
+
nullable: true;
|
|
19
|
+
}> & import("../../data/domain/field.js").FieldModifiers<string | null, {
|
|
20
|
+
nullable: true;
|
|
21
|
+
}>;
|
|
22
|
+
createdAt: import("../../data/domain/types.js").FieldDescriptor<Date, {
|
|
23
|
+
hasDefault: true;
|
|
24
|
+
}> & import("../../data/domain/field.js").FieldModifiers<Date, {
|
|
25
|
+
hasDefault: true;
|
|
26
|
+
}>;
|
|
27
|
+
createdBy: import("../../data/domain/types.js").FieldDescriptor<string | null, {
|
|
28
|
+
nullable: true;
|
|
29
|
+
}> & import("../../data/domain/field.js").FieldModifiers<string | null, {
|
|
30
|
+
nullable: true;
|
|
31
|
+
}>;
|
|
32
|
+
expiresAt: import("../../data/domain/types.js").FieldDescriptor<Date | null, {
|
|
33
|
+
nullable: true;
|
|
34
|
+
}> & import("../../data/domain/field.js").FieldModifiers<Date | null, {
|
|
35
|
+
nullable: true;
|
|
36
|
+
}>;
|
|
37
|
+
metadata: import("../../data/domain/types.js").FieldDescriptor<import("zod/v4").JSONType, {
|
|
38
|
+
nullable: true;
|
|
39
|
+
}> & import("../../data/domain/field.js").FieldModifiers<import("zod/v4").JSONType, {
|
|
40
|
+
nullable: true;
|
|
41
|
+
}>;
|
|
42
|
+
}, "storage_file">;
|
|
43
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/infra/storage/schema.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAetB,CAAA"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// The table behind file storage: one metadata row per stored file, tenant-scoped
|
|
2
|
+
// (forced row-level security in generated migrations). The bytes live in the backend.
|
|
3
|
+
import { define } from '../../data/domain/index.js';
|
|
4
|
+
import { field } from '../../data/domain/field.js';
|
|
5
|
+
import { schema } from '../../data/schema/namespace.js';
|
|
6
|
+
import { index } from '../../data/db/idx/index.js';
|
|
7
|
+
export const storageFile = define('storage_file', {
|
|
8
|
+
id: field.uuid().defaultRandom().primaryKey(),
|
|
9
|
+
tenantId: field.uuid().tenant(),
|
|
10
|
+
sha256: field.text(),
|
|
11
|
+
size: field.bigint(),
|
|
12
|
+
mediaType: field.text(),
|
|
13
|
+
filename: field.text().nullable(),
|
|
14
|
+
createdAt: field.timestamp().default('now'),
|
|
15
|
+
createdBy: field.text().nullable(),
|
|
16
|
+
expiresAt: field.timestamp().nullable(),
|
|
17
|
+
metadata: field.jsonb(schema.check.json()).nullable(),
|
|
18
|
+
}, {
|
|
19
|
+
indexes: (t) => ({
|
|
20
|
+
bySha: index('idx_storage_file_sha').on(t['tenantId'], t['sha256']),
|
|
21
|
+
}),
|
|
22
|
+
});
|
|
23
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/infra/storage/schema.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,sFAAsF;AAEtF,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAA;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAA;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,gCAAgC,CAAA;AACvD,OAAO,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAA;AAElD,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,cAAc,EAAE;IAChD,EAAE,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,aAAa,EAAE,CAAC,UAAU,EAAE;IAC7C,QAAQ,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE;IAC/B,MAAM,EAAE,KAAK,CAAC,IAAI,EAAE;IACpB,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE;IACpB,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE;IACvB,QAAQ,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;IACjC,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAC3C,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;IAClC,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,QAAQ,EAAE;IACvC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,EAAE;CACtD,EAAE;IACD,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACf,KAAK,EAAE,KAAK,CAAC,sBAAsB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;KACpE,CAAC;CACH,CAAC,CAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Schema, Infer } from '../../data/schema/types.js';
|
|
2
2
|
import type { DB } from '../../data/db/index.js';
|
|
3
|
-
import type { Handler, HttpMethod, RouteMeta, AuthRequirement, PermissionMeta, JobRetryConfig, JobTransactionMode, Checkpoint, RouteScreen, UploadConfig, BinaryUploadConfig, MultipartUploadConfig, BinaryUpload, MultipartUpload } from './types.js';
|
|
3
|
+
import type { Handler, HttpMethod, RouteMeta, AuthRequirement, PermissionMeta, JobRetryConfig, JobTransactionMode, Checkpoint, RouteScreen, UploadConfig, BinaryUploadConfig, MultipartUploadConfig, BinaryUpload, MultipartUpload, FileConfig } from './types.js';
|
|
4
4
|
import type { ResourceOperations, Resource, PaginationConfig } from '../fixture/types.js';
|
|
5
5
|
import type { HttpRequest, HttpResponse } from '../http/types.js';
|
|
6
6
|
interface BuilderState {
|
|
@@ -23,6 +23,7 @@ interface BuilderState {
|
|
|
23
23
|
wantsResponse: boolean;
|
|
24
24
|
wantsRawBody: boolean;
|
|
25
25
|
upload: UploadConfig | null;
|
|
26
|
+
file: FileConfig | null;
|
|
26
27
|
}
|
|
27
28
|
/**
|
|
28
29
|
* Deps shape after `.transaction('manual')`: the databases the handler declared
|
|
@@ -159,6 +160,32 @@ export declare class Builder<TDeps extends Record<string, unknown>, TDb extends
|
|
|
159
160
|
upload(config: MultipartUploadConfig): Builder<TDeps & {
|
|
160
161
|
upload: MultipartUpload;
|
|
161
162
|
}, TDb>;
|
|
163
|
+
/**
|
|
164
|
+
* Declares that this route answers with a file, returned from the handler as
|
|
165
|
+
* `http.file({ body, mediaType, filename? ... })`. `mediaTypes` lists what the
|
|
166
|
+
* route may send (`type/*` wildcards allowed) and `status` (default 200) is the
|
|
167
|
+
* status of the file response. OpenAPI gets `responses.<status>.content.<type>`
|
|
168
|
+
* as `{ type: 'string', format: 'binary' }` for each media type.
|
|
169
|
+
*
|
|
170
|
+
* A route declared with `.file()` must return `http.file(...)`: anything else,
|
|
171
|
+
* or a file whose `mediaType` is outside `mediaTypes`, is a 500 with the reason
|
|
172
|
+
* in the body. A Buffer is sent whole with Content-Length; a Readable is
|
|
173
|
+
* streamed (add `length` for Content-Length, else it is chunked). Content-
|
|
174
|
+
* Disposition carries an ASCII `filename=` fallback and the exact name in
|
|
175
|
+
* `filename*`, and the response sets `X-Content-Type-Options: nosniff`.
|
|
176
|
+
*
|
|
177
|
+
* The request transaction (auto mode) has committed before the body streams,
|
|
178
|
+
* so the stream must not need it. A stream that fails after the first byte
|
|
179
|
+
* destroys the connection and logs the error. Range requests and redirects are
|
|
180
|
+
* not supported.
|
|
181
|
+
*
|
|
182
|
+
* Cannot be combined with `.output()` for the same status, or with `.job()`;
|
|
183
|
+
* `fit()` throws.
|
|
184
|
+
*/
|
|
185
|
+
file(config: {
|
|
186
|
+
mediaTypes: readonly string[];
|
|
187
|
+
status?: number;
|
|
188
|
+
}): Builder<TDeps, TDb>;
|
|
162
189
|
fixture<TAuth>(name: string, operations: ResourceOperations<TAuth>): Resource<TAuth>;
|
|
163
190
|
fit<TReturn>(fn: (deps: TDeps) => Promise<TReturn>): Handler<TDeps, TReturn>;
|
|
164
191
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../../src/request/di/builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAA;AAC/D,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,wBAAwB,CAAA;AAEhD,OAAO,KAAK,EAAE,OAAO,EAAe,UAAU,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../../src/request/di/builder.ts"],"names":[],"mappings":"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"}
|