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:
196
backend-old-20260125/dist/services/flightService.js
vendored
Normal file
196
backend-old-20260125/dist/services/flightService.js
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
"use strict";
|
||||
// Real Flight tracking service with Google scraping
|
||||
// No mock data - only real flight information
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
class FlightService {
|
||||
constructor() {
|
||||
this.flightCache = new Map();
|
||||
this.updateIntervals = new Map();
|
||||
// No API keys needed for Google scraping
|
||||
}
|
||||
// Real flight lookup - no mock data
|
||||
async getFlightInfo(params) {
|
||||
const cacheKey = `${params.flightNumber}_${params.date}`;
|
||||
// Check cache first (shorter cache for real data)
|
||||
const cached = this.flightCache.get(cacheKey);
|
||||
if (cached && cached.expires > Date.now()) {
|
||||
return cached.data;
|
||||
}
|
||||
try {
|
||||
// Try Google scraping first
|
||||
let flightData = await this.scrapeGoogleFlights(params);
|
||||
// If Google fails, try AviationStack (if API key available)
|
||||
if (!flightData) {
|
||||
flightData = await this.getFromAviationStack(params);
|
||||
}
|
||||
// Cache the result for 2 minutes (shorter for real data)
|
||||
if (flightData) {
|
||||
this.flightCache.set(cacheKey, {
|
||||
data: flightData,
|
||||
expires: Date.now() + (2 * 60 * 1000)
|
||||
});
|
||||
}
|
||||
return flightData;
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error fetching flight data:', error);
|
||||
return null; // Return null instead of mock data
|
||||
}
|
||||
}
|
||||
// Google Flights scraping implementation
|
||||
async scrapeGoogleFlights(params) {
|
||||
try {
|
||||
// Google Flights URL format
|
||||
const googleUrl = `https://www.google.com/travel/flights/search?tfs=CBwQAhoeEgoyMDI1LTA3LTAxagcIARIDTEFYcgcIARIDSkZLQAFIAXABggELCP___________wFAAUgBmAEB&hl=en`;
|
||||
// For now, return null to indicate no real scraping implementation
|
||||
// In production, you would implement actual web scraping here
|
||||
console.log(`Would scrape Google for flight ${params.flightNumber} on ${params.date}`);
|
||||
return null;
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Google scraping error:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// AviationStack API integration (only if API key available)
|
||||
async getFromAviationStack(params) {
|
||||
const apiKey = process.env.AVIATIONSTACK_API_KEY;
|
||||
console.log('Checking AviationStack API key:', apiKey ? `Key present (${apiKey.length} chars)` : 'No key');
|
||||
if (!apiKey || apiKey === 'demo_key' || apiKey === '') {
|
||||
console.log('No valid AviationStack API key available');
|
||||
return null; // No API key available
|
||||
}
|
||||
try {
|
||||
// Format flight number: Remove spaces and convert to uppercase
|
||||
const formattedFlightNumber = params.flightNumber.replace(/\s+/g, '').toUpperCase();
|
||||
console.log(`Formatted flight number: ${params.flightNumber} -> ${formattedFlightNumber}`);
|
||||
// Note: Free tier doesn't support date filtering, so we get recent flights
|
||||
// For future dates, this won't work well - consider upgrading subscription
|
||||
const url = `http://api.aviationstack.com/v1/flights?access_key=${apiKey}&flight_iata=${formattedFlightNumber}&limit=10`;
|
||||
console.log('AviationStack API URL:', url.replace(apiKey, '***'));
|
||||
console.log('Note: Free tier returns recent flights only, not future scheduled flights');
|
||||
const response = await fetch(url);
|
||||
const data = await response.json();
|
||||
console.log('AviationStack response status:', response.status);
|
||||
if (!response.ok) {
|
||||
console.error('AviationStack API error - HTTP status:', response.status);
|
||||
return null;
|
||||
}
|
||||
// Check for API errors in response
|
||||
if (data.error) {
|
||||
console.error('AviationStack API error:', data.error);
|
||||
return null;
|
||||
}
|
||||
if (data.data && data.data.length > 0) {
|
||||
// This is a valid flight number that exists!
|
||||
console.log(`✅ Valid flight number: ${formattedFlightNumber} exists in the system`);
|
||||
// Try to find a flight matching the requested date
|
||||
let flight = data.data.find((f) => f.flight_date === params.date);
|
||||
// If no exact date match, use most recent for validation
|
||||
if (!flight) {
|
||||
flight = data.data[0];
|
||||
console.log(`ℹ️ Flight ${formattedFlightNumber} is valid`);
|
||||
console.log(`Recent flight: ${flight.departure.airport} → ${flight.arrival.airport}`);
|
||||
console.log(`Operated by: ${flight.airline?.name || 'Unknown'}`);
|
||||
console.log(`Note: Showing recent data from ${flight.flight_date} for validation`);
|
||||
}
|
||||
else {
|
||||
console.log(`✅ Flight found for exact date: ${params.date}`);
|
||||
}
|
||||
console.log('Flight route:', `${flight.departure.iata} → ${flight.arrival.iata}`);
|
||||
console.log('Status:', flight.flight_status);
|
||||
return {
|
||||
flightNumber: flight.flight.iata,
|
||||
flightDate: flight.flight_date,
|
||||
status: this.normalizeStatus(flight.flight_status),
|
||||
airline: flight.airline?.name,
|
||||
aircraft: flight.aircraft?.registration,
|
||||
departure: {
|
||||
airport: flight.departure.iata,
|
||||
airportName: flight.departure.airport,
|
||||
scheduled: flight.departure.scheduled,
|
||||
estimated: flight.departure.estimated,
|
||||
actual: flight.departure.actual,
|
||||
terminal: flight.departure.terminal,
|
||||
gate: flight.departure.gate
|
||||
},
|
||||
arrival: {
|
||||
airport: flight.arrival.iata,
|
||||
airportName: flight.arrival.airport,
|
||||
scheduled: flight.arrival.scheduled,
|
||||
estimated: flight.arrival.estimated,
|
||||
actual: flight.arrival.actual,
|
||||
terminal: flight.arrival.terminal,
|
||||
gate: flight.arrival.gate
|
||||
},
|
||||
delay: flight.departure.delay || 0,
|
||||
lastUpdated: new Date().toISOString(),
|
||||
source: 'aviationstack'
|
||||
};
|
||||
}
|
||||
console.log(`❌ Invalid flight number: ${formattedFlightNumber} not found`);
|
||||
console.log('This flight number does not exist or has not operated recently');
|
||||
return null;
|
||||
}
|
||||
catch (error) {
|
||||
console.error('AviationStack API error:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// Start periodic updates for a flight
|
||||
startPeriodicUpdates(params, intervalMinutes = 5) {
|
||||
const key = `${params.flightNumber}_${params.date}`;
|
||||
// Clear existing interval if any
|
||||
this.stopPeriodicUpdates(key);
|
||||
// Set up new interval
|
||||
const interval = setInterval(async () => {
|
||||
try {
|
||||
await this.getFlightInfo(params); // This will update the cache
|
||||
console.log(`Updated flight data for ${params.flightNumber} on ${params.date}`);
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`Error updating flight ${params.flightNumber}:`, error);
|
||||
}
|
||||
}, intervalMinutes * 60 * 1000);
|
||||
this.updateIntervals.set(key, interval);
|
||||
}
|
||||
// Stop periodic updates for a flight
|
||||
stopPeriodicUpdates(key) {
|
||||
const interval = this.updateIntervals.get(key);
|
||||
if (interval) {
|
||||
clearInterval(interval);
|
||||
this.updateIntervals.delete(key);
|
||||
}
|
||||
}
|
||||
// Get multiple flights with date specificity
|
||||
async getMultipleFlights(flightParams) {
|
||||
const results = {};
|
||||
for (const params of flightParams) {
|
||||
const key = `${params.flightNumber}_${params.date}`;
|
||||
results[key] = await this.getFlightInfo(params);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
// Normalize flight status across different APIs
|
||||
normalizeStatus(status) {
|
||||
const statusMap = {
|
||||
'scheduled': 'scheduled',
|
||||
'active': 'active',
|
||||
'landed': 'landed',
|
||||
'cancelled': 'cancelled',
|
||||
'incident': 'delayed',
|
||||
'diverted': 'diverted'
|
||||
};
|
||||
return statusMap[status.toLowerCase()] || status;
|
||||
}
|
||||
// Clean up resources
|
||||
cleanup() {
|
||||
for (const [key, interval] of this.updateIntervals) {
|
||||
clearInterval(interval);
|
||||
}
|
||||
this.updateIntervals.clear();
|
||||
this.flightCache.clear();
|
||||
}
|
||||
}
|
||||
exports.default = new FlightService();
|
||||
//# sourceMappingURL=flightService.js.map
|
||||
Reference in New Issue
Block a user