Reusable Dioxus admin UI (hero_admin_lib_dx + serve_admin_dioxus) — build + match existing admins 1:1 #13
Labels
No labels
prio_critical
prio_low
type_bug
type_contact
type_issue
type_lead
type_question
type_story
type_task
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_website_framework#13
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
A reusable Dioxus admin-UI library so any Hero service gets a consistent admin panel (service name left, user right; default tabs Docs / OpenRPC / MCP / Agent / Logs + custom tabs) for near-zero code, served via the standard lifecycle. Goal: match the existing Askama admins 1:1 (dark theme, domain tabs with counts, system-stats sidebar, polished API docs), while keeping the library generic.
What already exists (pushed to
development)hero_admin_lib_dx(hero_website_framework/crates/hero_admin_lib_dx,@691f378) — the Dioxus component library:Adminbuilder (Admin::new("svc").title(..).tab(name, fn).launch()), shell (top bar + tab bar + switcher), tabs (Docs=router.markdown, OpenRPC=schema-driven playground, MCP, Agent=router.agent.run, Logs=hero_proc), JSON-Schema renderer, router transport, discovery. README + QUICKSTART in the crate.hero_lifecycle::ServiceManifest::serve_admin_dioxus::<App>()(hero_lib@035a5d76) — serves an embeddeddxbundle onadmin.sockwith brotli/gzip + SPA fallback, reusing the whole admin lifecycle. Sibling toserve_admin/serve_admin_dashboard.hero_skills/skills/hero/ui/hero_admin_dioxus.{md,toml}— the full recipe (app crate + server crate + build.rs(dx) + service.toml + lab build/run).hero_proc/crates/hero_proc_admin_dx_app(Admin::new("hero_proc")), dx-builds + serves; pullshero_admin_lib_dxfrom git.Proven live
Discovery + schema playground (typed form → execute → typed result), Docs/MCP tabs, Logs (live hero_proc lines), switcher, dark mode — all verified in-browser against a running
hero_router.How a service uses it (target ergonomics)
App crate:
Admin::new("hero_<svc>").title("Hero <Svc>").tab("Custom", custom_tab).launch();Server crate:
ServiceManifest::from_toml(SERVICE_TOML).serve_admin_dioxus::<App>().await+ a one-linebuild.rsrunningdx.Same-origin transport through
hero_router→ no API URLs, no CORS.Remaining work (to reach 1:1 with the Askama admins)
/api/domains.json(depends on the multi-domain migration issue inhero_proc). Discover a service's domains, fetch each/api/{domain}/openrpc.json, route/api/{domain}/rpc. The domains map directly to the admin's domain tabs (Jobs/Logs/Secrets/System) — so this is how we get the rich domain-tab UI generically rather than hand-coding it.system.stats/ hero_proc, matching the Askama sidebar.domains.jsonand show counts, matching the Askama tab bar.dioxus-bootstrap-css(ecosystem convention) if desired.Reference (the design to match)
The existing Askama
hero_proc_admin(kept running as the 1:1 reference): rich dark UI, domain tabs with counts (Actions/Services/Runs/Jobs/Secrets/Schedules/Logs/Stats/Admin/API/Docs/Terminal), System Stats sidebar, two-panel API docs. Compare side-by-side at…/hero_proc/admin/(Askama) vs the Dioxus app (dx serve,?router=…).Iteration loop
App crate points at local
hero_admin_lib_dx(path dep) while iterating; switch back to the git dep + push when a change is ready, so improvements land in the shared library for all services.Related
hero_proc.Filed by Claude (owner-mode work for Timur).
Multi-domain support landed:
hero_website_framework@5e9724f.The admin now handles the one-socket multi-domain model: it discovers a service's domains via
/<svc>/rpc/api/domains.json, fetches each/api/<domain>/openrpc.json, and routes calls to/api/<domain>/rpc(domains.rs+OpenRpcTab). Forhero_procthe OpenRPC tab shows domain sub-tabs jobs / logs / secrets / system, each with its schema-driven playground (88 jobs methods render live). Also: dark-theme default, and a discovery filter to the mainrpc.sockentry (multi-domain services carry no inline cached spec).Validated live against new-model hero_proc through
hero_router.Remaining toward 1:1 with the Askama admin:
Multi-domain support landed:
hero_website_framework@5e9724f.The admin now handles the one-socket multi-domain model: it discovers a service's domains via
/<svc>/rpc/api/domains.json, fetches each/api/<domain>/openrpc.json, and routes calls to/api/<domain>/rpc(domains.rs+OpenRpcTab). Forhero_procthe OpenRPC tab shows domain sub-tabs jobs / logs / secrets / system, each with its schema-driven playground (88 jobs methods render live). Also: dark-theme default, and a discovery filter to the mainrpc.sockentry (multi-domain services carry no inline cached spec).Validated live against new-model hero_proc through
hero_router.Remaining toward 1:1 with the Askama admin:
Progress — multi-domain shell now 1:1 with the Askama reference
Pushed to
development(hero_website_framework@c9d5ab0). Validated live against new-modelhero_proc(onerpc.sock,/api/domains.json) via headless Chrome.Landed this round:
jobs 88 / logs 10 / secrets 11 / system 9, thenDocs / MCP / Agent / Logs. Each domain tab is the schema-driven playground for that domain (/<svc>/rpc/api/<domain>/rpc). The generic OpenRPC tab is dropped when domains are present, kept as the single-domain fallback.systemdomain'ssystem.statsevery 3s. Mounted only when the service exposes asystemdomain, so it stays generic across services. Live:Memory 11.4 GB (67%),CPU,RX/TX,OS processes 723 / Services 2,Jobs running/pending/failed/total.No cached OpenRPC specerror); single-domain still uses router-generated markdown.domains.rsgainedlist_with_specs()(list + per-domain spec in one pass, reused for both badges and panels) andstats().Remaining toward full polish: API method panel two-pane refinement (request/response side-by-side); richer per-domain table views (the Askama admin renders Actions/Jobs/Runs as bespoke tables vs. our generic playground); optional bootstrap-icons in the tab bar. The hero_proc app still uses a local path dep on
hero_admin_lib_dxfor iteration — switch to the git dep once design settles.Multi-domain socket migration for hero_proc itself remains tracked in hero_proc#148.
Follow-up (
hero_website_framework@0f69939): method panel is now two-pane — Parameters + Execute in aRequestcard on the left, result in aResponsecard on the right (stacks on narrow viewports). Validated live: ranjobs.action_listthrough the router →["hero_proc_admin","hero_router"]in the Response pane.Remaining in this issue is now just optional polish: bespoke per-domain table views (the generic schema playground is the reusable equivalent — deferring unless we want service-specific table renderers), tab-bar icons, and switching the hero_proc demo app from its local path dep to the git dep once the design is final.
Redesign — header domain-picker + island layout (
hero_website_framework@b00de26)Reworked the shell per design review:
domain: All domains ▾, each option shows its method count). The built-in tabs now render scoped to the selected domain (or all when none is picked).Playground is domain-aware: a specific domain shows its panel; All shows every domain's methods grouped, each routed to its own
/api/<domain>/rpc. Docs/MCP filter to the selected domain.Logs were rewired onto the new one-socket logs domain (
/<svc>/rpc/api/logs/rpcfindwith the requiredLogFilterfieldserror_only/limit/offset), mappingLogEntry{content,src,epoch}→ line, stripping ANSI, and classifying severity from the message text (the capture'serrorflag only means from stderr). The hero_proc admin shows all activity; other services scope to their own source prefix.Validated live: domain picker scopes the playground (
secrets→ 11 methods) and docs; the Logs island populates 80 clean lines.Note / follow-up: the playground executes destructive methods (
system.shutdown/reboot/wipe_all) live with no confirmation — worth a confirm guard in the method panel, since running shutdown takes down hero_proc (the supervisor) and the whole managed stack with it.Generic shell + service-defined tabs — hero_proc views ported
Architecture landed (
hero_website_framework@f9842db): the sidebar/shell is now a generic service UI any admin reuses — service name (+switcher), description, per-socket health dots, domain selector, and the service's logs (aggregated across its job sources, filterable). No service-specific UI in the shell. All bespoke views are service-defined tabs viaAdmin::tab(label, fn).hero_proc app (
hero_proc@12170e9, now committed + on the git dep to development) demonstrates the pattern with the full Askama view set ported as tabs:job_list_fulltable + Restart / Stop / Deleteservice_list_fulltable + Start / Restart / Stoprun_list_fulltable + Deleteschedule_list_fulltable + Deletesecret_list_full(secrets domain) + Delete; surfaces backend errors (the local secrets DB is currently malformed, shown as a banner)Safety: destructive actions (Stop/Delete/Restart/Admin ops) use an inline arm-then-confirm (first click → red "Confirm", second runs) so a stray click can't stop hero_router and cut the admin off.
Validated live via headless Chrome (tables populate, badges, actions, arm guard, error banners).
Remaining: Terminal. It can't be a pure app tab — there's no PTY method on the RPC domain, the router doesn't proxy the PTY WebSocket path (returns 400), and the interactive PTY is served by the admin's own web server. A working Terminal needs backend plumbing (expose/proxy the PTY WS via
serve_admin_dioxusor the router) plus xterm.js interop in the WASM frontend — a separate cross-cutting feature, deliberately deferred rather than shipped broken.Second service proves the shell is generic — hero_router admin (
hero_router@ffc955d)Built a coexisting Dioxus admin for hero_router on the same generic shell (
hero_admin_lib_dx), alongside its existing Askama admin — no shell changes were needed, which is the point.Generic shell (unchanged): Playground / Docs / MCP / Agent + the service sidebar (identity
Hero Router v0.2.1+ description, per-socket health, logs scoped to theroutersource). The Playground drives the router's own 38router.*methods via its cached spec.Router-specific tabs (
router.*RPC):router.services): title, context, health, method count, version, last checked, + Rescan.router.access.list) with context/state + Clear (here it showshero_proc_unreachable, surfaced in a banner, because the local hero_proc secrets DB is malformed).router.ssh_keys.list.Validated live against the running router: 5 services listed, status 5 total / 4 healthy / 1 inactive, uptime ~10h, access state surfaced.
App pattern is identical to the hero_proc one: an isolated wasm
[workspace]crate depending onhero_admin_lib_dxvia the git (development) dep, adding service tabs withAdmin::tab(..). Terminal remains the one deferred view for both services (needs PTY-WS backend plumbing).Dioxus admins now served behind hero_router (serve_admin_dioxus validated end-to-end)
The Dioxus admins are now reachable through hero_router exactly like the existing service admin UIs — not just
dx servedev. Per the hero_skills convention (/hero_sockets,/hero_web_default_routes): a service admin UI is a web app on a<svc>/<stem>.sockthat exposes/health.json+/heroservice.json; the router discovers it and proxies it at/{svc}/{socket_stem}/.Implemented a tiny server crate per service (
hero_proc_admin_dx,hero_router_admin_dx—hero_proc@f5bfead,hero_router@1457df1):[workspace]crate; deps =hero_lifecycle {git, branch=development, features=[webserver-admin]}+ rust-embed + tokio.main.rsis 3 lines:ServiceManifest::from_toml(TOML).peer("<svc>").serve_admin_dioxus::<App>().awaitwithApp = #[derive(RustEmbed)] #[folder="app_dist/"].service.tomldeclares an admin manifest on<svc>/admindx.sock(coexists with the Askamaadmin.sock).build.rsrunsdx build --platform web --base-path <svc>/admindxon the sibling app crate and stagesweb/public→app_dist/. The base-path equals the router proxy segment so the SPA's absolute asset URLs resolve behind the proxy; RPC calls go same-origin to the router (no?router=needed).Reachable at
http://<router:9988>/hero_proc/admindx/and/hero_router/admindx/— validated live: the full admin renders and Jobs lists live jobs through the router.This validates
hero_lifecycle::serve_admin_dioxus(035a5d76) end-to-end for the first time. Follow-up: register these admin servers with lab/zinit so they autostart alongside each service.