fix: portable 绿色单机版 SSH 连接 + 主机数据 + 打包体积
Some checks failed
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
Some checks failed
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
SSH 连接修复: - main.cjs 顶部在所有 ssh2 require 之前安装 boringSslDhCompat 补丁 - 5 条 SSH 连接路径统一 legacyAlgorithms 默认值为 true portable 版主机数据修复: - main.cjs portable 模式设置 NETCATTY_PORTABLE=1 - preload.cjs 暴露 window.netcatty.isPortable - useVaultState portable 模式跳过 safeStorage DPAPI 加密的 hosts_v1 - inspection saveVaultHosts handler 补 legacyAlgorithms/tags/os 字段映射 打包体积修复: - 恢复 electron-builder.config.cjs 原始 40+ 条排除规则 - package.json 加 pend 运行时依赖
This commit is contained in:
@@ -1442,7 +1442,13 @@ export const useVaultState = () => {
|
||||
recoverPluginImporterTransaction(localStorageAdapter, PLUGIN_IMPORT_TRANSACTION_KEYS);
|
||||
});
|
||||
if (cancelled) return;
|
||||
const savedHosts = localStorageAdapter.read<Host[]>(STORAGE_KEY_HOSTS);
|
||||
// portable 版 safeStorage DPAPI 绑定 userData 路径,解不开 dev 复制过来的
|
||||
// LS 加密 blob。强制跳过 hosts_v1,直接走 else 分支从 inspection-data.json
|
||||
// 读明文 _vault_hosts(含 password + legacyAlgorithms)。
|
||||
const isPortable = (window as any).netcatty?.isPortable === true;
|
||||
const savedHosts = isPortable
|
||||
? undefined
|
||||
: localStorageAdapter.read<Host[]>(STORAGE_KEY_HOSTS);
|
||||
|
||||
if (savedHosts) {
|
||||
// Capture version before the async gap so that any write occurring
|
||||
@@ -1452,6 +1458,17 @@ export const useVaultState = () => {
|
||||
const decrypted = await decryptHosts(savedHosts);
|
||||
if (cancelled) return;
|
||||
if (ver === hostsWriteVersion.current) {
|
||||
// safeStorage 解密失败时 decryptField 会返回 enc:v1 占位符,
|
||||
// sanitizeHost 再把它过滤成 undefined → 密码/私钥消失。
|
||||
// 检查解密后是否大部分凭证都被清空了,是的话强制 fallback
|
||||
// 到 inspectionStorage 的明文 _vault_hosts(打包版 portable 场景)。
|
||||
const hasAnyPassword = decrypted.some((h: Host) =>
|
||||
h.password && !h.password.startsWith("enc:v1:"));
|
||||
const hasAnyPrivateKey = decrypted.some((h: Host) =>
|
||||
h.privateKey && !h.privateKey.startsWith("enc:v1:"));
|
||||
const credentialsLikelyLost =
|
||||
decrypted.length > 0 && !hasAnyPassword && !hasAnyPrivateKey;
|
||||
|
||||
const sanitized = normalizeVaultOrder(
|
||||
migrateHostsFromLegacyLineTimestamps(
|
||||
decrypted.map((host) => sanitizeHost(host)),
|
||||
@@ -1473,6 +1490,41 @@ export const useVaultState = () => {
|
||||
return commitEncryptedHostsUnderVaultLock(ver, sanitized, enc);
|
||||
});
|
||||
hostsWritePendingRef.current = writePromise;
|
||||
|
||||
// 解密后凭证丢失 → 用 inspectionStorage 的明文覆盖
|
||||
if (credentialsLikelyLost) {
|
||||
const nc = (window as any).netcatty;
|
||||
if (nc?.inspection?.getVaultHosts) {
|
||||
try {
|
||||
const vaultHosts = await nc.inspection.getVaultHosts();
|
||||
if (Array.isArray(vaultHosts) && vaultHosts.length > 0) {
|
||||
const fallback: Host[] = vaultHosts.map((v: any) => ({
|
||||
id: v.id,
|
||||
label: v.label || "",
|
||||
hostname: v.hostname || "",
|
||||
port: v.port || 22,
|
||||
username: v.username || "",
|
||||
password: v.password || undefined,
|
||||
identityFileId: v.identityFileId || undefined,
|
||||
privateKey: v.privateKey || "",
|
||||
passphrase: v.passphrase || "",
|
||||
tags: v.tags || [],
|
||||
os: v.os || "linux",
|
||||
type: v.type || "",
|
||||
distro: v.distro || "",
|
||||
group: v.group || "",
|
||||
legacyAlgorithms: v.legacyAlgorithms,
|
||||
}));
|
||||
const fbSanitized = normalizeVaultOrder(
|
||||
fallback.map((h) => sanitizeHost(h)),
|
||||
);
|
||||
if (cancelled) return;
|
||||
hostsRef.current = fbSanitized;
|
||||
setHosts(fbSanitized);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 本地 Local Storage 为空(可能是 origin 隔离导致的打包版环境)
|
||||
@@ -1499,6 +1551,7 @@ export const useVaultState = () => {
|
||||
type: v.type || "",
|
||||
distro: v.distro || "",
|
||||
group: v.group || "",
|
||||
legacyAlgorithms: v.legacyAlgorithms,
|
||||
}));
|
||||
if (cancelled) return;
|
||||
const sanitized = normalizeVaultOrder(
|
||||
|
||||
Reference in New Issue
Block a user