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
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:
219
backend-old-20260125/dist/services/flightTrackingScheduler.js
vendored
Normal file
219
backend-old-20260125/dist/services/flightTrackingScheduler.js
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
"use strict";
|
||||
// Flight Tracking Scheduler Service
|
||||
// Efficiently batches flight API calls and manages tracking schedules
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
class FlightTrackingScheduler {
|
||||
constructor(flightService) {
|
||||
this.trackingSchedule = {};
|
||||
this.checkIntervals = new Map();
|
||||
this.flightService = flightService;
|
||||
}
|
||||
// Add flights for a VIP to the tracking schedule
|
||||
addVipFlights(vipId, vipName, flights) {
|
||||
flights.forEach(flight => {
|
||||
const key = flight.flightDate;
|
||||
if (!this.trackingSchedule[key]) {
|
||||
this.trackingSchedule[key] = [];
|
||||
}
|
||||
// Check if this flight is already being tracked
|
||||
const existingIndex = this.trackingSchedule[key].findIndex(f => f.flightNumber === flight.flightNumber && f.vipId === vipId);
|
||||
const scheduledFlight = {
|
||||
vipId,
|
||||
vipName,
|
||||
flightNumber: flight.flightNumber,
|
||||
flightDate: flight.flightDate,
|
||||
segment: flight.segment,
|
||||
scheduledDeparture: flight.validationData?.departure?.scheduled
|
||||
};
|
||||
if (existingIndex >= 0) {
|
||||
// Update existing entry
|
||||
this.trackingSchedule[key][existingIndex] = scheduledFlight;
|
||||
}
|
||||
else {
|
||||
// Add new entry
|
||||
this.trackingSchedule[key].push(scheduledFlight);
|
||||
}
|
||||
});
|
||||
// Start or update tracking for affected dates
|
||||
this.updateTrackingSchedules();
|
||||
}
|
||||
// Remove VIP flights from tracking
|
||||
removeVipFlights(vipId) {
|
||||
Object.keys(this.trackingSchedule).forEach(date => {
|
||||
this.trackingSchedule[date] = this.trackingSchedule[date].filter(f => f.vipId !== vipId);
|
||||
// Remove empty dates
|
||||
if (this.trackingSchedule[date].length === 0) {
|
||||
delete this.trackingSchedule[date];
|
||||
}
|
||||
});
|
||||
this.updateTrackingSchedules();
|
||||
}
|
||||
// Update tracking schedules based on current flights
|
||||
updateTrackingSchedules() {
|
||||
// Clear existing intervals
|
||||
this.checkIntervals.forEach(interval => clearInterval(interval));
|
||||
this.checkIntervals.clear();
|
||||
// Set up tracking for each date
|
||||
Object.keys(this.trackingSchedule).forEach(date => {
|
||||
this.setupDateTracking(date);
|
||||
});
|
||||
}
|
||||
// Set up tracking for a specific date
|
||||
setupDateTracking(date) {
|
||||
const flights = this.trackingSchedule[date];
|
||||
if (!flights || flights.length === 0)
|
||||
return;
|
||||
// Check if we should start tracking (4 hours before first flight)
|
||||
const now = new Date();
|
||||
const dateObj = new Date(date + 'T00:00:00');
|
||||
// Find earliest departure time
|
||||
let earliestDeparture = null;
|
||||
flights.forEach(flight => {
|
||||
if (flight.scheduledDeparture) {
|
||||
const depTime = new Date(flight.scheduledDeparture);
|
||||
if (!earliestDeparture || depTime < earliestDeparture) {
|
||||
earliestDeparture = depTime;
|
||||
}
|
||||
}
|
||||
});
|
||||
// If no departure times, assume noon
|
||||
if (!earliestDeparture) {
|
||||
earliestDeparture = new Date(date + 'T12:00:00');
|
||||
}
|
||||
// Start tracking 4 hours before earliest departure
|
||||
const trackingStartTime = new Date(earliestDeparture.getTime() - 4 * 60 * 60 * 1000);
|
||||
// If tracking should have started, begin immediately
|
||||
if (now >= trackingStartTime) {
|
||||
this.performBatchCheck(date);
|
||||
// Set up recurring checks every 60 minutes (or 30 if any delays)
|
||||
const interval = setInterval(() => {
|
||||
this.performBatchCheck(date);
|
||||
}, 60 * 60 * 1000); // 60 minutes
|
||||
this.checkIntervals.set(date, interval);
|
||||
}
|
||||
else {
|
||||
// Schedule first check for tracking start time
|
||||
const timeUntilStart = trackingStartTime.getTime() - now.getTime();
|
||||
setTimeout(() => {
|
||||
this.performBatchCheck(date);
|
||||
// Then set up recurring checks
|
||||
const interval = setInterval(() => {
|
||||
this.performBatchCheck(date);
|
||||
}, 60 * 60 * 1000);
|
||||
this.checkIntervals.set(date, interval);
|
||||
}, timeUntilStart);
|
||||
}
|
||||
}
|
||||
// Perform batch check for all flights on a date
|
||||
async performBatchCheck(date) {
|
||||
const flights = this.trackingSchedule[date];
|
||||
if (!flights || flights.length === 0)
|
||||
return;
|
||||
console.log(`\n=== Batch Flight Check for ${date} ===`);
|
||||
console.log(`Checking ${flights.length} flights...`);
|
||||
// Filter out flights that have already landed
|
||||
const activeFlights = flights.filter(f => !f.hasLanded);
|
||||
if (activeFlights.length === 0) {
|
||||
console.log('All flights have landed. Stopping tracking for this date.');
|
||||
this.stopDateTracking(date);
|
||||
return;
|
||||
}
|
||||
// Get unique flight numbers to check
|
||||
const uniqueFlights = Array.from(new Set(activeFlights.map(f => f.flightNumber)));
|
||||
console.log(`Unique flight numbers to check: ${uniqueFlights.join(', ')}`);
|
||||
try {
|
||||
// Make batch API call
|
||||
const flightParams = uniqueFlights.map(flightNumber => ({
|
||||
flightNumber,
|
||||
date
|
||||
}));
|
||||
const results = await this.flightService.getMultipleFlights(flightParams);
|
||||
// Update flight statuses
|
||||
let hasDelays = false;
|
||||
let allLanded = true;
|
||||
activeFlights.forEach(flight => {
|
||||
const key = `${flight.flightNumber}_${date}`;
|
||||
const data = results[key];
|
||||
if (data) {
|
||||
flight.lastChecked = new Date();
|
||||
flight.status = data.status;
|
||||
if (data.status === 'landed') {
|
||||
flight.hasLanded = true;
|
||||
console.log(`✅ ${flight.flightNumber} has landed`);
|
||||
}
|
||||
else {
|
||||
allLanded = false;
|
||||
if (data.delay && data.delay > 0) {
|
||||
hasDelays = true;
|
||||
console.log(`⚠️ ${flight.flightNumber} is delayed by ${data.delay} minutes`);
|
||||
}
|
||||
}
|
||||
// Log status for each VIP
|
||||
console.log(` VIP: ${flight.vipName} - Flight ${flight.segment}: ${flight.flightNumber} - Status: ${data.status}`);
|
||||
}
|
||||
});
|
||||
// Update check frequency if delays detected
|
||||
if (hasDelays && this.checkIntervals.has(date)) {
|
||||
console.log('Delays detected - increasing check frequency to 30 minutes');
|
||||
clearInterval(this.checkIntervals.get(date));
|
||||
const interval = setInterval(() => {
|
||||
this.performBatchCheck(date);
|
||||
}, 30 * 60 * 1000); // 30 minutes
|
||||
this.checkIntervals.set(date, interval);
|
||||
}
|
||||
// Stop tracking if all flights have landed
|
||||
if (allLanded) {
|
||||
console.log('All flights have landed. Stopping tracking for this date.');
|
||||
this.stopDateTracking(date);
|
||||
}
|
||||
// Calculate next check time
|
||||
const nextCheckTime = new Date(Date.now() + (hasDelays ? 30 : 60) * 60 * 1000);
|
||||
console.log(`Next check scheduled for: ${nextCheckTime.toLocaleTimeString()}`);
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error performing batch flight check:', error);
|
||||
}
|
||||
}
|
||||
// Stop tracking for a specific date
|
||||
stopDateTracking(date) {
|
||||
const interval = this.checkIntervals.get(date);
|
||||
if (interval) {
|
||||
clearInterval(interval);
|
||||
this.checkIntervals.delete(date);
|
||||
}
|
||||
// Mark all flights as completed
|
||||
if (this.trackingSchedule[date]) {
|
||||
this.trackingSchedule[date].forEach(f => f.hasLanded = true);
|
||||
}
|
||||
}
|
||||
// Get current tracking status
|
||||
getTrackingStatus() {
|
||||
const status = {};
|
||||
Object.entries(this.trackingSchedule).forEach(([date, flights]) => {
|
||||
const activeFlights = flights.filter(f => !f.hasLanded);
|
||||
const landedFlights = flights.filter(f => f.hasLanded);
|
||||
status[date] = {
|
||||
totalFlights: flights.length,
|
||||
activeFlights: activeFlights.length,
|
||||
landedFlights: landedFlights.length,
|
||||
flights: flights.map(f => ({
|
||||
vipName: f.vipName,
|
||||
flightNumber: f.flightNumber,
|
||||
segment: f.segment,
|
||||
status: f.status || 'Not checked yet',
|
||||
lastChecked: f.lastChecked,
|
||||
hasLanded: f.hasLanded
|
||||
}))
|
||||
};
|
||||
});
|
||||
return status;
|
||||
}
|
||||
// Clean up all tracking
|
||||
cleanup() {
|
||||
this.checkIntervals.forEach(interval => clearInterval(interval));
|
||||
this.checkIntervals.clear();
|
||||
this.trackingSchedule = {};
|
||||
}
|
||||
}
|
||||
exports.default = FlightTrackingScheduler;
|
||||
//# sourceMappingURL=flightTrackingScheduler.js.map
|
||||
Reference in New Issue
Block a user