Sloth Manager's dashboard showed per-system stats for DNS and Secrets (domains/records by type, expiry counts by type) that this app's Dashboard never had -- it only ever showed the six live integration widgets. Port those two sections over, generalized to sit alongside the integrations this app added that Sloth Manager never had. New server/src/dns/stats.ts (getDnsStats(), used by new GET /api/dns/stats): zone counts are fetched live per enabled provider (matching what the DNS page itself shows, since the zone cache table only gets a row once a zone has been synced and would undercount) -- one unreachable provider surfaces its error without blanking the rest. Record-type breakdown comes from the local cache instead, since records are only ever shown from cache elsewhere in this app too (fetching every zone's records live on every dashboard load would be far more expensive for no real accuracy gain). Dashboard.tsx gained three sections under clear headers: DNS (domain/ provider/record stat tiles + a "records by type" breakdown), Secrets (monitored/expiring/expired tiles + a "secrets by type" breakdown, computed client-side from the already-fetched secrets list, same as Sloth Manager did), and the existing Integrations widgets grouped under their own header for visual parity with the two new sections. Breakdowns render as a stacked proportion bar with a color-coded legend rather than a pie chart -- this app has no charting library anywhere yet, and a plain CSS progress bar (an idiom Tabler itself uses) gets the same "see the mix at a glance" value without adding one for a single dashboard. Verified getDnsStats() against a temp SQLite DB with real migrations and an enabled + a disabled DNS provider: enabled/total provider counts, live zone counting, and cached-record-type aggregation (sorted by count) all came back correct, and the disabled provider was correctly excluded from the zone count while still counting toward the total. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
157 lines
7.8 KiB
Markdown
157 lines
7.8 KiB
Markdown
# Homelab Manager
|
|
|
|
Repository: `git@10.200.5.13:bobban/Homelab-manager.git` ([gitea.labsconnect.se/bobban/Homelab-manager](https://gitea.labsconnect.se/bobban/Homelab-manager) externally).
|
|
|
|
A single dashboard for a homelab: Proxmox, Synology DSM, Semaphore, Tailscale,
|
|
Gitea, and Dockhand/Docker status and basic actions, plus DNS record
|
|
management, an IP address inventory (IPAM), and a secret-expiry tracker
|
|
(ported from [Sloth Manager](../Sloth%20manager)) and scheduled-task tracking
|
|
across Debian/Raspbian hosts (ported from
|
|
[Schedule Task Manager](../ScheduleTaskManager)). Looks and feels like a
|
|
[Tabler](https://tabler.io) admin dashboard. Sign-in is delegated to
|
|
Authentik (OIDC), with local admin/operator/viewer roles.
|
|
|
|
## Status
|
|
|
|
All modules from the original plan are built:
|
|
|
|
- Monorepo scaffold, Tabler-themed app shell/navigation
|
|
- Authentik OIDC login, roles (first user to sign in becomes admin), audit log
|
|
- **Dashboard** — an overview of every system this app tracks, not just the
|
|
six integrations: DNS (domain/record counts per provider, cached records
|
|
broken down by type), Secrets (monitored/expiring/expired counts, broken
|
|
down by type), and the existing live per-integration widgets
|
|
(Proxmox/Synology/Semaphore/Tailscale/Gitea/Dockhand). Breakdowns render
|
|
as a stacked proportion bar with a legend — no charting library, matching
|
|
the rest of the app's plain-Tabler-CSS approach.
|
|
- **Diagnostic Log** (admin-only) — every call this app makes to a DNS
|
|
provider or integration (Tailscale, Proxmox, Synology, Semaphore, Gitea,
|
|
Dockhand), success or failure, with latency and the error message if it
|
|
failed — the last 500 calls, filterable by source/result, for
|
|
troubleshooting connectivity issues (ported from Sloth Manager's
|
|
provider-diagnostics log, generalized to cover every integration this app
|
|
has, not just DNS)
|
|
- **Secrets** — expiry tracking for API tokens/certs/passwords
|
|
- **IP Addresses (IPAM)** — inventory of IPs across vendors/locations,
|
|
with "Sync from Tailscale" and "Sync from Proxmox" actions to pull in
|
|
tailnet device IPs and VM/LXC IPs (never overwrites a
|
|
manually-entered IP), and each entry now shows its matching DNS
|
|
record(s) from the DNS module's cache
|
|
- **DNS** — zone/record management across Cloudflare, Loopia, Pi-hole, Azure
|
|
DNS, cPanel, and Technitium; providers are configured in-app (not via env
|
|
vars) and their credentials are encrypted at rest
|
|
- **Servers** — cron/systemd tracking across Debian/Raspbian servers via a
|
|
lightweight push agent (`agent/linux/`), plus manual entries for things an
|
|
agent can't see (Docker jobs, backups). The Servers page itself just lists
|
|
registered servers and (admin-only) adds new ones / issues agent tokens;
|
|
clicking a server opens its detail page with CPU/RAM/disk status, IP
|
|
addresses, matching DNS names (looked up from the DNS module's cache), and
|
|
its scheduled tasks — live hardware from Proxmox for VM/LXC-backed
|
|
servers, or from the agent's own hardware report for everything else.
|
|
Proxmox-linked servers also get start/stop/restart buttons right on the
|
|
detail page. The detail page also has an **Admin Links** section
|
|
(operator/admin to add/edit/remove) for bookmarking that server's own
|
|
admin UIs — Dockge, Webmin, Cockpit, Portainer, or anything else reachable
|
|
by URL.
|
|
- **Tailscale**, **Proxmox**, **Synology**, **Semaphore**, **Gitea**,
|
|
and **Docker** each get their own top-level page (backed by the
|
|
matching integration) instead of living inside a shared Integrations
|
|
browsing view:
|
|
- **Tailscale** — device list with online/authorized status, and
|
|
authorize/deauthorize/remove actions; a live device-count widget.
|
|
- **Proxmox** — VM/LXC status across every node in the cluster, with
|
|
start/restart/shutdown/stop actions; a live running/total widget. Each
|
|
online node also gets its own host-stats card — uptime, CPU usage/cores/
|
|
load average, RAM and swap usage, and per-storage usage (local, LVM-thin,
|
|
ZFS, NFS, etc). Supports self-signed certificates (common in homelab
|
|
setups).
|
|
- **Synology** — volume and disk health (read-only by design).
|
|
Supports self-signed certificates.
|
|
- **Semaphore** — Ansible run status per template across every
|
|
project, with a "Run" action to trigger a template; a live
|
|
template-count widget (with a last-failed warning).
|
|
- **Gitea** — repo list with each repo's last CI run status, and
|
|
re-running just the failed jobs in a run; a live repo-count widget
|
|
(with a failing-build warning).
|
|
- **Docker** — container status across every Docker host Dockhand
|
|
manages (one credential covers all of them), with
|
|
start/stop/restart actions, a host filter, and a live
|
|
running/total widget.
|
|
|
|
Add/edit/enable/disable/remove each integration's credential itself
|
|
under Integrations → Manage integrations.
|
|
- Every table in the app is click-to-sort on any column (numbers, booleans,
|
|
and dates/text sort correctly regardless of how the column formats them)
|
|
and has an "Export CSV" button next to it that exports whatever's
|
|
currently sorted/filtered.
|
|
- **Settings** (admin-only) — notification channels (Gotify, ntfy, SMTP,
|
|
generic webhook) with per-channel test buttons, per-event toggles (DNS
|
|
record added/updated/deleted, daily secret-expiry reminder and daily
|
|
Tailscale key-expiry reminder — both sharing one configurable
|
|
time/timezone), badge-color customization for both DNS
|
|
providers and integration types, and a date/time display format
|
|
(date order, 12/24-hour clock) applied consistently to every table in
|
|
the app.
|
|
|
|
All six integrations follow the same config-in-UI + encrypted-credentials
|
|
pattern, added (and edited — e.g. to rotate an expired API token without
|
|
recreating the whole integration) through **Integrations → Manage
|
|
integrations**.
|
|
|
|
**Verified for real, end to end**: every module above — including all six
|
|
integrations, both their read-only views and their write actions
|
|
(start/stop/restart, trigger-a-run, authorize/deauthorize) — has been
|
|
exercised against the user's actual live homelab, not just built against
|
|
specs. That pass also found and fixed two real bugs: the Synology adapter
|
|
assumed HTTPS-only (the NAS is reached over plain HTTP), and the Tailscale
|
|
adapter read `online`/`isExitNode` fields that don't actually exist in the
|
|
real API response (fixed to derive them from `connectedToControl` and
|
|
`enabledRoutes`). See the git log for the full verification notes per
|
|
integration.
|
|
|
|
## Requirements
|
|
|
|
- Node.js 20+
|
|
- An Authentik instance reachable from wherever this app runs
|
|
|
|
## 1. Set up an Authentik application
|
|
|
|
1. Create an **OAuth2/OpenID Provider**:
|
|
- Redirect URI: `<APP_BASE_URL>/auth/callback`
|
|
- Scopes: `openid`, `email`, `profile`
|
|
2. Create an **Application** using that provider, and assign the users/groups
|
|
who should be able to sign in — Authentik controls who can authenticate;
|
|
the app's own admin/operator/viewer roles control what they can do once in.
|
|
3. Copy the provider's issuer URL, client ID, and client secret into `.env`.
|
|
|
|
## 2. Local development
|
|
|
|
```bash
|
|
cp .env.example .env # fill in AUTHENTIK_*, SESSION_SECRET, CREDENTIALS_ENCRYPTION_KEY
|
|
npm install
|
|
npm run dev:server # http://localhost:3000 (API)
|
|
npm run dev:web # http://localhost:5173 (Vite dev server, proxies /api and /auth to :3000)
|
|
```
|
|
|
|
Visit `http://localhost:5173` during development. Database migrations run
|
|
automatically on server start. SQLite data lands in `./data` (gitignored).
|
|
|
|
Generate `SESSION_SECRET` and `CREDENTIALS_ENCRYPTION_KEY` with:
|
|
|
|
```bash
|
|
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
|
```
|
|
|
|
## 3. Run with Docker
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
# edit .env
|
|
docker compose -f docker-compose.dev.yml up -d --build # build locally
|
|
# or, once an image is published to your registry:
|
|
docker compose up -d
|
|
```
|
|
|
|
The app listens on `HOST_PORT` (default `3000`); SQLite data persists in
|
|
`./data` on the host.
|