headroom-cms 0.1.11 → 0.2.1
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/admin/.well-known/headroom.json +9 -0
- package/admin/assets/{AdminsPage-BnzH9TL3.js → AdminsPage-lGd1MFW3.js} +1 -1
- package/admin/assets/AllContentPage-YykTt4xH.js +1 -0
- package/admin/assets/{ApiKeysPage-DEAa8eyC.js → ApiKeysPage-BCpNEKxF.js} +1 -1
- package/admin/assets/{AuditPage-BN9yNsxh.js → AuditPage-DBKiujoI.js} +1 -1
- package/admin/assets/BackupsPage-DCrmYnQc.js +1 -0
- package/admin/assets/{BlockEditor-3wnisTOZ.js → BlockEditor-BQVXN_Yn.js} +3 -3
- package/admin/assets/BlockTypeEditPage-BZmvEezy.js +1 -0
- package/admin/assets/{BlockTypesPage-Dhkho6T_.js → BlockTypesPage-CAr2JDj9.js} +1 -1
- package/admin/assets/BulkActionBar-CgHMxSkm.js +1 -0
- package/admin/assets/{CollectionEditPage-lOb4hEZy.js → CollectionEditPage-BQoXWAB-.js} +1 -1
- package/admin/assets/{CollectionsPage-CgtOloa1.js → CollectionsPage-Dakk_NyE.js} +1 -1
- package/admin/assets/{ContentCreatePage-LeQjahp_.js → ContentCreatePage-d-xxhoxO.js} +1 -1
- package/admin/assets/ContentEditPage-CUi_mFRC.js +1 -0
- package/admin/assets/{ContentField-pilCbdnA.js → ContentField-D6UO6W9G.js} +1 -1
- package/admin/assets/ContentListPage-8riubzdw.js +1 -0
- package/admin/assets/{CustomBlockPreview-DNnTFM0z.js → CustomBlockPreview-BuRfUKgt.js} +1 -1
- package/admin/assets/FieldRenderer-CUVay-yx.js +2 -0
- package/admin/assets/FileTypeIcon-DRD08pW_.js +1 -0
- package/admin/assets/FloatingComposerController-D4uLQfUX-Dc1_NZ4l.js +1 -0
- package/admin/assets/{IconPicker-CpIgiQTC.js → IconPicker-DR-6b0GI.js} +2 -2
- package/admin/assets/{LoginPage-D9ZsGLIi.js → LoginPage-DbHhSxgY.js} +1 -1
- package/admin/assets/MediaField-eRyEALYH.js +1 -0
- package/admin/assets/MediaPage-DW85F2UV.js +1 -0
- package/admin/assets/{Pagination-Df9nQ7Z0.js → Pagination-DTboMLK6.js} +1 -1
- package/admin/assets/{RelationshipPicker-B3Ftmqxp.js → RelationshipPicker-VVZN4dlP.js} +1 -1
- package/admin/assets/{SiteSettingsPage-6NvH7CiQ.js → SiteSettingsPage-5-HzcJLC.js} +1 -1
- package/admin/assets/{SiteUserEditPage-D5VaQ1Xq.js → SiteUserEditPage-C_PPvEye.js} +1 -1
- package/admin/assets/{SiteUsersPage-BYVduiqs.js → SiteUsersPage-CIgMMi-T.js} +1 -1
- package/admin/assets/{SitesPage-rfWWE0yK.js → SitesPage-DkG0gyHT.js} +1 -1
- package/admin/assets/SubmissionDetailPage-BwFWjkVu.js +1 -0
- package/admin/assets/SubmissionEditPage-C6Og25NH.js +1 -0
- package/admin/assets/SubmissionListPage-spPh8dwQ.js +1 -0
- package/admin/assets/{TagInput-57c4DG1w.js → TagInput-DxktEo07.js} +1 -1
- package/admin/assets/{TagsPage-BEO5AwCv.js → TagsPage-Dcc1IkF7.js} +1 -1
- package/admin/assets/TrashPage-8ybk22ZM.js +1 -0
- package/admin/assets/{UsersPage-BpIRorJ1.js → UsersPage-D5uXgLV4.js} +1 -1
- package/admin/assets/{WebhookEditPage-D5xgi56h.js → WebhookEditPage-T5KsZGqe.js} +1 -1
- package/admin/assets/{WebhooksPage-BY7AaiGr.js → WebhooksPage-DEIu5cX0.js} +1 -1
- package/admin/assets/{card-C9hfyHXf.js → card-CVAiqxnT.js} +1 -1
- package/admin/assets/{checkbox-DVJcwUt1.js → checkbox-BO-Fusdb.js} +1 -1
- package/admin/assets/{command-Bfmj0MEL.js → command-D0ojin3H.js} +1 -1
- package/admin/assets/{contentStatus-CkPi9Dh6.js → contentStatus-WXGfd7vX.js} +1 -1
- package/admin/assets/format-BRcflvs9.js +1 -0
- package/admin/assets/index-BCa3VYjL.css +1 -0
- package/admin/assets/{index-Ce5pmRMj.js → index-CbEa9yyd.js} +10 -10
- package/admin/assets/listCellValue-BUdbRyCz.js +1 -0
- package/admin/assets/{popover-CzaQYEEP.js → popover-B9WNjj2t.js} +1 -1
- package/admin/assets/{select-CrRhFGIi.js → select-B2yTIHJT.js} +1 -1
- package/admin/assets/{serializeToText-2VrsuRUh.js → serializeToText-Tv-7pIdy.js} +1 -1
- package/admin/assets/{table-_3bMY0_z.js → table-C3EQVw1z.js} +1 -1
- package/admin/assets/{textarea-6fq0R6VV.js → textarea-DIHLWabG.js} +1 -1
- package/admin/assets/{useAdminResolver-BJNPz3OG.js → useAdminResolver-_OHcUYwq.js} +1 -1
- package/admin/assets/useContent-DLo6FUYZ.js +1 -0
- package/admin/assets/{useContentSearch-B3aTjuCu.js → useContentSearch-D-D0veFh.js} +1 -1
- package/admin/assets/{usePageTitle-C1r1-C00.js → usePageTitle-BJ2ARyuc.js} +1 -1
- package/admin/assets/{useSiteUsers-DIaqgNSp.js → useSiteUsers-Bn3GiEYB.js} +1 -1
- package/admin/assets/{useTags-B-HgMVwo.js → useTags-CyGo0zXa.js} +1 -1
- package/admin/assets/useTrash-BFJMIP8N.js +1 -0
- package/admin/assets/{useWebhooks-BvZjUJkJ.js → useWebhooks-BgJL2-Ek.js} +1 -1
- package/admin/index.html +2 -2
- package/admin/sw.js +1 -1
- package/admin/workbox-362996ec.js +1 -0
- package/dist/admin-site.d.ts.map +1 -1
- package/dist/admin-site.js +46 -3
- package/dist/admin-site.js.map +1 -1
- package/dist/api.d.ts +2 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +57 -5
- package/dist/api.js.map +1 -1
- package/dist/backup.d.ts +29 -0
- package/dist/backup.d.ts.map +1 -0
- package/dist/backup.js +95 -0
- package/dist/backup.js.map +1 -0
- package/dist/cdn-api.d.ts.map +1 -1
- package/dist/cdn-api.js +20 -19
- package/dist/cdn-api.js.map +1 -1
- package/dist/cron.d.ts +42 -0
- package/dist/cron.d.ts.map +1 -0
- package/dist/cron.js +128 -0
- package/dist/cron.js.map +1 -0
- package/dist/image.d.ts +8 -1
- package/dist/image.d.ts.map +1 -1
- package/dist/image.js +26 -6
- package/dist/image.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +50 -1
- package/dist/index.js.map +1 -1
- package/dist/storage.d.ts +1 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +21 -0
- package/dist/storage.js.map +1 -1
- package/dist/webhooks.d.ts +4 -3
- package/dist/webhooks.d.ts.map +1 -1
- package/dist/webhooks.js +22 -35
- package/dist/webhooks.js.map +1 -1
- package/lambda/api/bootstrap +0 -0
- package/lambda/backup-worker/bootstrap +0 -0
- package/lambda/image-lambda/index.mjs +30 -6
- package/lambda/image-lambda/node_modules/.package-lock.json +3 -3
- package/lambda/image-lambda/node_modules/semver/classes/range.js +7 -0
- package/lambda/image-lambda/node_modules/semver/package.json +1 -1
- package/lambda/image-lambda/node_modules/semver/ranges/subset.js +2 -2
- package/lambda/trash-sweeper/bootstrap +0 -0
- package/lambda/webhook-worker/bootstrap +0 -0
- package/package.json +1 -1
- package/src/admin-site.ts +46 -3
- package/src/api.ts +58 -5
- package/src/backup.ts +114 -0
- package/src/cdn-api.ts +20 -22
- package/src/cron.ts +153 -0
- package/src/image.ts +30 -6
- package/src/index.ts +58 -1
- package/src/sst-env.d.ts +21 -0
- package/src/storage.ts +22 -0
- package/src/webhooks.ts +22 -39
- package/admin/assets/AllContentPage-BtObN6oy.js +0 -1
- package/admin/assets/BlockTypeEditPage-C2evAESK.js +0 -1
- package/admin/assets/BulkActionBar-BxdfUSrN.js +0 -1
- package/admin/assets/ContentEditPage-xczr4d_h.js +0 -1
- package/admin/assets/ContentListPage-BAKDn1Xy.js +0 -1
- package/admin/assets/FieldRenderer-DiOKvkWV.js +0 -2
- package/admin/assets/FilterBar-BZoa63zh.js +0 -1
- package/admin/assets/FloatingComposerController-D4uLQfUX-BMIvFCoE.js +0 -1
- package/admin/assets/MediaField-CxccCFGQ.js +0 -1
- package/admin/assets/MediaPage-QvMaH2YJ.js +0 -1
- package/admin/assets/SubmissionDetailPage-BSUR685F.js +0 -1
- package/admin/assets/SubmissionEditPage-DjLXHjWU.js +0 -1
- package/admin/assets/SubmissionListPage-DBxNEvde.js +0 -1
- package/admin/assets/format-C88SDH8g.js +0 -1
- package/admin/assets/index-BB9Syqw2.css +0 -1
- package/admin/assets/useContent-Bs7nel7C.js +0 -1
- package/admin/assets/useMedia-ae3s_ajC.js +0 -1
- package/admin/workbox-7d58179f.js +0 -1
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import path from "path";
|
|
|
9
9
|
import { createStorage } from "./storage.js";
|
|
10
10
|
import { createAuth } from "./auth.js";
|
|
11
11
|
import { createWebhooks } from "./webhooks.js";
|
|
12
|
+
import { createBackup } from "./backup.js";
|
|
12
13
|
import { createImage } from "./image.js";
|
|
13
14
|
import { createApi } from "./api.js";
|
|
14
15
|
import { createApiCdn } from "./cdn-api.js";
|
|
@@ -16,6 +17,7 @@ import { createMediaCdn } from "./cdn-media.js";
|
|
|
16
17
|
import { createScheduler } from "./scheduler.js";
|
|
17
18
|
import { createCollabTable, createCollabHandler } from "./collaboration.js";
|
|
18
19
|
import { createAdminSite } from "./admin-site.js";
|
|
20
|
+
import { createCron } from "./cron.js";
|
|
19
21
|
/**
|
|
20
22
|
* Resolve the headroom-cms package root directory.
|
|
21
23
|
*
|
|
@@ -63,7 +65,7 @@ export class HeadroomCMS {
|
|
|
63
65
|
? { handler: args.dev.customMessageHandler }
|
|
64
66
|
: undefined,
|
|
65
67
|
});
|
|
66
|
-
// 3. Webhooks (DynamoDB tables,
|
|
68
|
+
// 3. Webhooks (DynamoDB tables, DLQ, worker Lambda)
|
|
67
69
|
const webhooks = createWebhooks(name, {
|
|
68
70
|
sites: storage.sites,
|
|
69
71
|
pkgRoot,
|
|
@@ -71,6 +73,17 @@ export class HeadroomCMS {
|
|
|
71
73
|
? { handler: args.dev.webhookWorkerHandler }
|
|
72
74
|
: undefined,
|
|
73
75
|
});
|
|
76
|
+
// 3b. Backup worker Lambda (export + restore). Needs access to every
|
|
77
|
+
// site-scoped table plus the content + backup buckets. Created after
|
|
78
|
+
// webhooks so it can link the webhooks table for backup payloads.
|
|
79
|
+
const backup = createBackup(name, {
|
|
80
|
+
storage,
|
|
81
|
+
webhooks,
|
|
82
|
+
pkgRoot,
|
|
83
|
+
dev: args.dev
|
|
84
|
+
? { handler: args.dev.backupWorkerHandler }
|
|
85
|
+
: undefined,
|
|
86
|
+
});
|
|
74
87
|
// 4. Image Lambda (Sharp transform with HMAC-signed URLs)
|
|
75
88
|
const image = createImage(name, {
|
|
76
89
|
contentBucket: storage.contentBucket,
|
|
@@ -87,6 +100,7 @@ export class HeadroomCMS {
|
|
|
87
100
|
storage,
|
|
88
101
|
auth,
|
|
89
102
|
webhooks,
|
|
103
|
+
backup,
|
|
90
104
|
image,
|
|
91
105
|
collab: collabTable,
|
|
92
106
|
senderEmail: args.senderEmail,
|
|
@@ -119,6 +133,18 @@ export class HeadroomCMS {
|
|
|
119
133
|
? { handler: args.dev.schedulerHandler }
|
|
120
134
|
: undefined,
|
|
121
135
|
});
|
|
136
|
+
// 7b. Cron — daily trash sweeper Lambda (with an in-sweeper per-run purge
|
|
137
|
+
// circuit breaker; no CloudWatch metric/alarm). See `steering/TRASH_CAN.md`
|
|
138
|
+
// Phase 2b.
|
|
139
|
+
const cron = createCron(name, {
|
|
140
|
+
storage,
|
|
141
|
+
webhooks,
|
|
142
|
+
pkgRoot,
|
|
143
|
+
stage: $app.stage,
|
|
144
|
+
dev: args.dev?.trashSweeperHandler
|
|
145
|
+
? { handler: args.dev.trashSweeperHandler }
|
|
146
|
+
: undefined,
|
|
147
|
+
});
|
|
122
148
|
// 8a. API CDN (CloudFront distribution + edge auth, /v1/* and /health)
|
|
123
149
|
const apiCdn = createApiCdn(name, {
|
|
124
150
|
api,
|
|
@@ -164,6 +190,29 @@ export class HeadroomCMS {
|
|
|
164
190
|
userPoolId: auth.userPool.id,
|
|
165
191
|
userPoolClientId: auth.userPoolClient.id,
|
|
166
192
|
collabWs: collab.wsUrl,
|
|
193
|
+
// DynamoDB table names (with Pulumi-generated random suffixes baked
|
|
194
|
+
// in). Exposed so consumers — notably the Phase 6 E2E test harness —
|
|
195
|
+
// can discover real table names rather than guess them from the
|
|
196
|
+
// `<app>-<stage>-<Name>` template (which omits the random suffix).
|
|
197
|
+
// Per CLAUDE.md, `.sst/outputs.json` IS the supported interface for
|
|
198
|
+
// post-deploy discovery.
|
|
199
|
+
sitesTable: storage.sites.name,
|
|
200
|
+
contentTable: storage.content.name,
|
|
201
|
+
draftContentTable: storage.draftContent.name,
|
|
202
|
+
blocksTable: storage.blocks.name,
|
|
203
|
+
mediaTable: storage.media.name,
|
|
204
|
+
collectionsTable: storage.collections.name,
|
|
205
|
+
blockTypesTable: storage.blockTypes.name,
|
|
206
|
+
adminAuditTable: storage.adminAudit.name,
|
|
207
|
+
relationshipsTable: storage.relationships.name,
|
|
208
|
+
siteUsersTable: storage.siteUsers.name,
|
|
209
|
+
webhooksTable: webhooks.webhooks.name,
|
|
210
|
+
contentBucket: storage.contentBucket.name,
|
|
211
|
+
backupBucket: storage.backupBucket.name,
|
|
212
|
+
// Trash-sweeper Lambda function name (Phase 2b). Surfaced so ops/test
|
|
213
|
+
// scripts can target the sweeper by deterministic output instead of
|
|
214
|
+
// `aws lambda list-functions | grep`.
|
|
215
|
+
trashSweeperFunctionName: cron.trashSweeperCron.nodes.function.name,
|
|
167
216
|
};
|
|
168
217
|
}
|
|
169
218
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/storage.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export declare function createStorage(name: string): {
|
|
|
16
16
|
relationships: sst.aws.Dynamo;
|
|
17
17
|
siteUsers: sst.aws.Dynamo;
|
|
18
18
|
contentBucket: sst.aws.Bucket;
|
|
19
|
+
backupBucket: sst.aws.Bucket;
|
|
19
20
|
collabStateBucket: sst.aws.Bucket;
|
|
20
21
|
kvs: aws.cloudfront.KeyValueStore;
|
|
21
22
|
internalSecret: sst.Secret;
|
package/dist/storage.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM;;;;;;;;;;;;;;;;EA0MzC;AAED,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC"}
|
package/dist/storage.js
CHANGED
|
@@ -36,14 +36,22 @@ export function createStorage(name) {
|
|
|
36
36
|
fields: {
|
|
37
37
|
pk: "string",
|
|
38
38
|
sk: "string",
|
|
39
|
+
siteHost: "string",
|
|
39
40
|
},
|
|
40
41
|
primaryIndex: { hashKey: "pk", rangeKey: "sk" },
|
|
42
|
+
globalIndexes: {
|
|
43
|
+
bySite: { hashKey: "siteHost", rangeKey: "sk" },
|
|
44
|
+
},
|
|
41
45
|
});
|
|
42
46
|
const blocks = new sst.aws.Dynamo(`${name}Blocks`, {
|
|
43
47
|
fields: {
|
|
44
48
|
pk: "string",
|
|
49
|
+
siteHost: "string",
|
|
45
50
|
},
|
|
46
51
|
primaryIndex: { hashKey: "pk" },
|
|
52
|
+
globalIndexes: {
|
|
53
|
+
bySite: { hashKey: "siteHost", rangeKey: "pk" },
|
|
54
|
+
},
|
|
47
55
|
});
|
|
48
56
|
const media = new sst.aws.Dynamo(`${name}Media`, {
|
|
49
57
|
fields: {
|
|
@@ -85,10 +93,12 @@ export function createStorage(name) {
|
|
|
85
93
|
sk: "string",
|
|
86
94
|
targetPk: "string",
|
|
87
95
|
targetSk: "string",
|
|
96
|
+
siteHost: "string",
|
|
88
97
|
},
|
|
89
98
|
primaryIndex: { hashKey: "pk", rangeKey: "sk" },
|
|
90
99
|
globalIndexes: {
|
|
91
100
|
byTarget: { hashKey: "targetPk", rangeKey: "targetSk" },
|
|
101
|
+
bySite: { hashKey: "siteHost", rangeKey: "sk" },
|
|
92
102
|
},
|
|
93
103
|
});
|
|
94
104
|
const siteUsers = new sst.aws.Dynamo(`${name}SiteUsers`, {
|
|
@@ -96,13 +106,23 @@ export function createStorage(name) {
|
|
|
96
106
|
pk: "string",
|
|
97
107
|
sk: "string",
|
|
98
108
|
userId: "string",
|
|
109
|
+
siteHost: "string",
|
|
99
110
|
},
|
|
100
111
|
primaryIndex: { hashKey: "pk", rangeKey: "sk" },
|
|
101
112
|
globalIndexes: {
|
|
102
113
|
byUserId: { hashKey: "userId", rangeKey: "sk" },
|
|
114
|
+
bySite: { hashKey: "siteHost", rangeKey: "sk" },
|
|
103
115
|
},
|
|
104
116
|
ttl: "expiresAt",
|
|
105
117
|
});
|
|
118
|
+
// Backup bucket: archives of full site exports written by the backup worker.
|
|
119
|
+
// Layout: backups/{host}/{timestamp}.tar.gz + backups/{host}/latest.json.
|
|
120
|
+
// Versioning disabled — archives are immutable by convention (a new timestamp
|
|
121
|
+
// is a new object). No CloudFront origin — admin endpoints serve presigned
|
|
122
|
+
// URLs directly off S3.
|
|
123
|
+
const backupBucket = new sst.aws.Bucket(`${name}BackupBucket`, {
|
|
124
|
+
versioning: false,
|
|
125
|
+
});
|
|
106
126
|
const contentBucket = new sst.aws.Bucket(`${name}ContentBucket`, {
|
|
107
127
|
versioning: true,
|
|
108
128
|
access: "cloudfront",
|
|
@@ -166,6 +186,7 @@ export function createStorage(name) {
|
|
|
166
186
|
relationships,
|
|
167
187
|
siteUsers,
|
|
168
188
|
contentBucket,
|
|
189
|
+
backupBucket,
|
|
169
190
|
collabStateBucket,
|
|
170
191
|
kvs,
|
|
171
192
|
internalSecret,
|
package/dist/storage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,OAAO,EAAE;QAC/C,MAAM,EAAE;YACN,EAAE,EAAE,QAAQ;YACZ,EAAE,EAAE,QAAQ;SACb;QACD,YAAY,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;KAChD,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,SAAS,EAAE;QACnD,MAAM,EAAE;YACN,EAAE,EAAE,QAAQ;YACZ,EAAE,EAAE,QAAQ;YACZ,KAAK,EAAE,QAAQ;YACf,KAAK,EAAE,QAAQ;YACf,aAAa,EAAE,QAAQ;YACvB,eAAe,EAAE,QAAQ;YACzB,OAAO,EAAE,QAAQ;YACjB,cAAc,EAAE,QAAQ;YACxB,cAAc,EAAE,QAAQ;SACzB;QACD,YAAY,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC/C,aAAa,EAAE;YACb,eAAe,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE;YACxD,aAAa,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,eAAe,EAAE;YAC3D,iBAAiB,EAAE,EAAE,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,SAAS,EAAE;YACtE,eAAe,EAAE,EAAE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,gBAAgB,EAAE;SAC3E;KACF,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,cAAc,EAAE;QAC7D,MAAM,EAAE;YACN,EAAE,EAAE,QAAQ;YACZ,EAAE,EAAE,QAAQ;
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
|
package/dist/webhooks.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Webhook Infrastructure
|
|
3
3
|
*
|
|
4
|
-
* 2 DynamoDB tables +
|
|
5
|
-
*
|
|
4
|
+
* 2 DynamoDB tables + webhook worker Lambda (async-invoked by the API) + DLQ
|
|
5
|
+
* (Lambda async on-failure destination). The API directly invokes the worker
|
|
6
|
+
* via lambda:InvokeFunction; there is no SQS queue between them.
|
|
6
7
|
*/
|
|
7
8
|
import type { StorageResources } from "./storage.js";
|
|
8
9
|
export interface WebhookArgs {
|
|
@@ -16,7 +17,7 @@ export interface WebhookArgs {
|
|
|
16
17
|
export declare function createWebhooks(name: string, args: WebhookArgs): {
|
|
17
18
|
webhooks: sst.aws.Dynamo;
|
|
18
19
|
webhookDeliveries: sst.aws.Dynamo;
|
|
19
|
-
|
|
20
|
+
webhookWorker: sst.aws.Function;
|
|
20
21
|
webhookDeliveryDLQ: sst.aws.Queue;
|
|
21
22
|
};
|
|
22
23
|
export type WebhookResources = ReturnType<typeof createWebhooks>;
|
package/dist/webhooks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE;QACJ,qDAAqD;QACrD,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW;;;;;EA0E7D;AAED,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC"}
|
package/dist/webhooks.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Webhook Infrastructure
|
|
3
3
|
*
|
|
4
|
-
* 2 DynamoDB tables +
|
|
5
|
-
*
|
|
4
|
+
* 2 DynamoDB tables + webhook worker Lambda (async-invoked by the API) + DLQ
|
|
5
|
+
* (Lambda async on-failure destination). The API directly invokes the worker
|
|
6
|
+
* via lambda:InvokeFunction; there is no SQS queue between them.
|
|
6
7
|
*/
|
|
7
8
|
import path from "path";
|
|
8
9
|
export function createWebhooks(name, args) {
|
|
@@ -21,6 +22,9 @@ export function createWebhooks(name, args) {
|
|
|
21
22
|
primaryIndex: { hashKey: "pk", rangeKey: "sk" },
|
|
22
23
|
ttl: "ttl",
|
|
23
24
|
});
|
|
25
|
+
// DLQ retained as Lambda async OnFailure destination. Lambda writes a
|
|
26
|
+
// failure envelope (not the original payload verbatim) when retries are
|
|
27
|
+
// exhausted. No consumer polls this queue — it's a sink only.
|
|
24
28
|
const webhookDeliveryDLQ = new sst.aws.Queue(`${name}WebhookDeliveryDLQ`, {
|
|
25
29
|
transform: {
|
|
26
30
|
queue: (queueArgs) => {
|
|
@@ -28,22 +32,6 @@ export function createWebhooks(name, args) {
|
|
|
28
32
|
},
|
|
29
33
|
},
|
|
30
34
|
});
|
|
31
|
-
const webhookDeliveryQueue = new sst.aws.Queue(`${name}WebhookDeliveryQueue`, {
|
|
32
|
-
dlq: {
|
|
33
|
-
queue: webhookDeliveryDLQ.arn,
|
|
34
|
-
retry: 5,
|
|
35
|
-
},
|
|
36
|
-
transform: {
|
|
37
|
-
queue: (queueArgs) => {
|
|
38
|
-
queueArgs.visibilityTimeoutSeconds = 60;
|
|
39
|
-
queueArgs.messageRetentionSeconds = 4 * 24 * 60 * 60; // 4 days
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
// Webhook worker Lambda: processes SQS messages and delivers webhooks.
|
|
44
|
-
// Note: We use a manual Function + EventSourceMapping instead of
|
|
45
|
-
// queue.subscribe() to avoid a duplicate LambdaEncryptionKey issue
|
|
46
|
-
// caused by SST's dynamic import creating a separate Function class instance.
|
|
47
35
|
const workerConfig = args.dev
|
|
48
36
|
? {
|
|
49
37
|
handler: args.dev.handler,
|
|
@@ -61,30 +49,29 @@ export function createWebhooks(name, args) {
|
|
|
61
49
|
timeout: "30 seconds",
|
|
62
50
|
environment: {
|
|
63
51
|
WEBHOOK_DELIVERIES_TABLE: webhookDeliveries.name,
|
|
64
|
-
WEBHOOKS_TABLE: webhooks.name,
|
|
65
|
-
SITES_TABLE: args.sites.name,
|
|
66
52
|
},
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
"sqs:GetQueueAttributes",
|
|
74
|
-
],
|
|
75
|
-
resources: [webhookDeliveryQueue.arn],
|
|
76
|
-
},
|
|
77
|
-
],
|
|
53
|
+
// webhookDeliveryDLQ is linked so SST auto-grants sqs:SendMessage on the
|
|
54
|
+
// worker's execution role. Lambda async OnFailure delivery uses the
|
|
55
|
+
// function's own role to write the failure envelope to the DLQ — without
|
|
56
|
+
// this grant, AWS accepts the FunctionEventInvokeConfig at deploy time but
|
|
57
|
+
// silently drops failure envelopes at runtime.
|
|
58
|
+
link: [webhookDeliveries, webhookDeliveryDLQ],
|
|
78
59
|
});
|
|
79
|
-
|
|
80
|
-
|
|
60
|
+
// Lambda async retry + DLQ on terminal failure. MaximumRetryAttempts is
|
|
61
|
+
// 0–2 (industry standard for webhook delivery — Stripe/GitHub publish
|
|
62
|
+
// similar caps). Total attempts = initial + 2 retries = 3.
|
|
63
|
+
new aws.lambda.FunctionEventInvokeConfig(`${name}WebhookWorkerAsyncConfig`, {
|
|
81
64
|
functionName: webhookWorker.name,
|
|
82
|
-
|
|
65
|
+
maximumRetryAttempts: 2,
|
|
66
|
+
maximumEventAgeInSeconds: 6 * 60 * 60, // 6 hours
|
|
67
|
+
destinationConfig: {
|
|
68
|
+
onFailure: { destination: webhookDeliveryDLQ.arn },
|
|
69
|
+
},
|
|
83
70
|
});
|
|
84
71
|
return {
|
|
85
72
|
webhooks,
|
|
86
73
|
webhookDeliveries,
|
|
87
|
-
|
|
74
|
+
webhookWorker,
|
|
88
75
|
webhookDeliveryDLQ,
|
|
89
76
|
};
|
|
90
77
|
}
|
package/dist/webhooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,IAAI,MAAM,MAAM,CAAC;AAYxB,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,IAAiB;IAC5D,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,UAAU,EAAE;QACrD,MAAM,EAAE;YACN,EAAE,EAAE,QAAQ;YACZ,EAAE,EAAE,QAAQ;SACb;QACD,YAAY,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;KAChD,CAAC,CAAC;IAEH,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,mBAAmB,EAAE;QACvE,MAAM,EAAE;YACN,EAAE,EAAE,QAAQ;YACZ,EAAE,EAAE,QAAQ;SACb;QACD,YAAY,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC/C,GAAG,EAAE,KAAK;KACX,CAAC,CAAC;IAEH,sEAAsE;IACtE,wEAAwE;IACxE,8DAA8D;IAC9D,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,oBAAoB,EAAE;QACxE,SAAS,EAAE;YACT,KAAK,EAAE,CAAC,SAAc,EAAE,EAAE;gBACxB,SAAS,CAAC,uBAAuB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,UAAU;YACnE,CAAC;SACF;KACF,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG;QAC3B,CAAC,CAAC;YACE,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO;YACzB,OAAO,EAAE,IAAa;SACvB;QACH,CAAC,CAAC;YACE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,uBAAuB,CAAC;YACxD,OAAO,EAAE,WAAW;YACpB,OAAO,EAAE,iBAA0B;YACnC,YAAY,EAAE,OAAgB;SAC/B,CAAC;IAEN,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,IAAI,eAAe,EAAE;QACjE,GAAG,YAAY;QACf,MAAM,EAAE,QAAQ;QAChB,OAAO,EAAE,YAAY;QACrB,WAAW,EAAE;YACX,wBAAwB,EAAE,iBAAiB,CAAC,IAAI;SACjD;QACD,yEAAyE;QACzE,oEAAoE;QACpE,yEAAyE;QACzE,2EAA2E;QAC3E,+CAA+C;QAC/C,IAAI,EAAE,CAAC,iBAAiB,EAAE,kBAAkB,CAAC;KAC9C,CAAC,CAAC;IAEH,wEAAwE;IACxE,sEAAsE;IACtE,2DAA2D;IAC3D,IAAI,GAAG,CAAC,MAAM,CAAC,yBAAyB,CAAC,GAAG,IAAI,0BAA0B,EAAE;QAC1E,YAAY,EAAE,aAAa,CAAC,IAAI;QAChC,oBAAoB,EAAE,CAAC;QACvB,wBAAwB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,UAAU;QACjD,iBAAiB,EAAE;YACjB,SAAS,EAAE,EAAE,WAAW,EAAE,kBAAkB,CAAC,GAAG,EAAE;SACnD;KACF,CAAC,CAAC;IAEH,OAAO;QACL,QAAQ;QACR,iBAAiB;QACjB,aAAa;QACb,kBAAkB;KACnB,CAAC;AACJ,CAAC"}
|
package/lambda/api/bootstrap
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -4,7 +4,17 @@ import { S3Client, GetObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3
|
|
|
4
4
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
5
5
|
var s3 = new S3Client({});
|
|
6
6
|
var BUCKET = process.env.CONTENT_BUCKET;
|
|
7
|
-
var
|
|
7
|
+
var MASTER_SECRET = process.env.IMAGE_SIGNING_MASTER_SECRET;
|
|
8
|
+
var MASTER_SECRET_OLD = process.env.IMAGE_SIGNING_MASTER_SECRET_OLD;
|
|
9
|
+
var _timingSafeEqualCallCount = { count: 0 };
|
|
10
|
+
function _timingSafeEqual(a, b) {
|
|
11
|
+
_timingSafeEqualCallCount.count++;
|
|
12
|
+
return timingSafeEqual(a, b);
|
|
13
|
+
}
|
|
14
|
+
function deriveSiteSecret(master, siteHost) {
|
|
15
|
+
if (!master) return null;
|
|
16
|
+
return createHmac("sha256", master).update(siteHost).digest("hex").substring(0, 32);
|
|
17
|
+
}
|
|
8
18
|
var MAX_WIDTH = 2048;
|
|
9
19
|
var MAX_HEIGHT = 2048;
|
|
10
20
|
var MAX_PIXELS = 4e6;
|
|
@@ -31,6 +41,11 @@ async function handler(event) {
|
|
|
31
41
|
if (!match) {
|
|
32
42
|
return { statusCode: 400, body: "Invalid image path" };
|
|
33
43
|
}
|
|
44
|
+
const [, site, mediaId, ext] = match;
|
|
45
|
+
const sitePrimary = deriveSiteSecret(MASTER_SECRET, site);
|
|
46
|
+
if (!sitePrimary) {
|
|
47
|
+
return { statusCode: 503, body: "Image transforms not configured" };
|
|
48
|
+
}
|
|
34
49
|
const params = new URLSearchParams(rawQuery);
|
|
35
50
|
const sig = params.get("sig");
|
|
36
51
|
if (!sig) {
|
|
@@ -44,11 +59,18 @@ async function handler(event) {
|
|
|
44
59
|
if (!hasTransform) {
|
|
45
60
|
return { statusCode: 400, body: "No transform requested. Use /media/ path for originals." };
|
|
46
61
|
}
|
|
47
|
-
const
|
|
48
|
-
|
|
62
|
+
const siteOld = deriveSiteSecret(MASTER_SECRET_OLD, site);
|
|
63
|
+
const sigBuf = Buffer.from(sig);
|
|
64
|
+
const expectedPrimary = computeSignature(rawPath, params, sitePrimary);
|
|
65
|
+
const primaryMatch = _timingSafeEqual(sigBuf, Buffer.from(expectedPrimary));
|
|
66
|
+
let oldMatch = false;
|
|
67
|
+
if (siteOld) {
|
|
68
|
+
const expectedOld = computeSignature(rawPath, params, siteOld);
|
|
69
|
+
oldMatch = _timingSafeEqual(sigBuf, Buffer.from(expectedOld));
|
|
70
|
+
}
|
|
71
|
+
if (!primaryMatch && !oldMatch) {
|
|
49
72
|
return { statusCode: 403, body: "Invalid signature" };
|
|
50
73
|
}
|
|
51
|
-
const [, site, mediaId, ext] = match;
|
|
52
74
|
const s3Key = `sites/${site}/media/${mediaId}/original${ext}`;
|
|
53
75
|
const w = clamp(parseInt(params.get("w")) || null, 1, MAX_WIDTH);
|
|
54
76
|
const h = clamp(parseInt(params.get("h")) || null, 1, MAX_HEIGHT);
|
|
@@ -139,11 +161,11 @@ async function handler(event) {
|
|
|
139
161
|
return { statusCode: 500, body: "Image processing failed" };
|
|
140
162
|
}
|
|
141
163
|
}
|
|
142
|
-
function computeSignature(path, params) {
|
|
164
|
+
function computeSignature(path, params, key) {
|
|
143
165
|
const sorted = new URLSearchParams([...params.entries()].sort());
|
|
144
166
|
const qs = sorted.toString();
|
|
145
167
|
const canonical = qs ? `${path}?${qs}` : path;
|
|
146
|
-
return createHmac("sha256",
|
|
168
|
+
return createHmac("sha256", key).update(canonical).digest("hex").substring(0, 32);
|
|
147
169
|
}
|
|
148
170
|
function clamp(val, min, max) {
|
|
149
171
|
if (val == null) return null;
|
|
@@ -180,8 +202,10 @@ export {
|
|
|
180
202
|
MAX_WIDTH,
|
|
181
203
|
PATH_REGEX,
|
|
182
204
|
SHARP_MIME_TYPES,
|
|
205
|
+
_timingSafeEqualCallCount,
|
|
183
206
|
clamp,
|
|
184
207
|
computeSignature,
|
|
208
|
+
deriveSiteSecret,
|
|
185
209
|
extToFormat,
|
|
186
210
|
formatToMime,
|
|
187
211
|
handler
|
|
@@ -97,9 +97,9 @@
|
|
|
97
97
|
"license": "MIT"
|
|
98
98
|
},
|
|
99
99
|
"node_modules/semver": {
|
|
100
|
-
"version": "7.8.
|
|
101
|
-
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.
|
|
102
|
-
"integrity": "sha512-
|
|
100
|
+
"version": "7.8.1",
|
|
101
|
+
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz",
|
|
102
|
+
"integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==",
|
|
103
103
|
"license": "ISC",
|
|
104
104
|
"bin": {
|
|
105
105
|
"semver": "bin/semver.js"
|
|
@@ -98,6 +98,9 @@ class Range {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
parseRange (range) {
|
|
101
|
+
// strip build metadata so it can't bleed into the version
|
|
102
|
+
range = range.replace(BUILDSTRIPRE, '')
|
|
103
|
+
|
|
101
104
|
// memoize range parsing for performance.
|
|
102
105
|
// this is a very hot path, and fully deterministic.
|
|
103
106
|
const memoOpts =
|
|
@@ -223,6 +226,7 @@ const debug = require('../internal/debug')
|
|
|
223
226
|
const SemVer = require('./semver')
|
|
224
227
|
const {
|
|
225
228
|
safeRe: re,
|
|
229
|
+
src,
|
|
226
230
|
t,
|
|
227
231
|
comparatorTrimReplace,
|
|
228
232
|
tildeTrimReplace,
|
|
@@ -230,6 +234,9 @@ const {
|
|
|
230
234
|
} = require('../internal/re')
|
|
231
235
|
const { FLAG_INCLUDE_PRERELEASE, FLAG_LOOSE } = require('../internal/constants')
|
|
232
236
|
|
|
237
|
+
// unbounded global build-metadata stripper used by parseRange
|
|
238
|
+
const BUILDSTRIPRE = new RegExp(src[t.BUILD], 'g')
|
|
239
|
+
|
|
233
240
|
const isNullSet = c => c.value === '<0.0.0-0'
|
|
234
241
|
const isAny = c => c.value === ''
|
|
235
242
|
|
|
@@ -174,7 +174,7 @@ const simpleSubset = (sub, dom, options) => {
|
|
|
174
174
|
if (higher === c && higher !== gt) {
|
|
175
175
|
return false
|
|
176
176
|
}
|
|
177
|
-
} else if (gt.operator === '>=' && !
|
|
177
|
+
} else if (gt.operator === '>=' && !c.test(gt.semver)) {
|
|
178
178
|
return false
|
|
179
179
|
}
|
|
180
180
|
}
|
|
@@ -192,7 +192,7 @@ const simpleSubset = (sub, dom, options) => {
|
|
|
192
192
|
if (lower === c && lower !== lt) {
|
|
193
193
|
return false
|
|
194
194
|
}
|
|
195
|
-
} else if (lt.operator === '<=' && !
|
|
195
|
+
} else if (lt.operator === '<=' && !c.test(lt.semver)) {
|
|
196
196
|
return false
|
|
197
197
|
}
|
|
198
198
|
}
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
package/src/admin-site.ts
CHANGED
|
@@ -55,7 +55,18 @@ export function createAdminSite(name: string, args: AdminSiteArgs) {
|
|
|
55
55
|
: undefined;
|
|
56
56
|
|
|
57
57
|
if (args.dev) {
|
|
58
|
-
// Dev mode: standard StaticSite with Vite build
|
|
58
|
+
// Dev mode: standard StaticSite with Vite build.
|
|
59
|
+
//
|
|
60
|
+
// VITE_ADMIN_URL is set from the configured custom domain when present
|
|
61
|
+
// (we know it before the StaticSite is constructed). Without a custom
|
|
62
|
+
// domain the admin URL is only known *after* the StaticSite resolves —
|
|
63
|
+
// which would be circular at the `environment` map. In that case we
|
|
64
|
+
// leave VITE_ADMIN_URL empty; the discovery doc emits adminUrl: "" and
|
|
65
|
+
// the CLI tolerates the empty value. See:
|
|
66
|
+
// steering/CLI_BOOSTRAP_DESIGN.md §1.2 (env-injection circularity)
|
|
67
|
+
const viteAdminUrl = args.domain
|
|
68
|
+
? `https://${args.domain.name}`
|
|
69
|
+
: "";
|
|
59
70
|
const site = new sst.aws.StaticSite(`${name}Admin`, {
|
|
60
71
|
path: args.dev.adminPath,
|
|
61
72
|
dev: {
|
|
@@ -73,6 +84,7 @@ export function createAdminSite(name: string, args: AdminSiteArgs) {
|
|
|
73
84
|
VITE_AWS_REGION: aws.getRegionOutput().name,
|
|
74
85
|
VITE_COLLAB_WS_URL: collab.wsUrl,
|
|
75
86
|
VITE_COLLAB_ENABLED: collabEnabledStr,
|
|
87
|
+
VITE_ADMIN_URL: viteAdminUrl,
|
|
76
88
|
},
|
|
77
89
|
domain: domainConfig,
|
|
78
90
|
});
|
|
@@ -90,10 +102,22 @@ export function createAdminSite(name: string, args: AdminSiteArgs) {
|
|
|
90
102
|
fs.mkdirSync(workDir, { recursive: true });
|
|
91
103
|
fs.cpSync(adminSrc, workDir, { recursive: true });
|
|
92
104
|
|
|
105
|
+
// Compute the admin URL for placeholder substitution. When a custom domain
|
|
106
|
+
// is configured, use it directly; otherwise fall back to the CloudFront-
|
|
107
|
+
// distribution URL the StaticSite resolves to. Using `$interpolate` keeps
|
|
108
|
+
// the value as an SST Output until deploy time, where it's safely inlined
|
|
109
|
+
// into the node script via the existing $-interpolated template.
|
|
110
|
+
const adminUrlForSubstitution = args.domain
|
|
111
|
+
? `https://${args.domain.name}`
|
|
112
|
+
: "";
|
|
93
113
|
const site = new sst.aws.StaticSite(`${name}Admin`, {
|
|
94
114
|
path: workDir,
|
|
95
115
|
build: {
|
|
96
|
-
// Replace placeholder env vars in the pre-built JS/HTML files
|
|
116
|
+
// Replace placeholder env vars in the pre-built JS/HTML/discovery files.
|
|
117
|
+
//
|
|
118
|
+
// The walk filter intentionally only matches the exact path
|
|
119
|
+
// `.well-known/headroom.json` (not any *.json) so we don't touch
|
|
120
|
+
// manifest.webmanifest, asset manifests, or other runtime JSON files.
|
|
97
121
|
command: $interpolate`node -e "
|
|
98
122
|
const fs = require('fs');
|
|
99
123
|
const path = require('path');
|
|
@@ -105,12 +129,18 @@ export function createAdminSite(name: string, args: AdminSiteArgs) {
|
|
|
105
129
|
'__HEADROOM_AWS_REGION__': '${aws.getRegionOutput().name}',
|
|
106
130
|
'__HEADROOM_COLLAB_WS_URL__': '${collab.wsUrl}',
|
|
107
131
|
'__HEADROOM_COLLAB_ENABLED__': '${collabEnabledStr}',
|
|
132
|
+
'__HEADROOM_ADMIN_URL__': '${adminUrlForSubstitution}',
|
|
108
133
|
};
|
|
134
|
+
const discoveryFile = path.join('.well-known', 'headroom.json');
|
|
109
135
|
function walk(d) {
|
|
110
136
|
for (const f of fs.readdirSync(d)) {
|
|
111
137
|
const full = path.join(d, f);
|
|
112
138
|
if (fs.statSync(full).isDirectory()) walk(full);
|
|
113
|
-
else
|
|
139
|
+
else {
|
|
140
|
+
const isSubstitutable = f.endsWith('.js')
|
|
141
|
+
|| f.endsWith('.html')
|
|
142
|
+
|| full.endsWith(discoveryFile);
|
|
143
|
+
if (!isSubstitutable) continue;
|
|
114
144
|
let content = fs.readFileSync(full, 'utf8');
|
|
115
145
|
for (const [k, v] of Object.entries(replacements)) {
|
|
116
146
|
content = content.replaceAll(k, v);
|
|
@@ -120,6 +150,19 @@ export function createAdminSite(name: string, args: AdminSiteArgs) {
|
|
|
120
150
|
}
|
|
121
151
|
}
|
|
122
152
|
walk('.');
|
|
153
|
+
// Regression guard: the discovery doc must have all placeholders
|
|
154
|
+
// substituted by now. A surviving __HEADROOM_*__ would indicate a
|
|
155
|
+
// mismatch between the Vite-emitted file and the replacements map.
|
|
156
|
+
try {
|
|
157
|
+
const body = fs.readFileSync(discoveryFile, 'utf8');
|
|
158
|
+
if (/__HEADROOM_[A-Z_]+__/.test(body)) {
|
|
159
|
+
console.error('headroom: unsubstituted placeholders survive in ' + discoveryFile);
|
|
160
|
+
console.error(body);
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
} catch (e) {
|
|
164
|
+
if (e && e.code !== 'ENOENT') throw e;
|
|
165
|
+
}
|
|
123
166
|
"`,
|
|
124
167
|
output: ".",
|
|
125
168
|
},
|