@x12i/memorix-service 3.3.0 → 3.4.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/README.md +23 -6
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +4 -0
- package/dist/app.js.map +1 -1
- package/dist/blob-store/config.d.ts +31 -0
- package/dist/blob-store/config.d.ts.map +1 -0
- package/dist/blob-store/config.js +68 -0
- package/dist/blob-store/config.js.map +1 -0
- package/dist/blob-store/disk.d.ts +10 -0
- package/dist/blob-store/disk.d.ts.map +1 -0
- package/dist/blob-store/disk.js +104 -0
- package/dist/blob-store/disk.js.map +1 -0
- package/dist/blob-store/index.d.ts +6 -0
- package/dist/blob-store/index.d.ts.map +1 -0
- package/dist/blob-store/index.js +6 -0
- package/dist/blob-store/index.js.map +1 -0
- package/dist/blob-store/memory.d.ts +4 -0
- package/dist/blob-store/memory.d.ts.map +1 -0
- package/dist/blob-store/memory.js +47 -0
- package/dist/blob-store/memory.js.map +1 -0
- package/dist/blob-store/s3.d.ts +52 -0
- package/dist/blob-store/s3.d.ts.map +1 -0
- package/dist/blob-store/s3.js +151 -0
- package/dist/blob-store/s3.js.map +1 -0
- package/dist/blob-store/types.d.ts +30 -0
- package/dist/blob-store/types.d.ts.map +1 -0
- package/dist/blob-store/types.js +38 -0
- package/dist/blob-store/types.js.map +1 -0
- package/dist/catalog-store-cli.d.ts.map +1 -1
- package/dist/catalog-store-cli.js +35 -4
- package/dist/catalog-store-cli.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/integrations/index.d.ts +3 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +3 -0
- package/dist/integrations/index.js.map +1 -0
- package/dist/integrations/instance-store.d.ts +5 -0
- package/dist/integrations/instance-store.d.ts.map +1 -0
- package/dist/integrations/instance-store.js +327 -0
- package/dist/integrations/instance-store.js.map +1 -0
- package/dist/integrations/types.d.ts +92 -0
- package/dist/integrations/types.d.ts.map +1 -0
- package/dist/integrations/types.js +40 -0
- package/dist/integrations/types.js.map +1 -0
- package/dist/magit/magit-pack-service.d.ts +187 -3
- package/dist/magit/magit-pack-service.d.ts.map +1 -1
- package/dist/magit/magit-pack-service.js +401 -8
- package/dist/magit/magit-pack-service.js.map +1 -1
- package/dist/openapi/openapi.json +1348 -2
- package/dist/platform.d.ts +18 -0
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +61 -0
- package/dist/platform.js.map +1 -1
- package/dist/restricted-store/crypto.d.ts +10 -0
- package/dist/restricted-store/crypto.d.ts.map +1 -0
- package/dist/restricted-store/crypto.js +43 -0
- package/dist/restricted-store/crypto.js.map +1 -0
- package/dist/restricted-store/index.d.ts +4 -0
- package/dist/restricted-store/index.d.ts.map +1 -0
- package/dist/restricted-store/index.js +4 -0
- package/dist/restricted-store/index.js.map +1 -0
- package/dist/restricted-store/object-store.d.ts +12 -0
- package/dist/restricted-store/object-store.d.ts.map +1 -0
- package/dist/restricted-store/object-store.js +233 -0
- package/dist/restricted-store/object-store.js.map +1 -0
- package/dist/restricted-store/types.d.ts +78 -0
- package/dist/restricted-store/types.d.ts.map +1 -0
- package/dist/restricted-store/types.js +12 -0
- package/dist/restricted-store/types.js.map +1 -0
- package/dist/routes/integrations.d.ts +5 -0
- package/dist/routes/integrations.d.ts.map +1 -0
- package/dist/routes/integrations.js +126 -0
- package/dist/routes/integrations.js.map +1 -0
- package/dist/routes/metadata.d.ts.map +1 -1
- package/dist/routes/metadata.js +328 -4
- package/dist/routes/metadata.js.map +1 -1
- package/dist/routes/restricted-store.d.ts +5 -0
- package/dist/routes/restricted-store.d.ts.map +1 -0
- package/dist/routes/restricted-store.js +117 -0
- package/dist/routes/restricted-store.js.map +1 -0
- package/openapi/openapi.json +1348 -2
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Remote apps should **call** this service over HTTP — not reinvent wiring by in
|
|
|
7
7
|
## Install / run
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install @x12i/memorix-service@3.0
|
|
10
|
+
npm install @x12i/memorix-service@3.4.0 # composition library / CLI
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
Prefer the monorepo stack (service + Studio + opx-source):
|
|
@@ -33,6 +33,22 @@ MEMORIX_SERVICE_MODE=dev npm run dev # metadata in-memory; durable routes 503
|
|
|
33
33
|
|
|
34
34
|
Compliance: this process uses **`@x12i/core-service`** (zone port, health, live view, correlation-id).
|
|
35
35
|
|
|
36
|
+
## Dual-mode org blob plane (cloud / on-prem)
|
|
37
|
+
|
|
38
|
+
Not Magit. When storing **org/connector object payloads** that would use cloud buckets,
|
|
39
|
+
on-prem must use **local disk**.
|
|
40
|
+
|
|
41
|
+
| Env | Effect |
|
|
42
|
+
|-----|--------|
|
|
43
|
+
| `MEMORIX_BLOB_BACKEND` | `disk` / `onprem` (default) or `s3` / `cloud` |
|
|
44
|
+
| `MEMORIX_BLOB_ROOT` | Disk root for on-prem (default `.memorix-blobs`) |
|
|
45
|
+
| `MEMORIX_S3_BUCKET` | Required when backend=`s3` |
|
|
46
|
+
| `MEMORIX_RESTRICTED_KEK` | Optional AES-GCM KEK for disk-encrypted restricted bodies |
|
|
47
|
+
|
|
48
|
+
Used by **`/api/restricted-store/v1`** (FG-A3). See
|
|
49
|
+
[restricted-store.md](../docs/connector-framework/restricted-store.md) and
|
|
50
|
+
[integration-registry.md](../docs/connector-framework/integration-registry.md).
|
|
51
|
+
|
|
36
52
|
## Magit package catalog
|
|
37
53
|
|
|
38
54
|
Default-on when durable Mongo is configured (`METADATA_MAGIT=0` disables).
|
|
@@ -42,22 +58,23 @@ Default-on when durable Mongo is configured (`METADATA_MAGIT=0` disables).
|
|
|
42
58
|
| **A typed store** | `MAGIT_PACKAGE_STORE_SELECTOR` + `MAGIT_PACKAGE_TYPE_ID` |
|
|
43
59
|
| **B flat catalog** | both unset |
|
|
44
60
|
|
|
45
|
-
Requires `@x12i/magit@2.
|
|
61
|
+
Requires `@x12i/magit@2.14.0`. Defaults: `MAGIT_CATALOG_TENANT=x12i`, `MAGIT_CATALOG_REPO=memorix`.
|
|
46
62
|
|
|
47
|
-
Ops (provision / verify / recover /
|
|
63
|
+
Ops (provision / verify / recover / channels / smoke): see
|
|
48
64
|
[documentation/magit-typed-store-runbook.md](../documentation/magit-typed-store-runbook.md).
|
|
49
65
|
|
|
50
66
|
```bash
|
|
51
|
-
npm run catalog:provision -- --out /secure/magit-store.env
|
|
67
|
+
npm run catalog:provision -- --out /secure/magit-store.env --confirm-allowed-kinds
|
|
52
68
|
npm run catalog:verify
|
|
53
69
|
npm run catalog:recover -- --store-id st_… --out /secure/magit-store.env
|
|
54
70
|
npm run catalog:list
|
|
55
|
-
# Magit hosted Mode A proof (packaging in 2.12.2):
|
|
56
71
|
npm run magit:cross-env-smoke
|
|
57
72
|
npm run magit:cross-env-smoke -- --publish
|
|
58
73
|
```
|
|
59
74
|
|
|
60
|
-
**
|
|
75
|
+
**Owner mint:** repo `memorix`, Package stores **empty** until the store exists.
|
|
76
|
+
**Consumer:** store grant + `repoIds: []` (Repositories unchecked).
|
|
77
|
+
Channels and release sets need Mode A + publisher token to promote; consumers only resolve.
|
|
61
78
|
|
|
62
79
|
Env examples: [memorix-service/.env.example](./.env.example). Selector never appears in Studio or `GET /api/diagnostics/scope`.
|
|
63
80
|
|
package/dist/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAIA,OAAO,OAAO,EAAE,EAAE,KAAK,eAAe,EAAE,MAAM,SAAS,CAAC;AAIxD,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,YAAY,EAClB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAIA,OAAO,OAAO,EAAE,EAAE,KAAK,eAAe,EAAE,MAAM,SAAS,CAAC;AAIxD,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,YAAY,EAClB,MAAM,eAAe,CAAC;AA0BvB,MAAM,MAAM,8BAA8B,GAAG,YAAY,GAAG;IAC1D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B,CAAC;AAEF,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,8BAA8B,GACnC,OAAO,CAAC;IAAE,GAAG,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,eAAe,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CA2F5E;AAED,MAAM,MAAM,0BAA0B,GAAG,8BAA8B,GAAG;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,0BAA0B;;;;;GAM7E"}
|
package/dist/app.js
CHANGED
|
@@ -15,6 +15,8 @@ import { registerRelationshipRoutes } from "./routes/relationships.js";
|
|
|
15
15
|
import { registerMemoryRoutes } from "./routes/memory.js";
|
|
16
16
|
import { registerConnectorInvocationRoutes } from "./routes/connector-invocations.js";
|
|
17
17
|
import { registerConnectorOpsRoutes } from "./routes/connector-ops.js";
|
|
18
|
+
import { registerIntegrationRoutes } from "./routes/integrations.js";
|
|
19
|
+
import { registerRestrictedStoreRoutes } from "./routes/restricted-store.js";
|
|
18
20
|
import { registerPipelineRoutes } from "./routes/pipelines.js";
|
|
19
21
|
import { registerJobsRoutes } from "./routes/jobs.js";
|
|
20
22
|
import { registerOperationsRoutes } from "./routes/operations.js";
|
|
@@ -96,6 +98,8 @@ export async function createMemorixServiceApp(opts) {
|
|
|
96
98
|
await registerMemoryRoutes(app, platform);
|
|
97
99
|
await registerConnectorInvocationRoutes(app, platform);
|
|
98
100
|
await registerConnectorOpsRoutes(app, platform);
|
|
101
|
+
await registerIntegrationRoutes(app, platform);
|
|
102
|
+
await registerRestrictedStoreRoutes(app, platform);
|
|
99
103
|
await registerPipelineRoutes(app, platform);
|
|
100
104
|
await registerJobsRoutes(app, platform);
|
|
101
105
|
await registerOperationsRoutes(app, platform);
|
package/dist/app.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type S3BlobClient } from "./s3.js";
|
|
2
|
+
import type { BlobBackendKind, BlobObjectStore } from "./types.js";
|
|
3
|
+
export type BlobStoreConfig = {
|
|
4
|
+
backend: BlobBackendKind;
|
|
5
|
+
/** disk root (absolute). */
|
|
6
|
+
rootDir?: string;
|
|
7
|
+
s3Bucket?: string;
|
|
8
|
+
s3Prefix?: string;
|
|
9
|
+
s3Sse?: "AES256" | "aws:kms" | false;
|
|
10
|
+
s3KmsKeyId?: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Resolve blob backend from env.
|
|
14
|
+
* - disk (default / on-prem): local filesystem
|
|
15
|
+
* - s3 (cloud): S3-compatible bucket required
|
|
16
|
+
* - memory: tests / explicit only
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveBlobStoreConfig(env?: NodeJS.ProcessEnv): BlobStoreConfig;
|
|
19
|
+
export type CreatePlatformBlobStoreOptions = {
|
|
20
|
+
env?: NodeJS.ProcessEnv;
|
|
21
|
+
/** Inject S3 client (tests). */
|
|
22
|
+
s3Client?: S3BlobClient;
|
|
23
|
+
/** Force memory backend (unit tests). */
|
|
24
|
+
forceMemory?: boolean;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Create the process-wide BlobObjectStore used by restricted store bodies.
|
|
28
|
+
* Fail-closed: s3 without bucket throws.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createPlatformBlobStore(opts?: CreatePlatformBlobStoreOptions): Promise<BlobObjectStore>;
|
|
31
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/blob-store/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEnE,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,eAAe,CAAC;IACzB,4BAA4B;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,eAAe,CAgCjB;AAED,MAAM,MAAM,8BAA8B,GAAG;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,yCAAyC;IACzC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,GAAE,8BAAmC,GACxC,OAAO,CAAC,eAAe,CAAC,CA2B1B"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { createDiskBlobStore } from "./disk.js";
|
|
3
|
+
import { createMemoryBlobStore } from "./memory.js";
|
|
4
|
+
import { createAwsS3BlobClientFromEnv, createS3BlobStore, } from "./s3.js";
|
|
5
|
+
/**
|
|
6
|
+
* Resolve blob backend from env.
|
|
7
|
+
* - disk (default / on-prem): local filesystem
|
|
8
|
+
* - s3 (cloud): S3-compatible bucket required
|
|
9
|
+
* - memory: tests / explicit only
|
|
10
|
+
*/
|
|
11
|
+
export function resolveBlobStoreConfig(env = process.env) {
|
|
12
|
+
const raw = (env.MEMORIX_BLOB_BACKEND ?? "disk").trim().toLowerCase();
|
|
13
|
+
let backend;
|
|
14
|
+
if (raw === "s3" || raw === "cloud")
|
|
15
|
+
backend = "s3";
|
|
16
|
+
else if (raw === "memory")
|
|
17
|
+
backend = "memory";
|
|
18
|
+
else if (raw === "disk" || raw === "onprem" || raw === "on-prem")
|
|
19
|
+
backend = "disk";
|
|
20
|
+
else {
|
|
21
|
+
throw new Error(`MEMORIX_BLOB_BACKEND must be s3|disk|memory (or cloud|onprem), got "${raw}"`);
|
|
22
|
+
}
|
|
23
|
+
const rootDir = resolve(env.MEMORIX_BLOB_ROOT?.trim() || resolve(process.cwd(), ".memorix-blobs"));
|
|
24
|
+
const s3SseRaw = env.MEMORIX_S3_SSE?.trim();
|
|
25
|
+
let s3Sse = "AES256";
|
|
26
|
+
if (s3SseRaw === "0" || s3SseRaw === "false" || s3SseRaw === "none") {
|
|
27
|
+
s3Sse = false;
|
|
28
|
+
}
|
|
29
|
+
else if (s3SseRaw === "aws:kms" || s3SseRaw === "kms") {
|
|
30
|
+
s3Sse = "aws:kms";
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
backend,
|
|
34
|
+
rootDir,
|
|
35
|
+
s3Bucket: env.MEMORIX_S3_BUCKET?.trim() || undefined,
|
|
36
|
+
s3Prefix: env.MEMORIX_S3_PREFIX?.trim() || "memorix",
|
|
37
|
+
s3Sse,
|
|
38
|
+
s3KmsKeyId: env.MEMORIX_S3_KMS_KEY_ID?.trim() || undefined,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create the process-wide BlobObjectStore used by restricted store bodies.
|
|
43
|
+
* Fail-closed: s3 without bucket throws.
|
|
44
|
+
*/
|
|
45
|
+
export async function createPlatformBlobStore(opts = {}) {
|
|
46
|
+
if (opts.forceMemory)
|
|
47
|
+
return createMemoryBlobStore();
|
|
48
|
+
const env = opts.env ?? process.env;
|
|
49
|
+
const cfg = resolveBlobStoreConfig(env);
|
|
50
|
+
if (cfg.backend === "memory")
|
|
51
|
+
return createMemoryBlobStore();
|
|
52
|
+
if (cfg.backend === "disk") {
|
|
53
|
+
return createDiskBlobStore({ rootDir: cfg.rootDir });
|
|
54
|
+
}
|
|
55
|
+
// s3
|
|
56
|
+
if (!cfg.s3Bucket) {
|
|
57
|
+
throw new Error("MEMORIX_BLOB_BACKEND=s3 requires MEMORIX_S3_BUCKET (cloud object store)");
|
|
58
|
+
}
|
|
59
|
+
const client = opts.s3Client ?? (await createAwsS3BlobClientFromEnv(env));
|
|
60
|
+
return createS3BlobStore({
|
|
61
|
+
bucket: cfg.s3Bucket,
|
|
62
|
+
prefix: cfg.s3Prefix,
|
|
63
|
+
client,
|
|
64
|
+
serverSideEncryption: cfg.s3Sse,
|
|
65
|
+
sseKmsKeyId: cfg.s3KmsKeyId,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/blob-store/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type BlobObjectStore } from "./types.js";
|
|
2
|
+
export type DiskBlobStoreOptions = {
|
|
3
|
+
rootDir: string;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* On-prem object store: `{root}/{orgId}/{key...}`.
|
|
7
|
+
* Atomic write via tmp + rename. Never walks outside root.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createDiskBlobStore(opts: DiskBlobStoreOptions): BlobObjectStore;
|
|
10
|
+
//# sourceMappingURL=disk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disk.d.ts","sourceRoot":"","sources":["../../src/blob-store/disk.ts"],"names":[],"mappings":"AAUA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAcF;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,oBAAoB,GACzB,eAAe,CAgFjB"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdir, readFile, rename, stat, unlink, writeFile, } from "node:fs/promises";
|
|
3
|
+
import { dirname, join, resolve, sep } from "node:path";
|
|
4
|
+
import { assertSafeBlobKey, assertSafeOrgId, } from "./types.js";
|
|
5
|
+
function resolveObjectPath(rootDir, orgId, key) {
|
|
6
|
+
const org = assertSafeOrgId(orgId);
|
|
7
|
+
const safeKey = assertSafeBlobKey(key);
|
|
8
|
+
const root = resolve(rootDir);
|
|
9
|
+
const full = resolve(join(root, org, ...safeKey.split("/")));
|
|
10
|
+
const prefix = root.endsWith(sep) ? root : root + sep;
|
|
11
|
+
if (full !== root && !full.startsWith(prefix)) {
|
|
12
|
+
throw new Error("blob path escape rejected");
|
|
13
|
+
}
|
|
14
|
+
return full;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* On-prem object store: `{root}/{orgId}/{key...}`.
|
|
18
|
+
* Atomic write via tmp + rename. Never walks outside root.
|
|
19
|
+
*/
|
|
20
|
+
export function createDiskBlobStore(opts) {
|
|
21
|
+
const rootDir = resolve(opts.rootDir);
|
|
22
|
+
return {
|
|
23
|
+
backend: "disk",
|
|
24
|
+
async put(orgId, key, body, putOpts) {
|
|
25
|
+
const path = resolveObjectPath(rootDir, orgId, key);
|
|
26
|
+
await mkdir(dirname(path), { recursive: true });
|
|
27
|
+
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
28
|
+
await writeFile(tmp, body);
|
|
29
|
+
await rename(tmp, path);
|
|
30
|
+
const contentType = putOpts?.contentType;
|
|
31
|
+
if (contentType) {
|
|
32
|
+
await writeFile(`${path}.meta.json`, JSON.stringify({ contentType }), "utf8");
|
|
33
|
+
}
|
|
34
|
+
const digest = createHash("sha256").update(body).digest("hex");
|
|
35
|
+
return {
|
|
36
|
+
backend: "disk",
|
|
37
|
+
orgId: assertSafeOrgId(orgId),
|
|
38
|
+
key: assertSafeBlobKey(key),
|
|
39
|
+
sizeBytes: body.byteLength,
|
|
40
|
+
contentType,
|
|
41
|
+
etag: `sha256:${digest}`,
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
async get(orgId, key) {
|
|
45
|
+
const path = resolveObjectPath(rootDir, orgId, key);
|
|
46
|
+
try {
|
|
47
|
+
return await readFile(path);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
if (err.code === "ENOENT")
|
|
51
|
+
return null;
|
|
52
|
+
throw err;
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
async head(orgId, key) {
|
|
56
|
+
const path = resolveObjectPath(rootDir, orgId, key);
|
|
57
|
+
try {
|
|
58
|
+
const st = await stat(path);
|
|
59
|
+
let contentType;
|
|
60
|
+
try {
|
|
61
|
+
const metaRaw = await readFile(`${path}.meta.json`, "utf8");
|
|
62
|
+
const meta = JSON.parse(metaRaw);
|
|
63
|
+
contentType = meta.contentType;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
/* no meta */
|
|
67
|
+
}
|
|
68
|
+
const buf = await readFile(path);
|
|
69
|
+
const digest = createHash("sha256").update(buf).digest("hex");
|
|
70
|
+
return {
|
|
71
|
+
orgId: assertSafeOrgId(orgId),
|
|
72
|
+
key: assertSafeBlobKey(key),
|
|
73
|
+
sizeBytes: st.size,
|
|
74
|
+
contentType,
|
|
75
|
+
etag: `sha256:${digest}`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
if (err.code === "ENOENT")
|
|
80
|
+
return null;
|
|
81
|
+
throw err;
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
async delete(orgId, key) {
|
|
85
|
+
const path = resolveObjectPath(rootDir, orgId, key);
|
|
86
|
+
try {
|
|
87
|
+
await unlink(path);
|
|
88
|
+
try {
|
|
89
|
+
await unlink(`${path}.meta.json`);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
/* ignore meta */
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
if (err.code === "ENOENT")
|
|
98
|
+
return false;
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=disk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disk.js","sourceRoot":"","sources":["../../src/blob-store/disk.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
2
|
+
export { createDiskBlobStore } from "./disk.js";
|
|
3
|
+
export { createMemoryBlobStore } from "./memory.js";
|
|
4
|
+
export { createS3BlobStore, createAwsS3BlobClientFromEnv, type S3BlobClient, type S3BlobStoreOptions, } from "./s3.js";
|
|
5
|
+
export { resolveBlobStoreConfig, createPlatformBlobStore, type BlobStoreConfig, type CreatePlatformBlobStoreOptions, } from "./config.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/blob-store/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EACL,iBAAiB,EACjB,4BAA4B,EAC5B,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,sBAAsB,EACtB,uBAAuB,EACvB,KAAK,eAAe,EACpB,KAAK,8BAA8B,GACpC,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
2
|
+
export { createDiskBlobStore } from "./disk.js";
|
|
3
|
+
export { createMemoryBlobStore } from "./memory.js";
|
|
4
|
+
export { createS3BlobStore, createAwsS3BlobClientFromEnv, } from "./s3.js";
|
|
5
|
+
export { resolveBlobStoreConfig, createPlatformBlobStore, } from "./config.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/blob-store/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EACL,iBAAiB,EACjB,4BAA4B,GAG7B,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,sBAAsB,EACtB,uBAAuB,GAGxB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/blob-store/memory.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,eAAe,EAErB,MAAM,YAAY,CAAC;AAOpB,6DAA6D;AAC7D,wBAAgB,qBAAqB,IAAI,eAAe,CAgDvD"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { assertSafeBlobKey, assertSafeOrgId, } from "./types.js";
|
|
3
|
+
/** In-process blob store for unit tests and no-mongo dev. */
|
|
4
|
+
export function createMemoryBlobStore() {
|
|
5
|
+
const map = new Map();
|
|
6
|
+
const id = (orgId, key) => `${assertSafeOrgId(orgId)}\0${assertSafeBlobKey(key)}`;
|
|
7
|
+
return {
|
|
8
|
+
backend: "memory",
|
|
9
|
+
async put(orgId, key, body, opts) {
|
|
10
|
+
const k = id(orgId, key);
|
|
11
|
+
map.set(k, {
|
|
12
|
+
body: Buffer.from(body),
|
|
13
|
+
contentType: opts?.contentType,
|
|
14
|
+
});
|
|
15
|
+
const digest = createHash("sha256").update(body).digest("hex");
|
|
16
|
+
return {
|
|
17
|
+
backend: "memory",
|
|
18
|
+
orgId: assertSafeOrgId(orgId),
|
|
19
|
+
key: assertSafeBlobKey(key),
|
|
20
|
+
sizeBytes: body.byteLength,
|
|
21
|
+
contentType: opts?.contentType,
|
|
22
|
+
etag: `sha256:${digest}`,
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
async get(orgId, key) {
|
|
26
|
+
const e = map.get(id(orgId, key));
|
|
27
|
+
return e ? Buffer.from(e.body) : null;
|
|
28
|
+
},
|
|
29
|
+
async head(orgId, key) {
|
|
30
|
+
const e = map.get(id(orgId, key));
|
|
31
|
+
if (!e)
|
|
32
|
+
return null;
|
|
33
|
+
const digest = createHash("sha256").update(e.body).digest("hex");
|
|
34
|
+
return {
|
|
35
|
+
orgId: assertSafeOrgId(orgId),
|
|
36
|
+
key: assertSafeBlobKey(key),
|
|
37
|
+
sizeBytes: e.body.byteLength,
|
|
38
|
+
contentType: e.contentType,
|
|
39
|
+
etag: `sha256:${digest}`,
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
async delete(orgId, key) {
|
|
43
|
+
return map.delete(id(orgId, key));
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/blob-store/memory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,iBAAiB,EACjB,eAAe,GAGhB,MAAM,YAAY,CAAC;AAOpB,6DAA6D;AAC7D,MAAM,UAAU,qBAAqB;IACnC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAiB,CAAC;IAErC,MAAM,EAAE,GAAG,CAAC,KAAa,EAAE,GAAW,EAAE,EAAE,CACxC,GAAG,eAAe,CAAC,KAAK,CAAC,KAAK,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC;IAEzD,OAAO;QACL,OAAO,EAAE,QAAQ;QAEjB,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI;YAC9B,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YACzB,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE;gBACT,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;gBACvB,WAAW,EAAE,IAAI,EAAE,WAAW;aAC/B,CAAC,CAAC;YACH,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC/D,OAAO;gBACL,OAAO,EAAE,QAAQ;gBACjB,KAAK,EAAE,eAAe,CAAC,KAAK,CAAC;gBAC7B,GAAG,EAAE,iBAAiB,CAAC,GAAG,CAAC;gBAC3B,SAAS,EAAE,IAAI,CAAC,UAAU;gBAC1B,WAAW,EAAE,IAAI,EAAE,WAAW;gBAC9B,IAAI,EAAE,UAAU,MAAM,EAAE;aACD,CAAC;QAC5B,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG;YAClB,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;YAClC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACxC,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG;YACnB,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;YAClC,IAAI,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAC;YACpB,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACjE,OAAO;gBACL,KAAK,EAAE,eAAe,CAAC,KAAK,CAAC;gBAC7B,GAAG,EAAE,iBAAiB,CAAC,GAAG,CAAC;gBAC3B,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU;gBAC5B,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,IAAI,EAAE,UAAU,MAAM,EAAE;aACzB,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG;YACrB,OAAO,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;QACpC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type BlobObjectStore } from "./types.js";
|
|
2
|
+
/** Minimal S3 client surface so tests can inject mocks without network. */
|
|
3
|
+
export type S3BlobClient = {
|
|
4
|
+
putObject(input: {
|
|
5
|
+
Bucket: string;
|
|
6
|
+
Key: string;
|
|
7
|
+
Body: Buffer;
|
|
8
|
+
ContentType?: string;
|
|
9
|
+
ServerSideEncryption?: string;
|
|
10
|
+
SSEKMSKeyId?: string;
|
|
11
|
+
}): Promise<{
|
|
12
|
+
ETag?: string;
|
|
13
|
+
}>;
|
|
14
|
+
getObject(input: {
|
|
15
|
+
Bucket: string;
|
|
16
|
+
Key: string;
|
|
17
|
+
}): Promise<{
|
|
18
|
+
Body?: Uint8Array | Buffer | null;
|
|
19
|
+
ContentType?: string;
|
|
20
|
+
} | null>;
|
|
21
|
+
headObject(input: {
|
|
22
|
+
Bucket: string;
|
|
23
|
+
Key: string;
|
|
24
|
+
}): Promise<{
|
|
25
|
+
ContentLength?: number;
|
|
26
|
+
ContentType?: string;
|
|
27
|
+
ETag?: string;
|
|
28
|
+
} | null>;
|
|
29
|
+
deleteObject(input: {
|
|
30
|
+
Bucket: string;
|
|
31
|
+
Key: string;
|
|
32
|
+
}): Promise<void>;
|
|
33
|
+
};
|
|
34
|
+
export type S3BlobStoreOptions = {
|
|
35
|
+
bucket: string;
|
|
36
|
+
/** Optional key prefix (no leading/trailing slash required). */
|
|
37
|
+
prefix?: string;
|
|
38
|
+
client: S3BlobClient;
|
|
39
|
+
/** Default AES256 (SSE-S3). Set false to disable. */
|
|
40
|
+
serverSideEncryption?: "AES256" | "aws:kms" | false;
|
|
41
|
+
sseKmsKeyId?: string;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Cloud object store (S3-compatible). Bodies never touch local disk.
|
|
45
|
+
* Pass a real AWS SDK client or a test double via `client`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function createS3BlobStore(opts: S3BlobStoreOptions): BlobObjectStore;
|
|
48
|
+
/**
|
|
49
|
+
* Build SDK client from env when backend=s3. Lazy-imports @aws-sdk/client-s3.
|
|
50
|
+
*/
|
|
51
|
+
export declare function createAwsS3BlobClientFromEnv(env?: NodeJS.ProcessEnv): Promise<S3BlobClient>;
|
|
52
|
+
//# sourceMappingURL=s3.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../../src/blob-store/s3.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,eAAe,EAErB,MAAM,YAAY,CAAC;AAEpB,2EAA2E;AAC3E,MAAM,MAAM,YAAY,GAAG;IACzB,SAAS,CAAC,KAAK,EAAE;QACf,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,GAAG,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/B,SAAS,CAAC,KAAK,EAAE;QACf,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;KACb,GAAG,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IAChF,UAAU,CAAC,KAAK,EAAE;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;KACb,GAAG,OAAO,CAAC;QAAE,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACpF,YAAY,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrE,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,YAAY,CAAC;IACrB,qDAAqD;IACrD,oBAAoB,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAC;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAUF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,GAAG,eAAe,CAqE3E;AAED;;GAEG;AACH,wBAAsB,4BAA4B,CAChD,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,YAAY,CAAC,CAqEvB"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { assertSafeBlobKey, assertSafeOrgId, } from "./types.js";
|
|
3
|
+
function objectKey(prefix, orgId, key) {
|
|
4
|
+
const org = assertSafeOrgId(orgId);
|
|
5
|
+
const safeKey = assertSafeBlobKey(key);
|
|
6
|
+
const p = (prefix ?? "").replace(/^\/+|\/+$/g, "");
|
|
7
|
+
const rest = `${org}/${safeKey}`;
|
|
8
|
+
return p ? `${p}/${rest}` : rest;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Cloud object store (S3-compatible). Bodies never touch local disk.
|
|
12
|
+
* Pass a real AWS SDK client or a test double via `client`.
|
|
13
|
+
*/
|
|
14
|
+
export function createS3BlobStore(opts) {
|
|
15
|
+
if (!opts.bucket?.trim())
|
|
16
|
+
throw new Error("S3 bucket required");
|
|
17
|
+
const bucket = opts.bucket.trim();
|
|
18
|
+
const prefix = opts.prefix;
|
|
19
|
+
const sse = opts.serverSideEncryption === false
|
|
20
|
+
? undefined
|
|
21
|
+
: (opts.serverSideEncryption ?? "AES256");
|
|
22
|
+
return {
|
|
23
|
+
backend: "s3",
|
|
24
|
+
async put(orgId, key, body, putOpts) {
|
|
25
|
+
const Key = objectKey(prefix, orgId, key);
|
|
26
|
+
const out = await opts.client.putObject({
|
|
27
|
+
Bucket: bucket,
|
|
28
|
+
Key,
|
|
29
|
+
Body: body,
|
|
30
|
+
ContentType: putOpts?.contentType,
|
|
31
|
+
...(sse
|
|
32
|
+
? {
|
|
33
|
+
ServerSideEncryption: sse,
|
|
34
|
+
...(sse === "aws:kms" && opts.sseKmsKeyId
|
|
35
|
+
? { SSEKMSKeyId: opts.sseKmsKeyId }
|
|
36
|
+
: {}),
|
|
37
|
+
}
|
|
38
|
+
: {}),
|
|
39
|
+
});
|
|
40
|
+
const digest = createHash("sha256").update(body).digest("hex");
|
|
41
|
+
return {
|
|
42
|
+
backend: "s3",
|
|
43
|
+
orgId: assertSafeOrgId(orgId),
|
|
44
|
+
key: assertSafeBlobKey(key),
|
|
45
|
+
sizeBytes: body.byteLength,
|
|
46
|
+
contentType: putOpts?.contentType,
|
|
47
|
+
etag: out.ETag?.replace(/"/g, "") ?? `sha256:${digest}`,
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
async get(orgId, key) {
|
|
51
|
+
const Key = objectKey(prefix, orgId, key);
|
|
52
|
+
const out = await opts.client.getObject({ Bucket: bucket, Key });
|
|
53
|
+
if (!out?.Body)
|
|
54
|
+
return null;
|
|
55
|
+
return Buffer.isBuffer(out.Body)
|
|
56
|
+
? out.Body
|
|
57
|
+
: Buffer.from(out.Body);
|
|
58
|
+
},
|
|
59
|
+
async head(orgId, key) {
|
|
60
|
+
const Key = objectKey(prefix, orgId, key);
|
|
61
|
+
const out = await opts.client.headObject({ Bucket: bucket, Key });
|
|
62
|
+
if (!out)
|
|
63
|
+
return null;
|
|
64
|
+
return {
|
|
65
|
+
orgId: assertSafeOrgId(orgId),
|
|
66
|
+
key: assertSafeBlobKey(key),
|
|
67
|
+
sizeBytes: out.ContentLength ?? 0,
|
|
68
|
+
contentType: out.ContentType,
|
|
69
|
+
etag: out.ETag?.replace(/"/g, ""),
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
async delete(orgId, key) {
|
|
73
|
+
const Key = objectKey(prefix, orgId, key);
|
|
74
|
+
const existing = await opts.client.headObject({ Bucket: bucket, Key });
|
|
75
|
+
if (!existing)
|
|
76
|
+
return false;
|
|
77
|
+
await opts.client.deleteObject({ Bucket: bucket, Key });
|
|
78
|
+
return true;
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Build SDK client from env when backend=s3. Lazy-imports @aws-sdk/client-s3.
|
|
84
|
+
*/
|
|
85
|
+
export async function createAwsS3BlobClientFromEnv(env = process.env) {
|
|
86
|
+
const { S3Client, PutObjectCommand, GetObjectCommand, HeadObjectCommand, DeleteObjectCommand } = await import("@aws-sdk/client-s3");
|
|
87
|
+
const region = env.MEMORIX_S3_REGION?.trim() || env.AWS_REGION?.trim() || "us-east-1";
|
|
88
|
+
const endpoint = env.MEMORIX_S3_ENDPOINT?.trim() || undefined;
|
|
89
|
+
const client = new S3Client({
|
|
90
|
+
region,
|
|
91
|
+
...(endpoint
|
|
92
|
+
? {
|
|
93
|
+
endpoint,
|
|
94
|
+
forcePathStyle: env.MEMORIX_S3_FORCE_PATH_STYLE === "1" ||
|
|
95
|
+
env.MEMORIX_S3_FORCE_PATH_STYLE === "true",
|
|
96
|
+
}
|
|
97
|
+
: {}),
|
|
98
|
+
});
|
|
99
|
+
return {
|
|
100
|
+
async putObject(input) {
|
|
101
|
+
const out = await client.send(new PutObjectCommand({
|
|
102
|
+
Bucket: input.Bucket,
|
|
103
|
+
Key: input.Key,
|
|
104
|
+
Body: input.Body,
|
|
105
|
+
ContentType: input.ContentType,
|
|
106
|
+
ServerSideEncryption: input.ServerSideEncryption,
|
|
107
|
+
SSEKMSKeyId: input.SSEKMSKeyId,
|
|
108
|
+
}));
|
|
109
|
+
return { ETag: out.ETag };
|
|
110
|
+
},
|
|
111
|
+
async getObject(input) {
|
|
112
|
+
try {
|
|
113
|
+
const out = await client.send(new GetObjectCommand(input));
|
|
114
|
+
if (!out.Body)
|
|
115
|
+
return { Body: null, ContentType: out.ContentType };
|
|
116
|
+
const bytes = await out.Body.transformToByteArray();
|
|
117
|
+
return { Body: Buffer.from(bytes), ContentType: out.ContentType };
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
if (err.name === "NoSuchKey")
|
|
121
|
+
return null;
|
|
122
|
+
if (err.$metadata
|
|
123
|
+
?.httpStatusCode === 404)
|
|
124
|
+
return null;
|
|
125
|
+
throw err;
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
async headObject(input) {
|
|
129
|
+
try {
|
|
130
|
+
const out = await client.send(new HeadObjectCommand(input));
|
|
131
|
+
return {
|
|
132
|
+
ContentLength: out.ContentLength,
|
|
133
|
+
ContentType: out.ContentType,
|
|
134
|
+
ETag: out.ETag,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
if (err.name === "NotFound")
|
|
139
|
+
return null;
|
|
140
|
+
if (err.$metadata
|
|
141
|
+
?.httpStatusCode === 404)
|
|
142
|
+
return null;
|
|
143
|
+
throw err;
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
async deleteObject(input) {
|
|
147
|
+
await client.send(new DeleteObjectCommand(input));
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=s3.js.map
|