@ddd-ts-kit/kernel 0.8.0 → 0.9.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 +69 -0
- package/dist/application/files/accepts-media-type.d.ts +3 -0
- package/dist/application/files/accepts-media-type.d.ts.map +1 -0
- package/dist/application/files/accepts-media-type.js +11 -0
- package/dist/application/files/accepts-media-type.js.map +1 -0
- package/dist/application/files/assert-expiry.d.ts +3 -0
- package/dist/application/files/assert-expiry.d.ts.map +1 -0
- package/dist/application/files/assert-expiry.js +10 -0
- package/dist/application/files/assert-expiry.js.map +1 -0
- package/dist/application/files/assert-file-key.d.ts +3 -0
- package/dist/application/files/assert-file-key.d.ts.map +1 -0
- package/dist/application/files/assert-file-key.js +10 -0
- package/dist/application/files/assert-file-key.js.map +1 -0
- package/dist/application/files/assert-final-key.d.ts +3 -0
- package/dist/application/files/assert-final-key.d.ts.map +1 -0
- package/dist/application/files/assert-final-key.js +11 -0
- package/dist/application/files/assert-final-key.js.map +1 -0
- package/dist/application/files/content-disposition.d.ts +3 -0
- package/dist/application/files/content-disposition.d.ts.map +1 -0
- package/dist/application/files/content-disposition.js +9 -0
- package/dist/application/files/content-disposition.js.map +1 -0
- package/dist/application/files/content-expectation.d.ts +6 -0
- package/dist/application/files/content-expectation.d.ts.map +1 -0
- package/dist/application/files/content-expectation.js +2 -0
- package/dist/application/files/content-expectation.js.map +1 -0
- package/dist/application/files/content-matches.d.ts +7 -0
- package/dist/application/files/content-matches.d.ts.map +1 -0
- package/dist/application/files/content-matches.js +39 -0
- package/dist/application/files/content-matches.js.map +1 -0
- package/dist/application/files/download-url-options.d.ts +7 -0
- package/dist/application/files/download-url-options.d.ts.map +1 -0
- package/dist/application/files/download-url-options.js +2 -0
- package/dist/application/files/download-url-options.js.map +1 -0
- package/dist/application/files/file-content.d.ts +8 -0
- package/dist/application/files/file-content.d.ts.map +1 -0
- package/dist/application/files/file-content.js +2 -0
- package/dist/application/files/file-content.js.map +1 -0
- package/dist/application/files/file-error.d.ts +28 -0
- package/dist/application/files/file-error.d.ts.map +1 -0
- package/dist/application/files/file-error.js +29 -0
- package/dist/application/files/file-error.js.map +1 -0
- package/dist/application/files/file-metadata.d.ts +10 -0
- package/dist/application/files/file-metadata.d.ts.map +1 -0
- package/dist/application/files/file-metadata.js +2 -0
- package/dist/application/files/file-metadata.js.map +1 -0
- package/dist/application/files/file-source.d.ts +4 -0
- package/dist/application/files/file-source.d.ts.map +1 -0
- package/dist/application/files/file-source.js +2 -0
- package/dist/application/files/file-source.js.map +1 -0
- package/dist/application/files/file-upload.d.ts +25 -0
- package/dist/application/files/file-upload.d.ts.map +1 -0
- package/dist/application/files/file-upload.js +36 -0
- package/dist/application/files/file-upload.js.map +1 -0
- package/dist/application/files/index.d.ts +31 -0
- package/dist/application/files/index.d.ts.map +1 -0
- package/dist/application/files/index.js +31 -0
- package/dist/application/files/index.js.map +1 -0
- package/dist/application/files/inspection-limits.d.ts +8 -0
- package/dist/application/files/inspection-limits.d.ts.map +1 -0
- package/dist/application/files/inspection-limits.js +2 -0
- package/dist/application/files/inspection-limits.js.map +1 -0
- package/dist/application/files/is-file-key.d.ts +7 -0
- package/dist/application/files/is-file-key.d.ts.map +1 -0
- package/dist/application/files/is-file-key.js +11 -0
- package/dist/application/files/is-file-key.js.map +1 -0
- package/dist/application/files/listed-file.d.ts +7 -0
- package/dist/application/files/listed-file.d.ts.map +1 -0
- package/dist/application/files/listed-file.js +2 -0
- package/dist/application/files/listed-file.js.map +1 -0
- package/dist/application/files/max-signed-url-seconds.d.ts +3 -0
- package/dist/application/files/max-signed-url-seconds.d.ts.map +1 -0
- package/dist/application/files/max-signed-url-seconds.js +3 -0
- package/dist/application/files/max-signed-url-seconds.js.map +1 -0
- package/dist/application/files/media-type-of.d.ts +3 -0
- package/dist/application/files/media-type-of.d.ts.map +1 -0
- package/dist/application/files/media-type-of.js +5 -0
- package/dist/application/files/media-type-of.js.map +1 -0
- package/dist/application/files/pending-upload-link.d.ts +10 -0
- package/dist/application/files/pending-upload-link.d.ts.map +1 -0
- package/dist/application/files/pending-upload-link.js +2 -0
- package/dist/application/files/pending-upload-link.js.map +1 -0
- package/dist/application/files/pending-upload.d.ts +15 -0
- package/dist/application/files/pending-upload.d.ts.map +1 -0
- package/dist/application/files/pending-upload.js +2 -0
- package/dist/application/files/pending-upload.js.map +1 -0
- package/dist/application/files/put-options.d.ts +7 -0
- package/dist/application/files/put-options.d.ts.map +1 -0
- package/dist/application/files/put-options.js +2 -0
- package/dist/application/files/put-options.js.map +1 -0
- package/dist/application/files/received-file.d.ts +15 -0
- package/dist/application/files/received-file.d.ts.map +1 -0
- package/dist/application/files/received-file.js +2 -0
- package/dist/application/files/received-file.js.map +1 -0
- package/dist/application/files/scan-verdict.d.ts +8 -0
- package/dist/application/files/scan-verdict.d.ts.map +1 -0
- package/dist/application/files/scan-verdict.js +2 -0
- package/dist/application/files/scan-verdict.js.map +1 -0
- package/dist/application/files/signature-of.d.ts +3 -0
- package/dist/application/files/signature-of.d.ts.map +1 -0
- package/dist/application/files/signature-of.js +28 -0
- package/dist/application/files/signature-of.js.map +1 -0
- package/dist/application/files/signed-download.d.ts +6 -0
- package/dist/application/files/signed-download.d.ts.map +1 -0
- package/dist/application/files/signed-download.js +2 -0
- package/dist/application/files/signed-download.js.map +1 -0
- package/dist/application/files/signed-upload.d.ts +9 -0
- package/dist/application/files/signed-upload.d.ts.map +1 -0
- package/dist/application/files/signed-upload.js +2 -0
- package/dist/application/files/signed-upload.js.map +1 -0
- package/dist/application/files/signed-url.d.ts +6 -0
- package/dist/application/files/signed-url.d.ts.map +1 -0
- package/dist/application/files/signed-url.js +2 -0
- package/dist/application/files/signed-url.js.map +1 -0
- package/dist/application/files/staged-key-of.d.ts +5 -0
- package/dist/application/files/staged-key-of.d.ts.map +1 -0
- package/dist/application/files/staged-key-of.js +7 -0
- package/dist/application/files/staged-key-of.js.map +1 -0
- package/dist/application/files/stored-file.d.ts +16 -0
- package/dist/application/files/stored-file.d.ts.map +1 -0
- package/dist/application/files/stored-file.js +2 -0
- package/dist/application/files/stored-file.js.map +1 -0
- package/dist/application/files/upload-url-options.d.ts +12 -0
- package/dist/application/files/upload-url-options.d.ts.map +1 -0
- package/dist/application/files/upload-url-options.js +2 -0
- package/dist/application/files/upload-url-options.js.map +1 -0
- package/dist/application/ports/file-keys.d.ts +9 -0
- package/dist/application/ports/file-keys.d.ts.map +1 -0
- package/dist/application/ports/file-keys.js +10 -0
- package/dist/application/ports/file-keys.js.map +1 -0
- package/dist/application/ports/file-scanner.d.ts +13 -0
- package/dist/application/ports/file-scanner.d.ts.map +1 -0
- package/dist/application/ports/file-scanner.js +12 -0
- package/dist/application/ports/file-scanner.js.map +1 -0
- package/dist/application/ports/file-storage.d.ts +40 -0
- package/dist/application/ports/file-storage.d.ts.map +1 -0
- package/dist/application/ports/file-storage.js +15 -0
- package/dist/application/ports/file-storage.js.map +1 -0
- package/dist/application/ports/served-file-storage.d.ts +16 -0
- package/dist/application/ports/served-file-storage.d.ts.map +1 -0
- package/dist/application/ports/served-file-storage.js +12 -0
- package/dist/application/ports/served-file-storage.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/domain-events/after-commit.d.ts +10 -0
- package/dist/infrastructure/domain-events/after-commit.d.ts.map +1 -0
- package/dist/infrastructure/domain-events/after-commit.js +19 -0
- package/dist/infrastructure/domain-events/after-commit.js.map +1 -0
- package/dist/infrastructure/domain-events/at-commit.d.ts +11 -0
- package/dist/infrastructure/domain-events/at-commit.d.ts.map +1 -0
- package/dist/infrastructure/domain-events/at-commit.js +19 -0
- package/dist/infrastructure/domain-events/at-commit.js.map +1 -0
- package/dist/infrastructure/domain-events/commit-step.d.ts +10 -0
- package/dist/infrastructure/domain-events/commit-step.d.ts.map +1 -0
- package/dist/infrastructure/domain-events/commit-step.js +2 -0
- package/dist/infrastructure/domain-events/commit-step.js.map +1 -0
- package/dist/infrastructure/domain-events/unit-of-work-scope.d.ts +5 -0
- package/dist/infrastructure/domain-events/unit-of-work-scope.d.ts.map +1 -1
- package/dist/infrastructure/domain-events/unit-of-work-scope.js.map +1 -1
- package/dist/infrastructure/domain-events/within-unit-of-work.d.ts +3 -1
- package/dist/infrastructure/domain-events/within-unit-of-work.d.ts.map +1 -1
- package/dist/infrastructure/domain-events/within-unit-of-work.js +32 -5
- package/dist/infrastructure/domain-events/within-unit-of-work.js.map +1 -1
- package/dist/infrastructure/files/accept-every-file-scanner.d.ts +7 -0
- package/dist/infrastructure/files/accept-every-file-scanner.d.ts.map +1 -0
- package/dist/infrastructure/files/accept-every-file-scanner.js +7 -0
- package/dist/infrastructure/files/accept-every-file-scanner.js.map +1 -0
- package/dist/infrastructure/files/collect.d.ts +4 -0
- package/dist/infrastructure/files/collect.d.ts.map +1 -0
- package/dist/infrastructure/files/collect.js +11 -0
- package/dist/infrastructure/files/collect.js.map +1 -0
- package/dist/infrastructure/files/file-grant.d.ts +11 -0
- package/dist/infrastructure/files/file-grant.d.ts.map +1 -0
- package/dist/infrastructure/files/file-grant.js +2 -0
- package/dist/infrastructure/files/file-grant.js.map +1 -0
- package/dist/infrastructure/files/file-inspection.d.ts +22 -0
- package/dist/infrastructure/files/file-inspection.d.ts.map +1 -0
- package/dist/infrastructure/files/file-inspection.js +66 -0
- package/dist/infrastructure/files/file-inspection.js.map +1 -0
- package/dist/infrastructure/files/file-staging.d.ts +59 -0
- package/dist/infrastructure/files/file-staging.d.ts.map +1 -0
- package/dist/infrastructure/files/file-staging.js +142 -0
- package/dist/infrastructure/files/file-staging.js.map +1 -0
- package/dist/infrastructure/files/file-uploads-middleware.d.ts +11 -0
- package/dist/infrastructure/files/file-uploads-middleware.d.ts.map +1 -0
- package/dist/infrastructure/files/file-uploads-middleware.js +25 -0
- package/dist/infrastructure/files/file-uploads-middleware.js.map +1 -0
- package/dist/infrastructure/files/in-memory-file-storage.d.ts +25 -0
- package/dist/infrastructure/files/in-memory-file-storage.d.ts.map +1 -0
- package/dist/infrastructure/files/in-memory-file-storage.js +73 -0
- package/dist/infrastructure/files/in-memory-file-storage.js.map +1 -0
- package/dist/infrastructure/files/is-file-grant.d.ts +3 -0
- package/dist/infrastructure/files/is-file-grant.d.ts.map +1 -0
- package/dist/infrastructure/files/is-file-grant.js +16 -0
- package/dist/infrastructure/files/is-file-grant.js.map +1 -0
- package/dist/infrastructure/files/is-stored-metadata.d.ts +3 -0
- package/dist/infrastructure/files/is-stored-metadata.d.ts.map +1 -0
- package/dist/infrastructure/files/is-stored-metadata.js +8 -0
- package/dist/infrastructure/files/is-stored-metadata.js.map +1 -0
- package/dist/infrastructure/files/local-file-storage-options.d.ts +6 -0
- package/dist/infrastructure/files/local-file-storage-options.d.ts.map +1 -0
- package/dist/infrastructure/files/local-file-storage-options.js +2 -0
- package/dist/infrastructure/files/local-file-storage-options.js.map +1 -0
- package/dist/infrastructure/files/local-file-storage.d.ts +29 -0
- package/dist/infrastructure/files/local-file-storage.d.ts.map +1 -0
- package/dist/infrastructure/files/local-file-storage.js +103 -0
- package/dist/infrastructure/files/local-file-storage.js.map +1 -0
- package/dist/infrastructure/files/local-paths.d.ts +12 -0
- package/dist/infrastructure/files/local-paths.d.ts.map +1 -0
- package/dist/infrastructure/files/local-paths.js +33 -0
- package/dist/infrastructure/files/local-paths.js.map +1 -0
- package/dist/infrastructure/files/purge-staged-files.d.ts +16 -0
- package/dist/infrastructure/files/purge-staged-files.d.ts.map +1 -0
- package/dist/infrastructure/files/purge-staged-files.js +21 -0
- package/dist/infrastructure/files/purge-staged-files.js.map +1 -0
- package/dist/infrastructure/files/self-signed-file-storage.d.ts +23 -0
- package/dist/infrastructure/files/self-signed-file-storage.d.ts.map +1 -0
- package/dist/infrastructure/files/self-signed-file-storage.js +57 -0
- package/dist/infrastructure/files/self-signed-file-storage.js.map +1 -0
- package/dist/infrastructure/files/signed-file-tokens.d.ts +20 -0
- package/dist/infrastructure/files/signed-file-tokens.d.ts.map +1 -0
- package/dist/infrastructure/files/signed-file-tokens.js +45 -0
- package/dist/infrastructure/files/signed-file-tokens.js.map +1 -0
- package/dist/infrastructure/files/signed-link-options.d.ts +10 -0
- package/dist/infrastructure/files/signed-link-options.d.ts.map +1 -0
- package/dist/infrastructure/files/signed-link-options.js +2 -0
- package/dist/infrastructure/files/signed-link-options.js.map +1 -0
- package/dist/infrastructure/files/stored-metadata.d.ts +6 -0
- package/dist/infrastructure/files/stored-metadata.d.ts.map +1 -0
- package/dist/infrastructure/files/stored-metadata.js +2 -0
- package/dist/infrastructure/files/stored-metadata.js.map +1 -0
- package/dist/infrastructure/files/tenant-file-keys.d.ts +20 -0
- package/dist/infrastructure/files/tenant-file-keys.d.ts.map +1 -0
- package/dist/infrastructure/files/tenant-file-keys.js +27 -0
- package/dist/infrastructure/files/tenant-file-keys.js.map +1 -0
- package/dist/infrastructure/files/upload-desk.d.ts +8 -0
- package/dist/infrastructure/files/upload-desk.d.ts.map +1 -0
- package/dist/infrastructure/files/upload-desk.js +2 -0
- package/dist/infrastructure/files/upload-desk.js.map +1 -0
- package/dist/infrastructure/files/without-trailing-slashes.d.ts +3 -0
- package/dist/infrastructure/files/without-trailing-slashes.d.ts.map +1 -0
- package/dist/infrastructure/files/without-trailing-slashes.js +9 -0
- package/dist/infrastructure/files/without-trailing-slashes.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { assertFileKey } from '../../application/files/assert-file-key.js';
|
|
4
|
+
/** Where the local storage puts a key: its content, its metadata, and a private place for a write in progress. */
|
|
5
|
+
export class LocalPaths {
|
|
6
|
+
root;
|
|
7
|
+
constructor(directory) {
|
|
8
|
+
this.root = path.resolve(directory);
|
|
9
|
+
}
|
|
10
|
+
contentOf(key) {
|
|
11
|
+
return this.inside(assertFileKey(key).split('/'));
|
|
12
|
+
}
|
|
13
|
+
metadataOf(key) {
|
|
14
|
+
return this.inside(['.metadata', ...assertFileKey(key).split('/')]) + '.json';
|
|
15
|
+
}
|
|
16
|
+
incoming() {
|
|
17
|
+
return this.inside(['.incoming', randomUUID()]);
|
|
18
|
+
}
|
|
19
|
+
/** The key of a file found under the root, or `undefined` for what the storage keeps for itself. */
|
|
20
|
+
keyOf(file) {
|
|
21
|
+
const segments = path.relative(this.root, file).split(path.sep);
|
|
22
|
+
return segments.some((segment) => segment.startsWith('.') || segment === '') ? undefined : segments.join('/');
|
|
23
|
+
}
|
|
24
|
+
inside(segments) {
|
|
25
|
+
const resolved = path.resolve(this.root, ...segments);
|
|
26
|
+
// A valid key cannot climb out; this holds the promise even if the key rules change one day.
|
|
27
|
+
if (!resolved.startsWith(this.root + path.sep)) {
|
|
28
|
+
throw new Error('A file key resolved outside the directory of the storage.');
|
|
29
|
+
}
|
|
30
|
+
return resolved;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=local-paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-paths.js","sourceRoot":"","sources":["../../../src/infrastructure/files/local-paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,4CAA4C,CAAC;AAE3E,kHAAkH;AAClH,MAAM,OAAO,UAAU;IACZ,IAAI,CAAS;IAEtB,YAAY,SAAiB;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;IAED,SAAS,CAAC,GAAW;QACnB,OAAO,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,UAAU,CAAC,GAAW;QACpB,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;IAChF,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;IAClD,CAAC;IAED,oGAAoG;IACpG,KAAK,CAAC,IAAY;QAChB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEhE,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChH,CAAC;IAEO,MAAM,CAAC,QAA2B;QACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,QAAQ,CAAC,CAAC;QAEtD,6FAA6F;QAC7F,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/C,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;QAC/E,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Clock } from '../../application/ports/clock.js';
|
|
2
|
+
import type { ScheduledJob } from '../../application/scheduling/scheduled-job.js';
|
|
3
|
+
import type { FileStaging } from './file-staging.js';
|
|
4
|
+
/**
|
|
5
|
+
* The scheduled job of the platform that removes what rolled-back or crashed commands left in
|
|
6
|
+
* `staging/`: the files staged more than `olderThanMs` ago (a day by default). Registered under
|
|
7
|
+
* `files.purge-staged`, every hour, by the platform of a project that keeps files.
|
|
8
|
+
*/
|
|
9
|
+
export declare class PurgeStagedFiles implements ScheduledJob {
|
|
10
|
+
private readonly staging;
|
|
11
|
+
private readonly clock;
|
|
12
|
+
private readonly olderThanMs;
|
|
13
|
+
constructor(staging: FileStaging, clock: Clock, olderThanMs?: number);
|
|
14
|
+
run(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=purge-staged-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"purge-staged-files.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/files/purge-staged-files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kCAAkC,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+CAA+C,CAAC;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKrD;;;;GAIG;AACH,qBAAa,gBAAiB,YAAW,YAAY;IAEjD,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAH9B,YACmB,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,KAAK,EACZ,WAAW,GAAE,MAAiB,EAC7C;IAEE,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAEzB;CACF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** A day: a command that staged a file has long committed or rolled back. */
|
|
2
|
+
const A_DAY_MS = 86_400_000;
|
|
3
|
+
/**
|
|
4
|
+
* The scheduled job of the platform that removes what rolled-back or crashed commands left in
|
|
5
|
+
* `staging/`: the files staged more than `olderThanMs` ago (a day by default). Registered under
|
|
6
|
+
* `files.purge-staged`, every hour, by the platform of a project that keeps files.
|
|
7
|
+
*/
|
|
8
|
+
export class PurgeStagedFiles {
|
|
9
|
+
staging;
|
|
10
|
+
clock;
|
|
11
|
+
olderThanMs;
|
|
12
|
+
constructor(staging, clock, olderThanMs = A_DAY_MS) {
|
|
13
|
+
this.staging = staging;
|
|
14
|
+
this.clock = clock;
|
|
15
|
+
this.olderThanMs = olderThanMs;
|
|
16
|
+
}
|
|
17
|
+
async run() {
|
|
18
|
+
await this.staging.purge(new Date(this.clock.now().getTime() - this.olderThanMs));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=purge-staged-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"purge-staged-files.js","sourceRoot":"","sources":["../../../src/infrastructure/files/purge-staged-files.ts"],"names":[],"mappings":"AAIA,6EAA6E;AAC7E,MAAM,QAAQ,GAAG,UAAU,CAAC;AAE5B;;;;GAIG;AACH,MAAM,OAAO,gBAAgB;IAER,OAAO;IACP,KAAK;IACL,WAAW;IAH9B,YACmB,OAAoB,EACpB,KAAY,EACZ,WAAW,GAAW,QAAQ;uBAF9B,OAAO;qBACP,KAAK;2BACL,WAAW;IAC3B,CAAC;IAEJ,KAAK,CAAC,GAAG;QACP,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;IACpF,CAAC;CACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { DownloadUrlOptions } from '../../application/files/download-url-options.js';
|
|
2
|
+
import type { FileSource } from '../../application/files/file-source.js';
|
|
3
|
+
import type { SignedDownload } from '../../application/files/signed-download.js';
|
|
4
|
+
import type { SignedUpload } from '../../application/files/signed-upload.js';
|
|
5
|
+
import type { SignedUrl } from '../../application/files/signed-url.js';
|
|
6
|
+
import type { StoredFile } from '../../application/files/stored-file.js';
|
|
7
|
+
import type { UploadUrlOptions } from '../../application/files/upload-url-options.js';
|
|
8
|
+
import { ServedFileStorage } from '../../application/ports/served-file-storage.js';
|
|
9
|
+
import type { SignedLinkOptions } from './signed-link-options.js';
|
|
10
|
+
/** The signed links of the storages the application serves itself: one token per link, under `baseUrl`. */
|
|
11
|
+
export declare abstract class SelfSignedFileStorage extends ServedFileStorage {
|
|
12
|
+
private readonly tokens;
|
|
13
|
+
private readonly baseUrl;
|
|
14
|
+
protected constructor(options: SignedLinkOptions);
|
|
15
|
+
signedDownloadUrl(key: string, options: DownloadUrlOptions): Promise<SignedUrl>;
|
|
16
|
+
signedUploadUrl(key: string, options: UploadUrlOptions): Promise<SignedUpload>;
|
|
17
|
+
openSigned(token: string): Promise<SignedDownload | undefined>;
|
|
18
|
+
/** Stores what a signed upload link sends, refused beyond the size the link was signed for (`FILE_TOO_LARGE`). */
|
|
19
|
+
acceptSigned(token: string, content: FileSource, mediaType: string): Promise<StoredFile | undefined>;
|
|
20
|
+
/** The token travels in the query, where no router limits its length the way it limits a path parameter. */
|
|
21
|
+
private linkOf;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=self-signed-file-storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"self-signed-file-storage.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/files/self-signed-file-storage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAE1F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AAEzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AACjF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0CAA0C,CAAC;AAC7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uCAAuC,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAC;AACtF,OAAO,EAAE,iBAAiB,EAAE,MAAM,gDAAgD,CAAC;AAEnF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAIlE,2GAA2G;AAC3G,8BAAsB,qBAAsB,SAAQ,iBAAiB;IACnE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAE1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC,SAAS,aAAa,OAAO,EAAE,iBAAiB,EAS/C;IAED,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,SAAS,CAAC,CAM9E;IAED,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAW7E;IAEK,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CASnE;IAED,kHAAkH;IAC5G,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAUzG;IAED,4GAA4G;IAC5G,OAAO,CAAC,MAAM;CAGf"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { assertExpiry } from '../../application/files/assert-expiry.js';
|
|
3
|
+
import { assertFileKey } from '../../application/files/assert-file-key.js';
|
|
4
|
+
import { mediaTypeOf } from '../../application/files/media-type-of.js';
|
|
5
|
+
import { ServedFileStorage } from '../../application/ports/served-file-storage.js';
|
|
6
|
+
import { SystemClock } from '../clock/system-clock.js';
|
|
7
|
+
import { SignedFileTokens } from './signed-file-tokens.js';
|
|
8
|
+
import { withoutTrailingSlashes } from './without-trailing-slashes.js';
|
|
9
|
+
/** The signed links of the storages the application serves itself: one token per link, under `baseUrl`. */
|
|
10
|
+
export class SelfSignedFileStorage extends ServedFileStorage {
|
|
11
|
+
tokens;
|
|
12
|
+
baseUrl;
|
|
13
|
+
constructor(options) {
|
|
14
|
+
super();
|
|
15
|
+
this.tokens = new SignedFileTokens(options.secret ?? randomBytes(32).toString('hex'), options.clock ?? new SystemClock());
|
|
16
|
+
this.baseUrl = withoutTrailingSlashes(options.baseUrl ?? 'http://localhost:3000/files');
|
|
17
|
+
}
|
|
18
|
+
signedDownloadUrl(key, options) {
|
|
19
|
+
const grant = { action: 'download', key: assertFileKey(key) };
|
|
20
|
+
const named = options.fileName === undefined ? grant : { ...grant, fileName: options.fileName };
|
|
21
|
+
const { token, expiresAt } = this.tokens.sign(named, assertExpiry(options.expiresInSeconds));
|
|
22
|
+
return Promise.resolve({ url: this.linkOf(token), expiresAt });
|
|
23
|
+
}
|
|
24
|
+
signedUploadUrl(key, options) {
|
|
25
|
+
const mediaType = mediaTypeOf(options.mediaType);
|
|
26
|
+
const grant = { action: 'upload', key: assertFileKey(key), mediaType, maxBytes: options.maxBytes };
|
|
27
|
+
const { token, expiresAt } = this.tokens.sign(grant, assertExpiry(options.expiresInSeconds));
|
|
28
|
+
return Promise.resolve({
|
|
29
|
+
url: this.linkOf(token),
|
|
30
|
+
method: 'PUT',
|
|
31
|
+
headers: { 'content-type': mediaType },
|
|
32
|
+
expiresAt,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async openSigned(token) {
|
|
36
|
+
const grant = this.tokens.verify(token, 'download');
|
|
37
|
+
const content = grant === undefined ? undefined : await this.get(grant.key);
|
|
38
|
+
if (grant?.fileName === undefined || content === undefined) {
|
|
39
|
+
return content;
|
|
40
|
+
}
|
|
41
|
+
return { ...content, fileName: grant.fileName };
|
|
42
|
+
}
|
|
43
|
+
/** Stores what a signed upload link sends, refused beyond the size the link was signed for (`FILE_TOO_LARGE`). */
|
|
44
|
+
async acceptSigned(token, content, mediaType) {
|
|
45
|
+
const grant = this.tokens.verify(token, 'upload');
|
|
46
|
+
if (grant?.mediaType === undefined || grant.mediaType !== mediaTypeOf(mediaType)) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
const { key, maxBytes } = grant;
|
|
50
|
+
return this.put(key, content, { mediaType: grant.mediaType, ...(maxBytes === undefined ? {} : { maxBytes }) });
|
|
51
|
+
}
|
|
52
|
+
/** The token travels in the query, where no router limits its length the way it limits a path parameter. */
|
|
53
|
+
linkOf(token) {
|
|
54
|
+
return `${this.baseUrl}?token=${token}`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=self-signed-file-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"self-signed-file-storage.js","sourceRoot":"","sources":["../../../src/infrastructure/files/self-signed-file-storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Clock } from '../../application/ports/clock.js';
|
|
2
|
+
import type { FileGrant } from './file-grant.js';
|
|
3
|
+
/**
|
|
4
|
+
* The tokens of the links a `ServedFileStorage` signs: the grant in base64url, a dot, and its
|
|
5
|
+
* HMAC-SHA256 under the secret. Whoever holds the token holds the grant until it expires; whoever
|
|
6
|
+
* changes a byte of it holds nothing.
|
|
7
|
+
*/
|
|
8
|
+
export declare class SignedFileTokens {
|
|
9
|
+
private readonly secret;
|
|
10
|
+
private readonly clock;
|
|
11
|
+
constructor(secret: string, clock: Clock);
|
|
12
|
+
sign(grant: Omit<FileGrant, 'expiresAt'>, expiresInSeconds: number): {
|
|
13
|
+
token: string;
|
|
14
|
+
expiresAt: Date;
|
|
15
|
+
};
|
|
16
|
+
/** The grant of a genuine, unexpired token for this action, or `undefined`. */
|
|
17
|
+
verify(token: string, action: FileGrant['action']): FileGrant | undefined;
|
|
18
|
+
private signatureOf;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=signed-file-tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signed-file-tokens.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/files/signed-file-tokens.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kCAAkC,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAGjD;;;;GAIG;AACH,qBAAa,gBAAgB;IAEzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAFxB,YACmB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,KAAK,EAC3B;IAEJ,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,gBAAgB,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,IAAI,CAAA;KAAE,CAKtG;IAED,+EAA+E;IAC/E,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,GAAG,SAAS,GAAG,SAAS,CAaxE;IAED,OAAO,CAAC,WAAW;CAGpB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
import { isFileGrant } from './is-file-grant.js';
|
|
3
|
+
/**
|
|
4
|
+
* The tokens of the links a `ServedFileStorage` signs: the grant in base64url, a dot, and its
|
|
5
|
+
* HMAC-SHA256 under the secret. Whoever holds the token holds the grant until it expires; whoever
|
|
6
|
+
* changes a byte of it holds nothing.
|
|
7
|
+
*/
|
|
8
|
+
export class SignedFileTokens {
|
|
9
|
+
secret;
|
|
10
|
+
clock;
|
|
11
|
+
constructor(secret, clock) {
|
|
12
|
+
this.secret = secret;
|
|
13
|
+
this.clock = clock;
|
|
14
|
+
}
|
|
15
|
+
sign(grant, expiresInSeconds) {
|
|
16
|
+
const expiresAt = Math.floor(this.clock.now().getTime() / 1_000) + expiresInSeconds;
|
|
17
|
+
const payload = Buffer.from(JSON.stringify({ ...grant, expiresAt })).toString('base64url');
|
|
18
|
+
return { token: `${payload}.${this.signatureOf(payload)}`, expiresAt: new Date(expiresAt * 1_000) };
|
|
19
|
+
}
|
|
20
|
+
/** The grant of a genuine, unexpired token for this action, or `undefined`. */
|
|
21
|
+
verify(token, action) {
|
|
22
|
+
const [payload = '', signature = ''] = token.split('.');
|
|
23
|
+
const expected = Buffer.from(this.signatureOf(payload));
|
|
24
|
+
const given = Buffer.from(signature);
|
|
25
|
+
if (expected.length !== given.length || !timingSafeEqual(expected, given)) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
const grant = parsed(payload);
|
|
29
|
+
const now = this.clock.now().getTime() / 1_000;
|
|
30
|
+
return grant?.action === action && grant.expiresAt > now ? grant : undefined;
|
|
31
|
+
}
|
|
32
|
+
signatureOf(payload) {
|
|
33
|
+
return createHmac('sha256', this.secret).update(payload).digest('base64url');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function parsed(payload) {
|
|
37
|
+
try {
|
|
38
|
+
const value = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
|
|
39
|
+
return isFileGrant(value) ? value : undefined;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=signed-file-tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signed-file-tokens.js","sourceRoot":"","sources":["../../../src/infrastructure/files/signed-file-tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG1D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD;;;;GAIG;AACH,MAAM,OAAO,gBAAgB;IAER,MAAM;IACN,KAAK;IAFxB,YACmB,MAAc,EACd,KAAY;sBADZ,MAAM;qBACN,KAAK;IACrB,CAAC;IAEJ,IAAI,CAAC,KAAmC,EAAE,gBAAwB;QAChE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,GAAG,gBAAgB,CAAC;QACpF,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAE3F,OAAO,EAAE,KAAK,EAAE,GAAG,OAAO,IAAI,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,EAAE,CAAC;IACtG,CAAC;IAED,+EAA+E;IAC/E,MAAM,CAAC,KAAa,EAAE,MAA2B;QAC/C,MAAM,CAAC,OAAO,GAAG,EAAE,EAAE,SAAS,GAAG,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxD,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;QACxD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAErC,IAAI,QAAQ,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC;YAC1E,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC;QAE/C,OAAO,KAAK,EAAE,MAAM,KAAK,MAAM,IAAI,KAAK,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/E,CAAC;IAEO,WAAW,CAAC,OAAe;QACjC,OAAO,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC/E,CAAC;CACF;AAED,SAAS,MAAM,CAAC,OAAe;IAC7B,IAAI,CAAC;QACH,MAAM,KAAK,GAAY,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QAEtF,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Clock } from '../../application/ports/clock.js';
|
|
2
|
+
export interface SignedLinkOptions {
|
|
3
|
+
/** Where `signedFileRoutes` of `@ddd-ts-kit/http` answers, as a client reaches it: `http://localhost:3000/files`. A link is `<baseUrl>?token=<token>`. */
|
|
4
|
+
readonly baseUrl?: string;
|
|
5
|
+
/** Signs the links. Left out, a random one is drawn: the links then die with the process. */
|
|
6
|
+
readonly secret?: string;
|
|
7
|
+
/** Reads the expiry of the links. Default: the system clock. */
|
|
8
|
+
readonly clock?: Clock;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=signed-link-options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signed-link-options.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/files/signed-link-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kCAAkC,CAAC;AAE9D,MAAM,WAAW,iBAAiB;IAChC,0JAA0J;IAC1J,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,6FAA6F;IAC7F,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,gEAAgE;IAChE,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signed-link-options.js","sourceRoot":"","sources":["../../../src/infrastructure/files/signed-link-options.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stored-metadata.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/files/stored-metadata.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAC3G,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stored-metadata.js","sourceRoot":"","sources":["../../../src/infrastructure/files/stored-metadata.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Clock } from '../../application/ports/clock.js';
|
|
2
|
+
import type { ExecutionScope } from '../../application/ports/execution-scope.js';
|
|
3
|
+
import type { FileKeys } from '../../application/ports/file-keys.js';
|
|
4
|
+
import type { IdGenerator } from '../../application/ports/id-generator.js';
|
|
5
|
+
/**
|
|
6
|
+
* Keys of the form `tenants/<tenant>/<area>/<year>/<month>/<id>`, the tenant segment only when
|
|
7
|
+
* the work in progress names a tenant: the files of a tenant then share a prefix that a bucket
|
|
8
|
+
* policy, an export or an erasure can address. A tenant id is a key segment as it is: the kernel
|
|
9
|
+
* refuses any other at every entry (`isTenantId`), so two tenants never share a prefix. An area that would put the key under `staging/`
|
|
10
|
+
* is refused (`INVALID_FILE_KEY`). The id comes from the `IdGenerator`, so a key is
|
|
11
|
+
* not guessable when the generator is not.
|
|
12
|
+
*/
|
|
13
|
+
export declare class TenantFileKeys implements FileKeys {
|
|
14
|
+
private readonly ids;
|
|
15
|
+
private readonly clock;
|
|
16
|
+
private readonly scope?;
|
|
17
|
+
constructor(ids: IdGenerator, clock: Clock, scope?: Pick<ExecutionScope, 'current'> | undefined);
|
|
18
|
+
next(area: string): string;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=tenant-file-keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-file-keys.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/files/tenant-file-keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kCAAkC,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AACjF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AAG3E;;;;;;;GAOG;AACH,qBAAa,cAAe,YAAW,QAAQ;IAE3C,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IAHzB,YACmB,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,KAAK,EACZ,KAAK,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,YAAA,EACtD;IAEJ,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOzB;CACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { assertFinalKey } from '../../application/files/assert-final-key.js';
|
|
2
|
+
/**
|
|
3
|
+
* Keys of the form `tenants/<tenant>/<area>/<year>/<month>/<id>`, the tenant segment only when
|
|
4
|
+
* the work in progress names a tenant: the files of a tenant then share a prefix that a bucket
|
|
5
|
+
* policy, an export or an erasure can address. A tenant id is a key segment as it is: the kernel
|
|
6
|
+
* refuses any other at every entry (`isTenantId`), so two tenants never share a prefix. An area that would put the key under `staging/`
|
|
7
|
+
* is refused (`INVALID_FILE_KEY`). The id comes from the `IdGenerator`, so a key is
|
|
8
|
+
* not guessable when the generator is not.
|
|
9
|
+
*/
|
|
10
|
+
export class TenantFileKeys {
|
|
11
|
+
ids;
|
|
12
|
+
clock;
|
|
13
|
+
scope;
|
|
14
|
+
constructor(ids, clock, scope) {
|
|
15
|
+
this.ids = ids;
|
|
16
|
+
this.clock = clock;
|
|
17
|
+
this.scope = scope;
|
|
18
|
+
}
|
|
19
|
+
next(area) {
|
|
20
|
+
const now = this.clock.now();
|
|
21
|
+
const month = String(now.getUTCMonth() + 1).padStart(2, '0');
|
|
22
|
+
const key = `${area}/${String(now.getUTCFullYear())}/${month}/${this.ids.next()}`;
|
|
23
|
+
const tenantId = this.scope?.current()?.tenantId;
|
|
24
|
+
return assertFinalKey(tenantId === undefined ? key : `tenants/${tenantId}/${key}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=tenant-file-keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-file-keys.js","sourceRoot":"","sources":["../../../src/infrastructure/files/tenant-file-keys.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,6CAA6C,CAAC;AAE7E;;;;;;;GAOG;AACH,MAAM,OAAO,cAAc;IAEN,GAAG;IACH,KAAK;IACL,KAAK;IAHxB,YACmB,GAAgB,EAChB,KAAY,EACZ,KAAuC;mBAFvC,GAAG;qBACH,KAAK;qBACL,KAAK;IACrB,CAAC;IAEJ,IAAI,CAAC,IAAY;QACf,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC7D,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,MAAM,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;QAClF,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,QAAQ,CAAC;QAEjD,OAAO,cAAc,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,QAAQ,IAAI,GAAG,EAAE,CAAC,CAAC;IACrF,CAAC;CACF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FileKeys } from '../../application/ports/file-keys.js';
|
|
2
|
+
import type { FileStaging } from './file-staging.js';
|
|
3
|
+
/** What `fileUploadsMiddleware` stages a file upload with: the keys of the server and the staging. */
|
|
4
|
+
export interface UploadDesk {
|
|
5
|
+
readonly keys: FileKeys;
|
|
6
|
+
readonly staging: FileStaging;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=upload-desk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-desk.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/files/upload-desk.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,sGAAsG;AACtG,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;CAC/B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-desk.js","sourceRoot":"","sources":["../../../src/infrastructure/files/upload-desk.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"without-trailing-slashes.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/files/without-trailing-slashes.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ5D"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** `value` without the slashes it ends with. */
|
|
2
|
+
export function withoutTrailingSlashes(value) {
|
|
3
|
+
let end = value.length;
|
|
4
|
+
while (end > 0 && value[end - 1] === '/') {
|
|
5
|
+
end -= 1;
|
|
6
|
+
}
|
|
7
|
+
return value.slice(0, end);
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=without-trailing-slashes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"without-trailing-slashes.js","sourceRoot":"","sources":["../../../src/infrastructure/files/without-trailing-slashes.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,MAAM,UAAU,sBAAsB,CAAC,KAAa;IAClD,IAAI,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC;IAEvB,OAAO,GAAG,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;QACzC,GAAG,IAAI,CAAC,CAAC;IACX,CAAC;IAED,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC7B,CAAC"}
|