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>
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 underSite → Room → Binlocations or on aHost. - Every state change writes a
PartEventin the same transaction — nothing mutates silently. PartModels are grouped byManufacturerandCategory, with optional EOL dates that feed dashboard risk widgets.Tags andSavedViews for cross-cutting organization and reusable filters.
Field workflow
FMtickets track active issues per host, with attached repairs and close times.Repairlogs 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), headersx-vector-signature,x-vector-timestamp,x-vector-event, plus ax-vector-webhook: v1recursion 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
- API: http://localhost:3001
- Web: http://localhost:5173 (proxies
/api→ API) - Default credentials:
admin/admin— change them immediately.
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:
pnpm install --frozen-lockfileprisma generatepnpm lintpnpm typecheckpnpm -C packages/shared test+pnpm -C apps/api test:coveragepnpm build- 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/sharedbefore 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)). - Commits — Conventional Commits. Renovate already expects them.