@ampless/backend 0.2.0-alpha.9 → 1.0.0-alpha.20

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.
@@ -1,3 +1,5 @@
1
+ import "../chunk-BYXBJQAS.js";
2
+
1
3
  // src/auth/post-confirmation.ts
2
4
  import {
3
5
  CognitoIdentityProviderClient,
@@ -1,3 +1,5 @@
1
+ import "../chunk-BYXBJQAS.js";
2
+
1
3
  // src/auth/user-admin.ts
2
4
  import {
3
5
  CognitoIdentityProviderClient,
File without changes
@@ -1,3 +1,5 @@
1
+ import "../chunk-BYXBJQAS.js";
2
+
1
3
  // src/events/dispatcher.ts
2
4
  import { unmarshall } from "@aws-sdk/util-dynamodb";
3
5
  import {
@@ -29,7 +31,6 @@ function emitContentEvents(record, timestamp) {
29
31
  if (types.length === 0) return [];
30
32
  const item = newItem ?? oldItem ?? {};
31
33
  const payload = {
32
- siteId: item.siteId,
33
34
  postId: item.postId,
34
35
  slug: item.slug,
35
36
  title: item.title,
@@ -43,13 +44,11 @@ function emitKvEvents(record, timestamp) {
43
44
  const item = record.dynamodb?.NewImage ?? record.dynamodb?.OldImage ? unmarshall(record.dynamodb.NewImage ?? record.dynamodb.OldImage) : {};
44
45
  const pk = item.pk;
45
46
  if (!pk) return [];
46
- if (!pk.startsWith("siteconfig:")) return [];
47
- const siteId = pk.slice("siteconfig:".length);
48
- if (!siteId) return [];
47
+ if (pk !== "siteconfig") return [];
49
48
  return [
50
49
  {
51
50
  type: "site.settings.updated",
52
- payload: { siteId },
51
+ payload: {},
53
52
  timestamp
54
53
  }
55
54
  ];
@@ -23,13 +23,13 @@ interface CreateProcessorTrustedHandlerOpts {
23
23
  }
24
24
  /**
25
25
  * SQS-driven trusted plugin executor. Trusted plugins get a runtime
26
- * context with `listPublishedPosts` (one Query per site partition)
27
- * and `writePublicAsset` (S3 PutObject under
28
- * `public/plugins/{name}/{siteId}/{key}`).
26
+ * context with `listPublishedPosts` (one Query against the byStatus
27
+ * GSI) and `writePublicAsset` (S3 PutObject under
28
+ * `public/plugins/{name}/{key}`).
29
29
  *
30
30
  * Built-in: rebuilds the site-settings JSON cache at
31
- * `public/site-settings/{siteId}.json` whenever a
32
- * `site.settings.updated` event arrives.
31
+ * `public/site-settings.json` whenever a `site.settings.updated`
32
+ * event arrives.
33
33
  *
34
34
  * Re-exported by the template's thin shell
35
35
  * `amplify/events/processor-trusted/handler.ts` which supplies the
@@ -1,3 +1,5 @@
1
+ import "../chunk-BYXBJQAS.js";
2
+
1
3
  // src/events/processor-trusted.ts
2
4
  import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
3
5
  import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
@@ -10,7 +12,7 @@ function requireEnv(name) {
10
12
  if (!v) throw new Error(`processor-trusted: missing required env var ${name}`);
11
13
  return v;
12
14
  }
13
- var POST_BY_SITE_STATUS_INDEX = "bySiteIdStatus";
15
+ var POST_BY_STATUS_INDEX = "byStatus";
14
16
  function safeParse(s) {
15
17
  try {
16
18
  return JSON.parse(s);
@@ -29,18 +31,17 @@ function createProcessorTrustedHandler(opts) {
29
31
  const POST_TABLE = requireEnv("AMPLESS_POST_TABLE");
30
32
  const KV_TABLE = requireEnv("AMPLESS_KV_TABLE");
31
33
  const REGION = requireEnv("AWS_REGION");
32
- async function listPublished(siteId) {
34
+ async function listPublished() {
33
35
  const items = [];
34
36
  let exclusiveStartKey;
35
- const partition = `${siteId}#published`;
36
37
  do {
37
38
  const res = await ddb.send(
38
39
  new QueryCommand({
39
40
  TableName: POST_TABLE,
40
- IndexName: POST_BY_SITE_STATUS_INDEX,
41
- KeyConditionExpression: "#siteIdStatus = :siteIdStatus",
42
- ExpressionAttributeNames: { "#siteIdStatus": "siteIdStatus" },
43
- ExpressionAttributeValues: { ":siteIdStatus": partition },
41
+ IndexName: POST_BY_STATUS_INDEX,
42
+ KeyConditionExpression: "#status = :status",
43
+ ExpressionAttributeNames: { "#status": "status" },
44
+ ExpressionAttributeValues: { ":status": "published" },
44
45
  ScanIndexForward: false,
45
46
  ExclusiveStartKey: exclusiveStartKey
46
47
  })
@@ -48,7 +49,6 @@ function createProcessorTrustedHandler(opts) {
48
49
  for (const row of res.Items ?? []) {
49
50
  items.push({
50
51
  postId: row.postId,
51
- siteId: row.siteId,
52
52
  slug: row.slug,
53
53
  title: row.title,
54
54
  excerpt: row.excerpt ?? void 0,
@@ -63,13 +63,12 @@ function createProcessorTrustedHandler(opts) {
63
63
  } while (exclusiveStartKey);
64
64
  return items;
65
65
  }
66
- function makeContext(plugin, siteId) {
66
+ function makeContext(plugin) {
67
67
  return {
68
- siteId,
69
68
  site: opts.site,
70
- listPublishedPosts: () => listPublished(siteId),
69
+ listPublishedPosts: () => listPublished(),
71
70
  async writePublicAsset(key, body, contentType) {
72
- const objectKey = `public/plugins/${plugin.name}/${siteId}/${key}`;
71
+ const objectKey = `public/plugins/${plugin.name}/${key}`;
73
72
  await s3.send(
74
73
  new PutObjectCommand({
75
74
  Bucket: BUCKET,
@@ -83,7 +82,7 @@ function createProcessorTrustedHandler(opts) {
83
82
  }
84
83
  };
85
84
  }
86
- async function rebuildSiteSettingsCache(siteId) {
85
+ async function rebuildSiteSettingsCache() {
87
86
  const settings = {};
88
87
  let exclusiveStartKey;
89
88
  do {
@@ -92,7 +91,7 @@ function createProcessorTrustedHandler(opts) {
92
91
  TableName: KV_TABLE,
93
92
  KeyConditionExpression: "#pk = :pk",
94
93
  ExpressionAttributeNames: { "#pk": "pk" },
95
- ExpressionAttributeValues: { ":pk": `siteconfig:${siteId}` },
94
+ ExpressionAttributeValues: { ":pk": "siteconfig" },
96
95
  ExclusiveStartKey: exclusiveStartKey
97
96
  })
98
97
  );
@@ -104,7 +103,7 @@ function createProcessorTrustedHandler(opts) {
104
103
  }
105
104
  exclusiveStartKey = res.LastEvaluatedKey;
106
105
  } while (exclusiveStartKey);
107
- const objectKey = `public/site-settings/${siteId}.json`;
106
+ const objectKey = "public/site-settings.json";
108
107
  await s3.send(
109
108
  new PutObjectCommand({
110
109
  Bucket: BUCKET,
@@ -129,15 +128,11 @@ function createProcessorTrustedHandler(opts) {
129
128
  console.error("[trusted-processor] bad message", record.body, err);
130
129
  continue;
131
130
  }
132
- const siteId = parsed.payload.siteId ?? "default";
133
131
  if (parsed.type === "site.settings.updated") {
134
132
  try {
135
- await rebuildSiteSettingsCache(siteId);
133
+ await rebuildSiteSettingsCache();
136
134
  } catch (err) {
137
- console.error(
138
- `[trusted-processor] site-settings-cache rebuild failed for ${siteId}`,
139
- err
140
- );
135
+ console.error("[trusted-processor] site-settings-cache rebuild failed", err);
141
136
  throw err;
142
137
  }
143
138
  }
@@ -145,7 +140,7 @@ function createProcessorTrustedHandler(opts) {
145
140
  const hook = plugin.hooks?.[parsed.type];
146
141
  if (!hook) continue;
147
142
  try {
148
- await hook(parsed, makeContext(plugin, siteId));
143
+ await hook(parsed, makeContext(plugin));
149
144
  } catch (err) {
150
145
  console.error(`[trusted-processor] ${plugin.name}.${parsed.type} failed`, err);
151
146
  throw err;
@@ -1,11 +1,12 @@
1
+ import "../chunk-BYXBJQAS.js";
2
+
1
3
  // src/events/processor-untrusted.ts
2
4
  function createProcessorUntrustedHandler(opts) {
3
5
  const untrustedPlugins = (opts.plugins ?? []).filter(
4
6
  (p) => typeof p === "object" && p.trust_level === "untrusted"
5
7
  );
6
- function makeContext(siteId) {
8
+ function makeContext() {
7
9
  return {
8
- siteId,
9
10
  site: opts.site,
10
11
  async listPublishedPosts() {
11
12
  throw new Error("untrusted plugins cannot list posts");
@@ -25,12 +26,11 @@ function createProcessorUntrustedHandler(opts) {
25
26
  console.error("[untrusted-processor] bad message", record.body, err);
26
27
  continue;
27
28
  }
28
- const siteId = parsed.payload.siteId ?? "default";
29
29
  for (const plugin of untrustedPlugins) {
30
30
  const hook = plugin.hooks?.[parsed.type];
31
31
  if (!hook) continue;
32
32
  try {
33
- await hook(parsed, makeContext(siteId));
33
+ await hook(parsed, makeContext());
34
34
  } catch (err) {
35
35
  console.error(`[untrusted-processor] ${plugin.name}.${parsed.type} failed`, err);
36
36
  throw err;
@@ -1,3 +1,5 @@
1
+ import "../chunk-BYXBJQAS.js";
2
+
1
3
  // src/functions/api-key-renewer.ts
2
4
  import {
3
5
  AppSyncClient,
@@ -1,13 +1,21 @@
1
1
  /**
2
- * MCP HTTP endpoint Lambda. Phase 3: Bearer token validation only.
2
+ * MCP HTTP endpoint Lambda. Phase 5: Bearer validation + JSON-RPC 2.0
3
+ * tool dispatch over AppSync IAM auth, including `upload_media`.
3
4
  *
4
- * Reads the KvStore table directly (PK = 'mcp-tokens', SK = SHA-256
5
- * hash of the plaintext token) instead of going through AppSync — the
6
- * Lambda IAM role only needs `dynamodb:GetItem` on that one table,
7
- * which is much narrower than `appsync:GraphQL` and avoids the
8
- * IAM-auth-mode complexity of letting Lambdas call AppSync as
9
- * privileged identity. Phase 4 will add AppSync IAM auth when tool
10
- * dispatch actually needs schema-aware access.
5
+ * 1. Reads KvStore directly (PK = 'mcp-tokens', SK = SHA-256 hex)
6
+ * to validate `Authorization: Bearer amk_...`. Same narrow IAM
7
+ * grant as Phase 3 (`dynamodb:GetItem` on the KvStore table).
8
+ * 2. Parses the incoming JSON-RPC envelope by hand (no MCP SDK
9
+ * stdio transport in a Lambda runtime overkill for the three
10
+ * verbs we actually need).
11
+ * 3. Dispatches `tools/call` through the shared `@ampless/mcp-server/tools`
12
+ * registry. The GraphqlClient implementation hits AppSync with
13
+ * SigV4 (`AWS_IAM` auth mode), gated by `allow.resource(mcpHandler)
14
+ * .to(['query', 'mutate'])` on Post / PostTag in the schema.
15
+ * 4. `upload_media` decodes the base64 body inline and uploads to S3
16
+ * using the Lambda execution role (s3:PutObject on public/media/*).
17
+ * Payload limit ~6 MB (base64-inflated) covers typical CMS images;
18
+ * large files should use the stdio CLI.
11
19
  *
12
20
  * Function URL event format: Lambda Function URLs emit API Gateway
13
21
  * HTTP v2 events (https://docs.aws.amazon.com/lambda/latest/dg/urls-invocation.html#urls-payloads).