wire disk budget step + VM provisioning into installer wizard
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="./disk-budget-step.js"></script>
|
||||||
<script src="./wizard.js"></script>
|
<script src="./wizard.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -44,6 +44,12 @@ const state = {
|
|||||||
email: '',
|
email: '',
|
||||||
caName: 'DashCaddy Local CA'
|
caName: 'DashCaddy Local CA'
|
||||||
},
|
},
|
||||||
|
// Disk budget / VM sandbox configuration
|
||||||
|
diskBudget: {
|
||||||
|
preset: 'balanced',
|
||||||
|
diskSizeGB: 30,
|
||||||
|
customSizeGB: 30
|
||||||
|
},
|
||||||
// Detected network IPs
|
// Detected network IPs
|
||||||
network: {
|
network: {
|
||||||
lanIP: '',
|
lanIP: '',
|
||||||
@@ -91,6 +97,7 @@ const steps = [
|
|||||||
{ id: 'welcome', title: 'Welcome' },
|
{ id: 'welcome', title: 'Welcome' },
|
||||||
{ id: 'dependencies', title: 'Dependencies' },
|
{ id: 'dependencies', title: 'Dependencies' },
|
||||||
{ id: 'folder', title: 'Install Path' },
|
{ id: 'folder', title: 'Install Path' },
|
||||||
|
{ id: 'disk', title: 'Storage' },
|
||||||
{ id: 'tier', title: 'Tier' },
|
{ id: 'tier', title: 'Tier' },
|
||||||
{ id: 'access', title: 'Access' },
|
{ id: 'access', title: 'Access' },
|
||||||
{ id: 'dns', title: 'DNS' },
|
{ id: 'dns', title: 'DNS' },
|
||||||
@@ -179,7 +186,7 @@ function setupEventListeners() {
|
|||||||
state.result.dashboardUrl = data.dashboardUrl;
|
state.result.dashboardUrl = data.dashboardUrl;
|
||||||
state.result.installPath = data.installPath;
|
state.result.installPath = data.installPath;
|
||||||
state.result.health = data.health || null;
|
state.result.health = data.health || null;
|
||||||
state.currentStep = 8; // Move to complete step
|
state.currentStep = 9; // Move to complete step
|
||||||
render();
|
render();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -207,6 +214,21 @@ function setupEventListeners() {
|
|||||||
state.uninstall.error = data.error;
|
state.uninstall.error = data.error;
|
||||||
render();
|
render();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// VM provisioning progress / error listeners
|
||||||
|
window.electronAPI.onVMProgress((data) => {
|
||||||
|
if (state.installation.status === 'running') {
|
||||||
|
state.installation.progress = Math.min(data.progress || 0, 5);
|
||||||
|
state.installation.currentTask = data.task || 'Provisioning sandboxed virtual disk...';
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.electronAPI.onVMError((data) => {
|
||||||
|
state.installation.status = 'error';
|
||||||
|
state.installation.error = data.error || 'VM provisioning failed';
|
||||||
|
render();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
@@ -219,7 +241,7 @@ function nextStep() {
|
|||||||
case 1: // Dependencies
|
case 1: // Dependencies
|
||||||
checkDependencies();
|
checkDependencies();
|
||||||
break;
|
break;
|
||||||
case 7: // Installation
|
case 8: // Installation
|
||||||
startInstallation();
|
startInstallation();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -420,6 +442,33 @@ async function startInstallation() {
|
|||||||
render();
|
render();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// ── Provision sandboxed VM disk before installation ─────────
|
||||||
|
if (state.diskBudget && state.diskBudget.diskSizeGB) {
|
||||||
|
state.installation.currentTask = 'Provisioning sandboxed virtual disk...';
|
||||||
|
state.installation.progress = 1;
|
||||||
|
render();
|
||||||
|
|
||||||
|
const domain = state.domainMode === 'public'
|
||||||
|
? state.domain.publicDomain
|
||||||
|
: state.domainMode === 'custom-tld'
|
||||||
|
? state.domain.tld
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const vmResult = await window.electronAPI.vmProvision({
|
||||||
|
diskSizeGB: state.diskBudget.diskSizeGB,
|
||||||
|
installPath: state.paths.install,
|
||||||
|
apiPort: state.branding.apiPort,
|
||||||
|
domain
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!vmResult || !vmResult.success) {
|
||||||
|
throw new Error((vmResult && vmResult.error) || 'VM provisioning failed');
|
||||||
|
}
|
||||||
|
state.installation.completedTasks.push('Virtual disk provisioned');
|
||||||
|
state.installation.progress = 5;
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
await window.electronAPI.runInstallation({
|
await window.electronAPI.runInstallation({
|
||||||
installPath: state.paths.install,
|
installPath: state.paths.install,
|
||||||
dockerDataPath: state.paths.dockerData,
|
dockerDataPath: state.paths.dockerData,
|
||||||
@@ -511,12 +560,13 @@ function renderCurrentStep() {
|
|||||||
case 0: return renderWelcome();
|
case 0: return renderWelcome();
|
||||||
case 1: return renderDependencies();
|
case 1: return renderDependencies();
|
||||||
case 2: return renderFolderSelection();
|
case 2: return renderFolderSelection();
|
||||||
case 3: return renderTierSelection();
|
case 3: return renderDiskBudgetStep();
|
||||||
case 4: return renderAccessMode();
|
case 4: return renderTierSelection();
|
||||||
case 5: return renderDNSConfiguration();
|
case 5: return renderAccessMode();
|
||||||
case 6: return renderDashboardSetup();
|
case 6: return renderDNSConfiguration();
|
||||||
case 7: return renderInstallation();
|
case 7: return renderDashboardSetup();
|
||||||
case 8: return renderComplete();
|
case 8: return renderInstallation();
|
||||||
|
case 9: return renderComplete();
|
||||||
default: return '';
|
default: return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1125,7 +1175,7 @@ function renderComplete() {
|
|||||||
function renderFooter() {
|
function renderFooter() {
|
||||||
const isFirst = state.currentStep === 0;
|
const isFirst = state.currentStep === 0;
|
||||||
const isLast = state.currentStep === steps.length - 1;
|
const isLast = state.currentStep === steps.length - 1;
|
||||||
const isInstalling = state.currentStep === 7 && state.installation.status === 'running';
|
const isInstalling = state.currentStep === 8 && state.installation.status === 'running';
|
||||||
|
|
||||||
// Determine if user can proceed
|
// Determine if user can proceed
|
||||||
let canProceed = true;
|
let canProceed = true;
|
||||||
@@ -1136,7 +1186,7 @@ function renderFooter() {
|
|||||||
case 2: // Folder
|
case 2: // Folder
|
||||||
canProceed = !!state.paths.install;
|
canProceed = !!state.paths.install;
|
||||||
break;
|
break;
|
||||||
case 4: // Access mode
|
case 5: // Access mode
|
||||||
if (state.domainMode === 'public') {
|
if (state.domainMode === 'public') {
|
||||||
canProceed = !!state.domain.publicDomain && !!state.domain.email;
|
canProceed = !!state.domain.publicDomain && !!state.domain.email;
|
||||||
} else if (state.domainMode === 'custom-tld') {
|
} else if (state.domainMode === 'custom-tld') {
|
||||||
@@ -1165,7 +1215,7 @@ function renderFooter() {
|
|||||||
|
|
||||||
// Button text
|
// Button text
|
||||||
let nextLabel = 'Next';
|
let nextLabel = 'Next';
|
||||||
if (state.currentStep === 6) nextLabel = 'Install';
|
if (state.currentStep === 7) nextLabel = 'Install';
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="step-footer">
|
<div class="step-footer">
|
||||||
|
|||||||
Reference in New Issue
Block a user