7c0d422228
Ground-up TypeScript rewrite of the Vector hardware parts inventory
system. Ships the full roadmap (Phases 0-8) in one initial commit:
- pnpm + Turbo monorepo: apps/{api,web,e2e}, packages/{db,shared,ui,config}
- Express 5 + Prisma 5 + zod validation + JWT w/ refresh-token rotation
- React 19 + Vite + shadcn/ui + TanStack Query/Table + nuqs URL state
- Repair/RMA, tags, bulk ops, saved views, CSV audit export
- Analytics dashboard on Recharts + EOL tracking
- Signed webhook subscriptions (HMAC-SHA256) with in-process emitter
- Vitest unit tests (shared schemas, api services/helpers) + Playwright skeleton
- Gitea Actions CI (lint, typecheck, test+coverage, build) + Renovate
Deferred follow-ups: Postgres cutover (data-migration script ready),
BullMQ worker for webhook delivery, @react-pdf PDF export, CSV import wizard.
2.3 KiB
2.3 KiB
Postgres-only migrations (apply post-cutover)
Phase 3 locked in the full schema shape on SQLite. When the datasource flips to
postgresql we apply a follow-up migration that upgrades a few columns to
Postgres-native types and adds the Full-Text Search column required by the plan.
1. Part full-text search (tsvector + GIN)
ALTER TABLE "Part"
ADD COLUMN "searchVector" tsvector
GENERATED ALWAYS AS (
setweight(to_tsvector('simple', coalesce("serialNumber", '')), 'A') ||
setweight(to_tsvector('simple', coalesce("mpn", '')), 'B') ||
setweight(to_tsvector('english', coalesce("notes", '')), 'C')
) STORED;
CREATE INDEX "Part_searchVector_idx" ON "Part" USING GIN ("searchVector");
Query shape the API will use for the q= filter on /api/parts:
SELECT * FROM "Part"
WHERE "searchVector" @@ plainto_tsquery('simple', $1)
ORDER BY ts_rank("searchVector", plainto_tsquery('simple', $1)) DESC
LIMIT $2 OFFSET $3;
Expose on the Prisma model with @ignore (Prisma can't represent GENERATED
columns) and read via prisma.$queryRaw inside the parts service.
2. Convert JSON-string columns to native Postgres types
These were stored as String for SQLite portability.
ALTER TABLE "WebhookSubscription"
ALTER COLUMN "events" TYPE text[] USING string_to_array(
trim(both '[]' from "events"), ','
);
ALTER TABLE "SavedView"
ALTER COLUMN "filterJson" TYPE jsonb USING "filterJson"::jsonb;
ALTER TABLE "CsvImportJob"
ALTER COLUMN "errors" TYPE jsonb USING "errors"::jsonb;
After the DDL change, update packages/db/prisma/schema.prisma:
model WebhookSubscription {
// ...
events String[]
}
model SavedView {
// ...
filterJson Json
}
model CsvImportJob {
// ...
errors Json?
}
Regenerate the Prisma client and tighten the zod → Prisma marshaling in the
service layer (drop the JSON.stringify / JSON.parse bridges).
Verification checklist (P3)
prisma migrate deployapplies cleanly against a fresh Postgres snapshot.EXPLAIN ANALYZEon the FTS query shows aBitmap Index ScanonPart_searchVector_idxand returns ranked results in <50ms at 100k rows.- Integration test inserts 3 parts with overlapping serial/mpn tokens, queries
q=..., and asserts order-by-rank.