427 lines
15 KiB
JavaScript
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;
|
|
}
|