From 1b73a1e0702d113015e7eff46396778b1ea8202a Mon Sep 17 00:00:00 2001 From: Forbes Date: Sun, 8 Feb 2026 13:45:48 -0600 Subject: [PATCH 1/8] docs: add workflow integration mention to overview --- docs/SPECIFICATION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/SPECIFICATION.md b/docs/SPECIFICATION.md index 307a0f0..60533de 100644 --- a/docs/SPECIFICATION.md +++ b/docs/SPECIFICATION.md @@ -8,7 +8,7 @@ ## 1. Overview -Silo is an item database with configurable part number generation, designed for R&D-oriented workflows. It provides revision tracking, BOM management, file versioning, and physical inventory location management through a REST API and web UI. CAD integration (FreeCAD workbench, LibreOffice Calc extension) is maintained in separate repositories ([silo-mod](https://git.kindred-systems.com/kindred/silo-mod), [silo-calc](https://git.kindred-systems.com/kindred/silo-calc)). +Silo is an item database with configurable part number generation, designed for R&D-oriented workflows. It provides revision tracking, BOM management, file versioning, and physical inventory location management through a REST API and web UI. CAD and workflow integration (FreeCAD workbench, LibreOffice Calc extension) is maintained in separate repositories ([silo-mod](https://git.kindred-systems.com/kindred/silo-mod), [silo-calc](https://git.kindred-systems.com/kindred/silo-calc)). ### 1.1 Core Philosophy From e61df2db043d75672ea93cd7cf3250a63c815926 Mon Sep 17 00:00:00 2001 From: Forbes Date: Sun, 8 Feb 2026 14:00:50 -0600 Subject: [PATCH 2/8] docs: update README.md for React SPA and current state - Replace htmx reference with React SPA (Vite + TypeScript, Catppuccin Mocha) - Update component tree: add web/ subtree, testutil package, remove templates - Correct endpoint count (75) and migration count (11) - Expand documentation table from 6 to 10 entries (add CONFIGURATION.md, GAP_ANALYSIS.md, COMPONENT_AUDIT.md, frontend-spec.md) Closes #25 --- README.md | 57 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index a5978c3..58c2e80 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Kindred Silo is an R&D-oriented item database with: - **Role-based access control** (admin > editor > viewer) with API tokens and sessions - **ODS import/export** for items, BOMs, and project sheets - **Audit/completeness scoring** with weighted per-category property validation -- **Web UI** with htmx-based item browser, project management, and schema editing +- **Web UI** — React SPA (Vite + TypeScript, Catppuccin Mocha theme) for item browsing, project management, schema editing, and audit - **CAD integration** via REST API ([silo-mod](https://git.kindred-systems.com/kindred/silo-mod), [silo-calc](https://git.kindred-systems.com/kindred/silo-calc)) - **Physical inventory** tracking with hierarchical locations (schema ready) @@ -22,24 +22,33 @@ Kindred Silo is an R&D-oriented item database with: ``` silo/ ├── cmd/ -│ ├── silo/ # CLI tool -│ └── silod/ # API server +│ ├── silo/ # CLI tool +│ └── silod/ # API server ├── internal/ -│ ├── api/ # HTTP handlers, routes, templates (76 endpoints) -│ ├── auth/ # Authentication (local, LDAP, OIDC) -│ ├── config/ # Configuration loading -│ ├── db/ # PostgreSQL repositories -│ ├── migration/ # Property migration utilities -│ ├── odoo/ # Odoo ERP integration -│ ├── ods/ # ODS spreadsheet library -│ ├── partnum/ # Part number generation -│ ├── schema/ # YAML schema parsing -│ └── storage/ # MinIO file storage -├── migrations/ # Database migrations (10 files) -├── schemas/ # Part numbering schemas (YAML) -├── deployments/ # Docker Compose and systemd configs -├── scripts/ # Deployment and setup scripts -└── docs/ # Documentation +│ ├── api/ # HTTP handlers and routes (75 endpoints) +│ ├── auth/ # Authentication (local, LDAP, OIDC) +│ ├── config/ # Configuration loading +│ ├── db/ # PostgreSQL repositories +│ ├── migration/ # Property migration utilities +│ ├── odoo/ # Odoo ERP integration +│ ├── ods/ # ODS spreadsheet library +│ ├── partnum/ # Part number generation +│ ├── schema/ # YAML schema parsing +│ ├── storage/ # MinIO file storage +│ └── testutil/ # Test helpers +├── web/ # React SPA (Vite + TypeScript) +│ └── src/ +│ ├── api/ # API client and type definitions +│ ├── components/ # Reusable UI components +│ ├── context/ # Auth context provider +│ ├── hooks/ # Custom React hooks +│ ├── pages/ # Page components (Items, Projects, Schemas, Settings, Audit, Login) +│ └── styles/ # Catppuccin Mocha theme and global styles +├── migrations/ # Database migrations (11 files) +├── schemas/ # Part numbering schemas (YAML) +├── deployments/ # Docker Compose and systemd configs +├── scripts/ # Deployment and setup scripts +└── docs/ # Documentation ``` ## Quick Start @@ -95,12 +104,16 @@ The server provides the REST API and ODS endpoints consumed by these clients. | Document | Description | |----------|-------------| -| [docs/AUTH.md](docs/AUTH.md) | Authentication system design | -| [docs/AUTH_USER_GUIDE.md](docs/AUTH_USER_GUIDE.md) | User guide for login, tokens, and roles | -| [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) | Production deployment guide | | [docs/SPECIFICATION.md](docs/SPECIFICATION.md) | Full design specification and API reference | | [docs/STATUS.md](docs/STATUS.md) | Implementation status | -| [ROADMAP.md](ROADMAP.md) | Feature roadmap and gap analysis | +| [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) | Production deployment guide | +| [docs/CONFIGURATION.md](docs/CONFIGURATION.md) | Configuration reference (all `config.yaml` options) | +| [docs/AUTH.md](docs/AUTH.md) | Authentication system design | +| [docs/AUTH_USER_GUIDE.md](docs/AUTH_USER_GUIDE.md) | User guide for login, tokens, and roles | +| [docs/GAP_ANALYSIS.md](docs/GAP_ANALYSIS.md) | Gap analysis and revision control roadmap | +| [docs/COMPONENT_AUDIT.md](docs/COMPONENT_AUDIT.md) | Component audit tool design | +| [ROADMAP.md](ROADMAP.md) | Feature roadmap and SOLIDWORKS PDM comparison | +| [frontend-spec.md](frontend-spec.md) | React SPA frontend specification | ## License From 124eac1391d4c505050131d39478d5c25abaa3a5 Mon Sep 17 00:00:00 2001 From: Forbes Date: Sun, 8 Feb 2026 14:04:26 -0600 Subject: [PATCH 3/8] docs: update STATUS.md for current state - Update migration count to 11, add 011_item_files.sql - Update endpoint count from 74 to 75 - Replace htmx reference with React SPA - Add file attachments as complete feature - Update test file count from 1 to 9 - Update date to 2026-02-08 Closes #27 --- docs/STATUS.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/STATUS.md b/docs/STATUS.md index 0db0033..43235c2 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -1,6 +1,6 @@ # Silo Development Status -**Last Updated:** 2026-02-06 +**Last Updated:** 2026-02-08 --- @@ -10,10 +10,10 @@ | Component | Status | Notes | |-----------|--------|-------| -| PostgreSQL schema | Complete | 10 migrations applied | +| PostgreSQL schema | Complete | 11 migrations applied | | YAML schema parser | Complete | Supports enum, serial, constant, string segments | | Part number generator | Complete | Scoped sequences, category-based format | -| API server (`silod`) | Complete | 74 REST endpoints via chi/v5 | +| API server (`silod`) | Complete | 75 REST endpoints via chi/v5 | | CLI tool (`silo`) | Complete | Item registration and management | | MinIO file storage | Complete | Upload, download, versioning, checksums | | Revision control | Complete | Append-only history, rollback, comparison, status/labels | @@ -28,7 +28,8 @@ | Audit logging | Complete | audit_log table, completeness scoring | | CSRF protection | Complete | nosurf on web forms | | Fuzzy search | Complete | sahilm/fuzzy library | -| Web UI | Complete | Items, projects, schemas, audit pages (htmx) | +| Web UI | Complete | React SPA (Vite + TypeScript), 6 pages, Catppuccin Mocha theme | +| File attachments | Complete | Presigned uploads, item file association, thumbnails | | Odoo ERP integration | Partial | Config and sync-log CRUD functional; push/pull are stubs | | Docker Compose | Complete | Dev and production configurations | | Deployment scripts | Complete | setup-host, deploy, init-db, setup-ipa-nginx | @@ -46,7 +47,7 @@ FreeCAD workbench and LibreOffice Calc extension are maintained in separate repo | Inventory API endpoints | Database tables exist, no REST handlers | | Date segment type | Schema parser placeholder only | | Part number format validation | API accepts but does not validate format on creation | -| Unit tests | Minimal: 1 Go test file (`internal/ods/ods_test.go`) | +| Unit tests | 9 Go test files across api, db, ods, partnum, schema packages | --- @@ -92,3 +93,4 @@ The schema defines 170 category codes across 10 groups: | 008_odoo_integration.sql | Odoo ERP integration tables (integrations, sync_log) | | 009_auth.sql | Authentication system (users, api_tokens, sessions, audit_log, user tracking columns) | | 010_item_extended_fields.sql | Extended item fields (sourcing_type, sourcing_link, standard_cost, long_description) | +| 011_item_files.sql | Item file attachments (item_files table, thumbnail_key column) | From adf64d722c4ee8d84b579c95db83643dfda296c2 Mon Sep 17 00:00:00 2001 From: Forbes Date: Sun, 8 Feb 2026 14:05:08 -0600 Subject: [PATCH 4/8] docs: update ROADMAP.md for React SPA and current counts - Replace all htmx references with React SPA - Update endpoint count from 74 to 75 (6 occurrences) - Update migration count from 10 to 11 - Update test file count from 1 to 9 - Fix comparison matrix web access entry Closes #29 --- ROADMAP.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index cc730ca..4f28134 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -39,7 +39,7 @@ This document compares Silo's current capabilities against SOLIDWORKS PDM—the ### Implemented Features (MVP Complete) #### Core Database System -- PostgreSQL schema with 10 migrations +- PostgreSQL schema with 11 migrations - UUID-based identifiers throughout - Soft delete support via `archived_at` timestamps - Atomic sequence generation for part numbers @@ -92,14 +92,14 @@ This document compares Silo's current capabilities against SOLIDWORKS PDM—the - Template generation for import formatting #### API & Web Interface -- REST API with 74 endpoints +- REST API with 75 endpoints - Authentication: local (bcrypt), LDAP/FreeIPA, OIDC/Keycloak - Role-based access control (admin > editor > viewer) - API token management (SHA-256 hashed) - Session management (PostgreSQL-backed, 24h lifetime) - CSRF protection (nosurf on web forms) - Middleware: logging, CORS, recovery, request ID -- Web UI for items, projects, schemas, audit (htmx) +- Web UI — React SPA (Vite + TypeScript, Catppuccin Mocha theme) - Fuzzy search - Health and readiness probes @@ -123,7 +123,7 @@ This document compares Silo's current capabilities against SOLIDWORKS PDM—the | Part number validation | Not started | API accepts but doesn't validate format | | Location hierarchy CRUD | Schema only | Tables exist, no API endpoints | | Inventory tracking | Schema only | Tables exist, no API endpoints | -| Unit tests | Minimal | 1 Go test file (`internal/ods/ods_test.go`) | +| Unit tests | Partial | 9 Go test files across api, db, ods, partnum, schema packages | ### Infrastructure Status @@ -255,14 +255,14 @@ CAD integration is maintained in separate repositories ([silo-mod](https://git.k | Feature | SOLIDWORKS PDM | Silo Status | Priority | Complexity | |---------|---------------|-------------|----------|------------| | ERP integration | SAP, Dynamics, etc. | Partial (Odoo stubs) | Medium | Complex | -| API access | Full COM/REST API | Full REST API (74 endpoints) | - | - | +| API access | Full COM/REST API | Full REST API (75 endpoints) | - | - | | Dispatch scripts | Automation without coding | None | Medium | Moderate | | Task scheduler | Background processing | None | Medium | Moderate | | Email system | SMTP integration | None | High | Simple | -| Web portal | Browser access | Full (htmx + auth) | - | - | +| Web portal | Browser access | Full (React SPA + auth) | - | - | **Gap Analysis:** -Silo has a comprehensive REST API (74 endpoints) and a full web UI with authentication. Odoo ERP integration has config/sync-log scaffolding but push/pull operations are stubs. Remaining gaps: email notifications, task scheduler, dispatch automation. +Silo has a comprehensive REST API (75 endpoints) and a full web UI with authentication. Odoo ERP integration has config/sync-log scaffolding but push/pull operations are stubs. Remaining gaps: email notifications, task scheduler, dispatch automation. --- @@ -528,9 +528,9 @@ File storage works well. Thumbnail generation and file preview would significant | **Data** | CSV import/export | Yes | Yes | Yes | - | | | ODS import/export | No | No | Yes | - | | | Project management | Yes | Yes | Yes | - | -| **Integration** | API | Limited | Full | Full REST (74) | - | +| **Integration** | API | Limited | Full | Full REST (75) | - | | | ERP connectors | No | Yes | Partial (Odoo stubs) | Phase 6 | -| | Web access | No | Yes | Yes (htmx + auth) | - | +| | Web access | No | Yes | Yes (React SPA + auth) | - | | **Files** | Versioning | Yes | Yes | Yes | - | | | Preview | Yes | Yes | No | Phase 6 | | | Multi-site | No | Yes | No | Not Planned | From 5fd499fef309646918d7659a16634ee631f28d18 Mon Sep 17 00:00:00 2001 From: Forbes Date: Sun, 8 Feb 2026 14:06:18 -0600 Subject: [PATCH 5/8] docs: update GAP_ANALYSIS.md for current state - Remove silo-spec.md from doc table (deleted) - Add CONFIGURATION.md, AUTH.md, AUTH_USER_GUIDE.md, frontend-spec.md to doc table - Mark Configuration Reference as complete - Fix audit_handlers.go filename (was handlers_audit.go) - Update Appendix A file structure to match actual codebase - Fix future migration numbering (012/013, since 011 is taken) - Add file attachment endpoints to Appendix B - Remove completed Documentation Overhaul from medium-term Closes #28 --- docs/GAP_ANALYSIS.md | 66 +++++++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 19 deletions(-) diff --git a/docs/GAP_ANALYSIS.md b/docs/GAP_ANALYSIS.md index e94c9cb..a347833 100644 --- a/docs/GAP_ANALYSIS.md +++ b/docs/GAP_ANALYSIS.md @@ -1,6 +1,6 @@ # Silo Gap Analysis and Revision Control Roadmap -**Date:** 2026-02-06 +**Date:** 2026-02-08 **Status:** Analysis Complete (Updated) --- @@ -21,9 +21,12 @@ This document analyzes the current state of the Silo project against its specifi | `docs/SPECIFICATION.md` | Design specification, API reference | Current | | `docs/STATUS.md` | Implementation status summary | Current | | `docs/DEPLOYMENT.md` | Production deployment guide | Current | -| `docs/GAP_ANALYSIS.md` | SOLIDWORKS PDM comparison, roadmap | Current | -| `ROADMAP.md` | Feature roadmap and phases | Current | -| `silo-spec.md` | Redirect to `docs/SPECIFICATION.md` | Consolidated | +| `docs/CONFIGURATION.md` | Configuration reference (all config.yaml options) | Current | +| `docs/AUTH.md` | Authentication system design | Current | +| `docs/AUTH_USER_GUIDE.md` | User guide for login, tokens, and roles | Current | +| `docs/GAP_ANALYSIS.md` | Revision control roadmap | Current | +| `ROADMAP.md` | Feature roadmap and SOLIDWORKS PDM comparison | Current | +| `frontend-spec.md` | React SPA frontend specification | Current | ### 1.2 Documentation Gaps (Priority Order) @@ -34,7 +37,7 @@ This document analyzes the current state of the Silo project against its specifi | **API Reference** | Users cannot integrate programmatically | Medium | Addressed in SPECIFICATION.md Section 11 | | **Deployment Guide** | Cannot deploy to production | Medium | Complete (`docs/DEPLOYMENT.md`) | | **Database Schema Guide** | Migration troubleshooting difficult | Low | Open | -| **Configuration Reference** | config.yaml options undocumented | Low | Open | +| **Configuration Reference** | config.yaml options undocumented | Low | Complete (`docs/CONFIGURATION.md`) | #### Medium Priority @@ -54,10 +57,10 @@ This document analyzes the current state of the Silo project against its specifi ### 1.3 Recommended Actions -1. ~~**Consolidate specs**: Remove `silo-spec.md` duplicate~~ Done +1. ~~**Consolidate specs**: Remove `silo-spec.md` duplicate~~ Done (deleted) 2. ~~**Create API reference**: Full REST endpoint documentation~~ Addressed in SPECIFICATION.md 3. ~~**Create `docs/DEPLOYMENT.md`**: Production deployment guide~~ Done -4. **Create configuration reference**: Document all `config.yaml` options +4. ~~**Create configuration reference**: Document all `config.yaml` options~~ Done (`docs/CONFIGURATION.md`) 5. **Create database schema guide**: Document migrations and troubleshooting --- @@ -207,7 +210,7 @@ Audit logging is implemented via migration 009 with the `audit_log` table and co - `GET /api/audit/completeness` — summary of all items - `GET /api/audit/completeness/{partNumber}` — per-item scoring with weighted fields and tier classification -Code: `internal/api/handlers_audit.go` +Code: `internal/api/audit_handlers.go` --- @@ -304,7 +307,6 @@ Effort: Medium | Priority: Low | Risk: Low 7. **Release Management** - Product milestone tracking 8. **Thumbnail Generation** - Visual preview capability -9. **Documentation Overhaul** - API reference, deployment guide ### Long-term (Future) @@ -351,19 +353,33 @@ These design decisions remain unresolved: ## Appendix A: File Structure -Revision endpoints, status, labels, authentication, and audit logging are implemented. Current structure: +Revision endpoints, status, labels, authentication, audit logging, and file attachments are implemented. Current structure: ``` internal/ api/ - handlers_audit.go # Audit/completeness endpoints (implemented) - middleware.go # Auth middleware (implemented) + audit_handlers.go # Audit/completeness endpoints + auth_handlers.go # Login, tokens, OIDC + bom_handlers.go # Flat BOM, cost roll-up + file_handlers.go # Presigned uploads, item files, thumbnails + handlers.go # Items, schemas, projects, revisions + middleware.go # Auth middleware + odoo_handlers.go # Odoo integration endpoints + routes.go # Route registration (75 endpoints) + search.go # Fuzzy search auth/ - auth.go # Auth service: local, LDAP, OIDC (implemented) + auth.go # Auth service: local, LDAP, OIDC + db/ + items.go # Item and revision repository + item_files.go # File attachment repository + relationships.go # BOM repository + projects.go # Project repository + storage/ + storage.go # MinIO file storage helpers migrations/ - 008_odoo_integration.sql # Odoo ERP tables (implemented) - 009_auth.sql # Auth + audit tables (implemented) - 010_item_extended_fields.sql # Extended item fields (implemented) + 001_initial.sql # Core schema + ... + 011_item_files.sql # Item file attachments (latest) ``` Future features would add: @@ -371,13 +387,13 @@ Future features would add: ``` internal/ api/ - handlers_lock.go # Locking endpoints + lock_handlers.go # Locking endpoints db/ locks.go # Lock repository releases.go # Release repository migrations/ - 011_item_locks.sql # Locking table - 012_releases.sql # Release management + 012_item_locks.sql # Locking table + 013_releases.sql # Release management ``` --- @@ -407,6 +423,18 @@ GET /api/audit/completeness # All items completeness summary GET /api/audit/completeness/{partNumber} # Per-item scoring ``` +**File Attachments (Implemented):** +``` +GET /api/auth/config # Auth config (public) +POST /api/uploads/presign # Presigned upload URL +GET /api/items/{pn}/files # List item files +POST /api/items/{pn}/files # Associate file with item +DELETE /api/items/{pn}/files/{fileId} # Delete file association +PUT /api/items/{pn}/thumbnail # Set item thumbnail +GET /api/items/{pn}/bom/flat # Flattened BOM +GET /api/items/{pn}/bom/cost # Assembly cost roll-up +``` + **Locking (Not Implemented):** ``` POST /api/items/{pn}/lock # Acquire lock From eb43fbb9eca3a6e7f74bf4697d6a47bde9ecd433 Mon Sep 17 00:00:00 2001 From: Forbes Date: Sun, 8 Feb 2026 14:07:38 -0600 Subject: [PATCH 6/8] docs: update frontend-spec.md for completed migration - Mark Phase 4 (remove Go templates) as complete - Update architecture: SPA serves at / via NotFound handler - Update overview to past tense (migration is done) - Update file/line counts (~40 files, ~7,600 lines) - Mark backend changes 1-3 and 5 as implemented - Reorganize remaining work section Closes #30 --- frontend-spec.md | 47 +++++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/frontend-spec.md b/frontend-spec.md index 6354d28..4e29629 100644 --- a/frontend-spec.md +++ b/frontend-spec.md @@ -1,10 +1,10 @@ # Silo Frontend Specification -Current as of 2026-02-06. Tracks the React + Vite + TypeScript frontend migration (epic #6). +Current as of 2026-02-08. Documents the React + Vite + TypeScript frontend (migration from Go templates is complete). ## Overview -The Silo web UI is being migrated from server-rendered Go templates with vanilla JavaScript (~7,000 lines across 7 templates) to a React single-page application. The Go API server remains unchanged — it serves JSON at `/api/*` and the React app consumes it. +The Silo web UI has been migrated from server-rendered Go templates to a React single-page application. The Go templates (~7,000 lines across 7 files) have been removed. The Go API server serves JSON at `/api/*` and the React SPA at `/`. **Stack**: React 19, React Router 7, Vite 6, TypeScript 5.7 **Theme**: Catppuccin Mocha (dark) via CSS custom properties @@ -19,22 +19,21 @@ The Silo web UI is being migrated from server-rendered Go templates with vanilla | 1 | #7 | Scaffold React + Vite + TS, shared layout, auth, API client | Code complete | | 2 | #8 | Migrate Items page with UI improvements | Code complete | | 3 | #9 | Migrate Projects, Schemas, Settings, Login pages | Code complete | -| 4 | #10 | Remove Go templates, Docker integration, cleanup | Not started | +| 4 | #10 | Remove Go templates, Docker integration, cleanup | Complete | ## Architecture ``` Browser - └── React SPA (served at /app/* during transition, / after Phase 4) + └── React SPA (served at /) ├── Vite dev server (development) → proxies /api/* to Go backend └── Static files in web/dist/ (production) → served by Go binary Go Server (silod) - ├── /api/* JSON REST API (unchanged) + ├── /api/* JSON REST API ├── /login, /logout Session auth endpoints (form POST) ├── /auth/oidc OIDC redirect flow - ├── /app/* React SPA static files (current transition) - └── /* Go template pages (removed in Phase 4) + └── /* React SPA (NotFound handler serves index.html for client-side routing) ``` ### Auth Flow @@ -101,7 +100,7 @@ web/ └── AuditPage.tsx Audit completeness (placeholder, expanded in Issue #5) ``` -**Total**: 32 source files, ~5,300 lines of TypeScript/TSX. +**Total**: ~40 source files, ~7,600 lines of TypeScript/TSX. ## Design System @@ -229,25 +228,17 @@ Basic table showing audit completeness data from `GET /api/audit/completeness`. **Import**: CSVImportResult, CSVImportError **Errors**: ErrorResponse -## Remaining Work +## Completed Work -### Issue #10: Remove Go Templates + Docker Integration +### Issue #10: Remove Go Templates + Docker Integration -- COMPLETE -The final phase completes the migration: +Completed in commit `50923cf`. All Go templates deleted, `web.go` handler removed, SPA serves at `/` via `NotFound` handler with `index.html` fallback. `build/package/Dockerfile` added. -**Remove Go templates**: Delete `internal/api/templates/` (7 HTML files), remove template loading code, remove web handler route group and CSRF middleware for web routes, remove `HandleIndex`, `HandleProjects`, `HandleSchemas`, `HandleSettings` handlers. - -**SPA serving**: Serve `web/dist/` at `/` with SPA fallback (non-API routes return `index.html`). Either `go:embed` for single-binary deployment or filesystem serving. `/api/*` routes must take precedence. Cache headers for Vite's hashed assets. - -**Docker**: Update `build/package/Dockerfile` to multi-stage — Stage 1: Node (`npm ci && npm run build`), Stage 2: Go build with `web/dist/`, Final: minimal Alpine image with single binary. - -**Makefile**: Existing `web-install`, `web-dev`, `web-build` targets need verification. `make build` should include the web build step. `make clean` should include `web/dist/`. - -**Acceptance criteria**: Single Docker image serves both API and React frontend. `make build` produces working binary. No Go template code remains. All pages accessible at React Router paths. +### Remaining Work ### Issue #5: Component Audit UI (future) -After migration completes, the Audit page will be expanded with completeness scoring, inline editing, tier filtering, and category breakdowns. This will be built natively in React using the patterns established in the migration. +The Audit page will be expanded with completeness scoring, inline editing, tier filtering, and category breakdowns. This will be built natively in React using the patterns established in the migration. ## Development @@ -572,11 +563,11 @@ The right sidebar is divided into three sections with `borderBottom: 1px solid v **Thumbnail**: A 4:3 aspect ratio placeholder box (`--ctp-crust` bg, `--ctp-surface0` border) with centered text "Generated from CAD file or upload manually". Clicking opens file picker filtered to images. If a thumbnail is uploaded, show it as an `` with `object-fit: cover`. -## Backend Changes Required +## Backend Changes -The following API additions are needed. These should be tracked as sub-tasks or a separate issue. +Items 1-3 and 5 below are implemented (migration `011_item_files.sql`, `internal/api/file_handlers.go`). Item 4 (hierarchical categories) remains open. -### 1. Presigned Upload URL +### 1. Presigned Upload URL -- IMPLEMENTED ``` POST /api/uploads/presign @@ -586,7 +577,7 @@ Response: { "object_key": "uploads/tmp/{uuid}/{filename}", "upload_url": "https: The Go handler generates a presigned PUT URL via the MinIO SDK. Objects are uploaded to a temporary prefix. On item creation, they're moved/linked to the item's permanent prefix. -### 2. File Association +### 2. File Association -- IMPLEMENTED ``` POST /api/items/{id}/files @@ -596,7 +587,7 @@ Response: { "file_id": "uuid", "filename": "...", "size": ..., "created_at": ".. Moves the object from the temp prefix to `items/{item_id}/files/{file_id}` and creates a row in a new `item_files` table. -### 3. Thumbnail +### 3. Thumbnail -- IMPLEMENTED ``` PUT /api/items/{id}/thumbnail @@ -606,7 +597,7 @@ Response: 204 Stores the thumbnail at `items/{item_id}/thumbnail.png` in MinIO. Updates `item.thumbnail_key` column. -### 4. Hierarchical Categories +### 4. Hierarchical Categories -- NOT IMPLEMENTED If schemas don't currently support a hierarchical category tree, one of these approaches: @@ -634,7 +625,7 @@ DELETE /api/categories/{id} → cascade check **Recommendation**: Option B is more flexible and keeps categories as a first-class entity. The three-tier picker doesn't need to be limited to exactly three levels — it can render as many columns as the deepest category path, but three is the practical default (Domain → Group → Subtype). -### 5. Database Schema Addition +### 5. Database Schema Addition -- IMPLEMENTED ```sql CREATE TABLE item_files ( From e3da072229b3599034ddb2776b3232fe6bebd3bd Mon Sep 17 00:00:00 2001 From: Forbes Date: Sun, 8 Feb 2026 14:11:12 -0600 Subject: [PATCH 7/8] docs: update SPECIFICATION.md for React SPA and current endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace htmx with React SPA in architecture diagram and tech stack - Rewrite Section 6 (Web Interface) with React SPA architecture, pages table, and design patterns - Update Section 11.1: remove old web UI routes, add /api/auth/config, /api/uploads/presign, file attachment endpoints, thumbnail endpoint - Fix endpoint count: 76 → 75 (actual registered routes) - Fix migration count: 10 → 11 - Add file attachments to MVP checklist - Update test coverage note (9 test files exist) Closes #26 --- docs/SPECIFICATION.md | 77 ++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 30 deletions(-) diff --git a/docs/SPECIFICATION.md b/docs/SPECIFICATION.md index 60533de..8f89a93 100644 --- a/docs/SPECIFICATION.md +++ b/docs/SPECIFICATION.md @@ -37,13 +37,13 @@ Silo treats **part numbering schemas as configuration, not code**. Multiple numb ▼ ┌─────────────────────────────────────────────────────────────┐ │ Silo Server (silod) │ -│ - REST API (76 endpoints) │ +│ - REST API (75 endpoints) │ │ - Authentication (local, LDAP, OIDC) │ │ - Schema parsing and validation │ │ - Part number generation engine │ │ - Revision management │ │ - Relationship graph / BOM │ -│ - Web UI (htmx) │ +│ - Web UI (React SPA) │ └─────────────────────────────────────────────────────────────┘ │ ┌───────────────┴───────────────┐ @@ -68,7 +68,7 @@ Silo treats **part numbering schemas as configuration, not code**. Multiple numb | CLI & API Server | Go (1.24) | chi/v5 router, pgx/v5 driver, zerolog | | Authentication | Multi-backend | Local (bcrypt), LDAP/FreeIPA, OIDC/Keycloak | | Sessions | PostgreSQL pgxstore | alexedwards/scs, 24h lifetime | -| Web UI | Go html/template + htmx | Lightweight, minimal JS | +| Web UI | React 19, Vite 6, TypeScript 5.7 | Catppuccin Mocha theme, inline styles | --- @@ -362,15 +362,35 @@ Recommendation: Pessimistic locking for CAD files (merge is impractical). ## 6. Web Interface -### 6.1 Features +### 6.1 Architecture -- **Browse**: Navigate item hierarchy (project → assembly → subassembly → part) -- **Search**: Full-text search across part numbers, descriptions, properties -- **View**: Item details, revision history, relationships, location -- **BOM Viewer**: Expandable tree view of assembly structure -- **"Open in FreeCAD"**: Launch FreeCAD with specific item via URI handler +The web UI is a React single-page application served at `/` by the Go server. The SPA communicates with the backend exclusively via the JSON REST API at `/api/*`. -### 6.2 URI Handler +- **Stack**: React 19, React Router 7, Vite 6, TypeScript 5.7 +- **Theme**: Catppuccin Mocha (dark) via CSS custom properties +- **Styling**: Inline `React.CSSProperties` — no CSS modules, no Tailwind +- **State**: Local `useState` + custom hooks (no Redux/Zustand) +- **SPA serving**: `web/dist/` served by Go's `NotFound` handler with `index.html` fallback for client-side routing + +### 6.2 Pages + +| Page | Route | Description | +|------|-------|-------------| +| Items | `/` | Master-detail layout with resizable split panel, sortable table, 5-tab detail view (Main, Properties, Revisions, BOM, Where Used), in-pane CRUD forms | +| Projects | `/projects` | Project CRUD with sortable table, in-pane forms | +| Schemas | `/schemas` | Schema browser with collapsible segments, enum value CRUD | +| Settings | `/settings` | Account info, API token management | +| Audit | `/audit` | Item completeness scoring | +| Login | `/login` | Username/password form, conditional OIDC button | + +### 6.3 Design Patterns + +- **In-pane forms**: Create/Edit/Delete forms render in the detail pane area (Infor ERP-style), not as modal overlays +- **Permission checks**: Write actions conditionally rendered based on user role +- **Fuzzy search**: Debounced search with scope toggle (All/PN/Description) +- **Persistence**: `useLocalStorage` hook for user preferences (layout mode, column visibility) + +### 6.4 URI Handler Register `silo://` protocol handler: @@ -379,12 +399,7 @@ silo://open/PROTO-AS-0001 # Open latest revision silo://open/PROTO-AS-0001?rev=3 # Open specific revision ``` -### 6.3 Technology - -- **Backend**: Go with standard library HTTP -- **Frontend**: htmx for interactivity, minimal JavaScript -- **Templates**: Go html/template -- **Search**: PostgreSQL full-text search (pg_trgm for fuzzy matching) +See [frontend-spec.md](../frontend-spec.md) for full component specifications. --- @@ -583,7 +598,7 @@ See [AUTH.md](AUTH.md) for full architecture details and [AUTH_USER_GUIDE.md](AU ## 11. API Design -### 11.1 REST Endpoints (Implemented) +### 11.1 REST Endpoints (75 Implemented) ``` # Health (no auth) @@ -597,21 +612,18 @@ POST /logout # Logout GET /auth/oidc # OIDC login redirect GET /auth/callback # OIDC callback -# Web UI (auth + CSRF) -GET / # Items page -GET /projects # Projects page -GET /schemas # Schemas page -GET /audit # Audit/completeness page -GET /settings # User settings / token management -POST /settings/tokens # Create API token (web) -POST /settings/tokens/{id}/revoke # Revoke API token (web) +# Public API (no auth required) +GET /api/auth/config # Auth backend configuration (for login UI) -# Auth API +# Auth API (require auth) GET /api/auth/me # Current authenticated user GET /api/auth/tokens # List user's API tokens POST /api/auth/tokens # Create API token DELETE /api/auth/tokens/{id} # Revoke API token +# Presigned Uploads (editor) +POST /api/uploads/presign # Get presigned MinIO upload URL [editor] + # Schemas (read: viewer, write: editor) GET /api/schemas # List all schemas GET /api/schemas/{name} # Get schema details @@ -659,9 +671,13 @@ PATCH /api/items/{partNumber}/revisions/{revision} # Update status/labe POST /api/items/{partNumber}/revisions/{revision}/rollback # Rollback to revision [editor] # Files +GET /api/items/{partNumber}/files # List item file attachments GET /api/items/{partNumber}/file # Download latest file GET /api/items/{partNumber}/file/{revision} # Download file at revision POST /api/items/{partNumber}/file # Upload file [editor] +POST /api/items/{partNumber}/files # Associate uploaded file with item [editor] +DELETE /api/items/{partNumber}/files/{fileId} # Remove file association [editor] +PUT /api/items/{partNumber}/thumbnail # Set item thumbnail [editor] # BOM GET /api/items/{partNumber}/bom # List direct children @@ -718,11 +734,11 @@ POST /api/inventory/{partNumber}/move ### 12.1 Implemented -- [x] PostgreSQL database schema (10 migrations) +- [x] PostgreSQL database schema (11 migrations) - [x] YAML schema parser for part numbering - [x] Part number generation engine - [x] CLI tool (`cmd/silo`) -- [x] API server (`cmd/silod`) with 76 endpoints +- [x] API server (`cmd/silod`) with 75 endpoints - [x] MinIO integration for file storage with versioning - [x] BOM relationships (component, alternate, reference) - [x] Multi-level BOM (recursive expansion with configurable depth) @@ -736,7 +752,8 @@ POST /api/inventory/{partNumber}/move - [x] Project management with many-to-many item tagging - [x] CSV import/export with dry-run validation - [x] ODS spreadsheet import/export (items, BOMs, project sheets) -- [x] Web UI for items, projects, schemas, audit (htmx) +- [x] Web UI for items, projects, schemas, audit, settings (React SPA) +- [x] File attachments with presigned upload and thumbnail support - [x] Authentication (local, LDAP, OIDC) with role-based access control - [x] API token management (SHA-256 hashed) - [x] Session management (PostgreSQL-backed) @@ -757,7 +774,7 @@ POST /api/inventory/{partNumber}/move ### 12.3 Not Started -- [ ] Unit tests (Go server — minimal coverage exists) +- [ ] Unit tests (Go server — 9 test files exist, coverage is partial) - [ ] Schema migration tooling - [ ] Checkout locking - [ ] Approval workflows From c49f8f78c948ddc39805860017088d81c226c7d5 Mon Sep 17 00:00:00 2001 From: Forbes Date: Sun, 8 Feb 2026 15:11:20 -0600 Subject: [PATCH 8/8] fix: render project tags as strings, not objects The /api/items/{pn}/projects endpoint returns Project objects ({id, code, name, created_at}), but MainTab typed them as string[]. React error #31 was thrown when trying to render the object as a child node. Change itemProjects state from string[] to Project[] and use proj.code in all rendering and comparison logic. Closes #33 --- web/src/components/items/MainTab.tsx | 266 ++++++++++++++++++++------- 1 file changed, 202 insertions(+), 64 deletions(-) diff --git a/web/src/components/items/MainTab.tsx b/web/src/components/items/MainTab.tsx index 14a0d99..18e4ff0 100644 --- a/web/src/components/items/MainTab.tsx +++ b/web/src/components/items/MainTab.tsx @@ -1,6 +1,6 @@ -import { useState, useEffect } from 'react'; -import { get, post, del } from '../../api/client'; -import type { Item, Project, Revision } from '../../api/types'; +import { useState, useEffect } from "react"; +import { get, post, del } from "../../api/client"; +import type { Item, Project, Revision } from "../../api/types"; interface MainTabProps { item: Item; @@ -9,8 +9,14 @@ interface MainTabProps { } function formatDate(s: string) { - if (!s) return '—'; - return new Date(s).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); + if (!s) return "—"; + return new Date(s).toLocaleDateString("en-US", { + year: "numeric", + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + }); } function formatFileSize(bytes: number) { @@ -21,19 +27,23 @@ function formatFileSize(bytes: number) { } export function MainTab({ item, onReload, isEditor }: MainTabProps) { - const [itemProjects, setItemProjects] = useState([]); + const [itemProjects, setItemProjects] = useState([]); const [allProjects, setAllProjects] = useState([]); const [latestRev, setLatestRev] = useState(null); - const [addProject, setAddProject] = useState(''); + const [addProject, setAddProject] = useState(""); useEffect(() => { - get(`/api/items/${encodeURIComponent(item.part_number)}/projects`) + get( + `/api/items/${encodeURIComponent(item.part_number)}/projects`, + ) .then(setItemProjects) .catch(() => setItemProjects([])); - get('/api/projects') + get("/api/projects") .then(setAllProjects) .catch(() => {}); - get(`/api/items/${encodeURIComponent(item.part_number)}/revisions`) + get( + `/api/items/${encodeURIComponent(item.part_number)}/revisions`, + ) .then((revs) => { if (revs.length > 0) setLatestRev(revs[revs.length - 1]!); }) @@ -43,67 +53,144 @@ export function MainTab({ item, onReload, isEditor }: MainTabProps) { const handleAddProject = async () => { if (!addProject) return; try { - await post(`/api/items/${encodeURIComponent(item.part_number)}/projects`, { projects: [addProject] }); - setItemProjects((prev) => [...prev, addProject]); - setAddProject(''); + await post( + `/api/items/${encodeURIComponent(item.part_number)}/projects`, + { projects: [addProject] }, + ); + const proj = allProjects.find((p) => p.code === addProject); + if (proj) setItemProjects((prev) => [...prev, proj]); + setAddProject(""); onReload(); } catch (e) { - alert(e instanceof Error ? e.message : 'Failed to add project'); + alert(e instanceof Error ? e.message : "Failed to add project"); } }; const handleRemoveProject = async (code: string) => { try { - await del(`/api/items/${encodeURIComponent(item.part_number)}/projects/${encodeURIComponent(code)}`); - setItemProjects((prev) => prev.filter((p) => p !== code)); + await del( + `/api/items/${encodeURIComponent(item.part_number)}/projects/${encodeURIComponent(code)}`, + ); + setItemProjects((prev) => prev.filter((p) => p.code !== code)); onReload(); } catch (e) { - alert(e instanceof Error ? e.message : 'Failed to remove project'); + alert(e instanceof Error ? e.message : "Failed to remove project"); } }; const row = (label: string, value: React.ReactNode) => ( -
- {label} - {value} +
+ + {label} + + {value}
); return (
- {row('Part Number', {item.part_number})} - {row('Description', item.description)} - {row('Type', item.item_type)} - {row('Sourcing', item.sourcing_type || '—')} - {item.sourcing_link && row('Source Link', {item.sourcing_link})} - {item.standard_cost != null && row('Std Cost', `$${item.standard_cost.toFixed(2)}`)} - {row('Revision', `Rev ${item.current_revision}`)} - {row('Created', formatDate(item.created_at))} - {row('Updated', formatDate(item.updated_at))} + {row( + "Part Number", + + {item.part_number} + , + )} + {row("Description", item.description)} + {row("Type", item.item_type)} + {row("Sourcing", item.sourcing_type || "—")} + {item.sourcing_link && + row( + "Source Link", + + {item.sourcing_link} + , + )} + {item.standard_cost != null && + row("Std Cost", `$${item.standard_cost.toFixed(2)}`)} + {row("Revision", `Rev ${item.current_revision}`)} + {row("Created", formatDate(item.created_at))} + {row("Updated", formatDate(item.updated_at))} {item.long_description && ( -
-
Long Description
-
{item.long_description}
+
+
+ Long Description +
+
{item.long_description}
)} {/* Project Tags */} -
-
Projects
-
- {itemProjects.map((code) => ( - - {code} +
+
+ Projects +
+
+ {itemProjects.map((proj) => ( + + {proj.code} {isEditor && ( @@ -116,22 +203,36 @@ export function MainTab({ item, onReload, isEditor }: MainTabProps) { value={addProject} onChange={(e) => setAddProject(e.target.value)} style={{ - padding: '0.1rem 0.3rem', fontSize: '0.75rem', - backgroundColor: 'var(--ctp-surface0)', border: '1px solid var(--ctp-surface1)', - borderRadius: '0.3rem', color: 'var(--ctp-text)', + padding: "0.1rem 0.3rem", + fontSize: "0.75rem", + backgroundColor: "var(--ctp-surface0)", + border: "1px solid var(--ctp-surface1)", + borderRadius: "0.3rem", + color: "var(--ctp-text)", }} > {allProjects - .filter((p) => !itemProjects.includes(p.code)) - .map((p) => )} + .filter((p) => !itemProjects.some((ip) => ip.code === p.code)) + .map((p) => ( + + ))} {addProject && ( - )} @@ -142,21 +243,58 @@ export function MainTab({ item, onReload, isEditor }: MainTabProps) { {/* File Info */} {latestRev?.file_key && ( -
-
File Attachment (Rev {latestRev.revision_number})
-
- {latestRev.file_size != null && {formatFileSize(latestRev.file_size)}} +
+
+ File Attachment (Rev {latestRev.revision_number}) +
+
+ {latestRev.file_size != null && ( + {formatFileSize(latestRev.file_size)} + )} {latestRev.file_checksum && ( - + SHA256: {latestRev.file_checksum.substring(0, 12)}... )}