Update FStackMonkey.ps1

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

View File

@@ -857,413 +857,413 @@ 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 {
flex: 1; /* fill available space */
max-width: 45%; /* or whatever width you like */
border: 2px solid var(--border-color);
border-radius: 8px;
padding: 10px;
background-color: var(--dark-gray-color);
box-shadow: 0 4px 6px rgba(0,0,0,0.3);
}
.checkbox-group label {
display:flex; align-items:center; margin-bottom:8px;
}
.button-group { text-align:right; margin-top:20px; } /* column styling, same as old script */
.exit-button { .column {
background:var(--btn-danger); color:var(--white-color); flex: 1; /* fill available space */
padding:10px 20px; border:none; border-radius:5px; cursor:pointer; max-width: 45%; /* or whatever width you like */
} border: 2px solid var(--border-color);
border-radius: 8px;
padding: 10px;
background-color: var(--dark-gray-color);
box-shadow: 0 4px 6px rgba(0,0,0,0.3);
}
.checkbox-group label {
display:flex; align-items:center; margin-bottom:8px;
}
#PasswordContainer, #dattoRmmContainer { .button-group { text-align:right; margin-top:20px; }
margin-top: 1em; .exit-button {
} background:var(--btn-danger); color:var(--white-color);
padding:10px 20px; border:none; border-radius:5px; cursor:pointer;
}
/* Common styles for inputs, buttons, and selects */ #PasswordContainer, #dattoRmmContainer {
#PasswordContainer input, margin-top: 1em;
#PasswordContainer button, }
#dattoRmmContainer select {
background-color: var(--dark-gray-color);
color: var(--white-color);
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 8px;
font-size: 14px;
display: block;
width: 40%;
max-width: 200px;
}
/* Style specifically for the fetch button */ /* Common styles for inputs, buttons, and selects */
#PasswordContainer button { #PasswordContainer input,
background-color: var(--btn-sidebar-blue); #PasswordContainer button,
cursor: pointer; #dattoRmmContainer select {
transition: background-color 0.3s ease; background-color: var(--dark-gray-color);
} color: var(--white-color);
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 8px;
font-size: 14px;
display: block;
width: 40%;
max-width: 200px;
}
/* Hover effect for the fetch button */ /* Style specifically for the fetch button */
#PasswordContainer button:hover { #PasswordContainer button {
background-color: var(--btn-hover); background-color: var(--btn-sidebar-blue);
} cursor: pointer;
transition: background-color 0.3s ease;
}
/* Tag line */ /* Hover effect for the fetch button */
#tagline { #PasswordContainer button:hover {
font-size: 1.2rem; background-color: var(--btn-hover);
color: var(--light-gray-color); }
font-weight: bold;
justify-self: center;
}
@media (max-width:768px) { /* Tag line */
.container { flex-direction:column; } #tagline {
.sidebar { width:100%; } font-size: 1.2rem;
} color: var(--light-gray-color);
</style> font-weight: bold;
justify-self: center;
}
@media (max-width:768px) {
.container { flex-direction:column; }
.sidebar { width:100%; }
}
</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 {
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 = ''; // clear the loading message
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.");
}
}
async function triggerInstall() {
// disable the button so you can't double-click while work is in flight
const runBtn = document.querySelector('.run-button');
runBtn.disabled = true;
try { try {
const resp = await fetch("/getpw", { for (const t of tasks) {
method: "POST", const cb = document.getElementById(t.id);
headers: { "Content-Type": "application/json" }, if (!cb || !cb.checked) continue;
body: JSON.stringify({ password: pwd })
});
if (!resp.ok) throw("HTTP " + resp.status); if (t.id === 'installDattoRMM') {
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;
const sites = await resp.json(); await fetch('/installDattoRMM', {
dropdown.innerHTML = ''; // clear the loading message method: 'POST',
headers: { 'Content-Type': 'application/json' },
sites.forEach(site => { body: JSON.stringify({
const option = document.createElement("option"); checkedValues: sub,
option.value = site.UID; UID: uid,
option.textContent = site.Name; Name: name
dropdown.appendChild(option); })
}); });
} else {
document.getElementById("dattoRmmContainer").style.display = "block"; await fetch(t.handler, { method: 'GET' });
} }
catch (e) { }
console.error(e); } catch (e) {
dropdown.innerHTML = '<option disabled selected>Error loading sites</option>'; console.error('Error during triggerInstall:', e);
alert("Failed to fetch sites. Check password and try again."); } finally {
// always re-enable, even if an error occurred
runBtn.disabled = false;
} }
} }
// =======================================================================
// Shutdown Handler
// =======================================================================
function endSession() {
fetch("/quit", { method: "GET" })
.finally(() => window.close());
}
async function triggerInstall() { // =======================================================================
// disable the button so you can't double-click while work is in flight // Sub-Options Auto-Toggle for Tasks
const runBtn = document.querySelector('.run-button'); // =======================================================================
runBtn.disabled = true;
try {
for (const t of tasks) {
const cb = document.getElementById(t.id);
if (!cb || !cb.checked) continue;
if (t.id === 'installDattoRMM') { document.addEventListener('DOMContentLoaded', function () {
const sub = Array.from( // Auto-handle visibility and checking for tasks with sub-options
document.querySelectorAll('.sub-option-installDattoRMM:checked') const tasksWithSubOptions = document.querySelectorAll('[id$="OptionsContainer"]');
).map(x => x.value);
const dropdown = document.getElementById('dattoDropdown');
const uid = dropdown.value;
const name = dropdown.selectedOptions[0].text;
await fetch('/installDattoRMM', { tasksWithSubOptions.forEach(container => {
method: 'POST', const taskId = container.id.replace('OptionsContainer', '');
headers: { 'Content-Type': 'application/json' }, const masterCheckbox = document.getElementById(taskId);
body: JSON.stringify({
checkedValues: sub, if (!masterCheckbox) return;
UID: uid,
Name: name function updateVisibility() {
}) const checked = masterCheckbox.checked;
}); container.style.display = checked ? 'block' : 'none';
} else { container.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = checked);
await fetch(t.handler, { method: 'GET' });
} // Show/hide Password and RMM only if it's installDattoRMM
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';
} }
} catch (e) {
console.error('Error during triggerInstall:', e);
} finally {
// always re-enable, even if an error occurred
runBtn.disabled = false;
} }
}
masterCheckbox.addEventListener('change', updateVisibility);
updateVisibility(); // call once on load
// =======================================================================
// Shutdown Handler
// =======================================================================
function endSession() {
fetch("/quit", { method: "GET" })
.finally(() => window.close());
}
// =======================================================================
// Sub-Options Auto-Toggle for Tasks
// =======================================================================
document.addEventListener('DOMContentLoaded', function () {
// Auto-handle visibility and checking for tasks with sub-options
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);
// Show/hide Password and RMM only if it's installDattoRMM
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(); // 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>