- Add build-proxmox-iso.sh: remaster Ubuntu ISO with autoinstall config, offline packages, playbook, webapp, and boot files for zero-touch Proxmox VM deployment - Add boot-files/ directory for WinPE boot files (wimboot, boot.wim, BCD, ipxe.efi, etc.) sourced from WestJeff playbook - Update build-usb.sh and test-vm.sh to bundle boot-files automatically - Add usb_root variable to playbook, fix all file copy paths to use it - Unify Apache VirtualHost config (merge default site + webapp proxy) - Add CSRF token protection to all webapp POST forms and API endpoints - Update README with Proxmox deployment instructions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
121 lines
4.3 KiB
HTML
121 lines
4.3 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Image Import - PXE Server Manager{% endblock %}
|
|
|
|
{% block content %}
|
|
<h2 class="mb-4">Image Import</h2>
|
|
|
|
<div class="row">
|
|
<div class="col-lg-8">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<i class="bi bi-usb-drive me-2"></i> Import from USB Drive
|
|
</div>
|
|
<div class="card-body">
|
|
{% if usb_mounts %}
|
|
<form method="POST" id="importForm">
|
|
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
|
<div class="mb-3">
|
|
<label for="source" class="form-label fw-semibold">Source (USB Mount Point)</label>
|
|
<select class="form-select" name="source" id="source" required>
|
|
<option value="">-- Select a mounted USB drive --</option>
|
|
{% for mount in usb_mounts %}
|
|
<option value="{{ mount }}">{{ mount }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
<div class="form-text">
|
|
Select the mounted USB drive containing the WinPE deployment content.
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="target" class="form-label fw-semibold">Target Image Type</label>
|
|
<select class="form-select" name="target" id="target" required>
|
|
<option value="">-- Select target image --</option>
|
|
{% for it in image_types %}
|
|
<option value="{{ it }}">{{ friendly_names[it] }} ({{ it }})</option>
|
|
{% endfor %}
|
|
</select>
|
|
<div class="form-text">
|
|
Content will be copied into the Deploy directory for this image type.
|
|
</div>
|
|
</div>
|
|
|
|
<div class="alert alert-warning d-flex align-items-start" role="alert">
|
|
<i class="bi bi-exclamation-triangle-fill me-2 mt-1"></i>
|
|
<div>
|
|
<strong>Warning:</strong> Existing files in the target Deploy directory with the
|
|
same names will be overwritten. This operation may take several minutes for large
|
|
images.
|
|
</div>
|
|
</div>
|
|
|
|
<button type="submit" class="btn btn-primary" id="importBtn">
|
|
<i class="bi bi-download me-1"></i> Start Import
|
|
</button>
|
|
</form>
|
|
{% else %}
|
|
<div class="text-center py-5">
|
|
<i class="bi bi-usb-plug display-4 text-muted"></i>
|
|
<h5 class="mt-3 text-muted">No USB Drives Detected</h5>
|
|
<p class="text-muted mb-0">
|
|
No mounted USB drives were found under <code>/mnt/</code> or <code>/media/</code>.<br>
|
|
Mount a USB drive and refresh this page.
|
|
</p>
|
|
<button class="btn btn-outline-secondary btn-sm mt-3" onclick="location.reload()">
|
|
<i class="bi bi-arrow-clockwise"></i> Refresh
|
|
</button>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<i class="bi bi-info-circle me-2"></i> Current Image Status
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<table class="table table-sm mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Image</th>
|
|
<th>Content</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for img in images %}
|
|
<tr>
|
|
<td class="small">{{ img.friendly_name }}</td>
|
|
<td>
|
|
{% if img.has_content %}
|
|
<span class="badge bg-success">Present</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary">Empty</span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_scripts %}
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
var form = document.getElementById('importForm');
|
|
if (form) {
|
|
form.addEventListener('submit', function() {
|
|
var btn = document.getElementById('importBtn');
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span> Importing...';
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %}
|