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.
Files changed (135) hide show
  1. package/admin/.well-known/headroom.json +9 -0
  2. package/admin/assets/{AdminsPage-BnzH9TL3.js → AdminsPage-lGd1MFW3.js} +1 -1
  3. package/admin/assets/AllContentPage-YykTt4xH.js +1 -0
  4. package/admin/assets/{ApiKeysPage-DEAa8eyC.js → ApiKeysPage-BCpNEKxF.js} +1 -1
  5. package/admin/assets/{AuditPage-BN9yNsxh.js → AuditPage-DBKiujoI.js} +1 -1
  6. package/admin/assets/BackupsPage-DCrmYnQc.js +1 -0
  7. package/admin/assets/{BlockEditor-3wnisTOZ.js → BlockEditor-BQVXN_Yn.js} +3 -3
  8. package/admin/assets/BlockTypeEditPage-BZmvEezy.js +1 -0
  9. package/admin/assets/{BlockTypesPage-Dhkho6T_.js → BlockTypesPage-CAr2JDj9.js} +1 -1
  10. package/admin/assets/BulkActionBar-CgHMxSkm.js +1 -0
  11. package/admin/assets/{CollectionEditPage-lOb4hEZy.js → CollectionEditPage-BQoXWAB-.js} +1 -1
  12. package/admin/assets/{CollectionsPage-CgtOloa1.js → CollectionsPage-Dakk_NyE.js} +1 -1
  13. package/admin/assets/{ContentCreatePage-LeQjahp_.js → ContentCreatePage-d-xxhoxO.js} +1 -1
  14. package/admin/assets/ContentEditPage-CUi_mFRC.js +1 -0
  15. package/admin/assets/{ContentField-pilCbdnA.js → ContentField-D6UO6W9G.js} +1 -1
  16. package/admin/assets/ContentListPage-8riubzdw.js +1 -0
  17. package/admin/assets/{CustomBlockPreview-DNnTFM0z.js → CustomBlockPreview-BuRfUKgt.js} +1 -1
  18. package/admin/assets/FieldRenderer-CUVay-yx.js +2 -0
  19. package/admin/assets/FileTypeIcon-DRD08pW_.js +1 -0
  20. package/admin/assets/FloatingComposerController-D4uLQfUX-Dc1_NZ4l.js +1 -0
  21. package/admin/assets/{IconPicker-CpIgiQTC.js → IconPicker-DR-6b0GI.js} +2 -2
  22. package/admin/assets/{LoginPage-D9ZsGLIi.js → LoginPage-DbHhSxgY.js} +1 -1
  23. package/admin/assets/MediaField-eRyEALYH.js +1 -0
  24. package/admin/assets/MediaPage-DW85F2UV.js +1 -0
  25. package/admin/assets/{Pagination-Df9nQ7Z0.js → Pagination-DTboMLK6.js} +1 -1
  26. package/admin/assets/{RelationshipPicker-B3Ftmqxp.js → RelationshipPicker-VVZN4dlP.js} +1 -1
  27. package/admin/assets/{SiteSettingsPage-6NvH7CiQ.js → SiteSettingsPage-5-HzcJLC.js} +1 -1
  28. package/admin/assets/{SiteUserEditPage-D5VaQ1Xq.js → SiteUserEditPage-C_PPvEye.js} +1 -1
  29. package/admin/assets/{SiteUsersPage-BYVduiqs.js → SiteUsersPage-CIgMMi-T.js} +1 -1
  30. package/admin/assets/{SitesPage-rfWWE0yK.js → SitesPage-DkG0gyHT.js} +1 -1
  31. package/admin/assets/SubmissionDetailPage-BwFWjkVu.js +1 -0
  32. package/admin/assets/SubmissionEditPage-C6Og25NH.js +1 -0
  33. package/admin/assets/SubmissionListPage-spPh8dwQ.js +1 -0
  34. package/admin/assets/{TagInput-57c4DG1w.js → TagInput-DxktEo07.js} +1 -1
  35. package/admin/assets/{TagsPage-BEO5AwCv.js → TagsPage-Dcc1IkF7.js} +1 -1
  36. package/admin/assets/TrashPage-8ybk22ZM.js +1 -0
  37. package/admin/assets/{UsersPage-BpIRorJ1.js → UsersPage-D5uXgLV4.js} +1 -1
  38. package/admin/assets/{WebhookEditPage-D5xgi56h.js → WebhookEditPage-T5KsZGqe.js} +1 -1
  39. package/admin/assets/{WebhooksPage-BY7AaiGr.js → WebhooksPage-DEIu5cX0.js} +1 -1
  40. package/admin/assets/{card-C9hfyHXf.js → card-CVAiqxnT.js} +1 -1
  41. package/admin/assets/{checkbox-DVJcwUt1.js → checkbox-BO-Fusdb.js} +1 -1
  42. package/admin/assets/{command-Bfmj0MEL.js → command-D0ojin3H.js} +1 -1
  43. package/admin/assets/{contentStatus-CkPi9Dh6.js → contentStatus-WXGfd7vX.js} +1 -1
  44. package/admin/assets/format-BRcflvs9.js +1 -0
  45. package/admin/assets/index-BCa3VYjL.css +1 -0
  46. package/admin/assets/{index-Ce5pmRMj.js → index-CbEa9yyd.js} +10 -10
  47. package/admin/assets/listCellValue-BUdbRyCz.js +1 -0
  48. package/admin/assets/{popover-CzaQYEEP.js → popover-B9WNjj2t.js} +1 -1
  49. package/admin/assets/{select-CrRhFGIi.js → select-B2yTIHJT.js} +1 -1
  50. package/admin/assets/{serializeToText-2VrsuRUh.js → serializeToText-Tv-7pIdy.js} +1 -1
  51. package/admin/assets/{table-_3bMY0_z.js → table-C3EQVw1z.js} +1 -1
  52. package/admin/assets/{textarea-6fq0R6VV.js → textarea-DIHLWabG.js} +1 -1
  53. package/admin/assets/{useAdminResolver-BJNPz3OG.js → useAdminResolver-_OHcUYwq.js} +1 -1
  54. package/admin/assets/useContent-DLo6FUYZ.js +1 -0
  55. package/admin/assets/{useContentSearch-B3aTjuCu.js → useContentSearch-D-D0veFh.js} +1 -1
  56. package/admin/assets/{usePageTitle-C1r1-C00.js → usePageTitle-BJ2ARyuc.js} +1 -1
  57. package/admin/assets/{useSiteUsers-DIaqgNSp.js → useSiteUsers-Bn3GiEYB.js} +1 -1
  58. package/admin/assets/{useTags-B-HgMVwo.js → useTags-CyGo0zXa.js} +1 -1
  59. package/admin/assets/useTrash-BFJMIP8N.js +1 -0
  60. package/admin/assets/{useWebhooks-BvZjUJkJ.js → useWebhooks-BgJL2-Ek.js} +1 -1
  61. package/admin/index.html +2 -2
  62. package/admin/sw.js +1 -1
  63. package/admin/workbox-362996ec.js +1 -0
  64. package/dist/admin-site.d.ts.map +1 -1
  65. package/dist/admin-site.js +46 -3
  66. package/dist/admin-site.js.map +1 -1
  67. package/dist/api.d.ts +2 -0
  68. package/dist/api.d.ts.map +1 -1
  69. package/dist/api.js +57 -5
  70. package/dist/api.js.map +1 -1
  71. package/dist/backup.d.ts +29 -0
  72. package/dist/backup.d.ts.map +1 -0
  73. package/dist/backup.js +95 -0
  74. package/dist/backup.js.map +1 -0
  75. package/dist/cdn-api.d.ts.map +1 -1
  76. package/dist/cdn-api.js +20 -19
  77. package/dist/cdn-api.js.map +1 -1
  78. package/dist/cron.d.ts +42 -0
  79. package/dist/cron.d.ts.map +1 -0
  80. package/dist/cron.js +128 -0
  81. package/dist/cron.js.map +1 -0
  82. package/dist/image.d.ts +8 -1
  83. package/dist/image.d.ts.map +1 -1
  84. package/dist/image.js +26 -6
  85. package/dist/image.js.map +1 -1
  86. package/dist/index.d.ts +6 -0
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +50 -1
  89. package/dist/index.js.map +1 -1
  90. package/dist/storage.d.ts +1 -0
  91. package/dist/storage.d.ts.map +1 -1
  92. package/dist/storage.js +21 -0
  93. package/dist/storage.js.map +1 -1
  94. package/dist/webhooks.d.ts +4 -3
  95. package/dist/webhooks.d.ts.map +1 -1
  96. package/dist/webhooks.js +22 -35
  97. package/dist/webhooks.js.map +1 -1
  98. package/lambda/api/bootstrap +0 -0
  99. package/lambda/backup-worker/bootstrap +0 -0
  100. package/lambda/image-lambda/index.mjs +30 -6
  101. package/lambda/image-lambda/node_modules/.package-lock.json +3 -3
  102. package/lambda/image-lambda/node_modules/semver/classes/range.js +7 -0
  103. package/lambda/image-lambda/node_modules/semver/package.json +1 -1
  104. package/lambda/image-lambda/node_modules/semver/ranges/subset.js +2 -2
  105. package/lambda/trash-sweeper/bootstrap +0 -0
  106. package/lambda/webhook-worker/bootstrap +0 -0
  107. package/package.json +1 -1
  108. package/src/admin-site.ts +46 -3
  109. package/src/api.ts +58 -5
  110. package/src/backup.ts +114 -0
  111. package/src/cdn-api.ts +20 -22
  112. package/src/cron.ts +153 -0
  113. package/src/image.ts +30 -6
  114. package/src/index.ts +58 -1
  115. package/src/sst-env.d.ts +21 -0
  116. package/src/storage.ts +22 -0
  117. package/src/webhooks.ts +22 -39
  118. package/admin/assets/AllContentPage-BtObN6oy.js +0 -1
  119. package/admin/assets/BlockTypeEditPage-C2evAESK.js +0 -1
  120. package/admin/assets/BulkActionBar-BxdfUSrN.js +0 -1
  121. package/admin/assets/ContentEditPage-xczr4d_h.js +0 -1
  122. package/admin/assets/ContentListPage-BAKDn1Xy.js +0 -1
  123. package/admin/assets/FieldRenderer-DiOKvkWV.js +0 -2
  124. package/admin/assets/FilterBar-BZoa63zh.js +0 -1
  125. package/admin/assets/FloatingComposerController-D4uLQfUX-BMIvFCoE.js +0 -1
  126. package/admin/assets/MediaField-CxccCFGQ.js +0 -1
  127. package/admin/assets/MediaPage-QvMaH2YJ.js +0 -1
  128. package/admin/assets/SubmissionDetailPage-BSUR685F.js +0 -1
  129. package/admin/assets/SubmissionEditPage-DjLXHjWU.js +0 -1
  130. package/admin/assets/SubmissionListPage-DBxNEvde.js +0 -1
  131. package/admin/assets/format-C88SDH8g.js +0 -1
  132. package/admin/assets/index-BB9Syqw2.css +0 -1
  133. package/admin/assets/useContent-Bs7nel7C.js +0 -1
  134. package/admin/assets/useMedia-ae3s_ajC.js +0 -1
  135. 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, SQS queues, worker Lambda)
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":"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"}
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;
@@ -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;;;;;;;;;;;;;;;EAoLzC;AAED,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC"}
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,
@@ -1 +1 @@
1
- {"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Webhook Infrastructure
3
3
  *
4
- * 2 DynamoDB tables + 2 SQS queues + webhook worker Lambda.
5
- * Supports dev mode (Go source) and package mode (pre-compiled binary).
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
- webhookDeliveryQueue: sst.aws.Queue;
20
+ webhookWorker: sst.aws.Function;
20
21
  webhookDeliveryDLQ: sst.aws.Queue;
21
22
  };
22
23
  export type WebhookResources = ReturnType<typeof createWebhooks>;
@@ -1 +1 @@
1
- {"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;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;;;;;EA4F7D;AAED,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC"}
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 + 2 SQS queues + webhook worker Lambda.
5
- * Supports dev mode (Go source) and package mode (pre-compiled binary).
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
- link: [webhookDeliveries, webhooks, args.sites],
68
- permissions: [
69
- {
70
- actions: [
71
- "sqs:ReceiveMessage",
72
- "sqs:DeleteMessage",
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
- new aws.lambda.EventSourceMapping(`${name}WebhookWorkerEventSource`, {
80
- eventSourceArn: webhookDeliveryQueue.arn,
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
- batchSize: 1,
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
- webhookDeliveryQueue,
74
+ webhookWorker,
88
75
  webhookDeliveryDLQ,
89
76
  };
90
77
  }
@@ -1 +1 @@
1
- {"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"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"}
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"}
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 SIGNING_SECRET = process.env.IMAGE_SIGNING_SECRET;
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 expectedSig = computeSignature(rawPath, params);
48
- if (!timingSafeEqual(Buffer.from(sig), Buffer.from(expectedSig))) {
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", SIGNING_SECRET).update(canonical).digest("hex").substring(0, 32);
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.0",
101
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz",
102
- "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==",
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
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "semver",
3
- "version": "7.8.0",
3
+ "version": "7.8.1",
4
4
  "description": "The semantic version parser used by npm.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -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 === '>=' && !satisfies(gt.semver, String(c), options)) {
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 === '<=' && !satisfies(lt.semver, String(c), options)) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "headroom-cms",
3
- "version": "0.1.11",
3
+ "version": "0.2.1",
4
4
  "description": "Headroom CMS — Serverless headless CMS for AWS, deployed with SST",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
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 if (f.endsWith('.js') || f.endsWith('.html')) {
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
  },