Files
lunarfront-app/CLAUDE.md
Ryan Moon 7aa81c4e7c
All checks were successful
Build & Release / build (push) Successful in 15s
docs: add infrastructure, build pipeline, and dev box sections to CLAUDE.md
2026-04-04 10:30:40 -05:00

178 lines
8.4 KiB
Markdown

# LunarFront — Project Conventions
## App
- **Name:** LunarFront
- **Purpose:** Small business management platform (POS, inventory, rentals, scheduling, repairs, accounting)
- **Company:** Lunarfront Tech LLC
## Tech Stack
- **Runtime:** Bun
- **Language:** TypeScript (strict mode, end-to-end)
- **API:** Fastify with Pino JSON logging
- **ORM:** Drizzle ORM (PostgreSQL 16)
- **Validation:** Zod (shared schemas between frontend and backend)
- **Queue:** BullMQ (Valkey-backed)
- **Cache:** Valkey 8 (Redis-compatible fork)
- **Monorepo:** Turborepo with Bun workspaces
- **Testing:** bun test (built-in, uses bun:test imports)
- **Linting:** ESLint 9 flat config + Prettier
## Package Namespace
- `@lunarfront/shared` — types, Zod schemas, business logic, utils
- `@lunarfront/backend` — Fastify API server
## Database
- Dev: `lunarfront` on localhost:5432
- Test: `lunarfront_test` on localhost:5432
- Each deployed instance has its own isolated database — no multi-tenancy, no `company_id`
- Migrations via Drizzle Kit (`bunx drizzle-kit generate`, `bunx drizzle-kit migrate`)
## Key Entity Names
- `account` — billing entity (family, individual, or business)
- `member` — individual person on an account (NOT "student" — renamed to support multiple adults)
- `member.is_minor` — derived from date_of_birth, controls consent/portal rules
## Commands
- `bun run dev` — start all packages in dev mode
- `bun run test` — run all tests
- `bun run lint` — lint all packages
- `bun run format` — format all files with Prettier
## API Conventions
- **Every endpoint that returns a list must support pagination, search, and sorting** — no exceptions unless the endpoint is explicitly a lightweight lookup (see below)
- `?page=1&limit=25` — pagination (default: page 1, 25 per page, max 100)
- `?q=search+term` — full-text search across relevant columns
- `?sort=name&order=asc` — sorting by field name, asc or desc
- List responses always return `{ data: [...], pagination: { page, limit, total, totalPages } }`
- Search and filtering is ALWAYS server-side, never client-side
- Use `PaginationSchema` from `@lunarfront/shared/schemas` to parse query params
- Use pagination helpers from `packages/backend/src/utils/pagination.ts`
- **Lookup endpoints** (e.g., `/roles/all`, `/statuses/all`) are the exception — these return a flat unpaginated list for populating dropdowns/selects. Use a `/all` suffix to distinguish from the paginated list endpoint for the same resource.
## Frontend Table Conventions
- **Every table that displays data must use the shared `DataTable` component** (`components/shared/data-table.tsx`)
- All tables must support: **search** (via `?q=`), **sortable columns**, and **server-side pagination**
- Use the `usePagination()` hook (`hooks/use-pagination.ts`) — it manages page, search, and sort state via URL params
- All data columns that make sense to sort by should be sortable (e.g., name, email, date, status) — don't limit to just 1-2 columns
- Sub-resource tables (e.g., members within an account, payment methods) follow the same rules — use `DataTable` with pagination, not raw `<Table>` with unbounded queries
- Loading states should use skeleton loading (built into `DataTable`), not plain "Loading..." text
## Conventions
- Shared Zod schemas are the single source of truth for validation (used on both frontend and backend)
- Business logic lives in `@lunarfront/shared`, not in individual app packages
- API routes are thin — validate with Zod, call a service, return result
- All financial events must be auditable (append-only audit records)
- JSON structured logging with request IDs on every log line
---
## Infrastructure
### Overview
LunarFront runs on DigitalOcean Kubernetes (DOKS). Each customer gets an isolated namespace, database, and Helm release managed by ArgoCD.
### Key Services
- **Cluster:** `lunarfront` DOKS cluster, region NYC
- **Registry:** `registry.digitalocean.com/lunarfront` (DOCR) — stores Docker images and Helm charts
- **Git:** `git.lunarfront.tech` — self-hosted Gitea, source of truth for code and charts
- **CI:** Gitea Actions — builds Docker images and Helm charts on push to `main`
- **CD:** ArgoCD at `argocd.lunarfront.tech` — deploys from `lunarfront-charts` repo
- **Database:** DO Managed PostgreSQL — one database per customer, plus manager DB
- **Cache/Queue:** DO Managed Valkey — shared across all customers (key-prefixed per customer)
- **Ingress:** nginx ingress controller with Cloudflare proxy in front
- **TLS:** cert-manager with Let's Encrypt (letsencrypt-prod cluster issuer)
- **DNS:** Cloudflare — wildcard `*.lunarfront.tech` → cluster LB IP `167.99.21.170`
### Node Pools
- `system` — 2x s-2vcpu-4gb, runs ingress, ArgoCD, manager, pgbouncer
- `customers` — autoscales 0→N, s-4vcpu-8gb, runs customer app pods (tainted `role=customer`)
- `dev` — autoscales 0→1, s-4vcpu-8gb, runs dev pod only (tainted `dedicated=dev:NoSchedule`)
### Repos
- `lunarfront-app` — main application code (this repo)
- `lunarfront-charts` — Helm charts and ArgoCD app definitions
- `lunarfront-infra` — Terraform for DO infrastructure (DOKS, managed DBs, registry, DNS)
- `lunarfront-manager` — internal ops tool for provisioning/deprovisioning customers
---
## Build & Deploy Pipeline
### How it works
1. Push code to `main` on `lunarfront-app`
2. Gitea Actions runs `.gitea/workflows/build.yml`:
- Builds `lunarfront-app` Docker image → pushes as `0.1.{run_number}`, `{sha}`, `latest`
- Builds `lunarfront-frontend` Docker image → same tags
- Packages Helm chart → pushes as `0.1.{run_number}` to DOCR OCI registry
3. ArgoCD image updater detects new image digests → updates customer deployments
4. New customer provisions always get the latest chart version (queried from DOCR at provision time)
5. Existing customers upgraded via `POST /customers/:slug/upgrade` or `POST /customers/upgrade-all` in the manager
### Versioning
- Version format: `0.1.{gitea_run_number}` — always incrementing, no git commit-back needed
- No version stored in git — source of truth is DOCR tags
- Chart version and app version are kept in sync
### Key files
- `Dockerfile` — backend image (bun runtime, runs `packages/backend/src/main.ts` directly)
- `Dockerfile.frontend` — frontend nginx image
- `chart/` — Helm chart for customer app deployments
- `.gitea/workflows/build.yml` — CI pipeline
- `.gitea/workflows/build-devpod.yml` — builds dev box image on Dockerfile.devpod changes
---
## Dev Box
### What it is
A persistent development pod running in the `dev` namespace on the cluster. Provides a full remote dev environment accessible from anywhere.
- **VS Code in browser:** `dev.lunarfront.tech` (Cloudflare Access protected, OTP to email)
- **SSH:** `ssh -p 2222 root@dev-ssh.lunarfront.tech`
- **Storage:** 100GB DO block storage PVC mounted at `/root` — everything in home dir persists
- **Image:** `registry.digitalocean.com/lunarfront/manager:devpod-latest`
- **Tools:** bun, Claude Code CLI, code-server, kubectl, helm, k9s, doctl, psql, redis-cli, git
### Managing the dev pod
```bash
# Scale up (provisions node automatically)
kubectl scale deployment dev -n dev --replicas=1
# Scale down (node auto-terminates after ~15 min)
kubectl scale deployment dev -n dev --replicas=0
```
### Running the app locally on the dev box (no containers)
The dev box runs the app as plain Bun processes, connecting to the same DO managed services as production.
**Required env vars** (create a `.env` file in the repo root or export in `.bashrc`):
```bash
DATABASE_URL=postgresql://... # DO managed postgres, lunarfront database
REDIS_URL=rediss://... # DO managed valkey
JWT_SECRET=... # any random hex string for local dev
PORT=8000
```
**Start the app:**
```bash
cd ~/lunarfront-app
bun run dev
```
Access the running backend at `dev.lunarfront.tech/proxy/8000/` in the browser (code-server proxy), or via SSH port forward:
```bash
ssh -p 2222 -L 8000:localhost:8000 root@dev-ssh.lunarfront.tech
```
**Run migrations against the dev database:**
```bash
bunx drizzle-kit migrate
```
### Workflow
1. Edit code in VS Code at `dev.lunarfront.tech` or via SSH
2. Run and test locally with `bun run dev` — app connects to DO managed postgres/valkey
3. Push to `main` → Gitea Actions builds and pushes new Docker image + Helm chart
4. ArgoCD deploys to the cluster automatically
5. Use manager at `manager.lunarfront.tech` to upgrade customer instances if needed