---
name: "Homepage Dashboard"
client: Oliver Internal
status: active
server: optical-dev
tech: [Next.js, React, JavaScript, Tailwind CSS, Vitest, Docker, Kubernetes]
local_path: /Users/ai_leed/Documents/Projects/Oliver/homepage
deploy: docker-compose up
url: https://optical-dev.oliver.solutions/homepage
tags: [oliver, dashboard, homepage, selfhosted]
created: 2026-04-14
port: 3001
---

## Overview

Homepage is a self-hosted application dashboard built with Next.js that aggregates 100+ service integrations into a single customizable interface. It solves the problem of managing multiple self-hosted applications by providing a unified, fast, secure entry point with Docker/Kubernetes autodiscovery, YAML-based configuration, and full API request proxying to protect credentials. Designed for privacy-conscious users running containerized infrastructure.

## Tech Stack

- **Frontend:** Next.js 16, React 19, Tailwind CSS, Headless UI components
- **Backend:** Next.js API routes, Node.js runtime
- **Database:** None (fully static with YAML configuration)
- **Infrastructure:** Docker, Docker Compose, Kubernetes API support, dockerode for container integration
- **Internationalization:** next-i18next with 40+ language translations
- **Key libraries:** SWR (data fetching), Recharts (charts), React Icons, Winston (logging), js-yaml (config parsing), Luxon (date/time)

## Architecture

Homepage follows a **static-first, API-proxy architecture**:

```
┌─────────────────────────────────────────────────────┐
│          Browser / User Interface                    │
│  (Static React components + SWR data fetching)       │
└──────────────────┬──────────────────────────────────┘
                   │
         ┌─────────▼──────────┐
         │  Next.js Routes    │
         │  /api/widgets/...  │
         │  /api/docker/...   │
         │  /api/config/...   │
         │  /api/kubernetes/  │
         └─────────┬──────────┘
                   │
     ┌─────────────┼─────────────┐
     ▼             ▼             ▼
  External   Docker      Kubernetes
  Services   Socket      API
  (Radarr,   (Container  (Pod/Service
   Sonarr,    Discovery)  Discovery)
   etc.)
```

**Widget Plugin System** (`src/widgets/`): 162+ service integrations, each a self-contained directory with:
- `component.jsx` — React widget UI component
- `index.js` — metadata, configuration schema, validation
- `logo.svg` — service branding

**Configuration Layer** (`src/utils/config/`): Parses YAML files from `/config` volume mount. Supports:
- Manual service configuration (YAML)
- Automatic Docker container discovery via labels
- Kubernetes service discovery
- Environment variable overrides

**API Proxying** (`src/pages/api/widgets/[...service].js`): Routes all external service requests server-side, ensuring API credentials never reach the browser. Caches responses via `memory-cache`.

**Path Aliases** recognized by both Vitest and ESLint:
- `components`, `pages`, `styles`, `test-utils`, `utils`, `widgets` → map to `./src/<name>`

**i18n**: Translations in `public/locales/<lang>/`. Custom formatters for bytes, rates, durations defined in `next-i18next.config.js`.

## Dev Commands

```bash
# Install dependencies (pnpm enforced)
pnpm install

# Start dev server (http://localhost:3000)
pnpm dev

# Production build
pnpm build

# Linting
pnpm lint

# Testing
pnpm test                    # Run all tests once (526 test files)
pnpm test src/components/foo.test.jsx  # Single file
pnpm test:watch              # Watch mode
pnpm test:coverage           # Coverage report
```

**Testing conventions:**
- Component tests: add `// @vitest-environment jsdom` at top
- Utility/API tests: run in Node environment (default)
- Use `renderWithProviders()` from `test-utils` for context-dependent components
- `vitest.setup.js` globally mocks `next-i18next`

## Deployment

- **Server:** optical-dev (Oliver infrastructure)
- **Deploy:** `docker-compose up` (see `docker-compose.yml`)
- **URL:** https://optical-dev.oliver.solutions/homepage
- **Port:** 3001 (exposed via docker-compose)
- **Service:** Docker containerized (no systemd service)
- **Local path:** /Users/ai_leed/Documents/Projects/Oliver/homepage

**Docker setup:**
- Mounts `/config` volume for YAML configuration
- Mounts `/var/run/docker.sock` for container discovery
- Healthcheck via wget to `/api/healthcheck` every 10s
- Auto-restart on failure

## Environment Variables

- `NEXT_PUBLIC_BASE_PATH` — URL path prefix (e.g., `/homepage`); build arg in Dockerfile
- `HOSTNAME` — Server bind address (currently `::` for IPv6)
- `HOMEPAGE_ALLOWED_HOSTS` — Host validation for Next.js 15+ (set to `optical-dev.oliver.solutions`)
- `NEXT_PUBLIC_*` — Any vars prefixed with this are exposed to the browser

## API / Endpoints

- `/api/widgets/[...service]` — Proxied service data endpoints (e.g., `/api/widgets/radarr/movies`)
- `/api/docker/*` — Docker container integration, stats, autodiscovery
- `/api/kubernetes/*` — Kubernetes Pod/Service discovery
- `/api/config` — Serves parsed YAML configuration
- `/api/healthcheck` — Health probe endpoint
- `/_next/image` — Next.js image optimization

## Known Issues

- **No AI-generated PRs** without explicit disclosure (per CONTRIBUTING.md)
- **Pre-commit hooks** run Prettier and YAML validation — do not skip with `--no-verify`
- **ESLint rules enforce:**
  - No import cycles (`maxDepth: 1`)
  - Import ordering via plugin
  - No `else` after `return`
- Bugs tracked in **GitHub Discussions**, not Issues
- basePath support recently added (commits 7d80b21c–be5e2a5e); ensure all client-side API calls prepend basePath

## Git

- **Remote:** git@bitbucket.org:zlalani/homepage.git
- **Latest commits:** Focus on basePath support, deploy widget integration, Docker service discovery, and crowdin translations
- **Version:** 1.10.1

## Sessions
### 2026-04-14 – Project catalogued
**Done:** Added to Obsidian second brain.

---
## Change Log
| Date | Requested | Changed | Files |
|------|-----------|---------|-------|
| 2026-04-14 | Initial setup | Note created | — |