feat(workbench): 插件素材与画布视觉 - 完成 Eagle 外部来源与节点可读性适配
This commit is contained in:
1 parent
b54e001425
commit
ec5aa5ef82
35 files changed
+2512
-214
No files matched your search
@@ -0,0 +1,124 @@
|
||||
import "@/lib/plugins/builtin";
|
||||
|
||||
import { getRegisteredPlugin } from "@/lib/plugins/plugin-registry";
|
||||
import { pluginStorageFor } from "@/lib/plugins/plugin-storage";
|
||||
import type { Asset } from "@/stores/use-asset-store";
|
||||
import { usePluginStore } from "@/stores/use-plugin-store";
|
||||
import type { AssetSourceProvider, ExternalAssetItem, PluginInstallation } from "@/lib/plugins/plugin-types";
|
||||
|
||||
export type HostedAssetSource = {
|
||||
id: string;
|
||||
name: string;
|
||||
provider: AssetSourceProvider;
|
||||
};
|
||||
|
||||
export type ExternalAssetSyncRecord = {
|
||||
sourceId: string;
|
||||
sourceName: string;
|
||||
status: "synced" | "failed";
|
||||
externalId?: string;
|
||||
folderId?: string;
|
||||
error?: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export type ExternalAssetSyncResult = {
|
||||
records: ExternalAssetSyncRecord[];
|
||||
uploaded: ExternalAssetItem[];
|
||||
failures: Array<{ sourceId: string; sourceName: string; error: string }>;
|
||||
};
|
||||
|
||||
export function createHostedAssetSources(installations: PluginInstallation[]): HostedAssetSource[] {
|
||||
return installations.flatMap((installation) => {
|
||||
if (!installation.enabled) return [];
|
||||
const plugin = getRegisteredPlugin(installation.manifest.id);
|
||||
if (!plugin?.createAssetSource) return [];
|
||||
try {
|
||||
return [
|
||||
{
|
||||
id: plugin.manifest.id,
|
||||
name: plugin.manifest.name,
|
||||
provider: plugin.createAssetSource({
|
||||
manifest: plugin.manifest,
|
||||
permissions: new Set(plugin.manifest.permissions),
|
||||
storage: pluginStorageFor(plugin.manifest.id),
|
||||
config: installation.config,
|
||||
}),
|
||||
},
|
||||
];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function uploadGeneratedAssetToConfiguredSources(asset: Asset, signal?: AbortSignal): Promise<ExternalAssetSyncResult> {
|
||||
const installations = usePluginStore.getState().installations;
|
||||
const autoUploadInstallations = installations.filter((installation) => installation.enabled && isAutoUploadEnabled(installation.config.autoUploadGenerated));
|
||||
if (!autoUploadInstallations.length) return { records: readSyncRecords(asset), uploaded: [], failures: [] };
|
||||
|
||||
const sources = createHostedAssetSources(installations);
|
||||
const records = readSyncRecords(asset);
|
||||
const uploaded: ExternalAssetItem[] = [];
|
||||
const failures: ExternalAssetSyncResult["failures"] = [];
|
||||
for (const installation of autoUploadInstallations) {
|
||||
if (signal?.aborted) throw new DOMException("请求已取消", "AbortError");
|
||||
const source = sources.find((candidate) => candidate.id === installation.manifest.id);
|
||||
const folderId = stringConfig(installation.config.generatedFolderId);
|
||||
const existing = records.find((record) => record.sourceId === installation.manifest.id);
|
||||
if (existing?.status === "synced" && existing.folderId === folderId) continue;
|
||||
|
||||
if (!source) {
|
||||
const error = "插件未能加载素材来源";
|
||||
replaceSyncRecord(records, { sourceId: installation.manifest.id, sourceName: installation.manifest.name, status: "failed", folderId, error, updatedAt: new Date().toISOString() });
|
||||
failures.push({ sourceId: installation.manifest.id, sourceName: installation.manifest.name, error });
|
||||
continue;
|
||||
}
|
||||
if (!source.provider.uploadAsset && !source.provider.uploadAssetToFolder) {
|
||||
const error = "当前插件不支持自动写入生成结果";
|
||||
replaceSyncRecord(records, { sourceId: source.id, sourceName: source.name, status: "failed", folderId, error, updatedAt: new Date().toISOString() });
|
||||
failures.push({ sourceId: source.id, sourceName: source.name, error });
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const item = source.provider.uploadAssetToFolder ? await source.provider.uploadAssetToFolder(asset, folderId, signal) : await source.provider.uploadAsset!(asset, signal);
|
||||
uploaded.push(item);
|
||||
replaceSyncRecord(records, { sourceId: source.id, sourceName: source.name, status: "synced", externalId: item.id, folderId, updatedAt: new Date().toISOString() });
|
||||
} catch (reason) {
|
||||
if (reason instanceof DOMException && reason.name === "AbortError") throw reason;
|
||||
const error = reason instanceof Error ? reason.message : "插件写入失败";
|
||||
replaceSyncRecord(records, { sourceId: source.id, sourceName: source.name, status: "failed", folderId, error, updatedAt: new Date().toISOString() });
|
||||
failures.push({ sourceId: source.id, sourceName: source.name, error });
|
||||
}
|
||||
}
|
||||
return { records, uploaded, failures };
|
||||
}
|
||||
|
||||
export function externalAssetSyncFailures(asset: Asset) {
|
||||
return readSyncRecords(asset).filter((record) => record.status === "failed");
|
||||
}
|
||||
|
||||
function isAutoUploadEnabled(value: unknown) {
|
||||
return value !== false && value !== "false";
|
||||
}
|
||||
|
||||
function stringConfig(value: unknown) {
|
||||
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
||||
}
|
||||
|
||||
function readSyncRecords(asset: Asset): ExternalAssetSyncRecord[] {
|
||||
const raw = asset.metadata?.externalSync;
|
||||
if (!Array.isArray(raw)) return [];
|
||||
return raw.filter((item): item is ExternalAssetSyncRecord => {
|
||||
if (!item || typeof item !== "object") return false;
|
||||
const candidate = item as Partial<ExternalAssetSyncRecord>;
|
||||
return typeof candidate.sourceId === "string" && typeof candidate.sourceName === "string" && (candidate.status === "synced" || candidate.status === "failed") && typeof candidate.updatedAt === "string";
|
||||
});
|
||||
}
|
||||
|
||||
function replaceSyncRecord(records: ExternalAssetSyncRecord[], next: ExternalAssetSyncRecord) {
|
||||
const index = records.findIndex((record) => record.sourceId === next.sourceId);
|
||||
if (index === -1) records.push(next);
|
||||
else records[index] = next;
|
||||
}
|
||||
Reference in new issue
Block a user