[grade=A] Shipdeck fleet module and manual deploy flow

Adds validated fleet install/lifecycle routes, bridge-backed Git and OCI workflows, persistent service cards, and the Local-tab Shipdeck deployment UI while preserving catalog and External flows.

Judge: urn:ump:if6udffdyelsf4qvskkr65ikhuprsjiajzij6h653fhf2zgyynzq
This commit is contained in:
DashCaddy Polish Loop
2026-09-15 11:13:29 -07:00
parent f9cbb13a3d
commit 0dd8493f98
14 changed files with 1624 additions and 230 deletions
+37 -20
View File
@@ -162,36 +162,53 @@
<div class="grid-2col">
<div>
<label for="service-port-input" style="font-size: 0.8rem; color: var(--muted); margin-bottom: 4px; display: block;">Port</label>
<input type="number" id="service-port-input" placeholder="e.g., 8096" style="font-size: 1rem;" />
<label for="service-subdomain-input" style="font-size: 0.8rem; color: var(--muted); margin-bottom: 4px; display: block;">Subdomain</label>
<input type="text" id="service-subdomain-input" placeholder="auto-derived from name" required />
</div>
<div>
<label for="service-ip-input" style="font-size: 0.8rem; color: var(--muted); margin-bottom: 4px; display: block;">IP Address</label>
<input type="text" id="service-ip-input" placeholder="Auto-detected" style="font-size: 1rem;" />
<div class="quick-ip-buttons" style="display: flex; gap: 4px; margin-top: 4px; flex-wrap: wrap;">
<button type="button" class="quick-ip-btn" data-ip="127.0.0.1" title="Localhost" style="font-size: 0.7rem; padding: 2px 6px;">localhost</button>
<button type="button" class="quick-ip-btn" data-ip="" id="quick-ip-lan" title="LAN IP" style="font-size: 0.7rem; padding: 2px 6px;">LAN</button>
<button type="button" class="quick-ip-btn" data-ip="" id="quick-ip-tailscale" title="Tailscale IP" style="font-size: 0.7rem; padding: 2px 6px;">Tailscale</button>
</div>
<label for="service-port-input" style="font-size: 0.8rem; color: var(--muted); margin-bottom: 4px; display: block;">Port</label>
<input type="number" id="service-port-input" placeholder="e.g., 8096" min="1" max="65535" required style="font-size: 1rem;" />
</div>
</div>
<div>
<label for="service-source-url" style="font-size: 0.8rem; color: var(--muted); margin-bottom: 4px; display: block;">Source URL</label>
<input type="url" id="service-source-url" placeholder="https://github.com/owner/repo" required style="font-size: 1rem;" />
</div>
<div class="grid-2col">
<div>
<label for="service-sha256-input">Sha256 pin (optional)</label>
<input type="text" id="service-sha256-input" maxlength="64" autocomplete="off" placeholder="64 lowercase hex characters" />
</div>
<div>
<label for="service-git-token">Token (optional)</label>
<input type="password" id="service-git-token" maxlength="512" autocomplete="off" placeholder="Private repositories" />
</div>
</div>
<div class="grid-2col">
<div>
<label for="service-ip-input" style="font-size: 0.8rem; color: var(--muted); margin-bottom: 4px; display: block;">Deployed Host / IP</label>
<input type="text" id="service-ip-input" value="localhost" placeholder="localhost" style="font-size: 1rem;" />
</div>
<div>
<label for="service-logo-input" style="font-size: 0.8rem; color: var(--muted); margin-bottom: 4px; display: block;">Logo URL</label>
<input type="text" id="service-logo-input" placeholder="/assets/name.png" />
</div>
</div>
<details id="shipdeckfile-preview">
<summary id="shipdeckfile-toggle" style="cursor: pointer; color: var(--accent); font-size: 0.8rem; user-select: none;">Show Shipdeckfile</summary>
<pre id="shipdeckfile-content" style="white-space: pre-wrap; max-height: 220px; overflow: auto; font-size: 0.72rem; background: var(--card-bg); padding: 10px; border-radius: 6px;">Deploy the service to render its immutable Shipdeckfile.</pre>
</details>
<div id="shipdeck-deploy-status" aria-live="polite" style="font-size: 0.78rem; color: var(--accent); min-height: 1.2em;"></div>
<!-- Options (collapsed by default) -->
<details id="local-advanced-options">
<summary style="cursor: pointer; color: var(--accent); font-size: 0.8rem; user-select: none;">Options</summary>
<div style="margin-top: 10px; display: grid; gap: 10px; font-size: 0.8rem;">
<div class="grid-2col">
<div>
<label for="service-subdomain-input">Subdomain:</label>
<input type="text" id="service-subdomain-input" placeholder="auto-derived from name" />
</div>
<div>
<label for="service-logo-input">Logo URL:</label>
<input type="text" id="service-logo-input" placeholder="/assets/name.png" />
</div>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; align-items: start;">
<label style="display: flex; align-items: center; gap: 6px; cursor: pointer;">
<input type="checkbox" id="create-dns-record" checked />