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.
161 lines
6.0 KiB
TypeScript
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);
|
|
}
|
|
};
|
|
}
|
|
},
|
|
});
|