Homelab Automation Dashboard is an Express-based backend with a React + Vite single-page application for day-to-day homelab operations. It centralizes service automation, infrastructure lookups, and inventory reconciliation workflows behind one API and one UI.
Recent project changes are now part of the shipped application:
- The legacy static frontend has been replaced with a React 19 + Vite SPA under
client/. - The production server serves the built SPA from
dist/when a client build is present. - The inventory area has expanded from basic network CRUD into a configuration-aware import workflow with preview, apply, relationship tracing, and operator notes.
- GitLab pipeline status monitoring, recurring refresh, automerge, on-demand assigned merge request refresh, and MR diff review
- GitHub pull request lookup and file diff inspection
- Azure DNS zone lookup plus lazy-loaded Azure inventory for resource groups, web apps, function apps, storage accounts, Service Bus namespaces, and Application Insights
- Microsoft 365 tenant inventory for users, subscribed licenses, standard distribution groups, and Graph-discovered shared mailboxes
- Namecheap domain inventory with registrar metadata
- Wave customer listing and paginated invoice lookup
- RabbitMQ queue snapshot collection and grouped message-shape inspection
- Inventory import, preview, apply, relationship exploration, and note-taking for imported entities
- Scheduled security and analytics reporting with durable configuration, encrypted write-only secrets, child-process execution, and run-history visibility
src/contains the Express API, organized into routes, controllers, services, middleware, and utils.client/contains the React + Vite frontend, organized by app shell, shared components, and feature modules.dist/contains the production SPA build generated by Vite at the repository root.data/inventory.jsonstores the normalized inventory state. Inventory writes create adata/inventory.json.bakbackup before replacement.data/scheduled-jobs.json,data/scheduled-job-runs.json, anddata/scheduled-job-secrets.enc.jsonstore scheduled-job configuration, recent run metadata, and encrypted secrets.- Helmet-based security middleware remains enabled on the backend.
- Node.js 22+
- npm 10+
- Docker is optional for containerized runs
-
Clone the repository.
git clone https://github.com/Robotti-io/Homelab-Automation-Dashboard.git cd homelab-automation-dashboard -
Install dependencies for the root project and the frontend workspace.
npm install
-
Copy
.env.exampleto.envand add the credentials required for the integrations you plan to use.
Run the backend and frontend in separate terminals.
Backend:
npm run dev:serverFrontend:
npm run dev:clientDevelopment URLs:
- React SPA:
http://localhost:5173 - Express API:
http://localhost:3000 - Health check:
http://localhost:3000/health
During frontend development, Vite proxies /api and /health requests to the Express server.
Build the SPA:
npm run buildStart the backend:
node src/server.jsWhen dist/index.html exists, Express serves the built SPA and falls back to index.html for non-API routes. If dist/ is absent, the server runs as an API-only backend.
The Docker image uses a multi-stage build:
- Stage 1 installs client dependencies and builds the Vite app into
dist/ - Stage 2 installs production backend dependencies and serves the built SPA from Express
Build and run:
docker build -t homelab-dashboard .
docker run --env-file .env -p 3000:3000 homelab-dashboardRoot scripts:
npm startstarts the backend withnodemonnpm run dev:serverstarts the backend withnodemonnpm run dev:clientstarts the Vite dev server fromclient/npm run buildbuilds the frontend intodist/npm run lintruns ESLint and Markdown lintingnpm run testruns backend tests, frontend tests, and merges coverage into the rootcoverage/folder
Frontend workspace scripts:
npm run dev --workspace clientnpm run build --workspace clientnpm run preview --workspace clientnpm run test --workspace client
Server:
PORToptional HTTP port for Express, default3000JSON_BODY_LIMIToptional JSON request limit, default5mb
Scheduled Jobs:
SCHEDULED_JOBS_SECRET_PASSPHRASErequired to save or decrypt scheduled-job secretsSCHEDULED_JOBS_RUN_HISTORY_LIMIToptional retained run count per job, default25SCHEDULED_JOBS_DEFAULT_TIMEOUT_MSoptional default per-job timeout, default600000
GitLab:
GITLAB_API_URLorGITLAB_APIGitLab API base URLGITLAB_ACCESS_TOKENpersonal access token used for GitLab API callsGITLAB_ASSIGNEE_IDoptional default assignee ID used by automerge-created merge requests
Azure:
AZURE_SUBSCRIPTION_IDAzure subscription ID- Azure authentication must also be available to
DefaultAzureCredential - A typical local service-principal setup uses
AZURE_TENANT_ID,AZURE_CLIENT_ID, andAZURE_CLIENT_SECRET
Microsoft 365:
M365_TENANT_IDoptional explicit Entra tenant ID for Microsoft Graph, falls back toAZURE_TENANT_IDM365_CLIENT_IDoptional explicit app registration client ID for Microsoft Graph, falls back toAZURE_CLIENT_IDM365_CLIENT_SECREToptional explicit client secret for Microsoft Graph, falls back toAZURE_CLIENT_SECRET- The configured app registration must have Microsoft Graph application permissions with admin consent for at least
User.Read.All,GroupMember.Read.All,LicenseAssignment.Read.All, andMailboxSettings.Read.
The M365 integration uses an Entra app registration with application permissions, similar in operating model to the Azure resource integration.
Recommended setup:
- Create or reuse an Entra app registration for the dashboard.
- Create a client secret for that app registration.
- Grant Microsoft Graph application permissions:
User.Read.All,GroupMember.Read.All,LicenseAssignment.Read.All, andMailboxSettings.Read. - Grant admin consent for the tenant.
- Set either the dedicated
M365_*environment variables or reuse the existingAZURE_*service principal variables if the same app registration is used for both Azure and M365 access.
Current M365 scope and limitations:
- Included in this implementation: users, subscribed licenses / SKUs, standard mail-enabled distribution groups, and shared mailboxes discovered through Microsoft Graph mailbox settings.
- License rows are enriched with human-readable product names using an embedded Microsoft SKU catalog that ships with the application.
- Excluded in this implementation: dynamic distribution groups.
- Shared mailbox discovery may omit mailbox types that are not reliably exposed through the Graph
mailboxSettings/userPurposepath.
Namecheap:
NAMECHEAP_API_USERNAMECHEAP_API_KEYNAMECHEAP_USERNAMENAMECHEAP_CLIENT_IP
Wave:
WAVE_API_KEYWAVE_BUSINESS_ID
RabbitMQ:
RABBITMQ_HOSTRABBITMQ_PORToptional, default5672RABBITMQ_USERNAMERABBITMQ_PASSWORDRABBITMQ_VHOSToptional, default/
GitHub:
- No GitHub token is currently required by the implemented backend routes. The GitHub feature uses public GitHub API requests.
The scheduled-jobs feature adds two built-in operator-managed jobs:
security-reportfor request and bot-scan reporting against theloggerschemaanalytics-reportfor analytics and refresh-pipeline reporting against theanalyticsschema
Operator workflow:
- Open the Scheduled Jobs page in the SPA navigation.
- Configure each job's schedule, email delivery settings, database connection details, and report parameters.
- Save the required database and SMTP passwords through the write-only secret form.
- Confirm the job shows
Readybefore enabling or manually running it. - Use
Run nowfor an on-demand execution and monitor recent status in the run history table.
Important behavior:
- Secrets are encrypted at rest with AES-256-GCM and are never returned in plaintext by the API.
- Enabled jobs refresh their in-memory
node-cronregistration immediately after configuration or secret changes. - Jobs run in child processes and do not overlap for the same job ID.
- Changing the scheduled-jobs passphrase requires operators to re-enter stored secrets.
The inventory feature now supports configuration-derived datasets instead of only manual network records.
Implemented workflow:
- Select a source type in the React inventory page.
- Choose a local export file in the browser.
- The browser reads the file and submits JSON to the backend.
- Preview the normalized changes before applying, or apply directly.
- Inspect the resulting entities, warnings, conflicts, and relationships.
- Add operator notes without overriding imported authoritative values.
Supported import sources:
opnsensefor OPNsense firewall exports, accepted extensions.xml, limit2 MBwindows-dnsfor Windows DNS zone exports, accepted extensions.dnsand.txt, limit1 MBnginxfor NGINX config exports, accepted extensions.confand.txt, limit1 MB
Inventory data includes:
- Networks
- Hosts
- DNS records
- Firewall rules
- Websites
- Relationships between imported entities
- Source snapshot metadata
- User-authored notes
Legacy /api/inventory/networks CRUD endpoints still exist for compatibility with earlier data, but the current frontend centers inventory around authoritative imports, preview/apply flows, and read-only derived entities.
Backend tests use Jest. Frontend tests use Vitest with Testing Library. Coverage is generated separately and merged by scripts/mergeCoverage.js into the root coverage/ directory.
Generated coverage artifacts include:
coverage/coverage-summary.jsonmerged backend + frontend summarycoverage/lcov.infomerged backend + frontend LCOV reportcoverage/cobertura-coverage.xmlmerged backend + frontend Cobertura report for CI artifacts such as GitLab coverage ingestioncoverage/backend/cobertura-coverage.xmlbackend-only Cobertura outputclient/coverage/cobertura-coverage.xmlfrontend-only Cobertura output
Useful commands:
npm run test
npm run test:backend
npm run test:frontendCore endpoints currently exposed by the server:
GitLab:
GET /api/gitlab/usersGET /api/gitlab/pipeline-statusPOST /api/gitlab/automergeGET /api/gitlab/assigned-mrsGET /api/gitlab/mr-diff
GitHub:
GET /api/github/prsGET /api/github/pr-diff
Azure:
GET /api/azure/dns-zonesGET /api/azure/dns-zones/recordsGET /api/azure/inventory/resource-groupsGET /api/azure/inventory/web-appsGET /api/azure/inventory/function-appsGET /api/azure/inventory/storage-accountsGET /api/azure/inventory/service-bus-namespacesGET /api/azure/inventory/application-insights
Microsoft 365:
GET /api/m365/usersGET /api/m365/licensesGET /api/m365/distribution-groupsGET /api/m365/shared-mailboxes
Namecheap:
GET /api/namecheap/domains
Wave:
GET /api/wave/customersGET /api/wave/customers/:customerId/invoices
RabbitMQ:
GET /api/rabbitmq/snapshot
Inventory:
GET /api/inventory/networksGET /api/inventory/networks/:idPOST /api/inventory/networksPUT /api/inventory/networks/:idDELETE /api/inventory/networks/:idGET /api/inventory/summaryGET /api/inventory/relationshipsGET /api/inventory/import-sourcesPOST /api/inventory/imports/previewPOST /api/inventory/imports/applyDELETE /api/inventory/imports/:sourceTypePUT /api/inventory/notes
Health:
GET /health
homelab-automation-dashboard/
|-- client/ # React + Vite SPA
| |-- public/images/ # Frontend image assets
| `-- src/
| |-- app/ # App shell, routing, layout
| |-- components/ # Shared UI primitives
| |-- features/ # Feature modules by service area
| `-- lib/ # Shared API and formatting helpers
|-- data/ # Inventory data and sample import files
|-- dist/ # Production frontend build output
|-- scripts/ # Repo automation helpers
|-- src/
| |-- controllers/ # HTTP handlers
| |-- middleware/ # Security and validation middleware
| |-- routes/ # Express route definitions
| |-- services/ # Business logic and integrations
| `-- utils/ # Pure helper logic and parsers
|-- tests/ # Backend test suite
`-- README.mdTo add a new service area:
- Add a feature module under
client/src/features/. - Register its route in
client/src/app/App.jsx. - Add navigation in
client/src/app/layout/AppLayout.jsxwhen needed. - Reuse the shared client API helpers in
client/src/lib/api.js. - Add or extend backend routes, controllers, and services under
src/only when the UI needs backend support.
ISC (c) Robotti Tech Services
Do not commit secrets or production exports that should remain private. Use a proper secret-management workflow for real deployments.
