- Add .gitignore: exclude compiled binaries, build artifacts, and Helm values files containing real secrets (authentik, prometheus) - Add all Kubernetes deployment manifests (deployment/) - Add services source code: ha-sync, device-inventory, games-console, paperclip, parts-inventory - Add Ansible orchestration: playbooks, roles, inventory, cloud-init - Add hardware specs, execution plans, scripts, HOMELAB.md - Add skills/homelab/SKILL.md + skills/install.sh to preserve Copilot skill - Remove previously-tracked inventory-cli binary from git index Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
7.3 KiB
7.3 KiB
Parts Inventory — Implementation Plan
A full-stack web app + CLI to organize hardware (tools, screws, relays, electronics, etc.) with a flexible schema, fuzzy search, MongoDB backend, and Kubernetes deployment.
Architecture
Browser ──► nginx (parts-ui) ──► /api/* ──► parts-api ──► parts-db (MongoDB)
▲
parts-cli (Go)
| Component | Language | Description |
|---|---|---|
parts-api |
TypeScript / Node.js + Express | REST API, fuzzy search |
parts-ui |
TypeScript / React + Vite | Web UI, served by nginx |
parts-cli |
Go + cobra | CLI tool connecting to the API over HTTP |
parts-db |
MongoDB 7 (StatefulSet) | Database |
Project Structure
services/parts-inventory/
├── api/
│ ├── src/
│ │ ├── models/
│ │ │ └── part.ts # Mongoose schema + model
│ │ ├── routes/
│ │ │ ├── parts.ts # CRUD + search router
│ │ │ └── health.ts # GET /health
│ │ └── index.ts # Express app entry
│ ├── Dockerfile
│ ├── package.json
│ └── tsconfig.json
│
├── ui/
│ ├── src/
│ │ ├── api/
│ │ │ └── client.ts # Typed fetch wrapper
│ │ ├── components/
│ │ │ ├── SearchBar.tsx # Debounced input
│ │ │ ├── FilterPanel.tsx # Type + category dropdowns
│ │ │ ├── PartCard.tsx # Card in grid view
│ │ │ └── PartForm.tsx # Create/edit form + dynamic properties
│ │ ├── pages/
│ │ │ ├── Home.tsx # Search + card grid
│ │ │ └── PartDetail.tsx # Full detail + inline edit/delete
│ │ └── App.tsx
│ ├── nginx.conf # Serves static + proxies /api to parts-api
│ ├── Dockerfile
│ ├── package.json
│ └── vite.config.ts
│
├── cli/
│ ├── cmd/
│ │ ├── root.go # Root command, config loading
│ │ ├── list.go # parts list [--query] [--type] [--category]
│ │ ├── get.go # parts get <id>
│ │ ├── add.go # parts add (interactive prompts)
│ │ ├── update.go # parts update <id> (interactive)
│ │ └── delete.go # parts delete <id>
│ ├── internal/
│ │ └── client/
│ │ └── client.go # HTTP client for parts-api
│ ├── Dockerfile
│ ├── go.mod
│ └── go.sum
│
├── build-and-load.sh
└── PLAN.md # This file
MongoDB Schema (Part)
{
_id: ObjectId
title: string // required; text-indexed
type: string // e.g. "tool" | "screw" | "relay" | "sensor" | "cable"
category: string // e.g. "power tools" | "fasteners" | "automation"
manufacturer?: string // text-indexed
dimensions?: {
width?: number
length?: number
height?: number
unit?: string // "mm" | "cm" | "in"
}
quantity: number // default 1
location?: string // e.g. "shelf A3, bin 2"
notes?: string // text-indexed
tags: string[] // text-indexed; free-form labels
properties: Record<string, string | number | boolean> // type-specific extras
createdAt: Date
updatedAt: Date
}
Text index: title, manufacturer, category, notes, tags
Examples of properties in use:
- Screw:
{ thread: "M3", pitch_mm: 0.5, head: "hex", material: "stainless" } - Relay:
{ coil_voltage: 12, contact_rating_A: 10, form: "SPDT" } - Resistor:
{ value_ohm: 10000, tolerance: "1%", package: "0603" }
REST API Endpoints
| Method | Path | Description |
|---|---|---|
GET |
/health |
Health check |
GET |
/api/parts |
List / search (?q=, ?type=, ?category=, ?limit=, ?skip=) |
POST |
/api/parts |
Create a part |
GET |
/api/parts/meta/types |
Distinct type values |
GET |
/api/parts/meta/categories |
Distinct category values |
GET |
/api/parts/:id |
Get part by ID |
PUT |
/api/parts/:id |
Update part |
DELETE |
/api/parts/:id |
Delete part |
Fuzzy Search Strategy
- If
?q=present → MongoDB$textsearch to get candidates - Pass candidates through
fuse.js(keys:title,manufacturer,tags,notes) for fuzzy re-ranking - If very short query or no text match →
fuse.jsover a capped recent-fetch fallback - Filters (
type,category) applied before fuzzy pass
CLI Commands
Config: PARTS_API_URL env var (default: http://parts-api.infrastructure.svc.cluster.local:3001)
or ~/.parts-inventory.yaml with api_url: key.
parts list # List all parts (table view)
parts list --query "m3 screw" # Fuzzy search
parts list --type screw --category fasteners
parts get <id> # Show full detail
parts add # Interactive prompts to create a part
parts update <id> # Interactive update
parts delete <id> # Delete with confirmation
Kubernetes Manifests (deployment/infrastructure/parts-inventory.yaml)
Resources in order:
- Comment block — manual secret creation instructions
- PVC
parts-db-pvc—nfs-general,ReadWriteOnce, 5Gi - StatefulSet
parts-db—mongo:7, mounts PVC at/data/db - Service (headless)
parts-db— port 27017,clusterIP: None - Deployment
parts-api—parts-api:latest,imagePullPolicy: Never, port 3001 - Service
parts-api— ClusterIP, 3001 - Deployment
parts-ui—parts-ui:latest,imagePullPolicy: Never, port 8080 - Service
parts-ui— ClusterIP, 80→8080 - Ingress
parts-ui-ingress—parts.vandachevici.ro, letsencrypt-prod TLS
Secret (create manually before applying)
kubectl create secret generic parts-inventory-secret \
--from-literal=MONGO_URI="mongodb://parts-db.infrastructure.svc.cluster.local:27017/parts" \
-n infrastructure
Resource Estimates
| Component | CPU req / limit | Memory req / limit |
|---|---|---|
| parts-db | 100m / 500m | 256Mi / 512Mi |
| parts-api | 50m / 200m | 64Mi / 128Mi |
| parts-ui (nginx) | 10m / 100m | 16Mi / 64Mi |
Implementation Order
- project-scaffold — dir layout, package.json, tsconfig, go.mod skeletons
- mongo-model — Mongoose
Partschema with text indexes - api-crud — Express routes, all CRUD endpoints
- api-fuzzy-search —
fuse.jsintegration on?q=queries - go-cli — cobra CLI connecting to API over HTTP
- react-ui — full React UI (search, list, form, detail)
- dockerfiles — multi-stage Dockerfiles for api, ui, cli
- build-script —
build-and-load.sh - k8s-manifests —
deployment/infrastructure/parts-inventory.yaml - readme — usage docs, dev setup, deploy steps