Major Enhancement: NestJS Migration + CASL Authorization + Error Handling
Some checks failed
CI/CD Pipeline / Backend Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Tests (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled

Complete rewrite from Express to NestJS with enterprise-grade features:

## Backend Improvements
- Migrated from Express to NestJS 11.0.1 with TypeScript
- Implemented Prisma ORM 7.3.0 for type-safe database access
- Added CASL authorization system replacing role-based guards
- Created global exception filters with structured logging
- Implemented Auth0 JWT authentication with Passport.js
- Added vehicle management with conflict detection
- Enhanced event scheduling with driver/vehicle assignment
- Comprehensive error handling and logging

## Frontend Improvements
- Upgraded to React 19.2.0 with Vite 7.2.4
- Implemented CASL-based permission system
- Added AbilityContext for declarative permissions
- Created ErrorHandler utility for consistent error messages
- Enhanced API client with request/response logging
- Added War Room (Command Center) dashboard
- Created VIP Schedule view with complete itineraries
- Implemented Vehicle Management UI
- Added mock data generators for testing (288 events across 20 VIPs)

## New Features
- Vehicle fleet management (types, capacity, status tracking)
- Complete 3-day Jamboree schedule generation
- Individual VIP schedule pages with PDF export (planned)
- Real-time War Room dashboard with auto-refresh
- Permission-based navigation filtering
- First user auto-approval as administrator

## Documentation
- Created CASL_AUTHORIZATION.md (comprehensive guide)
- Created ERROR_HANDLING.md (error handling patterns)
- Updated CLAUDE.md with new architecture
- Added migration guides and best practices

## Technical Debt Resolved
- Removed custom authentication in favor of Auth0
- Replaced role checks with CASL abilities
- Standardized error responses across API
- Implemented proper TypeScript typing
- Added comprehensive logging

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-31 08:50:25 +01:00
parent 8ace1ab2c1
commit 868f7efc23
351 changed files with 44997 additions and 6276 deletions

View File

@@ -0,0 +1,6 @@
import { Request, Response, NextFunction } from 'express';
import { AppError } from '../types/errors';
export declare const errorHandler: (err: Error | AppError, req: Request, res: Response, next: NextFunction) => void;
export declare const asyncHandler: (fn: Function) => (req: Request, res: Response, next: NextFunction) => void;
export declare const notFoundHandler: (req: Request, res: Response) => void;
//# sourceMappingURL=errorHandler.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../src/middleware/errorHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAiB,MAAM,iBAAiB,CAAC;AAE1D,eAAO,MAAM,YAAY,GACvB,KAAK,KAAK,GAAG,QAAQ,EACrB,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,YAAY,KACjB,IA+CF,CAAC;AAGF,eAAO,MAAM,YAAY,GAAI,IAAI,QAAQ,MAC/B,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,SAGxD,CAAC;AAGF,eAAO,MAAM,eAAe,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,KAAG,IAY7D,CAAC"}

View File

@@ -0,0 +1,75 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.notFoundHandler = exports.asyncHandler = exports.errorHandler = void 0;
const errors_1 = require("../types/errors");
const errorHandler = (err, req, res, next) => {
// Default error values
let statusCode = 500;
let message = 'Internal server error';
let isOperational = false;
// If it's an AppError, use its properties
if (err instanceof errors_1.AppError) {
statusCode = err.statusCode;
message = err.message;
isOperational = err.isOperational;
}
else if (err.name === 'ValidationError') {
// Handle validation errors (e.g., from libraries)
statusCode = 400;
message = err.message;
isOperational = true;
}
else if (err.name === 'JsonWebTokenError') {
statusCode = 401;
message = 'Invalid token';
isOperational = true;
}
else if (err.name === 'TokenExpiredError') {
statusCode = 401;
message = 'Token expired';
isOperational = true;
}
// Log error details (in production, use proper logging service)
if (!isOperational) {
console.error('ERROR 💥:', err);
}
else {
console.error(`Operational error: ${message}`);
}
// Create error response
const errorResponse = {
success: false,
error: {
message,
...(process.env.NODE_ENV === 'development' && {
details: err.stack
})
},
timestamp: new Date().toISOString(),
path: req.path
};
res.status(statusCode).json(errorResponse);
};
exports.errorHandler = errorHandler;
// Async error wrapper to catch errors in async route handlers
const asyncHandler = (fn) => {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
};
exports.asyncHandler = asyncHandler;
// 404 Not Found handler
const notFoundHandler = (req, res) => {
const errorResponse = {
success: false,
error: {
message: `Route ${req.originalUrl} not found`,
code: 'ROUTE_NOT_FOUND'
},
timestamp: new Date().toISOString(),
path: req.path
};
res.status(404).json(errorResponse);
};
exports.notFoundHandler = notFoundHandler;
//# sourceMappingURL=errorHandler.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"errorHandler.js","sourceRoot":"","sources":["../../src/middleware/errorHandler.ts"],"names":[],"mappings":";;;AACA,4CAA0D;AAEnD,MAAM,YAAY,GAAG,CAC1B,GAAqB,EACrB,GAAY,EACZ,GAAa,EACb,IAAkB,EACZ,EAAE;IACR,uBAAuB;IACvB,IAAI,UAAU,GAAG,GAAG,CAAC;IACrB,IAAI,OAAO,GAAG,uBAAuB,CAAC;IACtC,IAAI,aAAa,GAAG,KAAK,CAAC;IAE1B,0CAA0C;IAC1C,IAAI,GAAG,YAAY,iBAAQ,EAAE,CAAC;QAC5B,UAAU,GAAG,GAAG,CAAC,UAAU,CAAC;QAC5B,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;QACtB,aAAa,GAAG,GAAG,CAAC,aAAa,CAAC;IACpC,CAAC;SAAM,IAAI,GAAG,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;QAC1C,kDAAkD;QAClD,UAAU,GAAG,GAAG,CAAC;QACjB,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;QACtB,aAAa,GAAG,IAAI,CAAC;IACvB,CAAC;SAAM,IAAI,GAAG,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;QAC5C,UAAU,GAAG,GAAG,CAAC;QACjB,OAAO,GAAG,eAAe,CAAC;QAC1B,aAAa,GAAG,IAAI,CAAC;IACvB,CAAC;SAAM,IAAI,GAAG,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;QAC5C,UAAU,GAAG,GAAG,CAAC;QACjB,OAAO,GAAG,eAAe,CAAC;QAC1B,aAAa,GAAG,IAAI,CAAC;IACvB,CAAC;IAED,gEAAgE;IAChE,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;IAClC,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,CAAC,sBAAsB,OAAO,EAAE,CAAC,CAAC;IACjD,CAAC;IAED,wBAAwB;IACxB,MAAM,aAAa,GAAkB;QACnC,OAAO,EAAE,KAAK;QACd,KAAK,EAAE;YACL,OAAO;YACP,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,IAAI;gBAC5C,OAAO,EAAE,GAAG,CAAC,KAAK;aACnB,CAAC;SACH;QACD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,IAAI,EAAE,GAAG,CAAC,IAAI;KACf,CAAC;IAEF,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AAC7C,CAAC,CAAC;AApDW,QAAA,YAAY,gBAoDvB;AAEF,8DAA8D;AACvD,MAAM,YAAY,GAAG,CAAC,EAAY,EAAE,EAAE;IAC3C,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;QACzD,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClD,CAAC,CAAC;AACJ,CAAC,CAAC;AAJW,QAAA,YAAY,gBAIvB;AAEF,wBAAwB;AACjB,MAAM,eAAe,GAAG,CAAC,GAAY,EAAE,GAAa,EAAQ,EAAE;IACnE,MAAM,aAAa,GAAkB;QACnC,OAAO,EAAE,KAAK;QACd,KAAK,EAAE;YACL,OAAO,EAAE,SAAS,GAAG,CAAC,WAAW,YAAY;YAC7C,IAAI,EAAE,iBAAiB;SACxB;QACD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,IAAI,EAAE,GAAG,CAAC,IAAI;KACf,CAAC;IAEF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AACtC,CAAC,CAAC;AAZW,QAAA,eAAe,mBAY1B"}

View File

@@ -0,0 +1,15 @@
import { Request, Response, NextFunction } from 'express';
declare module 'express' {
interface Request {
requestId?: string;
user?: {
id: string;
email: string;
name: string;
role: string;
};
}
}
export declare const requestLogger: (req: Request, res: Response, next: NextFunction) => void;
export declare const errorLogger: (err: Error, req: Request, res: Response, next: NextFunction) => void;
//# sourceMappingURL=logger.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/middleware/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAa1D,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,OAAO;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,IAAI,CAAC,EAAE;YACL,EAAE,EAAE,MAAM,CAAC;YACX,KAAK,EAAE,MAAM,CAAC;YACd,IAAI,EAAE,MAAM,CAAC;YACb,IAAI,EAAE,MAAM,CAAC;SACd,CAAC;KACH;CACF;AAQD,eAAO,MAAM,aAAa,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,KAAG,IAkC/E,CAAC;AAGF,eAAO,MAAM,WAAW,GAAI,KAAK,KAAK,EAAE,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,KAAG,IAmBzF,CAAC"}

View File

@@ -0,0 +1,58 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.errorLogger = exports.requestLogger = void 0;
// Generate a simple request ID
const generateRequestId = () => {
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
};
// Request logger middleware
const requestLogger = (req, res, next) => {
const requestId = generateRequestId();
// Attach request ID to request object
req.requestId = requestId;
const startTime = Date.now();
// Log request
const logContext = {
requestId,
method: req.method,
url: req.originalUrl,
ip: req.ip || 'unknown',
userAgent: req.get('user-agent'),
userId: req.user?.id
};
console.log(`[${new Date().toISOString()}] REQUEST:`, JSON.stringify(logContext));
// Log response
const originalSend = res.send;
res.send = function (data) {
const duration = Date.now() - startTime;
console.log(`[${new Date().toISOString()}] RESPONSE:`, JSON.stringify({
requestId,
statusCode: res.statusCode,
duration: `${duration}ms`
}));
return originalSend.call(this, data);
};
next();
};
exports.requestLogger = requestLogger;
// Error logger (to be used before error handler)
const errorLogger = (err, req, res, next) => {
const requestId = req.requestId || 'unknown';
console.error(`[${new Date().toISOString()}] ERROR:`, JSON.stringify({
requestId,
error: {
name: err.name,
message: err.message,
stack: err.stack
},
request: {
method: req.method,
url: req.originalUrl,
headers: req.headers,
body: req.body
}
}));
next(err);
};
exports.errorLogger = errorLogger;
//# sourceMappingURL=logger.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/middleware/logger.ts"],"names":[],"mappings":";;;AAyBA,+BAA+B;AAC/B,MAAM,iBAAiB,GAAG,GAAW,EAAE;IACrC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AACpE,CAAC,CAAC;AAEF,4BAA4B;AACrB,MAAM,aAAa,GAAG,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAQ,EAAE;IACrF,MAAM,SAAS,GAAG,iBAAiB,EAAE,CAAC;IAEtC,sCAAsC;IACtC,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC;IAE1B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE7B,cAAc;IACd,MAAM,UAAU,GAAe;QAC7B,SAAS;QACT,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,GAAG,EAAE,GAAG,CAAC,WAAW;QACpB,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,SAAS;QACvB,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC;QAChC,MAAM,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE;KACrB,CAAC;IAEF,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC;IAElF,eAAe;IACf,MAAM,YAAY,GAAG,GAAG,CAAC,IAAI,CAAC;IAC9B,GAAG,CAAC,IAAI,GAAG,UAAS,IAAa;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC;YACpE,SAAS;YACT,UAAU,EAAE,GAAG,CAAC,UAAU;YAC1B,QAAQ,EAAE,GAAG,QAAQ,IAAI;SAC1B,CAAC,CAAC,CAAC;QAEJ,OAAO,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC,CAAC;IAEF,IAAI,EAAE,CAAC;AACT,CAAC,CAAC;AAlCW,QAAA,aAAa,iBAkCxB;AAEF,iDAAiD;AAC1C,MAAM,WAAW,GAAG,CAAC,GAAU,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAQ,EAAE;IAC/F,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,IAAI,SAAS,CAAC;IAE7C,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC;QACnE,SAAS;QACT,KAAK,EAAE;YACL,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,KAAK,EAAE,GAAG,CAAC,KAAK;SACjB;QACD,OAAO,EAAE;YACP,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,GAAG,EAAE,GAAG,CAAC,WAAW;YACpB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,IAAI,EAAE,GAAG,CAAC,IAAI;SACf;KACF,CAAC,CAAC,CAAC;IAEJ,IAAI,CAAC,GAAG,CAAC,CAAC;AACZ,CAAC,CAAC;AAnBW,QAAA,WAAW,eAmBtB"}

View File

@@ -0,0 +1,197 @@
import { z } from 'zod';
import { Request, Response, NextFunction } from 'express';
export declare const schemas: {
createVip: z.ZodObject<{
name: z.ZodString;
organization: z.ZodOptional<z.ZodString>;
department: z.ZodDefault<z.ZodEnum<["Office of Development", "Admin"]>>;
transportMode: z.ZodDefault<z.ZodEnum<["flight", "self-driving"]>>;
flights: z.ZodOptional<z.ZodArray<z.ZodObject<{
flightNumber: z.ZodString;
airline: z.ZodOptional<z.ZodString>;
scheduledArrival: z.ZodString;
scheduledDeparture: z.ZodOptional<z.ZodString>;
}, "strip", z.ZodTypeAny, {
flightNumber: string;
scheduledArrival: string;
airline?: string | undefined;
scheduledDeparture?: string | undefined;
}, {
flightNumber: string;
scheduledArrival: string;
airline?: string | undefined;
scheduledDeparture?: string | undefined;
}>, "many">>;
expectedArrival: z.ZodOptional<z.ZodString>;
needsAirportPickup: z.ZodDefault<z.ZodBoolean>;
needsVenueTransport: z.ZodDefault<z.ZodBoolean>;
notes: z.ZodOptional<z.ZodString>;
}, "strip", z.ZodTypeAny, {
name: string;
department: "Office of Development" | "Admin";
transportMode: "flight" | "self-driving";
needsAirportPickup: boolean;
needsVenueTransport: boolean;
organization?: string | undefined;
flights?: {
flightNumber: string;
scheduledArrival: string;
airline?: string | undefined;
scheduledDeparture?: string | undefined;
}[] | undefined;
expectedArrival?: string | undefined;
notes?: string | undefined;
}, {
name: string;
organization?: string | undefined;
department?: "Office of Development" | "Admin" | undefined;
transportMode?: "flight" | "self-driving" | undefined;
flights?: {
flightNumber: string;
scheduledArrival: string;
airline?: string | undefined;
scheduledDeparture?: string | undefined;
}[] | undefined;
expectedArrival?: string | undefined;
needsAirportPickup?: boolean | undefined;
needsVenueTransport?: boolean | undefined;
notes?: string | undefined;
}>;
updateVip: z.ZodObject<{
name: z.ZodOptional<z.ZodString>;
organization: z.ZodOptional<z.ZodString>;
department: z.ZodOptional<z.ZodEnum<["Office of Development", "Admin"]>>;
transportMode: z.ZodOptional<z.ZodEnum<["flight", "self-driving"]>>;
flights: z.ZodOptional<z.ZodArray<z.ZodObject<{
flightNumber: z.ZodString;
airline: z.ZodOptional<z.ZodString>;
scheduledArrival: z.ZodString;
scheduledDeparture: z.ZodOptional<z.ZodString>;
}, "strip", z.ZodTypeAny, {
flightNumber: string;
scheduledArrival: string;
airline?: string | undefined;
scheduledDeparture?: string | undefined;
}, {
flightNumber: string;
scheduledArrival: string;
airline?: string | undefined;
scheduledDeparture?: string | undefined;
}>, "many">>;
expectedArrival: z.ZodOptional<z.ZodString>;
needsAirportPickup: z.ZodOptional<z.ZodBoolean>;
needsVenueTransport: z.ZodOptional<z.ZodBoolean>;
notes: z.ZodOptional<z.ZodString>;
}, "strip", z.ZodTypeAny, {
name?: string | undefined;
organization?: string | undefined;
department?: "Office of Development" | "Admin" | undefined;
transportMode?: "flight" | "self-driving" | undefined;
flights?: {
flightNumber: string;
scheduledArrival: string;
airline?: string | undefined;
scheduledDeparture?: string | undefined;
}[] | undefined;
expectedArrival?: string | undefined;
needsAirportPickup?: boolean | undefined;
needsVenueTransport?: boolean | undefined;
notes?: string | undefined;
}, {
name?: string | undefined;
organization?: string | undefined;
department?: "Office of Development" | "Admin" | undefined;
transportMode?: "flight" | "self-driving" | undefined;
flights?: {
flightNumber: string;
scheduledArrival: string;
airline?: string | undefined;
scheduledDeparture?: string | undefined;
}[] | undefined;
expectedArrival?: string | undefined;
needsAirportPickup?: boolean | undefined;
needsVenueTransport?: boolean | undefined;
notes?: string | undefined;
}>;
createDriver: z.ZodObject<{
name: z.ZodString;
email: z.ZodOptional<z.ZodString>;
phone: z.ZodString;
vehicleInfo: z.ZodOptional<z.ZodString>;
status: z.ZodDefault<z.ZodEnum<["available", "assigned", "unavailable"]>>;
}, "strip", z.ZodTypeAny, {
name: string;
phone: string;
status: "available" | "assigned" | "unavailable";
email?: string | undefined;
vehicleInfo?: string | undefined;
}, {
name: string;
phone: string;
email?: string | undefined;
vehicleInfo?: string | undefined;
status?: "available" | "assigned" | "unavailable" | undefined;
}>;
updateDriver: z.ZodObject<{
name: z.ZodOptional<z.ZodString>;
email: z.ZodOptional<z.ZodString>;
phone: z.ZodOptional<z.ZodString>;
vehicleInfo: z.ZodOptional<z.ZodString>;
status: z.ZodOptional<z.ZodEnum<["available", "assigned", "unavailable"]>>;
}, "strip", z.ZodTypeAny, {
name?: string | undefined;
email?: string | undefined;
phone?: string | undefined;
vehicleInfo?: string | undefined;
status?: "available" | "assigned" | "unavailable" | undefined;
}, {
name?: string | undefined;
email?: string | undefined;
phone?: string | undefined;
vehicleInfo?: string | undefined;
status?: "available" | "assigned" | "unavailable" | undefined;
}>;
createScheduleEvent: z.ZodObject<{
driverId: z.ZodOptional<z.ZodString>;
eventTime: z.ZodString;
eventType: z.ZodEnum<["pickup", "dropoff", "custom"]>;
location: z.ZodString;
notes: z.ZodOptional<z.ZodString>;
}, "strip", z.ZodTypeAny, {
eventTime: string;
eventType: "custom" | "pickup" | "dropoff";
location: string;
notes?: string | undefined;
driverId?: string | undefined;
}, {
eventTime: string;
eventType: "custom" | "pickup" | "dropoff";
location: string;
notes?: string | undefined;
driverId?: string | undefined;
}>;
updateScheduleEvent: z.ZodObject<{
driverId: z.ZodOptional<z.ZodString>;
eventTime: z.ZodOptional<z.ZodString>;
eventType: z.ZodOptional<z.ZodEnum<["pickup", "dropoff", "custom"]>>;
location: z.ZodOptional<z.ZodString>;
notes: z.ZodOptional<z.ZodString>;
status: z.ZodOptional<z.ZodEnum<["scheduled", "in_progress", "completed", "cancelled"]>>;
}, "strip", z.ZodTypeAny, {
notes?: string | undefined;
status?: "scheduled" | "in_progress" | "completed" | "cancelled" | undefined;
driverId?: string | undefined;
eventTime?: string | undefined;
eventType?: "custom" | "pickup" | "dropoff" | undefined;
location?: string | undefined;
}, {
notes?: string | undefined;
status?: "scheduled" | "in_progress" | "completed" | "cancelled" | undefined;
driverId?: string | undefined;
eventTime?: string | undefined;
eventType?: "custom" | "pickup" | "dropoff" | undefined;
location?: string | undefined;
}>;
};
export declare const validate: (schema: z.ZodSchema) => (req: Request, res: Response, next: NextFunction) => Promise<Response<any, Record<string, any>> | undefined>;
//# sourceMappingURL=simpleValidation.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"simpleValidation.d.ts","sourceRoot":"","sources":["../../src/middleware/simpleValidation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG1D,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsEnB,CAAC;AAGF,eAAO,MAAM,QAAQ,GAAI,QAAQ,CAAC,CAAC,SAAS,MAC5B,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,4DAc9D,CAAC"}

View File

@@ -0,0 +1,91 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.validate = exports.schemas = void 0;
const zod_1 = require("zod");
// Simplified validation schemas - removed unnecessary complexity
exports.schemas = {
// VIP schemas
createVip: zod_1.z.object({
name: zod_1.z.string().min(1).max(100),
organization: zod_1.z.string().max(100).optional(),
department: zod_1.z.enum(['Office of Development', 'Admin']).default('Office of Development'),
transportMode: zod_1.z.enum(['flight', 'self-driving']).default('flight'),
flights: zod_1.z.array(zod_1.z.object({
flightNumber: zod_1.z.string(),
airline: zod_1.z.string().optional(),
scheduledArrival: zod_1.z.string(),
scheduledDeparture: zod_1.z.string().optional()
})).optional(),
expectedArrival: zod_1.z.string().optional(),
needsAirportPickup: zod_1.z.boolean().default(true),
needsVenueTransport: zod_1.z.boolean().default(true),
notes: zod_1.z.string().max(500).optional()
}),
updateVip: zod_1.z.object({
name: zod_1.z.string().min(1).max(100).optional(),
organization: zod_1.z.string().max(100).optional(),
department: zod_1.z.enum(['Office of Development', 'Admin']).optional(),
transportMode: zod_1.z.enum(['flight', 'self-driving']).optional(),
flights: zod_1.z.array(zod_1.z.object({
flightNumber: zod_1.z.string(),
airline: zod_1.z.string().optional(),
scheduledArrival: zod_1.z.string(),
scheduledDeparture: zod_1.z.string().optional()
})).optional(),
expectedArrival: zod_1.z.string().optional(),
needsAirportPickup: zod_1.z.boolean().optional(),
needsVenueTransport: zod_1.z.boolean().optional(),
notes: zod_1.z.string().max(500).optional()
}),
// Driver schemas
createDriver: zod_1.z.object({
name: zod_1.z.string().min(1).max(100),
email: zod_1.z.string().email().optional(),
phone: zod_1.z.string(),
vehicleInfo: zod_1.z.string().max(200).optional(),
status: zod_1.z.enum(['available', 'assigned', 'unavailable']).default('available')
}),
updateDriver: zod_1.z.object({
name: zod_1.z.string().min(1).max(100).optional(),
email: zod_1.z.string().email().optional(),
phone: zod_1.z.string().optional(),
vehicleInfo: zod_1.z.string().max(200).optional(),
status: zod_1.z.enum(['available', 'assigned', 'unavailable']).optional()
}),
// Schedule schemas
createScheduleEvent: zod_1.z.object({
driverId: zod_1.z.string().optional(),
eventTime: zod_1.z.string(),
eventType: zod_1.z.enum(['pickup', 'dropoff', 'custom']),
location: zod_1.z.string().min(1).max(200),
notes: zod_1.z.string().max(500).optional()
}),
updateScheduleEvent: zod_1.z.object({
driverId: zod_1.z.string().optional(),
eventTime: zod_1.z.string().optional(),
eventType: zod_1.z.enum(['pickup', 'dropoff', 'custom']).optional(),
location: zod_1.z.string().min(1).max(200).optional(),
notes: zod_1.z.string().max(500).optional(),
status: zod_1.z.enum(['scheduled', 'in_progress', 'completed', 'cancelled']).optional()
})
};
// Single validation middleware
const validate = (schema) => {
return async (req, res, next) => {
try {
req.body = await schema.parseAsync(req.body);
next();
}
catch (error) {
if (error instanceof zod_1.z.ZodError) {
const message = error.errors
.map(err => `${err.path.join('.')}: ${err.message}`)
.join(', ');
return res.status(400).json({ error: message });
}
next(error);
}
};
};
exports.validate = validate;
//# sourceMappingURL=simpleValidation.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"simpleValidation.js","sourceRoot":"","sources":["../../src/middleware/simpleValidation.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,6 @@
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
export declare const validate: (schema: z.ZodSchema) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
export declare const validateQuery: (schema: z.ZodSchema) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
export declare const validateParams: (schema: z.ZodSchema) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
//# sourceMappingURL=validation.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/middleware/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,CAAC,EAAY,MAAM,KAAK,CAAC;AAGlC,eAAO,MAAM,QAAQ,GAAI,QAAQ,CAAC,CAAC,SAAS,MAC5B,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,kBAqB9D,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,QAAQ,CAAC,CAAC,SAAS,MACjC,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,kBAqB9D,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,QAAQ,CAAC,CAAC,SAAS,MAClC,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,kBAqB9D,CAAC"}

View File

@@ -0,0 +1,78 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateParams = exports.validateQuery = exports.validate = void 0;
const zod_1 = require("zod");
const errors_1 = require("../types/errors");
const validate = (schema) => {
return async (req, res, next) => {
try {
// Validate request body
req.body = await schema.parseAsync(req.body);
next();
}
catch (error) {
if (error instanceof zod_1.ZodError) {
// Format Zod errors into a user-friendly message
const errors = error.errors.map(err => ({
field: err.path.join('.'),
message: err.message
}));
const message = errors.map(e => `${e.field}: ${e.message}`).join(', ');
next(new errors_1.ValidationError(message));
}
else {
next(error);
}
}
};
};
exports.validate = validate;
const validateQuery = (schema) => {
return async (req, res, next) => {
try {
// Validate query parameters
req.query = await schema.parseAsync(req.query);
next();
}
catch (error) {
if (error instanceof zod_1.ZodError) {
// Format Zod errors into a user-friendly message
const errors = error.errors.map(err => ({
field: err.path.join('.'),
message: err.message
}));
const message = errors.map(e => `${e.field}: ${e.message}`).join(', ');
next(new errors_1.ValidationError(`Invalid query parameters: ${message}`));
}
else {
next(error);
}
}
};
};
exports.validateQuery = validateQuery;
const validateParams = (schema) => {
return async (req, res, next) => {
try {
// Validate route parameters
req.params = await schema.parseAsync(req.params);
next();
}
catch (error) {
if (error instanceof zod_1.ZodError) {
// Format Zod errors into a user-friendly message
const errors = error.errors.map(err => ({
field: err.path.join('.'),
message: err.message
}));
const message = errors.map(e => `${e.field}: ${e.message}`).join(', ');
next(new errors_1.ValidationError(`Invalid route parameters: ${message}`));
}
else {
next(error);
}
}
};
};
exports.validateParams = validateParams;
//# sourceMappingURL=validation.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/middleware/validation.ts"],"names":[],"mappings":"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"}