Three things, headlined by the long-blocked graphical PXE menu.
## 1. Graphical PXE boot background — the iVentoy feature, finally
iVentoy paints a PNG background on the PXE screen using stock iPXE
built with CONSOLE_FRAMEBUFFER + IMAGE_PNG + CONSOLE_CMD; the public
iPXE binaries omit those, so `console --picture` is a no-op on them.
We now build our own iPXE from upstream with that thin config delta
(deploy/ipxe/local/{general,console}.h).
The 8-release blocker was cc1 segfaulting when an amd64 gcc ran under
QEMU emulation on the arm64 build host. Fix: a new `ipxe-build`
Dockerfile stage pinned to $BUILDPLATFORM (native arch — no emulation)
that cross-compiles x86_64 iPXE with CROSS_COMPILE=x86_64-linux-gnu-.
The compiler runs native and emits x86_64. Validated end-to-end:
png.o + fbcon.o + pixbuf.o all compile and link (confirmed via the
linked-ELF symbol table, not just strings), ~112s, no segfault. Host
tools needed libc6-dev (dropped by --no-install-recommends; without
it the native host compile falls through to iPXE's freestanding
headers and dies on bits/stdint.h — fixed).
Server side:
- pxe_logo.rs is now a full-screen background compositor: a dark field
(matching the WebUI theme) with the operator's uploaded logo across
the top, or — with no upload — a default OpenPXE rainbow disc drawn
with pure pixel math (no font/SVG deps). Always 1024x768 (iPXE
doesn't scale; this is the universal mode). WebP/JPEG/GIF/PNG in,
PNG out (iPXE only eats PNG).
- /branding/pxe-logo always returns a PNG now (default when no logo,
default when SVG) so the menu always has a background.
- render_menu uses `console --picture … --top 290 || console`: paints
the background and reserves the logo band on PNG-capable binaries
(x86_64 UEFI), cleanly falls back to text on the others. The ASCII
wordmark is GONE.
Only x86_64 UEFI is built from source (host-arch-agnostic cross build);
BIOS/i386/arm64 keep upstream-fetched no-PNG binaries + text fallback.
Modern clients are overwhelmingly x86_64 UEFI.
## 2. NFS AUTH_SYS credential — fixes NFS3ERR_ACCES
v0.4.68's privileged-port fix got past MNT3ERR_ACCES (mount); operators
then hit NFS3ERR_ACCES on READDIR because nfs3_client defaults to
AUTH_NONE and virtually every server exports sec=sys. We now present an
AUTH_UNIX credential (uid 0 / gid 0): no_root_squash servers treat us
as root, root_squash servers map us to anon which reads any
world-readable ISO share. Kept fixed (no UI knob) to stay dead-simple.
Hint updated: a remaining NFS3ERR_ACCES is now a server-side
permission/squash issue, not IP/auth-flavor.
## 3. FleetDM-style full-width logo (top-left)
When a custom logo is uploaded the sidebar header drops the bundled
mark + "OpenPXE" wordmark and lets the logo span the header
(left-aligned, capped 200x50, contain). Rendered server-side via a
brand-class in index_html (has_custom_logo) so there's no flash of the
default. The bundled-default case is unchanged.
Tests: 164 passing. clippy -D warnings clean. iPXE build stage
validated in isolation before the full image build.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
78 lines
2.8 KiB
Rust
78 lines
2.8 KiB
Rust
//! Offline-only web UI. Everything the browser needs (HTML, CSS, JS, SVG
|
|
//! logo) is embedded in the compiled binary via `include_str!` /
|
|
//! `include_bytes!`. No CDN, no external fonts, no remote images —
|
|
//! OpenPXE renders identically on an air-gapped network.
|
|
//!
|
|
//! Layout follows the Netbox Labs pattern: dark left sidebar with primary
|
|
//! nav, top bar with secondary tabs, card-dense content panels.
|
|
#![forbid(unsafe_code)]
|
|
|
|
/// Render the top-level page.
|
|
///
|
|
/// * `base_url` is interpolated into the footer so operators can see at
|
|
/// a glance what URL clients are PXE-booting from.
|
|
/// * `asset_version` is appended as `?v=…` to every asset URL so each
|
|
/// release ships with brand-new asset URLs — browsers (and any
|
|
/// intermediary proxy) can't keep serving last release's `app.js`
|
|
/// when we know the new one is incompatible. Combined with
|
|
/// `Cache-Control: no-cache, must-revalidate` on the asset handlers,
|
|
/// the worst-case caching window is one version.
|
|
/// * `logo_rev` is appended to the brand-mark and favicon URLs as an
|
|
/// extra `&r=…` token. Unlike `asset_version` it changes every time
|
|
/// the operator swaps the custom logo, so the top-left mark updates
|
|
/// immediately on the next page load instead of being pinned to the
|
|
/// release version (which only changes on upgrade). `index.html`
|
|
/// itself is served `no-cache`, so the fresh token lands as soon as
|
|
/// the operator reloads after an upload.
|
|
/// * `has_custom_logo` switches the sidebar brand block between the
|
|
/// bundled mark + "OpenPXE" wordmark (false) and a FleetDM-style
|
|
/// full-width custom logo with the wordmark hidden (true). Rendered
|
|
/// server-side so there's no flash of the default mark before JS runs.
|
|
#[must_use]
|
|
pub fn index_html(
|
|
base_url: &str,
|
|
asset_version: &str,
|
|
logo_rev: u64,
|
|
has_custom_logo: bool,
|
|
) -> String {
|
|
let brand_class = if has_custom_logo {
|
|
"brand has-custom-logo"
|
|
} else {
|
|
"brand"
|
|
};
|
|
INDEX_HTML
|
|
.replace("{{BASE_URL}}", base_url)
|
|
.replace("{{ASSET_VERSION}}", asset_version)
|
|
.replace("{{LOGO_REV}}", &logo_rev.to_string())
|
|
.replace("{{BRAND_CLASS}}", brand_class)
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn app_js() -> &'static str {
|
|
APP_JS
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn app_css() -> &'static str {
|
|
APP_CSS
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn logo_svg() -> &'static str {
|
|
LOGO_SVG
|
|
}
|
|
|
|
/// Larger, faster-cycling rainbow disc — used for the page-load
|
|
/// transition and the imaging-progress widget on Dashboard / Queue.
|
|
/// Pure SVG + SMIL, no JS, no GIF.
|
|
#[must_use]
|
|
pub fn loader_svg() -> &'static str {
|
|
LOADER_SVG
|
|
}
|
|
|
|
const INDEX_HTML: &str = include_str!("index.html");
|
|
const APP_CSS: &str = include_str!("app.css");
|
|
const APP_JS: &str = include_str!("app.js");
|
|
const LOGO_SVG: &str = include_str!("logo.svg");
|
|
const LOADER_SVG: &str = include_str!("loader.svg");
|