Phase 1a: shared schemas, service layer, server tooling
- shared/schemas/: move Zod schemas out of routes so client + server share them - shared/types.ts: inferred types and enums for cross-package use - server tsconfig rootDir raised to ".." so shared/ compiles in-tree - server/src/services/: ticket, comment, cti, user, auth, notification (stub), search (stub) - Routes thinned to validate-delegate-return; business logic now testable in isolation - server/src/lib/httpError.ts: typed HttpError replaces ad-hoc throw shapes - server/src/lib/logger.ts: pino structured logging replaces console.log - autoClose job delegates to ticketService.closeStale() - express-rate-limit on /api/auth/login (10 / 15min / IP) - vitest + vitest-mock-extended; 20 service-level tests cover auth, ticket, comment, user flows - CI: lint + test jobs before docker builds Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+15
-2
@@ -2,6 +2,8 @@ import 'express-async-errors';
|
||||
import express from 'express';
|
||||
import cors from 'cors';
|
||||
import dotenv from 'dotenv';
|
||||
import pinoHttp from 'pino-http';
|
||||
import rateLimit from 'express-rate-limit';
|
||||
|
||||
import authRoutes from './routes/auth';
|
||||
import ticketRoutes from './routes/tickets';
|
||||
@@ -10,20 +12,31 @@ import userRoutes from './routes/users';
|
||||
import { authenticate } from './middleware/auth';
|
||||
import { errorHandler } from './middleware/errorHandler';
|
||||
import { startAutoCloseJob } from './jobs/autoClose';
|
||||
import { logger } from './lib/logger';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
if (!process.env.JWT_SECRET) {
|
||||
console.error('FATAL: JWT_SECRET is not set');
|
||||
logger.fatal('JWT_SECRET is not set');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(pinoHttp({ logger }));
|
||||
app.use(cors({ origin: process.env.CLIENT_URL || 'http://localhost:5173' }));
|
||||
app.use(express.json());
|
||||
|
||||
const loginLimiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 10,
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
message: { error: 'Too many login attempts. Try again in 15 minutes.' },
|
||||
});
|
||||
|
||||
// Public
|
||||
app.use('/api/auth/login', loginLimiter);
|
||||
app.use('/api/auth', authRoutes);
|
||||
|
||||
// Protected
|
||||
@@ -37,5 +50,5 @@ startAutoCloseJob();
|
||||
|
||||
const PORT = Number(process.env.PORT) || 3000;
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server running on port ${PORT}`);
|
||||
logger.info({ port: PORT }, 'Server started');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user