@de-otio/trellis 0.10.11 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/env.d.ts +232 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +221 -0
- package/dist/env.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/lambda/media-completion-worker.d.ts +175 -0
- package/dist/lambda/media-completion-worker.d.ts.map +1 -0
- package/dist/lambda/media-completion-worker.js +373 -0
- package/dist/lambda/media-completion-worker.js.map +1 -0
- package/dist/lambda/media-processing-worker.d.ts +172 -1
- package/dist/lambda/media-processing-worker.d.ts.map +1 -1
- package/dist/lambda/media-processing-worker.js +343 -49
- package/dist/lambda/media-processing-worker.js.map +1 -1
- package/dist/lib/app.d.ts.map +1 -1
- package/dist/lib/app.js +5 -0
- package/dist/lib/app.js.map +1 -1
- package/dist/lib/encrypted-settings/config.d.ts +13 -0
- package/dist/lib/encrypted-settings/config.d.ts.map +1 -0
- package/dist/lib/encrypted-settings/config.js +19 -0
- package/dist/lib/encrypted-settings/config.js.map +1 -0
- package/dist/lib/encrypted-settings/encrypted-settings-handler.d.ts +57 -0
- package/dist/lib/encrypted-settings/encrypted-settings-handler.d.ts.map +1 -0
- package/dist/lib/encrypted-settings/encrypted-settings-handler.js +178 -0
- package/dist/lib/encrypted-settings/encrypted-settings-handler.js.map +1 -0
- package/dist/lib/encrypted-settings/encrypted-settings-store.d.ts +110 -0
- package/dist/lib/encrypted-settings/encrypted-settings-store.d.ts.map +1 -0
- package/dist/lib/encrypted-settings/encrypted-settings-store.js +103 -0
- package/dist/lib/encrypted-settings/encrypted-settings-store.js.map +1 -0
- package/dist/lib/encrypted-settings/types.d.ts +26 -0
- package/dist/lib/encrypted-settings/types.d.ts.map +1 -0
- package/dist/lib/encrypted-settings/types.js +27 -0
- package/dist/lib/encrypted-settings/types.js.map +1 -0
- package/dist/lib/exif-stripper.d.ts +37 -22
- package/dist/lib/exif-stripper.d.ts.map +1 -1
- package/dist/lib/exif-stripper.js +101 -41
- package/dist/lib/exif-stripper.js.map +1 -1
- package/dist/lib/media/cas-keys.d.ts +63 -0
- package/dist/lib/media/cas-keys.d.ts.map +1 -0
- package/dist/lib/media/cas-keys.js +102 -0
- package/dist/lib/media/cas-keys.js.map +1 -0
- package/dist/lib/media/classify-worker-error.d.ts +48 -0
- package/dist/lib/media/classify-worker-error.d.ts.map +1 -0
- package/dist/lib/media/classify-worker-error.js +319 -0
- package/dist/lib/media/classify-worker-error.js.map +1 -0
- package/dist/lib/media/dedupe-key.d.ts +29 -0
- package/dist/lib/media/dedupe-key.d.ts.map +1 -0
- package/dist/lib/media/dedupe-key.js +49 -0
- package/dist/lib/media/dedupe-key.js.map +1 -0
- package/dist/lib/media/duration-cap.d.ts +30 -0
- package/dist/lib/media/duration-cap.d.ts.map +1 -0
- package/dist/lib/media/duration-cap.js +37 -0
- package/dist/lib/media/duration-cap.js.map +1 -0
- package/dist/lib/media/ffmpeg-args.d.ts +83 -0
- package/dist/lib/media/ffmpeg-args.d.ts.map +1 -0
- package/dist/lib/media/ffmpeg-args.js +119 -0
- package/dist/lib/media/ffmpeg-args.js.map +1 -0
- package/dist/lib/media/media-ports.d.ts +126 -0
- package/dist/lib/media/media-ports.d.ts.map +1 -0
- package/dist/lib/media/media-ports.js +129 -0
- package/dist/lib/media/media-ports.js.map +1 -0
- package/dist/lib/media/media-upsert.d.ts +55 -0
- package/dist/lib/media/media-upsert.d.ts.map +1 -0
- package/dist/lib/media/media-upsert.js +38 -0
- package/dist/lib/media/media-upsert.js.map +1 -0
- package/dist/lib/media/moderation-provider.d.ts +111 -0
- package/dist/lib/media/moderation-provider.d.ts.map +1 -0
- package/dist/lib/media/moderation-provider.js +130 -0
- package/dist/lib/media/moderation-provider.js.map +1 -0
- package/dist/lib/media/moderation-resolved-payload.d.ts +48 -0
- package/dist/lib/media/moderation-resolved-payload.d.ts.map +1 -0
- package/dist/lib/media/moderation-resolved-payload.js +37 -0
- package/dist/lib/media/moderation-resolved-payload.js.map +1 -0
- package/dist/lib/media/moderation-status.d.ts +98 -0
- package/dist/lib/media/moderation-status.d.ts.map +1 -0
- package/dist/lib/media/moderation-status.js +122 -0
- package/dist/lib/media/moderation-status.js.map +1 -0
- package/dist/lib/media/processing-types.d.ts +45 -0
- package/dist/lib/media/processing-types.d.ts.map +1 -0
- package/dist/lib/media/processing-types.js +9 -0
- package/dist/lib/media/processing-types.js.map +1 -0
- package/dist/lib/media/promote-decision.d.ts +64 -0
- package/dist/lib/media/promote-decision.d.ts.map +1 -0
- package/dist/lib/media/promote-decision.js +76 -0
- package/dist/lib/media/promote-decision.js.map +1 -0
- package/dist/lib/media/quota-check.d.ts +22 -0
- package/dist/lib/media/quota-check.d.ts.map +1 -0
- package/dist/lib/media/quota-check.js +42 -0
- package/dist/lib/media/quota-check.js.map +1 -0
- package/dist/lib/media/quota-types.d.ts +15 -0
- package/dist/lib/media/quota-types.d.ts.map +1 -0
- package/dist/lib/media/quota-types.js +9 -0
- package/dist/lib/media/quota-types.js.map +1 -0
- package/dist/lib/media/route-upload.d.ts +58 -0
- package/dist/lib/media/route-upload.d.ts.map +1 -0
- package/dist/lib/media/route-upload.js +80 -0
- package/dist/lib/media/route-upload.js.map +1 -0
- package/dist/lib/media/serve-gate.d.ts +51 -0
- package/dist/lib/media/serve-gate.d.ts.map +1 -0
- package/dist/lib/media/serve-gate.js +68 -0
- package/dist/lib/media/serve-gate.js.map +1 -0
- package/dist/lib/media/tenant-resolution.d.ts +42 -0
- package/dist/lib/media/tenant-resolution.d.ts.map +1 -0
- package/dist/lib/media/tenant-resolution.js +45 -0
- package/dist/lib/media/tenant-resolution.js.map +1 -0
- package/dist/lib/media/text-moderation.d.ts +28 -0
- package/dist/lib/media/text-moderation.d.ts.map +1 -0
- package/dist/lib/media/text-moderation.js +62 -0
- package/dist/lib/media/text-moderation.js.map +1 -0
- package/dist/lib/media/track-verdict.d.ts +45 -0
- package/dist/lib/media/track-verdict.d.ts.map +1 -0
- package/dist/lib/media/track-verdict.js +52 -0
- package/dist/lib/media/track-verdict.js.map +1 -0
- package/dist/lib/media/transcript-moderation.d.ts +47 -0
- package/dist/lib/media/transcript-moderation.d.ts.map +1 -0
- package/dist/lib/media/transcript-moderation.js +70 -0
- package/dist/lib/media/transcript-moderation.js.map +1 -0
- package/dist/lib/media-handler.d.ts.map +1 -1
- package/dist/lib/media-handler.js +15 -9
- package/dist/lib/media-handler.js.map +1 -1
- package/dist/lib/notification-handler.d.ts +11 -4
- package/dist/lib/notification-handler.d.ts.map +1 -1
- package/dist/lib/notification-handler.js +161 -29
- package/dist/lib/notification-handler.js.map +1 -1
- package/dist/lib/post-handler.d.ts.map +1 -1
- package/dist/lib/post-handler.js +4 -1
- package/dist/lib/post-handler.js.map +1 -1
- package/dist/lib/realtime/block-store.d.ts +61 -0
- package/dist/lib/realtime/block-store.d.ts.map +1 -0
- package/dist/lib/realtime/block-store.js +0 -0
- package/dist/lib/realtime/block-store.js.map +1 -0
- package/dist/lib/realtime/channel.d.ts +34 -0
- package/dist/lib/realtime/channel.d.ts.map +1 -0
- package/dist/lib/realtime/channel.js +100 -0
- package/dist/lib/realtime/channel.js.map +1 -0
- package/dist/lib/realtime/delivery-policy.d.ts +51 -0
- package/dist/lib/realtime/delivery-policy.d.ts.map +1 -0
- package/dist/lib/realtime/delivery-policy.js +98 -0
- package/dist/lib/realtime/delivery-policy.js.map +1 -0
- package/dist/lib/realtime/index.d.ts +21 -0
- package/dist/lib/realtime/index.d.ts.map +1 -0
- package/dist/lib/realtime/index.js +39 -0
- package/dist/lib/realtime/index.js.map +1 -0
- package/dist/lib/realtime/no-op-transport.d.ts +10 -0
- package/dist/lib/realtime/no-op-transport.d.ts.map +1 -0
- package/dist/lib/realtime/no-op-transport.js +44 -0
- package/dist/lib/realtime/no-op-transport.js.map +1 -0
- package/dist/lib/realtime/poll-transport.d.ts +11 -0
- package/dist/lib/realtime/poll-transport.d.ts.map +1 -0
- package/dist/lib/realtime/poll-transport.js +68 -0
- package/dist/lib/realtime/poll-transport.js.map +1 -0
- package/dist/lib/realtime/push-notifier.d.ts +39 -0
- package/dist/lib/realtime/push-notifier.d.ts.map +1 -0
- package/dist/lib/realtime/push-notifier.js +76 -0
- package/dist/lib/realtime/push-notifier.js.map +1 -0
- package/dist/lib/realtime/realtime-transport.d.ts +2 -0
- package/dist/lib/realtime/realtime-transport.d.ts.map +1 -0
- package/dist/lib/realtime/realtime-transport.js +23 -0
- package/dist/lib/realtime/realtime-transport.js.map +1 -0
- package/dist/lib/realtime/setting-store.d.ts +30 -0
- package/dist/lib/realtime/setting-store.d.ts.map +1 -0
- package/dist/lib/realtime/setting-store.js +0 -0
- package/dist/lib/realtime/setting-store.js.map +1 -0
- package/dist/lib/realtime/types.d.ts +200 -0
- package/dist/lib/realtime/types.d.ts.map +1 -0
- package/dist/lib/realtime/types.js +61 -0
- package/dist/lib/realtime/types.js.map +1 -0
- package/dist/lib/routes/index.d.ts.map +1 -1
- package/dist/lib/routes/index.js +3 -0
- package/dist/lib/routes/index.js.map +1 -1
- package/dist/lib/routes/media.d.ts +21 -0
- package/dist/lib/routes/media.d.ts.map +1 -1
- package/dist/lib/routes/media.js +584 -483
- package/dist/lib/routes/media.js.map +1 -1
- package/dist/lib/routes/settings.d.ts +17 -0
- package/dist/lib/routes/settings.d.ts.map +1 -0
- package/dist/lib/routes/settings.js +187 -0
- package/dist/lib/routes/settings.js.map +1 -0
- package/dist/lib/services/image-normalizer.d.ts +64 -6
- package/dist/lib/services/image-normalizer.d.ts.map +1 -1
- package/dist/lib/services/image-normalizer.js +88 -6
- package/dist/lib/services/image-normalizer.js.map +1 -1
- package/dist/lib/services/media-upload-service.d.ts +2 -2
- package/dist/lib/services/media-upload-service.d.ts.map +1 -1
- package/dist/lib/services/media-upload-service.js +22 -21
- package/dist/lib/services/media-upload-service.js.map +1 -1
- package/dist/lib/tenant-scope.d.ts.map +1 -1
- package/dist/lib/tenant-scope.js +18 -1
- package/dist/lib/tenant-scope.js.map +1 -1
- package/package.json +23 -22
- package/prisma/migrations/20260620051144_add_encrypted_user_settings/migration.sql +24 -0
- package/prisma/migrations/20260620120000_add_blocked_users/migration.sql +29 -0
- package/prisma/migrations/20260625000000_media_tenant_scope_and_moderation_status/migration.sql +49 -0
- package/prisma/migrations/20260625000001_p0b_moderation_jobs/migration.sql +73 -0
- package/prisma/schema.prisma +133 -15
- package/src/lambda/media-completion-worker.ts +567 -0
- package/src/lambda/media-processing-worker.ts +508 -59
|
@@ -1,15 +1,73 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Image
|
|
2
|
+
* Image re-encode pipeline (T7 — transcode-and-discard).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Re-encodes every uploaded image to a canonical safe raster format using
|
|
5
|
+
* sharp. This is the polyglot + pixel-bomb defense: a file that is
|
|
6
|
+
* simultaneously a valid image and valid JS/HTML/script payload is stripped to
|
|
7
|
+
* clean raster pixels by the transcode; a file that claims huge dimensions but
|
|
8
|
+
* tiny bytes is rejected by sharp's `limitInputPixels` guard before it can
|
|
9
|
+
* trigger a decompression bomb.
|
|
7
10
|
*
|
|
8
|
-
*
|
|
11
|
+
* Design constraints:
|
|
12
|
+
* - `.rotate()` bakes EXIF orientation into pixels (so the tag becomes stale
|
|
13
|
+
* and can safely be dropped — no `.withMetadata()` call).
|
|
14
|
+
* - NO `.withMetadata()` — metadata is intentionally dropped by the re-encode
|
|
15
|
+
* (T6 owns the post-encode assertion that it is gone).
|
|
16
|
+
* - Runs BEFORE the SHA-256 hash so the CAS hash is of the cleaned bytes.
|
|
17
|
+
* - Accepted MIME types MUST equal the set sharp can write (HEIC/HEIF and SVG
|
|
18
|
+
* are excluded — HEIC write is not supported without the optional libheif
|
|
19
|
+
* native module, and SVG cannot be safely transcoded to raster).
|
|
20
|
+
*
|
|
21
|
+
* The async Lambda media-processing-worker (P0b) performs the same transcode
|
|
22
|
+
* for derivatives; this file is the P0a synchronous path in the upload handler.
|
|
23
|
+
*/
|
|
24
|
+
import type { Env } from "../../env.js";
|
|
25
|
+
/**
|
|
26
|
+
* The MIME types that this build of sharp can reliably re-encode to a
|
|
27
|
+
* canonical raster format. Any type outside this set must be rejected at the
|
|
28
|
+
* allowlist boundary.
|
|
29
|
+
*
|
|
30
|
+
* Excluded:
|
|
31
|
+
* - `image/svg+xml` — no safe raster transcode; can embed scripts.
|
|
32
|
+
* - `image/heic` / `image/heif` — sharp write support requires the optional
|
|
33
|
+
* libheif native module; absent = encode fails at runtime. Full HEIC UX is
|
|
34
|
+
* deferred to P1/D12.
|
|
35
|
+
* - `image/tiff` — sharp can read but the tiff encoder is rarely needed;
|
|
36
|
+
* excluded from P0a canonical set.
|
|
37
|
+
*/
|
|
38
|
+
export declare const REENCODABLE_IMAGE_TYPES: ReadonlySet<string>;
|
|
39
|
+
/**
|
|
40
|
+
* Result of a successful re-encode.
|
|
41
|
+
*/
|
|
42
|
+
export interface ReencodeResult {
|
|
43
|
+
/** Re-encoded image bytes (the bytes to hash and store). */
|
|
44
|
+
buffer: Buffer;
|
|
45
|
+
/**
|
|
46
|
+
* The canonical MIME type of the output bytes.
|
|
47
|
+
* Always one of image/jpeg, image/png, image/webp (canonical set).
|
|
48
|
+
*/
|
|
49
|
+
canonicalMimeType: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Re-encode an image buffer to the canonical safe raster format.
|
|
53
|
+
*
|
|
54
|
+
* @param inputBytes - Raw bytes of the uploaded file.
|
|
55
|
+
* @param env - Application env (reads `env.media.maxPixels`,
|
|
56
|
+
* `env.media.canonicalFormat`, `env.media.canonicalQuality`).
|
|
57
|
+
* @returns Re-encoded bytes and the canonical MIME type.
|
|
58
|
+
* @throws If the input cannot be decoded (corrupt / pixel-bomb / unsupported),
|
|
59
|
+
* or if `env.media.maxPixels` is exceeded.
|
|
60
|
+
*/
|
|
61
|
+
export declare function reencodeImage(inputBytes: Buffer | ArrayBuffer | Uint8Array, env: Pick<Env, "media">): Promise<ReencodeResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Legacy class kept so the existing import in `media.ts` continues to compile
|
|
64
|
+
* until T9 removes it. The class is now a thin shim over `reencodeImage`.
|
|
65
|
+
*
|
|
66
|
+
* @deprecated Use `reencodeImage` directly. This class will be removed in T9.
|
|
9
67
|
*/
|
|
10
68
|
export declare class ImageNormalizer {
|
|
11
69
|
constructor(_images: unknown, _mediaBucket: unknown);
|
|
12
|
-
/**
|
|
70
|
+
/** No-op — the re-encode is now done inline in the upload handler. */
|
|
13
71
|
normalize(_originalKey: string, _contentHash: string): Promise<string | null>;
|
|
14
72
|
}
|
|
15
73
|
//# sourceMappingURL=image-normalizer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-normalizer.d.ts","sourceRoot":"","sources":["../../../src/lib/services/image-normalizer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"image-normalizer.d.ts","sourceRoot":"","sources":["../../../src/lib/services/image-normalizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAExC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,EAAE,WAAW,CAAC,MAAM,CAMtD,CAAC;AAEH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,EAC7C,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,GACtB,OAAO,CAAC,cAAc,CAAC,CAuCzB;AAED;;;;;GAKG;AACH,qBAAa,eAAe;gBAEd,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO;IAEnD,sEAAsE;IAChE,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;CAGpF"}
|
|
@@ -1,16 +1,98 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Image
|
|
2
|
+
* Image re-encode pipeline (T7 — transcode-and-discard).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Re-encodes every uploaded image to a canonical safe raster format using
|
|
5
|
+
* sharp. This is the polyglot + pixel-bomb defense: a file that is
|
|
6
|
+
* simultaneously a valid image and valid JS/HTML/script payload is stripped to
|
|
7
|
+
* clean raster pixels by the transcode; a file that claims huge dimensions but
|
|
8
|
+
* tiny bytes is rejected by sharp's `limitInputPixels` guard before it can
|
|
9
|
+
* trigger a decompression bomb.
|
|
7
10
|
*
|
|
8
|
-
*
|
|
11
|
+
* Design constraints:
|
|
12
|
+
* - `.rotate()` bakes EXIF orientation into pixels (so the tag becomes stale
|
|
13
|
+
* and can safely be dropped — no `.withMetadata()` call).
|
|
14
|
+
* - NO `.withMetadata()` — metadata is intentionally dropped by the re-encode
|
|
15
|
+
* (T6 owns the post-encode assertion that it is gone).
|
|
16
|
+
* - Runs BEFORE the SHA-256 hash so the CAS hash is of the cleaned bytes.
|
|
17
|
+
* - Accepted MIME types MUST equal the set sharp can write (HEIC/HEIF and SVG
|
|
18
|
+
* are excluded — HEIC write is not supported without the optional libheif
|
|
19
|
+
* native module, and SVG cannot be safely transcoded to raster).
|
|
20
|
+
*
|
|
21
|
+
* The async Lambda media-processing-worker (P0b) performs the same transcode
|
|
22
|
+
* for derivatives; this file is the P0a synchronous path in the upload handler.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* The MIME types that this build of sharp can reliably re-encode to a
|
|
26
|
+
* canonical raster format. Any type outside this set must be rejected at the
|
|
27
|
+
* allowlist boundary.
|
|
28
|
+
*
|
|
29
|
+
* Excluded:
|
|
30
|
+
* - `image/svg+xml` — no safe raster transcode; can embed scripts.
|
|
31
|
+
* - `image/heic` / `image/heif` — sharp write support requires the optional
|
|
32
|
+
* libheif native module; absent = encode fails at runtime. Full HEIC UX is
|
|
33
|
+
* deferred to P1/D12.
|
|
34
|
+
* - `image/tiff` — sharp can read but the tiff encoder is rarely needed;
|
|
35
|
+
* excluded from P0a canonical set.
|
|
36
|
+
*/
|
|
37
|
+
export const REENCODABLE_IMAGE_TYPES = new Set([
|
|
38
|
+
"image/jpeg",
|
|
39
|
+
"image/jpg", // alias; normalised to image/jpeg
|
|
40
|
+
"image/png",
|
|
41
|
+
"image/webp",
|
|
42
|
+
"image/gif", // static raster in P0a (animated → first frame only)
|
|
43
|
+
]);
|
|
44
|
+
/**
|
|
45
|
+
* Re-encode an image buffer to the canonical safe raster format.
|
|
46
|
+
*
|
|
47
|
+
* @param inputBytes - Raw bytes of the uploaded file.
|
|
48
|
+
* @param env - Application env (reads `env.media.maxPixels`,
|
|
49
|
+
* `env.media.canonicalFormat`, `env.media.canonicalQuality`).
|
|
50
|
+
* @returns Re-encoded bytes and the canonical MIME type.
|
|
51
|
+
* @throws If the input cannot be decoded (corrupt / pixel-bomb / unsupported),
|
|
52
|
+
* or if `env.media.maxPixels` is exceeded.
|
|
53
|
+
*/
|
|
54
|
+
export async function reencodeImage(inputBytes, env) {
|
|
55
|
+
const { default: sharp } = await import("sharp");
|
|
56
|
+
const buf = inputBytes instanceof Buffer
|
|
57
|
+
? inputBytes
|
|
58
|
+
: Buffer.from(inputBytes instanceof ArrayBuffer ? inputBytes : inputBytes.buffer);
|
|
59
|
+
const { maxPixels, canonicalFormat, canonicalQuality } = env.media;
|
|
60
|
+
// limitInputPixels MUST NOT be false or 0 — decompression-bomb guard.
|
|
61
|
+
const pipeline = sharp(buf, { limitInputPixels: maxPixels })
|
|
62
|
+
.rotate(); // bake EXIF orientation into pixels; drops orientation tag
|
|
63
|
+
// toFormat — NO withMetadata() so all metadata is dropped
|
|
64
|
+
let outputBuffer;
|
|
65
|
+
if (canonicalFormat === "png") {
|
|
66
|
+
outputBuffer = await pipeline.toFormat("png").toBuffer();
|
|
67
|
+
}
|
|
68
|
+
else if (canonicalFormat === "webp") {
|
|
69
|
+
outputBuffer = await pipeline
|
|
70
|
+
.toFormat("webp", { quality: canonicalQuality })
|
|
71
|
+
.toBuffer();
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
// Default: jpeg
|
|
75
|
+
outputBuffer = await pipeline
|
|
76
|
+
.toFormat("jpeg", { quality: canonicalQuality })
|
|
77
|
+
.toBuffer();
|
|
78
|
+
}
|
|
79
|
+
const canonicalMimeType = canonicalFormat === "png"
|
|
80
|
+
? "image/png"
|
|
81
|
+
: canonicalFormat === "webp"
|
|
82
|
+
? "image/webp"
|
|
83
|
+
: "image/jpeg";
|
|
84
|
+
return { buffer: outputBuffer, canonicalMimeType };
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Legacy class kept so the existing import in `media.ts` continues to compile
|
|
88
|
+
* until T9 removes it. The class is now a thin shim over `reencodeImage`.
|
|
89
|
+
*
|
|
90
|
+
* @deprecated Use `reencodeImage` directly. This class will be removed in T9.
|
|
9
91
|
*/
|
|
10
92
|
export class ImageNormalizer {
|
|
11
93
|
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
|
|
12
94
|
constructor(_images, _mediaBucket) { }
|
|
13
|
-
/**
|
|
95
|
+
/** No-op — the re-encode is now done inline in the upload handler. */
|
|
14
96
|
async normalize(_originalKey, _contentHash) {
|
|
15
97
|
return null;
|
|
16
98
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-normalizer.js","sourceRoot":"","sources":["../../../src/lib/services/image-normalizer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"image-normalizer.js","sourceRoot":"","sources":["../../../src/lib/services/image-normalizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAwB,IAAI,GAAG,CAAC;IAClE,YAAY;IACZ,WAAW,EAAI,kCAAkC;IACjD,WAAW;IACX,YAAY;IACZ,WAAW,EAAI,qDAAqD;CACrE,CAAC,CAAC;AAeH;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,UAA6C,EAC7C,GAAuB;IAEvB,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC;IAEjD,MAAM,GAAG,GACP,UAAU,YAAY,MAAM;QAC1B,CAAC,CAAC,UAAU;QACZ,CAAC,CAAC,MAAM,CAAC,IAAI,CACT,UAAU,YAAY,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CACnE,CAAC;IAER,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,gBAAgB,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC;IAEnE,sEAAsE;IACtE,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,EAAE,EAAE,gBAAgB,EAAE,SAAS,EAAE,CAAC;SACzD,MAAM,EAAE,CAAC,CAAC,2DAA2D;IAExE,0DAA0D;IAC1D,IAAI,YAAoB,CAAC;IACzB,IAAI,eAAe,KAAK,KAAK,EAAE,CAAC;QAC9B,YAAY,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC3D,CAAC;SAAM,IAAI,eAAe,KAAK,MAAM,EAAE,CAAC;QACtC,YAAY,GAAG,MAAM,QAAQ;aAC1B,QAAQ,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;aAC/C,QAAQ,EAAE,CAAC;IAChB,CAAC;SAAM,CAAC;QACN,gBAAgB;QAChB,YAAY,GAAG,MAAM,QAAQ;aAC1B,QAAQ,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;aAC/C,QAAQ,EAAE,CAAC;IAChB,CAAC;IAED,MAAM,iBAAiB,GACrB,eAAe,KAAK,KAAK;QACvB,CAAC,CAAC,WAAW;QACb,CAAC,CAAC,eAAe,KAAK,MAAM;YAC1B,CAAC,CAAC,YAAY;YACd,CAAC,CAAC,YAAY,CAAC;IAErB,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,iBAAiB,EAAE,CAAC;AACrD,CAAC;AAED;;;;;GAKG;AACH,MAAM,OAAO,eAAe;IAC1B,qEAAqE;IACrE,YAAY,OAAgB,EAAE,YAAqB,IAAG,CAAC;IAEvD,sEAAsE;IACtE,KAAK,CAAC,SAAS,CAAC,YAAoB,EAAE,YAAoB;QACxD,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
|
|
@@ -8,7 +8,7 @@ export declare class MediaUploadService {
|
|
|
8
8
|
/**
|
|
9
9
|
* Upload single file
|
|
10
10
|
*/
|
|
11
|
-
uploadSingle(file: File, userId: string, metadata?: {
|
|
11
|
+
uploadSingle(file: File, userId: string, tenantId: string, metadata?: {
|
|
12
12
|
width?: number;
|
|
13
13
|
height?: number;
|
|
14
14
|
duration?: number;
|
|
@@ -16,7 +16,7 @@ export declare class MediaUploadService {
|
|
|
16
16
|
/**
|
|
17
17
|
* Upload batch of files
|
|
18
18
|
*/
|
|
19
|
-
uploadBatch(files: File[], userId: string, metadataArray?: Array<{
|
|
19
|
+
uploadBatch(files: File[], userId: string, tenantId: string, metadataArray?: Array<{
|
|
20
20
|
width?: number;
|
|
21
21
|
height?: number;
|
|
22
22
|
duration?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-upload-service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/media-upload-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"media-upload-service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/media-upload-service.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAGV,YAAY,EACb,MAAM,kCAAkC,CAAC;AAmC1C,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,GAAG,CAAM;gBAEL,GAAG,EAAE,GAAG;IAUpB;;OAEG;IACG,YAAY,CAChB,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,EACjE,aAAa,CAAC,EAAE,WAAW,GAC1B,OAAO,CAAC,YAAY,CAAC;IA6FxB;;OAEG;IACG,WAAW,CACf,KAAK,EAAE,IAAI,EAAE,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,aAAa,CAAC,EAAE,KAAK,CAAC;QACpB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC,GACD,OAAO,CAAC,YAAY,EAAE,CAAC;CAsG3B"}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* 4. Reconciliation worker creates database record asynchronously
|
|
9
9
|
*/
|
|
10
10
|
import { getLogger } from "../logger.js";
|
|
11
|
+
import { casKey, isCasKeyError } from "../media/cas-keys.js";
|
|
11
12
|
/**
|
|
12
13
|
* Generate unique batch ID
|
|
13
14
|
*/
|
|
@@ -23,22 +24,20 @@ async function generateContentHash(file) {
|
|
|
23
24
|
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
24
25
|
}
|
|
25
26
|
/**
|
|
26
|
-
*
|
|
27
|
+
* Build the canonical CAS original key for a tenant-scoped object (T9 / D18).
|
|
28
|
+
*
|
|
29
|
+
* Delegates to the pure `casKey` builder (anchored allowlist validation). A
|
|
30
|
+
* validation failure here means the caller passed a malformed tenantId or hash
|
|
31
|
+
* — both are produced internally (tenantId resolved from auth context, hash
|
|
32
|
+
* from `crypto.subtle.digest`), so a failure is a programming error and is
|
|
33
|
+
* thrown rather than silently producing an unsafe key.
|
|
27
34
|
*/
|
|
28
|
-
function
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"image/webp": "webp",
|
|
35
|
-
"image/heic": "heic",
|
|
36
|
-
"image/heif": "heif",
|
|
37
|
-
"video/mp4": "mp4",
|
|
38
|
-
"video/webm": "webm",
|
|
39
|
-
"video/quicktime": "mov",
|
|
40
|
-
};
|
|
41
|
-
return mimeMap[mimeType] || "bin";
|
|
35
|
+
function buildCasOriginalKey(tenantId, contentHash) {
|
|
36
|
+
const key = casKey(tenantId, contentHash);
|
|
37
|
+
if (isCasKeyError(key)) {
|
|
38
|
+
throw new Error(`Invalid CAS key inputs: ${key.kind}`);
|
|
39
|
+
}
|
|
40
|
+
return key;
|
|
42
41
|
}
|
|
43
42
|
export class MediaUploadService {
|
|
44
43
|
r2Bucket;
|
|
@@ -57,7 +56,7 @@ export class MediaUploadService {
|
|
|
57
56
|
/**
|
|
58
57
|
* Upload single file
|
|
59
58
|
*/
|
|
60
|
-
async uploadSingle(file, userId, metadata, preReadBuffer) {
|
|
59
|
+
async uploadSingle(file, userId, tenantId, metadata, preReadBuffer) {
|
|
61
60
|
const logger = getLogger();
|
|
62
61
|
const batchId = generateBatchId();
|
|
63
62
|
try {
|
|
@@ -66,8 +65,10 @@ export class MediaUploadService {
|
|
|
66
65
|
// which may not work reliably on Cloudflare Workers for FormData Files)
|
|
67
66
|
const fileBuffer = preReadBuffer ?? await file.arrayBuffer();
|
|
68
67
|
const contentHash = await generateContentHash(fileBuffer);
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
// T9: the ONE canonical CAS scheme (D18) — `cas/{tenantId}/{hash}`.
|
|
69
|
+
// The same key is what the DB `originalKey` stores and what the serve
|
|
70
|
+
// path reads, so upload-target and serve-source can never disagree.
|
|
71
|
+
const originalKey = buildCasOriginalKey(tenantId, contentHash);
|
|
71
72
|
logger.info("[MediaUpload] Starting upload", {
|
|
72
73
|
contentHash,
|
|
73
74
|
mimeType: file.type,
|
|
@@ -142,7 +143,7 @@ export class MediaUploadService {
|
|
|
142
143
|
/**
|
|
143
144
|
* Upload batch of files
|
|
144
145
|
*/
|
|
145
|
-
async uploadBatch(files, userId, metadataArray) {
|
|
146
|
+
async uploadBatch(files, userId, tenantId, metadataArray) {
|
|
146
147
|
const logger = getLogger();
|
|
147
148
|
const batchId = generateBatchId();
|
|
148
149
|
logger.info("[MediaUpload] Starting batch upload", {
|
|
@@ -154,8 +155,8 @@ export class MediaUploadService {
|
|
|
154
155
|
const uploadResults = await Promise.allSettled(files.map(async (file, index) => {
|
|
155
156
|
const fileBuffer = await file.arrayBuffer();
|
|
156
157
|
const contentHash = await generateContentHash(fileBuffer);
|
|
157
|
-
|
|
158
|
-
const originalKey =
|
|
158
|
+
// T9: canonical CAS scheme — see uploadSingle.
|
|
159
|
+
const originalKey = buildCasOriginalKey(tenantId, contentHash);
|
|
159
160
|
const metadata = metadataArray?.[index];
|
|
160
161
|
const r2Metadata = {
|
|
161
162
|
contentHash,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-upload-service.js","sourceRoot":"","sources":["../../../src/lib/services/media-upload-service.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAU,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"media-upload-service.js","sourceRoot":"","sources":["../../../src/lib/services/media-upload-service.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAU,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAO7D;;GAEG;AACH,SAAS,eAAe;IACtB,OAAO,SAAS,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AAC7E,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,mBAAmB,CAAC,IAAiB;IAClD,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;IACzD,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACxE,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,mBAAmB,CAAC,QAAgB,EAAE,WAAmB;IAChE,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAC1C,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,2BAA2B,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,OAAO,kBAAkB;IACrB,QAAQ,CAAW;IACnB,KAAK,CAAoC;IACzC,SAAS,CAAS;IAClB,GAAG,CAAM;IAEjB,YAAY,GAAQ;QAClB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC,eAAe,CAAC;QACpC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,0BAA0B,CAAC;QAC5C,IAAI,CAAC,SAAS;YACZ,GAAG,CAAC,WAAW,KAAK,MAAM;gBACxB,CAAC,CAAC,yBAAyB;gBAC3B,CAAC,CAAC,qBAAqB,CAAC;IAC9B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAChB,IAAU,EACV,MAAc,EACd,QAAgB,EAChB,QAAiE,EACjE,aAA2B;QAE3B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,eAAe,EAAE,CAAC;QAElC,IAAI,CAAC;YACH,2BAA2B;YAC3B,yEAAyE;YACzE,wEAAwE;YACxE,MAAM,UAAU,GAAG,aAAa,IAAI,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;YAC7D,MAAM,WAAW,GAAG,MAAM,mBAAmB,CAAC,UAAU,CAAC,CAAC;YAC1D,oEAAoE;YACpE,sEAAsE;YACtE,oEAAoE;YACpE,MAAM,WAAW,GAAG,mBAAmB,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;YAE/D,MAAM,CAAC,IAAI,CAAC,+BAA+B,EAAE;gBAC3C,WAAW;gBACX,QAAQ,EAAE,IAAI,CAAC,IAAI;gBACnB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,MAAM;gBACN,OAAO;aACR,CAAC,CAAC;YAEH,gCAAgC;YAChC,MAAM,UAAU,GAAoB;gBAClC,WAAW;gBACX,UAAU,EAAE,MAAM;gBAClB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACpC,QAAQ,EAAE,IAAI,CAAC,IAAI;gBACnB,mBAAmB,EAAE,MAAM;gBAC3B,UAAU,EAAE,OAAO;gBACnB,OAAO;gBACP,GAAG,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC;gBAC5D,GAAG,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;gBAC/D,GAAG,CAAC,QAAQ,EAAE,QAAQ,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC;aACtE,CAAC;YAEF,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAE,UAAU,EAAE;gBAC/C,YAAY,EAAE;oBACZ,WAAW,EAAE,IAAI,CAAC,IAAI;iBACvB;gBACD,cAAc,EAAE,UAA+C;aAChE,CAAC,CAAC;YAEH,MAAM,CAAC,IAAI,CAAC,oCAAoC,EAAE;gBAChD,WAAW;gBACX,WAAW;aACZ,CAAC,CAAC;YAEH,yCAAyC;YACzC,MAAM,OAAO,GAA+B;gBAC1C,IAAI,EAAE,eAAe;gBACrB,OAAO;gBACP,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;gBACrB,OAAO,EAAE;oBACP;wBACE,WAAW;wBACX,WAAW;wBACX,QAAQ,EAAE,IAAI,CAAC,IAAI;wBACnB,IAAI,EAAE,IAAI,CAAC,IAAI;wBACf,UAAU,EAAE,MAAM;wBAClB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;wBACpC,GAAG,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;wBACjD,GAAG,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;wBACpD,GAAG,CAAC,QAAQ,EAAE,QAAQ,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC;qBAC3D;iBACF;aACF,CAAC;YAEF,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAE/B,MAAM,CAAC,IAAI,CAAC,qCAAqC,EAAE;gBACjD,WAAW;gBACX,OAAO;aACR,CAAC,CAAC;YAEH,gCAAgC;YAChC,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,WAAW;gBACX,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,cAAc,WAAW,EAAE;gBACjD,MAAM,EAAE,UAAU;aACnB,CAAC;QACJ,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,MAAM,CAAC,KAAK,CAAC,6BAA6B,EAAE;gBAC1C,KAAK,EAAE,KAAK,CAAC,OAAO;gBACpB,MAAM;gBACN,QAAQ,EAAE,IAAI,CAAC,IAAI;aACpB,CAAC,CAAC;YACH,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW,CACf,KAAa,EACb,MAAc,EACd,QAAgB,EAChB,aAIE;QAEF,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,eAAe,EAAE,CAAC;QAElC,MAAM,CAAC,IAAI,CAAC,qCAAqC,EAAE;YACjD,SAAS,EAAE,KAAK,CAAC,MAAM;YACvB,MAAM;YACN,OAAO;SACR,CAAC,CAAC;QAEH,qCAAqC;QACrC,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,UAAU,CAC5C,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;YAC9B,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;YAC5C,MAAM,WAAW,GAAG,MAAM,mBAAmB,CAAC,UAAU,CAAC,CAAC;YAC1D,+CAA+C;YAC/C,MAAM,WAAW,GAAG,mBAAmB,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;YAC/D,MAAM,QAAQ,GAAG,aAAa,EAAE,CAAC,KAAK,CAAC,CAAC;YAExC,MAAM,UAAU,GAAoB;gBAClC,WAAW;gBACX,UAAU,EAAE,MAAM;gBAClB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACpC,QAAQ,EAAE,IAAI,CAAC,IAAI;gBACnB,mBAAmB,EAAE,MAAM;gBAC3B,UAAU,EAAE,OAAO;gBACnB,OAAO;gBACP,GAAG,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC;gBAC5D,GAAG,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;gBAC/D,GAAG,CAAC,QAAQ,EAAE,QAAQ,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC;aACtE,CAAC;YAEF,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAE,UAAU,EAAE;gBAC/C,YAAY,EAAE;oBACZ,WAAW,EAAE,IAAI,CAAC,IAAI;iBACvB;gBACD,cAAc,EAAE,UAA+C;aAChE,CAAC,CAAC;YAEH,OAAO;gBACL,WAAW;gBACX,WAAW;gBACX,QAAQ,EAAE,IAAI,CAAC,IAAI;gBACnB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,UAAU,EAAE,MAAM;gBAClB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACpC,GAAG,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;gBACjD,GAAG,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;gBACpD,GAAG,CAAC,QAAQ,EAAE,QAAQ,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC;aAC3D,CAAC;QACJ,CAAC,CAAC,CACH,CAAC;QAEF,6BAA6B;QAC7B,MAAM,UAAU,GAAG,aAAa;aAC7B,MAAM,CAAC,CAAC,CAAC,EAAoC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC;aACzE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAEvB,MAAM,CAAC,IAAI,CAAC,wCAAwC,EAAE;YACpD,KAAK,EAAE,KAAK,CAAC,MAAM;YACnB,UAAU,EAAE,UAAU,CAAC,MAAM;YAC7B,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM;YACxC,OAAO;SACR,CAAC,CAAC;QAEH,8CAA8C;QAC9C,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,OAAO,GAA+B;gBAC1C,IAAI,EAAE,cAAc;gBACpB,OAAO;gBACP,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;gBACrB,OAAO,EAAE,UAAU;aACpB,CAAC;YAEF,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAE/B,MAAM,CAAC,IAAI,CAAC,2CAA2C,EAAE;gBACvD,KAAK,EAAE,UAAU,CAAC,MAAM;gBACxB,OAAO;aACR,CAAC,CAAC;QACL,CAAC;QAED,iBAAiB;QACjB,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;YACzC,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;gBAClC,OAAO;oBACL,OAAO,EAAE,IAAI;oBACb,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,WAAW;oBACrC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,cAAc,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE;oBAC9D,MAAM,EAAE,UAAmB;iBAC5B,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,OAAO;oBACL,OAAO,EAAE,KAAK;oBACd,WAAW,EAAE,EAAE;oBACf,GAAG,EAAE,EAAE;oBACP,MAAM,EAAE,UAAmB;oBAC3B,OAAO,EAAE,kBAAkB,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE;iBACnD,CAAC;YACJ,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tenant-scope.d.ts","sourceRoot":"","sources":["../../src/lib/tenant-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAOH,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE3D,6EAA6E;AAC7E,wBAAgB,sBAAsB,CACpC,GAAG,GAAE,MAAM,GAAG,SAAyC,GACtD,eAAe,CAEjB;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"tenant-scope.d.ts","sourceRoot":"","sources":["../../src/lib/tenant-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAOH,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE3D,6EAA6E;AAC7E,wBAAgB,sBAAsB,CACpC,GAAG,GAAE,MAAM,GAAG,SAAyC,GACtD,eAAe,CAEjB;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CA6BnD,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAqEtD,CAAC;AAqBH;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAG7D;AAYD,+EAA+E;AAC/E,MAAM,MAAM,SAAS,GACjB;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,GACzB;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,GAC1C;IAAE,MAAM,EAAE,oBAAoB,CAAA;CAAE,GAChC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC;AAEzD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC1C,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,QAAQ,EAAE,OAAO,CAAC;CACnB,GAAG,SAAS,CAqDZ;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,eAAe;;;;EAyCzD"}
|
package/dist/lib/tenant-scope.js
CHANGED
|
@@ -48,6 +48,7 @@ export const TENANT_SCOPED_MODELS = new Set([
|
|
|
48
48
|
"PostComment",
|
|
49
49
|
"Entity",
|
|
50
50
|
"Notification",
|
|
51
|
+
"BlockedUser",
|
|
51
52
|
"Group",
|
|
52
53
|
"GroupMember",
|
|
53
54
|
"EntityOwnership",
|
|
@@ -66,6 +67,11 @@ export const TENANT_SCOPED_MODELS = new Set([
|
|
|
66
67
|
// this middleware, always filter tenant_id explicitly).
|
|
67
68
|
"Relationship",
|
|
68
69
|
"EntityRelationship",
|
|
70
|
+
// D18: MediaFile is now tenant-scoped (carries its own tenantId). Dedup is
|
|
71
|
+
// within-tenant via @@unique([tenantId, contentHash]). PostMedia is
|
|
72
|
+
// "by-relation" (no own tenantId) and crosses the scope boundary via
|
|
73
|
+
// the post→media join — flagged for T9/integration.
|
|
74
|
+
"MediaFile",
|
|
69
75
|
]);
|
|
70
76
|
/**
|
|
71
77
|
* Every other model, with the reason it is NOT auto-scoped. Exhaustive on
|
|
@@ -90,7 +96,12 @@ export const UNSCOPED_MODELS = new Map([
|
|
|
90
96
|
["FeatureToggle", "global"],
|
|
91
97
|
["IngestState", "global"],
|
|
92
98
|
["RoleMetadata", "global"],
|
|
93
|
-
|
|
99
|
+
// P0b moderation-message dedupe ledger: a system-global exactly-once table
|
|
100
|
+
// keyed on an opaque messageDedupeKey. No tenantId column; identical bytes
|
|
101
|
+
// share fan-in across tenants by design — never auto-scoped.
|
|
102
|
+
["ProcessedModerationMessage", "global"],
|
|
103
|
+
// Note: MediaFile was here as "global-content-addressed" prior to D18.
|
|
104
|
+
// It now carries its own tenantId and is in TENANT_SCOPED_MODELS above.
|
|
94
105
|
// User-scoped (boundary is userId, not tenant).
|
|
95
106
|
["User", "user"],
|
|
96
107
|
["CircleConfig", "user"],
|
|
@@ -100,6 +111,7 @@ export const UNSCOPED_MODELS = new Map([
|
|
|
100
111
|
["UploadSession", "user"],
|
|
101
112
|
["MfaEnrollment", "user"],
|
|
102
113
|
["UserEncryptionKey", "user"],
|
|
114
|
+
["EncryptedUserSetting", "user"],
|
|
103
115
|
["Consent", "user"],
|
|
104
116
|
["NotificationPreference", "user"],
|
|
105
117
|
// Generalized report model (Surveillance-hardening Phase 0, E3; folds in the
|
|
@@ -112,6 +124,11 @@ export const UNSCOPED_MODELS = new Map([
|
|
|
112
124
|
["DirectMessage", "user-pair"],
|
|
113
125
|
// Scoped-by-relation: no own tenantId yet (doc/14 §04 C / WS0). Rely on the
|
|
114
126
|
// parent's scope + the RLS backstop until tenantId is denormalized onto them.
|
|
127
|
+
// P0b moderation job: tenant-scoped through its parent MediaFile via mediaId
|
|
128
|
+
// (onDelete: Cascade). No own tenantId column, so it cannot be auto-scoped by
|
|
129
|
+
// the where-merge/create-stamp extension — same posture as the other
|
|
130
|
+
// by-relation child tables (parent scope + RLS backstop).
|
|
131
|
+
["MediaModerationJob", "by-relation"],
|
|
115
132
|
["PostMedia", "by-relation"],
|
|
116
133
|
["PostSentiment", "by-relation"],
|
|
117
134
|
["PostSubject", "by-relation"],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tenant-scope.js","sourceRoot":"","sources":["../../src/lib/tenant-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAIxC,6EAA6E;AAC7E,MAAM,UAAU,sBAAsB,CACpC,MAA0B,OAAO,CAAC,GAAG,CAAC,iBAAiB;IAEvD,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;AAC7D,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAwB,IAAI,GAAG,CAAC;IAC/D,MAAM;IACN,aAAa;IACb,QAAQ;IACR,cAAc;IACd,OAAO;IACP,aAAa;IACb,iBAAiB;IACjB,gBAAgB;IAChB,0BAA0B;IAC1B,mBAAmB;IACnB,kBAAkB;IAClB,eAAe;IACf,6EAA6E;IAC7E,8EAA8E;IAC9E,2EAA2E;IAC3E,gBAAgB;IAChB,wEAAwE;IACxE,2EAA2E;IAC3E,4EAA4E;IAC5E,wDAAwD;IACxD,cAAc;IACd,oBAAoB;
|
|
1
|
+
{"version":3,"file":"tenant-scope.js","sourceRoot":"","sources":["../../src/lib/tenant-scope.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@de-otio/trellis",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"license": "AGPL-3.0-or-later",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -49,22 +49,22 @@
|
|
|
49
49
|
"@aws-lambda-powertools/logger": "^2.32.0",
|
|
50
50
|
"@aws-lambda-powertools/metrics": "^2.32.0",
|
|
51
51
|
"@aws-lambda-powertools/parameters": "^2.33.1",
|
|
52
|
-
"@aws-sdk/client-cloudwatch": "^3.
|
|
53
|
-
"@aws-sdk/client-cloudwatch-logs": "^3.
|
|
54
|
-
"@aws-sdk/client-cognito-identity-provider": "^3.
|
|
55
|
-
"@aws-sdk/client-dynamodb": "^3.
|
|
56
|
-
"@aws-sdk/client-kms": "^3.
|
|
57
|
-
"@aws-sdk/client-s3": "^3.
|
|
58
|
-
"@aws-sdk/client-secrets-manager": "^3.
|
|
59
|
-
"@aws-sdk/client-ses": "^3.
|
|
60
|
-
"@aws-sdk/client-sns": "^3.
|
|
61
|
-
"@aws-sdk/client-sqs": "^3.
|
|
62
|
-
"@aws-sdk/client-ssm": "^3.
|
|
63
|
-
"@aws-sdk/s3-request-presigner": "^3.
|
|
64
|
-
"@aws-sdk/util-dynamodb": "^3.996.
|
|
52
|
+
"@aws-sdk/client-cloudwatch": "^3.1071.0",
|
|
53
|
+
"@aws-sdk/client-cloudwatch-logs": "^3.1071.0",
|
|
54
|
+
"@aws-sdk/client-cognito-identity-provider": "^3.1071.0",
|
|
55
|
+
"@aws-sdk/client-dynamodb": "^3.1071.0",
|
|
56
|
+
"@aws-sdk/client-kms": "^3.1071.0",
|
|
57
|
+
"@aws-sdk/client-s3": "^3.1071.0",
|
|
58
|
+
"@aws-sdk/client-secrets-manager": "^3.1071.0",
|
|
59
|
+
"@aws-sdk/client-ses": "^3.1071.0",
|
|
60
|
+
"@aws-sdk/client-sns": "^3.1071.0",
|
|
61
|
+
"@aws-sdk/client-sqs": "^3.1071.0",
|
|
62
|
+
"@aws-sdk/client-ssm": "^3.1071.0",
|
|
63
|
+
"@aws-sdk/s3-request-presigner": "^3.1071.0",
|
|
64
|
+
"@aws-sdk/util-dynamodb": "^3.996.5",
|
|
65
65
|
"@de-otio/saas-foundation": "^0.3.0",
|
|
66
66
|
"@de-otio/trellis-extension-api": "^0.3.0",
|
|
67
|
-
"@de-otio/vestibulum": "^0.3.
|
|
67
|
+
"@de-otio/vestibulum": "^0.3.2",
|
|
68
68
|
"@fedify/fedify": "2.2.5",
|
|
69
69
|
"@prisma/adapter-pg": "^7.8.0",
|
|
70
70
|
"@prisma/client": "^7.8.0",
|
|
@@ -78,21 +78,22 @@
|
|
|
78
78
|
"zod": "^4.4.3"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
|
-
"@aws-sdk/client-bedrock-agent-runtime": "^3.
|
|
82
|
-
"@aws-sdk/client-cost-explorer": "^3.
|
|
83
|
-
"@aws-sdk/client-ecs": "^3.
|
|
84
|
-
"@aws-sdk/client-lambda": "^3.
|
|
81
|
+
"@aws-sdk/client-bedrock-agent-runtime": "^3.1071.0",
|
|
82
|
+
"@aws-sdk/client-cost-explorer": "^3.1071.0",
|
|
83
|
+
"@aws-sdk/client-ecs": "^3.1071.0",
|
|
84
|
+
"@aws-sdk/client-lambda": "^3.1071.0",
|
|
85
85
|
"@types/aws-lambda": "^8.10.162",
|
|
86
86
|
"@types/js-yaml": "^4.0.9",
|
|
87
87
|
"@types/node": "^25.9.3",
|
|
88
88
|
"@types/pg": "^8.18.0",
|
|
89
|
-
"@types/sharp": "^0.
|
|
90
|
-
"@vitest/coverage-v8": "^4.1.
|
|
89
|
+
"@types/sharp": "^0.32.0",
|
|
90
|
+
"@vitest/coverage-v8": "^4.1.9",
|
|
91
91
|
"@vitest/ui": "^4.1.8",
|
|
92
92
|
"aws-sdk-client-mock": "^4.1.0",
|
|
93
93
|
"esbuild": "^0.28.1",
|
|
94
|
+
"fast-check": "^3.23.2",
|
|
94
95
|
"form-data": "^4.0.1",
|
|
95
|
-
"mailparser": "^3.9.
|
|
96
|
+
"mailparser": "^3.9.11",
|
|
96
97
|
"prettier": "^3.8.4",
|
|
97
98
|
"prisma": "^7.8.0",
|
|
98
99
|
"tsx": "^4.22.4",
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
-- WS5 — encrypted-settings state-sync. Server-blind per-namespace encrypted
|
|
2
|
+
-- setting blob with optimistic-concurrency version + cascade on user delete.
|
|
3
|
+
|
|
4
|
+
-- CreateTable
|
|
5
|
+
CREATE TABLE "encrypted_user_settings" (
|
|
6
|
+
"id" TEXT NOT NULL,
|
|
7
|
+
"user_id" TEXT NOT NULL,
|
|
8
|
+
"namespace" TEXT NOT NULL,
|
|
9
|
+
"ciphertext" TEXT NOT NULL,
|
|
10
|
+
"version" INTEGER NOT NULL DEFAULT 1,
|
|
11
|
+
"updated_at" TIMESTAMP(3) NOT NULL,
|
|
12
|
+
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
13
|
+
|
|
14
|
+
CONSTRAINT "encrypted_user_settings_pkey" PRIMARY KEY ("id")
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
-- CreateIndex
|
|
18
|
+
CREATE INDEX "encrypted_user_settings_user_id_idx" ON "encrypted_user_settings"("user_id");
|
|
19
|
+
|
|
20
|
+
-- CreateIndex
|
|
21
|
+
CREATE UNIQUE INDEX "encrypted_user_settings_user_id_namespace_key" ON "encrypted_user_settings"("user_id", "namespace");
|
|
22
|
+
|
|
23
|
+
-- AddForeignKey
|
|
24
|
+
ALTER TABLE "encrypted_user_settings" ADD CONSTRAINT "encrypted_user_settings_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
-- Track D — block-list store for the realtime delivery FLOOR. Directed,
|
|
2
|
+
-- tenant-scoped block edges (blocker has blocked blocked). Consulted by
|
|
3
|
+
-- BlockStore.isBlocked to drop a blocked sender's wakeup at the floor.
|
|
4
|
+
|
|
5
|
+
-- CreateTable
|
|
6
|
+
CREATE TABLE "blocked_users" (
|
|
7
|
+
"id" TEXT NOT NULL,
|
|
8
|
+
"tenant_id" TEXT NOT NULL,
|
|
9
|
+
"blocker_id" TEXT NOT NULL,
|
|
10
|
+
"blocked_id" TEXT NOT NULL,
|
|
11
|
+
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
12
|
+
|
|
13
|
+
CONSTRAINT "blocked_users_pkey" PRIMARY KEY ("id")
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
-- CreateIndex
|
|
17
|
+
CREATE INDEX "blocked_users_tenant_id_blocker_id_idx" ON "blocked_users"("tenant_id", "blocker_id");
|
|
18
|
+
|
|
19
|
+
-- CreateIndex
|
|
20
|
+
CREATE UNIQUE INDEX "blocked_users_tenant_id_blocker_id_blocked_id_key" ON "blocked_users"("tenant_id", "blocker_id", "blocked_id");
|
|
21
|
+
|
|
22
|
+
-- AddForeignKey
|
|
23
|
+
ALTER TABLE "blocked_users" ADD CONSTRAINT "blocked_users_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
24
|
+
|
|
25
|
+
-- AddForeignKey
|
|
26
|
+
ALTER TABLE "blocked_users" ADD CONSTRAINT "blocked_users_blocker_id_fkey" FOREIGN KEY ("blocker_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
27
|
+
|
|
28
|
+
-- AddForeignKey
|
|
29
|
+
ALTER TABLE "blocked_users" ADD CONSTRAINT "blocked_users_blocked_id_fkey" FOREIGN KEY ("blocked_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
package/prisma/migrations/20260625000000_media_tenant_scope_and_moderation_status/migration.sql
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
-- T8: MediaFile tenant-scope reclassification + moderation status (D18, D13).
|
|
2
|
+
--
|
|
3
|
+
-- Summary of changes:
|
|
4
|
+
-- + tenantId (NOT NULL) — every object belongs to exactly one tenant
|
|
5
|
+
-- + ModerationStatus enum — PENDING | APPROVED | REVIEW | QUARANTINED | REJECTED
|
|
6
|
+
-- + moderationStatus column defaulting to PENDING (fail-closed)
|
|
7
|
+
-- - contentHash @unique (bare global unique) dropped
|
|
8
|
+
-- + @@unique([tenantId, contentHash]) — within-tenant dedup replaces global dedup
|
|
9
|
+
-- - gpsLatitude / gpsLongitude columns dropped (data-minimization, D13)
|
|
10
|
+
-- - @@index([gpsLatitude, gpsLongitude]) dropped with the columns
|
|
11
|
+
-- ~ metadataVisible default changed true -> false (D13 P0a)
|
|
12
|
+
-- + @@index([moderationStatus]) — moderation-queue queries
|
|
13
|
+
-- + @@index([tenantId, contentHash]) — tenant-scoped CAS serve path
|
|
14
|
+
--
|
|
15
|
+
-- Greenfield: Skybber is not live. No backfill. Dev media wiped + re-seeded
|
|
16
|
+
-- (human checkpoint per README §9 item 1 before apply).
|
|
17
|
+
|
|
18
|
+
-- CreateEnum
|
|
19
|
+
CREATE TYPE "ModerationStatus" AS ENUM ('PENDING', 'APPROVED', 'REVIEW', 'QUARANTINED', 'REJECTED');
|
|
20
|
+
|
|
21
|
+
-- AlterTable: add tenantId (NOT NULL), moderationStatus; drop GPS columns;
|
|
22
|
+
-- flip metadataVisible default.
|
|
23
|
+
ALTER TABLE "media_files"
|
|
24
|
+
ADD COLUMN "tenant_id" TEXT NOT NULL,
|
|
25
|
+
ADD COLUMN "moderation_status" "ModerationStatus" NOT NULL DEFAULT 'PENDING',
|
|
26
|
+
DROP COLUMN "gps_latitude",
|
|
27
|
+
DROP COLUMN "gps_longitude",
|
|
28
|
+
ALTER COLUMN "metadata_visible" SET DEFAULT false;
|
|
29
|
+
|
|
30
|
+
-- DropIndex: remove bare global unique on content_hash
|
|
31
|
+
DROP INDEX "media_files_content_hash_key";
|
|
32
|
+
|
|
33
|
+
-- DropIndex: the GPS composite index is already removed by the DROP COLUMN
|
|
34
|
+
-- above (Postgres cascades indexes that depend on a dropped column). IF EXISTS
|
|
35
|
+
-- makes this explicit drop a safe no-op whether or not the index is still
|
|
36
|
+
-- present / was ever named this in the migration history.
|
|
37
|
+
DROP INDEX IF EXISTS "media_files_gps_latitude_gps_longitude_idx";
|
|
38
|
+
|
|
39
|
+
-- CreateIndex: within-tenant dedup (replaces bare @unique)
|
|
40
|
+
CREATE UNIQUE INDEX "media_files_tenant_id_content_hash_key" ON "media_files"("tenant_id", "content_hash");
|
|
41
|
+
|
|
42
|
+
-- CreateIndex: moderation queue lookups
|
|
43
|
+
CREATE INDEX "media_files_moderation_status_idx" ON "media_files"("moderation_status");
|
|
44
|
+
|
|
45
|
+
-- CreateIndex: tenant-scoped CAS serve path
|
|
46
|
+
CREATE INDEX "media_files_tenant_id_content_hash_idx" ON "media_files"("tenant_id", "content_hash");
|
|
47
|
+
|
|
48
|
+
-- AddForeignKey: tenantId references tenants
|
|
49
|
+
ALTER TABLE "media_files" ADD CONSTRAINT "media_files_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|