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

161 lines
6.0 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 { exchangeKeys } from "./utils/cryptoKeys.js";
import { handleFileUpload, createPreviewUpdateFunction } from "./utils/uploadUtils.js";
import { syncSWStatus } from "./utils/swSync.js";
const app: CryptIOApp = rawApp as any;
const api: ComfyApi = rawApi;
// 注册扩展
app.registerExtension({
name: "cryptio.UploadImageCryptIO",
async setup() {
// 初始化时交换密钥
try {
await exchangeKeys();
console.log("CryptIO: Keys exchanged successfully");
await syncSWStatus();
} catch (error) {
console.error("CryptIO: Failed to exchange keys:", error);
}
},
async beforeRegisterNodeDef(
nodeType: any,
nodeData: ComfyNodeDef,
app: CryptIOApp
) {
if (nodeType.comfyClass === "UploadImageCryptIO") {
// 添加自定义的upload widget
const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function (this: CryptIONode) {
const r = onNodeCreated?.apply(this, arguments);
// Remove default upload widget
const uploadWidgetIndex = this.widgets.findIndex(w => w.name === "upload");
if (uploadWidgetIndex !== -1) {
this.removeWidget(this.widgets[uploadWidgetIndex]);
}
// 添加上传button widget
const uploadWidget = this.addWidget(
"button",
"cryptio-upload",
"image",
() => {
const fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.accept = "image/*,.encrypted";
fileInput.style.display = "none";
document.body.appendChild(fileInput);
fileInput.onchange = async () => {
if (fileInput.files && fileInput.files.length > 0) {
const file = fileInput.files[0];
await handleFileUpload(this, file, app as any);
}
document.body.removeChild(fileInput);
};
fileInput.click();
}
);
uploadWidget.label = "choose file to upload";
// 添加预览更新方法
this.updatePreview = createPreviewUpdateFunction(app as any);
// 当image值改变时更新预览
const imageWidget = this.widgets.find((w: any) => w.name === "image");
if (imageWidget) {
const originalCallback = imageWidget.callback;
if (this.updatePreview) {
this.updatePreview(imageWidget.value);
}
imageWidget.callback = async function (this: any, value: any) {
if (originalCallback) {
originalCallback.call(this, value);
}
if (value && value.endsWith(".encrypted")) {
const node = this.node as CryptIONode;
if (node.updatePreview) {
await node.updatePreview(value);
}
}
};
}
// 添加Drag and Drop支持
this.onDragOver = function (e: DragEvent) {
// 检查是否有文件
if (e.dataTransfer && e.dataTransfer.types.includes('Files')) {
// 阻止默认行为以允许drop
e.preventDefault();
e.stopPropagation();
// 设置拖放效果
e.dataTransfer.dropEffect = 'copy';
return true;
}
return false;
};
this.onDragDrop = async function (e: DragEvent) {
// 检查是否有文件
if (!e.dataTransfer || !e.dataTransfer.files || e.dataTransfer.files.length === 0) {
return false;
}
e.preventDefault();
e.stopPropagation();
// 获取拖放的文件
const file = e.dataTransfer.files[0];
// 检查是否为图片文件
if (!file.type.startsWith('image/')) {
alert('Please drop an image file');
return false;
}
// 上传文件
await handleFileUpload(this, file, app as any);
return true;
};
// 添加Paste支持
this.pasteFiles = async function (files: File[]) {
for (const file of files) {
// 查找图片类型
if (!file.type.startsWith('image/')) {
continue
}
// 上传文件
await handleFileUpload(this, file, app as any);
return true;
}
}
return r;
};
// Cleanup on node removal
const onRemoved = nodeType.prototype.onRemoved;
nodeType.prototype.onRemoved = function (this: CryptIONode) {
if (onRemoved) {
onRemoved.apply(this, arguments);
}
};
}
},
});