@ampless/backend 1.0.0-alpha.34 → 1.0.0-alpha.36
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.
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "@aws-sdk/lib-dynamodb";
|
|
10
10
|
import {
|
|
11
11
|
formatPublicAssetUrl,
|
|
12
|
+
isValidPluginKey,
|
|
12
13
|
validatePublicAssetKey
|
|
13
14
|
} from "ampless";
|
|
14
15
|
|
|
@@ -57,7 +58,16 @@ function safeParse(s) {
|
|
|
57
58
|
function createProcessorTrustedHandler(opts) {
|
|
58
59
|
const trustedPlugins = (opts.plugins ?? []).filter(
|
|
59
60
|
(p) => typeof p === "object" && p.trust_level === "trusted"
|
|
60
|
-
)
|
|
61
|
+
).filter((p) => {
|
|
62
|
+
const ns = p.instanceId ?? p.name;
|
|
63
|
+
if (!isValidPluginKey(ns)) {
|
|
64
|
+
console.warn(
|
|
65
|
+
`[trusted-processor] plugin "${p.name}" (instanceId="${p.instanceId ?? "(none)"}") has invalid namespace "${ns}". Must match /^[a-zA-Z0-9_-]+$/. Plugin skipped \u2014 no hooks will run for it.`
|
|
66
|
+
);
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return true;
|
|
70
|
+
});
|
|
61
71
|
const seenNamespaces = /* @__PURE__ */ new Set();
|
|
62
72
|
for (const plugin of trustedPlugins) {
|
|
63
73
|
const ns = plugin.instanceId ?? plugin.name;
|
|
@@ -368,6 +368,90 @@ async function uploadMedia(graphql, storage, args) {
|
|
|
368
368
|
});
|
|
369
369
|
return { media: data.createMedia, url: putResult.url };
|
|
370
370
|
}
|
|
371
|
+
var GET_BY_ID2 = (
|
|
372
|
+
/* GraphQL */
|
|
373
|
+
`
|
|
374
|
+
query GetMedia($mediaId: ID!) {
|
|
375
|
+
getMedia(mediaId: $mediaId) {
|
|
376
|
+
mediaId
|
|
377
|
+
src
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
`
|
|
381
|
+
);
|
|
382
|
+
var GET_BY_SRC = (
|
|
383
|
+
/* GraphQL */
|
|
384
|
+
`
|
|
385
|
+
query GetMediaBySrc($src: String!) {
|
|
386
|
+
getMediaBySrc(src: $src) {
|
|
387
|
+
mediaId
|
|
388
|
+
src
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
`
|
|
392
|
+
);
|
|
393
|
+
var DELETE = (
|
|
394
|
+
/* GraphQL */
|
|
395
|
+
`
|
|
396
|
+
mutation DeleteMedia($input: DeleteMediaInput!) {
|
|
397
|
+
deleteMedia(input: $input) {
|
|
398
|
+
mediaId
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
`
|
|
402
|
+
);
|
|
403
|
+
var deleteMediaSchema = {
|
|
404
|
+
type: "object",
|
|
405
|
+
properties: {
|
|
406
|
+
mediaId: {
|
|
407
|
+
type: "string",
|
|
408
|
+
description: "Media row primary key (e.g. 'media-1714400000000-abc123' as returned by `upload_media`). Provide either `mediaId` or `src`; mediaId is the direct path."
|
|
409
|
+
},
|
|
410
|
+
src: {
|
|
411
|
+
type: "string",
|
|
412
|
+
description: "Full S3 key including `public/` (e.g. 'public/media/2026/05/1714400000000-photo.jpg'). Use when you only have the public URL \u2014 strip `/api/media/` and prepend `public/`. Provide either `mediaId` or `src`."
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
async function deleteMedia(graphql, storage, args) {
|
|
417
|
+
if (!args.mediaId && !args.src) {
|
|
418
|
+
throw new Error("delete_media: provide `mediaId` or `src`");
|
|
419
|
+
}
|
|
420
|
+
let mediaId;
|
|
421
|
+
let src;
|
|
422
|
+
if (args.mediaId) {
|
|
423
|
+
const data = await graphql.query(GET_BY_ID2, { mediaId: args.mediaId });
|
|
424
|
+
if (data.getMedia) {
|
|
425
|
+
mediaId = data.getMedia.mediaId;
|
|
426
|
+
src = data.getMedia.src;
|
|
427
|
+
}
|
|
428
|
+
} else {
|
|
429
|
+
const data = await graphql.query(GET_BY_SRC, { src: args.src });
|
|
430
|
+
if (data.getMediaBySrc) {
|
|
431
|
+
mediaId = data.getMediaBySrc.mediaId;
|
|
432
|
+
src = data.getMediaBySrc.src;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
if (!mediaId) {
|
|
436
|
+
if (args.src) {
|
|
437
|
+
await storage.deleteObject(args.src);
|
|
438
|
+
return {
|
|
439
|
+
deleted: false,
|
|
440
|
+
reason: "media row not found; S3 object delete attempted (idempotent)",
|
|
441
|
+
src: args.src
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
return {
|
|
445
|
+
deleted: false,
|
|
446
|
+
reason: "media row not found for mediaId"
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
await storage.deleteObject(src);
|
|
450
|
+
await graphql.query(DELETE, {
|
|
451
|
+
input: { mediaId }
|
|
452
|
+
});
|
|
453
|
+
return { deleted: true, mediaId, src };
|
|
454
|
+
}
|
|
371
455
|
var getSchemaSchema = {
|
|
372
456
|
type: "object",
|
|
373
457
|
properties: {}
|
|
@@ -1275,6 +1359,17 @@ var tools = [
|
|
|
1275
1359
|
inputSchema: uploadMediaSchema,
|
|
1276
1360
|
handler: (args, ctx) => uploadMedia(ctx.graphql, ctx.storage(), args)
|
|
1277
1361
|
},
|
|
1362
|
+
{
|
|
1363
|
+
name: "delete_media",
|
|
1364
|
+
description: "Delete a media file: removes the S3 object and the Media row. Pass `mediaId` (from `upload_media`'s response) or `src` (full S3 key like `public/media/2026/05/...`). When only `src` is given, looks up the Media row via `getMediaBySrc`. S3 delete runs first, then the DDB row delete \u2014 both are idempotent so re-running converges on missing-key cases. Returns `{ deleted: false }` instead of throwing when no Media row matches; if `src` was supplied directly the S3 object is still removed (use this to sweep orphan files).",
|
|
1365
|
+
inputSchema: deleteMediaSchema,
|
|
1366
|
+
handler: (args, ctx) => deleteMedia(
|
|
1367
|
+
ctx.graphql,
|
|
1368
|
+
ctx.storage(),
|
|
1369
|
+
args
|
|
1370
|
+
),
|
|
1371
|
+
destructive: true
|
|
1372
|
+
},
|
|
1278
1373
|
{
|
|
1279
1374
|
name: "get_schema",
|
|
1280
1375
|
description: "Returns the CMS content schema (Post/Page/Media field shapes, format enum, notes). Useful as the first call to understand what fields are available.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ampless/backend",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.36",
|
|
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",
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"@smithy/protocol-http": "^5.4.4",
|
|
66
66
|
"@smithy/signature-v4": "^5.4.4",
|
|
67
67
|
"fflate": "^0.8.3",
|
|
68
|
-
"ampless": "1.0.0-alpha.
|
|
69
|
-
"@ampless/mcp-server": "1.0.0-alpha.
|
|
68
|
+
"ampless": "1.0.0-alpha.20",
|
|
69
|
+
"@ampless/mcp-server": "1.0.0-alpha.25"
|
|
70
70
|
},
|
|
71
71
|
"peerDependencies": {
|
|
72
72
|
"@aws-amplify/backend": "^1",
|