Files
SAMY/samy.js
2025-12-01 02:53:27 -05:00

482 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Use globals provided by the PowerShell-generated HTML bridge
const tasks = (window.SAMY_TASKS || []);
const defaultPage = (window.SAMY_DEFAULT_PAGE || "onboard");
let completedTasks = 0;
let totalTasks = 0;
// Progress / title handling
function setTotalTaskCount(count) {
totalTasks = count;
completedTasks = 0;
updateTitle();
}
function logProgress(label, isSuccess) {
const statusBox = document.getElementById("status-box");
completedTasks++;
updateTitle();
const msg = isSuccess
? ` ${completedTasks}/${totalTasks} done: ${label}`
: ` ${completedTasks}/${totalTasks} failed: ${label}`;
const div = document.createElement("div");
div.style.color = isSuccess ? "lime" : "red";
div.textContent = msg;
statusBox?.appendChild(div);
if (completedTasks === totalTasks) {
const finalMsg = document.createElement("div");
finalMsg.style.marginTop = "10px";
finalMsg.innerHTML = `<strong> All tasks completed (${completedTasks}/${totalTasks})</strong>`;
statusBox?.appendChild(finalMsg);
document.title = ` ScriptMonkey - Complete (${completedTasks}/${totalTasks})`;
const sound = new Audio(
"data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEAESsAACJWAAACABAAZGF0YQAAAAA="
);
sound.play().catch(() => {});
flashTitle(document.title);
}
}
function updateTitle() {
document.title = `ScriptMonkey - ${completedTasks}/${totalTasks} Done`;
}
function flashTitle(finalTitle) {
let flashes = 0;
const interval = setInterval(() => {
document.title = document.title === "" ? finalTitle : "";
flashes++;
if (flashes >= 10) {
clearInterval(interval);
document.title = finalTitle;
}
}, 800);
}
// =======================================================================
// Tab Navigation
// =======================================================================
document.addEventListener("DOMContentLoaded", () => {
const tabButtons = document.querySelectorAll(".tab-button");
const tabContents = document.querySelectorAll(".tab-content");
if (!tabButtons?.length || !tabContents?.length) {
console.error("ScriptMonkey: no tab buttons or tab contents found.");
return;
}
tabButtons.forEach((btn) => {
btn.addEventListener("click", () => {
tabButtons.forEach((b) => b.classList.remove("active"));
tabContents.forEach((c) => c.classList.remove("active"));
btn.classList.add("active");
const targetId = btn.dataset.tab;
const target = document.getElementById(targetId);
if (target) target.classList.add("active");
});
});
// Default tab from PS (onboard/offboard/tweaks/SVSApps)
const defaultTabId = `${defaultPage}Tab`;
const defaultBtn = document.querySelector(`.tab-button[data-tab='${defaultTabId}']`);
const defaultTab = document.getElementById(defaultTabId);
if (defaultBtn) defaultBtn.classList.add("active");
if (defaultTab) defaultTab.classList.add("active");
});
// =======================================================================
// Onboarding: Select-all left/right columns
// =======================================================================
function toggleColumn(col) {
const master = document.getElementById(
`selectAll${col[0].toUpperCase() + col.slice(1)}Checkbox`
);
const children = document.querySelectorAll(
`#onboardTab input[type=checkbox][data-column=${col}]`
);
children.forEach((cb) => {
cb.checked = master.checked;
});
setTimeout(() => {
children.forEach((cb) => {
cb.dispatchEvent(new Event("change"));
});
}, 0);
}
function updateSelectAll(col) {
const master = document.getElementById(
`selectAll${col[0].toUpperCase() + col.slice(1)}Checkbox`
);
const children = document.querySelectorAll(
`#onboardTab input[type=checkbox][data-column=${col}]`
);
master.checked = Array.from(children).every((cb) => cb.checked);
}
document.addEventListener("DOMContentLoaded", () => {
["left", "right"].forEach((col) => {
document
.querySelectorAll(`#onboardTab input[type=checkbox][data-column=${col}]`)
.forEach((cb) =>
cb.addEventListener("change", () => updateSelectAll(col))
);
});
});
// =======================================================================
// Off-boarding Select All
// =======================================================================
function toggleOffboardAll() {
const master = document.getElementById("offboardSelectAll");
const children = document.querySelectorAll(
"#offboardTab input[type=checkbox]:not(#offboardSelectAll)"
);
children.forEach((cb) => {
cb.checked = master.checked;
});
}
function updateOffboardSelectAll() {
const master = document.getElementById("offboardSelectAll");
if (!master) return;
const children = document.querySelectorAll(
"#offboardTab input[type=checkbox]:not(#offboardSelectAll)"
);
if (!children.length) {
master.checked = false;
return;
}
master.checked = Array.from(children).every((cb) => cb.checked);
}
document.addEventListener("DOMContentLoaded", () => {
const offChildren = document.querySelectorAll(
"#offboardTab input[type=checkbox]:not(#offboardSelectAll)"
);
if (!offChildren?.length) return;
offChildren.forEach((cb) =>
cb.addEventListener("change", updateOffboardSelectAll)
);
updateOffboardSelectAll();
});
// =======================================================================
// DattoRMM options + Enter key handling
// =======================================================================
function toggleDattoRMMOptions() {
const master = document.getElementById("installDattoRMM");
const container = document.getElementById("installDattoRMMOptionsContainer");
if (!container) return;
const checked = master?.checked;
container.style.display = checked ? "block" : "none";
container
.querySelectorAll('input[type="checkbox"]')
.forEach((cb) => (cb.checked = checked));
}
document.addEventListener("DOMContentLoaded", () => {
const master = document.getElementById("installDattoRMM");
if (master) {
master.addEventListener("change", toggleDattoRMMOptions);
}
const passwordField = document.getElementById("Password");
const goButton = document.querySelector("button[onclick='fetchSites()']");
if (passwordField && goButton) {
passwordField.addEventListener("keydown", (e) => {
if (e.key === "Enter") {
goButton.click();
}
});
}
const siteDropdown = document.getElementById("dattoDropdown");
const runButton = document.querySelector(".run-button");
if (siteDropdown && runButton) {
siteDropdown.addEventListener("keydown", (e) => {
if (e.key === "Enter" && siteDropdown.value) {
runButton.click();
}
});
}
});
// =======================================================================
// Fetch Sites handler (calls /getpw)
// =======================================================================
async function fetchSites() {
const pwdInput = document.getElementById("Password");
const pwd = pwdInput?.value;
if (!pwd) {
alert("Please enter the password.");
return;
}
const dropdown = document.getElementById("dattoDropdown");
dropdown.innerHTML = '<option disabled selected>Loading sites...</option>';
try {
const resp = await fetch("/getpw", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ password: pwd }),
});
if (!resp.ok) throw "HTTP " + resp.status;
const sites = await resp.json();
dropdown.innerHTML = "";
sites.forEach((site) => {
const option = document.createElement("option");
option.value = site.UID;
option.textContent = site.Name;
dropdown.appendChild(option);
});
document.getElementById("dattoRmmContainer").style.display = "block";
} catch (e) {
console.error(e);
dropdown.innerHTML =
'<option disabled selected>Error loading sites</option>';
alert("Failed to fetch sites. Check password and try again.");
}
}
// =======================================================================
// Run Selected (main trigger)
// =======================================================================
// =======================================================================
// Run Selected (main trigger)
// =======================================================================
async function triggerInstall() {
const runBtn = document.querySelector(".run-button");
if (!runBtn) return;
runBtn.disabled = true;
const statusBox = document.getElementById("status-box");
if (statusBox) statusBox.innerHTML = "";
try {
// Figure out which standard tasks are checked
const checkedTasks = tasks.filter((t) => {
const cb = document.getElementById(t.id);
return cb && cb.checked;
});
// Rename checkbox / textbox
const renameCB = document.getElementById("chkRenameComputer");
const newNameInput = document.getElementById("txtNewComputerName");
// Count how many "extra" tasks (rename) were doing
let extraTasks = 0;
if (renameCB && renameCB.checked) {
extraTasks = 1; // treat rename as one task in the progress counter
}
setTotalTaskCount(checkedTasks.length + extraTasks);
// 1. DattoRMM first
const dattoCB = document.getElementById("installDattoRMM");
if (dattoCB && dattoCB.checked) {
try {
const sub = Array.from(
document.querySelectorAll(".sub-option-installDattoRMM:checked")
).map((x) => x.value);
const dropdown = document.getElementById("dattoDropdown");
const uid = dropdown?.value;
const name = dropdown?.selectedOptions?.[0]?.text || "Datto";
await fetch("/installDattoRMM", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ checkedValues: sub, UID: uid, Name: name }),
});
logProgress("Install DattoRMM", true);
} catch (e) {
logProgress("Install DattoRMM", false);
console.error(e);
}
}
// 2. SVSMSP module second
const svsCB = document.getElementById("installSVSMSPModule");
if (svsCB && svsCB.checked) {
try {
await fetch("/installSVSMSPModule", { method: "GET" });
logProgress("Install SVSMSP Module", true);
} catch (e) {
logProgress("Install SVSMSP Module", false);
console.error(e);
}
}
// 3. Remaining tasks
for (const t of tasks) {
if (["installDattoRMM", "installSVSMSPModule"].includes(t.id)) continue;
const cb = document.getElementById(t.id);
if (!cb || !cb.checked) continue;
try {
await fetch(t.handler, { method: "GET" });
logProgress(t.label || t.id, true);
} catch (e) {
logProgress(t.label || t.id, false);
console.error(`Error running ${t.id}:`, e);
}
}
// 4. Rename computer (LAST)
if (renameCB && renameCB.checked && newNameInput) {
const newName = newNameInput.value.trim();
// Same basic rules youll enforce server-side
const nameIsValid =
newName.length > 0 &&
newName.length <= 15 &&
/^[A-Za-z0-9-]+$/.test(newName);
if (!nameIsValid) {
alert(
"Invalid computer name. Must be 115 characters and only letters, numbers, and hyphens."
);
// still mark it as a failed task so progress reaches 100%
logProgress("Rename computer", false);
} else {
try {
await fetch("/renameComputer", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ newName }),
});
logProgress("Rename computer", true);
} catch (e) {
console.error("Error calling /renameComputer:", e);
logProgress("Rename computer", false);
}
}
}
} catch (e) {
console.error("triggerInstall fatal error:", e);
} finally {
runBtn.disabled = false;
if (totalTasks > 0) {
console.info(
`[Info] All tasks completed (${completedTasks}/${totalTasks})`
);
}
// Best-effort notification to the server
try {
await fetch("/tasksCompleted", { method: "POST" });
} catch (err) {
console.warn("Could not notify server about completion:", err);
}
}
}
// =======================================================================
// Shutdown handler (Exit button & window close)
// =======================================================================
function endSession() {
fetch("/quit", { method: "GET" }).finally(() => window.close());
}
// Sub-options auto-toggle, tagline rotation, and beforeunload hook
document.addEventListener("DOMContentLoaded", () => {
// Sub-option containers
const tasksWithSubOptions = document.querySelectorAll(
'[id$="OptionsContainer"]'
);
tasksWithSubOptions.forEach((container) => {
const taskId = container.id.replace("OptionsContainer", "");
const masterCheckbox = document.getElementById(taskId);
if (!masterCheckbox) return;
function updateVisibility() {
const checked = masterCheckbox.checked;
container.style.display = checked ? "block" : "none";
container
.querySelectorAll('input[type="checkbox"]')
.forEach((cb) => (cb.checked = checked));
if (taskId === "installDattoRMM") {
const pwdBox = document.getElementById("PasswordContainer");
const rmmBox = document.getElementById("dattoRmmContainer");
if (pwdBox) pwdBox.style.display = checked ? "block" : "none";
if (rmmBox) rmmBox.style.display = checked ? "block" : "none";
}
}
masterCheckbox.addEventListener("change", updateVisibility);
updateVisibility();
});
// NEW: Rename computer checkbox -> show/hide text box
const renameCheckbox = document.getElementById("chkRenameComputer");
const renameBlock = document.getElementById("renameComputerBlock");
if (renameCheckbox && renameBlock) {
function updateRenameVisibility() {
renameBlock.style.display = renameCheckbox.checked ? "block" : "none";
}
renameCheckbox.addEventListener("change", updateRenameVisibility);
// Ensure correct initial state on load
updateRenameVisibility();
}
// Tagline rotation
const taglines = [
"Fast deployments, no monkey business.",
"Bananas for better builds.",
"Deploy without flinging code.",
"Tame your stack. Unleash the monkey.",
"Monkey see, monkey deploy.",
"Deploy smarter -- with a monkey on your team.",
"Don't pass the monkey -- let it deploy.",
"No more monkeying around. Stack handled.",
"Own your stack. But let the monkey do the work.",
"Why throw code when the monkey's got it?",
"Deployments so easy, a monkey could do it. Ours does.",
"Monkey in the stack, not on your back.",
];
const el = document.getElementById("tagline");
if (el) {
let idx = Math.floor(Math.random() * taglines.length);
el.textContent = taglines[idx];
setInterval(() => {
idx = (idx + 1) % taglines.length;
el.textContent = taglines[idx];
}, 10_000);
}
});
// notify server on window close
window.addEventListener("beforeunload", () => {
fetch("/quit", { method: "GET", keepalive: true });
});