Initial commit: DashCaddy v1.0

Full codebase including API server (32 modules + routes), dashboard frontend,
DashCA certificate distribution, installer script, and deployment skills.
This commit is contained in:
2026-03-05 02:26:12 -08:00
commit f61e85d9a7
337 changed files with 75282 additions and 0 deletions

414
status/js/setup-wizard.js Normal file
View File

@@ -0,0 +1,414 @@
// Shared timezone utility — used by setup wizard and settings modal
window.populateTimezoneSelect = function(selectEl, selectedTz) {
const timezones = Intl.supportedValuesOf('timeZone');
const detected = selectedTz || Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';
selectEl.innerHTML = '';
for (const tz of timezones) {
const opt = document.createElement('option');
opt.value = tz;
opt.textContent = tz.replace(/_/g, ' ');
if (tz === detected) opt.selected = true;
selectEl.appendChild(opt);
}
};
// Setup Wizard System - Server-side config storage
(function () {
let currentConfigType = 'homelab';
let serverConfig = null;
// Check server for existing config on page load
async function checkServerConfig() {
try {
const response = await fetch('/api/v1/config');
if (response.ok) {
serverConfig = await response.json();
if (serverConfig && serverConfig.setupComplete) {
// Config exists on server - don't show wizard
document.getElementById('setup-wizard').style.display = 'none';
return true;
}
}
} catch (error) {
console.warn('Could not fetch server config, checking localStorage fallback:', error.message);
}
// Fallback: check localStorage for backwards compatibility
const localSetup = safeGet('dashcaddy-setup');
if (localSetup) {
document.getElementById('setup-wizard').style.display = 'none';
return true;
}
// No config found - show wizard
document.getElementById('setup-wizard').style.display = 'flex';
return false;
}
// Initialize on page load
checkServerConfig();
// Populate timezone dropdown with auto-detection
const setupTzSelect = document.getElementById('setup-timezone');
if (setupTzSelect) window.populateTimezoneSelect(setupTzSelect);
// Step navigation
function showStep(stepId) {
document.querySelectorAll('.setup-step').forEach(step => {
step.style.display = 'none';
});
const targetStep = document.getElementById(stepId);
if (targetStep) {
targetStep.style.display = 'block';
}
}
// Show summary
function showSummary() {
const summaryContent = document.getElementById('setup-summary-content');
if (!summaryContent) return;
let html = '<div style="display: grid; gap: 20px;">';
if (currentConfigType === 'homelab') {
const tld = document.getElementById('setup-tld')?.value?.trim() || '.home';
const caName = document.getElementById('setup-ca-name')?.value?.trim() || '';
const dnsIP = document.getElementById('setup-dns-ip')?.value?.trim() || '';
const dnsPort = document.getElementById('setup-dns-port')?.value?.trim() || DC.DEFAULTS.DNS_PORT;
html += `
<div>
<h3 style="margin: 0 0 12px; color: var(--accent);">Home Lab Configuration</h3>
<div style="display: grid; gap: 12px; font-size: 0.95rem;">
<div><strong>TLD:</strong> ${tld}</div>
<div><strong>Certificate Authority:</strong> ${caName}</div>
<div><strong>DNS Server:</strong> ${dnsIP}:${dnsPort}</div>
<div><strong>Example URLs:</strong> https://uptime${tld}, https://nextcloud${tld}</div>
</div>
</div>
`;
} else if (currentConfigType === 'simple') {
const ip = document.getElementById('setup-simple-ip')?.value?.trim() || 'localhost';
html += `
<div>
<h3 style="margin: 0 0 12px; color: var(--accent);">Simple Setup</h3>
<div style="display: grid; gap: 12px; font-size: 0.95rem;">
<div><strong>Access Method:</strong> IP:Port only</div>
<div><strong>Default IP:</strong> ${ip}</div>
<div><strong>SSL:</strong> None (HTTP only)</div>
<div><strong>Example URLs:</strong> http://${ip}:8080, http://${ip}:3000</div>
</div>
</div>
`;
} else if (currentConfigType === 'public') {
const domain = document.getElementById('setup-public-domain')?.value?.trim() || '';
const email = document.getElementById('setup-public-email')?.value?.trim() || '';
html += `
<div>
<h3 style="margin: 0 0 12px; color: var(--accent);">Public Server</h3>
<div style="display: grid; gap: 12px; font-size: 0.95rem;">
<div><strong>Domain:</strong> ${domain}</div>
<div><strong>SSL:</strong> Let's Encrypt</div>
<div><strong>Email:</strong> ${email}</div>
<div><strong>Example URLs:</strong> https://app.${domain}, https://cloud.${domain}</div>
</div>
</div>
`;
}
// Timezone (universal across all config types)
const tz = document.getElementById('setup-timezone')?.value || Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';
html += `
<div style="margin-top: 8px; padding-top: 12px; border-top: 1px solid var(--border);">
<div style="font-size: 0.95rem;"><strong>Timezone:</strong> ${tz.replace(/_/g, ' ')}</div>
</div>
`;
html += '</div>';
summaryContent.innerHTML = html;
showStep('setup-step-summary');
}
// Save config to server
async function saveConfigToServer(config) {
try {
const response = await secureFetch('/api/v1/config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config)
});
if (response.ok) {
await response.json();
return true;
} else {
console.error('Failed to save config to server:', response.status);
return false;
}
} catch (error) {
console.error('Error saving config to server:', error);
return false;
}
}
// Finish setup handler
async function finishSetup() {
const config = {
setupComplete: true,
configurationType: currentConfigType,
timestamp: new Date().toISOString(),
timezone: document.getElementById('setup-timezone')?.value || Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'
};
if (currentConfigType === 'homelab') {
config.tld = document.getElementById('setup-tld')?.value?.trim() || '.home';
config.caName = document.getElementById('setup-ca-name')?.value?.trim() || '';
config.dns = {
provider: 'technitium',
ip: document.getElementById('setup-dns-ip')?.value?.trim() || '',
port: document.getElementById('setup-dns-port')?.value?.trim() || DC.DEFAULTS.DNS_PORT,
token: document.getElementById('setup-dns-token')?.value?.trim() || ''
};
config.defaults = {
dnsType: 'private',
sslType: 'internal',
targetIP: 'localhost'
};
} else if (currentConfigType === 'simple') {
config.defaultIP = document.getElementById('setup-simple-ip')?.value?.trim() || 'localhost';
config.defaults = {
dnsType: 'none',
sslType: 'none',
targetIP: config.defaultIP
};
} else if (currentConfigType === 'public') {
config.domain = document.getElementById('setup-public-domain')?.value?.trim() || '';
config.email = document.getElementById('setup-public-email')?.value?.trim() || '';
config.defaults = {
dnsType: 'public',
sslType: 'letsencrypt',
targetIP: 'localhost'
};
}
// Save to server (primary) and localStorage (fallback)
const savedToServer = await saveConfigToServer(config);
safeSet('dashcaddy-config', JSON.stringify(config));
safeSet('dashcaddy-setup', 'completed');
// Hide wizard
document.getElementById('setup-wizard').style.display = 'none';
// Show success notification
const configName = currentConfigType === 'homelab' ? 'Professional Home Lab' :
currentConfigType === 'simple' ? 'Simple Setup' : 'Public Server';
const saveLocation = savedToServer ? 'server (shared across all devices)' : 'locally (this browser only)';
showNotification(`Setup Complete! Configured for: ${configName}. Settings saved to: ${saveLocation}`, 'success', 5000);
// Reload page to apply configuration
setTimeout(() => location.reload(), 500);
}
// ===== Event Handlers using direct onclick for reliability =====
// Step 1: Continue button
const step1Next = document.getElementById('setup-step-1-next');
if (step1Next) {
step1Next.onclick = function(e) {
e.preventDefault();
const selected = document.querySelector('input[name="config-type"]:checked');
if (selected) {
currentConfigType = selected.value;
}
if (currentConfigType === 'homelab') {
showStep('setup-step-homelab');
} else if (currentConfigType === 'simple') {
showStep('setup-step-simple');
} else if (currentConfigType === 'public') {
showStep('setup-step-public');
} else {
showStep('setup-step-homelab');
}
};
}
// Skip setup button
const skipBtn = document.getElementById('setup-skip');
if (skipBtn) {
skipBtn.onclick = async function(e) {
e.preventDefault();
if (confirm('Skip setup? You can run it later from Settings.')) {
// Save skip status to server
await saveConfigToServer({ setupComplete: true, skipped: true, timestamp: new Date().toISOString() });
safeSet('dashcaddy-setup', 'skipped');
document.getElementById('setup-wizard').style.display = 'none';
}
};
}
// Home Lab: TLD Preview
const tldInput = document.getElementById('setup-tld');
if (tldInput) {
tldInput.oninput = function(e) {
const tld = e.target.value || '.home';
const preview1 = document.getElementById('tld-preview');
const preview2 = document.getElementById('tld-preview-2');
if (preview1) preview1.textContent = tld;
if (preview2) preview2.textContent = tld;
};
}
// Home Lab navigation
const homelabBack = document.getElementById('setup-homelab-back');
if (homelabBack) {
homelabBack.onclick = function(e) {
e.preventDefault();
showStep('setup-step-1');
};
}
const homelabNext = document.getElementById('setup-homelab-next');
if (homelabNext) {
homelabNext.onclick = function(e) {
e.preventDefault();
const tld = document.getElementById('setup-tld')?.value?.trim() || '';
const caName = document.getElementById('setup-ca-name')?.value?.trim() || '';
const dnsIP = document.getElementById('setup-dns-ip')?.value?.trim() || '';
if (!tld || !tld.startsWith('.')) {
showNotification('Please enter a valid TLD starting with a dot (e.g., .home)', 'warning');
return;
}
if (!caName) {
showNotification('Please enter a Certificate Authority name', 'warning');
return;
}
if (!dnsIP) {
showNotification('Please enter your DNS server IP address', 'warning');
return;
}
showSummary();
};
}
// Simple navigation
const simpleBack = document.getElementById('setup-simple-back');
if (simpleBack) {
simpleBack.onclick = function(e) {
e.preventDefault();
showStep('setup-step-1');
};
}
const simpleNext = document.getElementById('setup-simple-next');
if (simpleNext) {
simpleNext.onclick = function(e) {
e.preventDefault();
showSummary();
};
}
// Public navigation
const publicBack = document.getElementById('setup-public-back');
if (publicBack) {
publicBack.onclick = function(e) {
e.preventDefault();
showStep('setup-step-1');
};
}
const publicNext = document.getElementById('setup-public-next');
if (publicNext) {
publicNext.onclick = function(e) {
e.preventDefault();
const domain = document.getElementById('setup-public-domain')?.value?.trim() || '';
const email = document.getElementById('setup-public-email')?.value?.trim() || '';
if (!domain) {
showNotification('Please enter your domain name', 'warning');
return;
}
if (!email || !email.includes('@')) {
showNotification('Please enter a valid email address', 'warning');
return;
}
showSummary();
};
}
// Summary navigation
const summaryBack = document.getElementById('setup-summary-back');
if (summaryBack) {
summaryBack.onclick = function(e) {
e.preventDefault();
if (currentConfigType === 'homelab') {
showStep('setup-step-homelab');
} else if (currentConfigType === 'simple') {
showStep('setup-step-simple');
} else if (currentConfigType === 'public') {
showStep('setup-step-public');
}
};
}
// Finish setup button
const finishBtn = document.getElementById('setup-finish');
if (finishBtn) {
finishBtn.onclick = function(e) {
e.preventDefault();
finishSetup();
};
}
// Expose function to get global config (from server or localStorage)
window.getGlobalConfig = async function() {
// Try server first
try {
const response = await fetch('/api/v1/config');
if (response.ok) {
const config = await response.json();
if (config && config.setupComplete) {
return config;
}
}
} catch (e) {
console.warn('Could not fetch config from server');
}
// Fallback to localStorage
const configStr = safeGet('dashcaddy-config');
if (configStr) {
return JSON.parse(configStr);
}
// Return default config if not set
return {
setupComplete: false,
configurationType: 'homelab',
tld: '.home',
caName: '',
defaults: {
dnsType: 'private',
sslType: 'internal',
targetIP: 'localhost'
}
};
};
// Expose reset function for settings
window.resetSetupWizard = async function() {
if (confirm('Reset DashCaddy configuration? This will show the setup wizard again.')) {
try {
await secureFetch('/api/v1/config', { method: 'DELETE' });
} catch (e) {
console.warn('Could not delete server config');
}
safeRemove('dashcaddy-setup');
safeRemove('dashcaddy-config');
location.reload();
}
};
})();