josh be20fe587a
CI / Lint · Typecheck · Test · Build (push) Successful in 51s
CI / Playwright (smoke) (push) Has been skipped
CI / Build & push images (push) Successful in 1m59s
chore: remove auth rate limiting
Vector is an internal service — throttling /api/auth requests provides
no meaningful protection and gets in the way of legitimate use. Drops
the express-rate-limit middleware and dependency.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 20:19:12 -04:00
2026-04-19 20:19:12 -04:00
2026-04-16 20:52:32 -04:00
2026-04-16 20:52:32 -04:00

Vector

Serialized hardware inventory for engineering fleets. Track every part from receiving → bin → host → repair → destruction, with a complete per-part audit trail, field-maintenance ticketing, per-technician custody, and manufacturer EOL visibility.

Vector is a pnpm + Turborepo monorepo written in strict TypeScript end to end: an Express/Prisma API, a React/Vite web client, shared zod contracts, and a shadcn/ui design system.


What it does

Inventory

  • Serialized Parts live under Site → Room → Bin locations or on a Host.
  • Every state change writes a PartEvent in the same transaction — nothing mutates silently.
  • PartModels are grouped by Manufacturer and Category, with optional EOL dates that feed dashboard risk widgets.
  • Tags and SavedViews for cross-cutting organization and reusable filters.

Field workflow

  • FM tickets track active issues per host, with attached repairs and close times.
  • Repair logs pair a broken serial with its replacement and auto-ingest parts from a tech's input when the broken serial isn't yet in Vector.
  • Per-technician custody tracks parts held for repair, pending drop-off, or pending destruction — so broken or pre-staged parts never go missing between events.

Operations dashboard

  • Universal KPIs: total parts, total spent, deployed value, open FMs, past-EOL deployments, upcoming-EOL deployments (≤180 d).
  • Admin-only: 7/30-day repair tempo, FMs opened, average FM close time, repairs trend chart, open-FMs-by-host, and a custody backlog by user.

Integrations

  • Outbound webhooks signed with HMAC-SHA256(timestamp + "." + body), headers x-vector-signature, x-vector-timestamp, x-vector-event, plus a x-vector-webhook: v1 recursion guard.
  • Streaming CSV export of the audit log for admins.

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 — the source of truth for the API.
│   ├── ui/         shadcn primitives + Vector design tokens.
│   └── config/     Shared tsconfig + Tailwind tokens.
├── .gitea/workflows/ci.yaml   lint · typecheck · test · build, gated E2E job.
├── docker-compose.yml          api + web + redis (production-style stack).
└── turbo.json                  dev · build · test · typecheck · lint.

The API is strict about its boundaries. Every service takes (tx, input, actor) so controllers can compose multiple services inside a single prisma.$transaction — part mutations and their audit rows are always atomic or not at all. Controllers do no validation and no business logic; they parse through a zod schema and dispatch.


Tech stack

Layer Choice
Language TypeScript strict mode across every workspace
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 (single-file, shipped in prod via Docker volume)
Observability pino (JSON in prod, pretty in dev) with per-request requestId
Testing Vitest (unit), Playwright (E2E)
Monorepo pnpm workspaces + Turborepo
CI / deps Gitea Actions + self-hosted Renovate

The schema is intentionally Postgres-portable; SQLite is the default because Vector is deployed as a single-container stack per site.


Quick start

Prerequisites: Node 20+, pnpm 10+ (corepack enable).

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

pnpm -C packages/db exec prisma migrate dev
pnpm -C packages/db run db:seed

pnpm dev

Deployment

docker-compose.yml runs the full stack from prebuilt images (vector-api, vector-web, redis). The SQLite database lives in the vector-data volume.

# 1. authenticate to the registry
docker login gitea.thewrightserver.net

# 2. create a .env next to docker-compose.yml with at minimum:
#      JWT_SECRET=<64-char hex>
#      CLIENT_ORIGIN=https://vector.example.com
#      WEB_PORT=8080
#      COOKIE_SECURE=true          # required once you're behind TLS
#      TAG=latest                  # or a pinned commit SHA

docker compose pull && docker compose up -d

Redis is included in anticipation of the BullMQ-backed webhook worker; the in-process emitter currently used by the API doesn't depend on it yet.


Development commands

All commands run at the workspace root; Turbo fans them out with caching.

Command Does
pnpm dev API + web concurrently
pnpm build Build every package and app
pnpm typecheck tsc --noEmit across the graph
pnpm lint ESLint across the graph
pnpm test Vitest across packages that define it
pnpm -C apps/api test:coverage Services/lib coverage report (no threshold gate yet)
pnpm -C apps/e2e test Playwright, against a live stack (see below)
pnpm -C packages/db run db:studio Prisma Studio
pnpm -C packages/db run db:reset Drop schema, migrate, seed

Testing

Unit tests live next to the code they cover (*.test.ts). The API covers services, lib/ helpers, and the analytics aggregator (tested with an in-memory Tx double). Shared covers every zod contract.

End-to-end tests in apps/e2e/ run Playwright against a live stack and skip themselves unless TEST_USERNAME and TEST_PASSWORD are set:

pnpm dev                                         # terminal 1

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

.gitea/workflows/ci.yaml runs on every push and PR:

  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 a workflow artifact.

A second Playwright job is gated by repo variable ENABLE_E2E=true and needs secrets E2E_BASE_URL, E2E_USERNAME, E2E_PASSWORD.

Dependency updates come from self-hosted Renovate — 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 — errors are { code, message, requestId, details? }; paginated lists are { data, page, pageSize, total }.
  • Validation — every request body and query is parsed through a zod schema from @vector/shared before reaching a controller. No ad-hoc validation inside route handlers.
  • Query keys — hierarchical factory at apps/web/src/lib/queryKeys.ts. Invalidate by domain (queryKeys.parts.all) or by filter set (queryKeys.parts.list(filters)).
  • CommitsConventional Commits. Renovate already expects them.
S
Description
Serialized hardware inventory for engineering fleets
Readme 486 KiB
Languages
TypeScript 98.6%
CSS 0.5%
Dockerfile 0.5%
JavaScript 0.3%