Files
ForgeFlow/src/renderer/actions/setup-and-settings.js
T

427 lines
15 KiB
JavaScript

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;
}