chore: initial Vector 2.0 monorepo
CI / Lint · Typecheck · Test · Build (push) Failing after 5m41s
CI / Playwright (smoke) (push) Has been skipped

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.
This commit is contained in:
2026-04-16 20:52:32 -04:00
commit 7c0d422228
216 changed files with 19393 additions and 0 deletions
+95
View File
@@ -0,0 +1,95 @@
import express from 'express';
import cookieParser from 'cookie-parser';
import cors from 'cors';
import helmet from 'helmet';
import { pinoHttp } from 'pino-http';
import rateLimit from 'express-rate-limit';
import { prisma } from '@vector/db';
import { env } from './env.js';
import { logger } from './lib/logger.js';
import { requestId } from './middleware/request-id.js';
import { requireCsrf } from './middleware/csrf.js';
import { errorHandler } from './middleware/error.js';
import authRoutes from './routes/auth.js';
import userRoutes from './routes/users.js';
import manufacturerRoutes from './routes/manufacturers.js';
import siteRoutes from './routes/sites.js';
import roomRoutes from './routes/rooms.js';
import binRoutes from './routes/bins.js';
import partRoutes from './routes/parts.js';
import tagRoutes from './routes/tags.js';
import categoryRoutes from './routes/categories.js';
import hostRoutes from './routes/hosts.js';
import repairRoutes from './routes/repairs.js';
import savedViewRoutes from './routes/saved-views.js';
import analyticsRoutes from './routes/analytics.js';
import webhookRoutes from './routes/webhooks.js';
import auditRoutes from './routes/audit.js';
export const app = express();
app.disable('x-powered-by');
app.set('trust proxy', 1);
app.use(helmet({ contentSecurityPolicy: false, crossOriginResourcePolicy: { policy: 'same-site' } }));
app.use(
cors({
origin: env.CLIENT_ORIGIN,
credentials: true,
}),
);
app.use(express.json({ limit: '1mb' }));
app.use(cookieParser());
app.use(requestId);
app.use(
pinoHttp({
logger,
customProps: (req) => ({ requestId: (req as express.Request).requestId }),
customLogLevel: (_req, res, err) => {
if (err || res.statusCode >= 500) return 'error';
if (res.statusCode >= 400) return 'warn';
return 'info';
},
}),
);
app.get('/healthz', (_req, res) => {
res.json({ status: 'ok' });
});
app.get('/readyz', async (_req, res) => {
try {
await prisma.$queryRaw`SELECT 1`;
res.json({ status: 'ok', db: 'ok' });
} catch {
res.status(503).json({ status: 'error', db: 'unreachable' });
}
});
const authLimiter = rateLimit({
windowMs: 60 * 1000,
limit: env.NODE_ENV === 'production' ? 5 : 50,
standardHeaders: 'draft-7',
legacyHeaders: false,
message: { code: 'RATE_LIMITED', message: 'Too many auth requests. Try again soon.' },
});
app.use('/api/auth', authLimiter, authRoutes);
app.use('/api', requireCsrf);
app.use('/api/users', userRoutes);
app.use('/api/manufacturers', manufacturerRoutes);
app.use('/api/sites', siteRoutes);
app.use('/api/rooms', roomRoutes);
app.use('/api/bins', binRoutes);
app.use('/api/parts', partRoutes);
app.use('/api/tags', tagRoutes);
app.use('/api/categories', categoryRoutes);
app.use('/api/hosts', hostRoutes);
app.use('/api/repairs', repairRoutes);
app.use('/api/saved-views', savedViewRoutes);
app.use('/api/analytics', analyticsRoutes);
app.use('/api/admin/webhooks', webhookRoutes);
app.use('/api/admin/audit', auditRoutes);
app.use(errorHandler);