diff --git a/frontend/app.py b/frontend/app.py index 4eeb72f..4cd7bda 100644 --- a/frontend/app.py +++ b/frontend/app.py @@ -346,8 +346,8 @@ async def session_list(): return JSONResponse({"sessions": []}) -@app.get("/session/analyze/result/{analysis_id}", response_class=HTMLResponse) -async def session_analyze_result( +@app.get("/session/configure/{analysis_id}", response_class=HTMLResponse) +async def session_configure( request: Request, analysis_id: str, vmid: str = "", @@ -355,16 +355,18 @@ async def session_analyze_result( vm_name: str = "", session_id: str = "", ): - """Render the analysis result after polling completes.""" + """Full page with VM configuration + disk selection after analysis.""" try: status = api.get_analysis(analysis_id) result = status.get("result") if not result: - return render("_analysis.html", request=request, - error="Analysis result not ready.") + return render("configure.html", request=request, analysis_id=analysis_id, + source_filename=source_filename, session_id=session_id, + disks=[], error="Analysis result not ready.") except ApiError as exc: - return render("_analysis.html", request=request, - error=f"Failed to fetch analysis: {exc.detail}") + return render("configure.html", request=request, analysis_id=analysis_id, + source_filename=source_filename, session_id=session_id, + disks=[], error=f"Failed: {exc.detail}") pools = [] try: @@ -376,12 +378,36 @@ async def session_analyze_result( analysis = result name = vm_name.strip() if not name: - name = (analysis.get("os_type") or "vm") + f"-{vmid}" + name = (analysis.get("os_type") or "vm") - return render("_analysis.html", request=request, - vmid=vmid, source_filename=source_filename, - vm_name=name, analysis=analysis, storage_pools=pools, - session_id=session_id) + disks = analysis.get("all_disks", []) + if not disks: + disks = [{"filename": analysis.get("filename", ""), "format": analysis.get("disk_format", ""), "size_gb": analysis.get("disk_size_gb", 0)}] + + suggested_vmid = vmid if vmid else "" + + return render("configure.html", request=request, + analysis_id=analysis_id, source_filename=source_filename, + session_id=session_id, vm_name=name, storage_pools=pools, + disks=disks, suggested_vmid=suggested_vmid) + + +@app.post("/session/configure/submit") +async def configure_submit(request: Request): + """Receive config + disk selection, submit job to backend.""" + body = await request.json() + try: + result = api.create_job(body) + return result + except ApiError as exc: + return JSONResponse({"error": exc.detail}, status_code=502) + + +@app.get("/session/status/{job_id}", response_class=HTMLResponse) +async def session_status_page(request: Request, job_id: str): + """Full page showing job progress with polling.""" + return render("job_polling.html", request=request, job_id=job_id, + vmid=0, vm_name="", session_id="") @app.post("/session/confirm", response_class=HTMLResponse) diff --git a/frontend/templates/configure.html b/frontend/templates/configure.html new file mode 100644 index 0000000..84a565e --- /dev/null +++ b/frontend/templates/configure.html @@ -0,0 +1,197 @@ +{% extends "base.html" %} +{% block content %} + +
+

VM Configuration

+

Source: {{ source_filename }}

+
+ +
+
+ VM Settings +
+
+ +
+
+ + +
+
+ + +
+
+ +
+
+ + {% if storage_pools %} + + + {% else %} + + {% endif %} +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+
+ +
+
+ Disks Found +
+
+ + + + + + + + + + + + {% for d in disks %} + + + + + + + + {% endfor %} + +
BootFilenameFormatSizeResize
{{ d.filename }}{{ d.format }}{{ d.size_gb }} GB + +
+ +
+ TIP: The disk with the operating system should be the boot disk. + Data disks will be attached as additional drives. +
+ + +
+
+
+ + + +{% endblock %} \ No newline at end of file diff --git a/frontend/templates/index.html b/frontend/templates/index.html index 08e8874..5669ecb 100644 --- a/frontend/templates/index.html +++ b/frontend/templates/index.html @@ -287,13 +287,8 @@ async function runAnalysis(vmid, filename, vmName, sessionOverride) { setPhase('Analysing... ' + (sdata.message || '') + ' (' + elapsed + 's)', sdata.status === 'completed' ? 100 : 50, sdata.status !== 'completed'); if (sdata.status === 'completed') { - await sleep(500); var sid = window.RESUMED_SID || window.VM_BENCH_SID || ''; - var rr = await fetch('/session/analyze/result/' + analysisId + '?source_filename=' + encodeURIComponent(filename) + '&vm_name=' + encodeURIComponent(vmName) + '&vmid=' + vmid + '&session_id=' + encodeURIComponent(sid)); - var html = await rr.text(); - document.getElementById('analysis-section').innerHTML = html; - document.getElementById('session-status').classList.add('pve-hidden'); - initConfirmForm(); + window.location.href = '/session/configure/' + analysisId + '?source_filename=' + encodeURIComponent(filename) + '&vm_name=' + encodeURIComponent(vmName) + '&vmid=' + vmid + '&session_id=' + encodeURIComponent(sid); return; } if (sdata.status === 'failed') { diff --git a/frontend/templates/job_polling.html b/frontend/templates/job_polling.html new file mode 100644 index 0000000..c1003d9 --- /dev/null +++ b/frontend/templates/job_polling.html @@ -0,0 +1,97 @@ +{% extends "base.html" %} +{% block content %} + +
+
+ Converting VM {{ vmid or '...' }} +
+
+

Job: {{ job_id }}  |  {{ vm_name }}

+ +
+ Queued +
+ +
+
0%
+
+ +

Waiting for backend...

+

Elapsed: 0s

+
+
+
+ +
+
+ Conversion Complete — VM {{ vmid or '...' }} Ready +
+
+

Create another VM from this disk?

+ +
+
+ + + +{% endblock %} \ No newline at end of file diff --git a/frontend/templates/scp.html b/frontend/templates/scp.html index 7065ecc..7a2ab9c 100644 --- a/frontend/templates/scp.html +++ b/frontend/templates/scp.html @@ -135,11 +135,7 @@ async function startScpPull(e) { var elapsed = Math.round((Date.now() - astart) / 1000); setScpPhase('Analysing... ' + (sdata.message || '') + ' (' + elapsed + 's)', sdata.status === 'completed' ? 100 : 50, sdata.status !== 'completed'); if (sdata.status === 'completed') { - await new Promise(r => setTimeout(r, 500)); - var rr = await fetch('/session/analyze/result/' + analysisId + '?source_filename=' + encodeURIComponent(filename) + '&vm_name=' + encodeURIComponent(vmName) + '&vmid=' + vmid); - document.getElementById('scp-analysis-section').innerHTML = await rr.text(); - document.getElementById('scp-status').classList.add('pve-hidden'); - scpInitConfirmForm(); + window.location.href = '/session/configure/' + analysisId + '?source_filename=' + encodeURIComponent(filename) + '&vm_name=' + encodeURIComponent(vmName) + '&vmid=' + vmid + '&session_id=' + SESSION_ID; btn.disabled = false; return; } if (sdata.status === 'failed') {