Update FStackMonkey.ps1

This commit is contained in:
2025-06-29 04:43:16 -04:00
parent 6c24bda864
commit 833d7f6e9d

View File

@@ -857,288 +857,288 @@ function Invoke-ScriptMonkey {
# 1) Inline your full original CSS here # 1) Inline your full original CSS here
# #
$style = @' $style = @'
<style> <style>
:root { :root {
/* Cool Palette */ /* Cool Palette */
--background-color: rgba(18, 18, 18, 1); --background-color: rgba(18, 18, 18, 1);
--border-color: rgba(255,127,0,0.25); --border-color: rgba(255,127,0,0.25);
/* Neutral Colors */ /* Neutral Colors */
--white-color: rgba(255,255,255); --white-color: rgba(255,255,255);
--gray-color: rgba(102,102,102); --gray-color: rgba(102,102,102);
--dark-gray-color: rgba(51,51,51); --dark-gray-color: rgba(51,51,51);
--light-gray-color: rgba(187,187,187); --light-gray-color: rgba(187,187,187);
/* Sidebar Button Colors */ /* Sidebar Button Colors */
--btn-sidebar-light-gray: rgba(68,68,68); --btn-sidebar-light-gray: rgba(68,68,68);
--btn-sidebar-blue: rgba(30,144,255,1); --btn-sidebar-blue: rgba(30,144,255,1);
--btn-hover: rgba(0,86,179,1); --btn-hover: rgba(0,86,179,1);
--btn-hover-scale: 1.05; --btn-hover-scale: 1.05;
/* Button Colors */ /* Button Colors */
--btn-success: rgba(40,167,69); --btn-success: rgba(40,167,69);
--btn-success-disabled: rgba(108,117,125); --btn-success-disabled: rgba(108,117,125);
--btn-danger: rgba(220,53,69); --btn-danger: rgba(220,53,69);
} }
body { body {
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
margin: 0; padding: 0; margin: 0; padding: 0;
background-color: var(--background-color); background-color: var(--background-color);
color: var(--white-color); color: var(--white-color);
height: 100%; overflow: hidden; height: 100%; overflow: hidden;
} }
.logo-container { display: grid; grid-template-columns: auto 1fr; align-items: center; padding: 20px; } .logo-container { display: grid; grid-template-columns: auto 1fr; align-items: center; padding: 20px; }
.logo-container img { max-width:300px; height:auto; } .logo-container img { max-width:300px; height:auto; }
.subtitle { font-size: 1.2rem; color: var(--gray-color); margin-top: 0.5em; } .subtitle { font-size: 1.2rem; color: var(--gray-color); margin-top: 0.5em; }
.container { display:flex; height:100vh; overflow:hidden; } .container { display:flex; height:100vh; overflow:hidden; }
.sidebar { width:200px; background:var(--background-color); padding:10px; } .sidebar { width:200px; background:var(--background-color); padding:10px; }
.sidebar button { .sidebar button {
display:block; width:100%; margin-bottom:10px; padding:10px; display:block; width:100%; margin-bottom:10px; padding:10px;
color:var(--white-color); background:var(--btn-sidebar-light-gray); color:var(--white-color); background:var(--btn-sidebar-light-gray);
border:none; border-radius:5px; cursor:pointer; text-align:left; border:none; border-radius:5px; cursor:pointer; text-align:left;
transition:background-color 0.3s, transform 0.2s; transition:background-color 0.3s, transform 0.2s;
} }
.sidebar button.active { background:var(--btn-sidebar-blue); } .sidebar button.active { background:var(--btn-sidebar-blue); }
.sidebar button:hover { .sidebar button:hover {
background:var(--btn-hover); transform:scale(var(--btn-hover-scale)); background:var(--btn-hover); transform:scale(var(--btn-hover-scale));
} }
.content { .content {
position: relative; position: relative;
flex:1; flex:1;
padding:20px; padding:20px;
overflow-y:auto; overflow-y:auto;
max-height:calc(100vh - 50px); max-height:calc(100vh - 50px);
} }
.fixed-buttons { .fixed-buttons {
position: fixed; position: fixed;
bottom: 20px; bottom: 20px;
right: 20px; right: 20px;
display: flex; display: flex;
gap: 10px; /* space between Exit and Run */ gap: 10px; /* space between Exit and Run */
z-index: 1000; z-index: 1000;
} }
.exit-button, .exit-button,
.run-button { .run-button {
border: none; border: none;
border-radius: 5px; border-radius: 5px;
padding: 10px 20px; padding: 10px 20px;
cursor: pointer; cursor: pointer;
color: var(--white-color); color: var(--white-color);
} }
/* Specific overrides */ /* Specific overrides */
.exit-button { .exit-button {
background-color: var(--btn-danger); background-color: var(--btn-danger);
} }
/* Specific overrides */ /* Specific overrides */
.run-button { .run-button {
background-color: var(--btn-success); background-color: var(--btn-success);
} }
.tab-content { display:none; } .tab-content { display:none; }
.tab-content.active { display:block; } .tab-content.active { display:block; }
.columns-container { .columns-container {
display:flex; gap:20px; flex-wrap:wrap; align-items:flex-start; display:flex; gap:20px; flex-wrap:wrap; align-items:flex-start;
} }
/* column styling, same as old script */ /* column styling, same as old script */
.column { .column {
flex: 1; /* fill available space */ flex: 1; /* fill available space */
max-width: 45%; /* or whatever width you like */ max-width: 45%; /* or whatever width you like */
border: 2px solid var(--border-color); border: 2px solid var(--border-color);
border-radius: 8px; border-radius: 8px;
padding: 10px; padding: 10px;
background-color: var(--dark-gray-color); background-color: var(--dark-gray-color);
box-shadow: 0 4px 6px rgba(0,0,0,0.3); box-shadow: 0 4px 6px rgba(0,0,0,0.3);
} }
.checkbox-group label { .checkbox-group label {
display:flex; align-items:center; margin-bottom:8px; display:flex; align-items:center; margin-bottom:8px;
} }
.button-group { text-align:right; margin-top:20px; } .button-group { text-align:right; margin-top:20px; }
.exit-button { .exit-button {
background:var(--btn-danger); color:var(--white-color); background:var(--btn-danger); color:var(--white-color);
padding:10px 20px; border:none; border-radius:5px; cursor:pointer; padding:10px 20px; border:none; border-radius:5px; cursor:pointer;
} }
#PasswordContainer, #dattoRmmContainer { #PasswordContainer, #dattoRmmContainer {
margin-top: 1em; margin-top: 1em;
} }
/* Common styles for inputs, buttons, and selects */ /* Common styles for inputs, buttons, and selects */
#PasswordContainer input, #PasswordContainer input,
#PasswordContainer button, #PasswordContainer button,
#dattoRmmContainer select { #dattoRmmContainer select {
background-color: var(--dark-gray-color); background-color: var(--dark-gray-color);
color: var(--white-color); color: var(--white-color);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 4px; border-radius: 4px;
padding: 8px; padding: 8px;
font-size: 14px; font-size: 14px;
display: block; display: block;
width: 40%; width: 40%;
max-width: 200px; max-width: 200px;
} }
/* Style specifically for the fetch button */ /* Style specifically for the fetch button */
#PasswordContainer button { #PasswordContainer button {
background-color: var(--btn-sidebar-blue); background-color: var(--btn-sidebar-blue);
cursor: pointer; cursor: pointer;
transition: background-color 0.3s ease; transition: background-color 0.3s ease;
} }
/* Hover effect for the fetch button */ /* Hover effect for the fetch button */
#PasswordContainer button:hover { #PasswordContainer button:hover {
background-color: var(--btn-hover); background-color: var(--btn-hover);
} }
/* Tag line */ /* Tag line */
#tagline { #tagline {
font-size: 1.2rem; font-size: 1.2rem;
color: var(--light-gray-color); color: var(--light-gray-color);
font-weight: bold; font-weight: bold;
justify-self: center; justify-self: center;
} }
@media (max-width:768px) { @media (max-width:768px) {
.container { flex-direction:column; } .container { flex-direction:column; }
.sidebar { width:100%; } .sidebar { width:100%; }
} }
</style> </style>
'@ '@
$script = @' $script = @'
<script> <script>
// ======================================================================= // =======================================================================
// Tab Navigation // Tab Navigation
// ======================================================================= // =======================================================================
const tabButtons = document.querySelectorAll(".tab-button"); const tabButtons = document.querySelectorAll(".tab-button");
const tabContents = document.querySelectorAll(".tab-content"); const tabContents = document.querySelectorAll(".tab-content");
tabButtons.forEach(btn => { tabButtons.forEach(btn => {
btn.addEventListener("click", () => { btn.addEventListener("click", () => {
// clear active state // clear active state
tabButtons.forEach(b => b.classList.remove("active")); tabButtons.forEach(b => b.classList.remove("active"));
tabContents.forEach(c => c.classList.remove("active")); tabContents.forEach(c => c.classList.remove("active"));
// set new active // set new active
btn.classList.add("active"); btn.classList.add("active");
document.getElementById(btn.dataset.tab).classList.add("active"); document.getElementById(btn.dataset.tab).classList.add("active");
});
});
// initialize default tab on load
document.querySelector(".tab-button[data-tab='{{defaultPage}}Tab']").classList.add("active");
document.getElementById("{{defaultPage}}Tab").classList.add("active");
// =======================================================================
// Task Trigger
// =======================================================================
const tasks = [
{{tasksJsAll}}
];
// =======================================================================
// Column Select All toggling for On-Boarding
// =======================================================================
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;
}); });
});
// initialize default tab on load
document.querySelector(".tab-button[data-tab='{{defaultPage}}Tab']").classList.add("active");
document.getElementById("{{defaultPage}}Tab").classList.add("active");
// Now simulate change events after setting all checkboxes // =======================================================================
setTimeout(() => { // Task Trigger
children.forEach(cb => { // =======================================================================
cb.dispatchEvent(new Event('change')); const tasks = [
}); {{tasksJsAll}}
}, 0); ];
// =======================================================================
// Column Select All toggling for On-Boarding
// =======================================================================
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;
});
// Now simulate change events after setting all checkboxes
setTimeout(() => {
children.forEach(cb => {
cb.dispatchEvent(new Event('change'));
});
}, 0);
}
// =======================================================================
// Un-check Select All if any child is unchecked (& re-check if all are checked)
// =======================================================================
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);
} }
// Attach listeners on load
['left','right'].forEach(col => {
document
.querySelectorAll(`#onboardTab input[type=checkbox][data-column=${col}]`)
.forEach(cb => cb.addEventListener('change', () => updateSelectAll(col)));
});
// ======================================================================= // =======================================================================
// Un-check Select All if any child is unchecked (& re-check if all are checked) // DattoRMM Options
// ======================================================================= // =======================================================================
function updateSelectAll(col) { function toggleDattoRMMOptions() {
const master = document.getElementById( const master = document.getElementById('installDattoRMM');
`selectAll${col[0].toUpperCase() + col.slice(1)}Checkbox` const container = document.getElementById('installDattoRMMOptionsContainer');
); if (!container) return;
const children = document.querySelectorAll( container.style.display = master.checked ? 'block' : 'none';
`#onboardTab input[type=checkbox][data-column=${col}]` container.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = master.checked);
);
master.checked = Array.from(children).every(cb => cb.checked);
} }
// Attach listeners on load document.addEventListener('DOMContentLoaded', () => {
['left','right'].forEach(col => { const master = document.getElementById('installDattoRMM');
document if (master) master.addEventListener('change', toggleDattoRMMOptions);
.querySelectorAll(`#onboardTab input[type=checkbox][data-column=${col}]`)
.forEach(cb => cb.addEventListener('change', () => updateSelectAll(col)));
}); });
// =======================================================================
// DattoRMM Options
// =======================================================================
function toggleDattoRMMOptions() {
const master = document.getElementById('installDattoRMM');
const container = document.getElementById('installDattoRMMOptionsContainer');
if (!container) return;
container.style.display = master.checked ? 'block' : 'none';
container.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = master.checked);
}
document.addEventListener('DOMContentLoaded', () => {
const master = document.getElementById('installDattoRMM');
if (master) master.addEventListener('change', toggleDattoRMMOptions);
});
// ======================================================================= // =======================================================================
// Fetch Sites Handler // Fetch Sites Handler
// ======================================================================= // =======================================================================
async function fetchSites() { async function fetchSites() {
const pwd = document.getElementById("Password").value; const pwd = document.getElementById("Password").value;
if (!pwd) { if (!pwd) {
alert("Please enter the password."); alert("Please enter the password.");
return; return;
} }
const dropdown = document.getElementById("dattoDropdown"); const dropdown = document.getElementById("dattoDropdown");
dropdown.innerHTML = '<option disabled selected>Loading sites...</option>'; dropdown.innerHTML = '<option disabled selected>Loading sites...</option>';
try { try {
const resp = await fetch("/getpw", { const resp = await fetch("/getpw", {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ password: pwd }) body: JSON.stringify({ password: pwd })
}); });
if (!resp.ok) throw("HTTP " + resp.status); if (!resp.ok) throw("HTTP " + resp.status);
const sites = await resp.json(); const sites = await resp.json();
dropdown.innerHTML = ''; // clear the loading message dropdown.innerHTML = ''; // clear the loading message
sites.forEach(site => { sites.forEach(site => {
const option = document.createElement("option"); const option = document.createElement("option");
option.value = site.UID; option.value = site.UID;
option.textContent = site.Name; option.textContent = site.Name;
dropdown.appendChild(option); dropdown.appendChild(option);
}); });
document.getElementById("dattoRmmContainer").style.display = "block"; document.getElementById("dattoRmmContainer").style.display = "block";
} }
catch (e) { catch (e) {
console.error(e); console.error(e);
dropdown.innerHTML = '<option disabled selected>Error loading sites</option>'; dropdown.innerHTML = '<option disabled selected>Error loading sites</option>';
alert("Failed to fetch sites. Check password and try again."); alert("Failed to fetch sites. Check password and try again.");
} }
} }
@@ -1146,124 +1146,124 @@ async function fetchSites() {
async function triggerInstall() { async function triggerInstall() {
// disable the button so you can't double-click while work is in flight // disable the button so you can't double-click while work is in flight
const runBtn = document.querySelector('.run-button'); const runBtn = document.querySelector('.run-button');
runBtn.disabled = true; runBtn.disabled = true;
try { try {
for (const t of tasks) { for (const t of tasks) {
const cb = document.getElementById(t.id); const cb = document.getElementById(t.id);
if (!cb || !cb.checked) continue; if (!cb || !cb.checked) continue;
if (t.id === 'installDattoRMM') { if (t.id === 'installDattoRMM') {
const sub = Array.from( const sub = Array.from(
document.querySelectorAll('.sub-option-installDattoRMM:checked') document.querySelectorAll('.sub-option-installDattoRMM:checked')
).map(x => x.value); ).map(x => x.value);
const dropdown = document.getElementById('dattoDropdown'); const dropdown = document.getElementById('dattoDropdown');
const uid = dropdown.value; const uid = dropdown.value;
const name = dropdown.selectedOptions[0].text; const name = dropdown.selectedOptions[0].text;
await fetch('/installDattoRMM', { await fetch('/installDattoRMM', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
checkedValues: sub, checkedValues: sub,
UID: uid, UID: uid,
Name: name Name: name
}) })
}); });
} else { } else {
await fetch(t.handler, { method: 'GET' }); await fetch(t.handler, { method: 'GET' });
}
} }
} catch (e) {
console.error('Error during triggerInstall:', e);
} finally {
// always re-enable, even if an error occurred
runBtn.disabled = false;
} }
} catch (e) {
console.error('Error during triggerInstall:', e);
} finally {
// always re-enable, even if an error occurred
runBtn.disabled = false;
} }
}
// ======================================================================= // =======================================================================
// Shutdown Handler // Shutdown Handler
// ======================================================================= // =======================================================================
function endSession() { function endSession() {
fetch("/quit", { method: "GET" }) fetch("/quit", { method: "GET" })
.finally(() => window.close()); .finally(() => window.close());
} }
// ======================================================================= // =======================================================================
// Sub-Options Auto-Toggle for Tasks // Sub-Options Auto-Toggle for Tasks
// ======================================================================= // =======================================================================
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
// Auto-handle visibility and checking for tasks with sub-options // Auto-handle visibility and checking for tasks with sub-options
const tasksWithSubOptions = document.querySelectorAll('[id$="OptionsContainer"]'); const tasksWithSubOptions = document.querySelectorAll('[id$="OptionsContainer"]');
tasksWithSubOptions.forEach(container => { tasksWithSubOptions.forEach(container => {
const taskId = container.id.replace('OptionsContainer', ''); const taskId = container.id.replace('OptionsContainer', '');
const masterCheckbox = document.getElementById(taskId); const masterCheckbox = document.getElementById(taskId);
if (!masterCheckbox) return; if (!masterCheckbox) return;
function updateVisibility() { function updateVisibility() {
const checked = masterCheckbox.checked; const checked = masterCheckbox.checked;
container.style.display = checked ? 'block' : 'none'; container.style.display = checked ? 'block' : 'none';
container.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = checked); container.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = checked);
// Show/hide Password and RMM only if it's installDattoRMM // Show/hide Password and RMM only if it's installDattoRMM
if (taskId === 'installDattoRMM') { if (taskId === 'installDattoRMM') {
const pwdBox = document.getElementById('PasswordContainer'); const pwdBox = document.getElementById('PasswordContainer');
const rmmBox = document.getElementById('dattoRmmContainer'); const rmmBox = document.getElementById('dattoRmmContainer');
if (pwdBox) pwdBox.style.display = checked ? 'block' : 'none'; if (pwdBox) pwdBox.style.display = checked ? 'block' : 'none';
if (rmmBox) rmmBox.style.display = checked ? 'block' : 'none'; if (rmmBox) rmmBox.style.display = checked ? 'block' : 'none';
}
} }
}
masterCheckbox.addEventListener('change', updateVisibility); masterCheckbox.addEventListener('change', updateVisibility);
updateVisibility(); // call once on load updateVisibility(); // call once on load
});
}); });
});
// =========================================== // ===========================================
// rotating tagline // rotating tagline
// =========================================== // ===========================================
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const taglines = [ const taglines = [
"Fast deployments, no monkey business.", "Fast deployments, no monkey business.",
"Bananas for better builds.", "Bananas for better builds.",
"Deploy without flinging code.", "Deploy without flinging code.",
"Tame your stack. Unleash the monkey.", "Tame your stack. Unleash the monkey.",
"Monkey see, monkey deploy.", "Monkey see, monkey deploy.",
"Deploy smarter -- with a monkey on your team.", "Deploy smarter -- with a monkey on your team.",
"Don't pass the monkey -- let it deploy.", "Don't pass the monkey -- let it deploy.",
"No more monkeying around. Stack handled.", "No more monkeying around. Stack handled.",
"Own your stack. But let the monkey do the work.", "Own your stack. But let the monkey do the work.",
"Why throw code when the monkey's got it?", "Why throw code when the monkey's got it?",
"Deployments so easy, a monkey could do it. Ours does.", "Deployments so easy, a monkey could do it. Ours does.",
"Monkey in the stack, not on your back." "Monkey in the stack, not on your back."
]; ];
const el = document.getElementById("tagline"); const el = document.getElementById("tagline");
let idx = Math.floor(Math.random() * taglines.length); let idx = Math.floor(Math.random() * taglines.length);
el.textContent = taglines[idx];
setInterval(() => {
idx = (idx + 1) % taglines.length;
el.textContent = taglines[idx]; el.textContent = taglines[idx];
}, 10_000);
setInterval(() => {
idx = (idx + 1) % taglines.length;
el.textContent = taglines[idx];
}, 10_000);
}); });
// when the browser window is closed (X), notify the server to quit // when the browser window is closed (X), notify the server to quit
window.addEventListener('beforeunload', () => { window.addEventListener('beforeunload', () => {
// keepalive: true ensures the request is sent even as the page unloads // keepalive: true ensures the request is sent even as the page unloads
fetch('/quit', { method: 'GET', keepalive: true }); fetch('/quit', { method: 'GET', keepalive: true });
}); });
</script> </script>