Commit graph

10 commits

Author SHA1 Message Date
Samuel James
4f765b512a
Add per-integration custom icon + multi-repo fallback chain for Node Status (#40)
Settings → Integrations now has a universal "Icon" field (any integration
type) accepting a pasted URL or an uploaded image, stored as config.iconUrl.
This overrides the built-in icon for that integration's Node Status tile.

Node Status tiles now resolve their icon through a priority chain: custom
iconUrl, then each built-in CDN candidate in order (assets-public first
where available, falling back to the existing dashboard-icons CDN), and
finally the generic per-kind Lucide icon if every candidate 404s. AWS now
tries samuelsjames.github.io/assets-public's aws-logo.svg before the
jsDelivr fallback; SSH gets a Linux logo from the same repo. Proxmox,
Weather, and Remote Desktop have no built-in candidates yet (no matching
assets in that repo) and fall back to the generic icon until added.

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-21 16:03:51 -04:00
Samuel James
9f10e8ee6f
Group all integration node tiles by integration except Proxmox (#39)
Generalizes the Uptime Kuma monitor-grouping pattern to every integration:
Node Status now collapses each integration's resources into one tile (e.g.
30 EC2 instances under one "AWS" tile) instead of flooding the grid, with
members listed in Node Detail on selection. Proxmox stays ungrouped since
its VMs/LXCs are managed individually elsewhere in the app.

Adds integrationType to the /api/integrations/resources response so the
frontend can group/exclude by adapter type rather than resource kind (kind
alone can't distinguish Proxmox VMs from AWS VMs, for example).

Documents the grouping rule in HANDOFF.md and adds a paid-tier roadmap
entry for per-integration node tabs that will show every individual node.

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-21 09:35:55 -04:00
Claude
58f007e6db
Add per-resource kind labeling and proper Node Status icons
Each adapter now tags its Resources with a kind (vm, container, app,
host, network) so Node Status tiles show the right icon instead of a
generic server glyph — Proxmox LXCs/Docker containers get a container
icon, VMs get a VM icon, Uptime Kuma monitors get an app icon, etc.

Also stops silently swallowing listResources() failures — they're now
logged as warnings, since a connected-but-empty integration (e.g.
Uptime Kuma reporting zero monitors) was previously indistinguishable
from a real adapter error.
2026-06-21 12:03:25 +00:00
Samuel James
d863448495
Add auth Phase 3: multi-user accounts with admin/member roles (#28)
Implements Phase 3 of the auth roadmap: multiple user accounts (cap 10),
an admin/member role model, and admin-only gating of config-mutating
routes. Dashboard data stays shared across all users (per the product
decision in HANDOFF.md — this is a household/self-hosted dashboard, not
a multi-tenant app), so there is no per-user data isolation.

Schema (backend/src/db/index.ts):
- Idempotent migration adds `role` (default 'admin') and `active`
  (default 1) columns to `users` when missing. The 'admin' default means
  the pre-existing single user is backfilled to admin on deploy and keeps
  full access; newly created users are inserted explicitly as 'member'.
  Verified against a production-like old schema (columns added, existing
  user backfilled to admin/active).

Auth + access control:
- `/api/setup` creates the first user as admin. Login enforces `active`
  (deactivated accounts get 403) and embeds the live role in the session.
- `app.authenticate` now reads role+active fresh from the DB on every
  request (not from the possibly-stale JWT claim), rejects inactive
  accounts, and stashes the role on req.user.
- New `requireAdmin` (auth + role check) and `adminOnly` (role check for
  routes already behind the plugin-level authenticate hook) decorators.

User management (admin-only, in auth.ts):
- GET/POST/PUT/DELETE /api/users — list, create (admin sets a temp
  password; no public signup), change role, activate/deactivate, delete.
- 10-user cap enforced server-side; guard rails prevent removing the last
  active admin (demote/deactivate/delete) and deleting your own account;
  deactivating or deleting a user drops their sessions immediately.

Admin-only route gating (members get 403):
- integrations create/update/delete/test, tunnels create/delete, data
  export/import. Read routes and tunnel connect/disconnect stay open to
  all authenticated users, as do all the SSH/Docker/RDP tools and
  bookmarks (members are trusted to use the tooling, per product decision).

Frontend:
- api.ts: listUsers/createUser/updateUser/deleteUser + ManagedUser type;
  role+active added to AuthUser.
- Settings: new admin-only "Users" section (create form, role toggle,
  activate/deactivate, delete, 10-cap indicator). Nav filters the Users
  tab by role and guards ?tab= deep-links. Data & Backup shows an
  admin-only notice for members; Integrations shows a read-only banner
  for members. (Backend remains the real enforcement boundary.)

Verified end-to-end against a throwaway backend: role assignment,
member 403s on every admin-only route + 200s on shared/read routes,
admin 200/201s, last-admin guards (409/400), deactivation killing an
active session and blocking re-login (then reactivation restoring it),
and the 10-user cap (409 on the 11th). Both frontend and backend
type-check clean.

Co-authored-by: Samuel James <ssamjame@amazon.com>
Co-authored-by: Kiro <noreply@kiro.dev>
2026-06-20 12:43:24 -04:00
Samuel James
63adccb1c7
Show saved indicator for secret fields instead of appearing deleted (#18)
* Add editable display-name field to generic integrations

Lets users set a custom name for Proxmox, Docker, AWS, Remote Desktop,
Netbird, Cloudflare, Uptime Kuma, and Weather integrations, separate
from the host/IP field, mirroring the SSH host rename pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4

* Surface the new-integration name field as a labeled input

The name field for new generic integrations was a faint header input
with only placeholder text, easy to miss. Move it into the form grid
as a proper labeled "Name" field next to the other connection fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4

* Add file upload for SSH private key and certificate fields

Lets users pick a key file from disk (e.g. ~/.ssh) instead of pasting
its contents into the Private Key / OPKSSH Certificate fields.

* Fix SSH private key paste corrupting multi-line PEM format

Private Key and Certificate fields were single-line <input> elements,
which strip newlines on paste and corrupt PEM-formatted keys (causing
'Unsupported key format' errors). Render them as multi-line textareas
instead so pasted keys keep their line breaks.

* Show saved indicator for secret fields instead of appearing blank/deleted

GET /api/integrations never returns decrypted secret values (by design),
so after navigating away and back, secret/key fields rendered empty -
looking exactly like the saved key had been deleted, even though it was
still intact and encrypted in the database. Expose which secret keys
exist (names only, never values) via secretKeys, and use it to label
fields as "saved" with an appropriate placeholder instead of blank.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-20 08:53:56 -04:00
Claude
c37ad3d0d4
Phase 5: RDP/VNC/Telnet remote desktop via guacamole-lite + guacd
Adds a remote_desktop integration type and a /api/guacamole websocket
route that drives guacamole-lite's ClientConnection directly (bypassing
its Server class, which would otherwise attach an unfiltered upgrade
listener that conflicts with the existing @fastify/websocket routes).
The frontend RemoteDesktop page renders the Guacamole protocol stream
via guacamole-common-js. Verified end-to-end against a real guacd and
VNC server, including in an actual browser session.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BbJV5nm8KPVH1oNJYKpnoF
2026-06-19 15:25:10 +00:00
Claude
71f49e0700
Add Phase 1a: core SSH terminal (Termix migration)
Implements the minimal-viable terminal described in TERMIX_MIGRATION.md
Phase 1a: a real interactive SSH session in the browser over a
WebSocket, using xterm.js on the frontend and ssh2 on the backend.
Reuses ArchNest's existing SSH integrations (host/port/username/
password/privateKey/passphrase) instead of introducing a second,
duplicate host-management system the way Termix has one.

Backend: new /api/terminal WebSocket route (registered via
@fastify/websocket) handling connect/input/resize/disconnect messages,
authenticated via a JWT passed as a query param (browsers can't set
custom headers on the WS handshake). Extracted the integration secret
loader out of routes/integrations.ts into db/secrets.ts so the new
terminal route can reuse it without duplicating the decrypt logic.

Frontend: new Terminal.tsx page listing configured SSH hosts and
rendering an xterm.js terminal wired to the WebSocket; wired into
App.tsx at /terminal. vite.config.ts's dev proxy now forwards
WebSocket upgrades (ws: true) so this works under `npm run dev`.

Verified end-to-end against a real (test) ssh2-based SSH server:
connect, shell banner, keystroke echo, and prompt redraw all worked
correctly over the actual WebSocket protocol.

Deliberately deferred to Phase 1b/1c per the migration doc: jump-host
chaining, tab/split-pane UI, terminal theme/font settings, OPKSSH cert
auth, tmux session monitor, session recording.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BbJV5nm8KPVH1oNJYKpnoF
2026-06-19 10:52:04 +00:00
Claude
7524690ebd
Add SSH integration adapter for local infra without an API
Many self-hosted machines have no management API, so add an SSH-based
adapter (using ssh2) that connects with password or key auth and probes
hostname/disk/mem/load via a single shell command, surfacing health
status like the other integrations. Also fixes routes/integrations.ts's
hardcoded type enum, which was out of sync with the IntegrationType
union and rejected the new 'ssh' type.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BbJV5nm8KPVH1oNJYKpnoF
2026-06-18 21:06:16 +00:00
Claude
3b920fcfb2
Replace mock data on Glance and Infrastructure with real backend data
Adds an events table + logEvent helper for a genuine activity log, and
a /api/integrations/resources aggregate endpoint backed by a new optional
listResources adapter method (implemented for Docker via its containers API).
StatusCards, MiddleRow, BottomRow, and Infrastructure now render real
integration/resource/event data instead of hardcoded numbers, with empty
states where no data source exists yet (AWS cost, historical trends).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BbJV5nm8KPVH1oNJYKpnoF
2026-06-18 19:56:10 +00:00
Claude
a0b71c7028
Add backend skeleton: Fastify + SQLite API with auth and integrations
- Single-user JWT auth with a first-run /api/setup endpoint, gated by
  GET /api/system/setup-status, to back an upcoming enrollment page
- SQLite schema for users, integrations, secrets (AES-256-GCM encrypted),
  bookmarks, and bookmark categories
- Integration adapter registry with real health-check adapters for
  Uptime Kuma and Docker, stubs for the rest, wired to
  POST /api/integrations/:id/test
- CRUD routes for integrations and bookmarks
- backend/ as its own Docker service in docker-compose.yml, Vite dev
  proxy for /api, .env.example for required secrets

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BbJV5nm8KPVH1oNJYKpnoF
2026-06-18 19:04:48 +00:00