Files
unraid-podman/webui/plugins/podman/Podman.page
T
maggesandClaude Sonnet 5 e2fefcdf9c
Build Packages / Build .txz packages (push) Failing after 9s
Lint / ShellCheck (push) Failing after 43s
Lint / Validate .plg XML (push) Successful in 10s
Lint / EditorConfig (push) Failing after 6s
Add reproducible build system, native Unraid plugin, and WebUI
- versions.env pins podman, conmon, crun, netavark, aardvark-dns, passt,
  and fuse-overlayfs to verified upstream source checksums; SlackBuild
  recipes, scripts/build-packages.sh, checksums.sh, release.sh, and
  update-versions.sh implement the reproducible pipeline; GitHub Actions
  workflows build in a Slackware container and publish releases without
  committing any binaries.

- plugin/podman.plg installs/updates/removes all eight packages (the
  seven components plus the plugin's own unraid-podman scaffolding
  package) via upgradepkg, using the official Unraid array-event hook
  mechanism (event/disks_mounted, event/stopping) instead of editing
  /boot/config/go. rc.podman and the sbin/ helper scripts implement
  storage creation, config seeding/sync, preflight checks, autostart
  with per-container Safe-Mode, and package verify/update/rollback.

- webui/plugins/podman implements the Dashboard, Containers, Pods,
  Images, Volumes, Networks, Logs, Terminal, Compose, and Settings
  panels against the approved mockup (webui/mockups/prototype.html),
  talking to podman system service exclusively via PodmanClient.php
  (libpod REST API over the Unix socket), with two documented
  exceptions: Terminal's one-shot exec model and Compose's use of the
  podman compose CLI, since libpod has no REST equivalent for either.

- docs/ARCHITECTURE.md and docs/ROADMAP.md record the design decisions
  and honest current status (syntax-checked, unit- and
  integration-tested against fake sockets/servers; not yet run against
  a real Unraid/Podman/Slackware system).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 10:51:14 +00:00

257 lines
12 KiB
Plaintext

Menu="Podman"
Title="Podman"
Icon="podman"
---
<?php
/**
* Podman.page — Unraid Dynamix-style GUI page for unraid-podman.
*
* This file only renders the page shell: the header, the sub-navigation
* for the ten sections, and one (mostly empty) container per panel. Every
* panel fills itself in via AJAX after load — see javascript/app.js's
* router and each javascript/<panel>.js module — so this file has no
* business logic of its own and does not talk to PodmanClient directly.
* See docs/ARCHITECTURE.md section 18 for the overall WebUI design and
* webui/mockups/prototype.html for the approved visual reference this
* page's markup mirrors (same structure, same CSS classes, real data
* instead of static samples).
*/
?>
<link rel="stylesheet" type="text/css" href="/plugins/podman/styles/podman.css">
<div class="podman-plugin">
<header class="podman-pagehead">
<div class="titlewrap">
<div class="icon" aria-hidden="true">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M12 2l8 4.5v9L12 20l-8-4.5v-9L12 2z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/><path d="M12 11l8-4.5M12 11v9M12 11L4 6.5" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/></svg>
</div>
<div>
<h1>Podman</h1>
<div class="meta" id="podman-header-meta">Connecting…</div>
</div>
</div>
<div style="display:flex; gap:8px;">
<button class="podman-btn" id="podman-refresh-all">&#8635; Refresh</button>
</div>
</header>
<nav class="podman-subnav">
<button class="active" data-panel="dashboard">Dashboard</button>
<button data-panel="containers">Containers</button>
<button data-panel="pods">Pods</button>
<button data-panel="images">Images</button>
<button data-panel="volumes">Volumes</button>
<button data-panel="networks">Networks</button>
<button data-panel="logs">Logs</button>
<button data-panel="terminal">Terminal</button>
<button data-panel="compose">Compose</button>
<button data-panel="settings">Settings</button>
</nav>
<main class="podman-main">
<!-- ============================= DASHBOARD ============================= -->
<section class="podman-panel active" id="podman-panel-dashboard">
<div class="podman-grid podman-stat-grid">
<div class="podman-stat"><div class="label">Running</div><div class="value tnum"><span id="stat-running">—</span> <small id="stat-running-total"></small></div></div>
<div class="podman-stat"><div class="label">Pods</div><div class="value tnum" id="stat-pods">—</div></div>
<div class="podman-stat"><div class="label">Images</div><div class="value tnum" id="stat-images">—</div></div>
<div class="podman-stat"><div class="label">Volumes</div><div class="value tnum" id="stat-volumes">—</div></div>
<div class="podman-stat"><div class="label">Networks</div><div class="value tnum" id="stat-networks">—</div></div>
<div class="podman-stat"><div class="label">Images on Disk</div><div class="value tnum" id="stat-images-size">—</div></div>
</div>
</section>
<!-- ============================= CONTAINERS ============================= -->
<section class="podman-panel" id="podman-panel-containers">
<div class="podman-card">
<div class="podman-toolbar">
<input class="podman-search" id="containers-search" type="text" placeholder="Search containers by name or image…">
<div class="filterset" id="containers-filterset" style="display:flex; gap:4px; background:var(--surface-2); padding:3px; border-radius:8px;">
<button class="active" data-filter="all" id="containers-count-all">All</button>
<button data-filter="running" id="containers-count-running">Running</button>
<button data-filter="stopped" id="containers-count-stopped">Stopped</button>
</div>
</div>
<div class="podman-table-wrap">
<table>
<thead><tr><th>Status</th><th>Name</th><th>Image</th><th>CPU/Mem</th><th>Ports</th><th>Uptime</th><th></th></tr></thead>
<tbody id="containers-tbody"></tbody>
</table>
</div>
</div>
</section>
<!-- ============================= PODS ============================= -->
<section class="podman-panel" id="podman-panel-pods"></section>
<!-- ============================= IMAGES ============================= -->
<section class="podman-panel" id="podman-panel-images">
<div class="podman-card">
<div class="podman-toolbar">
<input class="podman-search" type="text" placeholder="Search images…" disabled title="Client-side filtering not yet wired up for Images">
<button class="podman-btn" id="images-pull-btn">&#11015; Pull Image</button>
</div>
<div class="podman-table-wrap">
<table>
<thead><tr><th>Repository</th><th>Tag</th><th>Image ID</th><th>Size</th><th>Created</th><th>Used By</th><th></th></tr></thead>
<tbody id="images-tbody"></tbody>
</table>
</div>
</div>
</section>
<!-- ============================= VOLUMES ============================= -->
<section class="podman-panel" id="podman-panel-volumes">
<div class="podman-card">
<div class="podman-toolbar">
<div style="flex:1;"></div>
<button class="podman-btn podman-btn-primary" id="volumes-create-btn">+ New Volume</button>
</div>
<div class="podman-table-wrap">
<table>
<thead><tr><th>Name</th><th>Driver</th><th>Mountpoint</th><th>Used By</th><th></th></tr></thead>
<tbody id="volumes-tbody"></tbody>
</table>
</div>
<div class="podman-empty-note" style="border-top:1px solid var(--border); text-align:left; padding:12px 18px;">
App data mapped from Unraid shares (e.g. <span class="mono">/mnt/user/appdata/…</span>) is managed as bind mounts per-container and does not appear here.
</div>
</div>
</section>
<!-- ============================= NETWORKS ============================= -->
<section class="podman-panel" id="podman-panel-networks">
<div class="podman-card">
<div class="podman-toolbar">
<div style="flex:1;"></div>
<button class="podman-btn podman-btn-primary" id="networks-create-btn">+ New Network</button>
</div>
<div class="podman-table-wrap">
<table>
<thead><tr><th>Name</th><th>Driver</th><th>Subnet</th><th>Gateway</th><th>Containers</th><th></th></tr></thead>
<tbody id="networks-tbody"></tbody>
</table>
</div>
</div>
</section>
<!-- ============================= LOGS ============================= -->
<section class="podman-panel" id="podman-panel-logs">
<div class="podman-card">
<div class="podman-logs-layout">
<div class="podman-logs-side" id="logs-sidebar"></div>
<div>
<div class="podman-toolbar">
<input class="podman-search" id="logs-filter" type="text" placeholder="Filter log output…" style="max-width:280px;">
<span id="logs-follow-toggle" style="display:flex; gap:4px; background:var(--surface-2); padding:3px; border-radius:8px;">
<button class="active" data-follow="true">Follow</button>
<button data-follow="false">Paused</button>
</span>
</div>
<div class="podman-log-pane" id="log-pane" role="log"></div>
</div>
</div>
</div>
</section>
<!-- ============================= TERMINAL ============================= -->
<section class="podman-panel" id="podman-panel-terminal">
<div class="podman-card">
<div class="podman-card-pad">
<div style="display:flex; gap:8px; align-items:center; margin-bottom:12px; font-size:12.5px; color:var(--text-dim);">
Exec into: <select id="term-container-select"></select>
</div>
<div class="podman-term" id="term-output"></div>
<input class="podman-term-input" id="term-input" type="text" placeholder="Type a command and press Enter… (one-shot exec — see Compose panel note on API scope)" autocomplete="off">
</div>
</div>
</section>
<!-- ============================= COMPOSE ============================= -->
<section class="podman-panel" id="podman-panel-compose">
<div class="podman-card">
<div class="podman-compose-layout">
<div class="podman-compose-side" id="compose-sidebar"></div>
<div>
<div class="podman-toolbar">
<strong id="compose-title" style="flex:1;">—</strong>
<button class="podman-btn" id="compose-action-pull">&#11015; Pull</button>
<button class="podman-btn" id="compose-action-down">&#9632; Down</button>
<button class="podman-btn podman-btn-primary" id="compose-action-up">&#9654; Up</button>
</div>
<pre class="podman-yaml" id="compose-yaml"></pre>
</div>
</div>
</div>
</section>
<!-- ============================= SETTINGS ============================= -->
<section class="podman-panel" id="podman-panel-settings">
<div class="podman-grid">
<div class="podman-card">
<div class="podman-card-head"><h2>Storage</h2></div>
<div class="podman-field-row">
<label for="settings-storage-path">Storage path</label>
<div>
<input type="text" id="settings-storage-path" class="mono">
<div class="hint">Cache pool or dedicated disk — never a path under /mnt/user (FUSE).</div>
</div>
</div>
<div class="podman-field-row">
<label for="settings-storage-size">podman.img size</label>
<div><input type="number" id="settings-storage-size" style="max-width:100px;"> <span style="font-size:12px;color:var(--text-dim);">GB</span></div>
</div>
</div>
<div class="podman-card">
<div class="podman-card-head"><h2>Autostart &amp; Lifecycle</h2></div>
<div class="podman-field-row">
<label for="settings-enabled">Start podman on array start</label>
<div><input type="checkbox" id="settings-enabled"></div>
</div>
<div class="podman-field-row">
<label for="settings-stop-timeout">Container stop timeout</label>
<div><input type="number" id="settings-stop-timeout" style="max-width:100px;"> <span style="font-size:12px;color:var(--text-dim);">seconds</span></div>
</div>
<div class="podman-field-row">
<label>Autostart order</label>
<div class="podman-table-wrap">
<table>
<thead><tr><th>#</th><th>Container</th><th></th></tr></thead>
<tbody id="autostart-tbody"></tbody>
</table>
</div>
</div>
<div class="podman-field-row">
<label></label>
<div><button class="podman-btn podman-btn-primary" id="settings-save-btn">Save Settings</button></div>
</div>
</div>
<div class="podman-card">
<div class="podman-card-head"><h2>Installed Packages</h2></div>
<div class="podman-field-row">
<label>Versions</label>
<div class="hint mono" id="settings-package-versions" style="max-width:none;">—</div>
</div>
</div>
</div>
</section>
</main>
</div>
<script src="/plugins/podman/javascript/app.js"></script>
<script src="/plugins/podman/javascript/dashboard.js"></script>
<script src="/plugins/podman/javascript/containers.js"></script>
<script src="/plugins/podman/javascript/pods.js"></script>
<script src="/plugins/podman/javascript/images.js"></script>
<script src="/plugins/podman/javascript/volumes.js"></script>
<script src="/plugins/podman/javascript/networks.js"></script>
<script src="/plugins/podman/javascript/logs.js"></script>
<script src="/plugins/podman/javascript/terminal.js"></script>
<script src="/plugins/podman/javascript/compose.js"></script>
<script src="/plugins/podman/javascript/settings.js"></script>