Files
Moonbase/CLAUDE.md
Bonna Moon fe24cd73be v0.27.0 — Garden brain: zone-4b knowledge, calendar suggestions, UW citations
src/lib/garden/knowledge.ts holds hand-curated zone-4b (~May 15 frost)
knowledge for 28 crops/fruits/flowers: monthly care timing, companion
friends/foes, rotation families. Pure matchers knowledgeFor/tipsForMonth
use whole-word stem matching (tested — 'Pear' must not hit 'Pea'). The
calendar gains a "Good to do this month" list matched to owned plants,
deduped against existing task titles, with one-click Remind me → YEARLY
task via /api/v1/tasks. Plant type pages gain a "Growing in zone 4b"
card (timeline, companions, rotation). Tips cite UW Extension via site
search URLs — no fabricated article links.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 19:35:45 -05:00

8.1 KiB
Raw Blame History

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. Harvest logs carry structured amount+unit (legacy freeform quantity kept for old rows — don't regress); a harvest can simultaneously create a pantry item (addPlantLog's pantry input → GARDEN-source consumable). PlantTypes can link a UWMadison Extension article (uwExtensionUrl/Title), searched via /api/v1/plant-types/uw-search (proxy to hort.extension.wisc.edu's WP REST API; helper src/lib/garden/uw-extension.ts).
  • 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. Shares the Location spine.
  • Pantry (Kitchen → Pantry & Freezer) — CONSUMABLE Items with categories, price paid, tags (shared Label table), and expiry. Pulls go through /api/v1/pantry/[id]/use or /bulk-use and write ItemLog type USED with a structured amount (consumption is queryable — don't regress to NOTE strings). Service src/lib/services/pantry.ts; pure logic + tests in src/lib/pantry/core.ts. Rides the items:* scopes (pantry items ARE Items).
  • 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 (incl. occurrencesInRange projection). /calendar renders a month view of all tasks (garden + house) plus PlantType bloom/harvest windows (src/lib/garden/season.ts) and garden-brain suggestions.
  • Garden brainsrc/lib/garden/knowledge.ts: hand-curated zone-4b (~May 15 frost) care timing, companions/foes, rotation families for ~28 crops; pure matchers knowledgeFor/tipsForMonth (whole-word stem match — beware "Pear"/"Pea"-style false hits, tested). Surfaced as calendar "Good to do this month" (one-click YEARLY task via /api/v1/tasks) and a "Growing in zone 4b" card on type pages. Tips cite UW Extension via site search URLs — never fabricate article URLs.
  • 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. Fetch first: parallel sessions push to master — check git show origin/master:src/lib/version.ts before picking a number (a v0.22.0 collision has already happened once).
  • 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).