refactor: split renderer ipc and unraid domains
This commit is contained in:
@@ -0,0 +1,426 @@
|
||||
async function handleSetupAndSettingsActions(event, target, action, repository) {
|
||||
if (action === "setup-run-preflight")
|
||||
await runSystemPreflight({ setup: true });
|
||||
else if (action === "setup-continue") {
|
||||
if (ui.systemPreflight?.summary?.ready) {
|
||||
ui.setupStep = 1;
|
||||
render();
|
||||
}
|
||||
} else if (action === "setup-validate") {
|
||||
setLoading(true, "Validating Gitea connection…");
|
||||
try {
|
||||
ui.setupValidation = await window.forgeflow.validateGitea(ui.setupDraft);
|
||||
ui.setupDraft.baseUrl = ui.setupValidation.baseUrl;
|
||||
ui.setupDraft.user = ui.setupValidation.user;
|
||||
ui.setupStep = 2;
|
||||
} catch (error) {
|
||||
showToast("Connection failed", error.message, "error");
|
||||
}
|
||||
setLoading(false);
|
||||
} else if (action === "setup-add-root") {
|
||||
const root = await window.forgeflow.selectDirectory({
|
||||
title: "Select a development folder",
|
||||
});
|
||||
if (root && !ui.setupDraft.roots.includes(root))
|
||||
ui.setupDraft.roots.push(root);
|
||||
render();
|
||||
} else if (action === "setup-remove-root") {
|
||||
ui.setupDraft.roots.splice(Number(target.dataset.index), 1);
|
||||
render();
|
||||
} else if (action === "setup-next") {
|
||||
if (ui.setupStep === 2) {
|
||||
ui.setupStep = 3;
|
||||
ui.setupDraft.discovered = [];
|
||||
render();
|
||||
try {
|
||||
ui.setupDraft.discovered = await window.forgeflow.discoverRepositories(
|
||||
ui.setupDraft.roots,
|
||||
);
|
||||
} catch (error) {
|
||||
showToast("Discovery failed", error.message, "error");
|
||||
}
|
||||
ui.setupStep = 4;
|
||||
render();
|
||||
}
|
||||
} else if (action === "setup-back") {
|
||||
ui.setupStep = Math.max(0, ui.setupStep - 1);
|
||||
render();
|
||||
} else if (action === "setup-finish") {
|
||||
setLoading(true, "Saving configuration…");
|
||||
try {
|
||||
const result = await window.forgeflow.completeSetup({
|
||||
baseUrl: ui.setupDraft.baseUrl,
|
||||
token: ui.setupDraft.token,
|
||||
user: ui.setupDraft.user,
|
||||
workspaceRoots: ui.setupDraft.roots,
|
||||
});
|
||||
ui.boot.state = result.state;
|
||||
await refreshRepositories(false);
|
||||
showToast(
|
||||
"Setup complete",
|
||||
result.tokenState.persistent
|
||||
? "Your token is stored securely."
|
||||
: "Your token is available for this session only.",
|
||||
"success",
|
||||
);
|
||||
} catch (error) {
|
||||
showToast("Could not complete setup", error.message, "error");
|
||||
}
|
||||
setLoading(false);
|
||||
} else if (action === "check-updates") {
|
||||
ui.updateChecking = true;
|
||||
render();
|
||||
try {
|
||||
ui.updateStatus = await window.forgeflow.checkForUpdates();
|
||||
showToast(
|
||||
ui.updateStatus.available ? "Update available" : "ForgeFlow is current",
|
||||
ui.updateStatus.available
|
||||
? `Version ${ui.updateStatus.remoteVersion} can be downloaded.`
|
||||
: `Version ${ui.updateStatus.currentVersion} is the newest release.`,
|
||||
ui.updateStatus.available ? "success" : "info",
|
||||
);
|
||||
} catch (error) {
|
||||
showToast("Update check failed", error.message, "error");
|
||||
}
|
||||
ui.updateChecking = false;
|
||||
render();
|
||||
} else if (action === "save-update-settings") {
|
||||
const updates = {
|
||||
owner: document.querySelector("#update-owner").value.trim(),
|
||||
repo: document.querySelector("#update-repo").value.trim(),
|
||||
branch: document.querySelector("#update-branch").value.trim(),
|
||||
autoCheck: document.querySelector("#update-auto-check").value === "true",
|
||||
};
|
||||
try {
|
||||
ui.boot.state = await window.forgeflow.setUpdatePreferences(updates);
|
||||
ui.updateStatus = null;
|
||||
showToast(
|
||||
"Update settings saved",
|
||||
"The next check will use this repository and branch.",
|
||||
"success",
|
||||
);
|
||||
} catch (error) {
|
||||
showToast("Could not save update settings", error.message, "error");
|
||||
}
|
||||
render();
|
||||
} else if (action === "download-update") {
|
||||
setLoading(true, "Downloading and verifying the exact ForgeFlow update…");
|
||||
try {
|
||||
ui.updateStatus = await window.forgeflow.downloadUpdate();
|
||||
showToast(
|
||||
"Update downloaded",
|
||||
`Version ${ui.updateStatus.remoteVersion} passed the integrity check.`,
|
||||
"success",
|
||||
);
|
||||
} catch (error) {
|
||||
showToast("Update download failed", error.message, "error");
|
||||
}
|
||||
setLoading(false);
|
||||
} else if (action === "apply-update") {
|
||||
if (
|
||||
!confirm(
|
||||
`Apply ForgeFlow ${ui.updateStatus?.remoteVersion || "update"} now? ForgeFlow closes, validates the update and restarts automatically.`,
|
||||
)
|
||||
)
|
||||
return;
|
||||
setLoading(true, "Launching safe updater…");
|
||||
try {
|
||||
await window.forgeflow.applyUpdate();
|
||||
showToast(
|
||||
"Update launched",
|
||||
"ForgeFlow will close and restart after validation.",
|
||||
"success",
|
||||
);
|
||||
} catch (error) {
|
||||
showToast("Could not launch update", error.message, "error");
|
||||
setLoading(false);
|
||||
}
|
||||
} else if (action === "use-server-password") {
|
||||
ui.modal = {
|
||||
type: "server-password",
|
||||
serverId: target.dataset.serverId,
|
||||
retry: { type: target.dataset.retry || "scan" },
|
||||
};
|
||||
render();
|
||||
} else if (action === "confirm-server-password") {
|
||||
const server = (ui.boot?.state?.servers || []).find((item) => item.id === target.dataset.serverId);
|
||||
const password = document.querySelector("#quick-server-password")?.value || "";
|
||||
if (!server || !password) {
|
||||
showToast("Password required", "Enter the Unraid SSH password.", "error");
|
||||
return;
|
||||
}
|
||||
const retry = ui.modal?.retry || { type: "scan" };
|
||||
setLoading(true, "Switching the server connection to password authentication…");
|
||||
try {
|
||||
const saved = await window.forgeflow.saveServer(
|
||||
{ ...server, authType: "password", privateKeyPath: "" },
|
||||
password,
|
||||
"",
|
||||
);
|
||||
ui.boot.state = saved.state;
|
||||
const tested = await window.forgeflow.testServer(server.id);
|
||||
ui.boot.state = tested.state;
|
||||
ui.modal = null;
|
||||
showToast("Server password saved", "ForgeFlow will no longer use an SSH key for this server.", "success");
|
||||
if (retry.type === "deploy") {
|
||||
const retryRepository = ui.repositories.find((item) => item.fullName === retry.repositoryFullName);
|
||||
if (retryRepository) ui.selectedRepoId = retryRepository.id;
|
||||
await executeDeployment(retry.profileId);
|
||||
} else {
|
||||
await refreshDeploymentTruth(true);
|
||||
}
|
||||
} catch (error) {
|
||||
showToast("Server authentication failed", error.message, "error");
|
||||
}
|
||||
setLoading(false);
|
||||
} else if (action === "open-add-server") {
|
||||
ui.modal = {
|
||||
type: "server-config",
|
||||
serverId: null,
|
||||
authType: "password",
|
||||
};
|
||||
render();
|
||||
} else if (action === "edit-server") {
|
||||
const server = (ui.boot.state.servers || []).find(
|
||||
(item) => item.id === target.dataset.serverId,
|
||||
);
|
||||
ui.modal = {
|
||||
type: "server-config",
|
||||
serverId: target.dataset.serverId,
|
||||
authType: server?.authType || "password",
|
||||
};
|
||||
render();
|
||||
} else if (action === "select-private-key") {
|
||||
const keyPath = await window.forgeflow.selectKeyFile({
|
||||
title: "Select SSH private key",
|
||||
defaultPath:
|
||||
document.querySelector("#server-private-key")?.value || undefined,
|
||||
});
|
||||
if (keyPath) document.querySelector("#server-private-key").value = keyPath;
|
||||
} else if (action === "save-server") {
|
||||
const authType = document.querySelector("#server-auth-type").value;
|
||||
const server = {
|
||||
id: target.dataset.serverId || undefined,
|
||||
name: document.querySelector("#server-name").value.trim(),
|
||||
host: document.querySelector("#server-host").value.trim(),
|
||||
port: Number(document.querySelector("#server-port").value),
|
||||
username: document.querySelector("#server-username").value.trim(),
|
||||
authType,
|
||||
basePath: document.querySelector("#server-base-path").value.trim(),
|
||||
scanRoots: document.querySelector("#server-scan-roots").value.split(/\r?\n/).map((value) => value.trim()).filter(Boolean),
|
||||
scanExcludes: document.querySelector("#server-scan-excludes").value.split(",").map((value) => value.trim()).filter(Boolean),
|
||||
privateKeyPath:
|
||||
document.querySelector("#server-private-key")?.value.trim() || "",
|
||||
hostFingerprint: document
|
||||
.querySelector("#server-fingerprint")
|
||||
.value.trim(),
|
||||
};
|
||||
const password = document.querySelector("#server-password")?.value || "";
|
||||
const passphrase =
|
||||
document.querySelector("#server-passphrase")?.value || "";
|
||||
setLoading(true, "Saving encrypted SSH configuration…");
|
||||
try {
|
||||
const result = await window.forgeflow.saveServer(
|
||||
server,
|
||||
password,
|
||||
passphrase,
|
||||
);
|
||||
ui.boot.state = result.state;
|
||||
ui.modal = null;
|
||||
showToast(
|
||||
"Server saved",
|
||||
"Run Test & trust before creating a deployment.",
|
||||
"success",
|
||||
);
|
||||
} catch (error) {
|
||||
showToast("Could not save server", error.message, "error");
|
||||
}
|
||||
setLoading(false);
|
||||
} else if (action === "test-server") {
|
||||
setLoading(
|
||||
true,
|
||||
"Checking SSH identity, Docker, Compose and optional Git capabilities…",
|
||||
);
|
||||
try {
|
||||
const result = await window.forgeflow.testServer(target.dataset.serverId);
|
||||
ui.boot.state = result.state;
|
||||
const capabilities = result.capabilities || {};
|
||||
const deploymentReady =
|
||||
capabilities.docker && capabilities.dockerReady && capabilities.compose;
|
||||
showToast(
|
||||
deploymentReady ? "SSH server ready" : "SSH connected with missing tools",
|
||||
`${result.server.name} presented ${result.fingerprint}. Docker ${capabilities.dockerReady ? "ready" : "unavailable"}; Compose ${capabilities.compose ? "ready" : "missing"}.`,
|
||||
deploymentReady ? "success" : "info",
|
||||
);
|
||||
} catch (error) {
|
||||
showToast("SSH test failed", error.message, "error");
|
||||
}
|
||||
setLoading(false);
|
||||
} else if (action === "delete-server") {
|
||||
if (
|
||||
!confirm("Delete this server and all deployment profiles linked to it?")
|
||||
)
|
||||
return;
|
||||
try {
|
||||
ui.boot.state = await window.forgeflow.deleteServer(
|
||||
target.dataset.serverId,
|
||||
);
|
||||
ui.modal = null;
|
||||
await refreshRepositories(false);
|
||||
showToast(
|
||||
"Server deleted",
|
||||
"Linked SSH deployment profiles were removed.",
|
||||
"success",
|
||||
);
|
||||
} catch (error) {
|
||||
showToast("Could not delete server", error.message, "error");
|
||||
}
|
||||
} else if (action === "add-root") {
|
||||
const root = await window.forgeflow.selectDirectory({
|
||||
title: "Add development folder",
|
||||
});
|
||||
if (root && !ui.boot.state.workspaceRoots.includes(root))
|
||||
ui.boot.state.workspaceRoots.push(root);
|
||||
render();
|
||||
} else if (action === "remove-root") {
|
||||
ui.boot.state.workspaceRoots.splice(Number(target.dataset.index), 1);
|
||||
render();
|
||||
} else if (action === "save-roots") {
|
||||
const roots = [...document.querySelectorAll("[data-root-index]")]
|
||||
.map((input) => input.value.trim())
|
||||
.filter(Boolean);
|
||||
setLoading(true, "Saving workspace folders…");
|
||||
try {
|
||||
ui.boot.state = await window.forgeflow.setWorkspaceRoots(roots);
|
||||
await refreshRepositories(false);
|
||||
showToast(
|
||||
"Folders saved",
|
||||
"Repository discovery has been refreshed.",
|
||||
"success",
|
||||
);
|
||||
} catch (error) {
|
||||
showToast("Could not save folders", error.message, "error");
|
||||
}
|
||||
setLoading(false);
|
||||
} else if (action === "save-gitea-settings") {
|
||||
const baseUrl = document.querySelector("#settings-gitea-url").value.trim();
|
||||
const token = document.querySelector("#settings-gitea-token").value.trim();
|
||||
setLoading(true, "Validating Gitea…");
|
||||
try {
|
||||
const result = await window.forgeflow.updateGitea({ baseUrl, token });
|
||||
ui.boot.state = result.state;
|
||||
await refreshRepositories(false);
|
||||
showToast(
|
||||
"Gitea connected",
|
||||
`Signed in as ${result.validation.user.login}.`,
|
||||
"success",
|
||||
);
|
||||
} catch (error) {
|
||||
showToast("Connection failed", error.message, "error");
|
||||
}
|
||||
setLoading(false);
|
||||
} else if (action === "save-preferences") {
|
||||
const preferences = {
|
||||
autoRefresh:
|
||||
document.querySelector("#pref-auto-refresh").value === "true",
|
||||
repositoryPollSeconds: Number(
|
||||
document.querySelector("#pref-repo-poll").value,
|
||||
),
|
||||
operationPollSeconds: Number(
|
||||
document.querySelector("#pref-operation-poll").value,
|
||||
),
|
||||
preferredCloneProtocol: document.querySelector("#pref-clone-protocol")
|
||||
.value,
|
||||
};
|
||||
setLoading(true, "Saving background settings…");
|
||||
try {
|
||||
ui.boot.state = await window.forgeflow.setPreferences(preferences);
|
||||
await refreshRepositories(false);
|
||||
showToast(
|
||||
"Settings saved",
|
||||
"Background awareness has been updated.",
|
||||
"success",
|
||||
);
|
||||
} catch (error) {
|
||||
showToast("Could not save settings", error.message, "error");
|
||||
}
|
||||
setLoading(false);
|
||||
} else if (action === "save-desktop-preferences") {
|
||||
const splitArgs = (selector) =>
|
||||
document
|
||||
.querySelector(selector)
|
||||
.value.split("|")
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean);
|
||||
const preferences = {
|
||||
editor: {
|
||||
executable: document
|
||||
.querySelector("#pref-editor-executable")
|
||||
.value.trim(),
|
||||
args: splitArgs("#pref-editor-args"),
|
||||
},
|
||||
terminal: {
|
||||
executable: document
|
||||
.querySelector("#pref-terminal-executable")
|
||||
.value.trim(),
|
||||
args: splitArgs("#pref-terminal-args"),
|
||||
},
|
||||
notificationsEnabled: document.querySelector("#pref-notifications")
|
||||
.checked,
|
||||
trayEnabled: document.querySelector("#pref-tray").checked,
|
||||
closeToTray: document.querySelector("#pref-close-tray").checked,
|
||||
startAtLogin: document.querySelector("#pref-login").checked,
|
||||
};
|
||||
try {
|
||||
ui.boot.state = await window.forgeflow.setPreferences(preferences);
|
||||
showToast(
|
||||
"Desktop integration saved",
|
||||
"Editor, terminal, tray and notification settings are active.",
|
||||
"success",
|
||||
);
|
||||
} catch (error) {
|
||||
showToast("Could not save desktop integration", error.message, "error");
|
||||
}
|
||||
render();
|
||||
} else if (
|
||||
action === "export-config-backup" ||
|
||||
action === "import-config-backup"
|
||||
) {
|
||||
const passphrase = document.querySelector("#backup-passphrase").value;
|
||||
if (passphrase.length < 12) {
|
||||
showToast("Passphrase too short", "Use at least 12 characters.", "error");
|
||||
return;
|
||||
}
|
||||
setLoading(
|
||||
true,
|
||||
action === "export-config-backup"
|
||||
? "Encrypting configuration backup…"
|
||||
: "Decrypting and validating configuration…",
|
||||
);
|
||||
try {
|
||||
const result =
|
||||
action === "export-config-backup"
|
||||
? await window.forgeflow.exportConfigurationBackup(passphrase)
|
||||
: await window.forgeflow.importConfigurationBackup(passphrase);
|
||||
if (result?.state) {
|
||||
ui.boot.state = result.state;
|
||||
await refreshRepositories(false);
|
||||
}
|
||||
if (result)
|
||||
showToast(
|
||||
action === "export-config-backup"
|
||||
? "Encrypted backup created"
|
||||
: "Configuration restored",
|
||||
action === "export-config-backup"
|
||||
? result.filePath
|
||||
: `Backup from ${result.exportedAt} imported; credentials were preserved only where already present.`,
|
||||
"success",
|
||||
);
|
||||
} catch (error) {
|
||||
showToast("Configuration backup failed", error.message, "error");
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
else return false;
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user