@endora-commerce/mod-assets-library 0.0.0-stage → 0.100.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/LICENSE +21 -0
- package/README.md +61 -2
- package/dist/admin/api/assets-library-client.d.ts +29 -0
- package/dist/admin/api/assets-library-client.d.ts.map +1 -0
- package/dist/admin/api/assets-library-client.js +70 -0
- package/dist/admin/api/assets-library-client.js.map +1 -0
- package/dist/admin/components/AssetDetailDrawer.d.ts +8 -0
- package/dist/admin/components/AssetDetailDrawer.d.ts.map +1 -0
- package/dist/admin/components/AssetDetailDrawer.js +96 -0
- package/dist/admin/components/AssetDetailDrawer.js.map +1 -0
- package/dist/admin/components/FolderTree.d.ts +13 -0
- package/dist/admin/components/FolderTree.d.ts.map +1 -0
- package/dist/admin/components/FolderTree.js +71 -0
- package/dist/admin/components/FolderTree.js.map +1 -0
- package/dist/admin/index.d.ts +44 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +46 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/LibraryPage.d.ts +9 -0
- package/dist/admin/pages/LibraryPage.d.ts.map +1 -0
- package/dist/admin/pages/LibraryPage.js +163 -0
- package/dist/admin/pages/LibraryPage.js.map +1 -0
- package/dist/backend/entities/asset-folder.entity.d.ts +16 -0
- package/dist/backend/entities/asset-folder.entity.d.ts.map +1 -0
- package/dist/backend/entities/asset-folder.entity.js +57 -0
- package/dist/backend/entities/asset-folder.entity.js.map +1 -0
- package/dist/backend/entities/asset.entity.d.ts +38 -0
- package/dist/backend/entities/asset.entity.d.ts.map +1 -0
- package/dist/backend/entities/asset.entity.js +128 -0
- package/dist/backend/entities/asset.entity.js.map +1 -0
- package/dist/backend/index.d.ts +57 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +150 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/jobs/hard-delete-asset.job.d.ts +18 -0
- package/dist/backend/jobs/hard-delete-asset.job.d.ts.map +1 -0
- package/dist/backend/jobs/hard-delete-asset.job.js +91 -0
- package/dist/backend/jobs/hard-delete-asset.job.js.map +1 -0
- package/dist/backend/plugin.d.ts +47 -0
- package/dist/backend/plugin.d.ts.map +1 -0
- package/dist/backend/plugin.js +96 -0
- package/dist/backend/plugin.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +15 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +189 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.public.d.ts +14 -0
- package/dist/backend/routes.public.d.ts.map +1 -0
- package/dist/backend/routes.public.js +99 -0
- package/dist/backend/routes.public.js.map +1 -0
- package/dist/backend/services/asset-read-port.d.ts +103 -0
- package/dist/backend/services/asset-read-port.d.ts.map +1 -0
- package/dist/backend/services/asset-read-port.js +167 -0
- package/dist/backend/services/asset-read-port.js.map +1 -0
- package/dist/backend/services/assets-library.service.d.ts +76 -0
- package/dist/backend/services/assets-library.service.d.ts.map +1 -0
- package/dist/backend/services/assets-library.service.js +261 -0
- package/dist/backend/services/assets-library.service.js.map +1 -0
- package/dist/backend/services/cms-body-resolver.d.ts +6 -0
- package/dist/backend/services/cms-body-resolver.d.ts.map +1 -0
- package/dist/backend/services/cms-body-resolver.js +44 -0
- package/dist/backend/services/cms-body-resolver.js.map +1 -0
- package/dist/backend/services/folders.service.d.ts +29 -0
- package/dist/backend/services/folders.service.d.ts.map +1 -0
- package/dist/backend/services/folders.service.js +231 -0
- package/dist/backend/services/folders.service.js.map +1 -0
- package/dist/backend/services/hmac.d.ts +28 -0
- package/dist/backend/services/hmac.d.ts.map +1 -0
- package/dist/backend/services/hmac.js +50 -0
- package/dist/backend/services/hmac.js.map +1 -0
- package/dist/backend/services/reference-registry.d.ts +36 -0
- package/dist/backend/services/reference-registry.d.ts.map +1 -0
- package/dist/backend/services/reference-registry.js +63 -0
- package/dist/backend/services/reference-registry.js.map +1 -0
- package/dist/backend/services/storage/adapter-registry.d.ts +77 -0
- package/dist/backend/services/storage/adapter-registry.d.ts.map +1 -0
- package/dist/backend/services/storage/adapter-registry.js +95 -0
- package/dist/backend/services/storage/adapter-registry.js.map +1 -0
- package/dist/backend/services/storage/errors.d.ts +18 -0
- package/dist/backend/services/storage/errors.d.ts.map +1 -0
- package/dist/backend/services/storage/errors.js +30 -0
- package/dist/backend/services/storage/errors.js.map +1 -0
- package/dist/backend/services/storage/gcs-adapter.d.ts +42 -0
- package/dist/backend/services/storage/gcs-adapter.d.ts.map +1 -0
- package/dist/backend/services/storage/gcs-adapter.js +106 -0
- package/dist/backend/services/storage/gcs-adapter.js.map +1 -0
- package/dist/backend/services/storage/legacy-resolver.d.ts +15 -0
- package/dist/backend/services/storage/legacy-resolver.d.ts.map +1 -0
- package/dist/backend/services/storage/legacy-resolver.js +32 -0
- package/dist/backend/services/storage/legacy-resolver.js.map +1 -0
- package/dist/backend/services/storage/local-fs-adapter.d.ts +59 -0
- package/dist/backend/services/storage/local-fs-adapter.d.ts.map +1 -0
- package/dist/backend/services/storage/local-fs-adapter.js +153 -0
- package/dist/backend/services/storage/local-fs-adapter.js.map +1 -0
- package/dist/backend/services/storage/locator.d.ts +5 -0
- package/dist/backend/services/storage/locator.d.ts.map +1 -0
- package/dist/backend/services/storage/locator.js +16 -0
- package/dist/backend/services/storage/locator.js.map +1 -0
- package/dist/backend/services/storage/object-storage-port.d.ts +46 -0
- package/dist/backend/services/storage/object-storage-port.d.ts.map +1 -0
- package/dist/backend/services/storage/object-storage-port.js +78 -0
- package/dist/backend/services/storage/object-storage-port.js.map +1 -0
- package/dist/backend/services/storage/public-url-base.d.ts +50 -0
- package/dist/backend/services/storage/public-url-base.d.ts.map +1 -0
- package/dist/backend/services/storage/public-url-base.js +70 -0
- package/dist/backend/services/storage/public-url-base.js.map +1 -0
- package/dist/backend/services/storage/s3-adapter.d.ts +50 -0
- package/dist/backend/services/storage/s3-adapter.d.ts.map +1 -0
- package/dist/backend/services/storage/s3-adapter.js +110 -0
- package/dist/backend/services/storage/s3-adapter.js.map +1 -0
- package/dist/backend/services/storage/settings-view.d.ts +4 -0
- package/dist/backend/services/storage/settings-view.d.ts.map +1 -0
- package/dist/backend/services/storage/settings-view.js +73 -0
- package/dist/backend/services/storage/settings-view.js.map +1 -0
- package/dist/backend/services/storage/storage-adapter.d.ts +62 -0
- package/dist/backend/services/storage/storage-adapter.d.ts.map +1 -0
- package/dist/backend/services/storage/storage-adapter.js +10 -0
- package/dist/backend/services/storage/storage-adapter.js.map +1 -0
- package/dist/backend/services/upload-pipeline.d.ts +41 -0
- package/dist/backend/services/upload-pipeline.d.ts.map +1 -0
- package/dist/backend/services/upload-pipeline.js +231 -0
- package/dist/backend/services/upload-pipeline.js.map +1 -0
- package/dist/manifest.d.ts +185 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +229 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260505T102206_assets_library_init.d.ts +35 -0
- package/dist/migrations/20260505T102206_assets_library_init.d.ts.map +1 -0
- package/dist/migrations/20260505T102206_assets_library_init.js +132 -0
- package/dist/migrations/20260505T102206_assets_library_init.js.map +1 -0
- package/dist/migrations/index.d.ts +27 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +29 -0
- package/dist/migrations/index.js.map +1 -0
- package/docs/assets-library/index.md +125 -0
- package/i18n/en.json +54 -0
- package/i18n/pl.json +54 -0
- package/package.json +99 -3
- package/tailwind.css +14 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/** Module-lifecycle manifest (feature 018). */
|
|
2
|
+
export declare const manifest: {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
version: string;
|
|
6
|
+
dependencies: string[];
|
|
7
|
+
description?: string | undefined;
|
|
8
|
+
acknowledgedDependencies?: {
|
|
9
|
+
moduleId: string;
|
|
10
|
+
port: string;
|
|
11
|
+
reason: string;
|
|
12
|
+
}[] | undefined;
|
|
13
|
+
nonBindingDependencies?: {
|
|
14
|
+
moduleId: string;
|
|
15
|
+
name: string;
|
|
16
|
+
kind: "contributes-to" | "degrades-without" | "refuses-without";
|
|
17
|
+
reason: string;
|
|
18
|
+
whenAbsent?: string | undefined;
|
|
19
|
+
}[] | undefined;
|
|
20
|
+
activation?: {
|
|
21
|
+
settingCode: string;
|
|
22
|
+
default: boolean;
|
|
23
|
+
} | {
|
|
24
|
+
nonDeactivatable: true;
|
|
25
|
+
reason: string;
|
|
26
|
+
} | undefined;
|
|
27
|
+
settings?: {
|
|
28
|
+
moduleCode: string;
|
|
29
|
+
groups: {
|
|
30
|
+
code: string;
|
|
31
|
+
name: string;
|
|
32
|
+
salesChannelCodes?: string[] | undefined;
|
|
33
|
+
isSystemProtected?: boolean | undefined;
|
|
34
|
+
}[];
|
|
35
|
+
settings: {
|
|
36
|
+
code: string;
|
|
37
|
+
name: string;
|
|
38
|
+
valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
|
|
39
|
+
defaultValue: unknown;
|
|
40
|
+
description?: string | undefined;
|
|
41
|
+
groupCode?: string | undefined;
|
|
42
|
+
previousDefaultValues?: unknown[] | undefined;
|
|
43
|
+
salesChannelCodes?: string[] | undefined;
|
|
44
|
+
enumOptions?: string[] | undefined;
|
|
45
|
+
configurationType?: string | undefined;
|
|
46
|
+
hidden?: boolean | undefined;
|
|
47
|
+
}[];
|
|
48
|
+
} | undefined;
|
|
49
|
+
i18n?: {
|
|
50
|
+
bundlesDir: string;
|
|
51
|
+
} | undefined;
|
|
52
|
+
docs?: false | {
|
|
53
|
+
dir: string;
|
|
54
|
+
} | undefined;
|
|
55
|
+
demo?: false | {
|
|
56
|
+
summary: string;
|
|
57
|
+
seed: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoSeedResult>;
|
|
58
|
+
reset: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoResetResult>;
|
|
59
|
+
after?: readonly string[] | undefined;
|
|
60
|
+
package?: string | undefined;
|
|
61
|
+
} | undefined;
|
|
62
|
+
actions?: {
|
|
63
|
+
id: string;
|
|
64
|
+
labelKey: string;
|
|
65
|
+
icon: "Plus" | "Sparkles" | "Settings" | "Search" | "Boxes" | "Layers" | "Menu" | "PlusCircle" | "PlusSquare" | "FilePlus" | "FolderPlus" | "Upload" | "FileUp" | "CloudUpload" | "Download" | "FileDown" | "FileText" | "BookOpen" | "Rss" | "Package" | "Tag" | "ShoppingCart" | "Receipt" | "CreditCard" | "Users" | "UserPlus" | "Inbox" | "ListChecks" | "ClipboardList" | "Image" | "Video" | "LayoutDashboard" | "PanelLeft" | "KeyRound" | "ShieldCheck" | "Edit" | "Archive" | "Box" | "Truck" | "CircleDollarSign" | "Activity" | "LineChart" | "Smartphone" | "Webhook" | "Scale" | "PlugZap" | "PercentDiamond" | "Newspaper" | "Languages" | "Eraser" | "Warehouse" | "TrendingDown" | "Bell" | "PackageOpen" | "Building2" | "Store" | "ClipboardCheck";
|
|
66
|
+
targetRoute: string;
|
|
67
|
+
keywords: string[];
|
|
68
|
+
weight: number;
|
|
69
|
+
descriptionKey?: string | undefined;
|
|
70
|
+
requiredPermission?: string | undefined;
|
|
71
|
+
}[] | undefined;
|
|
72
|
+
permissions?: {
|
|
73
|
+
code: string;
|
|
74
|
+
label: string;
|
|
75
|
+
module?: string | undefined;
|
|
76
|
+
description?: string | undefined;
|
|
77
|
+
requires?: string[] | undefined;
|
|
78
|
+
}[] | undefined;
|
|
79
|
+
transactionalEmails?: {
|
|
80
|
+
code: string;
|
|
81
|
+
name: string;
|
|
82
|
+
variables: {
|
|
83
|
+
key: string;
|
|
84
|
+
label: string;
|
|
85
|
+
sampleValue?: string | undefined;
|
|
86
|
+
description?: string | undefined;
|
|
87
|
+
}[];
|
|
88
|
+
description?: string | undefined;
|
|
89
|
+
group?: string | undefined;
|
|
90
|
+
}[] | undefined;
|
|
91
|
+
capabilities?: string[] | undefined;
|
|
92
|
+
exclusiveCapabilities?: {
|
|
93
|
+
key: string;
|
|
94
|
+
errorCode: string;
|
|
95
|
+
}[] | undefined;
|
|
96
|
+
errorCodes?: {
|
|
97
|
+
code: string;
|
|
98
|
+
tokens?: string[] | undefined;
|
|
99
|
+
}[] | undefined;
|
|
100
|
+
blocks?: {
|
|
101
|
+
name: string;
|
|
102
|
+
labelKey: string;
|
|
103
|
+
category: string;
|
|
104
|
+
contexts: ("invoice" | "email" | "cms" | "newsletter")[];
|
|
105
|
+
fields: Record<string, {
|
|
106
|
+
type: "number" | "object" | "array" | "text" | "textarea" | "select" | "radio" | "external" | "uuid" | "richtext";
|
|
107
|
+
label?: string | undefined;
|
|
108
|
+
required?: boolean | undefined;
|
|
109
|
+
options?: {
|
|
110
|
+
label: string;
|
|
111
|
+
value: string | number;
|
|
112
|
+
}[] | undefined;
|
|
113
|
+
refKind?: string | undefined;
|
|
114
|
+
}>;
|
|
115
|
+
descriptionKey?: string | undefined;
|
|
116
|
+
defaultProps?: Record<string, unknown> | undefined;
|
|
117
|
+
responsiveFields?: string[] | undefined;
|
|
118
|
+
previewIcon?: string | undefined;
|
|
119
|
+
weight?: number | undefined;
|
|
120
|
+
}[] | undefined;
|
|
121
|
+
blockCategories?: {
|
|
122
|
+
key: string;
|
|
123
|
+
titleKey: string;
|
|
124
|
+
contexts: ("invoice" | "email" | "cms" | "newsletter")[];
|
|
125
|
+
weight?: number | undefined;
|
|
126
|
+
visible?: boolean | undefined;
|
|
127
|
+
}[] | undefined;
|
|
128
|
+
env?: {
|
|
129
|
+
name: string;
|
|
130
|
+
describes: {
|
|
131
|
+
en: string;
|
|
132
|
+
pl: string;
|
|
133
|
+
};
|
|
134
|
+
requirement: {
|
|
135
|
+
kind: "required";
|
|
136
|
+
} | {
|
|
137
|
+
kind: "requiredWhen";
|
|
138
|
+
input: string;
|
|
139
|
+
equals: string;
|
|
140
|
+
} | {
|
|
141
|
+
kind: "optional";
|
|
142
|
+
without: {
|
|
143
|
+
en: string;
|
|
144
|
+
pl: string;
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
secret: boolean;
|
|
148
|
+
generable: boolean;
|
|
149
|
+
owner: {
|
|
150
|
+
kind: "platform";
|
|
151
|
+
} | {
|
|
152
|
+
kind: "application";
|
|
153
|
+
application: "admin" | "backend" | "storefront";
|
|
154
|
+
} | {
|
|
155
|
+
kind: "module";
|
|
156
|
+
moduleId: string;
|
|
157
|
+
};
|
|
158
|
+
consumers: ("admin" | "backend" | "storefront")[];
|
|
159
|
+
addressOf: "admin" | "backend" | "storefront" | null;
|
|
160
|
+
}[] | undefined;
|
|
161
|
+
};
|
|
162
|
+
/** Legacy export retained for backward compatibility. */
|
|
163
|
+
export declare const assetsLibraryManifest: {
|
|
164
|
+
moduleCode: string;
|
|
165
|
+
groups: {
|
|
166
|
+
code: string;
|
|
167
|
+
name: string;
|
|
168
|
+
salesChannelCodes?: string[] | undefined;
|
|
169
|
+
isSystemProtected?: boolean | undefined;
|
|
170
|
+
}[];
|
|
171
|
+
settings: {
|
|
172
|
+
code: string;
|
|
173
|
+
name: string;
|
|
174
|
+
valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
|
|
175
|
+
defaultValue: unknown;
|
|
176
|
+
description?: string | undefined;
|
|
177
|
+
groupCode?: string | undefined;
|
|
178
|
+
previousDefaultValues?: unknown[] | undefined;
|
|
179
|
+
salesChannelCodes?: string[] | undefined;
|
|
180
|
+
enumOptions?: string[] | undefined;
|
|
181
|
+
configurationType?: string | undefined;
|
|
182
|
+
hidden?: boolean | undefined;
|
|
183
|
+
}[];
|
|
184
|
+
};
|
|
185
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AA2GA,+CAA+C;AAC/C,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkInB,CAAC;AAEH,yDAAyD;AACzD,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;CAAW,CAAC"}
|
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { defineModuleManifest, defineModuleSettingsManifest, } from '@endora-commerce/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* Built-in manifest for the Assets Library — feature 013 / research.md R13.
|
|
4
|
+
* The Settings reconciler picks this up at boot and creates one DB row per
|
|
5
|
+
* setting under the `storage` group. The active adapter and per-adapter
|
|
6
|
+
* configuration are read by `AdapterRegistry` on demand.
|
|
7
|
+
*
|
|
8
|
+
* Limitation (v1): the Settings schema currently lacks a `redactedRead` flag,
|
|
9
|
+
* so credentials (`assets.s3.secretAccessKey`, `assets.gcs.serviceAccountJson`)
|
|
10
|
+
* are stored plaintext. FR-022 asked for masking parity with existing
|
|
11
|
+
* sensitive settings — there is no existing mechanism to inherit. A follow-up
|
|
12
|
+
* change to spec 004's manifest schema will add `redactedRead`; until then,
|
|
13
|
+
* operators must restrict admin Settings access to trusted users.
|
|
14
|
+
*/
|
|
15
|
+
const settings = defineModuleSettingsManifest({
|
|
16
|
+
moduleCode: 'assets_library',
|
|
17
|
+
groups: [
|
|
18
|
+
{
|
|
19
|
+
code: 'storage',
|
|
20
|
+
name: 'Storage',
|
|
21
|
+
// Empty salesChannelCodes ⇒ applies to every channel; assets are platform-global.
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
settings: [
|
|
25
|
+
// — adapter selection
|
|
26
|
+
{
|
|
27
|
+
code: 'assets.storage.adapter',
|
|
28
|
+
name: 'Active storage adapter',
|
|
29
|
+
groupCode: 'storage',
|
|
30
|
+
valueType: 'string',
|
|
31
|
+
defaultValue: 'local',
|
|
32
|
+
description: 'One of {local, s3, gcs}. Switching affects new uploads only.',
|
|
33
|
+
},
|
|
34
|
+
// — Local FS
|
|
35
|
+
{
|
|
36
|
+
code: 'assets.local.base_dir',
|
|
37
|
+
name: 'Local FS — base directory',
|
|
38
|
+
groupCode: 'storage',
|
|
39
|
+
valueType: 'string',
|
|
40
|
+
defaultValue: 'var/assets',
|
|
41
|
+
},
|
|
42
|
+
// — S3
|
|
43
|
+
{ code: 'assets.s3.bucket', name: 'S3 — bucket', groupCode: 'storage', valueType: 'string', defaultValue: '' },
|
|
44
|
+
{ code: 'assets.s3.region', name: 'S3 — region', groupCode: 'storage', valueType: 'string', defaultValue: '' },
|
|
45
|
+
{ code: 'assets.s3.access_key_id', name: 'S3 — access key id', groupCode: 'storage', valueType: 'string', defaultValue: '' },
|
|
46
|
+
{ code: 'assets.s3.secret_access_key', name: 'S3 — secret access key (sensitive)', groupCode: 'storage', valueType: 'string', defaultValue: '' },
|
|
47
|
+
{ code: 'assets.s3.endpoint', name: 'S3 — custom endpoint (optional)', groupCode: 'storage', valueType: 'string', defaultValue: '' },
|
|
48
|
+
{ code: 'assets.s3.prefix', name: 'S3 — key prefix (optional)', groupCode: 'storage', valueType: 'string', defaultValue: '' },
|
|
49
|
+
{ code: 'assets.s3.public_base_url', name: 'S3 — public CDN base URL (optional)', groupCode: 'storage', valueType: 'string', defaultValue: '' },
|
|
50
|
+
// — GCS
|
|
51
|
+
{ code: 'assets.gcs.bucket', name: 'GCS — bucket', groupCode: 'storage', valueType: 'string', defaultValue: '' },
|
|
52
|
+
{ code: 'assets.gcs.service_account_json', name: 'GCS — service-account JSON (sensitive)', groupCode: 'storage', valueType: 'string', defaultValue: '' },
|
|
53
|
+
{ code: 'assets.gcs.prefix', name: 'GCS — object prefix (optional)', groupCode: 'storage', valueType: 'string', defaultValue: '' },
|
|
54
|
+
{ code: 'assets.gcs.public_base_url', name: 'GCS — public CDN base URL (optional)', groupCode: 'storage', valueType: 'string', defaultValue: '' },
|
|
55
|
+
// — upload constraints
|
|
56
|
+
{
|
|
57
|
+
code: 'assets.allowed_file_types',
|
|
58
|
+
name: 'Allowed file types (extensions/MIME; * = any)',
|
|
59
|
+
groupCode: 'storage',
|
|
60
|
+
valueType: 'string_list',
|
|
61
|
+
defaultValue: ['*'],
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
code: 'assets.max_file_size_mb',
|
|
65
|
+
name: 'Max file size (MB; 0 = no cap)',
|
|
66
|
+
groupCode: 'storage',
|
|
67
|
+
valueType: 'number',
|
|
68
|
+
defaultValue: 0,
|
|
69
|
+
},
|
|
70
|
+
// — visibility / lifecycle
|
|
71
|
+
{
|
|
72
|
+
code: 'assets.private_url_ttl_sec',
|
|
73
|
+
name: 'Private signed-URL TTL (seconds)',
|
|
74
|
+
groupCode: 'storage',
|
|
75
|
+
valueType: 'number',
|
|
76
|
+
defaultValue: 300,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
code: 'assets.soft_delete_retention_days',
|
|
80
|
+
name: 'Soft-delete retention before hard-delete (days)',
|
|
81
|
+
groupCode: 'storage',
|
|
82
|
+
valueType: 'number',
|
|
83
|
+
defaultValue: 30,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
code: 'assets.local.public_url_base',
|
|
87
|
+
name: 'Local FS — public URL base (no trailing slash)',
|
|
88
|
+
groupCode: 'storage',
|
|
89
|
+
valueType: 'string',
|
|
90
|
+
defaultValue: '',
|
|
91
|
+
description: 'Public-facing URL prefix for /assets/file/:assetId — a CDN or the public hostname a ' +
|
|
92
|
+
'reverse proxy serves. Leave blank to build every URL on this deployment\'s public API ' +
|
|
93
|
+
'origin (PUBLIC_API_BASE_URL).',
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
});
|
|
97
|
+
/** Module-lifecycle manifest (feature 018). */
|
|
98
|
+
export const manifest = defineModuleManifest({
|
|
99
|
+
id: 'assets_library',
|
|
100
|
+
name: 'Assets Library',
|
|
101
|
+
description: 'Storage adapter registry (local / S3 / GCS), asset upload, and reference tracking.',
|
|
102
|
+
version: '1.0.0',
|
|
103
|
+
/**
|
|
104
|
+
* What this module needs from the environment (`specs/117-instance-bring-up/`
|
|
105
|
+
* FR-002). Only what it **owns**: its reads of platform-owned names are
|
|
106
|
+
* satisfied by `packages/platform/src/env/index.ts`.
|
|
107
|
+
*
|
|
108
|
+
* Why each of these is not a Setting is its entry in
|
|
109
|
+
* `backend/scripts/ledgers/module-environment-inputs/assets_library.ts`.
|
|
110
|
+
*/
|
|
111
|
+
env: [
|
|
112
|
+
{
|
|
113
|
+
name: 'ASSETS_LIBRARY_HMAC_KEY',
|
|
114
|
+
describes: {
|
|
115
|
+
en: 'The signing key that makes an asset download link valid, and unforgeable by anyone who has not got it.',
|
|
116
|
+
pl: 'Klucz podpisujący, dzięki któremu link do pobrania zasobu jest ważny i nie do podrobienia bez jego znajomości.',
|
|
117
|
+
},
|
|
118
|
+
requirement: { kind: 'required' },
|
|
119
|
+
secret: true,
|
|
120
|
+
generable: true,
|
|
121
|
+
owner: { kind: 'module', moduleId: 'assets_library' },
|
|
122
|
+
consumers: ['backend'],
|
|
123
|
+
addressOf: null,
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
// `auth` owns the `requireAdmin` port the admin routes are gated by; the
|
|
127
|
+
// edge became real with the conversion (feature 072, T092), which also made
|
|
128
|
+
// the gate non-optional.
|
|
129
|
+
dependencies: ['auth'],
|
|
130
|
+
settings,
|
|
131
|
+
// Feature 074 (Constitution XVII), test C3 — platform primitive. This module
|
|
132
|
+
// had no activation declaration at all, which resolved as "always activated"
|
|
133
|
+
// and read as an omission. Media storage carries no independent business
|
|
134
|
+
// decision: product images, CMS media and e-mail assets all resolve here, and
|
|
135
|
+
// "we do not want files" is not something a client chooses — it is something
|
|
136
|
+
// a deployment does by mistake.
|
|
137
|
+
/**
|
|
138
|
+
* Nothing to demonstrate of its own (feature 113, T224 — contract §1.2).
|
|
139
|
+
*
|
|
140
|
+
* The demo shop does hold ~500 assets, and not one of them is this module's
|
|
141
|
+
* demo data: every one is minted inside a loop over `catalog`'s products and
|
|
142
|
+
* its id handed straight to `product_assets`, `gallery_items`,
|
|
143
|
+
* `gallery_item_labels` or `product_attachments`. An asset with no product to
|
|
144
|
+
* belong to would demonstrate nothing, and an asset that has one is two
|
|
145
|
+
* modules' rows in one statement — a composition step (§5.1), and one whose
|
|
146
|
+
* filename and generated image are derived from the product it hangs off.
|
|
147
|
+
*
|
|
148
|
+
* `false` rather than absent, because the two are different states: this is a
|
|
149
|
+
* decision that the module owes nothing, not a module nobody has looked at.
|
|
150
|
+
*/
|
|
151
|
+
demo: false,
|
|
152
|
+
activation: {
|
|
153
|
+
nonDeactivatable: true,
|
|
154
|
+
reason: 'Media storage. Product images, CMS media and e-mail assets all resolve here, and "we ' +
|
|
155
|
+
'do not want files" is not a business decision.',
|
|
156
|
+
},
|
|
157
|
+
/**
|
|
158
|
+
* The fifteen error codes this module owns — feature 090 Phase 3
|
|
159
|
+
* (`specs/090-module-owned-error-codes/contracts/error-code-declaration.md`
|
|
160
|
+
* §1.1). This is where each sentence is looked up from: `errors.<CODE>` in
|
|
161
|
+
* this module's own `i18n/{en,pl}.json`, which holds all fifteen in both
|
|
162
|
+
* languages and no sixteenth.
|
|
163
|
+
*
|
|
164
|
+
* The list is answer-preserving, not a judgement (§6.2 and §6.5): it is
|
|
165
|
+
* exactly what the prefix chain in `@endora-commerce/mod-i18n` routes here
|
|
166
|
+
* today, copied from the frozen capture at
|
|
167
|
+
* `backend/test/fixtures/error-code-routing/chain-answers.ts` rather than
|
|
168
|
+
* re-derived. Re-routing a code to a better owner is
|
|
169
|
+
* `specs/082-error-code-ownership/rulings.md` §9's remaining work and is
|
|
170
|
+
* deliberately not done here.
|
|
171
|
+
*
|
|
172
|
+
* **One code a reader will look for here and not find.**
|
|
173
|
+
* `ASSET_KIND_NOT_SUPPORTED` carries this module's own `ASSET_` prefix and is
|
|
174
|
+
* `catalog`'s: the chain's `CATALOG_MISC_ERROR_CODES` set names it thirty
|
|
175
|
+
* lines before the `ASSET_` rule is reached, so the prefix never runs on it.
|
|
176
|
+
* `catalog` declares it and holds its sentence in that module's bundle. Read
|
|
177
|
+
* from the chain's source it looks like ours; read from its answer — the only
|
|
178
|
+
* reading that matches what a client receives today — it is not.
|
|
179
|
+
*
|
|
180
|
+
* **This module's list and this module's `throw`s are two different sets, in
|
|
181
|
+
* both directions**, because ownership follows the domain noun and never the
|
|
182
|
+
* thrower (D-95.2). Going one way, `ASSET_STORAGE_MISCONFIGURED` is declared
|
|
183
|
+
* here, is written in both languages, and has **no raise site anywhere in the
|
|
184
|
+
* tree** — nothing routes to it, so its two sentences are unreachable today.
|
|
185
|
+
* That is neither this merge request's to fix nor a finding any check makes
|
|
186
|
+
* (`check:error-translations`' `unreachable` asks the other question: a
|
|
187
|
+
* sentence written where the chain does not route). Going the other way, the
|
|
188
|
+
* two codes this module raises and does not own are `INTERNAL` and
|
|
189
|
+
* `VALIDATION_FAILED`, both the platform's.
|
|
190
|
+
*
|
|
191
|
+
* **No `tokens`, and it is derived rather than assumed.** The envelope's
|
|
192
|
+
* `refusalToken` (`packages/platform/src/http/error-envelope.ts`) reads
|
|
193
|
+
* exactly one member of `details` — `code`, and only when `details` is an
|
|
194
|
+
* object — as the tail of `errors.<CODE>.<token>`. All twenty-nine raises of
|
|
195
|
+
* these codes were enumerated over `packages` and `backend/src` rather than
|
|
196
|
+
* over this package alone (runbook §5): twenty-seven pass no fourth argument
|
|
197
|
+
* at all, and the two that do — `ASSET_REFERENCED` in
|
|
198
|
+
* `assets-library.service.ts` and in `folders.service.ts` — pass the
|
|
199
|
+
* Zod-style **array** of `{ path, issue }` pairs, which `refusalToken`
|
|
200
|
+
* refuses by construction. The bundle agrees from the other direction: it
|
|
201
|
+
* holds fifteen `errors.<CODE>` keys and not one `errors.<CODE>.<token>`.
|
|
202
|
+
*/
|
|
203
|
+
errorCodes: [
|
|
204
|
+
{ code: 'ASSET_ACCESS_DENIED' },
|
|
205
|
+
{ code: 'ASSET_FILE_MISSING' },
|
|
206
|
+
{ code: 'ASSET_FOLDER_CYCLE' },
|
|
207
|
+
{ code: 'ASSET_FOLDER_NAME_CONFLICT' },
|
|
208
|
+
{ code: 'ASSET_FOLDER_NOT_EMPTY' },
|
|
209
|
+
{ code: 'ASSET_FOLDER_NOT_FOUND' },
|
|
210
|
+
{ code: 'ASSET_GONE' },
|
|
211
|
+
{ code: 'ASSET_LEGACY_LOCATOR_CANNOT_HARDEN' },
|
|
212
|
+
{ code: 'ASSET_NOT_FOUND' },
|
|
213
|
+
{ code: 'ASSET_REFERENCED' },
|
|
214
|
+
{ code: 'ASSET_STORAGE_MISCONFIGURED' },
|
|
215
|
+
{ code: 'ASSET_STORAGE_UNAVAILABLE' },
|
|
216
|
+
{ code: 'ASSET_UPLOAD_NO_FILE' },
|
|
217
|
+
{ code: 'ASSET_UPLOAD_TOO_LARGE' },
|
|
218
|
+
{ code: 'ASSET_UPLOAD_TYPE_NOT_ALLOWED' },
|
|
219
|
+
],
|
|
220
|
+
i18n: { bundlesDir: 'i18n' },
|
|
221
|
+
docs: { dir: 'docs' },
|
|
222
|
+
permissions: [
|
|
223
|
+
{ code: 'assets.read', label: 'Browse assets library' },
|
|
224
|
+
{ code: 'assets.write', label: 'Upload and manage assets' },
|
|
225
|
+
],
|
|
226
|
+
});
|
|
227
|
+
/** Legacy export retained for backward compatibility. */
|
|
228
|
+
export const assetsLibraryManifest = settings;
|
|
229
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,4BAA4B,GAC7B,MAAM,4BAA4B,CAAC;AAEpC;;;;;;;;;;;;GAYG;AACH,MAAM,QAAQ,GAAG,4BAA4B,CAAC;IAC5C,UAAU,EAAE,gBAAgB;IAC5B,MAAM,EAAE;QACN;YACE,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,SAAS;YACf,kFAAkF;SACnF;KACF;IACD,QAAQ,EAAE;QACR,sBAAsB;QACtB;YACE,IAAI,EAAE,wBAAwB;YAC9B,IAAI,EAAE,wBAAwB;YAC9B,SAAS,EAAE,SAAS;YACpB,SAAS,EAAE,QAAQ;YACnB,YAAY,EAAE,OAAO;YACrB,WAAW,EAAE,8DAA8D;SAC5E;QAED,aAAa;QACb;YACE,IAAI,EAAE,uBAAuB;YAC7B,IAAI,EAAE,2BAA2B;YACjC,SAAS,EAAE,SAAS;YACpB,SAAS,EAAE,QAAQ;YACnB,YAAY,EAAE,YAAY;SAC3B;QAED,OAAO;QACP,EAAE,IAAI,EAAE,kBAAkB,EAAY,IAAI,EAAE,aAAa,EAA6B,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE;QACnJ,EAAE,IAAI,EAAE,kBAAkB,EAAY,IAAI,EAAE,aAAa,EAA6B,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE;QACnJ,EAAE,IAAI,EAAE,yBAAyB,EAAK,IAAI,EAAE,oBAAoB,EAAsB,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE;QACnJ,EAAE,IAAI,EAAE,6BAA6B,EAAE,IAAI,EAAE,oCAAoC,EAAK,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE;QACnJ,EAAE,IAAI,EAAE,oBAAoB,EAAU,IAAI,EAAE,iCAAiC,EAAS,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE;QACnJ,EAAE,IAAI,EAAE,kBAAkB,EAAY,IAAI,EAAE,4BAA4B,EAAc,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE;QACnJ,EAAE,IAAI,EAAE,2BAA2B,EAAG,IAAI,EAAE,qCAAqC,EAAK,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE;QAEnJ,QAAQ;QACR,EAAE,IAAI,EAAE,mBAAmB,EAAiB,IAAI,EAAE,cAAc,EAA2B,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE;QACxJ,EAAE,IAAI,EAAE,iCAAiC,EAAG,IAAI,EAAE,wCAAwC,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE;QACzJ,EAAE,IAAI,EAAE,mBAAmB,EAAiB,IAAI,EAAE,gCAAgC,EAAU,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE;QACzJ,EAAE,IAAI,EAAE,4BAA4B,EAAQ,IAAI,EAAE,sCAAsC,EAAI,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE;QAEzJ,uBAAuB;QACvB;YACE,IAAI,EAAE,2BAA2B;YACjC,IAAI,EAAE,+CAA+C;YACrD,SAAS,EAAE,SAAS;YACpB,SAAS,EAAE,aAAa;YACxB,YAAY,EAAE,CAAC,GAAG,CAAC;SACpB;QACD;YACE,IAAI,EAAE,yBAAyB;YAC/B,IAAI,EAAE,gCAAgC;YACtC,SAAS,EAAE,SAAS;YACpB,SAAS,EAAE,QAAQ;YACnB,YAAY,EAAE,CAAC;SAChB;QAED,2BAA2B;QAC3B;YACE,IAAI,EAAE,4BAA4B;YAClC,IAAI,EAAE,kCAAkC;YACxC,SAAS,EAAE,SAAS;YACpB,SAAS,EAAE,QAAQ;YACnB,YAAY,EAAE,GAAG;SAClB;QACD;YACE,IAAI,EAAE,mCAAmC;YACzC,IAAI,EAAE,iDAAiD;YACvD,SAAS,EAAE,SAAS;YACpB,SAAS,EAAE,QAAQ;YACnB,YAAY,EAAE,EAAE;SACjB;QACD;YACE,IAAI,EAAE,8BAA8B;YACpC,IAAI,EAAE,gDAAgD;YACtD,SAAS,EAAE,SAAS;YACpB,SAAS,EAAE,QAAQ;YACnB,YAAY,EAAE,EAAE;YAChB,WAAW,EACT,sFAAsF;gBACtF,wFAAwF;gBACxF,+BAA+B;SAClC;KACF;CACF,CAAC,CAAC;AAEH,+CAA+C;AAC/C,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAoB,CAAC;IAC3C,EAAE,EAAE,gBAAgB;IACpB,IAAI,EAAE,gBAAgB;IACtB,WAAW,EACT,oFAAoF;IACtF,OAAO,EAAE,OAAO;IAChB;;;;;;;OAOG;IACH,GAAG,EAAE;QACH;YACE,IAAI,EAAE,yBAAyB;YAC/B,SAAS,EAAE;gBACT,EAAE,EAAE,wGAAwG;gBAC5G,EAAE,EAAE,gHAAgH;aACrH;YACD,WAAW,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE;YACjC,MAAM,EAAE,IAAI;YACZ,SAAS,EAAE,IAAI;YACf,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,gBAAgB,EAAE;YACrD,SAAS,EAAE,CAAC,SAAS,CAAC;YACtB,SAAS,EAAE,IAAI;SAChB;KACF;IACD,yEAAyE;IACzE,4EAA4E;IAC5E,yBAAyB;IACzB,YAAY,EAAE,CAAC,MAAM,CAAC;IACtB,QAAQ;IACR,6EAA6E;IAC7E,6EAA6E;IAC7E,yEAAyE;IACzE,8EAA8E;IAC9E,6EAA6E;IAC7E,gCAAgC;IAChC;;;;;;;;;;;;;OAaG;IACH,IAAI,EAAE,KAAK;IACX,UAAU,EAAE;QACV,gBAAgB,EAAE,IAAI;QACtB,MAAM,EACJ,uFAAuF;YACvF,gDAAgD;KACnD;IACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6CG;IACH,UAAU,EAAE;QACV,EAAE,IAAI,EAAE,qBAAqB,EAAE;QAC/B,EAAE,IAAI,EAAE,oBAAoB,EAAE;QAC9B,EAAE,IAAI,EAAE,oBAAoB,EAAE;QAC9B,EAAE,IAAI,EAAE,4BAA4B,EAAE;QACtC,EAAE,IAAI,EAAE,wBAAwB,EAAE;QAClC,EAAE,IAAI,EAAE,wBAAwB,EAAE;QAClC,EAAE,IAAI,EAAE,YAAY,EAAE;QACtB,EAAE,IAAI,EAAE,oCAAoC,EAAE;QAC9C,EAAE,IAAI,EAAE,iBAAiB,EAAE;QAC3B,EAAE,IAAI,EAAE,kBAAkB,EAAE;QAC5B,EAAE,IAAI,EAAE,6BAA6B,EAAE;QACvC,EAAE,IAAI,EAAE,2BAA2B,EAAE;QACrC,EAAE,IAAI,EAAE,sBAAsB,EAAE;QAChC,EAAE,IAAI,EAAE,wBAAwB,EAAE;QAClC,EAAE,IAAI,EAAE,+BAA+B,EAAE;KAC1C;IACD,IAAI,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE;IAC5B,IAAI,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE;IACrB,WAAW,EAAE;QACX,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,uBAAuB,EAAE;QACvD,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,0BAA0B,EAAE;KAC5D;CACF,CAAC,CAAC;AAEH,yDAAyD;AACzD,MAAM,CAAC,MAAM,qBAAqB,GAAG,QAAQ,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Assets Library — feature 013 / data-model.md.
|
|
4
|
+
*
|
|
5
|
+
* Schema changes:
|
|
6
|
+
* - new `asset_folders` table (organisational tree for the Library page).
|
|
7
|
+
* - eight new columns on the existing `assets` table:
|
|
8
|
+
* folder_id, visibility, label, storage_backend, storage_locator,
|
|
9
|
+
* pending_cleanup, purge_after_at, mime_type_overridden.
|
|
10
|
+
* - new `categories.main_image_asset_id` FK column.
|
|
11
|
+
*
|
|
12
|
+
* It also created a GIN index on `cms_pages.body`, so reference-protection's
|
|
13
|
+
* JSONB-path scan stays fast (research R10), until
|
|
14
|
+
* `specs/120-migration-closure-bridge-ownership/` Phase 3. `cms_pages` is
|
|
15
|
+
* `cms`' table and this module does not declare `cms` — nor could it, `cms`
|
|
16
|
+
* declaring `assets_library` — so under D-226 the index moved to
|
|
17
|
+
* `Migration20260912T125709CmsPageBodyAssetRefIndex`, verbatim and
|
|
18
|
+
* `if not exists`. An instance that omits `cms` no longer has this migration
|
|
19
|
+
* indexing a table nothing builds; the index itself is unchanged, and the
|
|
20
|
+
* reference scan that needs it is unaffected.
|
|
21
|
+
*
|
|
22
|
+
* Backfill:
|
|
23
|
+
* - every pre-existing assets row is mapped to storage_backend='local'
|
|
24
|
+
* with storage_locator=storage_url.
|
|
25
|
+
* - rows whose pre-013 storage_url is an absolute URL are tagged
|
|
26
|
+
* storage_backend='legacy' (read-only escape hatch — research R11).
|
|
27
|
+
*
|
|
28
|
+
* The legacy `storage_url` column is left in place for one release; new
|
|
29
|
+
* code reads `storage_locator` exclusively.
|
|
30
|
+
*/
|
|
31
|
+
export declare class Migration20260505T102206AssetsLibraryInit extends Migration {
|
|
32
|
+
up(): Promise<void>;
|
|
33
|
+
down(): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=20260505T102206_assets_library_init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260505T102206_assets_library_init.d.ts","sourceRoot":"","sources":["../../src/migrations/20260505T102206_assets_library_init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,yCAA0C,SAAQ,SAAS;IACvD,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IA0FnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CA+BrC"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Assets Library — feature 013 / data-model.md.
|
|
4
|
+
*
|
|
5
|
+
* Schema changes:
|
|
6
|
+
* - new `asset_folders` table (organisational tree for the Library page).
|
|
7
|
+
* - eight new columns on the existing `assets` table:
|
|
8
|
+
* folder_id, visibility, label, storage_backend, storage_locator,
|
|
9
|
+
* pending_cleanup, purge_after_at, mime_type_overridden.
|
|
10
|
+
* - new `categories.main_image_asset_id` FK column.
|
|
11
|
+
*
|
|
12
|
+
* It also created a GIN index on `cms_pages.body`, so reference-protection's
|
|
13
|
+
* JSONB-path scan stays fast (research R10), until
|
|
14
|
+
* `specs/120-migration-closure-bridge-ownership/` Phase 3. `cms_pages` is
|
|
15
|
+
* `cms`' table and this module does not declare `cms` — nor could it, `cms`
|
|
16
|
+
* declaring `assets_library` — so under D-226 the index moved to
|
|
17
|
+
* `Migration20260912T125709CmsPageBodyAssetRefIndex`, verbatim and
|
|
18
|
+
* `if not exists`. An instance that omits `cms` no longer has this migration
|
|
19
|
+
* indexing a table nothing builds; the index itself is unchanged, and the
|
|
20
|
+
* reference scan that needs it is unaffected.
|
|
21
|
+
*
|
|
22
|
+
* Backfill:
|
|
23
|
+
* - every pre-existing assets row is mapped to storage_backend='local'
|
|
24
|
+
* with storage_locator=storage_url.
|
|
25
|
+
* - rows whose pre-013 storage_url is an absolute URL are tagged
|
|
26
|
+
* storage_backend='legacy' (read-only escape hatch — research R11).
|
|
27
|
+
*
|
|
28
|
+
* The legacy `storage_url` column is left in place for one release; new
|
|
29
|
+
* code reads `storage_locator` exclusively.
|
|
30
|
+
*/
|
|
31
|
+
export class Migration20260505T102206AssetsLibraryInit extends Migration {
|
|
32
|
+
async up() {
|
|
33
|
+
// --- asset_folders ---------------------------------------------------
|
|
34
|
+
this.addSql(`
|
|
35
|
+
create table "asset_folders" (
|
|
36
|
+
"id" uuid not null,
|
|
37
|
+
"parent_id" uuid null,
|
|
38
|
+
"name" varchar(160) not null,
|
|
39
|
+
"position" integer not null default 0,
|
|
40
|
+
"created_at" timestamptz not null,
|
|
41
|
+
"updated_at" timestamptz not null,
|
|
42
|
+
constraint "asset_folders_pkey" primary key ("id"),
|
|
43
|
+
constraint "asset_folders_parent_fk" foreign key ("parent_id")
|
|
44
|
+
references "asset_folders" ("id") on delete restrict,
|
|
45
|
+
constraint "asset_folders_no_self_parent" check ("id" <> "parent_id")
|
|
46
|
+
);
|
|
47
|
+
`);
|
|
48
|
+
this.addSql('create index "asset_folders_parent_position_index" on "asset_folders" ("parent_id", "position");');
|
|
49
|
+
// Case-insensitive uniqueness of (parent_id, name). NULL parent_id is
|
|
50
|
+
// coalesced to the all-zero UUID so the constraint also covers root.
|
|
51
|
+
this.addSql(`
|
|
52
|
+
create unique index "asset_folders_unique_name_per_parent"
|
|
53
|
+
on "asset_folders" (
|
|
54
|
+
coalesce("parent_id", '00000000-0000-0000-0000-000000000000'::uuid),
|
|
55
|
+
lower("name")
|
|
56
|
+
);
|
|
57
|
+
`);
|
|
58
|
+
// --- assets: extended columns ----------------------------------------
|
|
59
|
+
this.addSql(`alter table "assets"
|
|
60
|
+
add column "folder_id" uuid null,
|
|
61
|
+
add column "visibility" varchar(7) not null default 'public',
|
|
62
|
+
add column "label" text null,
|
|
63
|
+
add column "storage_backend" varchar(8) not null default 'local',
|
|
64
|
+
add column "storage_locator" varchar(2048) not null default '',
|
|
65
|
+
add column "pending_cleanup" boolean not null default false,
|
|
66
|
+
add column "purge_after_at" timestamptz null,
|
|
67
|
+
add column "mime_type_overridden" boolean not null default false;
|
|
68
|
+
`);
|
|
69
|
+
this.addSql(`
|
|
70
|
+
alter table "assets"
|
|
71
|
+
add constraint "assets_folder_fk" foreign key ("folder_id")
|
|
72
|
+
references "asset_folders" ("id") on delete set null,
|
|
73
|
+
add constraint "assets_visibility_check"
|
|
74
|
+
check ("visibility" in ('public','private')),
|
|
75
|
+
add constraint "assets_storage_backend_check"
|
|
76
|
+
check ("storage_backend" in ('local','s3','gcs','legacy'));
|
|
77
|
+
`);
|
|
78
|
+
this.addSql('create index "assets_folder_visible_index" on "assets" ("folder_id", "deleted_at", "created_at" desc);');
|
|
79
|
+
this.addSql('create index "assets_pending_cleanup_index" on "assets" ("pending_cleanup") where "pending_cleanup" = true;');
|
|
80
|
+
this.addSql('create index "assets_purge_due_index" on "assets" ("purge_after_at") where "deleted_at" is not null and "purge_after_at" is not null;');
|
|
81
|
+
this.addSql('create index "assets_storage_backend_index" on "assets" ("storage_backend", "deleted_at");');
|
|
82
|
+
// --- categories.main_image_asset_id ----------------------------------
|
|
83
|
+
this.addSql(`alter table "categories"
|
|
84
|
+
add column "main_image_asset_id" uuid null,
|
|
85
|
+
add constraint "categories_main_image_fk" foreign key ("main_image_asset_id")
|
|
86
|
+
references "assets" ("id") on delete set null;
|
|
87
|
+
`);
|
|
88
|
+
this.addSql('create index "categories_main_image_asset_id_index" on "categories" ("main_image_asset_id");');
|
|
89
|
+
// --- backfill --------------------------------------------------------
|
|
90
|
+
// Pass 1: copy storage_url into storage_locator for every row that
|
|
91
|
+
// still has the column-default empty string.
|
|
92
|
+
this.addSql(`
|
|
93
|
+
update "assets"
|
|
94
|
+
set "storage_locator" = "storage_url"
|
|
95
|
+
where "storage_locator" = '';
|
|
96
|
+
`);
|
|
97
|
+
// Pass 2: rows whose pre-013 storage_url is an absolute URL are
|
|
98
|
+
// routed through the runtime 'legacy' adapter. This preserves their
|
|
99
|
+
// public URL verbatim — see research.md R11.
|
|
100
|
+
this.addSql(`
|
|
101
|
+
update "assets"
|
|
102
|
+
set "storage_backend" = 'legacy'
|
|
103
|
+
where "storage_locator" ~ '^https?://';
|
|
104
|
+
`);
|
|
105
|
+
}
|
|
106
|
+
async down() {
|
|
107
|
+
this.addSql('alter table "categories" drop constraint if exists "categories_main_image_fk";');
|
|
108
|
+
this.addSql('drop index if exists "categories_main_image_asset_id_index";');
|
|
109
|
+
this.addSql('alter table "categories" drop column if exists "main_image_asset_id";');
|
|
110
|
+
this.addSql('drop index if exists "assets_storage_backend_index";');
|
|
111
|
+
this.addSql('drop index if exists "assets_purge_due_index";');
|
|
112
|
+
this.addSql('drop index if exists "assets_pending_cleanup_index";');
|
|
113
|
+
this.addSql('drop index if exists "assets_folder_visible_index";');
|
|
114
|
+
this.addSql('alter table "assets" drop constraint if exists "assets_storage_backend_check";');
|
|
115
|
+
this.addSql('alter table "assets" drop constraint if exists "assets_visibility_check";');
|
|
116
|
+
this.addSql('alter table "assets" drop constraint if exists "assets_folder_fk";');
|
|
117
|
+
this.addSql(`alter table "assets"
|
|
118
|
+
drop column if exists "mime_type_overridden",
|
|
119
|
+
drop column if exists "purge_after_at",
|
|
120
|
+
drop column if exists "pending_cleanup",
|
|
121
|
+
drop column if exists "storage_locator",
|
|
122
|
+
drop column if exists "storage_backend",
|
|
123
|
+
drop column if exists "label",
|
|
124
|
+
drop column if exists "visibility",
|
|
125
|
+
drop column if exists "folder_id";
|
|
126
|
+
`);
|
|
127
|
+
this.addSql('drop index if exists "asset_folders_unique_name_per_parent";');
|
|
128
|
+
this.addSql('drop index if exists "asset_folders_parent_position_index";');
|
|
129
|
+
this.addSql('drop table if exists "asset_folders" cascade;');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=20260505T102206_assets_library_init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260505T102206_assets_library_init.js","sourceRoot":"","sources":["../../src/migrations/20260505T102206_assets_library_init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,OAAO,yCAA0C,SAAQ,SAAS;IAC7D,KAAK,CAAC,EAAE;QACf,wEAAwE;QACxE,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;;;;KAaX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CACT,kGAAkG,CACnG,CAAC;QACF,sEAAsE;QACtE,qEAAqE;QACrE,IAAI,CAAC,MAAM,CAAC;;;;;;KAMX,CAAC,CAAC;QAEH,wEAAwE;QACxE,IAAI,CAAC,MAAM,CAAC;;;;;;;;;KASX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC;;;;;;;;KAQX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CACT,wGAAwG,CACzG,CAAC;QACF,IAAI,CAAC,MAAM,CACT,6GAA6G,CAC9G,CAAC;QACF,IAAI,CAAC,MAAM,CACT,uIAAuI,CACxI,CAAC;QACF,IAAI,CAAC,MAAM,CACT,4FAA4F,CAC7F,CAAC;QAEF,wEAAwE;QACxE,IAAI,CAAC,MAAM,CAAC;;;;KAIX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CACT,8FAA8F,CAC/F,CAAC;QAEF,wEAAwE;QACxE,mEAAmE;QACnE,6CAA6C;QAC7C,IAAI,CAAC,MAAM,CAAC;;;;KAIX,CAAC,CAAC;QACH,gEAAgE;QAChE,oEAAoE;QACpE,6CAA6C;QAC7C,IAAI,CAAC,MAAM,CAAC;;;;KAIX,CAAC,CAAC;IACL,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CACT,gFAAgF,CACjF,CAAC;QACF,IAAI,CAAC,MAAM,CACT,8DAA8D,CAC/D,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,uEAAuE,CAAC,CAAC;QAErF,IAAI,CAAC,MAAM,CAAC,sDAAsD,CAAC,CAAC;QACpE,IAAI,CAAC,MAAM,CAAC,gDAAgD,CAAC,CAAC;QAC9D,IAAI,CAAC,MAAM,CAAC,sDAAsD,CAAC,CAAC;QACpE,IAAI,CAAC,MAAM,CAAC,qDAAqD,CAAC,CAAC;QACnE,IAAI,CAAC,MAAM,CAAC,gFAAgF,CAAC,CAAC;QAC9F,IAAI,CAAC,MAAM,CAAC,2EAA2E,CAAC,CAAC;QACzF,IAAI,CAAC,MAAM,CAAC,oEAAoE,CAAC,CAAC;QAClF,IAAI,CAAC,MAAM,CAAC;;;;;;;;;KASX,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,8DAA8D,CAAC,CAAC;QAC5E,IAAI,CAAC,MAAM,CAAC,6DAA6D,CAAC,CAAC;QAC3E,IAAI,CAAC,MAAM,CAAC,+CAA+C,CAAC,CAAC;IAC/D,CAAC;CACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `./migrations` subpath — every migration class this module owns, as one
|
|
3
|
+
* ordered `migrations` array.
|
|
4
|
+
*
|
|
5
|
+
* The array is what the platform reads when this module is **installed**:
|
|
6
|
+
* `src/packages/package-runtime.ts` takes `exported['migrations']` and refuses
|
|
7
|
+
* the package outright when it is absent (D-168).
|
|
8
|
+
*
|
|
9
|
+
* Listed in ascending timestamp, which is the order of this module's own
|
|
10
|
+
* migrations and of nothing else (feature 081): a manifest `dependencies` array
|
|
11
|
+
* is the only thing ordering this block against another module's.
|
|
12
|
+
*
|
|
13
|
+
* The **named** exports stay beside the array, and the asymmetry with
|
|
14
|
+
* `./backend` — which publishes an array and no named class (D-168) — is
|
|
15
|
+
* deliberate. `db/migrations-registry.generated.ts` imports each class by name
|
|
16
|
+
* from this specifier, and a migration class name is contract in a way an entity
|
|
17
|
+
* class name is not: `mikro_orm_migrations` persists it, so it is a string every
|
|
18
|
+
* already-migrated database holds.
|
|
19
|
+
*
|
|
20
|
+
* A class that is in neither the array nor the barrel is a migration that does
|
|
21
|
+
* not run: `migration:pending` reports nothing pending and the first symptom is
|
|
22
|
+
* a query against a table nobody created.
|
|
23
|
+
*/
|
|
24
|
+
import { Migration20260505T102206AssetsLibraryInit } from './20260505T102206_assets_library_init.js';
|
|
25
|
+
export declare const migrations: (typeof Migration20260505T102206AssetsLibraryInit)[];
|
|
26
|
+
export { Migration20260505T102206AssetsLibraryInit, };
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,yCAAyC,EAAE,MAAM,0CAA0C,CAAC;AAErG,eAAO,MAAM,UAAU,sDAEtB,CAAC;AAEF,OAAO,EACL,yCAAyC,GAC1C,CAAC"}
|