feat: add multi-device provisioning and local remote-config UI
- add multi-device portal device management, key rotation, and sync request APIs - add Pi settings UI for remote server URL, device name, and API key - add local test-connection and sync-now actions - make upload and config sync reuse dynamic shared credentials - add Phase 25.5c migration and docs updates Co-authored-by: Copilot <[email protected]>
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
<div>
|
||||
<h3 class="text-dark mb-1 fw-bold">Device settings</h3>
|
||||
<p class="text-muted mb-0">
|
||||
Edit the identity synced to the Pi for <code>{{ configured_device_name }}</code>.
|
||||
Manage registered Pi devices, one-time API keys, and synced identity values.
|
||||
</p>
|
||||
</div>
|
||||
<button class="btn btn-primary" id="device-settings-save" type="submit" form="device-settings-form" disabled>
|
||||
@@ -18,64 +18,133 @@
|
||||
<div id="device-settings-message" class="alert" role="status" hidden></div>
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-12 col-xl-8">
|
||||
<div class="col-12 col-xl-4">
|
||||
<div class="card shadow-sm mb-3">
|
||||
<div class="card-header py-2 d-flex align-items-center justify-content-between">
|
||||
<h6 class="text-primary fw-bold m-0">Registered devices</h6>
|
||||
<span class="badge bg-secondary" id="device-count">0</span>
|
||||
</div>
|
||||
<div class="list-group list-group-flush" id="device-list">
|
||||
<div class="list-group-item text-muted small">Loading devices…</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header py-2">
|
||||
<h6 class="text-primary fw-bold m-0">Synced identity</h6>
|
||||
<h6 class="text-primary fw-bold m-0">Register new device</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="new-device-name">Device name</label>
|
||||
<input class="form-control" id="new-device-name" maxlength="100" placeholder="yaris-2013">
|
||||
<div class="form-text">This becomes <code>YARIS_DEVICE_NAME</code> on the Pi.</div>
|
||||
</div>
|
||||
<button class="btn btn-outline-primary" id="create-device-button" type="button">
|
||||
<i class="fas fa-plus me-1"></i>Create device + API key
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-xl-8">
|
||||
<div class="card shadow-sm mb-3">
|
||||
<div class="card-header py-2 d-flex flex-wrap align-items-center justify-content-between gap-2">
|
||||
<h6 class="text-primary fw-bold m-0">Selected device</h6>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<button class="btn btn-outline-secondary btn-sm" id="rotate-key-button" type="button" disabled>
|
||||
<i class="fas fa-key me-1"></i>Rotate API key
|
||||
</button>
|
||||
<button class="btn btn-outline-primary btn-sm" id="request-sync-button" type="button" disabled>
|
||||
<i class="fas fa-rotate me-1"></i>Request sync
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-3 mb-3 small">
|
||||
<div class="col-md-6">
|
||||
<div class="text-muted">Device name</div>
|
||||
<div class="fw-semibold" id="selected-device-name">—</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="text-muted">Portal default</div>
|
||||
<div class="fw-semibold" id="selected-device-default">—</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="text-muted">Last seen</div>
|
||||
<div class="fw-semibold" id="selected-device-last-seen">—</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="text-muted">Last config sync</div>
|
||||
<div class="fw-semibold" id="selected-device-last-sync">—</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form id="device-settings-form" novalidate>
|
||||
<div class="row g-3">
|
||||
<div class="col-12 col-md-6">
|
||||
<label class="form-label" for="owner_name">Owner name</label>
|
||||
<input class="form-control" id="owner_name" name="owner_name" maxlength="100" required>
|
||||
<input class="form-control" id="owner_name" name="owner_name" maxlength="100" required disabled>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<label class="form-label" for="car_model">Car model</label>
|
||||
<input class="form-control" id="car_model" name="car_model" maxlength="100" required>
|
||||
<input class="form-control" id="car_model" name="car_model" maxlength="100" required disabled>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<label class="form-label" for="device_name">Device name</label>
|
||||
<input class="form-control" id="device_name" name="device_name" maxlength="100">
|
||||
<div class="form-text">Used by the remote portal and future multi-device views.</div>
|
||||
<label class="form-label" for="device_name">Display device name</label>
|
||||
<input class="form-control" id="device_name" name="device_name" maxlength="100" disabled>
|
||||
<div class="form-text">Friendly label shown in the portal/device UI.</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<label class="form-label" for="avatar_path">Avatar path</label>
|
||||
<input class="form-control" id="avatar_path" name="avatar_path" maxlength="255" placeholder="img/yaris.png">
|
||||
<input class="form-control" id="avatar_path" name="avatar_path" maxlength="255" placeholder="img/yaris.png" disabled>
|
||||
<div class="form-text">Relative to the shared <code>static/</code> folder.</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-xl-4">
|
||||
<div class="card shadow-sm mb-3">
|
||||
<div class="card-header py-2">
|
||||
<h6 class="text-primary fw-bold m-0">Sync behavior</h6>
|
||||
<h6 class="text-primary fw-bold m-0">Pi credential handoff</h6>
|
||||
</div>
|
||||
<div class="card-body small text-muted">
|
||||
<p class="mb-2">
|
||||
The Pi picks these values up on its next successful internet-backed sync.
|
||||
<div class="card-body">
|
||||
<p class="small text-muted mb-3">
|
||||
Create or rotate a key, then paste these into the Pi Settings page under Remote server credentials.
|
||||
</p>
|
||||
<ul class="mb-0 ps-3">
|
||||
<li>Expected propagation delay: up to ~5 minutes while online.</li>
|
||||
<li>The Pi must have <strong>Sync with server</strong> enabled locally.</li>
|
||||
<li>Offline cars keep the last synced config until connectivity returns.</li>
|
||||
</ul>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label" for="credential-server-url">Server URL</label>
|
||||
<input class="form-control" id="credential-server-url" readonly>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label" for="credential-device-name">Device name</label>
|
||||
<input class="form-control" id="credential-device-name" readonly>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label" for="credential-api-key">API key</label>
|
||||
<input class="form-control" id="credential-api-key" readonly placeholder="Shown only after create / rotate">
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap gap-2 mt-3">
|
||||
<button class="btn btn-outline-secondary btn-sm" id="copy-credentials-button" type="button" disabled>
|
||||
<i class="fas fa-copy me-1"></i>Copy credentials
|
||||
</button>
|
||||
<span class="small text-muted align-self-center">Raw key is only shown immediately after create/rotate.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header py-2">
|
||||
<h6 class="text-primary fw-bold m-0">Preview</h6>
|
||||
<h6 class="text-primary fw-bold m-0">Sync behavior</h6>
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
<img id="avatar-preview" alt="Avatar preview" class="rounded-circle border mb-3" src="{{ url_for('static', filename='img/yaris.png') }}" style="width:96px;height:96px;object-fit:cover;">
|
||||
<div class="small text-muted mb-1">Config version</div>
|
||||
<div class="fw-bold fs-5" id="config-version">—</div>
|
||||
<div class="small text-muted mt-3" id="preview-caption">Preview updates as you type.</div>
|
||||
<div class="card-body small text-muted">
|
||||
<ul class="mb-0 ps-3">
|
||||
<li><strong>Request sync</strong> means “fetch this config on the Pi's next online contact”.</li>
|
||||
<li>The car can still be offline; the remote server cannot push directly into it.</li>
|
||||
<li>Use the Pi's local <strong>Sync now</strong> button for immediate pull attempts.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user