@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.
- package/dist/auth/post-confirmation.js +2 -0
- package/dist/auth/user-admin.js +2 -0
- package/dist/chunk-BYXBJQAS.js +0 -0
- package/dist/events/dispatcher.js +4 -5
- package/dist/events/processor-trusted.d.ts +5 -5
- package/dist/events/processor-trusted.js +17 -22
- package/dist/events/processor-untrusted.js +4 -4
- package/dist/functions/api-key-renewer.js +2 -0
- package/dist/functions/mcp-handler.d.ts +16 -8
- package/dist/functions/mcp-handler.js +1146 -11
- package/dist/index.d.ts +47 -1
- package/dist/index.js +60 -33
- package/package.json +8 -2
package/dist/auth/user-admin.js
CHANGED
|
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 (
|
|
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: {
|
|
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
|
|
27
|
-
* and `writePublicAsset` (S3 PutObject under
|
|
28
|
-
* `public/plugins/{name}/{
|
|
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
|
|
32
|
-
*
|
|
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
|
|
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(
|
|
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:
|
|
41
|
-
KeyConditionExpression: "#
|
|
42
|
-
ExpressionAttributeNames: { "#
|
|
43
|
-
ExpressionAttributeValues: { ":
|
|
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
|
|
66
|
+
function makeContext(plugin) {
|
|
67
67
|
return {
|
|
68
|
-
siteId,
|
|
69
68
|
site: opts.site,
|
|
70
|
-
listPublishedPosts: () => listPublished(
|
|
69
|
+
listPublishedPosts: () => listPublished(),
|
|
71
70
|
async writePublicAsset(key, body, contentType) {
|
|
72
|
-
const objectKey = `public/plugins/${plugin.name}/${
|
|
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(
|
|
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":
|
|
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 =
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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,13 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MCP HTTP endpoint Lambda. Phase
|
|
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
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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).
|