Files
Moonbase/CLAUDE.md
tonym b8af0f986b docs: refresh README + CLAUDE.md for current modules; add deploy guide for any Claude
- README/CLAUDE.md: Inventory depth (warranty/value/maintenance/sold/custom fields,
  HomeBox CSV import), Locations page, the two-world data-model rule, API-first +
  service-layer conventions, and the HTTPS-gated future work (MCP/PWA/camera)
- New docs/git-deploy-guide.md: self-contained git + deploy walkthrough for a
  Claude instance working without this repo's CLAUDE.md (NAS is a git checkout,
  push + pull/rebuild, never rsync, secrets in host .env, prisma-at-runtime gotcha)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 01:27:30 -05:00

6.5 KiB

Moon Base — Project Notes

Property management for the Moon homestead (Eau Claire, WI). Next.js 14 + Prisma + Postgres. Same conventions as FMB (/Users/bonnamoon/Projects/fullmoonbakehouse). Repo on Gitea: bonna61/Moonbase. Live at NAS:8053. See README.md for the user-facing overview.

Modules (all live)

  • Garden — Plant registry + care/harvest logs, plant groups, reusable PlantType records, Wikipedia photos. Plants sit on the shared location tree.
  • Inventory — durable Items that nest (parentItemId: a toolbox is an Item that holds Items, with a cycle guard), with Labels (M:N via LabelOnItem), ItemAttachment (PHOTO/MANUAL/RECEIPT/WARRANTY), and ItemLog. Items carry value/purchase/warranty (incl. lifetimeWarranty, insured), serial/modelNumber/brand, a sold lifecycle, and freeform customFields (JSON, e.g. VIN). Maintenance = a Task linked via Task.itemId. HomeBox CSV import at /api/v1/items/import (pure parser src/lib/inventory/homebox-import.ts). kind is DURABLE|CONSUMABLE — consumable/Pantry is unbuilt (fields present so it needs no migration). Shares the Location spine.
  • AnimalsAnimal + AnimalLog (pets/livestock); placed on the Location tree, and Tasks can link to an animal (Task.animalId).
  • LocationsLocation tree (self-ref parentId) shared by plants, items, animals.
  • RemindersTask + TaskCompletion; recurrence in src/lib/tasks/schedule.ts.
  • REST API/api/v1/* authed by ApiToken (per-scope; logic in src/lib/api/).
  • Suggestions@otm/account-panel (Gitea npm registry) → files issues to the repo.
  • Backup & Restore/api/admin/backup + /api/admin/restore (admin only).
  • Self-update — admin "Pull & rebuild" (see Deploy).

Data model (the one rule)

Two nesting worlds, crossed once. Places (Location, self-ref parentId, unlimited depth — LocationKind is an icon hint, never a constraint) nest in places. Items (things you own) nest in items, and an Item lives in a Place or inside another Item. You never put a Place inside an Item. Plants and Animals are leaves in a Place (own logs, don't nest). HomeBox + Grocy + farmOS collapse into: Places · Plants/Animals (leaves) · Items (durable + consumable, nesting). Logs are per-domain tables (PlantLog/ItemLog/AnimalLog), NOT a polymorphic table; Task carries nullable plantId/itemId/animalId.

Architecture conventions

  • API-first. New modules (Inventory, Animals) are built on /api/v1/* and the UI calls those same endpoints — no separate UI routes. Garden/Tasks predate this and still use /api/plants etc.
  • Service layer src/lib/services/* does DB work + AuditEvent, taking (ctx: AuthContext, input). Routes are thin: authenticateRequest(req, scope) → service → handleApiError. The MCP server (unbuilt) will reuse these services.
  • Auth helper src/lib/api/authenticate.ts accepts EITHER a NextAuth cookie session (full scope ["*"]) OR a Bearer PAT (limited to its scopes; write implies read). Scope constants in src/lib/api/scopes.ts (client-safe).
  • Decimals for money/quantity (@db.Decimal) — convert to Number() before passing to client components (Prisma Decimal isn't serializable across the boundary).

Blocked on a domain + HTTPS (deferred)

Camera scanning/photo-capture (getUserMedia needs a secure context), the PWA (service worker + install need HTTPS), and the MCP server (Claude needs a public HTTPS URL) all wait until there's a domain + reverse-proxy/tunnel. The app is LAN-only over http://NAS:8053 today. Pantry/consumables and other pure web work can ship before then.

Local dev

  • DB: postgresql://tonym@localhost:5432/moonbase_dev; copy .env.example.env.
  • npm install, npm run dev (3000). After schema edits: npx prisma generate.
  • npx prisma migrate deploy provisions Bonna + Tony. npm run db:seed for demo data (SEED_MODE=minimal = accounts only).
  • Backfills for the location/plant-type refactors: npm run db:backfill-locations, npm run db:backfill-plant-types.

Deploy — git checkout on the NAS (NOT rsync)

Working without this file (e.g. a Claude on another machine)? The self-contained version is docs/git-deploy-guide.md.

The NAS appdata (/mnt/user/appdata/moonbase) is a real git checkout tracking origin/master (remote URL carries the token). Deploy by:

git push origin master
ssh root@192.168.0.5 'cd /mnt/user/appdata/moonbase && git pull && docker compose up -d --build'

Or admins click Settings → Updates → Pull & rebuild, which signals the privileged updater sidecar (docker socket + adnanh/webhook) to run the same git pull && compose up -d --build app detached; the UI polls /api/version and reloads. Never rsync over the appdata dir — it fights the checkout.

  • Secrets (NEXTAUTH_SECRET, GITEA_TOKEN, UPDATER_SECRET) live in a gitignored host .env next to the compose file, interpolated in — never committed.
  • Docker runtime mirrors FMB: node:20-bookworm-slim + openssl, runs as root, full node_modules, next start. Migrations run on container start via the entrypoint (waits for Postgres first). Image gotcha: don't let npx prisma resolve at runtime — with no local prisma on PATH it pulls Prisma 7, which rejects url in the datasource.

Conventions (same as FMB)

  • Every commit bumps APP_VERSION in src/lib/version.ts + a plain-English entry atop src/lib/changelog.ts (the footer shows it + an "update available" badge from src/lib/update-check.ts). Minor for features/migrations, patch for fixes.
  • AuditEvent on every mutation — append-only.
  • Soft-delete via active=false.
  • Pure core + colocated test — logic in src/lib/... with a .test.ts; routes thin.

Auth

  • Username OR email login. Accounts bonna / tony, role ADMIN.
  • getSession() / requireAuth() / isAdmin() in src/lib/auth.ts.
  • Forced password change is wired (provisioned accounts ship moonbase! + mustChangePassword=true): the dashboard layout redirects to /change-password (outside the layout group; backed by /api/account/change-password). Don't remove that redirect.
  • NEXTAUTH_SECRET is asserted at runtime in production (skipped during build).

Color palette

Forest / earth theme: --canopy (sidebar), --leaf (accent), --soil (text), --dew (bg), --ember (destructive).