feat: move-device + logout-fix + Windows GUI installer (round 1) #10

Merged
HexC0d3rz merged 5 commits from feature/admin-round1-move-logout-installer-gui into main 2026-06-26 03:23:16 -04:00
HexC0d3rz commented 2026-06-26 02:27:36 -04:00 (Migrated from github.com)

Summary

Three sub-features in one round:

A — Move device to a different site

  • Server (devices.rs): New POST /api/devices/{id}/move handler (admin-only). Accepts { target_site_id: UUID | null }. Emits device.move audit entry with from_site_id / to_site_id. Backed by Device::set_site(pool, id, Option<Uuid>) added to db/models.rs.
  • Portal (device-actions.js, devices.js): Zeno.deviceActions.moveToSite(id, hostname) fetches /api/sites, shows a ui.dialog with a <select> containing all sites plus a "No site" option, POSTs to the new endpoint on confirm, shows snackbar. A new "Move to site…" row-action button (drive_file_move icon) calls this handler and refreshes on success.

B — Logout redirect to /login

  • app.js: The submit handler on form[action="/api/auth/logout"] now calls e.preventDefault() so the browser does not land on the bare 204. After clearing localStorage it navigates to /login.

C — Windows GUI installer (replaces console window)

New file crates/zeno-installer/src/ui.rs — Win32 form via CreateWindowExW:

PersistentInstall mode (default):

  • Dark-themed window titled "Zeno Endpoint Setup"; subtitle with server URL + site name
  • Scrollable read-only log EDIT control (Consolas 9pt, dark background) fed via EM_SETSEL + EM_REPLACESEL from the worker thread
  • Status line flips to "Installation complete / failed / rolled back" when done
  • Close button is disabled during install; enabled on completion (exits 0/1/2)

AdHocSupport mode (ad_hoc payload):

  • Window titled "Zeno Remote Support Session"
  • Technician name label + elapsed connected-time counter (WM_TIMER, 1s tick)
  • "Request help" (primary cyan, owner-drawn) — shows informational dialog; full WebSocket protocol deferred to round 2
  • "End support session" (outlined danger) — stops ZenoAgent service via sc.exe, closes window
  • WM_CLOSE (X, Alt+F4) also stops the service — no escape path leaves the session dangling

Console path unchanged: windows_subsystem = "windows" is applied only in release builds; AttachConsole(ATTACH_PARENT_PROCESS) at startup keeps INSTALL_OK / INSTALL_FAILED: lines visible to any parent process.

Files touched

File Change
crates/zeno-server/src/api/devices.rs move_device handler
crates/zeno-server/src/api/mod.rs POST /devices/{id}/move route
crates/zeno-server/src/db/models.rs Device::set_site
crates/zeno-server/src/web/static/js/device-actions.js moveToSite action
crates/zeno-server/src/web/static/js/devices.js Move button + handler
crates/zeno-server/src/web/static/js/app.js Logout redirect
crates/zeno-installer/src/ui.rs new — Win32 GUI forms
crates/zeno-installer/src/install.rs GUI/console path split
crates/zeno-installer/src/main.rs ui module + AttachConsole
crates/zeno-installer/Cargo.toml Win32_Graphics_Gdi, Win32_System_LibraryLoader

Test plan

  • B (logout): Click "Sign out" in the topbar — browser should land on /login without a blank-page flash.
  • A (move device): On /devices, click the new move icon (drive_file_move) on any row → dialog shows site list → select a site → snackbar confirms → row reflects new site after refresh.
  • A (server): POST /api/devices/{uuid}/move with {"target_site_id": null} → audit log shows device.move with from/to site IDs.
  • C (PersistentInstall, Windows hardware): Run zeno-installer.exe --server <lan> --site <uuid> --code <key> (no --non-interactive). Expect: GUI window appears, log streams, status → "Installation complete", Close button enables, click → exit 0.
  • C (AdHocSupport, Windows hardware): Run with ad_hoc embedded payload. Expect: GUI window with elapsed timer, End Session button closes window + stops ZenoAgent service.
  • C (non-interactive, Windows): Run with --non-interactive. Expect: no GUI, console output as before, INSTALL_OK / INSTALL_FAILED: parseable.

Note on "Request help": The button is wired and shows a placeholder dialog. Full server-side notification (new AgentMessage variant + /api/devices/{id}/request-help endpoint) is scoped for round 2.

🤖 Generated with Claude Code

## Summary Three sub-features in one round: ### A — Move device to a different site - **Server** (`devices.rs`): New `POST /api/devices/{id}/move` handler (admin-only). Accepts `{ target_site_id: UUID | null }`. Emits `device.move` audit entry with `from_site_id` / `to_site_id`. Backed by `Device::set_site(pool, id, Option<Uuid>)` added to `db/models.rs`. - **Portal** (`device-actions.js`, `devices.js`): `Zeno.deviceActions.moveToSite(id, hostname)` fetches `/api/sites`, shows a `ui.dialog` with a `<select>` containing all sites plus a "No site" option, POSTs to the new endpoint on confirm, shows snackbar. A new "Move to site…" row-action button (drive_file_move icon) calls this handler and refreshes on success. ### B — Logout redirect to /login - **`app.js`**: The submit handler on `form[action="/api/auth/logout"]` now calls `e.preventDefault()` so the browser does not land on the bare 204. After clearing localStorage it navigates to `/login`. ### C — Windows GUI installer (replaces console window) New file `crates/zeno-installer/src/ui.rs` — Win32 form via `CreateWindowExW`: **PersistentInstall mode** (default): - Dark-themed window titled "Zeno Endpoint Setup"; subtitle with server URL + site name - Scrollable read-only log EDIT control (Consolas 9pt, dark background) fed via EM_SETSEL + EM_REPLACESEL from the worker thread - Status line flips to "Installation complete / failed / rolled back" when done - Close button is disabled during install; enabled on completion (exits 0/1/2) **AdHocSupport mode** (ad_hoc payload): - Window titled "Zeno Remote Support Session" - Technician name label + elapsed connected-time counter (WM_TIMER, 1s tick) - "Request help" (primary cyan, owner-drawn) — shows informational dialog; full WebSocket protocol deferred to round 2 - "End support session" (outlined danger) — stops ZenoAgent service via sc.exe, closes window - WM_CLOSE (X, Alt+F4) also stops the service — no escape path leaves the session dangling **Console path unchanged**: `windows_subsystem = "windows"` is applied only in release builds; `AttachConsole(ATTACH_PARENT_PROCESS)` at startup keeps `INSTALL_OK` / `INSTALL_FAILED:` lines visible to any parent process. ## Files touched | File | Change | |------|--------| | `crates/zeno-server/src/api/devices.rs` | move_device handler | | `crates/zeno-server/src/api/mod.rs` | POST /devices/{id}/move route | | `crates/zeno-server/src/db/models.rs` | Device::set_site | | `crates/zeno-server/src/web/static/js/device-actions.js` | moveToSite action | | `crates/zeno-server/src/web/static/js/devices.js` | Move button + handler | | `crates/zeno-server/src/web/static/js/app.js` | Logout redirect | | `crates/zeno-installer/src/ui.rs` | **new** — Win32 GUI forms | | `crates/zeno-installer/src/install.rs` | GUI/console path split | | `crates/zeno-installer/src/main.rs` | ui module + AttachConsole | | `crates/zeno-installer/Cargo.toml` | Win32_Graphics_Gdi, Win32_System_LibraryLoader | ## Test plan - [ ] **B (logout)**: Click "Sign out" in the topbar — browser should land on `/login` without a blank-page flash. - [ ] **A (move device)**: On `/devices`, click the new move icon (drive_file_move) on any row → dialog shows site list → select a site → snackbar confirms → row reflects new site after refresh. - [ ] **A (server)**: `POST /api/devices/{uuid}/move` with `{"target_site_id": null}` → audit log shows `device.move` with from/to site IDs. - [ ] **C (PersistentInstall, Windows hardware)**: Run `zeno-installer.exe --server <lan> --site <uuid> --code <key>` (no `--non-interactive`). Expect: GUI window appears, log streams, status → "Installation complete", Close button enables, click → exit 0. - [ ] **C (AdHocSupport, Windows hardware)**: Run with ad_hoc embedded payload. Expect: GUI window with elapsed timer, End Session button closes window + stops ZenoAgent service. - [ ] **C (non-interactive, Windows)**: Run with `--non-interactive`. Expect: no GUI, console output as before, `INSTALL_OK` / `INSTALL_FAILED:` parseable. > **Note on "Request help"**: The button is wired and shows a placeholder dialog. Full server-side notification (new `AgentMessage` variant + `/api/devices/{id}/request-help` endpoint) is scoped for round 2. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.
No description provided.