Files
Suzu008 a58a5b8917 refactor(sw): bidirectional SW status sync with IndexedDB persistence
Replace fire-and-forget key delivery with a sync-status protocol that
verifies key consistency between main thread and Service Worker using
SHA-256 fingerprints via MessageChannel request-response.

- SW (ts/sw.ts): persist keys in IndexedDB, auto-restore on activate;
  respond to sync-status queries with loaded keys + fingerprints;
  support cryptio-clear-keys to wipe memory + DB.
- New ts/utils/swSync.ts: single entry point for SW registration,
  key sync, and status verification. Controller change listener
  re-syncs on SW restart. Deduplicates concurrent sync calls.
- Deleted ts/utils/swKeySender.ts (replaced by swSync.ts).
- Remove SW registration (~55 lines) from cryptioSaveImage.ts;
  setup() now calls syncSWStatus().
- uploadUtils.ts: drop 5-retry preview loop (~6.4s worst case);
  single attempt with syncSWStatus fallback on failure.
- cryptioSettings.ts: sync after key changes; clearSWKeys() on
  Clear All.
- cryptioUploadImage/Video.ts: syncSWStatus in setup + preview
  fallback.
- types.ts: add SWStatus interface.
2026-05-30 22:29:45 +08:00

101 lines
4.6 KiB
TypeScript

//@ts-ignore
import { app as rawApp } from "../../scripts/app.js";
//@ts-ignore
import { api as rawApi } from "../../scripts/api.js";
import type {
ComfyApp,
ComfyApi,
ComfyNodeDef,
} from "@comfyorg/comfyui-frontend-types";
import type { CryptIONode, CryptIOApp } from "./types.js";
import { downloadDecryptedImage } from "./utils/imageLoader.js";
import { syncSWStatus } from "./utils/swSync.js";
const app: CryptIOApp = rawApp as any;
const api: ComfyApi = rawApi;
// ──────────────────────────────────────────────────
// Node extension
// ──────────────────────────────────────────────────
app.registerExtension({
name: "cryptio.SaveImageCryptIO",
async setup() {
await syncSWStatus();
},
async beforeRegisterNodeDef(
nodeType: any,
nodeData: ComfyNodeDef,
app: CryptIOApp
) {
if (nodeType.comfyClass === "SaveImageCryptIO" || nodeType.comfyClass === "PreviewImageCryptIO") {
// 覆盖默认的图片处理
const onExecuted = nodeType.prototype.onExecuted;
nodeType.prototype.onExecuted = function (this: CryptIONode, message: any) {
// 调用原始处理 (populates this.imgs from message.images — SW解密透明)
if (onExecuted) {
onExecuted.apply(this, arguments);
}
// Auto-download: fetch the /view URL directly, SW decrypts transparently
if (message?.cryptio_images) {
const autoDownload = this.widgets.find((n: any) => n.name === "auto_download")?.value;
for (let i = 0; i < message.cryptio_images.length; i++) {
const imageInfo = message.cryptio_images[i];
if (autoDownload && imageInfo.filename && imageInfo.filename.endsWith(".encrypted")) {
const params = new URLSearchParams(imageInfo);
const url = api.apiURL(`/view?${params}`);
fetch(url)
.then((r) => r.blob())
.then((blob) => {
const downloadUrl = URL.createObjectURL(blob);
downloadDecryptedImage(downloadUrl, imageInfo.filename);
URL.revokeObjectURL(downloadUrl);
console.log(
"[CryptIO] Auto-downloaded:", imageInfo.filename.replace(/\.encrypted$/, "")
);
})
.catch((err) => console.error("[CryptIO] Auto-download failed:", err));
}
}
}
};
// 添加右键菜单支持查看/下载图片
const getExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
nodeType.prototype.getExtraMenuOptions = function (this: CryptIONode, canvas: any, options: any[]) {
if (getExtraMenuOptions) {
getExtraMenuOptions.apply(this, arguments);
}
if (this.imgs && this.imgs.length > 0) {
options.push({
content: "下载解密图片",
callback: async () => {
if (this.imgs) {
for (let i = 0; i < this.imgs.length; i++) {
const img = this.imgs[i];
const filename = this.images?.[i]?.filename || `image_${i}.png.encrypted`;
if (img.src) {
fetch(img.src)
.then((r) => r.blob())
.then((blob) => {
const url = URL.createObjectURL(blob);
downloadDecryptedImage(url, filename);
URL.revokeObjectURL(url);
})
.catch((err) => console.error("[CryptIO] Download failed:", err));
}
}
}
},
});
}
};
}
},
});