@ampless/backend 1.0.0-beta.78 → 1.0.0-beta.80

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.
@@ -64,14 +64,6 @@ function decryptSecret(rawKey, b64) {
64
64
  const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
65
65
  return plaintext.toString("utf8");
66
66
  }
67
- function safeParse(s) {
68
- try {
69
- return JSON.parse(s);
70
- } catch (err) {
71
- console.warn("[trusted-processor] post.body is not valid JSON; passing through as string", err);
72
- return s;
73
- }
74
- }
75
67
  function createProcessorTrustedHandler(opts) {
76
68
  const privilegedHookedPlugins = (opts.plugins ?? []).filter(
77
69
  (p) => typeof p === "object" && p.trust_level === "privileged" && !!p.hooks && Object.keys(p.hooks).length > 0
@@ -145,7 +137,15 @@ function createProcessorTrustedHandler(opts) {
145
137
  title: row.title,
146
138
  excerpt: row.excerpt ?? void 0,
147
139
  format: row.format ?? "markdown",
148
- body: typeof row.body === "string" ? safeParse(row.body) : row.body,
140
+ // DynamoDBDocumentClient unmarshals AWSJSON-backed attributes
141
+ // into native JS types (S→string, N→number, BOOL→boolean,
142
+ // M→object), so `row.body` is already correctly typed here and
143
+ // must NOT be re-parsed. Re-running JSON.parse on a pure-numeric
144
+ // or JSON-looking string body (e.g. a markdown/html body of
145
+ // "1470" or "123") double-decodes it into a number and corrupts
146
+ // the post. Only the Amplify GraphQL-client read path returns an
147
+ // AWSJSON wire string and needs decodeAwsJson.
148
+ body: row.body,
149
149
  status: row.status ?? "published",
150
150
  publishedAt: row.publishedAt ?? void 0,
151
151
  tags: Array.isArray(row.tags) ? row.tags : []
@@ -281,7 +281,7 @@ function createProcessorTrustedHandler(opts) {
281
281
  const sk = row.sk;
282
282
  if (!sk) continue;
283
283
  const raw = row.value;
284
- settings[sk] = typeof raw === "string" ? safeParse(raw) : raw;
284
+ settings[sk] = raw;
285
285
  }
286
286
  exclusiveStartKey = res.LastEvaluatedKey;
287
287
  } while (exclusiveStartKey);
@@ -314,7 +314,7 @@ function createProcessorTrustedHandler(opts) {
314
314
  try {
315
315
  await rebuildSiteSettingsCache();
316
316
  } catch (err) {
317
- console.error("[trusted-processor] site-settings-cache rebuild failed", err);
317
+ console.error("[trusted-processor][ALERT] site-settings-cache rebuild failed", err);
318
318
  throw err;
319
319
  }
320
320
  }
package/dist/index.d.ts CHANGED
@@ -170,6 +170,7 @@ declare function amplessAuthConfig(opts?: AmplessAuthConfigOpts): Parameters<typ
170
170
  * Build the ampless S3 bucket configuration as a plain options object
171
171
  * suitable for `defineStorage(...)`. Access map:
172
172
  * - `public/media/*` — guest read, admin/editor full
173
+ * - `public/static/*` — guest read, admin/editor full (static-bundle posts)
173
174
  * - `public/plugins/*` — guest read, admin full
174
175
  *
175
176
  * Bucket-level overrides (PublicAccessBlock, CORS, IAM policies for
package/dist/index.js CHANGED
@@ -7,6 +7,9 @@ import { FunctionUrlAuthType, HttpMethod, StartingPosition } from "aws-cdk-lib/a
7
7
  import { DynamoEventSource, SqsEventSource } from "aws-cdk-lib/aws-lambda-event-sources";
8
8
  import { Rule, Schedule } from "aws-cdk-lib/aws-events";
9
9
  import { LambdaFunction } from "aws-cdk-lib/aws-events-targets";
10
+ function siteSettingsCacheS3Resources(bucketArn) {
11
+ return [`${bucketArn}/public/plugins/*`, `${bucketArn}/public/site-settings.json`];
12
+ }
10
13
  function defineAmplessBackend(opts) {
11
14
  const backend = defineBackend({
12
15
  auth: opts.auth,
@@ -152,17 +155,15 @@ function defineAmplessBackend(opts) {
152
155
  new PolicyStatement({
153
156
  effect: Effect.ALLOW,
154
157
  actions: ["s3:PutObject", "s3:DeleteObject"],
155
- resources: [
156
- `${backend.storage.resources.bucket.bucketArn}/public/plugins/*`,
157
- // Built-in cache: rebuildSiteSettingsCache writes the single
158
- // JSON file the public site reads. Exact-match resource — a
159
- // wildcard like `public/site-settings/*` would NOT match the
160
- // single-file key `public/site-settings.json` and the
161
- // PutObject would fail silently with AccessDenied, so the
162
- // public site would never see admin-side theme / settings
163
- // changes.
164
- `${backend.storage.resources.bucket.bucketArn}/public/site-settings.json`
165
- ]
158
+ // Built-in cache: rebuildSiteSettingsCache writes the single JSON
159
+ // file the public site reads. The resource list MUST contain the
160
+ // exact-match key `public/site-settings.json` a wildcard like
161
+ // `public/site-settings/*` would NOT match the single-file key and
162
+ // the PutObject would fail silently with AccessDenied, so the public
163
+ // site would never see admin-side theme / settings changes. Extracted
164
+ // into siteSettingsCacheS3Resources() so backend.test.ts can guard
165
+ // the exact ARN against this regression.
166
+ resources: siteSettingsCacheS3Resources(backend.storage.resources.bucket.bucketArn)
166
167
  })
167
168
  );
168
169
  const pluginSecretTable = backend.data.resources.tables["PluginSecret"];
@@ -303,6 +304,16 @@ function amplessStorageConfig() {
303
304
  allow.guest.to(["read"]),
304
305
  allow.groups(["ampless-admin", "ampless-editor"]).to(["read", "write", "delete"])
305
306
  ],
307
+ // Static-bundle posts (`format: 'static'`) upload their files to
308
+ // `public/static/<slug>/...` from the browser admin via Cognito
309
+ // identity-pool credentials. Without this rule the admin group's
310
+ // role is denied `s3:PutObject` and saving the post fails. Mirrors
311
+ // the media grant; the MCP/Lambda upload path is granted separately
312
+ // on the function role in `defineAmplessBackend`.
313
+ "public/static/*": [
314
+ allow.guest.to(["read"]),
315
+ allow.groups(["ampless-admin", "ampless-editor"]).to(["read", "write", "delete"])
316
+ ],
306
317
  "public/plugins/*": [
307
318
  allow.guest.to(["read"]),
308
319
  allow.groups(["ampless-admin"]).to(["read", "write", "delete"])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ampless/backend",
3
- "version": "1.0.0-beta.78",
3
+ "version": "1.0.0-beta.80",
4
4
  "description": "Amplify Gen 2 backend factories for ampless: auth, data, storage, event processors, API key renewer",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -69,8 +69,8 @@
69
69
  "@smithy/protocol-http": "^5.4.4",
70
70
  "@smithy/signature-v4": "^5.4.4",
71
71
  "fflate": "^0.8.3",
72
- "@ampless/mcp-server": "1.0.0-beta.58",
73
- "ampless": "1.0.0-beta.52"
72
+ "@ampless/mcp-server": "1.0.0-beta.59",
73
+ "ampless": "1.0.0-beta.53"
74
74
  },
75
75
  "peerDependencies": {
76
76
  "@aws-amplify/backend": "^1.19.0",