josh b0e9c5d1d0
CI / Lint · Typecheck · Test · Build (push) Successful in 44s
CI / Playwright (smoke) (push) Has been skipped
CI / Build & push images (push) Successful in 1m5s
feat: host detail page + FM host context
Add /hosts/:id detail page with unified timeline (HostEvents + FMs + Repairs
+ part arrivals/departures) and a deployed-parts table. Hosts list rows now
link to the page. FM list + detail surface inline State/Stack badges next
to the asset ID, with the asset ID linking to the host page.

HostEvent audit model added; create/update in the hosts service now diff
and log state, stack, and field changes the same way parts.ts does.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 14:04:07 -04:00
2026-04-16 20:52:32 -04:00
2026-04-17 13:36:11 -04:00
2026-04-16 20:52:32 -04:00

Vector

Hardware parts inventory system. Tracks serialized parts across sites → rooms → bins and hosts (with externally-driven state/stack lifecycle), with a full audit trail, repair/RMA workflow, per-tech custody for broken-part holds and pre-staged spares, tag-based organization, category-per-model taxonomy, manufacturer EOL tracking, and signed webhook delivery for external integrations.

Vector 2.0 is a ground-up TypeScript rewrite of the original JavaScript codebase, delivered as a pnpm + Turbo monorepo with shadcn/ui on the frontend and a service-layered Express API on the backend.


Architecture

vector/
├── apps/
│   ├── api/          Express 5 + Prisma + zod. Controllers → services → tx.
│   ├── web/          React 19 + Vite + TanStack Query/Table + shadcn/ui.
│   └── e2e/          Playwright smoke tests (login, parts, repairs, admin).
├── packages/
│   ├── db/           Prisma schema, migrations, seed, singleton client.
│   ├── shared/       zod schemas + DTOs — single source of truth for the API contract.
│   ├── ui/           shadcn primitives + Vector design tokens.
│   └── config/       Shared tsconfig presets + Tailwind preset.
├── .gitea/workflows/ CI (lint · typecheck · test · build) and gated E2E job.
├── docker-compose.yml  Postgres + Redis for local development.
└── turbo.json        Pipeline: `build`, `test`, `typecheck`, `lint`.

The API is split along a strict controller → service → transaction boundary. Every service function takes (tx, input, actor) so controllers can compose multiple services inside a single prisma.$transaction, guaranteeing that part mutations and their PartEvent audit rows are atomic.


Tech stack

Layer Choice
Language TypeScript strict mode across all workspaces
API Express 5, Prisma 5, zod, JWT + refresh-token rotation, CSRF, helmet
Web React 19, Vite, TanStack Query, TanStack Table, react-hook-form, nuqs
UI shadcn/ui on Radix primitives, Tailwind v4, Sonner toasts, Recharts
Database SQLite for dev (Postgres cutover prepared — schema is portable)
Observability pino (JSON logs in prod, pretty in dev) + request-scoped requestId
Testing Vitest (unit, coverage gate on services/lib), Playwright (E2E)
Monorepo tools pnpm workspaces + Turborepo
CI Gitea Actions + Renovate (self-hosted)

Getting started

Prerequisites

  • Node.js ≥ 20
  • pnpm ≥ 10 (corepack enable or npm i -g pnpm)
  • Docker (optional; required for Postgres + Redis)

Install

pnpm install
pnpm -C packages/db exec prisma generate

Environment

Copy the API env template and generate a JWT secret:

cp apps/api/.env.example apps/api/.env
node -e "console.log(require('crypto').randomBytes(48).toString('hex'))"   # paste into JWT_SECRET

The default DATABASE_URL points at a local SQLite file. To use the containerized Postgres instead, start Docker and update the URL:

docker compose up -d postgres redis
# then edit apps/api/.env:
# DATABASE_URL=postgresql://vector:vector@localhost:5432/vector

Database

pnpm -C packages/db exec prisma migrate dev     # apply migrations
pnpm -C packages/db run db:seed                 # create default admin:admin user

Run

pnpm dev

Common tasks

All tasks run at the workspace root; Turbo fans them out to the right packages with caching.

Command What it does
pnpm dev Run apps/api and apps/web concurrently
pnpm build Build every package and app
pnpm typecheck tsc --noEmit across every workspace
pnpm lint ESLint across every workspace
pnpm test Run all Vitest unit test suites
pnpm -C apps/api test:coverage Unit tests + v8 coverage report on services/lib (report only)
pnpm -C apps/e2e test Run Playwright smoke tests (requires stack running + creds)
pnpm -C packages/db run db:studio Open Prisma Studio against the current database
pnpm -C packages/db run db:reset Drop schema, re-migrate, re-seed

Testing

Unit tests live next to the code they cover (*.test.ts). Coverage is reported on apps/api/src/{services,lib}/** and uploaded by CI as an artifact — no threshold gate today; add one once service-level coverage catches up.

  • packages/shared — zod schema contracts (parts, webhooks, auth).
  • apps/api — pure helpers (signBody, csvCell, http-error), plus the analytics aggregator tested with an in-memory Tx double.

End-to-end tests in apps/e2e/ run against a live stack. Each spec skips itself unless TEST_USERNAME and TEST_PASSWORD are present in the environment:

pnpm dev                                         # in one terminal
TEST_USERNAME=admin TEST_PASSWORD=admin \
  pnpm -C apps/e2e exec playwright install --with-deps chromium
TEST_USERNAME=admin TEST_PASSWORD=admin \
  pnpm -C apps/e2e test

Reports land in apps/e2e/playwright-report/.


Continuous integration

CI runs on Gitea Actions — .gitea/workflows/ci.yaml. Every push and pull request executes:

  1. pnpm install --frozen-lockfile
  2. prisma generate
  3. pnpm lint
  4. pnpm typecheck
  5. pnpm -C packages/shared test + pnpm -C apps/api test:coverage
  6. pnpm build
  7. API coverage uploaded as an artifact.

A second Playwright job is gated behind the repository variable ENABLE_E2E=true and requires these secrets:

Secret Purpose
E2E_BASE_URL URL of a running Vector stack
E2E_USERNAME Test admin username
E2E_PASSWORD Test admin password

Dependency updates come from a self-hosted Renovate instance configured via renovate.json — grouped minor/patch PRs weekly, auto-merge for dev-dep patches, prisma + @prisma/client grouped together, Radix/shadcn held for manual review.


Conventions

  • API shape: all responses follow { code, message, requestId, details? } for errors. Paginated lists use { data, page, pageSize, total }.
  • Validation: every request body and query string is parsed through a zod schema from @vector/shared before it reaches a controller. No ad-hoc validation inside controllers.
  • Query keys: the web app uses a hierarchical factory at apps/web/src/lib/queryKeys.ts. Invalidate by domain (queryKeys.parts.all) or by filter (queryKeys.parts.list(filters)).
  • Commits: Conventional Commits — Renovate already expects them.
  • Webhooks: every delivery is signed with HMAC-SHA256 over ${timestamp}.${body} and sent with headers x-vector-signature, x-vector-timestamp, x-vector-event, and the recursion-guard x-vector-webhook: v1.

Roadmap

All nine phases of the 2.0 rewrite are in-tree:

Phase Scope
0 Monorepo foundation (pnpm + Turbo, apps scaffolded)
1 Strict TypeScript, Prisma schema, env validation, singleton client
2 Service-layered API, transactional mutations, auth hardening, pagination
3 Schema extensions (hosts, repairs, tags, categories, webhooks, FTS-ready)
4 shadcn/ui design system + DataTable<T>, Form pattern, query-keys factory
5 Page-by-page rewrite (Parts, Locations split, Manufacturers, Users)
6 Feature slice — Repair/RMA, Tags, Bulk ops, Saved views
7 Analytics dashboard (Recharts) + EOL + Webhooks + streaming CSV export
8 Vitest + Playwright + Gitea Actions CI + Renovate

Deferred follow-ups

  • Postgres cutover. Schema is already portable; the data-migration script lives at packages/db/POSTGRES_FTS.md.
  • BullMQ worker. Replace the in-process webhook emitter in apps/api/src/lib/webhook-emitter.ts with a Redis-backed worker. Signature is stable — one-line swap.
  • PDF audit export via @react-pdf/renderer in the worker.
  • CSV import wizard UI to pair with the existing CsvImportJob staging table.
S
Description
Serialized hardware inventory for engineering fleets
Readme 486 KiB
Languages
TypeScript 98.6%
CSS 0.5%
Dockerfile 0.5%
JavaScript 0.3%