docs: replace boilerplate README with real documentation
create-next-app's stock README (npm/yarn/bun instructions, Geist font mention, Vercel deploy link) never got replaced. Documents what PulseNode actually is: quick start, the config.yml/.env split and hot- reload behavior, a reference table for all seven widget types and their fields, auto-discovery, the docker-compose deployment (including the DOCKER_GID gotcha found while testing M5), project structure, and the known gaps (no auth yet, system widget reflects container cgroup unless the host proc/sys are mounted in).
This commit is contained in:
@@ -1,36 +1,156 @@
|
||||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||
# PulseNode
|
||||
|
||||
## Getting Started
|
||||
A self-hosted infrastructure dashboard: system stats, Docker containers,
|
||||
databases, and HTTP/Traefik health, configured entirely through a YAML file
|
||||
and pushed to the browser live over WebSockets. No database of its own -
|
||||
`config.yml` is the source of truth, metrics are ephemeral.
|
||||
|
||||
First, run the development server:
|
||||
## Features
|
||||
|
||||
- **YAML-configured** widgets and layout, hot-reloaded - edit `config.yml`
|
||||
and connected browsers update without a page refresh
|
||||
- **Secrets via `.env`**, interpolated into `config.yml` at load time
|
||||
(`${VAR}` / `${VAR:-default}`), never sent to the browser
|
||||
- **Live updates over WebSocket** - each widget subscribes to its own topic;
|
||||
the server only serializes results for widgets someone is actually viewing
|
||||
- **Widget types**: Docker containers, Postgres/Redis instances, system
|
||||
resources, HTTP health checks, Traefik router status, bookmarks, search
|
||||
- **Optional label-based auto-discovery** - containers carrying
|
||||
`traefik.enable=true` can be turned into dashboard widgets automatically;
|
||||
manual `config.yml` entries always take precedence
|
||||
- **CSS-variable theming**, light/dark toggle, `theme.customCssPath` for a
|
||||
fully custom stylesheet
|
||||
- **Ships as a single Docker image**, designed to run non-root with a
|
||||
read-only root filesystem
|
||||
|
||||
## Quick start (local development)
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm install
|
||||
cp config/.env.example config/.env # fill in real values
|
||||
cp .env.example .env # only needed for docker compose
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
Open `http://localhost:3000`. `pnpm dev` runs the custom server
|
||||
(`server.ts`) via `tsx watch`, which boots Next.js, the WebSocket server, and
|
||||
the collector scheduler together, and restarts on server-side code changes.
|
||||
Editing `config.yml` itself does **not** restart the process - it's picked
|
||||
up live via the config watcher.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
Other scripts: `pnpm build` (production build), `pnpm start` (run the
|
||||
production build via the same custom server), `pnpm lint`, `pnpm exec tsc
|
||||
--noEmit`.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||
## Configuration
|
||||
|
||||
## Learn More
|
||||
### `config/config.yml`
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
Everything is organized into `groups`, each a list of `widgets`:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
```yaml
|
||||
settings:
|
||||
title: PulseNode
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
theme:
|
||||
mode: dark # dark | light | auto
|
||||
variables:
|
||||
--pn-accent: "#38bdf8"
|
||||
# customCssPath: custom.css # relative to config/, served at /api/theme/custom-css
|
||||
|
||||
## Deploy on Vercel
|
||||
discovery:
|
||||
docker:
|
||||
enabled: false # set true to auto-discover traefik-labeled containers
|
||||
network: falcon_network # only include containers on this docker network
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
groups:
|
||||
- name: Core Infra
|
||||
widgets:
|
||||
- type: docker
|
||||
name: Traefik
|
||||
containerName: traefik
|
||||
href: https://traefik.example.com
|
||||
interval: 10s
|
||||
```
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||
Config errors are **non-destructive**: if `config.yml` fails validation
|
||||
after a live edit, PulseNode logs the error and keeps serving the last
|
||||
valid config instead of crashing.
|
||||
|
||||
### `config/.env`
|
||||
|
||||
Secrets and per-deployment values referenced from `config.yml` via
|
||||
`${VAR}` or `${VAR:-default}`. Loaded once at startup and on every config
|
||||
reload; never exposed to the client. See `config/.env.example`.
|
||||
|
||||
### Widget types
|
||||
|
||||
| `type` | Required fields | Notes |
|
||||
|------------|-------------------------------------|-------|
|
||||
| `bookmark` | `name`, `href` | Static link card. Optional `description`. |
|
||||
| `search` | - | Client-side search box. Optional `engines` (`duckduckgo`/`google`/`bing`, default `[duckduckgo]`), `defaultEngine`. |
|
||||
| `docker` | `name`, `containerName` | Status, uptime, CPU/mem, restart count via `dockerode`. Optional `href`, `showStats` (default `true`), `interval` (default `5s`). |
|
||||
| `database` | `name`, `containerName`, `engine` | Thin skin over the same Docker collector - `engine` is `postgres` or `redis`, used for icon/label only. `interval` default `10s`. |
|
||||
| `system` | - | Host CPU/mem/disk/network via `systeminformation`. Optional `name` (default `System`), `interval` (default `5s`). |
|
||||
| `http` | `name`, `url` | HTTP health check with latency and consecutive-failure tracking. Optional `method` (default `GET`), `timeout` (default `5s`), `interval` (default `30s`), `expect.status` (default `200`). |
|
||||
| `traefik` | `apiUrl` | Router/entrypoint/middleware status from Traefik's own API (`--api.dashboard=true` must be enabled). Optional `name` (default `Traefik`), `interval` (default `15s`). |
|
||||
|
||||
`interval`/`timeout` values are duration strings: `500ms`, `5s`, `1m`, `1h`.
|
||||
|
||||
### Auto-discovery
|
||||
|
||||
With `discovery.docker.enabled: true`, PulseNode scans running containers
|
||||
every 60s (and immediately on any `config.yml` change) for
|
||||
`traefik.enable=true` labels, parses the router's `Host(...)` rule for a
|
||||
link, and adds anything not already listed manually to a synthetic
|
||||
"Discovered" group. `discovery.docker.excludeLabels` and `.network` narrow
|
||||
what qualifies; `.labelPrefix` (default `traefik`) controls which label
|
||||
namespace is read.
|
||||
|
||||
## Deployment
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
cp config/.env.example config/.env
|
||||
# edit both, then:
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
- `.env` (repo root) holds `TRAEFIK_HOST`, `NETWORK_NAME`, and `DOCKER_GID` -
|
||||
compose-level values, interpolated into `docker-compose.yml`'s Traefik
|
||||
labels and used to join the container to the docker group. Find your
|
||||
host's docker group id with `getent group docker | cut -d: -f3`; without
|
||||
it the non-root container gets `EACCES` on `/var/run/docker.sock`.
|
||||
- `config/.env` holds values referenced from `config.yml` (see above).
|
||||
- The compose file joins the container to an **external** `falcon_network`
|
||||
so collectors can reach services directly by container name
|
||||
(`http://umami:3000/...`) instead of only through Traefik.
|
||||
- `/var/run/docker.sock` is mounted read-only; `/app/config` is mounted
|
||||
read-only from `./config`.
|
||||
- The container runs as a non-root user with a read-only root filesystem,
|
||||
dropped capabilities, and `tini` as PID 1.
|
||||
- `/api/health` backs the container `HEALTHCHECK`.
|
||||
|
||||
## Project structure
|
||||
|
||||
```
|
||||
config/ config.yml + .env (gitignored) live here
|
||||
server.ts custom server: http + WebSocket (/ws) + collector scheduler
|
||||
app/ Next.js App Router pages and API routes
|
||||
components/widgets/ one folder per widget type (Widget.tsx + shared Skeleton/StatusDot)
|
||||
components/layout/ Dashboard shell, theme toggle, brand mark
|
||||
lib/config/ schema (zod), loader (parse/interpolate/watch), effective (+ discovery merge)
|
||||
lib/collectors/ docker, system, http, traefik collectors + the scheduler
|
||||
lib/discovery/ traefik-label auto-discovery
|
||||
lib/ws/ WebSocket server (topics) and the browser-side subscription hooks
|
||||
```
|
||||
|
||||
## Known limitations
|
||||
|
||||
- **No authentication yet.** PulseNode assumes it's reachable only on a
|
||||
private network (e.g. behind Traefik with no public route, or on a VPN).
|
||||
A minimal session-based auth gate is a planned follow-up, not yet built.
|
||||
- `system` widget metrics reflect whatever cgroup/namespace the container
|
||||
runs in - for true host-level stats rather than the container's own view,
|
||||
mount `/proc` and `/sys` from the host and run with `pid: host` (not
|
||||
configured by default).
|
||||
|
||||
Reference in New Issue
Block a user