feat(blossom): Phase 2 management — blob delete-on-orphan + kind-10063 server list (#30)

- deleteByUrl maps a Blossom URL back to its content-addressed blob (blobSha256FromUrl) and deletes it via BUD-12, defaulting to the URL's own origin. Wired into the edit-modal orphan cleanup so a superseded Blossom video/thumbnail is reclaimed (best-effort, never blocks the edit).

- BUD-03 kind-10063 server-list discovery: read (serversFromServerListEvent) + build (buildBlossomServerListEvent) + an extracted, testable import/publish helper (blossomServerList.js), plus 'Import from my server list' / 'Publish my server list' controls in the Storage pane's Blossom section.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
thePR0M3TH3ANandClaude Opus 4.8 committed 2026-07-12 10:32:22 -04:00
1 parent 51072e4871
commit e81e65ff40
9 files changed
+515

No files matched your search

+24
View File
@@ -1,5 +1,9 @@
import { devLogger } from "../utils/logger.js";
import r2Service from "../services/r2Service.js";
import blossomService, {
blobSha256FromUrl,
} from "../services/blossomService.js";
import { getActiveSigner } from "../nostrClientRegistry.js";
import { readRelayPublishSummary } from "../nostrPublish.js";
export default class EditModalController {
@@ -125,6 +129,26 @@ export default class EditModalController {
`[edit] Removed ${cleanup.deleted.length} superseded storage object(s).`
);
}
// Blossom orphans are content-addressed blobs, not R2/S3 keys — delete
// them via BUD-12 (best-effort, from each blob URL's own origin).
const blossomUrls = orphans
.map((o) => o.url || o.thumbnail || "")
.filter((u) => blobSha256FromUrl(u));
if (blossomUrls.length) {
const activeSigner = getActiveSigner();
if (activeSigner && typeof activeSigner.signEvent === "function") {
const signer = (draft) => activeSigner.signEvent(draft);
for (const url of blossomUrls) {
const res = await blossomService.deleteByUrl({ url, signer });
if (res.deleted?.length) {
devLogger.log(
`[edit] Deleted superseded Blossom blob ${res.sha256}.`
);
}
}
}
}
}
} catch (cleanupErr) {
devLogger.warn(