Files

6.8 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

LabGraph is a HomeLab infrastructure documentation engine. It maps physical hardware to virtual services using a graph model (Nodes + Edges). The stack is Django + Celery on the backend, Next.js 14 + DaisyUI on the frontend, all orchestrated via Docker Compose.

Running the Stack

# Start everything (from repo root)
docker compose up -d

# Include Flower (Celery monitoring at localhost:5555)
docker compose --profile dev-tools up -d

# View logs
docker compose logs -f web
docker compose logs -f celery-worker

# Rebuild after Dockerfile or requirements.txt changes
docker compose build
docker compose up -d

The web service runs backend/scripts/entrypoint.sh which runs migrate → collectstatic → gunicorn.

Backend Commands (inside container)

# Run migrations
docker compose exec web python manage.py migrate

# Create superuser
docker compose exec web python manage.py createsuperuser

# Make migrations after model changes
docker compose exec web python manage.py makemigrations

# Open Django shell
docker compose exec web python manage.py shell

# Inspect Celery workers
docker compose exec celery-worker celery -A config.celery inspect ping
docker compose exec celery-worker celery -A config.celery inspect active

Frontend Commands

cd frontend
npm run dev          # Dev server at localhost:3000
npm run type-check   # TypeScript check (tsc --noEmit)
npm run lint         # ESLint
npm run build        # Production build

Environment Setup

Copy .env.example to .env (repo root). Copy frontend/.env.local.example to frontend/.env.local.

Required variables that have no defaults and will crash on startup if missing:

  • SECRET_KEY — Django secret key (python3 -c "import secrets; print(secrets.token_urlsafe(50))")
  • POSTGRES_PASSWORD — PostgreSQL password (used directly by docker-compose)
  • REDIS_PASSWORD — Redis password (used directly by docker-compose)
  • NEXTAUTH_SECRET — NextAuth JWT signing key (openssl rand -base64 32)

Critical: DATABASE_URL, REDIS_URL, and CELERY_BROKER_URL must use the Docker service hostnames db and redis, not localhost. The Redis URL format is redis://:PASSWORD@redis:6379/0 (colon before password, no username).

Architecture

Backend (backend/)

Settings are split into config/settings/base.py (shared) → config/settings/development.py / production.py. Settings are loaded via django-environ from the root .env file (BASE_DIR.parent / ".env"). Never import base directly — always use development or production.

Celery is configured in config/celery.py and referenced by docker-compose as -A config.celery. Three queues: discovery (nmap/Proxmox scans), heartbeat (ICMP/TCP liveness), default (maintenance). Beat uses DatabaseScheduler, so periodic tasks are stored in the DB and can be edited at runtime via admin.

Graph model in apps/core/models.py:

  • Node — any infrastructure component (location → hardware → hypervisor → VM/container → application). All have UUID PKs, a node_type TextChoices enum, status, optional ip_address, and a metadata JSONField for per-type flexible data.
  • Edge — directed source→target relationship with edge_type (parent_child, network, dependency, physical). Unique constraint on (source, target, edge_type).
  • Network — VLAN/subnet records (separate from Nodes).
  • WikiPage — OneToOne to Node, stores raw Markdown; rendered_html() converts via the Markdown package.
  • HeartbeatLog — high-volume time-series; pruned nightly by tasks/maintenance.py.

Input validation uses Pydantic v2 schemas (apps/core/schemas.py) at the API boundary before data reaches the ORM. DRF serializers (to be added in Phase 2) handle output.

Celery tasks live in tasks/ (not inside an app). Phase 1 stubs are in tasks/discovery.py, tasks/heartbeat.py, tasks/maintenance.py. Autodiscovery is configured for the tasks package.

RLS: apps/core/apps.py connects a post_migrate signal that enables PostgreSQL Row-Level Security on core_node. The handler checks information_schema.tables first — it fires after every app's migrations, not just after core.

Health check at GET /api/health/ (no auth required) checks DB (SELECT 1) and Redis (cache round-trip). Returns {"status": "ok"|"degraded", "services": {...}, "version": "1.0.0"}. Used by docker-compose healthcheck.

OpenAPI schema at GET /api/schema/, Swagger UI at GET /api/docs/.

Frontend (frontend/)

Next.js 14 App Router. TypeScript strict mode with noUncheckedIndexedAccess and exactOptionalPropertyTypes — avoid any.

Auth flow: NextAuth (src/lib/auth.ts) uses CredentialsProvider. In development, any non-empty credentials are accepted (stub). Phase 2 wires authorize() to POST /api/auth/login/ on the Django backend. NEXTAUTH_SECRET must be stable in .env.local — changing it invalidates all existing sessions.

API client (src/lib/api.ts): all backend calls go through apiFetch() which validates responses against Zod schemas before returning. Throws ApiError on non-2xx, ZodError on schema mismatch.

Types (src/types/index.ts): Zod schemas are the source of truth; TypeScript types are inferred with z.infer<>. The schemas mirror the Django models exactly — keep them in sync when models change.

Routing: next.config.mjs proxies /backend/* → Django. Route groups: (auth) for login, (dashboard) for protected pages. Auth guard is done server-side with getServerSession(authOptions) + redirect().

Styling: Tailwind CSS + DaisyUI. Theme is set via html[data-theme] in layout.tsx. DaisyUI component classes (e.g. btn, card, alert) — no custom CSS unless DaisyUI can't cover it.

Phase Status

  • Phase 1 Complete — Docker stack, Django models, Celery stubs, Next.js scaffold
  • Phase 2 🔄 In Progress — DRF ViewSets, discovery scrapers (Proxmox/nmap), heartbeat (icmplib), Django auth endpoint
  • Phase 3 Pending — Nested inventory list, React Flow topology graph, Markdown wiki editor

Key Constraints

  • The requirements.txt lives in backend/ (Docker build context is ./backend).
  • next.config.ts is not supported in Next.js 14 — use next.config.mjs.
  • YAML > folded scalars in docker-compose do NOT fold newlines on more-indented continuation lines — use a shell script instead of multi-line sh -c commands.
  • HeartbeatLog is high-volume — always filter by node and use the (node, -timestamp) index; never do full-table scans.
  • Node.metadata is a free-form JSONField — validate its contents with the Pydantic schemas in apps/core/schemas.py before writing.