From b8af0f986b92da82fa8191489d7b5b2a574e435a Mon Sep 17 00:00:00 2001 From: tonym Date: Tue, 16 Jun 2026 01:27:30 -0500 Subject: [PATCH] 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) --- CLAUDE.md | 47 ++++++++++++++++++++++-- README.md | 19 ++++++---- docs/git-deploy-guide.md | 77 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 9 deletions(-) create mode 100644 docs/git-deploy-guide.md diff --git a/CLAUDE.md b/CLAUDE.md index c804e8f..f44040f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,8 +9,15 @@ user-facing overview. - **Garden** — Plant registry + care/harvest logs, plant **groups**, reusable **PlantType** records, Wikipedia photos. Plants sit on the shared location tree. -- **Inventory** — durable **Item**s that nest (`parentItemId`), with **Label**s - (M:N via `LabelOnItem`), `ItemAttachment`, and `ItemLog`. Shares the Location spine. +- **Inventory** — durable **Item**s that nest (`parentItemId`: a toolbox is an + Item that holds Items, with a cycle guard), with **Label**s (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. - **Animals** — `Animal` + `AnimalLog` (pets/livestock); placed on the Location tree, and `Task`s can link to an animal (`Task.animalId`). - **Locations** — `Location` tree (self-ref `parentId`) shared by plants, items, animals. @@ -20,6 +27,39 @@ user-facing overview. - **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`. @@ -31,6 +71,9 @@ user-facing overview. ## 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: diff --git a/README.md b/README.md index 58b8657..bfb4729 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,18 @@ Live at **http://192.168.0.5:8053**. reusable **plant types** (the "kind" record: care notes, sun/water, bloom & harvest months, pet toxicity), Wikipedia photos, and plants placed on the shared location tree. -- **Inventory** — durable **items** that nest (boxes within rooms within - buildings), with labels, attachments, and logs. Shares the same location spine - as the garden. -- **Animals** — pets & livestock with their own logs, placed on the shared - location tree; reminders can be linked to an animal. +- **Inventory** — durable **items** that nest (a toolbox holds the tools inside + it), with labels, photos + manuals/receipts/warranty docs, value & warranty + tracking (with expiry warnings), serial/brand, a sold record, and custom fields + (e.g. a vehicle's VIN). Schedule recurring **maintenance** per item. **Import + straight from HomeBox** via CSV. Shares the same location spine as the garden. +- **Animals** — pets & livestock with their own logs (feeding, vet, weight, meds) + and auto-computed age, placed on the shared location tree; reminders can be + linked to an animal. +- **Locations** — manage the whole place tree (areas, buildings, rooms, storage), + nested as deep as you like; everything (plants, items, animals) lives here. - **Reminders** — one-off or recurring tasks (every N days / monthly / yearly), - optionally linked to a plant or animal. + optionally linked to a plant, item, or animal. - **REST API** (`/api/v1/*`) — personal access **tokens** with per-scope permissions (read plants, log harvests, …) for phone shortcuts, Home Assistant, and automation. @@ -69,7 +74,7 @@ Two ways to ship a change: Do **not** rsync over the appdata dir — it would fight the checkout. Secrets (`NEXTAUTH_SECRET`, `GITEA_TOKEN`, `UPDATER_SECRET`) live in a gitignored host `.env` next to the compose file, never committed. Migrations apply automatically -on container start. +on container start. Full deploy walkthrough: [`docs/git-deploy-guide.md`](docs/git-deploy-guide.md). ## Conventions diff --git a/docs/git-deploy-guide.md b/docs/git-deploy-guide.md new file mode 100644 index 0000000..1be2983 --- /dev/null +++ b/docs/git-deploy-guide.md @@ -0,0 +1,77 @@ +# Editing & deploying Moon Base — a guide for any Claude instance + +Read this if you're a Claude (or anyone) working on Moon Base **without** the full +project `CLAUDE.md` in context — e.g. running on a different machine. It's the +self-contained version of how to change the code and get it onto the live server. + +## The repo + +- Lives on the home NAS Gitea: `http://192.168.0.5:3022/bonna61/Moonbase` +- Clone: `git clone http://tonym:@192.168.0.5:3022/bonna61/Moonbase.git` + (the API token lives in the workspace-level `~/Projects/CLAUDE.md` — **never commit it**) +- Default branch: **`master`**. + +## The golden rule: the NAS appdata is a GIT CHECKOUT, not a sync target + +The running app on the NAS lives at `/mnt/user/appdata/moonbase`, which is a real +`git clone` tracking `origin/master`. **You deploy by pushing to Gitea, then pulling +on the NAS — never by copying files.** Do **not** `rsync` over the appdata dir; it +fights the checkout and corrupts it. + +## How to make a change + +1. **Edit** code in your local working copy. +2. **Bump the version:** set `APP_VERSION` in `src/lib/version.ts` and add a + plain-English entry to the **top** of `src/lib/changelog.ts` — write it for + Bonna (the non-technical user): what changed and why. Minor (`0.X.0`) for + features/migrations, patch (`0.0.X`) for fixes. Docs-only changes don't need a bump. +3. **Schema change?** Create a migration: `npx prisma migrate dev --create-only + --name `, review the SQL, then `npx prisma migrate deploy` locally. The + server applies pending migrations automatically on container start. +4. **Verify locally:** `npx tsc --noEmit` and `npm run test`; run `npx next build` + for anything non-trivial (it catches client/server boundary errors `tsc` misses — + e.g. passing a Prisma `Decimal` to a client component). +5. **Commit.** If you're Claude, end the message with the `Co-Authored-By:` trailer. + +## How to deploy (two ways) + +**Preferred — in-app, no SSH:** push, then an admin clicks **Settings → Updates → +Pull & rebuild** in the live app. A privileged `updater` sidecar runs +`git pull && docker compose up -d --build app` detached and the UI reloads. + +```sh +git push origin master +# then click "Pull & rebuild" in the app +``` + +**Manual — needs SSH to the NAS:** + +```sh +git push origin master +ssh root@192.168.0.5 'cd /mnt/user/appdata/moonbase && git pull && docker compose up -d --build' +``` + +Confirm success: the footer version chip (or `GET /api/version`) should show the +version you just shipped. + +## Secrets — never commit them + +`NEXTAUTH_SECRET`, `GITEA_TOKEN`, `UPDATER_SECRET` live in a **gitignored host +`.env`** next to the compose file on the NAS, interpolated into compose as `${VAR}`. +They are not in the repo. Don't add them. + +## Gotchas + +- **Don't let `npx prisma` resolve at runtime.** With no local prisma on PATH it + pulls Prisma 7, which rejects `url` in the datasource. The Docker image ships the + full `node_modules` so the pinned prisma stays on PATH. +- **Migrations run on container start** (entrypoint waits for Postgres first). A bad + migration blocks startup — always test `migrate deploy` locally first. +- **First deploy of a big migration:** take a backup first (Settings → Backup & + Restore → Download) — the data is live (Bonna's garden). +- **Two people may be committing.** If you didn't write a file, don't sweep it into + your commit — stage only your own paths (`git add `) and check `git status` + before committing. + +That's the whole story: **edit locally → bump version → commit → push → pull/rebuild +on the NAS. Never rsync.**