diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 5583d84..887218c 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -34,7 +34,8 @@ "Bash(timeout /t 10 /nobreak)", "Bash(dir:*)", "Bash(lsof:*)", - "Bash(powershell -Command:*)" + "Bash(powershell -Command:*)", + "Bash(git rm:*)" ] } } diff --git a/.env.prod b/.env.prod deleted file mode 100644 index 252e633..0000000 --- a/.env.prod +++ /dev/null @@ -1,29 +0,0 @@ -# Production Environment Configuration - SECURE VALUES - -# Database Configuration -DB_PASSWORD=VipCoord2025SecureDB - -# Domain Configuration -DOMAIN=bsa.madeamess.online -VITE_API_URL=https://api.bsa.madeamess.online - -# Authentication Configuration (Secure production keys) -# JWT_SECRET - No longer needed! Keys are auto-generated and rotated every 24 hours -SESSION_SECRET=VipCoord2025SessionSecret9g8f7e6d5c4b3a2z1y0x9w8v7u6t5s4r3q2p1o0n9m8l7k6j5i4h3g2f1e - -# Google OAuth Configuration -GOOGLE_CLIENT_ID=308004695553-6k34bbq22frc4e76kejnkgq8mncepbbg.apps.googleusercontent.com -GOOGLE_CLIENT_SECRET=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 -GOOGLE_REDIRECT_URI=https://api.bsa.madeamess.online/auth/google/callback - -# Frontend URL -FRONTEND_URL=https://bsa.madeamess.online - -# Flight API Configuration -AVIATIONSTACK_API_KEY=your-aviationstack-api-key - -# Admin Configuration -ADMIN_PASSWORD=VipAdmin2025Secure - -# Port Configuration -PORT=3000 \ No newline at end of file diff --git a/.env.production b/.env.production deleted file mode 100644 index e69de29..0000000 diff --git a/.gitignore b/.gitignore index 58d8f71..9bc1b0c 100644 --- a/.gitignore +++ b/.gitignore @@ -76,6 +76,9 @@ Thumbs.db *backup* *.bak *.tmp +*-old-* +backend-old* +frontend-old* # Database files *.sqlite diff --git a/BUILD_STATUS.md b/BUILD_STATUS.md deleted file mode 100644 index a34d5a6..0000000 --- a/BUILD_STATUS.md +++ /dev/null @@ -1,373 +0,0 @@ -# VIP Coordinator - Build Status Report - -**Date:** January 25, 2026 -**Status:** Backend Complete āœ… | Frontend Pending - ---- - -## šŸŽ‰ What We've Built - -### āœ… Complete Backend API (100%) - -A production-ready NestJS backend with Auth0 authentication, Prisma ORM, and PostgreSQL. - -#### Tech Stack -- **Framework:** NestJS 10.x (TypeScript) -- **Database:** PostgreSQL 15 via Docker (port 5433) -- **ORM:** Prisma 5.x -- **Authentication:** Auth0 + Passport JWT -- **Validation:** class-validator + class-transformer -- **HTTP Client:** Axios (@nestjs/axios) - -#### Modules Implemented - -1. **Auth Module** āœ… - - JWT strategy with Auth0 integration - - JWKS key validation - - JWT auth guard (global) - - Roles guard for RBAC - - Custom decorators (@CurrentUser, @Roles, @Public) - - First user auto-approval as admin - - User approval workflow - -2. **Users Module** āœ… - - List all users - - Get user by ID - - Update user (name, role) - - Approve/deny pending users - - Soft delete users - - Admin-only access - -3. **VIPs Module** āœ… - - Create VIP profiles - - List all VIPs with flights and events - - Get VIP details - - Update VIP information - - Soft delete VIPs - - Two arrival modes: Flight, Self-driving - - Department organization - - Airport pickup / venue transport flags - -4. **Drivers Module** āœ… - - Create driver profiles - - List all drivers with schedules - - Get driver details - - Get complete driver schedule - - Update driver information - - Optional user account linking - - Soft delete drivers - -5. **Events Module** āœ… - - Create schedule events - - **Conflict detection** (prevents double-booking drivers) - - List all events - - Get event details - - Update events (with conflict recheck) - - Update event status (drivers can do this) - - Soft delete events - - 5 event types: Transport, Meeting, Event, Meal, Accommodation - - 4 event statuses: Scheduled, In-Progress, Completed, Cancelled - -6. **Flights Module** āœ… - - Create flight records - - List all flights - - Get flights by VIP - - Update flight information - - Delete flights - - **Real-time flight tracking** (AviationStack API integration) - - Multi-segment itinerary support - -#### Database Schema - -**5 Core Models:** -- User (auth0Id, email, role, isApproved, deletedAt) -- VIP (name, organization, department, arrivalMode, etc.) -- Driver (name, phone, department, userId, deletedAt) -- ScheduleEvent (vipId, driverId, times, type, status, deletedAt) -- Flight (vipId, flightNumber, airports, times, status) - -**3 Enums:** -- Role: ADMINISTRATOR, COORDINATOR, DRIVER -- Department: OFFICE_OF_DEVELOPMENT, ADMIN -- ArrivalMode: FLIGHT, SELF_DRIVING -- EventType: TRANSPORT, MEETING, EVENT, MEAL, ACCOMMODATION -- EventStatus: SCHEDULED, IN_PROGRESS, COMPLETED, CANCELLED - -**Features:** -- Soft deletes on all main entities -- Automatic timestamps (createdAt, updatedAt) -- Cascading relationships -- Indexed columns for performance - -#### API Endpoints (40+ endpoints) - -All endpoints prefixed with `/api/v1` - -**Public:** -- GET /health - Health check - -**Auth:** -- GET /auth/profile - Get current user - -**Users** (Admin only): -- GET /users -- GET /users/pending -- GET /users/:id -- PATCH /users/:id -- PATCH /users/:id/approve -- DELETE /users/:id - -**VIPs** (Admin, Coordinator; Drivers view-only): -- GET /vips -- POST /vips -- GET /vips/:id -- PATCH /vips/:id -- DELETE /vips/:id - -**Drivers** (Admin, Coordinator; Drivers view-only): -- GET /drivers -- POST /drivers -- GET /drivers/:id -- GET /drivers/:id/schedule -- PATCH /drivers/:id -- DELETE /drivers/:id - -**Events** (Admin, Coordinator create/update; Drivers can update status): -- GET /events -- POST /events (with conflict detection!) -- GET /events/:id -- PATCH /events/:id -- PATCH /events/:id/status -- DELETE /events/:id - -**Flights** (Admin, Coordinator): -- GET /flights -- POST /flights -- GET /flights/status/:flightNumber (real-time tracking!) -- GET /flights/vip/:vipId -- GET /flights/:id -- PATCH /flights/:id -- DELETE /flights/:id - -#### Security Features - -- āœ… JWT authentication on all routes (except @Public) -- āœ… Role-based access control (RBAC) -- āœ… User approval workflow (prevents unauthorized access) -- āœ… First user auto-admin (solves bootstrap problem) -- āœ… Input validation on all DTOs -- āœ… SQL injection prevention (Prisma ORM) -- āœ… Soft deletes (preserve data) -- āœ… CORS configuration - -#### Sample Data - -Database seeded with: -- 2 sample users (admin, coordinator) -- 2 sample VIPs (flight arrival, self-driving) -- 2 sample drivers -- 3 sample events (airport pickup, dinner, conference transport) - ---- - -## šŸ“ Project Structure - -``` -backend/ -ā”œā”€ā”€ prisma/ -│ ā”œā”€ā”€ schema.prisma # Database schema (source of truth) -│ ā”œā”€ā”€ migrations/ # Auto-generated migrations -│ │ └── 20260125085806_init/ -│ └── seed.ts # Sample data seeder -ā”œā”€ā”€ src/ -│ ā”œā”€ā”€ main.ts # App entry point -│ ā”œā”€ā”€ app.module.ts # Root module (imports all features) -│ ā”œā”€ā”€ app.controller.ts # Health check -│ ā”œā”€ā”€ app.service.ts -│ ā”œā”€ā”€ prisma/ -│ │ ā”œā”€ā”€ prisma.module.ts -│ │ └── prisma.service.ts # Database service (singleton) -│ ā”œā”€ā”€ auth/ -│ │ ā”œā”€ā”€ auth.module.ts -│ │ ā”œā”€ā”€ auth.service.ts -│ │ ā”œā”€ā”€ auth.controller.ts -│ │ ā”œā”€ā”€ strategies/ -│ │ │ └── jwt.strategy.ts (Auth0 JWT validation) -│ │ ā”œā”€ā”€ guards/ -│ │ │ ā”œā”€ā”€ jwt-auth.guard.ts (global guard) -│ │ │ └── roles.guard.ts (RBAC guard) -│ │ └── decorators/ -│ │ ā”œā”€ā”€ current-user.decorator.ts -│ │ ā”œā”€ā”€ roles.decorator.ts -│ │ └── public.decorator.ts -│ ā”œā”€ā”€ users/ -│ │ ā”œā”€ā”€ users.module.ts -│ │ ā”œā”€ā”€ users.service.ts -│ │ ā”œā”€ā”€ users.controller.ts -│ │ └── dto/ (UpdateUserDto, ApproveUserDto) -│ ā”œā”€ā”€ vips/ -│ │ ā”œā”€ā”€ vips.module.ts -│ │ ā”œā”€ā”€ vips.service.ts -│ │ ā”œā”€ā”€ vips.controller.ts -│ │ └── dto/ (CreateVipDto, UpdateVipDto) -│ ā”œā”€ā”€ drivers/ -│ │ ā”œā”€ā”€ drivers.module.ts -│ │ ā”œā”€ā”€ drivers.service.ts -│ │ ā”œā”€ā”€ drivers.controller.ts -│ │ └── dto/ (CreateDriverDto, UpdateDriverDto) -│ ā”œā”€ā”€ events/ -│ │ ā”œā”€ā”€ events.module.ts -│ │ ā”œā”€ā”€ events.service.ts (includes conflict detection) -│ │ ā”œā”€ā”€ events.controller.ts -│ │ └── dto/ (CreateEventDto, UpdateEventDto, UpdateEventStatusDto) -│ └── flights/ -│ ā”œā”€ā”€ flights.module.ts -│ ā”œā”€ā”€ flights.service.ts (AviationStack integration) -│ ā”œā”€ā”€ flights.controller.ts -│ └── dto/ (CreateFlightDto, UpdateFlightDto) -ā”œā”€ā”€ package.json -ā”œā”€ā”€ tsconfig.json -ā”œā”€ā”€ nest-cli.json -ā”œā”€ā”€ .env -ā”œā”€ā”€ .env.example -└── README.md -``` - ---- - -## šŸš€ Running the Backend - -### Prerequisites -- Node.js 20+ -- Docker Desktop -- Auth0 Account (free tier) - -### Quick Start - -```bash -# 1. Start PostgreSQL -cd vip-coordinator -docker-compose up -d postgres - -# 2. Install dependencies -cd backend -npm install - -# 3. Configure Auth0 -# Edit backend/.env with your Auth0 credentials - -# 4. Run migrations -npx prisma generate -npx prisma migrate dev - -# 5. Seed sample data (optional) -npm run prisma:seed - -# 6. Start backend -npm run start:dev -``` - -Backend will be available at: **http://localhost:3000/api/v1** - -### Test It - -```bash -# Health check (public) -curl http://localhost:3000/api/v1/health - -# Get profile (requires Auth0 token) -curl http://localhost:3000/api/v1/auth/profile \ - -H "Authorization: Bearer YOUR_AUTH0_TOKEN" -``` - ---- - -## šŸ“Š Build Statistics - -- **Total Files Created:** 60+ -- **Lines of Code:** ~3,500+ -- **Modules:** 6 feature modules -- **API Endpoints:** 40+ -- **Database Tables:** 5 models -- **Time to Build:** ~2 hours - ---- - -## āœ… What Works - -1. āœ… **Auth0 Integration** - JWT authentication fully configured -2. āœ… **User Management** - CRUD + approval workflow -3. āœ… **VIP Management** - Complete CRUD with relationships -4. āœ… **Driver Management** - Complete CRUD with schedule views -5. āœ… **Event Scheduling** - CRUD + intelligent conflict detection -6. āœ… **Flight Tracking** - CRUD + real-time API integration -7. āœ… **Role-Based Access** - Administrator, Coordinator, Driver permissions -8. āœ… **Database** - PostgreSQL with Prisma, migrations, seeding -9. āœ… **Docker** - PostgreSQL running in container -10. āœ… **TypeScript** - Fully typed, compiles without errors -11. āœ… **Validation** - All inputs validated with DTOs -12. āœ… **Soft Deletes** - Data preservation across all entities -13. āœ… **Logging** - NestJS logger throughout -14. āœ… **Documentation** - README.md, CLAUDE.md - ---- - -## šŸ”œ What's Next (Frontend) - -To complete the application, we need to build: - -1. **React Frontend** with Vite -2. **Shadcn UI** + Tailwind CSS -3. **Auth0 React SDK** for authentication -4. **TanStack Query** for data fetching -5. **React Router** for navigation -6. **Pages:** - - Login / Callback - - Dashboard - - VIP List / Details / Forms - - Driver List / Details / Forms - - Schedule Manager (calendar view) - - Flight Tracking - - User Management (admin) -7. **Components:** - - Protected routes - - Navigation - - Forms with validation - - Data tables - - Loading states - - Error handling - -**Estimated Time:** 4-6 hours for complete frontend - ---- - -## šŸŽÆ Current State - -**Backend:** āœ… 100% Complete & Tested -**Frontend:** ā³ 0% (not started) -**Total Progress:** ~50% of full application - -The backend is production-ready and can be deployed to Digital Ocean App Platform or any Docker-compatible host. It's fully functional and awaiting the React frontend to become a complete application. - ---- - -**Need to continue building?** Start with the React frontend initialization: - -```bash -cd vip-coordinator -npm create vite@latest frontend -- --template react-ts -cd frontend -npm install -``` - -Then add: -- Shadcn UI setup -- Auth0 React SDK -- TanStack Query -- React Router -- All pages and components - ---- - -**Last Updated:** January 25, 2026 -**Status:** Backend production-ready, awaiting frontend development diff --git a/CORRECTED_GOOGLE_OAUTH_SETUP.md b/CORRECTED_GOOGLE_OAUTH_SETUP.md deleted file mode 100644 index 19c2457..0000000 --- a/CORRECTED_GOOGLE_OAUTH_SETUP.md +++ /dev/null @@ -1,174 +0,0 @@ -# āœ… CORRECTED Google OAuth Setup Guide - -## āš ļø Issues Found with Previous Setup - -The previous coder was using **deprecated Google+ API** which was shut down in 2019. This guide provides the correct modern approach using Google Identity API. - -## šŸ”§ What Was Fixed - -1. **Removed Google+ API references** - Now uses Google Identity API -2. **Fixed redirect URI configuration** - Points to backend instead of frontend -3. **Added missing `/auth/setup` endpoint** - Frontend was calling non-existent endpoint -4. **Corrected OAuth flow** - Proper backend callback handling - -## šŸš€ Correct Setup Instructions - -### Step 1: Google Cloud Console Setup - -1. **Go to Google Cloud Console** - - Visit: https://console.cloud.google.com/ - -2. **Create or Select Project** - - Create new project: "VIP Coordinator" - - Or select existing project - -3. **Enable Google Identity API** āš ļø **NOT Google+ API** - - Go to "APIs & Services" → "Library" - - Search for "Google Identity API" or "Google+ API" - - **Important**: Use "Google Identity API" - Google+ is deprecated! - - Click "Enable" - -4. **Create OAuth 2.0 Credentials** - - Go to "APIs & Services" → "Credentials" - - Click "Create Credentials" → "OAuth 2.0 Client IDs" - - Application type: "Web application" - - Name: "VIP Coordinator Web App" - -5. **Configure Authorized URLs** āš ļø **CRITICAL: Use Backend URLs** - - **Authorized JavaScript origins:** - ``` - http://localhost:3000 - http://bsa.madeamess.online:3000 - ``` - - **Authorized redirect URIs:** āš ļø **Backend callback, NOT frontend** - ``` - http://localhost:3000/auth/google/callback - http://bsa.madeamess.online:3000/auth/google/callback - ``` - -6. **Save Credentials** - - Copy **Client ID** and **Client Secret** - -### Step 2: Update Environment Variables - -Edit `backend/.env`: - -```bash -# Replace these values with your actual Google OAuth credentials -GOOGLE_CLIENT_ID=your-actual-client-id-here.apps.googleusercontent.com -GOOGLE_CLIENT_SECRET=GOCSPX-your-actual-client-secret-here -GOOGLE_REDIRECT_URI=http://localhost:3000/auth/google/callback - -# For production, also update: -# GOOGLE_REDIRECT_URI=http://bsa.madeamess.online:3000/auth/google/callback -``` - -### Step 3: Test the Setup - -1. **Restart the backend:** - ```bash - cd vip-coordinator - docker-compose -f docker-compose.dev.yml restart backend - ``` - -2. **Test the OAuth flow:** - - Visit: http://localhost:5173 (or your frontend URL) - - Click "Continue with Google" - - Should redirect to Google login - - After login, should redirect back and log you in - -3. **Check backend logs:** - ```bash - docker-compose -f docker-compose.dev.yml logs backend - ``` - -## šŸ” How the Corrected Flow Works - -1. **User clicks "Continue with Google"** -2. **Frontend calls** `/auth/google/url` to get OAuth URL -3. **Frontend redirects** to Google OAuth -4. **Google redirects back** to `http://localhost:3000/auth/google/callback` -5. **Backend handles callback**, exchanges code for user info -6. **Backend creates JWT token** and redirects to frontend with token -7. **Frontend receives token** and authenticates user - -## šŸ› ļø Key Differences from Previous Implementation - -| Previous (Broken) | Corrected | -|-------------------|-----------| -| Google+ API (deprecated) | Google Identity API | -| Frontend redirect URI | Backend redirect URI | -| Missing `/auth/setup` endpoint | Added setup status endpoint | -| Inconsistent OAuth flow | Standard OAuth 2.0 flow | - -## šŸ”§ Troubleshooting - -### Common Issues: - -1. **"OAuth not configured" error:** - - Check `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` in `.env` - - Restart backend after changing environment variables - -2. **"Invalid redirect URI" error:** - - Verify redirect URIs in Google Console match exactly: - - `http://localhost:3000/auth/google/callback` - - `http://bsa.madeamess.online:3000/auth/google/callback` - - No trailing slashes! - -3. **"API not enabled" error:** - - Make sure you enabled "Google Identity API" (not Google+) - - Wait a few minutes for API to activate - -4. **Login button doesn't work:** - - Check browser console for errors - - Verify backend is running on port 3000 - - Check `/auth/setup` endpoint returns proper status - -### Debug Commands: - -```bash -# Check if backend is running -curl http://localhost:3000/api/health - -# Check OAuth setup status -curl http://localhost:3000/auth/setup - -# Check backend logs -docker-compose -f docker-compose.dev.yml logs backend - -# Check environment variables are loaded -docker exec vip-coordinator-backend-1 env | grep GOOGLE -``` - -## āœ… Verification Steps - -1. **Setup status should show configured:** - ```bash - curl http://localhost:3000/auth/setup - # Should return: {"setupCompleted": true, "firstAdminCreated": false, "oauthConfigured": true} - ``` - -2. **OAuth URL should be generated:** - ```bash - curl http://localhost:3000/auth/google/url - # Should return: {"url": "https://accounts.google.com/o/oauth2/v2/auth?..."} - ``` - -3. **Login flow should work:** - - Visit frontend - - Click "Continue with Google" - - Complete Google login - - Should be redirected back and logged in - -## šŸŽ‰ Success! - -Once working, you should see: -- āœ… Google login button works -- āœ… Redirects to Google OAuth -- āœ… Returns to app after login -- āœ… User is authenticated with JWT token -- āœ… First user becomes administrator - -The authentication system now uses modern Google Identity API and follows proper OAuth 2.0 standards! diff --git a/DATABASE_MIGRATION_SUMMARY.md b/DATABASE_MIGRATION_SUMMARY.md deleted file mode 100644 index ba2d94b..0000000 --- a/DATABASE_MIGRATION_SUMMARY.md +++ /dev/null @@ -1,221 +0,0 @@ -# VIP Coordinator Database Migration Summary - -## Overview -Successfully migrated the VIP Coordinator application from JSON file storage to a proper database architecture using PostgreSQL and Redis. - -## Architecture Changes - -### Before (JSON File Storage) -- All data stored in `backend/data/vip-coordinator.json` -- Single file for VIPs, drivers, schedules, and admin settings -- No concurrent access control -- No real-time capabilities -- Risk of data corruption - -### After (PostgreSQL + Redis) -- **PostgreSQL**: Persistent business data with ACID compliance -- **Redis**: Real-time data and caching -- Proper data relationships and constraints -- Concurrent access support -- Real-time location tracking -- Flight data caching - -## Database Schema - -### PostgreSQL Tables -1. **vips** - VIP profiles and basic information -2. **flights** - Flight details linked to VIPs -3. **drivers** - Driver profiles -4. **schedule_events** - Event scheduling with driver assignments -5. **admin_settings** - System configuration (key-value pairs) - -### Redis Data Structure -- `driver:{id}:location` - Real-time driver locations -- `event:{id}:status` - Live event status updates -- `flight:{key}` - Cached flight API responses - -## Key Features Implemented - -### 1. Database Configuration -- **PostgreSQL connection pool** (`backend/src/config/database.ts`) -- **Redis client setup** (`backend/src/config/redis.ts`) -- **Database schema** (`backend/src/config/schema.sql`) - -### 2. Data Services -- **DatabaseService** (`backend/src/services/databaseService.ts`) - - Database initialization and migration - - Redis operations for real-time data - - Automatic JSON data migration -- **EnhancedDataService** (`backend/src/services/enhancedDataService.ts`) - - PostgreSQL CRUD operations - - Complex queries with joins - - Transaction support - -### 3. Migration Features -- **Automatic migration** from existing JSON data -- **Backup creation** of original JSON file -- **Zero-downtime migration** process -- **Data validation** during migration - -### 4. Real-time Capabilities -- **Driver location tracking** in Redis -- **Event status updates** with timestamps -- **Flight data caching** with TTL -- **Performance optimization** through caching - -## Data Flow - -### VIP Management -``` -Frontend → API → EnhancedDataService → PostgreSQL - → Redis (for real-time data) -``` - -### Driver Location Updates -``` -Frontend → API → DatabaseService → Redis (hSet driver location) -``` - -### Flight Tracking -``` -Flight API → FlightService → Redis (cache) → Database (if needed) -``` - -## Benefits Achieved - -### Performance -- **Faster queries** with PostgreSQL indexes -- **Reduced API calls** through Redis caching -- **Concurrent access** without file locking issues - -### Scalability -- **Multiple server instances** supported -- **Database connection pooling** -- **Redis clustering** ready - -### Reliability -- **ACID transactions** for data integrity -- **Automatic backups** during migration -- **Error handling** and rollback support - -### Real-time Features -- **Live driver locations** via Redis -- **Event status tracking** with timestamps -- **Flight data caching** for performance - -## Configuration - -### Environment Variables -```bash -DATABASE_URL=postgresql://postgres:changeme@db:5432/vip_coordinator -REDIS_URL=redis://redis:6379 -``` - -### Docker Services -- **PostgreSQL 15** with persistent volume -- **Redis 7** for caching and real-time data -- **Backend** with database connections - -## Migration Process - -### Automatic Steps -1. **Schema creation** with tables and indexes -2. **Data validation** and transformation -3. **VIP migration** with flight relationships -4. **Driver migration** with location data to Redis -5. **Schedule migration** with proper relationships -6. **Admin settings** flattened to key-value pairs -7. **Backup creation** of original JSON file - -### Manual Steps (if needed) -1. Install dependencies: `npm install` -2. Start services: `make dev` -3. Verify migration in logs - -## API Changes - -### Enhanced Endpoints -- All VIP endpoints now use PostgreSQL -- Driver location updates go to Redis -- Flight data cached in Redis -- Schedule operations with proper relationships - -### Backward Compatibility -- All existing API endpoints maintained -- Same request/response formats -- Legacy field support during transition - -## Testing - -### Database Connection -```bash -# Health check includes database status -curl http://localhost:3000/api/health -``` - -### Data Verification -```bash -# Check VIPs migrated correctly -curl http://localhost:3000/api/vips - -# Check drivers with locations -curl http://localhost:3000/api/drivers -``` - -## Next Steps - -### Immediate -1. **Test the migration** with Docker -2. **Verify all endpoints** work correctly -3. **Check real-time features** function - -### Future Enhancements -1. **WebSocket integration** for live updates -2. **Advanced Redis patterns** for pub/sub -3. **Database optimization** with query analysis -4. **Monitoring and metrics** setup - -## Files Created/Modified - -### New Files -- `backend/src/config/database.ts` - PostgreSQL configuration -- `backend/src/config/redis.ts` - Redis configuration -- `backend/src/config/schema.sql` - Database schema -- `backend/src/services/databaseService.ts` - Migration and Redis ops -- `backend/src/services/enhancedDataService.ts` - PostgreSQL operations - -### Modified Files -- `backend/package.json` - Added pg, redis, uuid dependencies -- `backend/src/index.ts` - Updated to use new services -- `docker-compose.dev.yml` - Already configured for databases - -## Redis Usage Patterns - -### Driver Locations -```typescript -// Update location -await databaseService.updateDriverLocation(driverId, { lat: 39.7392, lng: -104.9903 }); - -// Get location -const location = await databaseService.getDriverLocation(driverId); -``` - -### Event Status -```typescript -// Set status -await databaseService.setEventStatus(eventId, 'in-progress'); - -// Get status -const status = await databaseService.getEventStatus(eventId); -``` - -### Flight Caching -```typescript -// Cache flight data -await databaseService.cacheFlightData(flightKey, flightData, 300); - -// Get cached data -const cached = await databaseService.getCachedFlightData(flightKey); -``` - -This migration provides a solid foundation for scaling the VIP Coordinator application with proper data persistence, real-time capabilities, and performance optimization. diff --git a/DOCKER_HUB_DEPLOYMENT_PLAN.md b/DOCKER_HUB_DEPLOYMENT_PLAN.md deleted file mode 100644 index 71ec407..0000000 --- a/DOCKER_HUB_DEPLOYMENT_PLAN.md +++ /dev/null @@ -1,130 +0,0 @@ -# šŸš€ Docker Hub Deployment Plan for VIP Coordinator - -## šŸ“‹ Overview -This document outlines the complete plan to prepare the VIP Coordinator project for Docker Hub deployment, ensuring it's secure, portable, and easy to deploy. - -## šŸ” Security Issues Identified & Resolved - -### āœ… Environment Configuration -- **FIXED**: Removed hardcoded sensitive data from environment files -- **FIXED**: Created single `.env.example` template for all deployments -- **FIXED**: Removed redundant environment files (`.env.production`, `backend/.env`) -- **FIXED**: Updated `.gitignore` to exclude sensitive files -- **FIXED**: Removed unused JWT_SECRET and SESSION_SECRET (auto-managed by jwtKeyManager) - -### āœ… Authentication System -- **SECURE**: JWT keys are automatically generated and rotated every 24 hours -- **SECURE**: No hardcoded authentication secrets in codebase -- **SECURE**: Google OAuth credentials must be provided by user - -## šŸ› ļø Remaining Tasks for Docker Hub Readiness - -### 1. Fix Docker Configuration Issues - -#### Backend Dockerfile Issues: -- Production stage runs `npm run dev` instead of production build -- Missing proper multi-stage optimization -- No health checks - -#### Frontend Dockerfile Issues: -- Need to verify production build configuration -- Ensure proper Nginx setup for production - -### 2. Create Docker Hub Deployment Documentation - -#### Required Files: -- [ ] `DEPLOYMENT.md` - Complete deployment guide -- [ ] `docker-compose.yml` - Single production-ready compose file -- [ ] Update `README.md` with Docker Hub instructions - -### 3. Security Hardening - -#### Container Security: -- [ ] Add health checks to Dockerfiles -- [ ] Use non-root users in containers -- [ ] Minimize container attack surface -- [ ] Add security scanning - -#### Environment Security: -- [ ] Validate all environment variables are properly templated -- [ ] Ensure no test data contains sensitive information -- [ ] Add environment validation on startup - -### 4. Portability Improvements - -#### Configuration: -- [ ] Make all hardcoded URLs configurable -- [ ] Ensure database initialization works in any environment -- [ ] Add proper error handling for missing configuration - -#### Documentation: -- [ ] Create quick-start guide for Docker Hub users -- [ ] Add troubleshooting section -- [ ] Include example configurations - -## šŸ“ Current File Structure (Clean) - -``` -vip-coordinator/ -ā”œā”€ā”€ .env.example # āœ… Single environment template -ā”œā”€ā”€ .gitignore # āœ… Excludes sensitive files -ā”œā”€ā”€ docker-compose.prod.yml # Production compose file -ā”œā”€ā”€ backend/ -│ ā”œā”€ā”€ Dockerfile # āš ļø Needs production fixes -│ └── src/ # āœ… Clean source code -ā”œā”€ā”€ frontend/ -│ ā”œā”€ā”€ Dockerfile # āš ļø Needs verification -│ └── src/ # āœ… Clean source code -└── README.md # āš ļø Needs Docker Hub instructions -``` - -## šŸŽÆ Next Steps Priority - -### High Priority (Required for Docker Hub) -1. **Fix Backend Dockerfile** - Production build configuration -2. **Fix Frontend Dockerfile** - Verify production setup -3. **Create DEPLOYMENT.md** - Complete user guide -4. **Update README.md** - Add Docker Hub quick start - -### Medium Priority (Security & Polish) -5. **Add Health Checks** - Container monitoring -6. **Security Hardening** - Non-root users, scanning -7. **Environment Validation** - Startup checks - -### Low Priority (Nice to Have) -8. **Advanced Documentation** - Troubleshooting, examples -9. **CI/CD Integration** - Automated builds -10. **Monitoring Setup** - Logging, metrics - -## šŸ”§ Implementation Plan - -### Phase 1: Core Fixes (Required) -- Fix Dockerfile production configurations -- Create deployment documentation -- Test complete deployment flow - -### Phase 2: Security & Polish -- Add container security measures -- Implement health checks -- Add environment validation - -### Phase 3: Documentation & Examples -- Create comprehensive guides -- Add example configurations -- Include troubleshooting help - -## āœ… Completed Tasks -- [x] Created `.env.example` template -- [x] Removed sensitive data from environment files -- [x] Updated `.gitignore` for security -- [x] Cleaned up redundant environment files -- [x] Updated SETUP_GUIDE.md references -- [x] Verified JWT/Session secret removal - -## 🚨 Critical Notes -- **AviationStack API Key**: Can be configured via admin interface, not required in environment -- **Google OAuth**: Must be configured by user for authentication to work -- **Database Password**: Must be changed from default for production -- **Admin Password**: Must be changed from default for security - -This plan ensures the VIP Coordinator will be secure, portable, and ready for Docker Hub deployment. \ No newline at end of file diff --git a/DOCKER_HUB_READY_SUMMARY.md b/DOCKER_HUB_READY_SUMMARY.md deleted file mode 100644 index f92664c..0000000 --- a/DOCKER_HUB_READY_SUMMARY.md +++ /dev/null @@ -1,148 +0,0 @@ -# šŸš€ VIP Coordinator - Docker Hub Ready Summary - -## āœ… Completed Tasks - -### šŸ” Security Hardening -- [x] **Removed all hardcoded sensitive data** from source code -- [x] **Created secure environment template** (`.env.example`) -- [x] **Removed redundant environment files** (`.env.production`, `backend/.env`) -- [x] **Updated .gitignore** to exclude sensitive files -- [x] **Cleaned hardcoded domains** from source code -- [x] **Secured admin password fallbacks** in source code -- [x] **Removed unused JWT/Session secrets** (auto-managed by jwtKeyManager) - -### 🐳 Docker Configuration -- [x] **Fixed Backend Dockerfile** - Proper production build with TypeScript compilation -- [x] **Fixed Frontend Dockerfile** - Multi-stage build with Nginx serving -- [x] **Updated docker-compose.prod.yml** - Removed sensitive defaults, added health checks -- [x] **Added .dockerignore** - Optimized build context -- [x] **Added health checks** - Container monitoring for all services -- [x] **Implemented non-root users** - Enhanced container security - -### šŸ“š Documentation -- [x] **Created DEPLOYMENT.md** - Comprehensive Docker Hub deployment guide -- [x] **Updated README.md** - Added Docker Hub quick start section -- [x] **Updated SETUP_GUIDE.md** - Fixed environment file references -- [x] **Created deployment plan** - Complete roadmap document - -## šŸ—ļø Architecture Improvements - -### Security Features -- **JWT Auto-Rotation**: Keys automatically rotate every 24 hours -- **Non-Root Containers**: All services run as non-privileged users -- **Health Monitoring**: Built-in health checks for all services -- **Secure Headers**: Nginx configured with security headers -- **Environment Isolation**: Clean separation of dev/prod configurations - -### Production Optimizations -- **Multi-Stage Builds**: Optimized Docker images -- **Static Asset Serving**: Nginx serves React build with caching -- **Database Health Checks**: PostgreSQL monitoring -- **Redis Health Checks**: Cache service monitoring -- **Dependency Optimization**: Production-only dependencies in final images - -## šŸ“ Clean File Structure - -``` -vip-coordinator/ -ā”œā”€ā”€ .env.example # āœ… Single environment template -ā”œā”€ā”€ .gitignore # āœ… Excludes sensitive files -ā”œā”€ā”€ .dockerignore # āœ… Optimizes Docker builds -ā”œā”€ā”€ docker-compose.prod.yml # āœ… Production-ready compose -ā”œā”€ā”€ DEPLOYMENT.md # āœ… Docker Hub deployment guide -ā”œā”€ā”€ backend/ -│ ā”œā”€ā”€ Dockerfile # āœ… Production-optimized -│ └── src/ # āœ… Clean source code -ā”œā”€ā”€ frontend/ -│ ā”œā”€ā”€ Dockerfile # āœ… Nginx + React build -│ ā”œā”€ā”€ nginx.conf # āœ… Production web server -│ └── src/ # āœ… Clean source code -└── README.md # āœ… Updated with Docker Hub info -``` - -## šŸ”§ Environment Configuration - -### Required Variables (All must be set by user) -- `DB_PASSWORD` - Secure database password -- `DOMAIN` - User's domain -- `VITE_API_URL` - API endpoint URL -- `GOOGLE_CLIENT_ID` - Google OAuth client ID -- `GOOGLE_CLIENT_SECRET` - Google OAuth client secret -- `GOOGLE_REDIRECT_URI` - OAuth redirect URI -- `FRONTEND_URL` - Frontend URL -- `ADMIN_PASSWORD` - Admin panel password - -### Removed Variables (No longer needed) -- āŒ `JWT_SECRET` - Auto-generated and rotated -- āŒ `SESSION_SECRET` - Not used in current implementation -- āŒ `AVIATIONSTACK_API_KEY` - Configurable via admin interface - -## šŸš€ Deployment Process - -### For Docker Hub Users -1. **Download**: `git clone ` -2. **Configure**: `cp .env.example .env.prod` and edit -3. **Deploy**: `docker-compose -f docker-compose.prod.yml up -d` -4. **Setup OAuth**: Configure Google Cloud Console -5. **Access**: Visit frontend URL and login - -### Services Available -- **Frontend**: Port 80 (Nginx serving React build) -- **Backend**: Port 3000 (Node.js API) -- **Database**: PostgreSQL with auto-schema setup -- **Redis**: Caching and real-time features - -## šŸ” Security Verification - -### āœ… No Sensitive Data in Source -- No hardcoded passwords -- No API keys in code -- No real domain names -- No OAuth credentials -- No database passwords - -### āœ… Secure Defaults -- Strong password requirements -- Environment variable validation -- Non-root container users -- Health check monitoring -- Secure HTTP headers - -## šŸ“‹ Pre-Deployment Checklist - -### Required by User -- [ ] Set secure `DB_PASSWORD` -- [ ] Configure own domain names -- [ ] Create Google OAuth credentials -- [ ] Set secure `ADMIN_PASSWORD` -- [ ] Configure SSL/TLS certificates (production) - -### Automatic -- [x] JWT key generation and rotation -- [x] Database schema initialization -- [x] Container health monitoring -- [x] Security headers configuration -- [x] Static asset optimization - -## šŸŽÆ Ready for Docker Hub - -The VIP Coordinator project is now **fully prepared for Docker Hub deployment** with: - -- āœ… **Security**: No sensitive data exposed -- āœ… **Portability**: Works in any environment with proper configuration -- āœ… **Documentation**: Complete deployment guides -- āœ… **Optimization**: Production-ready Docker configurations -- āœ… **Monitoring**: Health checks and logging -- āœ… **Usability**: Simple setup process for end users - -## 🚨 Important Notes - -1. **User Responsibility**: Users must provide their own OAuth credentials and secure passwords -2. **Domain Configuration**: All domain references must be updated by the user -3. **SSL/HTTPS**: Required for production deployments -4. **Database Security**: Default passwords must be changed -5. **Regular Updates**: Keep Docker images and dependencies updated - ---- - -**Status**: āœ… **READY FOR DOCKER HUB DEPLOYMENT** \ No newline at end of file diff --git a/DOCKER_HUB_SUMMARY.md b/DOCKER_HUB_SUMMARY.md deleted file mode 100644 index 1d6f8fc..0000000 --- a/DOCKER_HUB_SUMMARY.md +++ /dev/null @@ -1,170 +0,0 @@ -# VIP Coordinator - Docker Hub Deployment Summary - -## šŸŽ‰ Successfully Deployed to Docker Hub! - -The VIP Coordinator application has been successfully built and deployed to Docker Hub at: - -- **Backend Image**: `t72chevy/vip-coordinator:backend-latest` -- **Frontend Image**: `t72chevy/vip-coordinator:frontend-latest` - -## šŸ“¦ What's Included - -### Docker Images -- **Backend**: Node.js/Express API with TypeScript, JWT auto-rotation, Google OAuth -- **Frontend**: React application with Vite build, served by Nginx -- **Size**: Backend ~404MB, Frontend ~75MB (optimized for production) - -### Deployment Files -- `README.md` - Comprehensive documentation -- `docker-compose.yml` - Production-ready orchestration -- `.env.example` - Environment configuration template -- `deploy.sh` - Automated deployment script - -## šŸš€ Quick Start for Users - -Users can now deploy the VIP Coordinator with just a few commands: - -```bash -# Download deployment files -curl -O https://raw.githubusercontent.com/your-repo/vip-coordinator/main/docker-compose.yml -curl -O https://raw.githubusercontent.com/your-repo/vip-coordinator/main/.env.example -curl -O https://raw.githubusercontent.com/your-repo/vip-coordinator/main/deploy.sh - -# Make deploy script executable -chmod +x deploy.sh - -# Copy and configure environment -cp .env.example .env -# Edit .env with your configuration - -# Deploy the application -./deploy.sh -``` - -## šŸ”§ Key Features Deployed - -### Security Features -- āœ… JWT auto-rotation system -- āœ… Google OAuth integration -- āœ… Non-root container users -- āœ… Input validation and sanitization -- āœ… Secure environment variable handling - -### Production Features -- āœ… Multi-stage Docker builds -- āœ… Health checks for all services -- āœ… Automatic restart policies -- āœ… Optimized image sizes -- āœ… Comprehensive logging - -### Application Features -- āœ… Real-time VIP scheduling -- āœ… Driver management system -- āœ… Role-based access control -- āœ… Responsive web interface -- āœ… Data export capabilities - -## šŸ—ļø Architecture - -``` -ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” -│ Frontend │ │ Backend │ -│ (Nginx) │◄──►│ (Node.js) │ -│ Port: 80 │ │ Port: 3001 │ -ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ - │ │ - ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ - │ - ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” - │ PostgreSQL │ │ Redis │ - │ Port: 5432 │ │ Port: 6379 │ - ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ -``` - -## šŸ“Š Image Details - -### Backend Image (`t72chevy/vip-coordinator:backend-latest`) -- **Base**: Node.js 22 Alpine -- **Size**: ~404MB -- **Features**: TypeScript compilation, production dependencies only -- **Security**: Non-root user (nodejs:1001) -- **Health Check**: `/health` endpoint - -### Frontend Image (`t72chevy/vip-coordinator:frontend-latest`) -- **Base**: Nginx Alpine -- **Size**: ~75MB -- **Features**: Optimized React build, custom nginx config -- **Security**: Non-root user (appuser:1001) -- **Health Check**: HTTP response check - -## šŸ” Verification - -Both images have been tested and verified: - -```bash -āœ… Backend build: Successful -āœ… Frontend build: Successful -āœ… Docker Hub push: Successful -āœ… Image pull test: Successful -āœ… Health checks: Working -āœ… Production deployment: Tested -``` - -## 🌐 Access Points - -Once deployed, users can access: - -- **Frontend Application**: `http://localhost` (or your domain) -- **Backend API**: `http://localhost:3000` -- **Health Check**: `http://localhost:3000/health` -- **API Documentation**: Available via backend endpoints - -## šŸ“‹ Environment Requirements - -### Required Configuration -- Google OAuth credentials (Client ID & Secret) -- Secure PostgreSQL password -- Domain configuration for production - -### Optional Configuration -- Custom JWT secret (auto-generates if not provided) -- Redis configuration (defaults provided) -- Custom ports and URLs - -## šŸ†˜ Support & Troubleshooting - -### Common Issues -1. **Google OAuth Setup**: Ensure proper callback URLs -2. **Database Connection**: Check password special characters -3. **Port Conflicts**: Ensure ports 80 and 3000 are available -4. **Health Checks**: Allow time for services to start - -### Getting Help -- Check the comprehensive README.md -- Review Docker Compose logs -- Verify environment configuration -- Ensure all required variables are set - -## šŸ”„ Updates - -To update to newer versions: - -```bash -docker-compose pull -docker-compose up -d -``` - -## šŸ“ˆ Production Considerations - -For production deployment: -- Use HTTPS with SSL certificates -- Implement proper backup strategies -- Set up monitoring and alerting -- Use strong, unique passwords -- Consider load balancing for high availability - ---- - -**šŸŽÆ Mission Accomplished!** - -The VIP Coordinator is now available on Docker Hub and ready for deployment by users worldwide. The application provides enterprise-grade VIP transportation coordination with modern security practices and scalable architecture. \ No newline at end of file diff --git a/DOCUMENTATION_CLEANUP_SUMMARY.md b/DOCUMENTATION_CLEANUP_SUMMARY.md deleted file mode 100644 index fdb3ddf..0000000 --- a/DOCUMENTATION_CLEANUP_SUMMARY.md +++ /dev/null @@ -1,173 +0,0 @@ -# VIP Coordinator Documentation Cleanup - COMPLETED āœ… - -## šŸŽ‰ Complete Documentation Cleanup Successfully Finished - -The VIP Coordinator project has been **completely cleaned up and modernized**. We've streamlined from **30+ files** down to **10 essential files**, removing all outdated documentation and redundant scripts. - -## šŸ“Š Final Results - -### Before Cleanup (30+ files) -- **9 OAuth setup guides** - Multiple confusing, outdated approaches -- **8 Test data scripts** - External scripts for data population -- **3 One-time utility scripts** - API testing and migration scripts -- **8 Redundant documentation** - User management, troubleshooting, RBAC docs -- **2 Database migration docs** - Completed migration summaries -- **Scattered information** across many files - -### After Cleanup (10 files) -- **1 Setup guide** - Single, comprehensive SETUP_GUIDE.md -- **1 Project overview** - Modern README.md with current features -- **1 API guide** - Detailed README-API.md -- **2 API documentation files** - Interactive Swagger UI and OpenAPI spec -- **3 Docker configuration files** - Development and production environments -- **1 Development tool** - Makefile for commands -- **2 Code directories** - backend/ and frontend/ - -## āœ… Total Files Removed: 28 files - -### OAuth Documentation (9 files) āŒ REMOVED -- CORRECTED_GOOGLE_OAUTH_SETUP.md -- GOOGLE_OAUTH_DOMAIN_SETUP.md -- GOOGLE_OAUTH_QUICK_SETUP.md -- GOOGLE_OAUTH_SETUP.md -- OAUTH_CALLBACK_FIX_SUMMARY.md -- OAUTH_FRONTEND_ONLY_SETUP.md -- REVERSE_PROXY_OAUTH_SETUP.md -- SIMPLE_OAUTH_SETUP.md -- WEB_SERVER_PROXY_SETUP.md - -### Test Data Scripts (8 files) āŒ REMOVED -*Reason: Built into admin dashboard UI* -- populate-events-dynamic.js -- populate-events-dynamic.sh -- populate-events.js -- populate-events.sh -- populate-test-data.js -- populate-test-data.sh -- populate-vips.js -- quick-populate-events.sh - -### One-Time Utility Scripts (3 files) āŒ REMOVED -*Reason: No longer needed* -- test-aviationstack-endpoints.js (hardcoded API key, one-time testing) -- test-flight-api.js (redundant with admin dashboard API testing) -- update-departments.js (one-time migration script, already run) - -### Redundant Documentation (8 files) āŒ REMOVED -- DATABASE_MIGRATION_SUMMARY.md -- POSTGRESQL_USER_MANAGEMENT.md -- SIMPLE_USER_MANAGEMENT.md -- USER_MANAGEMENT_RECOMMENDATIONS.md -- DOCKER_TROUBLESHOOTING.md -- PERMISSION_ISSUES_FIXED.md -- PORT_3000_SETUP_GUIDE.md -- ROLE_BASED_ACCESS_CONTROL.md - -## šŸ“š Essential Files Preserved (10 files) - -### Core Documentation āœ… -1. **README.md** - Modern project overview with current features -2. **SETUP_GUIDE.md** - Comprehensive setup guide with Google OAuth -3. **README-API.md** - Detailed API documentation and examples - -### API Documentation āœ… -4. **api-docs.html** - Interactive Swagger UI documentation -5. **api-documentation.yaml** - OpenAPI specification - -### Development Configuration āœ… -6. **Makefile** - Development commands and workflows -7. **docker-compose.dev.yml** - Development environment -8. **docker-compose.prod.yml** - Production environment - -### Project Structure āœ… -9. **backend/** - Complete Node.js API server -10. **frontend/** - Complete React application - -## šŸš€ Key Improvements Achieved - -### 1. **Simplified Setup Process** -- **Before**: 9+ OAuth guides with conflicting instructions -- **After**: Single SETUP_GUIDE.md with clear, step-by-step Google OAuth setup - -### 2. **Modernized Test Data Management** -- **Before**: 8 external scripts requiring manual execution -- **After**: Built-in admin dashboard with one-click test data creation/removal - -### 3. **Streamlined Documentation Maintenance** -- **Before**: 28+ files to keep updated -- **After**: 3 core documentation files (90% reduction in maintenance) - -### 4. **Accurate System Representation** -- **Before**: Outdated documentation scattered across many files -- **After**: Current documentation reflecting JWT + Google OAuth architecture - -### 5. **Clean Project Structure** -- **Before**: Root directory cluttered with 30+ files -- **After**: Clean, organized structure with only essential files - -## šŸŽÆ Current System (Properly Documented) - -### Authentication System āœ… -- **JWT-based authentication** with Google OAuth -- **Role-based access control**: Administrator, Coordinator, Driver -- **User approval system** for new registrations -- **Simple setup** documented in SETUP_GUIDE.md - -### Test Data Management āœ… -- **Built-in admin dashboard** for test data creation -- **One-click VIP generation** (20 diverse test VIPs with full schedules) -- **Easy cleanup** - remove all test data with one click -- **No external scripts needed** - -### API Documentation āœ… -- **Interactive Swagger UI** at `/api-docs.html` -- **"Try it out" functionality** for testing endpoints -- **Comprehensive API guide** in README-API.md - -### Development Workflow āœ… -- **Single command setup**: `make dev` -- **Docker-based development** with automatic database initialization -- **Clear troubleshooting** in SETUP_GUIDE.md - -## šŸ“‹ Developer Experience - -### New Developer Onboarding -1. **Clone repository** -2. **Follow SETUP_GUIDE.md** (single source of truth) -3. **Run `make dev`** (starts everything) -4. **Configure Google OAuth** (clear instructions) -5. **Use admin dashboard** for test data (no scripts) -6. **Access API docs** at localhost:3000/api-docs.html - -### Documentation Maintenance -- **3 files to maintain** (vs. 28+ before) -- **No redundant information** -- **Clear ownership** of each documentation area - -## šŸŽ‰ Success Metrics - -- āœ… **28 files removed** (74% reduction) -- āœ… **All essential functionality preserved** -- āœ… **Test data management modernized** -- āœ… **Single, clear setup path established** -- āœ… **Documentation reflects current architecture** -- āœ… **Dramatically improved developer experience** -- āœ… **Massive reduction in maintenance burden** - -## šŸ”® Future Maintenance - -### What to Keep Updated -1. **README.md** - Project overview and features -2. **SETUP_GUIDE.md** - Setup instructions and troubleshooting -3. **README-API.md** - API documentation and examples - -### What's Self-Maintaining -- **api-docs.html** - Generated from OpenAPI spec -- **Test data** - Built into admin dashboard -- **OAuth setup** - Simplified to basic Google OAuth - ---- - -**The VIP Coordinator project now has clean, current, and maintainable documentation that accurately reflects the modern system architecture!** šŸš€ - -**Total Impact**: From 30+ files to 10 essential files (74% reduction) while significantly improving functionality and developer experience. \ No newline at end of file diff --git a/Dockerfile.e2e b/Dockerfile.e2e deleted file mode 100644 index b3a2f4b..0000000 --- a/Dockerfile.e2e +++ /dev/null @@ -1,23 +0,0 @@ -FROM mcr.microsoft.com/playwright:v1.41.0-jammy - -WORKDIR /app - -# Copy E2E test files -COPY ./e2e/package*.json ./e2e/ -RUN cd e2e && npm ci - -COPY ./e2e ./e2e - -# Install Playwright browsers -RUN cd e2e && npx playwright install - -# Set up non-root user -RUN useradd -m -u 1001 testuser && \ - chown -R testuser:testuser /app - -USER testuser - -WORKDIR /app/e2e - -# Default command runs tests -CMD ["npx", "playwright", "test"] \ No newline at end of file diff --git a/GOOGLE_OAUTH_DOMAIN_SETUP.md b/GOOGLE_OAUTH_DOMAIN_SETUP.md deleted file mode 100644 index 86e4e5e..0000000 --- a/GOOGLE_OAUTH_DOMAIN_SETUP.md +++ /dev/null @@ -1,108 +0,0 @@ -# Google OAuth2 Domain Setup for bsa.madeamess.online - -## šŸ”§ Current Configuration - -Your VIP Coordinator is now configured for your domain: -- **Backend URL**: `https://bsa.madeamess.online:3000` -- **Frontend URL**: `https://bsa.madeamess.online:5173` -- **OAuth Redirect URI**: `https://bsa.madeamess.online:3000/auth/google/callback` - -## šŸ“‹ Google Cloud Console Setup - -You need to update your Google Cloud Console OAuth2 configuration: - -### 1. Go to Google Cloud Console -- Visit: https://console.cloud.google.com/ -- Select your project (or create one) - -### 2. Enable APIs -- Go to "APIs & Services" → "Library" -- Enable "Google+ API" (or "People API") - -### 3. Configure OAuth2 Credentials -- Go to "APIs & Services" → "Credentials" -- Find your OAuth 2.0 Client ID: `308004695553-6k34bbq22frc4e76kejnkgq8mncepbbg.apps.googleusercontent.com` -- Click "Edit" (pencil icon) - -### 4. Update Authorized Redirect URIs -Add these exact URIs (case-sensitive): -``` -https://bsa.madeamess.online:3000/auth/google/callback -``` - -### 5. Update Authorized JavaScript Origins (if needed) -Add these origins: -``` -https://bsa.madeamess.online:3000 -https://bsa.madeamess.online:5173 -``` - -## šŸš€ Testing the OAuth Flow - -Once you've updated Google Cloud Console: - -1. **Visit the OAuth endpoint:** - ``` - https://bsa.madeamess.online:3000/auth/google - ``` - -2. **Expected flow:** - - Redirects to Google login - - After login, Google redirects to: `https://bsa.madeamess.online:3000/auth/google/callback` - - Backend processes the callback and redirects to: `https://bsa.madeamess.online:5173/auth/callback?token=JWT_TOKEN` - -3. **Check if backend is running:** - ```bash - curl https://bsa.madeamess.online:3000/api/health - ``` - -## šŸ” Troubleshooting - -### Common Issues: - -1. **"redirect_uri_mismatch" error:** - - Make sure the redirect URI in Google Console exactly matches: `https://bsa.madeamess.online:3000/auth/google/callback` - - No trailing slashes - - Exact case match - - Include the port number `:3000` - -2. **SSL/HTTPS issues:** - - Make sure your domain has valid SSL certificates - - Google requires HTTPS for production OAuth - -3. **Port access:** - - Ensure ports 3000 and 5173 are accessible from the internet - - Check firewall settings - -### Debug Commands: -```bash -# Check if containers are running -docker-compose -f docker-compose.dev.yml ps - -# Check backend logs -docker-compose -f docker-compose.dev.yml logs backend - -# Test backend health -curl https://bsa.madeamess.online:3000/api/health - -# Test auth status -curl https://bsa.madeamess.online:3000/auth/status -``` - -## šŸ“ Current Environment Variables - -Your `.env` file is configured with: -```bash -GOOGLE_CLIENT_ID=308004695553-6k34bbq22frc4e76kejnkgq8mncepbbg.apps.googleusercontent.com -GOOGLE_CLIENT_SECRET=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 -GOOGLE_REDIRECT_URI=https://bsa.madeamess.online:3000/auth/google/callback -FRONTEND_URL=https://bsa.madeamess.online:5173 -``` - -## āœ… Next Steps - -1. Update Google Cloud Console with the redirect URI above -2. Test the OAuth flow by visiting `https://bsa.madeamess.online:3000/auth/google` -3. Verify the frontend can handle the callback at `https://bsa.madeamess.online:5173/auth/callback` - -The OAuth2 system should now work correctly with your domain! šŸŽ‰ diff --git a/GOOGLE_OAUTH_QUICK_SETUP.md b/GOOGLE_OAUTH_QUICK_SETUP.md deleted file mode 100644 index 44a169c..0000000 --- a/GOOGLE_OAUTH_QUICK_SETUP.md +++ /dev/null @@ -1,48 +0,0 @@ -# Quick Google OAuth Setup Guide - -## Step 1: Get Your Google OAuth Credentials - -1. Go to [Google Cloud Console](https://console.cloud.google.com/) -2. Create a new project or select an existing one -3. Enable the Google+ API (or Google Identity API) -4. Go to "Credentials" → "Create Credentials" → "OAuth 2.0 Client IDs" -5. Set Application type to "Web application" -6. Add these Authorized redirect URIs: - - `http://localhost:5173/auth/google/callback` - - `http://bsa.madeamess.online:5173/auth/google/callback` - -## Step 2: Update Your .env File - -Replace these lines in `/home/kyle/Desktop/vip-coordinator/backend/.env`: - -```bash -# REPLACE THESE TWO LINES: -GOOGLE_CLIENT_ID=your-google-client-id-from-console -GOOGLE_CLIENT_SECRET=your-google-client-secret-from-console - -# WITH YOUR ACTUAL VALUES: -GOOGLE_CLIENT_ID=123456789-abcdefghijklmnop.apps.googleusercontent.com -GOOGLE_CLIENT_SECRET=GOCSPX-your_actual_secret_here -``` - -## Step 3: Restart the Backend - -After updating the .env file, restart the backend container: - -```bash -cd /home/kyle/Desktop/vip-coordinator -docker-compose -f docker-compose.dev.yml restart backend -``` - -## Step 4: Test the Login - -Visit: http://bsa.madeamess.online:5173 and click "Sign in with Google" -(The frontend proxies /auth requests to the backend automatically) - -## Bypass Option (Temporary) - -If you want to skip Google OAuth for now, visit: -http://bsa.madeamess.online:5173/admin-bypass - -This will take you directly to the admin dashboard without authentication. -(The frontend will proxy this request to the backend) diff --git a/GOOGLE_OAUTH_SETUP.md b/GOOGLE_OAUTH_SETUP.md deleted file mode 100644 index 4a4b6d3..0000000 --- a/GOOGLE_OAUTH_SETUP.md +++ /dev/null @@ -1,108 +0,0 @@ -# Google OAuth Setup Guide - -## Overview -Your VIP Coordinator now includes Google OAuth authentication! This guide will help you set up Google OAuth credentials so users can log in with their Google accounts. - -## Step 1: Google Cloud Console Setup - -### 1. Go to Google Cloud Console -Visit: https://console.cloud.google.com/ - -### 2. Create or Select a Project -- If you don't have a project, click "Create Project" -- Give it a name like "VIP Coordinator" -- Select your organization if applicable - -### 3. Enable Google+ API -- Go to "APIs & Services" → "Library" -- Search for "Google+ API" -- Click on it and press "Enable" - -### 4. Create OAuth 2.0 Credentials -- Go to "APIs & Services" → "Credentials" -- Click "Create Credentials" → "OAuth 2.0 Client IDs" -- Choose "Web application" as the application type -- Give it a name like "VIP Coordinator Web App" - -### 5. Configure Authorized URLs -**Authorized JavaScript origins:** -``` -http://bsa.madeamess.online:5173 -http://localhost:5173 -``` - -**Authorized redirect URIs:** -``` -http://bsa.madeamess.online:3000/auth/google/callback -http://localhost:3000/auth/google/callback -``` - -### 6. Save Your Credentials -- Copy the **Client ID** and **Client Secret** -- You'll need these for the next step - -## Step 2: Configure VIP Coordinator - -### 1. Access Admin Dashboard -- Go to: http://bsa.madeamess.online:5173/admin -- Enter the admin password: `admin123` - -### 2. Add Google OAuth Credentials -- Scroll to the "Google OAuth Credentials" section -- Paste your **Client ID** in the first field -- Paste your **Client Secret** in the second field -- Click "Save All Settings" - -## Step 3: Test the Setup - -### 1. Access the Application -- Go to: http://bsa.madeamess.online:5173 -- You should see a Google login button - -### 2. First Login (Admin Setup) -- The first person to log in will automatically become the administrator -- Subsequent users will be assigned the "coordinator" role by default -- Drivers will need to register separately - -### 3. User Roles -- **Administrator**: Full system access, user management, settings -- **Coordinator**: VIP and schedule management, driver assignments -- **Driver**: Personal schedule view, location updates - -## Troubleshooting - -### Common Issues: - -1. **"Blocked request" error** - - Make sure your domain is added to authorized JavaScript origins - - Check that the redirect URI matches exactly - -2. **"OAuth credentials not configured" warning** - - Verify you've entered both Client ID and Client Secret - - Make sure you clicked "Save All Settings" - -3. **Login button not working** - - Check browser console for errors - - Verify the backend is running on port 3000 - -### Getting Help: -- Check the browser console for error messages -- Verify all URLs match exactly (including http/https) -- Make sure the Google+ API is enabled in your project - -## Security Notes - -- Keep your Client Secret secure and never share it publicly -- The credentials are stored securely in your database -- Sessions last 24 hours as requested -- Only the frontend (port 5173) is exposed externally for security - -## Next Steps - -Once Google OAuth is working: -1. Test the login flow with different Google accounts -2. Assign appropriate roles to users through the admin dashboard -3. Create VIPs and schedules to test the full system -4. Set up additional API keys (AviationStack, etc.) as needed - -Your VIP Coordinator is now ready for secure, role-based access! diff --git a/KEYCLOAK_INTEGRATION_COMPLETE.md b/KEYCLOAK_INTEGRATION_COMPLETE.md deleted file mode 100644 index 2776581..0000000 --- a/KEYCLOAK_INTEGRATION_COMPLETE.md +++ /dev/null @@ -1,75 +0,0 @@ -# Keycloak Integration Complete! šŸŽ‰ - -## āœ… What Was Changed - -### Backend -1. **Created `backend/src/config/keycloak.ts`** - Keycloak JWT validation configuration -2. **Updated `backend/src/routes/auth.ts`** - Replaced Auth0 routes with Keycloak -3. **Updated `backend/src/services/userService.ts`** - Uses Keycloak user info API -4. **Updated `backend/src/middleware/auth.ts`** - Uses Keycloak config -5. **Updated `backend/src/index.ts`** - Uses Keycloak JWT middleware -6. **Updated `backend/.env`** - Replaced Auth0 vars with Keycloak vars -7. **Updated `docker-compose.dev.yml`** - Added Keycloak service, updated env vars - -### Frontend -1. **Created `frontend/src/contexts/KeycloakContext.tsx`** - Keycloak React provider -2. **Updated `frontend/src/main.tsx`** - Uses KeycloakProvider instead of Auth0Provider -3. **Updated `frontend/src/App.tsx`** - Uses useKeycloak hook -4. **Updated `frontend/src/components/Login.tsx`** - Uses Keycloak login -5. **Updated `frontend/src/pages/PendingApproval.tsx`** - Uses Keycloak token -6. **Updated `frontend/src/hooks/useAuthToken.ts`** - Uses Keycloak token -7. **Updated `frontend/package.json`** - Replaced @auth0/auth0-react with keycloak-js -8. **Updated `frontend/.env`** - Replaced Auth0 vars with Keycloak vars -9. **Updated `docker-compose.dev.yml`** - Updated frontend env vars - -## šŸ”§ Environment Variables - -### Backend (.env) -```env -KEYCLOAK_SERVER_URL=http://localhost:8080 -KEYCLOAK_REALM=vip-coordinator -KEYCLOAK_CLIENT_ID=vip-coordinator-frontend -``` - -### Frontend (.env) -```env -VITE_KEYCLOAK_URL=http://localhost:8080 -VITE_KEYCLOAK_REALM=vip-coordinator -VITE_KEYCLOAK_CLIENT_ID=vip-coordinator-frontend -``` - -## šŸš€ Next Steps - -1. **Rebuild containers:** - ```bash - docker compose -f docker-compose.dev.yml build - docker compose -f docker-compose.dev.yml up -d - ``` - -2. **Install frontend dependencies:** - ```bash - cd frontend - npm install - ``` - -3. **Test the login flow:** - - Go to http://localhost:5173 - - Click "Sign In with Keycloak" - - Login with Keycloak credentials - - First user becomes administrator - -## šŸ“ Notes - -- Database column `auth0_sub` still exists (stores Keycloak `sub` now) -- `identity_provider` column set to 'keycloak' for new users -- All Auth0 dependencies removed from package.json -- Keycloak runs on port 8080 -- Admin console: http://localhost:8080 (admin/admin) - -## šŸ› Troubleshooting - -If you see errors: -1. Make sure Keycloak is running: `docker ps | grep keycloak` -2. Check Keycloak logs: `docker logs vip-coordinator-keycloak-1` -3. Verify realm and client exist in Keycloak admin console -4. Check browser console for frontend errors diff --git a/KEYCLOAK_SETUP.md b/KEYCLOAK_SETUP.md deleted file mode 100644 index ecc98a2..0000000 --- a/KEYCLOAK_SETUP.md +++ /dev/null @@ -1,61 +0,0 @@ -# Keycloak Authentication Setup - -## Quick Start - -### 1. Start Services -```bash -docker compose -f docker-compose.dev.yml up -d -``` - -### 2. Access Keycloak Admin Console -- URL: http://localhost:8080 -- Username: `admin` -- Password: `admin` - -### 3. Create Realm -1. Click "Create Realm" -2. Name: `vip-coordinator` -3. Click "Create" - -### 4. Create Client (for your app) -1. Go to **Clients** → **Create client** -2. Client ID: `vip-coordinator-frontend` -3. Client type: `OpenID Connect` -4. Click **Next** -5. **Capability config:** - - āœ… Client authentication: OFF (public client) - - āœ… Authorization: OFF - - āœ… Standard flow: ON - - āœ… Direct access grants: ON -6. Click **Next** -7. **Login settings:** - - Valid redirect URIs: `http://localhost:5173/*` - - Web origins: `http://localhost:5173` - - Valid post logout redirect URIs: `http://localhost:5173/*` -8. Click **Save** - -### 5. Enable Google Social Login (Optional) -1. Go to **Identity providers** → **Add provider** → **Google** -2. Client ID: (your Google OAuth client ID) -3. Client Secret: (your Google OAuth secret) -4. Click **Save** - -### 6. Get Configuration -After setup, Keycloak provides: -- **Realm URL**: `http://localhost:8080/realms/vip-coordinator` -- **Client ID**: `vip-coordinator-frontend` -- **Discovery URL**: `http://localhost:8080/realms/vip-coordinator/.well-known/openid-configuration` - -## Next Steps -1. Update backend to use Keycloak JWT validation -2. Update frontend to use Keycloak React SDK -3. Test login flow - -## Benefits -- āœ… Self-hosted in Docker -- āœ… No external dependencies -- āœ… Full control over users and roles -- āœ… Social login support -- āœ… JWT tokens -- āœ… User management UI -- āœ… Role-based access control diff --git a/NAVIGATION_UX_IMPROVEMENTS.md b/NAVIGATION_UX_IMPROVEMENTS.md deleted file mode 100644 index e5c2caf..0000000 --- a/NAVIGATION_UX_IMPROVEMENTS.md +++ /dev/null @@ -1,277 +0,0 @@ -# Navigation UX Improvements - -## Changes Implemented āœ… - -### 1. **Nested Admin Navigation** -**Problem:** Top navigation was cluttered with both "Users" and "Admin Tools" tabs visible to administrators, taking up unnecessary space. - -**Solution:** -- Created a single "Admin" dropdown menu that contains both "Users" and "Admin Tools" -- Used Shield icon to represent admin functions (more intuitive than separate icons) -- Desktop: Hover/click dropdown with smooth animation -- Mobile: Expandable section with chevron indicator -- Reduces navigation items by 1 for admins, making the UI cleaner - -**Benefits:** -- āœ… Cleaner top navigation (reduced clutter by 50% for admin items) -- āœ… Hierarchical organization (admin functions grouped together) -- āœ… Better visual hierarchy with icon and dropdown indicator -- āœ… Follows popular UI patterns (Gmail, GitHub, AWS Console) - -### 2. **Reorganized Navigation by Workflow Priority** -**Before:** -1. Dashboard -2. War Room -3. VIPs -4. Drivers -5. Vehicles -6. Schedule -7. Flights -8. Users (admin) -9. Admin Tools (admin) - -**After:** -1. Dashboard (always visible - home) -2. War Room (high-priority operations) -3. VIPs (core resource) -4. Drivers (core resource) -5. Vehicles (supporting resource) -6. Schedule (planning) -7. Flights (logistics) -8. **Admin** (admin-only dropdown containing Users & Admin Tools) - -**Rationale:** -- Most frequently accessed items appear first -- Resources organized by importance to operations -- Admin functions grouped at the end (less frequently used) - -### 3. **Improved Mobile Navigation** -**Desktop (≄ 1024px):** -- Horizontal navigation bar with dropdown menu -- Click outside to close dropdown -- Chevron rotates when opened (visual feedback) - -**Mobile/Tablet (< 1024px):** -- Hamburger menu opens drawer from left -- Admin section is expandable/collapsible -- Nested items are indented for visual hierarchy -- All touch targets meet 44px minimum - -## Popular UX Trends Incorporated - -### āœ… **Progressive Disclosure** -- Hide complexity by nesting related items -- Show only what users need based on their role -- Expandable sections reveal more details on demand - -### āœ… **Grouping Related Actions** -- Admin functions are logically grouped together -- Resources grouped by type (people vs. things vs. logistics) - -### āœ… **Visual Hierarchy with Icons** -- Shield icon for admin functions (security/authority) -- Chevron indicators for expandable sections -- Clear active state highlighting - -### āœ… **Touch-Friendly Design** -- All buttons meet 44px minimum touch target -- Expandable sections have clear hit areas -- Proper spacing between interactive elements - -### āœ… **Consistent Patterns** -- Desktop dropdown follows standard web patterns -- Mobile drawer follows iOS/Android conventions -- Active state highlighting consistent across all nav items - -## Additional UX Recommendations - -### šŸŽÆ **High Priority Improvements** - -#### 1. **Add Breadcrumb Navigation** -**Why:** Users can get lost in nested admin pages -**How:** Add breadcrumbs below the header showing: Dashboard > Admin > Users -**Example:** -``` -Home / Admin / Users -``` -**Benefit:** Faster navigation back to parent pages - -#### 2. **Add Keyboard Shortcuts** -**Why:** Power users work faster with keyboard navigation -**How:** -- `Shift + D` → Dashboard -- `Shift + W` → War Room -- `Shift + V` → VIPs -- `Shift + A` → Admin dropdown toggle -- `Esc` → Close dropdown/drawer -**Benefit:** 40% faster navigation for frequent users - -#### 3. **Add Search Bar in Navigation** -**Why:** Quick access to specific VIPs/drivers/events -**How:** Add global search icon (magnifying glass) in top nav -**Example:** Slack, GitHub, Linear all have this -**Benefit:** Find anything in 1-2 seconds instead of navigating through pages - -#### 4. **Add Notification Badge** -**Why:** Admins need to know about pending approvals -**How:** Show red badge with count on Admin dropdown when there are pending approvals -**Example:** `Admin [3]` where 3 = pending approvals -**Benefit:** Proactive alerts, faster response time - -#### 5. **Add Recently Viewed** -**Why:** Users often switch between same 2-3 pages -**How:** Add "Recent" section at bottom of mobile drawer with last 3 visited pages -**Example:** Notion, Linear, Jira all have this -**Benefit:** 50% reduction in navigation clicks for common workflows - -### šŸ”¹ **Medium Priority Improvements** - -#### 6. **Favorites/Pinning** -**Why:** Let users customize their navigation -**How:** Add star icon next to nav items, pinned items appear first -**Example:** Chrome bookmarks bar -**Benefit:** Personalized experience for different user types - -#### 7. **Context-Aware Navigation** -**Why:** Show relevant actions based on current page -**How:** When viewing a VIP, show quick actions in header (Schedule, Edit, Delete) -**Example:** GitHub's file view has Edit/Delete buttons in header -**Benefit:** Reduces clicks for common actions - -#### 8. **Add Quick Actions Menu** -**Why:** Common tasks should be 1 click away -**How:** Add "+" button in header with dropdown: -- Add VIP -- Add Driver -- Create Event -- Schedule Trip -**Example:** Asana, Notion, Linear all have "New" buttons -**Benefit:** Faster task creation workflow - -#### 9. **Collapsible Navigation (Desktop)** -**Why:** Maximize screen space for content -**How:** Add toggle to collapse nav to icons only -**Example:** Gmail's left sidebar can collapse -**Benefit:** More room for tables/calendars on smaller screens - -#### 10. **Smart Positioning** -**Why:** Dropdown shouldn't go off-screen on small viewports -**How:** Auto-detect dropdown position and flip if needed -**Example:** If near right edge, open dropdown to the left -**Benefit:** Better UX on all screen sizes - -### šŸ’” **Low Priority / Future Enhancements** - -#### 11. **Tooltips on Hover** -**Why:** Explain what each navigation item does -**How:** Show brief description on hover -**Example:** "War Room - Real-time operations dashboard" -**Benefit:** Better onboarding for new users - -#### 12. **Navigation Analytics** -**Why:** Understand how users navigate the app -**How:** Track which nav items are clicked most often -**Example:** Google Analytics events -**Benefit:** Data-driven decisions for future nav improvements - -#### 13. **Navigation Themes** -**Why:** Let users customize appearance -**How:** Light/dark mode toggle in user menu -**Example:** Most modern apps have dark mode -**Benefit:** Better user experience in different lighting conditions - -#### 14. **Mobile App-Style Navigation** -**Why:** Native app feel on tablets -**How:** Add bottom tab bar on tablets with most common items -**Example:** Instagram, Twitter have bottom nav bars -**Benefit:** Easier one-handed use on tablets - -#### 15. **Command Palette** -**Why:** Super fast navigation for power users -**How:** Add Cmd+K / Ctrl+K shortcut to open command palette -**Example:** Linear, Raycast, VS Code all have this -**Benefit:** Navigate anywhere instantly - -## UX Principles Applied - -### 1. **Hick's Law** -- Reduced number of top-level choices by grouping admin items -- Faster decision-making when navigating - -### 2. **Fitts's Law** -- Touch targets meet 44px minimum -- Larger click areas for more important actions (Dashboard, War Room) - -### 3. **Jakob's Law** -- Users spend most of their time on other sites -- Our patterns match Gmail, GitHub, AWS (familiar) - -### 4. **Progressive Disclosure** -- Hide advanced features (admin tools) behind dropdown -- Show complexity only when needed - -### 5. **Recognition over Recall** -- Icons help users recognize functions quickly -- Visual hierarchy reduces cognitive load - -## Metrics to Track - -After implementing these improvements, measure: -1. **Time to Complete Tasks** - Should decrease by 20-30% -2. **Click Depth** - Average clicks to reach destination -3. **Navigation Bounce Rate** - % of users who click wrong nav item -4. **Mobile Usage** - Should increase with better mobile nav -5. **User Satisfaction** - Survey scores should improve - -## Testing Checklist - -- [x] Desktop navigation works (≄ 1024px) -- [x] Tablet portrait navigation works (768px) -- [x] Tablet landscape navigation works (1024px) -- [x] Mobile navigation works (< 768px) -- [x] Dropdown closes when clicking outside -- [x] Admin items only visible to administrators -- [x] Active state highlighting works correctly -- [x] Touch targets meet 44px minimum -- [x] Keyboard navigation works (Tab, Enter, Esc) -- [x] Screen reader compatible (aria-labels added) - -## Browser Compatibility - -Tested and working on: -- āœ… Chrome/Edge (Chromium) - Latest -- āœ… Safari - Latest -- āœ… Firefox - Latest -- āœ… Mobile Safari (iOS) -- āœ… Chrome Mobile (Android) - -## Accessibility (WCAG 2.1 AA) - -- āœ… Keyboard navigation supported -- āœ… ARIA labels added for screen readers -- āœ… Focus indicators visible -- āœ… Color contrast meets minimum ratio -- āœ… Touch targets meet minimum size -- āœ… Semantic HTML used - -## Performance Impact - -- **Bundle Size:** +2KB (dropdown component + animations) -- **Load Time:** No measurable impact -- **Runtime Performance:** Negligible (CSS transitions only) -- **Mobile Performance:** Improved (fewer DOM nodes in mobile menu) - ---- - -## Next Steps - -1. **Monitor user feedback** - Are users finding the new navigation easier? -2. **Implement high-priority recommendations** - Breadcrumbs, keyboard shortcuts, search -3. **A/B test variations** - Try different icon sets, positioning -4. **Iterate based on data** - Use analytics to inform next improvements - ---- - -**Last Updated:** 2026-01-31 -**Author:** Claude Sonnet 4.5 -**Status:** āœ… Implemented and Tested diff --git a/NOTIFICATION_BADGE_IMPLEMENTATION.md b/NOTIFICATION_BADGE_IMPLEMENTATION.md deleted file mode 100644 index 621ed5d..0000000 --- a/NOTIFICATION_BADGE_IMPLEMENTATION.md +++ /dev/null @@ -1,324 +0,0 @@ -# Notification Badge Implementation - -## Overview -Implemented notification badges on the Admin dropdown menu to show pending user approvals, making admin tasks more visible while decluttering the Dashboard. - -## Changes Implemented āœ… - -### 1. **Added Pending Approvals Badge to Admin Dropdown** - -#### Desktop Navigation ([Layout.tsx](frontend/src/components/Layout.tsx)) -```tsx - -Admin -{pendingApprovalsCount > 0 && ( - - {pendingApprovalsCount} - -)} -``` - -**Visual Result:** -- Badge appears next to "Admin" text when there are pending approvals -- Red background (`bg-red-600`) draws attention -- Small, rounded pill shape -- Auto-hides when count is 0 - -#### Mobile Navigation ([Layout.tsx](frontend/src/components/Layout.tsx)) -```tsx - -Admin -{pendingApprovalsCount > 0 && ( - - {pendingApprovalsCount} - -)} -``` - -**Features:** -- Same badge styling on mobile drawer -- Positioned next to Admin text -- Updates in real-time when users are approved - -### 2. **Removed Pending Approvals from Dashboard** - -#### Before: -``` -Dashboard Stats Grid: -ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” -│ Total VIPs │Active Drivers│ Events Today │ Flights Today│Pending Approvals(*)| -ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ -(*) Only visible to admins -``` - -#### After: -``` -Dashboard Stats Grid: -ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” -│ Total VIPs │Active Drivers│ Events Today │ Flights Today│ -ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ -Cleaner, more focused on operations -``` - -**Changes Made to [Dashboard.tsx](frontend/src/pages/Dashboard.tsx):** - -1. **Removed imports:** - - `UserCheck` icon (no longer needed) - - `useAuth` hook (no longer checking isAdmin) - -2. **Removed interfaces:** - - `User` interface (moved to Layout.tsx) - -3. **Removed queries:** - - `users` query (now only in Layout.tsx for admins) - - `isAdmin` check - - `backendUser` reference - -4. **Removed calculations:** - - `pendingApprovals` count - -5. **Removed stats:** - - Pending Approvals stat card - -6. **Updated grid layout:** - - **Before:** `grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5` - - **After:** `grid-cols-1 sm:grid-cols-2 lg:grid-cols-4` - - Cleaner breakpoints for exactly 4 stats - -### 3. **Real-Time Badge Updates** - -The badge automatically updates when: -- A user is approved (count decreases) -- A new user registers (count increases) -- Admin navigates between pages (React Query refetches) - -**Implementation Details:** -```tsx -// Layout.tsx - Fetch pending approvals for admins -const { data: users } = useQuery({ - queryKey: ['users'], - queryFn: async () => { - const { data } = await api.get('/users'); - return data; - }, - enabled: canAccessAdmin, // Only fetch if user can access admin -}); - -const pendingApprovalsCount = users?.filter((u) => !u.isApproved).length || 0; -``` - -**Benefits:** -- Uses React Query caching - no extra API calls -- Automatically refetches on window focus -- Shares cache with Users page (efficient) - -## Design Rationale - -### Why Badges on Navigation? -1. **Always Visible** - Admins see pending approvals count everywhere -2. **Non-Intrusive** - Doesn't clutter the main dashboard -3. **Industry Standard** - Gmail, Slack, GitHub all use nav badges -4. **Action-Oriented** - Badge is on the menu that leads to the Users page - -### Why Remove from Dashboard? -1. **Not Everyone Cares** - Only admins care about pending approvals -2. **Operational Focus** - Dashboard should focus on VIP operations, not admin tasks -3. **Cleaner Layout** - 4 stats look better than 5 (especially on tablets) -4. **Better Placement** - Badge on Admin menu is more contextual - -## Visual Examples - -### Desktop Navigation -``` -Before: -Dashboard | War Room | VIPs | Drivers | Vehicles | Schedule | Flights | Users | Admin Tools - -After: -Dashboard | War Room | VIPs | Drivers | Vehicles | Schedule | Flights | Admin [3] ā–¼ - ā”œā”€ Users - └─ Admin Tools -``` - -### Mobile Navigation -``` -Before: -☰ Menu -ā”œā”€ Dashboard -ā”œā”€ War Room -ā”œā”€ VIPs -ā”œā”€ Drivers -ā”œā”€ Vehicles -ā”œā”€ Schedule -ā”œā”€ Flights -ā”œā”€ Users -└─ Admin Tools - -After: -☰ Menu -ā”œā”€ Dashboard -ā”œā”€ War Room -ā”œā”€ VIPs -ā”œā”€ Drivers -ā”œā”€ Vehicles -ā”œā”€ Schedule -ā”œā”€ Flights -└─ Admin [3] ā–¼ - ā”œā”€ Users - └─ Admin Tools -``` - -## Dashboard Grid Responsiveness - -### New Layout Breakpoints: -- **Mobile (< 640px):** 1 column -- **Small (640px - 1024px):** 2 columns (2x2 grid) -- **Large (≄ 1024px):** 4 columns (1x4 row) - -**Visual:** -``` -Mobile: Tablet: Desktop: -ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” ā”Œā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā” ā”Œā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā” -│ VIPs │ │VIPs │Drive│ │VIP│Drv│Evt│Flt│ -ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤ ā”œā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”¤ ā””ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”˜ -│ Drivers │ │Evnt │Flgt │ -ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤ ā””ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”˜ -│ Events │ -ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤ -│ Flights │ -ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ -``` - -## Color Scheme - -**Badge Color:** `bg-red-600` -- **Reason:** Red signifies action needed (industry standard) -- **Contrast:** White text on red meets WCAG AAA standards -- **Visibility:** Stands out but not garish - -**Alternative Colors Considered:** -- āŒ Yellow (`bg-yellow-500`) - Too soft, less urgent -- āŒ Orange (`bg-orange-500`) - Could work but less common -- āœ… Red (`bg-red-600`) - Standard for notifications - -## Accessibility - -**ARIA Support:** -- Badge has `aria-label` via parent button context -- Screen readers announce: "Admin, 3 notifications" -- Color isn't the only indicator (badge contains number) - -**Keyboard Navigation:** -- Badge doesn't interfere with keyboard nav -- Still tab to Admin dropdown and press Enter -- Badge is purely visual enhancement - -**Touch Targets:** -- Badge doesn't change touch target size -- Admin button still meets 44px minimum - -## Performance Impact - -**Bundle Size:** +1KB (badge styling + query logic) -**Runtime Performance:** -- āœ… Uses existing React Query cache -- āœ… No extra API calls (shares with Users page) -- āœ… Minimal re-renders (only when count changes) - -**Memory Impact:** Negligible (one extra computed value) - -## Testing Checklist - -- [x] Badge shows correct count on desktop -- [x] Badge shows correct count on mobile -- [x] Badge hides when count is 0 -- [x] Badge updates when user is approved -- [x] Dashboard grid looks good with 4 stats -- [x] No console errors -- [x] HMR updates working correctly -- [x] React Query cache working efficiently - -## Browser Compatibility - -Tested and working on: -- āœ… Chrome/Edge (Chromium) - Latest -- āœ… Safari - Latest -- āœ… Firefox - Latest -- āœ… Mobile Safari (iOS) -- āœ… Chrome Mobile (Android) - -## Future Enhancements - -### šŸŽÆ Potential Improvements - -1. **Click Badge to Go to Users Page** - ```tsx - - 3 - - ``` - - One-click access to pending users - - Even more convenient - -2. **Different Badge Colors by Urgency** - ```tsx - {pendingApprovalsCount > 10 && 'bg-red-600'} // Many pending - {pendingApprovalsCount > 5 && 'bg-orange-500'} // Some pending - {pendingApprovalsCount > 0 && 'bg-yellow-500'} // Few pending - ``` - - Visual priority system - - More information at a glance - -3. **Hover Tooltip with Details** - ```tsx - - 3 - - ``` - - Show who's waiting - - Better context - -4. **Real-time Updates via WebSocket** - - Instant badge updates - - No need to refresh - - Better UX for multi-admin scenarios - -5. **Sound/Visual Alert for New Pending** - - Notification sound when new user registers - - Brief animation on badge - - More proactive alerts - -## Comparison to Industry Standards - -### Gmail -- Red badge on unread count -- Shows on sidebar categories -- āœ… We follow this pattern - -### Slack -- Red badge for mentions -- Shows on channel names -- āœ… Similar approach - -### GitHub -- Blue badge for notifications -- Shows on bell icon -- šŸ”µ We could add hover dropdown with details (future) - -### AWS Console -- No notification badges (different UX pattern) -- āŒ Not applicable - -## Metrics to Track - -After this implementation, monitor: -1. **Time to Approval** - Should decrease (more visible) -2. **Admin Engagement** - Track clicks on Admin dropdown -3. **User Satisfaction** - Survey admins about visibility -4. **Performance** - No measurable impact expected - ---- - -**Implementation Date:** 2026-01-31 -**Author:** Claude Sonnet 4.5 -**Status:** āœ… Completed and Tested -**Breaking Changes:** None (additive only) diff --git a/OAUTH_CALLBACK_FIX_SUMMARY.md b/OAUTH_CALLBACK_FIX_SUMMARY.md deleted file mode 100644 index 774ba4e..0000000 --- a/OAUTH_CALLBACK_FIX_SUMMARY.md +++ /dev/null @@ -1,92 +0,0 @@ -# āœ… OAuth Callback Issue RESOLVED! - -## šŸŽÆ Problem Identified & Fixed - -**Root Cause:** The Vite proxy configuration was intercepting ALL `/auth/*` routes and forwarding them to the backend, including the OAuth callback route `/auth/google/callback` that needed to be handled by the React frontend. - -## šŸ”§ Solution Applied - -**Fixed Vite Configuration** (`frontend/vite.config.ts`): - -**BEFORE (Problematic):** -```typescript -proxy: { - '/api': { - target: 'http://backend:3000', - changeOrigin: true, - }, - '/auth': { // āŒ This was intercepting ALL /auth routes - target: 'http://backend:3000', - changeOrigin: true, - }, -} -``` - -**AFTER (Fixed):** -```typescript -proxy: { - '/api': { - target: 'http://backend:3000', - changeOrigin: true, - }, - // āœ… Only proxy specific auth endpoints, not the callback route - '/auth/setup': { - target: 'http://backend:3000', - changeOrigin: true, - }, - '/auth/google/url': { - target: 'http://backend:3000', - changeOrigin: true, - }, - '/auth/google/exchange': { - target: 'http://backend:3000', - changeOrigin: true, - }, - '/auth/me': { - target: 'http://backend:3000', - changeOrigin: true, - }, - '/auth/logout': { - target: 'http://backend:3000', - changeOrigin: true, - }, - '/auth/status': { - target: 'http://backend:3000', - changeOrigin: true, - }, -} -``` - -## šŸ”„ How OAuth Flow Works Now - -1. **User clicks "Continue with Google"** - - Frontend calls `/auth/google/url` → Proxied to backend - - Backend returns Google OAuth URL with correct redirect URI - -2. **Google Authentication** - - User authenticates with Google - - Google redirects to: `https://bsa.madeamess.online:5173/auth/google/callback?code=...` - -3. **Frontend Handles Callback** āœ… - - `/auth/google/callback` is NOT proxied to backend - - React Router serves the frontend app - - Login component detects callback route and authorization code - - Calls `/auth/google/exchange` → Proxied to backend - - Backend exchanges code for JWT token - - Frontend receives token and user info, logs user in - -## šŸŽ‰ Current Status - -**āœ… All containers running successfully** -**āœ… Vite proxy configuration fixed** -**āœ… OAuth callback route now handled by frontend** -**āœ… Backend OAuth endpoints working correctly** - -## 🧪 Test the Fix - -1. Visit your domain: `https://bsa.madeamess.online:5173` -2. Click "Continue with Google" -3. Complete Google authentication -4. You should be redirected back and logged in successfully! - -The OAuth callback handoff issue has been completely resolved! šŸŽŠ diff --git a/OAUTH_FRONTEND_ONLY_SETUP.md b/OAUTH_FRONTEND_ONLY_SETUP.md deleted file mode 100644 index 621bf08..0000000 --- a/OAUTH_FRONTEND_ONLY_SETUP.md +++ /dev/null @@ -1,216 +0,0 @@ -# OAuth2 Setup for Frontend-Only Port (5173) - -## šŸŽÆ Configuration Overview - -Since you're only forwarding port 5173, the OAuth flow has been configured to work entirely through the frontend: - -**Current Setup:** -- **Frontend**: `https://bsa.madeamess.online:5173` (publicly accessible) -- **Backend**: `http://localhost:3000` (internal only) -- **OAuth Redirect**: `https://bsa.madeamess.online:5173/auth/google/callback` - -## šŸ”§ Google Cloud Console Configuration - -**Update your OAuth2 client with this redirect URI:** -``` -https://bsa.madeamess.online:5173/auth/google/callback -``` - -**Authorized JavaScript Origins:** -``` -https://bsa.madeamess.online:5173 -``` - -## šŸ”„ How the OAuth Flow Works - -### 1. **Frontend Initiates OAuth** -```javascript -// Frontend calls backend to get OAuth URL -const response = await fetch('/api/auth/google/url'); -const { url } = await response.json(); -window.location.href = url; // Redirect to Google -``` - -### 2. **Google Redirects to Frontend** -``` -https://bsa.madeamess.online:5173/auth/google/callback?code=AUTHORIZATION_CODE -``` - -### 3. **Frontend Exchanges Code for Token** -```javascript -// Frontend sends code to backend -const response = await fetch('/api/auth/google/exchange', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ code: authorizationCode }) -}); - -const { token, user } = await response.json(); -// Store token in localStorage or secure cookie -``` - -## šŸ› ļø Backend API Endpoints - -### **GET /api/auth/google/url** -Returns the Google OAuth URL for frontend to redirect to. - -**Response:** -```json -{ - "url": "https://accounts.google.com/o/oauth2/v2/auth?client_id=..." -} -``` - -### **POST /api/auth/google/exchange** -Exchanges authorization code for JWT token. - -**Request:** -```json -{ - "code": "authorization_code_from_google" -} -``` - -**Response:** -```json -{ - "token": "jwt_token_here", - "user": { - "id": "user_id", - "email": "user@example.com", - "name": "User Name", - "picture": "profile_picture_url", - "role": "coordinator" - } -} -``` - -### **GET /api/auth/status** -Check authentication status. - -**Headers:** -``` -Authorization: Bearer jwt_token_here -``` - -**Response:** -```json -{ - "authenticated": true, - "user": { ... } -} -``` - -## šŸ“ Frontend Implementation Example - -### **Login Component** -```javascript -const handleGoogleLogin = async () => { - try { - // Get OAuth URL from backend - const response = await fetch('/api/auth/google/url'); - const { url } = await response.json(); - - // Redirect to Google - window.location.href = url; - } catch (error) { - console.error('Login failed:', error); - } -}; -``` - -### **OAuth Callback Handler** -```javascript -// In your callback route component -useEffect(() => { - const urlParams = new URLSearchParams(window.location.search); - const code = urlParams.get('code'); - const error = urlParams.get('error'); - - if (error) { - console.error('OAuth error:', error); - return; - } - - if (code) { - exchangeCodeForToken(code); - } -}, []); - -const exchangeCodeForToken = async (code) => { - try { - const response = await fetch('/api/auth/google/exchange', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ code }) - }); - - const { token, user } = await response.json(); - - // Store token securely - localStorage.setItem('authToken', token); - - // Redirect to dashboard - navigate('/dashboard'); - } catch (error) { - console.error('Token exchange failed:', error); - } -}; -``` - -### **API Request Helper** -```javascript -const apiRequest = async (url, options = {}) => { - const token = localStorage.getItem('authToken'); - - return fetch(url, { - ...options, - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}`, - ...options.headers - } - }); -}; -``` - -## šŸš€ Testing the Setup - -### 1. **Test OAuth URL Generation** -```bash -curl http://localhost:3000/api/auth/google/url -``` - -### 2. **Test Full Flow** -1. Visit: `https://bsa.madeamess.online:5173` -2. Click login button -3. Should redirect to Google -4. After Google login, should redirect back to: `https://bsa.madeamess.online:5173/auth/google/callback?code=...` -5. Frontend should exchange code for token -6. User should be logged in - -### 3. **Test API Access** -```bash -# Get a token first, then: -curl -H "Authorization: Bearer YOUR_JWT_TOKEN" http://localhost:3000/api/auth/status -``` - -## āœ… Current Status - -**āœ… Containers Running:** -- Backend: http://localhost:3000 -- Frontend: http://localhost:5173 -- Database: PostgreSQL on port 5432 -- Redis: Running on port 6379 - -**āœ… OAuth Configuration:** -- Redirect URI: `https://bsa.madeamess.online:5173/auth/google/callback` -- Frontend URL: `https://bsa.madeamess.online:5173` -- Backend endpoints ready for frontend integration - -**šŸ”„ Next Steps:** -1. Update Google Cloud Console with the redirect URI -2. Implement frontend OAuth handling -3. Test the complete flow - -The OAuth system is now properly configured to work through your frontend-only port setup! šŸŽ‰ diff --git a/PERMISSION_ISSUES_FIXED.md b/PERMISSION_ISSUES_FIXED.md deleted file mode 100644 index 3ad0462..0000000 --- a/PERMISSION_ISSUES_FIXED.md +++ /dev/null @@ -1,122 +0,0 @@ -# User Permission Issues - Debugging Summary - -## Issues Found and Fixed - -### 1. **Token Storage Inconsistency** āŒ → āœ… -**Problem**: Different components were using different localStorage keys for the authentication token: -- `App.tsx` used `localStorage.getItem('authToken')` -- `UserManagement.tsx` used `localStorage.getItem('token')` in one place - -**Fix**: Standardized all components to use `'authToken'` as the localStorage key. - -**Files Fixed**: -- `frontend/src/components/UserManagement.tsx` - Line 69: Changed `localStorage.getItem('token')` to `localStorage.getItem('authToken')` - -### 2. **Missing Authentication Headers in VIP Operations** āŒ → āœ… -**Problem**: The VIP management operations (add, edit, delete, fetch) were not including authentication headers, causing 401/403 errors. - -**Fix**: Added proper authentication headers to all VIP API calls. - -**Files Fixed**: -- `frontend/src/pages/VipList.tsx`: - - Added `apiCall` import from config - - Updated `fetchVips()` to include `Authorization: Bearer ${token}` header - - Updated `handleAddVip()` to include authentication headers - - Updated `handleEditVip()` to include authentication headers - - Updated `handleDeleteVip()` to include authentication headers - - Fixed TypeScript error with EditVipForm props - -### 3. **API URL Configuration** āœ… -**Status**: Already correctly configured -- Frontend uses `https://api.bsa.madeamess.online` via `apiCall` helper -- Backend has proper CORS configuration for the frontend domain - -### 4. **Backend Authentication Middleware** āœ… -**Status**: Already properly implemented -- VIP routes are protected with `requireAuth` middleware -- Role-based access control with `requireRole(['coordinator', 'administrator'])` -- User management routes require `administrator` role - -## Backend Permission Structure (Already Working) - -```typescript -// VIP Operations - Require coordinator or administrator role -app.post('/api/vips', requireAuth, requireRole(['coordinator', 'administrator'])) -app.put('/api/vips/:id', requireAuth, requireRole(['coordinator', 'administrator'])) -app.delete('/api/vips/:id', requireAuth, requireRole(['coordinator', 'administrator'])) -app.get('/api/vips', requireAuth) // All authenticated users can view - -// User Management - Require administrator role only -app.get('/auth/users', requireAuth, requireRole(['administrator'])) -app.patch('/auth/users/:email/role', requireAuth, requireRole(['administrator'])) -app.delete('/auth/users/:email', requireAuth, requireRole(['administrator'])) -``` - -## Role Hierarchy - -1. **Administrator**: - - Full access to all features - - Can manage users and change roles - - Can add/edit/delete VIPs - - Can manage drivers and schedules - -2. **Coordinator**: - - Can add/edit/delete VIPs - - Can manage drivers and schedules - - Cannot manage users or change roles - -3. **Driver**: - - Can view assigned schedules - - Can update status - - Cannot manage VIPs or users - -## Testing the Fixes - -After these fixes, the admin should now be able to: - -1. āœ… **Add VIPs**: The "Add New VIP" button will work with proper authentication -2. āœ… **Change User Roles**: The role dropdown in User Management will work correctly -3. āœ… **View All Data**: All API calls now include proper authentication headers - -## What Was Happening Before - -1. **VIP Operations Failing**: When clicking "Add New VIP" or trying to edit/delete VIPs, the requests were being sent without authentication headers, causing the backend to return 401 Unauthorized errors. - -2. **User Role Changes Failing**: The user management component was using the wrong token storage key, so role update requests were failing with authentication errors. - -3. **Silent Failures**: The frontend wasn't showing proper error messages, so it appeared that buttons weren't working when actually the API calls were being rejected. - -## Additional Recommendations - -1. **Error Handling**: Consider adding user-friendly error messages when API calls fail -2. **Loading States**: Add loading indicators for user actions (role changes, VIP operations) -3. **Token Refresh**: Implement token refresh logic for long-running sessions -4. **Audit Logging**: Consider logging user actions for security auditing - -## Files Modified - -1. `frontend/src/components/UserManagement.tsx` - Fixed token storage key inconsistency -2. `frontend/src/pages/VipList.tsx` - Added authentication headers to all VIP operations -3. `frontend/src/pages/DriverList.tsx` - Added authentication headers to all driver operations -4. `frontend/src/pages/Dashboard.tsx` - Added authentication headers to dashboard data fetching -5. `vip-coordinator/PERMISSION_ISSUES_FIXED.md` - This documentation - -## Site-Wide Authentication Fix - -You were absolutely right - this was a site-wide problem! I've now fixed authentication headers across all major components: - -### āœ… Fixed Components: -- **VipList**: All CRUD operations (create, read, update, delete) now include auth headers -- **DriverList**: All CRUD operations now include auth headers -- **Dashboard**: Data fetching for VIPs, drivers, and schedules now includes auth headers -- **UserManagement**: Token storage key fixed and all operations include auth headers - -### šŸ” Components Still Needing Review: -- `ScheduleManager.tsx` - Schedule operations -- `DriverSelector.tsx` - Driver availability checks -- `VipDetails.tsx` - VIP detail fetching -- `DriverDashboard.tsx` - Driver schedule operations -- `FlightStatus.tsx` - Flight data fetching -- `VipForm.tsx` & `EditVipForm.tsx` - Flight validation - -The permission system is now working correctly with proper authentication and authorization for the main management operations! diff --git a/PORT_3000_SETUP_GUIDE.md b/PORT_3000_SETUP_GUIDE.md deleted file mode 100644 index 039ae85..0000000 --- a/PORT_3000_SETUP_GUIDE.md +++ /dev/null @@ -1,173 +0,0 @@ -# šŸš€ Port 3000 Direct Access Setup Guide - -## Your Optimal Setup (Based on Google's AI Analysis) - -Google's AI correctly identified that the OAuth redirect to `localhost:3000` is the issue. Here's the **simplest solution**: - -## Option A: Expose Port 3000 Directly (Recommended) - -### 1. Router/Firewall Configuration -Configure your router to forward **both ports**: - -``` -Internet → Router → Your Server -Port 443/80 → Frontend (port 5173) āœ… Already working -Port 3000 → Backend (port 3000) āš ļø ADD THIS -``` - -### 2. Google Cloud Console Update - -**Authorized JavaScript origins:** -``` -https://bsa.madeamess.online -https://bsa.madeamess.online:3000 -``` - -**Authorized redirect URIs:** -``` -https://bsa.madeamess.online:3000/auth/google/callback -``` - -### 3. Environment Variables (Already Updated) -āœ… I've already updated your `.env` file: -```bash -GOOGLE_REDIRECT_URI=https://bsa.madeamess.online:3000/auth/google/callback -FRONTEND_URL=https://bsa.madeamess.online -``` - -### 4. SSL Certificate for Port 3000 -You'll need SSL on port 3000. Options: - -**Option A: Reverse proxy for port 3000 too** -```nginx -# Frontend (existing) -server { - listen 443 ssl; - server_name bsa.madeamess.online; - location / { - proxy_pass http://localhost:5173; - } -} - -# Backend (add this) -server { - listen 3000 ssl; - server_name bsa.madeamess.online; - ssl_certificate /path/to/your/cert.pem; - ssl_certificate_key /path/to/your/key.pem; - - location / { - proxy_pass http://localhost:3000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} -``` - -**Option B: Direct Docker port mapping with SSL termination** -```yaml -# In docker-compose.dev.yml -services: - backend: - ports: - - "3000:3000" - environment: - - SSL_CERT_PATH=/certs/cert.pem - - SSL_KEY_PATH=/certs/key.pem -``` - -## Option B: Alternative - Use Standard HTTPS Port - -If you don't want to expose port 3000, use a subdomain: - -### 1. Create Subdomain -Point `api.bsa.madeamess.online` to your server - -### 2. Update Environment Variables -```bash -GOOGLE_REDIRECT_URI=https://api.bsa.madeamess.online/auth/google/callback -``` - -### 3. Configure Reverse Proxy -```nginx -server { - listen 443 ssl; - server_name api.bsa.madeamess.online; - - location / { - proxy_pass http://localhost:3000; - # ... headers - } -} -``` - -## Testing Your Setup - -### 1. Restart Containers -```bash -cd /home/kyle/Desktop/vip-coordinator -docker-compose -f docker-compose.dev.yml restart -``` - -### 2. Test Backend Accessibility -```bash -# Should work from internet -curl https://bsa.madeamess.online:3000/auth/setup -# Should return: {"setupCompleted":true,"firstAdminCreated":false,"oauthConfigured":true} -``` - -### 3. Test OAuth URL Generation -```bash -curl https://bsa.madeamess.online:3000/auth/google/url -# Should return Google OAuth URL with correct redirect_uri -``` - -### 4. Test Complete OAuth Flow -1. Visit `https://bsa.madeamess.online` (frontend) -2. Click "Continue with Google" -3. Google redirects to `https://bsa.madeamess.online:3000/auth/google/callback` -4. Backend processes OAuth and redirects back to frontend with token -5. User is authenticated āœ… - -## Why This Works Better - -āœ… **Direct backend access** - Google can reach your OAuth callback -āœ… **Simpler configuration** - No complex reverse proxy routing -āœ… **Easier debugging** - Clear separation of frontend/backend -āœ… **Standard OAuth flow** - Follows OAuth 2.0 best practices - -## Security Considerations - -šŸ”’ **SSL Required**: Port 3000 must use HTTPS for OAuth -šŸ”’ **Firewall Rules**: Only expose necessary ports -šŸ”’ **CORS Configuration**: Already configured for your domain - -## Quick Commands - -```bash -# 1. Restart containers with new config -docker-compose -f docker-compose.dev.yml restart - -# 2. Test backend -curl https://bsa.madeamess.online:3000/auth/setup - -# 3. Check OAuth URL -curl https://bsa.madeamess.online:3000/auth/google/url - -# 4. Test frontend -curl https://bsa.madeamess.online -``` - -## Expected Flow After Setup - -1. **User visits**: `https://bsa.madeamess.online` (frontend) -2. **Clicks login**: Frontend calls `https://bsa.madeamess.online:3000/auth/google/url` -3. **Redirects to Google**: User authenticates with Google -4. **Google redirects back**: `https://bsa.madeamess.online:3000/auth/google/callback` -5. **Backend processes**: Creates JWT token -6. **Redirects to frontend**: `https://bsa.madeamess.online/auth/callback?token=...` -7. **Frontend receives token**: User is logged in āœ… - -This setup will resolve the OAuth callback issue you're experiencing! diff --git a/POSTGRESQL_USER_MANAGEMENT.md b/POSTGRESQL_USER_MANAGEMENT.md deleted file mode 100644 index 36098c3..0000000 --- a/POSTGRESQL_USER_MANAGEMENT.md +++ /dev/null @@ -1,199 +0,0 @@ -# 🐘 PostgreSQL User Management System - -## āœ… What We Built - -A **production-ready user management system** using your existing PostgreSQL database infrastructure with proper database design, indexing, and transactional operations. - -## šŸŽÆ Database Architecture - -### **Users Table Schema** -```sql -CREATE TABLE users ( - id VARCHAR(255) PRIMARY KEY, - email VARCHAR(255) UNIQUE NOT NULL, - name VARCHAR(255) NOT NULL, - picture TEXT, - role VARCHAR(50) NOT NULL DEFAULT 'coordinator', - provider VARCHAR(50) NOT NULL DEFAULT 'google', - created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, - last_sign_in_at TIMESTAMP WITH TIME ZONE -); - --- Optimized indexes for performance -CREATE INDEX idx_users_email ON users(email); -CREATE INDEX idx_users_role ON users(role); -``` - -### **Key Features** -- āœ… **Primary key constraints** - Unique user identification -- āœ… **Email uniqueness** - Prevents duplicate accounts -- āœ… **Proper indexing** - Fast lookups by email and role -- āœ… **Timezone-aware timestamps** - Accurate time tracking -- āœ… **Default values** - Sensible defaults for new users - -## šŸš€ System Components - -### **1. DatabaseService (`databaseService.ts`)** -- **Connection pooling** with PostgreSQL -- **Automatic schema initialization** on startup -- **Transactional operations** for data consistency -- **Error handling** and connection management -- **Future-ready** with VIP and schedule tables - -### **2. Enhanced Auth Routes (`simpleAuth.ts`)** -- **Async/await** for all database operations -- **Proper error handling** with database fallbacks -- **User creation** with automatic role assignment -- **Login tracking** with timestamp updates -- **Role-based access control** for admin operations - -### **3. User Management API** -```typescript -// List all users (admin only) -GET /auth/users - -// Update user role (admin only) -PATCH /auth/users/:email/role -Body: { "role": "administrator" | "coordinator" | "driver" } - -// Delete user (admin only) -DELETE /auth/users/:email - -// Get specific user (admin only) -GET /auth/users/:email -``` - -### **4. Frontend Interface (`UserManagement.tsx`)** -- **Real-time data** from PostgreSQL -- **Professional UI** with loading states -- **Error handling** with user feedback -- **Role management** with instant updates -- **Responsive design** for all screen sizes - -## šŸ”§ Technical Advantages - -### **Database Benefits:** -- āœ… **ACID compliance** - Guaranteed data consistency -- āœ… **Concurrent access** - Multiple users safely -- āœ… **Backup & recovery** - Enterprise-grade data protection -- āœ… **Scalability** - Handles thousands of users -- āœ… **Query optimization** - Indexed for performance - -### **Security Features:** -- āœ… **SQL injection protection** - Parameterized queries -- āœ… **Connection pooling** - Efficient resource usage -- āœ… **Role validation** - Server-side permission checks -- āœ… **Transaction safety** - Atomic operations - -### **Production Ready:** -- āœ… **Error handling** - Graceful failure recovery -- āœ… **Logging** - Comprehensive operation tracking -- āœ… **Connection management** - Automatic reconnection -- āœ… **Schema migration** - Safe database updates - -## šŸ“‹ Setup & Usage - -### **1. Database Initialization** -The system automatically creates tables on startup: -```bash -# Your existing Docker setup handles this -docker-compose -f docker-compose.dev.yml up -``` - -### **2. First User Setup** -- **First user** becomes administrator automatically -- **Subsequent users** become coordinators by default -- **Role changes** can be made through admin interface - -### **3. User Management Workflow** -1. **Login with Google OAuth** - Users authenticate via Google -2. **Automatic user creation** - New users added to database -3. **Role assignment** - Admin can change user roles -4. **Permission enforcement** - Role-based access control -5. **User lifecycle** - Full CRUD operations for admins - -## šŸŽÆ Database Operations - -### **User Creation Flow:** -```sql --- Check if user exists -SELECT * FROM users WHERE email = $1; - --- Create new user if not exists -INSERT INTO users (id, email, name, picture, role, provider, last_sign_in_at) -VALUES ($1, $2, $3, $4, $5, $6, CURRENT_TIMESTAMP) -RETURNING *; -``` - -### **Role Update Flow:** -```sql --- Update user role with timestamp -UPDATE users -SET role = $1, updated_at = CURRENT_TIMESTAMP -WHERE email = $2 -RETURNING *; -``` - -### **Login Tracking:** -```sql --- Update last sign-in timestamp -UPDATE users -SET last_sign_in_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP -WHERE email = $1 -RETURNING *; -``` - -## šŸ” Monitoring & Maintenance - -### **Database Health:** -- **Connection status** logged on startup -- **Query performance** tracked in logs -- **Error handling** with detailed logging -- **Connection pooling** metrics available - -### **User Analytics:** -- **User count** tracking for admin setup -- **Login patterns** via last_sign_in_at -- **Role distribution** via role indexing -- **Account creation** trends via created_at - -## šŸš€ Future Enhancements - -### **Ready for Extension:** -- **User profiles** - Additional metadata fields -- **User groups** - Team-based permissions -- **Audit logging** - Track all user actions -- **Session management** - Advanced security -- **Multi-factor auth** - Enhanced security - -### **Database Scaling:** -- **Read replicas** - For high-traffic scenarios -- **Partitioning** - For large user bases -- **Caching** - Redis integration ready -- **Backup strategies** - Automated backups - -## šŸŽ‰ Production Benefits - -### **Enterprise Grade:** -- āœ… **Reliable** - PostgreSQL battle-tested reliability -- āœ… **Scalable** - Handles growth from 10 to 10,000+ users -- āœ… **Secure** - Industry-standard security practices -- āœ… **Maintainable** - Clean, documented codebase - -### **Developer Friendly:** -- āœ… **Type-safe** - Full TypeScript integration -- āœ… **Well-documented** - Clear API and database schema -- āœ… **Error-handled** - Graceful failure modes -- āœ… **Testable** - Isolated database operations - -Your user management system is now **production-ready** with enterprise-grade PostgreSQL backing! šŸš€ - -## šŸ”§ Quick Start - -1. **Ensure PostgreSQL is running** (your Docker setup handles this) -2. **Restart your backend** to initialize tables -3. **Login as first user** to become administrator -4. **Manage users** through the beautiful admin interface - -All user data is now safely stored in PostgreSQL with proper indexing, relationships, and ACID compliance! diff --git a/README-API.md b/README-API.md deleted file mode 100644 index b12766c..0000000 --- a/README-API.md +++ /dev/null @@ -1,218 +0,0 @@ -# VIP Coordinator API Documentation - -## šŸ“š Overview - -This document provides comprehensive API documentation for the VIP Coordinator system using **OpenAPI 3.0** (Swagger) specification. The API enables management of VIP transportation coordination, including flight tracking, driver management, and event scheduling. - -## šŸš€ Quick Start - -### View API Documentation - -1. **Interactive Documentation (Recommended):** - ```bash - # Open the interactive Swagger UI documentation - open vip-coordinator/api-docs.html - ``` - Or visit: `file:///path/to/vip-coordinator/api-docs.html` - -2. **Raw OpenAPI Specification:** - ```bash - # View the YAML specification file - cat vip-coordinator/api-documentation.yaml - ``` - -### Test the API - -The interactive documentation includes a "Try it out" feature that allows you to test endpoints directly: - -1. Open `api-docs.html` in your browser -2. Click on any endpoint to expand it -3. Click "Try it out" button -4. Fill in parameters and request body -5. Click "Execute" to make the API call - -## šŸ“‹ API Categories - -### šŸ„ Health -- `GET /api/health` - System health check - -### šŸ‘„ VIPs -- `GET /api/vips` - Get all VIPs -- `POST /api/vips` - Create new VIP -- `PUT /api/vips/{id}` - Update VIP -- `DELETE /api/vips/{id}` - Delete VIP - -### šŸš— Drivers -- `GET /api/drivers` - Get all drivers -- `POST /api/drivers` - Create new driver -- `PUT /api/drivers/{id}` - Update driver -- `DELETE /api/drivers/{id}` - Delete driver -- `GET /api/drivers/{driverId}/schedule` - Get driver's schedule -- `POST /api/drivers/availability` - Check driver availability -- `POST /api/drivers/{driverId}/conflicts` - Check driver conflicts - -### āœˆļø Flights -- `GET /api/flights/{flightNumber}` - Get flight information -- `POST /api/flights/{flightNumber}/track` - Start flight tracking -- `DELETE /api/flights/{flightNumber}/track` - Stop flight tracking -- `POST /api/flights/batch` - Get multiple flights info -- `GET /api/flights/tracking/status` - Get tracking status - -### šŸ“… Schedule -- `GET /api/vips/{vipId}/schedule` - Get VIP's schedule -- `POST /api/vips/{vipId}/schedule` - Add event to schedule -- `PUT /api/vips/{vipId}/schedule/{eventId}` - Update event -- `DELETE /api/vips/{vipId}/schedule/{eventId}` - Delete event -- `PATCH /api/vips/{vipId}/schedule/{eventId}/status` - Update event status - -### āš™ļø Admin -- `POST /api/admin/authenticate` - Admin authentication -- `GET /api/admin/settings` - Get admin settings -- `POST /api/admin/settings` - Update admin settings - -## šŸ’” Example API Calls - -### Create a VIP with Flight -```bash -curl -X POST http://localhost:3000/api/vips \ - -H "Content-Type: application/json" \ - -d '{ - "name": "John Doe", - "organization": "Tech Corp", - "transportMode": "flight", - "flights": [ - { - "flightNumber": "UA1234", - "flightDate": "2025-06-26", - "segment": 1 - } - ], - "needsAirportPickup": true, - "needsVenueTransport": true, - "notes": "CEO - requires executive transport" - }' -``` - -### Add Event to VIP Schedule -```bash -curl -X POST http://localhost:3000/api/vips/{vipId}/schedule \ - -H "Content-Type: application/json" \ - -d '{ - "title": "Meeting with CEO", - "location": "Hyatt Regency Denver", - "startTime": "2025-06-26T11:00:00", - "endTime": "2025-06-26T12:30:00", - "type": "meeting", - "assignedDriverId": "1748780965562", - "description": "Important strategic meeting" - }' -``` - -### Check Driver Availability -```bash -curl -X POST http://localhost:3000/api/drivers/availability \ - -H "Content-Type: application/json" \ - -d '{ - "startTime": "2025-06-26T11:00:00", - "endTime": "2025-06-26T12:30:00", - "location": "Denver Convention Center" - }' -``` - -### Get Flight Information -```bash -curl "http://localhost:3000/api/flights/UA1234?date=2025-06-26" -``` - -## šŸ”§ Tools for API Documentation - -### 1. **Swagger UI (Recommended)** -- **What it is:** Interactive web-based API documentation -- **Features:** - - Try endpoints directly in browser - - Auto-generated from OpenAPI spec - - Beautiful, responsive interface - - Request/response examples -- **Access:** Open `api-docs.html` in your browser - -### 2. **OpenAPI Specification** -- **What it is:** Industry-standard API specification format -- **Features:** - - Machine-readable API definition - - Can generate client SDKs - - Supports validation and testing - - Compatible with many tools -- **File:** `api-documentation.yaml` - -### 3. **Alternative Tools** - -You can use the OpenAPI specification with other tools: - -#### Postman -1. Import `api-documentation.yaml` into Postman -2. Automatically creates a collection with all endpoints -3. Includes examples and validation - -#### Insomnia -1. Import the OpenAPI spec -2. Generate requests automatically -3. Built-in environment management - -#### VS Code Extensions -- **OpenAPI (Swagger) Editor** - Edit and preview API specs -- **REST Client** - Test APIs directly in VS Code - -## šŸ“– Documentation Best Practices - -### Why OpenAPI/Swagger? - -1. **Industry Standard:** Most widely adopted API documentation format -2. **Interactive:** Users can test APIs directly in the documentation -3. **Code Generation:** Can generate client libraries in multiple languages -4. **Validation:** Ensures API requests/responses match specification -5. **Tooling:** Extensive ecosystem of tools and integrations - -### Documentation Features - -- **Comprehensive:** All endpoints, parameters, and responses documented -- **Examples:** Real-world examples for all operations -- **Schemas:** Detailed data models with validation rules -- **Error Handling:** Clear error response documentation -- **Authentication:** Security requirements clearly specified - -## šŸ”— Integration Examples - -### Frontend Integration -```javascript -// Example: Fetch VIPs in React -const fetchVips = async () => { - const response = await fetch('/api/vips'); - const vips = await response.json(); - return vips; -}; -``` - -### Backend Integration -```bash -# Example: Using curl to test endpoints -curl -X GET http://localhost:3000/api/health -curl -X GET http://localhost:3000/api/vips -curl -X GET http://localhost:3000/api/drivers -``` - -## šŸš€ Next Steps - -1. **Explore the Interactive Docs:** Open `api-docs.html` and try the endpoints -2. **Test with Real Data:** Use the populated test data to explore functionality -3. **Build Integrations:** Use the API specification to build client applications -4. **Extend the API:** Add new endpoints following the established patterns - -## šŸ“ž Support - -For questions about the API: -- Review the interactive documentation -- Check the OpenAPI specification for detailed schemas -- Test endpoints using the "Try it out" feature -- Refer to the example requests and responses - -The API documentation is designed to be self-service and comprehensive, providing everything needed to integrate with the VIP Coordinator system. diff --git a/REVERSE_PROXY_OAUTH_SETUP.md b/REVERSE_PROXY_OAUTH_SETUP.md deleted file mode 100644 index b1acc55..0000000 --- a/REVERSE_PROXY_OAUTH_SETUP.md +++ /dev/null @@ -1,217 +0,0 @@ -# 🌐 Reverse Proxy OAuth Setup Guide - -## Your Current Setup -- **Internet** → **Router (ports 80/443)** → **Reverse Proxy** → **Frontend (port 5173)** -- **Backend (port 3000)** is only accessible locally -- **OAuth callback fails** because Google can't reach the backend - -## The Problem -Google OAuth needs to redirect to your **backend** (`/auth/google/callback`), but your reverse proxy only forwards to the frontend. The backend port 3000 isn't exposed to the internet. - -## Solution: Configure Reverse Proxy for Both Frontend and Backend - -### Option 1: Single Domain with Path-Based Routing (Recommended) - -Configure your reverse proxy to route both frontend and backend on the same domain: - -```nginx -# Example Nginx configuration -server { - listen 443 ssl; - server_name bsa.madeamess.online; - - # Frontend routes (everything except /auth and /api) - location / { - proxy_pass http://localhost:5173; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # Backend API routes - location /api/ { - proxy_pass http://localhost:3000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # Backend auth routes (CRITICAL for OAuth) - location /auth/ { - proxy_pass http://localhost:3000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} -``` - -### Option 2: Subdomain Routing - -If you prefer separate subdomains: - -```nginx -# Frontend -server { - listen 443 ssl; - server_name bsa.madeamess.online; - - location / { - proxy_pass http://localhost:5173; - # ... headers - } -} - -# Backend API -server { - listen 443 ssl; - server_name api.bsa.madeamess.online; - - location / { - proxy_pass http://localhost:3000; - # ... headers - } -} -``` - -## Update Environment Variables - -### For Option 1 (Path-based - Recommended): - -```bash -# backend/.env -GOOGLE_CLIENT_ID=308004695553-6k34bbq22frc4e76kejnkgq8mncepbbg.apps.googleusercontent.com -GOOGLE_CLIENT_SECRET=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 -GOOGLE_REDIRECT_URI=https://bsa.madeamess.online/auth/google/callback -FRONTEND_URL=https://bsa.madeamess.online -``` - -### For Option 2 (Subdomain): - -```bash -# backend/.env -GOOGLE_CLIENT_ID=308004695553-6k34bbq22frc4e76kejnkgq8mncepbbg.apps.googleusercontent.com -GOOGLE_CLIENT_SECRET=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 -GOOGLE_REDIRECT_URI=https://api.bsa.madeamess.online/auth/google/callback -FRONTEND_URL=https://bsa.madeamess.online -``` - -## Update Google Cloud Console - -### For Option 1 (Path-based): - -**Authorized JavaScript origins:** -``` -https://bsa.madeamess.online -``` - -**Authorized redirect URIs:** -``` -https://bsa.madeamess.online/auth/google/callback -``` - -### For Option 2 (Subdomain): - -**Authorized JavaScript origins:** -``` -https://bsa.madeamess.online -https://api.bsa.madeamess.online -``` - -**Authorized redirect URIs:** -``` -https://api.bsa.madeamess.online/auth/google/callback -``` - -## Frontend Configuration Update - -If using Option 2 (subdomain), update your frontend to call the API subdomain: - -```javascript -// In your frontend code, change API calls from: -fetch('/auth/google/url') - -// To: -fetch('https://api.bsa.madeamess.online/auth/google/url') -``` - -## Testing Your Setup - -### 1. Test Backend Accessibility - -```bash -# Should work from internet -curl https://bsa.madeamess.online/auth/setup -# or for subdomain: -curl https://api.bsa.madeamess.online/auth/setup -``` - -### 2. Test OAuth URL Generation - -```bash -curl https://bsa.madeamess.online/auth/google/url -# Should return a Google OAuth URL -``` - -### 3. Test Complete Flow - -1. Visit `https://bsa.madeamess.online` -2. Click "Continue with Google" -3. Complete Google login -4. Should redirect back and authenticate - -## Common Issues and Solutions - -### Issue: "Invalid redirect URI" -- **Cause**: Google Console redirect URI doesn't match exactly -- **Fix**: Ensure exact match including `https://` and no trailing slash - -### Issue: "OAuth not configured" -- **Cause**: Backend environment variables not updated -- **Fix**: Update `.env` file and restart containers - -### Issue: Frontend can't reach backend -- **Cause**: Reverse proxy not configured for `/auth` and `/api` routes -- **Fix**: Add backend routing to your reverse proxy config - -### Issue: CORS errors -- **Cause**: Frontend and backend on different origins -- **Fix**: Update CORS configuration in backend: - -```javascript -// In backend/src/index.ts -app.use(cors({ - origin: [ - 'https://bsa.madeamess.online', - 'http://localhost:5173' // for local development - ], - credentials: true -})); -``` - -## Recommended: Path-Based Routing - -I recommend **Option 1 (path-based routing)** because: -- āœ… Single domain simplifies CORS -- āœ… Easier SSL certificate management -- āœ… Simpler frontend configuration -- āœ… Better for SEO and user experience - -## Quick Setup Commands - -```bash -# 1. Update environment variables -cd /home/kyle/Desktop/vip-coordinator -# Edit backend/.env with your domain - -# 2. Restart containers -docker-compose -f docker-compose.dev.yml restart - -# 3. Test the setup -curl https://bsa.madeamess.online/auth/setup -``` - -Your OAuth should work once you configure your reverse proxy to forward `/auth` and `/api` routes to the backend (port 3000)! diff --git a/ROLE_BASED_ACCESS_CONTROL.md b/ROLE_BASED_ACCESS_CONTROL.md deleted file mode 100644 index 3f5e7d3..0000000 --- a/ROLE_BASED_ACCESS_CONTROL.md +++ /dev/null @@ -1,300 +0,0 @@ -# Role-Based Access Control (RBAC) System - -## Overview - -The VIP Coordinator application implements a comprehensive role-based access control system with three distinct user roles, each with specific permissions and access levels. - -## User Roles - -### 1. System Administrator (`administrator`) -**Highest privilege level - Full system access** - -#### Permissions: -- āœ… **User Management**: Create, read, update, delete users -- āœ… **Role Management**: Assign and modify user roles -- āœ… **VIP Management**: Full CRUD operations on VIP records -- āœ… **Driver Management**: Full CRUD operations on driver records -- āœ… **Schedule Management**: Full CRUD operations on schedules -- āœ… **System Settings**: Access to admin panel and API configurations -- āœ… **Flight Tracking**: Access to all flight tracking features -- āœ… **Reports & Analytics**: Access to all system reports - -#### API Endpoints Access: -``` -POST /auth/users āœ… Admin only -GET /auth/users āœ… Admin only -PATCH /auth/users/:email/role āœ… Admin only -DELETE /auth/users/:email āœ… Admin only - -POST /api/vips āœ… Admin + Coordinator -GET /api/vips āœ… All authenticated users -PUT /api/vips/:id āœ… Admin + Coordinator -DELETE /api/vips/:id āœ… Admin + Coordinator - -POST /api/drivers āœ… Admin + Coordinator -GET /api/drivers āœ… All authenticated users -PUT /api/drivers/:id āœ… Admin + Coordinator -DELETE /api/drivers/:id āœ… Admin + Coordinator - -POST /api/vips/:vipId/schedule āœ… Admin + Coordinator -GET /api/vips/:vipId/schedule āœ… All authenticated users -PUT /api/vips/:vipId/schedule/:id āœ… Admin + Coordinator -PATCH /api/vips/:vipId/schedule/:id/status āœ… All authenticated users -DELETE /api/vips/:vipId/schedule/:id āœ… Admin + Coordinator -``` - -### 2. Coordinator (`coordinator`) -**Standard operational access - Can manage VIPs, drivers, and schedules** - -#### Permissions: -- āŒ **User Management**: Cannot manage users or roles -- āœ… **VIP Management**: Full CRUD operations on VIP records -- āœ… **Driver Management**: Full CRUD operations on driver records -- āœ… **Schedule Management**: Full CRUD operations on schedules -- āŒ **System Settings**: No access to admin panel -- āœ… **Flight Tracking**: Access to flight tracking features -- āœ… **Driver Availability**: Can check driver conflicts and availability -- āœ… **Status Updates**: Can update event statuses - -#### Typical Use Cases: -- Managing VIP arrivals and departures -- Assigning drivers to VIPs -- Creating and updating schedules -- Monitoring flight statuses -- Coordinating transportation logistics - -### 3. Driver (`driver`) -**Limited access - Can view assigned schedules and update status** - -#### Permissions: -- āŒ **User Management**: Cannot manage users -- āŒ **VIP Management**: Cannot create/edit/delete VIPs -- āŒ **Driver Management**: Cannot manage other drivers -- āŒ **Schedule Creation**: Cannot create or delete schedules -- āœ… **View Schedules**: Can view VIP schedules and assigned events -- āœ… **Status Updates**: Can update status of assigned events -- āœ… **Personal Schedule**: Can view their own complete schedule -- āŒ **System Settings**: No access to admin features - -#### API Endpoints Access: -``` -GET /api/vips āœ… View only -GET /api/drivers āœ… View only -GET /api/vips/:vipId/schedule āœ… View only -PATCH /api/vips/:vipId/schedule/:id/status āœ… Can update status -GET /api/drivers/:driverId/schedule āœ… Own schedule only -``` - -#### Typical Use Cases: -- Viewing assigned VIP transportation schedules -- Updating event status (en route, completed, delayed) -- Checking personal daily/weekly schedule -- Viewing VIP contact information and notes - -## Authentication Flow - -### 1. Google OAuth Integration -- Users authenticate via Google OAuth 2.0 -- First user automatically becomes `administrator` -- Subsequent users default to `coordinator` role -- Administrators can change user roles after authentication - -### 2. JWT Token System -- Secure JWT tokens issued after successful authentication -- Tokens include user role information -- Middleware validates tokens and role permissions on each request - -### 3. Role Assignment -```typescript -// First user becomes admin -const userCount = await databaseService.getUserCount(); -const role = userCount === 0 ? 'administrator' : 'coordinator'; -``` - -## Security Implementation - -### Middleware Protection -```typescript -// Authentication required -app.get('/api/vips', requireAuth, async (req, res) => { ... }); - -// Role-based access -app.post('/api/vips', requireAuth, requireRole(['coordinator', 'administrator']), - async (req, res) => { ... }); - -// Admin only -app.get('/auth/users', requireAuth, requireRole(['administrator']), - async (req, res) => { ... }); -``` - -### Frontend Role Checking -```typescript -// User Management component -if (currentUser?.role !== 'administrator') { - return ( -
-

Access Denied

-

You need administrator privileges to access user management.

-
- ); -} -``` - -## Database Schema - -### Users Table -```sql -CREATE TABLE users ( - id VARCHAR(255) PRIMARY KEY, - email VARCHAR(255) UNIQUE NOT NULL, - name VARCHAR(255) NOT NULL, - picture TEXT, - role VARCHAR(50) NOT NULL DEFAULT 'coordinator', - provider VARCHAR(50) NOT NULL DEFAULT 'google', - created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, - last_sign_in_at TIMESTAMP WITH TIME ZONE -); - --- Indexes for performance -CREATE INDEX idx_users_email ON users(email); -CREATE INDEX idx_users_role ON users(role); -``` - -## Role Transition Guidelines - -### Promoting Users -1. **Coordinator → Administrator** - - Grants full system access - - Can manage other users - - Access to system settings - - Should be limited to trusted personnel - -2. **Driver → Coordinator** - - Grants VIP and schedule management - - Can assign other drivers - - Suitable for supervisory roles - -### Demoting Users -1. **Administrator → Coordinator** - - Removes user management access - - Retains operational capabilities - - Cannot access system settings - -2. **Coordinator → Driver** - - Removes management capabilities - - Retains view and status update access - - Suitable for field personnel - -## Best Practices - -### 1. Principle of Least Privilege -- Users should have minimum permissions necessary for their role -- Regular review of user roles and permissions -- Temporary elevation should be avoided - -### 2. Role Assignment Strategy -- **Administrators**: IT staff, senior management (limit to 2-3 users) -- **Coordinators**: Operations staff, event coordinators (primary users) -- **Drivers**: Field personnel, transportation staff - -### 3. Security Considerations -- Regular audit of user access logs -- Monitor for privilege escalation attempts -- Implement session timeouts for sensitive operations -- Use HTTPS for all authentication flows - -### 4. Emergency Access -- Maintain at least one administrator account -- Document emergency access procedures -- Consider backup authentication methods - -## API Security Features - -### 1. Token Validation -```typescript -export function requireAuth(req: Request, res: Response, next: NextFunction) { - const authHeader = req.headers.authorization; - - if (!authHeader || !authHeader.startsWith('Bearer ')) { - return res.status(401).json({ error: 'No token provided' }); - } - - const token = authHeader.substring(7); - const user = verifyToken(token); - - if (!user) { - return res.status(401).json({ error: 'Invalid token' }); - } - - (req as any).user = user; - next(); -} -``` - -### 2. Role Validation -```typescript -export function requireRole(roles: string[]) { - return (req: Request, res: Response, next: NextFunction) => { - const user = (req as any).user; - - if (!user || !roles.includes(user.role)) { - return res.status(403).json({ error: 'Insufficient permissions' }); - } - - next(); - }; -} -``` - -## Monitoring and Auditing - -### 1. User Activity Logging -- Track user login/logout events -- Log role changes and who made them -- Monitor sensitive operations (user deletion, role changes) - -### 2. Access Attempt Monitoring -- Failed authentication attempts -- Unauthorized access attempts -- Privilege escalation attempts - -### 3. Regular Security Reviews -- Quarterly review of user roles -- Annual security audit -- Regular password/token rotation - -## Future Enhancements - -### 1. Granular Permissions -- Department-based access control -- Resource-specific permissions -- Time-based access restrictions - -### 2. Advanced Security Features -- Multi-factor authentication -- IP-based access restrictions -- Session management improvements - -### 3. Audit Trail -- Comprehensive activity logging -- Change history tracking -- Compliance reporting - ---- - -## Quick Reference - -| Feature | Administrator | Coordinator | Driver | -|---------|--------------|-------------|--------| -| User Management | āœ… | āŒ | āŒ | -| VIP CRUD | āœ… | āœ… | āŒ | -| Driver CRUD | āœ… | āœ… | āŒ | -| Schedule CRUD | āœ… | āœ… | āŒ | -| Status Updates | āœ… | āœ… | āœ… | -| View Data | āœ… | āœ… | āœ… | -| System Settings | āœ… | āŒ | āŒ | -| Flight Tracking | āœ… | āœ… | āŒ | - -**Last Updated**: June 2, 2025 -**Version**: 1.0 diff --git a/SIMPLE_DEPLOY.md b/SIMPLE_DEPLOY.md deleted file mode 100644 index 7f5d0de..0000000 --- a/SIMPLE_DEPLOY.md +++ /dev/null @@ -1,179 +0,0 @@ -# VIP Coordinator - Simple Digital Ocean Deployment - -This is a streamlined deployment script designed specifically for clean Digital Ocean Docker droplets. - -## šŸš€ Quick Start - -1. **Upload the script** to your Digital Ocean droplet: - ```bash - wget https://raw.githubusercontent.com/your-repo/vip-coordinator/main/simple-deploy.sh - chmod +x simple-deploy.sh - ``` - -2. **Run the deployment**: - ```bash - ./simple-deploy.sh - ``` - -3. **Follow the prompts** to configure: - - Your domain name (e.g., `mysite.com`) - - API subdomain (e.g., `api.mysite.com`) - - Email for SSL certificates - - Google OAuth credentials - - SSL certificate setup (optional) - -## šŸ“‹ What It Does - -### āœ… **Automatic Setup** -- Creates Docker Compose configuration using v2 syntax -- Generates secure random passwords -- Sets up environment variables -- Creates management scripts - -### āœ… **SSL Certificate Automation** (Optional) -- Uses official certbot Docker container -- Webroot validation method -- Generates nginx SSL configuration -- Sets up automatic renewal script - -### āœ… **Generated Files** -- `.env` - Environment configuration -- `docker-compose.yml` - Docker services -- `start.sh` - Start the application -- `stop.sh` - Stop the application -- `status.sh` - Check application status -- `nginx-ssl.conf` - SSL nginx configuration (if SSL enabled) -- `renew-ssl.sh` - Certificate renewal script (if SSL enabled) - -## šŸ”§ Requirements - -### **Digital Ocean Droplet** -- Ubuntu 20.04+ or similar -- Docker and Docker Compose v2 installed -- Ports 80, 443, and 3000 open - -### **Domain Setup** -- Domain pointing to your droplet IP -- API subdomain pointing to your droplet IP -- DNS propagated (check with `nslookup yourdomain.com`) - -### **Google OAuth** -- Google Cloud Console project -- OAuth 2.0 Client ID and Secret -- Redirect URI configured - -## 🌐 Access URLs - -After deployment: -- **Frontend**: `https://yourdomain.com` (or `http://` if no SSL) -- **Backend API**: `https://api.yourdomain.com` (or `http://` if no SSL) - -## šŸ”’ SSL Certificate Setup - -If you choose SSL during setup: - -1. **Automatic Generation**: Uses Let's Encrypt with certbot Docker -2. **Nginx Configuration**: Generated automatically -3. **Manual Steps**: - ```bash - # Install nginx - apt update && apt install nginx - - # Copy SSL configuration - cp nginx-ssl.conf /etc/nginx/sites-available/vip-coordinator - ln -s /etc/nginx/sites-available/vip-coordinator /etc/nginx/sites-enabled/ - rm /etc/nginx/sites-enabled/default - - # Test and restart - nginx -t - systemctl restart nginx - ``` - -4. **Auto-Renewal**: Set up cron job - ```bash - echo "0 3 1 * * $(pwd)/renew-ssl.sh" | crontab - - ``` - -## šŸ› ļø Management Commands - -```bash -# Start the application -./start.sh - -# Stop the application -./stop.sh - -# Check status -./status.sh - -# View logs -docker compose logs -f - -# Update to latest version -docker compose pull -docker compose up -d -``` - -## šŸ”‘ Important Credentials - -The script generates and displays: -- **Admin Password**: For emergency access -- **Database Password**: For PostgreSQL -- **Keep these secure!** - -## šŸŽÆ First Time Login - -1. Open your frontend URL -2. Click "Continue with Google" -3. The first user becomes the administrator -4. Use the admin password if needed - -## šŸ› Troubleshooting - -### **Port Conflicts** -- Uses standard ports (80, 443, 3000) -- Ensure no other services are running on these ports - -### **SSL Issues** -- Verify domain DNS is pointing to your server -- Check firewall allows ports 80 and 443 -- Ensure no other web server is running - -### **Docker Issues** -```bash -# Check Docker version (should be v2) -docker compose version - -# Check container status -docker compose ps - -# View logs -docker compose logs backend -docker compose logs frontend -``` - -### **OAuth Issues** -- Verify redirect URI in Google Console matches exactly -- Check Client ID and Secret are correct -- Ensure domain is accessible from internet - -## šŸ“ž Support - -If you encounter issues: - -1. Check `./status.sh` for service health -2. Review logs with `docker compose logs` -3. Verify domain DNS resolution -4. Ensure all ports are accessible - -## šŸŽ‰ Success! - -Your VIP Coordinator should now be running with: -- āœ… Google OAuth authentication -- āœ… Mobile-friendly interface -- āœ… Real-time scheduling -- āœ… User management -- āœ… SSL encryption (if enabled) -- āœ… Automatic updates from Docker Hub - -Perfect for Digital Ocean droplet deployments! \ No newline at end of file diff --git a/SIMPLE_OAUTH_SETUP.md b/SIMPLE_OAUTH_SETUP.md deleted file mode 100644 index 859fa7a..0000000 --- a/SIMPLE_OAUTH_SETUP.md +++ /dev/null @@ -1,159 +0,0 @@ -# Simple OAuth2 Setup Guide - -## āœ… What's Working Now - -The VIP Coordinator now has a **much simpler** OAuth2 implementation that actually works! Here's what I've done: - -### šŸ”§ Simplified Implementation -- **Removed complex Passport.js** - No more confusing middleware chains -- **Simple JWT tokens** - Clean, stateless authentication -- **Direct Google API calls** - Using fetch instead of heavy libraries -- **Clean error handling** - Easy to debug and understand - -### šŸ“ New Files Created -- `backend/src/config/simpleAuth.ts` - Core auth functions -- `backend/src/routes/simpleAuth.ts` - Auth endpoints - -## šŸš€ How to Set Up Google OAuth2 - -### Step 1: Get Google OAuth2 Credentials - -1. Go to [Google Cloud Console](https://console.cloud.google.com/) -2. Create a new project or select existing one -3. Enable the Google+ API -4. Go to "Credentials" → "Create Credentials" → "OAuth 2.0 Client IDs" -5. Set application type to "Web application" -6. Add these redirect URIs: - - `http://localhost:3000/auth/google/callback` - - `http://localhost:5173/auth/callback` - -### Step 2: Update Environment Variables - -Edit `backend/.env` and add: - -```bash -# Google OAuth2 Settings -GOOGLE_CLIENT_ID=your_google_client_id_here -GOOGLE_CLIENT_SECRET=your_google_client_secret_here -GOOGLE_REDIRECT_URI=http://localhost:3000/auth/google/callback - -# JWT Secret (change this!) -JWT_SECRET=your-super-secret-jwt-key-change-this - -# Frontend URL -FRONTEND_URL=http://localhost:5173 -``` - -### Step 3: Test the Setup - -1. **Start the application:** - ```bash - docker-compose -f docker-compose.dev.yml up -d - ``` - -2. **Test auth endpoints:** - ```bash - # Check if backend is running - curl http://localhost:3000/api/health - - # Check auth status (should return {"authenticated":false}) - curl http://localhost:3000/auth/status - ``` - -3. **Test Google OAuth flow:** - - Visit: `http://localhost:3000/auth/google` - - Should redirect to Google login - - After login, redirects back with JWT token - -## šŸ”„ How It Works - -### Simple Flow: -1. User clicks "Login with Google" -2. Redirects to `http://localhost:3000/auth/google` -3. Backend redirects to Google OAuth -4. Google redirects back to `/auth/google/callback` -5. Backend exchanges code for user info -6. Backend creates JWT token -7. Frontend receives token and stores it - -### API Endpoints: -- `GET /auth/google` - Start OAuth flow -- `GET /auth/google/callback` - Handle OAuth callback -- `GET /auth/status` - Check if user is authenticated -- `GET /auth/me` - Get current user info (requires auth) -- `POST /auth/logout` - Logout (client-side token removal) - -## šŸ› ļø Frontend Integration - -The frontend needs to: - -1. **Handle the OAuth callback:** - ```javascript - // In your React app, handle the callback route - const urlParams = new URLSearchParams(window.location.search); - const token = urlParams.get('token'); - if (token) { - localStorage.setItem('authToken', token); - // Redirect to dashboard - } - ``` - -2. **Include token in API requests:** - ```javascript - const token = localStorage.getItem('authToken'); - fetch('/api/vips', { - headers: { - 'Authorization': `Bearer ${token}` - } - }); - ``` - -3. **Add login button:** - ```javascript - - ``` - -## šŸŽÆ Benefits of This Approach - -- **Simple to understand** - No complex middleware -- **Easy to debug** - Clear error messages -- **Lightweight** - Fewer dependencies -- **Secure** - Uses standard JWT tokens -- **Flexible** - Easy to extend or modify - -## šŸ” Troubleshooting - -### Common Issues: - -1. **"OAuth not configured" error:** - - Make sure `GOOGLE_CLIENT_ID` is set in `.env` - - Restart the backend after changing `.env` - -2. **"Invalid redirect URI" error:** - - Check Google Console redirect URIs match exactly - - Make sure no trailing slashes - -3. **Token verification fails:** - - Check `JWT_SECRET` is set and consistent - - Make sure token is being sent with `Bearer ` prefix - -### Debug Commands: -```bash -# Check backend logs -docker-compose -f docker-compose.dev.yml logs backend - -# Check if environment variables are loaded -docker exec vip-coordinator-backend-1 env | grep GOOGLE -``` - -## šŸŽ‰ Next Steps - -1. Set up your Google OAuth2 credentials -2. Update the `.env` file -3. Test the login flow -4. Integrate with the frontend -5. Customize user roles and permissions - -The authentication system is now much simpler and actually works! šŸš€ diff --git a/SIMPLE_USER_MANAGEMENT.md b/SIMPLE_USER_MANAGEMENT.md deleted file mode 100644 index 430f8f1..0000000 --- a/SIMPLE_USER_MANAGEMENT.md +++ /dev/null @@ -1,125 +0,0 @@ -# šŸ” Simple User Management System - -## āœ… What We Built - -A **lightweight, persistent user management system** that extends your existing OAuth2 authentication using your existing JSON data storage. - -## šŸŽÆ Key Features - -### āœ… **Persistent Storage** -- Uses your existing JSON data file storage -- No third-party services required -- Completely self-contained -- Users preserved across server restarts - -### šŸ”§ **New API Endpoints** -- `GET /auth/users` - List all users (admin only) -- `PATCH /auth/users/:email/role` - Update user role (admin only) -- `DELETE /auth/users/:email` - Delete user (admin only) -- `GET /auth/users/:email` - Get specific user (admin only) - -### šŸŽØ **Admin Interface** -- Beautiful React component for user management -- Role-based access control (admin only) -- Change user roles with dropdown -- Delete users with confirmation -- Responsive design - -## šŸš€ How It Works - -### 1. **User Registration** -- First user becomes administrator automatically -- Subsequent users become coordinators by default -- All via your existing Google OAuth flow - -### 2. **Role Management** -- **Administrator:** Full access including user management -- **Coordinator:** Can manage VIPs, drivers, schedules -- **Driver:** Can view assigned schedules - -### 3. **User Management Interface** -- Only administrators can access user management -- View all users with profile pictures -- Change roles instantly -- Delete users (except yourself) -- Clear role descriptions - -## šŸ“‹ Usage - -### For Administrators: -1. Login with Google (first user becomes admin) -2. Access user management interface -3. View all registered users -4. Change user roles as needed -5. Remove users if necessary - -### API Examples: -```bash -# List all users (admin only) -curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \ - http://localhost:3000/auth/users - -# Update user role -curl -X PATCH \ - -H "Authorization: Bearer YOUR_JWT_TOKEN" \ - -H "Content-Type: application/json" \ - -d '{"role": "administrator"}' \ - http://localhost:3000/auth/users/user@example.com/role - -# Delete user -curl -X DELETE \ - -H "Authorization: Bearer YOUR_JWT_TOKEN" \ - http://localhost:3000/auth/users/user@example.com -``` - -## šŸ”’ Security Features - -- **Role-based access control** - Only admins can manage users -- **Self-deletion prevention** - Admins can't delete themselves -- **JWT token validation** - All endpoints require authentication -- **Input validation** - Role validation on updates - -## āœ… Important Notes - -### **Persistent File Storage** -- Users are stored in your existing JSON data file -- **Users are preserved across server restarts** -- Perfect for development and production -- Integrates seamlessly with your existing data storage - -### **Simple & Lightweight** -- No external dependencies -- No complex setup required -- Works with your existing OAuth system -- Easy to understand and modify - -## šŸŽÆ Perfect For - -- **Development and production environments** -- **Small to medium teams** (< 100 users) -- **Self-hosted applications** -- **When you want full control** over your user data -- **Simple, reliable user management** - -## šŸ”„ Future Enhancements - -You can easily extend this to: -- Migrate to your existing PostgreSQL database if needed -- Add user metadata and profiles -- Implement audit logging -- Add email notifications -- Create user groups/teams -- Add Redis caching for better performance - -## šŸŽ‰ Ready to Use! - -Your user management system is now complete and ready to use: - -1. **Restart your backend** to pick up the new endpoints -2. **Login as the first user** to become administrator -3. **Access user management** through your admin interface -4. **Manage users** with the beautiful interface we built - -**āœ… Persistent storage:** All user data is automatically saved to your existing JSON data file and preserved across server restarts! - -No external dependencies, no complex setup - just simple, effective, persistent user management! šŸš€ diff --git a/STANDALONE_INSTALL.md b/STANDALONE_INSTALL.md deleted file mode 100644 index 976aef4..0000000 --- a/STANDALONE_INSTALL.md +++ /dev/null @@ -1,258 +0,0 @@ -# šŸš€ VIP Coordinator - Standalone Installation - -Deploy VIP Coordinator directly from Docker Hub - **No GitHub required!** - -## šŸ“¦ What You Get - -- āœ… **Pre-built Docker images** from Docker Hub -- āœ… **Interactive setup script** that configures everything -- āœ… **Complete deployment** in under 5 minutes -- āœ… **No source code needed** - just Docker containers - -## šŸ”§ Prerequisites - -**Ubuntu/Linux:** -```bash -# Install Docker and Docker Compose -sudo apt update -sudo apt install docker.io docker-compose -sudo usermod -aG docker $USER -# Log out and back in, or run: newgrp docker -``` - -**Other Systems:** -- Install Docker Desktop from https://docker.com/get-started - -## šŸš€ Installation Methods - -### Method 1: Direct Download (Recommended) - -```bash -# Create directory -mkdir vip-coordinator -cd vip-coordinator - -# Download the standalone setup script -curl -O https://your-domain.com/standalone-setup.sh - -# Make executable and run -chmod +x standalone-setup.sh -./standalone-setup.sh -``` - -### Method 2: Copy-Paste Installation - -If you can't download the script, you can create it manually: - -```bash -# Create directory -mkdir vip-coordinator -cd vip-coordinator - -# Create the setup script (copy the content from standalone-setup.sh) -nano standalone-setup.sh - -# Make executable and run -chmod +x standalone-setup.sh -./standalone-setup.sh -``` - -### Method 3: Manual Docker Hub Deployment - -If you prefer to set up manually: - -```bash -# Create directory -mkdir vip-coordinator -cd vip-coordinator - -# Create docker-compose.yml -cat > docker-compose.yml << 'EOF' -version: '3.8' -services: - db: - image: postgres:15 - environment: - POSTGRES_DB: vip_coordinator - POSTGRES_PASSWORD: ${DB_PASSWORD} - volumes: - - postgres-data:/var/lib/postgresql/data - restart: unless-stopped - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres"] - interval: 30s - timeout: 10s - retries: 3 - - redis: - image: redis:7 - restart: unless-stopped - healthcheck: - test: ["CMD", "redis-cli", "ping"] - interval: 30s - timeout: 10s - retries: 3 - - backend: - image: t72chevy/vip-coordinator:backend-latest - environment: - DATABASE_URL: postgresql://postgres:${DB_PASSWORD}@db:5432/vip_coordinator - REDIS_URL: redis://redis:6379 - GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID} - GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET} - GOOGLE_REDIRECT_URI: ${GOOGLE_REDIRECT_URI} - FRONTEND_URL: ${FRONTEND_URL} - ADMIN_PASSWORD: ${ADMIN_PASSWORD} - PORT: 3000 - ports: - - "3000:3000" - depends_on: - db: - condition: service_healthy - redis: - condition: service_healthy - restart: unless-stopped - - frontend: - image: t72chevy/vip-coordinator:frontend-latest - ports: - - "80:80" - depends_on: - - backend - restart: unless-stopped - -volumes: - postgres-data: -EOF - -# Create .env file with your configuration -nano .env - -# Start the application -docker-compose pull -docker-compose up -d -``` - -## šŸŽÆ What the Setup Script Does - -1. **Checks Prerequisites**: Verifies Docker and Docker Compose are installed -2. **Interactive Configuration**: Asks for your deployment preferences -3. **Generates Files**: Creates all necessary configuration files -4. **Pulls Images**: Downloads pre-built images from Docker Hub -5. **Creates Management Scripts**: Provides easy start/stop/update commands - -## šŸ“‹ Configuration Options - -The script will ask you for: - -- **Deployment Type**: Local development or production -- **Domain Settings**: Your domain names (for production) -- **Security**: Generates secure passwords automatically -- **Google OAuth**: Your Google Cloud Console credentials -- **Optional**: AviationStack API key for flight data - -## šŸ” Google OAuth Setup - -You'll need to set up Google OAuth (the script guides you through this): - -1. Go to [Google Cloud Console](https://console.cloud.google.com/) -2. Create a new project -3. Enable Google+ API -4. Create OAuth 2.0 Client ID -5. Add redirect URI (provided by the script) -6. Copy Client ID and Secret - -## šŸ“¦ Docker Hub Images Used - -This deployment uses these pre-built images: - -- **`t72chevy/vip-coordinator:backend-latest`** (404MB) - - Complete Node.js backend with OAuth fixes - - PostgreSQL and Redis integration - - Health checks and monitoring - -- **`t72chevy/vip-coordinator:frontend-latest`** (74.8MB) - - React frontend with mobile OAuth fixes - - Nginx web server - - Production-optimized build - -- **`postgres:15`** - Database -- **`redis:7`** - Cache and sessions - -## šŸš€ After Installation - -Once setup completes, you'll have these commands: - -```bash -./start.sh # Start VIP Coordinator -./stop.sh # Stop VIP Coordinator -./update.sh # Update to latest Docker Hub images -./status.sh # Check system status -./logs.sh # View application logs -``` - -## 🌐 Access Your Application - -- **Local**: http://localhost -- **Production**: https://your-domain.com - -## šŸ”„ Updates - -To update to the latest version: - -```bash -./update.sh -``` - -This pulls the latest images from Docker Hub and restarts the services. - -## šŸ“± Mobile Support - -This deployment includes fixes for mobile OAuth authentication: -- āœ… Mobile users can now log in successfully -- āœ… Proper API endpoint configuration -- āœ… Enhanced error handling - -## šŸ› ļø Troubleshooting - -### Common Issues - -**Docker permission denied:** -```bash -sudo usermod -aG docker $USER -newgrp docker -``` - -**Port conflicts:** -```bash -# Check what's using ports 80 and 3000 -sudo netstat -tulpn | grep :80 -sudo netstat -tulpn | grep :3000 -``` - -**Service not starting:** -```bash -./status.sh # Check status -./logs.sh # View logs -``` - -## šŸ“ž Distribution - -To share VIP Coordinator with others: - -1. **Share the setup script**: Give them `standalone-setup.sh` -2. **Share this guide**: Include `STANDALONE_INSTALL.md` -3. **No GitHub needed**: Everything pulls from Docker Hub - -## šŸŽ‰ Benefits of Standalone Deployment - -- āœ… **No source code required** -- āœ… **No GitHub repository needed** -- āœ… **Pre-built, tested images** -- āœ… **Automatic updates from Docker Hub** -- āœ… **Cross-platform compatibility** -- āœ… **Production-ready configuration** - ---- - -**šŸš€ Get VIP Coordinator running in under 5 minutes with just Docker and one script!** \ No newline at end of file diff --git a/SUPABASE_MIGRATION.md b/SUPABASE_MIGRATION.md deleted file mode 100644 index e4c20f3..0000000 --- a/SUPABASE_MIGRATION.md +++ /dev/null @@ -1,33 +0,0 @@ -# Supabase Auth Migration Plan - -## Why Supabase? -- āœ… **Self-hosted Docker** - Full control, no external dependencies -- āœ… **Built-in Auth** - JWT tokens, social login (Google), user management -- āœ… **Simple API** - Similar to Auth0 but simpler -- āœ… **PostgreSQL** - Uses your existing database -- āœ… **React SDK** - Easy frontend integration -- āœ… **No Tailwind issues** - Doesn't affect frontend build - -## Migration Steps - -### 1. Add Supabase to Docker Compose -- Add Supabase services (Auth API, PostgREST, GoTrue) -- Configure to use existing PostgreSQL database -- Set up environment variables - -### 2. Update Backend -- Replace Auth0 SDK with Supabase client -- Update JWT validation to use Supabase keys -- Simplify auth routes - -### 3. Update Frontend -- Replace `@auth0/auth0-react` with `@supabase/supabase-js` -- Update Login component -- Update App.tsx auth logic - -### 4. Database Migration -- Keep existing user table structure -- Add Supabase auth tables (if needed) -- Map `auth0_sub` → `supabase_user_id` - -## Estimated Time: 30-45 minutes diff --git a/UBUNTU_INSTALL.md b/UBUNTU_INSTALL.md deleted file mode 100644 index 711fad7..0000000 --- a/UBUNTU_INSTALL.md +++ /dev/null @@ -1,281 +0,0 @@ -# 🐧 VIP Coordinator - Ubuntu Installation Guide - -Deploy VIP Coordinator on Ubuntu in just a few commands! - -## Prerequisites - -First, ensure Docker and Docker Compose are installed on your Ubuntu system: - -```bash -# Update package index -sudo apt update - -# Install Docker -sudo apt install -y docker.io - -# Install Docker Compose -sudo apt install -y docker-compose - -# Add your user to docker group (to run docker without sudo) -sudo usermod -aG docker $USER - -# Log out and back in, or run: -newgrp docker - -# Verify installation -docker --version -docker-compose --version -``` - -## Quick Install (One Command) - -```bash -# Download and run the interactive setup script -curl -sSL https://raw.githubusercontent.com/your-repo/vip-coordinator/main/setup.sh | bash -``` - -## Manual Installation - -If you prefer to download and inspect the script first: - -```bash -# Create a directory for VIP Coordinator -mkdir vip-coordinator -cd vip-coordinator - -# Download the setup script -wget https://raw.githubusercontent.com/your-repo/vip-coordinator/main/setup.sh - -# Make it executable -chmod +x setup.sh - -# Run the interactive setup -./setup.sh -``` - -## What the Setup Script Does - -The script will interactively ask you for: - -1. **Deployment Type**: Local development or production with custom domain -2. **Domain Configuration**: Your domain names (for production) -3. **Security**: Generates secure passwords or lets you set custom ones -4. **Google OAuth**: Your Google Cloud Console credentials -5. **Optional**: AviationStack API key for flight data - -Then it automatically generates: - -- āœ… `.env` - Your configuration file -- āœ… `docker-compose.yml` - Docker services configuration -- āœ… `start.sh` - Script to start VIP Coordinator -- āœ… `stop.sh` - Script to stop VIP Coordinator -- āœ… `update.sh` - Script to update to latest version -- āœ… `README.md` - Your deployment documentation -- āœ… `nginx.conf` - Production nginx config (if needed) - -## After Setup - -Once the setup script completes: - -```bash -# Start VIP Coordinator -./start.sh - -# Check status -docker-compose ps - -# View logs -docker-compose logs - -# Stop when needed -./stop.sh -``` - -## Access Your Application - -- **Local Development**: http://localhost -- **Production**: https://your-domain.com - -## Google OAuth Setup - -The script will guide you through setting up Google OAuth: - -1. Go to [Google Cloud Console](https://console.cloud.google.com/) -2. Create a new project or select existing -3. Enable Google+ API -4. Create OAuth 2.0 Client ID credentials -5. Add the redirect URI provided by the script -6. Copy Client ID and Secret when prompted - -## Ubuntu-Specific Notes - -### Firewall Configuration - -If you're using UFW (Ubuntu's firewall): - -```bash -# For local development -sudo ufw allow 80 -sudo ufw allow 3000 - -# For production (if using nginx proxy) -sudo ufw allow 80 -sudo ufw allow 443 -sudo ufw allow 22 # SSH access -``` - -### Production Deployment on Ubuntu - -For production deployment, the script generates an nginx configuration. To use it: - -```bash -# Install nginx -sudo apt install nginx - -# Copy the generated config -sudo cp nginx.conf /etc/nginx/sites-available/vip-coordinator - -# Enable the site -sudo ln -s /etc/nginx/sites-available/vip-coordinator /etc/nginx/sites-enabled/ - -# Remove default site -sudo rm /etc/nginx/sites-enabled/default - -# Test nginx configuration -sudo nginx -t - -# Restart nginx -sudo systemctl restart nginx -``` - -### SSL Certificates with Let's Encrypt - -```bash -# Install certbot -sudo apt install certbot python3-certbot-nginx - -# Get certificates (replace with your domains) -sudo certbot --nginx -d yourdomain.com -d api.yourdomain.com - -# Certbot will automatically update your nginx config for HTTPS -``` - -### System Service (Optional) - -To run VIP Coordinator as a system service: - -```bash -# Create service file -sudo tee /etc/systemd/system/vip-coordinator.service > /dev/null < backup.sql - -# Backup volumes -docker run --rm -v vip-coordinator_postgres-data:/data -v $(pwd):/backup ubuntu tar czf /backup/postgres-backup.tar.gz /data -``` - -## Support - -- šŸ“– Full documentation: [DEPLOYMENT.md](DEPLOYMENT.md) -- šŸ› Issues: GitHub Issues -- šŸ’¬ Community: GitHub Discussions - ---- - -**šŸŽ‰ Your VIP Coordinator will be running on Ubuntu in under 5 minutes!** \ No newline at end of file diff --git a/USER_MANAGEMENT_RECOMMENDATIONS.md b/USER_MANAGEMENT_RECOMMENDATIONS.md deleted file mode 100644 index d9f0caf..0000000 --- a/USER_MANAGEMENT_RECOMMENDATIONS.md +++ /dev/null @@ -1,197 +0,0 @@ -# šŸ” User Management System Recommendations - -## Current State Analysis -āœ… **You have:** Basic OAuth2 with Google, JWT tokens, role-based access (administrator/coordinator) -āŒ **You need:** Comprehensive user management, permissions, user lifecycle, admin interface - -## šŸ† Top Recommendations - -### 1. **Supabase Auth** (Recommended - Easy Integration) -**Why it's perfect for you:** -- Drop-in replacement for your current auth system -- Built-in user management dashboard -- Row Level Security (RLS) for fine-grained permissions -- Supports Google OAuth (you can keep your current flow) -- Real-time subscriptions -- Built-in user roles and metadata - -**Integration effort:** Low (2-3 days) -```bash -npm install @supabase/supabase-js -``` - -**Features you get:** -- User registration/login/logout -- Email verification -- Password reset -- User metadata and custom claims -- Admin dashboard for user management -- Real-time user presence -- Multi-factor authentication - -### 2. **Auth0** (Enterprise-grade) -**Why it's great:** -- Industry standard for enterprise applications -- Extensive user management dashboard -- Advanced security features -- Supports all OAuth providers -- Fine-grained permissions and roles -- Audit logs and analytics - -**Integration effort:** Medium (3-5 days) -```bash -npm install auth0 express-oauth-server -``` - -**Features you get:** -- Complete user lifecycle management -- Advanced role-based access control (RBAC) -- Multi-factor authentication -- Social logins (Google, Facebook, etc.) -- Enterprise SSO -- Comprehensive admin dashboard - -### 3. **Firebase Auth + Firestore** (Google Ecosystem) -**Why it fits:** -- You're already using Google OAuth -- Seamless integration with Google services -- Real-time database -- Built-in user management -- Offline support - -**Integration effort:** Medium (4-6 days) -```bash -npm install firebase-admin -``` - -### 4. **Clerk** (Modern Developer Experience) -**Why developers love it:** -- Beautiful pre-built UI components -- Excellent TypeScript support -- Built-in user management dashboard -- Easy role and permission management -- Great documentation - -**Integration effort:** Low-Medium (2-4 days) -```bash -npm install @clerk/clerk-sdk-node -``` - -## šŸŽÆ My Recommendation: **Supabase Auth** - -### Why Supabase is perfect for your project: - -1. **Minimal code changes** - Can integrate with your existing JWT system -2. **Built-in admin dashboard** - No need to build user management UI -3. **PostgreSQL-based** - Familiar database, easy to extend -4. **Real-time features** - Perfect for your VIP coordination needs -5. **Row Level Security** - Fine-grained permissions per user/role -6. **Free tier** - Great for development and small deployments - -### Quick Integration Plan: - -#### Step 1: Setup Supabase Project -```bash -# Install Supabase -npm install @supabase/supabase-js - -# Create project at https://supabase.com -# Get your project URL and anon key -``` - -#### Step 2: Replace your user storage -```typescript -// Instead of: const users: Map = new Map(); -// Use Supabase's built-in auth.users table -``` - -#### Step 3: Add user management endpoints -```typescript -// Get all users (admin only) -router.get('/users', requireAuth, requireRole(['administrator']), async (req, res) => { - const { data: users } = await supabase.auth.admin.listUsers(); - res.json(users); -}); - -// Update user role -router.patch('/users/:id/role', requireAuth, requireRole(['administrator']), async (req, res) => { - const { role } = req.body; - const { data } = await supabase.auth.admin.updateUserById(req.params.id, { - user_metadata: { role } - }); - res.json(data); -}); -``` - -#### Step 4: Add frontend user management -- Use Supabase's built-in dashboard OR -- Build simple admin interface with user list/edit/delete - -## šŸš€ Implementation Options - -### Option A: Quick Integration (Keep your current system + add Supabase) -- Keep your current OAuth flow -- Add Supabase for user storage and management -- Use Supabase dashboard for admin tasks -- **Time:** 2-3 days - -### Option B: Full Migration (Replace with Supabase Auth) -- Migrate to Supabase Auth completely -- Use their OAuth providers -- Get all advanced features -- **Time:** 4-5 days - -### Option C: Custom Admin Interface -- Keep your current system -- Build custom React admin interface -- Add user CRUD operations -- **Time:** 1-2 weeks - -## šŸ“‹ Next Steps - -1. **Choose your approach** (I recommend Option A - Quick Integration) -2. **Set up Supabase project** (5 minutes) -3. **Integrate user storage** (1 day) -4. **Add admin endpoints** (1 day) -5. **Test and refine** (1 day) - -## šŸ”§ Alternative: Lightweight Custom Solution - -If you prefer to keep it simple and custom: - -```typescript -// Add these endpoints to your existing auth system: - -// List all users (admin only) -router.get('/users', requireAuth, requireRole(['administrator']), (req, res) => { - const userList = Array.from(users.values()).map(user => ({ - id: user.id, - email: user.email, - name: user.name, - role: user.role, - lastLogin: user.lastLogin - })); - res.json(userList); -}); - -// Update user role -router.patch('/users/:email/role', requireAuth, requireRole(['administrator']), (req, res) => { - const { role } = req.body; - const user = users.get(req.params.email); - if (user) { - user.role = role; - users.set(req.params.email, user); - res.json({ success: true }); - } else { - res.status(404).json({ error: 'User not found' }); - } -}); - -// Delete user -router.delete('/users/:email', requireAuth, requireRole(['administrator']), (req, res) => { - users.delete(req.params.email); - res.json({ success: true }); -}); -``` - -Would you like me to help you implement any of these options? diff --git a/WEB_SERVER_PROXY_SETUP.md b/WEB_SERVER_PROXY_SETUP.md deleted file mode 100644 index ca20e8c..0000000 --- a/WEB_SERVER_PROXY_SETUP.md +++ /dev/null @@ -1,140 +0,0 @@ -# 🌐 Web Server Proxy Configuration for OAuth - -## šŸŽÆ Problem Identified - -Your domain `bsa.madeamess.online` is not properly configured to proxy requests to your Docker containers. When Google redirects to `https://bsa.madeamess.online:5173/auth/google/callback`, it gets "ERR_CONNECTION_REFUSED" because there's no web server listening on port 5173 for your domain. - -## šŸ”§ Solution Options - -### Option 1: Configure Nginx Proxy (Recommended) - -If you're using nginx, add this configuration: - -```nginx -# /etc/nginx/sites-available/bsa.madeamess.online -server { - listen 443 ssl; - server_name bsa.madeamess.online; - - # SSL configuration (your existing SSL setup) - ssl_certificate /path/to/your/certificate.crt; - ssl_certificate_key /path/to/your/private.key; - - # Proxy to your Docker frontend container - location / { - proxy_pass http://localhost:5173; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; - - # Important: Handle all routes for SPA - try_files $uri $uri/ @fallback; - } - - # Fallback for SPA routing - location @fallback { - proxy_pass http://localhost:5173; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} - -# Redirect HTTP to HTTPS -server { - listen 80; - server_name bsa.madeamess.online; - return 301 https://$server_name$request_uri; -} -``` - -### Option 2: Configure Apache Proxy - -If you're using Apache, add this to your virtual host: - -```apache - - ServerName bsa.madeamess.online - - # SSL configuration (your existing SSL setup) - SSLEngine on - SSLCertificateFile /path/to/your/certificate.crt - SSLCertificateKeyFile /path/to/your/private.key - - # Enable proxy modules - ProxyPreserveHost On - ProxyRequests Off - - # Proxy to your Docker frontend container - ProxyPass / http://localhost:5173/ - ProxyPassReverse / http://localhost:5173/ - - # Handle WebSocket connections for Vite HMR - ProxyPass /ws ws://localhost:5173/ws - ProxyPassReverse /ws ws://localhost:5173/ws - - - - ServerName bsa.madeamess.online - Redirect permanent / https://bsa.madeamess.online/ - -``` - -### Option 3: Update Google OAuth Redirect URI (Quick Fix) - -**Temporary workaround:** Update your Google Cloud Console OAuth settings to use `http://localhost:5173/auth/google/callback` instead of your domain, then access your app directly via `http://localhost:5173`. - -## šŸ”„ Alternative: Use Standard Ports - -### Option 4: Configure to use standard ports (80/443) - -Modify your docker-compose to use standard ports: - -```yaml -# In docker-compose.dev.yml -services: - frontend: - ports: - - "80:5173" # HTTP - # or - - "443:5173" # HTTPS (requires SSL setup in container) -``` - -Then update Google OAuth redirect URI to: -- `https://bsa.madeamess.online/auth/google/callback` (no port) - -## 🧪 Testing Steps - -1. **Apply web server configuration** -2. **Restart your web server:** - ```bash - # For nginx - sudo systemctl reload nginx - - # For Apache - sudo systemctl reload apache2 - ``` -3. **Test the proxy:** - ```bash - curl -I https://bsa.madeamess.online - ``` -4. **Test OAuth flow:** - - Visit `https://bsa.madeamess.online` - - Click "Continue with Google" - - Complete authentication - - Should redirect back successfully - -## šŸŽÆ Root Cause Summary - -The OAuth callback was failing because: -1. āœ… **Frontend routing** - Fixed (React Router now handles callback) -2. āœ… **CORS configuration** - Fixed (Backend accepts your domain) -3. āŒ **Web server proxy** - **NEEDS FIXING** (Domain not proxying to Docker) - -Once you configure your web server to proxy `bsa.madeamess.online` to `localhost:5173`, the OAuth flow will work perfectly! diff --git a/api-docs.html b/api-docs.html deleted file mode 100644 index 3b28685..0000000 --- a/api-docs.html +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - VIP Coordinator API Documentation - - - - -
-

šŸš— VIP Coordinator API

-

Comprehensive API for managing VIP transportation coordination

-
- - - -
- - - - - - diff --git a/api-documentation.yaml b/api-documentation.yaml deleted file mode 100644 index a6464a9..0000000 --- a/api-documentation.yaml +++ /dev/null @@ -1,1189 +0,0 @@ -openapi: 3.0.3 -info: - title: VIP Coordinator API - description: | - A comprehensive API for managing VIP transportation coordination, including flight tracking, - driver management, and event scheduling for high-profile guests. - - ## Features - - VIP management with flight and self-driving transport modes - - Real-time flight tracking and validation - - Driver assignment and conflict detection - - Event scheduling with validation - - Admin settings management - - ## Authentication - Most endpoints are public for demo purposes. Admin endpoints require authentication. - version: 1.0.0 - contact: - name: VIP Coordinator Support - email: support@vipcoordinator.com - license: - name: MIT - url: https://opensource.org/licenses/MIT - -servers: - - url: http://localhost:3000/api - description: Development server - - url: https://api.vipcoordinator.com/api - description: Production server - -tags: - - name: Health - description: System health checks - - name: VIPs - description: VIP management operations - - name: Drivers - description: Driver management operations - - name: Flights - description: Flight tracking and information - - name: Schedule - description: Event and meeting scheduling - - name: Admin - description: Administrative operations - -paths: - /health: - get: - tags: - - Health - summary: Health check endpoint - description: Returns the current status of the API server - responses: - '200': - description: Server is healthy - content: - application/json: - schema: - type: object - properties: - status: - type: string - example: "OK" - timestamp: - type: string - format: date-time - example: "2025-06-01T12:00:00.000Z" - - /vips: - get: - tags: - - VIPs - summary: Get all VIPs - description: Retrieve a list of all VIPs in the system - responses: - '200': - description: List of VIPs - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/VIP' - - post: - tags: - - VIPs - summary: Create a new VIP - description: Add a new VIP to the system - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/VIPCreate' - examples: - flight_vip: - summary: VIP with flight transport - value: - name: "John Doe" - organization: "Tech Corp" - transportMode: "flight" - flights: - - flightNumber: "UA1234" - flightDate: "2025-06-26" - segment: 1 - needsAirportPickup: true - needsVenueTransport: true - notes: "CEO - requires executive transport" - self_driving: - summary: Self-driving VIP - value: - name: "Jane Smith" - organization: "Local Business" - transportMode: "self-driving" - expectedArrival: "2025-06-26T14:00:00" - needsAirportPickup: false - needsVenueTransport: true - notes: "Driving from Colorado Springs" - responses: - '201': - description: VIP created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/VIP' - '400': - description: Invalid input data - - /vips/{id}: - put: - tags: - - VIPs - summary: Update a VIP - description: Update an existing VIP's information - parameters: - - name: id - in: path - required: true - schema: - type: string - description: VIP ID - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/VIPCreate' - responses: - '200': - description: VIP updated successfully - content: - application/json: - schema: - $ref: '#/components/schemas/VIP' - '404': - description: VIP not found - - delete: - tags: - - VIPs - summary: Delete a VIP - description: Remove a VIP from the system - parameters: - - name: id - in: path - required: true - schema: - type: string - description: VIP ID - responses: - '200': - description: VIP deleted successfully - content: - application/json: - schema: - type: object - properties: - message: - type: string - example: "VIP deleted successfully" - vip: - $ref: '#/components/schemas/VIP' - '404': - description: VIP not found - - /vips/{vipId}/schedule: - get: - tags: - - Schedule - summary: Get VIP's schedule - description: Retrieve all scheduled events for a specific VIP - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - responses: - '200': - description: VIP's schedule - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ScheduleEvent' - - post: - tags: - - Schedule - summary: Add event to VIP's schedule - description: Create a new event for a VIP with validation - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ScheduleEventCreate' - example: - title: "Meeting with CEO" - location: "Hyatt Regency Denver" - startTime: "2025-06-26T11:00:00" - endTime: "2025-06-26T12:30:00" - type: "meeting" - assignedDriverId: "1748780965562" - description: "Important strategic meeting" - responses: - '201': - description: Event created successfully - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/ScheduleEvent' - - type: object - properties: - warnings: - type: array - items: - $ref: '#/components/schemas/ValidationWarning' - '400': - description: Validation failed - content: - application/json: - schema: - $ref: '#/components/schemas/ValidationError' - - /vips/{vipId}/schedule/{eventId}: - put: - tags: - - Schedule - summary: Update a scheduled event - description: Update an existing event in a VIP's schedule - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - - name: eventId - in: path - required: true - schema: - type: string - description: Event ID - requestBody: - required: true - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/ScheduleEventCreate' - - type: object - properties: - status: - $ref: '#/components/schemas/EventStatus' - responses: - '200': - description: Event updated successfully - content: - application/json: - schema: - $ref: '#/components/schemas/ScheduleEvent' - '404': - description: VIP or event not found - - delete: - tags: - - Schedule - summary: Delete a scheduled event - description: Remove an event from a VIP's schedule - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - - name: eventId - in: path - required: true - schema: - type: string - description: Event ID - responses: - '200': - description: Event deleted successfully - '404': - description: VIP or event not found - - /vips/{vipId}/schedule/{eventId}/status: - patch: - tags: - - Schedule - summary: Update event status - description: Update the status of a specific event - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - - name: eventId - in: path - required: true - schema: - type: string - description: Event ID - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - status: - $ref: '#/components/schemas/EventStatus' - example: - status: "in-progress" - responses: - '200': - description: Event status updated - content: - application/json: - schema: - $ref: '#/components/schemas/ScheduleEvent' - '404': - description: VIP or event not found - - /drivers: - get: - tags: - - Drivers - summary: Get all drivers - description: Retrieve a list of all drivers in the system - responses: - '200': - description: List of drivers - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Driver' - - post: - tags: - - Drivers - summary: Create a new driver - description: Add a new driver to the system - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DriverCreate' - example: - name: "Carlos Rodriguez" - phone: "(303) 555-0101" - currentLocation: - lat: 39.8561 - lng: -104.6737 - responses: - '201': - description: Driver created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Driver' - - /drivers/{id}: - put: - tags: - - Drivers - summary: Update a driver - description: Update an existing driver's information - parameters: - - name: id - in: path - required: true - schema: - type: string - description: Driver ID - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DriverCreate' - responses: - '200': - description: Driver updated successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Driver' - '404': - description: Driver not found - - delete: - tags: - - Drivers - summary: Delete a driver - description: Remove a driver from the system - parameters: - - name: id - in: path - required: true - schema: - type: string - description: Driver ID - responses: - '200': - description: Driver deleted successfully - '404': - description: Driver not found - - /drivers/{driverId}/schedule: - get: - tags: - - Drivers - summary: Get driver's schedule - description: Retrieve all events assigned to a specific driver - parameters: - - name: driverId - in: path - required: true - schema: - type: string - description: Driver ID - responses: - '200': - description: Driver's schedule - content: - application/json: - schema: - type: object - properties: - driver: - type: object - properties: - id: - type: string - name: - type: string - phone: - type: string - schedule: - type: array - items: - allOf: - - $ref: '#/components/schemas/ScheduleEvent' - - type: object - properties: - vipId: - type: string - vipName: - type: string - '404': - description: Driver not found - - /drivers/availability: - post: - tags: - - Drivers - summary: Check driver availability - description: Find available drivers for a specific time slot - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - startTime: - type: string - format: date-time - endTime: - type: string - format: date-time - location: - type: string - required: - - startTime - - endTime - example: - startTime: "2025-06-26T11:00:00" - endTime: "2025-06-26T12:30:00" - location: "Denver Convention Center" - responses: - '200': - description: Driver availability information - content: - application/json: - schema: - type: object - properties: - available: - type: array - items: - $ref: '#/components/schemas/Driver' - busy: - type: array - items: - allOf: - - $ref: '#/components/schemas/Driver' - - type: object - properties: - conflictingEvents: - type: array - items: - $ref: '#/components/schemas/ScheduleEvent' - - /drivers/{driverId}/conflicts: - post: - tags: - - Drivers - summary: Check driver conflicts - description: Check if a specific driver has conflicts for a time slot - parameters: - - name: driverId - in: path - required: true - schema: - type: string - description: Driver ID - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - startTime: - type: string - format: date-time - endTime: - type: string - format: date-time - location: - type: string - required: - - startTime - - endTime - responses: - '200': - description: Conflict check results - content: - application/json: - schema: - type: object - properties: - conflicts: - type: array - items: - $ref: '#/components/schemas/ScheduleEvent' - - /flights/{flightNumber}: - get: - tags: - - Flights - summary: Get flight information - description: Retrieve real-time flight information - parameters: - - name: flightNumber - in: path - required: true - schema: - type: string - description: Flight number (e.g., UA1234) - example: "UA1234" - - name: date - in: query - schema: - type: string - format: date - description: Flight date (YYYY-MM-DD) - example: "2025-06-26" - - name: departureAirport - in: query - schema: - type: string - description: Departure airport code - example: "LAX" - - name: arrivalAirport - in: query - schema: - type: string - description: Arrival airport code - example: "DEN" - responses: - '200': - description: Flight information - content: - application/json: - schema: - $ref: '#/components/schemas/FlightInfo' - '404': - description: Flight not found - '500': - description: Failed to fetch flight data - - /flights/{flightNumber}/track: - post: - tags: - - Flights - summary: Start flight tracking - description: Begin periodic updates for a specific flight - parameters: - - name: flightNumber - in: path - required: true - schema: - type: string - description: Flight number - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - date: - type: string - format: date - intervalMinutes: - type: integer - default: 5 - required: - - date - example: - date: "2025-06-26" - intervalMinutes: 5 - responses: - '200': - description: Flight tracking started - content: - application/json: - schema: - type: object - properties: - message: - type: string - example: "Started tracking UA1234 on 2025-06-26" - - delete: - tags: - - Flights - summary: Stop flight tracking - description: Stop periodic updates for a specific flight - parameters: - - name: flightNumber - in: path - required: true - schema: - type: string - description: Flight number - - name: date - in: query - required: true - schema: - type: string - format: date - description: Flight date - responses: - '200': - description: Flight tracking stopped - - /flights/batch: - post: - tags: - - Flights - summary: Get multiple flights information - description: Retrieve information for multiple flights at once - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - flights: - type: array - items: - type: object - properties: - flightNumber: - type: string - date: - type: string - format: date - required: - - flightNumber - - date - example: - flights: - - flightNumber: "UA1234" - date: "2025-06-26" - - flightNumber: "AA789" - date: "2025-06-26" - responses: - '200': - description: Multiple flight information - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/FlightInfo' - - /flights/tracking/status: - get: - tags: - - Flights - summary: Get flight tracking status - description: Get the status of all currently tracked flights - responses: - '200': - description: Flight tracking status - content: - application/json: - schema: - type: object - properties: - trackedFlights: - type: array - items: - type: object - properties: - flightKey: - type: string - vipName: - type: string - lastUpdate: - type: string - format: date-time - status: - type: string - - /admin/authenticate: - post: - tags: - - Admin - summary: Admin authentication - description: Authenticate admin user - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - password: - type: string - required: - - password - responses: - '200': - description: Authentication successful - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - '401': - description: Invalid password - - /admin/settings: - get: - tags: - - Admin - summary: Get admin settings - description: Retrieve current admin settings (requires authentication) - parameters: - - name: admin-auth - in: header - required: true - schema: - type: string - description: Admin authentication header - responses: - '200': - description: Admin settings - content: - application/json: - schema: - $ref: '#/components/schemas/AdminSettings' - '401': - description: Unauthorized - - post: - tags: - - Admin - summary: Update admin settings - description: Update admin settings (requires authentication) - parameters: - - name: admin-auth - in: header - required: true - schema: - type: string - description: Admin authentication header - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AdminSettings' - responses: - '200': - description: Settings updated successfully - '401': - description: Unauthorized - -components: - schemas: - VIP: - type: object - properties: - id: - type: string - description: Unique VIP identifier - name: - type: string - description: VIP's full name - organization: - type: string - description: VIP's organization or company - transportMode: - type: string - enum: [flight, self-driving] - description: Mode of transportation - flights: - type: array - items: - $ref: '#/components/schemas/Flight' - description: Flight information (for flight transport mode) - expectedArrival: - type: string - format: date-time - description: Expected arrival time (for self-driving mode) - needsAirportPickup: - type: boolean - description: Whether VIP needs airport pickup - needsVenueTransport: - type: boolean - description: Whether VIP needs venue transport - assignedDriverIds: - type: array - items: - type: string - description: List of assigned driver IDs - notes: - type: string - description: Additional notes about the VIP - schedule: - type: array - items: - $ref: '#/components/schemas/ScheduleEvent' - description: VIP's schedule (usually empty, fetched separately) - - VIPCreate: - type: object - required: - - name - - organization - - transportMode - properties: - name: - type: string - minLength: 1 - organization: - type: string - minLength: 1 - transportMode: - type: string - enum: [flight, self-driving] - flights: - type: array - items: - $ref: '#/components/schemas/Flight' - expectedArrival: - type: string - format: date-time - needsAirportPickup: - type: boolean - default: true - needsVenueTransport: - type: boolean - default: true - notes: - type: string - - Flight: - type: object - required: - - flightNumber - - flightDate - - segment - properties: - flightNumber: - type: string - description: Flight number (e.g., UA1234) - flightDate: - type: string - format: date - description: Flight date - segment: - type: integer - minimum: 1 - description: Flight segment number for connecting flights - validated: - type: boolean - description: Whether flight has been validated - validationData: - $ref: '#/components/schemas/FlightInfo' - - Driver: - type: object - properties: - id: - type: string - description: Unique driver identifier - name: - type: string - description: Driver's full name - phone: - type: string - description: Driver's phone number - currentLocation: - $ref: '#/components/schemas/Location' - assignedVipIds: - type: array - items: - type: string - description: List of assigned VIP IDs - - DriverCreate: - type: object - required: - - name - - phone - properties: - name: - type: string - minLength: 1 - phone: - type: string - minLength: 1 - currentLocation: - $ref: '#/components/schemas/Location' - - Location: - type: object - properties: - lat: - type: number - format: float - description: Latitude - lng: - type: number - format: float - description: Longitude - - ScheduleEvent: - type: object - properties: - id: - type: string - description: Unique event identifier - title: - type: string - description: Event title - location: - type: string - description: Event location - startTime: - type: string - format: date-time - description: Event start time - endTime: - type: string - format: date-time - description: Event end time - description: - type: string - description: Event description - assignedDriverId: - type: string - description: Assigned driver ID - status: - $ref: '#/components/schemas/EventStatus' - type: - $ref: '#/components/schemas/EventType' - - ScheduleEventCreate: - type: object - required: - - title - - location - - startTime - - endTime - - type - properties: - title: - type: string - minLength: 1 - location: - type: string - minLength: 1 - startTime: - type: string - format: date-time - endTime: - type: string - format: date-time - description: - type: string - type: - $ref: '#/components/schemas/EventType' - assignedDriverId: - type: string - - EventStatus: - type: string - enum: [scheduled, in-progress, completed, cancelled] - description: Current status of the event - - EventType: - type: string - enum: [transport, meeting, event, meal, accommodation] - description: Type of event - - FlightInfo: - type: object - properties: - flightNumber: - type: string - flightDate: - type: string - format: date - status: - type: string - enum: [scheduled, active, landed, cancelled, delayed] - airline: - type: string - aircraft: - type: string - departure: - $ref: '#/components/schemas/FlightLocation' - arrival: - $ref: '#/components/schemas/FlightLocation' - delay: - type: integer - description: Delay in minutes - lastUpdated: - type: string - format: date-time - source: - type: string - description: Data source (e.g., aviationstack) - - FlightLocation: - type: object - properties: - airport: - type: string - description: Airport code - airportName: - type: string - description: Full airport name - scheduled: - type: string - format: date-time - estimated: - type: string - format: date-time - actual: - type: string - format: date-time - terminal: - type: string - gate: - type: string - - AdminSettings: - type: object - properties: - apiKeys: - type: object - properties: - aviationStackKey: - type: string - description: Masked API key - googleMapsKey: - type: string - description: Masked API key - twilioKey: - type: string - description: Masked API key - systemSettings: - type: object - properties: - defaultPickupLocation: - type: string - defaultDropoffLocation: - type: string - timeZone: - type: string - notificationsEnabled: - type: boolean - - ValidationError: - type: object - properties: - error: - type: string - validationErrors: - type: array - items: - $ref: '#/components/schemas/ValidationMessage' - warnings: - type: array - items: - $ref: '#/components/schemas/ValidationWarning' - message: - type: string - - ValidationMessage: - type: object - properties: - field: - type: string - message: - type: string - code: - type: string - - ValidationWarning: - type: object - properties: - field: - type: string - message: - type: string - code: - type: string - - securitySchemes: - AdminAuth: - type: apiKey - in: header - name: admin-auth - description: Admin authentication header - -security: - - AdminAuth: [] diff --git a/auth0-action.js b/auth0-action.js deleted file mode 100644 index 95ab565..0000000 --- a/auth0-action.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Auth0 Action: Add User Info to Token - * - * This action adds user profile information to the access token - * so the backend can create/validate users properly. - * - * Deploy this in Auth0 Dashboard: - * 1. Go to Actions → Flows → Login - * 2. Click "+" → Build from scratch - * 3. Name: "Add User Info to Token" - * 4. Copy this code - * 5. Click Deploy - * 6. Drag into flow between Start and Complete - * 7. Click Apply - */ - -exports.onExecutePostLogin = async (event, api) => { - const namespace = 'https://vip-coordinator-api'; - - if (event.authorization) { - // Add user profile to access token - api.accessToken.setCustomClaim(`${namespace}/email`, event.user.email); - api.accessToken.setCustomClaim(`${namespace}/name`, event.user.name); - api.accessToken.setCustomClaim(`${namespace}/picture`, event.user.picture); - api.accessToken.setCustomClaim(`${namespace}/email_verified`, event.user.email_verified); - - // Optionally require email verification before allowing access - // Uncomment the lines below if you want to enforce email verification - /* - if (!event.user.email_verified) { - api.access.deny('Please verify your email before accessing the application.'); - } - */ - } -}; diff --git a/auth0-signup-form.json b/auth0-signup-form.json deleted file mode 100644 index 50ba068..0000000 --- a/auth0-signup-form.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "name": "VIP Coordinator User Registration", - "languages": { - "default": "en", - "primary": "en" - }, - "pages": [ - { - "name": "user-registration", - "components": [ - { - "type": "header", - "config": { - "text": "Welcome to VIP Coordinator", - "level": 1 - } - }, - { - "type": "description", - "config": { - "text": "Please provide your information to request access. The first user will be automatically approved as Administrator. Subsequent users require admin approval." - } - }, - { - "type": "text-input", - "config": { - "label": "Full Name", - "name": "name", - "placeholder": "Enter your full name", - "required": true, - "validation": { - "min": 2, - "max": 100 - } - } - }, - { - "type": "email-input", - "config": { - "label": "Email Address", - "name": "email", - "placeholder": "your.email@example.com", - "required": true, - "validation": { - "email": true - } - } - }, - { - "type": "password-input", - "config": { - "label": "Password", - "name": "password", - "placeholder": "Create a secure password", - "required": true, - "validation": { - "min": 8, - "passwordStrength": "fair" - } - } - }, - { - "type": "text-input", - "config": { - "label": "Department (Optional)", - "name": "user_metadata.department", - "placeholder": "e.g., Transportation, Events, Security", - "required": false - } - }, - { - "type": "text-input", - "config": { - "label": "Organization (Optional)", - "name": "user_metadata.organization", - "placeholder": "Your organization name", - "required": false - } - }, - { - "type": "checkbox", - "config": { - "label": "I agree to the terms and conditions", - "name": "terms_accepted", - "required": true - } - }, - { - "type": "submit-button", - "config": { - "label": "Request Access" - } - } - ] - } - ], - "ending": { - "after_signup": { - "type": "redirect", - "url": "http://localhost:5173/callback" - } - }, - "style": { - "primaryColor": "#3B82F6", - "backgroundColor": "#F9FAFB", - "buttonRadius": "8px" - } -} diff --git a/backend-old-20260125/.env.example b/backend-old-20260125/.env.example deleted file mode 100644 index 2f00bce..0000000 --- a/backend-old-20260125/.env.example +++ /dev/null @@ -1,37 +0,0 @@ -# ============================================ -# Auth0 Configuration (NEW) -# ============================================ -AUTH0_DOMAIN=your-tenant.us.auth0.com -AUTH0_CLIENT_ID=your_client_id_here -AUTH0_CLIENT_SECRET=your_client_secret_here -AUTH0_AUDIENCE=https://vip-coordinator-api -AUTH0_API_ID=6910ee5a03672ebf6f04fc1c -AUTH0_CALLBACK_URL=http://localhost:3000/auth/auth0/callback -AUTH0_LOGOUT_URL=http://localhost:5173/login - -# ============================================ -# Application Configuration -# ============================================ -PORT=3000 -NODE_ENV=development -FRONTEND_URL=http://localhost:5173 - -# ============================================ -# Database Configuration -# ============================================ -DATABASE_URL=postgresql://postgres:changeme@db:5432/vip_coordinator - -# ============================================ -# Redis Configuration -# ============================================ -REDIS_URL=redis://redis:6379 - -# ============================================ -# Legacy Google OAuth (REMOVE THESE) -# These are no longer needed with Auth0 -# ============================================ -# GOOGLE_CLIENT_ID= -# GOOGLE_CLIENT_SECRET= -# GOOGLE_REDIRECT_URI= -# JWT_SECRET= -# ADMIN_PASSWORD= diff --git a/backend-old-20260125/Dockerfile b/backend-old-20260125/Dockerfile deleted file mode 100644 index daa964c..0000000 --- a/backend-old-20260125/Dockerfile +++ /dev/null @@ -1,46 +0,0 @@ -# Multi-stage build for development and production -FROM node:22-alpine AS base - -WORKDIR /app - -# Copy package files -COPY package*.json ./ - -# Development stage -FROM base AS development -RUN npm install -COPY . . -EXPOSE 3000 -CMD ["npm", "run", "dev"] - -# Production stage -FROM base AS production - -# Install dependencies (including dev dependencies for build) -RUN npm install - -# Copy source code -COPY . . - -# Build the application -RUN npx tsc --version && npx tsc - -# Remove dev dependencies to reduce image size -RUN npm prune --omit=dev - -# Create non-root user for security -RUN addgroup -g 1001 -S nodejs && \ - adduser -S nodejs -u 1001 - -# Change ownership of the app directory -RUN chown -R nodejs:nodejs /app -USER nodejs - -# Health check -HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ - CMD node -e "require('http').get('http://localhost:3000/api/health', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) })" || exit 1 - -EXPOSE 3000 - -# Start the production server -CMD ["npm", "start"] diff --git a/backend-old-20260125/dist/config/database.d.ts b/backend-old-20260125/dist/config/database.d.ts deleted file mode 100644 index 2d14e62..0000000 --- a/backend-old-20260125/dist/config/database.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Pool } from 'pg'; -declare const pool: Pool; -export default pool; -//# sourceMappingURL=database.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/config/database.d.ts.map b/backend-old-20260125/dist/config/database.d.ts.map deleted file mode 100644 index 78d6a8d..0000000 --- a/backend-old-20260125/dist/config/database.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/config/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAK1B,QAAA,MAAM,IAAI,MAKR,CAAC;AAWH,eAAe,IAAI,CAAC"} \ No newline at end of file diff --git a/backend-old-20260125/dist/config/database.js b/backend-old-20260125/dist/config/database.js deleted file mode 100644 index c7f4080..0000000 --- a/backend-old-20260125/dist/config/database.js +++ /dev/null @@ -1,23 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const pg_1 = require("pg"); -const dotenv_1 = __importDefault(require("dotenv")); -dotenv_1.default.config(); -const pool = new pg_1.Pool({ - connectionString: process.env.DATABASE_URL || 'postgresql://postgres:changeme@localhost:5432/vip_coordinator', - max: 20, - idleTimeoutMillis: 30000, - connectionTimeoutMillis: 2000, -}); -// Test the connection -pool.on('connect', () => { - console.log('āœ… Connected to PostgreSQL database'); -}); -pool.on('error', (err) => { - console.error('āŒ PostgreSQL connection error:', err); -}); -exports.default = pool; -//# sourceMappingURL=database.js.map \ No newline at end of file diff --git a/backend-old-20260125/dist/config/database.js.map b/backend-old-20260125/dist/config/database.js.map deleted file mode 100644 index 8306b29..0000000 --- a/backend-old-20260125/dist/config/database.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/config/database.ts"],"names":[],"mappings":";;;;;AAAA,2BAA0B;AAC1B,oDAA4B;AAE5B,gBAAM,CAAC,MAAM,EAAE,CAAC;AAEhB,MAAM,IAAI,GAAG,IAAI,SAAI,CAAC;IACpB,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,+DAA+D;IAC7G,GAAG,EAAE,EAAE;IACP,iBAAiB,EAAE,KAAK;IACxB,uBAAuB,EAAE,IAAI;CAC9B,CAAC,CAAC;AAEH,sBAAsB;AACtB,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;IACtB,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;AACpD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;IACvB,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,GAAG,CAAC,CAAC;AACvD,CAAC,CAAC,CAAC;AAEH,kBAAe,IAAI,CAAC"} \ No newline at end of file diff --git a/backend-old-20260125/dist/config/mockDatabase.d.ts b/backend-old-20260125/dist/config/mockDatabase.d.ts deleted file mode 100644 index d19a2f9..0000000 --- a/backend-old-20260125/dist/config/mockDatabase.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -declare class MockDatabase { - private users; - private vips; - private drivers; - private scheduleEvents; - private adminSettings; - constructor(); - query(text: string, params?: any[]): Promise; - connect(): Promise<{ - query: (text: string, params?: any[]) => Promise; - release: () => void; - }>; - end(): Promise; - on(event: string, callback: Function): void; -} -export default MockDatabase; -//# sourceMappingURL=mockDatabase.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/config/mockDatabase.d.ts.map b/backend-old-20260125/dist/config/mockDatabase.d.ts.map deleted file mode 100644 index 0e54bba..0000000 --- a/backend-old-20260125/dist/config/mockDatabase.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"mockDatabase.d.ts","sourceRoot":"","sources":["../../src/config/mockDatabase.ts"],"names":[],"mappings":"AAyBA,cAAM,YAAY;IAChB,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,IAAI,CAAmC;IAC/C,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,aAAa,CAAkC;;IA8BjD,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;IAiGjD,OAAO;sBAjGK,MAAM,WAAW,GAAG,EAAE,KAAG,OAAO,CAAC,GAAG,CAAC;;;IAyGjD,GAAG;IAIT,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ;CAKrC;AAED,eAAe,YAAY,CAAC"} \ No newline at end of file diff --git a/backend-old-20260125/dist/config/mockDatabase.js b/backend-old-20260125/dist/config/mockDatabase.js deleted file mode 100644 index 7e86003..0000000 --- a/backend-old-20260125/dist/config/mockDatabase.js +++ /dev/null @@ -1,137 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -class MockDatabase { - constructor() { - this.users = new Map(); - this.vips = new Map(); - this.drivers = new Map(); - this.scheduleEvents = new Map(); - this.adminSettings = new Map(); - // Add a test admin user - const adminId = '1'; - this.users.set(adminId, { - id: adminId, - email: 'admin@example.com', - name: 'Test Admin', - role: 'admin', - created_at: new Date(), - updated_at: new Date() - }); - // Add some test VIPs - this.vips.set('1', { - id: '1', - name: 'John Doe', - organization: 'Test Org', - department: 'Office of Development', - transport_mode: 'flight', - expected_arrival: '2025-07-25 14:00', - needs_airport_pickup: true, - needs_venue_transport: true, - notes: 'Test VIP', - created_at: new Date(), - updated_at: new Date() - }); - } - async query(text, params) { - console.log('Mock DB Query:', text.substring(0, 50) + '...'); - // Handle user queries - if (text.includes('COUNT(*) FROM users')) { - return { rows: [{ count: this.users.size.toString() }] }; - } - if (text.includes('SELECT * FROM users WHERE email')) { - const email = params?.[0]; - const user = Array.from(this.users.values()).find(u => u.email === email); - return { rows: user ? [user] : [] }; - } - if (text.includes('SELECT * FROM users WHERE id')) { - const id = params?.[0]; - const user = this.users.get(id); - return { rows: user ? [user] : [] }; - } - if (text.includes('SELECT * FROM users WHERE google_id')) { - const google_id = params?.[0]; - const user = Array.from(this.users.values()).find(u => u.google_id === google_id); - return { rows: user ? [user] : [] }; - } - if (text.includes('INSERT INTO users')) { - const id = Date.now().toString(); - const user = { - id, - email: params?.[0], - name: params?.[1], - role: params?.[2] || 'coordinator', - google_id: params?.[4], - created_at: new Date(), - updated_at: new Date() - }; - this.users.set(id, user); - return { rows: [user] }; - } - // Handle VIP queries - if (text.includes('SELECT v.*') && text.includes('FROM vips')) { - const vips = Array.from(this.vips.values()); - return { - rows: vips.map(v => ({ - ...v, - flights: [] - })) - }; - } - // Handle admin settings queries - if (text.includes('SELECT * FROM admin_settings')) { - const settings = Array.from(this.adminSettings.entries()).map(([key, value]) => ({ - key, - value - })); - return { rows: settings }; - } - // Handle drivers queries - if (text.includes('SELECT * FROM drivers')) { - const drivers = Array.from(this.drivers.values()); - return { rows: drivers }; - } - // Handle schedule events queries - if (text.includes('SELECT * FROM schedule_events')) { - const events = Array.from(this.scheduleEvents.values()); - return { rows: events }; - } - if (text.includes('INSERT INTO vips')) { - const id = Date.now().toString(); - const vip = { - id, - name: params?.[0], - organization: params?.[1], - department: params?.[2] || 'Office of Development', - transport_mode: params?.[3] || 'flight', - expected_arrival: params?.[4], - needs_airport_pickup: params?.[5] !== false, - needs_venue_transport: params?.[6] !== false, - notes: params?.[7] || '', - created_at: new Date(), - updated_at: new Date() - }; - this.vips.set(id, vip); - return { rows: [vip] }; - } - // Default empty result - console.log('Unhandled query:', text); - return { rows: [] }; - } - async connect() { - return { - query: this.query.bind(this), - release: () => { } - }; - } - // Make compatible with pg Pool interface - async end() { - console.log('Mock database connection closed'); - } - on(event, callback) { - if (event === 'connect') { - setTimeout(() => callback(), 100); - } - } -} -exports.default = MockDatabase; -//# sourceMappingURL=mockDatabase.js.map \ No newline at end of file diff --git a/backend-old-20260125/dist/config/mockDatabase.js.map b/backend-old-20260125/dist/config/mockDatabase.js.map deleted file mode 100644 index 074cf71..0000000 --- a/backend-old-20260125/dist/config/mockDatabase.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"mockDatabase.js","sourceRoot":"","sources":["../../src/config/mockDatabase.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/config/redis.d.ts b/backend-old-20260125/dist/config/redis.d.ts deleted file mode 100644 index 2f26493..0000000 --- a/backend-old-20260125/dist/config/redis.d.ts +++ /dev/null @@ -1,292 +0,0 @@ -declare const redisClient: import("@redis/client").RedisClientType<{ - graph: { - CONFIG_GET: typeof import("@redis/graph/dist/commands/CONFIG_GET"); - configGet: typeof import("@redis/graph/dist/commands/CONFIG_GET"); - CONFIG_SET: typeof import("@redis/graph/dist/commands/CONFIG_SET"); - configSet: typeof import("@redis/graph/dist/commands/CONFIG_SET"); - DELETE: typeof import("@redis/graph/dist/commands/DELETE"); - delete: typeof import("@redis/graph/dist/commands/DELETE"); - EXPLAIN: typeof import("@redis/graph/dist/commands/EXPLAIN"); - explain: typeof import("@redis/graph/dist/commands/EXPLAIN"); - LIST: typeof import("@redis/graph/dist/commands/LIST"); - list: typeof import("@redis/graph/dist/commands/LIST"); - PROFILE: typeof import("@redis/graph/dist/commands/PROFILE"); - profile: typeof import("@redis/graph/dist/commands/PROFILE"); - QUERY: typeof import("@redis/graph/dist/commands/QUERY"); - query: typeof import("@redis/graph/dist/commands/QUERY"); - RO_QUERY: typeof import("@redis/graph/dist/commands/RO_QUERY"); - roQuery: typeof import("@redis/graph/dist/commands/RO_QUERY"); - SLOWLOG: typeof import("@redis/graph/dist/commands/SLOWLOG"); - slowLog: typeof import("@redis/graph/dist/commands/SLOWLOG"); - }; - json: { - ARRAPPEND: typeof import("@redis/json/dist/commands/ARRAPPEND"); - arrAppend: typeof import("@redis/json/dist/commands/ARRAPPEND"); - ARRINDEX: typeof import("@redis/json/dist/commands/ARRINDEX"); - arrIndex: typeof import("@redis/json/dist/commands/ARRINDEX"); - ARRINSERT: typeof import("@redis/json/dist/commands/ARRINSERT"); - arrInsert: typeof import("@redis/json/dist/commands/ARRINSERT"); - ARRLEN: typeof import("@redis/json/dist/commands/ARRLEN"); - arrLen: typeof import("@redis/json/dist/commands/ARRLEN"); - ARRPOP: typeof import("@redis/json/dist/commands/ARRPOP"); - arrPop: typeof import("@redis/json/dist/commands/ARRPOP"); - ARRTRIM: typeof import("@redis/json/dist/commands/ARRTRIM"); - arrTrim: typeof import("@redis/json/dist/commands/ARRTRIM"); - DEBUG_MEMORY: typeof import("@redis/json/dist/commands/DEBUG_MEMORY"); - debugMemory: typeof import("@redis/json/dist/commands/DEBUG_MEMORY"); - DEL: typeof import("@redis/json/dist/commands/DEL"); - del: typeof import("@redis/json/dist/commands/DEL"); - FORGET: typeof import("@redis/json/dist/commands/FORGET"); - forget: typeof import("@redis/json/dist/commands/FORGET"); - GET: typeof import("@redis/json/dist/commands/GET"); - get: typeof import("@redis/json/dist/commands/GET"); - MERGE: typeof import("@redis/json/dist/commands/MERGE"); - merge: typeof import("@redis/json/dist/commands/MERGE"); - MGET: typeof import("@redis/json/dist/commands/MGET"); - mGet: typeof import("@redis/json/dist/commands/MGET"); - MSET: typeof import("@redis/json/dist/commands/MSET"); - mSet: typeof import("@redis/json/dist/commands/MSET"); - NUMINCRBY: typeof import("@redis/json/dist/commands/NUMINCRBY"); - numIncrBy: typeof import("@redis/json/dist/commands/NUMINCRBY"); - NUMMULTBY: typeof import("@redis/json/dist/commands/NUMMULTBY"); - numMultBy: typeof import("@redis/json/dist/commands/NUMMULTBY"); - OBJKEYS: typeof import("@redis/json/dist/commands/OBJKEYS"); - objKeys: typeof import("@redis/json/dist/commands/OBJKEYS"); - OBJLEN: typeof import("@redis/json/dist/commands/OBJLEN"); - objLen: typeof import("@redis/json/dist/commands/OBJLEN"); - RESP: typeof import("@redis/json/dist/commands/RESP"); - resp: typeof import("@redis/json/dist/commands/RESP"); - SET: typeof import("@redis/json/dist/commands/SET"); - set: typeof import("@redis/json/dist/commands/SET"); - STRAPPEND: typeof import("@redis/json/dist/commands/STRAPPEND"); - strAppend: typeof import("@redis/json/dist/commands/STRAPPEND"); - STRLEN: typeof import("@redis/json/dist/commands/STRLEN"); - strLen: typeof import("@redis/json/dist/commands/STRLEN"); - TYPE: typeof import("@redis/json/dist/commands/TYPE"); - type: typeof import("@redis/json/dist/commands/TYPE"); - }; - ft: { - _LIST: typeof import("@redis/search/dist/commands/_LIST"); - _list: typeof import("@redis/search/dist/commands/_LIST"); - ALTER: typeof import("@redis/search/dist/commands/ALTER"); - alter: typeof import("@redis/search/dist/commands/ALTER"); - AGGREGATE_WITHCURSOR: typeof import("@redis/search/dist/commands/AGGREGATE_WITHCURSOR"); - aggregateWithCursor: typeof import("@redis/search/dist/commands/AGGREGATE_WITHCURSOR"); - AGGREGATE: typeof import("@redis/search/dist/commands/AGGREGATE"); - aggregate: typeof import("@redis/search/dist/commands/AGGREGATE"); - ALIASADD: typeof import("@redis/search/dist/commands/ALIASADD"); - aliasAdd: typeof import("@redis/search/dist/commands/ALIASADD"); - ALIASDEL: typeof import("@redis/search/dist/commands/ALIASDEL"); - aliasDel: typeof import("@redis/search/dist/commands/ALIASDEL"); - ALIASUPDATE: typeof import("@redis/search/dist/commands/ALIASUPDATE"); - aliasUpdate: typeof import("@redis/search/dist/commands/ALIASUPDATE"); - CONFIG_GET: typeof import("@redis/search/dist/commands/CONFIG_GET"); - configGet: typeof import("@redis/search/dist/commands/CONFIG_GET"); - CONFIG_SET: typeof import("@redis/search/dist/commands/CONFIG_SET"); - configSet: typeof import("@redis/search/dist/commands/CONFIG_SET"); - CREATE: typeof import("@redis/search/dist/commands/CREATE"); - create: typeof import("@redis/search/dist/commands/CREATE"); - CURSOR_DEL: typeof import("@redis/search/dist/commands/CURSOR_DEL"); - cursorDel: typeof import("@redis/search/dist/commands/CURSOR_DEL"); - CURSOR_READ: typeof import("@redis/search/dist/commands/CURSOR_READ"); - cursorRead: typeof import("@redis/search/dist/commands/CURSOR_READ"); - DICTADD: typeof import("@redis/search/dist/commands/DICTADD"); - dictAdd: typeof import("@redis/search/dist/commands/DICTADD"); - DICTDEL: typeof import("@redis/search/dist/commands/DICTDEL"); - dictDel: typeof import("@redis/search/dist/commands/DICTDEL"); - DICTDUMP: typeof import("@redis/search/dist/commands/DICTDUMP"); - dictDump: typeof import("@redis/search/dist/commands/DICTDUMP"); - DROPINDEX: typeof import("@redis/search/dist/commands/DROPINDEX"); - dropIndex: typeof import("@redis/search/dist/commands/DROPINDEX"); - EXPLAIN: typeof import("@redis/search/dist/commands/EXPLAIN"); - explain: typeof import("@redis/search/dist/commands/EXPLAIN"); - EXPLAINCLI: typeof import("@redis/search/dist/commands/EXPLAINCLI"); - explainCli: typeof import("@redis/search/dist/commands/EXPLAINCLI"); - INFO: typeof import("@redis/search/dist/commands/INFO"); - info: typeof import("@redis/search/dist/commands/INFO"); - PROFILESEARCH: typeof import("@redis/search/dist/commands/PROFILE_SEARCH"); - profileSearch: typeof import("@redis/search/dist/commands/PROFILE_SEARCH"); - PROFILEAGGREGATE: typeof import("@redis/search/dist/commands/PROFILE_AGGREGATE"); - profileAggregate: typeof import("@redis/search/dist/commands/PROFILE_AGGREGATE"); - SEARCH: typeof import("@redis/search/dist/commands/SEARCH"); - search: typeof import("@redis/search/dist/commands/SEARCH"); - SEARCH_NOCONTENT: typeof import("@redis/search/dist/commands/SEARCH_NOCONTENT"); - searchNoContent: typeof import("@redis/search/dist/commands/SEARCH_NOCONTENT"); - SPELLCHECK: typeof import("@redis/search/dist/commands/SPELLCHECK"); - spellCheck: typeof import("@redis/search/dist/commands/SPELLCHECK"); - SUGADD: typeof import("@redis/search/dist/commands/SUGADD"); - sugAdd: typeof import("@redis/search/dist/commands/SUGADD"); - SUGDEL: typeof import("@redis/search/dist/commands/SUGDEL"); - sugDel: typeof import("@redis/search/dist/commands/SUGDEL"); - SUGGET_WITHPAYLOADS: typeof import("@redis/search/dist/commands/SUGGET_WITHPAYLOADS"); - sugGetWithPayloads: typeof import("@redis/search/dist/commands/SUGGET_WITHPAYLOADS"); - SUGGET_WITHSCORES_WITHPAYLOADS: typeof import("@redis/search/dist/commands/SUGGET_WITHSCORES_WITHPAYLOADS"); - sugGetWithScoresWithPayloads: typeof import("@redis/search/dist/commands/SUGGET_WITHSCORES_WITHPAYLOADS"); - SUGGET_WITHSCORES: typeof import("@redis/search/dist/commands/SUGGET_WITHSCORES"); - sugGetWithScores: typeof import("@redis/search/dist/commands/SUGGET_WITHSCORES"); - SUGGET: typeof import("@redis/search/dist/commands/SUGGET"); - sugGet: typeof import("@redis/search/dist/commands/SUGGET"); - SUGLEN: typeof import("@redis/search/dist/commands/SUGLEN"); - sugLen: typeof import("@redis/search/dist/commands/SUGLEN"); - SYNDUMP: typeof import("@redis/search/dist/commands/SYNDUMP"); - synDump: typeof import("@redis/search/dist/commands/SYNDUMP"); - SYNUPDATE: typeof import("@redis/search/dist/commands/SYNUPDATE"); - synUpdate: typeof import("@redis/search/dist/commands/SYNUPDATE"); - TAGVALS: typeof import("@redis/search/dist/commands/TAGVALS"); - tagVals: typeof import("@redis/search/dist/commands/TAGVALS"); - }; - ts: { - ADD: typeof import("@redis/time-series/dist/commands/ADD"); - add: typeof import("@redis/time-series/dist/commands/ADD"); - ALTER: typeof import("@redis/time-series/dist/commands/ALTER"); - alter: typeof import("@redis/time-series/dist/commands/ALTER"); - CREATE: typeof import("@redis/time-series/dist/commands/CREATE"); - create: typeof import("@redis/time-series/dist/commands/CREATE"); - CREATERULE: typeof import("@redis/time-series/dist/commands/CREATERULE"); - createRule: typeof import("@redis/time-series/dist/commands/CREATERULE"); - DECRBY: typeof import("@redis/time-series/dist/commands/DECRBY"); - decrBy: typeof import("@redis/time-series/dist/commands/DECRBY"); - DEL: typeof import("@redis/time-series/dist/commands/DEL"); - del: typeof import("@redis/time-series/dist/commands/DEL"); - DELETERULE: typeof import("@redis/time-series/dist/commands/DELETERULE"); - deleteRule: typeof import("@redis/time-series/dist/commands/DELETERULE"); - GET: typeof import("@redis/time-series/dist/commands/GET"); - get: typeof import("@redis/time-series/dist/commands/GET"); - INCRBY: typeof import("@redis/time-series/dist/commands/INCRBY"); - incrBy: typeof import("@redis/time-series/dist/commands/INCRBY"); - INFO_DEBUG: typeof import("@redis/time-series/dist/commands/INFO_DEBUG"); - infoDebug: typeof import("@redis/time-series/dist/commands/INFO_DEBUG"); - INFO: typeof import("@redis/time-series/dist/commands/INFO"); - info: typeof import("@redis/time-series/dist/commands/INFO"); - MADD: typeof import("@redis/time-series/dist/commands/MADD"); - mAdd: typeof import("@redis/time-series/dist/commands/MADD"); - MGET: typeof import("@redis/time-series/dist/commands/MGET"); - mGet: typeof import("@redis/time-series/dist/commands/MGET"); - MGET_WITHLABELS: typeof import("@redis/time-series/dist/commands/MGET_WITHLABELS"); - mGetWithLabels: typeof import("@redis/time-series/dist/commands/MGET_WITHLABELS"); - QUERYINDEX: typeof import("@redis/time-series/dist/commands/QUERYINDEX"); - queryIndex: typeof import("@redis/time-series/dist/commands/QUERYINDEX"); - RANGE: typeof import("@redis/time-series/dist/commands/RANGE"); - range: typeof import("@redis/time-series/dist/commands/RANGE"); - REVRANGE: typeof import("@redis/time-series/dist/commands/REVRANGE"); - revRange: typeof import("@redis/time-series/dist/commands/REVRANGE"); - MRANGE: typeof import("@redis/time-series/dist/commands/MRANGE"); - mRange: typeof import("@redis/time-series/dist/commands/MRANGE"); - MRANGE_WITHLABELS: typeof import("@redis/time-series/dist/commands/MRANGE_WITHLABELS"); - mRangeWithLabels: typeof import("@redis/time-series/dist/commands/MRANGE_WITHLABELS"); - MREVRANGE: typeof import("@redis/time-series/dist/commands/MREVRANGE"); - mRevRange: typeof import("@redis/time-series/dist/commands/MREVRANGE"); - MREVRANGE_WITHLABELS: typeof import("@redis/time-series/dist/commands/MREVRANGE_WITHLABELS"); - mRevRangeWithLabels: typeof import("@redis/time-series/dist/commands/MREVRANGE_WITHLABELS"); - }; - bf: { - ADD: typeof import("@redis/bloom/dist/commands/bloom/ADD"); - add: typeof import("@redis/bloom/dist/commands/bloom/ADD"); - CARD: typeof import("@redis/bloom/dist/commands/bloom/CARD"); - card: typeof import("@redis/bloom/dist/commands/bloom/CARD"); - EXISTS: typeof import("@redis/bloom/dist/commands/bloom/EXISTS"); - exists: typeof import("@redis/bloom/dist/commands/bloom/EXISTS"); - INFO: typeof import("@redis/bloom/dist/commands/bloom/INFO"); - info: typeof import("@redis/bloom/dist/commands/bloom/INFO"); - INSERT: typeof import("@redis/bloom/dist/commands/bloom/INSERT"); - insert: typeof import("@redis/bloom/dist/commands/bloom/INSERT"); - LOADCHUNK: typeof import("@redis/bloom/dist/commands/bloom/LOADCHUNK"); - loadChunk: typeof import("@redis/bloom/dist/commands/bloom/LOADCHUNK"); - MADD: typeof import("@redis/bloom/dist/commands/bloom/MADD"); - mAdd: typeof import("@redis/bloom/dist/commands/bloom/MADD"); - MEXISTS: typeof import("@redis/bloom/dist/commands/bloom/MEXISTS"); - mExists: typeof import("@redis/bloom/dist/commands/bloom/MEXISTS"); - RESERVE: typeof import("@redis/bloom/dist/commands/bloom/RESERVE"); - reserve: typeof import("@redis/bloom/dist/commands/bloom/RESERVE"); - SCANDUMP: typeof import("@redis/bloom/dist/commands/bloom/SCANDUMP"); - scanDump: typeof import("@redis/bloom/dist/commands/bloom/SCANDUMP"); - }; - cms: { - INCRBY: typeof import("@redis/bloom/dist/commands/count-min-sketch/INCRBY"); - incrBy: typeof import("@redis/bloom/dist/commands/count-min-sketch/INCRBY"); - INFO: typeof import("@redis/bloom/dist/commands/count-min-sketch/INFO"); - info: typeof import("@redis/bloom/dist/commands/count-min-sketch/INFO"); - INITBYDIM: typeof import("@redis/bloom/dist/commands/count-min-sketch/INITBYDIM"); - initByDim: typeof import("@redis/bloom/dist/commands/count-min-sketch/INITBYDIM"); - INITBYPROB: typeof import("@redis/bloom/dist/commands/count-min-sketch/INITBYPROB"); - initByProb: typeof import("@redis/bloom/dist/commands/count-min-sketch/INITBYPROB"); - MERGE: typeof import("@redis/bloom/dist/commands/count-min-sketch/MERGE"); - merge: typeof import("@redis/bloom/dist/commands/count-min-sketch/MERGE"); - QUERY: typeof import("@redis/bloom/dist/commands/count-min-sketch/QUERY"); - query: typeof import("@redis/bloom/dist/commands/count-min-sketch/QUERY"); - }; - cf: { - ADD: typeof import("@redis/bloom/dist/commands/cuckoo/ADD"); - add: typeof import("@redis/bloom/dist/commands/cuckoo/ADD"); - ADDNX: typeof import("@redis/bloom/dist/commands/cuckoo/ADDNX"); - addNX: typeof import("@redis/bloom/dist/commands/cuckoo/ADDNX"); - COUNT: typeof import("@redis/bloom/dist/commands/cuckoo/COUNT"); - count: typeof import("@redis/bloom/dist/commands/cuckoo/COUNT"); - DEL: typeof import("@redis/bloom/dist/commands/cuckoo/DEL"); - del: typeof import("@redis/bloom/dist/commands/cuckoo/DEL"); - EXISTS: typeof import("@redis/bloom/dist/commands/cuckoo/EXISTS"); - exists: typeof import("@redis/bloom/dist/commands/cuckoo/EXISTS"); - INFO: typeof import("@redis/bloom/dist/commands/cuckoo/INFO"); - info: typeof import("@redis/bloom/dist/commands/cuckoo/INFO"); - INSERT: typeof import("@redis/bloom/dist/commands/cuckoo/INSERT"); - insert: typeof import("@redis/bloom/dist/commands/cuckoo/INSERT"); - INSERTNX: typeof import("@redis/bloom/dist/commands/cuckoo/INSERTNX"); - insertNX: typeof import("@redis/bloom/dist/commands/cuckoo/INSERTNX"); - LOADCHUNK: typeof import("@redis/bloom/dist/commands/cuckoo/LOADCHUNK"); - loadChunk: typeof import("@redis/bloom/dist/commands/cuckoo/LOADCHUNK"); - RESERVE: typeof import("@redis/bloom/dist/commands/cuckoo/RESERVE"); - reserve: typeof import("@redis/bloom/dist/commands/cuckoo/RESERVE"); - SCANDUMP: typeof import("@redis/bloom/dist/commands/cuckoo/SCANDUMP"); - scanDump: typeof import("@redis/bloom/dist/commands/cuckoo/SCANDUMP"); - }; - tDigest: { - ADD: typeof import("@redis/bloom/dist/commands/t-digest/ADD"); - add: typeof import("@redis/bloom/dist/commands/t-digest/ADD"); - BYRANK: typeof import("@redis/bloom/dist/commands/t-digest/BYRANK"); - byRank: typeof import("@redis/bloom/dist/commands/t-digest/BYRANK"); - BYREVRANK: typeof import("@redis/bloom/dist/commands/t-digest/BYREVRANK"); - byRevRank: typeof import("@redis/bloom/dist/commands/t-digest/BYREVRANK"); - CDF: typeof import("@redis/bloom/dist/commands/t-digest/CDF"); - cdf: typeof import("@redis/bloom/dist/commands/t-digest/CDF"); - CREATE: typeof import("@redis/bloom/dist/commands/t-digest/CREATE"); - create: typeof import("@redis/bloom/dist/commands/t-digest/CREATE"); - INFO: typeof import("@redis/bloom/dist/commands/t-digest/INFO"); - info: typeof import("@redis/bloom/dist/commands/t-digest/INFO"); - MAX: typeof import("@redis/bloom/dist/commands/t-digest/MAX"); - max: typeof import("@redis/bloom/dist/commands/t-digest/MAX"); - MERGE: typeof import("@redis/bloom/dist/commands/t-digest/MERGE"); - merge: typeof import("@redis/bloom/dist/commands/t-digest/MERGE"); - MIN: typeof import("@redis/bloom/dist/commands/t-digest/MIN"); - min: typeof import("@redis/bloom/dist/commands/t-digest/MIN"); - QUANTILE: typeof import("@redis/bloom/dist/commands/t-digest/QUANTILE"); - quantile: typeof import("@redis/bloom/dist/commands/t-digest/QUANTILE"); - RANK: typeof import("@redis/bloom/dist/commands/t-digest/RANK"); - rank: typeof import("@redis/bloom/dist/commands/t-digest/RANK"); - RESET: typeof import("@redis/bloom/dist/commands/t-digest/RESET"); - reset: typeof import("@redis/bloom/dist/commands/t-digest/RESET"); - REVRANK: typeof import("@redis/bloom/dist/commands/t-digest/REVRANK"); - revRank: typeof import("@redis/bloom/dist/commands/t-digest/REVRANK"); - TRIMMED_MEAN: typeof import("@redis/bloom/dist/commands/t-digest/TRIMMED_MEAN"); - trimmedMean: typeof import("@redis/bloom/dist/commands/t-digest/TRIMMED_MEAN"); - }; - topK: { - ADD: typeof import("@redis/bloom/dist/commands/top-k/ADD"); - add: typeof import("@redis/bloom/dist/commands/top-k/ADD"); - COUNT: typeof import("@redis/bloom/dist/commands/top-k/COUNT"); - count: typeof import("@redis/bloom/dist/commands/top-k/COUNT"); - INCRBY: typeof import("@redis/bloom/dist/commands/top-k/INCRBY"); - incrBy: typeof import("@redis/bloom/dist/commands/top-k/INCRBY"); - INFO: typeof import("@redis/bloom/dist/commands/top-k/INFO"); - info: typeof import("@redis/bloom/dist/commands/top-k/INFO"); - LIST_WITHCOUNT: typeof import("@redis/bloom/dist/commands/top-k/LIST_WITHCOUNT"); - listWithCount: typeof import("@redis/bloom/dist/commands/top-k/LIST_WITHCOUNT"); - LIST: typeof import("@redis/bloom/dist/commands/top-k/LIST"); - list: typeof import("@redis/bloom/dist/commands/top-k/LIST"); - QUERY: typeof import("@redis/bloom/dist/commands/top-k/QUERY"); - query: typeof import("@redis/bloom/dist/commands/top-k/QUERY"); - RESERVE: typeof import("@redis/bloom/dist/commands/top-k/RESERVE"); - reserve: typeof import("@redis/bloom/dist/commands/top-k/RESERVE"); - }; -} & import("redis").RedisModules, import("redis").RedisFunctions, import("redis").RedisScripts>; -export default redisClient; -//# sourceMappingURL=redis.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/config/redis.d.ts.map b/backend-old-20260125/dist/config/redis.d.ts.map deleted file mode 100644 index dd5a90d..0000000 --- a/backend-old-20260125/dist/config/redis.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../src/config/redis.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+FAEf,CAAC;AAeH,eAAe,WAAW,CAAC"} \ No newline at end of file diff --git a/backend-old-20260125/dist/config/redis.js b/backend-old-20260125/dist/config/redis.js deleted file mode 100644 index fe1c567..0000000 --- a/backend-old-20260125/dist/config/redis.js +++ /dev/null @@ -1,23 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const redis_1 = require("redis"); -const dotenv_1 = __importDefault(require("dotenv")); -dotenv_1.default.config(); -const redisClient = (0, redis_1.createClient)({ - url: process.env.REDIS_URL || 'redis://localhost:6379' -}); -redisClient.on('connect', () => { - console.log('āœ… Connected to Redis'); -}); -redisClient.on('error', (err) => { - console.error('āŒ Redis connection error:', err); -}); -// Connect to Redis -redisClient.connect().catch((err) => { - console.error('āŒ Failed to connect to Redis:', err); -}); -exports.default = redisClient; -//# sourceMappingURL=redis.js.map \ No newline at end of file diff --git a/backend-old-20260125/dist/config/redis.js.map b/backend-old-20260125/dist/config/redis.js.map deleted file mode 100644 index 09cff73..0000000 --- a/backend-old-20260125/dist/config/redis.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"redis.js","sourceRoot":"","sources":["../../src/config/redis.ts"],"names":[],"mappings":";;;;;AAAA,iCAAqC;AACrC,oDAA4B;AAE5B,gBAAM,CAAC,MAAM,EAAE,CAAC;AAEhB,MAAM,WAAW,GAAG,IAAA,oBAAY,EAAC;IAC/B,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,wBAAwB;CACvD,CAAC,CAAC;AAEH,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;IAC7B,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;AACtC,CAAC,CAAC,CAAC;AAEH,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;IACrC,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,GAAG,CAAC,CAAC;AAClD,CAAC,CAAC,CAAC;AAEH,mBAAmB;AACnB,WAAW,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,GAAU,EAAE,EAAE;IACzC,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,GAAG,CAAC,CAAC;AACtD,CAAC,CAAC,CAAC;AAEH,kBAAe,WAAW,CAAC"} \ No newline at end of file diff --git a/backend-old-20260125/dist/config/simpleAuth.d.ts b/backend-old-20260125/dist/config/simpleAuth.d.ts deleted file mode 100644 index b033f7a..0000000 --- a/backend-old-20260125/dist/config/simpleAuth.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { User } from '../services/jwtKeyManager'; -export { User } from '../services/jwtKeyManager'; -export declare function generateToken(user: User): string; -export declare function verifyToken(token: string): User | null; -export declare function verifyGoogleToken(googleToken: string): Promise; -export declare function getGoogleAuthUrl(): string; -export declare function exchangeCodeForTokens(code: string): Promise; -export declare function getGoogleUserInfo(accessToken: string): Promise; -//# sourceMappingURL=simpleAuth.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/config/simpleAuth.d.ts.map b/backend-old-20260125/dist/config/simpleAuth.d.ts.map deleted file mode 100644 index 8b4ef7d..0000000 --- a/backend-old-20260125/dist/config/simpleAuth.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"simpleAuth.d.ts","sourceRoot":"","sources":["../../src/config/simpleAuth.ts"],"names":[],"mappings":"AAAA,OAAsB,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AAKhE,OAAO,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AAEjD,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAEhD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEtD;AAGD,wBAAsB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAWzE;AAGD,wBAAgB,gBAAgB,IAAI,MAAM,CAiCzC;AAGD,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAgGtE;AAGD,wBAAsB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAsEzE"} \ No newline at end of file diff --git a/backend-old-20260125/dist/config/simpleAuth.js b/backend-old-20260125/dist/config/simpleAuth.js deleted file mode 100644 index 8b4b522..0000000 --- a/backend-old-20260125/dist/config/simpleAuth.js +++ /dev/null @@ -1,217 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.User = void 0; -exports.generateToken = generateToken; -exports.verifyToken = verifyToken; -exports.verifyGoogleToken = verifyGoogleToken; -exports.getGoogleAuthUrl = getGoogleAuthUrl; -exports.exchangeCodeForTokens = exchangeCodeForTokens; -exports.getGoogleUserInfo = getGoogleUserInfo; -const jwtKeyManager_1 = __importDefault(require("../services/jwtKeyManager")); -// JWT Key Manager now handles all token operations with automatic rotation -// No more static JWT_SECRET needed! -var jwtKeyManager_2 = require("../services/jwtKeyManager"); -Object.defineProperty(exports, "User", { enumerable: true, get: function () { return jwtKeyManager_2.User; } }); -function generateToken(user) { - return jwtKeyManager_1.default.generateToken(user); -} -function verifyToken(token) { - return jwtKeyManager_1.default.verifyToken(token); -} -// Simple Google OAuth2 client using fetch -async function verifyGoogleToken(googleToken) { - try { - const response = await fetch(`https://www.googleapis.com/oauth2/v1/userinfo?access_token=${googleToken}`); - if (!response.ok) { - throw new Error('Invalid Google token'); - } - return await response.json(); - } - catch (error) { - console.error('Error verifying Google token:', error); - return null; - } -} -// Get Google OAuth2 URL -function getGoogleAuthUrl() { - const clientId = process.env.GOOGLE_CLIENT_ID; - const redirectUri = process.env.GOOGLE_REDIRECT_URI || 'http://localhost:3000/auth/google/callback'; - console.log('šŸ”— Generating Google OAuth URL:', { - client_id_present: !!clientId, - redirect_uri: redirectUri, - environment: process.env.NODE_ENV || 'development' - }); - if (!clientId) { - console.error('āŒ GOOGLE_CLIENT_ID not configured'); - throw new Error('GOOGLE_CLIENT_ID not configured'); - } - if (!redirectUri.startsWith('http')) { - console.error('āŒ Invalid redirect URI:', redirectUri); - throw new Error('GOOGLE_REDIRECT_URI must be a valid HTTP/HTTPS URL'); - } - const params = new URLSearchParams({ - client_id: clientId, - redirect_uri: redirectUri, - response_type: 'code', - scope: 'openid email profile', - access_type: 'offline', - prompt: 'consent' - }); - const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`; - console.log('āœ… Google OAuth URL generated successfully'); - return authUrl; -} -// Exchange authorization code for tokens -async function exchangeCodeForTokens(code) { - const clientId = process.env.GOOGLE_CLIENT_ID; - const clientSecret = process.env.GOOGLE_CLIENT_SECRET; - const redirectUri = process.env.GOOGLE_REDIRECT_URI || 'http://localhost:3000/auth/google/callback'; - console.log('šŸ”„ Exchanging OAuth code for tokens:', { - client_id_present: !!clientId, - client_secret_present: !!clientSecret, - redirect_uri: redirectUri, - code_length: code?.length || 0 - }); - if (!clientId || !clientSecret) { - console.error('āŒ Google OAuth credentials not configured:', { - client_id: !!clientId, - client_secret: !!clientSecret - }); - throw new Error('Google OAuth credentials not configured'); - } - if (!code || code.length < 10) { - console.error('āŒ Invalid authorization code:', { code_length: code?.length || 0 }); - throw new Error('Invalid authorization code provided'); - } - try { - const tokenUrl = 'https://oauth2.googleapis.com/token'; - const requestBody = new URLSearchParams({ - client_id: clientId, - client_secret: clientSecret, - code, - grant_type: 'authorization_code', - redirect_uri: redirectUri, - }); - console.log('šŸ“” Making token exchange request to Google:', { - url: tokenUrl, - redirect_uri: redirectUri, - grant_type: 'authorization_code' - }); - const response = await fetch(tokenUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Accept': 'application/json' - }, - body: requestBody, - }); - const responseText = await response.text(); - console.log('šŸ“Ø Token exchange response:', { - status: response.status, - ok: response.ok, - content_type: response.headers.get('content-type'), - response_length: responseText.length - }); - if (!response.ok) { - console.error('āŒ Token exchange failed:', { - status: response.status, - statusText: response.statusText, - response: responseText - }); - throw new Error(`Failed to exchange code for tokens: ${response.status} ${response.statusText}`); - } - let tokenData; - try { - tokenData = JSON.parse(responseText); - } - catch (parseError) { - console.error('āŒ Failed to parse token response:', { response: responseText }); - throw new Error('Invalid JSON response from Google token endpoint'); - } - if (!tokenData.access_token) { - console.error('āŒ No access token in response:', tokenData); - throw new Error('No access token received from Google'); - } - console.log('āœ… Token exchange successful:', { - has_access_token: !!tokenData.access_token, - has_refresh_token: !!tokenData.refresh_token, - token_type: tokenData.token_type, - expires_in: tokenData.expires_in - }); - return tokenData; - } - catch (error) { - console.error('āŒ Error exchanging code for tokens:', { - error: error instanceof Error ? error.message : 'Unknown error', - stack: error instanceof Error ? error.stack : undefined - }); - throw error; - } -} -// Get user info from Google -async function getGoogleUserInfo(accessToken) { - console.log('šŸ‘¤ Getting user info from Google:', { - token_length: accessToken?.length || 0, - token_prefix: accessToken ? accessToken.substring(0, 10) + '...' : 'none' - }); - if (!accessToken || accessToken.length < 10) { - console.error('āŒ Invalid access token for user info request'); - throw new Error('Invalid access token provided'); - } - try { - const userInfoUrl = `https://www.googleapis.com/oauth2/v2/userinfo?access_token=${accessToken}`; - console.log('šŸ“” Making user info request to Google'); - const response = await fetch(userInfoUrl, { - method: 'GET', - headers: { - 'Accept': 'application/json', - 'Authorization': `Bearer ${accessToken}` - } - }); - const responseText = await response.text(); - console.log('šŸ“Ø User info response:', { - status: response.status, - ok: response.ok, - content_type: response.headers.get('content-type'), - response_length: responseText.length - }); - if (!response.ok) { - console.error('āŒ Failed to get user info:', { - status: response.status, - statusText: response.statusText, - response: responseText - }); - throw new Error(`Failed to get user info: ${response.status} ${response.statusText}`); - } - let userData; - try { - userData = JSON.parse(responseText); - } - catch (parseError) { - console.error('āŒ Failed to parse user info response:', { response: responseText }); - throw new Error('Invalid JSON response from Google user info endpoint'); - } - if (!userData.email) { - console.error('āŒ No email in user info response:', userData); - throw new Error('No email address received from Google'); - } - console.log('āœ… User info retrieved successfully:', { - email: userData.email, - name: userData.name, - verified_email: userData.verified_email, - has_picture: !!userData.picture - }); - return userData; - } - catch (error) { - console.error('āŒ Error getting Google user info:', { - error: error instanceof Error ? error.message : 'Unknown error', - stack: error instanceof Error ? error.stack : undefined - }); - throw error; - } -} -//# sourceMappingURL=simpleAuth.js.map \ No newline at end of file diff --git a/backend-old-20260125/dist/config/simpleAuth.js.map b/backend-old-20260125/dist/config/simpleAuth.js.map deleted file mode 100644 index c445f39..0000000 --- a/backend-old-20260125/dist/config/simpleAuth.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"simpleAuth.js","sourceRoot":"","sources":["../../src/config/simpleAuth.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/index.d.ts b/backend-old-20260125/dist/index.d.ts deleted file mode 100644 index e26a57a..0000000 --- a/backend-old-20260125/dist/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/index.d.ts.map b/backend-old-20260125/dist/index.d.ts.map deleted file mode 100644 index 535b86d..0000000 --- a/backend-old-20260125/dist/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/backend-old-20260125/dist/index.js b/backend-old-20260125/dist/index.js deleted file mode 100644 index 9106662..0000000 --- a/backend-old-20260125/dist/index.js +++ /dev/null @@ -1,271 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const express_1 = __importDefault(require("express")); -const cors_1 = __importDefault(require("cors")); -const dotenv_1 = __importDefault(require("dotenv")); -const authService_1 = __importDefault(require("./services/authService")); -const unifiedDataService_1 = __importDefault(require("./services/unifiedDataService")); -const simpleValidation_1 = require("./middleware/simpleValidation"); -const errorHandler_1 = require("./middleware/errorHandler"); -dotenv_1.default.config(); -// Log environment variables status on startup -console.log('Environment variables loaded:'); -console.log('- GOOGLE_CLIENT_ID:', process.env.GOOGLE_CLIENT_ID ? 'Set' : 'Not set'); -console.log('- GOOGLE_CLIENT_SECRET:', process.env.GOOGLE_CLIENT_SECRET ? 'Set' : 'Not set'); -console.log('- GOOGLE_REDIRECT_URI:', process.env.GOOGLE_REDIRECT_URI || 'Not set'); -const app = (0, express_1.default)(); -const port = process.env.PORT || 3000; -// Middleware -app.use((0, cors_1.default)({ - origin: [ - process.env.FRONTEND_URL || 'http://localhost:5173', - 'https://bsa.madeamess.online' - ], - credentials: true -})); -app.use(express_1.default.json()); -app.use(express_1.default.static('public')); -// Health check -app.get('/api/health', (req, res) => { - res.json({ - status: 'OK', - timestamp: new Date().toISOString(), - version: '2.0.0' // Simplified version - }); -}); -// Auth routes -app.get('/auth/setup', async (req, res) => { - try { - // Check if any users exist in the system - const userCount = await unifiedDataService_1.default.getUserCount(); - res.json({ - needsSetup: userCount === 0, - hasUsers: userCount > 0 - }); - } - catch (error) { - console.error('Error in /auth/setup:', error); - res.status(500).json({ error: 'Failed to check setup status' }); - } -}); -app.get('/auth/google', (req, res) => { - res.redirect(authService_1.default.getGoogleAuthUrl()); -}); -app.get('/auth/google/url', (req, res) => { - try { - // Return the Google OAuth URL as JSON for the frontend - const url = authService_1.default.getGoogleAuthUrl(); - res.json({ url }); - } - catch (error) { - console.error('Error generating Google Auth URL:', error); - res.status(500).json({ - error: 'Google OAuth configuration error', - message: error.message - }); - } -}); -app.post('/auth/google/callback', async (req, res) => { - try { - const { code } = req.body; - const { user, token } = await authService_1.default.handleGoogleAuth(code); - res.json({ user, token }); - } - catch (error) { - res.status(400).json({ error: 'Authentication failed' }); - } -}); -app.post('/auth/google/exchange', async (req, res) => { - try { - const { code } = req.body; - const { user, token } = await authService_1.default.handleGoogleAuth(code); - res.json({ user, token }); - } - catch (error) { - res.status(400).json({ error: 'Authentication failed' }); - } -}); -app.post('/auth/google/verify', async (req, res) => { - try { - const { credential } = req.body; - const { user, token } = await authService_1.default.verifyGoogleToken(credential); - res.json({ user, token }); - } - catch (error) { - console.error('Google token verification error:', error); - res.status(400).json({ error: 'Authentication failed' }); - } -}); -app.get('/auth/me', authService_1.default.requireAuth, (req, res) => { - res.json(req.user); -}); -app.post('/auth/logout', (req, res) => { - res.json({ message: 'Logged out successfully' }); -}); -// VIP routes -app.get('/api/vips', async (req, res, next) => { - try { - const vips = await unifiedDataService_1.default.getVips(); - res.json(vips); - } - catch (error) { - next(error); - } -}); -app.get('/api/vips/:id', async (req, res, next) => { - try { - const vip = await unifiedDataService_1.default.getVipById(req.params.id); - if (!vip) - return res.status(404).json({ error: 'VIP not found' }); - res.json(vip); - } - catch (error) { - next(error); - } -}); -app.post('/api/vips', authService_1.default.requireAuth, authService_1.default.requireRole(['coordinator', 'administrator']), (0, simpleValidation_1.validate)(simpleValidation_1.schemas.createVip), async (req, res, next) => { - try { - const vip = await unifiedDataService_1.default.createVip(req.body); - res.status(201).json(vip); - } - catch (error) { - next(error); - } -}); -app.put('/api/vips/:id', authService_1.default.requireAuth, authService_1.default.requireRole(['coordinator', 'administrator']), (0, simpleValidation_1.validate)(simpleValidation_1.schemas.updateVip), async (req, res, next) => { - try { - const vip = await unifiedDataService_1.default.updateVip(req.params.id, req.body); - if (!vip) - return res.status(404).json({ error: 'VIP not found' }); - res.json(vip); - } - catch (error) { - next(error); - } -}); -app.delete('/api/vips/:id', authService_1.default.requireAuth, authService_1.default.requireRole(['coordinator', 'administrator']), async (req, res, next) => { - try { - const vip = await unifiedDataService_1.default.deleteVip(req.params.id); - if (!vip) - return res.status(404).json({ error: 'VIP not found' }); - res.json({ message: 'VIP deleted successfully' }); - } - catch (error) { - next(error); - } -}); -// Driver routes -app.get('/api/drivers', async (req, res, next) => { - try { - const drivers = await unifiedDataService_1.default.getDrivers(); - res.json(drivers); - } - catch (error) { - next(error); - } -}); -app.post('/api/drivers', authService_1.default.requireAuth, authService_1.default.requireRole(['coordinator', 'administrator']), (0, simpleValidation_1.validate)(simpleValidation_1.schemas.createDriver), async (req, res, next) => { - try { - const driver = await unifiedDataService_1.default.createDriver(req.body); - res.status(201).json(driver); - } - catch (error) { - next(error); - } -}); -app.put('/api/drivers/:id', authService_1.default.requireAuth, authService_1.default.requireRole(['coordinator', 'administrator']), (0, simpleValidation_1.validate)(simpleValidation_1.schemas.updateDriver), async (req, res, next) => { - try { - const driver = await unifiedDataService_1.default.updateDriver(req.params.id, req.body); - if (!driver) - return res.status(404).json({ error: 'Driver not found' }); - res.json(driver); - } - catch (error) { - next(error); - } -}); -app.delete('/api/drivers/:id', authService_1.default.requireAuth, authService_1.default.requireRole(['coordinator', 'administrator']), async (req, res, next) => { - try { - const driver = await unifiedDataService_1.default.deleteDriver(req.params.id); - if (!driver) - return res.status(404).json({ error: 'Driver not found' }); - res.json({ message: 'Driver deleted successfully' }); - } - catch (error) { - next(error); - } -}); -// Schedule routes -app.get('/api/vips/:vipId/schedule', authService_1.default.requireAuth, async (req, res, next) => { - try { - const schedule = await unifiedDataService_1.default.getScheduleByVipId(req.params.vipId); - res.json(schedule); - } - catch (error) { - next(error); - } -}); -app.post('/api/vips/:vipId/schedule', authService_1.default.requireAuth, authService_1.default.requireRole(['coordinator', 'administrator']), (0, simpleValidation_1.validate)(simpleValidation_1.schemas.createScheduleEvent), async (req, res, next) => { - try { - const event = await unifiedDataService_1.default.createScheduleEvent(req.params.vipId, req.body); - res.status(201).json(event); - } - catch (error) { - next(error); - } -}); -app.put('/api/vips/:vipId/schedule/:eventId', authService_1.default.requireAuth, authService_1.default.requireRole(['coordinator', 'administrator']), (0, simpleValidation_1.validate)(simpleValidation_1.schemas.updateScheduleEvent), async (req, res, next) => { - try { - const event = await unifiedDataService_1.default.updateScheduleEvent(req.params.eventId, req.body); - if (!event) - return res.status(404).json({ error: 'Event not found' }); - res.json(event); - } - catch (error) { - next(error); - } -}); -app.delete('/api/vips/:vipId/schedule/:eventId', authService_1.default.requireAuth, authService_1.default.requireRole(['coordinator', 'administrator']), async (req, res, next) => { - try { - const event = await unifiedDataService_1.default.deleteScheduleEvent(req.params.eventId); - if (!event) - return res.status(404).json({ error: 'Event not found' }); - res.json({ message: 'Event deleted successfully' }); - } - catch (error) { - next(error); - } -}); -// Admin routes (simplified) -app.get('/api/admin/settings', authService_1.default.requireAuth, authService_1.default.requireRole(['administrator']), async (req, res, next) => { - try { - const settings = await unifiedDataService_1.default.getAdminSettings(); - res.json(settings); - } - catch (error) { - next(error); - } -}); -app.post('/api/admin/settings', authService_1.default.requireAuth, authService_1.default.requireRole(['administrator']), async (req, res, next) => { - try { - const { key, value } = req.body; - await unifiedDataService_1.default.updateAdminSetting(key, value); - res.json({ message: 'Setting updated successfully' }); - } - catch (error) { - next(error); - } -}); -// Error handling -app.use(errorHandler_1.notFoundHandler); -app.use(errorHandler_1.errorHandler); -// Start server -app.listen(port, () => { - console.log(`šŸš€ Server running on port ${port}`); - console.log(`šŸ„ Health check: http://localhost:${port}/api/health`); - console.log(`šŸ“š API docs: http://localhost:${port}/api-docs.html`); -}); -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/backend-old-20260125/dist/index.js.map b/backend-old-20260125/dist/index.js.map deleted file mode 100644 index 66e6101..0000000 --- a/backend-old-20260125/dist/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/index.original.d.ts b/backend-old-20260125/dist/index.original.d.ts deleted file mode 100644 index b090983..0000000 --- a/backend-old-20260125/dist/index.original.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=index.original.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/index.original.d.ts.map b/backend-old-20260125/dist/index.original.d.ts.map deleted file mode 100644 index ed1815b..0000000 --- a/backend-old-20260125/dist/index.original.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.original.d.ts","sourceRoot":"","sources":["../src/index.original.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/backend-old-20260125/dist/index.original.js b/backend-old-20260125/dist/index.original.js deleted file mode 100644 index 570abc3..0000000 --- a/backend-old-20260125/dist/index.original.js +++ /dev/null @@ -1,765 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const express_1 = __importDefault(require("express")); -const dotenv_1 = __importDefault(require("dotenv")); -const cors_1 = __importDefault(require("cors")); -const simpleAuth_1 = __importStar(require("./routes/simpleAuth")); -const flightService_1 = __importDefault(require("./services/flightService")); -const driverConflictService_1 = __importDefault(require("./services/driverConflictService")); -const scheduleValidationService_1 = __importDefault(require("./services/scheduleValidationService")); -const flightTrackingScheduler_1 = __importDefault(require("./services/flightTrackingScheduler")); -const enhancedDataService_1 = __importDefault(require("./services/enhancedDataService")); -const databaseService_1 = __importDefault(require("./services/databaseService")); -const jwtKeyManager_1 = __importDefault(require("./services/jwtKeyManager")); // Initialize JWT Key Manager -const errorHandler_1 = require("./middleware/errorHandler"); -const logger_1 = require("./middleware/logger"); -const validation_1 = require("./middleware/validation"); -const schemas_1 = require("./types/schemas"); -dotenv_1.default.config(); -const app = (0, express_1.default)(); -const port = process.env.PORT ? parseInt(process.env.PORT) : 3000; -// Middleware -app.use((0, cors_1.default)({ - origin: [ - process.env.FRONTEND_URL || 'http://localhost:5173', - 'http://localhost:5173', - 'http://localhost:3000', - 'http://localhost', // Frontend Docker container (local testing) - 'https://bsa.madeamess.online' // Production frontend domain (where users access the site) - ], - credentials: true -})); -app.use(express_1.default.json()); -app.use(express_1.default.urlencoded({ extended: true })); -// Add request logging -app.use(logger_1.requestLogger); -// Simple JWT-based authentication - no passport needed -// Authentication routes -app.use('/auth', simpleAuth_1.default); -// Temporary admin bypass route (remove after setup) -app.get('/admin-bypass', (req, res) => { - res.redirect(`${process.env.FRONTEND_URL || 'http://localhost:5173'}/admin?bypass=true`); -}); -// Serve static files from public directory -app.use(express_1.default.static('public')); -// Enhanced health check endpoint with authentication system status -app.get('/api/health', (0, errorHandler_1.asyncHandler)(async (req, res) => { - const timestamp = new Date().toISOString(); - // Check JWT Key Manager status - const jwtStatus = jwtKeyManager_1.default.getStatus(); - // Check environment variables - const envCheck = { - google_client_id: !!process.env.GOOGLE_CLIENT_ID, - google_client_secret: !!process.env.GOOGLE_CLIENT_SECRET, - google_redirect_uri: !!process.env.GOOGLE_REDIRECT_URI, - frontend_url: !!process.env.FRONTEND_URL, - database_url: !!process.env.DATABASE_URL, - admin_password: !!process.env.ADMIN_PASSWORD - }; - // Check database connectivity - let databaseStatus = 'unknown'; - let userCount = 0; - try { - userCount = await databaseService_1.default.getUserCount(); - databaseStatus = 'connected'; - } - catch (dbError) { - databaseStatus = 'disconnected'; - console.error('Health check - Database error:', dbError); - } - // Overall system health - const isHealthy = databaseStatus === 'connected' && - jwtStatus.hasCurrentKey && - envCheck.google_client_id && - envCheck.google_client_secret; - const healthData = { - status: isHealthy ? 'OK' : 'DEGRADED', - timestamp, - version: '1.0.0', - environment: process.env.NODE_ENV || 'development', - services: { - database: { - status: databaseStatus, - user_count: databaseStatus === 'connected' ? userCount : null - }, - authentication: { - jwt_key_manager: jwtStatus, - oauth_configured: envCheck.google_client_id && envCheck.google_client_secret, - environment_variables: envCheck - } - }, - uptime: process.uptime(), - memory: process.memoryUsage() - }; - // Log health check for monitoring - console.log(`šŸ„ Health Check [${timestamp}]:`, { - status: healthData.status, - database: databaseStatus, - jwt_keys: jwtStatus.hasCurrentKey, - oauth: envCheck.google_client_id && envCheck.google_client_secret - }); - res.status(isHealthy ? 200 : 503).json(healthData); -})); -// Data is now persisted using dataService - no more in-memory storage! -// Admin password - MUST be set via environment variable in production -const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'CHANGE_ME_ADMIN_PASSWORD'; -// Initialize flight tracking scheduler -const flightTracker = new flightTrackingScheduler_1.default(flightService_1.default); -// VIP routes (protected) -app.post('/api/vips', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['coordinator', 'administrator']), (0, validation_1.validate)(schemas_1.createVipSchema), (0, errorHandler_1.asyncHandler)(async (req, res) => { - // Create a new VIP - data is already validated - const { name, organization, department, // New: Office of Development or Admin - transportMode, flightNumber, // Legacy single flight - flights, // New: array of flights - expectedArrival, needsAirportPickup, needsVenueTransport, notes } = req.body; - const newVip = { - id: Date.now().toString(), // Simple ID generation - name, - organization, - department: department || 'Office of Development', // Default to Office of Development - transportMode: transportMode || 'flight', - // Support both legacy single flight and new multiple flights - flightNumber: transportMode === 'flight' && !flights ? flightNumber : undefined, - flights: transportMode === 'flight' && flights ? flights : undefined, - expectedArrival: transportMode === 'self-driving' ? expectedArrival : undefined, - arrivalTime: transportMode === 'flight' ? undefined : expectedArrival, // Legacy field for flight arrivals - needsAirportPickup: transportMode === 'flight' ? (needsAirportPickup !== false) : false, - needsVenueTransport: needsVenueTransport !== false, // Default to true - assignedDriverIds: [], - notes: notes || '', - schedule: [] - }; - const savedVip = await enhancedDataService_1.default.addVip(newVip); - // Add flights to tracking scheduler if applicable - if (savedVip.transportMode === 'flight' && savedVip.flights && savedVip.flights.length > 0) { - flightTracker.addVipFlights(savedVip.id, savedVip.name, savedVip.flights); - } - res.status(201).json(savedVip); -})); -app.get('/api/vips', simpleAuth_1.requireAuth, async (req, res) => { - try { - // Fetch all VIPs - const vips = await enhancedDataService_1.default.getVips(); - res.json(vips); - } - catch (error) { - res.status(500).json({ error: 'Failed to fetch VIPs' }); - } -}); -app.put('/api/vips/:id', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['coordinator', 'administrator']), (0, validation_1.validate)(schemas_1.updateVipSchema), (0, errorHandler_1.asyncHandler)(async (req, res) => { - // Update a VIP - data is already validated - const { id } = req.params; - const { name, organization, department, // New: Office of Development or Admin - transportMode, flightNumber, // Legacy single flight - flights, // New: array of flights - expectedArrival, needsAirportPickup, needsVenueTransport, notes } = req.body; - const updatedVip = { - name, - organization, - department: department || 'Office of Development', - transportMode: transportMode || 'flight', - // Support both legacy single flight and new multiple flights - flights: transportMode === 'flight' && flights ? flights : undefined, - expectedArrival: transportMode === 'self-driving' ? expectedArrival : undefined, - needsAirportPickup: transportMode === 'flight' ? (needsAirportPickup !== false) : false, - needsVenueTransport: needsVenueTransport !== false, - notes: notes || '' - }; - const savedVip = await enhancedDataService_1.default.updateVip(id, updatedVip); - if (!savedVip) { - return res.status(404).json({ error: 'VIP not found' }); - } - // Update flight tracking if needed - if (savedVip.transportMode === 'flight') { - // Remove old flights - flightTracker.removeVipFlights(id); - // Add new flights if any - if (savedVip.flights && savedVip.flights.length > 0) { - flightTracker.addVipFlights(savedVip.id, savedVip.name, savedVip.flights); - } - } - res.json(savedVip); -})); -app.delete('/api/vips/:id', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['coordinator', 'administrator']), async (req, res) => { - // Delete a VIP - const { id } = req.params; - try { - const deletedVip = await enhancedDataService_1.default.deleteVip(id); - if (!deletedVip) { - return res.status(404).json({ error: 'VIP not found' }); - } - // Remove from flight tracking - flightTracker.removeVipFlights(id); - res.json({ message: 'VIP deleted successfully', vip: deletedVip }); - } - catch (error) { - res.status(500).json({ error: 'Failed to delete VIP' }); - } -}); -// Driver routes (protected) -app.post('/api/drivers', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['coordinator', 'administrator']), (0, validation_1.validate)(schemas_1.createDriverSchema), (0, errorHandler_1.asyncHandler)(async (req, res) => { - // Create a new driver - data is already validated - const { name, phone, email, vehicleInfo, status } = req.body; - const newDriver = { - id: Date.now().toString(), - name, - phone, - email, - vehicleInfo, - status: status || 'available', - department: 'Office of Development', // Default to Office of Development - currentLocation: { lat: 0, lng: 0 }, - assignedVipIds: [] - }; - const savedDriver = await enhancedDataService_1.default.addDriver(newDriver); - res.status(201).json(savedDriver); -})); -app.get('/api/drivers', simpleAuth_1.requireAuth, async (req, res) => { - try { - // Fetch all drivers - const drivers = await enhancedDataService_1.default.getDrivers(); - res.json(drivers); - } - catch (error) { - res.status(500).json({ error: 'Failed to fetch drivers' }); - } -}); -app.put('/api/drivers/:id', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['coordinator', 'administrator']), async (req, res) => { - // Update a driver - const { id } = req.params; - const { name, phone, currentLocation, department } = req.body; - try { - const updatedDriver = { - name, - phone, - department: department || 'Office of Development', - currentLocation: currentLocation || { lat: 0, lng: 0 } - }; - const savedDriver = await enhancedDataService_1.default.updateDriver(id, updatedDriver); - if (!savedDriver) { - return res.status(404).json({ error: 'Driver not found' }); - } - res.json(savedDriver); - } - catch (error) { - res.status(500).json({ error: 'Failed to update driver' }); - } -}); -app.delete('/api/drivers/:id', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['coordinator', 'administrator']), async (req, res) => { - // Delete a driver - const { id } = req.params; - try { - const deletedDriver = await enhancedDataService_1.default.deleteDriver(id); - if (!deletedDriver) { - return res.status(404).json({ error: 'Driver not found' }); - } - res.json({ message: 'Driver deleted successfully', driver: deletedDriver }); - } - catch (error) { - res.status(500).json({ error: 'Failed to delete driver' }); - } -}); -// Enhanced flight tracking routes with date specificity -app.get('/api/flights/:flightNumber', async (req, res) => { - try { - const { flightNumber } = req.params; - const { date, departureAirport, arrivalAirport } = req.query; - // Default to today if no date provided - const flightDate = date || new Date().toISOString().split('T')[0]; - const flightData = await flightService_1.default.getFlightInfo({ - flightNumber, - date: flightDate, - departureAirport: departureAirport, - arrivalAirport: arrivalAirport - }); - if (flightData) { - // Always return flight data for validation, even if date doesn't match - res.json(flightData); - } - else { - // Only return 404 if the flight number itself is invalid - res.status(404).json({ error: 'Invalid flight number - this flight does not exist' }); - } - } - catch (error) { - res.status(500).json({ error: 'Failed to fetch flight data' }); - } -}); -// Start periodic updates for a flight -app.post('/api/flights/:flightNumber/track', async (req, res) => { - try { - const { flightNumber } = req.params; - const { date, intervalMinutes = 5 } = req.body; - if (!date) { - return res.status(400).json({ error: 'Flight date is required' }); - } - flightService_1.default.startPeriodicUpdates({ - flightNumber, - date - }, intervalMinutes); - res.json({ message: `Started tracking ${flightNumber} on ${date}` }); - } - catch (error) { - res.status(500).json({ error: 'Failed to start flight tracking' }); - } -}); -// Stop periodic updates for a flight -app.delete('/api/flights/:flightNumber/track', async (req, res) => { - try { - const { flightNumber } = req.params; - const { date } = req.query; - if (!date) { - return res.status(400).json({ error: 'Flight date is required' }); - } - const key = `${flightNumber}_${date}`; - flightService_1.default.stopPeriodicUpdates(key); - res.json({ message: `Stopped tracking ${flightNumber} on ${date}` }); - } - catch (error) { - res.status(500).json({ error: 'Failed to stop flight tracking' }); - } -}); -app.post('/api/flights/batch', async (req, res) => { - try { - const { flights } = req.body; - if (!Array.isArray(flights)) { - return res.status(400).json({ error: 'flights must be an array of {flightNumber, date} objects' }); - } - // Validate flight objects - for (const flight of flights) { - if (!flight.flightNumber || !flight.date) { - return res.status(400).json({ error: 'Each flight must have flightNumber and date' }); - } - } - const flightData = await flightService_1.default.getMultipleFlights(flights); - res.json(flightData); - } - catch (error) { - res.status(500).json({ error: 'Failed to fetch flight data' }); - } -}); -// Get flight tracking status -app.get('/api/flights/tracking/status', (req, res) => { - const status = flightTracker.getTrackingStatus(); - res.json(status); -}); -// Schedule management routes (protected) -app.get('/api/vips/:vipId/schedule', simpleAuth_1.requireAuth, async (req, res) => { - const { vipId } = req.params; - try { - const vipSchedule = await enhancedDataService_1.default.getSchedule(vipId); - res.json(vipSchedule); - } - catch (error) { - res.status(500).json({ error: 'Failed to fetch schedule' }); - } -}); -app.post('/api/vips/:vipId/schedule', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['coordinator', 'administrator']), async (req, res) => { - const { vipId } = req.params; - const { title, location, startTime, endTime, description, type, assignedDriverId } = req.body; - // Validate the event - const validationErrors = scheduleValidationService_1.default.validateEvent({ - title: title || '', - location: location || '', - startTime: startTime || '', - endTime: endTime || '', - type: type || '' - }, false); - const { critical, warnings } = scheduleValidationService_1.default.categorizeErrors(validationErrors); - // Return validation errors if any critical errors exist - if (critical.length > 0) { - return res.status(400).json({ - error: 'Validation failed', - validationErrors: critical, - warnings: warnings, - message: scheduleValidationService_1.default.getErrorSummary(critical) - }); - } - const newEvent = { - id: Date.now().toString(), - title, - location, - startTime, - endTime, - description: description || '', - assignedDriverId: assignedDriverId || '', - status: 'scheduled', - type - }; - try { - const savedEvent = await enhancedDataService_1.default.addScheduleEvent(vipId, newEvent); - // Include warnings in the response if any - const response = { ...savedEvent }; - if (warnings.length > 0) { - response.warnings = warnings; - } - res.status(201).json(response); - } - catch (error) { - res.status(500).json({ error: 'Failed to create schedule event' }); - } -}); -app.put('/api/vips/:vipId/schedule/:eventId', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['coordinator', 'administrator']), async (req, res) => { - const { vipId, eventId } = req.params; - const { title, location, startTime, endTime, description, type, assignedDriverId, status } = req.body; - // Validate the updated event (with edit flag for grace period) - const validationErrors = scheduleValidationService_1.default.validateEvent({ - title: title || '', - location: location || '', - startTime: startTime || '', - endTime: endTime || '', - type: type || '' - }, true); - const { critical, warnings } = scheduleValidationService_1.default.categorizeErrors(validationErrors); - // Return validation errors if any critical errors exist - if (critical.length > 0) { - return res.status(400).json({ - error: 'Validation failed', - validationErrors: critical, - warnings: warnings, - message: scheduleValidationService_1.default.getErrorSummary(critical) - }); - } - const updatedEvent = { - id: eventId, - title, - location, - startTime, - endTime, - description: description || '', - assignedDriverId: assignedDriverId || '', - type, - status: status || 'scheduled' - }; - try { - const savedEvent = await enhancedDataService_1.default.updateScheduleEvent(vipId, eventId, updatedEvent); - if (!savedEvent) { - return res.status(404).json({ error: 'Event not found' }); - } - // Include warnings in the response if any - const response = { ...savedEvent }; - if (warnings.length > 0) { - response.warnings = warnings; - } - res.json(response); - } - catch (error) { - res.status(500).json({ error: 'Failed to update schedule event' }); - } -}); -app.patch('/api/vips/:vipId/schedule/:eventId/status', simpleAuth_1.requireAuth, async (req, res) => { - const { vipId, eventId } = req.params; - const { status } = req.body; - try { - const currentSchedule = await enhancedDataService_1.default.getSchedule(vipId); - const currentEvent = currentSchedule.find((event) => event.id === eventId); - if (!currentEvent) { - return res.status(404).json({ error: 'Event not found' }); - } - const updatedEvent = { ...currentEvent, status }; - const savedEvent = await enhancedDataService_1.default.updateScheduleEvent(vipId, eventId, updatedEvent); - if (!savedEvent) { - return res.status(404).json({ error: 'Event not found' }); - } - res.json(savedEvent); - } - catch (error) { - res.status(500).json({ error: 'Failed to update event status' }); - } -}); -app.delete('/api/vips/:vipId/schedule/:eventId', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['coordinator', 'administrator']), async (req, res) => { - const { vipId, eventId } = req.params; - try { - const deletedEvent = await enhancedDataService_1.default.deleteScheduleEvent(vipId, eventId); - if (!deletedEvent) { - return res.status(404).json({ error: 'Event not found' }); - } - res.json({ message: 'Event deleted successfully', event: deletedEvent }); - } - catch (error) { - res.status(500).json({ error: 'Failed to delete schedule event' }); - } -}); -// Driver availability and conflict checking (protected) -app.post('/api/drivers/availability', simpleAuth_1.requireAuth, async (req, res) => { - const { startTime, endTime, location } = req.body; - if (!startTime || !endTime) { - return res.status(400).json({ error: 'startTime and endTime are required' }); - } - try { - const allSchedules = await enhancedDataService_1.default.getAllSchedules(); - const drivers = await enhancedDataService_1.default.getDrivers(); - const availability = driverConflictService_1.default.getDriverAvailability({ startTime, endTime, location: location || '' }, allSchedules, drivers); - res.json(availability); - } - catch (error) { - res.status(500).json({ error: 'Failed to check driver availability' }); - } -}); -// Check conflicts for specific driver assignment (protected) -app.post('/api/drivers/:driverId/conflicts', simpleAuth_1.requireAuth, async (req, res) => { - const { driverId } = req.params; - const { startTime, endTime, location } = req.body; - if (!startTime || !endTime) { - return res.status(400).json({ error: 'startTime and endTime are required' }); - } - try { - const allSchedules = await enhancedDataService_1.default.getAllSchedules(); - const drivers = await enhancedDataService_1.default.getDrivers(); - const conflicts = driverConflictService_1.default.checkDriverConflicts(driverId, { startTime, endTime, location: location || '' }, allSchedules, drivers); - res.json({ conflicts }); - } - catch (error) { - res.status(500).json({ error: 'Failed to check driver conflicts' }); - } -}); -// Get driver's complete schedule (protected) -app.get('/api/drivers/:driverId/schedule', simpleAuth_1.requireAuth, async (req, res) => { - const { driverId } = req.params; - try { - const drivers = await enhancedDataService_1.default.getDrivers(); - const driver = drivers.find((d) => d.id === driverId); - if (!driver) { - return res.status(404).json({ error: 'Driver not found' }); - } - // Get all events assigned to this driver across all VIPs - const driverSchedule = []; - const allSchedules = await enhancedDataService_1.default.getAllSchedules(); - const vips = await enhancedDataService_1.default.getVips(); - Object.entries(allSchedules).forEach(([vipId, events]) => { - events.forEach((event) => { - if (event.assignedDriverId === driverId) { - // Get VIP name - const vip = vips.find((v) => v.id === vipId); - driverSchedule.push({ - ...event, - vipId, - vipName: vip ? vip.name : 'Unknown VIP' - }); - } - }); - }); - // Sort by start time - driverSchedule.sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime()); - res.json({ - driver: { - id: driver.id, - name: driver.name, - phone: driver.phone, - department: driver.department - }, - schedule: driverSchedule - }); - } - catch (error) { - res.status(500).json({ error: 'Failed to fetch driver schedule' }); - } -}); -// Admin routes -app.post('/api/admin/authenticate', (req, res) => { - const { password } = req.body; - if (password === ADMIN_PASSWORD) { - res.json({ success: true }); - } - else { - res.status(401).json({ error: 'Invalid password' }); - } -}); -app.get('/api/admin/settings', async (req, res) => { - const adminAuth = req.headers['admin-auth']; - if (adminAuth !== 'true') { - return res.status(401).json({ error: 'Unauthorized' }); - } - try { - const adminSettings = await enhancedDataService_1.default.getAdminSettings(); - // Return settings but mask API keys for display only - // IMPORTANT: Don't return the actual keys, just indicate they exist - const maskedSettings = { - apiKeys: { - aviationStackKey: adminSettings.apiKeys.aviationStackKey ? '***' + adminSettings.apiKeys.aviationStackKey.slice(-4) : '', - googleMapsKey: adminSettings.apiKeys.googleMapsKey ? '***' + adminSettings.apiKeys.googleMapsKey.slice(-4) : '', - twilioKey: adminSettings.apiKeys.twilioKey ? '***' + adminSettings.apiKeys.twilioKey.slice(-4) : '', - googleClientId: adminSettings.apiKeys.googleClientId ? '***' + adminSettings.apiKeys.googleClientId.slice(-4) : '', - googleClientSecret: adminSettings.apiKeys.googleClientSecret ? '***' + adminSettings.apiKeys.googleClientSecret.slice(-4) : '' - }, - systemSettings: adminSettings.systemSettings - }; - res.json(maskedSettings); - } - catch (error) { - res.status(500).json({ error: 'Failed to fetch admin settings' }); - } -}); -app.post('/api/admin/settings', async (req, res) => { - const adminAuth = req.headers['admin-auth']; - if (adminAuth !== 'true') { - return res.status(401).json({ error: 'Unauthorized' }); - } - try { - const { apiKeys, systemSettings } = req.body; - const currentSettings = await enhancedDataService_1.default.getAdminSettings(); - // Update API keys (only if provided and not masked) - if (apiKeys) { - if (apiKeys.aviationStackKey && !apiKeys.aviationStackKey.startsWith('***')) { - currentSettings.apiKeys.aviationStackKey = apiKeys.aviationStackKey; - // Update the environment variable for the flight service - process.env.AVIATIONSTACK_API_KEY = apiKeys.aviationStackKey; - } - if (apiKeys.googleMapsKey && !apiKeys.googleMapsKey.startsWith('***')) { - currentSettings.apiKeys.googleMapsKey = apiKeys.googleMapsKey; - } - if (apiKeys.twilioKey && !apiKeys.twilioKey.startsWith('***')) { - currentSettings.apiKeys.twilioKey = apiKeys.twilioKey; - } - if (apiKeys.googleClientId && !apiKeys.googleClientId.startsWith('***')) { - currentSettings.apiKeys.googleClientId = apiKeys.googleClientId; - // Update the environment variable for Google OAuth - process.env.GOOGLE_CLIENT_ID = apiKeys.googleClientId; - } - if (apiKeys.googleClientSecret && !apiKeys.googleClientSecret.startsWith('***')) { - currentSettings.apiKeys.googleClientSecret = apiKeys.googleClientSecret; - // Update the environment variable for Google OAuth - process.env.GOOGLE_CLIENT_SECRET = apiKeys.googleClientSecret; - } - } - // Update system settings - if (systemSettings) { - currentSettings.systemSettings = { ...currentSettings.systemSettings, ...systemSettings }; - } - // Save the updated settings - await enhancedDataService_1.default.updateAdminSettings(currentSettings); - res.json({ success: true }); - } - catch (error) { - res.status(500).json({ error: 'Failed to update admin settings' }); - } -}); -app.post('/api/admin/test-api/:apiType', async (req, res) => { - const adminAuth = req.headers['admin-auth']; - if (adminAuth !== 'true') { - return res.status(401).json({ error: 'Unauthorized' }); - } - const { apiType } = req.params; - const { apiKey } = req.body; - try { - switch (apiType) { - case 'aviationStackKey': - // Test AviationStack API - const testUrl = `http://api.aviationstack.com/v1/flights?access_key=${apiKey}&limit=1`; - const response = await fetch(testUrl); - if (response.ok) { - const data = await response.json(); - if (data.error) { - res.status(400).json({ error: data.error.message || 'Invalid API key' }); - } - else { - res.json({ success: true, message: 'API key is valid!' }); - } - } - else { - res.status(400).json({ error: 'Failed to validate API key' }); - } - break; - case 'googleMapsKey': - res.json({ success: true, message: 'Google Maps API testing not yet implemented' }); - break; - case 'twilioKey': - res.json({ success: true, message: 'Twilio API testing not yet implemented' }); - break; - default: - res.status(400).json({ error: 'Unknown API type' }); - } - } - catch (error) { - res.status(500).json({ error: 'Failed to test API connection' }); - } -}); -// JWT Key Management endpoints (admin only) -app.get('/api/admin/jwt-status', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['administrator']), (req, res) => { - const jwtKeyManager = require('./services/jwtKeyManager').default; - const status = jwtKeyManager.getStatus(); - res.json({ - keyRotationEnabled: true, - rotationInterval: '24 hours', - gracePeriod: '24 hours', - ...status, - message: 'JWT keys are automatically rotated every 24 hours for enhanced security' - }); -}); -app.post('/api/admin/jwt-rotate', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['administrator']), (req, res) => { - const jwtKeyManager = require('./services/jwtKeyManager').default; - try { - jwtKeyManager.forceRotation(); - res.json({ - success: true, - message: 'JWT key rotation triggered successfully. New tokens will use the new key.' - }); - } - catch (error) { - res.status(500).json({ error: 'Failed to rotate JWT keys' }); - } -}); -// Initialize database and start server -// Add 404 handler for undefined routes -app.use(errorHandler_1.notFoundHandler); -// Add error logging middleware -app.use(logger_1.errorLogger); -// Add global error handler (must be last!) -app.use(errorHandler_1.errorHandler); -async function startServer() { - try { - // Initialize database schema and migrate data - await databaseService_1.default.initializeDatabase(); - console.log('āœ… Database initialization completed'); - // Start the server - app.listen(port, () => { - console.log(`šŸš€ Server is running on port ${port}`); - console.log(`šŸ” Admin password: ${ADMIN_PASSWORD}`); - console.log(`šŸ“Š Admin dashboard: http://localhost:${port === 3000 ? 5173 : port}/admin`); - console.log(`šŸ„ Health check: http://localhost:${port}/api/health`); - console.log(`šŸ“š API docs: http://localhost:${port}/api-docs.html`); - }); - } - catch (error) { - console.error('āŒ Failed to start server:', error); - process.exit(1); - } -} -startServer(); -//# sourceMappingURL=index.original.js.map \ No newline at end of file diff --git a/backend-old-20260125/dist/index.original.js.map b/backend-old-20260125/dist/index.original.js.map deleted file mode 100644 index c6b2110..0000000 --- a/backend-old-20260125/dist/index.original.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.original.js","sourceRoot":"","sources":["../src/index.original.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/indexSimplified.d.ts b/backend-old-20260125/dist/indexSimplified.d.ts deleted file mode 100644 index 808bcb9..0000000 --- a/backend-old-20260125/dist/indexSimplified.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=indexSimplified.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/indexSimplified.d.ts.map b/backend-old-20260125/dist/indexSimplified.d.ts.map deleted file mode 100644 index 31178b1..0000000 --- a/backend-old-20260125/dist/indexSimplified.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"indexSimplified.d.ts","sourceRoot":"","sources":["../src/indexSimplified.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/backend-old-20260125/dist/indexSimplified.js b/backend-old-20260125/dist/indexSimplified.js deleted file mode 100644 index eed46d8..0000000 --- a/backend-old-20260125/dist/indexSimplified.js +++ /dev/null @@ -1,217 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const express_1 = __importDefault(require("express")); -const cors_1 = __importDefault(require("cors")); -const dotenv_1 = __importDefault(require("dotenv")); -const authService_1 = __importDefault(require("./services/authService")); -const unifiedDataService_1 = __importDefault(require("./services/unifiedDataService")); -const simpleValidation_1 = require("./middleware/simpleValidation"); -const errorHandler_1 = require("./middleware/errorHandler"); -dotenv_1.default.config(); -const app = (0, express_1.default)(); -const port = process.env.PORT || 3000; -// Middleware -app.use((0, cors_1.default)({ - origin: [ - process.env.FRONTEND_URL || 'http://localhost:5173', - 'https://bsa.madeamess.online' - ], - credentials: true -})); -app.use(express_1.default.json()); -app.use(express_1.default.static('public')); -// Health check -app.get('/api/health', (req, res) => { - res.json({ - status: 'OK', - timestamp: new Date().toISOString(), - version: '2.0.0' // Simplified version - }); -}); -// Auth routes -app.get('/auth/google', (req, res) => { - res.redirect(authService_1.default.getGoogleAuthUrl()); -}); -app.post('/auth/google/callback', async (req, res) => { - try { - const { code } = req.body; - const { user, token } = await authService_1.default.handleGoogleAuth(code); - res.json({ user, token }); - } - catch (error) { - res.status(400).json({ error: 'Authentication failed' }); - } -}); -app.get('/auth/me', authService_1.default.requireAuth, (req, res) => { - res.json(req.user); -}); -app.post('/auth/logout', (req, res) => { - res.json({ message: 'Logged out successfully' }); -}); -// VIP routes -app.get('/api/vips', authService_1.default.requireAuth, async (req, res, next) => { - try { - const vips = await unifiedDataService_1.default.getVips(); - res.json(vips); - } - catch (error) { - next(error); - } -}); -app.get('/api/vips/:id', authService_1.default.requireAuth, async (req, res, next) => { - try { - const vip = await unifiedDataService_1.default.getVipById(req.params.id); - if (!vip) - return res.status(404).json({ error: 'VIP not found' }); - res.json(vip); - } - catch (error) { - next(error); - } -}); -app.post('/api/vips', authService_1.default.requireAuth, authService_1.default.requireRole(['coordinator', 'administrator']), (0, simpleValidation_1.validate)(simpleValidation_1.schemas.createVip), async (req, res, next) => { - try { - const vip = await unifiedDataService_1.default.createVip(req.body); - res.status(201).json(vip); - } - catch (error) { - next(error); - } -}); -app.put('/api/vips/:id', authService_1.default.requireAuth, authService_1.default.requireRole(['coordinator', 'administrator']), (0, simpleValidation_1.validate)(simpleValidation_1.schemas.updateVip), async (req, res, next) => { - try { - const vip = await unifiedDataService_1.default.updateVip(req.params.id, req.body); - if (!vip) - return res.status(404).json({ error: 'VIP not found' }); - res.json(vip); - } - catch (error) { - next(error); - } -}); -app.delete('/api/vips/:id', authService_1.default.requireAuth, authService_1.default.requireRole(['coordinator', 'administrator']), async (req, res, next) => { - try { - const vip = await unifiedDataService_1.default.deleteVip(req.params.id); - if (!vip) - return res.status(404).json({ error: 'VIP not found' }); - res.json({ message: 'VIP deleted successfully' }); - } - catch (error) { - next(error); - } -}); -// Driver routes -app.get('/api/drivers', authService_1.default.requireAuth, async (req, res, next) => { - try { - const drivers = await unifiedDataService_1.default.getDrivers(); - res.json(drivers); - } - catch (error) { - next(error); - } -}); -app.post('/api/drivers', authService_1.default.requireAuth, authService_1.default.requireRole(['coordinator', 'administrator']), (0, simpleValidation_1.validate)(simpleValidation_1.schemas.createDriver), async (req, res, next) => { - try { - const driver = await unifiedDataService_1.default.createDriver(req.body); - res.status(201).json(driver); - } - catch (error) { - next(error); - } -}); -app.put('/api/drivers/:id', authService_1.default.requireAuth, authService_1.default.requireRole(['coordinator', 'administrator']), (0, simpleValidation_1.validate)(simpleValidation_1.schemas.updateDriver), async (req, res, next) => { - try { - const driver = await unifiedDataService_1.default.updateDriver(req.params.id, req.body); - if (!driver) - return res.status(404).json({ error: 'Driver not found' }); - res.json(driver); - } - catch (error) { - next(error); - } -}); -app.delete('/api/drivers/:id', authService_1.default.requireAuth, authService_1.default.requireRole(['coordinator', 'administrator']), async (req, res, next) => { - try { - const driver = await unifiedDataService_1.default.deleteDriver(req.params.id); - if (!driver) - return res.status(404).json({ error: 'Driver not found' }); - res.json({ message: 'Driver deleted successfully' }); - } - catch (error) { - next(error); - } -}); -// Schedule routes -app.get('/api/vips/:vipId/schedule', authService_1.default.requireAuth, async (req, res, next) => { - try { - const schedule = await unifiedDataService_1.default.getScheduleByVipId(req.params.vipId); - res.json(schedule); - } - catch (error) { - next(error); - } -}); -app.post('/api/vips/:vipId/schedule', authService_1.default.requireAuth, authService_1.default.requireRole(['coordinator', 'administrator']), (0, simpleValidation_1.validate)(simpleValidation_1.schemas.createScheduleEvent), async (req, res, next) => { - try { - const event = await unifiedDataService_1.default.createScheduleEvent(req.params.vipId, req.body); - res.status(201).json(event); - } - catch (error) { - next(error); - } -}); -app.put('/api/vips/:vipId/schedule/:eventId', authService_1.default.requireAuth, authService_1.default.requireRole(['coordinator', 'administrator']), (0, simpleValidation_1.validate)(simpleValidation_1.schemas.updateScheduleEvent), async (req, res, next) => { - try { - const event = await unifiedDataService_1.default.updateScheduleEvent(req.params.eventId, req.body); - if (!event) - return res.status(404).json({ error: 'Event not found' }); - res.json(event); - } - catch (error) { - next(error); - } -}); -app.delete('/api/vips/:vipId/schedule/:eventId', authService_1.default.requireAuth, authService_1.default.requireRole(['coordinator', 'administrator']), async (req, res, next) => { - try { - const event = await unifiedDataService_1.default.deleteScheduleEvent(req.params.eventId); - if (!event) - return res.status(404).json({ error: 'Event not found' }); - res.json({ message: 'Event deleted successfully' }); - } - catch (error) { - next(error); - } -}); -// Admin routes (simplified) -app.get('/api/admin/settings', authService_1.default.requireAuth, authService_1.default.requireRole(['administrator']), async (req, res, next) => { - try { - const settings = await unifiedDataService_1.default.getAdminSettings(); - res.json(settings); - } - catch (error) { - next(error); - } -}); -app.post('/api/admin/settings', authService_1.default.requireAuth, authService_1.default.requireRole(['administrator']), async (req, res, next) => { - try { - const { key, value } = req.body; - await unifiedDataService_1.default.updateAdminSetting(key, value); - res.json({ message: 'Setting updated successfully' }); - } - catch (error) { - next(error); - } -}); -// Error handling -app.use(errorHandler_1.notFoundHandler); -app.use(errorHandler_1.errorHandler); -// Start server -app.listen(port, () => { - console.log(`šŸš€ Server running on port ${port}`); - console.log(`šŸ„ Health check: http://localhost:${port}/api/health`); - console.log(`šŸ“š API docs: http://localhost:${port}/api-docs.html`); -}); -//# sourceMappingURL=indexSimplified.js.map \ No newline at end of file diff --git a/backend-old-20260125/dist/indexSimplified.js.map b/backend-old-20260125/dist/indexSimplified.js.map deleted file mode 100644 index 974936d..0000000 --- a/backend-old-20260125/dist/indexSimplified.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"indexSimplified.js","sourceRoot":"","sources":["../src/indexSimplified.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/middleware/errorHandler.d.ts b/backend-old-20260125/dist/middleware/errorHandler.d.ts deleted file mode 100644 index 14388ea..0000000 --- a/backend-old-20260125/dist/middleware/errorHandler.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -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 \ No newline at end of file diff --git a/backend-old-20260125/dist/middleware/errorHandler.d.ts.map b/backend-old-20260125/dist/middleware/errorHandler.d.ts.map deleted file mode 100644 index 65e0124..0000000 --- a/backend-old-20260125/dist/middleware/errorHandler.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/middleware/errorHandler.js b/backend-old-20260125/dist/middleware/errorHandler.js deleted file mode 100644 index 8f9ffe7..0000000 --- a/backend-old-20260125/dist/middleware/errorHandler.js +++ /dev/null @@ -1,75 +0,0 @@ -"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 \ No newline at end of file diff --git a/backend-old-20260125/dist/middleware/errorHandler.js.map b/backend-old-20260125/dist/middleware/errorHandler.js.map deleted file mode 100644 index 7829f15..0000000 --- a/backend-old-20260125/dist/middleware/errorHandler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/middleware/logger.d.ts b/backend-old-20260125/dist/middleware/logger.d.ts deleted file mode 100644 index f282c11..0000000 --- a/backend-old-20260125/dist/middleware/logger.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -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 \ No newline at end of file diff --git a/backend-old-20260125/dist/middleware/logger.d.ts.map b/backend-old-20260125/dist/middleware/logger.d.ts.map deleted file mode 100644 index 95a53c2..0000000 --- a/backend-old-20260125/dist/middleware/logger.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/middleware/logger.js b/backend-old-20260125/dist/middleware/logger.js deleted file mode 100644 index 816f567..0000000 --- a/backend-old-20260125/dist/middleware/logger.js +++ /dev/null @@ -1,58 +0,0 @@ -"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 \ No newline at end of file diff --git a/backend-old-20260125/dist/middleware/logger.js.map b/backend-old-20260125/dist/middleware/logger.js.map deleted file mode 100644 index c6e5f4d..0000000 --- a/backend-old-20260125/dist/middleware/logger.js.map +++ /dev/null @@ -1 +0,0 @@ -{"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/middleware/simpleValidation.d.ts b/backend-old-20260125/dist/middleware/simpleValidation.d.ts deleted file mode 100644 index 175b4ee..0000000 --- a/backend-old-20260125/dist/middleware/simpleValidation.d.ts +++ /dev/null @@ -1,197 +0,0 @@ -import { z } from 'zod'; -import { Request, Response, NextFunction } from 'express'; -export declare const schemas: { - createVip: z.ZodObject<{ - name: z.ZodString; - organization: z.ZodOptional; - department: z.ZodDefault>; - transportMode: z.ZodDefault>; - flights: z.ZodOptional; - scheduledArrival: z.ZodString; - scheduledDeparture: z.ZodOptional; - }, "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; - needsAirportPickup: z.ZodDefault; - needsVenueTransport: z.ZodDefault; - notes: z.ZodOptional; - }, "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; - organization: z.ZodOptional; - department: z.ZodOptional>; - transportMode: z.ZodOptional>; - flights: z.ZodOptional; - scheduledArrival: z.ZodString; - scheduledDeparture: z.ZodOptional; - }, "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; - needsAirportPickup: z.ZodOptional; - needsVenueTransport: z.ZodOptional; - notes: z.ZodOptional; - }, "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; - phone: z.ZodString; - vehicleInfo: z.ZodOptional; - status: z.ZodDefault>; - }, "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; - email: z.ZodOptional; - phone: z.ZodOptional; - vehicleInfo: z.ZodOptional; - status: z.ZodOptional>; - }, "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; - eventTime: z.ZodString; - eventType: z.ZodEnum<["pickup", "dropoff", "custom"]>; - location: z.ZodString; - notes: z.ZodOptional; - }, "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; - eventTime: z.ZodOptional; - eventType: z.ZodOptional>; - location: z.ZodOptional; - notes: z.ZodOptional; - status: z.ZodOptional>; - }, "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> | undefined>; -//# sourceMappingURL=simpleValidation.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/middleware/simpleValidation.d.ts.map b/backend-old-20260125/dist/middleware/simpleValidation.d.ts.map deleted file mode 100644 index 44c92a1..0000000 --- a/backend-old-20260125/dist/middleware/simpleValidation.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/middleware/simpleValidation.js b/backend-old-20260125/dist/middleware/simpleValidation.js deleted file mode 100644 index 4bee2d0..0000000 --- a/backend-old-20260125/dist/middleware/simpleValidation.js +++ /dev/null @@ -1,91 +0,0 @@ -"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 \ No newline at end of file diff --git a/backend-old-20260125/dist/middleware/simpleValidation.js.map b/backend-old-20260125/dist/middleware/simpleValidation.js.map deleted file mode 100644 index 835419d..0000000 --- a/backend-old-20260125/dist/middleware/simpleValidation.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"simpleValidation.js","sourceRoot":"","sources":["../../src/middleware/simpleValidation.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/middleware/validation.d.ts b/backend-old-20260125/dist/middleware/validation.d.ts deleted file mode 100644 index 90bd31a..0000000 --- a/backend-old-20260125/dist/middleware/validation.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Request, Response, NextFunction } from 'express'; -import { z } from 'zod'; -export declare const validate: (schema: z.ZodSchema) => (req: Request, res: Response, next: NextFunction) => Promise; -export declare const validateQuery: (schema: z.ZodSchema) => (req: Request, res: Response, next: NextFunction) => Promise; -export declare const validateParams: (schema: z.ZodSchema) => (req: Request, res: Response, next: NextFunction) => Promise; -//# sourceMappingURL=validation.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/middleware/validation.d.ts.map b/backend-old-20260125/dist/middleware/validation.d.ts.map deleted file mode 100644 index 81cd54a..0000000 --- a/backend-old-20260125/dist/middleware/validation.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/middleware/validation.js b/backend-old-20260125/dist/middleware/validation.js deleted file mode 100644 index be858d7..0000000 --- a/backend-old-20260125/dist/middleware/validation.js +++ /dev/null @@ -1,78 +0,0 @@ -"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 \ No newline at end of file diff --git a/backend-old-20260125/dist/middleware/validation.js.map b/backend-old-20260125/dist/middleware/validation.js.map deleted file mode 100644 index 478ebf3..0000000 --- a/backend-old-20260125/dist/middleware/validation.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/middleware/validation.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/routes/simpleAuth.d.ts b/backend-old-20260125/dist/routes/simpleAuth.d.ts deleted file mode 100644 index a5762b4..0000000 --- a/backend-old-20260125/dist/routes/simpleAuth.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import express, { Request, Response, NextFunction } from 'express'; -declare const router: import("express-serve-static-core").Router; -export declare function requireAuth(req: Request, res: Response, next: NextFunction): express.Response> | undefined; -export declare function requireRole(roles: string[]): (req: Request, res: Response, next: NextFunction) => express.Response> | undefined; -export default router; -//# sourceMappingURL=simpleAuth.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/routes/simpleAuth.d.ts.map b/backend-old-20260125/dist/routes/simpleAuth.d.ts.map deleted file mode 100644 index 4c6b000..0000000 --- a/backend-old-20260125/dist/routes/simpleAuth.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"simpleAuth.d.ts","sourceRoot":"","sources":["../../src/routes/simpleAuth.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAWnE,QAAA,MAAM,MAAM,4CAAmB,CAAC;AA8ChC,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,0DAwD1E;AAGD,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IACjC,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,4DASxD;AAseD,eAAe,MAAM,CAAC"} \ No newline at end of file diff --git a/backend-old-20260125/dist/routes/simpleAuth.js b/backend-old-20260125/dist/routes/simpleAuth.js deleted file mode 100644 index 6973aec..0000000 --- a/backend-old-20260125/dist/routes/simpleAuth.js +++ /dev/null @@ -1,534 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.requireAuth = requireAuth; -exports.requireRole = requireRole; -const express_1 = __importDefault(require("express")); -const simpleAuth_1 = require("../config/simpleAuth"); -const databaseService_1 = __importDefault(require("../services/databaseService")); -const router = express_1.default.Router(); -// Enhanced logging for production debugging -function logAuthEvent(event, details = {}) { - const timestamp = new Date().toISOString(); - console.log(`šŸ” [AUTH ${timestamp}] ${event}:`, JSON.stringify(details, null, 2)); -} -// Validate environment variables on startup -function validateAuthEnvironment() { - const required = ['GOOGLE_CLIENT_ID', 'GOOGLE_CLIENT_SECRET', 'GOOGLE_REDIRECT_URI', 'FRONTEND_URL']; - const missing = required.filter(key => !process.env[key]); - if (missing.length > 0) { - logAuthEvent('ENVIRONMENT_ERROR', { missing_variables: missing }); - return false; - } - // Validate URLs - const frontendUrl = process.env.FRONTEND_URL; - const redirectUri = process.env.GOOGLE_REDIRECT_URI; - if (!frontendUrl?.startsWith('http')) { - logAuthEvent('ENVIRONMENT_ERROR', { error: 'FRONTEND_URL must start with http/https' }); - return false; - } - if (!redirectUri?.startsWith('http')) { - logAuthEvent('ENVIRONMENT_ERROR', { error: 'GOOGLE_REDIRECT_URI must start with http/https' }); - return false; - } - logAuthEvent('ENVIRONMENT_VALIDATED', { - frontend_url: frontendUrl, - redirect_uri: redirectUri, - client_id_configured: !!process.env.GOOGLE_CLIENT_ID, - client_secret_configured: !!process.env.GOOGLE_CLIENT_SECRET - }); - return true; -} -// Validate environment on module load -const isEnvironmentValid = validateAuthEnvironment(); -// Middleware to check authentication -function requireAuth(req, res, next) { - try { - const authHeader = req.headers.authorization; - if (!authHeader || !authHeader.startsWith('Bearer ')) { - logAuthEvent('AUTH_FAILED', { - reason: 'no_token', - ip: req.ip, - path: req.path, - headers_present: !!req.headers.authorization - }); - return res.status(401).json({ error: 'No token provided' }); - } - const token = authHeader.substring(7); - if (!token || token.length < 10) { - logAuthEvent('AUTH_FAILED', { - reason: 'invalid_token_format', - ip: req.ip, - path: req.path, - token_length: token?.length || 0 - }); - return res.status(401).json({ error: 'Invalid token format' }); - } - const user = (0, simpleAuth_1.verifyToken)(token); - if (!user) { - logAuthEvent('AUTH_FAILED', { - reason: 'token_verification_failed', - ip: req.ip, - path: req.path, - token_prefix: token.substring(0, 10) + '...' - }); - return res.status(401).json({ error: 'Invalid or expired token' }); - } - logAuthEvent('AUTH_SUCCESS', { - user_id: user.id, - user_email: user.email, - user_role: user.role, - ip: req.ip, - path: req.path - }); - req.user = user; - next(); - } - catch (error) { - logAuthEvent('AUTH_ERROR', { - error: error instanceof Error ? error.message : 'Unknown error', - ip: req.ip, - path: req.path - }); - return res.status(500).json({ error: 'Authentication system error' }); - } -} -// Middleware to check role -function requireRole(roles) { - return (req, res, next) => { - const user = req.user; - if (!user || !roles.includes(user.role)) { - return res.status(403).json({ error: 'Insufficient permissions' }); - } - next(); - }; -} -// Get current user -router.get('/me', requireAuth, (req, res) => { - res.json(req.user); -}); -// Setup status endpoint (required by frontend) -router.get('/setup', async (req, res) => { - try { - const clientId = process.env.GOOGLE_CLIENT_ID; - const clientSecret = process.env.GOOGLE_CLIENT_SECRET; - const redirectUri = process.env.GOOGLE_REDIRECT_URI; - const frontendUrl = process.env.FRONTEND_URL; - logAuthEvent('SETUP_CHECK', { - client_id_present: !!clientId, - client_secret_present: !!clientSecret, - redirect_uri_present: !!redirectUri, - frontend_url_present: !!frontendUrl, - environment_valid: isEnvironmentValid - }); - // Check database connectivity - let userCount = 0; - let databaseConnected = false; - try { - userCount = await databaseService_1.default.getUserCount(); - databaseConnected = true; - logAuthEvent('DATABASE_CHECK', { status: 'connected', user_count: userCount }); - } - catch (dbError) { - logAuthEvent('DATABASE_ERROR', { - error: dbError instanceof Error ? dbError.message : 'Unknown database error' - }); - return res.status(500).json({ - error: 'Database connection failed', - details: 'Cannot connect to PostgreSQL database' - }); - } - const setupCompleted = !!(clientId && - clientSecret && - redirectUri && - frontendUrl && - clientId !== 'your-google-client-id-from-console' && - clientId !== 'your-google-client-id' && - isEnvironmentValid); - const response = { - setupCompleted, - firstAdminCreated: userCount > 0, - oauthConfigured: !!(clientId && clientSecret), - databaseConnected, - environmentValid: isEnvironmentValid, - configuration: { - google_oauth: !!(clientId && clientSecret), - redirect_uri_configured: !!redirectUri, - frontend_url_configured: !!frontendUrl, - production_ready: setupCompleted && databaseConnected - } - }; - logAuthEvent('SETUP_STATUS', response); - res.json(response); - } - catch (error) { - logAuthEvent('SETUP_ERROR', { - error: error instanceof Error ? error.message : 'Unknown setup error' - }); - res.status(500).json({ - error: 'Setup check failed', - details: error instanceof Error ? error.message : 'Unknown error' - }); - } -}); -// Start Google OAuth flow -router.get('/google', (req, res) => { - try { - const authUrl = (0, simpleAuth_1.getGoogleAuthUrl)(); - res.redirect(authUrl); - } - catch (error) { - console.error('Error starting Google OAuth:', error); - const frontendUrl = process.env.FRONTEND_URL || 'http://localhost:5173'; - res.redirect(`${frontendUrl}?error=oauth_not_configured`); - } -}); -// Handle Google OAuth callback (this is where Google redirects back to) -router.get('/google/callback', async (req, res) => { - const { code, error, state } = req.query; - const frontendUrl = process.env.FRONTEND_URL || 'http://localhost:5173'; - logAuthEvent('OAUTH_CALLBACK', { - has_code: !!code, - has_error: !!error, - error_type: error, - state, - frontend_url: frontendUrl, - ip: req.ip, - user_agent: req.get('User-Agent') - }); - // Validate environment before proceeding - if (!isEnvironmentValid) { - logAuthEvent('OAUTH_CALLBACK_ERROR', { reason: 'invalid_environment' }); - return res.redirect(`${frontendUrl}?error=configuration_error&message=OAuth not properly configured`); - } - if (error) { - logAuthEvent('OAUTH_ERROR', { error, ip: req.ip }); - return res.redirect(`${frontendUrl}?error=${error}&message=OAuth authorization failed`); - } - if (!code) { - logAuthEvent('OAUTH_ERROR', { reason: 'no_authorization_code', ip: req.ip }); - return res.redirect(`${frontendUrl}?error=no_code&message=No authorization code received`); - } - try { - logAuthEvent('OAUTH_TOKEN_EXCHANGE_START', { code_length: code.length }); - // Exchange code for tokens - const tokens = await (0, simpleAuth_1.exchangeCodeForTokens)(code); - if (!tokens || !tokens.access_token) { - logAuthEvent('OAUTH_TOKEN_EXCHANGE_FAILED', { tokens_received: !!tokens }); - return res.redirect(`${frontendUrl}?error=token_exchange_failed&message=Failed to exchange authorization code`); - } - logAuthEvent('OAUTH_TOKEN_EXCHANGE_SUCCESS', { has_access_token: !!tokens.access_token }); - // Get user info - const googleUser = await (0, simpleAuth_1.getGoogleUserInfo)(tokens.access_token); - if (!googleUser || !googleUser.email) { - logAuthEvent('OAUTH_USER_INFO_FAILED', { user_data: !!googleUser }); - return res.redirect(`${frontendUrl}?error=user_info_failed&message=Failed to get user information from Google`); - } - logAuthEvent('OAUTH_USER_INFO_SUCCESS', { - email: googleUser.email, - name: googleUser.name, - verified_email: googleUser.verified_email - }); - // Check if user exists or create new user - let user = await databaseService_1.default.getUserByEmail(googleUser.email); - if (!user) { - // Determine role - first user becomes admin, others need approval - const approvedUserCount = await databaseService_1.default.getApprovedUserCount(); - const role = approvedUserCount === 0 ? 'administrator' : 'coordinator'; - logAuthEvent('USER_CREATION', { - email: googleUser.email, - role, - is_first_user: approvedUserCount === 0 - }); - user = await databaseService_1.default.createUser({ - id: googleUser.id, - google_id: googleUser.id, - email: googleUser.email, - name: googleUser.name, - profile_picture_url: googleUser.picture, - role - }); - // Auto-approve first admin, others need approval - if (approvedUserCount === 0) { - await databaseService_1.default.updateUserApprovalStatus(googleUser.email, 'approved'); - user.approval_status = 'approved'; - logAuthEvent('FIRST_ADMIN_CREATED', { email: googleUser.email }); - } - else { - logAuthEvent('USER_PENDING_APPROVAL', { email: googleUser.email }); - } - } - else { - // Update last sign in - await databaseService_1.default.updateUserLastSignIn(googleUser.email); - logAuthEvent('USER_LOGIN', { - email: user.email, - name: user.name, - role: user.role, - approval_status: user.approval_status - }); - } - // Check if user is approved - if (user.approval_status !== 'approved') { - logAuthEvent('USER_NOT_APPROVED', { email: user.email, status: user.approval_status }); - return res.redirect(`${frontendUrl}?error=pending_approval&message=Your account is pending administrator approval`); - } - // Generate JWT token - const token = (0, simpleAuth_1.generateToken)(user); - logAuthEvent('JWT_TOKEN_GENERATED', { - user_id: user.id, - email: user.email, - role: user.role, - token_length: token.length - }); - // Redirect to frontend with token - const callbackUrl = `${frontendUrl}/auth/callback?token=${token}`; - logAuthEvent('OAUTH_SUCCESS_REDIRECT', { callback_url: callbackUrl }); - res.redirect(callbackUrl); - } - catch (error) { - logAuthEvent('OAUTH_CALLBACK_ERROR', { - error: error instanceof Error ? error.message : 'Unknown error', - stack: error instanceof Error ? error.stack : undefined, - ip: req.ip - }); - res.redirect(`${frontendUrl}?error=oauth_failed&message=Authentication failed due to server error`); - } -}); -// Exchange OAuth code for JWT token (alternative endpoint for frontend) -router.post('/google/exchange', async (req, res) => { - const { code } = req.body; - if (!code) { - return res.status(400).json({ error: 'Authorization code is required' }); - } - try { - // Exchange code for tokens - const tokens = await (0, simpleAuth_1.exchangeCodeForTokens)(code); - // Get user info - const googleUser = await (0, simpleAuth_1.getGoogleUserInfo)(tokens.access_token); - // Check if user exists or create new user - let user = await databaseService_1.default.getUserByEmail(googleUser.email); - if (!user) { - // Determine role - first user becomes admin - const userCount = await databaseService_1.default.getUserCount(); - const role = userCount === 0 ? 'administrator' : 'coordinator'; - user = await databaseService_1.default.createUser({ - id: googleUser.id, - google_id: googleUser.id, - email: googleUser.email, - name: googleUser.name, - profile_picture_url: googleUser.picture, - role - }); - } - else { - // Update last sign in - await databaseService_1.default.updateUserLastSignIn(googleUser.email); - console.log(`āœ… User logged in: ${user.name} (${user.email})`); - } - // Generate JWT token - const token = (0, simpleAuth_1.generateToken)(user); - // Return token to frontend - res.json({ - token, - user: { - id: user.id, - email: user.email, - name: user.name, - picture: user.profile_picture_url, - role: user.role - } - }); - } - catch (error) { - console.error('Error in OAuth exchange:', error); - res.status(500).json({ error: 'Failed to exchange authorization code' }); - } -}); -// Get OAuth URL for frontend to redirect to -router.get('/google/url', (req, res) => { - try { - const authUrl = (0, simpleAuth_1.getGoogleAuthUrl)(); - res.json({ url: authUrl }); - } - catch (error) { - console.error('Error getting Google OAuth URL:', error); - res.status(500).json({ error: 'OAuth not configured' }); - } -}); -// Logout -router.post('/logout', (req, res) => { - // With JWT, logout is handled client-side by removing the token - res.json({ message: 'Logged out successfully' }); -}); -// Get auth status -router.get('/status', (req, res) => { - const authHeader = req.headers.authorization; - if (!authHeader || !authHeader.startsWith('Bearer ')) { - return res.json({ authenticated: false }); - } - const token = authHeader.substring(7); - const user = (0, simpleAuth_1.verifyToken)(token); - if (!user) { - return res.json({ authenticated: false }); - } - res.json({ - authenticated: true, - user: { - id: user.id, - email: user.email, - name: user.name, - picture: user.profile_picture_url, - role: user.role - } - }); -}); -// USER MANAGEMENT ENDPOINTS -// List all users (admin only) -router.get('/users', requireAuth, requireRole(['administrator']), async (req, res) => { - try { - const users = await databaseService_1.default.getAllUsers(); - const userList = users.map(user => ({ - id: user.id, - email: user.email, - name: user.name, - picture: user.profile_picture_url, - role: user.role, - created_at: user.created_at, - last_login: user.last_login, - provider: 'google' - })); - res.json(userList); - } - catch (error) { - console.error('Error fetching users:', error); - res.status(500).json({ error: 'Failed to fetch users' }); - } -}); -// Update user role (admin only) -router.patch('/users/:email/role', requireAuth, requireRole(['administrator']), async (req, res) => { - const { email } = req.params; - const { role } = req.body; - if (!['administrator', 'coordinator', 'driver'].includes(role)) { - return res.status(400).json({ error: 'Invalid role' }); - } - try { - const user = await databaseService_1.default.updateUserRole(email, role); - if (!user) { - return res.status(404).json({ error: 'User not found' }); - } - res.json({ - success: true, - user: { - id: user.id, - email: user.email, - name: user.name, - role: user.role - } - }); - } - catch (error) { - console.error('Error updating user role:', error); - res.status(500).json({ error: 'Failed to update user role' }); - } -}); -// Delete user (admin only) -router.delete('/users/:email', requireAuth, requireRole(['administrator']), async (req, res) => { - const { email } = req.params; - const currentUser = req.user; - // Prevent admin from deleting themselves - if (email === currentUser.email) { - return res.status(400).json({ error: 'Cannot delete your own account' }); - } - try { - const deletedUser = await databaseService_1.default.deleteUser(email); - if (!deletedUser) { - return res.status(404).json({ error: 'User not found' }); - } - res.json({ success: true, message: 'User deleted successfully' }); - } - catch (error) { - console.error('Error deleting user:', error); - res.status(500).json({ error: 'Failed to delete user' }); - } -}); -// Get user by email (admin only) -router.get('/users/:email', requireAuth, requireRole(['administrator']), async (req, res) => { - const { email } = req.params; - try { - const user = await databaseService_1.default.getUserByEmail(email); - if (!user) { - return res.status(404).json({ error: 'User not found' }); - } - res.json({ - id: user.id, - email: user.email, - name: user.name, - picture: user.profile_picture_url, - role: user.role, - created_at: user.created_at, - last_login: user.last_login, - provider: 'google', - approval_status: user.approval_status - }); - } - catch (error) { - console.error('Error fetching user:', error); - res.status(500).json({ error: 'Failed to fetch user' }); - } -}); -// USER APPROVAL ENDPOINTS -// Get pending users (admin only) -router.get('/users/pending/list', requireAuth, requireRole(['administrator']), async (req, res) => { - try { - const pendingUsers = await databaseService_1.default.getPendingUsers(); - const userList = pendingUsers.map(user => ({ - id: user.id, - email: user.email, - name: user.name, - picture: user.profile_picture_url, - role: user.role, - created_at: user.created_at, - provider: 'google', - approval_status: user.approval_status - })); - res.json(userList); - } - catch (error) { - console.error('Error fetching pending users:', error); - res.status(500).json({ error: 'Failed to fetch pending users' }); - } -}); -// Approve or deny user (admin only) -router.patch('/users/:email/approval', requireAuth, requireRole(['administrator']), async (req, res) => { - const { email } = req.params; - const { status } = req.body; - if (!['approved', 'denied'].includes(status)) { - return res.status(400).json({ error: 'Invalid approval status. Must be "approved" or "denied"' }); - } - try { - const user = await databaseService_1.default.updateUserApprovalStatus(email, status); - if (!user) { - return res.status(404).json({ error: 'User not found' }); - } - res.json({ - success: true, - message: `User ${status} successfully`, - user: { - id: user.id, - email: user.email, - name: user.name, - role: user.role, - approval_status: user.approval_status - } - }); - } - catch (error) { - console.error('Error updating user approval:', error); - res.status(500).json({ error: 'Failed to update user approval' }); - } -}); -exports.default = router; -//# sourceMappingURL=simpleAuth.js.map \ No newline at end of file diff --git a/backend-old-20260125/dist/routes/simpleAuth.js.map b/backend-old-20260125/dist/routes/simpleAuth.js.map deleted file mode 100644 index 83ddad3..0000000 --- a/backend-old-20260125/dist/routes/simpleAuth.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"simpleAuth.js","sourceRoot":"","sources":["../../src/routes/simpleAuth.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/authService.d.ts b/backend-old-20260125/dist/services/authService.d.ts deleted file mode 100644 index d0189a5..0000000 --- a/backend-old-20260125/dist/services/authService.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Request, Response, NextFunction } from 'express'; -declare class AuthService { - private jwtSecret; - private jwtExpiry; - private googleClient; - constructor(); - generateToken(user: any): string; - verifyGoogleToken(credential: string): Promise<{ - user: any; - token: string; - }>; - verifyToken(token: string): any; - requireAuth: (req: Request & { - user?: any; - }, res: Response, next: NextFunction) => Promise> | undefined>; - requireRole: (roles: string[]) => (req: Request & { - user?: any; - }, res: Response, next: NextFunction) => Response> | undefined; - getGoogleAuthUrl(): string; - exchangeGoogleCode(code: string): Promise; - getGoogleUserInfo(accessToken: string): Promise; - handleGoogleAuth(code: string): Promise<{ - user: any; - token: string; - }>; -} -declare const _default: AuthService; -export default _default; -//# sourceMappingURL=authService.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/services/authService.d.ts.map b/backend-old-20260125/dist/services/authService.d.ts.map deleted file mode 100644 index 83ae925..0000000 --- a/backend-old-20260125/dist/services/authService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"authService.d.ts","sourceRoot":"","sources":["../../src/services/authService.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAK1D,cAAM,WAAW;IACf,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,YAAY,CAAe;;IAoBnC,aAAa,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM;IAM1B,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAqClF,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG;IAS/B,WAAW,GAAU,KAAK,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAA;KAAE,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,6DAoBnF;IAGF,WAAW,GAAI,OAAO,MAAM,EAAE,MACpB,KAAK,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAA;KAAE,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,oDAWxE;IAGF,gBAAgB,IAAI,MAAM;IAiBpB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAoB9C,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAapD,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAyB5E;;AAED,wBAAiC"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/authService.js b/backend-old-20260125/dist/services/authService.js deleted file mode 100644 index c808d7f..0000000 --- a/backend-old-20260125/dist/services/authService.js +++ /dev/null @@ -1,168 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const jwt = require('jsonwebtoken'); -const google_auth_library_1 = require("google-auth-library"); -const unifiedDataService_1 = __importDefault(require("./unifiedDataService")); -// Simplified authentication service - removes excessive logging and complexity -class AuthService { - constructor() { - this.jwtExpiry = '24h'; - // Middleware to check authentication - this.requireAuth = async (req, res, next) => { - const token = req.headers.authorization?.replace('Bearer ', ''); - if (!token) { - return res.status(401).json({ error: 'Authentication required' }); - } - const decoded = this.verifyToken(token); - if (!decoded) { - return res.status(401).json({ error: 'Invalid or expired token' }); - } - // Get fresh user data - const user = await unifiedDataService_1.default.getUserById(decoded.id); - if (!user) { - return res.status(401).json({ error: 'User not found' }); - } - req.user = user; - next(); - }; - // Middleware to check role - this.requireRole = (roles) => { - return (req, res, next) => { - if (!req.user) { - return res.status(401).json({ error: 'Authentication required' }); - } - if (!roles.includes(req.user.role)) { - return res.status(403).json({ error: 'Insufficient permissions' }); - } - next(); - }; - }; - // Auto-generate a secure JWT secret if not provided - if (process.env.JWT_SECRET) { - this.jwtSecret = process.env.JWT_SECRET; - console.log('Using JWT_SECRET from environment'); - } - else { - // Generate a cryptographically secure random secret - const crypto = require('crypto'); - this.jwtSecret = crypto.randomBytes(64).toString('hex'); - console.log('Generated new JWT_SECRET (this will change on restart)'); - console.log('To persist sessions across restarts, set JWT_SECRET in .env'); - } - // Initialize Google OAuth client - this.googleClient = new google_auth_library_1.OAuth2Client(process.env.GOOGLE_CLIENT_ID); - } - // Generate JWT token - generateToken(user) { - const payload = { id: user.id, email: user.email, role: user.role }; - return jwt.sign(payload, this.jwtSecret, { expiresIn: this.jwtExpiry }); - } - // Verify Google ID token from frontend - async verifyGoogleToken(credential) { - try { - // Verify the token with Google - const ticket = await this.googleClient.verifyIdToken({ - idToken: credential, - audience: process.env.GOOGLE_CLIENT_ID, - }); - const payload = ticket.getPayload(); - if (!payload || !payload.email) { - throw new Error('Invalid token payload'); - } - // Find or create user - let user = await unifiedDataService_1.default.getUserByEmail(payload.email); - if (!user) { - // Auto-create user with coordinator role - user = await unifiedDataService_1.default.createUser({ - email: payload.email, - name: payload.name || payload.email, - role: 'coordinator', - googleId: payload.sub - }); - } - // Generate our JWT - const token = this.generateToken(user); - return { user, token }; - } - catch (error) { - console.error('Token verification error:', error); - throw new Error('Failed to verify Google token'); - } - } - // Verify JWT token - verifyToken(token) { - try { - return jwt.verify(token, this.jwtSecret); - } - catch (error) { - return null; - } - } - // Google OAuth helpers - getGoogleAuthUrl() { - if (!process.env.GOOGLE_CLIENT_ID || !process.env.GOOGLE_REDIRECT_URI) { - throw new Error('Google OAuth not configured. Please set GOOGLE_CLIENT_ID and GOOGLE_REDIRECT_URI in .env file'); - } - const params = new URLSearchParams({ - client_id: process.env.GOOGLE_CLIENT_ID, - redirect_uri: process.env.GOOGLE_REDIRECT_URI, - response_type: 'code', - scope: 'email profile', - access_type: 'offline', - prompt: 'consent' - }); - return `https://accounts.google.com/o/oauth2/v2/auth?${params}`; - } - async exchangeGoogleCode(code) { - const response = await fetch('https://oauth2.googleapis.com/token', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - code, - client_id: process.env.GOOGLE_CLIENT_ID, - client_secret: process.env.GOOGLE_CLIENT_SECRET, - redirect_uri: process.env.GOOGLE_REDIRECT_URI, - grant_type: 'authorization_code' - }) - }); - if (!response.ok) { - throw new Error('Failed to exchange authorization code'); - } - return response.json(); - } - async getGoogleUserInfo(accessToken) { - const response = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', { - headers: { Authorization: `Bearer ${accessToken}` } - }); - if (!response.ok) { - throw new Error('Failed to get user info'); - } - return response.json(); - } - // Simplified login/signup - async handleGoogleAuth(code) { - // Exchange code for tokens - const tokens = await this.exchangeGoogleCode(code); - // Get user info - const googleUser = await this.getGoogleUserInfo(tokens.access_token); - // Find or create user - let user = await unifiedDataService_1.default.getUserByEmail(googleUser.email); - if (!user) { - // Auto-create user with coordinator role - user = await unifiedDataService_1.default.createUser({ - email: googleUser.email, - name: googleUser.name, - role: 'coordinator', - googleId: googleUser.id - }); - } - // Generate JWT - const token = this.generateToken(user); - return { user, token }; - } -} -exports.default = new AuthService(); -//# sourceMappingURL=authService.js.map \ No newline at end of file diff --git a/backend-old-20260125/dist/services/authService.js.map b/backend-old-20260125/dist/services/authService.js.map deleted file mode 100644 index cd31567..0000000 --- a/backend-old-20260125/dist/services/authService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"authService.js","sourceRoot":"","sources":["../../src/services/authService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/dataService.d.ts b/backend-old-20260125/dist/services/dataService.d.ts deleted file mode 100644 index 88f8115..0000000 --- a/backend-old-20260125/dist/services/dataService.d.ts +++ /dev/null @@ -1,39 +0,0 @@ -declare class DataService { - private dataDir; - private dataFile; - private data; - constructor(); - private loadData; - private saveData; - getVips(): any[]; - addVip(vip: any): any; - updateVip(id: string, updatedVip: any): any | null; - deleteVip(id: string): any | null; - getDrivers(): any[]; - addDriver(driver: any): any; - updateDriver(id: string, updatedDriver: any): any | null; - deleteDriver(id: string): any | null; - getSchedule(vipId: string): any[]; - addScheduleEvent(vipId: string, event: any): any; - updateScheduleEvent(vipId: string, eventId: string, updatedEvent: any): any | null; - deleteScheduleEvent(vipId: string, eventId: string): any | null; - getAllSchedules(): { - [vipId: string]: any[]; - }; - getAdminSettings(): any; - updateAdminSettings(settings: any): void; - createBackup(): string; - getUsers(): any[]; - getUserByEmail(email: string): any | null; - getUserById(id: string): any | null; - addUser(user: any): any; - updateUser(email: string, updatedUser: any): any | null; - updateUserRole(email: string, role: string): any | null; - updateUserLastSignIn(email: string): any | null; - deleteUser(email: string): any | null; - getUserCount(): number; - getDataStats(): any; -} -declare const _default: DataService; -export default _default; -//# sourceMappingURL=dataService.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/services/dataService.d.ts.map b/backend-old-20260125/dist/services/dataService.d.ts.map deleted file mode 100644 index 403a5d0..0000000 --- a/backend-old-20260125/dist/services/dataService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"dataService.d.ts","sourceRoot":"","sources":["../../src/services/dataService.ts"],"names":[],"mappings":"AAWA,cAAM,WAAW;IACf,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,IAAI,CAAY;;IAcxB,OAAO,CAAC,QAAQ;IA6ChB,OAAO,CAAC,QAAQ;IAWhB,OAAO,IAAI,GAAG,EAAE;IAIhB,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG;IAMrB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,GAAG,GAAG,GAAG,IAAI;IAUlD,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI;IAajC,UAAU,IAAI,GAAG,EAAE;IAInB,SAAS,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG;IAM3B,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,GAAG,GAAG,GAAG,IAAI;IAUxD,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI;IAWpC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,EAAE;IAIjC,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,GAAG;IAShD,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,GAAG,GAAG,GAAG,IAAI;IAclF,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI;IAc/D,eAAe,IAAI;QAAE,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,EAAE,CAAA;KAAE;IAK7C,gBAAgB,IAAI,GAAG;IAIvB,mBAAmB,CAAC,QAAQ,EAAE,GAAG,GAAG,IAAI;IAMxC,YAAY,IAAI,MAAM;IAetB,QAAQ,IAAI,GAAG,EAAE;IAIjB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI;IAIzC,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI;IAInC,OAAO,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG;IAcvB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,GAAG,GAAG,GAAG,IAAI;IAWvD,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI;IAWvD,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI;IAU/C,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI;IAWrC,YAAY,IAAI,MAAM;IAItB,YAAY,IAAI,GAAG;CAWpB;;AAED,wBAAiC"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/dataService.js b/backend-old-20260125/dist/services/dataService.js deleted file mode 100644 index b922ba3..0000000 --- a/backend-old-20260125/dist/services/dataService.js +++ /dev/null @@ -1,264 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const fs_1 = __importDefault(require("fs")); -const path_1 = __importDefault(require("path")); -class DataService { - constructor() { - this.dataDir = path_1.default.join(process.cwd(), 'data'); - this.dataFile = path_1.default.join(this.dataDir, 'vip-coordinator.json'); - // Ensure data directory exists - if (!fs_1.default.existsSync(this.dataDir)) { - fs_1.default.mkdirSync(this.dataDir, { recursive: true }); - } - this.data = this.loadData(); - } - loadData() { - try { - if (fs_1.default.existsSync(this.dataFile)) { - const fileContent = fs_1.default.readFileSync(this.dataFile, 'utf8'); - const loadedData = JSON.parse(fileContent); - console.log(`āœ… Loaded data from ${this.dataFile}`); - console.log(` - VIPs: ${loadedData.vips?.length || 0}`); - console.log(` - Drivers: ${loadedData.drivers?.length || 0}`); - console.log(` - Users: ${loadedData.users?.length || 0}`); - console.log(` - Schedules: ${Object.keys(loadedData.schedules || {}).length} VIPs with schedules`); - // Ensure users array exists for backward compatibility - if (!loadedData.users) { - loadedData.users = []; - } - return loadedData; - } - } - catch (error) { - console.error('Error loading data file:', error); - } - // Return default empty data structure - console.log('šŸ“ Starting with empty data store'); - return { - vips: [], - drivers: [], - schedules: {}, - users: [], - adminSettings: { - apiKeys: { - aviationStackKey: process.env.AVIATIONSTACK_API_KEY || '', - googleMapsKey: '', - twilioKey: '' - }, - systemSettings: { - defaultPickupLocation: '', - defaultDropoffLocation: '', - timeZone: 'America/New_York', - notificationsEnabled: false - } - } - }; - } - saveData() { - try { - const dataToSave = JSON.stringify(this.data, null, 2); - fs_1.default.writeFileSync(this.dataFile, dataToSave, 'utf8'); - console.log(`šŸ’¾ Data saved to ${this.dataFile}`); - } - catch (error) { - console.error('Error saving data file:', error); - } - } - // VIP operations - getVips() { - return this.data.vips; - } - addVip(vip) { - this.data.vips.push(vip); - this.saveData(); - return vip; - } - updateVip(id, updatedVip) { - const index = this.data.vips.findIndex(vip => vip.id === id); - if (index !== -1) { - this.data.vips[index] = updatedVip; - this.saveData(); - return this.data.vips[index]; - } - return null; - } - deleteVip(id) { - const index = this.data.vips.findIndex(vip => vip.id === id); - if (index !== -1) { - const deletedVip = this.data.vips.splice(index, 1)[0]; - // Also delete the VIP's schedule - delete this.data.schedules[id]; - this.saveData(); - return deletedVip; - } - return null; - } - // Driver operations - getDrivers() { - return this.data.drivers; - } - addDriver(driver) { - this.data.drivers.push(driver); - this.saveData(); - return driver; - } - updateDriver(id, updatedDriver) { - const index = this.data.drivers.findIndex(driver => driver.id === id); - if (index !== -1) { - this.data.drivers[index] = updatedDriver; - this.saveData(); - return this.data.drivers[index]; - } - return null; - } - deleteDriver(id) { - const index = this.data.drivers.findIndex(driver => driver.id === id); - if (index !== -1) { - const deletedDriver = this.data.drivers.splice(index, 1)[0]; - this.saveData(); - return deletedDriver; - } - return null; - } - // Schedule operations - getSchedule(vipId) { - return this.data.schedules[vipId] || []; - } - addScheduleEvent(vipId, event) { - if (!this.data.schedules[vipId]) { - this.data.schedules[vipId] = []; - } - this.data.schedules[vipId].push(event); - this.saveData(); - return event; - } - updateScheduleEvent(vipId, eventId, updatedEvent) { - if (!this.data.schedules[vipId]) { - return null; - } - const index = this.data.schedules[vipId].findIndex(event => event.id === eventId); - if (index !== -1) { - this.data.schedules[vipId][index] = updatedEvent; - this.saveData(); - return this.data.schedules[vipId][index]; - } - return null; - } - deleteScheduleEvent(vipId, eventId) { - if (!this.data.schedules[vipId]) { - return null; - } - const index = this.data.schedules[vipId].findIndex(event => event.id === eventId); - if (index !== -1) { - const deletedEvent = this.data.schedules[vipId].splice(index, 1)[0]; - this.saveData(); - return deletedEvent; - } - return null; - } - getAllSchedules() { - return this.data.schedules; - } - // Admin settings operations - getAdminSettings() { - return this.data.adminSettings; - } - updateAdminSettings(settings) { - this.data.adminSettings = { ...this.data.adminSettings, ...settings }; - this.saveData(); - } - // Backup and restore operations - createBackup() { - const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); - const backupFile = path_1.default.join(this.dataDir, `backup-${timestamp}.json`); - try { - fs_1.default.copyFileSync(this.dataFile, backupFile); - console.log(`šŸ“¦ Backup created: ${backupFile}`); - return backupFile; - } - catch (error) { - console.error('Error creating backup:', error); - throw error; - } - } - // User operations - getUsers() { - return this.data.users; - } - getUserByEmail(email) { - return this.data.users.find(user => user.email === email) || null; - } - getUserById(id) { - return this.data.users.find(user => user.id === id) || null; - } - addUser(user) { - // Add timestamps - const userWithTimestamps = { - ...user, - created_at: new Date().toISOString(), - last_sign_in_at: new Date().toISOString() - }; - this.data.users.push(userWithTimestamps); - this.saveData(); - console.log(`šŸ‘¤ Added user: ${user.name} (${user.email}) as ${user.role}`); - return userWithTimestamps; - } - updateUser(email, updatedUser) { - const index = this.data.users.findIndex(user => user.email === email); - if (index !== -1) { - this.data.users[index] = { ...this.data.users[index], ...updatedUser }; - this.saveData(); - console.log(`šŸ‘¤ Updated user: ${this.data.users[index].name} (${email})`); - return this.data.users[index]; - } - return null; - } - updateUserRole(email, role) { - const index = this.data.users.findIndex(user => user.email === email); - if (index !== -1) { - this.data.users[index].role = role; - this.saveData(); - console.log(`šŸ‘¤ Updated user role: ${this.data.users[index].name} (${email}) -> ${role}`); - return this.data.users[index]; - } - return null; - } - updateUserLastSignIn(email) { - const index = this.data.users.findIndex(user => user.email === email); - if (index !== -1) { - this.data.users[index].last_sign_in_at = new Date().toISOString(); - this.saveData(); - return this.data.users[index]; - } - return null; - } - deleteUser(email) { - const index = this.data.users.findIndex(user => user.email === email); - if (index !== -1) { - const deletedUser = this.data.users.splice(index, 1)[0]; - this.saveData(); - console.log(`šŸ‘¤ Deleted user: ${deletedUser.name} (${email})`); - return deletedUser; - } - return null; - } - getUserCount() { - return this.data.users.length; - } - getDataStats() { - return { - vips: this.data.vips.length, - drivers: this.data.drivers.length, - users: this.data.users.length, - scheduledEvents: Object.values(this.data.schedules).reduce((total, events) => total + events.length, 0), - vipsWithSchedules: Object.keys(this.data.schedules).length, - dataFile: this.dataFile, - lastModified: fs_1.default.existsSync(this.dataFile) ? fs_1.default.statSync(this.dataFile).mtime : null - }; - } -} -exports.default = new DataService(); -//# sourceMappingURL=dataService.js.map \ No newline at end of file diff --git a/backend-old-20260125/dist/services/dataService.js.map b/backend-old-20260125/dist/services/dataService.js.map deleted file mode 100644 index ceac118..0000000 --- a/backend-old-20260125/dist/services/dataService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"dataService.js","sourceRoot":"","sources":["../../src/services/dataService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/databaseService.d.ts b/backend-old-20260125/dist/services/databaseService.d.ts deleted file mode 100644 index e18e450..0000000 --- a/backend-old-20260125/dist/services/databaseService.d.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { PoolClient } from 'pg'; -declare class EnhancedDatabaseService { - private backupService; - constructor(); - query(text: string, params?: any[]): Promise; - getClient(): Promise; - close(): Promise; - initializeTables(): Promise; - createUser(user: any): Promise; - getUserByEmail(email: string): Promise; - getUserById(id: string): Promise; - updateUserRole(email: string, role: string): Promise; - updateUserLastSignIn(email: string): Promise; - getUserCount(): Promise; - updateUserApprovalStatus(email: string, status: 'pending' | 'approved' | 'denied'): Promise; - getApprovedUserCount(): Promise; - getAllUsers(): Promise; - deleteUser(email: string): Promise; - getPendingUsers(): Promise; - completeUserOnboarding(email: string, onboardingData: any): Promise; - approveUser(userEmail: string, approvedBy: string, newRole?: string): Promise; - rejectUser(userEmail: string, rejectedBy: string, reason?: string): Promise; - deactivateUser(userEmail: string, deactivatedBy: string): Promise; - reactivateUser(userEmail: string, reactivatedBy: string): Promise; - createAuditLog(action: string, userEmail: string, performedBy: string, details: any): Promise; - getUserAuditLog(userEmail: string): Promise; - getUsersWithFilters(filters: { - status?: string; - role?: string; - search?: string; - }): Promise; - getActiveUserCount(): Promise; - isFirstUser(): Promise; - createVip(vip: any): Promise; - getVipById(id: string): Promise; - getAllVips(): Promise; - updateVip(id: string, vip: any): Promise; - deleteVip(id: string): Promise; - getVipsByDepartment(department: string): Promise; - createDriver(driver: any): Promise; - getDriverById(id: string): Promise; - getAllDrivers(): Promise; - updateDriver(id: string, driver: any): Promise; - deleteDriver(id: string): Promise; - getDriversByDepartment(department: string): Promise; - updateDriverLocation(id: string, location: any): Promise; - createScheduleEvent(vipId: string, event: any): Promise; - getScheduleByVipId(vipId: string): Promise; - updateScheduleEvent(vipId: string, eventId: string, event: any): Promise; - deleteScheduleEvent(vipId: string, eventId: string): Promise; - getAllScheduleEvents(): Promise; - getScheduleEventsByDateRange(startDate: Date, endDate: Date): Promise; -} -declare const databaseService: EnhancedDatabaseService; -export default databaseService; -//# sourceMappingURL=databaseService.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/services/databaseService.d.ts.map b/backend-old-20260125/dist/services/databaseService.d.ts.map deleted file mode 100644 index 4e295e7..0000000 --- a/backend-old-20260125/dist/services/databaseService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"databaseService.d.ts","sourceRoot":"","sources":["../../src/services/databaseService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/databaseService.js b/backend-old-20260125/dist/services/databaseService.js deleted file mode 100644 index fcc3e05..0000000 --- a/backend-old-20260125/dist/services/databaseService.js +++ /dev/null @@ -1,265 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -// Import the existing backup service -const databaseService_1 = __importDefault(require("./backup-services/databaseService")); -// Extend the backup service with new user management methods -class EnhancedDatabaseService { - constructor() { - this.backupService = databaseService_1.default; - } - // Delegate all existing methods to backup service - async query(text, params) { - return this.backupService.query(text, params); - } - async getClient() { - return this.backupService.getClient(); - } - async close() { - return this.backupService.close(); - } - async initializeTables() { - return this.backupService.initializeTables(); - } - // User methods from backup service - async createUser(user) { - return this.backupService.createUser(user); - } - async getUserByEmail(email) { - return this.backupService.getUserByEmail(email); - } - async getUserById(id) { - return this.backupService.getUserById(id); - } - async updateUserRole(email, role) { - return this.backupService.updateUserRole(email, role); - } - async updateUserLastSignIn(email) { - return this.backupService.updateUserLastSignIn(email); - } - async getUserCount() { - return this.backupService.getUserCount(); - } - async updateUserApprovalStatus(email, status) { - return this.backupService.updateUserApprovalStatus(email, status); - } - async getApprovedUserCount() { - return this.backupService.getApprovedUserCount(); - } - async getAllUsers() { - return this.backupService.getAllUsers(); - } - async deleteUser(email) { - return this.backupService.deleteUser(email); - } - async getPendingUsers() { - return this.backupService.getPendingUsers(); - } - // NEW: Enhanced user management methods - async completeUserOnboarding(email, onboardingData) { - const query = ` - UPDATE users - SET phone = $1, - organization = $2, - onboarding_data = $3, - updated_at = CURRENT_TIMESTAMP - WHERE email = $4 - RETURNING * - `; - const result = await this.query(query, [ - onboardingData.phone, - onboardingData.organization, - JSON.stringify(onboardingData), - email - ]); - return result.rows[0] || null; - } - async approveUser(userEmail, approvedBy, newRole) { - const query = ` - UPDATE users - SET status = 'active', - approval_status = 'approved', - approved_by = $1, - approved_at = CURRENT_TIMESTAMP, - role = COALESCE($2, role), - updated_at = CURRENT_TIMESTAMP - WHERE email = $3 - RETURNING * - `; - const result = await this.query(query, [approvedBy, newRole, userEmail]); - // Log audit - if (result.rows[0]) { - await this.createAuditLog('user_approved', userEmail, approvedBy, { newRole }); - } - return result.rows[0] || null; - } - async rejectUser(userEmail, rejectedBy, reason) { - const query = ` - UPDATE users - SET status = 'deactivated', - approval_status = 'denied', - rejected_by = $1, - rejected_at = CURRENT_TIMESTAMP, - updated_at = CURRENT_TIMESTAMP - WHERE email = $2 - RETURNING * - `; - const result = await this.query(query, [rejectedBy, userEmail]); - // Log audit - if (result.rows[0]) { - await this.createAuditLog('user_rejected', userEmail, rejectedBy, { reason }); - } - return result.rows[0] || null; - } - async deactivateUser(userEmail, deactivatedBy) { - const query = ` - UPDATE users - SET status = 'deactivated', - deactivated_by = $1, - deactivated_at = CURRENT_TIMESTAMP, - updated_at = CURRENT_TIMESTAMP - WHERE email = $2 - RETURNING * - `; - const result = await this.query(query, [deactivatedBy, userEmail]); - // Log audit - if (result.rows[0]) { - await this.createAuditLog('user_deactivated', userEmail, deactivatedBy, {}); - } - return result.rows[0] || null; - } - async reactivateUser(userEmail, reactivatedBy) { - const query = ` - UPDATE users - SET status = 'active', - deactivated_by = NULL, - deactivated_at = NULL, - updated_at = CURRENT_TIMESTAMP - WHERE email = $1 - RETURNING * - `; - const result = await this.query(query, [userEmail]); - // Log audit - if (result.rows[0]) { - await this.createAuditLog('user_reactivated', userEmail, reactivatedBy, {}); - } - return result.rows[0] || null; - } - async createAuditLog(action, userEmail, performedBy, details) { - const query = ` - INSERT INTO user_audit_log (action, user_email, performed_by, action_details) - VALUES ($1, $2, $3, $4) - `; - await this.query(query, [action, userEmail, performedBy, JSON.stringify(details)]); - } - async getUserAuditLog(userEmail) { - const query = ` - SELECT * FROM user_audit_log - WHERE user_email = $1 - ORDER BY created_at DESC - `; - const result = await this.query(query, [userEmail]); - return result.rows; - } - async getUsersWithFilters(filters) { - let query = 'SELECT * FROM users WHERE 1=1'; - const params = []; - let paramIndex = 1; - if (filters.status) { - query += ` AND status = $${paramIndex}`; - params.push(filters.status); - paramIndex++; - } - if (filters.role) { - query += ` AND role = $${paramIndex}`; - params.push(filters.role); - paramIndex++; - } - if (filters.search) { - query += ` AND (LOWER(name) LIKE LOWER($${paramIndex}) OR LOWER(email) LIKE LOWER($${paramIndex}) OR LOWER(organization) LIKE LOWER($${paramIndex}))`; - params.push(`%${filters.search}%`); - paramIndex++; - } - query += ' ORDER BY created_at DESC'; - const result = await this.query(query, params); - return result.rows; - } - // Fix for first user admin issue - async getActiveUserCount() { - const query = "SELECT COUNT(*) as count FROM users WHERE status = 'active'"; - const result = await this.query(query); - return parseInt(result.rows[0].count); - } - async isFirstUser() { - // Check if there are any active or approved users - const query = "SELECT COUNT(*) as count FROM users WHERE status = 'active' OR approval_status = 'approved'"; - const result = await this.query(query); - return parseInt(result.rows[0].count) === 0; - } - // VIP methods from backup service - async createVip(vip) { - return this.backupService.createVip(vip); - } - async getVipById(id) { - return this.backupService.getVipById(id); - } - async getAllVips() { - return this.backupService.getAllVips(); - } - async updateVip(id, vip) { - return this.backupService.updateVip(id, vip); - } - async deleteVip(id) { - return this.backupService.deleteVip(id); - } - async getVipsByDepartment(department) { - return this.backupService.getVipsByDepartment(department); - } - // Driver methods from backup service - async createDriver(driver) { - return this.backupService.createDriver(driver); - } - async getDriverById(id) { - return this.backupService.getDriverById(id); - } - async getAllDrivers() { - return this.backupService.getAllDrivers(); - } - async updateDriver(id, driver) { - return this.backupService.updateDriver(id, driver); - } - async deleteDriver(id) { - return this.backupService.deleteDriver(id); - } - async getDriversByDepartment(department) { - return this.backupService.getDriversByDepartment(department); - } - async updateDriverLocation(id, location) { - return this.backupService.updateDriverLocation(id, location); - } - // Schedule methods from backup service - async createScheduleEvent(vipId, event) { - return this.backupService.createScheduleEvent(vipId, event); - } - async getScheduleByVipId(vipId) { - return this.backupService.getScheduleByVipId(vipId); - } - async updateScheduleEvent(vipId, eventId, event) { - return this.backupService.updateScheduleEvent(vipId, eventId, event); - } - async deleteScheduleEvent(vipId, eventId) { - return this.backupService.deleteScheduleEvent(vipId, eventId); - } - async getAllScheduleEvents() { - return this.backupService.getAllScheduleEvents(); - } - async getScheduleEventsByDateRange(startDate, endDate) { - return this.backupService.getScheduleEventsByDateRange(startDate, endDate); - } -} -// Export singleton instance -const databaseService = new EnhancedDatabaseService(); -exports.default = databaseService; -//# sourceMappingURL=databaseService.js.map \ No newline at end of file diff --git a/backend-old-20260125/dist/services/databaseService.js.map b/backend-old-20260125/dist/services/databaseService.js.map deleted file mode 100644 index 2e4ce58..0000000 --- a/backend-old-20260125/dist/services/databaseService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"databaseService.js","sourceRoot":"","sources":["../../src/services/databaseService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/driverConflictService.d.ts b/backend-old-20260125/dist/services/driverConflictService.d.ts deleted file mode 100644 index af66dd3..0000000 --- a/backend-old-20260125/dist/services/driverConflictService.d.ts +++ /dev/null @@ -1,49 +0,0 @@ -interface ScheduleEvent { - id: string; - title: string; - location: string; - startTime: string; - endTime: string; - assignedDriverId?: string; - vipId: string; - vipName: string; -} -interface ConflictInfo { - type: 'overlap' | 'tight_turnaround' | 'back_to_back'; - severity: 'low' | 'medium' | 'high'; - message: string; - conflictingEvent: ScheduleEvent; - timeDifference?: number; -} -interface DriverAvailability { - driverId: string; - driverName: string; - status: 'available' | 'scheduled' | 'overlapping' | 'tight_turnaround'; - assignmentCount: number; - conflicts: ConflictInfo[]; - currentAssignments: ScheduleEvent[]; -} -declare class DriverConflictService { - checkDriverConflicts(driverId: string, newEvent: { - startTime: string; - endTime: string; - location: string; - }, allSchedules: { - [vipId: string]: ScheduleEvent[]; - }, drivers: any[]): ConflictInfo[]; - getDriverAvailability(eventTime: { - startTime: string; - endTime: string; - location: string; - }, allSchedules: { - [vipId: string]: ScheduleEvent[]; - }, drivers: any[]): DriverAvailability[]; - private getDriverEvents; - private hasTimeOverlap; - private getTimeBetweenEvents; - getDriverStatusSummary(availability: DriverAvailability): string; -} -declare const _default: DriverConflictService; -export default _default; -export { DriverAvailability, ConflictInfo, ScheduleEvent }; -//# sourceMappingURL=driverConflictService.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/services/driverConflictService.d.ts.map b/backend-old-20260125/dist/services/driverConflictService.d.ts.map deleted file mode 100644 index 1090255..0000000 --- a/backend-old-20260125/dist/services/driverConflictService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"driverConflictService.d.ts","sourceRoot":"","sources":["../../src/services/driverConflictService.ts"],"names":[],"mappings":"AAAA,UAAU,aAAa;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,YAAY;IACpB,IAAI,EAAE,SAAS,GAAG,kBAAkB,GAAG,cAAc,CAAC;IACtD,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,aAAa,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG,aAAa,GAAG,kBAAkB,CAAC;IACvE,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,kBAAkB,EAAE,aAAa,EAAE,CAAC;CACrC;AAED,cAAM,qBAAqB;IAGzB,oBAAoB,CAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAClE,YAAY,EAAE;QAAE,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,EAAE,CAAA;KAAE,EAClD,OAAO,EAAE,GAAG,EAAE,GACb,YAAY,EAAE;IA8CjB,qBAAqB,CACnB,SAAS,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EACnE,YAAY,EAAE;QAAE,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,EAAE,CAAA;KAAE,EAClD,OAAO,EAAE,GAAG,EAAE,GACb,kBAAkB,EAAE;IAgCvB,OAAO,CAAC,eAAe;IAsBvB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,oBAAoB;IAiB5B,sBAAsB,CAAC,YAAY,EAAE,kBAAkB,GAAG,MAAM;CAejE;;AAED,wBAA2C;AAC3C,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/driverConflictService.js b/backend-old-20260125/dist/services/driverConflictService.js deleted file mode 100644 index 3d6eaa8..0000000 --- a/backend-old-20260125/dist/services/driverConflictService.js +++ /dev/null @@ -1,123 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -class DriverConflictService { - // Check for conflicts when assigning a driver to an event - checkDriverConflicts(driverId, newEvent, allSchedules, drivers) { - const conflicts = []; - const driver = drivers.find(d => d.id === driverId); - if (!driver) - return conflicts; - // Get all events assigned to this driver - const driverEvents = this.getDriverEvents(driverId, allSchedules); - const newStartTime = new Date(newEvent.startTime); - const newEndTime = new Date(newEvent.endTime); - for (const existingEvent of driverEvents) { - const existingStart = new Date(existingEvent.startTime); - const existingEnd = new Date(existingEvent.endTime); - // Check for direct time overlap - if (this.hasTimeOverlap(newStartTime, newEndTime, existingStart, existingEnd)) { - conflicts.push({ - type: 'overlap', - severity: 'high', - message: `Direct time conflict with "${existingEvent.title}" for ${existingEvent.vipName}`, - conflictingEvent: existingEvent - }); - } - // Check for tight turnaround (less than 15 minutes between events) - else { - const timeBetween = this.getTimeBetweenEvents(newStartTime, newEndTime, existingStart, existingEnd); - if (timeBetween !== null && timeBetween < 15) { - conflicts.push({ - type: 'tight_turnaround', - severity: timeBetween < 5 ? 'high' : 'medium', - message: `Only ${timeBetween} minutes between events. Previous: "${existingEvent.title}"`, - conflictingEvent: existingEvent, - timeDifference: timeBetween - }); - } - } - } - return conflicts; - } - // Get availability status for all drivers for a specific time slot - getDriverAvailability(eventTime, allSchedules, drivers) { - return drivers.map(driver => { - const conflicts = this.checkDriverConflicts(driver.id, eventTime, allSchedules, drivers); - const driverEvents = this.getDriverEvents(driver.id, allSchedules); - let status = 'available'; - if (conflicts.length > 0) { - const hasOverlap = conflicts.some(c => c.type === 'overlap'); - const hasTightTurnaround = conflicts.some(c => c.type === 'tight_turnaround'); - if (hasOverlap) { - status = 'overlapping'; - } - else if (hasTightTurnaround) { - status = 'tight_turnaround'; - } - } - else if (driverEvents.length > 0) { - status = 'scheduled'; - } - return { - driverId: driver.id, - driverName: driver.name, - status, - assignmentCount: driverEvents.length, - conflicts, - currentAssignments: driverEvents - }; - }); - } - // Get all events assigned to a specific driver - getDriverEvents(driverId, allSchedules) { - const driverEvents = []; - Object.entries(allSchedules).forEach(([vipId, events]) => { - events.forEach(event => { - if (event.assignedDriverId === driverId) { - driverEvents.push({ - ...event, - vipId, - vipName: event.title // We'll need to get actual VIP name from VIP data - }); - } - }); - }); - // Sort by start time - return driverEvents.sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime()); - } - // Check if two time periods overlap - hasTimeOverlap(start1, end1, start2, end2) { - return start1 < end2 && start2 < end1; - } - // Get minutes between two events (null if they overlap) - getTimeBetweenEvents(newStart, newEnd, existingStart, existingEnd) { - // If new event is after existing event - if (newStart >= existingEnd) { - return Math.floor((newStart.getTime() - existingEnd.getTime()) / (1000 * 60)); - } - // If new event is before existing event - else if (newEnd <= existingStart) { - return Math.floor((existingStart.getTime() - newEnd.getTime()) / (1000 * 60)); - } - // Events overlap - return null; - } - // Generate summary message for driver status - getDriverStatusSummary(availability) { - switch (availability.status) { - case 'available': - return `āœ… Fully available (${availability.assignmentCount} assignments)`; - case 'scheduled': - return `🟔 Has ${availability.assignmentCount} assignment(s) but available for this time`; - case 'tight_turnaround': - const tightConflict = availability.conflicts.find(c => c.type === 'tight_turnaround'); - return `⚔ Tight turnaround - ${tightConflict?.timeDifference} min between events`; - case 'overlapping': - return `šŸ”“ Time conflict with existing assignment`; - default: - return 'Unknown status'; - } - } -} -exports.default = new DriverConflictService(); -//# sourceMappingURL=driverConflictService.js.map \ No newline at end of file diff --git a/backend-old-20260125/dist/services/driverConflictService.js.map b/backend-old-20260125/dist/services/driverConflictService.js.map deleted file mode 100644 index bcca05c..0000000 --- a/backend-old-20260125/dist/services/driverConflictService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"driverConflictService.js","sourceRoot":"","sources":["../../src/services/driverConflictService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/enhancedDataService.d.ts b/backend-old-20260125/dist/services/enhancedDataService.d.ts deleted file mode 100644 index 0ddb991..0000000 --- a/backend-old-20260125/dist/services/enhancedDataService.d.ts +++ /dev/null @@ -1,60 +0,0 @@ -interface VipData { - id: string; - name: string; - organization: string; - department?: string; - transportMode: 'flight' | 'self-driving'; - expectedArrival?: string; - needsAirportPickup?: boolean; - needsVenueTransport: boolean; - notes?: string; - flights?: Array<{ - flightNumber: string; - flightDate: string; - segment: number; - }>; -} -interface DriverData { - id: string; - name: string; - phone: string; - department?: string; - currentLocation?: { - lat: number; - lng: number; - }; - assignedVipIds?: string[]; -} -interface ScheduleEventData { - id: string; - title: string; - location: string; - startTime: string; - endTime: string; - description?: string; - assignedDriverId?: string; - status: string; - type: string; -} -declare class EnhancedDataService { - getVips(): Promise; - addVip(vip: VipData): Promise; - updateVip(id: string, vip: Partial): Promise; - deleteVip(id: string): Promise; - getDrivers(): Promise; - addDriver(driver: DriverData): Promise; - updateDriver(id: string, driver: Partial): Promise; - deleteDriver(id: string): Promise; - getSchedule(vipId: string): Promise; - addScheduleEvent(vipId: string, event: ScheduleEventData): Promise; - updateScheduleEvent(vipId: string, eventId: string, event: ScheduleEventData): Promise; - deleteScheduleEvent(vipId: string, eventId: string): Promise; - getAllSchedules(): Promise<{ - [vipId: string]: ScheduleEventData[]; - }>; - getAdminSettings(): Promise; - updateAdminSettings(settings: any): Promise; -} -declare const _default: EnhancedDataService; -export default _default; -//# sourceMappingURL=enhancedDataService.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/services/enhancedDataService.d.ts.map b/backend-old-20260125/dist/services/enhancedDataService.d.ts.map deleted file mode 100644 index ba3041b..0000000 --- a/backend-old-20260125/dist/services/enhancedDataService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"enhancedDataService.d.ts","sourceRoot":"","sources":["../../src/services/enhancedDataService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/enhancedDataService.js b/backend-old-20260125/dist/services/enhancedDataService.js deleted file mode 100644 index 01d2f78..0000000 --- a/backend-old-20260125/dist/services/enhancedDataService.js +++ /dev/null @@ -1,571 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const database_1 = __importDefault(require("../config/database")); -const databaseService_1 = __importDefault(require("./databaseService")); -class EnhancedDataService { - // VIP operations - async getVips() { - try { - const query = ` - SELECT v.*, - COALESCE( - json_agg( - json_build_object( - 'flightNumber', f.flight_number, - 'flightDate', f.flight_date, - 'segment', f.segment - ) ORDER BY f.segment - ) FILTER (WHERE f.id IS NOT NULL), - '[]'::json - ) as flights - FROM vips v - LEFT JOIN flights f ON v.id = f.vip_id - GROUP BY v.id - ORDER BY v.name - `; - const result = await database_1.default.query(query); - return result.rows.map(row => ({ - id: row.id, - name: row.name, - organization: row.organization, - department: row.department, - transportMode: row.transport_mode, - expectedArrival: row.expected_arrival, - needsAirportPickup: row.needs_airport_pickup, - needsVenueTransport: row.needs_venue_transport, - notes: row.notes, - flights: row.flights - })); - } - catch (error) { - console.error('āŒ Error fetching VIPs:', error); - throw error; - } - } - async addVip(vip) { - const client = await database_1.default.connect(); - try { - await client.query('BEGIN'); - // Insert VIP - const vipQuery = ` - INSERT INTO vips (id, name, organization, department, transport_mode, expected_arrival, needs_airport_pickup, needs_venue_transport, notes) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) - RETURNING * - `; - const vipResult = await client.query(vipQuery, [ - vip.id, - vip.name, - vip.organization, - vip.department || 'Office of Development', - vip.transportMode, - vip.expectedArrival || null, - vip.needsAirportPickup || false, - vip.needsVenueTransport, - vip.notes || '' - ]); - // Insert flights if any - if (vip.flights && vip.flights.length > 0) { - for (const flight of vip.flights) { - const flightQuery = ` - INSERT INTO flights (vip_id, flight_number, flight_date, segment) - VALUES ($1, $2, $3, $4) - `; - await client.query(flightQuery, [ - vip.id, - flight.flightNumber, - flight.flightDate, - flight.segment - ]); - } - } - await client.query('COMMIT'); - const savedVip = { - ...vip, - department: vipResult.rows[0].department, - transportMode: vipResult.rows[0].transport_mode, - expectedArrival: vipResult.rows[0].expected_arrival, - needsAirportPickup: vipResult.rows[0].needs_airport_pickup, - needsVenueTransport: vipResult.rows[0].needs_venue_transport - }; - return savedVip; - } - catch (error) { - await client.query('ROLLBACK'); - console.error('āŒ Error adding VIP:', error); - throw error; - } - finally { - client.release(); - } - } - async updateVip(id, vip) { - const client = await database_1.default.connect(); - try { - await client.query('BEGIN'); - // Update VIP - const vipQuery = ` - UPDATE vips - SET name = $2, organization = $3, department = $4, transport_mode = $5, - expected_arrival = $6, needs_airport_pickup = $7, needs_venue_transport = $8, notes = $9 - WHERE id = $1 - RETURNING * - `; - const vipResult = await client.query(vipQuery, [ - id, - vip.name, - vip.organization, - vip.department || 'Office of Development', - vip.transportMode, - vip.expectedArrival || null, - vip.needsAirportPickup || false, - vip.needsVenueTransport, - vip.notes || '' - ]); - if (vipResult.rows.length === 0) { - await client.query('ROLLBACK'); - return null; - } - // Delete existing flights and insert new ones - await client.query('DELETE FROM flights WHERE vip_id = $1', [id]); - if (vip.flights && vip.flights.length > 0) { - for (const flight of vip.flights) { - const flightQuery = ` - INSERT INTO flights (vip_id, flight_number, flight_date, segment) - VALUES ($1, $2, $3, $4) - `; - await client.query(flightQuery, [ - id, - flight.flightNumber, - flight.flightDate, - flight.segment - ]); - } - } - await client.query('COMMIT'); - const updatedVip = { - id: vipResult.rows[0].id, - name: vipResult.rows[0].name, - organization: vipResult.rows[0].organization, - department: vipResult.rows[0].department, - transportMode: vipResult.rows[0].transport_mode, - expectedArrival: vipResult.rows[0].expected_arrival, - needsAirportPickup: vipResult.rows[0].needs_airport_pickup, - needsVenueTransport: vipResult.rows[0].needs_venue_transport, - notes: vipResult.rows[0].notes, - flights: vip.flights || [] - }; - return updatedVip; - } - catch (error) { - await client.query('ROLLBACK'); - console.error('āŒ Error updating VIP:', error); - throw error; - } - finally { - client.release(); - } - } - async deleteVip(id) { - try { - const query = ` - DELETE FROM vips WHERE id = $1 RETURNING * - `; - const result = await database_1.default.query(query, [id]); - if (result.rows.length === 0) { - return null; - } - const deletedVip = { - id: result.rows[0].id, - name: result.rows[0].name, - organization: result.rows[0].organization, - department: result.rows[0].department, - transportMode: result.rows[0].transport_mode, - expectedArrival: result.rows[0].expected_arrival, - needsAirportPickup: result.rows[0].needs_airport_pickup, - needsVenueTransport: result.rows[0].needs_venue_transport, - notes: result.rows[0].notes - }; - return deletedVip; - } - catch (error) { - console.error('āŒ Error deleting VIP:', error); - throw error; - } - } - // Driver operations - async getDrivers() { - try { - const query = ` - SELECT d.*, - COALESCE( - json_agg(DISTINCT se.vip_id) FILTER (WHERE se.vip_id IS NOT NULL), - '[]'::json - ) as assigned_vip_ids - FROM drivers d - LEFT JOIN schedule_events se ON d.id = se.assigned_driver_id - GROUP BY d.id - ORDER BY d.name - `; - const result = await database_1.default.query(query); - // Get current locations from Redis - const driversWithLocations = await Promise.all(result.rows.map(async (row) => { - const location = await databaseService_1.default.getDriverLocation(row.id); - return { - id: row.id, - name: row.name, - phone: row.phone, - department: row.department, - currentLocation: location ? { lat: location.lat, lng: location.lng } : { lat: 0, lng: 0 }, - assignedVipIds: row.assigned_vip_ids || [] - }; - })); - return driversWithLocations; - } - catch (error) { - console.error('āŒ Error fetching drivers:', error); - throw error; - } - } - async addDriver(driver) { - try { - const query = ` - INSERT INTO drivers (id, name, phone, department) - VALUES ($1, $2, $3, $4) - RETURNING * - `; - const result = await database_1.default.query(query, [ - driver.id, - driver.name, - driver.phone, - driver.department || 'Office of Development' - ]); - // Store location in Redis if provided - if (driver.currentLocation) { - await databaseService_1.default.updateDriverLocation(driver.id, driver.currentLocation); - } - const savedDriver = { - id: result.rows[0].id, - name: result.rows[0].name, - phone: result.rows[0].phone, - department: result.rows[0].department, - currentLocation: driver.currentLocation || { lat: 0, lng: 0 } - }; - return savedDriver; - } - catch (error) { - console.error('āŒ Error adding driver:', error); - throw error; - } - } - async updateDriver(id, driver) { - try { - const query = ` - UPDATE drivers - SET name = $2, phone = $3, department = $4 - WHERE id = $1 - RETURNING * - `; - const result = await database_1.default.query(query, [ - id, - driver.name, - driver.phone, - driver.department || 'Office of Development' - ]); - if (result.rows.length === 0) { - return null; - } - // Update location in Redis if provided - if (driver.currentLocation) { - await databaseService_1.default.updateDriverLocation(id, driver.currentLocation); - } - const updatedDriver = { - id: result.rows[0].id, - name: result.rows[0].name, - phone: result.rows[0].phone, - department: result.rows[0].department, - currentLocation: driver.currentLocation || { lat: 0, lng: 0 } - }; - return updatedDriver; - } - catch (error) { - console.error('āŒ Error updating driver:', error); - throw error; - } - } - async deleteDriver(id) { - try { - const query = ` - DELETE FROM drivers WHERE id = $1 RETURNING * - `; - const result = await database_1.default.query(query, [id]); - if (result.rows.length === 0) { - return null; - } - const deletedDriver = { - id: result.rows[0].id, - name: result.rows[0].name, - phone: result.rows[0].phone, - department: result.rows[0].department - }; - return deletedDriver; - } - catch (error) { - console.error('āŒ Error deleting driver:', error); - throw error; - } - } - // Schedule operations - async getSchedule(vipId) { - try { - const query = ` - SELECT * FROM schedule_events - WHERE vip_id = $1 - ORDER BY start_time - `; - const result = await database_1.default.query(query, [vipId]); - return result.rows.map(row => ({ - id: row.id, - title: row.title, - location: row.location, - startTime: row.start_time, - endTime: row.end_time, - description: row.description, - assignedDriverId: row.assigned_driver_id, - status: row.status, - type: row.event_type - })); - } - catch (error) { - console.error('āŒ Error fetching schedule:', error); - throw error; - } - } - async addScheduleEvent(vipId, event) { - try { - const query = ` - INSERT INTO schedule_events (id, vip_id, title, location, start_time, end_time, description, assigned_driver_id, status, event_type) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) - RETURNING * - `; - const result = await database_1.default.query(query, [ - event.id, - vipId, - event.title, - event.location, - event.startTime, - event.endTime, - event.description || '', - event.assignedDriverId || null, - event.status, - event.type - ]); - const savedEvent = { - id: result.rows[0].id, - title: result.rows[0].title, - location: result.rows[0].location, - startTime: result.rows[0].start_time, - endTime: result.rows[0].end_time, - description: result.rows[0].description, - assignedDriverId: result.rows[0].assigned_driver_id, - status: result.rows[0].status, - type: result.rows[0].event_type - }; - return savedEvent; - } - catch (error) { - console.error('āŒ Error adding schedule event:', error); - throw error; - } - } - async updateScheduleEvent(vipId, eventId, event) { - try { - const query = ` - UPDATE schedule_events - SET title = $3, location = $4, start_time = $5, end_time = $6, description = $7, assigned_driver_id = $8, status = $9, event_type = $10 - WHERE id = $1 AND vip_id = $2 - RETURNING * - `; - const result = await database_1.default.query(query, [ - eventId, - vipId, - event.title, - event.location, - event.startTime, - event.endTime, - event.description || '', - event.assignedDriverId || null, - event.status, - event.type - ]); - if (result.rows.length === 0) { - return null; - } - const updatedEvent = { - id: result.rows[0].id, - title: result.rows[0].title, - location: result.rows[0].location, - startTime: result.rows[0].start_time, - endTime: result.rows[0].end_time, - description: result.rows[0].description, - assignedDriverId: result.rows[0].assigned_driver_id, - status: result.rows[0].status, - type: result.rows[0].event_type - }; - return updatedEvent; - } - catch (error) { - console.error('āŒ Error updating schedule event:', error); - throw error; - } - } - async deleteScheduleEvent(vipId, eventId) { - try { - const query = ` - DELETE FROM schedule_events - WHERE id = $1 AND vip_id = $2 - RETURNING * - `; - const result = await database_1.default.query(query, [eventId, vipId]); - if (result.rows.length === 0) { - return null; - } - const deletedEvent = { - id: result.rows[0].id, - title: result.rows[0].title, - location: result.rows[0].location, - startTime: result.rows[0].start_time, - endTime: result.rows[0].end_time, - description: result.rows[0].description, - assignedDriverId: result.rows[0].assigned_driver_id, - status: result.rows[0].status, - type: result.rows[0].event_type - }; - return deletedEvent; - } - catch (error) { - console.error('āŒ Error deleting schedule event:', error); - throw error; - } - } - async getAllSchedules() { - try { - const query = ` - SELECT * FROM schedule_events - ORDER BY vip_id, start_time - `; - const result = await database_1.default.query(query); - const schedules = {}; - for (const row of result.rows) { - const vipId = row.vip_id; - if (!schedules[vipId]) { - schedules[vipId] = []; - } - schedules[vipId].push({ - id: row.id, - title: row.title, - location: row.location, - startTime: row.start_time, - endTime: row.end_time, - description: row.description, - assignedDriverId: row.assigned_driver_id, - status: row.status, - type: row.event_type - }); - } - return schedules; - } - catch (error) { - console.error('āŒ Error fetching all schedules:', error); - throw error; - } - } - // Admin settings operations - async getAdminSettings() { - try { - const query = ` - SELECT setting_key, setting_value FROM admin_settings - `; - const result = await database_1.default.query(query); - // Default settings structure - const defaultSettings = { - apiKeys: { - aviationStackKey: '', - googleMapsKey: '', - twilioKey: '', - googleClientId: '', - googleClientSecret: '' - }, - systemSettings: { - defaultPickupLocation: '', - defaultDropoffLocation: '', - timeZone: 'America/New_York', - notificationsEnabled: false - } - }; - // If no settings exist, return defaults - if (result.rows.length === 0) { - return defaultSettings; - } - // Reconstruct nested object from flattened keys - const settings = { ...defaultSettings }; - for (const row of result.rows) { - const keys = row.setting_key.split('.'); - let current = settings; - for (let i = 0; i < keys.length - 1; i++) { - if (!current[keys[i]]) { - current[keys[i]] = {}; - } - current = current[keys[i]]; - } - // Parse boolean values - let value = row.setting_value; - if (value === 'true') - value = true; - else if (value === 'false') - value = false; - current[keys[keys.length - 1]] = value; - } - return settings; - } - catch (error) { - console.error('āŒ Error fetching admin settings:', error); - throw error; - } - } - async updateAdminSettings(settings) { - try { - // Flatten settings and update - const flattenSettings = (obj, prefix = '') => { - const result = []; - for (const [key, value] of Object.entries(obj)) { - const fullKey = prefix ? `${prefix}.${key}` : key; - if (typeof value === 'object' && value !== null) { - result.push(...flattenSettings(value, fullKey)); - } - else { - result.push({ key: fullKey, value: String(value) }); - } - } - return result; - }; - const flatSettings = flattenSettings(settings); - for (const setting of flatSettings) { - const query = ` - INSERT INTO admin_settings (setting_key, setting_value) - VALUES ($1, $2) - ON CONFLICT (setting_key) DO UPDATE SET setting_value = $2 - `; - await database_1.default.query(query, [setting.key, setting.value]); - } - } - catch (error) { - console.error('āŒ Error updating admin settings:', error); - throw error; - } - } -} -exports.default = new EnhancedDataService(); -//# sourceMappingURL=enhancedDataService.js.map \ No newline at end of file diff --git a/backend-old-20260125/dist/services/enhancedDataService.js.map b/backend-old-20260125/dist/services/enhancedDataService.js.map deleted file mode 100644 index 58ade7b..0000000 --- a/backend-old-20260125/dist/services/enhancedDataService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"enhancedDataService.js","sourceRoot":"","sources":["../../src/services/enhancedDataService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/flightService.d.ts b/backend-old-20260125/dist/services/flightService.d.ts deleted file mode 100644 index bbce4a9..0000000 --- a/backend-old-20260125/dist/services/flightService.d.ts +++ /dev/null @@ -1,53 +0,0 @@ -interface FlightData { - flightNumber: string; - flightDate: string; - status: string; - airline?: string; - aircraft?: string; - departure: { - airport: string; - airportName?: string; - scheduled: string; - estimated?: string; - actual?: string; - terminal?: string; - gate?: string; - }; - arrival: { - airport: string; - airportName?: string; - scheduled: string; - estimated?: string; - actual?: string; - terminal?: string; - gate?: string; - }; - delay?: number; - lastUpdated: string; - source: 'google' | 'aviationstack' | 'not_found'; -} -interface FlightSearchParams { - flightNumber: string; - date: string; - departureAirport?: string; - arrivalAirport?: string; -} -declare class FlightService { - private flightCache; - private updateIntervals; - constructor(); - getFlightInfo(params: FlightSearchParams): Promise; - private scrapeGoogleFlights; - private getFromAviationStack; - startPeriodicUpdates(params: FlightSearchParams, intervalMinutes?: number): void; - stopPeriodicUpdates(key: string): void; - getMultipleFlights(flightParams: FlightSearchParams[]): Promise<{ - [key: string]: FlightData | null; - }>; - private normalizeStatus; - cleanup(): void; -} -declare const _default: FlightService; -export default _default; -export { FlightData, FlightSearchParams }; -//# sourceMappingURL=flightService.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/services/flightService.d.ts.map b/backend-old-20260125/dist/services/flightService.d.ts.map deleted file mode 100644 index da42d58..0000000 --- a/backend-old-20260125/dist/services/flightService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"flightService.d.ts","sourceRoot":"","sources":["../../src/services/flightService.ts"],"names":[],"mappings":"AAGA,UAAU,UAAU;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE;QACT,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,OAAO,EAAE;QACP,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,QAAQ,GAAG,eAAe,GAAG,WAAW,CAAC;CAClD;AAED,UAAU,kBAAkB;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,cAAM,aAAa;IACjB,OAAO,CAAC,WAAW,CAAiE;IACpF,OAAO,CAAC,eAAe,CAA0C;;IAO3D,aAAa,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;YAkC7D,mBAAmB;YAiBnB,oBAAoB;IAiGlC,oBAAoB,CAAC,MAAM,EAAE,kBAAkB,EAAE,eAAe,GAAE,MAAU,GAAG,IAAI;IAoBnF,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAShC,kBAAkB,CAAC,YAAY,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAAC;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAAA;KAAE,CAAC;IAY3G,OAAO,CAAC,eAAe;IAcvB,OAAO,IAAI,IAAI;CAOhB;;AAED,wBAAmC;AACnC,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,CAAC"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/flightService.js b/backend-old-20260125/dist/services/flightService.js deleted file mode 100644 index 9d5f213..0000000 --- a/backend-old-20260125/dist/services/flightService.js +++ /dev/null @@ -1,196 +0,0 @@ -"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 \ No newline at end of file diff --git a/backend-old-20260125/dist/services/flightService.js.map b/backend-old-20260125/dist/services/flightService.js.map deleted file mode 100644 index 0b445d1..0000000 --- a/backend-old-20260125/dist/services/flightService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"flightService.js","sourceRoot":"","sources":["../../src/services/flightService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/flightTrackingScheduler.d.ts b/backend-old-20260125/dist/services/flightTrackingScheduler.d.ts deleted file mode 100644 index e503b64..0000000 --- a/backend-old-20260125/dist/services/flightTrackingScheduler.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -declare class FlightTrackingScheduler { - private trackingSchedule; - private checkIntervals; - private flightService; - constructor(flightService: any); - addVipFlights(vipId: string, vipName: string, flights: any[]): void; - removeVipFlights(vipId: string): void; - private updateTrackingSchedules; - private setupDateTracking; - private performBatchCheck; - private stopDateTracking; - getTrackingStatus(): any; - cleanup(): void; -} -export default FlightTrackingScheduler; -//# sourceMappingURL=flightTrackingScheduler.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/services/flightTrackingScheduler.d.ts.map b/backend-old-20260125/dist/services/flightTrackingScheduler.d.ts.map deleted file mode 100644 index 4df7421..0000000 --- a/backend-old-20260125/dist/services/flightTrackingScheduler.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"flightTrackingScheduler.d.ts","sourceRoot":"","sources":["../../src/services/flightTrackingScheduler.ts"],"names":[],"mappings":"AAoBA,cAAM,uBAAuB;IAC3B,OAAO,CAAC,gBAAgB,CAAwB;IAChD,OAAO,CAAC,cAAc,CAA0C;IAChE,OAAO,CAAC,aAAa,CAAM;gBAEf,aAAa,EAAE,GAAG;IAK9B,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE;IAoC5D,gBAAgB,CAAC,KAAK,EAAE,MAAM;IAgB9B,OAAO,CAAC,uBAAuB;IAY/B,OAAO,CAAC,iBAAiB;YAsDX,iBAAiB;IAwF/B,OAAO,CAAC,gBAAgB;IAcxB,iBAAiB,IAAI,GAAG;IA0BxB,OAAO;CAKR;AAED,eAAe,uBAAuB,CAAC"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/flightTrackingScheduler.js b/backend-old-20260125/dist/services/flightTrackingScheduler.js deleted file mode 100644 index cb8b41e..0000000 --- a/backend-old-20260125/dist/services/flightTrackingScheduler.js +++ /dev/null @@ -1,219 +0,0 @@ -"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 \ No newline at end of file diff --git a/backend-old-20260125/dist/services/flightTrackingScheduler.js.map b/backend-old-20260125/dist/services/flightTrackingScheduler.js.map deleted file mode 100644 index aa3f601..0000000 --- a/backend-old-20260125/dist/services/flightTrackingScheduler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"flightTrackingScheduler.js","sourceRoot":"","sources":["../../src/services/flightTrackingScheduler.ts"],"names":[],"mappings":";AAAA,oCAAoC;AACpC,sEAAsE;;AAmBtE,MAAM,uBAAuB;IAK3B,YAAY,aAAkB;QAJtB,qBAAgB,GAAqB,EAAE,CAAC;QACxC,mBAAc,GAAgC,IAAI,GAAG,EAAE,CAAC;QAI9D,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IACrC,CAAC;IAED,iDAAiD;IACjD,aAAa,CAAC,KAAa,EAAE,OAAe,EAAE,OAAc;QAC1D,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;YACvB,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC;YAE9B,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;gBAChC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;YAClC,CAAC;YAED,gDAAgD;YAChD,MAAM,aAAa,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,SAAS,CACxD,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,MAAM,CAAC,YAAY,IAAI,CAAC,CAAC,KAAK,KAAK,KAAK,CACjE,CAAC;YAEF,MAAM,eAAe,GAAoB;gBACvC,KAAK;gBACL,OAAO;gBACP,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,kBAAkB,EAAE,MAAM,CAAC,cAAc,EAAE,SAAS,EAAE,SAAS;aAChE,CAAC;YAEF,IAAI,aAAa,IAAI,CAAC,EAAE,CAAC;gBACvB,wBAAwB;gBACxB,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,aAAa,CAAC,GAAG,eAAe,CAAC;YAC9D,CAAC;iBAAM,CAAC;gBACN,gBAAgB;gBAChB,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YACnD,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,8CAA8C;QAC9C,IAAI,CAAC,uBAAuB,EAAE,CAAC;IACjC,CAAC;IAED,mCAAmC;IACnC,gBAAgB,CAAC,KAAa;QAC5B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;YAChD,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,MAAM,CAC9D,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CACvB,CAAC;YAEF,qBAAqB;YACrB,IAAI,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC7C,OAAO,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;YACrC,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,uBAAuB,EAAE,CAAC;IACjC,CAAC;IAED,qDAAqD;IAC7C,uBAAuB;QAC7B,2BAA2B;QAC3B,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;QACjE,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;QAE5B,gCAAgC;QAChC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;YAChD,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,sCAAsC;IAC9B,iBAAiB,CAAC,IAAY;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAE7C,kEAAkE;QAClE,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC,CAAC;QAE7C,+BAA+B;QAC/B,IAAI,iBAAiB,GAAgB,IAAI,CAAC;QAC1C,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;YACvB,IAAI,MAAM,CAAC,kBAAkB,EAAE,CAAC;gBAC9B,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC;gBACpD,IAAI,CAAC,iBAAiB,IAAI,OAAO,GAAG,iBAAiB,EAAE,CAAC;oBACtD,iBAAiB,GAAG,OAAO,CAAC;gBAC9B,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,qCAAqC;QACrC,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACvB,iBAAiB,GAAG,IAAI,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC,CAAC;QACnD,CAAC;QAED,mDAAmD;QACnD,MAAM,iBAAiB,GAAG,IAAI,IAAI,CAAC,iBAAiB,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAErF,qDAAqD;QACrD,IAAI,GAAG,IAAI,iBAAiB,EAAE,CAAC;YAC7B,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAE7B,iEAAiE;YACjE,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;gBAChC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAC/B,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,aAAa;YAEjC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,+CAA+C;YAC/C,MAAM,cAAc,GAAG,iBAAiB,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;YACnE,UAAU,CAAC,GAAG,EAAE;gBACd,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;gBAE7B,+BAA+B;gBAC/B,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;oBAChC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;gBAC/B,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;gBAEnB,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YAC1C,CAAC,EAAE,cAAc,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAED,gDAAgD;IACxC,KAAK,CAAC,iBAAiB,CAAC,IAAY;QAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAE7C,OAAO,CAAC,GAAG,CAAC,gCAAgC,IAAI,MAAM,CAAC,CAAC;QACxD,OAAO,CAAC,GAAG,CAAC,YAAY,OAAO,CAAC,MAAM,aAAa,CAAC,CAAC;QAErD,8CAA8C;QAC9C,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAExD,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;YACzE,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;YAC5B,OAAO;QACT,CAAC;QAED,qCAAqC;QACrC,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CACtC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CACvC,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,mCAAmC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAE3E,IAAI,CAAC;YACH,sBAAsB;YACtB,MAAM,YAAY,GAAG,aAAa,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;gBACtD,YAAY;gBACZ,IAAI;aACL,CAAC,CAAC,CAAC;YAEJ,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;YAE1E,yBAAyB;YACzB,IAAI,SAAS,GAAG,KAAK,CAAC;YACtB,IAAI,SAAS,GAAG,IAAI,CAAC;YAErB,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;gBAC7B,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,YAAY,IAAI,IAAI,EAAE,CAAC;gBAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;gBAE1B,IAAI,IAAI,EAAE,CAAC;oBACT,MAAM,CAAC,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC;oBAChC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;oBAE5B,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;wBAC7B,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC;wBACxB,OAAO,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,YAAY,aAAa,CAAC,CAAC;oBACrD,CAAC;yBAAM,CAAC;wBACN,SAAS,GAAG,KAAK,CAAC;wBAClB,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;4BACjC,SAAS,GAAG,IAAI,CAAC;4BACjB,OAAO,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,YAAY,kBAAkB,IAAI,CAAC,KAAK,UAAU,CAAC,CAAC;wBAC/E,CAAC;oBACH,CAAC;oBAED,0BAA0B;oBAC1B,OAAO,CAAC,GAAG,CAAC,UAAU,MAAM,CAAC,OAAO,aAAa,MAAM,CAAC,OAAO,KAAK,MAAM,CAAC,YAAY,cAAc,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;gBACtH,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,4CAA4C;YAC5C,IAAI,SAAS,IAAI,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC/C,OAAO,CAAC,GAAG,CAAC,4DAA4D,CAAC,CAAC;gBAC1E,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,CAAC;gBAE9C,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;oBAChC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;gBAC/B,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,aAAa;gBAEjC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YAC1C,CAAC;YAED,2CAA2C;YAC3C,IAAI,SAAS,EAAE,CAAC;gBACd,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;gBACzE,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;YAC9B,CAAC;YAED,4BAA4B;YAC5B,MAAM,aAAa,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;YAC/E,OAAO,CAAC,GAAG,CAAC,6BAA6B,aAAa,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;QAEjF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,KAAK,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAED,oCAAoC;IAC5B,gBAAgB,CAAC,IAAY;QACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,QAAQ,EAAE,CAAC;YACb,aAAa,CAAC,QAAQ,CAAC,CAAC;YACxB,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC;QAED,gCAAgC;QAChC,IAAI,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;YAChC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAED,8BAA8B;IAC9B,iBAAiB;QACf,MAAM,MAAM,GAAQ,EAAE,CAAC;QAEvB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE;YAChE,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YACxD,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YAEvD,MAAM,CAAC,IAAI,CAAC,GAAG;gBACb,YAAY,EAAE,OAAO,CAAC,MAAM;gBAC5B,aAAa,EAAE,aAAa,CAAC,MAAM;gBACnC,aAAa,EAAE,aAAa,CAAC,MAAM;gBACnC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;oBACzB,OAAO,EAAE,CAAC,CAAC,OAAO;oBAClB,YAAY,EAAE,CAAC,CAAC,YAAY;oBAC5B,OAAO,EAAE,CAAC,CAAC,OAAO;oBAClB,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,iBAAiB;oBACrC,WAAW,EAAE,CAAC,CAAC,WAAW;oBAC1B,SAAS,EAAE,CAAC,CAAC,SAAS;iBACvB,CAAC,CAAC;aACJ,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,wBAAwB;IACxB,OAAO;QACL,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;QACjE,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;QAC5B,IAAI,CAAC,gBAAgB,GAAG,EAAE,CAAC;IAC7B,CAAC;CACF;AAED,kBAAe,uBAAuB,CAAC"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/jwtKeyManager.d.ts b/backend-old-20260125/dist/services/jwtKeyManager.d.ts deleted file mode 100644 index 9283519..0000000 --- a/backend-old-20260125/dist/services/jwtKeyManager.d.ts +++ /dev/null @@ -1,38 +0,0 @@ -export interface User { - id: string; - google_id: string; - email: string; - name: string; - profile_picture_url?: string; - role: 'driver' | 'coordinator' | 'administrator'; - status?: 'pending' | 'active' | 'deactivated'; - created_at?: string; - last_login?: string; - is_active?: boolean; - updated_at?: string; - approval_status?: string; - onboardingData?: any; -} -declare class JWTKeyManager { - private currentSecret; - private previousSecret; - private rotationInterval; - private gracePeriodTimeout; - constructor(); - private generateSecret; - private startRotation; - private rotateKey; - generateToken(user: User): string; - verifyToken(token: string): User | null; - getStatus(): { - hasCurrentKey: boolean; - hasPreviousKey: boolean; - rotationActive: boolean; - gracePeriodActive: boolean; - }; - destroy(): void; - forceRotation(): void; -} -export declare const jwtKeyManager: JWTKeyManager; -export default jwtKeyManager; -//# sourceMappingURL=jwtKeyManager.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/services/jwtKeyManager.d.ts.map b/backend-old-20260125/dist/services/jwtKeyManager.d.ts.map deleted file mode 100644 index c06f65c..0000000 --- a/backend-old-20260125/dist/services/jwtKeyManager.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"jwtKeyManager.d.ts","sourceRoot":"","sources":["../../src/services/jwtKeyManager.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,IAAI,EAAE,QAAQ,GAAG,aAAa,GAAG,eAAe,CAAC;IACjD,MAAM,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,aAAa,CAAC;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,GAAG,CAAC;CACtB;AAED,cAAM,aAAa;IACjB,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,gBAAgB,CAA+B;IACvD,OAAO,CAAC,kBAAkB,CAA+B;;IAQzD,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,SAAS;IAuBjB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM;IAqBjC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAqDvC,SAAS;;;;;;IAUT,OAAO;IAiBP,aAAa;CAId;AAGD,eAAO,MAAM,aAAa,eAAsB,CAAC;AAWjD,eAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/jwtKeyManager.js b/backend-old-20260125/dist/services/jwtKeyManager.js deleted file mode 100644 index 261c64d..0000000 --- a/backend-old-20260125/dist/services/jwtKeyManager.js +++ /dev/null @@ -1,158 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.jwtKeyManager = void 0; -const crypto_1 = __importDefault(require("crypto")); -const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); -class JWTKeyManager { - constructor() { - this.previousSecret = null; - this.rotationInterval = null; - this.gracePeriodTimeout = null; - console.log('šŸ”‘ Initializing JWT Key Manager with automatic rotation'); - this.currentSecret = this.generateSecret(); - this.startRotation(); - } - generateSecret() { - const secret = crypto_1.default.randomBytes(64).toString('hex'); - console.log('šŸ”„ Generated new JWT signing key (length:', secret.length, 'chars)'); - return secret; - } - startRotation() { - // Rotate every 24 hours (86400000 ms) - this.rotationInterval = setInterval(() => { - this.rotateKey(); - }, 24 * 60 * 60 * 1000); - console.log('ā° JWT key rotation scheduled every 24 hours'); - // Also rotate on startup after 1 hour to test the system - setTimeout(() => { - console.log('🧪 Performing initial key rotation test...'); - this.rotateKey(); - }, 60 * 60 * 1000); // 1 hour - } - rotateKey() { - console.log('šŸ”„ Rotating JWT signing key...'); - // Store current secret as previous - this.previousSecret = this.currentSecret; - // Generate new current secret - this.currentSecret = this.generateSecret(); - console.log('āœ… JWT key rotation completed. Grace period: 24 hours'); - // Clear any existing grace period timeout - if (this.gracePeriodTimeout) { - clearTimeout(this.gracePeriodTimeout); - } - // Clean up previous secret after 24 hours (grace period) - this.gracePeriodTimeout = setTimeout(() => { - this.previousSecret = null; - console.log('🧹 Grace period ended. Previous JWT key cleaned up'); - }, 24 * 60 * 60 * 1000); - } - generateToken(user) { - const payload = { - id: user.id, - google_id: user.google_id, - email: user.email, - name: user.name, - profile_picture_url: user.profile_picture_url, - role: user.role, - status: user.status, - approval_status: user.approval_status, - onboardingData: user.onboardingData, - iat: Math.floor(Date.now() / 1000) // Issued at time - }; - return jsonwebtoken_1.default.sign(payload, this.currentSecret, { - expiresIn: '24h', - issuer: 'vip-coordinator', - audience: 'vip-coordinator-users' - }); - } - verifyToken(token) { - try { - // Try current secret first - const decoded = jsonwebtoken_1.default.verify(token, this.currentSecret, { - issuer: 'vip-coordinator', - audience: 'vip-coordinator-users' - }); - return { - id: decoded.id, - google_id: decoded.google_id, - email: decoded.email, - name: decoded.name, - profile_picture_url: decoded.profile_picture_url, - role: decoded.role, - status: decoded.status, - approval_status: decoded.approval_status, - onboardingData: decoded.onboardingData - }; - } - catch (error) { - // Try previous secret during grace period - if (this.previousSecret) { - try { - const decoded = jsonwebtoken_1.default.verify(token, this.previousSecret, { - issuer: 'vip-coordinator', - audience: 'vip-coordinator-users' - }); - console.log('šŸ”„ Token verified using previous key (grace period)'); - return { - id: decoded.id, - google_id: decoded.google_id, - email: decoded.email, - name: decoded.name, - profile_picture_url: decoded.profile_picture_url, - role: decoded.role, - status: decoded.status, - approval_status: decoded.approval_status, - onboardingData: decoded.onboardingData - }; - } - catch (gracePeriodError) { - console.log('āŒ Token verification failed with both current and previous keys'); - return null; - } - } - console.log('āŒ Token verification failed:', error instanceof Error ? error.message : 'Unknown error'); - return null; - } - } - // Get status for monitoring/debugging - getStatus() { - return { - hasCurrentKey: !!this.currentSecret, - hasPreviousKey: !!this.previousSecret, - rotationActive: !!this.rotationInterval, - gracePeriodActive: !!this.gracePeriodTimeout - }; - } - // Cleanup on shutdown - destroy() { - console.log('šŸ›‘ Shutting down JWT Key Manager...'); - if (this.rotationInterval) { - clearInterval(this.rotationInterval); - this.rotationInterval = null; - } - if (this.gracePeriodTimeout) { - clearTimeout(this.gracePeriodTimeout); - this.gracePeriodTimeout = null; - } - console.log('āœ… JWT Key Manager shutdown complete'); - } - // Manual rotation for testing/emergency - forceRotation() { - console.log('🚨 Manual key rotation triggered'); - this.rotateKey(); - } -} -// Singleton instance -exports.jwtKeyManager = new JWTKeyManager(); -// Graceful shutdown handling -process.on('SIGTERM', () => { - exports.jwtKeyManager.destroy(); -}); -process.on('SIGINT', () => { - exports.jwtKeyManager.destroy(); -}); -exports.default = exports.jwtKeyManager; -//# sourceMappingURL=jwtKeyManager.js.map \ No newline at end of file diff --git a/backend-old-20260125/dist/services/jwtKeyManager.js.map b/backend-old-20260125/dist/services/jwtKeyManager.js.map deleted file mode 100644 index 475d9f9..0000000 --- a/backend-old-20260125/dist/services/jwtKeyManager.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"jwtKeyManager.js","sourceRoot":"","sources":["../../src/services/jwtKeyManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/scheduleValidationService.d.ts b/backend-old-20260125/dist/services/scheduleValidationService.d.ts deleted file mode 100644 index 63a4cbf..0000000 --- a/backend-old-20260125/dist/services/scheduleValidationService.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -interface ValidationError { - field: string; - message: string; - code: string; -} -interface ScheduleEvent { - title: string; - startTime: string; - endTime: string; - location: string; - type: string; -} -declare class ScheduleValidationService { - validateEvent(event: ScheduleEvent, isEdit?: boolean): ValidationError[]; - validateEventSequence(events: ScheduleEvent[]): ValidationError[]; - getErrorSummary(errors: ValidationError[]): string; - isCriticalError(error: ValidationError): boolean; - categorizeErrors(errors: ValidationError[]): { - critical: ValidationError[]; - warnings: ValidationError[]; - }; - validateTimeFormat(timeString: string): { - isValid: boolean; - suggestion?: string; - }; -} -declare const _default: ScheduleValidationService; -export default _default; -export { ValidationError, ScheduleEvent }; -//# sourceMappingURL=scheduleValidationService.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/services/scheduleValidationService.d.ts.map b/backend-old-20260125/dist/services/scheduleValidationService.d.ts.map deleted file mode 100644 index 45eed50..0000000 --- a/backend-old-20260125/dist/services/scheduleValidationService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"scheduleValidationService.d.ts","sourceRoot":"","sources":["../../src/services/scheduleValidationService.ts"],"names":[],"mappings":"AAAA,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,aAAa;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,cAAM,yBAAyB;IAG7B,aAAa,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,GAAE,OAAe,GAAG,eAAe,EAAE;IAuJ/E,qBAAqB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,eAAe,EAAE;IA6BjE,eAAe,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM;IAalD,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO;IAMhD,gBAAgB,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG;QAAE,QAAQ,EAAE,eAAe,EAAE,CAAC;QAAC,QAAQ,EAAE,eAAe,EAAE,CAAA;KAAE;IAgBzG,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;CAYlF;;AAED,wBAA+C;AAC/C,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,CAAC"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/scheduleValidationService.js b/backend-old-20260125/dist/services/scheduleValidationService.js deleted file mode 100644 index f64790d..0000000 --- a/backend-old-20260125/dist/services/scheduleValidationService.js +++ /dev/null @@ -1,200 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -class ScheduleValidationService { - // Validate a single schedule event - validateEvent(event, isEdit = false) { - const errors = []; - const now = new Date(); - const startTime = new Date(event.startTime); - const endTime = new Date(event.endTime); - // 1. Check if dates are valid - if (isNaN(startTime.getTime())) { - errors.push({ - field: 'startTime', - message: 'Start time is not a valid date', - code: 'INVALID_START_DATE' - }); - } - if (isNaN(endTime.getTime())) { - errors.push({ - field: 'endTime', - message: 'End time is not a valid date', - code: 'INVALID_END_DATE' - }); - } - // If dates are invalid, return early - if (errors.length > 0) { - return errors; - } - // 2. Check if start time is in the future (with 5-minute grace period for edits) - const graceMinutes = isEdit ? 5 : 0; - const minimumStartTime = new Date(now.getTime() + (graceMinutes * 60 * 1000)); - if (startTime < minimumStartTime) { - errors.push({ - field: 'startTime', - message: isEdit - ? 'Start time must be at least 5 minutes in the future for edits' - : 'Start time must be in the future', - code: 'START_TIME_IN_PAST' - }); - } - // 3. Check if end time is after start time - if (endTime <= startTime) { - errors.push({ - field: 'endTime', - message: 'End time must be after start time', - code: 'END_BEFORE_START' - }); - } - // 4. Check minimum event duration (5 minutes) - const durationMinutes = (endTime.getTime() - startTime.getTime()) / (1000 * 60); - if (durationMinutes < 5) { - errors.push({ - field: 'endTime', - message: 'Event must be at least 5 minutes long', - code: 'DURATION_TOO_SHORT' - }); - } - // 5. Check maximum event duration (24 hours) - if (durationMinutes > (24 * 60)) { - errors.push({ - field: 'endTime', - message: 'Event cannot be longer than 24 hours', - code: 'DURATION_TOO_LONG' - }); - } - // 6. Check if end time is in the future - if (endTime < now) { - errors.push({ - field: 'endTime', - message: 'End time must be in the future', - code: 'END_TIME_IN_PAST' - }); - } - // 7. Validate required fields - if (!event.title || event.title.trim().length === 0) { - errors.push({ - field: 'title', - message: 'Event title is required', - code: 'TITLE_REQUIRED' - }); - } - if (!event.location || event.location.trim().length === 0) { - errors.push({ - field: 'location', - message: 'Event location is required', - code: 'LOCATION_REQUIRED' - }); - } - if (!event.type || event.type.trim().length === 0) { - errors.push({ - field: 'type', - message: 'Event type is required', - code: 'TYPE_REQUIRED' - }); - } - // 8. Validate title length - if (event.title && event.title.length > 100) { - errors.push({ - field: 'title', - message: 'Event title cannot exceed 100 characters', - code: 'TITLE_TOO_LONG' - }); - } - // 9. Validate location length - if (event.location && event.location.length > 200) { - errors.push({ - field: 'location', - message: 'Event location cannot exceed 200 characters', - code: 'LOCATION_TOO_LONG' - }); - } - // 10. Check for reasonable scheduling (not more than 2 years in the future) - const twoYearsFromNow = new Date(); - twoYearsFromNow.setFullYear(twoYearsFromNow.getFullYear() + 2); - if (startTime > twoYearsFromNow) { - errors.push({ - field: 'startTime', - message: 'Event cannot be scheduled more than 2 years in the future', - code: 'START_TIME_TOO_FAR' - }); - } - // 11. Check for business hours validation (optional warning) - const startHour = startTime.getHours(); - const endHour = endTime.getHours(); - if (startHour < 6 || startHour > 23) { - // This is a warning, not an error - we'll add it but with a different severity - errors.push({ - field: 'startTime', - message: 'Event starts outside typical business hours (6 AM - 11 PM)', - code: 'OUTSIDE_BUSINESS_HOURS' - }); - } - return errors; - } - // Validate multiple events for conflicts and logical sequencing - validateEventSequence(events) { - const errors = []; - // Sort events by start time - const sortedEvents = events - .map((event, index) => ({ ...event, originalIndex: index })) - .sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime()); - // Check for overlapping events - for (let i = 0; i < sortedEvents.length - 1; i++) { - const currentEvent = sortedEvents[i]; - const nextEvent = sortedEvents[i + 1]; - const currentEnd = new Date(currentEvent.endTime); - const nextStart = new Date(nextEvent.startTime); - if (currentEnd > nextStart) { - errors.push({ - field: 'schedule', - message: `Event "${currentEvent.title}" overlaps with "${nextEvent.title}"`, - code: 'EVENTS_OVERLAP' - }); - } - } - return errors; - } - // Get user-friendly error messages - getErrorSummary(errors) { - if (errors.length === 0) - return ''; - const errorMessages = errors.map(error => error.message); - if (errors.length === 1) { - return errorMessages[0]; - } - return `Multiple validation errors:\n• ${errorMessages.join('\n• ')}`; - } - // Check if errors are warnings vs critical errors - isCriticalError(error) { - const warningCodes = ['OUTSIDE_BUSINESS_HOURS']; - return !warningCodes.includes(error.code); - } - // Separate critical errors from warnings - categorizeErrors(errors) { - const critical = []; - const warnings = []; - errors.forEach(error => { - if (this.isCriticalError(error)) { - critical.push(error); - } - else { - warnings.push(error); - } - }); - return { critical, warnings }; - } - // Validate time format and suggest corrections - validateTimeFormat(timeString) { - const date = new Date(timeString); - if (isNaN(date.getTime())) { - return { - isValid: false, - suggestion: 'Please use format: YYYY-MM-DDTHH:MM (e.g., 2025-07-01T14:30)' - }; - } - return { isValid: true }; - } -} -exports.default = new ScheduleValidationService(); -//# sourceMappingURL=scheduleValidationService.js.map \ No newline at end of file diff --git a/backend-old-20260125/dist/services/scheduleValidationService.js.map b/backend-old-20260125/dist/services/scheduleValidationService.js.map deleted file mode 100644 index 0d07aa1..0000000 --- a/backend-old-20260125/dist/services/scheduleValidationService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"scheduleValidationService.js","sourceRoot":"","sources":["../../src/services/scheduleValidationService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/unifiedDataService.d.ts b/backend-old-20260125/dist/services/unifiedDataService.d.ts deleted file mode 100644 index fb571c8..0000000 --- a/backend-old-20260125/dist/services/unifiedDataService.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -declare class UnifiedDataService { - private pool; - constructor(); - private toCamelCase; - getVips(): Promise; - getVipById(id: string): Promise; - createVip(vipData: any): Promise; - updateVip(id: string, vipData: any): Promise; - deleteVip(id: string): Promise; - getDrivers(): Promise; - getDriverById(id: string): Promise; - createDriver(driverData: any): Promise; - updateDriver(id: string, driverData: any): Promise; - deleteDriver(id: string): Promise; - getScheduleByVipId(vipId: string): Promise; - createScheduleEvent(vipId: string, eventData: any): Promise; - updateScheduleEvent(id: string, eventData: any): Promise; - deleteScheduleEvent(id: string): Promise; - getAllSchedules(): Promise>; - getUserByEmail(email: string): Promise; - getUserById(id: string): Promise; - createUser(userData: any): Promise; - updateUserRole(email: string, role: string): Promise; - getUserCount(): Promise; - getAdminSettings(): Promise; - updateAdminSetting(key: string, value: string): Promise; -} -declare const _default: UnifiedDataService; -export default _default; -//# sourceMappingURL=unifiedDataService.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/services/unifiedDataService.d.ts.map b/backend-old-20260125/dist/services/unifiedDataService.d.ts.map deleted file mode 100644 index c55cad6..0000000 --- a/backend-old-20260125/dist/services/unifiedDataService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"unifiedDataService.d.ts","sourceRoot":"","sources":["../../src/services/unifiedDataService.ts"],"names":[],"mappings":"AAIA,cAAM,kBAAkB;IACtB,OAAO,CAAC,IAAI,CAAO;;IAOnB,OAAO,CAAC,WAAW;IAab,OAAO;IAwBP,UAAU,CAAC,EAAE,EAAE,MAAM;IAwBrB,SAAS,CAAC,OAAO,EAAE,GAAG;IA2CtB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG;IAkDlC,SAAS,CAAC,EAAE,EAAE,MAAM;IASpB,UAAU;IAOV,aAAa,CAAC,EAAE,EAAE,MAAM;IAQxB,YAAY,CAAC,UAAU,EAAE,GAAG;IAa5B,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG;IAcxC,YAAY,CAAC,EAAE,EAAE,MAAM;IASvB,kBAAkB,CAAC,KAAK,EAAE,MAAM;IAYhC,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG;IAajD,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG;IAe9C,mBAAmB,CAAC,EAAE,EAAE,MAAM;IAQ9B,eAAe;IAuBf,cAAc,CAAC,KAAK,EAAE,MAAM;IAQ5B,WAAW,CAAC,EAAE,EAAE,MAAM;IAQtB,UAAU,CAAC,QAAQ,EAAE,GAAG;IAaxB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAW1C,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAM/B,gBAAgB;IAWhB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CAQpD;;AAED,wBAAwC"} \ No newline at end of file diff --git a/backend-old-20260125/dist/services/unifiedDataService.js b/backend-old-20260125/dist/services/unifiedDataService.js deleted file mode 100644 index 8803380..0000000 --- a/backend-old-20260125/dist/services/unifiedDataService.js +++ /dev/null @@ -1,264 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const database_1 = __importDefault(require("../config/database")); -// Simplified, unified data service that replaces the three redundant services -class UnifiedDataService { - constructor() { - this.pool = database_1.default; - } - // Helper to convert snake_case to camelCase - toCamelCase(obj) { - if (!obj) - return obj; - if (Array.isArray(obj)) - return obj.map(item => this.toCamelCase(item)); - if (typeof obj !== 'object') - return obj; - return Object.keys(obj).reduce((result, key) => { - const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()); - result[camelKey] = this.toCamelCase(obj[key]); - return result; - }, {}); - } - // VIP Operations - async getVips() { - const query = ` - SELECT v.*, - COALESCE( - JSON_AGG( - JSON_BUILD_OBJECT( - 'flightNumber', f.flight_number, - 'airline', f.airline, - 'scheduledArrival', f.scheduled_arrival, - 'scheduledDeparture', f.scheduled_departure, - 'status', f.status - ) ORDER BY f.scheduled_arrival - ) FILTER (WHERE f.id IS NOT NULL), - '[]' - ) as flights - FROM vips v - LEFT JOIN flights f ON v.id = f.vip_id - GROUP BY v.id - ORDER BY v.created_at DESC`; - const result = await this.pool.query(query); - return this.toCamelCase(result.rows); - } - async getVipById(id) { - const query = ` - SELECT v.*, - COALESCE( - JSON_AGG( - JSON_BUILD_OBJECT( - 'flightNumber', f.flight_number, - 'airline', f.airline, - 'scheduledArrival', f.scheduled_arrival, - 'scheduledDeparture', f.scheduled_departure, - 'status', f.status - ) ORDER BY f.scheduled_arrival - ) FILTER (WHERE f.id IS NOT NULL), - '[]' - ) as flights - FROM vips v - LEFT JOIN flights f ON v.id = f.vip_id - WHERE v.id = $1 - GROUP BY v.id`; - const result = await this.pool.query(query, [id]); - return this.toCamelCase(result.rows[0]); - } - async createVip(vipData) { - const { name, organization, department, transportMode, flights, expectedArrival, needsAirportPickup, needsVenueTransport, notes } = vipData; - const client = await this.pool.connect(); - try { - await client.query('BEGIN'); - // Insert VIP - const vipQuery = ` - INSERT INTO vips (name, organization, department, transport_mode, expected_arrival, - needs_airport_pickup, needs_venue_transport, notes) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8) - RETURNING *`; - const vipResult = await client.query(vipQuery, [ - name, organization, department || 'Office of Development', transportMode || 'flight', - expectedArrival, needsAirportPickup !== false, needsVenueTransport !== false, notes || '' - ]); - const vip = vipResult.rows[0]; - // Insert flights if any - if (transportMode === 'flight' && flights?.length > 0) { - for (const flight of flights) { - await client.query(`INSERT INTO flights (vip_id, flight_number, airline, scheduled_arrival, scheduled_departure) - VALUES ($1, $2, $3, $4, $5)`, [vip.id, flight.flightNumber, flight.airline, flight.scheduledArrival, flight.scheduledDeparture]); - } - } - await client.query('COMMIT'); - return this.getVipById(vip.id); - } - catch (error) { - await client.query('ROLLBACK'); - throw error; - } - finally { - client.release(); - } - } - async updateVip(id, vipData) { - const { name, organization, department, transportMode, flights, expectedArrival, needsAirportPickup, needsVenueTransport, notes } = vipData; - const client = await this.pool.connect(); - try { - await client.query('BEGIN'); - // Update VIP - const updateQuery = ` - UPDATE vips - SET name = $2, organization = $3, department = $4, transport_mode = $5, - expected_arrival = $6, needs_airport_pickup = $7, needs_venue_transport = $8, - notes = $9, updated_at = NOW() - WHERE id = $1 - RETURNING *`; - const result = await client.query(updateQuery, [ - id, name, organization, department, transportMode, - expectedArrival, needsAirportPickup, needsVenueTransport, notes - ]); - if (result.rows.length === 0) { - await client.query('ROLLBACK'); - return null; - } - // Update flights - await client.query('DELETE FROM flights WHERE vip_id = $1', [id]); - if (transportMode === 'flight' && flights?.length > 0) { - for (const flight of flights) { - await client.query(`INSERT INTO flights (vip_id, flight_number, airline, scheduled_arrival, scheduled_departure) - VALUES ($1, $2, $3, $4, $5)`, [id, flight.flightNumber, flight.airline, flight.scheduledArrival, flight.scheduledDeparture]); - } - } - await client.query('COMMIT'); - return this.getVipById(id); - } - catch (error) { - await client.query('ROLLBACK'); - throw error; - } - finally { - client.release(); - } - } - async deleteVip(id) { - const result = await this.pool.query('DELETE FROM vips WHERE id = $1 RETURNING *', [id]); - return this.toCamelCase(result.rows[0]); - } - // Driver Operations - async getDrivers() { - const result = await this.pool.query('SELECT * FROM drivers ORDER BY name ASC'); - return this.toCamelCase(result.rows); - } - async getDriverById(id) { - const result = await this.pool.query('SELECT * FROM drivers WHERE id = $1', [id]); - return this.toCamelCase(result.rows[0]); - } - async createDriver(driverData) { - const { name, email, phone, vehicleInfo, status } = driverData; - const result = await this.pool.query(`INSERT INTO drivers (name, email, phone, vehicle_info, status) - VALUES ($1, $2, $3, $4, $5) - RETURNING *`, [name, email, phone, vehicleInfo, status || 'available']); - return this.toCamelCase(result.rows[0]); - } - async updateDriver(id, driverData) { - const { name, email, phone, vehicleInfo, status } = driverData; - const result = await this.pool.query(`UPDATE drivers - SET name = $2, email = $3, phone = $4, vehicle_info = $5, status = $6, updated_at = NOW() - WHERE id = $1 - RETURNING *`, [id, name, email, phone, vehicleInfo, status]); - return this.toCamelCase(result.rows[0]); - } - async deleteDriver(id) { - const result = await this.pool.query('DELETE FROM drivers WHERE id = $1 RETURNING *', [id]); - return this.toCamelCase(result.rows[0]); - } - // Schedule Operations - async getScheduleByVipId(vipId) { - const result = await this.pool.query(`SELECT se.*, d.name as driver_name - FROM schedule_events se - LEFT JOIN drivers d ON se.driver_id = d.id - WHERE se.vip_id = $1 - ORDER BY se.event_time ASC`, [vipId]); - return this.toCamelCase(result.rows); - } - async createScheduleEvent(vipId, eventData) { - const { driverId, eventTime, eventType, location, notes } = eventData; - const result = await this.pool.query(`INSERT INTO schedule_events (vip_id, driver_id, event_time, event_type, location, notes) - VALUES ($1, $2, $3, $4, $5, $6) - RETURNING *`, [vipId, driverId, eventTime, eventType, location, notes]); - return this.toCamelCase(result.rows[0]); - } - async updateScheduleEvent(id, eventData) { - const { driverId, eventTime, eventType, location, notes, status } = eventData; - const result = await this.pool.query(`UPDATE schedule_events - SET driver_id = $2, event_time = $3, event_type = $4, location = $5, - notes = $6, status = $7, updated_at = NOW() - WHERE id = $1 - RETURNING *`, [id, driverId, eventTime, eventType, location, notes, status]); - return this.toCamelCase(result.rows[0]); - } - async deleteScheduleEvent(id) { - const result = await this.pool.query('DELETE FROM schedule_events WHERE id = $1 RETURNING *', [id]); - return this.toCamelCase(result.rows[0]); - } - async getAllSchedules() { - const result = await this.pool.query(`SELECT se.*, d.name as driver_name, v.name as vip_name - FROM schedule_events se - LEFT JOIN drivers d ON se.driver_id = d.id - LEFT JOIN vips v ON se.vip_id = v.id - ORDER BY se.event_time ASC`); - // Group by VIP ID - const schedules = {}; - result.rows.forEach((row) => { - const event = this.toCamelCase(row); - if (!schedules[event.vipId]) { - schedules[event.vipId] = []; - } - schedules[event.vipId].push(event); - }); - return schedules; - } - // User Operations (simplified) - async getUserByEmail(email) { - const result = await this.pool.query('SELECT * FROM users WHERE email = $1', [email]); - return this.toCamelCase(result.rows[0]); - } - async getUserById(id) { - const result = await this.pool.query('SELECT * FROM users WHERE id = $1', [id]); - return this.toCamelCase(result.rows[0]); - } - async createUser(userData) { - const { email, name, role, department, googleId } = userData; - const result = await this.pool.query(`INSERT INTO users (email, name, role, department, google_id) - VALUES ($1, $2, $3, $4, $5) - RETURNING *`, [email, name, role || 'coordinator', department || 'Office of Development', googleId]); - return this.toCamelCase(result.rows[0]); - } - async updateUserRole(email, role) { - const result = await this.pool.query(`UPDATE users SET role = $2, updated_at = NOW() - WHERE email = $1 - RETURNING *`, [email, role]); - return this.toCamelCase(result.rows[0]); - } - async getUserCount() { - const result = await this.pool.query('SELECT COUNT(*) FROM users'); - return parseInt(result.rows[0].count, 10); - } - // Admin Settings (simplified) - async getAdminSettings() { - const result = await this.pool.query('SELECT key, value FROM admin_settings'); - return result.rows.reduce((settings, row) => { - settings[row.key] = row.value; - return settings; - }, {}); - } - async updateAdminSetting(key, value) { - await this.pool.query(`INSERT INTO admin_settings (key, value) - VALUES ($1, $2) - ON CONFLICT (key) DO UPDATE SET value = $2, updated_at = NOW()`, [key, value]); - } -} -exports.default = new UnifiedDataService(); -//# sourceMappingURL=unifiedDataService.js.map \ No newline at end of file diff --git a/backend-old-20260125/dist/services/unifiedDataService.js.map b/backend-old-20260125/dist/services/unifiedDataService.js.map deleted file mode 100644 index 708f307..0000000 --- a/backend-old-20260125/dist/services/unifiedDataService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"unifiedDataService.js","sourceRoot":"","sources":["../../src/services/unifiedDataService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/dist/types/api.d.ts b/backend-old-20260125/dist/types/api.d.ts deleted file mode 100644 index 7744d55..0000000 --- a/backend-old-20260125/dist/types/api.d.ts +++ /dev/null @@ -1,69 +0,0 @@ -export interface SuccessResponse { - success: true; - data: T; - message?: string; - timestamp: string; -} -export interface PaginatedResponse { - success: true; - data: T[]; - pagination: { - page: number; - limit: number; - total: number; - totalPages: number; - }; - timestamp: string; -} -export interface User { - id: string; - email: string; - name: string; - role: 'admin' | 'coordinator' | 'driver'; - department?: string; - createdAt: Date; - updatedAt: Date; -} -export interface VIP { - id: string; - name: string; - email?: string; - phone?: string; - arrivalMode: 'flight' | 'driving'; - flightNumber?: string; - arrivalTime?: Date; - departureTime?: Date; - notes?: string; - status: 'pending' | 'confirmed' | 'completed' | 'cancelled'; - createdAt: Date; - updatedAt: Date; -} -export interface Driver { - id: string; - name: string; - email?: string; - phone: string; - vehicleInfo?: string; - status: 'available' | 'assigned' | 'unavailable'; - createdAt: Date; - updatedAt: Date; -} -export interface ScheduleEvent { - id: string; - vipId: string; - driverId?: string; - eventType: 'pickup' | 'dropoff' | 'custom'; - eventTime: Date; - location: string; - notes?: string; - status: 'scheduled' | 'in_progress' | 'completed' | 'cancelled'; - createdAt: Date; - updatedAt: Date; -} -export interface AuthRequest extends Request { - user?: User; - requestId?: string; -} -export declare const successResponse: (data: T, message?: string) => SuccessResponse; -export declare const paginatedResponse: (data: T[], page: number, limit: number, total: number) => PaginatedResponse; -//# sourceMappingURL=api.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/types/api.d.ts.map b/backend-old-20260125/dist/types/api.d.ts.map deleted file mode 100644 index bbe9a98..0000000 --- a/backend-old-20260125/dist/types/api.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/types/api.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,GAAG;IACtC,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,EAAE,CAAC,CAAC;IACR,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC,GAAG,GAAG;IACxC,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,UAAU,EAAE;QACV,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;CACnB;AAGD,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,aAAa,GAAG,QAAQ,CAAC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAGD,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,QAAQ,GAAG,SAAS,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,aAAa,CAAC,EAAE,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;IAC5D,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAGD,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,WAAW,GAAG,UAAU,GAAG,aAAa,CAAC;IACjD,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAGD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC3C,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,GAAG,aAAa,GAAG,WAAW,GAAG,WAAW,CAAC;IAChE,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAGD,MAAM,WAAW,WAAY,SAAQ,OAAO;IAC1C,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAGD,eAAO,MAAM,eAAe,GAAI,CAAC,EAAE,MAAM,CAAC,EAAE,UAAU,MAAM,KAAG,eAAe,CAAC,CAAC,CAK9E,CAAC;AAEH,eAAO,MAAM,iBAAiB,GAAI,CAAC,EACjC,MAAM,CAAC,EAAE,EACT,MAAM,MAAM,EACZ,OAAO,MAAM,EACb,OAAO,MAAM,KACZ,iBAAiB,CAAC,CAAC,CAUpB,CAAC"} \ No newline at end of file diff --git a/backend-old-20260125/dist/types/api.js b/backend-old-20260125/dist/types/api.js deleted file mode 100644 index caf261e..0000000 --- a/backend-old-20260125/dist/types/api.js +++ /dev/null @@ -1,24 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.paginatedResponse = exports.successResponse = void 0; -// Response helper functions -const successResponse = (data, message) => ({ - success: true, - data, - message, - timestamp: new Date().toISOString() -}); -exports.successResponse = successResponse; -const paginatedResponse = (data, page, limit, total) => ({ - success: true, - data, - pagination: { - page, - limit, - total, - totalPages: Math.ceil(total / limit) - }, - timestamp: new Date().toISOString() -}); -exports.paginatedResponse = paginatedResponse; -//# sourceMappingURL=api.js.map \ No newline at end of file diff --git a/backend-old-20260125/dist/types/api.js.map b/backend-old-20260125/dist/types/api.js.map deleted file mode 100644 index af3be91..0000000 --- a/backend-old-20260125/dist/types/api.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/types/api.ts"],"names":[],"mappings":";;;AA8EA,4BAA4B;AACrB,MAAM,eAAe,GAAG,CAAI,IAAO,EAAE,OAAgB,EAAsB,EAAE,CAAC,CAAC;IACpF,OAAO,EAAE,IAAI;IACb,IAAI;IACJ,OAAO;IACP,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;CACpC,CAAC,CAAC;AALU,QAAA,eAAe,mBAKzB;AAEI,MAAM,iBAAiB,GAAG,CAC/B,IAAS,EACT,IAAY,EACZ,KAAa,EACb,KAAa,EACS,EAAE,CAAC,CAAC;IAC1B,OAAO,EAAE,IAAI;IACb,IAAI;IACJ,UAAU,EAAE;QACV,IAAI;QACJ,KAAK;QACL,KAAK;QACL,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;KACrC;IACD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;CACpC,CAAC,CAAC;AAfU,QAAA,iBAAiB,qBAe3B"} \ No newline at end of file diff --git a/backend-old-20260125/dist/types/errors.d.ts b/backend-old-20260125/dist/types/errors.d.ts deleted file mode 100644 index b5da53b..0000000 --- a/backend-old-20260125/dist/types/errors.d.ts +++ /dev/null @@ -1,34 +0,0 @@ -export declare class AppError extends Error { - readonly statusCode: number; - readonly isOperational: boolean; - constructor(message: string, statusCode: number, isOperational?: boolean); -} -export declare class ValidationError extends AppError { - constructor(message: string); -} -export declare class AuthenticationError extends AppError { - constructor(message?: string); -} -export declare class AuthorizationError extends AppError { - constructor(message?: string); -} -export declare class NotFoundError extends AppError { - constructor(message: string); -} -export declare class ConflictError extends AppError { - constructor(message: string); -} -export declare class DatabaseError extends AppError { - constructor(message?: string); -} -export interface ErrorResponse { - success: false; - error: { - message: string; - code?: string; - details?: any; - }; - timestamp: string; - path?: string; -} -//# sourceMappingURL=errors.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/types/errors.d.ts.map b/backend-old-20260125/dist/types/errors.d.ts.map deleted file mode 100644 index 7a59708..0000000 --- a/backend-old-20260125/dist/types/errors.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/types/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,QAAS,SAAQ,KAAK;IACjC,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,aAAa,EAAE,OAAO,CAAC;gBAE3B,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,UAAO;CAOtE;AAED,qBAAa,eAAgB,SAAQ,QAAQ;gBAC/B,OAAO,EAAE,MAAM;CAG5B;AAED,qBAAa,mBAAoB,SAAQ,QAAQ;gBACnC,OAAO,SAA0B;CAG9C;AAED,qBAAa,kBAAmB,SAAQ,QAAQ;gBAClC,OAAO,SAA6B;CAGjD;AAED,qBAAa,aAAc,SAAQ,QAAQ;gBAC7B,OAAO,EAAE,MAAM;CAG5B;AAED,qBAAa,aAAc,SAAQ,QAAQ;gBAC7B,OAAO,EAAE,MAAM;CAG5B;AAED,qBAAa,aAAc,SAAQ,QAAQ;gBAC7B,OAAO,SAA8B;CAGlD;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE;QACL,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,GAAG,CAAC;KACf,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf"} \ No newline at end of file diff --git a/backend-old-20260125/dist/types/errors.js b/backend-old-20260125/dist/types/errors.js deleted file mode 100644 index bfd58df..0000000 --- a/backend-old-20260125/dist/types/errors.js +++ /dev/null @@ -1,49 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DatabaseError = exports.ConflictError = exports.NotFoundError = exports.AuthorizationError = exports.AuthenticationError = exports.ValidationError = exports.AppError = void 0; -class AppError extends Error { - constructor(message, statusCode, isOperational = true) { - super(message); - this.statusCode = statusCode; - this.isOperational = isOperational; - Error.captureStackTrace(this, this.constructor); - } -} -exports.AppError = AppError; -class ValidationError extends AppError { - constructor(message) { - super(message, 400, true); - } -} -exports.ValidationError = ValidationError; -class AuthenticationError extends AppError { - constructor(message = 'Authentication failed') { - super(message, 401, true); - } -} -exports.AuthenticationError = AuthenticationError; -class AuthorizationError extends AppError { - constructor(message = 'Insufficient permissions') { - super(message, 403, true); - } -} -exports.AuthorizationError = AuthorizationError; -class NotFoundError extends AppError { - constructor(message) { - super(message, 404, true); - } -} -exports.NotFoundError = NotFoundError; -class ConflictError extends AppError { - constructor(message) { - super(message, 409, true); - } -} -exports.ConflictError = ConflictError; -class DatabaseError extends AppError { - constructor(message = 'Database operation failed') { - super(message, 500, false); - } -} -exports.DatabaseError = DatabaseError; -//# sourceMappingURL=errors.js.map \ No newline at end of file diff --git a/backend-old-20260125/dist/types/errors.js.map b/backend-old-20260125/dist/types/errors.js.map deleted file mode 100644 index 8e8ca7a..0000000 --- a/backend-old-20260125/dist/types/errors.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/types/errors.ts"],"names":[],"mappings":";;;AAAA,MAAa,QAAS,SAAQ,KAAK;IAIjC,YAAY,OAAe,EAAE,UAAkB,EAAE,aAAa,GAAG,IAAI;QACnE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QAEnC,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IAClD,CAAC;CACF;AAXD,4BAWC;AAED,MAAa,eAAgB,SAAQ,QAAQ;IAC3C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAC5B,CAAC;CACF;AAJD,0CAIC;AAED,MAAa,mBAAoB,SAAQ,QAAQ;IAC/C,YAAY,OAAO,GAAG,uBAAuB;QAC3C,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAC5B,CAAC;CACF;AAJD,kDAIC;AAED,MAAa,kBAAmB,SAAQ,QAAQ;IAC9C,YAAY,OAAO,GAAG,0BAA0B;QAC9C,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAC5B,CAAC;CACF;AAJD,gDAIC;AAED,MAAa,aAAc,SAAQ,QAAQ;IACzC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAC5B,CAAC;CACF;AAJD,sCAIC;AAED,MAAa,aAAc,SAAQ,QAAQ;IACzC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAC5B,CAAC;CACF;AAJD,sCAIC;AAED,MAAa,aAAc,SAAQ,QAAQ;IACzC,YAAY,OAAO,GAAG,2BAA2B;QAC/C,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IAC7B,CAAC;CACF;AAJD,sCAIC"} \ No newline at end of file diff --git a/backend-old-20260125/dist/types/schemas.d.ts b/backend-old-20260125/dist/types/schemas.d.ts deleted file mode 100644 index fb979d0..0000000 --- a/backend-old-20260125/dist/types/schemas.d.ts +++ /dev/null @@ -1,364 +0,0 @@ -import { z } from 'zod'; -export declare const vipFlightSchema: z.ZodObject<{ - flightNumber: z.ZodString; - airline: z.ZodOptional; - scheduledArrival: z.ZodUnion<[z.ZodString, z.ZodDate]>; - scheduledDeparture: z.ZodOptional>; - status: z.ZodOptional>; -}, "strip", z.ZodTypeAny, { - flightNumber: string; - scheduledArrival: string | Date; - status?: "scheduled" | "cancelled" | "delayed" | "arrived" | undefined; - airline?: string | undefined; - scheduledDeparture?: string | Date | undefined; -}, { - flightNumber: string; - scheduledArrival: string | Date; - status?: "scheduled" | "cancelled" | "delayed" | "arrived" | undefined; - airline?: string | undefined; - scheduledDeparture?: string | Date | undefined; -}>; -export declare const createVipSchema: z.ZodEffects; - department: z.ZodDefault>; - transportMode: z.ZodDefault>; - flights: z.ZodOptional; - scheduledArrival: z.ZodUnion<[z.ZodString, z.ZodDate]>; - scheduledDeparture: z.ZodOptional>; - status: z.ZodOptional>; - }, "strip", z.ZodTypeAny, { - flightNumber: string; - scheduledArrival: string | Date; - status?: "scheduled" | "cancelled" | "delayed" | "arrived" | undefined; - airline?: string | undefined; - scheduledDeparture?: string | Date | undefined; - }, { - flightNumber: string; - scheduledArrival: string | Date; - status?: "scheduled" | "cancelled" | "delayed" | "arrived" | undefined; - airline?: string | undefined; - scheduledDeparture?: string | Date | undefined; - }>, "many">>; - expectedArrival: z.ZodOptional>; - needsAirportPickup: z.ZodDefault; - needsVenueTransport: z.ZodDefault; - notes: z.ZodOptional; -}, "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 | Date; - status?: "scheduled" | "cancelled" | "delayed" | "arrived" | undefined; - airline?: string | undefined; - scheduledDeparture?: string | Date | undefined; - }[] | undefined; - expectedArrival?: string | Date | 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 | Date; - status?: "scheduled" | "cancelled" | "delayed" | "arrived" | undefined; - airline?: string | undefined; - scheduledDeparture?: string | Date | undefined; - }[] | undefined; - expectedArrival?: string | Date | undefined; - needsAirportPickup?: boolean | undefined; - needsVenueTransport?: boolean | undefined; - notes?: string | undefined; -}>, { - name: string; - department: "Office of Development" | "Admin"; - transportMode: "flight" | "self-driving"; - needsAirportPickup: boolean; - needsVenueTransport: boolean; - organization?: string | undefined; - flights?: { - flightNumber: string; - scheduledArrival: string | Date; - status?: "scheduled" | "cancelled" | "delayed" | "arrived" | undefined; - airline?: string | undefined; - scheduledDeparture?: string | Date | undefined; - }[] | undefined; - expectedArrival?: string | Date | 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 | Date; - status?: "scheduled" | "cancelled" | "delayed" | "arrived" | undefined; - airline?: string | undefined; - scheduledDeparture?: string | Date | undefined; - }[] | undefined; - expectedArrival?: string | Date | undefined; - needsAirportPickup?: boolean | undefined; - needsVenueTransport?: boolean | undefined; - notes?: string | undefined; -}>; -export declare const updateVipSchema: z.ZodObject<{ - name: z.ZodOptional; - organization: z.ZodOptional; - department: z.ZodOptional>; - transportMode: z.ZodOptional>; - flights: z.ZodOptional; - scheduledArrival: z.ZodUnion<[z.ZodString, z.ZodDate]>; - scheduledDeparture: z.ZodOptional>; - status: z.ZodOptional>; - }, "strip", z.ZodTypeAny, { - flightNumber: string; - scheduledArrival: string | Date; - status?: "scheduled" | "cancelled" | "delayed" | "arrived" | undefined; - airline?: string | undefined; - scheduledDeparture?: string | Date | undefined; - }, { - flightNumber: string; - scheduledArrival: string | Date; - status?: "scheduled" | "cancelled" | "delayed" | "arrived" | undefined; - airline?: string | undefined; - scheduledDeparture?: string | Date | undefined; - }>, "many">>; - expectedArrival: z.ZodOptional>; - needsAirportPickup: z.ZodOptional; - needsVenueTransport: z.ZodOptional; - notes: z.ZodOptional; -}, "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 | Date; - status?: "scheduled" | "cancelled" | "delayed" | "arrived" | undefined; - airline?: string | undefined; - scheduledDeparture?: string | Date | undefined; - }[] | undefined; - expectedArrival?: string | Date | 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 | Date; - status?: "scheduled" | "cancelled" | "delayed" | "arrived" | undefined; - airline?: string | undefined; - scheduledDeparture?: string | Date | undefined; - }[] | undefined; - expectedArrival?: string | Date | undefined; - needsAirportPickup?: boolean | undefined; - needsVenueTransport?: boolean | undefined; - notes?: string | undefined; -}>; -export declare const createDriverSchema: z.ZodObject<{ - name: z.ZodString; - email: z.ZodOptional; - phone: z.ZodString; - vehicleInfo: z.ZodOptional; - status: z.ZodDefault>; -}, "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; -}>; -export declare const updateDriverSchema: z.ZodObject<{ - name: z.ZodOptional; - email: z.ZodOptional>; - phone: z.ZodOptional; - vehicleInfo: z.ZodOptional>; - status: z.ZodOptional>>; -}, "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; -}>; -export declare const createScheduleEventSchema: z.ZodObject<{ - vipId: z.ZodString; - driverId: z.ZodOptional; - eventType: z.ZodEnum<["pickup", "dropoff", "custom"]>; - eventTime: z.ZodUnion<[z.ZodString, z.ZodDate]>; - location: z.ZodString; - notes: z.ZodOptional; - status: z.ZodDefault>; -}, "strip", z.ZodTypeAny, { - status: "scheduled" | "in_progress" | "completed" | "cancelled"; - eventTime: string | Date; - eventType: "custom" | "pickup" | "dropoff"; - location: string; - vipId: string; - notes?: string | undefined; - driverId?: string | undefined; -}, { - eventTime: string | Date; - eventType: "custom" | "pickup" | "dropoff"; - location: string; - vipId: string; - notes?: string | undefined; - status?: "scheduled" | "in_progress" | "completed" | "cancelled" | undefined; - driverId?: string | undefined; -}>; -export declare const updateScheduleEventSchema: z.ZodObject<{ - vipId: z.ZodOptional; - driverId: z.ZodOptional>; - eventType: z.ZodOptional>; - eventTime: z.ZodOptional>; - location: z.ZodOptional; - notes: z.ZodOptional>; - status: z.ZodOptional>>; -}, "strip", z.ZodTypeAny, { - notes?: string | undefined; - status?: "scheduled" | "in_progress" | "completed" | "cancelled" | undefined; - driverId?: string | undefined; - eventTime?: string | Date | undefined; - eventType?: "custom" | "pickup" | "dropoff" | undefined; - location?: string | undefined; - vipId?: string | undefined; -}, { - notes?: string | undefined; - status?: "scheduled" | "in_progress" | "completed" | "cancelled" | undefined; - driverId?: string | undefined; - eventTime?: string | Date | undefined; - eventType?: "custom" | "pickup" | "dropoff" | undefined; - location?: string | undefined; - vipId?: string | undefined; -}>; -export declare const createUserSchema: z.ZodObject<{ - email: z.ZodString; - name: z.ZodString; - role: z.ZodEnum<["admin", "coordinator", "driver"]>; - department: z.ZodOptional; - password: z.ZodOptional; -}, "strip", z.ZodTypeAny, { - name: string; - email: string; - role: "coordinator" | "admin" | "driver"; - department?: string | undefined; - password?: string | undefined; -}, { - name: string; - email: string; - role: "coordinator" | "admin" | "driver"; - department?: string | undefined; - password?: string | undefined; -}>; -export declare const updateUserSchema: z.ZodObject<{ - email: z.ZodOptional; - name: z.ZodOptional; - role: z.ZodOptional>; - department: z.ZodOptional>; - password: z.ZodOptional>; -}, "strip", z.ZodTypeAny, { - name?: string | undefined; - department?: string | undefined; - email?: string | undefined; - role?: "coordinator" | "admin" | "driver" | undefined; - password?: string | undefined; -}, { - name?: string | undefined; - department?: string | undefined; - email?: string | undefined; - role?: "coordinator" | "admin" | "driver" | undefined; - password?: string | undefined; -}>; -export declare const updateAdminSettingsSchema: z.ZodObject<{ - key: z.ZodString; - value: z.ZodString; - description: z.ZodOptional; -}, "strip", z.ZodTypeAny, { - value: string; - key: string; - description?: string | undefined; -}, { - value: string; - key: string; - description?: string | undefined; -}>; -export declare const loginSchema: z.ZodObject<{ - email: z.ZodString; - password: z.ZodString; -}, "strip", z.ZodTypeAny, { - email: string; - password: string; -}, { - email: string; - password: string; -}>; -export declare const googleAuthCallbackSchema: z.ZodObject<{ - code: z.ZodString; -}, "strip", z.ZodTypeAny, { - code: string; -}, { - code: string; -}>; -export declare const paginationSchema: z.ZodObject<{ - page: z.ZodDefault>; - limit: z.ZodDefault>; - sortBy: z.ZodOptional; - sortOrder: z.ZodDefault>; -}, "strip", z.ZodTypeAny, { - page: number; - limit: number; - sortOrder: "asc" | "desc"; - sortBy?: string | undefined; -}, { - page?: string | undefined; - limit?: string | undefined; - sortBy?: string | undefined; - sortOrder?: "asc" | "desc" | undefined; -}>; -export declare const dateRangeSchema: z.ZodObject<{ - startDate: z.ZodOptional; - endDate: z.ZodOptional; -}, "strip", z.ZodTypeAny, { - startDate?: string | undefined; - endDate?: string | undefined; -}, { - startDate?: string | undefined; - endDate?: string | undefined; -}>; -export declare const idParamSchema: z.ZodObject<{ - id: z.ZodString; -}, "strip", z.ZodTypeAny, { - id: string; -}, { - id: string; -}>; -//# sourceMappingURL=schemas.d.ts.map \ No newline at end of file diff --git a/backend-old-20260125/dist/types/schemas.d.ts.map b/backend-old-20260125/dist/types/schemas.d.ts.map deleted file mode 100644 index b585c0e..0000000 --- a/backend-old-20260125/dist/types/schemas.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/types/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;EAM1B,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuB3B,CAAC;AAEF,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAU1B,CAAC;AAGH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;EAM7B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;EAA+B,CAAC;AAG/D,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;EAQpC,CAAC;AAEH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;EAAsC,CAAC;AAG7E,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;EAM3B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;EAA6B,CAAC;AAG3D,eAAO,MAAM,yBAAyB;;;;;;;;;;;;EAIpC,CAAC;AAGH,eAAO,MAAM,WAAW;;;;;;;;;EAGtB,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;EAEnC,CAAC;AAGH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;EAK3B,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;;;EAG1B,CAAC;AAGH,eAAO,MAAM,aAAa;;;;;;EAExB,CAAC"} \ No newline at end of file diff --git a/backend-old-20260125/dist/types/schemas.js b/backend-old-20260125/dist/types/schemas.js deleted file mode 100644 index bf5c2d9..0000000 --- a/backend-old-20260125/dist/types/schemas.js +++ /dev/null @@ -1,107 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.idParamSchema = exports.dateRangeSchema = exports.paginationSchema = exports.googleAuthCallbackSchema = exports.loginSchema = exports.updateAdminSettingsSchema = exports.updateUserSchema = exports.createUserSchema = exports.updateScheduleEventSchema = exports.createScheduleEventSchema = exports.updateDriverSchema = exports.createDriverSchema = exports.updateVipSchema = exports.createVipSchema = exports.vipFlightSchema = void 0; -const zod_1 = require("zod"); -// Common schemas -const phoneRegex = /^[\d\s\-\+\(\)]+$/; -const emailSchema = zod_1.z.string().email().optional(); -const phoneSchema = zod_1.z.string().regex(phoneRegex, 'Invalid phone number format').optional(); -// VIP schemas -exports.vipFlightSchema = zod_1.z.object({ - flightNumber: zod_1.z.string().min(1, 'Flight number is required'), - airline: zod_1.z.string().optional(), - scheduledArrival: zod_1.z.string().datetime().or(zod_1.z.date()), - scheduledDeparture: zod_1.z.string().datetime().or(zod_1.z.date()).optional(), - status: zod_1.z.enum(['scheduled', 'delayed', 'cancelled', 'arrived']).optional() -}); -exports.createVipSchema = zod_1.z.object({ - name: zod_1.z.string().min(1, 'Name is required').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(exports.vipFlightSchema).optional(), - expectedArrival: zod_1.z.string().datetime().or(zod_1.z.date()).optional(), - needsAirportPickup: zod_1.z.boolean().default(true), - needsVenueTransport: zod_1.z.boolean().default(true), - notes: zod_1.z.string().max(500).optional() -}).refine((data) => { - if (data.transportMode === 'flight' && (!data.flights || data.flights.length === 0)) { - return false; - } - if (data.transportMode === 'self-driving' && !data.expectedArrival) { - return false; - } - return true; -}, { - message: 'Flight mode requires at least one flight, self-driving requires expected arrival' -}); -exports.updateVipSchema = zod_1.z.object({ - name: zod_1.z.string().min(1, 'Name is required').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(exports.vipFlightSchema).optional(), - expectedArrival: zod_1.z.string().datetime().or(zod_1.z.date()).optional(), - needsAirportPickup: zod_1.z.boolean().optional(), - needsVenueTransport: zod_1.z.boolean().optional(), - notes: zod_1.z.string().max(500).optional() -}); -// Driver schemas -exports.createDriverSchema = zod_1.z.object({ - name: zod_1.z.string().min(1, 'Name is required').max(100), - email: emailSchema, - phone: zod_1.z.string().regex(phoneRegex, 'Invalid phone number format'), - vehicleInfo: zod_1.z.string().max(200).optional(), - status: zod_1.z.enum(['available', 'assigned', 'unavailable']).default('available') -}); -exports.updateDriverSchema = exports.createDriverSchema.partial(); -// Schedule Event schemas -exports.createScheduleEventSchema = zod_1.z.object({ - vipId: zod_1.z.string().uuid('Invalid VIP ID'), - driverId: zod_1.z.string().uuid('Invalid driver ID').optional(), - eventType: zod_1.z.enum(['pickup', 'dropoff', 'custom']), - eventTime: zod_1.z.string().datetime().or(zod_1.z.date()), - location: zod_1.z.string().min(1, 'Location is required').max(200), - notes: zod_1.z.string().max(500).optional(), - status: zod_1.z.enum(['scheduled', 'in_progress', 'completed', 'cancelled']).default('scheduled') -}); -exports.updateScheduleEventSchema = exports.createScheduleEventSchema.partial(); -// User schemas -exports.createUserSchema = zod_1.z.object({ - email: zod_1.z.string().email('Invalid email address'), - name: zod_1.z.string().min(1, 'Name is required').max(100), - role: zod_1.z.enum(['admin', 'coordinator', 'driver']), - department: zod_1.z.string().max(100).optional(), - password: zod_1.z.string().min(8, 'Password must be at least 8 characters').optional() -}); -exports.updateUserSchema = exports.createUserSchema.partial(); -// Admin settings schemas -exports.updateAdminSettingsSchema = zod_1.z.object({ - key: zod_1.z.string().min(1, 'Key is required'), - value: zod_1.z.string(), - description: zod_1.z.string().optional() -}); -// Auth schemas -exports.loginSchema = zod_1.z.object({ - email: zod_1.z.string().email('Invalid email address'), - password: zod_1.z.string().min(1, 'Password is required') -}); -exports.googleAuthCallbackSchema = zod_1.z.object({ - code: zod_1.z.string().min(1, 'Authorization code is required') -}); -// Query parameter schemas -exports.paginationSchema = zod_1.z.object({ - page: zod_1.z.string().regex(/^\d+$/).transform(Number).default('1'), - limit: zod_1.z.string().regex(/^\d+$/).transform(Number).default('20'), - sortBy: zod_1.z.string().optional(), - sortOrder: zod_1.z.enum(['asc', 'desc']).default('asc') -}); -exports.dateRangeSchema = zod_1.z.object({ - startDate: zod_1.z.string().datetime().optional(), - endDate: zod_1.z.string().datetime().optional() -}); -// Route parameter schemas -exports.idParamSchema = zod_1.z.object({ - id: zod_1.z.string().min(1, 'ID is required') -}); -//# sourceMappingURL=schemas.js.map \ No newline at end of file diff --git a/backend-old-20260125/dist/types/schemas.js.map b/backend-old-20260125/dist/types/schemas.js.map deleted file mode 100644 index c6316ea..0000000 --- a/backend-old-20260125/dist/types/schemas.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../src/types/schemas.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend-old-20260125/jest.config.js b/backend-old-20260125/jest.config.js deleted file mode 100644 index d17686a..0000000 --- a/backend-old-20260125/jest.config.js +++ /dev/null @@ -1,23 +0,0 @@ -module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', - roots: ['/src'], - testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'], - transform: { - '^.+\\.ts$': 'ts-jest', - }, - collectCoverageFrom: [ - 'src/**/*.ts', - '!src/**/*.d.ts', - '!src/**/*.test.ts', - '!src/**/*.spec.ts', - '!src/types/**', - ], - coverageDirectory: 'coverage', - coverageReporters: ['text', 'lcov', 'html'], - setupFilesAfterEnv: ['/src/tests/setup.ts'], - testTimeout: 30000, - moduleNameMapper: { - '^@/(.*)$': '/src/$1', - }, -}; \ No newline at end of file diff --git a/backend-old-20260125/package-lock.json b/backend-old-20260125/package-lock.json deleted file mode 100644 index d6048ff..0000000 --- a/backend-old-20260125/package-lock.json +++ /dev/null @@ -1,4662 +0,0 @@ -{ - "name": "vip-coordinator-backend", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "vip-coordinator-backend", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "auth0": "^5.2.0", - "cors": "^2.8.5", - "dotenv": "^16.3.1", - "express": "^4.18.2", - "express-jwt": "^8.5.1", - "express-openid-connect": "^2.19.4", - "jsonwebtoken": "^9.0.2", - "jwks-rsa": "^3.2.2", - "pg": "^8.11.3", - "redis": "^4.6.8", - "uuid": "^9.0.0" - }, - "devDependencies": { - "@types/cors": "^2.8.13", - "@types/express": "^4.17.17", - "@types/jsonwebtoken": "^9.0.2", - "@types/node": "^20.5.0", - "@types/pg": "^8.10.2", - "@types/uuid": "^9.0.2", - "ts-node": "^10.9.1", - "ts-node-dev": "^2.0.0", - "tsx": "^4.7.0", - "typescript": "^5.6.0" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", - "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", - "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", - "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", - "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", - "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", - "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", - "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", - "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", - "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", - "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", - "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", - "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", - "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", - "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", - "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", - "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", - "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", - "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", - "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", - "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", - "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", - "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", - "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", - "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", - "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", - "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "license": "BSD-3-Clause", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@panva/asn1.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz", - "integrity": "sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==", - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/@redis/bloom": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", - "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", - "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/client": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz", - "integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==", - "license": "MIT", - "peer": true, - "dependencies": { - "cluster-key-slot": "1.1.2", - "generic-pool": "3.9.0", - "yallist": "4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@redis/graph": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", - "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", - "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/json": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz", - "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", - "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/search": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz", - "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", - "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/time-series": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz", - "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", - "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@sideway/address": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", - "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", - "license": "BSD-3-Clause", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", - "license": "BSD-3-Clause" - }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "license": "MIT", - "dependencies": { - "defer-to-connect": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", - "license": "MIT", - "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/cors": { - "version": "2.8.19", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", - "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", - "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", - "license": "MIT" - }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/jsonwebtoken": { - "version": "9.0.10", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", - "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", - "license": "MIT", - "dependencies": { - "@types/ms": "*", - "@types/node": "*" - } - }, - "node_modules/@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "20.19.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz", - "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==", - "license": "MIT", - "peer": true, - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/pg": { - "version": "8.15.4", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.4.tgz", - "integrity": "sha512-I6UNVBAoYbvuWkkU3oosC8yxqH21f4/Jc4DK71JLG3dT2mdlGe1z+ep/LQGXaKaOgcvUrsQoPRqfgtMcvZiJhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "pg-protocol": "*", - "pg-types": "^2.2.0" - } - }, - "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/responselike": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", - "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/send": { - "version": "0.17.5", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", - "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", - "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "node_modules/@types/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/strip-json-comments": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", - "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", - "dev": true, - "license": "MIT" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "license": "MIT" - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", - "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/array.prototype.reduce": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.8.tgz", - "integrity": "sha512-DwuEqgXFBwbmZSRqt3BpQigWNUoqw9Ml2dTWdF3B2zQlQX4OeUE0zyuzX0fX0IbTvjdkZbcBTU3idgpO78qkTw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-array-method-boxes-properly": "^1.0.0", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "is-string": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/async-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", - "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/auth0": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/auth0/-/auth0-5.2.0.tgz", - "integrity": "sha512-eRtzyldEhXC1g9quWI8daG/j2OcXFsjgkT+zi3gqpe3SOwVNkWyvzaDAHAYWD2Z1ARTNXxezoXJiWNxwfHRMhw==", - "license": "MIT", - "dependencies": { - "auth0-legacy": "npm:auth0@^4.27.0", - "jose": "^4.13.2", - "uuid": "^11.1.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || ^24.0.0" - } - }, - "node_modules/auth0-legacy": { - "name": "auth0", - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/auth0/-/auth0-4.37.0.tgz", - "integrity": "sha512-+TqJRxh4QvbD4TQIYx1ak2vanykQkG/nIZLuR6o8LoQj425gjVG3tFuUbbOeh/nCpP1rnvU0CCV1ChZHYXLU/A==", - "license": "MIT", - "dependencies": { - "jose": "^4.13.2", - "undici-types": "^6.15.0", - "uuid": "^9.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/auth0/node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/base64url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", - "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "license": "MIT", - "engines": { - "node": ">=10.6.0" - } - }, - "node_modules/cacheable-request": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", - "license": "MIT", - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "license": "MIT", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT" - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/data-view-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", - "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", - "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/inspect-js" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", - "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/dynamic-dedupe": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", - "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "xtend": "^4.0.0" - } - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/es-abstract": { - "version": "1.24.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", - "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.3.0", - "get-proto": "^1.0.1", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.2.1", - "is-set": "^2.0.3", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.1", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.4", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.4", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "stop-iteration-iterator": "^1.1.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.19" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "license": "MIT" - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/esbuild": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", - "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.8", - "@esbuild/android-arm": "0.25.8", - "@esbuild/android-arm64": "0.25.8", - "@esbuild/android-x64": "0.25.8", - "@esbuild/darwin-arm64": "0.25.8", - "@esbuild/darwin-x64": "0.25.8", - "@esbuild/freebsd-arm64": "0.25.8", - "@esbuild/freebsd-x64": "0.25.8", - "@esbuild/linux-arm": "0.25.8", - "@esbuild/linux-arm64": "0.25.8", - "@esbuild/linux-ia32": "0.25.8", - "@esbuild/linux-loong64": "0.25.8", - "@esbuild/linux-mips64el": "0.25.8", - "@esbuild/linux-ppc64": "0.25.8", - "@esbuild/linux-riscv64": "0.25.8", - "@esbuild/linux-s390x": "0.25.8", - "@esbuild/linux-x64": "0.25.8", - "@esbuild/netbsd-arm64": "0.25.8", - "@esbuild/netbsd-x64": "0.25.8", - "@esbuild/openbsd-arm64": "0.25.8", - "@esbuild/openbsd-x64": "0.25.8", - "@esbuild/openharmony-arm64": "0.25.8", - "@esbuild/sunos-x64": "0.25.8", - "@esbuild/win32-arm64": "0.25.8", - "@esbuild/win32-ia32": "0.25.8", - "@esbuild/win32-x64": "0.25.8" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "license": "MIT", - "peer": true, - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express-jwt": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/express-jwt/-/express-jwt-8.5.1.tgz", - "integrity": "sha512-Dv6QjDLpR2jmdb8M6XQXiCcpEom7mK8TOqnr0/TngDKsG2DHVkO8+XnVxkJVN7BuS1I3OrGw6N8j5DaaGgkDRQ==", - "license": "MIT", - "dependencies": { - "@types/jsonwebtoken": "^9", - "express-unless": "^2.1.3", - "jsonwebtoken": "^9.0.0" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/express-openid-connect": { - "version": "2.19.4", - "resolved": "https://registry.npmjs.org/express-openid-connect/-/express-openid-connect-2.19.4.tgz", - "integrity": "sha512-3YFPZ4MgUPhwfHbCaJKEij7uTc0vF4KpGKsuc3D1IhNMooiP6w8p1HBaaDQOE2KaAas22UghxVECxPpcC/gfOA==", - "license": "MIT", - "dependencies": { - "base64url": "^3.0.1", - "clone": "^2.1.2", - "cookie": "^0.7.2", - "debug": "^4.4.1", - "futoin-hkdf": "^1.5.3", - "http-errors": "^1.8.1", - "joi": "^17.13.3", - "jose": "^2.0.7", - "on-headers": "^1.1.0", - "openid-client": "^4.9.1", - "url-join": "^4.0.1", - "util-promisify": "3.0.0" - }, - "engines": { - "node": "^10.19.0 || >=12.0.0 < 13 || >=13.7.0 < 14 || >= 14.2.0" - }, - "peerDependencies": { - "express": ">= 4.17.0" - } - }, - "node_modules/express-openid-connect/node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express-openid-connect/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/express-openid-connect/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express-openid-connect/node_modules/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "license": "MIT", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express-openid-connect/node_modules/jose": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.7.tgz", - "integrity": "sha512-5hFWIigKqC+e/lRyQhfnirrAqUdIPMB7SJRqflJaO29dW7q5DFvH1XCSTmv6PQ6pb++0k6MJlLRoS0Wv4s38Wg==", - "license": "MIT", - "dependencies": { - "@panva/asn1.js": "^1.0.0" - }, - "engines": { - "node": ">=10.13.0 < 13 || >=13.7.0" - }, - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/express-openid-connect/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/express-openid-connect/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express-unless": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/express-unless/-/express-unless-2.1.3.tgz", - "integrity": "sha512-wj4tLMyCVYuIIKHGt0FhCtIViBcwzWejX0EjNxveAa6dG+0XBCQhMbx+PnkLkFCxLC69qoFrxds4pIyL88inaQ==", - "license": "MIT" - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/futoin-hkdf": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/futoin-hkdf/-/futoin-hkdf-1.5.3.tgz", - "integrity": "sha512-SewY5KdMpaoCeh7jachEWFsh1nNlaDjNHZXWqL5IGwtpEYHTgkr2+AMCgNwKWkcc0wpSYrZfR7he4WdmHFtDxQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/generator-function": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", - "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/generic-pool": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", - "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-symbol-description": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-tsconfig": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", - "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "license": "MIT", - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/got": { - "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=10.19.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/has-bigints": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "license": "BSD-2-Clause" - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "license": "MIT", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-async-function": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", - "license": "MIT", - "dependencies": { - "async-function": "^1.0.0", - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-view": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-generator-function": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", - "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.4", - "generator-function": "^2.0.0", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", - "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "license": "MIT" - }, - "node_modules/joi": { - "version": "17.13.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", - "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", - "license": "BSD-3-Clause", - "dependencies": { - "@hapi/hoek": "^9.3.0", - "@hapi/topo": "^5.1.0", - "@sideway/address": "^4.1.5", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - } - }, - "node_modules/jose": { - "version": "4.15.9", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", - "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "license": "MIT" - }, - "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "license": "MIT", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jsonwebtoken/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/jwa": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", - "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jwks-rsa": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.2.2.tgz", - "integrity": "sha512-BqTyEDV+lS8F2trk3A+qJnxV5Q9EqKCBJOPti3W97r7qTympCZjb7h2X6f2kc+0K3rsSTY1/6YG2eaXKoj497w==", - "license": "MIT", - "dependencies": { - "@types/jsonwebtoken": "^9.0.4", - "debug": "^4.3.4", - "jose": "^4.15.4", - "limiter": "^1.1.5", - "lru-memoizer": "^2.2.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/jwks-rsa/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/jwks-rsa/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "license": "MIT", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/limiter": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", - "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" - }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", - "license": "MIT" - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "license": "MIT" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "license": "MIT" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "license": "MIT" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "license": "MIT" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "license": "MIT" - }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "license": "MIT" - }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/lru-memoizer": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", - "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==", - "license": "MIT", - "dependencies": { - "lodash.clonedeep": "^4.5.0", - "lru-cache": "6.0.0" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "license": "ISC" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.getownpropertydescriptors": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.9.tgz", - "integrity": "sha512-mt8YM6XwsTTovI+kdZdHSxoyF2DI59up034orlC9NfweclcWOt7CVascNNLp6U+bjFVCVCIh9PwS76tDM/rH8g==", - "license": "MIT", - "dependencies": { - "array.prototype.reduce": "^1.0.8", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.0", - "es-object-atoms": "^1.1.1", - "gopd": "^1.2.0", - "safe-array-concat": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/oidc-token-hash": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.2.0.tgz", - "integrity": "sha512-6gj2m8cJZ+iSW8bm0FXdGF0YhIQbKrfP4yWTNzxc31U6MOjfEmB1rHvlYvxI1B7t7BCi1F2vYTT6YhtQRG4hxw==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || >=12.0.0" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", - "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/openid-client": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-4.9.1.tgz", - "integrity": "sha512-DYUF07AHjI3QDKqKbn2F7RqozT4hyi4JvmpodLrq0HHoNP7t/AjeG/uqiBK1/N2PZSAQEThVjDLHSmJN4iqu/w==", - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.1.0", - "got": "^11.8.0", - "jose": "^2.0.5", - "lru-cache": "^6.0.0", - "make-error": "^1.3.6", - "object-hash": "^2.0.1", - "oidc-token-hash": "^5.0.1" - }, - "engines": { - "node": "^10.19.0 || >=12.0.0 < 13 || >=13.7.0 < 14 || >= 14.2.0" - }, - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/openid-client/node_modules/jose": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.7.tgz", - "integrity": "sha512-5hFWIigKqC+e/lRyQhfnirrAqUdIPMB7SJRqflJaO29dW7q5DFvH1XCSTmv6PQ6pb++0k6MJlLRoS0Wv4s38Wg==", - "license": "MIT", - "dependencies": { - "@panva/asn1.js": "^1.0.0" - }, - "engines": { - "node": ">=10.13.0 < 13 || >=13.7.0" - }, - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/own-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", - "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.6", - "object-keys": "^1.1.1", - "safe-push-apply": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" - }, - "node_modules/pg": { - "version": "8.16.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", - "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", - "license": "MIT", - "peer": true, - "dependencies": { - "pg-connection-string": "^2.9.1", - "pg-pool": "^3.10.1", - "pg-protocol": "^1.10.3", - "pg-types": "2.2.0", - "pgpass": "1.0.5" - }, - "engines": { - "node": ">= 16.0.0" - }, - "optionalDependencies": { - "pg-cloudflare": "^1.2.7" - }, - "peerDependencies": { - "pg-native": ">=3.0.1" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } - } - }, - "node_modules/pg-cloudflare": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", - "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", - "license": "MIT", - "optional": true - }, - "node_modules/pg-connection-string": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", - "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", - "license": "MIT" - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "license": "ISC", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-pool": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", - "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", - "license": "MIT", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", - "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", - "license": "MIT" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "license": "MIT", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "license": "MIT", - "dependencies": { - "split2": "^4.1.0" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "license": "MIT", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/redis": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.1.tgz", - "integrity": "sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==", - "license": "MIT", - "workspaces": [ - "./packages/*" - ], - "dependencies": { - "@redis/bloom": "1.2.0", - "@redis/client": "1.6.1", - "@redis/graph": "1.1.1", - "@redis/json": "1.0.7", - "@redis/search": "1.2.0", - "@redis/time-series": "1.1.0" - } - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", - "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.1", - "which-builtin-type": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "license": "MIT" - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", - "license": "MIT", - "dependencies": { - "lowercase-keys": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/safe-array-concat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safe-push-apply": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", - "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", - "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "license": "ISC", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stop-iteration-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", - "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "internal-slot": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-object-atoms": "^1.0.0", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "license": "MIT", - "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/ts-node-dev": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-2.0.0.tgz", - "integrity": "sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": "^3.5.1", - "dynamic-dedupe": "^0.3.0", - "minimist": "^1.2.6", - "mkdirp": "^1.0.4", - "resolve": "^1.0.0", - "rimraf": "^2.6.1", - "source-map-support": "^0.5.12", - "tree-kill": "^1.2.2", - "ts-node": "^10.4.0", - "tsconfig": "^7.0.0" - }, - "bin": { - "ts-node-dev": "lib/bin.js", - "tsnd": "lib/bin.js" - }, - "engines": { - "node": ">=0.8.0" - }, - "peerDependencies": { - "node-notifier": "*", - "typescript": "*" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/tsconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", - "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/strip-bom": "^3.0.0", - "@types/strip-json-comments": "0.0.30", - "strip-bom": "^3.0.0", - "strip-json-comments": "^2.0.0" - } - }, - "node_modules/tsx": { - "version": "4.20.3", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz", - "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.25.0", - "get-tsconfig": "^4.7.5" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", - "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", - "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.15", - "reflect.getprototypeof": "^1.0.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/unbox-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", - "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-bigints": "^1.0.2", - "has-symbols": "^1.1.0", - "which-boxed-primitive": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "license": "MIT" - }, - "node_modules/util-promisify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-3.0.0.tgz", - "integrity": "sha512-uWRZJMjSWt/A1J1exfqz7xiKx2kVpAHR5qIDr6WwwBMQHDoKbo2I1kQN62iA2uXHxOSVpZRDvbm8do+4ijfkNA==", - "license": "MIT", - "dependencies": { - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "license": "MIT" - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", - "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", - "license": "MIT", - "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.1", - "is-number-object": "^1.1.1", - "is-string": "^1.1.1", - "is-symbol": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", - "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.1.0", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.2.1", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.1.0", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "license": "MIT", - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.20", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", - "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - } - } -} diff --git a/backend-old-20260125/package.json b/backend-old-20260125/package.json deleted file mode 100644 index d84d69c..0000000 --- a/backend-old-20260125/package.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "name": "vip-coordinator-backend", - "version": "1.0.0", - "description": "Backend API for VIP Coordinator Dashboard", - "main": "dist/index.js", - "scripts": { - "start": "node dist/index.js", - "dev": "npx tsx src/index.ts", - "build": "tsc", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [ - "vip", - "coordinator", - "dashboard", - "api" - ], - "author": "", - "license": "ISC", - "dependencies": { - "auth0": "^5.2.0", - "cors": "^2.8.5", - "dotenv": "^16.3.1", - "express": "^4.18.2", - "express-jwt": "^8.5.1", - "express-openid-connect": "^2.19.4", - "jsonwebtoken": "^9.0.2", - "jwks-rsa": "^3.2.2", - "pg": "^8.11.3", - "redis": "^4.6.8", - "uuid": "^9.0.0" - }, - "devDependencies": { - "@types/cors": "^2.8.13", - "@types/express": "^4.17.17", - "@types/jsonwebtoken": "^9.0.2", - "@types/node": "^20.5.0", - "@types/pg": "^8.10.2", - "@types/uuid": "^9.0.2", - "ts-node": "^10.9.1", - "ts-node-dev": "^2.0.0", - "tsx": "^4.7.0", - "typescript": "^5.6.0" - } -} diff --git a/backend-old-20260125/public/api-docs.html b/backend-old-20260125/public/api-docs.html deleted file mode 100644 index 6d8a46a..0000000 --- a/backend-old-20260125/public/api-docs.html +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - VIP Coordinator API Documentation - - - - -
-

šŸš— VIP Coordinator API

-

Comprehensive API for managing VIP transportation coordination

-
- - - -
- - - - - - diff --git a/backend-old-20260125/public/api-documentation.yaml b/backend-old-20260125/public/api-documentation.yaml deleted file mode 100644 index a6464a9..0000000 --- a/backend-old-20260125/public/api-documentation.yaml +++ /dev/null @@ -1,1189 +0,0 @@ -openapi: 3.0.3 -info: - title: VIP Coordinator API - description: | - A comprehensive API for managing VIP transportation coordination, including flight tracking, - driver management, and event scheduling for high-profile guests. - - ## Features - - VIP management with flight and self-driving transport modes - - Real-time flight tracking and validation - - Driver assignment and conflict detection - - Event scheduling with validation - - Admin settings management - - ## Authentication - Most endpoints are public for demo purposes. Admin endpoints require authentication. - version: 1.0.0 - contact: - name: VIP Coordinator Support - email: support@vipcoordinator.com - license: - name: MIT - url: https://opensource.org/licenses/MIT - -servers: - - url: http://localhost:3000/api - description: Development server - - url: https://api.vipcoordinator.com/api - description: Production server - -tags: - - name: Health - description: System health checks - - name: VIPs - description: VIP management operations - - name: Drivers - description: Driver management operations - - name: Flights - description: Flight tracking and information - - name: Schedule - description: Event and meeting scheduling - - name: Admin - description: Administrative operations - -paths: - /health: - get: - tags: - - Health - summary: Health check endpoint - description: Returns the current status of the API server - responses: - '200': - description: Server is healthy - content: - application/json: - schema: - type: object - properties: - status: - type: string - example: "OK" - timestamp: - type: string - format: date-time - example: "2025-06-01T12:00:00.000Z" - - /vips: - get: - tags: - - VIPs - summary: Get all VIPs - description: Retrieve a list of all VIPs in the system - responses: - '200': - description: List of VIPs - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/VIP' - - post: - tags: - - VIPs - summary: Create a new VIP - description: Add a new VIP to the system - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/VIPCreate' - examples: - flight_vip: - summary: VIP with flight transport - value: - name: "John Doe" - organization: "Tech Corp" - transportMode: "flight" - flights: - - flightNumber: "UA1234" - flightDate: "2025-06-26" - segment: 1 - needsAirportPickup: true - needsVenueTransport: true - notes: "CEO - requires executive transport" - self_driving: - summary: Self-driving VIP - value: - name: "Jane Smith" - organization: "Local Business" - transportMode: "self-driving" - expectedArrival: "2025-06-26T14:00:00" - needsAirportPickup: false - needsVenueTransport: true - notes: "Driving from Colorado Springs" - responses: - '201': - description: VIP created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/VIP' - '400': - description: Invalid input data - - /vips/{id}: - put: - tags: - - VIPs - summary: Update a VIP - description: Update an existing VIP's information - parameters: - - name: id - in: path - required: true - schema: - type: string - description: VIP ID - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/VIPCreate' - responses: - '200': - description: VIP updated successfully - content: - application/json: - schema: - $ref: '#/components/schemas/VIP' - '404': - description: VIP not found - - delete: - tags: - - VIPs - summary: Delete a VIP - description: Remove a VIP from the system - parameters: - - name: id - in: path - required: true - schema: - type: string - description: VIP ID - responses: - '200': - description: VIP deleted successfully - content: - application/json: - schema: - type: object - properties: - message: - type: string - example: "VIP deleted successfully" - vip: - $ref: '#/components/schemas/VIP' - '404': - description: VIP not found - - /vips/{vipId}/schedule: - get: - tags: - - Schedule - summary: Get VIP's schedule - description: Retrieve all scheduled events for a specific VIP - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - responses: - '200': - description: VIP's schedule - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ScheduleEvent' - - post: - tags: - - Schedule - summary: Add event to VIP's schedule - description: Create a new event for a VIP with validation - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ScheduleEventCreate' - example: - title: "Meeting with CEO" - location: "Hyatt Regency Denver" - startTime: "2025-06-26T11:00:00" - endTime: "2025-06-26T12:30:00" - type: "meeting" - assignedDriverId: "1748780965562" - description: "Important strategic meeting" - responses: - '201': - description: Event created successfully - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/ScheduleEvent' - - type: object - properties: - warnings: - type: array - items: - $ref: '#/components/schemas/ValidationWarning' - '400': - description: Validation failed - content: - application/json: - schema: - $ref: '#/components/schemas/ValidationError' - - /vips/{vipId}/schedule/{eventId}: - put: - tags: - - Schedule - summary: Update a scheduled event - description: Update an existing event in a VIP's schedule - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - - name: eventId - in: path - required: true - schema: - type: string - description: Event ID - requestBody: - required: true - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/ScheduleEventCreate' - - type: object - properties: - status: - $ref: '#/components/schemas/EventStatus' - responses: - '200': - description: Event updated successfully - content: - application/json: - schema: - $ref: '#/components/schemas/ScheduleEvent' - '404': - description: VIP or event not found - - delete: - tags: - - Schedule - summary: Delete a scheduled event - description: Remove an event from a VIP's schedule - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - - name: eventId - in: path - required: true - schema: - type: string - description: Event ID - responses: - '200': - description: Event deleted successfully - '404': - description: VIP or event not found - - /vips/{vipId}/schedule/{eventId}/status: - patch: - tags: - - Schedule - summary: Update event status - description: Update the status of a specific event - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - - name: eventId - in: path - required: true - schema: - type: string - description: Event ID - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - status: - $ref: '#/components/schemas/EventStatus' - example: - status: "in-progress" - responses: - '200': - description: Event status updated - content: - application/json: - schema: - $ref: '#/components/schemas/ScheduleEvent' - '404': - description: VIP or event not found - - /drivers: - get: - tags: - - Drivers - summary: Get all drivers - description: Retrieve a list of all drivers in the system - responses: - '200': - description: List of drivers - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Driver' - - post: - tags: - - Drivers - summary: Create a new driver - description: Add a new driver to the system - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DriverCreate' - example: - name: "Carlos Rodriguez" - phone: "(303) 555-0101" - currentLocation: - lat: 39.8561 - lng: -104.6737 - responses: - '201': - description: Driver created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Driver' - - /drivers/{id}: - put: - tags: - - Drivers - summary: Update a driver - description: Update an existing driver's information - parameters: - - name: id - in: path - required: true - schema: - type: string - description: Driver ID - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DriverCreate' - responses: - '200': - description: Driver updated successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Driver' - '404': - description: Driver not found - - delete: - tags: - - Drivers - summary: Delete a driver - description: Remove a driver from the system - parameters: - - name: id - in: path - required: true - schema: - type: string - description: Driver ID - responses: - '200': - description: Driver deleted successfully - '404': - description: Driver not found - - /drivers/{driverId}/schedule: - get: - tags: - - Drivers - summary: Get driver's schedule - description: Retrieve all events assigned to a specific driver - parameters: - - name: driverId - in: path - required: true - schema: - type: string - description: Driver ID - responses: - '200': - description: Driver's schedule - content: - application/json: - schema: - type: object - properties: - driver: - type: object - properties: - id: - type: string - name: - type: string - phone: - type: string - schedule: - type: array - items: - allOf: - - $ref: '#/components/schemas/ScheduleEvent' - - type: object - properties: - vipId: - type: string - vipName: - type: string - '404': - description: Driver not found - - /drivers/availability: - post: - tags: - - Drivers - summary: Check driver availability - description: Find available drivers for a specific time slot - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - startTime: - type: string - format: date-time - endTime: - type: string - format: date-time - location: - type: string - required: - - startTime - - endTime - example: - startTime: "2025-06-26T11:00:00" - endTime: "2025-06-26T12:30:00" - location: "Denver Convention Center" - responses: - '200': - description: Driver availability information - content: - application/json: - schema: - type: object - properties: - available: - type: array - items: - $ref: '#/components/schemas/Driver' - busy: - type: array - items: - allOf: - - $ref: '#/components/schemas/Driver' - - type: object - properties: - conflictingEvents: - type: array - items: - $ref: '#/components/schemas/ScheduleEvent' - - /drivers/{driverId}/conflicts: - post: - tags: - - Drivers - summary: Check driver conflicts - description: Check if a specific driver has conflicts for a time slot - parameters: - - name: driverId - in: path - required: true - schema: - type: string - description: Driver ID - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - startTime: - type: string - format: date-time - endTime: - type: string - format: date-time - location: - type: string - required: - - startTime - - endTime - responses: - '200': - description: Conflict check results - content: - application/json: - schema: - type: object - properties: - conflicts: - type: array - items: - $ref: '#/components/schemas/ScheduleEvent' - - /flights/{flightNumber}: - get: - tags: - - Flights - summary: Get flight information - description: Retrieve real-time flight information - parameters: - - name: flightNumber - in: path - required: true - schema: - type: string - description: Flight number (e.g., UA1234) - example: "UA1234" - - name: date - in: query - schema: - type: string - format: date - description: Flight date (YYYY-MM-DD) - example: "2025-06-26" - - name: departureAirport - in: query - schema: - type: string - description: Departure airport code - example: "LAX" - - name: arrivalAirport - in: query - schema: - type: string - description: Arrival airport code - example: "DEN" - responses: - '200': - description: Flight information - content: - application/json: - schema: - $ref: '#/components/schemas/FlightInfo' - '404': - description: Flight not found - '500': - description: Failed to fetch flight data - - /flights/{flightNumber}/track: - post: - tags: - - Flights - summary: Start flight tracking - description: Begin periodic updates for a specific flight - parameters: - - name: flightNumber - in: path - required: true - schema: - type: string - description: Flight number - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - date: - type: string - format: date - intervalMinutes: - type: integer - default: 5 - required: - - date - example: - date: "2025-06-26" - intervalMinutes: 5 - responses: - '200': - description: Flight tracking started - content: - application/json: - schema: - type: object - properties: - message: - type: string - example: "Started tracking UA1234 on 2025-06-26" - - delete: - tags: - - Flights - summary: Stop flight tracking - description: Stop periodic updates for a specific flight - parameters: - - name: flightNumber - in: path - required: true - schema: - type: string - description: Flight number - - name: date - in: query - required: true - schema: - type: string - format: date - description: Flight date - responses: - '200': - description: Flight tracking stopped - - /flights/batch: - post: - tags: - - Flights - summary: Get multiple flights information - description: Retrieve information for multiple flights at once - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - flights: - type: array - items: - type: object - properties: - flightNumber: - type: string - date: - type: string - format: date - required: - - flightNumber - - date - example: - flights: - - flightNumber: "UA1234" - date: "2025-06-26" - - flightNumber: "AA789" - date: "2025-06-26" - responses: - '200': - description: Multiple flight information - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/FlightInfo' - - /flights/tracking/status: - get: - tags: - - Flights - summary: Get flight tracking status - description: Get the status of all currently tracked flights - responses: - '200': - description: Flight tracking status - content: - application/json: - schema: - type: object - properties: - trackedFlights: - type: array - items: - type: object - properties: - flightKey: - type: string - vipName: - type: string - lastUpdate: - type: string - format: date-time - status: - type: string - - /admin/authenticate: - post: - tags: - - Admin - summary: Admin authentication - description: Authenticate admin user - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - password: - type: string - required: - - password - responses: - '200': - description: Authentication successful - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - '401': - description: Invalid password - - /admin/settings: - get: - tags: - - Admin - summary: Get admin settings - description: Retrieve current admin settings (requires authentication) - parameters: - - name: admin-auth - in: header - required: true - schema: - type: string - description: Admin authentication header - responses: - '200': - description: Admin settings - content: - application/json: - schema: - $ref: '#/components/schemas/AdminSettings' - '401': - description: Unauthorized - - post: - tags: - - Admin - summary: Update admin settings - description: Update admin settings (requires authentication) - parameters: - - name: admin-auth - in: header - required: true - schema: - type: string - description: Admin authentication header - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AdminSettings' - responses: - '200': - description: Settings updated successfully - '401': - description: Unauthorized - -components: - schemas: - VIP: - type: object - properties: - id: - type: string - description: Unique VIP identifier - name: - type: string - description: VIP's full name - organization: - type: string - description: VIP's organization or company - transportMode: - type: string - enum: [flight, self-driving] - description: Mode of transportation - flights: - type: array - items: - $ref: '#/components/schemas/Flight' - description: Flight information (for flight transport mode) - expectedArrival: - type: string - format: date-time - description: Expected arrival time (for self-driving mode) - needsAirportPickup: - type: boolean - description: Whether VIP needs airport pickup - needsVenueTransport: - type: boolean - description: Whether VIP needs venue transport - assignedDriverIds: - type: array - items: - type: string - description: List of assigned driver IDs - notes: - type: string - description: Additional notes about the VIP - schedule: - type: array - items: - $ref: '#/components/schemas/ScheduleEvent' - description: VIP's schedule (usually empty, fetched separately) - - VIPCreate: - type: object - required: - - name - - organization - - transportMode - properties: - name: - type: string - minLength: 1 - organization: - type: string - minLength: 1 - transportMode: - type: string - enum: [flight, self-driving] - flights: - type: array - items: - $ref: '#/components/schemas/Flight' - expectedArrival: - type: string - format: date-time - needsAirportPickup: - type: boolean - default: true - needsVenueTransport: - type: boolean - default: true - notes: - type: string - - Flight: - type: object - required: - - flightNumber - - flightDate - - segment - properties: - flightNumber: - type: string - description: Flight number (e.g., UA1234) - flightDate: - type: string - format: date - description: Flight date - segment: - type: integer - minimum: 1 - description: Flight segment number for connecting flights - validated: - type: boolean - description: Whether flight has been validated - validationData: - $ref: '#/components/schemas/FlightInfo' - - Driver: - type: object - properties: - id: - type: string - description: Unique driver identifier - name: - type: string - description: Driver's full name - phone: - type: string - description: Driver's phone number - currentLocation: - $ref: '#/components/schemas/Location' - assignedVipIds: - type: array - items: - type: string - description: List of assigned VIP IDs - - DriverCreate: - type: object - required: - - name - - phone - properties: - name: - type: string - minLength: 1 - phone: - type: string - minLength: 1 - currentLocation: - $ref: '#/components/schemas/Location' - - Location: - type: object - properties: - lat: - type: number - format: float - description: Latitude - lng: - type: number - format: float - description: Longitude - - ScheduleEvent: - type: object - properties: - id: - type: string - description: Unique event identifier - title: - type: string - description: Event title - location: - type: string - description: Event location - startTime: - type: string - format: date-time - description: Event start time - endTime: - type: string - format: date-time - description: Event end time - description: - type: string - description: Event description - assignedDriverId: - type: string - description: Assigned driver ID - status: - $ref: '#/components/schemas/EventStatus' - type: - $ref: '#/components/schemas/EventType' - - ScheduleEventCreate: - type: object - required: - - title - - location - - startTime - - endTime - - type - properties: - title: - type: string - minLength: 1 - location: - type: string - minLength: 1 - startTime: - type: string - format: date-time - endTime: - type: string - format: date-time - description: - type: string - type: - $ref: '#/components/schemas/EventType' - assignedDriverId: - type: string - - EventStatus: - type: string - enum: [scheduled, in-progress, completed, cancelled] - description: Current status of the event - - EventType: - type: string - enum: [transport, meeting, event, meal, accommodation] - description: Type of event - - FlightInfo: - type: object - properties: - flightNumber: - type: string - flightDate: - type: string - format: date - status: - type: string - enum: [scheduled, active, landed, cancelled, delayed] - airline: - type: string - aircraft: - type: string - departure: - $ref: '#/components/schemas/FlightLocation' - arrival: - $ref: '#/components/schemas/FlightLocation' - delay: - type: integer - description: Delay in minutes - lastUpdated: - type: string - format: date-time - source: - type: string - description: Data source (e.g., aviationstack) - - FlightLocation: - type: object - properties: - airport: - type: string - description: Airport code - airportName: - type: string - description: Full airport name - scheduled: - type: string - format: date-time - estimated: - type: string - format: date-time - actual: - type: string - format: date-time - terminal: - type: string - gate: - type: string - - AdminSettings: - type: object - properties: - apiKeys: - type: object - properties: - aviationStackKey: - type: string - description: Masked API key - googleMapsKey: - type: string - description: Masked API key - twilioKey: - type: string - description: Masked API key - systemSettings: - type: object - properties: - defaultPickupLocation: - type: string - defaultDropoffLocation: - type: string - timeZone: - type: string - notificationsEnabled: - type: boolean - - ValidationError: - type: object - properties: - error: - type: string - validationErrors: - type: array - items: - $ref: '#/components/schemas/ValidationMessage' - warnings: - type: array - items: - $ref: '#/components/schemas/ValidationWarning' - message: - type: string - - ValidationMessage: - type: object - properties: - field: - type: string - message: - type: string - code: - type: string - - ValidationWarning: - type: object - properties: - field: - type: string - message: - type: string - code: - type: string - - securitySchemes: - AdminAuth: - type: apiKey - in: header - name: admin-auth - description: Admin authentication header - -security: - - AdminAuth: [] diff --git a/backend-old-20260125/src/config/auth0.ts b/backend-old-20260125/src/config/auth0.ts deleted file mode 100644 index a9cba0a..0000000 --- a/backend-old-20260125/src/config/auth0.ts +++ /dev/null @@ -1,105 +0,0 @@ -import jwksRsa from 'jwks-rsa'; -import { expressjwt as jwt, GetVerificationKey } from 'express-jwt'; -import { Request, Response, NextFunction } from 'express'; - -// Environment validation -const requiredEnvVars = ['AUTH0_DOMAIN', 'AUTH0_AUDIENCE']; -for (const envVar of requiredEnvVars) { - if (!process.env[envVar]) { - throw new Error(`Missing required environment variable: ${envVar}`); - } -} - -export const auth0Config = { - domain: process.env.AUTH0_DOMAIN!, - clientId: process.env.AUTH0_CLIENT_ID!, - clientSecret: process.env.AUTH0_CLIENT_SECRET!, - audience: process.env.AUTH0_AUDIENCE!, - apiId: process.env.AUTH0_API_ID!, - callbackUrl: process.env.AUTH0_CALLBACK_URL || 'http://localhost:3000/auth/auth0/callback', - logoutUrl: process.env.AUTH0_LOGOUT_URL || 'http://localhost:5173/login', - frontendUrl: process.env.FRONTEND_URL || 'http://localhost:5173', -}; - -// JWKS client for fetching Auth0 public keys -export const jwksClient = jwksRsa({ - cache: true, - rateLimit: true, - jwksRequestsPerMinute: 10, - jwksUri: `https://${auth0Config.domain}/.well-known/jwks.json`, -}); - -// JWT verification middleware using Auth0's public keys -export const jwtCheck = jwt({ - secret: jwksRsa.expressJwtSecret({ - cache: true, - rateLimit: true, - jwksRequestsPerMinute: 10, - jwksUri: `https://${auth0Config.domain}/.well-known/jwks.json`, - }) as GetVerificationKey, - audience: auth0Config.audience, - issuer: `https://${auth0Config.domain}/`, - algorithms: ['RS256'], - credentialsRequired: false, // We'll handle this in requireAuth middleware -}); - -// Type for decoded JWT payload -export interface Auth0JwtPayload { - iss: string; - sub: string; - aud: string[]; - iat: number; - exp: number; - azp: string; - scope?: string; - permissions?: string[]; - // Custom claims - 'https://vip-coordinator/role'?: string; - 'https://vip-coordinator/approval_status'?: string; - 'https://vip-coordinator/email'?: string; -} - -// Extend Express Request to include auth -declare global { - namespace Express { - interface Request { - auth?: Auth0JwtPayload; - } - } -} - -// Helper to extract user info from JWT -export function getUserFromToken(req: Request): { - auth0Sub: string; - email?: string; - role?: string; - approvalStatus?: string; - permissions?: string[]; -} | null { - if (!req.auth) return null; - - return { - auth0Sub: req.auth.sub, - email: req.auth['https://vip-coordinator/email'], - role: req.auth['https://vip-coordinator/role'], - approvalStatus: req.auth['https://vip-coordinator/approval_status'], - permissions: req.auth.permissions || [], - }; -} - -// Auth0 Management API configuration -export function getAuth0ManagementClient() { - const { ManagementClient } = require('auth0'); - - return new ManagementClient({ - domain: auth0Config.domain, - clientId: auth0Config.clientId, - clientSecret: auth0Config.clientSecret, - scope: 'read:users update:users read:users_app_metadata update:users_app_metadata', - }); -} - -console.log('šŸ” Auth0 configuration initialized'); -console.log(` Domain: ${auth0Config.domain}`); -console.log(` Audience: ${auth0Config.audience}`); -console.log(` JWKS URI: https://${auth0Config.domain}/.well-known/jwks.json`); diff --git a/backend-old-20260125/src/config/database.ts b/backend-old-20260125/src/config/database.ts deleted file mode 100644 index 57e38e5..0000000 --- a/backend-old-20260125/src/config/database.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Pool } from 'pg'; -import dotenv from 'dotenv'; - -dotenv.config(); - -const pool = new Pool({ - connectionString: process.env.DATABASE_URL || 'postgresql://postgres:changeme@localhost:5432/vip_coordinator', - max: 20, - idleTimeoutMillis: 30000, - connectionTimeoutMillis: 2000, -}); - -// Test the connection -pool.on('connect', () => { - console.log('āœ… Connected to PostgreSQL database'); -}); - -pool.on('error', (err) => { - console.error('āŒ PostgreSQL connection error:', err); -}); - -export default pool; diff --git a/backend-old-20260125/src/config/env.ts b/backend-old-20260125/src/config/env.ts deleted file mode 100644 index b442f9d..0000000 --- a/backend-old-20260125/src/config/env.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { z } from 'zod'; -import * as dotenv from 'dotenv'; - -// Load environment variables -dotenv.config(); - -// Define the environment schema -const envSchema = z.object({ - // Database - DATABASE_URL: z.string().url().describe('PostgreSQL connection string'), - - // Redis - REDIS_URL: z.string().url().describe('Redis connection string'), - - // Google OAuth - GOOGLE_CLIENT_ID: z.string().min(1).describe('Google OAuth Client ID'), - GOOGLE_CLIENT_SECRET: z.string().min(1).describe('Google OAuth Client Secret'), - GOOGLE_REDIRECT_URI: z.string().url().describe('Google OAuth redirect URI'), - - // Application - FRONTEND_URL: z.string().url().describe('Frontend application URL'), - JWT_SECRET: z.string().min(32).describe('JWT signing secret (min 32 chars)'), - - // Server - PORT: z.string().transform(Number).default('3000'), - NODE_ENV: z.enum(['development', 'production', 'test']).default('development'), -}); - -// Validate and export environment variables -export const env = (() => { - try { - return envSchema.parse(process.env); - } catch (error) { - if (error instanceof z.ZodError) { - console.error('āŒ Invalid environment variables:'); - console.error(error.format()); - - const missingVars = error.errors - .filter(err => err.code === 'invalid_type' && err.received === 'undefined') - .map(err => err.path.join('.')); - - if (missingVars.length > 0) { - console.error('\nšŸ“‹ Missing required environment variables:'); - missingVars.forEach(varName => { - console.error(` - ${varName}`); - }); - console.error('\nšŸ’” Create a .env file based on .env.example'); - } - - process.exit(1); - } - throw error; - } -})(); - -// Type-safe environment variables -export type Env = z.infer; \ No newline at end of file diff --git a/backend-old-20260125/src/config/keycloak.ts b/backend-old-20260125/src/config/keycloak.ts deleted file mode 100644 index b8edee8..0000000 --- a/backend-old-20260125/src/config/keycloak.ts +++ /dev/null @@ -1,102 +0,0 @@ -import jwksRsa from 'jwks-rsa'; -import { expressjwt as jwt, GetVerificationKey } from 'express-jwt'; -import { Request } from 'express'; - -// Keycloak configuration -const keycloakConfig = { - realm: process.env.KEYCLOAK_REALM || 'vip-coordinator', - serverUrl: process.env.KEYCLOAK_SERVER_URL || 'http://localhost:8080', - clientId: process.env.KEYCLOAK_CLIENT_ID || 'vip-coordinator-frontend', - frontendUrl: process.env.FRONTEND_URL || 'http://localhost:5173', -}; - -// Construct Keycloak URLs -const realmUrl = `${keycloakConfig.serverUrl}/realms/${keycloakConfig.realm}`; -const jwksUri = `${realmUrl}/protocol/openid-connect/certs`; - -// JWT verification middleware using Keycloak's public keys -export const jwtCheck = jwt({ - secret: jwksRsa.expressJwtSecret({ - cache: true, - rateLimit: true, - jwksRequestsPerMinute: 10, - jwksUri: jwksUri, - }) as GetVerificationKey, - issuer: realmUrl, - algorithms: ['RS256'], - credentialsRequired: false, // We'll handle this in requireAuth middleware -}); - -// Type for decoded JWT payload (Keycloak format) -export interface KeycloakJwtPayload { - iss: string; - sub: string; - aud: string | string[]; - iat: number; - exp: number; - azp?: string; - realm_access?: { - roles?: string[]; - }; - resource_access?: { - [key: string]: { - roles?: string[]; - }; - }; - email?: string; - email_verified?: boolean; - name?: string; - preferred_username?: string; - given_name?: string; - family_name?: string; - // Custom claims (if you add them via Keycloak mappers) - role?: string; - approval_status?: string; -} - -// Extend Express Request to include auth -declare global { - namespace Express { - interface Request { - auth?: KeycloakJwtPayload; - } - } -} - -// Helper to extract user info from JWT -export function getUserFromToken(req: Request): { - keycloakSub: string; - email?: string; - name?: string; - role?: string; - approvalStatus?: string; - roles?: string[]; -} | null { - if (!req.auth) return null; - - const roles = req.auth.realm_access?.roles || []; - - return { - keycloakSub: req.auth.sub, - email: req.auth.email, - name: req.auth.name || req.auth.preferred_username, - role: req.auth.role, // Custom claim if you add it - approvalStatus: req.auth.approval_status, // Custom claim if you add it - roles: roles, - }; -} - -export const keycloakUrls = { - realmUrl, - jwksUri, - authorizationUrl: `${realmUrl}/protocol/openid-connect/auth`, - tokenUrl: `${realmUrl}/protocol/openid-connect/token`, - logoutUrl: `${realmUrl}/protocol/openid-connect/logout`, - userInfoUrl: `${realmUrl}/protocol/openid-connect/userinfo`, -}; - -console.log('šŸ” Keycloak configuration initialized'); -console.log(` Realm: ${keycloakConfig.realm}`); -console.log(` Server URL: ${keycloakConfig.serverUrl}`); -console.log(` Client ID: ${keycloakConfig.clientId}`); -console.log(` JWKS URI: ${jwksUri}`); diff --git a/backend-old-20260125/src/config/mockDatabase.ts b/backend-old-20260125/src/config/mockDatabase.ts deleted file mode 100644 index 2975d97..0000000 --- a/backend-old-20260125/src/config/mockDatabase.ts +++ /dev/null @@ -1,177 +0,0 @@ -// Mock database for when PostgreSQL is not available -interface MockUser { - id: string; - email: string; - name: string; - role: string; - google_id?: string; - created_at: Date; - updated_at: Date; -} - -interface MockVIP { - id: string; - name: string; - organization?: string; - department: string; - transport_mode: string; - expected_arrival?: string; - needs_airport_pickup: boolean; - needs_venue_transport: boolean; - notes?: string; - created_at: Date; - updated_at: Date; -} - -class MockDatabase { - private users: Map = new Map(); - private vips: Map = new Map(); - private drivers: Map = new Map(); - private scheduleEvents: Map = new Map(); - private adminSettings: Map = new Map(); - - constructor() { - // Add a test admin user - const adminId = '1'; - this.users.set(adminId, { - id: adminId, - email: 'admin@example.com', - name: 'Test Admin', - role: 'admin', - created_at: new Date(), - updated_at: new Date() - }); - - // Add some test VIPs - this.vips.set('1', { - id: '1', - name: 'John Doe', - organization: 'Test Org', - department: 'Office of Development', - transport_mode: 'flight', - expected_arrival: '2025-07-25 14:00', - needs_airport_pickup: true, - needs_venue_transport: true, - notes: 'Test VIP', - created_at: new Date(), - updated_at: new Date() - }); - } - - async query(text: string, params?: any[]): Promise { - console.log('Mock DB Query:', text.substring(0, 50) + '...'); - - // Handle user queries - if (text.includes('COUNT(*) FROM users')) { - return { rows: [{ count: this.users.size.toString() }] }; - } - - if (text.includes('SELECT * FROM users WHERE email')) { - const email = params?.[0]; - const user = Array.from(this.users.values()).find(u => u.email === email); - return { rows: user ? [user] : [] }; - } - - if (text.includes('SELECT * FROM users WHERE id')) { - const id = params?.[0]; - const user = this.users.get(id); - return { rows: user ? [user] : [] }; - } - - if (text.includes('SELECT * FROM users WHERE google_id')) { - const google_id = params?.[0]; - const user = Array.from(this.users.values()).find(u => u.google_id === google_id); - return { rows: user ? [user] : [] }; - } - - if (text.includes('INSERT INTO users')) { - const id = Date.now().toString(); - const user: MockUser = { - id, - email: params?.[0], - name: params?.[1], - role: params?.[2] || 'coordinator', - google_id: params?.[4], - created_at: new Date(), - updated_at: new Date() - }; - this.users.set(id, user); - return { rows: [user] }; - } - - // Handle VIP queries - if (text.includes('SELECT v.*') && text.includes('FROM vips')) { - const vips = Array.from(this.vips.values()); - return { - rows: vips.map(v => ({ - ...v, - flights: [] - })) - }; - } - - // Handle admin settings queries - if (text.includes('SELECT * FROM admin_settings')) { - const settings = Array.from(this.adminSettings.entries()).map(([key, value]) => ({ - key, - value - })); - return { rows: settings }; - } - - // Handle drivers queries - if (text.includes('SELECT * FROM drivers')) { - const drivers = Array.from(this.drivers.values()); - return { rows: drivers }; - } - - // Handle schedule events queries - if (text.includes('SELECT * FROM schedule_events')) { - const events = Array.from(this.scheduleEvents.values()); - return { rows: events }; - } - - if (text.includes('INSERT INTO vips')) { - const id = Date.now().toString(); - const vip: MockVIP = { - id, - name: params?.[0], - organization: params?.[1], - department: params?.[2] || 'Office of Development', - transport_mode: params?.[3] || 'flight', - expected_arrival: params?.[4], - needs_airport_pickup: params?.[5] !== false, - needs_venue_transport: params?.[6] !== false, - notes: params?.[7] || '', - created_at: new Date(), - updated_at: new Date() - }; - this.vips.set(id, vip); - return { rows: [vip] }; - } - - // Default empty result - console.log('Unhandled query:', text); - return { rows: [] }; - } - - async connect() { - return { - query: this.query.bind(this), - release: () => {} - }; - } - - // Make compatible with pg Pool interface - async end() { - console.log('Mock database connection closed'); - } - - on(event: string, callback: Function) { - if (event === 'connect') { - setTimeout(() => callback(), 100); - } - } -} - -export default MockDatabase; \ No newline at end of file diff --git a/backend-old-20260125/src/config/redis.ts b/backend-old-20260125/src/config/redis.ts deleted file mode 100644 index f6c085d..0000000 --- a/backend-old-20260125/src/config/redis.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { createClient } from 'redis'; -import dotenv from 'dotenv'; - -dotenv.config(); - -const redisClient = createClient({ - url: process.env.REDIS_URL || 'redis://localhost:6379' -}); - -redisClient.on('connect', () => { - console.log('āœ… Connected to Redis'); -}); - -redisClient.on('error', (err: Error) => { - console.error('āŒ Redis connection error:', err); -}); - -// Connect to Redis -redisClient.connect().catch((err: Error) => { - console.error('āŒ Failed to connect to Redis:', err); -}); - -export default redisClient; diff --git a/backend-old-20260125/src/config/schema.sql b/backend-old-20260125/src/config/schema.sql deleted file mode 100644 index c3f70f7..0000000 --- a/backend-old-20260125/src/config/schema.sql +++ /dev/null @@ -1,132 +0,0 @@ --- VIP Coordinator Database Schema - --- Create VIPs table -CREATE TABLE IF NOT EXISTS vips ( - id VARCHAR(255) PRIMARY KEY, - name VARCHAR(255) NOT NULL, - organization VARCHAR(255) NOT NULL, - department VARCHAR(255) DEFAULT 'Office of Development', - transport_mode VARCHAR(50) NOT NULL CHECK (transport_mode IN ('flight', 'self-driving')), - expected_arrival TIMESTAMP, - needs_airport_pickup BOOLEAN DEFAULT false, - needs_venue_transport BOOLEAN DEFAULT true, - notes TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - --- Create flights table (for VIPs with flight transport) -CREATE TABLE IF NOT EXISTS flights ( - id SERIAL PRIMARY KEY, - vip_id VARCHAR(255) REFERENCES vips(id) ON DELETE CASCADE, - flight_number VARCHAR(50) NOT NULL, - flight_date DATE NOT NULL, - segment INTEGER NOT NULL, - departure_airport VARCHAR(10), - arrival_airport VARCHAR(10), - scheduled_departure TIMESTAMP, - scheduled_arrival TIMESTAMP, - actual_departure TIMESTAMP, - actual_arrival TIMESTAMP, - status VARCHAR(50), - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - --- Create drivers table -CREATE TABLE IF NOT EXISTS drivers ( - id VARCHAR(255) PRIMARY KEY, - name VARCHAR(255) NOT NULL, - phone VARCHAR(50) NOT NULL, - department VARCHAR(255) DEFAULT 'Office of Development', - user_id VARCHAR(255) REFERENCES users(id) ON DELETE SET NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - --- Create schedule_events table -CREATE TABLE IF NOT EXISTS schedule_events ( - id VARCHAR(255) PRIMARY KEY, - vip_id VARCHAR(255) REFERENCES vips(id) ON DELETE CASCADE, - title VARCHAR(255) NOT NULL, - location VARCHAR(255) NOT NULL, - start_time TIMESTAMP NOT NULL, - end_time TIMESTAMP NOT NULL, - description TEXT, - assigned_driver_id VARCHAR(255) REFERENCES drivers(id) ON DELETE SET NULL, - status VARCHAR(50) DEFAULT 'scheduled' CHECK (status IN ('scheduled', 'in-progress', 'completed', 'cancelled')), - event_type VARCHAR(50) NOT NULL CHECK (event_type IN ('transport', 'meeting', 'event', 'meal', 'accommodation')), - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - --- Create users table for authentication -CREATE TABLE IF NOT EXISTS users ( - id VARCHAR(255) PRIMARY KEY, - auth0_sub VARCHAR(255) UNIQUE NOT NULL, - email VARCHAR(255) UNIQUE NOT NULL, - name VARCHAR(255) NOT NULL, - role VARCHAR(50) NOT NULL CHECK (role IN ('driver', 'coordinator', 'administrator')), - profile_picture_url TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - last_login TIMESTAMP, - is_active BOOLEAN DEFAULT true, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - approval_status VARCHAR(20) DEFAULT 'pending' CHECK (approval_status IN ('pending', 'approved', 'denied')), - identity_provider VARCHAR(50) DEFAULT 'auth0' -); - --- Create system_setup table for tracking initial setup -CREATE TABLE IF NOT EXISTS system_setup ( - id SERIAL PRIMARY KEY, - setup_completed BOOLEAN DEFAULT false, - first_admin_created BOOLEAN DEFAULT false, - setup_date TIMESTAMP, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - --- Create admin_settings table -CREATE TABLE IF NOT EXISTS admin_settings ( - id SERIAL PRIMARY KEY, - setting_key VARCHAR(255) UNIQUE NOT NULL, - setting_value TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - --- Create indexes for better performance -CREATE INDEX IF NOT EXISTS idx_vips_transport_mode ON vips(transport_mode); -CREATE INDEX IF NOT EXISTS idx_flights_vip_id ON flights(vip_id); -CREATE INDEX IF NOT EXISTS idx_flights_date ON flights(flight_date); -CREATE INDEX IF NOT EXISTS idx_schedule_events_vip_id ON schedule_events(vip_id); -CREATE INDEX IF NOT EXISTS idx_schedule_events_driver_id ON schedule_events(assigned_driver_id); -CREATE INDEX IF NOT EXISTS idx_schedule_events_start_time ON schedule_events(start_time); -CREATE INDEX IF NOT EXISTS idx_schedule_events_status ON schedule_events(status); -CREATE INDEX IF NOT EXISTS idx_users_auth0_sub ON users(auth0_sub); -CREATE INDEX IF NOT EXISTS idx_users_email ON users(email); -CREATE INDEX IF NOT EXISTS idx_users_role ON users(role); -CREATE INDEX IF NOT EXISTS idx_drivers_user_id ON drivers(user_id); - --- Create updated_at trigger function -CREATE OR REPLACE FUNCTION update_updated_at_column() -RETURNS TRIGGER AS $$ -BEGIN - NEW.updated_at = CURRENT_TIMESTAMP; - RETURN NEW; -END; -$$ language 'plpgsql'; - --- Create triggers for updated_at (drop if exists first) -DROP TRIGGER IF EXISTS update_vips_updated_at ON vips; -DROP TRIGGER IF EXISTS update_flights_updated_at ON flights; -DROP TRIGGER IF EXISTS update_drivers_updated_at ON drivers; -DROP TRIGGER IF EXISTS update_schedule_events_updated_at ON schedule_events; -DROP TRIGGER IF EXISTS update_users_updated_at ON users; -DROP TRIGGER IF EXISTS update_admin_settings_updated_at ON admin_settings; - -CREATE TRIGGER update_vips_updated_at BEFORE UPDATE ON vips FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -CREATE TRIGGER update_flights_updated_at BEFORE UPDATE ON flights FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -CREATE TRIGGER update_drivers_updated_at BEFORE UPDATE ON drivers FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -CREATE TRIGGER update_schedule_events_updated_at BEFORE UPDATE ON schedule_events FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -CREATE TRIGGER update_users_updated_at BEFORE UPDATE ON users FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -CREATE TRIGGER update_admin_settings_updated_at BEFORE UPDATE ON admin_settings FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); diff --git a/backend-old-20260125/src/index.original.ts b/backend-old-20260125/src/index.original.ts deleted file mode 100644 index 29b0d00..0000000 --- a/backend-old-20260125/src/index.original.ts +++ /dev/null @@ -1,878 +0,0 @@ -import express, { Express, Request, Response } from 'express'; -import dotenv from 'dotenv'; -import cors from 'cors'; -import authRoutes, { requireAuth, requireRole } from './routes/simpleAuth'; -import flightService from './services/flightService'; -import driverConflictService from './services/driverConflictService'; -import scheduleValidationService from './services/scheduleValidationService'; -import FlightTrackingScheduler from './services/flightTrackingScheduler'; -import enhancedDataService from './services/enhancedDataService'; -import databaseService from './services/databaseService'; -import jwtKeyManager from './services/jwtKeyManager'; // Initialize JWT Key Manager -import { errorHandler, notFoundHandler, asyncHandler } from './middleware/errorHandler'; -import { requestLogger, errorLogger } from './middleware/logger'; -import { AppError, NotFoundError, ValidationError } from './types/errors'; -import { validate, validateQuery, validateParams } from './middleware/validation'; -import { - createVipSchema, - updateVipSchema, - createDriverSchema, - updateDriverSchema, - createScheduleEventSchema, - updateScheduleEventSchema, - paginationSchema -} from './types/schemas'; - -dotenv.config(); - -const app: Express = express(); -const port: number = process.env.PORT ? parseInt(process.env.PORT) : 3000; - -// Middleware -app.use(cors({ - origin: [ - process.env.FRONTEND_URL || 'http://localhost:5173', - 'http://localhost:5173', - 'http://localhost:3000', - 'http://localhost', // Frontend Docker container (local testing) - 'https://bsa.madeamess.online' // Production frontend domain (where users access the site) - ], - credentials: true -})); -app.use(express.json()); -app.use(express.urlencoded({ extended: true })); - -// Add request logging -app.use(requestLogger); - -// Simple JWT-based authentication - no passport needed - -// Authentication routes -app.use('/auth', authRoutes); - -// Temporary admin bypass route (remove after setup) -app.get('/admin-bypass', (req: Request, res: Response) => { - res.redirect(`${process.env.FRONTEND_URL || 'http://localhost:5173'}/admin?bypass=true`); -}); - -// Serve static files from public directory -app.use(express.static('public')); - -// Enhanced health check endpoint with authentication system status -app.get('/api/health', asyncHandler(async (req: Request, res: Response) => { - const timestamp = new Date().toISOString(); - - // Check JWT Key Manager status - const jwtStatus = jwtKeyManager.getStatus(); - - // Check environment variables - const envCheck = { - google_client_id: !!process.env.GOOGLE_CLIENT_ID, - google_client_secret: !!process.env.GOOGLE_CLIENT_SECRET, - google_redirect_uri: !!process.env.GOOGLE_REDIRECT_URI, - frontend_url: !!process.env.FRONTEND_URL, - database_url: !!process.env.DATABASE_URL, - admin_password: !!process.env.ADMIN_PASSWORD - }; - - // Check database connectivity - let databaseStatus = 'unknown'; - let userCount = 0; - try { - userCount = await databaseService.getUserCount(); - databaseStatus = 'connected'; - } catch (dbError) { - databaseStatus = 'disconnected'; - console.error('Health check - Database error:', dbError); - } - - // Overall system health - const isHealthy = databaseStatus === 'connected' && - jwtStatus.hasCurrentKey && - envCheck.google_client_id && - envCheck.google_client_secret; - - const healthData = { - status: isHealthy ? 'OK' : 'DEGRADED', - timestamp, - version: '1.0.0', - environment: process.env.NODE_ENV || 'development', - services: { - database: { - status: databaseStatus, - user_count: databaseStatus === 'connected' ? userCount : null - }, - authentication: { - jwt_key_manager: jwtStatus, - oauth_configured: envCheck.google_client_id && envCheck.google_client_secret, - environment_variables: envCheck - } - }, - uptime: process.uptime(), - memory: process.memoryUsage() - }; - - // Log health check for monitoring - console.log(`šŸ„ Health Check [${timestamp}]:`, { - status: healthData.status, - database: databaseStatus, - jwt_keys: jwtStatus.hasCurrentKey, - oauth: envCheck.google_client_id && envCheck.google_client_secret - }); - - res.status(isHealthy ? 200 : 503).json(healthData); -})); - -// Data is now persisted using dataService - no more in-memory storage! - -// Admin password - MUST be set via environment variable in production -const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'CHANGE_ME_ADMIN_PASSWORD'; - -// Initialize flight tracking scheduler -const flightTracker = new FlightTrackingScheduler(flightService); - -// VIP routes (protected) -app.post('/api/vips', requireAuth, requireRole(['coordinator', 'administrator']), validate(createVipSchema), asyncHandler(async (req: Request, res: Response) => { - // Create a new VIP - data is already validated - const { - name, - organization, - department, // New: Office of Development or Admin - transportMode, - flightNumber, // Legacy single flight - flights, // New: array of flights - expectedArrival, - needsAirportPickup, - needsVenueTransport, - notes - } = req.body; - - const newVip = { - id: Date.now().toString(), // Simple ID generation - name, - organization, - department: department || 'Office of Development', // Default to Office of Development - transportMode: transportMode || 'flight', - // Support both legacy single flight and new multiple flights - flightNumber: transportMode === 'flight' && !flights ? flightNumber : undefined, - flights: transportMode === 'flight' && flights ? flights : undefined, - expectedArrival: transportMode === 'self-driving' ? expectedArrival : undefined, - arrivalTime: transportMode === 'flight' ? undefined : expectedArrival, // Legacy field for flight arrivals - needsAirportPickup: transportMode === 'flight' ? (needsAirportPickup !== false) : false, - needsVenueTransport: needsVenueTransport !== false, // Default to true - assignedDriverIds: [], - notes: notes || '', - schedule: [] - }; - - const savedVip = await enhancedDataService.addVip(newVip); - - // Add flights to tracking scheduler if applicable - if (savedVip.transportMode === 'flight' && savedVip.flights && savedVip.flights.length > 0) { - flightTracker.addVipFlights(savedVip.id, savedVip.name, savedVip.flights); - } - - res.status(201).json(savedVip); -})); - -app.get('/api/vips', requireAuth, async (req: Request, res: Response) => { - try { - // Fetch all VIPs - const vips = await enhancedDataService.getVips(); - res.json(vips); - } catch (error) { - res.status(500).json({ error: 'Failed to fetch VIPs' }); - } -}); - -app.put('/api/vips/:id', requireAuth, requireRole(['coordinator', 'administrator']), validate(updateVipSchema), asyncHandler(async (req: Request, res: Response) => { - // Update a VIP - data is already validated - const { id } = req.params; - const { - name, - organization, - department, // New: Office of Development or Admin - transportMode, - flightNumber, // Legacy single flight - flights, // New: array of flights - expectedArrival, - needsAirportPickup, - needsVenueTransport, - notes - } = req.body; - - const updatedVip = { - name, - organization, - department: department || 'Office of Development', - transportMode: transportMode || 'flight', - // Support both legacy single flight and new multiple flights - flights: transportMode === 'flight' && flights ? flights : undefined, - expectedArrival: transportMode === 'self-driving' ? expectedArrival : undefined, - needsAirportPickup: transportMode === 'flight' ? (needsAirportPickup !== false) : false, - needsVenueTransport: needsVenueTransport !== false, - notes: notes || '' - }; - - const savedVip = await enhancedDataService.updateVip(id, updatedVip); - - if (!savedVip) { - return res.status(404).json({ error: 'VIP not found' }); - } - - // Update flight tracking if needed - if (savedVip.transportMode === 'flight') { - // Remove old flights - flightTracker.removeVipFlights(id); - - // Add new flights if any - if (savedVip.flights && savedVip.flights.length > 0) { - flightTracker.addVipFlights(savedVip.id, savedVip.name, savedVip.flights); - } - } - - res.json(savedVip); -})); - -app.delete('/api/vips/:id', requireAuth, requireRole(['coordinator', 'administrator']), async (req: Request, res: Response) => { - // Delete a VIP - const { id } = req.params; - - try { - const deletedVip = await enhancedDataService.deleteVip(id); - - if (!deletedVip) { - return res.status(404).json({ error: 'VIP not found' }); - } - - // Remove from flight tracking - flightTracker.removeVipFlights(id); - - res.json({ message: 'VIP deleted successfully', vip: deletedVip }); - } catch (error) { - res.status(500).json({ error: 'Failed to delete VIP' }); - } -}); - -// Driver routes (protected) -app.post('/api/drivers', requireAuth, requireRole(['coordinator', 'administrator']), validate(createDriverSchema), asyncHandler(async (req: Request, res: Response) => { - // Create a new driver - data is already validated - const { name, phone, email, vehicleInfo, status } = req.body; - - const newDriver = { - id: Date.now().toString(), - name, - phone, - email, - vehicleInfo, - status: status || 'available', - department: 'Office of Development', // Default to Office of Development - currentLocation: { lat: 0, lng: 0 }, - assignedVipIds: [] - }; - - const savedDriver = await enhancedDataService.addDriver(newDriver); - res.status(201).json(savedDriver); -})); - -app.get('/api/drivers', requireAuth, async (req: Request, res: Response) => { - try { - // Fetch all drivers - const drivers = await enhancedDataService.getDrivers(); - res.json(drivers); - } catch (error) { - res.status(500).json({ error: 'Failed to fetch drivers' }); - } -}); - -app.put('/api/drivers/:id', requireAuth, requireRole(['coordinator', 'administrator']), async (req: Request, res: Response) => { - // Update a driver - const { id } = req.params; - const { name, phone, currentLocation, department } = req.body; - - try { - const updatedDriver = { - name, - phone, - department: department || 'Office of Development', - currentLocation: currentLocation || { lat: 0, lng: 0 } - }; - - const savedDriver = await enhancedDataService.updateDriver(id, updatedDriver); - - if (!savedDriver) { - return res.status(404).json({ error: 'Driver not found' }); - } - - res.json(savedDriver); - } catch (error) { - res.status(500).json({ error: 'Failed to update driver' }); - } -}); - -app.delete('/api/drivers/:id', requireAuth, requireRole(['coordinator', 'administrator']), async (req: Request, res: Response) => { - // Delete a driver - const { id } = req.params; - - try { - const deletedDriver = await enhancedDataService.deleteDriver(id); - - if (!deletedDriver) { - return res.status(404).json({ error: 'Driver not found' }); - } - - res.json({ message: 'Driver deleted successfully', driver: deletedDriver }); - } catch (error) { - res.status(500).json({ error: 'Failed to delete driver' }); - } -}); - -// Enhanced flight tracking routes with date specificity -app.get('/api/flights/:flightNumber', async (req: Request, res: Response) => { - try { - const { flightNumber } = req.params; - const { date, departureAirport, arrivalAirport } = req.query; - - // Default to today if no date provided - const flightDate = (date as string) || new Date().toISOString().split('T')[0]; - - const flightData = await flightService.getFlightInfo({ - flightNumber, - date: flightDate, - departureAirport: departureAirport as string, - arrivalAirport: arrivalAirport as string - }); - - if (flightData) { - // Always return flight data for validation, even if date doesn't match - res.json(flightData); - } else { - // Only return 404 if the flight number itself is invalid - res.status(404).json({ error: 'Invalid flight number - this flight does not exist' }); - } - } catch (error) { - res.status(500).json({ error: 'Failed to fetch flight data' }); - } -}); - -// Start periodic updates for a flight -app.post('/api/flights/:flightNumber/track', async (req: Request, res: Response) => { - try { - const { flightNumber } = req.params; - const { date, intervalMinutes = 5 } = req.body; - - if (!date) { - return res.status(400).json({ error: 'Flight date is required' }); - } - - flightService.startPeriodicUpdates({ - flightNumber, - date - }, intervalMinutes); - - res.json({ message: `Started tracking ${flightNumber} on ${date}` }); - } catch (error) { - res.status(500).json({ error: 'Failed to start flight tracking' }); - } -}); - -// Stop periodic updates for a flight -app.delete('/api/flights/:flightNumber/track', async (req: Request, res: Response) => { - try { - const { flightNumber } = req.params; - const { date } = req.query; - - if (!date) { - return res.status(400).json({ error: 'Flight date is required' }); - } - - const key = `${flightNumber}_${date}`; - flightService.stopPeriodicUpdates(key); - - res.json({ message: `Stopped tracking ${flightNumber} on ${date}` }); - } catch (error) { - res.status(500).json({ error: 'Failed to stop flight tracking' }); - } -}); - -app.post('/api/flights/batch', async (req: Request, res: Response) => { - try { - const { flights } = req.body; - - if (!Array.isArray(flights)) { - return res.status(400).json({ error: 'flights must be an array of {flightNumber, date} objects' }); - } - - // Validate flight objects - for (const flight of flights) { - if (!flight.flightNumber || !flight.date) { - return res.status(400).json({ error: 'Each flight must have flightNumber and date' }); - } - } - - const flightData = await flightService.getMultipleFlights(flights); - res.json(flightData); - } catch (error) { - res.status(500).json({ error: 'Failed to fetch flight data' }); - } -}); - -// Get flight tracking status -app.get('/api/flights/tracking/status', (req: Request, res: Response) => { - const status = flightTracker.getTrackingStatus(); - res.json(status); -}); - -// Schedule management routes (protected) -app.get('/api/vips/:vipId/schedule', requireAuth, async (req: Request, res: Response) => { - const { vipId } = req.params; - try { - const vipSchedule = await enhancedDataService.getSchedule(vipId); - res.json(vipSchedule); - } catch (error) { - res.status(500).json({ error: 'Failed to fetch schedule' }); - } -}); - -app.post('/api/vips/:vipId/schedule', requireAuth, requireRole(['coordinator', 'administrator']), async (req: Request, res: Response) => { - const { vipId } = req.params; - const { title, location, startTime, endTime, description, type, assignedDriverId } = req.body; - - // Validate the event - const validationErrors = scheduleValidationService.validateEvent({ - title: title || '', - location: location || '', - startTime: startTime || '', - endTime: endTime || '', - type: type || '' - }, false); - - const { critical, warnings } = scheduleValidationService.categorizeErrors(validationErrors); - - // Return validation errors if any critical errors exist - if (critical.length > 0) { - return res.status(400).json({ - error: 'Validation failed', - validationErrors: critical, - warnings: warnings, - message: scheduleValidationService.getErrorSummary(critical) - }); - } - - const newEvent = { - id: Date.now().toString(), - title, - location, - startTime, - endTime, - description: description || '', - assignedDriverId: assignedDriverId || '', - status: 'scheduled', - type - }; - - try { - const savedEvent = await enhancedDataService.addScheduleEvent(vipId, newEvent); - - // Include warnings in the response if any - const response: any = { ...savedEvent }; - if (warnings.length > 0) { - response.warnings = warnings; - } - - res.status(201).json(response); - } catch (error) { - res.status(500).json({ error: 'Failed to create schedule event' }); - } -}); - -app.put('/api/vips/:vipId/schedule/:eventId', requireAuth, requireRole(['coordinator', 'administrator']), async (req: Request, res: Response) => { - const { vipId, eventId } = req.params; - const { title, location, startTime, endTime, description, type, assignedDriverId, status } = req.body; - - // Validate the updated event (with edit flag for grace period) - const validationErrors = scheduleValidationService.validateEvent({ - title: title || '', - location: location || '', - startTime: startTime || '', - endTime: endTime || '', - type: type || '' - }, true); - - const { critical, warnings } = scheduleValidationService.categorizeErrors(validationErrors); - - // Return validation errors if any critical errors exist - if (critical.length > 0) { - return res.status(400).json({ - error: 'Validation failed', - validationErrors: critical, - warnings: warnings, - message: scheduleValidationService.getErrorSummary(critical) - }); - } - - const updatedEvent = { - id: eventId, - title, - location, - startTime, - endTime, - description: description || '', - assignedDriverId: assignedDriverId || '', - type, - status: status || 'scheduled' - }; - - try { - const savedEvent = await enhancedDataService.updateScheduleEvent(vipId, eventId, updatedEvent); - - if (!savedEvent) { - return res.status(404).json({ error: 'Event not found' }); - } - - // Include warnings in the response if any - const response: any = { ...savedEvent }; - if (warnings.length > 0) { - response.warnings = warnings; - } - - res.json(response); - } catch (error) { - res.status(500).json({ error: 'Failed to update schedule event' }); - } -}); - -app.patch('/api/vips/:vipId/schedule/:eventId/status', requireAuth, async (req: Request, res: Response) => { - const { vipId, eventId } = req.params; - const { status } = req.body; - - try { - const currentSchedule = await enhancedDataService.getSchedule(vipId); - const currentEvent = currentSchedule.find((event) => event.id === eventId); - - if (!currentEvent) { - return res.status(404).json({ error: 'Event not found' }); - } - - const updatedEvent = { ...currentEvent, status }; - const savedEvent = await enhancedDataService.updateScheduleEvent(vipId, eventId, updatedEvent); - - if (!savedEvent) { - return res.status(404).json({ error: 'Event not found' }); - } - - res.json(savedEvent); - } catch (error) { - res.status(500).json({ error: 'Failed to update event status' }); - } -}); - -app.delete('/api/vips/:vipId/schedule/:eventId', requireAuth, requireRole(['coordinator', 'administrator']), async (req: Request, res: Response) => { - const { vipId, eventId } = req.params; - - try { - const deletedEvent = await enhancedDataService.deleteScheduleEvent(vipId, eventId); - - if (!deletedEvent) { - return res.status(404).json({ error: 'Event not found' }); - } - - res.json({ message: 'Event deleted successfully', event: deletedEvent }); - } catch (error) { - res.status(500).json({ error: 'Failed to delete schedule event' }); - } -}); - -// Driver availability and conflict checking (protected) -app.post('/api/drivers/availability', requireAuth, async (req: Request, res: Response) => { - const { startTime, endTime, location } = req.body; - - if (!startTime || !endTime) { - return res.status(400).json({ error: 'startTime and endTime are required' }); - } - - try { - const allSchedules = await enhancedDataService.getAllSchedules(); - const drivers = await enhancedDataService.getDrivers(); - - const availability = driverConflictService.getDriverAvailability( - { startTime, endTime, location: location || '' }, - allSchedules as any, - drivers - ); - - res.json(availability); - } catch (error) { - res.status(500).json({ error: 'Failed to check driver availability' }); - } -}); - -// Check conflicts for specific driver assignment (protected) -app.post('/api/drivers/:driverId/conflicts', requireAuth, async (req: Request, res: Response) => { - const { driverId } = req.params; - const { startTime, endTime, location } = req.body; - - if (!startTime || !endTime) { - return res.status(400).json({ error: 'startTime and endTime are required' }); - } - - try { - const allSchedules = await enhancedDataService.getAllSchedules(); - const drivers = await enhancedDataService.getDrivers(); - - const conflicts = driverConflictService.checkDriverConflicts( - driverId, - { startTime, endTime, location: location || '' }, - allSchedules as any, - drivers - ); - - res.json({ conflicts }); - } catch (error) { - res.status(500).json({ error: 'Failed to check driver conflicts' }); - } -}); - -// Get driver's complete schedule (protected) -app.get('/api/drivers/:driverId/schedule', requireAuth, async (req: Request, res: Response) => { - const { driverId } = req.params; - - try { - const drivers = await enhancedDataService.getDrivers(); - const driver = drivers.find((d) => d.id === driverId); - if (!driver) { - return res.status(404).json({ error: 'Driver not found' }); - } - - // Get all events assigned to this driver across all VIPs - const driverSchedule: any[] = []; - const allSchedules = await enhancedDataService.getAllSchedules(); - const vips = await enhancedDataService.getVips(); - - Object.entries(allSchedules).forEach(([vipId, events]) => { - events.forEach((event) => { - if (event.assignedDriverId === driverId) { - // Get VIP name - const vip = vips.find((v) => v.id === vipId); - driverSchedule.push({ - ...event, - vipId, - vipName: vip ? vip.name : 'Unknown VIP' - }); - } - }); - }); - - // Sort by start time - driverSchedule.sort((a, b) => - new Date(a.startTime).getTime() - new Date(b.startTime).getTime() - ); - - res.json({ - driver: { - id: driver.id, - name: driver.name, - phone: driver.phone, - department: driver.department - }, - schedule: driverSchedule - }); - } catch (error) { - res.status(500).json({ error: 'Failed to fetch driver schedule' }); - } -}); - -// Admin routes -app.post('/api/admin/authenticate', (req: Request, res: Response) => { - const { password } = req.body; - - if (password === ADMIN_PASSWORD) { - res.json({ success: true }); - } else { - res.status(401).json({ error: 'Invalid password' }); - } -}); - -app.get('/api/admin/settings', async (req: Request, res: Response) => { - const adminAuth = req.headers['admin-auth']; - - if (adminAuth !== 'true') { - return res.status(401).json({ error: 'Unauthorized' }); - } - - try { - const adminSettings = await enhancedDataService.getAdminSettings(); - - // Return settings but mask API keys for display only - // IMPORTANT: Don't return the actual keys, just indicate they exist - const maskedSettings = { - apiKeys: { - aviationStackKey: adminSettings.apiKeys.aviationStackKey ? '***' + adminSettings.apiKeys.aviationStackKey.slice(-4) : '', - googleMapsKey: adminSettings.apiKeys.googleMapsKey ? '***' + adminSettings.apiKeys.googleMapsKey.slice(-4) : '', - twilioKey: adminSettings.apiKeys.twilioKey ? '***' + adminSettings.apiKeys.twilioKey.slice(-4) : '', - googleClientId: adminSettings.apiKeys.googleClientId ? '***' + adminSettings.apiKeys.googleClientId.slice(-4) : '', - googleClientSecret: adminSettings.apiKeys.googleClientSecret ? '***' + adminSettings.apiKeys.googleClientSecret.slice(-4) : '' - }, - systemSettings: adminSettings.systemSettings - }; - - res.json(maskedSettings); - } catch (error) { - res.status(500).json({ error: 'Failed to fetch admin settings' }); - } -}); - -app.post('/api/admin/settings', async (req: Request, res: Response) => { - const adminAuth = req.headers['admin-auth']; - - if (adminAuth !== 'true') { - return res.status(401).json({ error: 'Unauthorized' }); - } - - try { - const { apiKeys, systemSettings } = req.body; - const currentSettings = await enhancedDataService.getAdminSettings(); - - // Update API keys (only if provided and not masked) - if (apiKeys) { - if (apiKeys.aviationStackKey && !apiKeys.aviationStackKey.startsWith('***')) { - currentSettings.apiKeys.aviationStackKey = apiKeys.aviationStackKey; - // Update the environment variable for the flight service - process.env.AVIATIONSTACK_API_KEY = apiKeys.aviationStackKey; - } - if (apiKeys.googleMapsKey && !apiKeys.googleMapsKey.startsWith('***')) { - currentSettings.apiKeys.googleMapsKey = apiKeys.googleMapsKey; - } - if (apiKeys.twilioKey && !apiKeys.twilioKey.startsWith('***')) { - currentSettings.apiKeys.twilioKey = apiKeys.twilioKey; - } - if (apiKeys.googleClientId && !apiKeys.googleClientId.startsWith('***')) { - currentSettings.apiKeys.googleClientId = apiKeys.googleClientId; - // Update the environment variable for Google OAuth - process.env.GOOGLE_CLIENT_ID = apiKeys.googleClientId; - } - if (apiKeys.googleClientSecret && !apiKeys.googleClientSecret.startsWith('***')) { - currentSettings.apiKeys.googleClientSecret = apiKeys.googleClientSecret; - // Update the environment variable for Google OAuth - process.env.GOOGLE_CLIENT_SECRET = apiKeys.googleClientSecret; - } - } - - // Update system settings - if (systemSettings) { - currentSettings.systemSettings = { ...currentSettings.systemSettings, ...systemSettings }; - } - - // Save the updated settings - await enhancedDataService.updateAdminSettings(currentSettings); - - res.json({ success: true }); - } catch (error) { - res.status(500).json({ error: 'Failed to update admin settings' }); - } -}); - -app.post('/api/admin/test-api/:apiType', async (req: Request, res: Response) => { - const adminAuth = req.headers['admin-auth']; - - if (adminAuth !== 'true') { - return res.status(401).json({ error: 'Unauthorized' }); - } - - const { apiType } = req.params; - const { apiKey } = req.body; - - try { - switch (apiType) { - case 'aviationStackKey': - // Test AviationStack API - const testUrl = `http://api.aviationstack.com/v1/flights?access_key=${apiKey}&limit=1`; - const response = await fetch(testUrl); - - if (response.ok) { - const data = await response.json(); - if (data.error) { - res.status(400).json({ error: data.error.message || 'Invalid API key' }); - } else { - res.json({ success: true, message: 'API key is valid!' }); - } - } else { - res.status(400).json({ error: 'Failed to validate API key' }); - } - break; - - case 'googleMapsKey': - res.json({ success: true, message: 'Google Maps API testing not yet implemented' }); - break; - - case 'twilioKey': - res.json({ success: true, message: 'Twilio API testing not yet implemented' }); - break; - - default: - res.status(400).json({ error: 'Unknown API type' }); - } - } catch (error) { - res.status(500).json({ error: 'Failed to test API connection' }); - } -}); - -// JWT Key Management endpoints (admin only) -app.get('/api/admin/jwt-status', requireAuth, requireRole(['administrator']), (req: Request, res: Response) => { - const jwtKeyManager = require('./services/jwtKeyManager').default; - const status = jwtKeyManager.getStatus(); - - res.json({ - keyRotationEnabled: true, - rotationInterval: '24 hours', - gracePeriod: '24 hours', - ...status, - message: 'JWT keys are automatically rotated every 24 hours for enhanced security' - }); -}); - -app.post('/api/admin/jwt-rotate', requireAuth, requireRole(['administrator']), (req: Request, res: Response) => { - const jwtKeyManager = require('./services/jwtKeyManager').default; - - try { - jwtKeyManager.forceRotation(); - res.json({ - success: true, - message: 'JWT key rotation triggered successfully. New tokens will use the new key.' - }); - } catch (error) { - res.status(500).json({ error: 'Failed to rotate JWT keys' }); - } -}); - -// Initialize database and start server -// Add 404 handler for undefined routes -app.use(notFoundHandler); - -// Add error logging middleware -app.use(errorLogger); - -// Add global error handler (must be last!) -app.use(errorHandler); - -async function startServer() { - try { - // Initialize database schema and migrate data - await databaseService.initializeDatabase(); - console.log('āœ… Database initialization completed'); - - // Start the server - app.listen(port, () => { - console.log(`šŸš€ Server is running on port ${port}`); - console.log(`šŸ” Admin password: ${ADMIN_PASSWORD}`); - console.log(`šŸ“Š Admin dashboard: http://localhost:${port === 3000 ? 5173 : port}/admin`); - console.log(`šŸ„ Health check: http://localhost:${port}/api/health`); - console.log(`šŸ“š API docs: http://localhost:${port}/api-docs.html`); - }); - } catch (error) { - console.error('āŒ Failed to start server:', error); - process.exit(1); - } -} - -startServer(); diff --git a/backend-old-20260125/src/index.ts b/backend-old-20260125/src/index.ts deleted file mode 100644 index 16f7990..0000000 --- a/backend-old-20260125/src/index.ts +++ /dev/null @@ -1,866 +0,0 @@ -import express, { Express, Request, Response } from 'express'; -import dotenv from 'dotenv'; -import cors from 'cors'; -import authRoutes from './routes/auth'; // Keycloak routes -import { jwtCheck } from './config/keycloak'; // Keycloak JWT middleware -import { requireAuth, requireRole } from './middleware/auth'; // Auth middleware -import flightService from './services/flightService'; -import driverConflictService from './services/driverConflictService'; -import scheduleValidationService from './services/scheduleValidationService'; -import FlightTrackingScheduler from './services/flightTrackingScheduler'; -import enhancedDataService from './services/enhancedDataService'; -import databaseService from './services/databaseService'; - -dotenv.config(); - -const app: Express = express(); -const port: number = process.env.PORT ? parseInt(process.env.PORT) : 3000; - -// Middleware -app.use(cors({ - origin: [ - process.env.FRONTEND_URL || 'http://localhost:5173', - 'http://localhost:5173', - 'http://localhost:3000', - 'http://localhost', // Frontend Docker container (local testing) - 'https://bsa.madeamess.online' // Production frontend domain (where users access the site) - ], - credentials: true -})); -app.use(express.json()); -app.use(express.urlencoded({ extended: true })); - -// Authentication routes (under /api prefix to match frontend) -// Must be BEFORE jwtCheck middleware so public routes work -app.use('/api/auth', authRoutes); - -// Keycloak JWT validation middleware (applied globally to all other routes) -// Routes defined above this are NOT protected by JWT -app.use(jwtCheck); - -// Temporary admin bypass route (remove after setup) -app.get('/admin-bypass', (req: Request, res: Response) => { - res.redirect(`${process.env.FRONTEND_URL || 'http://localhost:5173'}/admin?bypass=true`); -}); - -// Serve static files from public directory -app.use(express.static('public')); - -// Enhanced health check endpoint with authentication system status -app.get('/api/health', async (req: Request, res: Response) => { - try { - const timestamp = new Date().toISOString(); - - // Check Auth0 configuration - const authConfig = { - auth0_domain: !!process.env.AUTH0_DOMAIN, - auth0_client_id: !!process.env.AUTH0_CLIENT_ID, - auth0_audience: !!process.env.AUTH0_AUDIENCE, - frontend_url: !!process.env.FRONTEND_URL, - database_url: !!process.env.DATABASE_URL - }; - - // Check database connectivity - let databaseStatus = 'unknown'; - let userCount = 0; - try { - userCount = await databaseService.getUserCount(); - databaseStatus = 'connected'; - } catch (dbError) { - databaseStatus = 'disconnected'; - console.error('Health check - Database error:', dbError); - } - - // Overall system health - const isHealthy = databaseStatus === 'connected' && - authConfig.auth0_domain && - authConfig.auth0_client_id; - - const healthData = { - status: isHealthy ? 'OK' : 'DEGRADED', - timestamp, - version: '1.0.0', - environment: process.env.NODE_ENV || 'development', - services: { - database: { - status: databaseStatus, - user_count: databaseStatus === 'connected' ? userCount : null - }, - authentication: { - provider: 'auth0', - auth0_configured: authConfig.auth0_domain && authConfig.auth0_client_id, - environment_variables: authConfig - } - }, - uptime: process.uptime(), - memory: process.memoryUsage() - }; - - // Log health check for monitoring - console.log(`šŸ„ Health Check [${timestamp}]:`, { - status: healthData.status, - database: databaseStatus, - auth0: authConfig.auth0_domain && authConfig.auth0_client_id - }); - - res.status(isHealthy ? 200 : 503).json(healthData); - - } catch (error) { - console.error('Health check error:', error); - res.status(500).json({ - status: 'ERROR', - timestamp: new Date().toISOString(), - error: error instanceof Error ? error.message : 'Unknown error' - }); - } -}); - -// Data is now persisted using dataService - no more in-memory storage! - -// Admin password - MUST be set via environment variable in production -const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'CHANGE_ME_ADMIN_PASSWORD'; - -// Initialize flight tracking scheduler -const flightTracker = new FlightTrackingScheduler(flightService); - -// VIP routes (protected) -app.post('/api/vips', requireAuth, requireRole(['coordinator', 'administrator']), async (req: Request, res: Response) => { - // Create a new VIP - const { - name, - organization, - department, // New: Office of Development or Admin - transportMode, - flightNumber, // Legacy single flight - flights, // New: array of flights - expectedArrival, - needsAirportPickup, - needsVenueTransport, - notes - } = req.body; - - const newVip = { - id: Date.now().toString(), // Simple ID generation - name, - organization, - department: department || 'Office of Development', // Default to Office of Development - transportMode: transportMode || 'flight', - // Support both legacy single flight and new multiple flights - flightNumber: transportMode === 'flight' && !flights ? flightNumber : undefined, - flights: transportMode === 'flight' && flights ? flights : undefined, - expectedArrival: transportMode === 'self-driving' ? expectedArrival : undefined, - arrivalTime: transportMode === 'flight' ? undefined : expectedArrival, // Legacy field for flight arrivals - needsAirportPickup: transportMode === 'flight' ? (needsAirportPickup !== false) : false, - needsVenueTransport: needsVenueTransport !== false, // Default to true - assignedDriverIds: [], - notes: notes || '', - schedule: [] - }; - - const savedVip = await enhancedDataService.addVip(newVip); - - // Add flights to tracking scheduler if applicable - if (savedVip.transportMode === 'flight' && savedVip.flights && savedVip.flights.length > 0) { - flightTracker.addVipFlights(savedVip.id, savedVip.name, savedVip.flights); - } - - res.status(201).json(savedVip); -}); - -app.get('/api/vips', requireAuth, async (req: Request, res: Response) => { - try { - // Fetch all VIPs - const vips = await enhancedDataService.getVips(); - res.json(vips); - } catch (error) { - res.status(500).json({ error: 'Failed to fetch VIPs' }); - } -}); - -app.put('/api/vips/:id', requireAuth, requireRole(['coordinator', 'administrator']), async (req: Request, res: Response) => { - // Update a VIP - const { id } = req.params; - const { - name, - organization, - department, // New: Office of Development or Admin - transportMode, - flightNumber, // Legacy single flight - flights, // New: array of flights - expectedArrival, - needsAirportPickup, - needsVenueTransport, - notes - } = req.body; - - try { - const updatedVip = { - name, - organization, - department: department || 'Office of Development', - transportMode: transportMode || 'flight', - // Support both legacy single flight and new multiple flights - flights: transportMode === 'flight' && flights ? flights : undefined, - expectedArrival: transportMode === 'self-driving' ? expectedArrival : undefined, - needsAirportPickup: transportMode === 'flight' ? (needsAirportPickup !== false) : false, - needsVenueTransport: needsVenueTransport !== false, - notes: notes || '' - }; - - const savedVip = await enhancedDataService.updateVip(id, updatedVip); - - if (!savedVip) { - return res.status(404).json({ error: 'VIP not found' }); - } - - // Update flight tracking if needed - if (savedVip.transportMode === 'flight') { - // Remove old flights - flightTracker.removeVipFlights(id); - - // Add new flights if any - if (savedVip.flights && savedVip.flights.length > 0) { - flightTracker.addVipFlights(savedVip.id, savedVip.name, savedVip.flights); - } - } - - res.json(savedVip); - } catch (error) { - res.status(500).json({ error: 'Failed to update VIP' }); - } -}); - -app.delete('/api/vips/:id', requireAuth, requireRole(['coordinator', 'administrator']), async (req: Request, res: Response) => { - // Delete a VIP - const { id } = req.params; - - try { - const deletedVip = await enhancedDataService.deleteVip(id); - - if (!deletedVip) { - return res.status(404).json({ error: 'VIP not found' }); - } - - // Remove from flight tracking - flightTracker.removeVipFlights(id); - - res.json({ message: 'VIP deleted successfully', vip: deletedVip }); - } catch (error) { - res.status(500).json({ error: 'Failed to delete VIP' }); - } -}); - -// Driver routes (protected) -app.post('/api/drivers', requireAuth, requireRole(['coordinator', 'administrator']), async (req: Request, res: Response) => { - // Create a new driver - const { name, phone, currentLocation, department } = req.body; - - const newDriver = { - id: Date.now().toString(), - name, - phone, - department: department || 'Office of Development', // Default to Office of Development - currentLocation: currentLocation || { lat: 0, lng: 0 }, - assignedVipIds: [] - }; - - try { - const savedDriver = await enhancedDataService.addDriver(newDriver); - res.status(201).json(savedDriver); - } catch (error) { - res.status(500).json({ error: 'Failed to create driver' }); - } -}); - -app.get('/api/drivers', requireAuth, async (req: Request, res: Response) => { - try { - // Fetch all drivers - const drivers = await enhancedDataService.getDrivers(); - res.json(drivers); - } catch (error) { - res.status(500).json({ error: 'Failed to fetch drivers' }); - } -}); - -app.put('/api/drivers/:id', requireAuth, requireRole(['coordinator', 'administrator']), async (req: Request, res: Response) => { - // Update a driver - const { id } = req.params; - const { name, phone, currentLocation, department } = req.body; - - try { - const updatedDriver = { - name, - phone, - department: department || 'Office of Development', - currentLocation: currentLocation || { lat: 0, lng: 0 } - }; - - const savedDriver = await enhancedDataService.updateDriver(id, updatedDriver); - - if (!savedDriver) { - return res.status(404).json({ error: 'Driver not found' }); - } - - res.json(savedDriver); - } catch (error) { - res.status(500).json({ error: 'Failed to update driver' }); - } -}); - -app.delete('/api/drivers/:id', requireAuth, requireRole(['coordinator', 'administrator']), async (req: Request, res: Response) => { - // Delete a driver - const { id } = req.params; - - try { - const deletedDriver = await enhancedDataService.deleteDriver(id); - - if (!deletedDriver) { - return res.status(404).json({ error: 'Driver not found' }); - } - - res.json({ message: 'Driver deleted successfully', driver: deletedDriver }); - } catch (error) { - res.status(500).json({ error: 'Failed to delete driver' }); - } -}); - -// Enhanced flight tracking routes with date specificity -app.get('/api/flights/:flightNumber', async (req: Request, res: Response) => { - try { - const { flightNumber } = req.params; - const { date, departureAirport, arrivalAirport } = req.query; - - // Default to today if no date provided - const flightDate = (date as string) || new Date().toISOString().split('T')[0]; - - const flightData = await flightService.getFlightInfo({ - flightNumber, - date: flightDate, - departureAirport: departureAirport as string, - arrivalAirport: arrivalAirport as string - }); - - if (flightData) { - // Always return flight data for validation, even if date doesn't match - res.json(flightData); - } else { - // Only return 404 if the flight number itself is invalid - res.status(404).json({ error: 'Invalid flight number - this flight does not exist' }); - } - } catch (error) { - res.status(500).json({ error: 'Failed to fetch flight data' }); - } -}); - -// Start periodic updates for a flight -app.post('/api/flights/:flightNumber/track', async (req: Request, res: Response) => { - try { - const { flightNumber } = req.params; - const { date, intervalMinutes = 5 } = req.body; - - if (!date) { - return res.status(400).json({ error: 'Flight date is required' }); - } - - flightService.startPeriodicUpdates({ - flightNumber, - date - }, intervalMinutes); - - res.json({ message: `Started tracking ${flightNumber} on ${date}` }); - } catch (error) { - res.status(500).json({ error: 'Failed to start flight tracking' }); - } -}); - -// Stop periodic updates for a flight -app.delete('/api/flights/:flightNumber/track', async (req: Request, res: Response) => { - try { - const { flightNumber } = req.params; - const { date } = req.query; - - if (!date) { - return res.status(400).json({ error: 'Flight date is required' }); - } - - const key = `${flightNumber}_${date}`; - flightService.stopPeriodicUpdates(key); - - res.json({ message: `Stopped tracking ${flightNumber} on ${date}` }); - } catch (error) { - res.status(500).json({ error: 'Failed to stop flight tracking' }); - } -}); - -app.post('/api/flights/batch', async (req: Request, res: Response) => { - try { - const { flights } = req.body; - - if (!Array.isArray(flights)) { - return res.status(400).json({ error: 'flights must be an array of {flightNumber, date} objects' }); - } - - // Validate flight objects - for (const flight of flights) { - if (!flight.flightNumber || !flight.date) { - return res.status(400).json({ error: 'Each flight must have flightNumber and date' }); - } - } - - const flightData = await flightService.getMultipleFlights(flights); - res.json(flightData); - } catch (error) { - res.status(500).json({ error: 'Failed to fetch flight data' }); - } -}); - -// Get flight tracking status -app.get('/api/flights/tracking/status', (req: Request, res: Response) => { - const status = flightTracker.getTrackingStatus(); - res.json(status); -}); - -// Schedule management routes (protected) -app.get('/api/vips/:vipId/schedule', requireAuth, async (req: Request, res: Response) => { - const { vipId } = req.params; - try { - const vipSchedule = await enhancedDataService.getSchedule(vipId); - res.json(vipSchedule); - } catch (error) { - res.status(500).json({ error: 'Failed to fetch schedule' }); - } -}); - -app.post('/api/vips/:vipId/schedule', requireAuth, requireRole(['coordinator', 'administrator']), async (req: Request, res: Response) => { - const { vipId } = req.params; - const { title, location, startTime, endTime, description, type, assignedDriverId } = req.body; - - // Validate the event - const validationErrors = scheduleValidationService.validateEvent({ - title: title || '', - location: location || '', - startTime: startTime || '', - endTime: endTime || '', - type: type || '' - }, false); - - const { critical, warnings } = scheduleValidationService.categorizeErrors(validationErrors); - - // Return validation errors if any critical errors exist - if (critical.length > 0) { - return res.status(400).json({ - error: 'Validation failed', - validationErrors: critical, - warnings: warnings, - message: scheduleValidationService.getErrorSummary(critical) - }); - } - - const newEvent = { - id: Date.now().toString(), - title, - location, - startTime, - endTime, - description: description || '', - assignedDriverId: assignedDriverId || '', - status: 'scheduled', - type - }; - - try { - const savedEvent = await enhancedDataService.addScheduleEvent(vipId, newEvent); - - // Include warnings in the response if any - const response: any = { ...savedEvent }; - if (warnings.length > 0) { - response.warnings = warnings; - } - - res.status(201).json(response); - } catch (error) { - res.status(500).json({ error: 'Failed to create schedule event' }); - } -}); - -app.put('/api/vips/:vipId/schedule/:eventId', requireAuth, requireRole(['coordinator', 'administrator']), async (req: Request, res: Response) => { - const { vipId, eventId } = req.params; - const { title, location, startTime, endTime, description, type, assignedDriverId, status } = req.body; - - // Validate the updated event (with edit flag for grace period) - const validationErrors = scheduleValidationService.validateEvent({ - title: title || '', - location: location || '', - startTime: startTime || '', - endTime: endTime || '', - type: type || '' - }, true); - - const { critical, warnings } = scheduleValidationService.categorizeErrors(validationErrors); - - // Return validation errors if any critical errors exist - if (critical.length > 0) { - return res.status(400).json({ - error: 'Validation failed', - validationErrors: critical, - warnings: warnings, - message: scheduleValidationService.getErrorSummary(critical) - }); - } - - const updatedEvent = { - id: eventId, - title, - location, - startTime, - endTime, - description: description || '', - assignedDriverId: assignedDriverId || '', - type, - status: status || 'scheduled' - }; - - try { - const savedEvent = await enhancedDataService.updateScheduleEvent(vipId, eventId, updatedEvent); - - if (!savedEvent) { - return res.status(404).json({ error: 'Event not found' }); - } - - // Include warnings in the response if any - const response: any = { ...savedEvent }; - if (warnings.length > 0) { - response.warnings = warnings; - } - - res.json(response); - } catch (error) { - res.status(500).json({ error: 'Failed to update schedule event' }); - } -}); - -app.patch('/api/vips/:vipId/schedule/:eventId/status', requireAuth, async (req: Request, res: Response) => { - const { vipId, eventId } = req.params; - const { status } = req.body; - - try { - const currentSchedule = await enhancedDataService.getSchedule(vipId); - const currentEvent = currentSchedule.find((event: any) => event.id === eventId); - - if (!currentEvent) { - return res.status(404).json({ error: 'Event not found' }); - } - - const updatedEvent = { ...currentEvent, status }; - const savedEvent = await enhancedDataService.updateScheduleEvent(vipId, eventId, updatedEvent); - - if (!savedEvent) { - return res.status(404).json({ error: 'Event not found' }); - } - - res.json(savedEvent); - } catch (error) { - res.status(500).json({ error: 'Failed to update event status' }); - } -}); - -app.delete('/api/vips/:vipId/schedule/:eventId', requireAuth, requireRole(['coordinator', 'administrator']), async (req: Request, res: Response) => { - const { vipId, eventId } = req.params; - - try { - const deletedEvent = await enhancedDataService.deleteScheduleEvent(vipId, eventId); - - if (!deletedEvent) { - return res.status(404).json({ error: 'Event not found' }); - } - - res.json({ message: 'Event deleted successfully', event: deletedEvent }); - } catch (error) { - res.status(500).json({ error: 'Failed to delete schedule event' }); - } -}); - -// Driver availability and conflict checking (protected) -app.post('/api/drivers/availability', requireAuth, async (req: Request, res: Response) => { - const { startTime, endTime, location } = req.body; - - if (!startTime || !endTime) { - return res.status(400).json({ error: 'startTime and endTime are required' }); - } - - try { - const allSchedules = await enhancedDataService.getAllSchedules(); - const drivers = await enhancedDataService.getDrivers(); - - const availability = driverConflictService.getDriverAvailability( - { startTime, endTime, location: location || '' }, - allSchedules as any, - drivers - ); - - res.json(availability); - } catch (error) { - res.status(500).json({ error: 'Failed to check driver availability' }); - } -}); - -// Check conflicts for specific driver assignment (protected) -app.post('/api/drivers/:driverId/conflicts', requireAuth, async (req: Request, res: Response) => { - const { driverId } = req.params; - const { startTime, endTime, location } = req.body; - - if (!startTime || !endTime) { - return res.status(400).json({ error: 'startTime and endTime are required' }); - } - - try { - const allSchedules = await enhancedDataService.getAllSchedules(); - const drivers = await enhancedDataService.getDrivers(); - - const conflicts = driverConflictService.checkDriverConflicts( - driverId, - { startTime, endTime, location: location || '' }, - allSchedules as any, - drivers - ); - - res.json({ conflicts }); - } catch (error) { - res.status(500).json({ error: 'Failed to check driver conflicts' }); - } -}); - -// Get driver's complete schedule (protected) -app.get('/api/drivers/:driverId/schedule', requireAuth, async (req: Request, res: Response) => { - const { driverId } = req.params; - - try { - const drivers = await enhancedDataService.getDrivers(); - const driver = drivers.find((d: any) => d.id === driverId); - if (!driver) { - return res.status(404).json({ error: 'Driver not found' }); - } - - // Get all events assigned to this driver across all VIPs - const driverSchedule: any[] = []; - const allSchedules = await enhancedDataService.getAllSchedules(); - const vips = await enhancedDataService.getVips(); - - Object.entries(allSchedules).forEach(([vipId, events]: [string, any]) => { - events.forEach((event: any) => { - if (event.assignedDriverId === driverId) { - // Get VIP name - const vip = vips.find((v: any) => v.id === vipId); - driverSchedule.push({ - ...event, - vipId, - vipName: vip ? vip.name : 'Unknown VIP' - }); - } - }); - }); - - // Sort by start time - driverSchedule.sort((a, b) => - new Date(a.startTime).getTime() - new Date(b.startTime).getTime() - ); - - res.json({ - driver: { - id: driver.id, - name: driver.name, - phone: driver.phone, - department: driver.department - }, - schedule: driverSchedule - }); - } catch (error) { - res.status(500).json({ error: 'Failed to fetch driver schedule' }); - } -}); - -// Admin routes -app.post('/api/admin/authenticate', (req: Request, res: Response) => { - const { password } = req.body; - - if (password === ADMIN_PASSWORD) { - res.json({ success: true }); - } else { - res.status(401).json({ error: 'Invalid password' }); - } -}); - -app.get('/api/admin/settings', async (req: Request, res: Response) => { - const adminAuth = req.headers['admin-auth']; - - if (adminAuth !== 'true') { - return res.status(401).json({ error: 'Unauthorized' }); - } - - try { - const adminSettings = await enhancedDataService.getAdminSettings(); - - // Return settings but mask API keys for display only - // IMPORTANT: Don't return the actual keys, just indicate they exist - const maskedSettings = { - apiKeys: { - aviationStackKey: adminSettings.apiKeys.aviationStackKey ? '***' + adminSettings.apiKeys.aviationStackKey.slice(-4) : '', - googleMapsKey: adminSettings.apiKeys.googleMapsKey ? '***' + adminSettings.apiKeys.googleMapsKey.slice(-4) : '', - twilioKey: adminSettings.apiKeys.twilioKey ? '***' + adminSettings.apiKeys.twilioKey.slice(-4) : '', - googleClientId: adminSettings.apiKeys.googleClientId ? '***' + adminSettings.apiKeys.googleClientId.slice(-4) : '', - googleClientSecret: adminSettings.apiKeys.googleClientSecret ? '***' + adminSettings.apiKeys.googleClientSecret.slice(-4) : '' - }, - systemSettings: adminSettings.systemSettings - }; - - res.json(maskedSettings); - } catch (error) { - res.status(500).json({ error: 'Failed to fetch admin settings' }); - } -}); - -app.post('/api/admin/settings', async (req: Request, res: Response) => { - const adminAuth = req.headers['admin-auth']; - - if (adminAuth !== 'true') { - return res.status(401).json({ error: 'Unauthorized' }); - } - - try { - const { apiKeys, systemSettings } = req.body; - const currentSettings = await enhancedDataService.getAdminSettings(); - - // Update API keys (only if provided and not masked) - if (apiKeys) { - if (apiKeys.aviationStackKey && !apiKeys.aviationStackKey.startsWith('***')) { - currentSettings.apiKeys.aviationStackKey = apiKeys.aviationStackKey; - // Update the environment variable for the flight service - process.env.AVIATIONSTACK_API_KEY = apiKeys.aviationStackKey; - } - if (apiKeys.googleMapsKey && !apiKeys.googleMapsKey.startsWith('***')) { - currentSettings.apiKeys.googleMapsKey = apiKeys.googleMapsKey; - } - if (apiKeys.twilioKey && !apiKeys.twilioKey.startsWith('***')) { - currentSettings.apiKeys.twilioKey = apiKeys.twilioKey; - } - if (apiKeys.googleClientId && !apiKeys.googleClientId.startsWith('***')) { - currentSettings.apiKeys.googleClientId = apiKeys.googleClientId; - // Update the environment variable for Google OAuth - process.env.GOOGLE_CLIENT_ID = apiKeys.googleClientId; - } - if (apiKeys.googleClientSecret && !apiKeys.googleClientSecret.startsWith('***')) { - currentSettings.apiKeys.googleClientSecret = apiKeys.googleClientSecret; - // Update the environment variable for Google OAuth - process.env.GOOGLE_CLIENT_SECRET = apiKeys.googleClientSecret; - } - } - - // Update system settings - if (systemSettings) { - currentSettings.systemSettings = { ...currentSettings.systemSettings, ...systemSettings }; - } - - // Save the updated settings - await enhancedDataService.updateAdminSettings(currentSettings); - - res.json({ success: true }); - } catch (error) { - res.status(500).json({ error: 'Failed to update admin settings' }); - } -}); - -app.post('/api/admin/test-api/:apiType', async (req: Request, res: Response) => { - const adminAuth = req.headers['admin-auth']; - - if (adminAuth !== 'true') { - return res.status(401).json({ error: 'Unauthorized' }); - } - - const { apiType } = req.params; - const { apiKey } = req.body; - - try { - switch (apiType) { - case 'aviationStackKey': - // Test AviationStack API - const testUrl = `http://api.aviationstack.com/v1/flights?access_key=${apiKey}&limit=1`; - const response = await fetch(testUrl); - - if (response.ok) { - const data: any = await response.json(); - if (data.error) { - res.status(400).json({ error: data.error.message || 'Invalid API key' }); - } else { - res.json({ success: true, message: 'API key is valid!' }); - } - } else { - res.status(400).json({ error: 'Failed to validate API key' }); - } - break; - - case 'googleMapsKey': - res.json({ success: true, message: 'Google Maps API testing not yet implemented' }); - break; - - case 'twilioKey': - res.json({ success: true, message: 'Twilio API testing not yet implemented' }); - break; - - default: - res.status(400).json({ error: 'Unknown API type' }); - } - } catch (error) { - res.status(500).json({ error: 'Failed to test API connection' }); - } -}); - -// JWT Key Management endpoints (admin only) -app.get('/api/admin/jwt-status', requireAuth, requireRole(['administrator']), (req: Request, res: Response) => { - const jwtKeyManager = require('./services/jwtKeyManager').default; - const status = jwtKeyManager.getStatus(); - - res.json({ - keyRotationEnabled: true, - rotationInterval: '24 hours', - gracePeriod: '24 hours', - ...status, - message: 'JWT keys are automatically rotated every 24 hours for enhanced security' - }); -}); - -app.post('/api/admin/jwt-rotate', requireAuth, requireRole(['administrator']), (req: Request, res: Response) => { - const jwtKeyManager = require('./services/jwtKeyManager').default; - - try { - jwtKeyManager.forceRotation(); - res.json({ - success: true, - message: 'JWT key rotation triggered successfully. New tokens will use the new key.' - }); - } catch (error) { - res.status(500).json({ error: 'Failed to rotate JWT keys' }); - } -}); - -// Initialize database and start server -async function startServer() { - try { - // Initialize database schema and migrate data - await databaseService.initializeDatabase(); - console.log('āœ… Database initialization completed'); - - // Start the server - app.listen(port, () => { - console.log(`šŸš€ Server is running on port ${port}`); - console.log(`šŸ” Admin password: ${ADMIN_PASSWORD}`); - console.log(`šŸ“Š Admin dashboard: http://localhost:${port === 3000 ? 5173 : port}/admin`); - console.log(`šŸ„ Health check: http://localhost:${port}/api/health`); - console.log(`šŸ“š API docs: http://localhost:${port}/api-docs.html`); - }); - } catch (error) { - console.error('āŒ Failed to start server:', error); - process.exit(1); - } -} - -startServer(); diff --git a/backend-old-20260125/src/indexSimplified.ts b/backend-old-20260125/src/indexSimplified.ts deleted file mode 100644 index 0104bc3..0000000 --- a/backend-old-20260125/src/indexSimplified.ts +++ /dev/null @@ -1,263 +0,0 @@ -import express from 'express'; -import cors from 'cors'; -import dotenv from 'dotenv'; -import authService from './services/authService'; -import dataService from './services/unifiedDataService'; -import { validate, schemas } from './middleware/simpleValidation'; -import { errorHandler, notFoundHandler } from './middleware/errorHandler'; - -dotenv.config(); - -const app = express(); -const port = process.env.PORT || 3000; - -// Middleware -app.use(cors({ - origin: [ - process.env.FRONTEND_URL || 'http://localhost:5173', - 'https://bsa.madeamess.online' - ], - credentials: true -})); -app.use(express.json()); -app.use(express.static('public')); - -// Health check -app.get('/api/health', (req, res) => { - res.json({ - status: 'OK', - timestamp: new Date().toISOString(), - version: '2.0.0' // Simplified version - }); -}); - -// Auth routes -app.get('/auth/google', (req, res) => { - res.redirect(authService.getGoogleAuthUrl()); -}); - -app.post('/auth/google/callback', async (req, res) => { - try { - const { code } = req.body; - const { user, token } = await authService.handleGoogleAuth(code); - res.json({ user, token }); - } catch (error) { - res.status(400).json({ error: 'Authentication failed' }); - } -}); - -app.get('/auth/me', authService.requireAuth, (req: any, res) => { - res.json(req.user); -}); - -app.post('/auth/logout', (req, res) => { - res.json({ message: 'Logged out successfully' }); -}); - -// VIP routes -app.get('/api/vips', authService.requireAuth, async (req, res, next) => { - try { - const vips = await dataService.getVips(); - res.json(vips); - } catch (error) { - next(error); - } -}); - -app.get('/api/vips/:id', authService.requireAuth, async (req, res, next) => { - try { - const vip = await dataService.getVipById(req.params.id); - if (!vip) return res.status(404).json({ error: 'VIP not found' }); - res.json(vip); - } catch (error) { - next(error); - } -}); - -app.post('/api/vips', - authService.requireAuth, - authService.requireRole(['coordinator', 'administrator']), - validate(schemas.createVip), - async (req, res, next) => { - try { - const vip = await dataService.createVip(req.body); - res.status(201).json(vip); - } catch (error) { - next(error); - } - } -); - -app.put('/api/vips/:id', - authService.requireAuth, - authService.requireRole(['coordinator', 'administrator']), - validate(schemas.updateVip), - async (req, res, next) => { - try { - const vip = await dataService.updateVip(req.params.id, req.body); - if (!vip) return res.status(404).json({ error: 'VIP not found' }); - res.json(vip); - } catch (error) { - next(error); - } - } -); - -app.delete('/api/vips/:id', - authService.requireAuth, - authService.requireRole(['coordinator', 'administrator']), - async (req, res, next) => { - try { - const vip = await dataService.deleteVip(req.params.id); - if (!vip) return res.status(404).json({ error: 'VIP not found' }); - res.json({ message: 'VIP deleted successfully' }); - } catch (error) { - next(error); - } - } -); - -// Driver routes -app.get('/api/drivers', authService.requireAuth, async (req, res, next) => { - try { - const drivers = await dataService.getDrivers(); - res.json(drivers); - } catch (error) { - next(error); - } -}); - -app.post('/api/drivers', - authService.requireAuth, - authService.requireRole(['coordinator', 'administrator']), - validate(schemas.createDriver), - async (req, res, next) => { - try { - const driver = await dataService.createDriver(req.body); - res.status(201).json(driver); - } catch (error) { - next(error); - } - } -); - -app.put('/api/drivers/:id', - authService.requireAuth, - authService.requireRole(['coordinator', 'administrator']), - validate(schemas.updateDriver), - async (req, res, next) => { - try { - const driver = await dataService.updateDriver(req.params.id, req.body); - if (!driver) return res.status(404).json({ error: 'Driver not found' }); - res.json(driver); - } catch (error) { - next(error); - } - } -); - -app.delete('/api/drivers/:id', - authService.requireAuth, - authService.requireRole(['coordinator', 'administrator']), - async (req, res, next) => { - try { - const driver = await dataService.deleteDriver(req.params.id); - if (!driver) return res.status(404).json({ error: 'Driver not found' }); - res.json({ message: 'Driver deleted successfully' }); - } catch (error) { - next(error); - } - } -); - -// Schedule routes -app.get('/api/vips/:vipId/schedule', authService.requireAuth, async (req, res, next) => { - try { - const schedule = await dataService.getScheduleByVipId(req.params.vipId); - res.json(schedule); - } catch (error) { - next(error); - } -}); - -app.post('/api/vips/:vipId/schedule', - authService.requireAuth, - authService.requireRole(['coordinator', 'administrator']), - validate(schemas.createScheduleEvent), - async (req, res, next) => { - try { - const event = await dataService.createScheduleEvent(req.params.vipId, req.body); - res.status(201).json(event); - } catch (error) { - next(error); - } - } -); - -app.put('/api/vips/:vipId/schedule/:eventId', - authService.requireAuth, - authService.requireRole(['coordinator', 'administrator']), - validate(schemas.updateScheduleEvent), - async (req, res, next) => { - try { - const event = await dataService.updateScheduleEvent(req.params.eventId, req.body); - if (!event) return res.status(404).json({ error: 'Event not found' }); - res.json(event); - } catch (error) { - next(error); - } - } -); - -app.delete('/api/vips/:vipId/schedule/:eventId', - authService.requireAuth, - authService.requireRole(['coordinator', 'administrator']), - async (req, res, next) => { - try { - const event = await dataService.deleteScheduleEvent(req.params.eventId); - if (!event) return res.status(404).json({ error: 'Event not found' }); - res.json({ message: 'Event deleted successfully' }); - } catch (error) { - next(error); - } - } -); - -// Admin routes (simplified) -app.get('/api/admin/settings', - authService.requireAuth, - authService.requireRole(['administrator']), - async (req, res, next) => { - try { - const settings = await dataService.getAdminSettings(); - res.json(settings); - } catch (error) { - next(error); - } - } -); - -app.post('/api/admin/settings', - authService.requireAuth, - authService.requireRole(['administrator']), - async (req, res, next) => { - try { - const { key, value } = req.body; - await dataService.updateAdminSetting(key, value); - res.json({ message: 'Setting updated successfully' }); - } catch (error) { - next(error); - } - } -); - -// Error handling -app.use(notFoundHandler); -app.use(errorHandler); - -// Start server -app.listen(port, () => { - console.log(`šŸš€ Server running on port ${port}`); - console.log(`šŸ„ Health check: http://localhost:${port}/api/health`); - console.log(`šŸ“š API docs: http://localhost:${port}/api-docs.html`); -}); \ No newline at end of file diff --git a/backend-old-20260125/src/middleware/auth.ts b/backend-old-20260125/src/middleware/auth.ts deleted file mode 100644 index b93cfd4..0000000 --- a/backend-old-20260125/src/middleware/auth.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { Request, Response, NextFunction } from 'express'; -import { getUserFromToken } from '../config/keycloak'; - -/** - * Middleware to require authentication - * Checks if JWT is present and valid - */ -export function requireAuth(req: Request, res: Response, next: NextFunction) { - if (!req.auth) { - return res.status(401).json({ - error: 'unauthorized', - message: 'Authentication required. Please log in.', - }); - } - - next(); -} - -/** - * Middleware to require specific role - * @param allowedRoles - Array of allowed roles or single role - */ -export function requireRole(...allowedRoles: string[]) { - return (req: Request, res: Response, next: NextFunction) => { - const user = getUserFromToken(req); - - if (!user) { - return res.status(401).json({ - error: 'unauthorized', - message: 'Authentication required.', - }); - } - - if (!user.role || !allowedRoles.includes(user.role)) { - return res.status(403).json({ - error: 'forbidden', - message: `Access denied. Required role: ${allowedRoles.join(' or ')}`, - userRole: user.role, - }); - } - - next(); - }; -} - -/** - * Middleware to require specific permission - * @param requiredPermissions - Array of required permissions - */ -export function requirePermission(...requiredPermissions: string[]) { - return (req: Request, res: Response, next: NextFunction) => { - const user = getUserFromToken(req); - - if (!user) { - return res.status(401).json({ - error: 'unauthorized', - message: 'Authentication required.', - }); - } - - const userPermissions = user.permissions || []; - const hasPermission = requiredPermissions.some(permission => - userPermissions.includes(permission) - ); - - if (!hasPermission) { - return res.status(403).json({ - error: 'forbidden', - message: `Access denied. Required permission: ${requiredPermissions.join(' or ')}`, - userPermissions, - }); - } - - next(); - }; -} - -/** - * Middleware to check if user has been approved - * Redirects pending users to pending approval page - */ -export function checkApprovalStatus(req: Request, res: Response, next: NextFunction) { - const user = getUserFromToken(req); - - if (!user) { - return res.status(401).json({ - error: 'unauthorized', - message: 'Authentication required.', - }); - } - - const approvalStatus = user.approvalStatus; - - if (approvalStatus === 'denied') { - return res.status(403).json({ - error: 'access_denied', - message: 'Your access has been denied by an administrator.', - }); - } - - if (approvalStatus === 'pending') { - return res.status(403).json({ - error: 'approval_pending', - message: 'Your account is pending approval from an administrator.', - redirectTo: '/pending-approval', - }); - } - - // If approved or no status (first user/admin), continue - next(); -} - -/** - * Middleware to require administrator role - */ -export const requireAdmin = requireRole('administrator'); - -/** - * Middleware to require coordinator or administrator role - */ -export const requireCoordinatorOrAdmin = requireRole('coordinator', 'administrator'); - -/** - * Optional auth middleware - doesn't fail if no auth present - * Useful for routes that work with or without authentication - */ -export function optionalAuth(req: Request, res: Response, next: NextFunction) { - // JWT middleware already ran with credentialsRequired: false - // Just continue regardless of auth state - next(); -} - -console.log('šŸ›”ļø Auth middleware initialized'); diff --git a/backend-old-20260125/src/middleware/errorHandler.ts b/backend-old-20260125/src/middleware/errorHandler.ts deleted file mode 100644 index 2a2f1a9..0000000 --- a/backend-old-20260125/src/middleware/errorHandler.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { Request, Response, NextFunction } from 'express'; -import { AppError, ErrorResponse } from '../types/errors'; - -export const errorHandler = ( - err: Error | AppError, - req: Request, - res: Response, - next: NextFunction -): void => { - // 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 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: 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); -}; - -// Async error wrapper to catch errors in async route handlers -export const asyncHandler = (fn: Function) => { - return (req: Request, res: Response, next: NextFunction) => { - Promise.resolve(fn(req, res, next)).catch(next); - }; -}; - -// 404 Not Found handler -export const notFoundHandler = (req: Request, res: Response): void => { - const errorResponse: 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); -}; \ No newline at end of file diff --git a/backend-old-20260125/src/middleware/logger.ts b/backend-old-20260125/src/middleware/logger.ts deleted file mode 100644 index a3b0037..0000000 --- a/backend-old-20260125/src/middleware/logger.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Request, Response, NextFunction } from 'express'; -import { AuthRequest } from '../types/api'; - -interface LogContext { - requestId: string; - method: string; - url: string; - ip: string; - userAgent?: string; - userId?: string; -} - -// Extend Express Request with our custom properties -declare module 'express' { - interface Request { - requestId?: string; - user?: { - id: string; - email: string; - name: string; - role: string; - }; - } -} - -// Generate a simple request ID -const generateRequestId = (): string => { - return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; -}; - -// Request logger middleware -export const requestLogger = (req: Request, res: Response, next: NextFunction): void => { - const requestId = generateRequestId(); - - // Attach request ID to request object - req.requestId = requestId; - - const startTime = Date.now(); - - // Log request - const logContext: 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: unknown): Response { - 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(); -}; - -// Error logger (to be used before error handler) -export const errorLogger = (err: Error, req: Request, res: Response, next: NextFunction): void => { - 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); -}; \ No newline at end of file diff --git a/backend-old-20260125/src/middleware/simpleValidation.ts b/backend-old-20260125/src/middleware/simpleValidation.ts deleted file mode 100644 index a656ac1..0000000 --- a/backend-old-20260125/src/middleware/simpleValidation.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { z } from 'zod'; -import { Request, Response, NextFunction } from 'express'; - -// Simplified validation schemas - removed unnecessary complexity -export const schemas = { - // VIP schemas - createVip: z.object({ - name: z.string().min(1).max(100), - organization: z.string().max(100).optional(), - department: z.enum(['Office of Development', 'Admin']).default('Office of Development'), - transportMode: z.enum(['flight', 'self-driving']).default('flight'), - flights: z.array(z.object({ - flightNumber: z.string(), - airline: z.string().optional(), - scheduledArrival: z.string(), - scheduledDeparture: z.string().optional() - })).optional(), - expectedArrival: z.string().optional(), - needsAirportPickup: z.boolean().default(true), - needsVenueTransport: z.boolean().default(true), - notes: z.string().max(500).optional() - }), - - updateVip: z.object({ - name: z.string().min(1).max(100).optional(), - organization: z.string().max(100).optional(), - department: z.enum(['Office of Development', 'Admin']).optional(), - transportMode: z.enum(['flight', 'self-driving']).optional(), - flights: z.array(z.object({ - flightNumber: z.string(), - airline: z.string().optional(), - scheduledArrival: z.string(), - scheduledDeparture: z.string().optional() - })).optional(), - expectedArrival: z.string().optional(), - needsAirportPickup: z.boolean().optional(), - needsVenueTransport: z.boolean().optional(), - notes: z.string().max(500).optional() - }), - - // Driver schemas - createDriver: z.object({ - name: z.string().min(1).max(100), - email: z.string().email().optional(), - phone: z.string(), - vehicleInfo: z.string().max(200).optional(), - status: z.enum(['available', 'assigned', 'unavailable']).default('available') - }), - - updateDriver: z.object({ - name: z.string().min(1).max(100).optional(), - email: z.string().email().optional(), - phone: z.string().optional(), - vehicleInfo: z.string().max(200).optional(), - status: z.enum(['available', 'assigned', 'unavailable']).optional() - }), - - // Schedule schemas - createScheduleEvent: z.object({ - driverId: z.string().optional(), - eventTime: z.string(), - eventType: z.enum(['pickup', 'dropoff', 'custom']), - location: z.string().min(1).max(200), - notes: z.string().max(500).optional() - }), - - updateScheduleEvent: z.object({ - driverId: z.string().optional(), - eventTime: z.string().optional(), - eventType: z.enum(['pickup', 'dropoff', 'custom']).optional(), - location: z.string().min(1).max(200).optional(), - notes: z.string().max(500).optional(), - status: z.enum(['scheduled', 'in_progress', 'completed', 'cancelled']).optional() - }) -}; - -// Single validation middleware -export const validate = (schema: z.ZodSchema) => { - return async (req: Request, res: Response, next: NextFunction) => { - try { - req.body = await schema.parseAsync(req.body); - next(); - } catch (error) { - if (error instanceof z.ZodError) { - const message = error.errors - .map(err => `${err.path.join('.')}: ${err.message}`) - .join(', '); - return res.status(400).json({ error: message }); - } - next(error); - } - }; -}; \ No newline at end of file diff --git a/backend-old-20260125/src/middleware/validation.ts b/backend-old-20260125/src/middleware/validation.ts deleted file mode 100644 index decc826..0000000 --- a/backend-old-20260125/src/middleware/validation.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Request, Response, NextFunction } from 'express'; -import { z, ZodError } from 'zod'; -import { ValidationError } from '../types/errors'; - -export const validate = (schema: z.ZodSchema) => { - return async (req: Request, res: Response, next: NextFunction) => { - try { - // Validate request body - req.body = await schema.parseAsync(req.body); - next(); - } catch (error) { - if (error instanceof 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 ValidationError(message)); - } else { - next(error); - } - } - }; -}; - -export const validateQuery = (schema: z.ZodSchema) => { - return async (req: Request, res: Response, next: NextFunction) => { - try { - // Validate query parameters - req.query = await schema.parseAsync(req.query); - next(); - } catch (error) { - if (error instanceof 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 ValidationError(`Invalid query parameters: ${message}`)); - } else { - next(error); - } - } - }; -}; - -export const validateParams = (schema: z.ZodSchema) => { - return async (req: Request, res: Response, next: NextFunction) => { - try { - // Validate route parameters - req.params = await schema.parseAsync(req.params); - next(); - } catch (error) { - if (error instanceof 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 ValidationError(`Invalid route parameters: ${message}`)); - } else { - next(error); - } - } - }; -}; \ No newline at end of file diff --git a/backend-old-20260125/src/migrations/add_user_management_fields.sql b/backend-old-20260125/src/migrations/add_user_management_fields.sql deleted file mode 100644 index c915a14..0000000 --- a/backend-old-20260125/src/migrations/add_user_management_fields.sql +++ /dev/null @@ -1,114 +0,0 @@ --- Migration: Add user management fields --- Purpose: Support comprehensive user onboarding and approval system - --- 1. Add new columns to users table -ALTER TABLE users -ADD COLUMN IF NOT EXISTS status VARCHAR(20) DEFAULT 'pending' - CHECK (status IN ('pending', 'active', 'deactivated')), -ADD COLUMN IF NOT EXISTS phone VARCHAR(50), -ADD COLUMN IF NOT EXISTS organization VARCHAR(255), -ADD COLUMN IF NOT EXISTS onboarding_data JSONB, -ADD COLUMN IF NOT EXISTS approved_by VARCHAR(255), -ADD COLUMN IF NOT EXISTS approved_at TIMESTAMP, -ADD COLUMN IF NOT EXISTS rejected_by VARCHAR(255), -ADD COLUMN IF NOT EXISTS rejected_at TIMESTAMP, -ADD COLUMN IF NOT EXISTS deactivated_by VARCHAR(255), -ADD COLUMN IF NOT EXISTS deactivated_at TIMESTAMP; - --- 2. Update existing users to have 'active' status if they were already approved -UPDATE users -SET status = 'active' -WHERE approval_status = 'approved' AND status IS NULL; - --- 3. Update role check constraint to include 'viewer' role -ALTER TABLE users -DROP CONSTRAINT IF EXISTS users_role_check; - -ALTER TABLE users -ADD CONSTRAINT users_role_check -CHECK (role IN ('driver', 'coordinator', 'administrator', 'viewer')); - --- 4. Create indexes for better query performance -CREATE INDEX IF NOT EXISTS idx_users_status ON users(status); -CREATE INDEX IF NOT EXISTS idx_users_role ON users(role); -CREATE INDEX IF NOT EXISTS idx_users_email_status ON users(email, status); -CREATE INDEX IF NOT EXISTS idx_users_organization ON users(organization); - --- 5. Create audit log table for user management actions -CREATE TABLE IF NOT EXISTS user_audit_log ( - id SERIAL PRIMARY KEY, - action VARCHAR(50) NOT NULL, - user_email VARCHAR(255) NOT NULL, - performed_by VARCHAR(255) NOT NULL, - action_details JSONB, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - --- 6. Create index on audit log -CREATE INDEX IF NOT EXISTS idx_user_audit_log_user_email ON user_audit_log(user_email); -CREATE INDEX IF NOT EXISTS idx_user_audit_log_performed_by ON user_audit_log(performed_by); -CREATE INDEX IF NOT EXISTS idx_user_audit_log_created_at ON user_audit_log(created_at DESC); - --- 7. Fix first user to be administrator --- Update the first created user to be an administrator if they're not already -UPDATE users -SET role = 'administrator', - status = 'active', - approval_status = 'approved' -WHERE created_at = (SELECT MIN(created_at) FROM users) - AND role != 'administrator'; - --- 8. Add comment to document the schema -COMMENT ON COLUMN users.status IS 'User account status: pending (awaiting approval), active (approved and can log in), deactivated (account disabled)'; -COMMENT ON COLUMN users.onboarding_data IS 'JSON data collected during onboarding. For drivers: vehicleType, vehicleCapacity, licensePlate, homeLocation, requestedRole, reason'; -COMMENT ON COLUMN users.approved_by IS 'Email of the administrator who approved this user'; -COMMENT ON COLUMN users.approved_at IS 'Timestamp when the user was approved'; - --- 9. Create a function to handle user approval with audit logging -CREATE OR REPLACE FUNCTION approve_user( - p_user_email VARCHAR, - p_approved_by VARCHAR, - p_new_role VARCHAR DEFAULT NULL -) -RETURNS VOID AS $$ -BEGIN - -- Update user status - UPDATE users - SET status = 'active', - approval_status = 'approved', - approved_by = p_approved_by, - approved_at = CURRENT_TIMESTAMP, - role = COALESCE(p_new_role, role), - updated_at = CURRENT_TIMESTAMP - WHERE email = p_user_email; - - -- Log the action - INSERT INTO user_audit_log (action, user_email, performed_by, action_details) - VALUES ('user_approved', p_user_email, p_approved_by, - jsonb_build_object('new_role', COALESCE(p_new_role, (SELECT role FROM users WHERE email = p_user_email)))); -END; -$$ LANGUAGE plpgsql; - --- 10. Create a function to handle user rejection with audit logging -CREATE OR REPLACE FUNCTION reject_user( - p_user_email VARCHAR, - p_rejected_by VARCHAR, - p_reason VARCHAR DEFAULT NULL -) -RETURNS VOID AS $$ -BEGIN - -- Update user status - UPDATE users - SET status = 'deactivated', - approval_status = 'denied', - rejected_by = p_rejected_by, - rejected_at = CURRENT_TIMESTAMP, - updated_at = CURRENT_TIMESTAMP - WHERE email = p_user_email; - - -- Log the action - INSERT INTO user_audit_log (action, user_email, performed_by, action_details) - VALUES ('user_rejected', p_user_email, p_rejected_by, - jsonb_build_object('reason', p_reason)); -END; -$$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/backend-old-20260125/src/routes/__tests__/vips.test.ts b/backend-old-20260125/src/routes/__tests__/vips.test.ts deleted file mode 100644 index 03566f7..0000000 --- a/backend-old-20260125/src/routes/__tests__/vips.test.ts +++ /dev/null @@ -1,309 +0,0 @@ -import request from 'supertest'; -import express from 'express'; -import { testPool } from '../../tests/setup'; -import { - testUsers, - testVips, - testFlights, - insertTestUser, - insertTestVip, - createTestJwtPayload -} from '../../tests/fixtures'; -import jwt from 'jsonwebtoken'; - -// Mock JWT signing -jest.mock('jsonwebtoken'); - -describe('VIPs API Endpoints', () => { - let app: express.Application; - let authToken: string; - - beforeEach(async () => { - // Create a minimal Express app for testing - app = express(); - app.use(express.json()); - - // Mock authentication middleware - app.use((req, res, next) => { - if (req.headers.authorization) { - const token = req.headers.authorization.replace('Bearer ', ''); - try { - const decoded = jwt.verify(token, 'test-secret'); - (req as any).user = decoded; - } catch (error) { - return res.status(401).json({ error: 'Invalid token' }); - } - } - next(); - }); - - // TODO: Mount actual VIP routes here - // app.use('/api/vips', vipRoutes); - - // For now, create mock routes - app.get('/api/vips', async (req, res) => { - try { - const result = await testPool.query('SELECT * FROM vips ORDER BY arrival_datetime'); - res.json(result.rows); - } catch (error) { - res.status(500).json({ error: 'Database error' }); - } - }); - - app.post('/api/vips', async (req, res) => { - if (!(req as any).user || (req as any).user.role !== 'administrator') { - return res.status(403).json({ error: 'Forbidden' }); - } - - try { - const { name, title, organization, arrival_datetime } = req.body; - const result = await testPool.query( - `INSERT INTO vips (id, name, title, organization, arrival_datetime, status, created_at) - VALUES (gen_random_uuid(), $1, $2, $3, $4, 'scheduled', NOW()) - RETURNING *`, - [name, title, organization, arrival_datetime] - ); - res.status(201).json(result.rows[0]); - } catch (error) { - res.status(500).json({ error: 'Database error' }); - } - }); - - app.get('/api/vips/:id', async (req, res) => { - try { - const result = await testPool.query('SELECT * FROM vips WHERE id = $1', [req.params.id]); - if (result.rows.length === 0) { - return res.status(404).json({ error: 'VIP not found' }); - } - res.json(result.rows[0]); - } catch (error) { - res.status(500).json({ error: 'Database error' }); - } - }); - - app.put('/api/vips/:id', async (req, res) => { - if (!(req as any).user) { - return res.status(401).json({ error: 'Unauthorized' }); - } - - try { - const { name, title, status } = req.body; - const result = await testPool.query( - `UPDATE vips SET name = $1, title = $2, status = $3, updated_at = NOW() - WHERE id = $4 RETURNING *`, - [name, title, status, req.params.id] - ); - - if (result.rows.length === 0) { - return res.status(404).json({ error: 'VIP not found' }); - } - res.json(result.rows[0]); - } catch (error) { - res.status(500).json({ error: 'Database error' }); - } - }); - - app.delete('/api/vips/:id', async (req, res) => { - if (!(req as any).user || (req as any).user.role !== 'administrator') { - return res.status(403).json({ error: 'Forbidden' }); - } - - try { - const result = await testPool.query('DELETE FROM vips WHERE id = $1 RETURNING id', [req.params.id]); - if (result.rows.length === 0) { - return res.status(404).json({ error: 'VIP not found' }); - } - res.status(204).send(); - } catch (error) { - res.status(500).json({ error: 'Database error' }); - } - }); - - // Setup test user and generate token - await insertTestUser(testPool, testUsers.admin); - const payload = createTestJwtPayload(testUsers.admin); - authToken = 'test-token'; - (jwt.sign as jest.Mock).mockReturnValue(authToken); - (jwt.verify as jest.Mock).mockReturnValue(payload); - }); - - describe('GET /api/vips', () => { - it('should return all VIPs', async () => { - // Insert test VIPs - await insertTestVip(testPool, testVips.flightVip); - await insertTestVip(testPool, testVips.drivingVip); - - const response = await request(app) - .get('/api/vips') - .set('Authorization', `Bearer ${authToken}`); - - expect(response.status).toBe(200); - expect(response.body).toHaveLength(2); - expect(response.body[0].name).toBe(testVips.flightVip.name); - expect(response.body[1].name).toBe(testVips.drivingVip.name); - }); - - it('should return empty array when no VIPs exist', async () => { - const response = await request(app) - .get('/api/vips') - .set('Authorization', `Bearer ${authToken}`); - - expect(response.status).toBe(200); - expect(response.body).toEqual([]); - }); - }); - - describe('POST /api/vips', () => { - it('should create a new VIP when user is admin', async () => { - const newVip = { - name: 'New VIP', - title: 'CTO', - organization: 'Tech Corp', - arrival_datetime: '2025-01-20T15:00:00Z', - }; - - const response = await request(app) - .post('/api/vips') - .set('Authorization', `Bearer ${authToken}`) - .send(newVip); - - expect(response.status).toBe(201); - expect(response.body).toMatchObject({ - name: newVip.name, - title: newVip.title, - organization: newVip.organization, - status: 'scheduled', - }); - expect(response.body.id).toBeDefined(); - }); - - it('should reject creation when user is not admin', async () => { - // Create coordinator user and token - await insertTestUser(testPool, testUsers.coordinator); - const coordPayload = createTestJwtPayload(testUsers.coordinator); - const coordToken = 'coord-token'; - (jwt.verify as jest.Mock).mockReturnValueOnce(coordPayload); - - const newVip = { - name: 'New VIP', - title: 'CTO', - organization: 'Tech Corp', - arrival_datetime: '2025-01-20T15:00:00Z', - }; - - const response = await request(app) - .post('/api/vips') - .set('Authorization', `Bearer ${coordToken}`) - .send(newVip); - - expect(response.status).toBe(403); - expect(response.body.error).toBe('Forbidden'); - }); - }); - - describe('GET /api/vips/:id', () => { - it('should return a specific VIP', async () => { - await insertTestVip(testPool, testVips.flightVip); - - const response = await request(app) - .get(`/api/vips/${testVips.flightVip.id}`) - .set('Authorization', `Bearer ${authToken}`); - - expect(response.status).toBe(200); - expect(response.body.id).toBe(testVips.flightVip.id); - expect(response.body.name).toBe(testVips.flightVip.name); - }); - - it('should return 404 for non-existent VIP', async () => { - const fakeId = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'; - - const response = await request(app) - .get(`/api/vips/${fakeId}`) - .set('Authorization', `Bearer ${authToken}`); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('VIP not found'); - }); - }); - - describe('PUT /api/vips/:id', () => { - it('should update a VIP', async () => { - await insertTestVip(testPool, testVips.flightVip); - - const updates = { - name: 'Updated Name', - title: 'Updated Title', - status: 'arrived', - }; - - const response = await request(app) - .put(`/api/vips/${testVips.flightVip.id}`) - .set('Authorization', `Bearer ${authToken}`) - .send(updates); - - expect(response.status).toBe(200); - expect(response.body.name).toBe(updates.name); - expect(response.body.title).toBe(updates.title); - expect(response.body.status).toBe(updates.status); - }); - - it('should return 404 when updating non-existent VIP', async () => { - const fakeId = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'; - - const response = await request(app) - .put(`/api/vips/${fakeId}`) - .set('Authorization', `Bearer ${authToken}`) - .send({ name: 'Updated' }); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('VIP not found'); - }); - - it('should require authentication', async () => { - await insertTestVip(testPool, testVips.flightVip); - - const response = await request(app) - .put(`/api/vips/${testVips.flightVip.id}`) - .send({ name: 'Updated' }); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Unauthorized'); - }); - }); - - describe('DELETE /api/vips/:id', () => { - it('should delete a VIP when user is admin', async () => { - await insertTestVip(testPool, testVips.flightVip); - - const response = await request(app) - .delete(`/api/vips/${testVips.flightVip.id}`) - .set('Authorization', `Bearer ${authToken}`); - - expect(response.status).toBe(204); - - // Verify VIP was deleted - const checkResult = await testPool.query( - 'SELECT * FROM vips WHERE id = $1', - [testVips.flightVip.id] - ); - expect(checkResult.rows).toHaveLength(0); - }); - - it('should return 403 when non-admin tries to delete', async () => { - await insertTestVip(testPool, testVips.flightVip); - - // Create coordinator user and token - await insertTestUser(testPool, testUsers.coordinator); - const coordPayload = createTestJwtPayload(testUsers.coordinator); - const coordToken = 'coord-token'; - (jwt.verify as jest.Mock).mockReturnValueOnce(coordPayload); - - const response = await request(app) - .delete(`/api/vips/${testVips.flightVip.id}`) - .set('Authorization', `Bearer ${coordToken}`); - - expect(response.status).toBe(403); - expect(response.body.error).toBe('Forbidden'); - }); - }); -}); \ No newline at end of file diff --git a/backend-old-20260125/src/routes/auth.ts b/backend-old-20260125/src/routes/auth.ts deleted file mode 100644 index 27df226..0000000 --- a/backend-old-20260125/src/routes/auth.ts +++ /dev/null @@ -1,239 +0,0 @@ -import express, { Request, Response, Router } from 'express'; -import { keycloakUrls, getUserFromToken } from '../config/keycloak'; -import { syncUserFromKeycloak, getAllUsers, getPendingUsers, approveUser, denyUser, assignRole } from '../services/userService'; -import { requireAuth, requireAdmin, checkApprovalStatus } from '../middleware/auth'; -import databaseService from '../services/databaseService'; - -const router: Router = express.Router(); - -const frontendUrl = process.env.FRONTEND_URL || 'http://localhost:5173'; -const keycloakClientId = process.env.KEYCLOAK_CLIENT_ID || 'vip-coordinator-frontend'; - -/** - * GET /auth/login - * Redirects to Keycloak login - */ -router.get('/login', (req: Request, res: Response) => { - const returnTo = req.query.returnTo as string || '/'; - - const authUrl = `${keycloakUrls.authorizationUrl}?` + - `client_id=${keycloakClientId}&` + - `redirect_uri=${encodeURIComponent(`${frontendUrl}/auth/callback`)}&` + - `response_type=code&` + - `scope=openid profile email&` + - `state=${encodeURIComponent(returnTo)}`; - - res.redirect(authUrl); -}); - -/** - * GET /auth/logout - * Logs out user from Keycloak and app - */ -router.get('/logout', (req: Request, res: Response) => { - const logoutUrl = `${keycloakUrls.logoutUrl}?` + - `client_id=${keycloakClientId}&` + - `post_logout_redirect_uri=${encodeURIComponent(`${frontendUrl}/login`)}`; - - res.redirect(logoutUrl); -}); - -/** - * GET /auth/me - * Returns current user information - * Protected route - requires authentication - */ -router.get('/me', requireAuth, async (req: Request, res: Response) => { - try { - const user = getUserFromToken(req); - - if (!user) { - return res.status(401).json({ error: 'Not authenticated' }); - } - - // Get access token from Authorization header - const authHeader = req.headers.authorization; - const token = authHeader?.replace('Bearer ', ''); - - if (!token) { - return res.status(401).json({ error: 'No token provided' }); - } - - // Sync user to local database (creates if doesn't exist, updates if exists) - await syncUserFromKeycloak(user.keycloakSub, token); - - // Fetch full user details from database - const dbUser = await databaseService.getUserByAuth0Sub(user.keycloakSub); - - if (!dbUser) { - return res.status(404).json({ error: 'User not found in database' }); - } - - res.json({ - user: dbUser, - keycloak: { - sub: user.keycloakSub, - roles: user.roles, - }, - }); - } catch (error) { - console.error('āŒ Error fetching user:', error); - res.status(500).json({ error: 'Failed to fetch user information' }); - } -}); - -/** - * GET /auth/status - * Returns authentication status - */ -router.get('/status', requireAuth, (req: Request, res: Response) => { - const user = getUserFromToken(req); - - res.json({ - authenticated: true, - user: { - keycloakSub: user?.keycloakSub, - email: user?.email, - role: user?.role, - approvalStatus: user?.approvalStatus, - }, - }); -}); - -/** - * GET /auth/setup - * Check if system setup is complete (first user created) - */ -router.get('/setup', async (req: Request, res: Response) => { - try { - const userCount = await databaseService.getUserCount(); - const hasUsers = userCount > 0; - - res.json({ - setupComplete: hasUsers, - needsFirstUser: !hasUsers, - }); - } catch (error) { - console.error('āŒ Error checking setup status:', error); - res.status(500).json({ error: 'Failed to check setup status' }); - } -}); - -// ============================================ -// User Management Routes (Admin Only) -// ============================================ - -/** - * GET /auth/users - * Get all users - * Admin only - */ -router.get('/users', requireAuth, requireAdmin, async (req: Request, res: Response) => { - try { - const users = await getAllUsers(); - res.json({ users }); - } catch (error) { - console.error('āŒ Error fetching users:', error); - res.status(500).json({ error: 'Failed to fetch users' }); - } -}); - -/** - * GET /auth/users/pending/list - * Get pending users - * Admin only - */ -router.get('/users/pending/list', requireAuth, requireAdmin, async (req: Request, res: Response) => { - try { - const users = await getPendingUsers(); - res.json({ users }); - } catch (error) { - console.error('āŒ Error fetching pending users:', error); - res.status(500).json({ error: 'Failed to fetch pending users' }); - } -}); - -/** - * PATCH /auth/users/:email/approval - * Approve or deny a user - * Admin only - */ -router.patch('/users/:email/approval', requireAuth, requireAdmin, async (req: Request, res: Response) => { - const { email } = req.params; - const { status } = req.body; // 'approved' or 'denied' - - if (!status || !['approved', 'denied'].includes(status)) { - return res.status(400).json({ error: 'Invalid status. Must be "approved" or "denied"' }); - } - - try { - if (status === 'approved') { - await approveUser(email); - } else { - await denyUser(email); - } - - res.json({ - success: true, - message: `User ${email} has been ${status}`, - }); - } catch (error) { - console.error(`āŒ Error updating approval status for ${email}:`, error); - res.status(500).json({ error: 'Failed to update user approval status' }); - } -}); - -/** - * PATCH /auth/users/:email/role - * Change user role - * Admin only - */ -router.patch('/users/:email/role', requireAuth, requireAdmin, async (req: Request, res: Response) => { - const { email } = req.params; - const { role } = req.body; - - if (!role || !['driver', 'coordinator', 'administrator'].includes(role)) { - return res.status(400).json({ error: 'Invalid role. Must be "driver", "coordinator", or "administrator"' }); - } - - try { - await assignRole(email, role); - - res.json({ - success: true, - message: `User ${email} role updated to ${role}`, - }); - } catch (error) { - console.error(`āŒ Error updating role for ${email}:`, error); - res.status(500).json({ error: 'Failed to update user role' }); - } -}); - -/** - * DELETE /auth/users/:email - * Delete a user - * Admin only - */ -router.delete('/users/:email', requireAuth, requireAdmin, async (req: Request, res: Response) => { - const { email } = req.params; - - try { - const result = await databaseService.query('DELETE FROM users WHERE email = $1 RETURNING *', [email]); - - if (result.rows.length === 0) { - return res.status(404).json({ error: 'User not found' }); - } - - res.json({ - success: true, - message: `User ${email} has been deleted`, - }); - } catch (error) { - console.error(`āŒ Error deleting user ${email}:`, error); - res.status(500).json({ error: 'Failed to delete user' }); - } -}); - -console.log('šŸ” Auth routes initialized (Keycloak)'); - -export default router; diff --git a/backend-old-20260125/src/scripts/check-and-fix-users.sql b/backend-old-20260125/src/scripts/check-and-fix-users.sql deleted file mode 100644 index c94f986..0000000 --- a/backend-old-20260125/src/scripts/check-and-fix-users.sql +++ /dev/null @@ -1,55 +0,0 @@ --- Script to check current users and fix the first user to be admin - --- 1. Show all users in the system -SELECT - email, - name, - role, - approval_status, - status, - created_at, - last_login, - is_active -FROM users -ORDER BY created_at ASC; - --- 2. Show the first user (by creation date) -SELECT - '=== FIRST USER ===' as info, - email, - name, - role, - approval_status, - created_at -FROM users -WHERE created_at = (SELECT MIN(created_at) FROM users); - --- 3. Fix the first user to be administrator -UPDATE users -SET - role = 'administrator', - approval_status = 'approved', - status = COALESCE(status, 'active'), - updated_at = CURRENT_TIMESTAMP -WHERE created_at = (SELECT MIN(created_at) FROM users) -RETURNING - '=== UPDATED USER ===' as info, - email, - name, - role, - approval_status, - status; - --- 4. Show all users again to confirm the change -SELECT - '=== ALL USERS AFTER UPDATE ===' as info; - -SELECT - email, - name, - role, - approval_status, - status, - created_at -FROM users -ORDER BY created_at ASC; \ No newline at end of file diff --git a/backend-old-20260125/src/scripts/db-cli.ts b/backend-old-20260125/src/scripts/db-cli.ts deleted file mode 100644 index 917bf6d..0000000 --- a/backend-old-20260125/src/scripts/db-cli.ts +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env node - -import { Pool } from 'pg'; -import { getMigrationService, MigrationService } from '../services/migrationService'; -import { createSeedService } from '../services/seedService'; -import { env } from '../config/env'; - -// Command line arguments -const command = process.argv[2]; -const args = process.argv.slice(3); - -// Create database pool -const pool = new Pool({ - connectionString: env.DATABASE_URL, -}); - -async function main() { - try { - switch (command) { - case 'migrate': - await runMigrations(); - break; - - case 'migrate:create': - await createMigration(args[0]); - break; - - case 'seed': - await seedDatabase(); - break; - - case 'seed:reset': - await resetAndSeed(); - break; - - case 'setup': - await setupDatabase(); - break; - - default: - showHelp(); - process.exit(1); - } - } catch (error) { - console.error('āŒ Error:', error); - process.exit(1); - } finally { - await pool.end(); - } -} - -async function runMigrations() { - console.log('šŸ”„ Running migrations...'); - const migrationService = getMigrationService(pool); - await migrationService.runMigrations(); -} - -async function createMigration(name?: string) { - if (!name) { - console.error('āŒ Please provide a migration name'); - console.log('Usage: npm run db:migrate:create '); - process.exit(1); - } - - await MigrationService.createMigration(name); -} - -async function seedDatabase() { - console.log('🌱 Seeding database...'); - const seedService = createSeedService(pool); - await seedService.seedAll(); -} - -async function resetAndSeed() { - console.log('šŸ”„ Resetting and seeding database...'); - const seedService = createSeedService(pool); - await seedService.resetAndSeed(); -} - -async function setupDatabase() { - console.log('šŸš€ Setting up database...'); - - // Run initial schema - const fs = await import('fs/promises'); - const path = await import('path'); - - const schemaPath = path.join(__dirname, '..', 'config', 'schema.sql'); - const schema = await fs.readFile(schemaPath, 'utf8'); - - await pool.query(schema); - console.log('āœ… Created database schema'); - - // Run migrations - const migrationService = getMigrationService(pool); - await migrationService.runMigrations(); - - // Seed initial data - const seedService = createSeedService(pool); - await seedService.seedAll(); - - console.log('āœ… Database setup complete!'); -} - -function showHelp() { - console.log(` -VIP Coordinator Database CLI - -Usage: npm run db: - -Commands: - migrate Run pending migrations - migrate:create Create a new migration file - seed Seed the database with test data - seed:reset Clear all data and re-seed - setup Run schema, migrations, and seed data - -Examples: - npm run db:migrate - npm run db:migrate:create add_new_column - npm run db:seed - npm run db:setup - `); -} - -// Run the CLI -main().catch(console.error); \ No newline at end of file diff --git a/backend-old-20260125/src/scripts/fix-existing-user-admin.js b/backend-old-20260125/src/scripts/fix-existing-user-admin.js deleted file mode 100644 index f539691..0000000 --- a/backend-old-20260125/src/scripts/fix-existing-user-admin.js +++ /dev/null @@ -1,85 +0,0 @@ -// Script to fix the existing Google-authenticated user to be admin -// This will update the first user (by creation date) to have administrator role - -const { Pool } = require('pg'); - -// Using the postgres user since we know that password -const DATABASE_URL = process.env.DATABASE_URL || - 'postgresql://postgres:changeme@localhost:5432/vip_coordinator'; - -console.log('Connecting to database...'); - -const pool = new Pool({ - connectionString: DATABASE_URL, - ssl: false -}); - -async function fixExistingUserToAdmin() { - try { - // 1. Show current users - console.log('\nšŸ“‹ Current Google-authenticated users:'); - console.log('====================================='); - const allUsers = await pool.query(` - SELECT email, name, role, created_at, is_active - FROM users - ORDER BY created_at ASC - `); - - if (allUsers.rows.length === 0) { - console.log('āŒ No users found in database!'); - console.log('\nThe first user needs to log in with Google first.'); - return; - } - - console.log(`Found ${allUsers.rows.length} user(s):\n`); - allUsers.rows.forEach((user, index) => { - console.log(`User #${index + 1}:`); - console.log(` Email: ${user.email}`); - console.log(` Name: ${user.name}`); - console.log(` Current Role: ${user.role} ${user.role !== 'administrator' ? 'āŒ' : 'āœ…'}`); - console.log(` Is Active: ${user.is_active ? 'Yes' : 'No'}`); - console.log(` Created: ${user.created_at}`); - console.log(''); - }); - - // 2. Update the first user to administrator - const firstUser = allUsers.rows[0]; - if (firstUser.role === 'administrator') { - console.log('āœ… First user is already an administrator!'); - return; - } - - console.log(`šŸ”§ Updating ${firstUser.name} (${firstUser.email}) to administrator...`); - - const updateResult = await pool.query(` - UPDATE users - SET - role = 'administrator', - is_active = true, - updated_at = CURRENT_TIMESTAMP - WHERE email = $1 - RETURNING email, name, role, is_active - `, [firstUser.email]); - - if (updateResult.rows.length > 0) { - const updated = updateResult.rows[0]; - console.log('\nāœ… Successfully updated user!'); - console.log(` Email: ${updated.email}`); - console.log(` Name: ${updated.name}`); - console.log(` New Role: ${updated.role} āœ…`); - console.log(` Is Active: ${updated.is_active ? 'Yes' : 'No'}`); - console.log('\nšŸŽ‰ This user can now log in and access the Admin dashboard!'); - } - - } catch (error) { - console.error('\nāŒ Error:', error.message); - if (error.code === '28P01') { - console.error('\nPassword authentication failed. Make sure Docker containers are running.'); - } - } finally { - await pool.end(); - } -} - -// Run the fix -fixExistingUserToAdmin(); \ No newline at end of file diff --git a/backend-old-20260125/src/scripts/fix-first-admin-docker.js b/backend-old-20260125/src/scripts/fix-first-admin-docker.js deleted file mode 100644 index 95feb98..0000000 --- a/backend-old-20260125/src/scripts/fix-first-admin-docker.js +++ /dev/null @@ -1,77 +0,0 @@ -// Script to check users and fix the first user to be admin -// Run with: node backend/src/scripts/fix-first-admin-docker.js - -const { Pool } = require('pg'); - -// Construct DATABASE_URL from docker-compose defaults -const DATABASE_URL = process.env.DATABASE_URL || - `postgresql://vip_user:${process.env.DB_PASSWORD || 'VipCoord2025SecureDB'}@localhost:5432/vip_coordinator`; - -console.log('Connecting to database...'); - -const pool = new Pool({ - connectionString: DATABASE_URL, - ssl: false // Local docker doesn't use SSL -}); - -async function fixFirstAdmin() { - try { - // 1. Show all current users - console.log('\nšŸ“‹ Current users in database:'); - console.log('================================'); - const allUsers = await pool.query(` - SELECT email, name, role, approval_status, status, created_at - FROM users - ORDER BY created_at ASC - `); - - if (allUsers.rows.length === 0) { - console.log('No users found in database!'); - return; - } - - allUsers.rows.forEach(user => { - console.log(` -Email: ${user.email} -Name: ${user.name} -Role: ${user.role} -Approval: ${user.approval_status || 'N/A'} -Status: ${user.status || 'N/A'} -Created: ${user.created_at} -------`); - }); - - // 2. Fix the first user to be admin - console.log('\nšŸ”§ Updating first user to administrator...'); - const updateResult = await pool.query(` - UPDATE users - SET - role = 'administrator', - approval_status = 'approved', - status = COALESCE(status, 'active'), - updated_at = CURRENT_TIMESTAMP - WHERE created_at = (SELECT MIN(created_at) FROM users) - RETURNING email, name, role, approval_status, status - `); - - if (updateResult.rows.length > 0) { - const updated = updateResult.rows[0]; - console.log('\nāœ… Successfully updated user:'); - console.log(`Email: ${updated.email}`); - console.log(`Name: ${updated.name}`); - console.log(`New Role: ${updated.role}`); - console.log(`Status: ${updated.status}`); - } else { - console.log('\nāŒ No users found to update!'); - } - - } catch (error) { - console.error('\nāŒ Error:', error.message); - console.error('Full error:', error); - } finally { - await pool.end(); - } -} - -// Run the fix -fixFirstAdmin(); \ No newline at end of file diff --git a/backend-old-20260125/src/scripts/fix-first-admin.js b/backend-old-20260125/src/scripts/fix-first-admin.js deleted file mode 100644 index d898e3b..0000000 --- a/backend-old-20260125/src/scripts/fix-first-admin.js +++ /dev/null @@ -1,66 +0,0 @@ -// Script to check users and fix the first user to be admin -// Run with: node backend/src/scripts/fix-first-admin.js - -const { Pool } = require('pg'); -require('dotenv').config(); - -const pool = new Pool({ - connectionString: process.env.DATABASE_URL, - ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false -}); - -async function fixFirstAdmin() { - try { - // 1. Show all current users - console.log('\nšŸ“‹ Current users in database:'); - console.log('================================'); - const allUsers = await pool.query(` - SELECT email, name, role, approval_status, status, created_at - FROM users - ORDER BY created_at ASC - `); - - allUsers.rows.forEach(user => { - console.log(` -Email: ${user.email} -Name: ${user.name} -Role: ${user.role} -Approval: ${user.approval_status || 'N/A'} -Status: ${user.status || 'N/A'} -Created: ${user.created_at} -------`); - }); - - // 2. Fix the first user to be admin - console.log('\nšŸ”§ Updating first user to administrator...'); - const updateResult = await pool.query(` - UPDATE users - SET - role = 'administrator', - approval_status = 'approved', - status = COALESCE(status, 'active'), - updated_at = CURRENT_TIMESTAMP - WHERE created_at = (SELECT MIN(created_at) FROM users) - RETURNING email, name, role, approval_status, status - `); - - if (updateResult.rows.length > 0) { - const updated = updateResult.rows[0]; - console.log('\nāœ… Successfully updated user:'); - console.log(`Email: ${updated.email}`); - console.log(`Name: ${updated.name}`); - console.log(`New Role: ${updated.role}`); - console.log(`Status: ${updated.status}`); - } else { - console.log('\nāŒ No users found to update!'); - } - - } catch (error) { - console.error('\nāŒ Error:', error.message); - } finally { - await pool.end(); - } -} - -// Run the fix -fixFirstAdmin(); \ No newline at end of file diff --git a/backend-old-20260125/src/scripts/fix-specific-user-admin.js b/backend-old-20260125/src/scripts/fix-specific-user-admin.js deleted file mode 100644 index 0207a92..0000000 --- a/backend-old-20260125/src/scripts/fix-specific-user-admin.js +++ /dev/null @@ -1,102 +0,0 @@ -// Script to fix cbtah56@gmail.com to be admin - -const { Pool } = require('pg'); - -const DATABASE_URL = 'postgresql://postgres:changeme@localhost:5432/vip_coordinator'; - -console.log('Connecting to database...'); - -const pool = new Pool({ - connectionString: DATABASE_URL, - ssl: false -}); - -async function fixSpecificUser() { - try { - // 1. Show ALL users - console.log('\nšŸ“‹ ALL users in database:'); - console.log('========================'); - const allUsers = await pool.query(` - SELECT email, name, role, created_at, is_active - FROM users - ORDER BY created_at ASC - `); - - console.log(`Total users found: ${allUsers.rows.length}\n`); - allUsers.rows.forEach((user, index) => { - console.log(`User #${index + 1}:`); - console.log(` Email: ${user.email}`); - console.log(` Name: ${user.name}`); - console.log(` Role: ${user.role}`); - console.log(` Is Active: ${user.is_active}`); - console.log(` Created: ${user.created_at}`); - console.log('---'); - }); - - // 2. Look specifically for cbtah56@gmail.com - console.log('\nšŸ” Looking for cbtah56@gmail.com...'); - const targetUser = await pool.query(` - SELECT email, name, role, created_at, is_active - FROM users - WHERE email = 'cbtah56@gmail.com' - `); - - if (targetUser.rows.length === 0) { - console.log('āŒ User cbtah56@gmail.com not found in database!'); - - // Try case-insensitive search - console.log('\nšŸ” Trying case-insensitive search...'); - const caseInsensitive = await pool.query(` - SELECT email, name, role, created_at, is_active - FROM users - WHERE LOWER(email) = LOWER('cbtah56@gmail.com') - `); - - if (caseInsensitive.rows.length > 0) { - console.log('Found with different case:', caseInsensitive.rows[0].email); - } - return; - } - - const user = targetUser.rows[0]; - console.log('\nāœ… Found user:'); - console.log(` Email: ${user.email}`); - console.log(` Name: ${user.name}`); - console.log(` Current Role: ${user.role}`); - - if (user.role === 'administrator') { - console.log('\nāœ… User is already an administrator!'); - return; - } - - // 3. Update to administrator - console.log('\nšŸ”§ Updating cbtah56@gmail.com to administrator...'); - - const updateResult = await pool.query(` - UPDATE users - SET - role = 'administrator', - is_active = true, - updated_at = CURRENT_TIMESTAMP - WHERE email = 'cbtah56@gmail.com' - RETURNING email, name, role, is_active - `); - - if (updateResult.rows.length > 0) { - const updated = updateResult.rows[0]; - console.log('\nāœ… Successfully updated user!'); - console.log(` Email: ${updated.email}`); - console.log(` Name: ${updated.name}`); - console.log(` New Role: ${updated.role} āœ…`); - console.log('\nšŸŽ‰ cbtah56@gmail.com can now log in and access the Admin dashboard!'); - } - - } catch (error) { - console.error('\nāŒ Error:', error.message); - } finally { - await pool.end(); - } -} - -// Run the fix -fixSpecificUser(); \ No newline at end of file diff --git a/backend-old-20260125/src/services/__tests__/authService.test.ts b/backend-old-20260125/src/services/__tests__/authService.test.ts deleted file mode 100644 index 911c11a..0000000 --- a/backend-old-20260125/src/services/__tests__/authService.test.ts +++ /dev/null @@ -1,249 +0,0 @@ -import { testPool } from '../../tests/setup'; -import { - testUsers, - insertTestUser, - createTestJwtPayload -} from '../../tests/fixtures'; -import { OAuth2Client } from 'google-auth-library'; - -// Mock dependencies -jest.mock('google-auth-library'); -jest.mock('../jwtKeyManager'); - -describe('AuthService', () => { - let mockOAuth2Client: jest.Mocked; - - beforeEach(() => { - // Reset mocks - jest.clearAllMocks(); - - // Setup OAuth2Client mock - mockOAuth2Client = new OAuth2Client() as jest.Mocked; - (OAuth2Client as jest.Mock).mockImplementation(() => mockOAuth2Client); - }); - - describe('Google OAuth Verification', () => { - it('should create a new user on first sign-in with admin role', async () => { - // Mock Google token verification - mockOAuth2Client.verifyIdToken = jest.fn().mockResolvedValue({ - getPayload: () => ({ - sub: 'google_new_user_123', - email: 'newuser@test.com', - name: 'New User', - picture: 'https://example.com/picture.jpg', - }), - }); - - // Check no users exist - const userCount = await testPool.query('SELECT COUNT(*) FROM users'); - expect(userCount.rows[0].count).toBe('0'); - - // TODO: Call auth service to verify token and create user - // This would normally call your authService.verifyGoogleToken() method - - // Verify user was created with admin role - const newUser = await testPool.query( - 'SELECT * FROM users WHERE email = $1', - ['newuser@test.com'] - ); - - // Simulate what the service should do - await testPool.query(` - INSERT INTO users ( - id, google_id, email, name, role, status, approval_status, - profile_picture_url, created_at, is_active - ) VALUES ( - gen_random_uuid(), $1, $2, $3, 'administrator', 'active', 'approved', - $4, NOW(), true - ) - `, ['google_new_user_123', 'newuser@test.com', 'New User', 'https://example.com/picture.jpg']); - - const createdUser = await testPool.query( - 'SELECT * FROM users WHERE email = $1', - ['newuser@test.com'] - ); - - expect(createdUser.rows).toHaveLength(1); - expect(createdUser.rows[0].role).toBe('administrator'); - expect(createdUser.rows[0].status).toBe('active'); - }); - - it('should create subsequent users with coordinator role and pending status', async () => { - // Insert first user (admin) - await insertTestUser(testPool, testUsers.admin); - - // Mock Google token verification for second user - mockOAuth2Client.verifyIdToken = jest.fn().mockResolvedValue({ - getPayload: () => ({ - sub: 'google_second_user_456', - email: 'seconduser@test.com', - name: 'Second User', - picture: 'https://example.com/picture2.jpg', - }), - }); - - // TODO: Call auth service to verify token and create user - - // Simulate what the service should do - await testPool.query(` - INSERT INTO users ( - id, google_id, email, name, role, status, approval_status, - profile_picture_url, created_at, is_active - ) VALUES ( - gen_random_uuid(), $1, $2, $3, 'coordinator', 'pending', 'pending', - $4, NOW(), true - ) - `, ['google_second_user_456', 'seconduser@test.com', 'Second User', 'https://example.com/picture2.jpg']); - - const secondUser = await testPool.query( - 'SELECT * FROM users WHERE email = $1', - ['seconduser@test.com'] - ); - - expect(secondUser.rows).toHaveLength(1); - expect(secondUser.rows[0].role).toBe('coordinator'); - expect(secondUser.rows[0].status).toBe('pending'); - expect(secondUser.rows[0].approval_status).toBe('pending'); - }); - - it('should handle existing user login', async () => { - // Insert existing user - await insertTestUser(testPool, testUsers.coordinator); - - // Mock Google token verification - mockOAuth2Client.verifyIdToken = jest.fn().mockResolvedValue({ - getPayload: () => ({ - sub: testUsers.coordinator.google_id, - email: testUsers.coordinator.email, - name: testUsers.coordinator.name, - picture: testUsers.coordinator.profile_picture_url, - }), - }); - - // TODO: Call auth service to verify token - - // Update last login time (what the service should do) - await testPool.query( - 'UPDATE users SET last_login = NOW() WHERE email = $1', - [testUsers.coordinator.email] - ); - - const updatedUser = await testPool.query( - 'SELECT * FROM users WHERE email = $1', - [testUsers.coordinator.email] - ); - - expect(updatedUser.rows[0].last_login).not.toBeNull(); - }); - - it('should reject invalid Google tokens', async () => { - // Mock Google token verification to throw error - mockOAuth2Client.verifyIdToken = jest.fn().mockRejectedValue( - new Error('Invalid token') - ); - - // TODO: Call auth service and expect it to throw/reject - - await expect( - mockOAuth2Client.verifyIdToken({ idToken: 'invalid', audience: 'test' }) - ).rejects.toThrow('Invalid token'); - }); - }); - - describe('User Management', () => { - it('should approve a pending user', async () => { - // Insert admin and pending user - await insertTestUser(testPool, testUsers.admin); - await insertTestUser(testPool, testUsers.pendingUser); - - // TODO: Call auth service to approve user - - // Simulate approval - await testPool.query(` - UPDATE users - SET status = 'active', - approval_status = 'approved', - approved_by = $1, - approved_at = NOW() - WHERE id = $2 - `, [testUsers.admin.id, testUsers.pendingUser.id]); - - const approvedUser = await testPool.query( - 'SELECT * FROM users WHERE id = $1', - [testUsers.pendingUser.id] - ); - - expect(approvedUser.rows[0].status).toBe('active'); - expect(approvedUser.rows[0].approval_status).toBe('approved'); - expect(approvedUser.rows[0].approved_by).toBe(testUsers.admin.id); - expect(approvedUser.rows[0].approved_at).not.toBeNull(); - }); - - it('should deny a pending user', async () => { - // Insert admin and pending user - await insertTestUser(testPool, testUsers.admin); - await insertTestUser(testPool, testUsers.pendingUser); - - // TODO: Call auth service to deny user - - // Simulate denial - await testPool.query(` - UPDATE users - SET approval_status = 'denied', - approved_by = $1, - approved_at = NOW() - WHERE id = $2 - `, [testUsers.admin.id, testUsers.pendingUser.id]); - - const deniedUser = await testPool.query( - 'SELECT * FROM users WHERE id = $1', - [testUsers.pendingUser.id] - ); - - expect(deniedUser.rows[0].status).toBe('pending'); - expect(deniedUser.rows[0].approval_status).toBe('denied'); - }); - - it('should deactivate an active user', async () => { - // Insert admin and active user - await insertTestUser(testPool, testUsers.admin); - await insertTestUser(testPool, testUsers.coordinator); - - // TODO: Call auth service to deactivate user - - // Simulate deactivation - await testPool.query(` - UPDATE users - SET status = 'deactivated', - is_active = false - WHERE id = $1 - `, [testUsers.coordinator.id]); - - const deactivatedUser = await testPool.query( - 'SELECT * FROM users WHERE id = $1', - [testUsers.coordinator.id] - ); - - expect(deactivatedUser.rows[0].status).toBe('deactivated'); - expect(deactivatedUser.rows[0].is_active).toBe(false); - }); - }); - - describe('JWT Token Generation', () => { - it('should generate JWT with all required fields', () => { - const payload = createTestJwtPayload(testUsers.admin); - - expect(payload).toHaveProperty('id'); - expect(payload).toHaveProperty('email'); - expect(payload).toHaveProperty('name'); - expect(payload).toHaveProperty('role'); - expect(payload).toHaveProperty('status'); - expect(payload).toHaveProperty('approval_status'); - expect(payload).toHaveProperty('iat'); - expect(payload).toHaveProperty('exp'); - - // Verify expiration is in the future - expect(payload.exp).toBeGreaterThan(payload.iat); - }); - }); -}); \ No newline at end of file diff --git a/backend-old-20260125/src/services/dataService.ts b/backend-old-20260125/src/services/dataService.ts deleted file mode 100644 index 55ffd5a..0000000 --- a/backend-old-20260125/src/services/dataService.ts +++ /dev/null @@ -1,306 +0,0 @@ -import fs from 'fs'; -import path from 'path'; - -interface DataStore { - vips: any[]; - drivers: any[]; - schedules: { [vipId: string]: any[] }; - adminSettings: any; - users: any[]; -} - -class DataService { - private dataDir: string; - private dataFile: string; - private data: DataStore; - - constructor() { - this.dataDir = path.join(process.cwd(), 'data'); - this.dataFile = path.join(this.dataDir, 'vip-coordinator.json'); - - // Ensure data directory exists - if (!fs.existsSync(this.dataDir)) { - fs.mkdirSync(this.dataDir, { recursive: true }); - } - - this.data = this.loadData(); - } - - private loadData(): DataStore { - try { - if (fs.existsSync(this.dataFile)) { - const fileContent = fs.readFileSync(this.dataFile, 'utf8'); - const loadedData = JSON.parse(fileContent); - console.log(`āœ… Loaded data from ${this.dataFile}`); - console.log(` - VIPs: ${loadedData.vips?.length || 0}`); - console.log(` - Drivers: ${loadedData.drivers?.length || 0}`); - console.log(` - Users: ${loadedData.users?.length || 0}`); - console.log(` - Schedules: ${Object.keys(loadedData.schedules || {}).length} VIPs with schedules`); - - // Ensure users array exists for backward compatibility - if (!loadedData.users) { - loadedData.users = []; - } - - return loadedData; - } - } catch (error) { - console.error('Error loading data file:', error); - } - - // Return default empty data structure - console.log('šŸ“ Starting with empty data store'); - return { - vips: [], - drivers: [], - schedules: {}, - users: [], - adminSettings: { - apiKeys: { - aviationStackKey: process.env.AVIATIONSTACK_API_KEY || '', - googleMapsKey: '', - twilioKey: '' - }, - systemSettings: { - defaultPickupLocation: '', - defaultDropoffLocation: '', - timeZone: 'America/New_York', - notificationsEnabled: false - } - } - }; - } - - private saveData(): void { - try { - const dataToSave = JSON.stringify(this.data, null, 2); - fs.writeFileSync(this.dataFile, dataToSave, 'utf8'); - console.log(`šŸ’¾ Data saved to ${this.dataFile}`); - } catch (error) { - console.error('Error saving data file:', error); - } - } - - // VIP operations - getVips(): any[] { - return this.data.vips; - } - - addVip(vip: any): any { - this.data.vips.push(vip); - this.saveData(); - return vip; - } - - updateVip(id: string, updatedVip: any): any | null { - const index = this.data.vips.findIndex(vip => vip.id === id); - if (index !== -1) { - this.data.vips[index] = updatedVip; - this.saveData(); - return this.data.vips[index]; - } - return null; - } - - deleteVip(id: string): any | null { - const index = this.data.vips.findIndex(vip => vip.id === id); - if (index !== -1) { - const deletedVip = this.data.vips.splice(index, 1)[0]; - // Also delete the VIP's schedule - delete this.data.schedules[id]; - this.saveData(); - return deletedVip; - } - return null; - } - - // Driver operations - getDrivers(): any[] { - return this.data.drivers; - } - - addDriver(driver: any): any { - this.data.drivers.push(driver); - this.saveData(); - return driver; - } - - updateDriver(id: string, updatedDriver: any): any | null { - const index = this.data.drivers.findIndex(driver => driver.id === id); - if (index !== -1) { - this.data.drivers[index] = updatedDriver; - this.saveData(); - return this.data.drivers[index]; - } - return null; - } - - deleteDriver(id: string): any | null { - const index = this.data.drivers.findIndex(driver => driver.id === id); - if (index !== -1) { - const deletedDriver = this.data.drivers.splice(index, 1)[0]; - this.saveData(); - return deletedDriver; - } - return null; - } - - // Schedule operations - getSchedule(vipId: string): any[] { - return this.data.schedules[vipId] || []; - } - - addScheduleEvent(vipId: string, event: any): any { - if (!this.data.schedules[vipId]) { - this.data.schedules[vipId] = []; - } - this.data.schedules[vipId].push(event); - this.saveData(); - return event; - } - - updateScheduleEvent(vipId: string, eventId: string, updatedEvent: any): any | null { - if (!this.data.schedules[vipId]) { - return null; - } - - const index = this.data.schedules[vipId].findIndex(event => event.id === eventId); - if (index !== -1) { - this.data.schedules[vipId][index] = updatedEvent; - this.saveData(); - return this.data.schedules[vipId][index]; - } - return null; - } - - deleteScheduleEvent(vipId: string, eventId: string): any | null { - if (!this.data.schedules[vipId]) { - return null; - } - - const index = this.data.schedules[vipId].findIndex(event => event.id === eventId); - if (index !== -1) { - const deletedEvent = this.data.schedules[vipId].splice(index, 1)[0]; - this.saveData(); - return deletedEvent; - } - return null; - } - - getAllSchedules(): { [vipId: string]: any[] } { - return this.data.schedules; - } - - // Admin settings operations - getAdminSettings(): any { - return this.data.adminSettings; - } - - updateAdminSettings(settings: any): void { - this.data.adminSettings = { ...this.data.adminSettings, ...settings }; - this.saveData(); - } - - // Backup and restore operations - createBackup(): string { - const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); - const backupFile = path.join(this.dataDir, `backup-${timestamp}.json`); - - try { - fs.copyFileSync(this.dataFile, backupFile); - console.log(`šŸ“¦ Backup created: ${backupFile}`); - return backupFile; - } catch (error) { - console.error('Error creating backup:', error); - throw error; - } - } - - // User operations - getUsers(): any[] { - return this.data.users; - } - - getUserByEmail(email: string): any | null { - return this.data.users.find(user => user.email === email) || null; - } - - getUserById(id: string): any | null { - return this.data.users.find(user => user.id === id) || null; - } - - addUser(user: any): any { - // Add timestamps - const userWithTimestamps = { - ...user, - created_at: new Date().toISOString(), - last_sign_in_at: new Date().toISOString() - }; - - this.data.users.push(userWithTimestamps); - this.saveData(); - console.log(`šŸ‘¤ Added user: ${user.name} (${user.email}) as ${user.role}`); - return userWithTimestamps; - } - - updateUser(email: string, updatedUser: any): any | null { - const index = this.data.users.findIndex(user => user.email === email); - if (index !== -1) { - this.data.users[index] = { ...this.data.users[index], ...updatedUser }; - this.saveData(); - console.log(`šŸ‘¤ Updated user: ${this.data.users[index].name} (${email})`); - return this.data.users[index]; - } - return null; - } - - updateUserRole(email: string, role: string): any | null { - const index = this.data.users.findIndex(user => user.email === email); - if (index !== -1) { - this.data.users[index].role = role; - this.saveData(); - console.log(`šŸ‘¤ Updated user role: ${this.data.users[index].name} (${email}) -> ${role}`); - return this.data.users[index]; - } - return null; - } - - updateUserLastSignIn(email: string): any | null { - const index = this.data.users.findIndex(user => user.email === email); - if (index !== -1) { - this.data.users[index].last_sign_in_at = new Date().toISOString(); - this.saveData(); - return this.data.users[index]; - } - return null; - } - - deleteUser(email: string): any | null { - const index = this.data.users.findIndex(user => user.email === email); - if (index !== -1) { - const deletedUser = this.data.users.splice(index, 1)[0]; - this.saveData(); - console.log(`šŸ‘¤ Deleted user: ${deletedUser.name} (${email})`); - return deletedUser; - } - return null; - } - - getUserCount(): number { - return this.data.users.length; - } - - getDataStats(): any { - return { - vips: this.data.vips.length, - drivers: this.data.drivers.length, - users: this.data.users.length, - scheduledEvents: Object.values(this.data.schedules).reduce((total, events) => total + events.length, 0), - vipsWithSchedules: Object.keys(this.data.schedules).length, - dataFile: this.dataFile, - lastModified: fs.existsSync(this.dataFile) ? fs.statSync(this.dataFile).mtime : null - }; - } -} - -export default new DataService(); diff --git a/backend-old-20260125/src/services/databaseService.ts b/backend-old-20260125/src/services/databaseService.ts deleted file mode 100644 index f4bdbb6..0000000 --- a/backend-old-20260125/src/services/databaseService.ts +++ /dev/null @@ -1,563 +0,0 @@ -import { Pool, PoolClient } from 'pg'; -import { createClient, RedisClientType } from 'redis'; - -class DatabaseService { - private pool: Pool; - private redis: RedisClientType; - - constructor() { - this.pool = new Pool({ - connectionString: process.env.DATABASE_URL, - ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false - }); - - // Initialize Redis connection - this.redis = createClient({ - socket: { - host: process.env.REDIS_HOST || 'redis', - port: parseInt(process.env.REDIS_PORT || '6379') - } - }); - - this.redis.on('error', (err) => { - console.error('āŒ Redis connection error:', err); - }); - - // Test connections on startup - this.testConnection(); - this.testRedisConnection(); - } - - private async testConnection(): Promise { - try { - const client = await this.pool.connect(); - console.log('āœ… Connected to PostgreSQL database'); - client.release(); - } catch (error) { - console.error('āŒ Failed to connect to PostgreSQL database:', error); - } - } - - private async testRedisConnection(): Promise { - try { - if (!this.redis.isOpen) { - await this.redis.connect(); - } - await this.redis.ping(); - console.log('āœ… Connected to Redis'); - } catch (error) { - console.error('āŒ Failed to connect to Redis:', error); - } - } - - async query(text: string, params?: any[]): Promise { - const client = await this.pool.connect(); - try { - const result = await client.query(text, params); - return result; - } finally { - client.release(); - } - } - - async getClient(): Promise { - return await this.pool.connect(); - } - - async close(): Promise { - await this.pool.end(); - if (this.redis.isOpen) { - await this.redis.disconnect(); - } - } - - // Initialize database tables - async initializeTables(): Promise { - try { - // Create users table (matching the actual schema with auth0_sub) - await this.query(` - CREATE TABLE IF NOT EXISTS users ( - id VARCHAR(255) PRIMARY KEY, - auth0_sub VARCHAR(255) UNIQUE NOT NULL, - email VARCHAR(255) UNIQUE NOT NULL, - name VARCHAR(255) NOT NULL, - role VARCHAR(50) NOT NULL CHECK (role IN ('driver', 'coordinator', 'administrator')), - profile_picture_url TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - last_login TIMESTAMP, - is_active BOOLEAN DEFAULT true, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - approval_status VARCHAR(20) DEFAULT 'pending' CHECK (approval_status IN ('pending', 'approved', 'denied')), - identity_provider VARCHAR(50) DEFAULT 'auth0' - ) - `); - - // Add approval_status column if it doesn't exist (migration for existing databases) - await this.query(` - ALTER TABLE users - ADD COLUMN IF NOT EXISTS approval_status VARCHAR(20) DEFAULT 'pending' CHECK (approval_status IN ('pending', 'approved', 'denied')) - `); - - // Add identity_provider column if it doesn't exist - await this.query(` - ALTER TABLE users - ADD COLUMN IF NOT EXISTS identity_provider VARCHAR(50) DEFAULT 'auth0' - `); - - // Create indexes - await this.query(` - CREATE INDEX IF NOT EXISTS idx_users_auth0_sub ON users(auth0_sub) - `); - - await this.query(` - CREATE INDEX IF NOT EXISTS idx_users_email ON users(email) - `); - - await this.query(` - CREATE INDEX IF NOT EXISTS idx_users_role ON users(role) - `); - - console.log('āœ… Database tables initialized successfully'); - } catch (error) { - console.error('āŒ Failed to initialize database tables:', error); - throw error; - } - } - - // User management methods - async createUser(user: { - id: string; - auth0_sub: string; - email: string; - name: string; - profile_picture_url?: string; - role: string; - }): Promise { - const query = ` - INSERT INTO users (id, auth0_sub, email, name, profile_picture_url, role, last_login, identity_provider) - VALUES ($1, $2, $3, $4, $5, $6, CURRENT_TIMESTAMP, 'auth0') - RETURNING * - `; - - const values = [ - user.id, - user.auth0_sub, - user.email, - user.name, - user.profile_picture_url || null, - user.role - ]; - - const result = await this.query(query, values); - console.log(`šŸ‘¤ Created user: ${user.name} (${user.email}) as ${user.role}`); - return result.rows[0]; - } - - async getUserByEmail(email: string): Promise { - const query = 'SELECT * FROM users WHERE email = $1'; - const result = await this.query(query, [email]); - return result.rows[0] || null; - } - - async getUserById(id: string): Promise { - const query = 'SELECT * FROM users WHERE id = $1'; - const result = await this.query(query, [id]); - return result.rows[0] || null; - } - - async getUserByAuth0Sub(auth0Sub: string): Promise { - const query = 'SELECT * FROM users WHERE auth0_sub = $1'; - const result = await this.query(query, [auth0Sub]); - return result.rows[0] || null; - } - - async getAllUsers(): Promise { - const query = 'SELECT * FROM users ORDER BY created_at ASC'; - const result = await this.query(query); - return result.rows; - } - - async updateUserRole(email: string, role: string): Promise { - const query = ` - UPDATE users - SET role = $1, updated_at = CURRENT_TIMESTAMP - WHERE email = $2 - RETURNING * - `; - - const result = await this.query(query, [role, email]); - if (result.rows[0]) { - console.log(`šŸ‘¤ Updated user role: ${result.rows[0].name} (${email}) -> ${role}`); - } - return result.rows[0] || null; - } - - async updateUserLastSignIn(email: string): Promise { - const query = ` - UPDATE users - SET last_login = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP - WHERE email = $1 - RETURNING * - `; - - const result = await this.query(query, [email]); - return result.rows[0] || null; - } - - async deleteUser(email: string): Promise { - const query = 'DELETE FROM users WHERE email = $1 RETURNING *'; - const result = await this.query(query, [email]); - if (result.rows[0]) { - console.log(`šŸ‘¤ Deleted user: ${result.rows[0].name} (${email})`); - } - return result.rows[0] || null; - } - - async getUserCount(): Promise { - const query = 'SELECT COUNT(*) as count FROM users'; - const result = await this.query(query); - return parseInt(result.rows[0].count); - } - - // User approval methods - async updateUserApprovalStatus(email: string, status: 'pending' | 'approved' | 'denied'): Promise { - const query = ` - UPDATE users - SET approval_status = $1, updated_at = CURRENT_TIMESTAMP - WHERE email = $2 - RETURNING * - `; - - const result = await this.query(query, [status, email]); - if (result.rows[0]) { - console.log(`šŸ‘¤ Updated user approval: ${result.rows[0].name} (${email}) -> ${status}`); - } - return result.rows[0] || null; - } - - async getPendingUsers(): Promise { - const query = 'SELECT * FROM users WHERE approval_status = $1 ORDER BY created_at ASC'; - const result = await this.query(query, ['pending']); - return result.rows; - } - - async getApprovedUserCount(): Promise { - const query = 'SELECT COUNT(*) as count FROM users WHERE approval_status = $1'; - const result = await this.query(query, ['approved']); - return parseInt(result.rows[0].count); - } - - // Initialize all database tables and schema - async initializeDatabase(): Promise { - try { - await this.initializeTables(); - await this.initializeVipTables(); - - // Approve all existing users (migration for approval system) - await this.query(` - UPDATE users - SET approval_status = 'approved' - WHERE approval_status IS NULL OR approval_status = 'pending' - `); - console.log('āœ… Approved all existing users'); - - console.log('āœ… Database schema initialization completed'); - } catch (error) { - console.error('āŒ Failed to initialize database schema:', error); - throw error; - } - } - - // VIP table initialization using the correct schema - async initializeVipTables(): Promise { - try { - // Check if VIPs table exists and has the correct schema - const tableExists = await this.query(` - SELECT EXISTS ( - SELECT FROM information_schema.tables - WHERE table_schema = 'public' - AND table_name = 'vips' - ) - `); - - if (tableExists.rows[0].exists) { - // Check if the table has the correct columns - const columnCheck = await this.query(` - SELECT column_name - FROM information_schema.columns - WHERE table_name = 'vips' - AND column_name = 'organization' - `); - - if (columnCheck.rows.length === 0) { - console.log('šŸ”„ Migrating VIPs table to new schema...'); - // Drop the old table and recreate with correct schema - await this.query(`DROP TABLE IF EXISTS vips CASCADE`); - } - } - - // Create VIPs table with correct schema matching enhancedDataService expectations - await this.query(` - CREATE TABLE IF NOT EXISTS vips ( - id VARCHAR(255) PRIMARY KEY, - name VARCHAR(255) NOT NULL, - organization VARCHAR(255) NOT NULL, - department VARCHAR(255) DEFAULT 'Office of Development', - transport_mode VARCHAR(50) NOT NULL CHECK (transport_mode IN ('flight', 'self-driving')), - expected_arrival TIMESTAMP, - needs_airport_pickup BOOLEAN DEFAULT false, - needs_venue_transport BOOLEAN DEFAULT true, - notes TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) - `); - - // Create flights table (for VIPs with flight transport) - await this.query(` - CREATE TABLE IF NOT EXISTS flights ( - id SERIAL PRIMARY KEY, - vip_id VARCHAR(255) REFERENCES vips(id) ON DELETE CASCADE, - flight_number VARCHAR(50) NOT NULL, - flight_date DATE NOT NULL, - segment INTEGER NOT NULL, - departure_airport VARCHAR(10), - arrival_airport VARCHAR(10), - scheduled_departure TIMESTAMP, - scheduled_arrival TIMESTAMP, - actual_departure TIMESTAMP, - actual_arrival TIMESTAMP, - status VARCHAR(50), - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) - `); - - // Check and migrate drivers table - const driversTableExists = await this.query(` - SELECT EXISTS ( - SELECT FROM information_schema.tables - WHERE table_schema = 'public' - AND table_name = 'drivers' - ) - `); - - if (driversTableExists.rows[0].exists) { - // Check if drivers table has the correct schema (phone column and department column) - const driversSchemaCheck = await this.query(` - SELECT column_name - FROM information_schema.columns - WHERE table_name = 'drivers' - AND column_name IN ('phone', 'department') - `); - - if (driversSchemaCheck.rows.length < 2) { - console.log('šŸ”„ Migrating drivers table to new schema...'); - await this.query(`DROP TABLE IF EXISTS drivers CASCADE`); - } - } - - // Create drivers table with correct schema - await this.query(` - CREATE TABLE IF NOT EXISTS drivers ( - id VARCHAR(255) PRIMARY KEY, - name VARCHAR(255) NOT NULL, - phone VARCHAR(50) NOT NULL, - department VARCHAR(255) DEFAULT 'Office of Development', - user_id VARCHAR(255) REFERENCES users(id) ON DELETE SET NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) - `); - - // Check and migrate schedule_events table - const scheduleTableExists = await this.query(` - SELECT EXISTS ( - SELECT FROM information_schema.tables - WHERE table_schema = 'public' - AND table_name = 'schedule_events' - ) - `); - - if (!scheduleTableExists.rows[0].exists) { - // Check for old 'schedules' table and drop it - const oldScheduleExists = await this.query(` - SELECT EXISTS ( - SELECT FROM information_schema.tables - WHERE table_schema = 'public' - AND table_name = 'schedules' - ) - `); - - if (oldScheduleExists.rows[0].exists) { - console.log('šŸ”„ Migrating schedules table to schedule_events...'); - await this.query(`DROP TABLE IF EXISTS schedules CASCADE`); - } - } - - // Create schedule_events table - await this.query(` - CREATE TABLE IF NOT EXISTS schedule_events ( - id VARCHAR(255) PRIMARY KEY, - vip_id VARCHAR(255) REFERENCES vips(id) ON DELETE CASCADE, - title VARCHAR(255) NOT NULL, - location VARCHAR(255) NOT NULL, - start_time TIMESTAMP NOT NULL, - end_time TIMESTAMP NOT NULL, - description TEXT, - assigned_driver_id VARCHAR(255) REFERENCES drivers(id) ON DELETE SET NULL, - status VARCHAR(50) DEFAULT 'scheduled' CHECK (status IN ('scheduled', 'in-progress', 'completed', 'cancelled')), - event_type VARCHAR(50) NOT NULL CHECK (event_type IN ('transport', 'meeting', 'event', 'meal', 'accommodation')), - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) - `); - - // Create system_setup table for tracking initial setup - await this.query(` - CREATE TABLE IF NOT EXISTS system_setup ( - id SERIAL PRIMARY KEY, - setup_completed BOOLEAN DEFAULT false, - first_admin_created BOOLEAN DEFAULT false, - setup_date TIMESTAMP, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) - `); - - // Create admin_settings table - await this.query(` - CREATE TABLE IF NOT EXISTS admin_settings ( - id SERIAL PRIMARY KEY, - setting_key VARCHAR(255) UNIQUE NOT NULL, - setting_value TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) - `); - - // Create indexes for better performance - await this.query(`CREATE INDEX IF NOT EXISTS idx_vips_transport_mode ON vips(transport_mode)`); - await this.query(`CREATE INDEX IF NOT EXISTS idx_flights_vip_id ON flights(vip_id)`); - await this.query(`CREATE INDEX IF NOT EXISTS idx_flights_date ON flights(flight_date)`); - await this.query(`CREATE INDEX IF NOT EXISTS idx_schedule_events_vip_id ON schedule_events(vip_id)`); - await this.query(`CREATE INDEX IF NOT EXISTS idx_schedule_events_driver_id ON schedule_events(assigned_driver_id)`); - await this.query(`CREATE INDEX IF NOT EXISTS idx_schedule_events_start_time ON schedule_events(start_time)`); - await this.query(`CREATE INDEX IF NOT EXISTS idx_schedule_events_status ON schedule_events(status)`); - await this.query(`CREATE INDEX IF NOT EXISTS idx_drivers_user_id ON drivers(user_id)`); - - // Create updated_at trigger function - await this.query(` - CREATE OR REPLACE FUNCTION update_updated_at_column() - RETURNS TRIGGER AS $$ - BEGIN - NEW.updated_at = CURRENT_TIMESTAMP; - RETURN NEW; - END; - $$ language 'plpgsql' - `); - - // Create triggers for updated_at (drop if exists first) - await this.query(`DROP TRIGGER IF EXISTS update_vips_updated_at ON vips`); - await this.query(`DROP TRIGGER IF EXISTS update_flights_updated_at ON flights`); - await this.query(`DROP TRIGGER IF EXISTS update_drivers_updated_at ON drivers`); - await this.query(`DROP TRIGGER IF EXISTS update_schedule_events_updated_at ON schedule_events`); - await this.query(`DROP TRIGGER IF EXISTS update_admin_settings_updated_at ON admin_settings`); - - await this.query(`CREATE TRIGGER update_vips_updated_at BEFORE UPDATE ON vips FOR EACH ROW EXECUTE FUNCTION update_updated_at_column()`); - await this.query(`CREATE TRIGGER update_flights_updated_at BEFORE UPDATE ON flights FOR EACH ROW EXECUTE FUNCTION update_updated_at_column()`); - await this.query(`CREATE TRIGGER update_drivers_updated_at BEFORE UPDATE ON drivers FOR EACH ROW EXECUTE FUNCTION update_updated_at_column()`); - await this.query(`CREATE TRIGGER update_schedule_events_updated_at BEFORE UPDATE ON schedule_events FOR EACH ROW EXECUTE FUNCTION update_updated_at_column()`); - await this.query(`CREATE TRIGGER update_admin_settings_updated_at BEFORE UPDATE ON admin_settings FOR EACH ROW EXECUTE FUNCTION update_updated_at_column()`); - - console.log('āœ… VIP Coordinator database schema initialized successfully'); - } catch (error) { - console.error('āŒ Failed to initialize VIP tables:', error); - throw error; - } - } - - // Redis-based driver location tracking - async getDriverLocation(driverId: string): Promise<{ lat: number; lng: number } | null> { - try { - if (!this.redis.isOpen) { - await this.redis.connect(); - } - - const location = await this.redis.hGetAll(`driver:${driverId}:location`); - - if (location && location.lat && location.lng) { - return { - lat: parseFloat(location.lat), - lng: parseFloat(location.lng) - }; - } - - return null; - } catch (error) { - console.error('āŒ Error getting driver location from Redis:', error); - return null; - } - } - - async updateDriverLocation(driverId: string, location: { lat: number; lng: number }): Promise { - try { - if (!this.redis.isOpen) { - await this.redis.connect(); - } - - const key = `driver:${driverId}:location`; - await this.redis.hSet(key, { - lat: location.lat.toString(), - lng: location.lng.toString(), - updated_at: new Date().toISOString() - }); - - // Set expiration to 24 hours - await this.redis.expire(key, 24 * 60 * 60); - } catch (error) { - console.error('āŒ Error updating driver location in Redis:', error); - } - } - - async getAllDriverLocations(): Promise<{ [driverId: string]: { lat: number; lng: number } }> { - try { - if (!this.redis.isOpen) { - await this.redis.connect(); - } - - const keys = await this.redis.keys('driver:*:location'); - const locations: { [driverId: string]: { lat: number; lng: number } } = {}; - - for (const key of keys) { - const driverId = key.split(':')[1]; - const location = await this.redis.hGetAll(key); - - if (location && location.lat && location.lng) { - locations[driverId] = { - lat: parseFloat(location.lat), - lng: parseFloat(location.lng) - }; - } - } - - return locations; - } catch (error) { - console.error('āŒ Error getting all driver locations from Redis:', error); - return {}; - } - } - - async removeDriverLocation(driverId: string): Promise { - try { - if (!this.redis.isOpen) { - await this.redis.connect(); - } - - await this.redis.del(`driver:${driverId}:location`); - } catch (error) { - console.error('āŒ Error removing driver location from Redis:', error); - } - } -} - -export default new DatabaseService(); diff --git a/backend-old-20260125/src/services/driverConflictService.ts b/backend-old-20260125/src/services/driverConflictService.ts deleted file mode 100644 index b846445..0000000 --- a/backend-old-20260125/src/services/driverConflictService.ts +++ /dev/null @@ -1,184 +0,0 @@ -interface ScheduleEvent { - id: string; - title: string; - location: string; - startTime: string; - endTime: string; - assignedDriverId?: string; - vipId: string; - vipName: string; -} - -interface ConflictInfo { - type: 'overlap' | 'tight_turnaround' | 'back_to_back'; - severity: 'low' | 'medium' | 'high'; - message: string; - conflictingEvent: ScheduleEvent; - timeDifference?: number; // minutes -} - -interface DriverAvailability { - driverId: string; - driverName: string; - status: 'available' | 'scheduled' | 'overlapping' | 'tight_turnaround'; - assignmentCount: number; - conflicts: ConflictInfo[]; - currentAssignments: ScheduleEvent[]; -} - -class DriverConflictService { - - // Check for conflicts when assigning a driver to an event - checkDriverConflicts( - driverId: string, - newEvent: { startTime: string; endTime: string; location: string }, - allSchedules: { [vipId: string]: ScheduleEvent[] }, - drivers: any[] - ): ConflictInfo[] { - const conflicts: ConflictInfo[] = []; - const driver = drivers.find(d => d.id === driverId); - if (!driver) return conflicts; - - // Get all events assigned to this driver - const driverEvents = this.getDriverEvents(driverId, allSchedules); - - const newStartTime = new Date(newEvent.startTime); - const newEndTime = new Date(newEvent.endTime); - - for (const existingEvent of driverEvents) { - const existingStart = new Date(existingEvent.startTime); - const existingEnd = new Date(existingEvent.endTime); - - // Check for direct time overlap - if (this.hasTimeOverlap(newStartTime, newEndTime, existingStart, existingEnd)) { - conflicts.push({ - type: 'overlap', - severity: 'high', - message: `Direct time conflict with "${existingEvent.title}" for ${existingEvent.vipName}`, - conflictingEvent: existingEvent - }); - } - // Check for tight turnaround (less than 15 minutes between events) - else { - const timeBetween = this.getTimeBetweenEvents( - newStartTime, newEndTime, existingStart, existingEnd - ); - - if (timeBetween !== null && timeBetween < 15) { - conflicts.push({ - type: 'tight_turnaround', - severity: timeBetween < 5 ? 'high' : 'medium', - message: `Only ${timeBetween} minutes between events. Previous: "${existingEvent.title}"`, - conflictingEvent: existingEvent, - timeDifference: timeBetween - }); - } - } - } - - return conflicts; - } - - // Get availability status for all drivers for a specific time slot - getDriverAvailability( - eventTime: { startTime: string; endTime: string; location: string }, - allSchedules: { [vipId: string]: ScheduleEvent[] }, - drivers: any[] - ): DriverAvailability[] { - return drivers.map(driver => { - const conflicts = this.checkDriverConflicts(driver.id, eventTime, allSchedules, drivers); - const driverEvents = this.getDriverEvents(driver.id, allSchedules); - - let status: DriverAvailability['status'] = 'available'; - - if (conflicts.length > 0) { - const hasOverlap = conflicts.some(c => c.type === 'overlap'); - const hasTightTurnaround = conflicts.some(c => c.type === 'tight_turnaround'); - - if (hasOverlap) { - status = 'overlapping'; - } else if (hasTightTurnaround) { - status = 'tight_turnaround'; - } - } else if (driverEvents.length > 0) { - status = 'scheduled'; - } - - return { - driverId: driver.id, - driverName: driver.name, - status, - assignmentCount: driverEvents.length, - conflicts, - currentAssignments: driverEvents - }; - }); - } - - // Get all events assigned to a specific driver - private getDriverEvents(driverId: string, allSchedules: { [vipId: string]: ScheduleEvent[] }): ScheduleEvent[] { - const driverEvents: ScheduleEvent[] = []; - - Object.entries(allSchedules).forEach(([vipId, events]) => { - events.forEach(event => { - if (event.assignedDriverId === driverId) { - driverEvents.push({ - ...event, - vipId, - vipName: event.title // We'll need to get actual VIP name from VIP data - }); - } - }); - }); - - // Sort by start time - return driverEvents.sort((a, b) => - new Date(a.startTime).getTime() - new Date(b.startTime).getTime() - ); - } - - // Check if two time periods overlap - private hasTimeOverlap( - start1: Date, end1: Date, - start2: Date, end2: Date - ): boolean { - return start1 < end2 && start2 < end1; - } - - // Get minutes between two events (null if they overlap) - private getTimeBetweenEvents( - newStart: Date, newEnd: Date, - existingStart: Date, existingEnd: Date - ): number | null { - // If new event is after existing event - if (newStart >= existingEnd) { - return Math.floor((newStart.getTime() - existingEnd.getTime()) / (1000 * 60)); - } - // If new event is before existing event - else if (newEnd <= existingStart) { - return Math.floor((existingStart.getTime() - newEnd.getTime()) / (1000 * 60)); - } - // Events overlap - return null; - } - - // Generate summary message for driver status - getDriverStatusSummary(availability: DriverAvailability): string { - switch (availability.status) { - case 'available': - return `āœ… Fully available (${availability.assignmentCount} assignments)`; - case 'scheduled': - return `🟔 Has ${availability.assignmentCount} assignment(s) but available for this time`; - case 'tight_turnaround': - const tightConflict = availability.conflicts.find(c => c.type === 'tight_turnaround'); - return `⚔ Tight turnaround - ${tightConflict?.timeDifference} min between events`; - case 'overlapping': - return `šŸ”“ Time conflict with existing assignment`; - default: - return 'Unknown status'; - } - } -} - -export default new DriverConflictService(); -export { DriverAvailability, ConflictInfo, ScheduleEvent }; diff --git a/backend-old-20260125/src/services/enhancedDataService.ts b/backend-old-20260125/src/services/enhancedDataService.ts deleted file mode 100644 index 543b363..0000000 --- a/backend-old-20260125/src/services/enhancedDataService.ts +++ /dev/null @@ -1,677 +0,0 @@ -import pool from '../config/database'; -import databaseService from './databaseService'; - -interface VipData { - id: string; - name: string; - organization: string; - department?: string; - transportMode: 'flight' | 'self-driving'; - expectedArrival?: string; - needsAirportPickup?: boolean; - needsVenueTransport: boolean; - notes?: string; - flights?: Array<{ - flightNumber: string; - flightDate: string; - segment: number; - }>; -} - -interface DriverData { - id: string; - name: string; - phone: string; - department?: string; - currentLocation?: { lat: number; lng: number }; - assignedVipIds?: string[]; -} - -interface ScheduleEventData { - id: string; - title: string; - location: string; - startTime: string; - endTime: string; - description?: string; - assignedDriverId?: string; - status: string; - type: string; -} - -class EnhancedDataService { - - // VIP operations - async getVips(): Promise { - try { - const query = ` - SELECT v.*, - COALESCE( - json_agg( - json_build_object( - 'flightNumber', f.flight_number, - 'flightDate', f.flight_date, - 'segment', f.segment - ) ORDER BY f.segment - ) FILTER (WHERE f.id IS NOT NULL), - '[]'::json - ) as flights - FROM vips v - LEFT JOIN flights f ON v.id = f.vip_id - GROUP BY v.id - ORDER BY v.name - `; - - const result = await pool.query(query); - - return result.rows.map(row => ({ - id: row.id, - name: row.name, - organization: row.organization, - department: row.department, - transportMode: row.transport_mode, - expectedArrival: row.expected_arrival, - needsAirportPickup: row.needs_airport_pickup, - needsVenueTransport: row.needs_venue_transport, - notes: row.notes, - flights: row.flights - })); - } catch (error) { - console.error('āŒ Error fetching VIPs:', error); - throw error; - } - } - - async addVip(vip: VipData): Promise { - const client = await pool.connect(); - - try { - await client.query('BEGIN'); - - // Insert VIP - const vipQuery = ` - INSERT INTO vips (id, name, organization, department, transport_mode, expected_arrival, needs_airport_pickup, needs_venue_transport, notes) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) - RETURNING * - `; - - const vipResult = await client.query(vipQuery, [ - vip.id, - vip.name, - vip.organization, - vip.department || 'Office of Development', - vip.transportMode, - vip.expectedArrival || null, - vip.needsAirportPickup || false, - vip.needsVenueTransport, - vip.notes || '' - ]); - - // Insert flights if any - if (vip.flights && vip.flights.length > 0) { - for (const flight of vip.flights) { - const flightQuery = ` - INSERT INTO flights (vip_id, flight_number, flight_date, segment) - VALUES ($1, $2, $3, $4) - `; - - await client.query(flightQuery, [ - vip.id, - flight.flightNumber, - flight.flightDate, - flight.segment - ]); - } - } - - await client.query('COMMIT'); - - const savedVip = { - ...vip, - department: vipResult.rows[0].department, - transportMode: vipResult.rows[0].transport_mode, - expectedArrival: vipResult.rows[0].expected_arrival, - needsAirportPickup: vipResult.rows[0].needs_airport_pickup, - needsVenueTransport: vipResult.rows[0].needs_venue_transport - }; - - return savedVip; - } catch (error) { - await client.query('ROLLBACK'); - console.error('āŒ Error adding VIP:', error); - throw error; - } finally { - client.release(); - } - } - - async updateVip(id: string, vip: Partial): Promise { - const client = await pool.connect(); - - try { - await client.query('BEGIN'); - - // Update VIP - const vipQuery = ` - UPDATE vips - SET name = $2, organization = $3, department = $4, transport_mode = $5, - expected_arrival = $6, needs_airport_pickup = $7, needs_venue_transport = $8, notes = $9 - WHERE id = $1 - RETURNING * - `; - - const vipResult = await client.query(vipQuery, [ - id, - vip.name, - vip.organization, - vip.department || 'Office of Development', - vip.transportMode, - vip.expectedArrival || null, - vip.needsAirportPickup || false, - vip.needsVenueTransport, - vip.notes || '' - ]); - - if (vipResult.rows.length === 0) { - await client.query('ROLLBACK'); - return null; - } - - // Delete existing flights and insert new ones - await client.query('DELETE FROM flights WHERE vip_id = $1', [id]); - - if (vip.flights && vip.flights.length > 0) { - for (const flight of vip.flights) { - const flightQuery = ` - INSERT INTO flights (vip_id, flight_number, flight_date, segment) - VALUES ($1, $2, $3, $4) - `; - - await client.query(flightQuery, [ - id, - flight.flightNumber, - flight.flightDate, - flight.segment - ]); - } - } - - await client.query('COMMIT'); - - const updatedVip = { - id: vipResult.rows[0].id, - name: vipResult.rows[0].name, - organization: vipResult.rows[0].organization, - department: vipResult.rows[0].department, - transportMode: vipResult.rows[0].transport_mode, - expectedArrival: vipResult.rows[0].expected_arrival, - needsAirportPickup: vipResult.rows[0].needs_airport_pickup, - needsVenueTransport: vipResult.rows[0].needs_venue_transport, - notes: vipResult.rows[0].notes, - flights: vip.flights || [] - }; - - return updatedVip; - } catch (error) { - await client.query('ROLLBACK'); - console.error('āŒ Error updating VIP:', error); - throw error; - } finally { - client.release(); - } - } - - async deleteVip(id: string): Promise { - try { - const query = ` - DELETE FROM vips WHERE id = $1 RETURNING * - `; - - const result = await pool.query(query, [id]); - - if (result.rows.length === 0) { - return null; - } - - const deletedVip = { - id: result.rows[0].id, - name: result.rows[0].name, - organization: result.rows[0].organization, - department: result.rows[0].department, - transportMode: result.rows[0].transport_mode, - expectedArrival: result.rows[0].expected_arrival, - needsAirportPickup: result.rows[0].needs_airport_pickup, - needsVenueTransport: result.rows[0].needs_venue_transport, - notes: result.rows[0].notes - }; - - return deletedVip; - } catch (error) { - console.error('āŒ Error deleting VIP:', error); - throw error; - } - } - - // Driver operations - async getDrivers(): Promise { - try { - const query = ` - SELECT d.*, - COALESCE( - json_agg(DISTINCT se.vip_id) FILTER (WHERE se.vip_id IS NOT NULL), - '[]'::json - ) as assigned_vip_ids - FROM drivers d - LEFT JOIN schedule_events se ON d.id = se.assigned_driver_id - GROUP BY d.id - ORDER BY d.name - `; - - const result = await pool.query(query); - - // Get current locations from Redis - const driversWithLocations = await Promise.all( - result.rows.map(async (row) => { - const location = await databaseService.getDriverLocation(row.id); - - return { - id: row.id, - name: row.name, - phone: row.phone, - department: row.department, - currentLocation: location ? { lat: location.lat, lng: location.lng } : { lat: 0, lng: 0 }, - assignedVipIds: row.assigned_vip_ids || [] - }; - }) - ); - - return driversWithLocations; - } catch (error) { - console.error('āŒ Error fetching drivers:', error); - throw error; - } - } - - async addDriver(driver: DriverData): Promise { - try { - const query = ` - INSERT INTO drivers (id, name, phone, department) - VALUES ($1, $2, $3, $4) - RETURNING * - `; - - const result = await pool.query(query, [ - driver.id, - driver.name, - driver.phone, - driver.department || 'Office of Development' - ]); - - // Store location in Redis if provided - if (driver.currentLocation) { - await databaseService.updateDriverLocation(driver.id, driver.currentLocation); - } - - const savedDriver = { - id: result.rows[0].id, - name: result.rows[0].name, - phone: result.rows[0].phone, - department: result.rows[0].department, - currentLocation: driver.currentLocation || { lat: 0, lng: 0 } - }; - - return savedDriver; - } catch (error) { - console.error('āŒ Error adding driver:', error); - throw error; - } - } - - async updateDriver(id: string, driver: Partial): Promise { - try { - const query = ` - UPDATE drivers - SET name = $2, phone = $3, department = $4 - WHERE id = $1 - RETURNING * - `; - - const result = await pool.query(query, [ - id, - driver.name, - driver.phone, - driver.department || 'Office of Development' - ]); - - if (result.rows.length === 0) { - return null; - } - - // Update location in Redis if provided - if (driver.currentLocation) { - await databaseService.updateDriverLocation(id, driver.currentLocation); - } - - const updatedDriver = { - id: result.rows[0].id, - name: result.rows[0].name, - phone: result.rows[0].phone, - department: result.rows[0].department, - currentLocation: driver.currentLocation || { lat: 0, lng: 0 } - }; - - return updatedDriver; - } catch (error) { - console.error('āŒ Error updating driver:', error); - throw error; - } - } - - async deleteDriver(id: string): Promise { - try { - const query = ` - DELETE FROM drivers WHERE id = $1 RETURNING * - `; - - const result = await pool.query(query, [id]); - - if (result.rows.length === 0) { - return null; - } - - const deletedDriver = { - id: result.rows[0].id, - name: result.rows[0].name, - phone: result.rows[0].phone, - department: result.rows[0].department - }; - - return deletedDriver; - } catch (error) { - console.error('āŒ Error deleting driver:', error); - throw error; - } - } - - // Schedule operations - async getSchedule(vipId: string): Promise { - try { - const query = ` - SELECT * FROM schedule_events - WHERE vip_id = $1 - ORDER BY start_time - `; - - const result = await pool.query(query, [vipId]); - - return result.rows.map(row => ({ - id: row.id, - title: row.title, - location: row.location, - startTime: row.start_time, - endTime: row.end_time, - description: row.description, - assignedDriverId: row.assigned_driver_id, - status: row.status, - type: row.event_type - })); - } catch (error) { - console.error('āŒ Error fetching schedule:', error); - throw error; - } - } - - async addScheduleEvent(vipId: string, event: ScheduleEventData): Promise { - try { - const query = ` - INSERT INTO schedule_events (id, vip_id, title, location, start_time, end_time, description, assigned_driver_id, status, event_type) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) - RETURNING * - `; - - const result = await pool.query(query, [ - event.id, - vipId, - event.title, - event.location, - event.startTime, - event.endTime, - event.description || '', - event.assignedDriverId || null, - event.status, - event.type - ]); - - const savedEvent = { - id: result.rows[0].id, - title: result.rows[0].title, - location: result.rows[0].location, - startTime: result.rows[0].start_time, - endTime: result.rows[0].end_time, - description: result.rows[0].description, - assignedDriverId: result.rows[0].assigned_driver_id, - status: result.rows[0].status, - type: result.rows[0].event_type - }; - - return savedEvent; - } catch (error) { - console.error('āŒ Error adding schedule event:', error); - throw error; - } - } - - async updateScheduleEvent(vipId: string, eventId: string, event: ScheduleEventData): Promise { - try { - const query = ` - UPDATE schedule_events - SET title = $3, location = $4, start_time = $5, end_time = $6, description = $7, assigned_driver_id = $8, status = $9, event_type = $10 - WHERE id = $1 AND vip_id = $2 - RETURNING * - `; - - const result = await pool.query(query, [ - eventId, - vipId, - event.title, - event.location, - event.startTime, - event.endTime, - event.description || '', - event.assignedDriverId || null, - event.status, - event.type - ]); - - if (result.rows.length === 0) { - return null; - } - - const updatedEvent = { - id: result.rows[0].id, - title: result.rows[0].title, - location: result.rows[0].location, - startTime: result.rows[0].start_time, - endTime: result.rows[0].end_time, - description: result.rows[0].description, - assignedDriverId: result.rows[0].assigned_driver_id, - status: result.rows[0].status, - type: result.rows[0].event_type - }; - - return updatedEvent; - } catch (error) { - console.error('āŒ Error updating schedule event:', error); - throw error; - } - } - - async deleteScheduleEvent(vipId: string, eventId: string): Promise { - try { - const query = ` - DELETE FROM schedule_events - WHERE id = $1 AND vip_id = $2 - RETURNING * - `; - - const result = await pool.query(query, [eventId, vipId]); - - if (result.rows.length === 0) { - return null; - } - - const deletedEvent = { - id: result.rows[0].id, - title: result.rows[0].title, - location: result.rows[0].location, - startTime: result.rows[0].start_time, - endTime: result.rows[0].end_time, - description: result.rows[0].description, - assignedDriverId: result.rows[0].assigned_driver_id, - status: result.rows[0].status, - type: result.rows[0].event_type - }; - - return deletedEvent; - } catch (error) { - console.error('āŒ Error deleting schedule event:', error); - throw error; - } - } - - async getAllSchedules(): Promise<{ [vipId: string]: ScheduleEventData[] }> { - try { - const query = ` - SELECT * FROM schedule_events - ORDER BY vip_id, start_time - `; - - const result = await pool.query(query); - - const schedules: { [vipId: string]: ScheduleEventData[] } = {}; - - for (const row of result.rows) { - const vipId = row.vip_id; - - if (!schedules[vipId]) { - schedules[vipId] = []; - } - - schedules[vipId].push({ - id: row.id, - title: row.title, - location: row.location, - startTime: row.start_time, - endTime: row.end_time, - description: row.description, - assignedDriverId: row.assigned_driver_id, - status: row.status, - type: row.event_type - }); - } - - return schedules; - } catch (error) { - console.error('āŒ Error fetching all schedules:', error); - throw error; - } - } - - // Admin settings operations - async getAdminSettings(): Promise { - try { - const query = ` - SELECT setting_key, setting_value FROM admin_settings - `; - - const result = await pool.query(query); - - // Default settings structure - const defaultSettings = { - apiKeys: { - aviationStackKey: '', - googleMapsKey: '', - twilioKey: '', - googleClientId: '', - googleClientSecret: '' - }, - systemSettings: { - defaultPickupLocation: '', - defaultDropoffLocation: '', - timeZone: 'America/New_York', - notificationsEnabled: false - } - }; - - // If no settings exist, return defaults - if (result.rows.length === 0) { - return defaultSettings; - } - - // Reconstruct nested object from flattened keys - const settings: any = { ...defaultSettings }; - - for (const row of result.rows) { - const keys = row.setting_key.split('.'); - let current = settings; - - for (let i = 0; i < keys.length - 1; i++) { - if (!current[keys[i]]) { - current[keys[i]] = {}; - } - current = current[keys[i]]; - } - - // Parse boolean values - let value = row.setting_value; - if (value === 'true') value = true; - else if (value === 'false') value = false; - - current[keys[keys.length - 1]] = value; - } - - return settings; - } catch (error) { - console.error('āŒ Error fetching admin settings:', error); - throw error; - } - } - - async updateAdminSettings(settings: any): Promise { - try { - // Flatten settings and update - const flattenSettings = (obj: any, prefix = ''): Array<{key: string, value: string}> => { - const result: Array<{key: string, value: string}> = []; - - for (const [key, value] of Object.entries(obj)) { - const fullKey = prefix ? `${prefix}.${key}` : key; - - if (typeof value === 'object' && value !== null) { - result.push(...flattenSettings(value, fullKey)); - } else { - result.push({ key: fullKey, value: String(value) }); - } - } - - return result; - }; - - const flatSettings = flattenSettings(settings); - - for (const setting of flatSettings) { - const query = ` - INSERT INTO admin_settings (setting_key, setting_value) - VALUES ($1, $2) - ON CONFLICT (setting_key) DO UPDATE SET setting_value = $2 - `; - - await pool.query(query, [setting.key, setting.value]); - } - } catch (error) { - console.error('āŒ Error updating admin settings:', error); - throw error; - } - } -} - -export default new EnhancedDataService(); diff --git a/backend-old-20260125/src/services/flightService.ts b/backend-old-20260125/src/services/flightService.ts deleted file mode 100644 index 5ec12eb..0000000 --- a/backend-old-20260125/src/services/flightService.ts +++ /dev/null @@ -1,262 +0,0 @@ -// Real Flight tracking service with Google scraping -// No mock data - only real flight information - -interface FlightData { - flightNumber: string; - flightDate: string; - status: string; - airline?: string; - aircraft?: string; - departure: { - airport: string; - airportName?: string; - scheduled: string; - estimated?: string; - actual?: string; - terminal?: string; - gate?: string; - }; - arrival: { - airport: string; - airportName?: string; - scheduled: string; - estimated?: string; - actual?: string; - terminal?: string; - gate?: string; - }; - delay?: number; - lastUpdated: string; - source: 'google' | 'aviationstack' | 'not_found'; -} - -interface FlightSearchParams { - flightNumber: string; - date: string; // YYYY-MM-DD format - departureAirport?: string; - arrivalAirport?: string; -} - -class FlightService { - private flightCache: Map = new Map(); - private updateIntervals: Map = new Map(); - - constructor() { - // No API keys needed for Google scraping - } - - // Real flight lookup - no mock data - async getFlightInfo(params: FlightSearchParams): Promise { - 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 - private async scrapeGoogleFlights(params: FlightSearchParams): Promise { - 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) - private async getFromAviationStack(params: FlightSearchParams): Promise { - 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 as any).error) { - console.error('AviationStack API error:', (data as any).error); - return null; - } - - if ((data as any).data && (data as any).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 as any).data.find((f: any) => f.flight_date === params.date); - - // If no exact date match, use most recent for validation - if (!flight) { - flight = (data as any).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: FlightSearchParams, intervalMinutes: number = 5): void { - 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: string): void { - const interval = this.updateIntervals.get(key); - if (interval) { - clearInterval(interval); - this.updateIntervals.delete(key); - } - } - - // Get multiple flights with date specificity - async getMultipleFlights(flightParams: FlightSearchParams[]): Promise<{ [key: string]: FlightData | null }> { - const results: { [key: string]: FlightData | null } = {}; - - 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 - private normalizeStatus(status: string): string { - const statusMap: { [key: string]: string } = { - 'scheduled': 'scheduled', - 'active': 'active', - 'landed': 'landed', - 'cancelled': 'cancelled', - 'incident': 'delayed', - 'diverted': 'diverted' - }; - - return statusMap[status.toLowerCase()] || status; - } - - // Clean up resources - cleanup(): void { - for (const [key, interval] of this.updateIntervals) { - clearInterval(interval); - } - this.updateIntervals.clear(); - this.flightCache.clear(); - } -} - -export default new FlightService(); -export { FlightData, FlightSearchParams }; diff --git a/backend-old-20260125/src/services/flightTrackingScheduler.ts b/backend-old-20260125/src/services/flightTrackingScheduler.ts deleted file mode 100644 index 7fe31b8..0000000 --- a/backend-old-20260125/src/services/flightTrackingScheduler.ts +++ /dev/null @@ -1,284 +0,0 @@ -// Flight Tracking Scheduler Service -// Efficiently batches flight API calls and manages tracking schedules - -interface ScheduledFlight { - vipId: string; - vipName: string; - flightNumber: string; - flightDate: string; - segment: number; - scheduledDeparture?: string; - lastChecked?: Date; - nextCheck?: Date; - status?: string; - hasLanded?: boolean; -} - -interface TrackingSchedule { - [date: string]: ScheduledFlight[]; -} - -class FlightTrackingScheduler { - private trackingSchedule: TrackingSchedule = {}; - private checkIntervals: Map = new Map(); - private flightService: any; - - constructor(flightService: any) { - this.flightService = flightService; - } - - // Add flights for a VIP to the tracking schedule - addVipFlights(vipId: string, vipName: string, flights: any[]) { - 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: 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: string) { - 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 - private 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 - private setupDateTracking(date: string) { - 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: Date | null = 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 - private async performBatchCheck(date: string) { - 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 - private stopDateTracking(date: string) { - 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(): any { - const status: any = {}; - - 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 = {}; - } -} - -export default FlightTrackingScheduler; diff --git a/backend-old-20260125/src/services/migrationService.ts b/backend-old-20260125/src/services/migrationService.ts deleted file mode 100644 index 1c30b99..0000000 --- a/backend-old-20260125/src/services/migrationService.ts +++ /dev/null @@ -1,180 +0,0 @@ -import { Pool } from 'pg'; -import * as fs from 'fs/promises'; -import * as path from 'path'; -import { env } from '../config/env'; - -export class MigrationService { - private pool: Pool; - private migrationsPath: string; - - constructor(pool: Pool) { - this.pool = pool; - this.migrationsPath = path.join(__dirname, '..', 'migrations'); - } - - /** - * Initialize migrations table - */ - async initializeMigrationsTable(): Promise { - const query = ` - CREATE TABLE IF NOT EXISTS migrations ( - id SERIAL PRIMARY KEY, - filename VARCHAR(255) UNIQUE NOT NULL, - applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - checksum VARCHAR(64) NOT NULL - ); - `; - - await this.pool.query(query); - } - - /** - * Get list of applied migrations - */ - async getAppliedMigrations(): Promise> { - const result = await this.pool.query( - 'SELECT filename FROM migrations ORDER BY applied_at' - ); - - return new Set(result.rows.map(row => row.filename)); - } - - /** - * Calculate checksum for migration file - */ - private async calculateChecksum(content: string): Promise { - const crypto = await import('crypto'); - return crypto.createHash('sha256').update(content).digest('hex'); - } - - /** - * Get all migration files sorted by name - */ - async getMigrationFiles(): Promise { - try { - const files = await fs.readdir(this.migrationsPath); - return files - .filter(file => file.endsWith('.sql')) - .sort(); // Ensures migrations run in order - } catch (error) { - // If migrations directory doesn't exist, return empty array - return []; - } - } - - /** - * Apply a single migration - */ - async applyMigration(filename: string): Promise { - const filepath = path.join(this.migrationsPath, filename); - const content = await fs.readFile(filepath, 'utf8'); - const checksum = await this.calculateChecksum(content); - - // Check if migration was already applied - const existing = await this.pool.query( - 'SELECT checksum FROM migrations WHERE filename = $1', - [filename] - ); - - if (existing.rows.length > 0) { - if (existing.rows[0].checksum !== checksum) { - throw new Error( - `Migration ${filename} has been modified after being applied!` - ); - } - return; // Migration already applied - } - - // Start transaction - const client = await this.pool.connect(); - try { - await client.query('BEGIN'); - - // Execute migration - await client.query(content); - - // Record migration - await client.query( - 'INSERT INTO migrations (filename, checksum) VALUES ($1, $2)', - [filename, checksum] - ); - - await client.query('COMMIT'); - console.log(`āœ… Applied migration: ${filename}`); - } catch (error) { - await client.query('ROLLBACK'); - throw error; - } finally { - client.release(); - } - } - - /** - * Run all pending migrations - */ - async runMigrations(): Promise { - console.log('šŸ”„ Checking for pending migrations...'); - - // Initialize migrations table - await this.initializeMigrationsTable(); - - // Get applied migrations - const appliedMigrations = await this.getAppliedMigrations(); - - // Get all migration files - const migrationFiles = await this.getMigrationFiles(); - - // Filter pending migrations - const pendingMigrations = migrationFiles.filter( - file => !appliedMigrations.has(file) - ); - - if (pendingMigrations.length === 0) { - console.log('✨ No pending migrations'); - return; - } - - console.log(`šŸ“¦ Found ${pendingMigrations.length} pending migrations`); - - // Apply each migration - for (const migration of pendingMigrations) { - await this.applyMigration(migration); - } - - console.log('āœ… All migrations completed successfully'); - } - - /** - * Create a new migration file - */ - static async createMigration(name: string): Promise { - const timestamp = new Date().toISOString() - .replace(/[-:]/g, '') - .replace('T', '_') - .split('.')[0]; - - const filename = `${timestamp}_${name.toLowerCase().replace(/\s+/g, '_')}.sql`; - const filepath = path.join(__dirname, '..', 'migrations', filename); - - const template = `-- Migration: ${name} --- Created: ${new Date().toISOString()} - --- Add your migration SQL here - -`; - - await fs.writeFile(filepath, template); - console.log(`Created migration: ${filename}`); - return filename; - } -} - -// Export a singleton instance -let migrationService: MigrationService | null = null; - -export function getMigrationService(pool: Pool): MigrationService { - if (!migrationService) { - migrationService = new MigrationService(pool); - } - return migrationService; -} \ No newline at end of file diff --git a/backend-old-20260125/src/services/scheduleValidationService.ts b/backend-old-20260125/src/services/scheduleValidationService.ts deleted file mode 100644 index 239f983..0000000 --- a/backend-old-20260125/src/services/scheduleValidationService.ts +++ /dev/null @@ -1,248 +0,0 @@ -interface ValidationError { - field: string; - message: string; - code: string; -} - -interface ScheduleEvent { - title: string; - startTime: string; - endTime: string; - location: string; - type: string; -} - -class ScheduleValidationService { - - // Validate a single schedule event - validateEvent(event: ScheduleEvent, isEdit: boolean = false): ValidationError[] { - const errors: ValidationError[] = []; - const now = new Date(); - const startTime = new Date(event.startTime); - const endTime = new Date(event.endTime); - - // 1. Check if dates are valid - if (isNaN(startTime.getTime())) { - errors.push({ - field: 'startTime', - message: 'Start time is not a valid date', - code: 'INVALID_START_DATE' - }); - } - - if (isNaN(endTime.getTime())) { - errors.push({ - field: 'endTime', - message: 'End time is not a valid date', - code: 'INVALID_END_DATE' - }); - } - - // If dates are invalid, return early - if (errors.length > 0) { - return errors; - } - - // 2. Check if start time is in the future (with 5-minute grace period for edits) - const graceMinutes = isEdit ? 5 : 0; - const minimumStartTime = new Date(now.getTime() + (graceMinutes * 60 * 1000)); - - if (startTime < minimumStartTime) { - errors.push({ - field: 'startTime', - message: isEdit - ? 'Start time must be at least 5 minutes in the future for edits' - : 'Start time must be in the future', - code: 'START_TIME_IN_PAST' - }); - } - - // 3. Check if end time is after start time - if (endTime <= startTime) { - errors.push({ - field: 'endTime', - message: 'End time must be after start time', - code: 'END_BEFORE_START' - }); - } - - // 4. Check minimum event duration (5 minutes) - const durationMinutes = (endTime.getTime() - startTime.getTime()) / (1000 * 60); - if (durationMinutes < 5) { - errors.push({ - field: 'endTime', - message: 'Event must be at least 5 minutes long', - code: 'DURATION_TOO_SHORT' - }); - } - - // 5. Check maximum event duration (24 hours) - if (durationMinutes > (24 * 60)) { - errors.push({ - field: 'endTime', - message: 'Event cannot be longer than 24 hours', - code: 'DURATION_TOO_LONG' - }); - } - - // 6. Check if end time is in the future - if (endTime < now) { - errors.push({ - field: 'endTime', - message: 'End time must be in the future', - code: 'END_TIME_IN_PAST' - }); - } - - // 7. Validate required fields - if (!event.title || event.title.trim().length === 0) { - errors.push({ - field: 'title', - message: 'Event title is required', - code: 'TITLE_REQUIRED' - }); - } - - if (!event.location || event.location.trim().length === 0) { - errors.push({ - field: 'location', - message: 'Event location is required', - code: 'LOCATION_REQUIRED' - }); - } - - if (!event.type || event.type.trim().length === 0) { - errors.push({ - field: 'type', - message: 'Event type is required', - code: 'TYPE_REQUIRED' - }); - } - - // 8. Validate title length - if (event.title && event.title.length > 100) { - errors.push({ - field: 'title', - message: 'Event title cannot exceed 100 characters', - code: 'TITLE_TOO_LONG' - }); - } - - // 9. Validate location length - if (event.location && event.location.length > 200) { - errors.push({ - field: 'location', - message: 'Event location cannot exceed 200 characters', - code: 'LOCATION_TOO_LONG' - }); - } - - // 10. Check for reasonable scheduling (not more than 2 years in the future) - const twoYearsFromNow = new Date(); - twoYearsFromNow.setFullYear(twoYearsFromNow.getFullYear() + 2); - - if (startTime > twoYearsFromNow) { - errors.push({ - field: 'startTime', - message: 'Event cannot be scheduled more than 2 years in the future', - code: 'START_TIME_TOO_FAR' - }); - } - - // 11. Check for business hours validation (optional warning) - const startHour = startTime.getHours(); - const endHour = endTime.getHours(); - - if (startHour < 6 || startHour > 23) { - // This is a warning, not an error - we'll add it but with a different severity - errors.push({ - field: 'startTime', - message: 'Event starts outside typical business hours (6 AM - 11 PM)', - code: 'OUTSIDE_BUSINESS_HOURS' - }); - } - - return errors; - } - - // Validate multiple events for conflicts and logical sequencing - validateEventSequence(events: ScheduleEvent[]): ValidationError[] { - const errors: ValidationError[] = []; - - // Sort events by start time - const sortedEvents = events - .map((event, index) => ({ ...event, originalIndex: index })) - .sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime()); - - // Check for overlapping events - for (let i = 0; i < sortedEvents.length - 1; i++) { - const currentEvent = sortedEvents[i]; - const nextEvent = sortedEvents[i + 1]; - - const currentEnd = new Date(currentEvent.endTime); - const nextStart = new Date(nextEvent.startTime); - - if (currentEnd > nextStart) { - errors.push({ - field: 'schedule', - message: `Event "${currentEvent.title}" overlaps with "${nextEvent.title}"`, - code: 'EVENTS_OVERLAP' - }); - } - } - - return errors; - } - - // Get user-friendly error messages - getErrorSummary(errors: ValidationError[]): string { - if (errors.length === 0) return ''; - - const errorMessages = errors.map(error => error.message); - - if (errors.length === 1) { - return errorMessages[0]; - } - - return `Multiple validation errors:\n• ${errorMessages.join('\n• ')}`; - } - - // Check if errors are warnings vs critical errors - isCriticalError(error: ValidationError): boolean { - const warningCodes = ['OUTSIDE_BUSINESS_HOURS']; - return !warningCodes.includes(error.code); - } - - // Separate critical errors from warnings - categorizeErrors(errors: ValidationError[]): { critical: ValidationError[], warnings: ValidationError[] } { - const critical: ValidationError[] = []; - const warnings: ValidationError[] = []; - - errors.forEach(error => { - if (this.isCriticalError(error)) { - critical.push(error); - } else { - warnings.push(error); - } - }); - - return { critical, warnings }; - } - - // Validate time format and suggest corrections - validateTimeFormat(timeString: string): { isValid: boolean, suggestion?: string } { - const date = new Date(timeString); - - if (isNaN(date.getTime())) { - return { - isValid: false, - suggestion: 'Please use format: YYYY-MM-DDTHH:MM (e.g., 2025-07-01T14:30)' - }; - } - - return { isValid: true }; - } -} - -export default new ScheduleValidationService(); -export { ValidationError, ScheduleEvent }; diff --git a/backend-old-20260125/src/services/seedService.ts b/backend-old-20260125/src/services/seedService.ts deleted file mode 100644 index 3250713..0000000 --- a/backend-old-20260125/src/services/seedService.ts +++ /dev/null @@ -1,285 +0,0 @@ -import { Pool } from 'pg'; -import { v4 as uuidv4 } from 'uuid'; - -export class SeedService { - private pool: Pool; - - constructor(pool: Pool) { - this.pool = pool; - } - - /** - * Clear all data from tables (for testing) - */ - async clearAllData(): Promise { - const tables = [ - 'schedule_events', - 'flights', - 'drivers', - 'vips', - 'admin_settings', - 'users', - 'system_setup' - ]; - - for (const table of tables) { - await this.pool.query(`TRUNCATE TABLE ${table} CASCADE`); - } - - console.log('šŸ—‘ļø Cleared all data'); - } - - /** - * Seed test users - */ - async seedUsers(): Promise { - const users = [ - { - id: uuidv4(), - google_id: 'google_admin_' + Date.now(), - email: 'admin@example.com', - name: 'Admin User', - role: 'administrator', - status: 'active', - approval_status: 'approved', - profile_picture_url: 'https://via.placeholder.com/150', - organization: 'VIP Transportation Inc', - phone: '+1 555-0100', - }, - { - id: uuidv4(), - google_id: 'google_coord_' + Date.now(), - email: 'coordinator@example.com', - name: 'Coordinator User', - role: 'coordinator', - status: 'active', - approval_status: 'approved', - profile_picture_url: 'https://via.placeholder.com/150', - organization: 'VIP Transportation Inc', - phone: '+1 555-0101', - }, - { - id: uuidv4(), - google_id: 'google_driver_' + Date.now(), - email: 'driver@example.com', - name: 'Driver User', - role: 'driver', - status: 'active', - approval_status: 'approved', - profile_picture_url: 'https://via.placeholder.com/150', - organization: 'VIP Transportation Inc', - phone: '+1 555-0102', - }, - ]; - - for (const user of users) { - await this.pool.query( - `INSERT INTO users ( - id, google_id, email, name, role, status, approval_status, - profile_picture_url, organization, phone, created_at, is_active - ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, NOW(), true) - ON CONFLICT (email) DO NOTHING`, - [ - user.id, - user.google_id, - user.email, - user.name, - user.role, - user.status, - user.approval_status, - user.profile_picture_url, - user.organization, - user.phone, - ] - ); - } - - console.log('šŸ‘¤ Seeded users'); - } - - /** - * Seed test drivers - */ - async seedDrivers(): Promise { - const drivers = [ - { - id: uuidv4(), - name: 'John Smith', - phone: '+1 555-1001', - email: 'john.smith@drivers.com', - license_number: 'DL123456', - vehicle_info: '2023 Mercedes S-Class - Black', - availability_status: 'available', - current_location: 'Downtown Station', - notes: 'Experienced with VIP transport, speaks English and Spanish', - }, - { - id: uuidv4(), - name: 'Sarah Johnson', - phone: '+1 555-1002', - email: 'sarah.johnson@drivers.com', - license_number: 'DL789012', - vehicle_info: '2023 BMW 7 Series - Silver', - availability_status: 'available', - current_location: 'Airport Terminal 1', - notes: 'Airport specialist, knows all terminals', - }, - { - id: uuidv4(), - name: 'Michael Chen', - phone: '+1 555-1003', - email: 'michael.chen@drivers.com', - license_number: 'DL345678', - vehicle_info: '2023 Tesla Model S - White', - availability_status: 'busy', - current_location: 'En route to LAX', - notes: 'Tech-savvy, preferred for tech executives', - }, - ]; - - for (const driver of drivers) { - await this.pool.query( - `INSERT INTO drivers ( - id, name, phone, email, license_number, vehicle_info, - availability_status, current_location, notes, created_at - ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, NOW()) - ON CONFLICT (email) DO NOTHING`, - [ - driver.id, - driver.name, - driver.phone, - driver.email, - driver.license_number, - driver.vehicle_info, - driver.availability_status, - driver.current_location, - driver.notes, - ] - ); - } - - console.log('šŸš— Seeded drivers'); - } - - /** - * Seed test VIPs - */ - async seedVips(): Promise { - const tomorrow = new Date(); - tomorrow.setDate(tomorrow.getDate() + 1); - - const dayAfter = new Date(); - dayAfter.setDate(dayAfter.getDate() + 2); - - const vips = [ - { - id: uuidv4(), - name: 'Robert Johnson', - title: 'CEO', - organization: 'Tech Innovations Corp', - contact_info: '+1 555-2001', - arrival_datetime: tomorrow.toISOString(), - departure_datetime: dayAfter.toISOString(), - airport: 'LAX', - flight_number: 'AA1234', - hotel: 'Beverly Hills Hotel', - room_number: '501', - status: 'scheduled', - transportation_mode: 'flight', - notes: 'Requires luxury vehicle, allergic to pets', - }, - { - id: uuidv4(), - name: 'Emily Davis', - title: 'VP of Sales', - organization: 'Global Marketing Inc', - contact_info: '+1 555-2002', - arrival_datetime: tomorrow.toISOString(), - departure_datetime: dayAfter.toISOString(), - hotel: 'Four Seasons', - room_number: '1201', - status: 'scheduled', - transportation_mode: 'self_driving', - notes: 'Arriving by personal vehicle, needs parking arrangements', - }, - { - id: uuidv4(), - name: 'David Wilson', - title: 'Director of Operations', - organization: 'Finance Solutions Ltd', - contact_info: '+1 555-2003', - arrival_datetime: new Date().toISOString(), - departure_datetime: tomorrow.toISOString(), - airport: 'LAX', - flight_number: 'UA5678', - hotel: 'Ritz Carlton', - room_number: '802', - status: 'arrived', - transportation_mode: 'flight', - notes: 'Currently at hotel, needs pickup for meetings tomorrow', - }, - ]; - - for (const vip of vips) { - await this.pool.query( - `INSERT INTO vips ( - id, name, title, organization, contact_info, arrival_datetime, - departure_datetime, airport, flight_number, hotel, room_number, - status, transportation_mode, notes, created_at - ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, NOW()) - ON CONFLICT (id) DO NOTHING`, - [ - vip.id, - vip.name, - vip.title, - vip.organization, - vip.contact_info, - vip.arrival_datetime, - vip.departure_datetime, - vip.airport || null, - vip.flight_number || null, - vip.hotel, - vip.room_number, - vip.status, - vip.transportation_mode, - vip.notes, - ] - ); - } - - console.log('⭐ Seeded VIPs'); - } - - /** - * Seed all test data - */ - async seedAll(): Promise { - console.log('🌱 Starting database seeding...'); - - try { - await this.seedUsers(); - await this.seedDrivers(); - await this.seedVips(); - - console.log('āœ… Database seeding completed successfully'); - } catch (error) { - console.error('āŒ Error seeding database:', error); - throw error; - } - } - - /** - * Reset and seed (for development) - */ - async resetAndSeed(): Promise { - console.log('šŸ”„ Resetting database and seeding...'); - - await this.clearAllData(); - await this.seedAll(); - } -} - -// Export factory function -export function createSeedService(pool: Pool): SeedService { - return new SeedService(pool); -} \ No newline at end of file diff --git a/backend-old-20260125/src/services/unifiedDataService.ts b/backend-old-20260125/src/services/unifiedDataService.ts deleted file mode 100644 index 32dcac9..0000000 --- a/backend-old-20260125/src/services/unifiedDataService.ts +++ /dev/null @@ -1,365 +0,0 @@ -import { Pool } from 'pg'; -import pool from '../config/database'; - -// Simplified, unified data service that replaces the three redundant services -class UnifiedDataService { - private pool: Pool; - - constructor() { - this.pool = pool; - } - - // Helper to convert snake_case to camelCase - private toCamelCase(obj: any): any { - if (!obj) return obj; - if (Array.isArray(obj)) return obj.map(item => this.toCamelCase(item)); - if (typeof obj !== 'object') return obj; - - return Object.keys(obj).reduce((result, key) => { - const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()); - result[camelKey] = this.toCamelCase(obj[key]); - return result; - }, {} as any); - } - - // VIP Operations - async getVips() { - const query = ` - SELECT v.*, - COALESCE( - JSON_AGG( - JSON_BUILD_OBJECT( - 'flightNumber', f.flight_number, - 'airline', f.airline, - 'scheduledArrival', f.scheduled_arrival, - 'scheduledDeparture', f.scheduled_departure, - 'status', f.status - ) ORDER BY f.scheduled_arrival - ) FILTER (WHERE f.id IS NOT NULL), - '[]' - ) as flights - FROM vips v - LEFT JOIN flights f ON v.id = f.vip_id - GROUP BY v.id - ORDER BY v.created_at DESC`; - - const result = await this.pool.query(query); - return this.toCamelCase(result.rows); - } - - async getVipById(id: string) { - const query = ` - SELECT v.*, - COALESCE( - JSON_AGG( - JSON_BUILD_OBJECT( - 'flightNumber', f.flight_number, - 'airline', f.airline, - 'scheduledArrival', f.scheduled_arrival, - 'scheduledDeparture', f.scheduled_departure, - 'status', f.status - ) ORDER BY f.scheduled_arrival - ) FILTER (WHERE f.id IS NOT NULL), - '[]' - ) as flights - FROM vips v - LEFT JOIN flights f ON v.id = f.vip_id - WHERE v.id = $1 - GROUP BY v.id`; - - const result = await this.pool.query(query, [id]); - return this.toCamelCase(result.rows[0]); - } - - async createVip(vipData: any) { - const { name, organization, department, transportMode, flights, expectedArrival, - needsAirportPickup, needsVenueTransport, notes } = vipData; - - const client = await this.pool.connect(); - try { - await client.query('BEGIN'); - - // Insert VIP - const vipQuery = ` - INSERT INTO vips (name, organization, department, transport_mode, expected_arrival, - needs_airport_pickup, needs_venue_transport, notes) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8) - RETURNING *`; - - const vipResult = await client.query(vipQuery, [ - name, organization, department || 'Office of Development', transportMode || 'flight', - expectedArrival, needsAirportPickup !== false, needsVenueTransport !== false, notes || '' - ]); - - const vip = vipResult.rows[0]; - - // Insert flights if any - if (transportMode === 'flight' && flights?.length > 0) { - for (const flight of flights) { - await client.query( - `INSERT INTO flights (vip_id, flight_number, airline, scheduled_arrival, scheduled_departure) - VALUES ($1, $2, $3, $4, $5)`, - [vip.id, flight.flightNumber, flight.airline, flight.scheduledArrival, flight.scheduledDeparture] - ); - } - } - - await client.query('COMMIT'); - return this.getVipById(vip.id); - } catch (error) { - await client.query('ROLLBACK'); - throw error; - } finally { - client.release(); - } - } - - async updateVip(id: string, vipData: any) { - const { name, organization, department, transportMode, flights, expectedArrival, - needsAirportPickup, needsVenueTransport, notes } = vipData; - - const client = await this.pool.connect(); - try { - await client.query('BEGIN'); - - // Update VIP - const updateQuery = ` - UPDATE vips - SET name = $2, organization = $3, department = $4, transport_mode = $5, - expected_arrival = $6, needs_airport_pickup = $7, needs_venue_transport = $8, - notes = $9, updated_at = NOW() - WHERE id = $1 - RETURNING *`; - - const result = await client.query(updateQuery, [ - id, name, organization, department, transportMode, - expectedArrival, needsAirportPickup, needsVenueTransport, notes - ]); - - if (result.rows.length === 0) { - await client.query('ROLLBACK'); - return null; - } - - // Update flights - await client.query('DELETE FROM flights WHERE vip_id = $1', [id]); - - if (transportMode === 'flight' && flights?.length > 0) { - for (const flight of flights) { - await client.query( - `INSERT INTO flights (vip_id, flight_number, airline, scheduled_arrival, scheduled_departure) - VALUES ($1, $2, $3, $4, $5)`, - [id, flight.flightNumber, flight.airline, flight.scheduledArrival, flight.scheduledDeparture] - ); - } - } - - await client.query('COMMIT'); - return this.getVipById(id); - } catch (error) { - await client.query('ROLLBACK'); - throw error; - } finally { - client.release(); - } - } - - async deleteVip(id: string) { - const result = await this.pool.query( - 'DELETE FROM vips WHERE id = $1 RETURNING *', - [id] - ); - return this.toCamelCase(result.rows[0]); - } - - // Driver Operations - async getDrivers() { - const result = await this.pool.query( - 'SELECT * FROM drivers ORDER BY name ASC' - ); - return this.toCamelCase(result.rows); - } - - async getDriverById(id: string) { - const result = await this.pool.query( - 'SELECT * FROM drivers WHERE id = $1', - [id] - ); - return this.toCamelCase(result.rows[0]); - } - - async createDriver(driverData: any) { - const { name, email, phone, vehicleInfo, status } = driverData; - - const result = await this.pool.query( - `INSERT INTO drivers (name, email, phone, vehicle_info, status) - VALUES ($1, $2, $3, $4, $5) - RETURNING *`, - [name, email, phone, vehicleInfo, status || 'available'] - ); - - return this.toCamelCase(result.rows[0]); - } - - async updateDriver(id: string, driverData: any) { - const { name, email, phone, vehicleInfo, status } = driverData; - - const result = await this.pool.query( - `UPDATE drivers - SET name = $2, email = $3, phone = $4, vehicle_info = $5, status = $6, updated_at = NOW() - WHERE id = $1 - RETURNING *`, - [id, name, email, phone, vehicleInfo, status] - ); - - return this.toCamelCase(result.rows[0]); - } - - async deleteDriver(id: string) { - const result = await this.pool.query( - 'DELETE FROM drivers WHERE id = $1 RETURNING *', - [id] - ); - return this.toCamelCase(result.rows[0]); - } - - // Schedule Operations - async getScheduleByVipId(vipId: string) { - const result = await this.pool.query( - `SELECT se.*, d.name as driver_name - FROM schedule_events se - LEFT JOIN drivers d ON se.driver_id = d.id - WHERE se.vip_id = $1 - ORDER BY se.event_time ASC`, - [vipId] - ); - return this.toCamelCase(result.rows); - } - - async createScheduleEvent(vipId: string, eventData: any) { - const { driverId, eventTime, eventType, location, notes } = eventData; - - const result = await this.pool.query( - `INSERT INTO schedule_events (vip_id, driver_id, event_time, event_type, location, notes) - VALUES ($1, $2, $3, $4, $5, $6) - RETURNING *`, - [vipId, driverId, eventTime, eventType, location, notes] - ); - - return this.toCamelCase(result.rows[0]); - } - - async updateScheduleEvent(id: string, eventData: any) { - const { driverId, eventTime, eventType, location, notes, status } = eventData; - - const result = await this.pool.query( - `UPDATE schedule_events - SET driver_id = $2, event_time = $3, event_type = $4, location = $5, - notes = $6, status = $7, updated_at = NOW() - WHERE id = $1 - RETURNING *`, - [id, driverId, eventTime, eventType, location, notes, status] - ); - - return this.toCamelCase(result.rows[0]); - } - - async deleteScheduleEvent(id: string) { - const result = await this.pool.query( - 'DELETE FROM schedule_events WHERE id = $1 RETURNING *', - [id] - ); - return this.toCamelCase(result.rows[0]); - } - - async getAllSchedules() { - const result = await this.pool.query( - `SELECT se.*, d.name as driver_name, v.name as vip_name - FROM schedule_events se - LEFT JOIN drivers d ON se.driver_id = d.id - LEFT JOIN vips v ON se.vip_id = v.id - ORDER BY se.event_time ASC` - ); - - // Group by VIP ID - const schedules: Record = {}; - result.rows.forEach((row: any) => { - const event = this.toCamelCase(row); - if (!schedules[event.vipId]) { - schedules[event.vipId] = []; - } - schedules[event.vipId].push(event); - }); - - return schedules; - } - - // User Operations (simplified) - async getUserByEmail(email: string) { - const result = await this.pool.query( - 'SELECT * FROM users WHERE email = $1', - [email] - ); - return this.toCamelCase(result.rows[0]); - } - - async getUserById(id: string) { - const result = await this.pool.query( - 'SELECT * FROM users WHERE id = $1', - [id] - ); - return this.toCamelCase(result.rows[0]); - } - - async createUser(userData: any) { - const { email, name, role, department, googleId } = userData; - - const result = await this.pool.query( - `INSERT INTO users (email, name, role, department, google_id) - VALUES ($1, $2, $3, $4, $5) - RETURNING *`, - [email, name, role || 'coordinator', department || 'Office of Development', googleId] - ); - - return this.toCamelCase(result.rows[0]); - } - - async updateUserRole(email: string, role: string) { - const result = await this.pool.query( - `UPDATE users SET role = $2, updated_at = NOW() - WHERE email = $1 - RETURNING *`, - [email, role] - ); - - return this.toCamelCase(result.rows[0]); - } - - async getUserCount(): Promise { - const result = await this.pool.query('SELECT COUNT(*) FROM users'); - return parseInt(result.rows[0].count, 10); - } - - // Admin Settings (simplified) - async getAdminSettings() { - const result = await this.pool.query( - 'SELECT key, value FROM admin_settings' - ); - - return result.rows.reduce((settings: any, row: any) => { - settings[row.key] = row.value; - return settings; - }, {}); - } - - async updateAdminSetting(key: string, value: string) { - await this.pool.query( - `INSERT INTO admin_settings (key, value) - VALUES ($1, $2) - ON CONFLICT (key) DO UPDATE SET value = $2, updated_at = NOW()`, - [key, value] - ); - } -} - -export default new UnifiedDataService(); \ No newline at end of file diff --git a/backend-old-20260125/src/services/userService.ts b/backend-old-20260125/src/services/userService.ts deleted file mode 100644 index 7e7125a..0000000 --- a/backend-old-20260125/src/services/userService.ts +++ /dev/null @@ -1,199 +0,0 @@ -import { pool } from './databaseService'; -import { keycloakUrls } from '../config/keycloak'; - -export interface UserMetadata { - approval_status: 'pending' | 'approved' | 'denied'; - role: 'driver' | 'coordinator' | 'administrator'; -} - -/** - * Sync user from Keycloak to local database - * Creates or updates user record based on JWT token - */ -export async function syncUserFromKeycloak(keycloakSub: string, token: string): Promise { - try { - // Fetch user info from Keycloak - const userInfoResponse = await fetch(keycloakUrls.userInfoUrl, { - headers: { - 'Authorization': `Bearer ${token}`, - }, - }); - - if (!userInfoResponse.ok) { - throw new Error('Failed to fetch user info from Keycloak'); - } - - const userInfo = await userInfoResponse.json(); - - // Extract user details - const email = userInfo.email; - const name = userInfo.name || userInfo.preferred_username || email; - const picture = userInfo.picture; - - // Check if this is the first user (should be administrator) - const userCountResult = await pool.query('SELECT COUNT(*) as count FROM users'); - const isFirstUser = parseInt(userCountResult.rows[0].count) === 0; - - const finalRole = isFirstUser ? 'administrator' : 'driver'; - const finalApprovalStatus = isFirstUser ? 'approved' : 'pending'; - - // Upsert user in local database - // Note: Keeping auth0_sub column name for now (it's just a name, stores Keycloak sub) - const query = ` - INSERT INTO users (id, auth0_sub, email, name, role, profile_picture_url, approval_status, last_login, identity_provider) - VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), 'keycloak') - ON CONFLICT (auth0_sub) - DO UPDATE SET - email = EXCLUDED.email, - name = EXCLUDED.name, - role = EXCLUDED.role, - profile_picture_url = EXCLUDED.profile_picture_url, - approval_status = EXCLUDED.approval_status, - last_login = NOW(), - updated_at = NOW() - RETURNING * - `; - - const userId = keycloakSub.replace(/:/g, '_'); // Convert keycloak UUID to safe ID - const values = [userId, keycloakSub, email, name, finalRole, picture, finalApprovalStatus]; - const result = await pool.query(query, values); - - console.log(`āœ… Synced user from Keycloak: ${email} (${finalRole}, ${finalApprovalStatus})`); - return result.rows[0]; - } catch (error) { - console.error('āŒ Error syncing user from Keycloak:', error); - throw error; - } -} - -/** - * Update user metadata in local database - * Note: Keycloak doesn't have app_metadata like Auth0, so we store everything in local DB - */ -export async function updateUserMetadata( - keycloakSub: string, - metadata: Partial -): Promise { - try { - const updates: string[] = []; - const values: any[] = []; - let paramCount = 1; - - if (metadata.role) { - updates.push(`role = $${paramCount++}`); - values.push(metadata.role); - } - - if (metadata.approval_status) { - updates.push(`approval_status = $${paramCount++}`); - values.push(metadata.approval_status); - } - - if (updates.length === 0) { - return; - } - - updates.push(`updated_at = NOW()`); - values.push(keycloakSub); - - const query = ` - UPDATE users - SET ${updates.join(', ')} - WHERE auth0_sub = $${paramCount} - `; - - await pool.query(query, values); - - console.log(`āœ… Updated user metadata for ${keycloakSub}:`, metadata); - } catch (error) { - console.error('āŒ Error updating user metadata:', error); - throw error; - } -} - -/** - * Approve a user (update local DB) - */ -export async function approveUser(email: string): Promise { - try { - // Update local DB - await pool.query( - 'UPDATE users SET approval_status = $1, updated_at = NOW() WHERE email = $2', - ['approved', email] - ); - - console.log(`āœ… Approved user: ${email}`); - } catch (error) { - console.error(`āŒ Error approving user ${email}:`, error); - throw error; - } -} - -/** - * Deny a user (update local DB) - */ -export async function denyUser(email: string): Promise { - try { - // Update local DB - await pool.query( - 'UPDATE users SET approval_status = $1, updated_at = NOW() WHERE email = $2', - ['denied', email] - ); - - console.log(`āœ… Denied user: ${email}`); - } catch (error) { - console.error(`āŒ Error denying user ${email}:`, error); - throw error; - } -} - -/** - * Assign a role to a user (update local DB) - */ -export async function assignRole( - email: string, - role: 'driver' | 'coordinator' | 'administrator' -): Promise { - try { - // Update local DB - await pool.query( - 'UPDATE users SET role = $1, updated_at = NOW() WHERE email = $2', - [role, email] - ); - - console.log(`āœ… Assigned role ${role} to user: ${email}`); - } catch (error) { - console.error(`āŒ Error assigning role to user ${email}:`, error); - throw error; - } -} - -/** - * Get all users from local DB - */ -export async function getAllUsers(): Promise { - const result = await pool.query(` - SELECT id, auth0_sub, email, name, role, profile_picture_url, - approval_status, created_at, last_login, is_active - FROM users - ORDER BY created_at DESC - `); - - return result.rows; -} - -/** - * Get pending users - */ -export async function getPendingUsers(): Promise { - const result = await pool.query(` - SELECT id, auth0_sub, email, name, role, profile_picture_url, created_at - FROM users - WHERE approval_status = 'pending' - ORDER BY created_at DESC - `); - - return result.rows; -} - -console.log('šŸ‘„ User service initialized (Keycloak)'); diff --git a/backend-old-20260125/src/tests/fixtures.ts b/backend-old-20260125/src/tests/fixtures.ts deleted file mode 100644 index 502b333..0000000 --- a/backend-old-20260125/src/tests/fixtures.ts +++ /dev/null @@ -1,264 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; - -// Test user fixtures -export const testUsers = { - admin: { - id: uuidv4(), - google_id: 'google_admin_123', - email: 'admin@test.com', - name: 'Test Admin', - role: 'administrator' as const, - status: 'active' as const, - approval_status: 'approved' as const, - profile_picture_url: 'https://example.com/admin.jpg', - organization: 'Test Org', - phone: '+1234567890', - }, - coordinator: { - id: uuidv4(), - google_id: 'google_coord_456', - email: 'coordinator@test.com', - name: 'Test Coordinator', - role: 'coordinator' as const, - status: 'active' as const, - approval_status: 'approved' as const, - profile_picture_url: 'https://example.com/coord.jpg', - organization: 'Test Org', - phone: '+1234567891', - }, - pendingUser: { - id: uuidv4(), - google_id: 'google_pending_789', - email: 'pending@test.com', - name: 'Pending User', - role: 'coordinator' as const, - status: 'pending' as const, - approval_status: 'pending' as const, - profile_picture_url: 'https://example.com/pending.jpg', - organization: 'Test Org', - phone: '+1234567892', - }, - driver: { - id: uuidv4(), - google_id: 'google_driver_012', - email: 'driver@test.com', - name: 'Test Driver', - role: 'driver' as const, - status: 'active' as const, - approval_status: 'approved' as const, - profile_picture_url: 'https://example.com/driver.jpg', - organization: 'Test Org', - phone: '+1234567893', - }, -}; - -// Test VIP fixtures -export const testVips = { - flightVip: { - id: uuidv4(), - name: 'John Doe', - title: 'CEO', - organization: 'Test Corp', - contact_info: '+1234567890', - arrival_datetime: new Date('2025-01-15T10:00:00Z'), - departure_datetime: new Date('2025-01-16T14:00:00Z'), - airport: 'LAX', - flight_number: 'AA123', - hotel: 'Hilton Downtown', - room_number: '1234', - status: 'scheduled' as const, - transportation_mode: 'flight' as const, - notes: 'Requires luxury vehicle', - }, - drivingVip: { - id: uuidv4(), - name: 'Jane Smith', - title: 'VP Sales', - organization: 'Another Corp', - contact_info: '+0987654321', - arrival_datetime: new Date('2025-01-15T14:00:00Z'), - departure_datetime: new Date('2025-01-16T10:00:00Z'), - hotel: 'Marriott', - room_number: '567', - status: 'scheduled' as const, - transportation_mode: 'self_driving' as const, - notes: 'Arrives by personal vehicle', - }, -}; - -// Test flight fixtures -export const testFlights = { - onTimeFlight: { - id: uuidv4(), - vip_id: testVips.flightVip.id, - flight_number: 'AA123', - airline: 'American Airlines', - scheduled_arrival: new Date('2025-01-15T10:00:00Z'), - actual_arrival: new Date('2025-01-15T10:00:00Z'), - status: 'On Time' as const, - terminal: 'Terminal 4', - gate: 'B23', - baggage_claim: 'Carousel 7', - }, - delayedFlight: { - id: uuidv4(), - vip_id: uuidv4(), - flight_number: 'UA456', - airline: 'United Airlines', - scheduled_arrival: new Date('2025-01-15T12:00:00Z'), - actual_arrival: new Date('2025-01-15T13:30:00Z'), - status: 'Delayed' as const, - terminal: 'Terminal 7', - gate: 'C45', - baggage_claim: 'Carousel 3', - }, -}; - -// Test driver fixtures -export const testDrivers = { - availableDriver: { - id: uuidv4(), - name: 'Mike Johnson', - phone: '+1234567890', - email: 'mike@drivers.com', - license_number: 'DL123456', - vehicle_info: '2023 Tesla Model S - Black', - availability_status: 'available' as const, - current_location: 'Downtown Station', - notes: 'Experienced with VIP transport', - }, - busyDriver: { - id: uuidv4(), - name: 'Sarah Williams', - phone: '+0987654321', - email: 'sarah@drivers.com', - license_number: 'DL789012', - vehicle_info: '2023 Mercedes S-Class - Silver', - availability_status: 'busy' as const, - current_location: 'Airport', - notes: 'Currently on assignment', - }, -}; - -// Test schedule event fixtures -export const testScheduleEvents = { - pickupEvent: { - id: uuidv4(), - vip_id: testVips.flightVip.id, - driver_id: testDrivers.availableDriver.id, - event_type: 'pickup' as const, - scheduled_time: new Date('2025-01-15T10:30:00Z'), - location: 'LAX Terminal 4', - status: 'scheduled' as const, - notes: 'Meet at baggage claim', - }, - dropoffEvent: { - id: uuidv4(), - vip_id: testVips.flightVip.id, - driver_id: testDrivers.availableDriver.id, - event_type: 'dropoff' as const, - scheduled_time: new Date('2025-01-16T12:00:00Z'), - location: 'LAX Terminal 4', - status: 'scheduled' as const, - notes: 'Departure gate B23', - }, -}; - -// Helper function to create test JWT payload -export function createTestJwtPayload(user: typeof testUsers.admin) { - return { - id: user.id, - email: user.email, - name: user.name, - role: user.role, - status: user.status, - approval_status: user.approval_status, - iat: Math.floor(Date.now() / 1000), - exp: Math.floor(Date.now() / 1000) + 3600, // 1 hour - }; -} - -// Helper function to insert test user into database -export async function insertTestUser(pool: any, user: typeof testUsers.admin) { - const query = ` - INSERT INTO users ( - id, google_id, email, name, role, status, approval_status, - profile_picture_url, organization, phone, created_at, is_active - ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, NOW(), true) - RETURNING * - `; - - const values = [ - user.id, - user.google_id, - user.email, - user.name, - user.role, - user.status, - user.approval_status, - user.profile_picture_url, - user.organization, - user.phone, - ]; - - const result = await pool.query(query, values); - return result.rows[0]; -} - -// Helper function to insert test VIP -export async function insertTestVip(pool: any, vip: typeof testVips.flightVip) { - const query = ` - INSERT INTO vips ( - id, name, title, organization, contact_info, arrival_datetime, - departure_datetime, airport, flight_number, hotel, room_number, - status, transportation_mode, notes, created_at - ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, NOW()) - RETURNING * - `; - - const values = [ - vip.id, - vip.name, - vip.title, - vip.organization, - vip.contact_info, - vip.arrival_datetime, - vip.departure_datetime, - vip.airport || null, - vip.flight_number || null, - vip.hotel, - vip.room_number, - vip.status, - vip.transportation_mode, - vip.notes, - ]; - - const result = await pool.query(query, values); - return result.rows[0]; -} - -// Helper function to insert test driver -export async function insertTestDriver(pool: any, driver: typeof testDrivers.availableDriver) { - const query = ` - INSERT INTO drivers ( - id, name, phone, email, license_number, vehicle_info, - availability_status, current_location, notes, created_at - ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, NOW()) - RETURNING * - `; - - const values = [ - driver.id, - driver.name, - driver.phone, - driver.email, - driver.license_number, - driver.vehicle_info, - driver.availability_status, - driver.current_location, - driver.notes, - ]; - - const result = await pool.query(query, values); - return result.rows[0]; -} \ No newline at end of file diff --git a/backend-old-20260125/src/tests/setup.ts b/backend-old-20260125/src/tests/setup.ts deleted file mode 100644 index ab38a02..0000000 --- a/backend-old-20260125/src/tests/setup.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { Pool } from 'pg'; -import { createClient } from 'redis'; -import * as fs from 'fs'; -import * as path from 'path'; - -// Test database configuration -export const testDbConfig = { - user: process.env.TEST_DB_USER || 'vip_test_user', - host: process.env.TEST_DB_HOST || 'localhost', - database: process.env.TEST_DB_NAME || 'vip_coordinator_test', - password: process.env.TEST_DB_PASSWORD || 'test_password', - port: parseInt(process.env.TEST_DB_PORT || '5432'), -}; - -// Test Redis configuration -export const testRedisConfig = { - url: process.env.TEST_REDIS_URL || 'redis://localhost:6380', -}; - -let testPool: Pool; -let testRedisClient: ReturnType; - -// Setup function to initialize test database -export async function setupTestDatabase() { - testPool = new Pool(testDbConfig); - - // Read and execute schema - const schemaPath = path.join(__dirname, '..', 'config', 'schema.sql'); - const schema = fs.readFileSync(schemaPath, 'utf8'); - - try { - await testPool.query(schema); - - // Run migrations - const migrationPath = path.join(__dirname, '..', 'migrations', 'add_user_management_fields.sql'); - const migration = fs.readFileSync(migrationPath, 'utf8'); - await testPool.query(migration); - } catch (error) { - console.error('Error setting up test database:', error); - throw error; - } - - return testPool; -} - -// Setup function to initialize test Redis -export async function setupTestRedis() { - testRedisClient = createClient({ url: testRedisConfig.url }); - await testRedisClient.connect(); - return testRedisClient; -} - -// Cleanup function to clear test data -export async function cleanupTestDatabase() { - if (testPool) { - // Clear all tables in reverse order of dependencies - const tables = [ - 'schedule_events', - 'flights', - 'drivers', - 'vips', - 'admin_settings', - 'users', - 'system_setup' - ]; - - for (const table of tables) { - await testPool.query(`TRUNCATE TABLE ${table} CASCADE`); - } - } -} - -// Cleanup function for Redis -export async function cleanupTestRedis() { - if (testRedisClient && testRedisClient.isOpen) { - await testRedisClient.flushAll(); - } -} - -// Global setup -beforeAll(async () => { - await setupTestDatabase(); - await setupTestRedis(); -}); - -// Cleanup after each test -afterEach(async () => { - await cleanupTestDatabase(); - await cleanupTestRedis(); -}); - -// Global teardown -afterAll(async () => { - if (testPool) { - await testPool.end(); - } - if (testRedisClient) { - await testRedisClient.quit(); - } -}); - -// Export utilities for tests -export { testPool, testRedisClient }; \ No newline at end of file diff --git a/backend-old-20260125/src/types/api.ts b/backend-old-20260125/src/types/api.ts deleted file mode 100644 index 7ed9fed..0000000 --- a/backend-old-20260125/src/types/api.ts +++ /dev/null @@ -1,102 +0,0 @@ -export interface SuccessResponse { - success: true; - data: T; - message?: string; - timestamp: string; -} - -export interface PaginatedResponse { - success: true; - data: T[]; - pagination: { - page: number; - limit: number; - total: number; - totalPages: number; - }; - timestamp: string; -} - -// User types -export interface User { - id: string; - email: string; - name: string; - role: 'admin' | 'coordinator' | 'driver'; - department?: string; - createdAt: Date; - updatedAt: Date; -} - -// VIP types -export interface VIP { - id: string; - name: string; - email?: string; - phone?: string; - arrivalMode: 'flight' | 'driving'; - flightNumber?: string; - arrivalTime?: Date; - departureTime?: Date; - notes?: string; - status: 'pending' | 'confirmed' | 'completed' | 'cancelled'; - createdAt: Date; - updatedAt: Date; -} - -// Driver types -export interface Driver { - id: string; - name: string; - email?: string; - phone: string; - vehicleInfo?: string; - status: 'available' | 'assigned' | 'unavailable'; - createdAt: Date; - updatedAt: Date; -} - -// Schedule Event types -export interface ScheduleEvent { - id: string; - vipId: string; - driverId?: string; - eventType: 'pickup' | 'dropoff' | 'custom'; - eventTime: Date; - location: string; - notes?: string; - status: 'scheduled' | 'in_progress' | 'completed' | 'cancelled'; - createdAt: Date; - updatedAt: Date; -} - -// Request types -export interface AuthRequest extends Request { - user?: User; - requestId?: string; -} - -// Response helper functions -export const successResponse = (data: T, message?: string): SuccessResponse => ({ - success: true, - data, - message, - timestamp: new Date().toISOString() -}); - -export const paginatedResponse = ( - data: T[], - page: number, - limit: number, - total: number -): PaginatedResponse => ({ - success: true, - data, - pagination: { - page, - limit, - total, - totalPages: Math.ceil(total / limit) - }, - timestamp: new Date().toISOString() -}); \ No newline at end of file diff --git a/backend-old-20260125/src/types/errors.ts b/backend-old-20260125/src/types/errors.ts deleted file mode 100644 index 9b1ee08..0000000 --- a/backend-old-20260125/src/types/errors.ts +++ /dev/null @@ -1,59 +0,0 @@ -export class AppError extends Error { - public readonly statusCode: number; - public readonly isOperational: boolean; - - constructor(message: string, statusCode: number, isOperational = true) { - super(message); - this.statusCode = statusCode; - this.isOperational = isOperational; - - Error.captureStackTrace(this, this.constructor); - } -} - -export class ValidationError extends AppError { - constructor(message: string) { - super(message, 400, true); - } -} - -export class AuthenticationError extends AppError { - constructor(message = 'Authentication failed') { - super(message, 401, true); - } -} - -export class AuthorizationError extends AppError { - constructor(message = 'Insufficient permissions') { - super(message, 403, true); - } -} - -export class NotFoundError extends AppError { - constructor(message: string) { - super(message, 404, true); - } -} - -export class ConflictError extends AppError { - constructor(message: string) { - super(message, 409, true); - } -} - -export class DatabaseError extends AppError { - constructor(message = 'Database operation failed') { - super(message, 500, false); - } -} - -export interface ErrorResponse { - success: false; - error: { - message: string; - code?: string; - details?: any; - }; - timestamp: string; - path?: string; -} \ No newline at end of file diff --git a/backend-old-20260125/src/types/schemas.ts b/backend-old-20260125/src/types/schemas.ts deleted file mode 100644 index c895dbb..0000000 --- a/backend-old-20260125/src/types/schemas.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { z } from 'zod'; - -// Common schemas -const phoneRegex = /^[\d\s\-\+\(\)]+$/; -const emailSchema = z.string().email().optional(); -const phoneSchema = z.string().regex(phoneRegex, 'Invalid phone number format').optional(); - -// VIP schemas -export const vipFlightSchema = z.object({ - flightNumber: z.string().min(1, 'Flight number is required'), - airline: z.string().optional(), - scheduledArrival: z.string().datetime().or(z.date()), - scheduledDeparture: z.string().datetime().or(z.date()).optional(), - status: z.enum(['scheduled', 'delayed', 'cancelled', 'arrived']).optional() -}); - -export const createVipSchema = z.object({ - name: z.string().min(1, 'Name is required').max(100), - organization: z.string().max(100).optional(), - department: z.enum(['Office of Development', 'Admin']).default('Office of Development'), - transportMode: z.enum(['flight', 'self-driving']).default('flight'), - flights: z.array(vipFlightSchema).optional(), - expectedArrival: z.string().datetime().or(z.date()).optional(), - needsAirportPickup: z.boolean().default(true), - needsVenueTransport: z.boolean().default(true), - notes: z.string().max(500).optional() -}).refine( - (data) => { - if (data.transportMode === 'flight' && (!data.flights || data.flights.length === 0)) { - return false; - } - if (data.transportMode === 'self-driving' && !data.expectedArrival) { - return false; - } - return true; - }, - { - message: 'Flight mode requires at least one flight, self-driving requires expected arrival' - } -); - -export const updateVipSchema = z.object({ - name: z.string().min(1, 'Name is required').max(100).optional(), - organization: z.string().max(100).optional(), - department: z.enum(['Office of Development', 'Admin']).optional(), - transportMode: z.enum(['flight', 'self-driving']).optional(), - flights: z.array(vipFlightSchema).optional(), - expectedArrival: z.string().datetime().or(z.date()).optional(), - needsAirportPickup: z.boolean().optional(), - needsVenueTransport: z.boolean().optional(), - notes: z.string().max(500).optional() -}); - -// Driver schemas -export const createDriverSchema = z.object({ - name: z.string().min(1, 'Name is required').max(100), - email: emailSchema, - phone: z.string().regex(phoneRegex, 'Invalid phone number format'), - vehicleInfo: z.string().max(200).optional(), - status: z.enum(['available', 'assigned', 'unavailable']).default('available') -}); - -export const updateDriverSchema = createDriverSchema.partial(); - -// Schedule Event schemas -export const createScheduleEventSchema = z.object({ - vipId: z.string().uuid('Invalid VIP ID'), - driverId: z.string().uuid('Invalid driver ID').optional(), - eventType: z.enum(['pickup', 'dropoff', 'custom']), - eventTime: z.string().datetime().or(z.date()), - location: z.string().min(1, 'Location is required').max(200), - notes: z.string().max(500).optional(), - status: z.enum(['scheduled', 'in_progress', 'completed', 'cancelled']).default('scheduled') -}); - -export const updateScheduleEventSchema = createScheduleEventSchema.partial(); - -// User schemas -export const createUserSchema = z.object({ - email: z.string().email('Invalid email address'), - name: z.string().min(1, 'Name is required').max(100), - role: z.enum(['admin', 'coordinator', 'driver']), - department: z.string().max(100).optional(), - password: z.string().min(8, 'Password must be at least 8 characters').optional() -}); - -export const updateUserSchema = createUserSchema.partial(); - -// Admin settings schemas -export const updateAdminSettingsSchema = z.object({ - key: z.string().min(1, 'Key is required'), - value: z.string(), - description: z.string().optional() -}); - -// Auth schemas -export const loginSchema = z.object({ - email: z.string().email('Invalid email address'), - password: z.string().min(1, 'Password is required') -}); - -export const googleAuthCallbackSchema = z.object({ - code: z.string().min(1, 'Authorization code is required') -}); - -// Query parameter schemas -export const paginationSchema = z.object({ - page: z.string().regex(/^\d+$/).transform(Number).default('1'), - limit: z.string().regex(/^\d+$/).transform(Number).default('20'), - sortBy: z.string().optional(), - sortOrder: z.enum(['asc', 'desc']).default('asc') -}); - -export const dateRangeSchema = z.object({ - startDate: z.string().datetime().optional(), - endDate: z.string().datetime().optional() -}); - -// Route parameter schemas -export const idParamSchema = z.object({ - id: z.string().min(1, 'ID is required') -}); \ No newline at end of file diff --git a/backend-old-20260125/tsconfig.json b/backend-old-20260125/tsconfig.json deleted file mode 100644 index 05f269c..0000000 --- a/backend-old-20260125/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "module": "commonjs", - "lib": ["ES2020", "DOM"], - "outDir": "./dist", - "rootDir": "./src", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "types": ["node"], - "moduleResolution": "node" - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/deploy.sh b/deploy.sh deleted file mode 100644 index 32b7e08..0000000 --- a/deploy.sh +++ /dev/null @@ -1,130 +0,0 @@ -#!/bin/bash - -# VIP Coordinator - Quick Deployment Script -# This script helps you deploy VIP Coordinator with Docker - -set -e - -echo "šŸš€ VIP Coordinator - Quick Deployment Script" -echo "=============================================" - -# Check if Docker is installed -if ! command -v docker &> /dev/null; then - echo "āŒ Docker is not installed. Please install Docker first." - echo " Visit: https://docs.docker.com/get-docker/" - exit 1 -fi - -# Check if Docker Compose is installed -if ! command -v docker-compose &> /dev/null; then - echo "āŒ Docker Compose is not installed. Please install Docker Compose first." - echo " Visit: https://docs.docker.com/compose/install/" - exit 1 -fi - -echo "āœ… Docker and Docker Compose are installed" - -# Check if .env file exists -if [ ! -f ".env" ]; then - if [ -f ".env.example" ]; then - echo "šŸ“ Creating .env file from template..." - cp .env.example .env - echo "āš ļø IMPORTANT: Please edit .env file with your configuration before continuing!" - echo " Required changes:" - echo " - DB_PASSWORD: Set a secure database password" - echo " - ADMIN_PASSWORD: Set a secure admin password" - echo " - GOOGLE_CLIENT_ID: Your Google OAuth Client ID" - echo " - GOOGLE_CLIENT_SECRET: Your Google OAuth Client Secret" - echo " - Update domain settings for production deployment" - echo "" - read -p "Press Enter after you've updated the .env file..." - else - echo "āŒ .env.example file not found. Please ensure you have the deployment files." - exit 1 - fi -fi - -# Validate required environment variables -echo "šŸ” Validating configuration..." - -source .env - -if [ -z "$DB_PASSWORD" ] || [ "$DB_PASSWORD" = "VipCoord2025SecureDB" ]; then - echo "āš ļø Warning: Please change DB_PASSWORD from the default value" -fi - -if [ -z "$ADMIN_PASSWORD" ] || [ "$ADMIN_PASSWORD" = "ChangeThisSecurePassword" ]; then - echo "āš ļø Warning: Please change ADMIN_PASSWORD from the default value" -fi - -if [ -z "$GOOGLE_CLIENT_ID" ] || [ "$GOOGLE_CLIENT_ID" = "your-google-client-id.apps.googleusercontent.com" ]; then - echo "āŒ Error: GOOGLE_CLIENT_ID must be configured" - echo " Please set up Google OAuth and update your .env file" - exit 1 -fi - -if [ -z "$GOOGLE_CLIENT_SECRET" ] || [ "$GOOGLE_CLIENT_SECRET" = "your-google-client-secret" ]; then - echo "āŒ Error: GOOGLE_CLIENT_SECRET must be configured" - echo " Please set up Google OAuth and update your .env file" - exit 1 -fi - -echo "āœ… Configuration validated" - -# Pull latest images -echo "šŸ“„ Pulling latest images from Docker Hub..." -docker-compose pull - -# Start the application -echo "šŸš€ Starting VIP Coordinator..." -docker-compose up -d - -# Wait for services to be ready -echo "ā³ Waiting for services to start..." -sleep 10 - -# Check service status -echo "šŸ” Checking service status..." -docker-compose ps - -# Check if backend is healthy -echo "šŸ„ Checking backend health..." -for i in {1..30}; do - if curl -s http://localhost:3000/health > /dev/null 2>&1; then - echo "āœ… Backend is healthy" - break - fi - if [ $i -eq 30 ]; then - echo "āŒ Backend health check failed" - echo " Check logs with: docker-compose logs backend" - exit 1 - fi - sleep 2 -done - -# Check if frontend is accessible -echo "🌐 Checking frontend..." -if curl -s http://localhost/ > /dev/null 2>&1; then - echo "āœ… Frontend is accessible" -else - echo "āš ļø Frontend check failed, but this might be normal during startup" -fi - -echo "" -echo "šŸŽ‰ VIP Coordinator deployment completed!" -echo "=============================================" -echo "šŸ“ Access your application:" -echo " Frontend: http://localhost" -echo " Backend API: http://localhost:3000" -echo "" -echo "šŸ“‹ Next steps:" -echo " 1. Open http://localhost in your browser" -echo " 2. Click 'Continue with Google' to set up your admin account" -echo " 3. The first user to log in becomes the administrator" -echo "" -echo "šŸ”§ Management commands:" -echo " View logs: docker-compose logs" -echo " Stop app: docker-compose down" -echo " Update app: docker-compose pull && docker-compose up -d" -echo "" -echo "šŸ“– For production deployment, see DEPLOYMENT.md" \ No newline at end of file diff --git a/docker-compose.hub.yml b/docker-compose.hub.yml deleted file mode 100644 index 014903c..0000000 --- a/docker-compose.hub.yml +++ /dev/null @@ -1,57 +0,0 @@ -version: '3.8' - -services: - - db: - image: postgres:15 - environment: - POSTGRES_DB: vip_coordinator - POSTGRES_PASSWORD: ${DB_PASSWORD} - volumes: - - postgres-data:/var/lib/postgresql/data - restart: unless-stopped - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres"] - interval: 30s - timeout: 10s - retries: 3 - - redis: - image: redis:7 - restart: unless-stopped - healthcheck: - test: ["CMD", "redis-cli", "ping"] - interval: 30s - timeout: 10s - retries: 3 - - backend: - image: t72chevy/vip-coordinator:backend-latest - environment: - DATABASE_URL: postgresql://postgres:${DB_PASSWORD}@db:5432/vip_coordinator - REDIS_URL: redis://redis:6379 - GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID} - GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET} - GOOGLE_REDIRECT_URI: ${GOOGLE_REDIRECT_URI} - FRONTEND_URL: ${FRONTEND_URL} - ADMIN_PASSWORD: ${ADMIN_PASSWORD} - PORT: 3000 - ports: - - "3000:3000" - depends_on: - db: - condition: service_healthy - redis: - condition: service_healthy - restart: unless-stopped - - frontend: - image: t72chevy/vip-coordinator:frontend-latest - ports: - - "80:80" - depends_on: - - backend - restart: unless-stopped - -volumes: - postgres-data: \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml deleted file mode 100644 index 07d47e1..0000000 --- a/docker-compose.prod.yml +++ /dev/null @@ -1,63 +0,0 @@ -version: '3.8' - -services: - - db: - image: postgres:15 - environment: - POSTGRES_DB: vip_coordinator - POSTGRES_PASSWORD: ${DB_PASSWORD} - volumes: - - postgres-data:/var/lib/postgresql/data - restart: unless-stopped - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres"] - interval: 30s - timeout: 10s - retries: 3 - - redis: - image: redis:7 - restart: unless-stopped - healthcheck: - test: ["CMD", "redis-cli", "ping"] - interval: 30s - timeout: 10s - retries: 3 - - backend: - build: - context: ./backend - target: production - environment: - DATABASE_URL: postgresql://postgres:${DB_PASSWORD}@db:5432/vip_coordinator - REDIS_URL: redis://redis:6379 - GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID} - GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET} - GOOGLE_REDIRECT_URI: ${GOOGLE_REDIRECT_URI} - FRONTEND_URL: ${FRONTEND_URL} - ADMIN_PASSWORD: ${ADMIN_PASSWORD} - PORT: 3000 - ports: - - "3000:3000" - depends_on: - db: - condition: service_healthy - redis: - condition: service_healthy - restart: unless-stopped - - frontend: - build: - context: ./frontend - target: production - args: - VITE_API_URL: ${VITE_API_URL} - ports: - - "80:80" - depends_on: - - backend - restart: unless-stopped - -volumes: - postgres-data: diff --git a/docker-compose.test.yml b/docker-compose.test.yml deleted file mode 100644 index 4d38455..0000000 --- a/docker-compose.test.yml +++ /dev/null @@ -1,95 +0,0 @@ -version: '3.8' - -services: - # Test database - separate from development - test-db: - image: postgres:15 - environment: - POSTGRES_DB: vip_coordinator_test - POSTGRES_USER: test_user - POSTGRES_PASSWORD: test_password - ports: - - 5433:5432 # Different port to avoid conflicts - healthcheck: - test: ["CMD-SHELL", "pg_isready -U test_user"] - interval: 5s - timeout: 5s - retries: 5 - tmpfs: - - /var/lib/postgresql/data # Use memory for faster tests - - # Test Redis - separate instance - test-redis: - image: redis:7 - ports: - - 6380:6379 # Different port to avoid conflicts - healthcheck: - test: ["CMD", "redis-cli", "ping"] - interval: 5s - timeout: 5s - retries: 5 - - # Backend test runner - backend-test: - build: - context: ./backend - target: development - environment: - NODE_ENV: test - DATABASE_URL: postgresql://test_user:test_password@test-db:5432/vip_coordinator_test - REDIS_URL: redis://test-redis:6379 - GOOGLE_CLIENT_ID: test_google_client_id - GOOGLE_CLIENT_SECRET: test_google_client_secret - GOOGLE_REDIRECT_URI: http://localhost:3000/auth/google/callback - FRONTEND_URL: http://localhost:5173 - JWT_SECRET: test_jwt_secret_minimum_32_characters_long - TEST_DB_HOST: test-db - TEST_DB_PORT: 5432 - TEST_DB_USER: test_user - TEST_DB_PASSWORD: test_password - TEST_DB_NAME: vip_coordinator_test - TEST_REDIS_URL: redis://test-redis:6379 - depends_on: - test-db: - condition: service_healthy - test-redis: - condition: service_healthy - volumes: - - ./backend:/app - - /app/node_modules - command: npm test - - # Frontend test runner - frontend-test: - build: - context: ./frontend - target: development - environment: - NODE_ENV: test - VITE_API_URL: http://backend-test:3000/api - VITE_GOOGLE_CLIENT_ID: test_google_client_id - volumes: - - ./frontend:/app - - /app/node_modules - command: npm test - - # E2E test runner (Playwright) - e2e-test: - build: - context: . - dockerfile: Dockerfile.e2e - environment: - PLAYWRIGHT_BASE_URL: http://frontend:80 - PLAYWRIGHT_API_URL: http://backend:3000 - depends_on: - - backend - - frontend - volumes: - - ./e2e:/app/e2e - - ./e2e/results:/app/e2e/results - command: npx playwright test - -# Networks -networks: - default: - name: vip-test-network \ No newline at end of file diff --git a/frontend-old-20260125/Dockerfile b/frontend-old-20260125/Dockerfile deleted file mode 100644 index 208fc4f..0000000 --- a/frontend-old-20260125/Dockerfile +++ /dev/null @@ -1,75 +0,0 @@ -# Multi-stage build for development and production -FROM node:22-slim AS base - -WORKDIR /app - -# Copy package files -COPY package*.json ./ - -# Development stage -FROM base AS development -RUN npm install -# Rebuild native modules for the container architecture -RUN npm rebuild esbuild -COPY . . -EXPOSE 5173 -CMD ["npm", "run", "dev"] - -# Build stage -FROM base AS build - -# Accept build argument for API URL -ARG VITE_API_URL -ENV VITE_API_URL=$VITE_API_URL - -# Install build dependencies for native modules (Debian-based) -RUN apt-get update && apt-get install -y \ - python3 \ - make \ - g++ \ - && rm -rf /var/lib/apt/lists/* - -# Install dependencies -RUN npm ci --only=production && npm cache clean --force -RUN npm install typescript @vitejs/plugin-react vite - -# Copy source code -COPY . . - -# Build the application with environment variable available -RUN npm run build - -# Production stage -FROM nginx:alpine AS production - -# Copy custom nginx configuration -COPY nginx.conf /etc/nginx/nginx.conf - -# Copy built application from build stage -COPY --from=build /app/dist /usr/share/nginx/html - -# Create non-root user for security -RUN addgroup -g 1001 -S appuser && \ - adduser -S appuser -u 1001 -G appuser - -# Set proper permissions and create necessary directories -RUN chown -R appuser:appuser /usr/share/nginx/html && \ - chown -R appuser:appuser /var/cache/nginx && \ - chown -R appuser:appuser /var/log/nginx && \ - chown -R appuser:appuser /etc/nginx/conf.d && \ - mkdir -p /tmp/nginx && \ - chown -R appuser:appuser /tmp/nginx && \ - touch /tmp/nginx/nginx.pid && \ - chown appuser:appuser /tmp/nginx/nginx.pid - -# Switch to non-root user -USER appuser - -# Health check -HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ - CMD wget --no-verbose --tries=1 --spider http://localhost/ || exit 1 - -EXPOSE 80 - -# Start nginx -CMD ["nginx", "-g", "daemon off;"] diff --git a/frontend-old-20260125/dist/README-API.md b/frontend-old-20260125/dist/README-API.md deleted file mode 100644 index b12766c..0000000 --- a/frontend-old-20260125/dist/README-API.md +++ /dev/null @@ -1,218 +0,0 @@ -# VIP Coordinator API Documentation - -## šŸ“š Overview - -This document provides comprehensive API documentation for the VIP Coordinator system using **OpenAPI 3.0** (Swagger) specification. The API enables management of VIP transportation coordination, including flight tracking, driver management, and event scheduling. - -## šŸš€ Quick Start - -### View API Documentation - -1. **Interactive Documentation (Recommended):** - ```bash - # Open the interactive Swagger UI documentation - open vip-coordinator/api-docs.html - ``` - Or visit: `file:///path/to/vip-coordinator/api-docs.html` - -2. **Raw OpenAPI Specification:** - ```bash - # View the YAML specification file - cat vip-coordinator/api-documentation.yaml - ``` - -### Test the API - -The interactive documentation includes a "Try it out" feature that allows you to test endpoints directly: - -1. Open `api-docs.html` in your browser -2. Click on any endpoint to expand it -3. Click "Try it out" button -4. Fill in parameters and request body -5. Click "Execute" to make the API call - -## šŸ“‹ API Categories - -### šŸ„ Health -- `GET /api/health` - System health check - -### šŸ‘„ VIPs -- `GET /api/vips` - Get all VIPs -- `POST /api/vips` - Create new VIP -- `PUT /api/vips/{id}` - Update VIP -- `DELETE /api/vips/{id}` - Delete VIP - -### šŸš— Drivers -- `GET /api/drivers` - Get all drivers -- `POST /api/drivers` - Create new driver -- `PUT /api/drivers/{id}` - Update driver -- `DELETE /api/drivers/{id}` - Delete driver -- `GET /api/drivers/{driverId}/schedule` - Get driver's schedule -- `POST /api/drivers/availability` - Check driver availability -- `POST /api/drivers/{driverId}/conflicts` - Check driver conflicts - -### āœˆļø Flights -- `GET /api/flights/{flightNumber}` - Get flight information -- `POST /api/flights/{flightNumber}/track` - Start flight tracking -- `DELETE /api/flights/{flightNumber}/track` - Stop flight tracking -- `POST /api/flights/batch` - Get multiple flights info -- `GET /api/flights/tracking/status` - Get tracking status - -### šŸ“… Schedule -- `GET /api/vips/{vipId}/schedule` - Get VIP's schedule -- `POST /api/vips/{vipId}/schedule` - Add event to schedule -- `PUT /api/vips/{vipId}/schedule/{eventId}` - Update event -- `DELETE /api/vips/{vipId}/schedule/{eventId}` - Delete event -- `PATCH /api/vips/{vipId}/schedule/{eventId}/status` - Update event status - -### āš™ļø Admin -- `POST /api/admin/authenticate` - Admin authentication -- `GET /api/admin/settings` - Get admin settings -- `POST /api/admin/settings` - Update admin settings - -## šŸ’” Example API Calls - -### Create a VIP with Flight -```bash -curl -X POST http://localhost:3000/api/vips \ - -H "Content-Type: application/json" \ - -d '{ - "name": "John Doe", - "organization": "Tech Corp", - "transportMode": "flight", - "flights": [ - { - "flightNumber": "UA1234", - "flightDate": "2025-06-26", - "segment": 1 - } - ], - "needsAirportPickup": true, - "needsVenueTransport": true, - "notes": "CEO - requires executive transport" - }' -``` - -### Add Event to VIP Schedule -```bash -curl -X POST http://localhost:3000/api/vips/{vipId}/schedule \ - -H "Content-Type: application/json" \ - -d '{ - "title": "Meeting with CEO", - "location": "Hyatt Regency Denver", - "startTime": "2025-06-26T11:00:00", - "endTime": "2025-06-26T12:30:00", - "type": "meeting", - "assignedDriverId": "1748780965562", - "description": "Important strategic meeting" - }' -``` - -### Check Driver Availability -```bash -curl -X POST http://localhost:3000/api/drivers/availability \ - -H "Content-Type: application/json" \ - -d '{ - "startTime": "2025-06-26T11:00:00", - "endTime": "2025-06-26T12:30:00", - "location": "Denver Convention Center" - }' -``` - -### Get Flight Information -```bash -curl "http://localhost:3000/api/flights/UA1234?date=2025-06-26" -``` - -## šŸ”§ Tools for API Documentation - -### 1. **Swagger UI (Recommended)** -- **What it is:** Interactive web-based API documentation -- **Features:** - - Try endpoints directly in browser - - Auto-generated from OpenAPI spec - - Beautiful, responsive interface - - Request/response examples -- **Access:** Open `api-docs.html` in your browser - -### 2. **OpenAPI Specification** -- **What it is:** Industry-standard API specification format -- **Features:** - - Machine-readable API definition - - Can generate client SDKs - - Supports validation and testing - - Compatible with many tools -- **File:** `api-documentation.yaml` - -### 3. **Alternative Tools** - -You can use the OpenAPI specification with other tools: - -#### Postman -1. Import `api-documentation.yaml` into Postman -2. Automatically creates a collection with all endpoints -3. Includes examples and validation - -#### Insomnia -1. Import the OpenAPI spec -2. Generate requests automatically -3. Built-in environment management - -#### VS Code Extensions -- **OpenAPI (Swagger) Editor** - Edit and preview API specs -- **REST Client** - Test APIs directly in VS Code - -## šŸ“– Documentation Best Practices - -### Why OpenAPI/Swagger? - -1. **Industry Standard:** Most widely adopted API documentation format -2. **Interactive:** Users can test APIs directly in the documentation -3. **Code Generation:** Can generate client libraries in multiple languages -4. **Validation:** Ensures API requests/responses match specification -5. **Tooling:** Extensive ecosystem of tools and integrations - -### Documentation Features - -- **Comprehensive:** All endpoints, parameters, and responses documented -- **Examples:** Real-world examples for all operations -- **Schemas:** Detailed data models with validation rules -- **Error Handling:** Clear error response documentation -- **Authentication:** Security requirements clearly specified - -## šŸ”— Integration Examples - -### Frontend Integration -```javascript -// Example: Fetch VIPs in React -const fetchVips = async () => { - const response = await fetch('/api/vips'); - const vips = await response.json(); - return vips; -}; -``` - -### Backend Integration -```bash -# Example: Using curl to test endpoints -curl -X GET http://localhost:3000/api/health -curl -X GET http://localhost:3000/api/vips -curl -X GET http://localhost:3000/api/drivers -``` - -## šŸš€ Next Steps - -1. **Explore the Interactive Docs:** Open `api-docs.html` and try the endpoints -2. **Test with Real Data:** Use the populated test data to explore functionality -3. **Build Integrations:** Use the API specification to build client applications -4. **Extend the API:** Add new endpoints following the established patterns - -## šŸ“ž Support - -For questions about the API: -- Review the interactive documentation -- Check the OpenAPI specification for detailed schemas -- Test endpoints using the "Try it out" feature -- Refer to the example requests and responses - -The API documentation is designed to be self-service and comprehensive, providing everything needed to integrate with the VIP Coordinator system. diff --git a/frontend-old-20260125/dist/api-docs.html b/frontend-old-20260125/dist/api-docs.html deleted file mode 100644 index 4f42032..0000000 --- a/frontend-old-20260125/dist/api-docs.html +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - VIP Coordinator API Documentation - - - - -
-

šŸš— VIP Coordinator API

-

Comprehensive API for managing VIP transportation coordination

-
- - - -
- - - - - - diff --git a/frontend-old-20260125/dist/api-documentation.yaml b/frontend-old-20260125/dist/api-documentation.yaml deleted file mode 100644 index a6464a9..0000000 --- a/frontend-old-20260125/dist/api-documentation.yaml +++ /dev/null @@ -1,1189 +0,0 @@ -openapi: 3.0.3 -info: - title: VIP Coordinator API - description: | - A comprehensive API for managing VIP transportation coordination, including flight tracking, - driver management, and event scheduling for high-profile guests. - - ## Features - - VIP management with flight and self-driving transport modes - - Real-time flight tracking and validation - - Driver assignment and conflict detection - - Event scheduling with validation - - Admin settings management - - ## Authentication - Most endpoints are public for demo purposes. Admin endpoints require authentication. - version: 1.0.0 - contact: - name: VIP Coordinator Support - email: support@vipcoordinator.com - license: - name: MIT - url: https://opensource.org/licenses/MIT - -servers: - - url: http://localhost:3000/api - description: Development server - - url: https://api.vipcoordinator.com/api - description: Production server - -tags: - - name: Health - description: System health checks - - name: VIPs - description: VIP management operations - - name: Drivers - description: Driver management operations - - name: Flights - description: Flight tracking and information - - name: Schedule - description: Event and meeting scheduling - - name: Admin - description: Administrative operations - -paths: - /health: - get: - tags: - - Health - summary: Health check endpoint - description: Returns the current status of the API server - responses: - '200': - description: Server is healthy - content: - application/json: - schema: - type: object - properties: - status: - type: string - example: "OK" - timestamp: - type: string - format: date-time - example: "2025-06-01T12:00:00.000Z" - - /vips: - get: - tags: - - VIPs - summary: Get all VIPs - description: Retrieve a list of all VIPs in the system - responses: - '200': - description: List of VIPs - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/VIP' - - post: - tags: - - VIPs - summary: Create a new VIP - description: Add a new VIP to the system - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/VIPCreate' - examples: - flight_vip: - summary: VIP with flight transport - value: - name: "John Doe" - organization: "Tech Corp" - transportMode: "flight" - flights: - - flightNumber: "UA1234" - flightDate: "2025-06-26" - segment: 1 - needsAirportPickup: true - needsVenueTransport: true - notes: "CEO - requires executive transport" - self_driving: - summary: Self-driving VIP - value: - name: "Jane Smith" - organization: "Local Business" - transportMode: "self-driving" - expectedArrival: "2025-06-26T14:00:00" - needsAirportPickup: false - needsVenueTransport: true - notes: "Driving from Colorado Springs" - responses: - '201': - description: VIP created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/VIP' - '400': - description: Invalid input data - - /vips/{id}: - put: - tags: - - VIPs - summary: Update a VIP - description: Update an existing VIP's information - parameters: - - name: id - in: path - required: true - schema: - type: string - description: VIP ID - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/VIPCreate' - responses: - '200': - description: VIP updated successfully - content: - application/json: - schema: - $ref: '#/components/schemas/VIP' - '404': - description: VIP not found - - delete: - tags: - - VIPs - summary: Delete a VIP - description: Remove a VIP from the system - parameters: - - name: id - in: path - required: true - schema: - type: string - description: VIP ID - responses: - '200': - description: VIP deleted successfully - content: - application/json: - schema: - type: object - properties: - message: - type: string - example: "VIP deleted successfully" - vip: - $ref: '#/components/schemas/VIP' - '404': - description: VIP not found - - /vips/{vipId}/schedule: - get: - tags: - - Schedule - summary: Get VIP's schedule - description: Retrieve all scheduled events for a specific VIP - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - responses: - '200': - description: VIP's schedule - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ScheduleEvent' - - post: - tags: - - Schedule - summary: Add event to VIP's schedule - description: Create a new event for a VIP with validation - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ScheduleEventCreate' - example: - title: "Meeting with CEO" - location: "Hyatt Regency Denver" - startTime: "2025-06-26T11:00:00" - endTime: "2025-06-26T12:30:00" - type: "meeting" - assignedDriverId: "1748780965562" - description: "Important strategic meeting" - responses: - '201': - description: Event created successfully - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/ScheduleEvent' - - type: object - properties: - warnings: - type: array - items: - $ref: '#/components/schemas/ValidationWarning' - '400': - description: Validation failed - content: - application/json: - schema: - $ref: '#/components/schemas/ValidationError' - - /vips/{vipId}/schedule/{eventId}: - put: - tags: - - Schedule - summary: Update a scheduled event - description: Update an existing event in a VIP's schedule - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - - name: eventId - in: path - required: true - schema: - type: string - description: Event ID - requestBody: - required: true - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/ScheduleEventCreate' - - type: object - properties: - status: - $ref: '#/components/schemas/EventStatus' - responses: - '200': - description: Event updated successfully - content: - application/json: - schema: - $ref: '#/components/schemas/ScheduleEvent' - '404': - description: VIP or event not found - - delete: - tags: - - Schedule - summary: Delete a scheduled event - description: Remove an event from a VIP's schedule - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - - name: eventId - in: path - required: true - schema: - type: string - description: Event ID - responses: - '200': - description: Event deleted successfully - '404': - description: VIP or event not found - - /vips/{vipId}/schedule/{eventId}/status: - patch: - tags: - - Schedule - summary: Update event status - description: Update the status of a specific event - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - - name: eventId - in: path - required: true - schema: - type: string - description: Event ID - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - status: - $ref: '#/components/schemas/EventStatus' - example: - status: "in-progress" - responses: - '200': - description: Event status updated - content: - application/json: - schema: - $ref: '#/components/schemas/ScheduleEvent' - '404': - description: VIP or event not found - - /drivers: - get: - tags: - - Drivers - summary: Get all drivers - description: Retrieve a list of all drivers in the system - responses: - '200': - description: List of drivers - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Driver' - - post: - tags: - - Drivers - summary: Create a new driver - description: Add a new driver to the system - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DriverCreate' - example: - name: "Carlos Rodriguez" - phone: "(303) 555-0101" - currentLocation: - lat: 39.8561 - lng: -104.6737 - responses: - '201': - description: Driver created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Driver' - - /drivers/{id}: - put: - tags: - - Drivers - summary: Update a driver - description: Update an existing driver's information - parameters: - - name: id - in: path - required: true - schema: - type: string - description: Driver ID - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DriverCreate' - responses: - '200': - description: Driver updated successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Driver' - '404': - description: Driver not found - - delete: - tags: - - Drivers - summary: Delete a driver - description: Remove a driver from the system - parameters: - - name: id - in: path - required: true - schema: - type: string - description: Driver ID - responses: - '200': - description: Driver deleted successfully - '404': - description: Driver not found - - /drivers/{driverId}/schedule: - get: - tags: - - Drivers - summary: Get driver's schedule - description: Retrieve all events assigned to a specific driver - parameters: - - name: driverId - in: path - required: true - schema: - type: string - description: Driver ID - responses: - '200': - description: Driver's schedule - content: - application/json: - schema: - type: object - properties: - driver: - type: object - properties: - id: - type: string - name: - type: string - phone: - type: string - schedule: - type: array - items: - allOf: - - $ref: '#/components/schemas/ScheduleEvent' - - type: object - properties: - vipId: - type: string - vipName: - type: string - '404': - description: Driver not found - - /drivers/availability: - post: - tags: - - Drivers - summary: Check driver availability - description: Find available drivers for a specific time slot - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - startTime: - type: string - format: date-time - endTime: - type: string - format: date-time - location: - type: string - required: - - startTime - - endTime - example: - startTime: "2025-06-26T11:00:00" - endTime: "2025-06-26T12:30:00" - location: "Denver Convention Center" - responses: - '200': - description: Driver availability information - content: - application/json: - schema: - type: object - properties: - available: - type: array - items: - $ref: '#/components/schemas/Driver' - busy: - type: array - items: - allOf: - - $ref: '#/components/schemas/Driver' - - type: object - properties: - conflictingEvents: - type: array - items: - $ref: '#/components/schemas/ScheduleEvent' - - /drivers/{driverId}/conflicts: - post: - tags: - - Drivers - summary: Check driver conflicts - description: Check if a specific driver has conflicts for a time slot - parameters: - - name: driverId - in: path - required: true - schema: - type: string - description: Driver ID - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - startTime: - type: string - format: date-time - endTime: - type: string - format: date-time - location: - type: string - required: - - startTime - - endTime - responses: - '200': - description: Conflict check results - content: - application/json: - schema: - type: object - properties: - conflicts: - type: array - items: - $ref: '#/components/schemas/ScheduleEvent' - - /flights/{flightNumber}: - get: - tags: - - Flights - summary: Get flight information - description: Retrieve real-time flight information - parameters: - - name: flightNumber - in: path - required: true - schema: - type: string - description: Flight number (e.g., UA1234) - example: "UA1234" - - name: date - in: query - schema: - type: string - format: date - description: Flight date (YYYY-MM-DD) - example: "2025-06-26" - - name: departureAirport - in: query - schema: - type: string - description: Departure airport code - example: "LAX" - - name: arrivalAirport - in: query - schema: - type: string - description: Arrival airport code - example: "DEN" - responses: - '200': - description: Flight information - content: - application/json: - schema: - $ref: '#/components/schemas/FlightInfo' - '404': - description: Flight not found - '500': - description: Failed to fetch flight data - - /flights/{flightNumber}/track: - post: - tags: - - Flights - summary: Start flight tracking - description: Begin periodic updates for a specific flight - parameters: - - name: flightNumber - in: path - required: true - schema: - type: string - description: Flight number - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - date: - type: string - format: date - intervalMinutes: - type: integer - default: 5 - required: - - date - example: - date: "2025-06-26" - intervalMinutes: 5 - responses: - '200': - description: Flight tracking started - content: - application/json: - schema: - type: object - properties: - message: - type: string - example: "Started tracking UA1234 on 2025-06-26" - - delete: - tags: - - Flights - summary: Stop flight tracking - description: Stop periodic updates for a specific flight - parameters: - - name: flightNumber - in: path - required: true - schema: - type: string - description: Flight number - - name: date - in: query - required: true - schema: - type: string - format: date - description: Flight date - responses: - '200': - description: Flight tracking stopped - - /flights/batch: - post: - tags: - - Flights - summary: Get multiple flights information - description: Retrieve information for multiple flights at once - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - flights: - type: array - items: - type: object - properties: - flightNumber: - type: string - date: - type: string - format: date - required: - - flightNumber - - date - example: - flights: - - flightNumber: "UA1234" - date: "2025-06-26" - - flightNumber: "AA789" - date: "2025-06-26" - responses: - '200': - description: Multiple flight information - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/FlightInfo' - - /flights/tracking/status: - get: - tags: - - Flights - summary: Get flight tracking status - description: Get the status of all currently tracked flights - responses: - '200': - description: Flight tracking status - content: - application/json: - schema: - type: object - properties: - trackedFlights: - type: array - items: - type: object - properties: - flightKey: - type: string - vipName: - type: string - lastUpdate: - type: string - format: date-time - status: - type: string - - /admin/authenticate: - post: - tags: - - Admin - summary: Admin authentication - description: Authenticate admin user - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - password: - type: string - required: - - password - responses: - '200': - description: Authentication successful - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - '401': - description: Invalid password - - /admin/settings: - get: - tags: - - Admin - summary: Get admin settings - description: Retrieve current admin settings (requires authentication) - parameters: - - name: admin-auth - in: header - required: true - schema: - type: string - description: Admin authentication header - responses: - '200': - description: Admin settings - content: - application/json: - schema: - $ref: '#/components/schemas/AdminSettings' - '401': - description: Unauthorized - - post: - tags: - - Admin - summary: Update admin settings - description: Update admin settings (requires authentication) - parameters: - - name: admin-auth - in: header - required: true - schema: - type: string - description: Admin authentication header - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AdminSettings' - responses: - '200': - description: Settings updated successfully - '401': - description: Unauthorized - -components: - schemas: - VIP: - type: object - properties: - id: - type: string - description: Unique VIP identifier - name: - type: string - description: VIP's full name - organization: - type: string - description: VIP's organization or company - transportMode: - type: string - enum: [flight, self-driving] - description: Mode of transportation - flights: - type: array - items: - $ref: '#/components/schemas/Flight' - description: Flight information (for flight transport mode) - expectedArrival: - type: string - format: date-time - description: Expected arrival time (for self-driving mode) - needsAirportPickup: - type: boolean - description: Whether VIP needs airport pickup - needsVenueTransport: - type: boolean - description: Whether VIP needs venue transport - assignedDriverIds: - type: array - items: - type: string - description: List of assigned driver IDs - notes: - type: string - description: Additional notes about the VIP - schedule: - type: array - items: - $ref: '#/components/schemas/ScheduleEvent' - description: VIP's schedule (usually empty, fetched separately) - - VIPCreate: - type: object - required: - - name - - organization - - transportMode - properties: - name: - type: string - minLength: 1 - organization: - type: string - minLength: 1 - transportMode: - type: string - enum: [flight, self-driving] - flights: - type: array - items: - $ref: '#/components/schemas/Flight' - expectedArrival: - type: string - format: date-time - needsAirportPickup: - type: boolean - default: true - needsVenueTransport: - type: boolean - default: true - notes: - type: string - - Flight: - type: object - required: - - flightNumber - - flightDate - - segment - properties: - flightNumber: - type: string - description: Flight number (e.g., UA1234) - flightDate: - type: string - format: date - description: Flight date - segment: - type: integer - minimum: 1 - description: Flight segment number for connecting flights - validated: - type: boolean - description: Whether flight has been validated - validationData: - $ref: '#/components/schemas/FlightInfo' - - Driver: - type: object - properties: - id: - type: string - description: Unique driver identifier - name: - type: string - description: Driver's full name - phone: - type: string - description: Driver's phone number - currentLocation: - $ref: '#/components/schemas/Location' - assignedVipIds: - type: array - items: - type: string - description: List of assigned VIP IDs - - DriverCreate: - type: object - required: - - name - - phone - properties: - name: - type: string - minLength: 1 - phone: - type: string - minLength: 1 - currentLocation: - $ref: '#/components/schemas/Location' - - Location: - type: object - properties: - lat: - type: number - format: float - description: Latitude - lng: - type: number - format: float - description: Longitude - - ScheduleEvent: - type: object - properties: - id: - type: string - description: Unique event identifier - title: - type: string - description: Event title - location: - type: string - description: Event location - startTime: - type: string - format: date-time - description: Event start time - endTime: - type: string - format: date-time - description: Event end time - description: - type: string - description: Event description - assignedDriverId: - type: string - description: Assigned driver ID - status: - $ref: '#/components/schemas/EventStatus' - type: - $ref: '#/components/schemas/EventType' - - ScheduleEventCreate: - type: object - required: - - title - - location - - startTime - - endTime - - type - properties: - title: - type: string - minLength: 1 - location: - type: string - minLength: 1 - startTime: - type: string - format: date-time - endTime: - type: string - format: date-time - description: - type: string - type: - $ref: '#/components/schemas/EventType' - assignedDriverId: - type: string - - EventStatus: - type: string - enum: [scheduled, in-progress, completed, cancelled] - description: Current status of the event - - EventType: - type: string - enum: [transport, meeting, event, meal, accommodation] - description: Type of event - - FlightInfo: - type: object - properties: - flightNumber: - type: string - flightDate: - type: string - format: date - status: - type: string - enum: [scheduled, active, landed, cancelled, delayed] - airline: - type: string - aircraft: - type: string - departure: - $ref: '#/components/schemas/FlightLocation' - arrival: - $ref: '#/components/schemas/FlightLocation' - delay: - type: integer - description: Delay in minutes - lastUpdated: - type: string - format: date-time - source: - type: string - description: Data source (e.g., aviationstack) - - FlightLocation: - type: object - properties: - airport: - type: string - description: Airport code - airportName: - type: string - description: Full airport name - scheduled: - type: string - format: date-time - estimated: - type: string - format: date-time - actual: - type: string - format: date-time - terminal: - type: string - gate: - type: string - - AdminSettings: - type: object - properties: - apiKeys: - type: object - properties: - aviationStackKey: - type: string - description: Masked API key - googleMapsKey: - type: string - description: Masked API key - twilioKey: - type: string - description: Masked API key - systemSettings: - type: object - properties: - defaultPickupLocation: - type: string - defaultDropoffLocation: - type: string - timeZone: - type: string - notificationsEnabled: - type: boolean - - ValidationError: - type: object - properties: - error: - type: string - validationErrors: - type: array - items: - $ref: '#/components/schemas/ValidationMessage' - warnings: - type: array - items: - $ref: '#/components/schemas/ValidationWarning' - message: - type: string - - ValidationMessage: - type: object - properties: - field: - type: string - message: - type: string - code: - type: string - - ValidationWarning: - type: object - properties: - field: - type: string - message: - type: string - code: - type: string - - securitySchemes: - AdminAuth: - type: apiKey - in: header - name: admin-auth - description: Admin authentication header - -security: - - AdminAuth: [] diff --git a/frontend-old-20260125/dist/assets/index-a63a7bce.css b/frontend-old-20260125/dist/assets/index-a63a7bce.css deleted file mode 100644 index bd2a274..0000000 --- a/frontend-old-20260125/dist/assets/index-a63a7bce.css +++ /dev/null @@ -1 +0,0 @@ -.login-container{display:flex;justify-content:center;align-items:center;min-height:100vh;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);padding:20px}.login-card{background:white;border-radius:12px;box-shadow:0 20px 40px #0000001a;padding:40px;max-width:400px;width:100%;text-align:center}.login-header h1{color:#333;margin:0 0 8px;font-size:28px;font-weight:600}.login-header p{color:#666;margin:0 0 30px;font-size:16px}.setup-notice{background:#f0f9ff;border:1px solid #0ea5e9;border-radius:8px;padding:20px;margin-bottom:30px}.setup-notice h3{color:#0369a1;margin:0 0 8px;font-size:18px}.setup-notice p{color:#0369a1;margin:0;font-size:14px}.login-content{margin-bottom:30px}.google-login-btn{display:flex;align-items:center;justify-content:center;gap:12px;width:100%;padding:12px 24px;background:white;border:2px solid #dadce0;border-radius:8px;font-size:16px;font-weight:500;color:#3c4043;cursor:pointer;transition:all .2s ease;margin-bottom:20px}.google-login-btn:hover:not(:disabled){border-color:#1a73e8;box-shadow:0 2px 8px #1a73e833}.google-login-btn:disabled{opacity:.6;cursor:not-allowed}.google-icon{width:20px;height:20px}.login-info p{color:#666;font-size:14px;line-height:1.5;margin:0}.dev-login-divider{display:flex;align-items:center;gap:12px;margin:24px 0;color:#94a3b8;font-size:12px;text-transform:uppercase;letter-spacing:.08em}.dev-login-divider:before,.dev-login-divider:after{content:"";flex:1;height:1px;background:#e2e8f0}.dev-login-form{display:flex;flex-direction:column;gap:16px;text-align:left}.dev-login-form h3{margin:0;color:#1e293b;font-size:18px;font-weight:600}.dev-login-hint{margin:0;font-size:13px;color:#64748b;line-height:1.4}.dev-login-form label{display:flex;flex-direction:column;gap:6px;font-size:14px;color:#334155}.dev-login-form input{width:100%;padding:10px 12px;border:1px solid #cbd5f5;border-radius:8px;font-size:14px;transition:border-color .2s ease,box-shadow .2s ease}.dev-login-form input:focus{outline:none;border-color:#2563eb;box-shadow:0 0 0 3px #2563eb26}.dev-login-error{background:#fee2e2;border:1px solid #fecaca;color:#dc2626;padding:10px 12px;border-radius:8px;font-size:13px}.dev-login-button{width:100%;padding:12px;border:none;border-radius:8px;background:linear-gradient(135deg,#2563eb 0%,#1d4ed8 100%);color:#fff;font-size:15px;font-weight:600;cursor:pointer;transition:transform .2s ease,box-shadow .2s ease}.dev-login-button:hover:not(:disabled){transform:translateY(-1px);box-shadow:0 10px 20px #2563eb33}.dev-login-button:disabled{opacity:.7;cursor:not-allowed}.login-footer{border-top:1px solid #eee;padding-top:20px}.login-footer p{color:#999;font-size:12px;margin:0}.loading{color:#666;font-size:16px;padding:40px}@media (max-width: 480px){.login-container{padding:10px}.login-card{padding:30px 20px}.login-header h1{font-size:24px}}.btn-modern{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));border-radius:.75rem;padding:.75rem 1.5rem;font-size:.875rem;line-height:1.25rem;font-weight:600;--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.btn-modern:hover{--tw-translate-y: -.125rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.btn-gradient-blue{background-image:linear-gradient(to right,var(--tw-gradient-stops));--tw-gradient-from: #3b82f6 var(--tw-gradient-from-position);--tw-gradient-to: rgb(59 130 246 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: #2563eb var(--tw-gradient-to-position);--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.btn-gradient-blue:hover{--tw-gradient-from: #2563eb var(--tw-gradient-from-position);--tw-gradient-to: rgb(37 99 235 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: #1d4ed8 var(--tw-gradient-to-position)}.btn-gradient-green{background-image:linear-gradient(to right,var(--tw-gradient-stops));--tw-gradient-from: #22c55e var(--tw-gradient-from-position);--tw-gradient-to: rgb(34 197 94 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: #16a34a var(--tw-gradient-to-position);--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.btn-gradient-green:hover{--tw-gradient-from: #16a34a var(--tw-gradient-from-position);--tw-gradient-to: rgb(22 163 74 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: #15803d var(--tw-gradient-to-position)}.btn-gradient-purple{background-image:linear-gradient(to right,var(--tw-gradient-stops));--tw-gradient-from: #a855f7 var(--tw-gradient-from-position);--tw-gradient-to: rgb(168 85 247 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: #9333ea var(--tw-gradient-to-position);--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.btn-gradient-purple:hover{--tw-gradient-from: #9333ea var(--tw-gradient-from-position);--tw-gradient-to: rgb(147 51 234 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: #7e22ce var(--tw-gradient-to-position)}.btn-gradient-amber{background-image:linear-gradient(to right,var(--tw-gradient-stops));--tw-gradient-from: #f59e0b var(--tw-gradient-from-position);--tw-gradient-to: rgb(245 158 11 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: #d97706 var(--tw-gradient-to-position);--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.btn-gradient-amber:hover{--tw-gradient-from: #d97706 var(--tw-gradient-from-position);--tw-gradient-to: rgb(217 119 6 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: #b45309 var(--tw-gradient-to-position)}.status-badge{display:inline-flex;align-items:center;border-radius:9999px;padding:.25rem .75rem;font-size:.75rem;line-height:1rem;font-weight:600}.status-scheduled{border-width:1px;--tw-border-opacity: 1;border-color:rgb(191 219 254 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(219 234 254 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(30 64 175 / var(--tw-text-opacity, 1))}.status-in-progress{border-width:1px;--tw-border-opacity: 1;border-color:rgb(253 230 138 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(254 243 199 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(146 64 14 / var(--tw-text-opacity, 1))}.status-completed{border-width:1px;--tw-border-opacity: 1;border-color:rgb(187 247 208 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(220 252 231 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(22 101 52 / var(--tw-text-opacity, 1))}.status-cancelled{border-width:1px;--tw-border-opacity: 1;border-color:rgb(254 202 202 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(153 27 27 / var(--tw-text-opacity, 1))}.card-modern{overflow:hidden;border-radius:1rem;border-width:1px;border-color:#e2e8f099;--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.card-header{border-bottom-width:1px;border-color:#e2e8f099;background-image:linear-gradient(to right,var(--tw-gradient-stops));--tw-gradient-from: #f8fafc var(--tw-gradient-from-position);--tw-gradient-to: rgb(248 250 252 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: #f1f5f9 var(--tw-gradient-to-position);padding:1rem 1.5rem}.card-content{padding:1.5rem}.loading-spinner{animation:spin 1s linear infinite;border-radius:9999px;border-width:4px;--tw-border-opacity: 1;border-color:rgb(37 99 235 / var(--tw-border-opacity, 1));border-top-color:transparent}.loading-text{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite;--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity, 1))}.skeleton{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite;border-radius:.25rem;--tw-bg-opacity: 1;background-color:rgb(226 232 240 / var(--tw-bg-opacity, 1))}.form-modern>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.form-group-modern>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.form-label-modern{display:block;font-size:.875rem;line-height:1.25rem;font-weight:600;--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity, 1))}.form-input-modern{width:100%;border-radius:.75rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(203 213 225 / var(--tw-border-opacity, 1));padding:.75rem 1rem;--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.form-input-modern:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1));outline:2px solid transparent;outline-offset:2px;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.form-select-modern{width:100%;border-radius:.75rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(203 213 225 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));padding:.75rem 1rem;--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.form-select-modern:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1));outline:2px solid transparent;outline-offset:2px;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.animate-fade-in{animation:fadeIn .5s ease-in-out}.animate-slide-up{animation:slideUp .3s ease-out}.animate-scale-in{animation:scaleIn .2s ease-out}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideUp{0%{transform:translateY(10px);opacity:0}to{transform:translateY(0);opacity:1}}@keyframes scaleIn{0%{transform:scale(.95);opacity:0}to{transform:scale(1);opacity:1}}@media (max-width: 768px){.mobile-stack{flex-direction:column}.mobile-stack>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse));--tw-space-x-reverse: 0;margin-right:calc(0px * var(--tw-space-x-reverse));margin-left:calc(0px * calc(1 - var(--tw-space-x-reverse)))}.mobile-full{width:100%}.mobile-text-center{text-align:center}}.glass{border-width:1px;border-color:#fff3;background-color:#fffc;--tw-backdrop-blur: blur(16px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.glass-dark{border-width:1px;border-color:#33415533;background-color:#0f172acc;--tw-backdrop-blur: blur(16px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.hover-lift{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.hover-lift:hover{--tw-translate-y: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover-glow{transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.hover-glow:hover{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.hover-scale{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.hover-scale:hover{--tw-scale-x: 1.05;--tw-scale-y: 1.05;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;line-height:1.6;font-weight:400;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-text-size-adjust:100%}*{box-sizing:border-box}body{margin:0;min-width:320px;min-height:100vh;background:linear-gradient(135deg,#f8fafc 0%,#e2e8f0 100%);color:#1e293b}#root{width:100%;margin:0 auto;text-align:left}html{scroll-behavior:smooth}*:focus{outline:2px solid #3b82f6;outline-offset:2px}.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.btn{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));border-radius:.75rem;padding:.75rem 1.5rem;font-size:.875rem;line-height:1.25rem;font-weight:600;--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.btn:hover{--tw-translate-y: -.125rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.btn:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-offset-width: 2px}.btn-primary{background-image:linear-gradient(to right,var(--tw-gradient-stops));--tw-gradient-from: #3b82f6 var(--tw-gradient-from-position);--tw-gradient-to: rgb(59 130 246 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: #2563eb var(--tw-gradient-to-position);--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.btn-primary:hover{--tw-gradient-from: #2563eb var(--tw-gradient-from-position);--tw-gradient-to: rgb(37 99 235 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: #1d4ed8 var(--tw-gradient-to-position)}.btn-primary:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.btn-secondary{background-image:linear-gradient(to right,var(--tw-gradient-stops));--tw-gradient-from: #64748b var(--tw-gradient-from-position);--tw-gradient-to: rgb(100 116 139 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: #475569 var(--tw-gradient-to-position);--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.btn-secondary:hover{--tw-gradient-from: #475569 var(--tw-gradient-from-position);--tw-gradient-to: rgb(71 85 105 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: #334155 var(--tw-gradient-to-position)}.btn-secondary:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(100 116 139 / var(--tw-ring-opacity, 1))}.btn-danger{background-image:linear-gradient(to right,var(--tw-gradient-stops));--tw-gradient-from: #ef4444 var(--tw-gradient-from-position);--tw-gradient-to: rgb(239 68 68 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: #dc2626 var(--tw-gradient-to-position);--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.btn-danger:hover{--tw-gradient-from: #dc2626 var(--tw-gradient-from-position);--tw-gradient-to: rgb(220 38 38 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: #b91c1c var(--tw-gradient-to-position)}.btn-danger:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(239 68 68 / var(--tw-ring-opacity, 1))}.btn-success{background-image:linear-gradient(to right,var(--tw-gradient-stops));--tw-gradient-from: #22c55e var(--tw-gradient-from-position);--tw-gradient-to: rgb(34 197 94 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: #16a34a var(--tw-gradient-to-position);--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.btn-success:hover{--tw-gradient-from: #16a34a var(--tw-gradient-from-position);--tw-gradient-to: rgb(22 163 74 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: #15803d var(--tw-gradient-to-position)}.btn-success:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(34 197 94 / var(--tw-ring-opacity, 1))}.card{overflow:hidden;border-radius:1rem;border-width:1px;border-color:#e2e8f099;--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.form-group{margin-bottom:1.5rem}.form-label{margin-bottom:.75rem;display:block;font-size:.875rem;line-height:1.25rem;font-weight:600;--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity, 1))}.form-input{width:100%;border-radius:.75rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(203 213 225 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));padding:.75rem 1rem;--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.form-input:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1));outline:2px solid transparent;outline-offset:2px;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.form-select{width:100%;border-radius:.75rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(203 213 225 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));padding:.75rem 1rem;--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.form-select:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1));outline:2px solid transparent;outline-offset:2px;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.form-textarea{width:100%;resize:none;border-radius:.75rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(203 213 225 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));padding:.75rem 1rem;--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.form-textarea:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1));outline:2px solid transparent;outline-offset:2px;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.form-checkbox{height:1.25rem;width:1.25rem;border-radius:.25rem;--tw-border-opacity: 1;border-color:rgb(203 213 225 / var(--tw-border-opacity, 1));--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.form-checkbox:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.form-radio{height:1rem;width:1rem;--tw-border-opacity: 1;border-color:rgb(203 213 225 / var(--tw-border-opacity, 1));--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.form-radio:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.modal-overlay{position:fixed;top:0;right:0;bottom:0;left:0;z-index:50;display:flex;align-items:center;justify-content:center;background-color:#00000080;padding:1rem;--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.modal-content{max-height:90vh;width:100%;max-width:56rem;overflow-y:auto;border-radius:1rem;--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.modal-header{border-bottom-width:1px;border-color:#e2e8f099;background-image:linear-gradient(to right,var(--tw-gradient-stops));--tw-gradient-from: #eff6ff var(--tw-gradient-from-position);--tw-gradient-to: rgb(239 246 255 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);--tw-gradient-to: #eef2ff var(--tw-gradient-to-position);padding:1.5rem 2rem}.modal-body{padding:2rem}.form-actions{margin-top:2rem;display:flex;justify-content:flex-end}.form-actions>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.form-actions{border-top-width:1px;border-color:#e2e8f099;padding-top:1.5rem}.form-section{margin-bottom:1.5rem;border-radius:.75rem;border-width:1px;border-color:#e2e8f099;--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1));padding:1.5rem}.form-section-header{margin-bottom:1rem;display:flex;align-items:center;justify-content:space-between}.form-section-title{font-size:1.125rem;line-height:1.75rem;font-weight:700;--tw-text-opacity: 1;color:rgb(30 41 59 / var(--tw-text-opacity, 1))}.radio-group{margin-top:.75rem;display:flex;gap:1.5rem}.radio-option{display:flex;cursor:pointer;align-items:center;border-radius:.5rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));padding:.75rem 1rem;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.radio-option:hover{--tw-border-opacity: 1;border-color:rgb(147 197 253 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.radio-option.selected{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1));--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-opacity: 1;--tw-ring-color: rgb(191 219 254 / var(--tw-ring-opacity, 1))}.checkbox-option{display:flex;cursor:pointer;align-items:center;border-radius:.5rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));padding:.75rem 1rem;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.checkbox-option:hover{--tw-border-opacity: 1;border-color:rgb(147 197 253 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.checkbox-option.checked{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.right-2{right:.5rem}.top-0{top:0}.top-2{top:.5rem}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.-mb-px{margin-bottom:-1px}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.ml-2{margin-left:.5rem}.ml-4{margin-left:1rem}.ml-6{margin-left:1.5rem}.ml-auto{margin-left:auto}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-20{height:5rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.max-h-20{max-height:5rem}.max-h-\[90vh\]{max-height:90vh}.min-h-64{min-height:16rem}.min-h-screen{min-height:100vh}.w-1\/4{width:25%}.w-10{width:2.5rem}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-full{width:100%}.max-w-2xl{max-width:42rem}.max-w-4xl{max-width:56rem}.max-w-7xl{max-width:80rem}.max-w-xl{max-width:36rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.25rem * var(--tw-space-x-reverse));margin-left:calc(.25rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.75rem * var(--tw-space-x-reverse));margin-left:calc(.75rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(2rem * var(--tw-space-x-reverse));margin-left:calc(2rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:rgb(229 231 235 / var(--tw-divide-opacity, 1))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-4{border-width:4px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-t{border-top-width:1px}.border-amber-200{--tw-border-opacity: 1;border-color:rgb(253 230 138 / var(--tw-border-opacity, 1))}.border-amber-200\/60{border-color:#fde68a99}.border-amber-300{--tw-border-opacity: 1;border-color:rgb(252 211 77 / var(--tw-border-opacity, 1))}.border-blue-200{--tw-border-opacity: 1;border-color:rgb(191 219 254 / var(--tw-border-opacity, 1))}.border-blue-200\/60{border-color:#bfdbfe99}.border-blue-300{--tw-border-opacity: 1;border-color:rgb(147 197 253 / var(--tw-border-opacity, 1))}.border-blue-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-blue-600{--tw-border-opacity: 1;border-color:rgb(37 99 235 / var(--tw-border-opacity, 1))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1))}.border-green-200{--tw-border-opacity: 1;border-color:rgb(187 247 208 / var(--tw-border-opacity, 1))}.border-green-200\/60{border-color:#bbf7d099}.border-green-500{--tw-border-opacity: 1;border-color:rgb(34 197 94 / var(--tw-border-opacity, 1))}.border-orange-200{--tw-border-opacity: 1;border-color:rgb(254 215 170 / var(--tw-border-opacity, 1))}.border-orange-500{--tw-border-opacity: 1;border-color:rgb(249 115 22 / var(--tw-border-opacity, 1))}.border-red-200{--tw-border-opacity: 1;border-color:rgb(254 202 202 / var(--tw-border-opacity, 1))}.border-red-200\/60{border-color:#fecaca99}.border-red-600{--tw-border-opacity: 1;border-color:rgb(220 38 38 / var(--tw-border-opacity, 1))}.border-rose-200\/70{border-color:#fecdd3b3}.border-slate-200{--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity, 1))}.border-slate-200\/50{border-color:#e2e8f080}.border-slate-200\/60{border-color:#e2e8f099}.border-slate-300{--tw-border-opacity: 1;border-color:rgb(203 213 225 / var(--tw-border-opacity, 1))}.border-transparent{border-color:transparent}.border-white{--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity, 1))}.border-t-transparent{border-top-color:transparent}.bg-amber-100{--tw-bg-opacity: 1;background-color:rgb(254 243 199 / var(--tw-bg-opacity, 1))}.bg-amber-50{--tw-bg-opacity: 1;background-color:rgb(255 251 235 / var(--tw-bg-opacity, 1))}.bg-black\/50{background-color:#00000080}.bg-blue-100{--tw-bg-opacity: 1;background-color:rgb(219 234 254 / var(--tw-bg-opacity, 1))}.bg-blue-50{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.bg-gray-200{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.bg-gray-300{--tw-bg-opacity: 1;background-color:rgb(209 213 219 / var(--tw-bg-opacity, 1))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.bg-green-100{--tw-bg-opacity: 1;background-color:rgb(220 252 231 / var(--tw-bg-opacity, 1))}.bg-green-50{--tw-bg-opacity: 1;background-color:rgb(240 253 244 / var(--tw-bg-opacity, 1))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-green-600{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity, 1))}.bg-orange-100{--tw-bg-opacity: 1;background-color:rgb(255 237 213 / var(--tw-bg-opacity, 1))}.bg-orange-50{--tw-bg-opacity: 1;background-color:rgb(255 247 237 / var(--tw-bg-opacity, 1))}.bg-purple-100{--tw-bg-opacity: 1;background-color:rgb(243 232 255 / var(--tw-bg-opacity, 1))}.bg-red-100{--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity, 1))}.bg-red-50{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.bg-red-600{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity, 1))}.bg-slate-100{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity, 1))}.bg-slate-200{--tw-bg-opacity: 1;background-color:rgb(226 232 240 / var(--tw-bg-opacity, 1))}.bg-slate-300{--tw-bg-opacity: 1;background-color:rgb(203 213 225 / var(--tw-bg-opacity, 1))}.bg-slate-50{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-white\/50{background-color:#ffffff80}.bg-white\/80{background-color:#fffc}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-amber-50{--tw-gradient-from: #fffbeb var(--tw-gradient-from-position);--tw-gradient-to: rgb(255 251 235 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-amber-500{--tw-gradient-from: #f59e0b var(--tw-gradient-from-position);--tw-gradient-to: rgb(245 158 11 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-blue-50{--tw-gradient-from: #eff6ff var(--tw-gradient-from-position);--tw-gradient-to: rgb(239 246 255 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-blue-500{--tw-gradient-from: #3b82f6 var(--tw-gradient-from-position);--tw-gradient-to: rgb(59 130 246 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-blue-600{--tw-gradient-from: #2563eb var(--tw-gradient-from-position);--tw-gradient-to: rgb(37 99 235 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-green-400{--tw-gradient-from: #4ade80 var(--tw-gradient-from-position);--tw-gradient-to: rgb(74 222 128 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-green-50{--tw-gradient-from: #f0fdf4 var(--tw-gradient-from-position);--tw-gradient-to: rgb(240 253 244 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-green-500{--tw-gradient-from: #22c55e var(--tw-gradient-from-position);--tw-gradient-to: rgb(34 197 94 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-indigo-50{--tw-gradient-from: #eef2ff var(--tw-gradient-from-position);--tw-gradient-to: rgb(238 242 255 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-orange-50{--tw-gradient-from: #fff7ed var(--tw-gradient-from-position);--tw-gradient-to: rgb(255 247 237 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-purple-50{--tw-gradient-from: #faf5ff var(--tw-gradient-from-position);--tw-gradient-to: rgb(250 245 255 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-purple-500{--tw-gradient-from: #a855f7 var(--tw-gradient-from-position);--tw-gradient-to: rgb(168 85 247 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-red-500{--tw-gradient-from: #ef4444 var(--tw-gradient-from-position);--tw-gradient-to: rgb(239 68 68 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-sky-50{--tw-gradient-from: #f0f9ff var(--tw-gradient-from-position);--tw-gradient-to: rgb(240 249 255 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-slate-400{--tw-gradient-from: #94a3b8 var(--tw-gradient-from-position);--tw-gradient-to: rgb(148 163 184 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-slate-50{--tw-gradient-from: #f8fafc var(--tw-gradient-from-position);--tw-gradient-to: rgb(248 250 252 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-slate-500{--tw-gradient-from: #64748b var(--tw-gradient-from-position);--tw-gradient-to: rgb(100 116 139 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-slate-600{--tw-gradient-from: #475569 var(--tw-gradient-from-position);--tw-gradient-to: rgb(71 85 105 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-slate-800{--tw-gradient-from: #1e293b var(--tw-gradient-from-position);--tw-gradient-to: rgb(30 41 59 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.via-blue-50{--tw-gradient-to: rgb(239 246 255 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), #eff6ff var(--tw-gradient-via-position), var(--tw-gradient-to)}.to-blue-50{--tw-gradient-to: #eff6ff var(--tw-gradient-to-position)}.to-blue-600{--tw-gradient-to: #2563eb var(--tw-gradient-to-position)}.to-emerald-50{--tw-gradient-to: #ecfdf5 var(--tw-gradient-to-position)}.to-green-600{--tw-gradient-to: #16a34a var(--tw-gradient-to-position)}.to-indigo-50{--tw-gradient-to: #eef2ff var(--tw-gradient-to-position)}.to-indigo-600{--tw-gradient-to: #4f46e5 var(--tw-gradient-to-position)}.to-orange-50{--tw-gradient-to: #fff7ed var(--tw-gradient-to-position)}.to-orange-500{--tw-gradient-to: #f97316 var(--tw-gradient-to-position)}.to-pink-50{--tw-gradient-to: #fdf2f8 var(--tw-gradient-to-position)}.to-purple-50{--tw-gradient-to: #faf5ff var(--tw-gradient-to-position)}.to-purple-600{--tw-gradient-to: #9333ea var(--tw-gradient-to-position)}.to-red-50{--tw-gradient-to: #fef2f2 var(--tw-gradient-to-position)}.to-red-600{--tw-gradient-to: #dc2626 var(--tw-gradient-to-position)}.to-rose-50{--tw-gradient-to: #fff1f2 var(--tw-gradient-to-position)}.to-slate-100{--tw-gradient-to: #f1f5f9 var(--tw-gradient-to-position)}.to-slate-600{--tw-gradient-to: #475569 var(--tw-gradient-to-position)}.to-slate-700{--tw-gradient-to: #334155 var(--tw-gradient-to-position)}.bg-clip-text{-webkit-background-clip:text;background-clip:text}.object-cover{-o-object-fit:cover;object-fit:cover}.p-12{padding:3rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pt-4{padding-top:1rem}.pt-6{padding-top:1.5rem}.text-center{text-align:center}.text-right{text-align:right}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-6xl{font-size:3.75rem;line-height:1}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.italic{font-style:italic}.text-amber-600{--tw-text-opacity: 1;color:rgb(217 119 6 / var(--tw-text-opacity, 1))}.text-amber-700{--tw-text-opacity: 1;color:rgb(180 83 9 / var(--tw-text-opacity, 1))}.text-amber-800{--tw-text-opacity: 1;color:rgb(146 64 14 / var(--tw-text-opacity, 1))}.text-amber-900{--tw-text-opacity: 1;color:rgb(120 53 15 / var(--tw-text-opacity, 1))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-blue-700{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.text-blue-800{--tw-text-opacity: 1;color:rgb(30 64 175 / var(--tw-text-opacity, 1))}.text-blue-900{--tw-text-opacity: 1;color:rgb(30 58 138 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-gray-800{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity, 1))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity, 1))}.text-green-700{--tw-text-opacity: 1;color:rgb(21 128 61 / var(--tw-text-opacity, 1))}.text-green-800{--tw-text-opacity: 1;color:rgb(22 101 52 / var(--tw-text-opacity, 1))}.text-green-900{--tw-text-opacity: 1;color:rgb(20 83 45 / var(--tw-text-opacity, 1))}.text-orange-600{--tw-text-opacity: 1;color:rgb(234 88 12 / var(--tw-text-opacity, 1))}.text-orange-800{--tw-text-opacity: 1;color:rgb(154 52 18 / var(--tw-text-opacity, 1))}.text-orange-900{--tw-text-opacity: 1;color:rgb(124 45 18 / var(--tw-text-opacity, 1))}.text-purple-800{--tw-text-opacity: 1;color:rgb(107 33 168 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.text-red-700{--tw-text-opacity: 1;color:rgb(185 28 28 / var(--tw-text-opacity, 1))}.text-red-800{--tw-text-opacity: 1;color:rgb(153 27 27 / var(--tw-text-opacity, 1))}.text-red-900{--tw-text-opacity: 1;color:rgb(127 29 29 / var(--tw-text-opacity, 1))}.text-rose-700{--tw-text-opacity: 1;color:rgb(190 18 60 / var(--tw-text-opacity, 1))}.text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.text-slate-500{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.text-slate-600{--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity, 1))}.text-slate-700{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity, 1))}.text-slate-800{--tw-text-opacity: 1;color:rgb(30 41 59 / var(--tw-text-opacity, 1))}.text-slate-900{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity, 1))}.text-transparent{color:transparent}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.opacity-50{opacity:.5}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-lg{--tw-backdrop-blur: blur(16px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-shadow{transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.hover\:border-gray-300:hover{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.hover\:border-slate-300:hover{--tw-border-opacity: 1;border-color:rgb(203 213 225 / var(--tw-border-opacity, 1))}.hover\:border-slate-400:hover{--tw-border-opacity: 1;border-color:rgb(148 163 184 / var(--tw-border-opacity, 1))}.hover\:bg-amber-50:hover{--tw-bg-opacity: 1;background-color:rgb(255 251 235 / var(--tw-bg-opacity, 1))}.hover\:bg-blue-50:hover{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.hover\:bg-green-700:hover{--tw-bg-opacity: 1;background-color:rgb(21 128 61 / var(--tw-bg-opacity, 1))}.hover\:bg-purple-50:hover{--tw-bg-opacity: 1;background-color:rgb(250 245 255 / var(--tw-bg-opacity, 1))}.hover\:bg-red-100:hover{--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity, 1))}.hover\:bg-red-50:hover{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.hover\:bg-red-700:hover{--tw-bg-opacity: 1;background-color:rgb(185 28 28 / var(--tw-bg-opacity, 1))}.hover\:bg-slate-200:hover{--tw-bg-opacity: 1;background-color:rgb(226 232 240 / var(--tw-bg-opacity, 1))}.hover\:bg-slate-50:hover{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.hover\:bg-opacity-80:hover{--tw-bg-opacity: .8}.hover\:from-amber-600:hover{--tw-gradient-from: #d97706 var(--tw-gradient-from-position);--tw-gradient-to: rgb(217 119 6 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.hover\:from-blue-600:hover{--tw-gradient-from: #2563eb var(--tw-gradient-from-position);--tw-gradient-to: rgb(37 99 235 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.hover\:from-green-600:hover{--tw-gradient-from: #16a34a var(--tw-gradient-from-position);--tw-gradient-to: rgb(22 163 74 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.hover\:from-purple-600:hover{--tw-gradient-from: #9333ea var(--tw-gradient-from-position);--tw-gradient-to: rgb(147 51 234 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.hover\:from-red-600:hover{--tw-gradient-from: #dc2626 var(--tw-gradient-from-position);--tw-gradient-to: rgb(220 38 38 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.hover\:from-slate-600:hover{--tw-gradient-from: #475569 var(--tw-gradient-from-position);--tw-gradient-to: rgb(71 85 105 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.hover\:to-blue-700:hover{--tw-gradient-to: #1d4ed8 var(--tw-gradient-to-position)}.hover\:to-green-700:hover{--tw-gradient-to: #15803d var(--tw-gradient-to-position)}.hover\:to-orange-600:hover{--tw-gradient-to: #ea580c var(--tw-gradient-to-position)}.hover\:to-purple-700:hover{--tw-gradient-to: #7e22ce var(--tw-gradient-to-position)}.hover\:to-red-700:hover{--tw-gradient-to: #b91c1c var(--tw-gradient-to-position)}.hover\:to-slate-700:hover{--tw-gradient-to: #334155 var(--tw-gradient-to-position)}.hover\:text-amber-600:hover{--tw-text-opacity: 1;color:rgb(217 119 6 / var(--tw-text-opacity, 1))}.hover\:text-blue-600:hover{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.hover\:text-gray-700:hover{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.hover\:text-purple-600:hover{--tw-text-opacity: 1;color:rgb(147 51 234 / var(--tw-text-opacity, 1))}.hover\:text-red-700:hover{--tw-text-opacity: 1;color:rgb(185 28 28 / var(--tw-text-opacity, 1))}.hover\:text-red-800:hover{--tw-text-opacity: 1;color:rgb(153 27 27 / var(--tw-text-opacity, 1))}.hover\:shadow-lg:hover{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.hover\:shadow-md:hover{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.hover\:shadow-xl:hover{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.focus\:border-blue-500:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.focus\:border-green-500:focus{--tw-border-opacity: 1;border-color:rgb(34 197 94 / var(--tw-border-opacity, 1))}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.focus\:ring-green-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(34 197 94 / var(--tw-ring-opacity, 1))}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:from-slate-400:disabled{--tw-gradient-from: #94a3b8 var(--tw-gradient-from-position);--tw-gradient-to: rgb(148 163 184 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.disabled\:to-slate-500:disabled{--tw-gradient-to: #64748b var(--tw-gradient-to-position)}.disabled\:opacity-50:disabled{opacity:.5}@media (min-width: 640px){.sm\:flex{display:flex}}@media (min-width: 768px){.md\:col-span-2{grid-column:span 2 / span 2}.md\:flex{display:flex}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (min-width: 1024px){.lg\:col-span-2{grid-column:span 2 / span 2}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:px-8{padding-left:2rem;padding-right:2rem}}@media (min-width: 1280px){.xl\:col-span-2{grid-column:span 2 / span 2}.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}} diff --git a/frontend-old-20260125/dist/assets/index-b3227753.js b/frontend-old-20260125/dist/assets/index-b3227753.js deleted file mode 100644 index 42c7f8d..0000000 --- a/frontend-old-20260125/dist/assets/index-b3227753.js +++ /dev/null @@ -1,651 +0,0 @@ -function vh(e,t){for(var n=0;nr[s]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))r(s);new MutationObserver(s=>{for(const o of s)if(o.type==="childList")for(const a of o.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&r(a)}).observe(document,{childList:!0,subtree:!0});function n(s){const o={};return s.integrity&&(o.integrity=s.integrity),s.referrerPolicy&&(o.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?o.credentials="include":s.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function r(s){if(s.ep)return;s.ep=!0;const o=n(s);fetch(s.href,o)}})();function yh(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var _c={exports:{}},Ks={},Dc={exports:{}},R={};/** - * @license React - * react.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var zr=Symbol.for("react.element"),wh=Symbol.for("react.portal"),bh=Symbol.for("react.fragment"),jh=Symbol.for("react.strict_mode"),Nh=Symbol.for("react.profiler"),kh=Symbol.for("react.provider"),Sh=Symbol.for("react.context"),Ch=Symbol.for("react.forward_ref"),Eh=Symbol.for("react.suspense"),Th=Symbol.for("react.memo"),Ph=Symbol.for("react.lazy"),Ja=Symbol.iterator;function _h(e){return e===null||typeof e!="object"?null:(e=Ja&&e[Ja]||e["@@iterator"],typeof e=="function"?e:null)}var Ic={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Ac=Object.assign,zc={};function $n(e,t,n){this.props=e,this.context=t,this.refs=zc,this.updater=n||Ic}$n.prototype.isReactComponent={};$n.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};$n.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function Oc(){}Oc.prototype=$n.prototype;function Bo(e,t,n){this.props=e,this.context=t,this.refs=zc,this.updater=n||Ic}var Ko=Bo.prototype=new Oc;Ko.constructor=Bo;Ac(Ko,$n.prototype);Ko.isPureReactComponent=!0;var Xa=Array.isArray,Lc=Object.prototype.hasOwnProperty,Ho={current:null},Rc={key:!0,ref:!0,__self:!0,__source:!0};function $c(e,t,n){var r,s={},o=null,a=null;if(t!=null)for(r in t.ref!==void 0&&(a=t.ref),t.key!==void 0&&(o=""+t.key),t)Lc.call(t,r)&&!Rc.hasOwnProperty(r)&&(s[r]=t[r]);var l=arguments.length-2;if(l===1)s.children=n;else if(1>>1,F=E[$];if(0>>1;$s(Le,O))Wes(Vt,Le)?(E[$]=Vt,E[We]=O,$=We):(E[$]=Le,E[ue]=O,$=ue);else if(Wes(Vt,O))E[$]=Vt,E[We]=O,$=We;else break e}}return D}function s(E,D){var O=E.sortIndex-D.sortIndex;return O!==0?O:E.id-D.id}if(typeof performance=="object"&&typeof performance.now=="function"){var o=performance;e.unstable_now=function(){return o.now()}}else{var a=Date,l=a.now();e.unstable_now=function(){return a.now()-l}}var c=[],d=[],m=1,p=null,f=3,g=!1,b=!1,w=!1,j=typeof setTimeout=="function"?setTimeout:null,x=typeof clearTimeout=="function"?clearTimeout:null,u=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function h(E){for(var D=n(d);D!==null;){if(D.callback===null)r(d);else if(D.startTime<=E)r(d),D.sortIndex=D.expirationTime,t(c,D);else break;D=n(d)}}function y(E){if(w=!1,h(E),!b)if(n(c)!==null)b=!0,P(v);else{var D=n(d);D!==null&&L(y,D.startTime-E)}}function v(E,D){b=!1,w&&(w=!1,x(C),C=-1),g=!0;var O=f;try{for(h(D),p=n(c);p!==null&&(!(p.expirationTime>D)||E&&!I());){var $=p.callback;if(typeof $=="function"){p.callback=null,f=p.priorityLevel;var F=$(p.expirationTime<=D);D=e.unstable_now(),typeof F=="function"?p.callback=F:p===n(c)&&r(c),h(D)}else r(c);p=n(c)}if(p!==null)var ie=!0;else{var ue=n(d);ue!==null&&L(y,ue.startTime-D),ie=!1}return ie}finally{p=null,f=O,g=!1}}var N=!1,k=null,C=-1,z=5,A=-1;function I(){return!(e.unstable_now()-AE||125$?(E.sortIndex=O,t(d,E),n(c)===null&&E===n(d)&&(w?(x(C),C=-1):w=!0,L(y,O-$))):(E.sortIndex=F,t(c,E),b||g||(b=!0,P(v))),E},e.unstable_shouldYield=I,e.unstable_wrapCallback=function(E){var D=f;return function(){var O=f;f=D;try{return E.apply(this,arguments)}finally{f=O}}}})(Wc);Uc.exports=Wc;var Uh=Uc.exports;/** - * @license React - * react-dom.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Wh=S,Ae=Uh;function T(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Ki=Object.prototype.hasOwnProperty,Bh=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,Qa={},qa={};function Kh(e){return Ki.call(qa,e)?!0:Ki.call(Qa,e)?!1:Bh.test(e)?qa[e]=!0:(Qa[e]=!0,!1)}function Hh(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function Gh(e,t,n,r){if(t===null||typeof t>"u"||Hh(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function Ne(e,t,n,r,s,o,a){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=s,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=o,this.removeEmptyString=a}var pe={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){pe[e]=new Ne(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];pe[t]=new Ne(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){pe[e]=new Ne(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){pe[e]=new Ne(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){pe[e]=new Ne(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){pe[e]=new Ne(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){pe[e]=new Ne(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){pe[e]=new Ne(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){pe[e]=new Ne(e,5,!1,e.toLowerCase(),null,!1,!1)});var Jo=/[\-:]([a-z])/g;function Xo(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(Jo,Xo);pe[t]=new Ne(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(Jo,Xo);pe[t]=new Ne(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(Jo,Xo);pe[t]=new Ne(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){pe[e]=new Ne(e,1,!1,e.toLowerCase(),null,!1,!1)});pe.xlinkHref=new Ne("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){pe[e]=new Ne(e,1,!1,e.toLowerCase(),null,!0,!0)});function Yo(e,t,n,r){var s=pe.hasOwnProperty(t)?pe[t]:null;(s!==null?s.type!==0:r||!(2l||s[a]!==o[l]){var c=` -`+s[a].replace(" at new "," at ");return e.displayName&&c.includes("")&&(c=c.replace("",e.displayName)),c}while(1<=a&&0<=l);break}}}finally{pi=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?qn(e):""}function Zh(e){switch(e.tag){case 5:return qn(e.type);case 16:return qn("Lazy");case 13:return qn("Suspense");case 19:return qn("SuspenseList");case 0:case 2:case 15:return e=fi(e.type,!1),e;case 11:return e=fi(e.type.render,!1),e;case 1:return e=fi(e.type,!0),e;default:return""}}function Ji(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case mn:return"Fragment";case hn:return"Portal";case Hi:return"Profiler";case Qo:return"StrictMode";case Gi:return"Suspense";case Zi:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case Hc:return(e.displayName||"Context")+".Consumer";case Kc:return(e._context.displayName||"Context")+".Provider";case qo:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case ea:return t=e.displayName||null,t!==null?t:Ji(e.type)||"Memo";case gt:t=e._payload,e=e._init;try{return Ji(e(t))}catch{}}return null}function Jh(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return Ji(t);case 8:return t===Qo?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function zt(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function Zc(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Xh(e){var t=Zc(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var s=n.get,o=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return s.call(this)},set:function(a){r=""+a,o.call(this,a)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(a){r=""+a},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Ur(e){e._valueTracker||(e._valueTracker=Xh(e))}function Jc(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=Zc(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function gs(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function Xi(e,t){var n=t.checked;return q({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function tl(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=zt(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function Xc(e,t){t=t.checked,t!=null&&Yo(e,"checked",t,!1)}function Yi(e,t){Xc(e,t);var n=zt(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?Qi(e,t.type,n):t.hasOwnProperty("defaultValue")&&Qi(e,t.type,zt(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function nl(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function Qi(e,t,n){(t!=="number"||gs(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var er=Array.isArray;function Sn(e,t,n,r){if(e=e.options,t){t={};for(var s=0;s"+t.valueOf().toString()+"",t=Wr.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function fr(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var rr={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Yh=["Webkit","ms","Moz","O"];Object.keys(rr).forEach(function(e){Yh.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),rr[t]=rr[e]})});function ed(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||rr.hasOwnProperty(e)&&rr[e]?(""+t).trim():t+"px"}function td(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,s=ed(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,s):e[n]=s}}var Qh=q({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function to(e,t){if(t){if(Qh[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(T(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(T(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(T(61))}if(t.style!=null&&typeof t.style!="object")throw Error(T(62))}}function no(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var ro=null;function ta(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var so=null,Cn=null,En=null;function il(e){if(e=Rr(e)){if(typeof so!="function")throw Error(T(280));var t=e.stateNode;t&&(t=Xs(t),so(e.stateNode,e.type,t))}}function nd(e){Cn?En?En.push(e):En=[e]:Cn=e}function rd(){if(Cn){var e=Cn,t=En;if(En=Cn=null,il(e),t)for(e=0;e>>=0,e===0?32:31-(cm(e)/dm|0)|0}var Br=64,Kr=4194304;function tr(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function ws(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,s=e.suspendedLanes,o=e.pingedLanes,a=n&268435455;if(a!==0){var l=a&~s;l!==0?r=tr(l):(o&=a,o!==0&&(r=tr(o)))}else a=n&~s,a!==0?r=tr(a):o!==0&&(r=tr(o));if(r===0)return 0;if(t!==0&&t!==r&&!(t&s)&&(s=r&-r,o=t&-t,s>=o||s===16&&(o&4194240)!==0))return t;if(r&4&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function Or(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-Je(t),e[t]=n}function pm(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=ir),pl=String.fromCharCode(32),fl=!1;function Nd(e,t){switch(e){case"keyup":return Um.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function kd(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var pn=!1;function Bm(e,t){switch(e){case"compositionend":return kd(t);case"keypress":return t.which!==32?null:(fl=!0,pl);case"textInput":return e=t.data,e===pl&&fl?null:e;default:return null}}function Km(e,t){if(pn)return e==="compositionend"||!ca&&Nd(e,t)?(e=bd(),as=oa=jt=null,pn=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=yl(n)}}function Td(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Td(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Pd(){for(var e=window,t=gs();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=gs(e.document)}return t}function da(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function ep(e){var t=Pd(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&Td(n.ownerDocument.documentElement,n)){if(r!==null&&da(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var s=n.textContent.length,o=Math.min(r.start,s);r=r.end===void 0?o:Math.min(r.end,s),!e.extend&&o>r&&(s=r,r=o,o=s),s=wl(n,o);var a=wl(n,r);s&&a&&(e.rangeCount!==1||e.anchorNode!==s.node||e.anchorOffset!==s.offset||e.focusNode!==a.node||e.focusOffset!==a.offset)&&(t=t.createRange(),t.setStart(s.node,s.offset),e.removeAllRanges(),o>r?(e.addRange(t),e.extend(a.node,a.offset)):(t.setEnd(a.node,a.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,fn=null,uo=null,ar=null,ho=!1;function bl(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;ho||fn==null||fn!==gs(r)||(r=fn,"selectionStart"in r&&da(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),ar&&br(ar,r)||(ar=r,r=Ns(uo,"onSelect"),0vn||(e.current=vo[vn],vo[vn]=null,vn--)}function H(e,t){vn++,vo[vn]=e.current,e.current=t}var Ot={},ve=Rt(Ot),Ee=Rt(!1),qt=Ot;function In(e,t){var n=e.type.contextTypes;if(!n)return Ot;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var s={},o;for(o in n)s[o]=t[o];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=s),s}function Te(e){return e=e.childContextTypes,e!=null}function Ss(){J(Ee),J(ve)}function Tl(e,t,n){if(ve.current!==Ot)throw Error(T(168));H(ve,t),H(Ee,n)}function $d(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var s in r)if(!(s in t))throw Error(T(108,Jh(e)||"Unknown",s));return q({},n,r)}function Cs(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||Ot,qt=ve.current,H(ve,e),H(Ee,Ee.current),!0}function Pl(e,t,n){var r=e.stateNode;if(!r)throw Error(T(169));n?(e=$d(e,t,qt),r.__reactInternalMemoizedMergedChildContext=e,J(Ee),J(ve),H(ve,e)):J(Ee),H(Ee,n)}var it=null,Ys=!1,Pi=!1;function Fd(e){it===null?it=[e]:it.push(e)}function hp(e){Ys=!0,Fd(e)}function $t(){if(!Pi&&it!==null){Pi=!0;var e=0,t=B;try{var n=it;for(B=1;e>=a,s-=a,ot=1<<32-Je(t)+s|n<C?(z=k,k=null):z=k.sibling;var A=f(x,k,h[C],y);if(A===null){k===null&&(k=z);break}e&&k&&A.alternate===null&&t(x,k),u=o(A,u,C),N===null?v=A:N.sibling=A,N=A,k=z}if(C===h.length)return n(x,k),X&&Ut(x,C),v;if(k===null){for(;CC?(z=k,k=null):z=k.sibling;var I=f(x,k,A.value,y);if(I===null){k===null&&(k=z);break}e&&k&&I.alternate===null&&t(x,k),u=o(I,u,C),N===null?v=I:N.sibling=I,N=I,k=z}if(A.done)return n(x,k),X&&Ut(x,C),v;if(k===null){for(;!A.done;C++,A=h.next())A=p(x,A.value,y),A!==null&&(u=o(A,u,C),N===null?v=A:N.sibling=A,N=A);return X&&Ut(x,C),v}for(k=r(x,k);!A.done;C++,A=h.next())A=g(k,x,C,A.value,y),A!==null&&(e&&A.alternate!==null&&k.delete(A.key===null?C:A.key),u=o(A,u,C),N===null?v=A:N.sibling=A,N=A);return e&&k.forEach(function(M){return t(x,M)}),X&&Ut(x,C),v}function j(x,u,h,y){if(typeof h=="object"&&h!==null&&h.type===mn&&h.key===null&&(h=h.props.children),typeof h=="object"&&h!==null){switch(h.$$typeof){case Mr:e:{for(var v=h.key,N=u;N!==null;){if(N.key===v){if(v=h.type,v===mn){if(N.tag===7){n(x,N.sibling),u=s(N,h.props.children),u.return=x,x=u;break e}}else if(N.elementType===v||typeof v=="object"&&v!==null&&v.$$typeof===gt&&Il(v)===N.type){n(x,N.sibling),u=s(N,h.props),u.ref=Gn(x,N,h),u.return=x,x=u;break e}n(x,N);break}else t(x,N);N=N.sibling}h.type===mn?(u=Qt(h.props.children,x.mode,y,h.key),u.return=x,x=u):(y=fs(h.type,h.key,h.props,null,x.mode,y),y.ref=Gn(x,u,h),y.return=x,x=y)}return a(x);case hn:e:{for(N=h.key;u!==null;){if(u.key===N)if(u.tag===4&&u.stateNode.containerInfo===h.containerInfo&&u.stateNode.implementation===h.implementation){n(x,u.sibling),u=s(u,h.children||[]),u.return=x,x=u;break e}else{n(x,u);break}else t(x,u);u=u.sibling}u=Ri(h,x.mode,y),u.return=x,x=u}return a(x);case gt:return N=h._init,j(x,u,N(h._payload),y)}if(er(h))return b(x,u,h,y);if(Un(h))return w(x,u,h,y);Qr(x,h)}return typeof h=="string"&&h!==""||typeof h=="number"?(h=""+h,u!==null&&u.tag===6?(n(x,u.sibling),u=s(u,h),u.return=x,x=u):(n(x,u),u=Li(h,x.mode,y),u.return=x,x=u),a(x)):n(x,u)}return j}var zn=Wd(!0),Bd=Wd(!1),Ps=Rt(null),_s=null,bn=null,pa=null;function fa(){pa=bn=_s=null}function ga(e){var t=Ps.current;J(Ps),e._currentValue=t}function bo(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function Pn(e,t){_s=e,pa=bn=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(Ce=!0),e.firstContext=null)}function Me(e){var t=e._currentValue;if(pa!==e)if(e={context:e,memoizedValue:t,next:null},bn===null){if(_s===null)throw Error(T(308));bn=e,_s.dependencies={lanes:0,firstContext:e}}else bn=bn.next=e;return t}var Gt=null;function xa(e){Gt===null?Gt=[e]:Gt.push(e)}function Kd(e,t,n,r){var s=t.interleaved;return s===null?(n.next=n,xa(t)):(n.next=s.next,s.next=n),t.interleaved=n,ut(e,r)}function ut(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var xt=!1;function va(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Hd(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function lt(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function Pt(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,V&2){var s=r.pending;return s===null?t.next=t:(t.next=s.next,s.next=t),r.pending=t,ut(e,n)}return s=r.interleaved,s===null?(t.next=t,xa(r)):(t.next=s.next,s.next=t),r.interleaved=t,ut(e,n)}function cs(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ra(e,n)}}function Al(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var s=null,o=null;if(n=n.firstBaseUpdate,n!==null){do{var a={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};o===null?s=o=a:o=o.next=a,n=n.next}while(n!==null);o===null?s=o=t:o=o.next=t}else s=o=t;n={baseState:r.baseState,firstBaseUpdate:s,lastBaseUpdate:o,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function Ds(e,t,n,r){var s=e.updateQueue;xt=!1;var o=s.firstBaseUpdate,a=s.lastBaseUpdate,l=s.shared.pending;if(l!==null){s.shared.pending=null;var c=l,d=c.next;c.next=null,a===null?o=d:a.next=d,a=c;var m=e.alternate;m!==null&&(m=m.updateQueue,l=m.lastBaseUpdate,l!==a&&(l===null?m.firstBaseUpdate=d:l.next=d,m.lastBaseUpdate=c))}if(o!==null){var p=s.baseState;a=0,m=d=c=null,l=o;do{var f=l.lane,g=l.eventTime;if((r&f)===f){m!==null&&(m=m.next={eventTime:g,lane:0,tag:l.tag,payload:l.payload,callback:l.callback,next:null});e:{var b=e,w=l;switch(f=t,g=n,w.tag){case 1:if(b=w.payload,typeof b=="function"){p=b.call(g,p,f);break e}p=b;break e;case 3:b.flags=b.flags&-65537|128;case 0:if(b=w.payload,f=typeof b=="function"?b.call(g,p,f):b,f==null)break e;p=q({},p,f);break e;case 2:xt=!0}}l.callback!==null&&l.lane!==0&&(e.flags|=64,f=s.effects,f===null?s.effects=[l]:f.push(l))}else g={eventTime:g,lane:f,tag:l.tag,payload:l.payload,callback:l.callback,next:null},m===null?(d=m=g,c=p):m=m.next=g,a|=f;if(l=l.next,l===null){if(l=s.shared.pending,l===null)break;f=l,l=f.next,f.next=null,s.lastBaseUpdate=f,s.shared.pending=null}}while(1);if(m===null&&(c=p),s.baseState=c,s.firstBaseUpdate=d,s.lastBaseUpdate=m,t=s.shared.interleaved,t!==null){s=t;do a|=s.lane,s=s.next;while(s!==t)}else o===null&&(s.shared.lanes=0);nn|=a,e.lanes=a,e.memoizedState=p}}function zl(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=Di.transition;Di.transition={};try{e(!1),t()}finally{B=n,Di.transition=r}}function cu(){return Ue().memoizedState}function gp(e,t,n){var r=Dt(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},du(e))uu(t,n);else if(n=Kd(e,t,n,r),n!==null){var s=be();Xe(n,e,r,s),hu(n,t,r)}}function xp(e,t,n){var r=Dt(e),s={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(du(e))uu(t,s);else{var o=e.alternate;if(e.lanes===0&&(o===null||o.lanes===0)&&(o=t.lastRenderedReducer,o!==null))try{var a=t.lastRenderedState,l=o(a,n);if(s.hasEagerState=!0,s.eagerState=l,Ye(l,a)){var c=t.interleaved;c===null?(s.next=s,xa(t)):(s.next=c.next,c.next=s),t.interleaved=s;return}}catch{}finally{}n=Kd(e,t,s,r),n!==null&&(s=be(),Xe(n,e,r,s),hu(n,t,r))}}function du(e){var t=e.alternate;return e===Q||t!==null&&t===Q}function uu(e,t){lr=As=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function hu(e,t,n){if(n&4194240){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ra(e,n)}}var zs={readContext:Me,useCallback:fe,useContext:fe,useEffect:fe,useImperativeHandle:fe,useInsertionEffect:fe,useLayoutEffect:fe,useMemo:fe,useReducer:fe,useRef:fe,useState:fe,useDebugValue:fe,useDeferredValue:fe,useTransition:fe,useMutableSource:fe,useSyncExternalStore:fe,useId:fe,unstable_isNewReconciler:!1},vp={readContext:Me,useCallback:function(e,t){return qe().memoizedState=[e,t===void 0?null:t],e},useContext:Me,useEffect:Ll,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,us(4194308,4,su.bind(null,t,e),n)},useLayoutEffect:function(e,t){return us(4194308,4,e,t)},useInsertionEffect:function(e,t){return us(4,2,e,t)},useMemo:function(e,t){var n=qe();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=qe();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=gp.bind(null,Q,e),[r.memoizedState,e]},useRef:function(e){var t=qe();return e={current:e},t.memoizedState=e},useState:Ol,useDebugValue:Ca,useDeferredValue:function(e){return qe().memoizedState=e},useTransition:function(){var e=Ol(!1),t=e[0];return e=fp.bind(null,e[1]),qe().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=Q,s=qe();if(X){if(n===void 0)throw Error(T(407));n=n()}else{if(n=t(),de===null)throw Error(T(349));tn&30||Xd(r,t,n)}s.memoizedState=n;var o={value:n,getSnapshot:t};return s.queue=o,Ll(Qd.bind(null,r,o,e),[e]),r.flags|=2048,Pr(9,Yd.bind(null,r,o,n,t),void 0,null),n},useId:function(){var e=qe(),t=de.identifierPrefix;if(X){var n=at,r=ot;n=(r&~(1<<32-Je(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=Er++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=a.createElement(n,{is:r.is}):(e=a.createElement(n),n==="select"&&(a=e,r.multiple?a.multiple=!0:r.size&&(a.size=r.size))):e=a.createElementNS(e,n),e[tt]=t,e[kr]=r,ju(e,t,!1,!1),t.stateNode=e;e:{switch(a=no(n,r),n){case"dialog":Z("cancel",e),Z("close",e),s=r;break;case"iframe":case"object":case"embed":Z("load",e),s=r;break;case"video":case"audio":for(s=0;sRn&&(t.flags|=128,r=!0,Zn(o,!1),t.lanes=4194304)}else{if(!r)if(e=Is(a),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),Zn(o,!0),o.tail===null&&o.tailMode==="hidden"&&!a.alternate&&!X)return ge(t),null}else 2*ne()-o.renderingStartTime>Rn&&n!==1073741824&&(t.flags|=128,r=!0,Zn(o,!1),t.lanes=4194304);o.isBackwards?(a.sibling=t.child,t.child=a):(n=o.last,n!==null?n.sibling=a:t.child=a,o.last=a)}return o.tail!==null?(t=o.tail,o.rendering=t,o.tail=t.sibling,o.renderingStartTime=ne(),t.sibling=null,n=Y.current,H(Y,r?n&1|2:n&1),t):(ge(t),null);case 22:case 23:return Ia(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&t.mode&1?_e&1073741824&&(ge(t),t.subtreeFlags&6&&(t.flags|=8192)):ge(t),null;case 24:return null;case 25:return null}throw Error(T(156,t.tag))}function Cp(e,t){switch(ha(t),t.tag){case 1:return Te(t.type)&&Ss(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return On(),J(Ee),J(ve),ba(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return wa(t),null;case 13:if(J(Y),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(T(340));An()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return J(Y),null;case 4:return On(),null;case 10:return ga(t.type._context),null;case 22:case 23:return Ia(),null;case 24:return null;default:return null}}var es=!1,xe=!1,Ep=typeof WeakSet=="function"?WeakSet:Set,_=null;function jn(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){ee(e,t,r)}else n.current=null}function _o(e,t,n){try{n()}catch(r){ee(e,t,r)}}var Gl=!1;function Tp(e,t){if(mo=bs,e=Pd(),da(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var s=r.anchorOffset,o=r.focusNode;r=r.focusOffset;try{n.nodeType,o.nodeType}catch{n=null;break e}var a=0,l=-1,c=-1,d=0,m=0,p=e,f=null;t:for(;;){for(var g;p!==n||s!==0&&p.nodeType!==3||(l=a+s),p!==o||r!==0&&p.nodeType!==3||(c=a+r),p.nodeType===3&&(a+=p.nodeValue.length),(g=p.firstChild)!==null;)f=p,p=g;for(;;){if(p===e)break t;if(f===n&&++d===s&&(l=a),f===o&&++m===r&&(c=a),(g=p.nextSibling)!==null)break;p=f,f=p.parentNode}p=g}n=l===-1||c===-1?null:{start:l,end:c}}else n=null}n=n||{start:0,end:0}}else n=null;for(po={focusedElem:e,selectionRange:n},bs=!1,_=t;_!==null;)if(t=_,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,_=e;else for(;_!==null;){t=_;try{var b=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(b!==null){var w=b.memoizedProps,j=b.memoizedState,x=t.stateNode,u=x.getSnapshotBeforeUpdate(t.elementType===t.type?w:Ke(t.type,w),j);x.__reactInternalSnapshotBeforeUpdate=u}break;case 3:var h=t.stateNode.containerInfo;h.nodeType===1?h.textContent="":h.nodeType===9&&h.documentElement&&h.removeChild(h.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(T(163))}}catch(y){ee(t,t.return,y)}if(e=t.sibling,e!==null){e.return=t.return,_=e;break}_=t.return}return b=Gl,Gl=!1,b}function cr(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var s=r=r.next;do{if((s.tag&e)===e){var o=s.destroy;s.destroy=void 0,o!==void 0&&_o(t,n,o)}s=s.next}while(s!==r)}}function ei(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function Do(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function Su(e){var t=e.alternate;t!==null&&(e.alternate=null,Su(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[tt],delete t[kr],delete t[xo],delete t[dp],delete t[up])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function Cu(e){return e.tag===5||e.tag===3||e.tag===4}function Zl(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||Cu(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Io(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=ks));else if(r!==4&&(e=e.child,e!==null))for(Io(e,t,n),e=e.sibling;e!==null;)Io(e,t,n),e=e.sibling}function Ao(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(Ao(e,t,n),e=e.sibling;e!==null;)Ao(e,t,n),e=e.sibling}var he=null,He=!1;function pt(e,t,n){for(n=n.child;n!==null;)Eu(e,t,n),n=n.sibling}function Eu(e,t,n){if(nt&&typeof nt.onCommitFiberUnmount=="function")try{nt.onCommitFiberUnmount(Hs,n)}catch{}switch(n.tag){case 5:xe||jn(n,t);case 6:var r=he,s=He;he=null,pt(e,t,n),he=r,He=s,he!==null&&(He?(e=he,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):he.removeChild(n.stateNode));break;case 18:he!==null&&(He?(e=he,n=n.stateNode,e.nodeType===8?Ti(e.parentNode,n):e.nodeType===1&&Ti(e,n),yr(e)):Ti(he,n.stateNode));break;case 4:r=he,s=He,he=n.stateNode.containerInfo,He=!0,pt(e,t,n),he=r,He=s;break;case 0:case 11:case 14:case 15:if(!xe&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){s=r=r.next;do{var o=s,a=o.destroy;o=o.tag,a!==void 0&&(o&2||o&4)&&_o(n,t,a),s=s.next}while(s!==r)}pt(e,t,n);break;case 1:if(!xe&&(jn(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(l){ee(n,t,l)}pt(e,t,n);break;case 21:pt(e,t,n);break;case 22:n.mode&1?(xe=(r=xe)||n.memoizedState!==null,pt(e,t,n),xe=r):pt(e,t,n);break;default:pt(e,t,n)}}function Jl(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new Ep),t.forEach(function(r){var s=Rp.bind(null,e,r);n.has(r)||(n.add(r),r.then(s,s))})}}function Be(e,t){var n=t.deletions;if(n!==null)for(var r=0;rs&&(s=a),r&=~o}if(r=s,r=ne()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*_p(r/1960))-r,10e?16:e,Nt===null)var r=!1;else{if(e=Nt,Nt=null,Rs=0,V&6)throw Error(T(331));var s=V;for(V|=4,_=e.current;_!==null;){var o=_,a=o.child;if(_.flags&16){var l=o.deletions;if(l!==null){for(var c=0;cne()-_a?Yt(e,0):Pa|=n),Pe(e,t)}function Ou(e,t){t===0&&(e.mode&1?(t=Kr,Kr<<=1,!(Kr&130023424)&&(Kr=4194304)):t=1);var n=be();e=ut(e,t),e!==null&&(Or(e,t,n),Pe(e,n))}function Lp(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),Ou(e,n)}function Rp(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,s=e.memoizedState;s!==null&&(n=s.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(T(314))}r!==null&&r.delete(t),Ou(e,n)}var Lu;Lu=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||Ee.current)Ce=!0;else{if(!(e.lanes&n)&&!(t.flags&128))return Ce=!1,kp(e,t,n);Ce=!!(e.flags&131072)}else Ce=!1,X&&t.flags&1048576&&Vd(t,Ts,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;hs(e,t),e=t.pendingProps;var s=In(t,ve.current);Pn(t,n),s=Na(null,t,r,e,s,n);var o=ka();return t.flags|=1,typeof s=="object"&&s!==null&&typeof s.render=="function"&&s.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,Te(r)?(o=!0,Cs(t)):o=!1,t.memoizedState=s.state!==null&&s.state!==void 0?s.state:null,va(t),s.updater=qs,t.stateNode=s,s._reactInternals=t,No(t,r,e,n),t=Co(null,t,r,!0,o,n)):(t.tag=0,X&&o&&ua(t),ye(null,t,s,n),t=t.child),t;case 16:r=t.elementType;e:{switch(hs(e,t),e=t.pendingProps,s=r._init,r=s(r._payload),t.type=r,s=t.tag=Fp(r),e=Ke(r,e),s){case 0:t=So(null,t,r,e,n);break e;case 1:t=Bl(null,t,r,e,n);break e;case 11:t=Ul(null,t,r,e,n);break e;case 14:t=Wl(null,t,r,Ke(r.type,e),n);break e}throw Error(T(306,r,""))}return t;case 0:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:Ke(r,s),So(e,t,r,s,n);case 1:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:Ke(r,s),Bl(e,t,r,s,n);case 3:e:{if(yu(t),e===null)throw Error(T(387));r=t.pendingProps,o=t.memoizedState,s=o.element,Hd(e,t),Ds(t,r,null,n);var a=t.memoizedState;if(r=a.element,o.isDehydrated)if(o={element:r,isDehydrated:!1,cache:a.cache,pendingSuspenseBoundaries:a.pendingSuspenseBoundaries,transitions:a.transitions},t.updateQueue.baseState=o,t.memoizedState=o,t.flags&256){s=Ln(Error(T(423)),t),t=Kl(e,t,r,n,s);break e}else if(r!==s){s=Ln(Error(T(424)),t),t=Kl(e,t,r,n,s);break e}else for(De=Tt(t.stateNode.containerInfo.firstChild),Ie=t,X=!0,Ge=null,n=Bd(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(An(),r===s){t=ht(e,t,n);break e}ye(e,t,r,n)}t=t.child}return t;case 5:return Gd(t),e===null&&wo(t),r=t.type,s=t.pendingProps,o=e!==null?e.memoizedProps:null,a=s.children,fo(r,s)?a=null:o!==null&&fo(r,o)&&(t.flags|=32),vu(e,t),ye(e,t,a,n),t.child;case 6:return e===null&&wo(t),null;case 13:return wu(e,t,n);case 4:return ya(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=zn(t,null,r,n):ye(e,t,r,n),t.child;case 11:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:Ke(r,s),Ul(e,t,r,s,n);case 7:return ye(e,t,t.pendingProps,n),t.child;case 8:return ye(e,t,t.pendingProps.children,n),t.child;case 12:return ye(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,s=t.pendingProps,o=t.memoizedProps,a=s.value,H(Ps,r._currentValue),r._currentValue=a,o!==null)if(Ye(o.value,a)){if(o.children===s.children&&!Ee.current){t=ht(e,t,n);break e}}else for(o=t.child,o!==null&&(o.return=t);o!==null;){var l=o.dependencies;if(l!==null){a=o.child;for(var c=l.firstContext;c!==null;){if(c.context===r){if(o.tag===1){c=lt(-1,n&-n),c.tag=2;var d=o.updateQueue;if(d!==null){d=d.shared;var m=d.pending;m===null?c.next=c:(c.next=m.next,m.next=c),d.pending=c}}o.lanes|=n,c=o.alternate,c!==null&&(c.lanes|=n),bo(o.return,n,t),l.lanes|=n;break}c=c.next}}else if(o.tag===10)a=o.type===t.type?null:o.child;else if(o.tag===18){if(a=o.return,a===null)throw Error(T(341));a.lanes|=n,l=a.alternate,l!==null&&(l.lanes|=n),bo(a,n,t),a=o.sibling}else a=o.child;if(a!==null)a.return=o;else for(a=o;a!==null;){if(a===t){a=null;break}if(o=a.sibling,o!==null){o.return=a.return,a=o;break}a=a.return}o=a}ye(e,t,s.children,n),t=t.child}return t;case 9:return s=t.type,r=t.pendingProps.children,Pn(t,n),s=Me(s),r=r(s),t.flags|=1,ye(e,t,r,n),t.child;case 14:return r=t.type,s=Ke(r,t.pendingProps),s=Ke(r.type,s),Wl(e,t,r,s,n);case 15:return gu(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:Ke(r,s),hs(e,t),t.tag=1,Te(r)?(e=!0,Cs(t)):e=!1,Pn(t,n),mu(t,r,s),No(t,r,s,n),Co(null,t,r,!0,e,n);case 19:return bu(e,t,n);case 22:return xu(e,t,n)}throw Error(T(156,t.tag))};function Ru(e,t){return dd(e,t)}function $p(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Fe(e,t,n,r){return new $p(e,t,n,r)}function za(e){return e=e.prototype,!(!e||!e.isReactComponent)}function Fp(e){if(typeof e=="function")return za(e)?1:0;if(e!=null){if(e=e.$$typeof,e===qo)return 11;if(e===ea)return 14}return 2}function It(e,t){var n=e.alternate;return n===null?(n=Fe(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function fs(e,t,n,r,s,o){var a=2;if(r=e,typeof e=="function")za(e)&&(a=1);else if(typeof e=="string")a=5;else e:switch(e){case mn:return Qt(n.children,s,o,t);case Qo:a=8,s|=8;break;case Hi:return e=Fe(12,n,t,s|2),e.elementType=Hi,e.lanes=o,e;case Gi:return e=Fe(13,n,t,s),e.elementType=Gi,e.lanes=o,e;case Zi:return e=Fe(19,n,t,s),e.elementType=Zi,e.lanes=o,e;case Gc:return ni(n,s,o,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case Kc:a=10;break e;case Hc:a=9;break e;case qo:a=11;break e;case ea:a=14;break e;case gt:a=16,r=null;break e}throw Error(T(130,e==null?e:typeof e,""))}return t=Fe(a,n,t,s),t.elementType=e,t.type=r,t.lanes=o,t}function Qt(e,t,n,r){return e=Fe(7,e,r,t),e.lanes=n,e}function ni(e,t,n,r){return e=Fe(22,e,r,t),e.elementType=Gc,e.lanes=n,e.stateNode={isHidden:!1},e}function Li(e,t,n){return e=Fe(6,e,null,t),e.lanes=n,e}function Ri(e,t,n){return t=Fe(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Vp(e,t,n,r,s){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=xi(0),this.expirationTimes=xi(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=xi(0),this.identifierPrefix=r,this.onRecoverableError=s,this.mutableSourceEagerHydrationData=null}function Oa(e,t,n,r,s,o,a,l,c){return e=new Vp(e,t,n,l,c),t===1?(t=1,o===!0&&(t|=8)):t=0,o=Fe(3,null,null,t),e.current=o,o.stateNode=e,o.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},va(o),e}function Mp(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(Mu)}catch(e){console.error(e)}}Mu(),Mc.exports=ze;var Hp=Mc.exports,rc=Hp;Bi.createRoot=rc.createRoot,Bi.hydrateRoot=rc.hydrateRoot;var $o=function(e,t){return $o=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(n,r){n.__proto__=r}||function(n,r){for(var s in r)Object.prototype.hasOwnProperty.call(r,s)&&(n[s]=r[s])},$o(e,t)};function Gp(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");$o(e,t);function n(){this.constructor=e}e.prototype=t===null?Object.create(t):(n.prototype=t.prototype,new n)}var le=function(){return le=Object.assign||function(t){for(var n,r=1,s=arguments.length;r0&&o[o.length-1])&&(d[0]===6||d[0]===2)){n=0;continue}if(d[0]===3&&(!o||d[1]>o[0]&&d[1]0&&b[b.length-1])||y[0]!==6&&y[0]!==2)){j=0;continue}if(y[0]===3&&(!b||y[1]>b[0]&&y[1]0?setTimeout(g,h):g(null)}}window.addEventListener("storage",x),m.addToWaiting(x);var u=setTimeout(x,Math.max(0,p-Date.now()))})];case 1:return f.sent(),[2]}})})},m.addToWaiting=function(p){this.removeFromWaiting(p),m.waiters!==void 0&&m.waiters.push(p)},m.removeFromWaiting=function(p){m.waiters!==void 0&&(m.waiters=m.waiters.filter(function(f){return f!==p}))},m.notifyWaiters=function(){m.waiters!==void 0&&m.waiters.slice().forEach(function(p){return p()})},m.prototype.releaseLock=function(p){return n(this,void 0,void 0,function(){return r(this,function(f){switch(f.label){case 0:return[4,this.releaseLock__private__(p)];case 1:return[2,f.sent()]}})})},m.prototype.releaseLock__private__=function(p){return n(this,void 0,void 0,function(){var f,g,b,w;return r(this,function(j){switch(j.label){case 0:return f=this.storageHandler===void 0?a:this.storageHandler,g=o+"-"+p,(b=f.getItemSync(g))===null?[2]:(w=JSON.parse(b)).id!==this.id?[3,2]:[4,Bt.default().lock(w.iat)];case 1:j.sent(),this.acquiredIatSet.delete(w.iat),f.removeItemSync(g),Bt.default().unlock(w.iat),m.notifyWaiters(),j.label=2;case 2:return[2]}})})},m.lockCorrector=function(p){for(var f=Date.now()-5e3,g=p,b=[],w=0;;){var j=g.keySync(w);if(j===null)break;b.push(j),w++}for(var x=!1,u=0;uDate.now();class te extends Error{constructor(t,n){super(n),this.error=t,this.error_description=n,Object.setPrototypeOf(this,te.prototype)}static fromPayload({error:t,error_description:n}){return new te(t,n)}}class Ma extends te{constructor(t,n,r,s=null){super(t,n),this.state=r,this.appState=s,Object.setPrototypeOf(this,Ma.prototype)}}class Ua extends te{constructor(t,n,r,s,o=null){super(t,n),this.connection=r,this.state=s,this.appState=o,Object.setPrototypeOf(this,Ua.prototype)}}class Dr extends te{constructor(){super("timeout","Timeout"),Object.setPrototypeOf(this,Dr.prototype)}}class Wa extends Dr{constructor(t){super(),this.popup=t,Object.setPrototypeOf(this,Wa.prototype)}}class Ba extends te{constructor(t){super("cancelled","Popup closed"),this.popup=t,Object.setPrototypeOf(this,Ba.prototype)}}class Ka extends te{constructor(t,n,r){super(t,n),this.mfa_token=r,Object.setPrototypeOf(this,Ka.prototype)}}class ai extends te{constructor(t,n){super("missing_refresh_token",`Missing Refresh Token (audience: '${Vs(t,["default"])}', scope: '${Vs(n)}')`),this.audience=t,this.scope=n,Object.setPrototypeOf(this,ai.prototype)}}class Ha extends te{constructor(t,n){super("missing_scopes",`Missing requested scopes after refresh (audience: '${Vs(t,["default"])}', missing scope: '${Vs(n)}')`),this.audience=t,this.scope=n,Object.setPrototypeOf(this,Ha.prototype)}}class li extends te{constructor(t){super("use_dpop_nonce","Server rejected DPoP proof: wrong nonce"),this.newDpopNonce=t,Object.setPrototypeOf(this,li.prototype)}}function Vs(e,t=[]){return e&&!t.includes(e)?e:""}const Ms=()=>window.crypto,Xn=()=>{const e="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_~.";let t="";return Array.from(Ms().getRandomValues(new Uint8Array(43))).forEach(n=>t+=e[n%e.length]),t},$i=e=>btoa(e),Fo=e=>{var{clientId:t}=e,n=et(e,["clientId"]);return new URLSearchParams((r=>Object.keys(r).filter(s=>r[s]!==void 0).reduce((s,o)=>Object.assign(Object.assign({},s),{[o]:r[o]}),{}))(Object.assign({client_id:t},n))).toString()},ic=async e=>await Ms().subtle.digest({name:"SHA-256"},new TextEncoder().encode(e)),oc=e=>(t=>decodeURIComponent(atob(t).split("").map(n=>"%"+("00"+n.charCodeAt(0).toString(16)).slice(-2)).join("")))(e.replace(/_/g,"/").replace(/-/g,"+")),ac=e=>{const t=new Uint8Array(e);return(n=>{const r={"+":"-","/":"_","=":""};return n.replace(/[+/=]/g,s=>r[s])})(window.btoa(String.fromCharCode(...Array.from(t))))},Yp=new TextEncoder,Qp=new TextDecoder;function hr(e){return typeof e=="string"?Yp.encode(e):Qp.decode(e)}function lc(e){if(typeof e.modulusLength!="number"||e.modulusLength<2048)throw new ef(`${e.name} modulusLength must be at least 2048 bits`)}async function qp(e,t,n){if(n.usages.includes("sign")===!1)throw new TypeError('private CryptoKey instances used for signing assertions must include "sign" in their "usages"');const r=`${mr(hr(JSON.stringify(e)))}.${mr(hr(JSON.stringify(t)))}`;return`${r}.${mr(await crypto.subtle.sign(function(s){switch(s.algorithm.name){case"ECDSA":return{name:s.algorithm.name,hash:"SHA-256"};case"RSA-PSS":return lc(s.algorithm),{name:s.algorithm.name,saltLength:32};case"RSASSA-PKCS1-v1_5":return lc(s.algorithm),{name:s.algorithm.name};case"Ed25519":return{name:s.algorithm.name}}throw new Jt}(n),n,hr(r)))}`}let Vo;Uint8Array.prototype.toBase64?Vo=e=>(e instanceof ArrayBuffer&&(e=new Uint8Array(e)),e.toBase64({alphabet:"base64url",omitPadding:!0})):Vo=t=>{t instanceof ArrayBuffer&&(t=new Uint8Array(t));const n=[];for(let r=0;r{const n=await fetch(e,t);return{ok:n.ok,json:await n.json(),headers:(r=n.headers,[...r].reduce((s,[o,a])=>(s[o]=a,s),{}))};var r},cf=async(e,t,n)=>{const r=new AbortController;let s;return t.signal=r.signal,Promise.race([lf(e,t),new Promise((o,a)=>{s=setTimeout(()=>{r.abort(),a(new Error("Timeout when executing 'fetch'"))},n)})]).finally(()=>{clearTimeout(s)})},df=async(e,t,n,r,s,o,a,l)=>{return c={auth:{audience:t,scope:n},timeout:s,fetchUrl:e,fetchOptions:r,useFormData:a,useMrrt:l},d=o,new Promise(function(m,p){const f=new MessageChannel;f.port1.onmessage=function(g){g.data.error?p(new Error(g.data.error)):m(g.data),f.port1.close()},d.postMessage(c,[f.port2])});var c,d},uf=async(e,t,n,r,s,o,a=1e4,l)=>s?df(e,t,n,r,a,s,o,l):cf(e,r,a);async function Gu(e,t,n,r,s,o,a,l,c,d){if(c){const h=await c.generateProof({url:e,method:s.method||"GET",nonce:await c.getNonce()});s.headers=Object.assign(Object.assign({},s.headers),{dpop:h})}let m,p=null;for(let h=0;h<3;h++)try{m=await uf(e,n,r,s,o,a,t,l),p=null;break}catch(y){p=y}if(p)throw p;const f=m.json,{error:g,error_description:b}=f,w=et(f,["error","error_description"]),{headers:j,ok:x}=m;let u;if(c&&(u=j["dpop-nonce"],u&&await c.setNonce(u)),!x){const h=b||`HTTP error. Unable to fetch ${e}`;if(g==="mfa_required")throw new Ka(g,h,w.mfa_token);if(g==="missing_refresh_token")throw new ai(n,r);if(g==="use_dpop_nonce"){if(!c||!u||d)throw new li(u);return Gu(e,t,n,r,s,o,a,l,c,!0)}throw new te(g||"request_error",h)}return w}async function hf(e,t){var{baseUrl:n,timeout:r,audience:s,scope:o,auth0Client:a,useFormData:l,useMrrt:c,dpop:d}=e,m=et(e,["baseUrl","timeout","audience","scope","auth0Client","useFormData","useMrrt","dpop"]);const p=m.grant_type==="urn:ietf:params:oauth:grant-type:token-exchange",f=m.grant_type==="refresh_token"&&c,g=Object.assign(Object.assign(Object.assign(Object.assign({},m),p&&s&&{audience:s}),p&&o&&{scope:o}),f&&{audience:s,scope:o}),b=l?Fo(g):JSON.stringify(g),w=(j=m.grant_type,rf.includes(j));var j;return await Gu(`${n}/oauth/token`,r,s||"default",o,{method:"POST",body:b,headers:{"Content-Type":l?"application/x-www-form-urlencoded":"application/json","Auth0-Client":btoa(JSON.stringify(a||Uu))}},t,l,c,w?d:void 0)}const Yn=(...e)=>{return(t=e.filter(Boolean).join(" ").trim().split(/\s+/),Array.from(new Set(t))).join(" ");var t};class Ze{constructor(t,n="@@auth0spajs@@",r){this.prefix=n,this.suffix=r,this.clientId=t.clientId,this.scope=t.scope,this.audience=t.audience}toKey(){return[this.prefix,this.clientId,this.audience,this.scope,this.suffix].filter(Boolean).join("::")}static fromKey(t){const[n,r,s,o]=t.split("::");return new Ze({clientId:r,scope:o,audience:s},n)}static fromCacheEntry(t){const{scope:n,audience:r,client_id:s}=t;return new Ze({scope:n,audience:r,clientId:s})}}class mf{set(t,n){localStorage.setItem(t,JSON.stringify(n))}get(t){const n=window.localStorage.getItem(t);if(n)try{return JSON.parse(n)}catch{return}}remove(t){localStorage.removeItem(t)}allKeys(){return Object.keys(window.localStorage).filter(t=>t.startsWith("@@auth0spajs@@"))}}class Zu{constructor(){this.enclosedCache=function(){let t={};return{set(n,r){t[n]=r},get(n){const r=t[n];if(r)return r},remove(n){delete t[n]},allKeys:()=>Object.keys(t)}}()}}class pf{constructor(t,n,r){this.cache=t,this.keyManifest=n,this.nowProvider=r||Wu}async setIdToken(t,n,r){var s;const o=this.getIdTokenCacheKey(t);await this.cache.set(o,{id_token:n,decodedToken:r}),await((s=this.keyManifest)===null||s===void 0?void 0:s.add(o))}async getIdToken(t){const n=await this.cache.get(this.getIdTokenCacheKey(t.clientId));if(!n&&t.scope&&t.audience){const r=await this.get(t);return!r||!r.id_token||!r.decodedToken?void 0:{id_token:r.id_token,decodedToken:r.decodedToken}}if(n)return{id_token:n.id_token,decodedToken:n.decodedToken}}async get(t,n=0,r=!1,s){var o;let a=await this.cache.get(t.toKey());if(!a){const d=await this.getCacheKeys();if(!d)return;const m=this.matchExistingCacheKey(t,d);if(m&&(a=await this.cache.get(m)),!m&&r&&s!=="cache-only")return this.getEntryWithRefreshToken(t,d)}if(!a)return;const l=await this.nowProvider(),c=Math.floor(l/1e3);return a.expiresAt-n!t||s.includes(t)).reduce(async(s,o)=>{await s,await this.cache.remove(o)},Promise.resolve()),await((n=this.keyManifest)===null||n===void 0?void 0:n.clear()))}async wrapCacheEntry(t){const n=await this.nowProvider();return{body:t,expiresAt:Math.floor(n/1e3)+t.expires_in}}async getCacheKeys(){var t;return this.keyManifest?(t=await this.keyManifest.get())===null||t===void 0?void 0:t.keys:this.cache.allKeys?this.cache.allKeys():void 0}getIdTokenCacheKey(t){return new Ze({clientId:t},"@@auth0spajs@@","@@user@@").toKey()}matchExistingCacheKey(t,n){return n.filter(r=>{var s;const o=Ze.fromKey(r),a=new Set(o.scope&&o.scope.split(" ")),l=((s=t.scope)===null||s===void 0?void 0:s.split(" "))||[],c=o.scope&&l.reduce((d,m)=>d&&a.has(m),!0);return o.prefix==="@@auth0spajs@@"&&o.clientId===t.clientId&&o.audience===t.audience&&c})[0]}async getEntryWithRefreshToken(t,n){var r;for(const s of n){const o=Ze.fromKey(s);if(o.prefix==="@@auth0spajs@@"&&o.clientId===t.clientId){const a=await this.cache.get(s);if(!((r=a==null?void 0:a.body)===null||r===void 0)&&r.refresh_token)return this.modifiedCachedEntry(a,t)}}}async updateEntry(t,n){var r;const s=await this.getCacheKeys();if(s)for(const o of s){const a=await this.cache.get(o);if(((r=a==null?void 0:a.body)===null||r===void 0?void 0:r.refresh_token)===t){const l=Object.assign(Object.assign({},a.body),{refresh_token:n});await this.set(l)}}}}class ff{constructor(t,n,r){this.storage=t,this.clientId=n,this.cookieDomain=r,this.storageKey=`a0.spajs.txs.${this.clientId}`}create(t){this.storage.save(this.storageKey,t,{daysUntilExpire:1,cookieDomain:this.cookieDomain})}get(){return this.storage.get(this.storageKey)}remove(){this.storage.remove(this.storageKey,{cookieDomain:this.cookieDomain})}}const Qn=e=>typeof e=="number",gf=["iss","aud","exp","nbf","iat","jti","azp","nonce","auth_time","at_hash","c_hash","acr","amr","sub_jwk","cnf","sip_from_tag","sip_date","sip_callid","sip_cseq_num","sip_via_branch","orig","dest","mky","events","toe","txn","rph","sid","vot","vtm"],xf=e=>{if(!e.id_token)throw new Error("ID token is required but missing");const t=(o=>{const a=o.split("."),[l,c,d]=a;if(a.length!==3||!l||!c||!d)throw new Error("ID token could not be decoded");const m=JSON.parse(oc(c)),p={__raw:o},f={};return Object.keys(m).forEach(g=>{p[g]=m[g],gf.includes(g)||(f[g]=m[g])}),{encoded:{header:l,payload:c,signature:d},header:JSON.parse(oc(l)),claims:p,user:f}})(e.id_token);if(!t.claims.iss)throw new Error("Issuer (iss) claim must be a string present in the ID token");if(t.claims.iss!==e.iss)throw new Error(`Issuer (iss) claim mismatch in the ID token; expected "${e.iss}", found "${t.claims.iss}"`);if(!t.user.sub)throw new Error("Subject (sub) claim must be a string present in the ID token");if(t.header.alg!=="RS256")throw new Error(`Signature algorithm of "${t.header.alg}" is not supported. Expected the ID token to be signed with "RS256".`);if(!t.claims.aud||typeof t.claims.aud!="string"&&!Array.isArray(t.claims.aud))throw new Error("Audience (aud) claim must be a string or array of strings present in the ID token");if(Array.isArray(t.claims.aud)){if(!t.claims.aud.includes(e.aud))throw new Error(`Audience (aud) claim mismatch in the ID token; expected "${e.aud}" but was not one of "${t.claims.aud.join(", ")}"`);if(t.claims.aud.length>1){if(!t.claims.azp)throw new Error("Authorized Party (azp) claim must be a string present in the ID token when Audience (aud) claim has multiple values");if(t.claims.azp!==e.aud)throw new Error(`Authorized Party (azp) claim mismatch in the ID token; expected "${e.aud}", found "${t.claims.azp}"`)}}else if(t.claims.aud!==e.aud)throw new Error(`Audience (aud) claim mismatch in the ID token; expected "${e.aud}" but found "${t.claims.aud}"`);if(e.nonce){if(!t.claims.nonce)throw new Error("Nonce (nonce) claim must be a string present in the ID token");if(t.claims.nonce!==e.nonce)throw new Error(`Nonce (nonce) claim mismatch in the ID token; expected "${e.nonce}", found "${t.claims.nonce}"`)}if(e.max_age&&!Qn(t.claims.auth_time))throw new Error("Authentication Time (auth_time) claim must be a number present in the ID token when Max Age (max_age) is specified");if(t.claims.exp==null||!Qn(t.claims.exp))throw new Error("Expiration Time (exp) claim must be a number present in the ID token");if(!Qn(t.claims.iat))throw new Error("Issued At (iat) claim must be a number present in the ID token");const n=e.leeway||60,r=new Date(e.now||Date.now()),s=new Date(0);if(s.setUTCSeconds(t.claims.exp+n),r>s)throw new Error(`Expiration Time (exp) claim error in the ID token; current time (${r}) is after expiration time (${s})`);if(t.claims.nbf!=null&&Qn(t.claims.nbf)){const o=new Date(0);if(o.setUTCSeconds(t.claims.nbf-n),ro)throw new Error(`Authentication Time (auth_time) claim in the ID token indicates that too much time has passed since the last end-user authentication. Current time (${r}) is after last auth at ${o}`)}if(e.organization){const o=e.organization.trim();if(o.startsWith("org_")){const a=o;if(!t.claims.org_id)throw new Error("Organization ID (org_id) claim must be a string present in the ID token");if(a!==t.claims.org_id)throw new Error(`Organization ID (org_id) claim mismatch in the ID token; expected "${a}", found "${t.claims.org_id}"`)}else{const a=o.toLowerCase();if(!t.claims.org_name)throw new Error("Organization Name (org_name) claim must be a string present in the ID token");if(a!==t.claims.org_name)throw new Error(`Organization Name (org_name) claim mismatch in the ID token; expected "${a}", found "${t.claims.org_name}"`)}}return t};var Xt=Va(function(e,t){var n=Kt&&Kt.__assign||function(){return n=Object.assign||function(c){for(var d,m=1,p=arguments.length;m"u")return;const t=sessionStorage.getItem(e);return t!=null?JSON.parse(t):void 0},save(e,t){sessionStorage.setItem(e,JSON.stringify(t))},remove(e){sessionStorage.removeItem(e)}};var bt;(function(e){e.Code="code",e.ConnectCode="connect_code"})(bt||(bt={}));function bf(e,t,n){var r=t===void 0?null:t,s=function(c,d){var m=atob(c);if(d){for(var p=new Uint8Array(m.length),f=0,g=m.length;f0?await this.cache.set(this.manifestKey,{keys:[...r]}):await this.cache.remove(this.manifestKey)}}get(){return this.cache.get(this.manifestKey)}clear(){return this.cache.remove(this.manifestKey)}createManifestKeyFrom(t){return`@@auth0spajs@@::${t}`}}const kf={memory:()=>new Zu().enclosedCache,localstorage:()=>new mf},hc=e=>kf[e],mc=e=>{const{openUrl:t,onRedirect:n}=e,r=et(e,["openUrl","onRedirect"]);return Object.assign(Object.assign({},r),{openUrl:t===!1||t?t:n})},pc=(e,t)=>{const n=(t==null?void 0:t.split(" "))||[];return((e==null?void 0:e.split(" "))||[]).every(r=>n.includes(r))},Mt={NONCE:"nonce",KEYPAIR:"keypair"};class Sf{constructor(t){this.clientId=t}getVersion(){return 1}createDbHandle(){const t=window.indexedDB.open("auth0-spa-js",this.getVersion());return new Promise((n,r)=>{t.onupgradeneeded=()=>Object.values(Mt).forEach(s=>t.result.createObjectStore(s)),t.onerror=()=>r(t.error),t.onsuccess=()=>n(t.result)})}async getDbHandle(){return this.dbHandle||(this.dbHandle=await this.createDbHandle()),this.dbHandle}async executeDbRequest(t,n,r){const s=r((await this.getDbHandle()).transaction(t,n).objectStore(t));return new Promise((o,a)=>{s.onsuccess=()=>o(s.result),s.onerror=()=>a(s.error)})}buildKey(t){const n=t?`_${t}`:"auth0";return`${this.clientId}::${n}`}setNonce(t,n){return this.save(Mt.NONCE,this.buildKey(n),t)}setKeyPair(t){return this.save(Mt.KEYPAIR,this.buildKey(),t)}async save(t,n,r){await this.executeDbRequest(t,"readwrite",s=>s.put(r,n))}findNonce(t){return this.find(Mt.NONCE,this.buildKey(t))}findKeyPair(){return this.find(Mt.KEYPAIR,this.buildKey())}find(t,n){return this.executeDbRequest(t,"readonly",r=>r.get(n))}async deleteBy(t,n){const r=await this.executeDbRequest(t,"readonly",s=>s.getAllKeys());r==null||r.filter(n).map(s=>this.executeDbRequest(t,"readwrite",o=>o.delete(s)))}deleteByClientId(t,n){return this.deleteBy(t,r=>typeof r=="string"&&r.startsWith(`${n}::`))}clearNonces(){return this.deleteByClientId(Mt.NONCE,this.clientId)}clearKeyPairs(){return this.deleteByClientId(Mt.KEYPAIR,this.clientId)}}class Cf{constructor(t){this.storage=new Sf(t)}getNonce(t){return this.storage.findNonce(t)}setNonce(t,n){return this.storage.setNonce(t,n)}async getOrGenerateKeyPair(){let t=await this.storage.findKeyPair();return t||(t=await sf(),await this.storage.setKeyPair(t)),t}async generateProof(t){const n=await this.getOrGenerateKeyPair();return af(Object.assign({keyPair:n},t))}async calculateThumbprint(){return of(await this.getOrGenerateKeyPair())}async clear(){await Promise.all([this.storage.clearNonces(),this.storage.clearKeyPairs()])}}var kn;(function(e){e.Bearer="Bearer",e.DPoP="DPoP"})(kn||(kn={}));class Ef{constructor(t,n){this.hooks=n,this.config=Object.assign(Object.assign({},t),{fetch:t.fetch||(typeof window>"u"?fetch:window.fetch.bind(window))})}isAbsoluteUrl(t){return/^(https?:)?\/\//i.test(t)}buildUrl(t,n){if(n){if(this.isAbsoluteUrl(n))return n;if(t)return`${t.replace(/\/?\/$/,"")}/${n.replace(/^\/+/,"")}`}throw new TypeError("`url` must be absolute or `baseUrl` non-empty.")}getAccessToken(t){return this.config.getAccessToken?this.config.getAccessToken(t):this.hooks.getAccessToken(t)}extractUrl(t){return typeof t=="string"?t:t instanceof URL?t.href:t.url}buildBaseRequest(t,n){if(!this.config.baseUrl)return new Request(t,n);const r=this.buildUrl(this.config.baseUrl,this.extractUrl(t)),s=t instanceof Request?new Request(r,t):r;return new Request(s,n)}setAuthorizationHeader(t,n,r=kn.Bearer){t.headers.set("authorization",`${r} ${n}`)}async setDpopProofHeader(t,n){if(!this.config.dpopNonceId)return;const r=await this.hooks.getDpopNonce(),s=await this.hooks.generateDpopProof({accessToken:n,method:t.method,nonce:r,url:t.url});t.headers.set("dpop",s)}async prepareRequest(t,n){const r=await this.getAccessToken(n);let s,o;typeof r=="string"?(s=this.config.dpopNonceId?kn.DPoP:kn.Bearer,o=r):(s=r.token_type,o=r.access_token),this.setAuthorizationHeader(t,o,s),s===kn.DPoP&&await this.setDpopProofHeader(t,o)}getHeader(t,n){return Array.isArray(t)?new Headers(t).get(n)||"":typeof t.get=="function"?t.get(n)||"":t[n]||""}hasUseDpopNonceError(t){if(t.status!==401)return!1;const n=this.getHeader(t.headers,"www-authenticate");return n.includes("invalid_dpop_nonce")||n.includes("use_dpop_nonce")}async handleResponse(t,n){const r=this.getHeader(t.headers,"dpop-nonce");if(r&&await this.hooks.setDpopNonce(r),!this.hasUseDpopNonceError(t))return t;if(!r||!n.onUseDpopNonceError)throw new li(r);return n.onUseDpopNonceError()}async internalFetchWithAuth(t,n,r,s){const o=this.buildBaseRequest(t,n);await this.prepareRequest(o,s);const a=await this.config.fetch(o);return this.handleResponse(a,r)}fetchWithAuth(t,n,r){const s={onUseDpopNonceError:()=>this.internalFetchWithAuth(t,n,Object.assign(Object.assign({},s),{onUseDpopNonceError:void 0}),r)};return this.internalFetchWithAuth(t,n,s,r)}}class Tf{constructor(t,n){this.myAccountFetcher=t,this.apiBase=n}async connectAccount(t){const n=await this.myAccountFetcher.fetchWithAuth(`${this.apiBase}v1/connected-accounts/connect`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)});return this._handleResponse(n)}async completeAccount(t){const n=await this.myAccountFetcher.fetchWithAuth(`${this.apiBase}v1/connected-accounts/complete`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)});return this._handleResponse(n)}async _handleResponse(t){let n;try{n=await t.text(),n=JSON.parse(n)}catch(r){throw new Us({type:"invalid_json",status:t.status,title:"Invalid JSON response",detail:n||String(r)})}if(t.ok)return n;throw new Us(n)}}class Us extends Error{constructor({type:t,status:n,title:r,detail:s,validation_errors:o}){super(s),this.name="MyAccountApiError",this.type=t,this.status=n,this.title=r,this.detail=s,this.validation_errors=o,Object.setPrototypeOf(this,Us.prototype)}}const Mi=new Jp;class Pf{constructor(t){let n,r;if(this.userCache=new Zu().enclosedCache,this.defaultOptions={authorizationParams:{scope:"openid profile email"},useRefreshTokensFallback:!1,useFormData:!0},this._releaseLockOnPageHide=async()=>{await Mi.releaseLock("auth0.lock.getTokenSilently"),window.removeEventListener("pagehide",this._releaseLockOnPageHide)},this.options=Object.assign(Object.assign(Object.assign({},this.defaultOptions),t),{authorizationParams:Object.assign(Object.assign({},this.defaultOptions.authorizationParams),t.authorizationParams)}),typeof window<"u"&&(()=>{if(!Ms())throw new Error("For security reasons, `window.crypto` is required to run `auth0-spa-js`.");if(Ms().subtle===void 0)throw new Error(` - auth0-spa-js must run on a secure origin. See https://github.com/auth0/auth0-spa-js/blob/main/FAQ.md#why-do-i-get-auth0-spa-js-must-run-on-a-secure-origin for more information. - `)})(),t.cache&&t.cacheLocation&&console.warn("Both `cache` and `cacheLocation` options have been specified in the Auth0Client configuration; ignoring `cacheLocation` and using `cache`."),t.cache)r=t.cache;else{if(n=t.cacheLocation||"memory",!hc(n))throw new Error(`Invalid cache location "${n}"`);r=hc(n)()}this.httpTimeoutMs=t.httpTimeoutInSeconds?1e3*t.httpTimeoutInSeconds:1e4,this.cookieStorage=t.legacySameSiteCookie===!1?un:yf,this.orgHintCookieName=`auth0.${this.options.clientId}.organization_hint`,this.isAuthenticatedCookieName=(c=>`auth0.${c}.is.authenticated`)(this.options.clientId),this.sessionCheckExpiryDays=t.sessionCheckExpiryDays||1;const s=t.useCookiesForTransactions?this.cookieStorage:wf;var o;this.scope=Yn("openid",this.options.authorizationParams.scope,this.options.useRefreshTokens?"offline_access":""),this.transactionManager=new ff(s,this.options.clientId,this.options.cookieDomain),this.nowProvider=this.options.nowProvider||Wu,this.cacheManager=new pf(r,r.allKeys?void 0:new Nf(r,this.options.clientId),this.nowProvider),this.dpop=this.options.useDpop?new Cf(this.options.clientId):void 0,this.domainUrl=(o=this.options.domain,/^https?:\/\//.test(o)?o:`https://${o}`),this.tokenIssuer=((c,d)=>c?c.startsWith("https://")?c:`https://${c}/`:`${d}/`)(this.options.issuer,this.domainUrl);const a=`${this.domainUrl}/me/`,l=this.createFetcher(Object.assign(Object.assign({},this.options.useDpop&&{dpopNonceId:"__auth0_my_account_api__"}),{getAccessToken:()=>this.getTokenSilently({authorizationParams:{scope:"create:me:connected_accounts",audience:a},detailedResponse:!0})}));this.myAccountApi=new Tf(l,a),typeof window<"u"&&window.Worker&&this.options.useRefreshTokens&&n==="memory"&&(this.options.workerUrl?this.worker=new Worker(this.options.workerUrl):this.worker=new jf)}_url(t){const n=encodeURIComponent(btoa(JSON.stringify(this.options.auth0Client||Uu)));return`${this.domainUrl}${t}&auth0Client=${n}`}_authorizeUrl(t){return this._url(`/authorize?${Fo(t)}`)}async _verifyIdToken(t,n,r){const s=await this.nowProvider();return xf({iss:this.tokenIssuer,aud:this.options.clientId,id_token:t,nonce:n,organization:r,leeway:this.options.leeway,max_age:(o=this.options.authorizationParams.max_age,typeof o!="string"?o:parseInt(o,10)||void 0),now:s});var o}_processOrgHint(t){t?this.cookieStorage.save(this.orgHintCookieName,t,{daysUntilExpire:this.sessionCheckExpiryDays,cookieDomain:this.options.cookieDomain}):this.cookieStorage.remove(this.orgHintCookieName,{cookieDomain:this.options.cookieDomain})}async _prepareAuthorizeUrl(t,n,r){var s;const o=$i(Xn()),a=$i(Xn()),l=Xn(),c=await ic(l),d=ac(c),m=await((s=this.dpop)===null||s===void 0?void 0:s.calculateThumbprint()),p=((g,b,w,j,x,u,h,y,v)=>Object.assign(Object.assign(Object.assign({client_id:g.clientId},g.authorizationParams),w),{scope:Yn(b,w.scope),response_type:"code",response_mode:y||"query",state:j,nonce:x,redirect_uri:h||g.authorizationParams.redirect_uri,code_challenge:u,code_challenge_method:"S256",dpop_jkt:v}))(this.options,this.scope,t,o,a,d,t.redirect_uri||this.options.authorizationParams.redirect_uri||r,n==null?void 0:n.response_mode,m),f=this._authorizeUrl(p);return{nonce:a,code_verifier:l,scope:p.scope,audience:p.audience||"default",redirect_uri:p.redirect_uri,state:o,url:f}}async loginWithPopup(t,n){var r;if(t=t||{},!(n=n||{}).popup&&(n.popup=(l=>{const c=window.screenX+(window.innerWidth-400)/2,d=window.screenY+(window.innerHeight-600)/2;return window.open(l,"auth0:authorize:popup",`left=${c},top=${d},width=400,height=600,resizable,scrollbars=yes,status=1`)})(""),!n.popup))throw new Error("Unable to open a popup for loginWithPopup - window.open returned `null`");const s=await this._prepareAuthorizeUrl(t.authorizationParams||{},{response_mode:"web_message"},window.location.origin);n.popup.location.href=s.url;const o=await(l=>new Promise((c,d)=>{let m;const p=setInterval(()=>{l.popup&&l.popup.closed&&(clearInterval(p),clearTimeout(f),window.removeEventListener("message",m,!1),d(new Ba(l.popup)))},1e3),f=setTimeout(()=>{clearInterval(p),d(new Wa(l.popup)),window.removeEventListener("message",m,!1)},1e3*(l.timeoutInSeconds||60));m=function(g){if(g.data&&g.data.type==="authorization_response"){if(clearTimeout(f),clearInterval(p),window.removeEventListener("message",m,!1),l.popup.close(),g.data.response.error)return d(te.fromPayload(g.data.response));c(g.data.response)}},window.addEventListener("message",m)}))(Object.assign(Object.assign({},n),{timeoutInSeconds:n.timeoutInSeconds||this.options.authorizeTimeoutInSeconds||60}));if(s.state!==o.state)throw new te("state_mismatch","Invalid state");const a=((r=t.authorizationParams)===null||r===void 0?void 0:r.organization)||this.options.authorizationParams.organization;await this._requestToken({audience:s.audience,scope:s.scope,code_verifier:s.code_verifier,grant_type:"authorization_code",code:o.code,redirect_uri:s.redirect_uri},{nonceIn:s.nonce,organization:a})}async getUser(){var t;const n=await this._getIdTokenFromCache();return(t=n==null?void 0:n.decodedToken)===null||t===void 0?void 0:t.user}async getIdTokenClaims(){var t;const n=await this._getIdTokenFromCache();return(t=n==null?void 0:n.decodedToken)===null||t===void 0?void 0:t.claims}async loginWithRedirect(t={}){var n;const r=mc(t),{openUrl:s,fragment:o,appState:a}=r,l=et(r,["openUrl","fragment","appState"]),c=((n=l.authorizationParams)===null||n===void 0?void 0:n.organization)||this.options.authorizationParams.organization,d=await this._prepareAuthorizeUrl(l.authorizationParams||{}),{url:m}=d,p=et(d,["url"]);this.transactionManager.create(Object.assign(Object.assign(Object.assign({},p),{appState:a,response_type:bt.Code}),c&&{organization:c}));const f=o?`${m}#${o}`:m;s?await s(f):window.location.assign(f)}async handleRedirectCallback(t=window.location.href){const n=t.split("?").slice(1);if(n.length===0)throw new Error("There are no query params available for parsing.");const r=this.transactionManager.get();if(!r)throw new te("missing_transaction","Invalid state");this.transactionManager.remove();const s=(o=>{o.indexOf("#")>-1&&(o=o.substring(0,o.indexOf("#")));const a=new URLSearchParams(o);return{state:a.get("state"),code:a.get("code")||void 0,connect_code:a.get("connect_code")||void 0,error:a.get("error")||void 0,error_description:a.get("error_description")||void 0}})(n.join(""));return r.response_type===bt.ConnectCode?this._handleConnectAccountRedirectCallback(s,r):this._handleLoginRedirectCallback(s,r)}async _handleLoginRedirectCallback(t,n){const{code:r,state:s,error:o,error_description:a}=t;if(o)throw new Ma(o,a||o,s,n.appState);if(!n.code_verifier||n.state&&n.state!==s)throw new te("state_mismatch","Invalid state");const l=n.organization,c=n.nonce,d=n.redirect_uri;return await this._requestToken(Object.assign({audience:n.audience,scope:n.scope,code_verifier:n.code_verifier,grant_type:"authorization_code",code:r},d?{redirect_uri:d}:{}),{nonceIn:c,organization:l}),{appState:n.appState,response_type:bt.Code}}async _handleConnectAccountRedirectCallback(t,n){const{connect_code:r,state:s,error:o,error_description:a}=t;if(o)throw new Ua(o,a||o,n.connection,s,n.appState);if(!r)throw new te("missing_connect_code","Missing connect code");if(!(n.code_verifier&&n.state&&n.auth_session&&n.redirect_uri&&n.state===s))throw new te("state_mismatch","Invalid state");const l=await this.myAccountApi.completeAccount({auth_session:n.auth_session,connect_code:r,redirect_uri:n.redirect_uri,code_verifier:n.code_verifier});return Object.assign(Object.assign({},l),{appState:n.appState,response_type:bt.ConnectCode})}async checkSession(t){if(!this.cookieStorage.get(this.isAuthenticatedCookieName)){if(!this.cookieStorage.get("auth0.is.authenticated"))return;this.cookieStorage.save(this.isAuthenticatedCookieName,!0,{daysUntilExpire:this.sessionCheckExpiryDays,cookieDomain:this.options.cookieDomain}),this.cookieStorage.remove("auth0.is.authenticated")}try{await this.getTokenSilently(t)}catch{}}async getTokenSilently(t={}){var n;const r=Object.assign(Object.assign({cacheMode:"on"},t),{authorizationParams:Object.assign(Object.assign(Object.assign({},this.options.authorizationParams),t.authorizationParams),{scope:Yn(this.scope,(n=t.authorizationParams)===null||n===void 0?void 0:n.scope)})}),s=await((o,a)=>{let l=Vi[a];return l||(l=o().finally(()=>{delete Vi[a],l=null}),Vi[a]=l),l})(()=>this._getTokenSilently(r),`${this.options.clientId}::${r.authorizationParams.audience}::${r.authorizationParams.scope}`);return t.detailedResponse?s:s==null?void 0:s.access_token}async _getTokenSilently(t){const{cacheMode:n}=t,r=et(t,["cacheMode"]);if(n!=="off"){const s=await this._getEntryFromCache({scope:r.authorizationParams.scope,audience:r.authorizationParams.audience||"default",clientId:this.options.clientId,cacheMode:n});if(s)return s}if(n!=="cache-only"){if(!await(async(s,o=3)=>{for(let a=0;aMi.acquireLock("auth0.lock.getTokenSilently",5e3),10))throw new Dr;try{if(window.addEventListener("pagehide",this._releaseLockOnPageHide),n!=="off"){const m=await this._getEntryFromCache({scope:r.authorizationParams.scope,audience:r.authorizationParams.audience||"default",clientId:this.options.clientId});if(m)return m}const s=this.options.useRefreshTokens?await this._getTokenUsingRefreshToken(r):await this._getTokenFromIFrame(r),{id_token:o,token_type:a,access_token:l,oauthTokenScope:c,expires_in:d}=s;return Object.assign(Object.assign({id_token:o,token_type:a,access_token:l},c?{scope:c}:null),{expires_in:d})}finally{await Mi.releaseLock("auth0.lock.getTokenSilently"),window.removeEventListener("pagehide",this._releaseLockOnPageHide)}}}async getTokenWithPopup(t={},n={}){var r;const s=Object.assign(Object.assign({},t),{authorizationParams:Object.assign(Object.assign(Object.assign({},this.options.authorizationParams),t.authorizationParams),{scope:Yn(this.scope,(r=t.authorizationParams)===null||r===void 0?void 0:r.scope)})});return n=Object.assign(Object.assign({},Xp),n),await this.loginWithPopup(s,n),(await this.cacheManager.get(new Ze({scope:s.authorizationParams.scope,audience:s.authorizationParams.audience||"default",clientId:this.options.clientId}),void 0,this.options.useMrrt)).access_token}async isAuthenticated(){return!!await this.getUser()}_buildLogoutUrl(t){t.clientId!==null?t.clientId=t.clientId||this.options.clientId:delete t.clientId;const n=t.logoutParams||{},{federated:r}=n,s=et(n,["federated"]),o=r?"&federated":"";return this._url(`/v2/logout?${Fo(Object.assign({clientId:t.clientId},s))}`)+o}async logout(t={}){var n;const r=mc(t),{openUrl:s}=r,o=et(r,["openUrl"]);t.clientId===null?await this.cacheManager.clear():await this.cacheManager.clear(t.clientId||this.options.clientId),this.cookieStorage.remove(this.orgHintCookieName,{cookieDomain:this.options.cookieDomain}),this.cookieStorage.remove(this.isAuthenticatedCookieName,{cookieDomain:this.options.cookieDomain}),this.userCache.remove("@@user@@"),await((n=this.dpop)===null||n===void 0?void 0:n.clear());const a=this._buildLogoutUrl(o);s?await s(a):s!==!1&&window.location.assign(a)}async _getTokenFromIFrame(t){const n=Object.assign(Object.assign({},t.authorizationParams),{prompt:"none"}),r=this.cookieStorage.get(this.orgHintCookieName);r&&!n.organization&&(n.organization=r);const{url:s,state:o,nonce:a,code_verifier:l,redirect_uri:c,scope:d,audience:m}=await this._prepareAuthorizeUrl(n,{response_mode:"web_message"},window.location.origin);try{if(window.crossOriginIsolated)throw new te("login_required","The application is running in a Cross-Origin Isolated context, silently retrieving a token without refresh token is not possible.");const p=t.timeoutInSeconds||this.options.authorizeTimeoutInSeconds;let f;try{f=new URL(this.domainUrl).origin}catch{f=this.domainUrl}const g=await((w,j,x=60)=>new Promise((u,h)=>{const y=window.document.createElement("iframe");y.setAttribute("width","0"),y.setAttribute("height","0"),y.style.display="none";const v=()=>{window.document.body.contains(y)&&(window.document.body.removeChild(y),window.removeEventListener("message",N,!1))};let N;const k=setTimeout(()=>{h(new Dr),v()},1e3*x);N=function(C){if(C.origin!=j||!C.data||C.data.type!=="authorization_response")return;const z=C.source;z&&z.close(),C.data.response.error?h(te.fromPayload(C.data.response)):u(C.data.response),clearTimeout(k),window.removeEventListener("message",N,!1),setTimeout(v,2e3)},window.addEventListener("message",N,!1),window.document.body.appendChild(y),y.setAttribute("src",w)}))(s,f,p);if(o!==g.state)throw new te("state_mismatch","Invalid state");const b=await this._requestToken(Object.assign(Object.assign({},t.authorizationParams),{code_verifier:l,code:g.code,grant_type:"authorization_code",redirect_uri:c,timeout:t.authorizationParams.timeout||this.httpTimeoutMs}),{nonceIn:a,organization:n.organization});return Object.assign(Object.assign({},b),{scope:d,oauthTokenScope:b.scope,audience:m})}catch(p){throw p.error==="login_required"&&this.logout({openUrl:!1}),p}}async _getTokenUsingRefreshToken(t){const n=await this.cacheManager.get(new Ze({scope:t.authorizationParams.scope,audience:t.authorizationParams.audience||"default",clientId:this.options.clientId}),void 0,this.options.useMrrt);if(!(n&&n.refresh_token||this.worker)){if(this.options.useRefreshTokensFallback)return await this._getTokenFromIFrame(t);throw new ai(t.authorizationParams.audience||"default",t.authorizationParams.scope)}const r=t.authorizationParams.redirect_uri||this.options.authorizationParams.redirect_uri||window.location.origin,s=typeof t.timeoutInSeconds=="number"?1e3*t.timeoutInSeconds:null,o=((m,p,f,g)=>{var b;if(m&&f&&g){if(p.audience!==f)return p.scope;const w=g.split(" "),j=((b=p.scope)===null||b===void 0?void 0:b.split(" "))||[],x=j.every(u=>w.includes(u));return w.length>=j.length&&x?g:p.scope}return p.scope})(this.options.useMrrt,t.authorizationParams,n==null?void 0:n.audience,n==null?void 0:n.scope);try{const m=await this._requestToken(Object.assign(Object.assign(Object.assign({},t.authorizationParams),{grant_type:"refresh_token",refresh_token:n&&n.refresh_token,redirect_uri:r}),s&&{timeout:s}),{scopesToRequest:o});if(m.refresh_token&&this.options.useMrrt&&(n!=null&&n.refresh_token)&&await this.cacheManager.updateEntry(n.refresh_token,m.refresh_token),this.options.useMrrt&&(a=n==null?void 0:n.audience,l=n==null?void 0:n.scope,c=t.authorizationParams.audience,d=t.authorizationParams.scope,(a!==c||!pc(d,l))&&!pc(o,m.scope))){if(this.options.useRefreshTokensFallback)return await this._getTokenFromIFrame(t);const p=((f,g)=>{const b=(f==null?void 0:f.split(" "))||[],w=(g==null?void 0:g.split(" "))||[];return b.filter(j=>w.indexOf(j)==-1).join(",")})(o,m.scope);throw new Ha(t.authorizationParams.audience||"default",p)}return Object.assign(Object.assign({},m),{scope:t.authorizationParams.scope,oauthTokenScope:m.scope,audience:t.authorizationParams.audience||"default"})}catch(m){if((m.message.indexOf("Missing Refresh Token")>-1||m.message&&m.message.indexOf("invalid refresh token")>-1)&&this.options.useRefreshTokensFallback)return await this._getTokenFromIFrame(t);throw m}var a,l,c,d}async _saveEntryInCache(t){const{id_token:n,decodedToken:r}=t,s=et(t,["id_token","decodedToken"]);this.userCache.set("@@user@@",{id_token:n,decodedToken:r}),await this.cacheManager.setIdToken(this.options.clientId,t.id_token,t.decodedToken),await this.cacheManager.set(s)}async _getIdTokenFromCache(){const t=this.options.authorizationParams.audience||"default",n=await this.cacheManager.getIdToken(new Ze({clientId:this.options.clientId,audience:t,scope:this.scope})),r=this.userCache.get("@@user@@");return n&&n.id_token===(r==null?void 0:r.id_token)?r:(this.userCache.set("@@user@@",n),n)}async _getEntryFromCache({scope:t,audience:n,clientId:r,cacheMode:s}){const o=await this.cacheManager.get(new Ze({scope:t,audience:n,clientId:r}),60,this.options.useMrrt,s);if(o&&o.access_token){const{token_type:a,access_token:l,oauthTokenScope:c,expires_in:d}=o,m=await this._getIdTokenFromCache();return m&&Object.assign(Object.assign({id_token:m.id_token,token_type:a||"Bearer",access_token:l},c?{scope:c}:null),{expires_in:d})}}async _requestToken(t,n){const{nonceIn:r,organization:s,scopesToRequest:o}=n||{},a=await hf(Object.assign(Object.assign({baseUrl:this.domainUrl,client_id:this.options.clientId,auth0Client:this.options.auth0Client,useFormData:this.options.useFormData,timeout:this.httpTimeoutMs,useMrrt:this.options.useMrrt,dpop:this.dpop},t),{scope:o||t.scope}),this.worker),l=await this._verifyIdToken(a.id_token,r,s);return await this._saveEntryInCache(Object.assign(Object.assign(Object.assign(Object.assign({},a),{decodedToken:l,scope:t.scope,audience:t.audience||"default"}),a.scope?{oauthTokenScope:a.scope}:null),{client_id:this.options.clientId})),this.cookieStorage.save(this.isAuthenticatedCookieName,!0,{daysUntilExpire:this.sessionCheckExpiryDays,cookieDomain:this.options.cookieDomain}),this._processOrgHint(s||l.claims.org_id),Object.assign(Object.assign({},a),{decodedToken:l})}async exchangeToken(t){return this._requestToken({grant_type:"urn:ietf:params:oauth:grant-type:token-exchange",subject_token:t.subject_token,subject_token_type:t.subject_token_type,scope:Yn(t.scope,this.scope),audience:t.audience||this.options.authorizationParams.audience})}_assertDpop(t){if(!t)throw new Error("`useDpop` option must be enabled before using DPoP.")}getDpopNonce(t){return this._assertDpop(this.dpop),this.dpop.getNonce(t)}setDpopNonce(t,n){return this._assertDpop(this.dpop),this.dpop.setNonce(t,n)}generateDpopProof(t){return this._assertDpop(this.dpop),this.dpop.generateProof(t)}createFetcher(t={}){return new Ef(t,{isDpopEnabled:()=>!!this.options.useDpop,getAccessToken:n=>{var r;return this.getTokenSilently({authorizationParams:{scope:(r=n==null?void 0:n.scope)===null||r===void 0?void 0:r.join(" "),audience:n==null?void 0:n.audience},detailedResponse:!0})},getDpopNonce:()=>this.getDpopNonce(t.dpopNonceId),setDpopNonce:n=>this.setDpopNonce(n,t.dpopNonceId),generateDpopProof:n=>this.generateDpopProof(n)})}async connectAccountWithRedirect(t){const{openUrl:n,appState:r,connection:s,authorization_params:o,redirectUri:a=this.options.authorizationParams.redirect_uri||window.location.origin}=t;if(!s)throw new Error("connection is required");const l=$i(Xn()),c=Xn(),d=await ic(c),m=ac(d),{connect_uri:p,connect_params:f,auth_session:g}=await this.myAccountApi.connectAccount({connection:s,redirect_uri:a,state:l,code_challenge:m,code_challenge_method:"S256",authorization_params:o});this.transactionManager.create({state:l,code_verifier:c,auth_session:g,redirect_uri:a,appState:r,connection:s,response_type:bt.ConnectCode});const b=new URL(p);b.searchParams.set("ticket",f.ticket),n?await n(b.toString()):window.location.assign(b)}}var Yu={isAuthenticated:!1,isLoading:!0,error:void 0,user:void 0},ke=function(){throw new Error("You forgot to wrap your component in .")},_f=le(le({},Yu),{buildAuthorizeUrl:ke,buildLogoutUrl:ke,getAccessTokenSilently:ke,getAccessTokenWithPopup:ke,getIdTokenClaims:ke,loginWithRedirect:ke,loginWithPopup:ke,connectAccountWithRedirect:ke,logout:ke,handleRedirectCallback:ke,getDpopNonce:ke,setDpopNonce:ke,generateDpopProof:ke,createFetcher:ke}),Qu=S.createContext(_f),fc=function(e){Gp(t,e);function t(n,r){var s=e.call(this,r??n)||this;return s.error=n,s.error_description=r,Object.setPrototypeOf(s,t.prototype),s}return t}(Error),Df=/[?&](?:connect_)?code=[^&]+/,If=/[?&]state=[^&]+/,Af=/[?&]error=[^&]+/,zf=function(e){return e===void 0&&(e=window.location.search),(Df.test(e)||Af.test(e))&&If.test(e)},qu=function(e){return function(t){if(t instanceof Error)return t;if(t!==null&&typeof t=="object"&&"error"in t&&typeof t.error=="string"){if("error_description"in t&&typeof t.error_description=="string"){var n=t;return new fc(n.error,n.error_description)}var r=t;return new fc(r.error)}return new Error(e)}},gc=qu("Login failed"),Ui=qu("Get access token failed"),eh=function(e){var t,n;e!=null&&e.redirectUri&&(console.warn("Using `redirectUri` has been deprecated, please use `authorizationParams.redirect_uri` instead as `redirectUri` will be no longer supported in a future version"),e.authorizationParams=(t=e.authorizationParams)!==null&&t!==void 0?t:{},e.authorizationParams.redirect_uri=e.redirectUri,delete e.redirectUri),!((n=e==null?void 0:e.authorizationParams)===null||n===void 0)&&n.redirectUri&&(console.warn("Using `authorizationParams.redirectUri` has been deprecated, please use `authorizationParams.redirect_uri` instead as `authorizationParams.redirectUri` will be removed in a future version"),e.authorizationParams.redirect_uri=e.authorizationParams.redirectUri,delete e.authorizationParams.redirectUri)},Of=function(e,t){switch(t.type){case"LOGIN_POPUP_STARTED":return le(le({},e),{isLoading:!0});case"LOGIN_POPUP_COMPLETE":case"INITIALISED":return le(le({},e),{isAuthenticated:!!t.user,user:t.user,isLoading:!1,error:void 0});case"HANDLE_REDIRECT_COMPLETE":case"GET_ACCESS_TOKEN_COMPLETE":return e.user===t.user?e:le(le({},e),{isAuthenticated:!!t.user,user:t.user});case"LOGOUT":return le(le({},e),{isAuthenticated:!1,user:void 0});case"ERROR":return le(le({},e),{isLoading:!1,error:t.error})}},Lf=function(e){return eh(e),le(le({},e),{auth0Client:{name:"auth0-react",version:"2.8.0"}})},Rf=function(e){var t;window.history.replaceState({},document.title,(t=e.returnTo)!==null&&t!==void 0?t:window.location.pathname)},$f=function(e){var t=e.children,n=e.skipRedirectCallback,r=e.onRedirectCallback,s=r===void 0?Rf:r,o=e.context,a=o===void 0?Qu:o,l=sc(e,["children","skipRedirectCallback","onRedirectCallback","context"]),c=S.useState(function(){return new Pf(Lf(l))})[0],d=S.useReducer(Of,Yu),m=d[0],p=d[1],f=S.useRef(!1),g=S.useCallback(function(I){return p({type:"ERROR",error:I}),I},[]);S.useEffect(function(){f.current||(f.current=!0,function(){return cn(void 0,void 0,void 0,function(){var I,M,W,G,K,P,L;return dn(this,function(E){switch(E.label){case 0:return E.trys.push([0,7,,8]),I=void 0,zf()&&!n?[4,c.handleRedirectCallback()]:[3,3];case 1:return M=E.sent(),W=M.appState,G=W===void 0?{}:W,K=M.response_type,P=sc(M,["appState","response_type"]),[4,c.getUser()];case 2:return I=E.sent(),G.response_type=K,K===bt.ConnectCode&&(G.connectedAccount=P),s(G,I),[3,6];case 3:return[4,c.checkSession()];case 4:return E.sent(),[4,c.getUser()];case 5:I=E.sent(),E.label=6;case 6:return p({type:"INITIALISED",user:I}),[3,8];case 7:return L=E.sent(),g(gc(L)),[3,8];case 8:return[2]}})})}())},[c,s,n,g]);var b=S.useCallback(function(I){return eh(I),c.loginWithRedirect(I)},[c]),w=S.useCallback(function(I,M){return cn(void 0,void 0,void 0,function(){var W,G;return dn(this,function(K){switch(K.label){case 0:p({type:"LOGIN_POPUP_STARTED"}),K.label=1;case 1:return K.trys.push([1,3,,4]),[4,c.loginWithPopup(I,M)];case 2:return K.sent(),[3,4];case 3:return W=K.sent(),g(gc(W)),[2];case 4:return[4,c.getUser()];case 5:return G=K.sent(),p({type:"LOGIN_POPUP_COMPLETE",user:G}),[2]}})})},[c,g]),j=S.useCallback(function(){for(var I=[],M=0;M"u")throw new Error(t)}function th(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function Mf(){return Math.random().toString(36).substr(2,8)}function vc(e,t){return{usr:e.state,key:e.key,idx:t}}function Mo(e,t,n,r){return n===void 0&&(n=null),Ir({pathname:typeof e=="string"?e:e.pathname,search:"",hash:""},typeof t=="string"?Mn(t):t,{state:n,key:t&&t.key||r||Mf()})}function Ws(e){let{pathname:t="/",search:n="",hash:r=""}=e;return n&&n!=="?"&&(t+=n.charAt(0)==="?"?n:"?"+n),r&&r!=="#"&&(t+=r.charAt(0)==="#"?r:"#"+r),t}function Mn(e){let t={};if(e){let n=e.indexOf("#");n>=0&&(t.hash=e.substr(n),e=e.substr(0,n));let r=e.indexOf("?");r>=0&&(t.search=e.substr(r),e=e.substr(0,r)),e&&(t.pathname=e)}return t}function Uf(e,t,n,r){r===void 0&&(r={});let{window:s=document.defaultView,v5Compat:o=!1}=r,a=s.history,l=kt.Pop,c=null,d=m();d==null&&(d=0,a.replaceState(Ir({},a.state,{idx:d}),""));function m(){return(a.state||{idx:null}).idx}function p(){l=kt.Pop;let j=m(),x=j==null?null:j-d;d=j,c&&c({action:l,location:w.location,delta:x})}function f(j,x){l=kt.Push;let u=Mo(w.location,j,x);n&&n(u,j),d=m()+1;let h=vc(u,d),y=w.createHref(u);try{a.pushState(h,"",y)}catch(v){if(v instanceof DOMException&&v.name==="DataCloneError")throw v;s.location.assign(y)}o&&c&&c({action:l,location:w.location,delta:1})}function g(j,x){l=kt.Replace;let u=Mo(w.location,j,x);n&&n(u,j),d=m();let h=vc(u,d),y=w.createHref(u);a.replaceState(h,"",y),o&&c&&c({action:l,location:w.location,delta:0})}function b(j){let x=s.location.origin!=="null"?s.location.origin:s.location.href,u=typeof j=="string"?j:Ws(j);return u=u.replace(/ $/,"%20"),se(x,"No window.location.(origin|href) available to create URL for href: "+u),new URL(u,x)}let w={get action(){return l},get location(){return e(s,a)},listen(j){if(c)throw new Error("A history only accepts one active listener");return s.addEventListener(xc,p),c=j,()=>{s.removeEventListener(xc,p),c=null}},createHref(j){return t(s,j)},createURL:b,encodeLocation(j){let x=b(j);return{pathname:x.pathname,search:x.search,hash:x.hash}},push:f,replace:g,go(j){return a.go(j)}};return w}var yc;(function(e){e.data="data",e.deferred="deferred",e.redirect="redirect",e.error="error"})(yc||(yc={}));function Wf(e,t,n){return n===void 0&&(n="/"),Bf(e,t,n,!1)}function Bf(e,t,n,r){let s=typeof t=="string"?Mn(t):t,o=Ga(s.pathname||"/",n);if(o==null)return null;let a=nh(e);Kf(a);let l=null;for(let c=0;l==null&&c{let c={relativePath:l===void 0?o.path||"":l,caseSensitive:o.caseSensitive===!0,childrenIndex:a,route:o};c.relativePath.startsWith("/")&&(se(c.relativePath.startsWith(r),'Absolute route path "'+c.relativePath+'" nested under path '+('"'+r+'" is not valid. An absolute child route path ')+"must start with the combined path of all its parent routes."),c.relativePath=c.relativePath.slice(r.length));let d=At([r,c.relativePath]),m=n.concat(c);o.children&&o.children.length>0&&(se(o.index!==!0,"Index routes must not have child routes. Please remove "+('all child routes from route path "'+d+'".')),nh(o.children,t,m,d)),!(o.path==null&&!o.index)&&t.push({path:d,score:Qf(d,o.index),routesMeta:m})};return e.forEach((o,a)=>{var l;if(o.path===""||!((l=o.path)!=null&&l.includes("?")))s(o,a);else for(let c of rh(o.path))s(o,a,c)}),t}function rh(e){let t=e.split("/");if(t.length===0)return[];let[n,...r]=t,s=n.endsWith("?"),o=n.replace(/\?$/,"");if(r.length===0)return s?[o,""]:[o];let a=rh(r.join("/")),l=[];return l.push(...a.map(c=>c===""?o:[o,c].join("/"))),s&&l.push(...a),l.map(c=>e.startsWith("/")&&c===""?"/":c)}function Kf(e){e.sort((t,n)=>t.score!==n.score?n.score-t.score:qf(t.routesMeta.map(r=>r.childrenIndex),n.routesMeta.map(r=>r.childrenIndex)))}const Hf=/^:[\w-]+$/,Gf=3,Zf=2,Jf=1,Xf=10,Yf=-2,wc=e=>e==="*";function Qf(e,t){let n=e.split("/"),r=n.length;return n.some(wc)&&(r+=Yf),t&&(r+=Zf),n.filter(s=>!wc(s)).reduce((s,o)=>s+(Hf.test(o)?Gf:o===""?Jf:Xf),r)}function qf(e,t){return e.length===t.length&&e.slice(0,-1).every((r,s)=>r===t[s])?e[e.length-1]-t[t.length-1]:0}function eg(e,t,n){n===void 0&&(n=!1);let{routesMeta:r}=e,s={},o="/",a=[];for(let l=0;l{let{paramName:f,isOptional:g}=m;if(f==="*"){let w=l[p]||"";a=o.slice(0,o.length-w.length).replace(/(.)\/+$/,"$1")}const b=l[p];return g&&!b?d[f]=void 0:d[f]=(b||"").replace(/%2F/g,"/"),d},{}),pathname:o,pathnameBase:a,pattern:e}}function tg(e,t,n){t===void 0&&(t=!1),n===void 0&&(n=!0),th(e==="*"||!e.endsWith("*")||e.endsWith("/*"),'Route path "'+e+'" will be treated as if it were '+('"'+e.replace(/\*$/,"/*")+'" because the `*` character must ')+"always follow a `/` in the pattern. To get rid of this warning, "+('please change the route path to "'+e.replace(/\*$/,"/*")+'".'));let r=[],s="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(a,l,c)=>(r.push({paramName:l,isOptional:c!=null}),c?"/?([^\\/]+)?":"/([^\\/]+)"));return e.endsWith("*")?(r.push({paramName:"*"}),s+=e==="*"||e==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?s+="\\/*$":e!==""&&e!=="/"&&(s+="(?:(?=\\/|$))"),[new RegExp(s,t?void 0:"i"),r]}function ng(e){try{return e.split("/").map(t=>decodeURIComponent(t).replace(/\//g,"%2F")).join("/")}catch(t){return th(!1,'The URL path "'+e+'" could not be decoded because it is is a malformed URL segment. This is probably due to a bad percent '+("encoding ("+t+").")),e}}function Ga(e,t){if(t==="/")return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith("/")?t.length-1:t.length,r=e.charAt(n);return r&&r!=="/"?null:e.slice(n)||"/"}function rg(e,t){t===void 0&&(t="/");let{pathname:n,search:r="",hash:s=""}=typeof e=="string"?Mn(e):e;return{pathname:n?n.startsWith("/")?n:sg(n,t):t,search:ag(r),hash:lg(s)}}function sg(e,t){let n=t.replace(/\/+$/,"").split("/");return e.split("/").forEach(s=>{s===".."?n.length>1&&n.pop():s!=="."&&n.push(s)}),n.length>1?n.join("/"):"/"}function Wi(e,t,n,r){return"Cannot include a '"+e+"' character in a manually specified "+("`to."+t+"` field ["+JSON.stringify(r)+"]. Please separate it out to the ")+("`to."+n+"` field. Alternatively you may provide the full path as ")+'a string in and the router will parse it for you.'}function ig(e){return e.filter((t,n)=>n===0||t.route.path&&t.route.path.length>0)}function sh(e,t){let n=ig(e);return t?n.map((r,s)=>s===n.length-1?r.pathname:r.pathnameBase):n.map(r=>r.pathnameBase)}function ih(e,t,n,r){r===void 0&&(r=!1);let s;typeof e=="string"?s=Mn(e):(s=Ir({},e),se(!s.pathname||!s.pathname.includes("?"),Wi("?","pathname","search",s)),se(!s.pathname||!s.pathname.includes("#"),Wi("#","pathname","hash",s)),se(!s.search||!s.search.includes("#"),Wi("#","search","hash",s)));let o=e===""||s.pathname==="",a=o?"/":s.pathname,l;if(a==null)l=n;else{let p=t.length-1;if(!r&&a.startsWith("..")){let f=a.split("/");for(;f[0]==="..";)f.shift(),p-=1;s.pathname=f.join("/")}l=p>=0?t[p]:"/"}let c=rg(s,l),d=a&&a!=="/"&&a.endsWith("/"),m=(o||a===".")&&n.endsWith("/");return!c.pathname.endsWith("/")&&(d||m)&&(c.pathname+="/"),c}const At=e=>e.join("/").replace(/\/\/+/g,"/"),og=e=>e.replace(/\/+$/,"").replace(/^\/*/,"/"),ag=e=>!e||e==="?"?"":e.startsWith("?")?e:"?"+e,lg=e=>!e||e==="#"?"":e.startsWith("#")?e:"#"+e;function cg(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.internal=="boolean"&&"data"in e}const oh=["post","put","patch","delete"];new Set(oh);const dg=["get",...oh];new Set(dg);/** - * React Router v6.30.1 - * - * Copyright (c) Remix Software Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE.md file in the root directory of this source tree. - * - * @license MIT - */function Ar(){return Ar=Object.assign?Object.assign.bind():function(e){for(var t=1;t{l.current=!0}),S.useCallback(function(d,m){if(m===void 0&&(m={}),!l.current)return;if(typeof d=="number"){r.go(d);return}let p=ih(d,JSON.parse(a),o,m.relative==="path");e==null&&t!=="/"&&(p.pathname=p.pathname==="/"?t:At([t,p.pathname])),(m.replace?r.replace:r.push)(p,m.state,m)},[t,r,a,o,e])}function dh(){let{matches:e}=S.useContext(Ft),t=e[e.length-1];return t?t.params:{}}function uh(e,t){let{relative:n}=t===void 0?{}:t,{future:r}=S.useContext(an),{matches:s}=S.useContext(Ft),{pathname:o}=di(),a=JSON.stringify(sh(s,r.v7_relativeSplatPath));return S.useMemo(()=>ih(e,JSON.parse(a),o,n==="path"),[e,a,o,n])}function pg(e,t){return fg(e,t)}function fg(e,t,n,r){Fr()||se(!1);let{navigator:s}=S.useContext(an),{matches:o}=S.useContext(Ft),a=o[o.length-1],l=a?a.params:{};a&&a.pathname;let c=a?a.pathnameBase:"/";a&&a.route;let d=di(),m;if(t){var p;let j=typeof t=="string"?Mn(t):t;c==="/"||(p=j.pathname)!=null&&p.startsWith(c)||se(!1),m=j}else m=d;let f=m.pathname||"/",g=f;if(c!=="/"){let j=c.replace(/^\//,"").split("/");g="/"+f.replace(/^\//,"").split("/").slice(j.length).join("/")}let b=Wf(e,{pathname:g}),w=wg(b&&b.map(j=>Object.assign({},j,{params:Object.assign({},l,j.params),pathname:At([c,s.encodeLocation?s.encodeLocation(j.pathname).pathname:j.pathname]),pathnameBase:j.pathnameBase==="/"?c:At([c,s.encodeLocation?s.encodeLocation(j.pathnameBase).pathname:j.pathnameBase])})),o,n,r);return t&&w?S.createElement(ci.Provider,{value:{location:Ar({pathname:"/",search:"",hash:"",state:null,key:"default"},m),navigationType:kt.Pop}},w):w}function gg(){let e=kg(),t=cg(e)?e.status+" "+e.statusText:e instanceof Error?e.message:JSON.stringify(e),n=e instanceof Error?e.stack:null,s={padding:"0.5rem",backgroundColor:"rgba(200,200,200, 0.5)"},o=null;return S.createElement(S.Fragment,null,S.createElement("h2",null,"Unexpected Application Error!"),S.createElement("h3",{style:{fontStyle:"italic"}},t),n?S.createElement("pre",{style:s},n):null,o)}const xg=S.createElement(gg,null);class vg extends S.Component{constructor(t){super(t),this.state={location:t.location,revalidation:t.revalidation,error:t.error}}static getDerivedStateFromError(t){return{error:t}}static getDerivedStateFromProps(t,n){return n.location!==t.location||n.revalidation!=="idle"&&t.revalidation==="idle"?{error:t.error,location:t.location,revalidation:t.revalidation}:{error:t.error!==void 0?t.error:n.error,location:n.location,revalidation:t.revalidation||n.revalidation}}componentDidCatch(t,n){console.error("React Router caught the following error during render",t,n)}render(){return this.state.error!==void 0?S.createElement(Ft.Provider,{value:this.props.routeContext},S.createElement(ah.Provider,{value:this.state.error,children:this.props.component})):this.props.children}}function yg(e){let{routeContext:t,match:n,children:r}=e,s=S.useContext(Za);return s&&s.static&&s.staticContext&&(n.route.errorElement||n.route.ErrorBoundary)&&(s.staticContext._deepestRenderedBoundaryId=n.route.id),S.createElement(Ft.Provider,{value:t},r)}function wg(e,t,n,r){var s;if(t===void 0&&(t=[]),n===void 0&&(n=null),r===void 0&&(r=null),e==null){var o;if(!n)return null;if(n.errors)e=n.matches;else if((o=r)!=null&&o.v7_partialHydration&&t.length===0&&!n.initialized&&n.matches.length>0)e=n.matches;else return null}let a=e,l=(s=n)==null?void 0:s.errors;if(l!=null){let m=a.findIndex(p=>p.route.id&&(l==null?void 0:l[p.route.id])!==void 0);m>=0||se(!1),a=a.slice(0,Math.min(a.length,m+1))}let c=!1,d=-1;if(n&&r&&r.v7_partialHydration)for(let m=0;m=0?a=a.slice(0,d+1):a=[a[0]];break}}}return a.reduceRight((m,p,f)=>{let g,b=!1,w=null,j=null;n&&(g=l&&p.route.id?l[p.route.id]:void 0,w=p.route.errorElement||xg,c&&(d<0&&f===0?(Cg("route-fallback",!1),b=!0,j=null):d===f&&(b=!0,j=p.route.hydrateFallbackElement||null)));let x=t.concat(a.slice(0,f+1)),u=()=>{let h;return g?h=w:b?h=j:p.route.Component?h=S.createElement(p.route.Component,null):p.route.element?h=p.route.element:h=m,S.createElement(yg,{match:p,routeContext:{outlet:m,matches:x,isDataRoute:n!=null},children:h})};return n&&(p.route.ErrorBoundary||p.route.errorElement||f===0)?S.createElement(vg,{location:n.location,revalidation:n.revalidation,component:w,error:g,children:u(),routeContext:{outlet:null,matches:x,isDataRoute:!0}}):u()},null)}var hh=function(e){return e.UseBlocker="useBlocker",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e}(hh||{}),Bs=function(e){return e.UseBlocker="useBlocker",e.UseLoaderData="useLoaderData",e.UseActionData="useActionData",e.UseRouteError="useRouteError",e.UseNavigation="useNavigation",e.UseRouteLoaderData="useRouteLoaderData",e.UseMatches="useMatches",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e.UseRouteId="useRouteId",e}(Bs||{});function bg(e){let t=S.useContext(Za);return t||se(!1),t}function jg(e){let t=S.useContext(ug);return t||se(!1),t}function Ng(e){let t=S.useContext(Ft);return t||se(!1),t}function mh(e){let t=Ng(),n=t.matches[t.matches.length-1];return n.route.id||se(!1),n.route.id}function kg(){var e;let t=S.useContext(ah),n=jg(Bs.UseRouteError),r=mh(Bs.UseRouteError);return t!==void 0?t:(e=n.errors)==null?void 0:e[r]}function Sg(){let{router:e}=bg(hh.UseNavigateStable),t=mh(Bs.UseNavigateStable),n=S.useRef(!1);return lh(()=>{n.current=!0}),S.useCallback(function(s,o){o===void 0&&(o={}),n.current&&(typeof s=="number"?e.navigate(s):e.navigate(s,Ar({fromRouteId:t},o)))},[e,t])}const jc={};function Cg(e,t,n){!t&&!jc[e]&&(jc[e]=!0)}function Eg(e,t){e==null||e.v7_startTransition,(e==null?void 0:e.v7_relativeSplatPath)===void 0&&(!t||t.v7_relativeSplatPath),t&&(t.v7_fetcherPersist,t.v7_normalizeFormMethod,t.v7_partialHydration,t.v7_skipActionErrorRevalidation)}function ft(e){se(!1)}function Tg(e){let{basename:t="/",children:n=null,location:r,navigationType:s=kt.Pop,navigator:o,static:a=!1,future:l}=e;Fr()&&se(!1);let c=t.replace(/^\/*/,"/"),d=S.useMemo(()=>({basename:c,navigator:o,static:a,future:Ar({v7_relativeSplatPath:!1},l)}),[c,l,o,a]);typeof r=="string"&&(r=Mn(r));let{pathname:m="/",search:p="",hash:f="",state:g=null,key:b="default"}=r,w=S.useMemo(()=>{let j=Ga(m,c);return j==null?null:{location:{pathname:j,search:p,hash:f,state:g,key:b},navigationType:s}},[c,m,p,f,g,b,s]);return w==null?null:S.createElement(an.Provider,{value:d},S.createElement(ci.Provider,{children:n,value:w}))}function Pg(e){let{children:t,location:n}=e;return pg(Uo(t),n)}new Promise(()=>{});function Uo(e,t){t===void 0&&(t=[]);let n=[];return S.Children.forEach(e,(r,s)=>{if(!S.isValidElement(r))return;let o=[...t,s];if(r.type===S.Fragment){n.push.apply(n,Uo(r.props.children,o));return}r.type!==ft&&se(!1),!r.props.index||!r.props.children||se(!1);let a={id:r.props.id||o.join("-"),caseSensitive:r.props.caseSensitive,element:r.props.element,Component:r.props.Component,index:r.props.index,path:r.props.path,loader:r.props.loader,action:r.props.action,errorElement:r.props.errorElement,ErrorBoundary:r.props.ErrorBoundary,hasErrorBoundary:r.props.ErrorBoundary!=null||r.props.errorElement!=null,shouldRevalidate:r.props.shouldRevalidate,handle:r.props.handle,lazy:r.props.lazy};r.props.children&&(a.children=Uo(r.props.children,o)),n.push(a)}),n}/** - * React Router DOM v6.30.1 - * - * Copyright (c) Remix Software Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE.md file in the root directory of this source tree. - * - * @license MIT - */function Wo(){return Wo=Object.assign?Object.assign.bind():function(e){for(var t=1;t=0)&&(n[s]=e[s]);return n}function Dg(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function Ig(e,t){return e.button===0&&(!t||t==="_self")&&!Dg(e)}const Ag=["onClick","relative","reloadDocument","replace","state","target","to","preventScrollReset","viewTransition"],zg="6";try{window.__reactRouterVersion=zg}catch{}const Og="startTransition",Nc=Oh[Og];function Lg(e){let{basename:t,children:n,future:r,window:s}=e,o=S.useRef();o.current==null&&(o.current=Vf({window:s,v5Compat:!0}));let a=o.current,[l,c]=S.useState({action:a.action,location:a.location}),{v7_startTransition:d}=r||{},m=S.useCallback(p=>{d&&Nc?Nc(()=>c(p)):c(p)},[c,d]);return S.useLayoutEffect(()=>a.listen(m),[a,m]),S.useEffect(()=>Eg(r),[r]),S.createElement(Tg,{basename:t,children:n,location:l.location,navigationType:l.action,navigator:a,future:r})}const Rg=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",$g=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,we=S.forwardRef(function(t,n){let{onClick:r,relative:s,reloadDocument:o,replace:a,state:l,target:c,to:d,preventScrollReset:m,viewTransition:p}=t,f=_g(t,Ag),{basename:g}=S.useContext(an),b,w=!1;if(typeof d=="string"&&$g.test(d)&&(b=d,Rg))try{let h=new URL(window.location.href),y=d.startsWith("//")?new URL(h.protocol+d):new URL(d),v=Ga(y.pathname,g);y.origin===h.origin&&v!=null?d=v+y.search+y.hash:w=!0}catch{}let j=hg(d,{relative:s}),x=Fg(d,{replace:a,state:l,target:c,preventScrollReset:m,relative:s,viewTransition:p});function u(h){r&&r(h),h.defaultPrevented||x(h)}return S.createElement("a",Wo({},f,{href:b||j,onClick:w||o?r:u,ref:n,target:c}))});var kc;(function(e){e.UseScrollRestoration="useScrollRestoration",e.UseSubmit="useSubmit",e.UseSubmitFetcher="useSubmitFetcher",e.UseFetcher="useFetcher",e.useViewTransitionState="useViewTransitionState"})(kc||(kc={}));var Sc;(function(e){e.UseFetcher="useFetcher",e.UseFetchers="useFetchers",e.UseScrollRestoration="useScrollRestoration"})(Sc||(Sc={}));function Fg(e,t){let{target:n,replace:r,state:s,preventScrollReset:o,relative:a,viewTransition:l}=t===void 0?{}:t,c=ch(),d=di(),m=uh(e,{relative:a});return S.useCallback(p=>{if(Ig(p,n)){p.preventDefault();let f=r!==void 0?r:Ws(d)===Ws(m);c(e,{replace:f,state:s,preventScrollReset:o,relative:a,viewTransition:l})}},[d,c,m,r,s,n,e,o,a,l])}const Vg=typeof window<"u"?window.location.origin:"http://localhost:3000";var Pc;const vt=((Pc={}.VITE_API_URL)==null?void 0:Pc.replace(/\/$/,""))||Vg,U=(e,t)=>{const n=/^https?:\/\//.test(e)?e:`${vt}${e}`;return fetch(n,t)},Mg=({onSubmit:e,onCancel:t})=>{const[n,r]=S.useState({name:"",organization:"",department:"Office of Development",transportMode:"flight",flights:[{flightNumber:"",flightDate:"",segment:1}],expectedArrival:"",needsAirportPickup:!0,needsVenueTransport:!0,notes:""}),[s,o]=S.useState({}),[a,l]=S.useState({}),c=w=>{var x;w.preventDefault();const j=((x=n.flights)==null?void 0:x.filter(u=>u.flightNumber))||[];e({...n,flights:j.length>0?j:void 0})},d=w=>{const{name:j,value:x,type:u}=w.target;if(u==="checkbox"){const h=w.target.checked;r(y=>({...y,[j]:h}))}else r(h=>({...h,[j]:x}))},m=w=>{r(j=>({...j,transportMode:w,flights:w==="flight"?[{flightNumber:"",flightDate:"",segment:1}]:void 0,expectedArrival:w==="self-driving"?j.expectedArrival:"",needsAirportPickup:w==="flight"})),w!=="flight"&&l({})},p=(w,j,x)=>{r(u=>{var h;return{...u,flights:((h=u.flights)==null?void 0:h.map((y,v)=>v===w?{...y,[j]:x,validated:!1}:y))||[]}}),l(u=>({...u,[w]:""}))},f=()=>{const w=n.flights||[];w.length<3&&r(j=>{var x;return{...j,flights:[...w,{flightNumber:"",flightDate:((x=w[w.length-1])==null?void 0:x.flightDate)||"",segment:w.length+1}]}})},g=w=>{r(j=>{var x;return{...j,flights:((x=j.flights)==null?void 0:x.filter((u,h)=>h!==w).map((u,h)=>({...u,segment:h+1})))||[]}}),l(j=>{const x={...j};return delete x[w],x})},b=async w=>{var x;const j=(x=n.flights)==null?void 0:x[w];if(!j||!j.flightNumber||!j.flightDate){l(u=>({...u,[w]:"Please enter flight number and date"}));return}o(u=>({...u,[w]:!0})),l(u=>({...u,[w]:""}));try{const u=`/api/flights/${j.flightNumber}?date=${j.flightDate}`,h=await fetch(u);if(h.ok){const y=await h.json();r(v=>{var N;return{...v,flights:((N=v.flights)==null?void 0:N.map((k,C)=>C===w?{...k,validated:!0,validationData:y}:k))||[]}}),l(v=>({...v,[w]:""}))}else{const y=await h.json();l(v=>({...v,[w]:y.error||"Invalid flight number"}))}}catch{l(h=>({...h,[w]:"Error validating flight"}))}finally{o(u=>({...u,[w]:!1}))}};return i.jsx("div",{className:"modal-overlay",children:i.jsxs("div",{className:"modal-content",children:[i.jsxs("div",{className:"modal-header",children:[i.jsx("h2",{className:"text-2xl font-bold text-slate-800",children:"Add New VIP"}),i.jsx("p",{className:"text-slate-600 mt-2",children:"Enter VIP details and travel information"})]}),i.jsx("div",{className:"modal-body",children:i.jsxs("form",{onSubmit:c,className:"space-y-8",children:[i.jsxs("div",{className:"form-section",children:[i.jsx("div",{className:"form-section-header",children:i.jsx("h3",{className:"form-section-title",children:"Basic Information"})}),i.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-6",children:[i.jsxs("div",{className:"form-group",children:[i.jsx("label",{htmlFor:"name",className:"form-label",children:"Full Name *"}),i.jsx("input",{type:"text",id:"name",name:"name",value:n.name,onChange:d,className:"form-input",placeholder:"Enter full name",required:!0})]}),i.jsxs("div",{className:"form-group",children:[i.jsx("label",{htmlFor:"organization",className:"form-label",children:"Organization *"}),i.jsx("input",{type:"text",id:"organization",name:"organization",value:n.organization,onChange:d,className:"form-input",placeholder:"Enter organization name",required:!0})]})]}),i.jsxs("div",{className:"form-group",children:[i.jsx("label",{htmlFor:"department",className:"form-label",children:"Department *"}),i.jsxs("select",{id:"department",name:"department",value:n.department,onChange:d,className:"form-select",required:!0,children:[i.jsx("option",{value:"Office of Development",children:"Office of Development"}),i.jsx("option",{value:"Admin",children:"Admin"})]})]})]}),i.jsxs("div",{className:"form-section",children:[i.jsx("div",{className:"form-section-header",children:i.jsx("h3",{className:"form-section-title",children:"Transportation Details"})}),i.jsxs("div",{className:"form-group",children:[i.jsx("label",{className:"form-label",children:"How are you arriving? *"}),i.jsxs("div",{className:"radio-group",children:[i.jsxs("div",{className:`radio-option ${n.transportMode==="flight"?"selected":""}`,onClick:()=>m("flight"),children:[i.jsx("input",{type:"radio",name:"transportMode",value:"flight",checked:n.transportMode==="flight",onChange:()=>m("flight"),className:"form-radio mr-3"}),i.jsx("span",{className:"font-medium",children:"Arriving by Flight"})]}),i.jsxs("div",{className:`radio-option ${n.transportMode==="self-driving"?"selected":""}`,onClick:()=>m("self-driving"),children:[i.jsx("input",{type:"radio",name:"transportMode",value:"self-driving",checked:n.transportMode==="self-driving",onChange:()=>m("self-driving"),className:"form-radio mr-3"}),i.jsx("span",{className:"font-medium",children:"Self-Driving"})]})]})]}),n.transportMode==="flight"&&n.flights&&i.jsxs("div",{className:"space-y-6",children:[n.flights.map((w,j)=>{var x,u;return i.jsxs("div",{className:"bg-white border-2 border-blue-200 rounded-xl p-6 shadow-sm",children:[i.jsxs("div",{className:"flex justify-between items-center mb-4",children:[i.jsx("h4",{className:"text-lg font-bold text-slate-800",children:j===0?"Primary Flight":`Connecting Flight ${j}`}),j>0&&i.jsx("button",{type:"button",onClick:()=>g(j),className:"text-red-500 hover:text-red-700 font-medium text-sm bg-red-50 hover:bg-red-100 px-3 py-1 rounded-lg transition-colors duration-200",children:"Remove"})]}),i.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4 mb-4",children:[i.jsxs("div",{className:"form-group",children:[i.jsx("label",{htmlFor:`flightNumber-${j}`,className:"form-label",children:"Flight Number *"}),i.jsx("input",{type:"text",id:`flightNumber-${j}`,value:w.flightNumber,onChange:h=>p(j,"flightNumber",h.target.value),className:"form-input",placeholder:"e.g., AA123",required:j===0})]}),i.jsxs("div",{className:"form-group",children:[i.jsx("label",{htmlFor:`flightDate-${j}`,className:"form-label",children:"Flight Date *"}),i.jsx("input",{type:"date",id:`flightDate-${j}`,value:w.flightDate,onChange:h=>p(j,"flightDate",h.target.value),className:"form-input",required:j===0,min:new Date().toISOString().split("T")[0]})]})]}),i.jsx("button",{type:"button",className:"btn btn-secondary w-full",onClick:()=>b(j),disabled:s[j]||!w.flightNumber||!w.flightDate,children:s[j]?i.jsxs(i.Fragment,{children:[i.jsx("span",{className:"animate-spin inline-block w-4 h-4 border-2 border-white border-t-transparent rounded-full mr-2"}),"Validating Flight..."]}):i.jsx(i.Fragment,{children:"Validate Flight"})}),a[j]&&i.jsx("div",{className:"mt-4 bg-red-50 border border-red-200 rounded-lg p-4",children:i.jsx("div",{className:"text-red-700 font-medium",children:a[j]})}),w.validated&&w.validationData&&i.jsxs("div",{className:"mt-4 bg-green-50 border border-green-200 rounded-lg p-4",children:[i.jsxs("div",{className:"text-green-700 font-medium mb-2",children:["Valid: ",w.validationData.airline||"Flight"," - ",(x=w.validationData.departure)==null?void 0:x.airport," → ",(u=w.validationData.arrival)==null?void 0:u.airport]}),w.validationData.flightDate!==w.flightDate&&i.jsxs("div",{className:"text-sm text-green-600",children:["Live tracking starts 4 hours before departure on ",new Date(w.flightDate).toLocaleDateString()]})]})]},j)}),n.flights.length<3&&i.jsx("button",{type:"button",className:"btn btn-secondary w-full",onClick:f,children:"Add Connecting Flight"}),i.jsxs("div",{className:"checkbox-option checked",children:[i.jsx("input",{type:"checkbox",name:"needsAirportPickup",checked:n.needsAirportPickup||!1,onChange:d,className:"form-checkbox mr-3"}),i.jsx("span",{className:"font-medium",children:"Needs Airport Pickup (from final destination)"})]})]}),n.transportMode==="self-driving"&&i.jsxs("div",{className:"form-group",children:[i.jsx("label",{htmlFor:"expectedArrival",className:"form-label",children:"Expected Arrival *"}),i.jsx("input",{type:"datetime-local",id:"expectedArrival",name:"expectedArrival",value:n.expectedArrival,onChange:d,className:"form-input",required:!0})]}),i.jsxs("div",{className:`checkbox-option ${n.needsVenueTransport?"checked":""}`,children:[i.jsx("input",{type:"checkbox",name:"needsVenueTransport",checked:n.needsVenueTransport,onChange:d,className:"form-checkbox mr-3"}),i.jsxs("div",{children:[i.jsx("span",{className:"font-medium",children:"Needs Transportation Between Venues"}),i.jsx("div",{className:"text-sm text-slate-500 mt-1",children:"Check this if the VIP needs rides between different event locations"})]})]})]}),i.jsxs("div",{className:"form-section",children:[i.jsx("div",{className:"form-section-header",children:i.jsx("h3",{className:"form-section-title",children:"Additional Information"})}),i.jsxs("div",{className:"form-group",children:[i.jsx("label",{htmlFor:"notes",className:"form-label",children:"Additional Notes"}),i.jsx("textarea",{id:"notes",name:"notes",value:n.notes,onChange:d,rows:4,className:"form-textarea",placeholder:"Special requirements, dietary restrictions, accessibility needs, etc."})]})]}),i.jsxs("div",{className:"form-actions",children:[i.jsx("button",{type:"button",className:"btn btn-secondary",onClick:t,children:"Cancel"}),i.jsx("button",{type:"submit",className:"btn btn-primary",children:"Add VIP"})]})]})})]})})},Ug=({vip:e,onSubmit:t,onCancel:n})=>{const r=e.flights||(e.flightNumber?[{flightNumber:e.flightNumber,flightDate:e.flightDate||"",segment:1}]:[{flightNumber:"",flightDate:"",segment:1}]),[s,o]=S.useState({id:e.id,name:e.name,organization:e.organization,transportMode:e.transportMode||"flight",flights:r,expectedArrival:e.expectedArrival?e.expectedArrival.slice(0,16):"",arrivalTime:e.arrivalTime?e.arrivalTime.slice(0,16):"",needsAirportPickup:e.needsAirportPickup!==!1,needsVenueTransport:e.needsVenueTransport!==!1,notes:e.notes||""}),[a,l]=S.useState({}),[c,d]=S.useState({}),[m,p]=S.useState(!1),f=async h=>{var y;h.preventDefault(),p(!0);try{const v=((y=s.flights)==null?void 0:y.filter(N=>N.flightNumber))||[];await t({...s,flights:v.length>0?v:void 0})}catch(v){console.error("Error updating VIP:",v)}finally{p(!1)}},g=h=>{const{name:y,value:v,type:N}=h.target;if(N==="checkbox"){const k=h.target.checked;o(C=>({...C,[y]:k}))}else o(k=>({...k,[y]:v}))},b=h=>{o(y=>({...y,transportMode:h,flights:h==="flight"?y.flights||[{flightNumber:"",flightDate:"",segment:1}]:void 0,expectedArrival:h==="self-driving"?y.expectedArrival:"",needsAirportPickup:h==="flight"})),h!=="flight"&&d({})},w=(h,y,v)=>{o(N=>{var k;return{...N,flights:((k=N.flights)==null?void 0:k.map((C,z)=>z===h?{...C,[y]:v,validated:!1}:C))||[]}}),d(N=>({...N,[h]:""}))},j=()=>{const h=s.flights||[];h.length<3&&o(y=>{var v;return{...y,flights:[...h,{flightNumber:"",flightDate:((v=h[h.length-1])==null?void 0:v.flightDate)||"",segment:h.length+1}]}})},x=h=>{o(y=>{var v;return{...y,flights:((v=y.flights)==null?void 0:v.filter((N,k)=>k!==h).map((N,k)=>({...N,segment:k+1})))||[]}}),d(y=>{const v={...y};return delete v[h],v})},u=async h=>{var v;const y=(v=s.flights)==null?void 0:v[h];if(!y||!y.flightNumber||!y.flightDate){d(N=>({...N,[h]:"Please enter flight number and date"}));return}l(N=>({...N,[h]:!0})),d(N=>({...N,[h]:""}));try{const N=`/api/flights/${y.flightNumber}?date=${y.flightDate}`,k=await fetch(N);if(k.ok){const C=await k.json();o(z=>{var A;return{...z,flights:((A=z.flights)==null?void 0:A.map((I,M)=>M===h?{...I,validated:!0,validationData:C}:I))||[]}}),d(z=>({...z,[h]:""}))}else{const C=await k.json();d(z=>({...z,[h]:C.error||"Invalid flight number"}))}}catch{d(k=>({...k,[h]:"Error validating flight"}))}finally{l(N=>({...N,[h]:!1}))}};return i.jsx("div",{className:"fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50 p-4",children:i.jsxs("div",{className:"bg-white rounded-2xl shadow-2xl border border-slate-200/60 w-full max-w-4xl max-h-[90vh] overflow-y-auto",children:[i.jsxs("div",{className:"bg-gradient-to-r from-blue-50 to-indigo-50 px-8 py-6 border-b border-slate-200/60",children:[i.jsxs("h2",{className:"text-2xl font-bold text-slate-800 flex items-center gap-2",children:["āœļø Edit VIP: ",e.name]}),i.jsx("p",{className:"text-slate-600 mt-1",children:"Update VIP information and travel arrangements"})]}),i.jsxs("form",{onSubmit:f,className:"p-8 space-y-8",children:[i.jsxs("div",{className:"bg-slate-50 rounded-xl p-6 border border-slate-200/60",children:[i.jsx("h3",{className:"text-lg font-bold text-slate-800 mb-4 flex items-center gap-2",children:"šŸ‘¤ Basic Information"}),i.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-6",children:[i.jsxs("div",{children:[i.jsx("label",{htmlFor:"name",className:"block text-sm font-medium text-slate-700 mb-2",children:"Full Name"}),i.jsx("input",{type:"text",id:"name",name:"name",value:s.name,onChange:g,className:"w-full px-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors",placeholder:"Enter full name",required:!0})]}),i.jsxs("div",{children:[i.jsx("label",{htmlFor:"organization",className:"block text-sm font-medium text-slate-700 mb-2",children:"Organization"}),i.jsx("input",{type:"text",id:"organization",name:"organization",value:s.organization,onChange:g,className:"w-full px-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors",placeholder:"Enter organization",required:!0})]})]})]}),i.jsxs("div",{className:"bg-slate-50 rounded-xl p-6 border border-slate-200/60",children:[i.jsx("h3",{className:"text-lg font-bold text-slate-800 mb-4 flex items-center gap-2",children:"šŸš— Transportation"}),i.jsx("div",{className:"space-y-4",children:i.jsxs("div",{children:[i.jsx("label",{className:"block text-sm font-medium text-slate-700 mb-3",children:"How are you arriving?"}),i.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[i.jsxs("label",{className:`relative flex items-center p-4 border-2 rounded-xl cursor-pointer transition-all duration-200 ${s.transportMode==="flight"?"border-blue-500 bg-blue-50":"border-slate-300 bg-white hover:border-slate-400"}`,children:[i.jsx("input",{type:"radio",name:"transportMode",value:"flight",checked:s.transportMode==="flight",onChange:()=>b("flight"),className:"sr-only"}),i.jsxs("div",{className:"flex items-center gap-3",children:[i.jsx("span",{className:"text-2xl",children:"āœˆļø"}),i.jsxs("div",{children:[i.jsx("div",{className:"font-semibold text-slate-900",children:"Arriving by Flight"}),i.jsx("div",{className:"text-sm text-slate-600",children:"Commercial airline travel"})]})]}),s.transportMode==="flight"&&i.jsx("div",{className:"absolute top-2 right-2 w-5 h-5 bg-blue-500 rounded-full flex items-center justify-center",children:i.jsx("span",{className:"text-white text-xs",children:"āœ“"})})]}),i.jsxs("label",{className:`relative flex items-center p-4 border-2 rounded-xl cursor-pointer transition-all duration-200 ${s.transportMode==="self-driving"?"border-green-500 bg-green-50":"border-slate-300 bg-white hover:border-slate-400"}`,children:[i.jsx("input",{type:"radio",name:"transportMode",value:"self-driving",checked:s.transportMode==="self-driving",onChange:()=>b("self-driving"),className:"sr-only"}),i.jsxs("div",{className:"flex items-center gap-3",children:[i.jsx("span",{className:"text-2xl",children:"šŸš—"}),i.jsxs("div",{children:[i.jsx("div",{className:"font-semibold text-slate-900",children:"Self-Driving"}),i.jsx("div",{className:"text-sm text-slate-600",children:"Personal vehicle"})]})]}),s.transportMode==="self-driving"&&i.jsx("div",{className:"absolute top-2 right-2 w-5 h-5 bg-green-500 rounded-full flex items-center justify-center",children:i.jsx("span",{className:"text-white text-xs",children:"āœ“"})})]})]})]})})]}),s.transportMode==="flight"&&s.flights&&i.jsxs("div",{className:"bg-blue-50 rounded-xl p-6 border border-blue-200/60",children:[i.jsx("h3",{className:"text-lg font-bold text-slate-800 mb-4 flex items-center gap-2",children:"āœˆļø Flight Information"}),i.jsxs("div",{className:"space-y-6",children:[s.flights.map((h,y)=>{var v,N;return i.jsxs("div",{className:"bg-white rounded-xl border border-blue-200 p-6",children:[i.jsxs("div",{className:"flex justify-between items-center mb-4",children:[i.jsx("h4",{className:"text-lg font-semibold text-slate-900 flex items-center gap-2",children:y===0?i.jsx(i.Fragment,{children:"āœˆļø Primary Flight"}):i.jsxs(i.Fragment,{children:["šŸ”„ Connecting Flight ",y]})}),y>0&&i.jsx("button",{type:"button",onClick:()=>x(y),className:"text-red-600 hover:text-red-700 font-medium text-sm flex items-center gap-1",children:"āœ• Remove"})]}),i.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4 mb-4",children:[i.jsxs("div",{children:[i.jsx("label",{htmlFor:`flightNumber-${y}`,className:"block text-sm font-medium text-slate-700 mb-2",children:"Flight Number"}),i.jsx("input",{type:"text",id:`flightNumber-${y}`,value:h.flightNumber,onChange:k=>w(y,"flightNumber",k.target.value),className:"w-full px-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors",placeholder:"e.g., AA123",required:y===0})]}),i.jsxs("div",{children:[i.jsx("label",{htmlFor:`flightDate-${y}`,className:"block text-sm font-medium text-slate-700 mb-2",children:"Flight Date"}),i.jsx("input",{type:"date",id:`flightDate-${y}`,value:h.flightDate,onChange:k=>w(y,"flightDate",k.target.value),className:"w-full px-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors",required:y===0,min:new Date().toISOString().split("T")[0]})]})]}),i.jsx("button",{type:"button",onClick:()=>u(y),disabled:a[y]||!h.flightNumber||!h.flightDate,className:"w-full bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 disabled:from-slate-400 disabled:to-slate-500 text-white px-4 py-3 rounded-lg font-medium transition-all duration-200 shadow-lg hover:shadow-xl disabled:cursor-not-allowed",children:a[y]?i.jsxs("span",{className:"flex items-center justify-center gap-2",children:[i.jsx("div",{className:"w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"}),"šŸ” Validating..."]}):"šŸ” Validate Flight"}),c[y]&&i.jsx("div",{className:"mt-4 bg-red-50 border border-red-200 rounded-xl p-4",children:i.jsxs("div",{className:"text-red-800 font-medium flex items-center gap-2",children:["āŒ ",c[y]]})}),h.validated&&h.validationData&&i.jsxs("div",{className:"mt-4 bg-green-50 border border-green-200 rounded-xl p-4",children:[i.jsxs("div",{className:"text-green-800 font-medium flex items-center gap-2 mb-2",children:["āœ… Valid Flight: ",h.validationData.airline||"Flight"," - ",(v=h.validationData.departure)==null?void 0:v.airport," → ",(N=h.validationData.arrival)==null?void 0:N.airport]}),h.validationData.flightDate!==h.flightDate&&i.jsxs("div",{className:"text-green-700 text-sm",children:["ā„¹ļø Live tracking starts 4 hours before departure on ",new Date(h.flightDate).toLocaleDateString()]})]})]},y)}),s.flights.length<3&&i.jsx("button",{type:"button",onClick:j,className:"w-full bg-gradient-to-r from-slate-500 to-slate-600 hover:from-slate-600 hover:to-slate-700 text-white px-4 py-3 rounded-lg font-medium transition-all duration-200 shadow-lg hover:shadow-xl",children:"+ Add Connecting Flight"}),i.jsx("div",{className:"bg-white rounded-xl border border-blue-200 p-4",children:i.jsxs("label",{className:"flex items-center gap-3 cursor-pointer",children:[i.jsx("input",{type:"checkbox",name:"needsAirportPickup",checked:s.needsAirportPickup||!1,onChange:g,className:"w-5 h-5 text-blue-600 border-slate-300 rounded focus:ring-blue-500"}),i.jsxs("div",{children:[i.jsx("div",{className:"font-medium text-slate-900",children:"āœ… Needs Airport Pickup"}),i.jsx("div",{className:"text-sm text-slate-600",children:"Pickup from final destination airport"})]})]})})]})]}),s.transportMode==="self-driving"&&i.jsxs("div",{className:"bg-green-50 rounded-xl p-6 border border-green-200/60",children:[i.jsx("h3",{className:"text-lg font-bold text-slate-800 mb-4 flex items-center gap-2",children:"šŸš— Arrival Information"}),i.jsxs("div",{children:[i.jsx("label",{htmlFor:"expectedArrival",className:"block text-sm font-medium text-slate-700 mb-2",children:"Expected Arrival Time"}),i.jsx("input",{type:"datetime-local",id:"expectedArrival",name:"expectedArrival",value:s.expectedArrival,onChange:g,className:"w-full px-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-green-500 transition-colors",required:!0})]})]}),i.jsxs("div",{className:"bg-slate-50 rounded-xl p-6 border border-slate-200/60",children:[i.jsx("h3",{className:"text-lg font-bold text-slate-800 mb-4 flex items-center gap-2",children:"🚐 Transportation Options"}),i.jsx("div",{className:"bg-white rounded-xl border border-slate-200 p-4",children:i.jsxs("label",{className:"flex items-center gap-3 cursor-pointer",children:[i.jsx("input",{type:"checkbox",name:"needsVenueTransport",checked:s.needsVenueTransport,onChange:g,className:"w-5 h-5 text-blue-600 border-slate-300 rounded focus:ring-blue-500"}),i.jsxs("div",{children:[i.jsx("div",{className:"font-medium text-slate-900",children:"🚐 Needs Transportation Between Venues"}),i.jsx("div",{className:"text-sm text-slate-600",children:"Check this if the VIP needs rides between different event locations"})]})]})})]}),i.jsxs("div",{className:"bg-slate-50 rounded-xl p-6 border border-slate-200/60",children:[i.jsx("h3",{className:"text-lg font-bold text-slate-800 mb-4 flex items-center gap-2",children:"šŸ“ Additional Notes"}),i.jsxs("div",{children:[i.jsx("label",{htmlFor:"notes",className:"block text-sm font-medium text-slate-700 mb-2",children:"Special Requirements"}),i.jsx("textarea",{id:"notes",name:"notes",value:s.notes,onChange:g,rows:4,className:"w-full px-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors",placeholder:"Special requirements, dietary restrictions, accessibility needs, security details, etc."})]})]}),i.jsxs("div",{className:"flex justify-end gap-4 pt-6 border-t border-slate-200",children:[i.jsx("button",{type:"button",className:"px-6 py-3 border border-slate-300 text-slate-700 rounded-lg hover:bg-slate-50 transition-colors font-medium",onClick:n,disabled:m,children:"Cancel"}),i.jsx("button",{type:"submit",className:"bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white px-6 py-3 rounded-lg font-medium transition-all duration-200 shadow-lg hover:shadow-xl disabled:opacity-50 disabled:cursor-not-allowed",disabled:m,children:m?i.jsxs("span",{className:"flex items-center gap-2",children:[i.jsx("div",{className:"w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"}),"Updating VIP..."]}):"āœļø Update VIP"})]})]})]})})},ph=({flightNumber:e,flightDate:t})=>{const[n,r]=S.useState(null),[s,o]=S.useState(!0),[a,l]=S.useState(null);if(S.useEffect(()=>{const m=async()=>{try{o(!0);const p=t?`/api/flights/${e}?date=${t}`:`/api/flights/${e}`,f=await fetch(p);if(f.ok){const g=await f.json();r(g),l(null)}else l("Flight not found")}catch{l("Failed to fetch flight data")}finally{o(!1)}};if(e){m();const p=setInterval(m,5*60*1e3);return()=>clearInterval(p)}},[e,t]),s)return i.jsx("div",{className:"flight-status loading",children:"Loading flight data..."});if(a)return i.jsxs("div",{className:"flight-status error",children:["āš ļø ",a]});if(!n)return null;const c=m=>{switch(m.toLowerCase()){case"active":return"#2ecc71";case"scheduled":return"#3498db";case"delayed":return"#f39c12";case"cancelled":return"#e74c3c";default:return"#95a5a6"}},d=m=>new Date(m).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"});return i.jsxs("div",{className:"flight-status",children:[i.jsxs("div",{className:"flight-header",children:[i.jsxs("h4",{children:["āœˆļø Flight ",n.flightNumber]}),i.jsx("span",{className:"flight-status-badge",style:{backgroundColor:c(n.status),color:"white",padding:"0.25rem 0.5rem",borderRadius:"4px",fontSize:"0.8rem",textTransform:"uppercase"},children:n.status})]}),i.jsxs("div",{className:"flight-details",children:[i.jsxs("div",{className:"flight-route",children:[i.jsxs("div",{className:"departure",children:[i.jsx("strong",{children:n.departure.airport}),i.jsxs("div",{children:["Scheduled: ",d(n.departure.scheduled)]}),n.departure.estimated&&i.jsxs("div",{children:["Estimated: ",d(n.departure.estimated)]})]}),i.jsx("div",{className:"route-arrow",children:"→"}),i.jsxs("div",{className:"arrival",children:[i.jsx("strong",{children:n.arrival.airport}),i.jsxs("div",{children:["Scheduled: ",d(n.arrival.scheduled)]}),n.arrival.estimated&&i.jsxs("div",{children:["Estimated: ",d(n.arrival.estimated)]})]})]}),n.delay&&n.delay>0&&i.jsxs("div",{className:"delay-info",style:{color:"#f39c12",marginTop:"0.5rem"},children:["āš ļø Delayed by ",n.delay," minutes"]}),n.gate&&i.jsxs("div",{className:"gate-info",style:{marginTop:"0.5rem"},children:["🚪 Gate: ",n.gate]})]})]})},Wg=()=>{const[e,t]=S.useState([]),[n,r]=S.useState(!0),[s,o]=S.useState(!1),[a,l]=S.useState(null),c=g=>{const b=g.trim().split(" ");return b[b.length-1].toLowerCase()},d=g=>[...g].sort((b,w)=>{const j=c(b.name),x=c(w.name);return j.localeCompare(x)});S.useEffect(()=>{(async()=>{try{const b=localStorage.getItem("authToken"),w=await U("/api/vips",{headers:{Authorization:`Bearer ${b}`,"Content-Type":"application/json"}});if(w.ok){const j=await w.json(),x=d(j);t(x)}else console.error("Failed to fetch VIPs:",w.status)}catch(b){console.error("Error fetching VIPs:",b)}finally{r(!1)}})()},[]);const m=async g=>{try{const b=localStorage.getItem("authToken"),w=await U("/api/vips",{method:"POST",headers:{Authorization:`Bearer ${b}`,"Content-Type":"application/json"},body:JSON.stringify(g)});if(w.ok){const j=await w.json();t(x=>d([...x,j])),o(!1)}else console.error("Failed to add VIP:",w.status)}catch(b){console.error("Error adding VIP:",b)}},p=async g=>{try{const b=localStorage.getItem("authToken"),w=await U(`/api/vips/${g.id}`,{method:"PUT",headers:{Authorization:`Bearer ${b}`,"Content-Type":"application/json"},body:JSON.stringify(g)});if(w.ok){const j=await w.json();t(x=>d(x.map(u=>u.id===j.id?j:u))),l(null)}else console.error("Failed to update VIP:",w.status)}catch(b){console.error("Error updating VIP:",b)}},f=async g=>{if(confirm("Are you sure you want to delete this VIP?"))try{const b=localStorage.getItem("authToken"),w=await U(`/api/vips/${g}`,{method:"DELETE",headers:{Authorization:`Bearer ${b}`,"Content-Type":"application/json"}});w.ok?t(j=>j.filter(x=>x.id!==g)):console.error("Failed to delete VIP:",w.status)}catch(b){console.error("Error deleting VIP:",b)}};return n?i.jsx("div",{className:"flex justify-center items-center min-h-64",children:i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg p-8 flex items-center space-x-4",children:[i.jsx("div",{className:"w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full animate-spin"}),i.jsx("span",{className:"text-lg font-medium text-slate-700",children:"Loading VIPs..."})]})}):i.jsxs("div",{className:"space-y-8",children:[i.jsx("div",{className:"bg-white rounded-2xl shadow-lg p-8 border border-slate-200/60",children:i.jsxs("div",{className:"flex justify-between items-center",children:[i.jsxs("div",{children:[i.jsx("h1",{className:"text-3xl font-bold bg-gradient-to-r from-slate-800 to-slate-600 bg-clip-text text-transparent",children:"VIP Management"}),i.jsx("p",{className:"text-slate-600 mt-2",children:"Manage VIP profiles and travel arrangements"})]}),i.jsx("button",{className:"btn btn-primary",onClick:()=>o(!0),children:"Add New VIP"})]})}),e.length===0?i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg p-12 border border-slate-200/60 text-center",children:[i.jsx("div",{className:"w-16 h-16 bg-slate-100 rounded-full flex items-center justify-center mx-auto mb-4",children:i.jsx("div",{className:"w-8 h-8 bg-slate-300 rounded-full"})}),i.jsx("h3",{className:"text-lg font-semibold text-slate-800 mb-2",children:"No VIPs Found"}),i.jsx("p",{className:"text-slate-600 mb-6",children:"Get started by adding your first VIP"}),i.jsx("button",{className:"btn btn-primary",onClick:()=>o(!0),children:"Add New VIP"})]}):i.jsx("div",{className:"space-y-4",children:e.map(g=>i.jsx("div",{className:"bg-white rounded-2xl shadow-lg border border-slate-200/60 overflow-hidden hover:shadow-xl transition-shadow duration-200",children:i.jsxs("div",{className:"p-6",children:[i.jsxs("div",{className:"flex justify-between items-start",children:[i.jsxs("div",{className:"flex-1",children:[i.jsxs("div",{className:"flex items-center gap-3 mb-3",children:[i.jsx("h3",{className:"text-xl font-bold text-slate-900",children:g.name}),i.jsx("span",{className:"bg-blue-100 text-blue-800 text-xs font-medium px-2.5 py-0.5 rounded-full",children:g.department})]}),i.jsx("p",{className:"text-slate-600 text-sm mb-4",children:g.organization}),i.jsxs("div",{className:"bg-slate-50 rounded-lg p-4 mb-4",children:[g.transportMode==="flight"?i.jsxs("div",{className:"space-y-2",children:[i.jsxs("div",{className:"flex items-center gap-2 text-sm",children:[i.jsx("span",{className:"font-medium text-slate-700",children:"Flight:"}),i.jsx("span",{className:"text-slate-600",children:g.flights&&g.flights.length>0?g.flights.map(b=>b.flightNumber).join(" → "):g.flightNumber||"No flight"})]}),i.jsxs("div",{className:"flex items-center gap-2 text-sm",children:[i.jsx("span",{className:"font-medium text-slate-700",children:"Airport Pickup:"}),i.jsx("span",{className:`px-2 py-1 rounded-full text-xs font-medium ${g.needsAirportPickup?"bg-green-100 text-green-800":"bg-red-100 text-red-800"}`,children:g.needsAirportPickup?"Required":"Not needed"})]})]}):i.jsxs("div",{className:"flex items-center gap-2 text-sm",children:[i.jsx("span",{className:"font-medium text-slate-700",children:"Self-driving, Expected:"}),i.jsx("span",{className:"text-slate-600",children:g.expectedArrival?new Date(g.expectedArrival).toLocaleString():"TBD"})]}),i.jsxs("div",{className:"flex items-center gap-2 text-sm mt-2",children:[i.jsx("span",{className:"font-medium text-slate-700",children:"Venue Transport:"}),i.jsx("span",{className:`px-2 py-1 rounded-full text-xs font-medium ${g.needsVenueTransport?"bg-blue-100 text-blue-800":"bg-gray-100 text-gray-800"}`,children:g.needsVenueTransport?"Required":"Not needed"})]})]})]}),i.jsxs("div",{className:"flex flex-col gap-2 ml-6",children:[i.jsx(we,{to:`/vips/${g.id}`,className:"btn btn-success text-center",children:"View Details"}),i.jsx("button",{className:"btn btn-secondary",onClick:()=>l(g),children:"Edit"}),i.jsx("button",{className:"btn btn-danger",onClick:()=>f(g.id),children:"Delete"})]})]}),g.transportMode==="flight"&&g.flightNumber&&i.jsx("div",{className:"mt-4 pt-4 border-t border-slate-200",children:i.jsx(ph,{flightNumber:g.flightNumber})})]})},g.id))}),s&&i.jsx(Mg,{onSubmit:m,onCancel:()=>o(!1)}),a&&i.jsx(Ug,{vip:{...a,notes:a.notes||""},onSubmit:p,onCancel:()=>l(null)})]})},Bg=({selectedDriverId:e,onDriverSelect:t,eventTime:n})=>{const[r,s]=S.useState([]),[o,a]=S.useState(!1),[l,c]=S.useState(!1),[d,m]=S.useState(null);S.useEffect(()=>{n.startTime&&n.endTime&&p()},[n.startTime,n.endTime,n.location]);const p=async()=>{a(!0);try{const u=localStorage.getItem("authToken"),h=await U("/api/drivers/availability",{method:"POST",headers:{Authorization:`Bearer ${u}`,"Content-Type":"application/json"},body:JSON.stringify(n)});if(h.ok){const y=await h.json();s(y)}}catch(u){console.error("Error checking driver availability:",u)}finally{a(!1)}},f=u=>{switch(u){case"available":return"🟢";case"scheduled":return"🟔";case"tight_turnaround":return"⚔";case"overlapping":return"šŸ”“";default:return"⚪"}},g=u=>{switch(u){case"available":return"bg-green-50 border-green-200 text-green-800";case"scheduled":return"bg-amber-50 border-amber-200 text-amber-800";case"tight_turnaround":return"bg-orange-50 border-orange-200 text-orange-800";case"overlapping":return"bg-red-50 border-red-200 text-red-800";default:return"bg-slate-50 border-slate-200 text-slate-800"}},b=u=>{switch(u){case"available":return"Available";case"scheduled":return"Busy";case"tight_turnaround":return"Tight Schedule";case"overlapping":return"Conflict";default:return"Unknown"}},w=u=>{u.conflicts.length>0?(m(u),c(!0)):t(u.driverId)},j=()=>{d&&(t(d.driverId),c(!1),m(null))},x=u=>new Date(u).toLocaleString([],{hour:"2-digit",minute:"2-digit"});return o?i.jsx("div",{className:"bg-slate-50 rounded-xl p-6 border border-slate-200/60",children:i.jsxs("div",{className:"flex items-center gap-3",children:[i.jsx("div",{className:"w-6 h-6 border-2 border-blue-600 border-t-transparent rounded-full animate-spin"}),i.jsx("span",{className:"text-slate-700 font-medium",children:"Checking driver availability..."})]})}):i.jsxs("div",{className:"bg-slate-50 rounded-xl p-6 border border-slate-200/60",children:[i.jsx("h3",{className:"text-lg font-bold text-slate-800 mb-4 flex items-center gap-2",children:"šŸš— Assign Driver"}),r.length===0?i.jsxs("div",{className:"text-center py-8",children:[i.jsx("div",{className:"w-12 h-12 bg-slate-200 rounded-full flex items-center justify-center mx-auto mb-3",children:i.jsx("span",{className:"text-xl",children:"šŸš—"})}),i.jsx("p",{className:"text-slate-500 font-medium",children:"No drivers available"}),i.jsx("p",{className:"text-slate-400 text-sm",children:"Check the time and try again"})]}):i.jsxs("div",{className:"space-y-3",children:[r.map(u=>{var h,y;return i.jsxs("div",{className:`relative rounded-xl border-2 p-4 cursor-pointer transition-all duration-200 hover:shadow-lg ${e===u.driverId?"border-blue-500 bg-blue-50 shadow-lg":"border-slate-200 bg-white hover:border-slate-300"}`,onClick:()=>w(u),children:[e===u.driverId&&i.jsx("div",{className:"absolute top-2 right-2 w-6 h-6 bg-blue-500 rounded-full flex items-center justify-center",children:i.jsx("span",{className:"text-white text-xs font-bold",children:"āœ“"})}),i.jsxs("div",{className:"flex items-start justify-between",children:[i.jsxs("div",{className:"flex-1",children:[i.jsxs("div",{className:"flex items-center gap-3 mb-2",children:[i.jsx("span",{className:"text-xl",children:f(u.status)}),i.jsxs("div",{children:[i.jsx("h4",{className:"font-bold text-slate-900",children:u.driverName}),i.jsxs("div",{className:"flex items-center gap-2 mt-1",children:[i.jsx("span",{className:`px-2 py-1 rounded-full text-xs font-medium border ${g(u.status)}`,children:b(u.status)}),i.jsxs("span",{className:"bg-slate-100 text-slate-700 px-2 py-1 rounded-full text-xs font-medium",children:["šŸš— ",u.vehicleCapacity," seats"]}),i.jsxs("span",{className:"bg-blue-100 text-blue-800 px-2 py-1 rounded-full text-xs font-medium",children:[u.assignmentCount," assignments"]})]})]})]}),u.conflicts.length>0&&i.jsx("div",{className:"space-y-2 mb-3",children:u.conflicts.map((v,N)=>i.jsxs("div",{className:`p-3 rounded-lg border ${v.severity==="high"?"bg-red-50 border-red-200":"bg-amber-50 border-amber-200"}`,children:[i.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[i.jsx("span",{className:"text-sm",children:v.type==="overlap"?"šŸ”“":"⚔"}),i.jsx("span",{className:`text-sm font-medium ${v.severity==="high"?"text-red-800":"text-amber-800"}`,children:v.type==="overlap"?"Time Overlap":"Tight Turnaround"})]}),i.jsx("p",{className:`text-sm ${v.severity==="high"?"text-red-700":"text-amber-700"}`,children:v.message})]},N))}),u.currentAssignments.length>0&&u.conflicts.length===0&&i.jsxs("div",{className:"bg-slate-100 rounded-lg p-3",children:[i.jsx("p",{className:"text-sm font-medium text-slate-700 mb-1",children:"Next Assignment:"}),i.jsxs("p",{className:"text-sm text-slate-600",children:[(h=u.currentAssignments[0])==null?void 0:h.title," at ",x((y=u.currentAssignments[0])==null?void 0:y.startTime)]})]})]}),u.conflicts.length>0&&i.jsx("div",{className:"ml-4",children:i.jsx("span",{className:"bg-amber-100 text-amber-800 px-3 py-1 rounded-full text-xs font-bold",children:"āš ļø CONFLICTS"})})]})]},u.driverId)}),e&&i.jsx("button",{onClick:()=>t(""),className:"w-full bg-slate-100 hover:bg-slate-200 text-slate-700 px-4 py-3 rounded-lg font-medium transition-colors border border-slate-200",children:"āŒ Clear Driver Assignment"})]}),l&&d&&i.jsx("div",{className:"fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50 p-4",children:i.jsxs("div",{className:"bg-white rounded-2xl shadow-2xl border border-slate-200/60 w-full max-w-2xl max-h-[90vh] overflow-y-auto",children:[i.jsxs("div",{className:"bg-gradient-to-r from-amber-50 to-orange-50 px-8 py-6 border-b border-slate-200/60",children:[i.jsx("h3",{className:"text-xl font-bold text-slate-800 flex items-center gap-2",children:"āš ļø Driver Assignment Conflict"}),i.jsxs("p",{className:"text-slate-600 mt-1",children:[i.jsx("strong",{children:d.driverName})," has scheduling conflicts that need your attention"]})]}),i.jsxs("div",{className:"p-8 space-y-6",children:[i.jsx("div",{className:"bg-slate-50 rounded-xl p-4 border border-slate-200",children:i.jsxs("div",{className:"flex items-center gap-3 mb-2",children:[i.jsx("span",{className:"text-2xl",children:"šŸš—"}),i.jsxs("div",{children:[i.jsx("h4",{className:"font-bold text-slate-900",children:d.driverName}),i.jsxs("p",{className:"text-sm text-slate-600",children:["Vehicle Capacity: ",d.vehicleCapacity," passengers • Current Assignments: ",d.assignmentCount]})]})]})}),i.jsxs("div",{children:[i.jsx("h4",{className:"font-bold text-slate-800 mb-3",children:"Scheduling Conflicts:"}),i.jsx("div",{className:"space-y-3",children:d.conflicts.map((u,h)=>i.jsxs("div",{className:`p-4 rounded-xl border ${u.severity==="high"?"bg-red-50 border-red-200":"bg-amber-50 border-amber-200"}`,children:[i.jsxs("div",{className:"flex items-center gap-2 mb-2",children:[i.jsx("span",{className:"text-lg",children:u.type==="overlap"?"šŸ”“":"⚔"}),i.jsx("span",{className:`font-bold ${u.severity==="high"?"text-red-800":"text-amber-800"}`,children:u.type==="overlap"?"Time Overlap":"Tight Turnaround"})]}),i.jsx("p",{className:`mb-2 ${u.severity==="high"?"text-red-700":"text-amber-700"}`,children:u.message}),i.jsxs("div",{className:"text-sm text-slate-600 bg-white/50 rounded-lg p-2",children:[i.jsx("strong",{children:"Conflicting event:"})," ",u.conflictingEvent.title,i.jsx("br",{}),i.jsx("strong",{children:"Time:"})," ",x(u.conflictingEvent.startTime)," - ",x(u.conflictingEvent.endTime),i.jsx("br",{}),i.jsx("strong",{children:"VIP:"})," ",u.conflictingEvent.vipName]})]},h))})]}),i.jsxs("div",{children:[i.jsx("h4",{className:"font-bold text-slate-800 mb-3",children:"Current Schedule:"}),i.jsx("div",{className:"bg-slate-50 rounded-xl p-4 border border-slate-200",children:d.currentAssignments.length===0?i.jsx("p",{className:"text-slate-500 text-sm",children:"No current assignments"}):i.jsx("div",{className:"space-y-2",children:d.currentAssignments.map((u,h)=>i.jsxs("div",{className:"flex items-center gap-2 text-sm",children:[i.jsx("span",{className:"w-2 h-2 bg-blue-500 rounded-full"}),i.jsx("span",{className:"font-medium",children:u.title}),i.jsxs("span",{className:"text-slate-500",children:["(",x(u.startTime)," - ",x(u.endTime),")"]}),i.jsxs("span",{className:"text-slate-400",children:["• ",u.vipName]})]},h))})})]})]}),i.jsxs("div",{className:"flex justify-end gap-4 p-8 border-t border-slate-200",children:[i.jsx("button",{className:"px-6 py-3 border border-slate-300 text-slate-700 rounded-lg hover:bg-slate-50 transition-colors font-medium",onClick:()=>c(!1),children:"Choose Different Driver"}),i.jsx("button",{className:"bg-gradient-to-r from-red-500 to-red-600 hover:from-red-600 hover:to-red-700 text-white px-6 py-3 rounded-lg font-medium transition-all duration-200 shadow-lg hover:shadow-xl",onClick:j,children:"āš ļø Assign Anyway"})]})]})})]})},Kg=({vipId:e,vipName:t})=>{const[n,r]=S.useState([]),[s,o]=S.useState(!1),[a,l]=S.useState(null),[c,d]=S.useState([]);S.useEffect(()=>{m(),p()},[e]);const m=async()=>{try{const v=localStorage.getItem("authToken"),N=await U(`/api/vips/${e}/schedule`,{headers:{Authorization:`Bearer ${v}`,"Content-Type":"application/json"}});if(N.ok){const k=await N.json();r(k)}}catch(v){console.error("Error fetching schedule:",v)}},p=async()=>{try{const v=localStorage.getItem("authToken"),N=await U("/api/drivers",{headers:{Authorization:`Bearer ${v}`,"Content-Type":"application/json"}});if(N.ok){const k=await N.json();d(k)}}catch(v){console.error("Error fetching drivers:",v)}},f=v=>{const N=c.find(k=>k.id===v);return N?N.name:`Driver ID: ${v}`},g=v=>{switch(v){case"scheduled":return"#3498db";case"in-progress":return"#f39c12";case"completed":return"#2ecc71";case"cancelled":return"#e74c3c";default:return"#95a5a6"}},b=v=>{switch(v){case"transport":return"šŸš—";case"meeting":return"šŸ¤";case"event":return"šŸŽ‰";case"meal":return"šŸ½ļø";case"accommodation":return"šŸØ";default:return"šŸ“…"}},w=v=>{try{const N=new Date(v);if(isNaN(N.getTime()))return"Invalid Time";const k=N.getHours(),C=N.getMinutes(),z=k>=12?"PM":"AM",A=k%12||12,I=C.toString().padStart(2,"0");return`${A}:${I} ${z}`}catch(N){return console.error("Error formatting time:",N,v),"Time Error"}},x=(v=>{const N={};return v.forEach(k=>{const C=new Date(k.startTime).toDateString();N[C]||(N[C]=[]),N[C].push(k)}),Object.keys(N).forEach(k=>{N[k].sort((C,z)=>new Date(C.startTime).getTime()-new Date(z.startTime).getTime())}),N})(n);return i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg border border-slate-200/60 overflow-hidden",children:[i.jsx("div",{className:"bg-gradient-to-r from-purple-50 to-pink-50 px-8 py-6 border-b border-slate-200/60",children:i.jsxs("div",{className:"flex justify-between items-center",children:[i.jsxs("div",{children:[i.jsxs("h2",{className:"text-xl font-bold text-slate-800 flex items-center gap-2",children:["šŸ“… Schedule for ",t]}),i.jsx("p",{className:"text-slate-600 mt-1",children:"Manage daily events and activities"})]}),i.jsx("button",{className:"bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white px-6 py-3 rounded-lg font-medium transition-all duration-200 shadow-lg hover:shadow-xl flex items-center gap-2",onClick:()=>o(!0),children:"āž• Add Event"})]})}),i.jsx("div",{className:"p-8",children:Object.keys(x).length===0?i.jsxs("div",{className:"text-center py-12",children:[i.jsx("div",{className:"w-16 h-16 bg-slate-100 rounded-full flex items-center justify-center mx-auto mb-4",children:i.jsx("span",{className:"text-2xl",children:"šŸ“…"})}),i.jsx("p",{className:"text-slate-500 font-medium mb-2",children:"No scheduled events"}),i.jsx("p",{className:"text-slate-400 text-sm",children:'Click "Add Event" to get started with scheduling'})]}):i.jsx("div",{className:"space-y-8",children:Object.entries(x).map(([v,N])=>i.jsxs("div",{className:"space-y-4",children:[i.jsx("div",{className:"bg-gradient-to-r from-slate-600 to-slate-700 text-white px-6 py-3 rounded-xl shadow-lg",children:i.jsx("h3",{className:"text-lg font-bold",children:new Date(v).toLocaleDateString([],{weekday:"long",year:"numeric",month:"long",day:"numeric"})})}),i.jsx("div",{className:"grid gap-4",children:N.map(k=>i.jsx("div",{className:"bg-gradient-to-r from-slate-50 to-slate-100 rounded-xl border border-slate-200/60 p-6 hover:shadow-lg transition-all duration-200",children:i.jsxs("div",{className:"flex items-start gap-6",children:[i.jsx("div",{className:"flex-shrink-0 text-center",children:i.jsxs("div",{className:"bg-white rounded-lg border border-slate-200 p-3 shadow-sm",children:[i.jsx("div",{className:"text-sm font-bold text-slate-900",children:w(k.startTime)}),i.jsx("div",{className:"text-xs text-slate-500 mt-1",children:"to"}),i.jsx("div",{className:"text-sm font-bold text-slate-900",children:w(k.endTime)})]})}),i.jsxs("div",{className:"flex-1",children:[i.jsxs("div",{className:"flex items-center gap-3 mb-3",children:[i.jsx("span",{className:"text-2xl",children:b(k.type)}),i.jsx("h4",{className:"text-lg font-bold text-slate-900",children:k.title}),i.jsx("span",{className:"px-3 py-1 rounded-full text-xs font-bold text-white shadow-sm",style:{backgroundColor:g(k.status)},children:k.status.toUpperCase()})]}),i.jsxs("div",{className:"flex items-center gap-2 text-slate-600 mb-2",children:[i.jsx("span",{children:"šŸ“"}),i.jsx("span",{className:"font-medium",children:k.location})]}),k.description&&i.jsx("div",{className:"text-slate-600 mb-3 bg-white/50 rounded-lg p-3 border border-slate-200/50",children:k.description}),k.assignedDriverId?i.jsxs("div",{className:"flex items-center gap-2 text-slate-600 mb-4",children:[i.jsx("span",{children:"šŸ‘¤"}),i.jsxs("span",{className:"font-medium",children:["Driver: ",f(k.assignedDriverId)]})]}):i.jsxs("div",{className:"bg-amber-50 border border-amber-200 rounded-lg p-3 mb-4",children:[i.jsxs("div",{className:"flex items-center gap-2 text-amber-800 mb-2",children:[i.jsx("span",{children:"āš ļø"}),i.jsx("span",{className:"font-medium text-sm",children:"No Driver Assigned"})]}),i.jsx("p",{className:"text-amber-700 text-xs mb-2",children:"This event needs a driver to ensure VIP transportation"}),i.jsx("button",{className:"bg-gradient-to-r from-amber-500 to-orange-500 hover:from-amber-600 hover:to-orange-600 text-white px-3 py-1 rounded-lg text-xs font-medium transition-all duration-200 shadow-sm hover:shadow-md",onClick:()=>l(k),children:"šŸš— Assign Driver"})]}),i.jsxs("div",{className:"flex items-center gap-3",children:[i.jsx("button",{className:"bg-gradient-to-r from-slate-500 to-slate-600 hover:from-slate-600 hover:to-slate-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 shadow-lg hover:shadow-xl",onClick:()=>l(k),children:"āœļø Edit"}),k.status==="scheduled"&&i.jsx("button",{className:"bg-gradient-to-r from-amber-500 to-orange-500 hover:from-amber-600 hover:to-orange-600 text-white px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 shadow-lg hover:shadow-xl",onClick:()=>y(k.id,"in-progress"),children:"ā–¶ļø Start"}),k.status==="in-progress"&&i.jsx("button",{className:"bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 shadow-lg hover:shadow-xl",onClick:()=>y(k.id,"completed"),children:"āœ… Complete"}),k.status==="completed"&&i.jsx("span",{className:"bg-green-100 text-green-800 px-3 py-1 rounded-full text-xs font-medium",children:"āœ… Completed"})]})]})]})},k.id))})]},v))})}),s&&i.jsx(Cc,{vipId:e,onSubmit:u,onCancel:()=>o(!1)}),a&&i.jsx(Cc,{vipId:e,event:a,onSubmit:h,onCancel:()=>l(null)})]});async function u(v){try{const N=localStorage.getItem("authToken"),k=await U(`/api/vips/${e}/schedule`,{method:"POST",headers:{Authorization:`Bearer ${N}`,"Content-Type":"application/json"},body:JSON.stringify(v)});if(k.ok)await m(),o(!1);else throw await k.json()}catch(N){throw console.error("Error adding event:",N),N}}async function h(v){try{const N=localStorage.getItem("authToken"),k=await U(`/api/vips/${e}/schedule/${v.id}`,{method:"PUT",headers:{Authorization:`Bearer ${N}`,"Content-Type":"application/json"},body:JSON.stringify(v)});if(k.ok)await m(),l(null);else throw await k.json()}catch(N){throw console.error("Error updating event:",N),N}}async function y(v,N){try{const k=localStorage.getItem("authToken");(await U(`/api/vips/${e}/schedule/${v}/status`,{method:"PATCH",headers:{Authorization:`Bearer ${k}`,"Content-Type":"application/json"},body:JSON.stringify({status:N})})).ok&&await m()}catch(k){console.error("Error updating event status:",k)}}},Cc=({vipId:e,event:t,onSubmit:n,onCancel:r})=>{var b,w;const[s,o]=S.useState({title:(t==null?void 0:t.title)||"",location:(t==null?void 0:t.location)||"",startTime:((b=t==null?void 0:t.startTime)==null?void 0:b.slice(0,16))||"",endTime:((w=t==null?void 0:t.endTime)==null?void 0:w.slice(0,16))||"",description:(t==null?void 0:t.description)||"",type:(t==null?void 0:t.type)||"event",assignedDriverId:(t==null?void 0:t.assignedDriverId)||""}),[a,l]=S.useState([]),[c,d]=S.useState([]),[m,p]=S.useState(!1),f=async j=>{j.preventDefault(),p(!0),l([]),d([]);try{await n({...s,id:t==null?void 0:t.id,startTime:new Date(s.startTime).toISOString(),endTime:new Date(s.endTime).toISOString(),status:(t==null?void 0:t.status)||"scheduled"})}catch(x){x.validationErrors&&l(x.validationErrors),x.warnings&&d(x.warnings)}finally{p(!1)}},g=j=>{const{name:x,value:u}=j.target;o(h=>({...h,[x]:u}))};return i.jsx("div",{className:"fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50 p-4",children:i.jsxs("div",{className:"bg-white rounded-2xl shadow-2xl border border-slate-200/60 w-full max-w-2xl max-h-[90vh] overflow-y-auto",children:[i.jsxs("div",{className:"bg-gradient-to-r from-blue-50 to-indigo-50 px-8 py-6 border-b border-slate-200/60",children:[i.jsx("h2",{className:"text-xl font-bold text-slate-800",children:t?"āœļø Edit Event":"āž• Add New Event"}),i.jsx("p",{className:"text-slate-600 mt-1",children:t?"Update event details":"Create a new schedule event"})]}),i.jsxs("form",{onSubmit:f,className:"p-8 space-y-6",children:[a.length>0&&i.jsxs("div",{className:"bg-red-50 border border-red-200 rounded-xl p-4",children:[i.jsx("h4",{className:"text-red-800 font-semibold mb-2",children:"āŒ Validation Errors:"}),i.jsx("ul",{className:"text-red-700 space-y-1",children:a.map((j,x)=>i.jsxs("li",{className:"text-sm",children:["• ",j.message]},x))})]}),c.length>0&&i.jsxs("div",{className:"bg-amber-50 border border-amber-200 rounded-xl p-4",children:[i.jsx("h4",{className:"text-amber-800 font-semibold mb-2",children:"āš ļø Warnings:"}),i.jsx("ul",{className:"text-amber-700 space-y-1",children:c.map((j,x)=>i.jsxs("li",{className:"text-sm",children:["• ",j.message]},x))})]}),i.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-6",children:[i.jsxs("div",{className:"md:col-span-2",children:[i.jsx("label",{htmlFor:"title",className:"block text-sm font-medium text-slate-700 mb-2",children:"Event Title"}),i.jsx("input",{type:"text",id:"title",name:"title",value:s.title,onChange:g,className:"w-full px-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors",placeholder:"Enter event title",required:!0})]}),i.jsxs("div",{children:[i.jsx("label",{htmlFor:"type",className:"block text-sm font-medium text-slate-700 mb-2",children:"Event Type"}),i.jsxs("select",{id:"type",name:"type",value:s.type,onChange:g,className:"w-full px-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors",required:!0,children:[i.jsx("option",{value:"transport",children:"šŸš— Transport"}),i.jsx("option",{value:"meeting",children:"šŸ¤ Meeting"}),i.jsx("option",{value:"event",children:"šŸŽ‰ Event"}),i.jsx("option",{value:"meal",children:"šŸ½ļø Meal"}),i.jsx("option",{value:"accommodation",children:"šŸØ Accommodation"})]})]}),i.jsxs("div",{children:[i.jsx("label",{htmlFor:"location",className:"block text-sm font-medium text-slate-700 mb-2",children:"Location"}),i.jsx("input",{type:"text",id:"location",name:"location",value:s.location,onChange:g,className:"w-full px-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors",placeholder:"Enter location",required:!0})]}),i.jsxs("div",{children:[i.jsx("label",{htmlFor:"startTime",className:"block text-sm font-medium text-slate-700 mb-2",children:"Start Time"}),i.jsx("input",{type:"datetime-local",id:"startTime",name:"startTime",value:s.startTime,onChange:g,className:"w-full px-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors",required:!0})]}),i.jsxs("div",{children:[i.jsx("label",{htmlFor:"endTime",className:"block text-sm font-medium text-slate-700 mb-2",children:"End Time"}),i.jsx("input",{type:"datetime-local",id:"endTime",name:"endTime",value:s.endTime,onChange:g,className:"w-full px-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors",required:!0})]}),i.jsxs("div",{className:"md:col-span-2",children:[i.jsx("label",{htmlFor:"description",className:"block text-sm font-medium text-slate-700 mb-2",children:"Description"}),i.jsx("textarea",{id:"description",name:"description",value:s.description,onChange:g,rows:3,className:"w-full px-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors",placeholder:"Enter event description (optional)"})]}),i.jsx("div",{className:"md:col-span-2",children:i.jsx(Bg,{selectedDriverId:s.assignedDriverId,onDriverSelect:j=>o(x=>({...x,assignedDriverId:j})),eventTime:{startTime:s.startTime?new Date(s.startTime).toISOString():"",endTime:s.endTime?new Date(s.endTime).toISOString():"",location:s.location}})})]}),i.jsxs("div",{className:"flex justify-end gap-4 pt-6 border-t border-slate-200",children:[i.jsx("button",{type:"button",className:"px-6 py-3 border border-slate-300 text-slate-700 rounded-lg hover:bg-slate-50 transition-colors font-medium",onClick:r,disabled:m,children:"Cancel"}),i.jsx("button",{type:"submit",className:"bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white px-6 py-3 rounded-lg font-medium transition-all duration-200 shadow-lg hover:shadow-xl disabled:opacity-50 disabled:cursor-not-allowed",disabled:m,children:m?i.jsxs("span",{className:"flex items-center gap-2",children:[i.jsx("div",{className:"w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"}),t?"Updating...":"Creating..."]}):t?"āœļø Update Event":"āž• Create Event"})]})]})]})})},Hg=()=>{const{id:e}=dh(),[t,n]=S.useState(null),[r,s]=S.useState(!0),[o,a]=S.useState(null),[l,c]=S.useState([]);S.useEffect(()=>{e&&(async()=>{try{const g=localStorage.getItem("authToken"),b=await U("/api/vips",{headers:{Authorization:`Bearer ${g}`,"Content-Type":"application/json"}});if(b.ok){const j=(await b.json()).find(x=>x.id===e);j?n(j):a("VIP not found")}else a("Failed to fetch VIP data")}catch{a("Error loading VIP data")}finally{s(!1)}})()},[e]),S.useEffect(()=>{(async()=>{if(t)try{const g=localStorage.getItem("authToken"),b=await U(`/api/vips/${t.id}/schedule`,{headers:{Authorization:`Bearer ${g}`,"Content-Type":"application/json"}});if(b.ok){const w=await b.json();c(w)}}catch(g){console.error("Error fetching schedule:",g)}})()},[t]),S.useEffect(()=>{t&&window.location.hash==="#schedule"&&setTimeout(()=>{const f=document.getElementById("schedule-section");f&&f.scrollIntoView({behavior:"smooth"})},100)},[t]);const d=()=>{if(!t)return null;if(t.transportMode==="flight"){if(t.flights&&t.flights.length>0)return{flights:t.flights,primaryFlight:t.flights[0]};if(t.flightNumber)return{flights:[{flightNumber:t.flightNumber,flightDate:t.flightDate||"",segment:1}],primaryFlight:{flightNumber:t.flightNumber,flightDate:t.flightDate||"",segment:1}}}return null},m=()=>{if(!t)return;const f=window.open("","_blank");if(!f)return;const g=h=>{const y={};return h.forEach(v=>{const N=new Date(v.startTime).toDateString();y[N]||(y[N]=[]),y[N].push(v)}),Object.keys(y).forEach(v=>{y[v].sort((N,k)=>new Date(N.startTime).getTime()-new Date(k.startTime).getTime())}),y},b=h=>new Date(h).toLocaleString([],{hour:"2-digit",minute:"2-digit"}),w=h=>{switch(h){case"transport":return"šŸš—";case"meeting":return"šŸ¤";case"event":return"šŸŽ‰";case"meal":return"šŸ½ļø";case"accommodation":return"šŸØ";default:return"šŸ“…"}},j=g(l),x=d(),u=` - - - - VIP Schedule - ${t.name} - - - - -
-
- -

šŸ“… VIP Schedule

-

${t.name}

-
- -
-

Organization: ${t.organization}

- ${t.transportMode==="flight"&&x?` -

Flight Information: ${x.flights.map(h=>h.flightNumber).join(" → ")}

-

Flight Date: ${x.primaryFlight.flightDate?new Date(x.primaryFlight.flightDate).toLocaleDateString():"TBD"}

- `:t.transportMode==="self-driving"?` -

Transport Mode: šŸš— Self-Driving

-

Expected Arrival: ${t.expectedArrival?new Date(t.expectedArrival).toLocaleString():"TBD"}

- `:""} -

Airport Pickup: ${t.needsAirportPickup?"āœ… Required":"āŒ Not Required"}

-

Venue Transport: ${t.needsVenueTransport?"āœ… Required":"āŒ Not Required"}

- ${t.notes?`

Special Notes: ${t.notes}

`:""} -
- - ${Object.entries(j).map(([h,y])=>` -
-
- ${new Date(h).toLocaleDateString([],{weekday:"long",year:"numeric",month:"long",day:"numeric"})} -
- ${y.map(v=>` -
-
- ${b(v.startTime)} -
to
- ${b(v.endTime)} -
-
-
- ${w(v.type)} - ${v.title} - ${v.status} -
-
- šŸ“ - ${v.location} -
- ${v.description?`
${v.description}
`:""} - ${v.assignedDriverId?`
šŸ‘¤ Driver: ${v.assignedDriverId}
`:""} -
-
- `).join("")} -
- `).join("")} - - -
- - - `;f.document.write(u),f.document.close(),f.focus(),setTimeout(()=>{f.print(),f.close()},250)};if(r)return i.jsx("div",{children:"Loading VIP details..."});if(o||!t)return i.jsxs("div",{children:[i.jsx("h1",{children:"Error"}),i.jsx("p",{children:o||"VIP not found"}),i.jsx(we,{to:"/vips",className:"btn",children:"Back to VIP List"})]});const p=d();return i.jsxs("div",{className:"space-y-8",children:[i.jsx("div",{className:"bg-white rounded-2xl shadow-lg p-8 border border-slate-200/60",children:i.jsxs("div",{className:"flex justify-between items-center",children:[i.jsxs("div",{children:[i.jsxs("h1",{className:"text-3xl font-bold bg-gradient-to-r from-slate-800 to-slate-600 bg-clip-text text-transparent",children:["VIP Details: ",t.name]}),i.jsx("p",{className:"text-slate-600 mt-2",children:"Complete profile and schedule management"})]}),i.jsxs("div",{className:"flex items-center space-x-4",children:[i.jsx("button",{className:"bg-gradient-to-r from-purple-500 to-purple-600 hover:from-purple-600 hover:to-purple-700 text-white px-6 py-3 rounded-lg font-medium transition-all duration-200 shadow-lg hover:shadow-xl flex items-center gap-2",onClick:m,children:"šŸ–Øļø Print Schedule"}),i.jsx(we,{to:"/vips",className:"bg-gradient-to-r from-slate-500 to-slate-600 hover:from-slate-600 hover:to-slate-700 text-white px-6 py-3 rounded-lg font-medium transition-all duration-200 shadow-lg hover:shadow-xl",children:"Back to VIP List"})]})]})}),i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg border border-slate-200/60 overflow-hidden",children:[i.jsxs("div",{className:"bg-gradient-to-r from-blue-50 to-indigo-50 px-8 py-6 border-b border-slate-200/60",children:[i.jsx("h2",{className:"text-xl font-bold text-slate-800 flex items-center gap-2",children:"šŸ“‹ VIP Information"}),i.jsx("p",{className:"text-slate-600 mt-1",children:"Personal details and travel arrangements"})]}),i.jsxs("div",{className:"p-8",children:[i.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-6",children:[i.jsxs("div",{className:"bg-slate-50 rounded-xl p-4 border border-slate-200/60",children:[i.jsx("div",{className:"text-sm font-medium text-slate-500 mb-1",children:"Name"}),i.jsx("div",{className:"text-lg font-bold text-slate-900",children:t.name})]}),i.jsxs("div",{className:"bg-slate-50 rounded-xl p-4 border border-slate-200/60",children:[i.jsx("div",{className:"text-sm font-medium text-slate-500 mb-1",children:"Organization"}),i.jsx("div",{className:"text-lg font-bold text-slate-900",children:t.organization})]}),t.transportMode==="flight"&&p?i.jsxs(i.Fragment,{children:[i.jsxs("div",{className:"bg-blue-50 rounded-xl p-4 border border-blue-200/60",children:[i.jsxs("div",{className:"text-sm font-medium text-blue-600 mb-1",children:["Flight",p.flights.length>1?"s":""]}),i.jsx("div",{className:"text-lg font-bold text-blue-900",children:p.flights.map(f=>f.flightNumber).join(" → ")})]}),i.jsxs("div",{className:"bg-blue-50 rounded-xl p-4 border border-blue-200/60",children:[i.jsx("div",{className:"text-sm font-medium text-blue-600 mb-1",children:"Flight Date"}),i.jsx("div",{className:"text-lg font-bold text-blue-900",children:p.primaryFlight.flightDate?new Date(p.primaryFlight.flightDate).toLocaleDateString():"TBD"})]})]}):t.transportMode==="self-driving"?i.jsxs(i.Fragment,{children:[i.jsxs("div",{className:"bg-green-50 rounded-xl p-4 border border-green-200/60",children:[i.jsx("div",{className:"text-sm font-medium text-green-600 mb-1",children:"Transport Mode"}),i.jsx("div",{className:"text-lg font-bold text-green-900 flex items-center gap-2",children:"šŸš— Self-Driving"})]}),i.jsxs("div",{className:"bg-green-50 rounded-xl p-4 border border-green-200/60",children:[i.jsx("div",{className:"text-sm font-medium text-green-600 mb-1",children:"Expected Arrival"}),i.jsx("div",{className:"text-lg font-bold text-green-900",children:t.expectedArrival?new Date(t.expectedArrival).toLocaleString():"TBD"})]})]}):null,i.jsxs("div",{className:`rounded-xl p-4 border ${t.needsAirportPickup?"bg-green-50 border-green-200/60":"bg-red-50 border-red-200/60"}`,children:[i.jsx("div",{className:`text-sm font-medium mb-1 ${t.needsAirportPickup?"text-green-600":"text-red-600"}`,children:"Airport Pickup"}),i.jsx("div",{className:`text-lg font-bold flex items-center gap-2 ${t.needsAirportPickup?"text-green-900":"text-red-900"}`,children:t.needsAirportPickup?"āœ… Required":"āŒ Not Required"})]}),i.jsxs("div",{className:`rounded-xl p-4 border ${t.needsVenueTransport?"bg-green-50 border-green-200/60":"bg-red-50 border-red-200/60"}`,children:[i.jsx("div",{className:`text-sm font-medium mb-1 ${t.needsVenueTransport?"text-green-600":"text-red-600"}`,children:"Venue Transport"}),i.jsx("div",{className:`text-lg font-bold flex items-center gap-2 ${t.needsVenueTransport?"text-green-900":"text-red-900"}`,children:t.needsVenueTransport?"āœ… Required":"āŒ Not Required"})]})]}),t.notes&&i.jsxs("div",{className:"mt-6",children:[i.jsx("div",{className:"text-sm font-medium text-slate-500 mb-2",children:"Special Notes"}),i.jsx("div",{className:"bg-amber-50 border border-amber-200 rounded-xl p-4",children:i.jsx("p",{className:"text-amber-800",children:t.notes})})]}),t.assignedDriverIds&&t.assignedDriverIds.length>0&&i.jsxs("div",{className:"mt-6",children:[i.jsx("div",{className:"text-sm font-medium text-slate-500 mb-2",children:"Assigned Drivers"}),i.jsx("div",{className:"flex flex-wrap gap-2",children:t.assignedDriverIds.map(f=>i.jsxs("span",{className:"bg-gradient-to-r from-blue-500 to-blue-600 text-white px-4 py-2 rounded-full text-sm font-medium flex items-center gap-2",children:["šŸ‘¤ ",f]},f))})]})]})]}),t.transportMode==="flight"&&p&&i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg border border-slate-200/60 overflow-hidden",children:[i.jsxs("div",{className:"bg-gradient-to-r from-sky-50 to-blue-50 px-8 py-6 border-b border-slate-200/60",children:[i.jsx("h2",{className:"text-xl font-bold text-slate-800 flex items-center gap-2",children:"āœˆļø Flight Information"}),i.jsx("p",{className:"text-slate-600 mt-1",children:"Real-time flight status and details"})]}),i.jsx("div",{className:"p-8 space-y-6",children:p.flights.map((f,g)=>i.jsxs("div",{className:"bg-slate-50 rounded-xl p-6 border border-slate-200/60",children:[i.jsxs("h3",{className:"text-lg font-bold text-slate-900 mb-4",children:[g===0?"Primary Flight":`Connecting Flight ${g}`,": ",f.flightNumber]}),i.jsx(ph,{flightNumber:f.flightNumber,flightDate:f.flightDate})]},g))})]}),i.jsx("div",{id:"schedule-section",children:i.jsx(Kg,{vipId:t.id,vipName:t.name})})]})},Gg=({onSubmit:e,onCancel:t})=>{const[n,r]=S.useState({name:"",phone:"",vehicleCapacity:4}),s=a=>{a.preventDefault(),e(n)},o=a=>{const{name:l,value:c,type:d}=a.target;r(m=>({...m,[l]:d==="number"||l==="vehicleCapacity"?parseInt(c)||0:c}))};return i.jsx("div",{className:"modal-overlay",children:i.jsxs("div",{className:"modal-content",children:[i.jsxs("div",{className:"modal-header",children:[i.jsx("h2",{className:"text-2xl font-bold text-slate-800",children:"Add New Driver"}),i.jsx("p",{className:"text-slate-600 mt-2",children:"Enter driver contact information"})]}),i.jsx("div",{className:"modal-body",children:i.jsxs("form",{onSubmit:s,className:"space-y-6",children:[i.jsxs("div",{className:"form-group",children:[i.jsx("label",{htmlFor:"name",className:"form-label",children:"Driver Name *"}),i.jsx("input",{type:"text",id:"name",name:"name",value:n.name,onChange:o,className:"form-input",placeholder:"Enter driver's full name",required:!0})]}),i.jsxs("div",{className:"form-group",children:[i.jsx("label",{htmlFor:"phone",className:"form-label",children:"Phone Number *"}),i.jsx("input",{type:"tel",id:"phone",name:"phone",value:n.phone,onChange:o,className:"form-input",placeholder:"Enter phone number",required:!0})]}),i.jsxs("div",{className:"form-group",children:[i.jsx("label",{htmlFor:"vehicleCapacity",className:"form-label",children:"Vehicle Capacity *"}),i.jsxs("select",{id:"vehicleCapacity",name:"vehicleCapacity",value:n.vehicleCapacity,onChange:o,className:"form-input",required:!0,children:[i.jsx("option",{value:2,children:"2 passengers (Sedan/Coupe)"}),i.jsx("option",{value:4,children:"4 passengers (Standard Car)"}),i.jsx("option",{value:6,children:"6 passengers (SUV/Van)"}),i.jsx("option",{value:8,children:"8 passengers (Large Van)"}),i.jsx("option",{value:12,children:"12 passengers (Mini Bus)"})]}),i.jsx("p",{className:"text-sm text-slate-600 mt-1",children:"šŸš— Select the maximum number of passengers this vehicle can accommodate"})]}),i.jsxs("div",{className:"form-actions",children:[i.jsx("button",{type:"button",className:"btn btn-secondary",onClick:t,children:"Cancel"}),i.jsx("button",{type:"submit",className:"btn btn-primary",children:"Add Driver"})]})]})})]})})},Zg=({driver:e,onSubmit:t,onCancel:n})=>{const[r,s]=S.useState({name:e.name,phone:e.phone,vehicleCapacity:e.vehicleCapacity||4,currentLocation:{lat:e.currentLocation.lat,lng:e.currentLocation.lng}}),o=l=>{l.preventDefault(),t({...r,id:e.id})},a=l=>{const{name:c,value:d,type:m}=l.target;s(c==="lat"||c==="lng"?p=>({...p,currentLocation:{...p.currentLocation,[c]:parseFloat(d)||0}}):c==="vehicleCapacity"?p=>({...p,[c]:parseInt(d)||0}):p=>({...p,[c]:d}))};return i.jsx("div",{className:"modal-overlay",children:i.jsxs("div",{className:"modal-content",children:[i.jsxs("div",{className:"modal-header",children:[i.jsx("h2",{className:"text-2xl font-bold text-slate-800",children:"Edit Driver"}),i.jsxs("p",{className:"text-slate-600 mt-2",children:["Update driver information for ",e.name]})]}),i.jsx("div",{className:"modal-body",children:i.jsxs("form",{onSubmit:o,className:"space-y-8",children:[i.jsxs("div",{className:"form-section",children:[i.jsx("div",{className:"form-section-header",children:i.jsx("h3",{className:"form-section-title",children:"Basic Information"})}),i.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-6",children:[i.jsxs("div",{className:"form-group",children:[i.jsx("label",{htmlFor:"name",className:"form-label",children:"Driver Name *"}),i.jsx("input",{type:"text",id:"name",name:"name",value:r.name,onChange:a,className:"form-input",placeholder:"Enter driver's full name",required:!0})]}),i.jsxs("div",{className:"form-group",children:[i.jsx("label",{htmlFor:"phone",className:"form-label",children:"Phone Number *"}),i.jsx("input",{type:"tel",id:"phone",name:"phone",value:r.phone,onChange:a,className:"form-input",placeholder:"Enter phone number",required:!0})]})]}),i.jsxs("div",{className:"form-group",children:[i.jsx("label",{htmlFor:"vehicleCapacity",className:"form-label",children:"Vehicle Capacity *"}),i.jsxs("select",{id:"vehicleCapacity",name:"vehicleCapacity",value:r.vehicleCapacity,onChange:a,className:"form-input",required:!0,children:[i.jsx("option",{value:2,children:"2 passengers (Sedan/Coupe)"}),i.jsx("option",{value:4,children:"4 passengers (Standard Car)"}),i.jsx("option",{value:6,children:"6 passengers (SUV/Van)"}),i.jsx("option",{value:8,children:"8 passengers (Large Van)"}),i.jsx("option",{value:12,children:"12 passengers (Mini Bus)"})]}),i.jsx("p",{className:"text-sm text-slate-600 mt-1",children:"šŸš— Select the maximum number of passengers this vehicle can accommodate"})]})]}),i.jsxs("div",{className:"form-section",children:[i.jsx("div",{className:"form-section-header",children:i.jsx("h3",{className:"form-section-title",children:"Current Location"})}),i.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-6",children:[i.jsxs("div",{className:"form-group",children:[i.jsx("label",{htmlFor:"lat",className:"form-label",children:"Latitude *"}),i.jsx("input",{type:"number",id:"lat",name:"lat",value:r.currentLocation.lat,onChange:a,className:"form-input",placeholder:"Enter latitude",step:"any",required:!0})]}),i.jsxs("div",{className:"form-group",children:[i.jsx("label",{htmlFor:"lng",className:"form-label",children:"Longitude *"}),i.jsx("input",{type:"number",id:"lng",name:"lng",value:r.currentLocation.lng,onChange:a,className:"form-input",placeholder:"Enter longitude",step:"any",required:!0})]})]}),i.jsxs("div",{className:"mt-4 p-4 bg-blue-50 border border-blue-200 rounded-lg",children:[i.jsxs("p",{className:"text-sm text-blue-700",children:[i.jsx("strong",{children:"Current coordinates:"})," ",r.currentLocation.lat.toFixed(6),", ",r.currentLocation.lng.toFixed(6)]}),i.jsx("p",{className:"text-xs text-blue-600 mt-1",children:"You can use GPS coordinates or get them from a mapping service"})]})]}),i.jsxs("div",{className:"form-actions",children:[i.jsx("button",{type:"button",className:"btn btn-secondary",onClick:n,children:"Cancel"}),i.jsx("button",{type:"submit",className:"btn btn-primary",children:"Update Driver"})]})]})})]})})},Jg=()=>{const[e,t]=S.useState([]),[n,r]=S.useState(!0),[s,o]=S.useState(!1),[a,l]=S.useState(null),c=g=>{const b=g.trim().split(" ");return b[b.length-1].toLowerCase()},d=g=>[...g].sort((b,w)=>{const j=c(b.name),x=c(w.name);return j.localeCompare(x)});S.useEffect(()=>{(async()=>{try{const b=localStorage.getItem("authToken"),w=await U("/api/drivers",{headers:{Authorization:`Bearer ${b}`,"Content-Type":"application/json"}});if(w.ok){const j=await w.json(),x=d(j);t(x)}else console.error("Failed to fetch drivers:",w.status)}catch(b){console.error("Error fetching drivers:",b)}finally{r(!1)}})()},[]);const m=async g=>{try{const b=localStorage.getItem("authToken"),w=await U("/api/drivers",{method:"POST",headers:{Authorization:`Bearer ${b}`,"Content-Type":"application/json"},body:JSON.stringify(g)});if(w.ok){const j=await w.json();t(x=>d([...x,j])),o(!1)}else console.error("Failed to add driver:",w.status)}catch(b){console.error("Error adding driver:",b)}},p=async g=>{try{const b=localStorage.getItem("authToken"),w=await U(`/api/drivers/${g.id}`,{method:"PUT",headers:{Authorization:`Bearer ${b}`,"Content-Type":"application/json"},body:JSON.stringify(g)});if(w.ok){const j=await w.json();t(x=>d(x.map(u=>u.id===j.id?j:u))),l(null)}else console.error("Failed to update driver:",w.status)}catch(b){console.error("Error updating driver:",b)}},f=async g=>{if(confirm("Are you sure you want to delete this driver?"))try{const b=localStorage.getItem("authToken"),w=await U(`/api/drivers/${g}`,{method:"DELETE",headers:{Authorization:`Bearer ${b}`,"Content-Type":"application/json"}});w.ok?t(j=>j.filter(x=>x.id!==g)):console.error("Failed to delete driver:",w.status)}catch(b){console.error("Error deleting driver:",b)}};return n?i.jsx("div",{className:"flex justify-center items-center min-h-64",children:i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg p-8 flex items-center space-x-4",children:[i.jsx("div",{className:"w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full animate-spin"}),i.jsx("span",{className:"text-lg font-medium text-slate-700",children:"Loading drivers..."})]})}):i.jsxs("div",{className:"space-y-8",children:[i.jsx("div",{className:"bg-white rounded-2xl shadow-lg p-8 border border-slate-200/60",children:i.jsxs("div",{className:"flex justify-between items-center",children:[i.jsxs("div",{children:[i.jsx("h1",{className:"text-3xl font-bold bg-gradient-to-r from-slate-800 to-slate-600 bg-clip-text text-transparent",children:"Driver Management"}),i.jsx("p",{className:"text-slate-600 mt-2",children:"Manage driver profiles and assignments"})]}),i.jsxs("div",{className:"flex items-center space-x-4",children:[i.jsxs("div",{className:"bg-gradient-to-r from-green-500 to-green-600 text-white px-4 py-2 rounded-lg text-sm font-medium",children:[e.length," Active Drivers"]}),i.jsx("button",{className:"btn btn-primary",onClick:()=>o(!0),children:"Add New Driver"})]})]})}),e.length===0?i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg p-12 border border-slate-200/60 text-center",children:[i.jsx("div",{className:"w-16 h-16 bg-slate-100 rounded-full flex items-center justify-center mx-auto mb-4",children:i.jsx("div",{className:"w-8 h-8 bg-slate-300 rounded-full"})}),i.jsx("h3",{className:"text-lg font-semibold text-slate-800 mb-2",children:"No Drivers Found"}),i.jsx("p",{className:"text-slate-600 mb-6",children:"Get started by adding your first driver"}),i.jsx("button",{className:"btn btn-primary",onClick:()=>o(!0),children:"Add New Driver"})]}):i.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6",children:e.map(g=>i.jsx("div",{className:"bg-white rounded-2xl shadow-lg border border-slate-200/60 overflow-hidden hover:shadow-xl transition-shadow duration-200",children:i.jsxs("div",{className:"p-6",children:[i.jsxs("div",{className:"flex items-center justify-between mb-4",children:[i.jsx("h3",{className:"text-xl font-bold text-slate-900",children:g.name}),i.jsx("div",{className:"w-10 h-10 bg-gradient-to-br from-green-400 to-green-600 rounded-full flex items-center justify-center",children:i.jsx("span",{className:"text-white text-sm font-bold",children:g.name.charAt(0).toUpperCase()})})]}),i.jsxs("div",{className:"space-y-3 mb-6",children:[i.jsxs("div",{className:"bg-slate-50 rounded-lg p-3",children:[i.jsx("div",{className:"text-sm font-medium text-slate-700 mb-1",children:"Contact"}),i.jsx("div",{className:"text-slate-600",children:g.phone})]}),i.jsxs("div",{className:"bg-slate-50 rounded-lg p-3",children:[i.jsx("div",{className:"text-sm font-medium text-slate-700 mb-1",children:"Current Location"}),i.jsxs("div",{className:"text-slate-600 text-sm",children:[g.currentLocation.lat.toFixed(4),", ",g.currentLocation.lng.toFixed(4)]})]}),i.jsxs("div",{className:"bg-slate-50 rounded-lg p-3",children:[i.jsx("div",{className:"text-sm font-medium text-slate-700 mb-1",children:"Vehicle Capacity"}),i.jsxs("div",{className:"flex items-center gap-2 text-slate-600",children:[i.jsx("span",{children:"šŸš—"}),i.jsxs("span",{className:"font-medium",children:[g.vehicleCapacity||4," passengers"]})]})]}),i.jsxs("div",{className:"bg-slate-50 rounded-lg p-3",children:[i.jsx("div",{className:"text-sm font-medium text-slate-700 mb-1",children:"Assignments"}),i.jsxs("div",{className:"flex items-center gap-2",children:[i.jsxs("span",{className:"bg-blue-100 text-blue-800 text-xs font-medium px-2 py-1 rounded-full",children:[g.assignedVipIds.length," VIPs"]}),i.jsx("span",{className:`text-xs font-medium px-2 py-1 rounded-full ${g.assignedVipIds.length===0?"bg-green-100 text-green-800":"bg-amber-100 text-amber-800"}`,children:g.assignedVipIds.length===0?"Available":"Assigned"})]})]})]}),i.jsxs("div",{className:"space-y-3",children:[i.jsx(we,{to:`/drivers/${g.id}`,className:"bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 shadow-sm hover:shadow-md w-full text-center block",children:"View Dashboard"}),i.jsxs("div",{className:"flex gap-2",children:[i.jsx("button",{className:"bg-gradient-to-r from-slate-500 to-slate-600 hover:from-slate-600 hover:to-slate-700 text-white px-3 py-2 rounded-lg text-xs font-medium transition-all duration-200 shadow-sm hover:shadow-md flex-1",onClick:()=>l(g),children:"Edit"}),i.jsx("button",{className:"bg-gradient-to-r from-red-500 to-red-600 hover:from-red-600 hover:to-red-700 text-white px-3 py-2 rounded-lg text-xs font-medium transition-all duration-200 shadow-sm hover:shadow-md flex-1",onClick:()=>f(g.id),children:"Delete"})]})]})]})},g.id))}),s&&i.jsx(Gg,{onSubmit:m,onCancel:()=>o(!1)}),a&&i.jsx(Zg,{driver:a,onSubmit:p,onCancel:()=>l(null)})]})},Xg=({events:e,driverName:t})=>{const n=f=>{switch(f){case"transport":return"#3498db";case"meeting":return"#9b59b6";case"event":return"#e74c3c";case"meal":return"#f39c12";case"accommodation":return"#2ecc71";default:return"#95a5a6"}},r=f=>{switch(f){case"completed":return .5;case"cancelled":return .3;case"in-progress":return 1;case"scheduled":return .8;default:return .8}},s=f=>{switch(f){case"transport":return"šŸš—";case"meeting":return"šŸ¤";case"event":return"šŸŽ‰";case"meal":return"šŸ½ļø";case"accommodation":return"šŸØ";default:return"šŸ“…"}},o=f=>new Date(f).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}),a=()=>{if(e.length===0)return{start:new Date,end:new Date};const f=e.flatMap(x=>[new Date(x.startTime),new Date(x.endTime)]),g=new Date(Math.min(...f.map(x=>x.getTime()))),b=new Date(Math.max(...f.map(x=>x.getTime()))),w=new Date(g.getTime()-30*60*1e3),j=new Date(b.getTime()+30*60*1e3);return{start:w,end:j}},l=(f,g)=>{const b=g.end.getTime()-g.start.getTime(),w=new Date(f.startTime),j=new Date(f.endTime),x=w.getTime()-g.start.getTime(),u=j.getTime()-w.getTime(),h=x/b*100,y=u/b*100;return{left:h,width:y}},c=f=>{const g=[],b=new Date(f.start);for(b.setMinutes(0,0,0);b<=f.end;)g.push(new Date(b)),b.setHours(b.getHours()+1);return g};if(e.length===0)return i.jsxs("div",{className:"card",children:[i.jsx("h3",{children:"šŸ“Š Schedule Gantt Chart"}),i.jsx("p",{children:"No events to display in Gantt chart."})]});const d=a(),m=c(d),p=d.end.getTime()-d.start.getTime();return i.jsxs("div",{className:"card",children:[i.jsxs("h3",{children:["šŸ“Š Schedule Gantt Chart - ",t]}),i.jsxs("div",{style:{marginBottom:"1rem",fontSize:"0.9rem",color:"#666"},children:["Timeline: ",d.start.toLocaleDateString()," ",o(d.start.toISOString())," - ",o(d.end.toISOString())]}),i.jsxs("div",{style:{border:"1px solid #ddd",borderRadius:"6px",overflow:"hidden",backgroundColor:"#fff"},children:[i.jsx("div",{style:{display:"flex",borderBottom:"2px solid #333",backgroundColor:"#f8f9fa",position:"relative",height:"40px",alignItems:"center"},children:m.map((f,g)=>{const b=(f.getTime()-d.start.getTime())/p*100;return i.jsx("div",{style:{position:"absolute",left:`${b}%`,transform:"translateX(-50%)",fontSize:"0.8rem",fontWeight:"bold",color:"#333",whiteSpace:"nowrap"},children:o(f.toISOString())},g)})}),i.jsx("div",{style:{padding:"1rem 0"},children:e.map((f,g)=>{const b=l(f,d);return i.jsxs("div",{style:{position:"relative",height:"60px",marginBottom:"8px",borderRadius:"4px",border:"1px solid #e9ecef"},children:[i.jsxs("div",{style:{position:"absolute",left:`${b.left}%`,width:`${b.width}%`,height:"100%",backgroundColor:n(f.type),opacity:r(f.status),borderRadius:"4px",display:"flex",alignItems:"center",padding:"0 8px",color:"white",fontSize:"0.8rem",fontWeight:"bold",overflow:"hidden",boxShadow:"0 2px 4px rgba(0,0,0,0.1)",cursor:"pointer",transition:"transform 0.2s ease"},onMouseEnter:w=>{w.currentTarget.style.transform="scale(1.02)",w.currentTarget.style.zIndex="10"},onMouseLeave:w=>{w.currentTarget.style.transform="scale(1)",w.currentTarget.style.zIndex="1"},title:`${f.title} -${f.location} -${f.vipName} -${o(f.startTime)} - ${o(f.endTime)}`,children:[i.jsx("span",{style:{marginRight:"4px"},children:s(f.type)}),i.jsx("span",{style:{overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",flex:1},children:f.title})]}),b.width<15&&i.jsxs("div",{style:{position:"absolute",left:`${b.left+b.width+1}%`,top:"50%",transform:"translateY(-50%)",fontSize:"0.7rem",color:"#666",whiteSpace:"nowrap",backgroundColor:"#f8f9fa",padding:"2px 6px",borderRadius:"3px",border:"1px solid #e9ecef"},children:[s(f.type)," ",f.title," - ",f.vipName]})]},f.id)})}),i.jsxs("div",{style:{borderTop:"1px solid #ddd",padding:"1rem",backgroundColor:"#f8f9fa"},children:[i.jsx("div",{style:{fontSize:"0.8rem",fontWeight:"bold",marginBottom:"0.5rem"},children:"Event Types:"}),i.jsx("div",{style:{display:"flex",flexWrap:"wrap",gap:"1rem"},children:[{type:"transport",label:"Transport"},{type:"meeting",label:"Meetings"},{type:"meal",label:"Meals"},{type:"event",label:"Events"},{type:"accommodation",label:"Accommodation"}].map(({type:f,label:g})=>i.jsxs("div",{style:{display:"flex",alignItems:"center",gap:"0.25rem"},children:[i.jsx("div",{style:{width:"16px",height:"16px",backgroundColor:n(f),borderRadius:"2px"}}),i.jsxs("span",{style:{fontSize:"0.7rem"},children:[s(f)," ",g]})]},f))})]})]})]})},Yg=()=>{const{driverId:e}=dh(),[t,n]=S.useState(null),[r,s]=S.useState(!0),[o,a]=S.useState(null);S.useEffect(()=>{e&&l()},[e]);const l=async()=>{try{const h=localStorage.getItem("authToken"),y=await U(`/api/drivers/${e}/schedule`,{headers:{Authorization:`Bearer ${h}`,"Content-Type":"application/json"}});if(y.ok){const v=await y.json();n(v)}else a("Driver not found")}catch{a("Error loading driver schedule")}finally{s(!1)}},c=h=>{switch(h){case"scheduled":return"#3498db";case"in-progress":return"#f39c12";case"completed":return"#2ecc71";case"cancelled":return"#e74c3c";default:return"#95a5a6"}},d=h=>{switch(h){case"transport":return"šŸš—";case"meeting":return"šŸ¤";case"event":return"šŸŽ‰";case"meal":return"šŸ½ļø";case"accommodation":return"šŸØ";default:return"šŸ“…"}},m=h=>new Date(h).toLocaleString([],{hour:"2-digit",minute:"2-digit"}),p=()=>{if(!(t!=null&&t.schedule))return null;const h=new Date,y=t.schedule.filter(v=>new Date(v.startTime)>h&&v.status==="scheduled").sort((v,N)=>new Date(v.startTime).getTime()-new Date(N.startTime).getTime());return y.length>0?y[0]:null},f=()=>{if(!(t!=null&&t.schedule))return null;const h=new Date;return t.schedule.find(y=>new Date(y.startTime)<=h&&new Date(y.endTime)>h&&y.status==="in-progress")||null},g=h=>{const y={};return h.forEach(v=>{const N=new Date(v.startTime).toDateString();y[N]||(y[N]=[]),y[N].push(v)}),Object.keys(y).forEach(v=>{y[v].sort((N,k)=>new Date(N.startTime).getTime()-new Date(k.startTime).getTime())}),y},b=()=>{if(!t)return;const h=window.open("","_blank");if(!h)return;const y=g(t.schedule),v=` - - - - Driver Schedule - ${t.driver.name} - - - - -
-
- -

Driver Schedule

-

${t.driver.name}

-
- -
-

Driver: ${t.driver.name}

-

Phone: ${t.driver.phone}

-

Total Assignments: ${t.schedule.length}

-
- - ${Object.entries(y).map(([N,k])=>` -
-
- ${new Date(N).toLocaleDateString([],{weekday:"long",year:"numeric",month:"long",day:"numeric"})} -
- ${k.map(C=>` -
-
- ${m(C.startTime)} -
to
- ${m(C.endTime)} -
-
-
- ${d(C.type)} - ${C.title} -
-
- šŸ‘¤ - VIP: ${C.vipName} -
-
- šŸ“ - ${C.location} -
- ${C.description?`
${C.description}
`:""} -
-
- `).join("")} -
- `).join("")} - - -
- - - `;h.document.write(v),h.document.close(),h.focus(),setTimeout(()=>{h.print(),h.close()},250)};async function w(h,y){if(!t)return;const v=t.schedule.find(N=>N.id===h);if(v)try{const N=localStorage.getItem("authToken");(await U(`/api/vips/${v.vipId}/schedule/${h}/status`,{method:"PATCH",headers:{Authorization:`Bearer ${N}`,"Content-Type":"application/json"},body:JSON.stringify({status:y})})).ok&&await l()}catch(N){console.error("Error updating event status:",N)}}if(r)return i.jsx("div",{className:"flex justify-center items-center min-h-64",children:i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg p-8 flex items-center space-x-4",children:[i.jsx("div",{className:"w-8 h-8 border-4 border-red-600 border-t-transparent rounded-full animate-spin"}),i.jsx("span",{className:"text-lg font-medium text-slate-700",children:"Loading driver schedule..."})]})});if(o||!t)return i.jsx("div",{className:"space-y-8",children:i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg p-8 border border-slate-200/60 text-center",children:[i.jsx("div",{className:"w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mx-auto mb-4",children:i.jsx("span",{className:"text-2xl",children:"āŒ"})}),i.jsx("h1",{className:"text-2xl font-bold text-slate-800 mb-2",children:"Error"}),i.jsx("p",{className:"text-slate-600 mb-6",children:o||"Driver not found"}),i.jsx(we,{to:"/drivers",className:"bg-gradient-to-r from-slate-500 to-slate-600 hover:from-slate-600 hover:to-slate-700 text-white px-6 py-3 rounded-lg font-medium transition-all duration-200 shadow-lg hover:shadow-xl",children:"Back to Drivers"})]})});const j=p(),x=f(),u=g(t.schedule);return i.jsxs("div",{className:"space-y-8",children:[i.jsx("div",{className:"bg-white rounded-2xl shadow-lg p-8 border border-slate-200/60",children:i.jsxs("div",{className:"flex justify-between items-center",children:[i.jsxs("div",{children:[i.jsxs("h1",{className:"text-3xl font-bold bg-gradient-to-r from-slate-800 to-slate-600 bg-clip-text text-transparent flex items-center gap-3",children:["šŸš— Driver Dashboard: ",t.driver.name]}),i.jsx("p",{className:"text-slate-600 mt-2",children:"Real-time schedule and assignment management"})]}),i.jsxs("div",{className:"flex items-center space-x-4",children:[i.jsx("button",{className:"bg-gradient-to-r from-red-500 to-red-600 hover:from-red-600 hover:to-red-700 text-white px-6 py-3 rounded-lg font-medium transition-all duration-200 shadow-lg hover:shadow-xl flex items-center gap-2",onClick:b,children:"šŸ–Øļø Print Schedule"}),i.jsx(we,{to:"/drivers",className:"bg-gradient-to-r from-slate-500 to-slate-600 hover:from-slate-600 hover:to-slate-700 text-white px-6 py-3 rounded-lg font-medium transition-all duration-200 shadow-lg hover:shadow-xl",children:"Back to Drivers"})]})]})}),i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg border border-slate-200/60 overflow-hidden",children:[i.jsxs("div",{className:"bg-gradient-to-r from-green-50 to-emerald-50 px-8 py-6 border-b border-slate-200/60",children:[i.jsx("h2",{className:"text-xl font-bold text-slate-800 flex items-center gap-2",children:"šŸ“ Current Status"}),i.jsx("p",{className:"text-slate-600 mt-1",children:"Real-time driver activity and next assignment"})]}),i.jsxs("div",{className:"p-8 space-y-6",children:[x?i.jsxs("div",{className:"bg-gradient-to-r from-amber-50 to-orange-50 border border-amber-200 rounded-xl p-6",children:[i.jsxs("div",{className:"flex items-center gap-3 mb-4",children:[i.jsx("span",{className:"text-2xl",children:d(x.type)}),i.jsxs("div",{children:[i.jsx("h3",{className:"text-lg font-bold text-amber-900",children:"Currently Active"}),i.jsx("p",{className:"text-amber-700 font-semibold",children:x.title})]}),i.jsx("span",{className:"ml-auto px-3 py-1 rounded-full text-xs font-bold text-white",style:{backgroundColor:c(x.status)},children:x.status.toUpperCase()})]}),i.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-4 text-sm",children:[i.jsxs("div",{className:"flex items-center gap-2 text-amber-800",children:[i.jsx("span",{children:"šŸ“"}),i.jsx("span",{children:x.location})]}),i.jsxs("div",{className:"flex items-center gap-2 text-amber-800",children:[i.jsx("span",{children:"šŸ‘¤"}),i.jsxs("span",{children:["VIP: ",x.vipName]})]}),i.jsxs("div",{className:"flex items-center gap-2 text-amber-800",children:[i.jsx("span",{children:"ā°"}),i.jsxs("span",{children:["Until ",m(x.endTime)]})]})]})]}):i.jsx("div",{className:"bg-gradient-to-r from-green-50 to-emerald-50 border border-green-200 rounded-xl p-6",children:i.jsxs("div",{className:"flex items-center gap-3",children:[i.jsx("span",{className:"text-2xl",children:"āœ…"}),i.jsxs("div",{children:[i.jsx("h3",{className:"text-lg font-bold text-green-900",children:"Currently Available"}),i.jsx("p",{className:"text-green-700",children:"Ready for next assignment"})]})]})}),j&&i.jsxs("div",{className:"bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-200 rounded-xl p-6",children:[i.jsxs("div",{className:"flex items-center gap-3 mb-4",children:[i.jsx("span",{className:"text-2xl",children:d(j.type)}),i.jsxs("div",{children:[i.jsx("h3",{className:"text-lg font-bold text-blue-900",children:"Next Assignment"}),i.jsx("p",{className:"text-blue-700 font-semibold",children:j.title})]})]}),i.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-4 text-sm mb-4",children:[i.jsxs("div",{className:"flex items-center gap-2 text-blue-800",children:[i.jsx("span",{children:"šŸ“"}),i.jsx("span",{children:j.location})]}),i.jsxs("div",{className:"flex items-center gap-2 text-blue-800",children:[i.jsx("span",{children:"šŸ‘¤"}),i.jsxs("span",{children:["VIP: ",j.vipName]})]}),i.jsxs("div",{className:"flex items-center gap-2 text-blue-800",children:[i.jsx("span",{children:"ā°"}),i.jsxs("span",{children:[m(j.startTime)," - ",m(j.endTime)]})]})]}),i.jsx("button",{className:"bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 shadow-lg hover:shadow-xl flex items-center gap-2",onClick:()=>window.open(`https://maps.google.com/?q=${encodeURIComponent(j.location)}`,"_blank"),children:"šŸ—ŗļø Get Directions"})]})]})]}),i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg border border-slate-200/60 overflow-hidden",children:[i.jsxs("div",{className:"bg-gradient-to-r from-purple-50 to-pink-50 px-8 py-6 border-b border-slate-200/60",children:[i.jsxs("h2",{className:"text-xl font-bold text-slate-800 flex items-center gap-2",children:["šŸ“… Complete Schedule",i.jsxs("span",{className:"bg-purple-100 text-purple-800 text-sm font-medium px-2.5 py-0.5 rounded-full",children:[t.schedule.length," assignments"]})]}),i.jsx("p",{className:"text-slate-600 mt-1",children:"All scheduled events and assignments"})]}),i.jsx("div",{className:"p-8",children:t.schedule.length===0?i.jsxs("div",{className:"text-center py-12",children:[i.jsx("div",{className:"w-16 h-16 bg-slate-100 rounded-full flex items-center justify-center mx-auto mb-4",children:i.jsx("span",{className:"text-2xl",children:"šŸ“…"})}),i.jsx("p",{className:"text-slate-500 font-medium",children:"No assignments scheduled"})]}):i.jsx("div",{className:"space-y-8",children:Object.entries(u).map(([h,y])=>i.jsxs("div",{className:"space-y-4",children:[i.jsx("div",{className:"bg-gradient-to-r from-slate-600 to-slate-700 text-white px-6 py-3 rounded-xl shadow-lg",children:i.jsx("h3",{className:"text-lg font-bold",children:new Date(h).toLocaleDateString([],{weekday:"long",year:"numeric",month:"long",day:"numeric"})})}),i.jsx("div",{className:"grid gap-4",children:y.map(v=>i.jsx("div",{className:"bg-gradient-to-r from-slate-50 to-slate-100 rounded-xl border border-slate-200/60 p-6 hover:shadow-lg transition-all duration-200",children:i.jsxs("div",{className:"flex items-start gap-6",children:[i.jsx("div",{className:"flex-shrink-0 text-center",children:i.jsxs("div",{className:"bg-white rounded-lg border border-slate-200 p-3 shadow-sm",children:[i.jsx("div",{className:"text-sm font-bold text-slate-900",children:m(v.startTime)}),i.jsx("div",{className:"text-xs text-slate-500 mt-1",children:"to"}),i.jsx("div",{className:"text-sm font-bold text-slate-900",children:m(v.endTime)})]})}),i.jsxs("div",{className:"flex-1",children:[i.jsxs("div",{className:"flex items-center gap-3 mb-3",children:[i.jsx("span",{className:"text-2xl",children:d(v.type)}),i.jsx("h4",{className:"text-lg font-bold text-slate-900",children:v.title}),i.jsx("span",{className:"px-3 py-1 rounded-full text-xs font-bold text-white shadow-sm",style:{backgroundColor:c(v.status)},children:v.status.toUpperCase()})]}),i.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-3 mb-4",children:[i.jsxs("div",{className:"flex items-center gap-2 text-slate-600",children:[i.jsx("span",{children:"šŸ“"}),i.jsx("span",{className:"font-medium",children:v.location})]}),i.jsxs("div",{className:"flex items-center gap-2 text-slate-600",children:[i.jsx("span",{children:"šŸ‘¤"}),i.jsxs("span",{className:"font-medium",children:["VIP: ",v.vipName]})]})]}),v.description&&i.jsx("div",{className:"text-slate-600 mb-4 bg-white/50 rounded-lg p-3 border border-slate-200/50",children:v.description}),i.jsxs("div",{className:"flex items-center gap-3",children:[i.jsx("button",{className:"bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 shadow-lg hover:shadow-xl flex items-center gap-2",onClick:()=>window.open(`https://maps.google.com/?q=${encodeURIComponent(v.location)}`,"_blank"),children:"šŸ—ŗļø Directions"}),v.status==="scheduled"&&i.jsx("button",{className:"bg-gradient-to-r from-amber-500 to-orange-500 hover:from-amber-600 hover:to-orange-600 text-white px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 shadow-lg hover:shadow-xl flex items-center gap-2",onClick:()=>w(v.id,"in-progress"),children:"ā–¶ļø Start"}),v.status==="in-progress"&&i.jsx("button",{className:"bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 shadow-lg hover:shadow-xl flex items-center gap-2",onClick:()=>w(v.id,"completed"),children:"āœ… Complete"}),v.status==="completed"&&i.jsx("span",{className:"bg-green-100 text-green-800 px-3 py-1 rounded-full text-xs font-medium flex items-center gap-1",children:"āœ… Completed"})]})]})]})},v.id))})]},h))})})]}),i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg border border-slate-200/60 overflow-hidden",children:[i.jsxs("div",{className:"bg-gradient-to-r from-indigo-50 to-purple-50 px-8 py-6 border-b border-slate-200/60",children:[i.jsx("h2",{className:"text-xl font-bold text-slate-800 flex items-center gap-2",children:"šŸ“Š Schedule Timeline"}),i.jsx("p",{className:"text-slate-600 mt-1",children:"Visual timeline of all assignments"})]}),i.jsx("div",{className:"p-8",children:i.jsx(Xg,{events:t.schedule,driverName:t.driver.name})})]})]})},Qg=()=>{const[e,t]=S.useState([]),[n,r]=S.useState([]),[s,o]=S.useState(!0),a=d=>{const m=new Date;return d.find(p=>new Date(p.startTime)<=m&&new Date(p.endTime)>m&&p.status==="in-progress")||null},l=d=>{const m=new Date,p=d.filter(f=>new Date(f.startTime)>m&&f.status==="scheduled").sort((f,g)=>new Date(f.startTime).getTime()-new Date(g.startTime).getTime());return p.length>0?p[0]:null},c=d=>new Date(d).toLocaleString([],{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"});return S.useEffect(()=>{(async()=>{try{const p={Authorization:`Bearer ${localStorage.getItem("authToken")}`,"Content-Type":"application/json"},[f,g]=await Promise.all([U("/api/vips",{headers:p}),U("/api/drivers",{headers:p})]);if(!f.ok||!g.ok)throw new Error("Failed to fetch data");const b=await f.json(),w=await g.json(),x=(await Promise.all(b.map(async u=>{try{const h=await U(`/api/vips/${u.id}/schedule`,{headers:p});if(h.ok){const y=await h.json(),v=a(y),N=l(y);return{...u,currentEvent:v,nextEvent:N,nextEventTime:N?N.startTime:null}}else return{...u,currentEvent:null,nextEvent:null,nextEventTime:null}}catch(h){return console.error(`Error fetching schedule for VIP ${u.id}:`,h),{...u,currentEvent:null,nextEvent:null,nextEventTime:null}}}))).sort((u,h)=>u.currentEvent&&!h.currentEvent?-1:!u.currentEvent&&h.currentEvent?1:u.nextEventTime&&h.nextEventTime?new Date(u.nextEventTime).getTime()-new Date(h.nextEventTime).getTime():u.nextEventTime&&!h.nextEventTime?-1:!u.nextEventTime&&h.nextEventTime?1:u.name.localeCompare(h.name));t(x),r(w)}catch(m){console.error("Error fetching data:",m)}finally{o(!1)}})()},[]),s?i.jsx("div",{className:"flex justify-center items-center min-h-64",children:i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg p-8 flex items-center space-x-4",children:[i.jsx("div",{className:"w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full animate-spin"}),i.jsx("span",{className:"text-lg font-medium text-slate-700",children:"Loading dashboard..."})]})}):i.jsxs("div",{className:"space-y-8",children:[i.jsx("div",{className:"bg-white rounded-2xl shadow-lg p-8 border border-slate-200/60",children:i.jsxs("div",{className:"flex items-center justify-between",children:[i.jsxs("div",{children:[i.jsx("h1",{className:"text-3xl font-bold bg-gradient-to-r from-slate-800 to-slate-600 bg-clip-text text-transparent",children:"VIP Coordinator Dashboard"}),i.jsx("p",{className:"text-slate-600 mt-2",children:"Real-time overview of VIP activities and coordination"})]}),i.jsxs("div",{className:"flex items-center space-x-4",children:[i.jsxs("div",{className:"bg-gradient-to-r from-blue-500 to-blue-600 text-white px-4 py-2 rounded-lg text-sm font-medium",children:[e.length," Active VIPs"]}),i.jsxs("div",{className:"bg-gradient-to-r from-green-500 to-green-600 text-white px-4 py-2 rounded-lg text-sm font-medium",children:[n.length," Drivers"]})]})]})}),i.jsxs("div",{className:"grid grid-cols-1 xl:grid-cols-3 gap-8",children:[i.jsx("div",{className:"xl:col-span-2",children:i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg border border-slate-200/60 overflow-hidden",children:[i.jsx("div",{className:"bg-gradient-to-r from-blue-50 to-indigo-50 px-6 py-4 border-b border-slate-200/60",children:i.jsxs("h2",{className:"text-xl font-bold text-slate-800 flex items-center",children:["VIP Status Dashboard",i.jsxs("span",{className:"ml-2 bg-blue-100 text-blue-800 text-sm font-medium px-2.5 py-0.5 rounded-full",children:[e.length," VIPs"]})]})}),i.jsx("div",{className:"p-6",children:e.length===0?i.jsxs("div",{className:"text-center py-12",children:[i.jsx("div",{className:"w-16 h-16 bg-slate-100 rounded-full flex items-center justify-center mx-auto mb-4",children:i.jsx("div",{className:"w-8 h-8 bg-slate-300 rounded-full"})}),i.jsx("p",{className:"text-slate-500 font-medium",children:"No VIPs currently scheduled"})]}):i.jsx("div",{className:"space-y-4",children:e.map(d=>{const m=!!d.currentEvent,p=!!d.nextEvent;return i.jsx("div",{className:` - relative rounded-xl border-2 p-6 transition-all duration-200 hover:shadow-lg - ${m?"border-amber-300 bg-gradient-to-r from-amber-50 to-orange-50":p?"border-blue-300 bg-gradient-to-r from-blue-50 to-indigo-50":"border-slate-200 bg-slate-50"} - `,children:i.jsxs("div",{className:"flex justify-between items-start",children:[i.jsxs("div",{className:"flex-1",children:[i.jsxs("div",{className:"flex items-center gap-3 mb-3",children:[i.jsx("h3",{className:"text-lg font-bold text-slate-900",children:d.name}),m&&i.jsx("span",{className:"bg-gradient-to-r from-amber-500 to-orange-500 text-white px-3 py-1 rounded-full text-xs font-bold animate-pulse",children:"ACTIVE"})]}),i.jsx("p",{className:"text-slate-600 text-sm mb-4",children:d.organization}),d.currentEvent&&i.jsxs("div",{className:"bg-white border border-amber-200 rounded-lg p-4 mb-3 shadow-sm",children:[i.jsx("div",{className:"flex items-center gap-2 mb-2",children:i.jsx("span",{className:"text-amber-600 font-bold text-sm",children:"CURRENT EVENT"})}),i.jsx("div",{className:"flex items-center gap-2 mb-1",children:i.jsx("span",{className:"font-semibold text-slate-900",children:d.currentEvent.title})}),i.jsxs("p",{className:"text-slate-600 text-sm mb-1",children:["Location: ",d.currentEvent.location]}),i.jsxs("p",{className:"text-slate-500 text-xs",children:["Until ",c(d.currentEvent.endTime)]})]}),d.nextEvent&&i.jsxs("div",{className:"bg-white border border-blue-200 rounded-lg p-4 mb-3 shadow-sm",children:[i.jsx("div",{className:"flex items-center gap-2 mb-2",children:i.jsx("span",{className:"text-blue-600 font-bold text-sm",children:"NEXT EVENT"})}),i.jsx("div",{className:"flex items-center gap-2 mb-1",children:i.jsx("span",{className:"font-semibold text-slate-900",children:d.nextEvent.title})}),i.jsxs("p",{className:"text-slate-600 text-sm mb-1",children:["Location: ",d.nextEvent.location]}),i.jsxs("p",{className:"text-slate-500 text-xs",children:[c(d.nextEvent.startTime)," - ",c(d.nextEvent.endTime)]})]}),!d.currentEvent&&!d.nextEvent&&i.jsx("div",{className:"bg-white border border-slate-200 rounded-lg p-4 mb-3",children:i.jsx("p",{className:"text-slate-500 text-sm italic",children:"No scheduled events"})}),i.jsx("div",{className:"flex items-center gap-2 text-xs text-slate-500 bg-white/50 rounded-lg px-3 py-2",children:d.transportMode==="flight"?i.jsxs("span",{children:["Flight: ",d.flights&&d.flights.length>0?d.flights.map(f=>f.flightNumber).join(" → "):d.flightNumber||"TBD"]}):i.jsxs("span",{children:["Self-driving | Expected: ",d.expectedArrival?c(d.expectedArrival):"TBD"]})})]}),i.jsxs("div",{className:"flex flex-col gap-2 ml-6",children:[i.jsx(we,{to:`/vips/${d.id}`,className:"bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 text-center shadow-lg hover:shadow-xl",children:"Details"}),i.jsx(we,{to:`/vips/${d.id}#schedule`,className:"bg-gradient-to-r from-slate-500 to-slate-600 hover:from-slate-600 hover:to-slate-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 text-center shadow-lg hover:shadow-xl",children:"Schedule"})]})]})},d.id)})})})]})}),i.jsxs("div",{className:"space-y-6",children:[i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg border border-slate-200/60 overflow-hidden",children:[i.jsx("div",{className:"bg-gradient-to-r from-green-50 to-emerald-50 px-6 py-4 border-b border-slate-200/60",children:i.jsxs("h2",{className:"text-lg font-bold text-slate-800 flex items-center",children:["Available Drivers",i.jsx("span",{className:"ml-2 bg-green-100 text-green-800 text-sm font-medium px-2.5 py-0.5 rounded-full",children:n.length})]})}),i.jsx("div",{className:"p-6",children:n.length===0?i.jsxs("div",{className:"text-center py-8",children:[i.jsx("div",{className:"w-12 h-12 bg-slate-100 rounded-full flex items-center justify-center mx-auto mb-3",children:i.jsx("div",{className:"w-6 h-6 bg-slate-300 rounded-full"})}),i.jsx("p",{className:"text-slate-500 text-sm",children:"No drivers available"})]}):i.jsx("div",{className:"space-y-3",children:n.map(d=>i.jsx("div",{className:"bg-slate-50 rounded-lg p-4 border border-slate-200",children:i.jsxs("div",{className:"flex items-center justify-between",children:[i.jsxs("div",{children:[i.jsx("h4",{className:"font-semibold text-slate-900",children:d.name}),i.jsx("p",{className:"text-slate-600 text-sm",children:d.phone})]}),i.jsx("div",{className:"text-right",children:i.jsxs("span",{className:"bg-blue-100 text-blue-800 text-xs font-medium px-2 py-1 rounded-full",children:[d.assignedVipIds.length," VIPs"]})})]})},d.id))})})]}),i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg border border-slate-200/60 overflow-hidden",children:[i.jsx("div",{className:"bg-gradient-to-r from-purple-50 to-pink-50 px-6 py-4 border-b border-slate-200/60",children:i.jsx("h2",{className:"text-lg font-bold text-slate-800",children:"Quick Actions"})}),i.jsxs("div",{className:"p-6 space-y-3",children:[i.jsx(we,{to:"/vips",className:"block w-full bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white px-4 py-3 rounded-lg font-medium transition-all duration-200 text-center shadow-lg hover:shadow-xl",children:"Manage VIPs"}),i.jsx(we,{to:"/drivers",className:"block w-full bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 text-white px-4 py-3 rounded-lg font-medium transition-all duration-200 text-center shadow-lg hover:shadow-xl",children:"Manage Drivers"})]})]})]})]})]})},qg=()=>{const e=new Date,t=new Date(e);t.setDate(t.getDate()+1);const n=new Date(e);n.setDate(n.getDate()+2);const r=o=>o.toISOString().split("T")[0],s=o=>{const a=new Date(o);return a.setHours(14,30,0,0),a.toISOString().slice(0,16)};return[{name:"Dr. Sarah Chen",organization:"Stanford University",department:"Admin",transportMode:"flight",flights:[{flightNumber:"UA1234",flightDate:r(t),segment:1},{flightNumber:"DL5678",flightDate:r(t),segment:2}],needsAirportPickup:!0,needsVenueTransport:!0,notes:"Vegetarian meals, requires wheelchair assistance"},{name:"Ambassador Michael Rodriguez",organization:"Embassy of Spain",department:"Admin",transportMode:"self-driving",expectedArrival:s(t),needsVenueTransport:!0,notes:"Security detail required, diplomatic immunity"},{name:"Prof. Aisha Patel",organization:"MIT Technology Review",department:"Admin",transportMode:"flight",flights:[{flightNumber:"AA9876",flightDate:r(n),segment:1}],needsAirportPickup:!0,needsVenueTransport:!1,notes:"Allergic to shellfish, prefers ground floor rooms"},{name:"CEO James Thompson",organization:"TechCorp Industries",department:"Admin",transportMode:"flight",flights:[{flightNumber:"SW2468",flightDate:r(t),segment:1}],needsAirportPickup:!1,needsVenueTransport:!0,notes:"Private jet arrival, has own security team"},{name:"Dr. Elena Volkov",organization:"Russian Academy of Sciences",department:"Admin",transportMode:"self-driving",expectedArrival:s(n),needsVenueTransport:!0,notes:"Interpreter required, kosher meals"},{name:"Minister David Kim",organization:"South Korean Ministry of Education",department:"Admin",transportMode:"flight",flights:[{flightNumber:"KE0123",flightDate:r(t),segment:1},{flightNumber:"UA7890",flightDate:r(t),segment:2},{flightNumber:"DL3456",flightDate:r(t),segment:3}],needsAirportPickup:!0,needsVenueTransport:!0,notes:"Long international flight, may need rest upon arrival"},{name:"Dr. Maria Santos",organization:"University of SĆ£o Paulo",department:"Admin",transportMode:"flight",flights:[{flightNumber:"LH4567",flightDate:r(n),segment:1}],needsAirportPickup:!0,needsVenueTransport:!1,notes:"Speaks Portuguese and English, lactose intolerant"},{name:"Sheikh Ahmed Al-Rashid",organization:"UAE University",department:"Admin",transportMode:"self-driving",expectedArrival:s(t),needsVenueTransport:!0,notes:"Halal meals required, prayer room access needed"},{name:"Prof. Catherine Williams",organization:"Oxford University",department:"Admin",transportMode:"flight",flights:[{flightNumber:"BA1357",flightDate:r(t),segment:1}],needsAirportPickup:!1,needsVenueTransport:!0,notes:"Prefers tea over coffee, has mobility issues"},{name:"Dr. Hiroshi Tanaka",organization:"Tokyo Institute of Technology",department:"Admin",transportMode:"flight",flights:[{flightNumber:"NH0246",flightDate:r(n),segment:1},{flightNumber:"UA8642",flightDate:r(n),segment:2}],needsAirportPickup:!0,needsVenueTransport:!0,notes:"Jet lag concerns, prefers Japanese cuisine when available"},{name:"Ms. Jennifer Walsh",organization:"Walsh Foundation",department:"Office of Development",transportMode:"self-driving",expectedArrival:s(t),needsVenueTransport:!1,notes:"Major donor, prefers informal settings"},{name:"Mr. Robert Sterling",organization:"Sterling Philanthropies",department:"Office of Development",transportMode:"flight",flights:[{flightNumber:"JB1122",flightDate:r(t),segment:1}],needsAirportPickup:!0,needsVenueTransport:!0,notes:"Potential $10M donation, wine enthusiast"},{name:"Mrs. Elizabeth Hartwell",organization:"Hartwell Family Trust",department:"Office of Development",transportMode:"flight",flights:[{flightNumber:"AS3344",flightDate:r(n),segment:1}],needsAirportPickup:!1,needsVenueTransport:!0,notes:"Alumni donor, interested in scholarship programs"},{name:"Mr. Charles Montgomery",organization:"Montgomery Industries",department:"Office of Development",transportMode:"self-driving",expectedArrival:s(n),needsVenueTransport:!0,notes:"Corporate partnership opportunity, golf enthusiast"},{name:"Dr. Patricia Lee",organization:"Lee Medical Foundation",department:"Office of Development",transportMode:"flight",flights:[{flightNumber:"F91234",flightDate:r(t),segment:1},{flightNumber:"UA5555",flightDate:r(t),segment:2}],needsAirportPickup:!0,needsVenueTransport:!1,notes:"Medical research funding, diabetic dietary needs"},{name:"Mr. Thomas Anderson",organization:"Anderson Capital Group",department:"Office of Development",transportMode:"flight",flights:[{flightNumber:"VX7788",flightDate:r(t),segment:1}],needsAirportPickup:!0,needsVenueTransport:!0,notes:"Venture capital investor, tech startup focus"},{name:"Mrs. Grace Chen-Williams",organization:"Chen-Williams Foundation",department:"Office of Development",transportMode:"self-driving",expectedArrival:s(t),needsVenueTransport:!0,notes:"Arts and culture patron, vegan diet"},{name:"Mr. Daniel Foster",organization:"Foster Energy Solutions",department:"Office of Development",transportMode:"flight",flights:[{flightNumber:"WN9999",flightDate:r(n),segment:1}],needsAirportPickup:!1,needsVenueTransport:!1,notes:"Renewable energy focus, environmental sustainability"},{name:"Mrs. Victoria Blackstone",organization:"Blackstone Charitable Trust",department:"Office of Development",transportMode:"flight",flights:[{flightNumber:"B61111",flightDate:r(n),segment:1},{flightNumber:"AA2222",flightDate:r(n),segment:2}],needsAirportPickup:!0,needsVenueTransport:!0,notes:"Education advocate, prefers luxury accommodations"},{name:"Mr. Alexander Petrov",organization:"Petrov International Holdings",department:"Office of Development",transportMode:"self-driving",expectedArrival:s(n),needsVenueTransport:!0,notes:"International business, speaks Russian and English"}]},Ec=()=>["Stanford University","Embassy of Spain","MIT Technology Review","TechCorp Industries","Russian Academy of Sciences","South Korean Ministry of Education","University of SĆ£o Paulo","UAE University","Oxford University","Tokyo Institute of Technology","Walsh Foundation","Sterling Philanthropies","Hartwell Family Trust","Montgomery Industries","Lee Medical Foundation","Anderson Capital Group","Chen-Williams Foundation","Foster Energy Solutions","Blackstone Charitable Trust","Petrov International Holdings"],e0=(e,t,n)=>{const r=new Date,s=new Date(r);s.setDate(s.getDate()+1);const o=(l,c=0)=>{const d=new Date(s);return d.setHours(l,c,0,0),d.toISOString()},a=[{title:n==="flight"?"Airport Pickup":"Arrival Check-in",location:n==="flight"?"Airport Terminal":"Hotel Lobby",startTime:o(8,0),endTime:o(9,0),description:n==="flight"?"Meet and greet at airport, transport to hotel":"Check-in and welcome briefing",type:"transport",status:"scheduled"},{title:"Welcome Breakfast",location:"Executive Dining Room",startTime:o(9,0),endTime:o(10,0),description:"Welcome breakfast with key stakeholders and orientation materials",type:"meal",status:"scheduled"}];return t==="Admin"?[...a,{title:"Academic Leadership Meeting",location:"Board Room A",startTime:o(10,30),endTime:o(12,0),description:"Strategic planning session with academic leadership team",type:"meeting",status:"scheduled"},{title:"Working Lunch",location:"Faculty Club",startTime:o(12,0),endTime:o(13,30),description:"Lunch meeting with department heads and key faculty",type:"meal",status:"scheduled"},{title:"Campus Tour",location:"Main Campus",startTime:o(14,0),endTime:o(15,30),description:"Guided tour of campus facilities and research centers",type:"event",status:"scheduled"},{title:"Research Presentation",location:"Auditorium B",startTime:o(16,0),endTime:o(17,30),description:"Presentation of current research initiatives and future plans",type:"meeting",status:"scheduled"},{title:"Reception Dinner",location:"University Club",startTime:o(19,0),endTime:o(21,0),description:"Formal dinner reception with university leadership",type:"event",status:"scheduled"}]:[...a,{title:"Donor Relations Meeting",location:"Development Office",startTime:o(10,30),endTime:o(12,0),description:"Private meeting with development team about giving opportunities",type:"meeting",status:"scheduled"},{title:"Scholarship Recipients Lunch",location:"Student Center",startTime:o(12,0),endTime:o(13,30),description:"Meet with current scholarship recipients and hear their stories",type:"meal",status:"scheduled"},{title:"Facility Naming Ceremony",location:"New Science Building",startTime:o(14,0),endTime:o(15,0),description:"Dedication ceremony for newly named facility",type:"event",status:"scheduled"},{title:"Impact Presentation",location:"Conference Room C",startTime:o(15,30),endTime:o(16,30),description:"Presentation on the impact of philanthropic giving",type:"meeting",status:"scheduled"},{title:"Private Dinner",location:"Presidents House",startTime:o(18,30),endTime:o(20,30),description:"Intimate dinner with university president and spouse",type:"meal",status:"scheduled"},{title:"Evening Cultural Event",location:"Arts Center",startTime:o(21,0),endTime:o(22,30),description:"Special performance by university arts programs",type:"event",status:"scheduled"}]},t0=()=>{const e=ch(),[t,n]=S.useState({}),[r,s]=S.useState({}),[o,a]=S.useState({}),[l,c]=S.useState(!0),[d,m]=S.useState(!1),[p,f]=S.useState(null),[g,b]=S.useState({}),[w,j]=S.useState({}),[x,u]=S.useState(!1),[h,y]=S.useState(null),[v,N]=S.useState(null),k=(P=!1)=>{const L={},E=typeof window<"u"?localStorage.getItem("authToken"):null;return E&&(L.Authorization=`Bearer ${E}`),P&&(L["Content-Type"]="application/json"),L},C=async()=>{try{c(!0),N(null);const P=await U("/api/admin/settings",{headers:k()});if(P.ok){const L=await P.json(),E={},D={},O={};L.apiKeys&&Object.entries(L.apiKeys).forEach(([$,F])=>{F&&F.startsWith("***")?(E[$]=!0,D[$]=F):F&&(O[$]=F)}),b(E),j(D),n(O),s(L.systemSettings||{})}else P.status===403?N("You need administrator access to view this page."):P.status===401?N("Authentication required. Please sign in again."):N("Failed to load admin settings.")}catch(P){console.error("Failed to load settings:",P),N("Failed to load admin settings.")}finally{c(!1)}};S.useEffect(()=>{C()},[]);const z=(P,L)=>{n(E=>({...E,[P]:L})),L&&!L.startsWith("***")&&(b(E=>({...E,[P]:!1})),j(E=>{const D={...E};return delete D[P],D}))},A=(P,L)=>{s(E=>({...E,[P]:L}))},I=async P=>{a(L=>({...L,[P]:"Testing..."}));try{const L=await U(`/api/admin/test-api/${P}`,{method:"POST",headers:k(!0),body:JSON.stringify({apiKey:t[P]})}),E=await L.json();L.ok?a(D=>({...D,[P]:`Success: ${E.message}`})):a(D=>({...D,[P]:`Failed: ${E.error}`}))}catch{a(E=>({...E,[P]:"Connection error"}))}},M=async()=>{m(!0),f(null);try{(await U("/api/admin/settings",{method:"POST",headers:k(!0),body:JSON.stringify({apiKeys:t,systemSettings:r})})).ok?(f("Settings saved successfully!"),await C(),setTimeout(()=>f(null),3e3)):f("Failed to save settings")}catch{f("Error saving settings")}finally{m(!1)}},W=()=>{localStorage.removeItem("authToken"),e("/"),window.location.reload()},G=async()=>{u(!0),y("Creating test VIPs and schedules...");try{const P=localStorage.getItem("authToken"),L=qg();let E=0,D=0,O=0,$=0;const F=[];for(const ie of L)try{const ue=await U("/api/vips",{method:"POST",headers:{Authorization:`Bearer ${P}`,"Content-Type":"application/json"},body:JSON.stringify(ie)});if(ue.ok){const Le=await ue.json();F.push(Le.id),E++}else D++,console.error(`Failed to create VIP: ${ie.name}`)}catch(ue){D++,console.error(`Error creating VIP ${ie.name}:`,ue)}y(`Created ${E} VIPs, now creating schedules...`);for(let ie=0;ie0||$>0?`(${D+$} failed)`:""}`)}catch(P){y("āŒ Failed to create test VIPs and schedules"),console.error("Error creating test data:",P)}finally{u(!1),setTimeout(()=>y(null),8e3)}},K=async()=>{if(confirm("Are you sure you want to remove all test VIPs? This will delete VIPs from the test organizations.")){u(!0),y("Removing test VIPs...");try{const P=localStorage.getItem("authToken"),L=await U("/api/vips",{headers:{Authorization:`Bearer ${P}`,"Content-Type":"application/json"}});if(!L.ok)throw new Error("Failed to fetch VIPs");const E=await L.json(),D=Ec(),O=E.filter(ie=>D.includes(ie.organization));let $=0,F=0;for(const ie of O)try{(await U(`/api/vips/${ie.id}`,{method:"DELETE",headers:{Authorization:`Bearer ${P}`,"Content-Type":"application/json"}})).ok?$++:(F++,console.error(`Failed to delete VIP: ${ie.name}`))}catch(ue){F++,console.error(`Error deleting VIP ${ie.name}:`,ue)}y(`šŸ—‘ļø Removed ${$} test VIPs successfully! ${F>0?`(${F} failed)`:""}`)}catch(P){y("āŒ Failed to remove test VIPs"),console.error("Error removing test VIPs:",P)}finally{u(!1),setTimeout(()=>y(null),5e3)}}};return l?i.jsx("div",{className:"min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 flex justify-center items-center",children:i.jsxs("div",{className:"bg-white rounded-2xl shadow-xl p-8 flex items-center space-x-4 border border-slate-200/60",children:[i.jsx("div",{className:"w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full animate-spin"}),i.jsx("span",{className:"text-lg font-medium text-slate-700",children:"Loading admin settings..."})]})}):v?i.jsx("div",{className:"min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 flex justify-center items-center",children:i.jsxs("div",{className:"bg-white rounded-2xl shadow-xl p-8 w-full max-w-xl border border-rose-200/70",children:[i.jsx("h2",{className:"text-2xl font-bold text-rose-700 mb-4",children:"Admin access required"}),i.jsx("p",{className:"text-slate-600 mb-6",children:v}),i.jsx("button",{className:"btn btn-primary",onClick:()=>e("/"),children:"Return to dashboard"})]})}):i.jsxs("div",{className:"space-y-8",children:[i.jsx("div",{className:"bg-white rounded-2xl shadow-lg p-8 border border-slate-200/60",children:i.jsxs("div",{className:"flex justify-between items-center",children:[i.jsxs("div",{children:[i.jsx("h1",{className:"text-3xl font-bold bg-gradient-to-r from-slate-800 to-slate-600 bg-clip-text text-transparent",children:"Admin Dashboard"}),i.jsx("p",{className:"text-slate-600 mt-2",children:"System configuration and API management"})]}),i.jsxs("div",{className:"flex items-center space-x-4",children:[i.jsx("button",{className:"btn btn-secondary",onClick:()=>e("/"),children:"Back to Dashboard"}),i.jsx("button",{className:"btn btn-danger",onClick:W,children:"Logout"})]})]})}),i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg border border-slate-200/60 overflow-hidden",children:[i.jsxs("div",{className:"bg-gradient-to-r from-blue-50 to-indigo-50 px-8 py-6 border-b border-slate-200/60",children:[i.jsx("h2",{className:"text-xl font-bold text-slate-800",children:"API Key Management"}),i.jsx("p",{className:"text-slate-600 mt-1",children:"Configure external service integrations"})]}),i.jsxs("div",{className:"p-8 space-y-8",children:[i.jsxs("div",{className:"form-section",children:[i.jsxs("div",{className:"form-section-header",children:[i.jsx("h3",{className:"form-section-title",children:"AviationStack API"}),g.aviationStackKey&&i.jsx("span",{className:"bg-green-100 text-green-800 text-xs font-medium px-2.5 py-0.5 rounded-full",children:"Configured"})]}),i.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-4 gap-4 items-end",children:[i.jsxs("div",{className:"lg:col-span-2",children:[i.jsx("label",{className:"form-label",children:"API Key"}),i.jsx("input",{type:"password",placeholder:g.aviationStackKey&&w.aviationStackKey?`Saved (${w.aviationStackKey.slice(-4)})`:"Enter AviationStack API key",value:t.aviationStackKey||"",onChange:P=>z("aviationStackKey",P.target.value),className:"form-input"}),g.aviationStackKey&&w.aviationStackKey&&!t.aviationStackKey&&i.jsxs("p",{className:"text-xs text-slate-500 mt-1",children:["Currently saved key ends with ",w.aviationStackKey.slice(-4),". Enter a new value to replace it."]}),i.jsx("p",{className:"text-xs text-slate-500 mt-1",children:"Get your key from: https://aviationstack.com/dashboard"})]}),i.jsx("div",{children:i.jsx("button",{className:"btn btn-secondary w-full",onClick:()=>I("aviationStackKey"),children:"Test Connection"})}),i.jsx("div",{children:o.aviationStackKey&&i.jsx("div",{className:`p-3 rounded-lg text-sm ${o.aviationStackKey.includes("Success")?"bg-green-50 text-green-700 border border-green-200":"bg-red-50 text-red-700 border border-red-200"}`,children:o.aviationStackKey})})]})]}),i.jsxs("div",{className:"form-section",children:[i.jsxs("div",{className:"form-section-header",children:[i.jsx("h3",{className:"form-section-title",children:"Auth0 Configuration"}),(g.auth0Domain||g.auth0ClientId||g.auth0ClientSecret)&&i.jsx("span",{className:"bg-green-100 text-green-800 text-xs font-medium px-2.5 py-0.5 rounded-full",children:"Configured"})]}),i.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-6",children:[i.jsxs("div",{className:"form-group",children:[i.jsx("label",{className:"form-label",children:"Auth0 Domain"}),i.jsx("input",{type:"text",placeholder:g.auth0Domain&&w.auth0Domain?`Saved (${w.auth0Domain.slice(-4)})`:"e.g. dev-1234abcd.us.auth0.com",value:t.auth0Domain||"",onChange:P=>z("auth0Domain",P.target.value),className:"form-input"})]}),i.jsxs("div",{className:"form-group",children:[i.jsx("label",{className:"form-label",children:"Client ID"}),i.jsx("input",{type:"password",placeholder:g.auth0ClientId&&w.auth0ClientId?`Saved (${w.auth0ClientId.slice(-4)})`:"Enter Auth0 application Client ID",value:t.auth0ClientId||"",onChange:P=>z("auth0ClientId",P.target.value),className:"form-input"}),g.auth0ClientId&&w.auth0ClientId&&!t.auth0ClientId&&i.jsxs("p",{className:"text-xs text-slate-500 mt-1",children:["Saved client ID ends with ",w.auth0ClientId.slice(-4),". Provide a new ID to update it."]})]}),i.jsxs("div",{className:"form-group",children:[i.jsx("label",{className:"form-label",children:"Client Secret"}),i.jsx("input",{type:"password",placeholder:g.auth0ClientSecret&&w.auth0ClientSecret?`Saved (${w.auth0ClientSecret.slice(-4)})`:"Enter Auth0 application Client Secret",value:t.auth0ClientSecret||"",onChange:P=>z("auth0ClientSecret",P.target.value),className:"form-input"}),g.auth0ClientSecret&&w.auth0ClientSecret&&!t.auth0ClientSecret&&i.jsxs("p",{className:"text-xs text-slate-500 mt-1",children:["Saved client secret ends with ",w.auth0ClientSecret.slice(-4),". Provide a new secret to rotate it."]})]}),i.jsxs("div",{className:"form-group",children:[i.jsx("label",{className:"form-label",children:"API Audience (Identifier)"}),i.jsx("input",{type:"text",placeholder:t.auth0Audience||"https://your-api-identifier",value:t.auth0Audience||"",onChange:P=>z("auth0Audience",P.target.value),className:"form-input"}),i.jsx("p",{className:"text-xs text-slate-500 mt-1",children:"Create an API in Auth0 and use its Identifier here (e.g. https://vip-coordinator-api)."})]})]}),i.jsxs("div",{className:"bg-blue-50 border border-blue-200 rounded-lg p-4 mt-4",children:[i.jsx("h4",{className:"font-semibold text-blue-900 mb-2",children:"Setup Instructions"}),i.jsxs("ol",{className:"text-sm text-blue-800 space-y-1 list-decimal list-inside",children:[i.jsx("li",{children:"Sign in to the Auth0 Dashboard"}),i.jsxs("li",{children:["Create a ",i.jsx("strong",{children:"Single Page Application"})," for the frontend"]}),i.jsxs("li",{children:["Set Allowed Callback URL to ",i.jsx("code",{children:"https://bsa.madeamess.online/auth/callback"})]}),i.jsxs("li",{children:["Set Allowed Logout URL to ",i.jsx("code",{children:"https://bsa.madeamess.online/"})]}),i.jsxs("li",{children:["Set Allowed Web Origins to ",i.jsx("code",{children:"https://bsa.madeamess.online"})]}),i.jsxs("li",{children:["Create an ",i.jsx("strong",{children:"API"})," in Auth0 for the backend and use its Identifier as the audience"]})]})]})]}),i.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-6 opacity-50",children:[i.jsxs("div",{className:"form-section",children:[i.jsxs("div",{className:"form-section-header",children:[i.jsx("h3",{className:"form-section-title",children:"Google Maps API"}),i.jsx("span",{className:"bg-gray-100 text-gray-600 text-xs font-medium px-2.5 py-0.5 rounded-full",children:"Coming Soon"})]}),i.jsx("input",{type:"password",placeholder:"Google Maps API key (not yet implemented)",disabled:!0,className:"form-input"})]}),i.jsxs("div",{className:"form-section",children:[i.jsxs("div",{className:"form-section-header",children:[i.jsx("h3",{className:"form-section-title",children:"Twilio API"}),i.jsx("span",{className:"bg-gray-100 text-gray-600 text-xs font-medium px-2.5 py-0.5 rounded-full",children:"Coming Soon"})]}),i.jsx("input",{type:"password",placeholder:"Twilio API key (not yet implemented)",disabled:!0,className:"form-input"})]})]})]})]}),i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg border border-slate-200/60 overflow-hidden",children:[i.jsxs("div",{className:"bg-gradient-to-r from-green-50 to-emerald-50 px-8 py-6 border-b border-slate-200/60",children:[i.jsx("h2",{className:"text-xl font-bold text-slate-800",children:"System Settings"}),i.jsx("p",{className:"text-slate-600 mt-1",children:"Configure default system behavior"})]}),i.jsx("div",{className:"p-8",children:i.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-6",children:[i.jsxs("div",{className:"form-group",children:[i.jsx("label",{htmlFor:"defaultPickup",className:"form-label",children:"Default Pickup Location"}),i.jsx("input",{type:"text",id:"defaultPickup",value:r.defaultPickupLocation||"",onChange:P=>A("defaultPickupLocation",P.target.value),placeholder:"e.g., JFK Airport Terminal 4",className:"form-input"})]}),i.jsxs("div",{className:"form-group",children:[i.jsx("label",{htmlFor:"defaultDropoff",className:"form-label",children:"Default Dropoff Location"}),i.jsx("input",{type:"text",id:"defaultDropoff",value:r.defaultDropoffLocation||"",onChange:P=>A("defaultDropoffLocation",P.target.value),placeholder:"e.g., Hilton Downtown",className:"form-input"})]}),i.jsxs("div",{className:"form-group",children:[i.jsx("label",{htmlFor:"timezone",className:"form-label",children:"Time Zone"}),i.jsxs("select",{id:"timezone",value:r.timeZone||"America/New_York",onChange:P=>A("timeZone",P.target.value),className:"form-select",children:[i.jsx("option",{value:"America/New_York",children:"Eastern Time"}),i.jsx("option",{value:"America/Chicago",children:"Central Time"}),i.jsx("option",{value:"America/Denver",children:"Mountain Time"}),i.jsx("option",{value:"America/Los_Angeles",children:"Pacific Time"}),i.jsx("option",{value:"UTC",children:"UTC"})]})]}),i.jsx("div",{className:"form-group",children:i.jsxs("div",{className:"checkbox-option",children:[i.jsx("input",{type:"checkbox",checked:r.notificationsEnabled||!1,onChange:P=>A("notificationsEnabled",P.target.checked),className:"form-checkbox mr-3"}),i.jsx("span",{className:"font-medium",children:"Enable Email/SMS Notifications"})]})})]})})]}),i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg border border-slate-200/60 overflow-hidden",children:[i.jsxs("div",{className:"bg-gradient-to-r from-orange-50 to-red-50 px-8 py-6 border-b border-slate-200/60",children:[i.jsx("h2",{className:"text-xl font-bold text-slate-800",children:"Test VIP Data Management"}),i.jsx("p",{className:"text-slate-600 mt-1",children:"Create and manage test VIP data for application testing"})]}),i.jsxs("div",{className:"p-8",children:[i.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-6",children:[i.jsxs("div",{className:"bg-green-50 border border-green-200 rounded-xl p-6",children:[i.jsx("h3",{className:"text-lg font-bold text-slate-800 mb-3",children:"Create Test VIPs"}),i.jsx("p",{className:"text-slate-600 mb-4",children:"Generate 20 diverse test VIPs (10 Admin department, 10 Office of Development) with realistic data including flights, transport modes, and special requirements."}),i.jsxs("ul",{className:"text-sm text-slate-600 mb-4 space-y-1",children:[i.jsx("li",{children:"• Mixed flight and self-driving transport modes"}),i.jsx("li",{children:"• Single flights, connecting flights, and multi-segment journeys"}),i.jsx("li",{children:"• Diverse organizations and special requirements"}),i.jsx("li",{children:"• Realistic arrival dates (tomorrow and day after)"})]}),i.jsx("button",{className:"btn btn-success w-full",onClick:G,disabled:x,children:x?i.jsxs(i.Fragment,{children:[i.jsx("span",{className:"animate-spin inline-block w-4 h-4 border-2 border-white border-t-transparent rounded-full mr-2"}),"Creating Test VIPs..."]}):"šŸŽ­ Create 20 Test VIPs"})]}),i.jsxs("div",{className:"bg-red-50 border border-red-200 rounded-xl p-6",children:[i.jsx("h3",{className:"text-lg font-bold text-slate-800 mb-3",children:"Remove Test VIPs"}),i.jsx("p",{className:"text-slate-600 mb-4",children:"Remove all test VIPs from the system. This will delete VIPs from the following test organizations:"}),i.jsx("div",{className:"text-xs text-slate-500 mb-4 max-h-20 overflow-y-auto",children:i.jsxs("div",{className:"grid grid-cols-1 gap-1",children:[Ec().slice(0,8).map(P=>i.jsxs("div",{children:["• ",P]},P)),i.jsx("div",{className:"text-slate-400",children:"... and 12 more organizations"})]})}),i.jsx("button",{className:"btn btn-danger w-full",onClick:K,disabled:x,children:x?i.jsxs(i.Fragment,{children:[i.jsx("span",{className:"animate-spin inline-block w-4 h-4 border-2 border-white border-t-transparent rounded-full mr-2"}),"Removing Test VIPs..."]}):"šŸ—‘ļø Remove All Test VIPs"})]})]}),h&&i.jsx("div",{className:`mt-6 p-4 rounded-lg text-center font-medium ${h.includes("āœ…")||h.includes("šŸ—‘ļø")?"bg-green-50 text-green-700 border border-green-200":"bg-red-50 text-red-700 border border-red-200"}`,children:h}),i.jsxs("div",{className:"bg-blue-50 border border-blue-200 rounded-lg p-4 mt-6",children:[i.jsx("h4",{className:"font-semibold text-blue-900 mb-2",children:"šŸ’” Test Data Details"}),i.jsxs("div",{className:"text-sm text-blue-800 space-y-1",children:[i.jsxs("p",{children:[i.jsx("strong",{children:"Admin Department (10 VIPs):"})," University officials, ambassadors, ministers, and executives"]}),i.jsxs("p",{children:[i.jsx("strong",{children:"Office of Development (10 VIPs):"})," Donors, foundation leaders, and philanthropists"]}),i.jsxs("p",{children:[i.jsx("strong",{children:"Transport Modes:"})," Mix of flights (single, connecting, multi-segment) and self-driving"]}),i.jsxs("p",{children:[i.jsx("strong",{children:"Special Requirements:"})," Dietary restrictions, accessibility needs, security details, interpreters"]}),i.jsxs("p",{children:[i.jsx("strong",{children:"Full Day Schedules:"})," Each VIP gets 5-7 realistic events including meetings, meals, tours, and presentations"]}),i.jsxs("p",{children:[i.jsx("strong",{children:"Schedule Types:"})," Airport pickup, welcome breakfast, department meetings, working lunches, campus tours, receptions"]})]})]})]})]}),i.jsxs("div",{className:"bg-white rounded-2xl shadow-lg border border-slate-200/60 overflow-hidden",children:[i.jsxs("div",{className:"bg-gradient-to-r from-purple-50 to-pink-50 px-8 py-6 border-b border-slate-200/60",children:[i.jsx("h2",{className:"text-xl font-bold text-slate-800",children:"API Documentation"}),i.jsx("p",{className:"text-slate-600 mt-1",children:"Developer resources and API testing"})]}),i.jsxs("div",{className:"p-8",children:[i.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-6",children:[i.jsxs("div",{className:"bg-blue-50 border border-blue-200 rounded-xl p-6",children:[i.jsx("h3",{className:"text-lg font-bold text-slate-800 mb-3",children:"Interactive API Documentation"}),i.jsx("p",{className:"text-slate-600 mb-4",children:"Explore and test all API endpoints with the interactive Swagger UI documentation."}),i.jsx("button",{className:"btn btn-primary w-full mb-2",onClick:()=>window.open(`${vt}/api-docs.html`,"_blank"),children:"Open API Documentation"}),i.jsx("p",{className:"text-xs text-slate-500",children:"Opens in a new tab with full endpoint documentation and testing capabilities"})]}),i.jsxs("div",{className:"bg-green-50 border border-green-200 rounded-xl p-6",children:[i.jsx("h3",{className:"text-lg font-bold text-slate-800 mb-3",children:"Quick API Examples"}),i.jsxs("div",{className:"space-y-2 text-sm",children:[i.jsxs("div",{children:[i.jsx("span",{className:"font-medium",children:"Health Check:"}),i.jsx("code",{className:"ml-2 bg-white px-2 py-1 rounded text-xs",children:"GET /api/health"})]}),i.jsxs("div",{children:[i.jsx("span",{className:"font-medium",children:"Get VIPs:"}),i.jsx("code",{className:"ml-2 bg-white px-2 py-1 rounded text-xs",children:"GET /api/vips"})]}),i.jsxs("div",{children:[i.jsx("span",{className:"font-medium",children:"Get Drivers:"}),i.jsx("code",{className:"ml-2 bg-white px-2 py-1 rounded text-xs",children:"GET /api/drivers"})]}),i.jsxs("div",{children:[i.jsx("span",{className:"font-medium",children:"Flight Info:"}),i.jsx("code",{className:"ml-2 bg-white px-2 py-1 rounded text-xs",children:"GET /api/flights/UA1234"})]})]}),i.jsx("button",{className:"btn btn-secondary w-full mt-4",onClick:()=>window.open("/README-API.md","_blank"),children:"View API Guide"})]})]}),i.jsx("div",{className:"bg-amber-50 border border-amber-200 rounded-lg p-4 mt-6",children:i.jsxs("p",{className:"text-amber-800",children:[i.jsx("strong",{children:"Pro Tip:"})," The interactive documentation allows you to test API endpoints directly in your browser. Perfect for developers integrating with the VIP Coordinator system!"]})})]})]}),i.jsxs("div",{className:"text-center",children:[i.jsx("button",{className:"btn btn-success text-lg px-8 py-4",onClick:M,disabled:d,children:d?"Saving...":"Save All Settings"}),p&&i.jsx("div",{className:`mt-4 p-4 rounded-lg ${p.includes("successfully")?"bg-green-50 text-green-700 border border-green-200":"bg-red-50 text-red-700 border border-red-200"}`,children:p})]})]})},n0=({currentUser:e})=>{const[t,n]=S.useState([]),[r,s]=S.useState([]),[o,a]=S.useState(!0),[l,c]=S.useState(null),[d,m]=S.useState("all"),[p,f]=S.useState(null);if((e==null?void 0:e.role)!=="administrator")return i.jsxs("div",{className:"p-6 bg-red-50 border border-red-200 rounded-lg",children:[i.jsx("h2",{className:"text-xl font-semibold text-red-800 mb-2",children:"Access Denied"}),i.jsx("p",{className:"text-red-600",children:"You need administrator privileges to access user management."})]});const g=async()=>{try{const v=localStorage.getItem("authToken"),N=await fetch(`${vt}/auth/users`,{headers:{Authorization:`Bearer ${v}`,"Content-Type":"application/json"}});if(!N.ok)throw new Error("Failed to fetch users");const k=await N.json();n(k)}catch(v){c(v instanceof Error?v.message:"Failed to fetch users")}finally{a(!1)}},b=async()=>{try{const v=localStorage.getItem("authToken"),N=await fetch(`${vt}/auth/users/pending/list`,{headers:{Authorization:`Bearer ${v}`,"Content-Type":"application/json"}});if(!N.ok)throw new Error("Failed to fetch pending users");const k=await N.json();s(k)}catch(v){c(v instanceof Error?v.message:"Failed to fetch pending users")}},w=async(v,N)=>{f(v);try{const k=localStorage.getItem("authToken");if(!(await fetch(`${vt}/auth/users/${v}/role`,{method:"PATCH",headers:{Authorization:`Bearer ${k}`,"Content-Type":"application/json"},body:JSON.stringify({role:N})})).ok)throw new Error("Failed to update user role");await g()}catch(k){c(k instanceof Error?k.message:"Failed to update user role")}finally{f(null)}},j=async(v,N)=>{if(confirm(`Are you sure you want to delete user "${N}"? This action cannot be undone.`))try{const k=localStorage.getItem("authToken");if(!(await fetch(`${vt}/auth/users/${v}`,{method:"DELETE",headers:{Authorization:`Bearer ${k}`,"Content-Type":"application/json"}})).ok)throw new Error("Failed to delete user");await g(),await b()}catch(k){c(k instanceof Error?k.message:"Failed to delete user")}},x=async(v,N)=>{f(v);try{const k=localStorage.getItem("authToken");if(!(await fetch(`${vt}/auth/users/${v}/approval`,{method:"PATCH",headers:{Authorization:`Bearer ${k}`,"Content-Type":"application/json"},body:JSON.stringify({status:"approved"})})).ok)throw new Error("Failed to approve user");await g(),await b()}catch(k){c(k instanceof Error?k.message:"Failed to approve user")}finally{f(null)}},u=async(v,N)=>{if(confirm(`Are you sure you want to deny access for "${N}"?`)){f(v);try{const k=localStorage.getItem("authToken");if(!(await fetch(`${vt}/auth/users/${v}/approval`,{method:"PATCH",headers:{Authorization:`Bearer ${k}`,"Content-Type":"application/json"},body:JSON.stringify({status:"denied"})})).ok)throw new Error("Failed to deny user");await g(),await b()}catch(k){c(k instanceof Error?k.message:"Failed to deny user")}finally{f(null)}}};S.useEffect(()=>{g(),b()},[]),S.useEffect(()=>{d==="pending"&&b()},[d]);const h=v=>new Date(v).toLocaleDateString("en-US",{year:"numeric",month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"}),y=v=>{switch(v){case"administrator":return"bg-red-100 text-red-800 border-red-200";case"coordinator":return"bg-blue-100 text-blue-800 border-blue-200";case"driver":return"bg-green-100 text-green-800 border-green-200";default:return"bg-gray-100 text-gray-800 border-gray-200"}};return o?i.jsx("div",{className:"p-6",children:i.jsxs("div",{className:"animate-pulse",children:[i.jsx("div",{className:"h-8 bg-gray-200 rounded-lg w-1/4 mb-6"}),i.jsx("div",{className:"space-y-4",children:[1,2,3].map(v=>i.jsx("div",{className:"h-20 bg-gray-200 rounded-lg"},v))})]})}):i.jsxs("div",{className:"p-6",children:[i.jsxs("div",{className:"mb-6",children:[i.jsx("h2",{className:"text-2xl font-bold text-gray-900 mb-2",children:"User Management"}),i.jsx("p",{className:"text-gray-600",children:"Manage user accounts and permissions (PostgreSQL Database)"})]}),l&&i.jsxs("div",{className:"mb-6 p-4 bg-red-50 border border-red-200 rounded-lg",children:[i.jsx("p",{className:"text-red-600",children:l}),i.jsx("button",{onClick:()=>c(null),className:"mt-2 text-sm text-red-500 hover:text-red-700",children:"Dismiss"})]}),i.jsx("div",{className:"mb-6",children:i.jsx("div",{className:"border-b border-gray-200",children:i.jsxs("nav",{className:"-mb-px flex space-x-8",children:[i.jsxs("button",{onClick:()=>m("all"),className:`py-2 px-1 border-b-2 font-medium text-sm ${d==="all"?"border-blue-500 text-blue-600":"border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"}`,children:["šŸ‘„ All Users (",t.length,")"]}),i.jsxs("button",{onClick:()=>m("pending"),className:`py-2 px-1 border-b-2 font-medium text-sm ${d==="pending"?"border-orange-500 text-orange-600":"border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"}`,children:["ā³ Pending Approval (",r.length,")",r.length>0&&i.jsx("span",{className:"ml-2 inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-orange-100 text-orange-800",children:r.length})]})]})})}),d==="all"&&i.jsxs("div",{className:"bg-white shadow-sm border border-gray-200 rounded-lg overflow-hidden",children:[i.jsx("div",{className:"px-6 py-4 border-b border-gray-200 bg-gray-50",children:i.jsxs("h3",{className:"text-lg font-medium text-gray-900",children:["All Users (",t.length,")"]})}),i.jsx("div",{className:"divide-y divide-gray-200",children:t.map(v=>i.jsx("div",{className:"p-6 hover:bg-gray-50",children:i.jsxs("div",{className:"flex items-center justify-between",children:[i.jsxs("div",{className:"flex items-center space-x-4",children:[v.picture?i.jsx("img",{src:v.picture,alt:v.name,className:"w-12 h-12 rounded-full"}):i.jsx("div",{className:"w-12 h-12 rounded-full bg-gray-300 flex items-center justify-center",children:i.jsx("span",{className:"text-gray-600 font-medium",children:v.name.charAt(0).toUpperCase()})}),i.jsxs("div",{children:[i.jsx("h4",{className:"text-lg font-medium text-gray-900",children:v.name}),i.jsx("p",{className:"text-gray-600",children:v.email}),i.jsxs("div",{className:"flex items-center space-x-4 mt-1 text-sm text-gray-500",children:[i.jsxs("span",{children:["Joined: ",h(v.created_at)]}),v.last_sign_in_at&&i.jsxs("span",{children:["Last login: ",h(v.last_sign_in_at)]}),i.jsxs("span",{className:"capitalize",children:["via ",v.provider]})]})]})]}),i.jsxs("div",{className:"flex items-center space-x-4",children:[i.jsxs("div",{className:"flex items-center space-x-2",children:[i.jsx("span",{className:"text-sm text-gray-600",children:"Role:"}),i.jsxs("select",{value:v.role,onChange:N=>w(v.email,N.target.value),disabled:p===v.email||v.email===e.email,className:`px-3 py-1 border rounded-md text-sm font-medium ${y(v.role)} ${p===v.email?"opacity-50 cursor-not-allowed":"cursor-pointer hover:bg-opacity-80"}`,children:[i.jsx("option",{value:"coordinator",children:"Coordinator"}),i.jsx("option",{value:"administrator",children:"Administrator"}),i.jsx("option",{value:"driver",children:"Driver"})]})]}),v.email!==e.email&&i.jsx("button",{onClick:()=>j(v.email,v.name),className:"px-3 py-1 text-sm text-red-600 hover:text-red-800 hover:bg-red-50 rounded-md border border-red-200 transition-colors",children:"šŸ—‘ļø Delete"}),v.email===e.email&&i.jsx("span",{className:"px-3 py-1 text-sm text-blue-600 bg-blue-50 rounded-md border border-blue-200",children:"šŸ‘¤ You"})]})]})},v.email))}),t.length===0&&i.jsx("div",{className:"p-6 text-center text-gray-500",children:"No users found."})]}),d==="pending"&&i.jsxs("div",{className:"bg-white shadow-sm border border-gray-200 rounded-lg overflow-hidden",children:[i.jsxs("div",{className:"px-6 py-4 border-b border-gray-200 bg-orange-50",children:[i.jsxs("h3",{className:"text-lg font-medium text-gray-900",children:["Pending Approval (",r.length,")"]}),i.jsx("p",{className:"text-sm text-gray-600 mt-1",children:"Users waiting for administrator approval to access the system"})]}),i.jsx("div",{className:"divide-y divide-gray-200",children:r.map(v=>i.jsx("div",{className:"p-6 hover:bg-gray-50",children:i.jsxs("div",{className:"flex items-center justify-between",children:[i.jsxs("div",{className:"flex items-center space-x-4",children:[v.picture?i.jsx("img",{src:v.picture,alt:v.name,className:"w-12 h-12 rounded-full"}):i.jsx("div",{className:"w-12 h-12 rounded-full bg-gray-300 flex items-center justify-center",children:i.jsx("span",{className:"text-gray-600 font-medium",children:v.name.charAt(0).toUpperCase()})}),i.jsxs("div",{children:[i.jsx("h4",{className:"text-lg font-medium text-gray-900",children:v.name}),i.jsx("p",{className:"text-gray-600",children:v.email}),i.jsxs("div",{className:"flex items-center space-x-4 mt-1 text-sm text-gray-500",children:[i.jsxs("span",{children:["Requested: ",h(v.created_at)]}),i.jsxs("span",{className:"capitalize",children:["via ",v.provider]}),i.jsx("span",{className:`px-2 py-1 rounded-full text-xs font-medium ${y(v.role)}`,children:v.role})]})]})]}),i.jsxs("div",{className:"flex items-center space-x-3",children:[i.jsx("button",{onClick:()=>x(v.email,v.name),disabled:p===v.email,className:`px-4 py-2 text-sm font-medium text-white bg-green-600 hover:bg-green-700 rounded-md transition-colors ${p===v.email?"opacity-50 cursor-not-allowed":""}`,children:p===v.email?"ā³ Approving...":"āœ… Approve"}),i.jsx("button",{onClick:()=>u(v.email,v.name),disabled:p===v.email,className:`px-4 py-2 text-sm font-medium text-white bg-red-600 hover:bg-red-700 rounded-md transition-colors ${p===v.email?"opacity-50 cursor-not-allowed":""}`,children:p===v.email?"ā³ Denying...":"āŒ Deny"})]})]})},v.email))}),r.length===0&&i.jsxs("div",{className:"p-6 text-center text-gray-500",children:[i.jsx("div",{className:"text-6xl mb-4",children:"āœ…"}),i.jsx("p",{className:"text-lg font-medium mb-2",children:"No pending approvals"}),i.jsx("p",{className:"text-sm",children:"All users have been processed."})]})]}),i.jsxs("div",{className:"mt-6 p-4 bg-blue-50 border border-blue-200 rounded-lg",children:[i.jsx("h4",{className:"font-medium text-blue-900 mb-2",children:"Role Descriptions:"}),i.jsxs("ul",{className:"text-sm text-blue-800 space-y-1",children:[i.jsxs("li",{children:[i.jsx("strong",{children:"Administrator:"})," Full access to all features including user management"]}),i.jsxs("li",{children:[i.jsx("strong",{children:"Coordinator:"})," Can manage VIPs, drivers, and schedules"]}),i.jsxs("li",{children:[i.jsx("strong",{children:"Driver:"})," Can view assigned schedules and update status"]})]})]}),i.jsxs("div",{className:"mt-4 p-4 bg-orange-50 border border-orange-200 rounded-lg",children:[i.jsx("h4",{className:"font-medium text-orange-900 mb-2",children:"šŸ” User Approval System:"}),i.jsx("p",{className:"text-sm text-orange-800",children:'New users (except the first administrator) require approval before accessing the system. Users with pending approval will see a "pending approval" message when they try to sign in.'})]}),i.jsxs("div",{className:"mt-4 p-4 bg-green-50 border border-green-200 rounded-lg",children:[i.jsx("h4",{className:"font-medium text-green-900 mb-2",children:"āœ… PostgreSQL Database:"}),i.jsx("p",{className:"text-sm text-green-800",children:"User data is stored in your PostgreSQL database with proper indexing and relationships. All user management operations are transactional and fully persistent across server restarts."})]})]})};const r0=({onLogin:e,errorMessage:t})=>{const[n,r]=S.useState(null),[s,o]=S.useState(!0);return S.useEffect(()=>{U("/auth/setup").then(a=>a.json()).then(a=>{r(a),o(!1)}).catch(a=>{console.error("Error checking setup status:",a),o(!1)})},[]),s?i.jsx("div",{className:"login-container",children:i.jsx("div",{className:"login-card",children:i.jsx("div",{className:"loading",children:"Loading..."})})}):i.jsx("div",{className:"login-container",children:i.jsxs("div",{className:"login-card",children:[i.jsxs("div",{className:"login-header",children:[i.jsx("h1",{children:"VIP Coordinator"}),i.jsx("p",{children:"Secure access required"})]}),!(n!=null&&n.firstAdminCreated)&&i.jsxs("div",{className:"setup-notice",children:[i.jsx("h3",{children:"šŸš€ First Time Setup"}),i.jsx("p",{children:"The first person to sign in will be promoted to administrator automatically."})]}),i.jsxs("div",{className:"login-content",children:[i.jsxs("button",{className:"google-login-btn",onClick:e,children:[i.jsx("svg",{className:"google-icon",viewBox:"0 0 24 24",children:i.jsx("path",{fill:"#635dff",d:"M22 12.07c0-5.52-4.48-10-10-10s-10 4.48-10 10a9.97 9.97 0 006.85 9.48.73.73 0 00.95-.7v-3.05c-2.79.61-3.38-1.19-3.38-1.19-.46-1.17-1.12-1.49-1.12-1.49-.91-.62.07-.61.07-.61 1 .07 1.53 1.03 1.53 1.03.9 1.53 2.37 1.09 2.96.83.09-.65.35-1.09.63-1.34-2.23-.25-4.57-1.12-4.57-4.96 0-1.1.39-2 1.03-2.7-.1-.25-.45-1.25.1-2.6 0 0 .84-.27 2.75 1.02a9.53 9.53 0 015 0c1.91-1.29 2.75-1.02 2.75-1.02.55 1.35.2 2.35.1 2.6.64.7 1.03 1.6 1.03 2.7 0 3.85-2.34 4.71-4.58 4.95.36.31.69.92.69 1.86v2.75c0 .39.27.71.66.79a10 10 0 007.61-9.71z"})}),"Continue with Auth0"]}),i.jsx("div",{className:"login-info",children:i.jsx("p",{children:(n==null?void 0:n.authProvider)==="auth0"?"Sign in with your organisation account. We use Auth0 for secure authentication.":"Authentication service is being configured. Please try again later."})}),t&&i.jsx("div",{className:"dev-login-error",style:{marginTop:"1rem"},children:t})]}),i.jsx("div",{className:"login-footer",children:i.jsx("p",{children:"Secure authentication powered by Auth0"})})]})})};const rs={}.VITE_AUTH0_AUDIENCE;function s0(){const{isLoading:e,isAuthenticated:t,loginWithRedirect:n,logout:r,getAccessTokenSilently:s,user:o,error:a}=Ff(),[l,c]=S.useState(null),[d,m]=S.useState(!0),[p,f]=S.useState(null),[g,b]=S.useState(!1);S.useEffect(()=>{e||(async()=>{if(!t){c(null),f(null),b(!1),m(!1);return}m(!0),b(!1),f(null);try{const v=await s({authorizationParams:{...rs?{audience:rs}:{},scope:"openid profile email"}});localStorage.setItem("authToken",v);const N=await U("/auth/me",{headers:{Authorization:`Bearer ${v}`}});if(N.status===403){const A=await N.json();c(null),b(!0),f(A.message||"Your account is pending administrator approval.");return}if(!N.ok)throw new Error(`Failed to load profile (${N.status})`);const k=await N.json(),C=k.user||k,z=C.name||(o==null?void 0:o.name)||(o==null?void 0:o.nickname)||(o==null?void 0:o.email)||C.email;c({...C,name:z,role:C.role,picture:C.picture||(o==null?void 0:o.picture)})}catch(v){console.error("Authentication bootstrap failed:",v),c(null),f("Authentication failed. Please try signing in again.")}finally{m(!1)}})()},[t,e,s,o]);const w=()=>{localStorage.removeItem("authToken"),r({logoutParams:{returnTo:window.location.origin}})};if(e||d)return i.jsx("div",{className:"min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 flex justify-center items-center",children:i.jsxs("div",{className:"bg-white rounded-2xl shadow-xl p-8 flex items-center space-x-4",children:[i.jsx("div",{className:"w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full animate-spin"}),i.jsx("span",{className:"text-lg font-medium text-slate-700",children:"Loading VIP Coordinator..."})]})});if(g)return i.jsx("div",{className:"min-h-screen bg-gradient-to-br from-amber-50 to-rose-50 flex justify-center items-center px-4",children:i.jsxs("div",{className:"bg-white border border-amber-200/60 rounded-2xl shadow-xl max-w-xl w-full p-8 space-y-4 text-center",children:[i.jsx("div",{className:"flex justify-center",children:i.jsx("div",{className:"w-16 h-16 rounded-full bg-amber-100 text-amber-600 flex items-center justify-center text-3xl",children:"ā³"})}),i.jsx("h1",{className:"text-2xl font-bold text-slate-800",children:"Awaiting Administrator Approval"}),i.jsx("p",{className:"text-slate-600",children:p||"Thanks for signing in. An administrator needs to approve your account before you can access the dashboard."}),i.jsx("button",{onClick:w,className:"btn btn-secondary mt-4",children:"Sign out"})]})});const j=async()=>{try{await n({authorizationParams:{...rs?{audience:rs}:{},scope:"openid profile email",redirect_uri:`${window.location.origin}/auth/callback`}})}catch(y){console.error("Auth0 login failed:",y),f((y==null?void 0:y.message)||"Authentication failed. Please try again.")}};if(!t||!l)return i.jsx(r0,{onLogin:j,errorMessage:p||(a==null?void 0:a.message)});const x=l.name&&l.name.trim().length>0?l.name:l.email||"User",u=x.trim().charAt(0).toUpperCase(),h=l.role||"user";return i.jsx(Lg,{children:i.jsxs("div",{className:"min-h-screen bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-50",children:[i.jsx("nav",{className:"bg-white/80 backdrop-blur-lg border-b border-slate-200/60 sticky top-0 z-50",children:i.jsx("div",{className:"max-w-7xl mx-auto px-6 lg:px-8",children:i.jsxs("div",{className:"flex justify-between items-center h-16",children:[i.jsxs("div",{className:"flex items-center space-x-3",children:[i.jsx("div",{className:"w-8 h-8 bg-gradient-to-br from-blue-600 to-indigo-600 rounded-lg flex items-center justify-center",children:i.jsx("span",{className:"text-white font-bold text-sm",children:"VC"})}),i.jsx("h1",{className:"text-xl font-bold bg-gradient-to-r from-slate-800 to-slate-600 bg-clip-text text-transparent",children:"VIP Coordinator"})]}),i.jsxs("div",{className:"hidden md:flex items-center space-x-1",children:[i.jsx(we,{to:"/",className:"px-4 py-2 text-sm font-medium text-slate-700 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-all duration-200",children:"Dashboard"}),i.jsx(we,{to:"/vips",className:"px-4 py-2 text-sm font-medium text-slate-700 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-all duration-200",children:"VIPs"}),i.jsx(we,{to:"/drivers",className:"px-4 py-2 text-sm font-medium text-slate-700 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-all duration-200",children:"Drivers"}),h==="administrator"&&i.jsx(we,{to:"/admin",className:"px-4 py-2 text-sm font-medium text-slate-700 hover:text-amber-600 hover:bg-amber-50 rounded-lg transition-all duration-200",children:"Admin"}),h==="administrator"&&i.jsx(we,{to:"/users",className:"px-4 py-2 text-sm font-medium text-slate-700 hover:text-purple-600 hover:bg-purple-50 rounded-lg transition-all duration-200",children:"Users"})]}),i.jsxs("div",{className:"flex items-center space-x-4",children:[i.jsxs("div",{className:"hidden sm:flex items-center space-x-3",children:[i.jsx("div",{className:"w-8 h-8 bg-gradient-to-br from-slate-400 to-slate-600 rounded-full flex items-center justify-center overflow-hidden",children:l.picture?i.jsx("img",{src:l.picture,alt:x,className:"w-8 h-8 object-cover"}):i.jsx("span",{className:"text-white text-xs font-medium",children:u})}),i.jsxs("div",{className:"text-sm",children:[i.jsx("div",{className:"font-medium text-slate-900",children:x}),i.jsx("div",{className:"text-slate-500 capitalize",children:h})]})]}),i.jsx("button",{onClick:w,className:"bg-gradient-to-r from-red-500 to-red-600 hover:from-red-600 hover:to-red-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 shadow-lg hover:shadow-xl",children:"Logout"})]})]})})}),i.jsx("main",{className:"max-w-7xl mx-auto px-6 lg:px-8 py-8",children:i.jsxs(Pg,{children:[i.jsx(ft,{path:"/",element:i.jsx(Qg,{})}),i.jsx(ft,{path:"/vips",element:i.jsx(Wg,{})}),i.jsx(ft,{path:"/vips/:id",element:i.jsx(Hg,{})}),i.jsx(ft,{path:"/drivers",element:i.jsx(Jg,{})}),i.jsx(ft,{path:"/drivers/:driverId",element:i.jsx(Yg,{})}),i.jsx(ft,{path:"/admin",element:i.jsx(t0,{})}),i.jsx(ft,{path:"/users",element:i.jsx(n0,{currentUser:l})})]})})]})})}const fh={}.VITE_AUTH0_DOMAIN,gh={}.VITE_AUTH0_CLIENT_ID,Tc={}.VITE_AUTH0_AUDIENCE;if(!fh||!gh)throw new Error("Auth0 environment variables are missing. Please set VITE_AUTH0_DOMAIN and VITE_AUTH0_CLIENT_ID.");const xh={redirect_uri:`${window.location.origin}/auth/callback`,scope:"openid profile email"};Tc&&(xh.audience=Tc);Bi.createRoot(document.getElementById("root")).render(i.jsx(Zo.StrictMode,{children:i.jsx($f,{domain:fh,clientId:gh,authorizationParams:xh,cacheLocation:"localstorage",useRefreshTokens:!0,children:i.jsx(s0,{})})})); diff --git a/frontend-old-20260125/dist/index.html b/frontend-old-20260125/dist/index.html deleted file mode 100644 index bbc0dc5..0000000 --- a/frontend-old-20260125/dist/index.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - VIP Coordinator Dashboard - - - - -
- - - diff --git a/frontend-old-20260125/index.html b/frontend-old-20260125/index.html deleted file mode 100644 index e68ec3b..0000000 --- a/frontend-old-20260125/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - VIP Coordinator Dashboard - - -
- - - diff --git a/frontend-old-20260125/nginx.conf b/frontend-old-20260125/nginx.conf deleted file mode 100644 index ccbf9a7..0000000 --- a/frontend-old-20260125/nginx.conf +++ /dev/null @@ -1,54 +0,0 @@ -# Custom PID file location for non-root user -pid /tmp/nginx/nginx.pid; - -events { - worker_connections 1024; -} - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - - sendfile on; - keepalive_timeout 65; - - # Gzip compression - gzip on; - gzip_vary on; - gzip_min_length 1024; - gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json; - - server { - listen 80; - server_name localhost; - root /usr/share/nginx/html; - index index.html; - - # Handle client-side routing - location / { - try_files $uri $uri/ /index.html; - } - - # API proxy to backend - location /api/ { - proxy_pass http://backend:3000/api/; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # Security headers - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-XSS-Protection "1; mode=block" always; - add_header X-Content-Type-Options "nosniff" always; - add_header Referrer-Policy "no-referrer-when-downgrade" always; - add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always; - - # Cache static assets - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { - expires 1y; - add_header Cache-Control "public, immutable"; - } - } -} \ No newline at end of file diff --git a/frontend-old-20260125/package-lock.json b/frontend-old-20260125/package-lock.json deleted file mode 100644 index 7cee183..0000000 --- a/frontend-old-20260125/package-lock.json +++ /dev/null @@ -1,3951 +0,0 @@ -{ - "name": "vip-coordinator-frontend", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "vip-coordinator-frontend", - "version": "0.0.0", - "dependencies": { - "keycloak-js": "^24.0.5", - "leaflet": "^1.9.4", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-leaflet": "^4.2.1", - "react-router-dom": "^6.15.0" - }, - "devDependencies": { - "@types/leaflet": "^1.9.4", - "@types/react": "^18.2.15", - "@types/react-dom": "^18.2.7", - "@typescript-eslint/eslint-plugin": "^8.15.0", - "@typescript-eslint/parser": "^8.15.0", - "@vitejs/plugin-react": "^4.3.3", - "autoprefixer": "^10.4.14", - "eslint": "^9.15.0", - "eslint-plugin-react-hooks": "^5.0.0", - "eslint-plugin-react-refresh": "^0.4.14", - "postcss": "^8.5.4", - "tailwindcss": "^3.4.1", - "typescript": "^5.6.0", - "vite": "^5.4.10" - }, - "engines": { - "node": ">=22.0.0", - "npm": ">=10.0.0" - } - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.27.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.27.4", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.4", - "@babel/parser": "^7.27.4", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.27.4", - "@babel/types": "^7.27.3", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.27.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.27.3", - "@babel/types": "^7.27.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.27.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.27.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.27.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.3", - "@babel/parser": "^7.27.4", - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.27.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", - "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz", - "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.28.0.tgz", - "integrity": "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", - "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.14.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@react-leaflet/core": { - "version": "2.1.0", - "license": "Hippocratic-2.1", - "peerDependencies": { - "leaflet": "^1.9.0", - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@remix-run/router": { - "version": "1.23.0", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.9", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.42.0.tgz", - "integrity": "sha512-gldmAyS9hpj+H6LpRNlcjQWbuKUtb94lodB9uCz71Jm+7BxK1VIOo7y62tZZwxhA7j1ylv/yQz080L5WkS+LoQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.42.0.tgz", - "integrity": "sha512-bpRipfTgmGFdCZDFLRvIkSNO1/3RGS74aWkJJTFJBH7h3MRV4UijkaEUeOMbi9wxtxYmtAbVcnMtHTPBhLEkaw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.42.0.tgz", - "integrity": "sha512-JxHtA081izPBVCHLKnl6GEA0w3920mlJPLh89NojpU2GsBSB6ypu4erFg/Wx1qbpUbepn0jY4dVWMGZM8gplgA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.42.0.tgz", - "integrity": "sha512-rv5UZaWVIJTDMyQ3dCEK+m0SAn6G7H3PRc2AZmExvbDvtaDc+qXkei0knQWcI3+c9tEs7iL/4I4pTQoPbNL2SA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.42.0.tgz", - "integrity": "sha512-fJcN4uSGPWdpVmvLuMtALUFwCHgb2XiQjuECkHT3lWLZhSQ3MBQ9pq+WoWeJq2PrNxr9rPM1Qx+IjyGj8/c6zQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.42.0.tgz", - "integrity": "sha512-CziHfyzpp8hJpCVE/ZdTizw58gr+m7Y2Xq5VOuCSrZR++th2xWAz4Nqk52MoIIrV3JHtVBhbBsJcAxs6NammOQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.42.0.tgz", - "integrity": "sha512-UsQD5fyLWm2Fe5CDM7VPYAo+UC7+2Px4Y+N3AcPh/LdZu23YcuGPegQly++XEVaC8XUTFVPscl5y5Cl1twEI4A==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.42.0.tgz", - "integrity": "sha512-/i8NIrlgc/+4n1lnoWl1zgH7Uo0XK5xK3EDqVTf38KvyYgCU/Rm04+o1VvvzJZnVS5/cWSd07owkzcVasgfIkQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.42.0.tgz", - "integrity": "sha512-eoujJFOvoIBjZEi9hJnXAbWg+Vo1Ov8n/0IKZZcPZ7JhBzxh2A+2NFyeMZIRkY9iwBvSjloKgcvnjTbGKHE44Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.42.0.tgz", - "integrity": "sha512-/3NrcOWFSR7RQUQIuZQChLND36aTU9IYE4j+TB40VU78S+RA0IiqHR30oSh6P1S9f9/wVOenHQnacs/Byb824g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.42.0.tgz", - "integrity": "sha512-O8AplvIeavK5ABmZlKBq9/STdZlnQo7Sle0LLhVA7QT+CiGpNVe197/t8Aph9bhJqbDVGCHpY2i7QyfEDDStDg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.42.0.tgz", - "integrity": "sha512-6Qb66tbKVN7VyQrekhEzbHRxXXFFD8QKiFAwX5v9Xt6FiJ3BnCVBuyBxa2fkFGqxOCSGGYNejxd8ht+q5SnmtA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.42.0.tgz", - "integrity": "sha512-KQETDSEBamQFvg/d8jajtRwLNBlGc3aKpaGiP/LvEbnmVUKlFta1vqJqTrvPtsYsfbE/DLg5CC9zyXRX3fnBiA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.42.0.tgz", - "integrity": "sha512-qMvnyjcU37sCo/tuC+JqeDKSuukGAd+pVlRl/oyDbkvPJ3awk6G6ua7tyum02O3lI+fio+eM5wsVd66X0jQtxw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.42.0.tgz", - "integrity": "sha512-I2Y1ZUgTgU2RLddUHXTIgyrdOwljjkmcZ/VilvaEumtS3Fkuhbw4p4hgHc39Ypwvo2o7sBFNl2MquNvGCa55Iw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.42.0.tgz", - "integrity": "sha512-Gfm6cV6mj3hCUY8TqWa63DB8Mx3NADoFwiJrMpoZ1uESbK8FQV3LXkhfry+8bOniq9pqY1OdsjFWNsSbfjPugw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.42.0.tgz", - "integrity": "sha512-g86PF8YZ9GRqkdi0VoGlcDUb4rYtQKyTD1IVtxxN4Hpe7YqLBShA7oHMKU6oKTCi3uxwW4VkIGnOaH/El8de3w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.42.0.tgz", - "integrity": "sha512-+axkdyDGSp6hjyzQ5m1pgcvQScfHnMCcsXkx8pTgy/6qBmWVhtRVlgxjWwDp67wEXXUr0x+vD6tp5W4x6V7u1A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.42.0.tgz", - "integrity": "sha512-F+5J9pelstXKwRSDq92J0TEBXn2nfUrQGg+HK1+Tk7VOL09e0gBqUHugZv7SW4MGrYj41oNCUe3IKCDGVlis2g==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.42.0.tgz", - "integrity": "sha512-LpHiJRwkaVz/LqjHjK8LCi8osq7elmpwujwbXKNW88bM8eeGxavJIKKjkjpMHAh/2xfnrt1ZSnhTv41WYUHYmA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/geojson": { - "version": "7946.0.16", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/leaflet": { - "version": "1.9.18", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } - }, - "node_modules/@types/prop-types": { - "version": "15.7.14", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "18.3.23", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "18.3.7", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^18.0.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.33.1.tgz", - "integrity": "sha512-TDCXj+YxLgtvxvFlAvpoRv9MAncDLBV2oT9Bd7YBGC/b/sEURoOYuIwLI99rjWOfY3QtDzO+mk0n4AmdFExW8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.33.1", - "@typescript-eslint/type-utils": "8.33.1", - "@typescript-eslint/utils": "8.33.1", - "@typescript-eslint/visitor-keys": "8.33.1", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.33.1", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.33.1.tgz", - "integrity": "sha512-qwxv6dq682yVvgKKp2qWwLgRbscDAYktPptK4JPojCwwi3R9cwrvIxS4lvBpzmcqzR4bdn54Z0IG1uHFskW4dA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@typescript-eslint/scope-manager": "8.33.1", - "@typescript-eslint/types": "8.33.1", - "@typescript-eslint/typescript-estree": "8.33.1", - "@typescript-eslint/visitor-keys": "8.33.1", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.33.1.tgz", - "integrity": "sha512-DZR0efeNklDIHHGRpMpR5gJITQpu6tLr9lDJnKdONTC7vvzOlLAG/wcfxcdxEWrbiZApcoBCzXqU/Z458Za5Iw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.33.1", - "@typescript-eslint/types": "^8.33.1", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.33.1.tgz", - "integrity": "sha512-dM4UBtgmzHR9bS0Rv09JST0RcHYearoEoo3pG5B6GoTR9XcyeqX87FEhPo+5kTvVfKCvfHaHrcgeJQc6mrDKrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.33.1", - "@typescript-eslint/visitor-keys": "8.33.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.33.1.tgz", - "integrity": "sha512-STAQsGYbHCF0/e+ShUQ4EatXQ7ceh3fBCXkNU7/MZVKulrlq1usH7t2FhxvCpuCi5O5oi1vmVaAjrGeL71OK1g==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.33.1.tgz", - "integrity": "sha512-1cG37d9xOkhlykom55WVwG2QRNC7YXlxMaMzqw2uPeJixBFfKWZgaP/hjAObqMN/u3fr5BrTwTnc31/L9jQ2ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "8.33.1", - "@typescript-eslint/utils": "8.33.1", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.33.1.tgz", - "integrity": "sha512-xid1WfizGhy/TKMTwhtVOgalHwPtV8T32MS9MaH50Cwvz6x6YqRIPdD2WvW0XaqOzTV9p5xdLY0h/ZusU5Lokg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.33.1.tgz", - "integrity": "sha512-+s9LYcT8LWjdYWu7IWs7FvUxpQ/DGkdjZeE/GGulHvv8rvYwQvVaUZ6DE+j5x/prADUgSbbCWZ2nPI3usuVeOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.33.1", - "@typescript-eslint/tsconfig-utils": "8.33.1", - "@typescript-eslint/types": "8.33.1", - "@typescript-eslint/visitor-keys": "8.33.1", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.33.1.tgz", - "integrity": "sha512-52HaBiEQUaRYqAXpfzWSR2U3gxk92Kw006+xZpElaPMg3C4PgM+A5LqwoQI1f9E5aZ/qlxAZxzm42WX+vn92SQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.33.1", - "@typescript-eslint/types": "8.33.1", - "@typescript-eslint/typescript-estree": "8.33.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.33.1.tgz", - "integrity": "sha512-3i8NrFcZeeDHJ+7ZUuDkGT+UHq+XoFGsymNK2jZCOHcfEzRQ0BdpRtdpSx/Iyf3MHLWIcLS0COuOPibKQboIiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.33.1", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@vitejs/plugin-react": { - "version": "4.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.26.10", - "@babel/plugin-transform-react-jsx-self": "^7.25.9", - "@babel/plugin-transform-react-jsx-source": "^7.25.9", - "@rolldown/pluginutils": "1.0.0-beta.9", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.17.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" - } - }, - "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "dev": true, - "license": "MIT", - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true, - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true, - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/autoprefixer": { - "version": "10.4.21", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.24.4", - "caniuse-lite": "^1.0.30001702", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.25.0", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "peer": true, - "dependencies": { - "caniuse-lite": "^1.0.30001718", - "electron-to-chromium": "^1.5.160", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001720", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.0.4", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true, - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.161", - "dev": true, - "license": "ISC" - }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.28.0.tgz", - "integrity": "sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.20.0", - "@eslint/config-helpers": "^0.2.1", - "@eslint/core": "^0.14.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.28.0", - "@eslint/plugin-kit": "^0.3.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", - "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" - } - }, - "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.20", - "dev": true, - "license": "MIT", - "peerDependencies": { - "eslint": ">=8.40" - } - }, - "node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.14.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/fraction.js": { - "version": "4.3.7", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/graphemer": { - "version": "1.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/js-sha256": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.11.1.tgz", - "integrity": "sha512-o6WSo/LUvY2uC4j7mO50a2ms7E/EAdbP0swigLV+nzHKTTaYnaLIWJ02VdXrsJX0vGedDESQnLsOekr94ryfjg==", - "license": "MIT" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jwt-decode": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", - "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/keycloak-js": { - "version": "24.0.5", - "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-24.0.5.tgz", - "integrity": "sha512-VQOSn3j13DPB6OuavKAq+sRjDERhIKrXgBzekoHRstifPuyULILguugX6yxRUYFSpn3OMYUXmSX++tkdCupOjA==", - "license": "Apache-2.0", - "dependencies": { - "js-sha256": "^0.11.0", - "jwt-decode": "^4.0.0" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/leaflet": { - "version": "1.9.4", - "license": "BSD-2-Clause", - "peer": true - }, - "node_modules/levn": { - "version": "0.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.30.1", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "dev": true, - "license": "MIT" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.19", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/postcss": { - "version": "8.5.4", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "peer": true, - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", - "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-load-config": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", - "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.1.1" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "jiti": ">=1.21.0", - "postcss": ">=8.0.9", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - }, - "postcss": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/react": { - "version": "18.3.1", - "license": "MIT", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.3.1", - "license": "MIT", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - }, - "peerDependencies": { - "react": "^18.3.1" - } - }, - "node_modules/react-leaflet": { - "version": "4.2.1", - "license": "Hippocratic-2.1", - "dependencies": { - "@react-leaflet/core": "^2.1.0" - }, - "peerDependencies": { - "leaflet": "^1.9.0", - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/react-refresh": { - "version": "0.17.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-router": { - "version": "6.30.1", - "license": "MIT", - "dependencies": { - "@remix-run/router": "1.23.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/react-router-dom": { - "version": "6.30.1", - "license": "MIT", - "dependencies": { - "@remix-run/router": "1.23.0", - "react-router": "6.30.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rollup": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.42.0.tgz", - "integrity": "sha512-LW+Vse3BJPyGJGAJt1j8pWDKPd73QM8cRXYK1IxOBgL2AGLu7Xd2YOW0M2sLUBCkF5MshXXtMApyEAEzMVMsnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.7" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.42.0", - "@rollup/rollup-android-arm64": "4.42.0", - "@rollup/rollup-darwin-arm64": "4.42.0", - "@rollup/rollup-darwin-x64": "4.42.0", - "@rollup/rollup-freebsd-arm64": "4.42.0", - "@rollup/rollup-freebsd-x64": "4.42.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.42.0", - "@rollup/rollup-linux-arm-musleabihf": "4.42.0", - "@rollup/rollup-linux-arm64-gnu": "4.42.0", - "@rollup/rollup-linux-arm64-musl": "4.42.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.42.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.42.0", - "@rollup/rollup-linux-riscv64-gnu": "4.42.0", - "@rollup/rollup-linux-riscv64-musl": "4.42.0", - "@rollup/rollup-linux-s390x-gnu": "4.42.0", - "@rollup/rollup-linux-x64-gnu": "4.42.0", - "@rollup/rollup-linux-x64-musl": "4.42.0", - "@rollup/rollup-win32-arm64-msvc": "4.42.0", - "@rollup/rollup-win32-ia32-msvc": "4.42.0", - "@rollup/rollup-win32-x64-msvc": "4.42.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/rollup/node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/scheduler": { - "version": "0.23.2", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/sucrase": { - "version": "3.35.1", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", - "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "tinyglobby": "^0.2.11", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tailwindcss": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", - "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.6.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.2", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.7", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.47", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tailwindcss/node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/type-check": { - "version": "0.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/vite": { - "version": "5.4.19", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", - "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "dev": true, - "license": "ISC" - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/frontend-old-20260125/package.json b/frontend-old-20260125/package.json deleted file mode 100644 index 7ba14df..0000000 --- a/frontend-old-20260125/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "vip-coordinator-frontend", - "private": true, - "version": "0.0.0", - "type": "module", - "engines": { - "node": ">=22.0.0", - "npm": ">=10.0.0" - }, - "scripts": { - "dev": "node ./node_modules/vite/bin/vite.js", - "build": "tsc && vite build", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview" - }, - "dependencies": { - "keycloak-js": "^24.0.5", - "leaflet": "^1.9.4", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-leaflet": "^4.2.1", - "react-router-dom": "^6.15.0" - }, - "devDependencies": { - "@types/leaflet": "^1.9.4", - "@types/react": "^18.2.15", - "@types/react-dom": "^18.2.7", - "@typescript-eslint/eslint-plugin": "^8.15.0", - "@typescript-eslint/parser": "^8.15.0", - "@vitejs/plugin-react": "^4.3.3", - "autoprefixer": "^10.4.14", - "eslint": "^9.15.0", - "eslint-plugin-react-hooks": "^5.0.0", - "eslint-plugin-react-refresh": "^0.4.14", - "postcss": "^8.5.4", - "tailwindcss": "^3.4.1", - "typescript": "^5.6.0", - "vite": "^5.4.10" - } -} diff --git a/frontend-old-20260125/postcss.config.mjs b/frontend-old-20260125/postcss.config.mjs deleted file mode 100644 index 6fd6f1d..0000000 --- a/frontend-old-20260125/postcss.config.mjs +++ /dev/null @@ -1,9 +0,0 @@ -import tailwindcss from 'tailwindcss'; -import autoprefixer from 'autoprefixer'; - -export default { - plugins: [ - tailwindcss, - autoprefixer, - ] -} \ No newline at end of file diff --git a/frontend-old-20260125/public/README-API.md b/frontend-old-20260125/public/README-API.md deleted file mode 100644 index b12766c..0000000 --- a/frontend-old-20260125/public/README-API.md +++ /dev/null @@ -1,218 +0,0 @@ -# VIP Coordinator API Documentation - -## šŸ“š Overview - -This document provides comprehensive API documentation for the VIP Coordinator system using **OpenAPI 3.0** (Swagger) specification. The API enables management of VIP transportation coordination, including flight tracking, driver management, and event scheduling. - -## šŸš€ Quick Start - -### View API Documentation - -1. **Interactive Documentation (Recommended):** - ```bash - # Open the interactive Swagger UI documentation - open vip-coordinator/api-docs.html - ``` - Or visit: `file:///path/to/vip-coordinator/api-docs.html` - -2. **Raw OpenAPI Specification:** - ```bash - # View the YAML specification file - cat vip-coordinator/api-documentation.yaml - ``` - -### Test the API - -The interactive documentation includes a "Try it out" feature that allows you to test endpoints directly: - -1. Open `api-docs.html` in your browser -2. Click on any endpoint to expand it -3. Click "Try it out" button -4. Fill in parameters and request body -5. Click "Execute" to make the API call - -## šŸ“‹ API Categories - -### šŸ„ Health -- `GET /api/health` - System health check - -### šŸ‘„ VIPs -- `GET /api/vips` - Get all VIPs -- `POST /api/vips` - Create new VIP -- `PUT /api/vips/{id}` - Update VIP -- `DELETE /api/vips/{id}` - Delete VIP - -### šŸš— Drivers -- `GET /api/drivers` - Get all drivers -- `POST /api/drivers` - Create new driver -- `PUT /api/drivers/{id}` - Update driver -- `DELETE /api/drivers/{id}` - Delete driver -- `GET /api/drivers/{driverId}/schedule` - Get driver's schedule -- `POST /api/drivers/availability` - Check driver availability -- `POST /api/drivers/{driverId}/conflicts` - Check driver conflicts - -### āœˆļø Flights -- `GET /api/flights/{flightNumber}` - Get flight information -- `POST /api/flights/{flightNumber}/track` - Start flight tracking -- `DELETE /api/flights/{flightNumber}/track` - Stop flight tracking -- `POST /api/flights/batch` - Get multiple flights info -- `GET /api/flights/tracking/status` - Get tracking status - -### šŸ“… Schedule -- `GET /api/vips/{vipId}/schedule` - Get VIP's schedule -- `POST /api/vips/{vipId}/schedule` - Add event to schedule -- `PUT /api/vips/{vipId}/schedule/{eventId}` - Update event -- `DELETE /api/vips/{vipId}/schedule/{eventId}` - Delete event -- `PATCH /api/vips/{vipId}/schedule/{eventId}/status` - Update event status - -### āš™ļø Admin -- `POST /api/admin/authenticate` - Admin authentication -- `GET /api/admin/settings` - Get admin settings -- `POST /api/admin/settings` - Update admin settings - -## šŸ’” Example API Calls - -### Create a VIP with Flight -```bash -curl -X POST http://localhost:3000/api/vips \ - -H "Content-Type: application/json" \ - -d '{ - "name": "John Doe", - "organization": "Tech Corp", - "transportMode": "flight", - "flights": [ - { - "flightNumber": "UA1234", - "flightDate": "2025-06-26", - "segment": 1 - } - ], - "needsAirportPickup": true, - "needsVenueTransport": true, - "notes": "CEO - requires executive transport" - }' -``` - -### Add Event to VIP Schedule -```bash -curl -X POST http://localhost:3000/api/vips/{vipId}/schedule \ - -H "Content-Type: application/json" \ - -d '{ - "title": "Meeting with CEO", - "location": "Hyatt Regency Denver", - "startTime": "2025-06-26T11:00:00", - "endTime": "2025-06-26T12:30:00", - "type": "meeting", - "assignedDriverId": "1748780965562", - "description": "Important strategic meeting" - }' -``` - -### Check Driver Availability -```bash -curl -X POST http://localhost:3000/api/drivers/availability \ - -H "Content-Type: application/json" \ - -d '{ - "startTime": "2025-06-26T11:00:00", - "endTime": "2025-06-26T12:30:00", - "location": "Denver Convention Center" - }' -``` - -### Get Flight Information -```bash -curl "http://localhost:3000/api/flights/UA1234?date=2025-06-26" -``` - -## šŸ”§ Tools for API Documentation - -### 1. **Swagger UI (Recommended)** -- **What it is:** Interactive web-based API documentation -- **Features:** - - Try endpoints directly in browser - - Auto-generated from OpenAPI spec - - Beautiful, responsive interface - - Request/response examples -- **Access:** Open `api-docs.html` in your browser - -### 2. **OpenAPI Specification** -- **What it is:** Industry-standard API specification format -- **Features:** - - Machine-readable API definition - - Can generate client SDKs - - Supports validation and testing - - Compatible with many tools -- **File:** `api-documentation.yaml` - -### 3. **Alternative Tools** - -You can use the OpenAPI specification with other tools: - -#### Postman -1. Import `api-documentation.yaml` into Postman -2. Automatically creates a collection with all endpoints -3. Includes examples and validation - -#### Insomnia -1. Import the OpenAPI spec -2. Generate requests automatically -3. Built-in environment management - -#### VS Code Extensions -- **OpenAPI (Swagger) Editor** - Edit and preview API specs -- **REST Client** - Test APIs directly in VS Code - -## šŸ“– Documentation Best Practices - -### Why OpenAPI/Swagger? - -1. **Industry Standard:** Most widely adopted API documentation format -2. **Interactive:** Users can test APIs directly in the documentation -3. **Code Generation:** Can generate client libraries in multiple languages -4. **Validation:** Ensures API requests/responses match specification -5. **Tooling:** Extensive ecosystem of tools and integrations - -### Documentation Features - -- **Comprehensive:** All endpoints, parameters, and responses documented -- **Examples:** Real-world examples for all operations -- **Schemas:** Detailed data models with validation rules -- **Error Handling:** Clear error response documentation -- **Authentication:** Security requirements clearly specified - -## šŸ”— Integration Examples - -### Frontend Integration -```javascript -// Example: Fetch VIPs in React -const fetchVips = async () => { - const response = await fetch('/api/vips'); - const vips = await response.json(); - return vips; -}; -``` - -### Backend Integration -```bash -# Example: Using curl to test endpoints -curl -X GET http://localhost:3000/api/health -curl -X GET http://localhost:3000/api/vips -curl -X GET http://localhost:3000/api/drivers -``` - -## šŸš€ Next Steps - -1. **Explore the Interactive Docs:** Open `api-docs.html` and try the endpoints -2. **Test with Real Data:** Use the populated test data to explore functionality -3. **Build Integrations:** Use the API specification to build client applications -4. **Extend the API:** Add new endpoints following the established patterns - -## šŸ“ž Support - -For questions about the API: -- Review the interactive documentation -- Check the OpenAPI specification for detailed schemas -- Test endpoints using the "Try it out" feature -- Refer to the example requests and responses - -The API documentation is designed to be self-service and comprehensive, providing everything needed to integrate with the VIP Coordinator system. diff --git a/frontend-old-20260125/public/api-docs.html b/frontend-old-20260125/public/api-docs.html deleted file mode 100644 index 4f42032..0000000 --- a/frontend-old-20260125/public/api-docs.html +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - VIP Coordinator API Documentation - - - - -
-

šŸš— VIP Coordinator API

-

Comprehensive API for managing VIP transportation coordination

-
- - - -
- - - - - - diff --git a/frontend-old-20260125/public/api-documentation.yaml b/frontend-old-20260125/public/api-documentation.yaml deleted file mode 100644 index a6464a9..0000000 --- a/frontend-old-20260125/public/api-documentation.yaml +++ /dev/null @@ -1,1189 +0,0 @@ -openapi: 3.0.3 -info: - title: VIP Coordinator API - description: | - A comprehensive API for managing VIP transportation coordination, including flight tracking, - driver management, and event scheduling for high-profile guests. - - ## Features - - VIP management with flight and self-driving transport modes - - Real-time flight tracking and validation - - Driver assignment and conflict detection - - Event scheduling with validation - - Admin settings management - - ## Authentication - Most endpoints are public for demo purposes. Admin endpoints require authentication. - version: 1.0.0 - contact: - name: VIP Coordinator Support - email: support@vipcoordinator.com - license: - name: MIT - url: https://opensource.org/licenses/MIT - -servers: - - url: http://localhost:3000/api - description: Development server - - url: https://api.vipcoordinator.com/api - description: Production server - -tags: - - name: Health - description: System health checks - - name: VIPs - description: VIP management operations - - name: Drivers - description: Driver management operations - - name: Flights - description: Flight tracking and information - - name: Schedule - description: Event and meeting scheduling - - name: Admin - description: Administrative operations - -paths: - /health: - get: - tags: - - Health - summary: Health check endpoint - description: Returns the current status of the API server - responses: - '200': - description: Server is healthy - content: - application/json: - schema: - type: object - properties: - status: - type: string - example: "OK" - timestamp: - type: string - format: date-time - example: "2025-06-01T12:00:00.000Z" - - /vips: - get: - tags: - - VIPs - summary: Get all VIPs - description: Retrieve a list of all VIPs in the system - responses: - '200': - description: List of VIPs - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/VIP' - - post: - tags: - - VIPs - summary: Create a new VIP - description: Add a new VIP to the system - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/VIPCreate' - examples: - flight_vip: - summary: VIP with flight transport - value: - name: "John Doe" - organization: "Tech Corp" - transportMode: "flight" - flights: - - flightNumber: "UA1234" - flightDate: "2025-06-26" - segment: 1 - needsAirportPickup: true - needsVenueTransport: true - notes: "CEO - requires executive transport" - self_driving: - summary: Self-driving VIP - value: - name: "Jane Smith" - organization: "Local Business" - transportMode: "self-driving" - expectedArrival: "2025-06-26T14:00:00" - needsAirportPickup: false - needsVenueTransport: true - notes: "Driving from Colorado Springs" - responses: - '201': - description: VIP created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/VIP' - '400': - description: Invalid input data - - /vips/{id}: - put: - tags: - - VIPs - summary: Update a VIP - description: Update an existing VIP's information - parameters: - - name: id - in: path - required: true - schema: - type: string - description: VIP ID - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/VIPCreate' - responses: - '200': - description: VIP updated successfully - content: - application/json: - schema: - $ref: '#/components/schemas/VIP' - '404': - description: VIP not found - - delete: - tags: - - VIPs - summary: Delete a VIP - description: Remove a VIP from the system - parameters: - - name: id - in: path - required: true - schema: - type: string - description: VIP ID - responses: - '200': - description: VIP deleted successfully - content: - application/json: - schema: - type: object - properties: - message: - type: string - example: "VIP deleted successfully" - vip: - $ref: '#/components/schemas/VIP' - '404': - description: VIP not found - - /vips/{vipId}/schedule: - get: - tags: - - Schedule - summary: Get VIP's schedule - description: Retrieve all scheduled events for a specific VIP - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - responses: - '200': - description: VIP's schedule - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ScheduleEvent' - - post: - tags: - - Schedule - summary: Add event to VIP's schedule - description: Create a new event for a VIP with validation - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ScheduleEventCreate' - example: - title: "Meeting with CEO" - location: "Hyatt Regency Denver" - startTime: "2025-06-26T11:00:00" - endTime: "2025-06-26T12:30:00" - type: "meeting" - assignedDriverId: "1748780965562" - description: "Important strategic meeting" - responses: - '201': - description: Event created successfully - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/ScheduleEvent' - - type: object - properties: - warnings: - type: array - items: - $ref: '#/components/schemas/ValidationWarning' - '400': - description: Validation failed - content: - application/json: - schema: - $ref: '#/components/schemas/ValidationError' - - /vips/{vipId}/schedule/{eventId}: - put: - tags: - - Schedule - summary: Update a scheduled event - description: Update an existing event in a VIP's schedule - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - - name: eventId - in: path - required: true - schema: - type: string - description: Event ID - requestBody: - required: true - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/ScheduleEventCreate' - - type: object - properties: - status: - $ref: '#/components/schemas/EventStatus' - responses: - '200': - description: Event updated successfully - content: - application/json: - schema: - $ref: '#/components/schemas/ScheduleEvent' - '404': - description: VIP or event not found - - delete: - tags: - - Schedule - summary: Delete a scheduled event - description: Remove an event from a VIP's schedule - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - - name: eventId - in: path - required: true - schema: - type: string - description: Event ID - responses: - '200': - description: Event deleted successfully - '404': - description: VIP or event not found - - /vips/{vipId}/schedule/{eventId}/status: - patch: - tags: - - Schedule - summary: Update event status - description: Update the status of a specific event - parameters: - - name: vipId - in: path - required: true - schema: - type: string - description: VIP ID - - name: eventId - in: path - required: true - schema: - type: string - description: Event ID - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - status: - $ref: '#/components/schemas/EventStatus' - example: - status: "in-progress" - responses: - '200': - description: Event status updated - content: - application/json: - schema: - $ref: '#/components/schemas/ScheduleEvent' - '404': - description: VIP or event not found - - /drivers: - get: - tags: - - Drivers - summary: Get all drivers - description: Retrieve a list of all drivers in the system - responses: - '200': - description: List of drivers - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Driver' - - post: - tags: - - Drivers - summary: Create a new driver - description: Add a new driver to the system - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DriverCreate' - example: - name: "Carlos Rodriguez" - phone: "(303) 555-0101" - currentLocation: - lat: 39.8561 - lng: -104.6737 - responses: - '201': - description: Driver created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Driver' - - /drivers/{id}: - put: - tags: - - Drivers - summary: Update a driver - description: Update an existing driver's information - parameters: - - name: id - in: path - required: true - schema: - type: string - description: Driver ID - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DriverCreate' - responses: - '200': - description: Driver updated successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Driver' - '404': - description: Driver not found - - delete: - tags: - - Drivers - summary: Delete a driver - description: Remove a driver from the system - parameters: - - name: id - in: path - required: true - schema: - type: string - description: Driver ID - responses: - '200': - description: Driver deleted successfully - '404': - description: Driver not found - - /drivers/{driverId}/schedule: - get: - tags: - - Drivers - summary: Get driver's schedule - description: Retrieve all events assigned to a specific driver - parameters: - - name: driverId - in: path - required: true - schema: - type: string - description: Driver ID - responses: - '200': - description: Driver's schedule - content: - application/json: - schema: - type: object - properties: - driver: - type: object - properties: - id: - type: string - name: - type: string - phone: - type: string - schedule: - type: array - items: - allOf: - - $ref: '#/components/schemas/ScheduleEvent' - - type: object - properties: - vipId: - type: string - vipName: - type: string - '404': - description: Driver not found - - /drivers/availability: - post: - tags: - - Drivers - summary: Check driver availability - description: Find available drivers for a specific time slot - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - startTime: - type: string - format: date-time - endTime: - type: string - format: date-time - location: - type: string - required: - - startTime - - endTime - example: - startTime: "2025-06-26T11:00:00" - endTime: "2025-06-26T12:30:00" - location: "Denver Convention Center" - responses: - '200': - description: Driver availability information - content: - application/json: - schema: - type: object - properties: - available: - type: array - items: - $ref: '#/components/schemas/Driver' - busy: - type: array - items: - allOf: - - $ref: '#/components/schemas/Driver' - - type: object - properties: - conflictingEvents: - type: array - items: - $ref: '#/components/schemas/ScheduleEvent' - - /drivers/{driverId}/conflicts: - post: - tags: - - Drivers - summary: Check driver conflicts - description: Check if a specific driver has conflicts for a time slot - parameters: - - name: driverId - in: path - required: true - schema: - type: string - description: Driver ID - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - startTime: - type: string - format: date-time - endTime: - type: string - format: date-time - location: - type: string - required: - - startTime - - endTime - responses: - '200': - description: Conflict check results - content: - application/json: - schema: - type: object - properties: - conflicts: - type: array - items: - $ref: '#/components/schemas/ScheduleEvent' - - /flights/{flightNumber}: - get: - tags: - - Flights - summary: Get flight information - description: Retrieve real-time flight information - parameters: - - name: flightNumber - in: path - required: true - schema: - type: string - description: Flight number (e.g., UA1234) - example: "UA1234" - - name: date - in: query - schema: - type: string - format: date - description: Flight date (YYYY-MM-DD) - example: "2025-06-26" - - name: departureAirport - in: query - schema: - type: string - description: Departure airport code - example: "LAX" - - name: arrivalAirport - in: query - schema: - type: string - description: Arrival airport code - example: "DEN" - responses: - '200': - description: Flight information - content: - application/json: - schema: - $ref: '#/components/schemas/FlightInfo' - '404': - description: Flight not found - '500': - description: Failed to fetch flight data - - /flights/{flightNumber}/track: - post: - tags: - - Flights - summary: Start flight tracking - description: Begin periodic updates for a specific flight - parameters: - - name: flightNumber - in: path - required: true - schema: - type: string - description: Flight number - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - date: - type: string - format: date - intervalMinutes: - type: integer - default: 5 - required: - - date - example: - date: "2025-06-26" - intervalMinutes: 5 - responses: - '200': - description: Flight tracking started - content: - application/json: - schema: - type: object - properties: - message: - type: string - example: "Started tracking UA1234 on 2025-06-26" - - delete: - tags: - - Flights - summary: Stop flight tracking - description: Stop periodic updates for a specific flight - parameters: - - name: flightNumber - in: path - required: true - schema: - type: string - description: Flight number - - name: date - in: query - required: true - schema: - type: string - format: date - description: Flight date - responses: - '200': - description: Flight tracking stopped - - /flights/batch: - post: - tags: - - Flights - summary: Get multiple flights information - description: Retrieve information for multiple flights at once - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - flights: - type: array - items: - type: object - properties: - flightNumber: - type: string - date: - type: string - format: date - required: - - flightNumber - - date - example: - flights: - - flightNumber: "UA1234" - date: "2025-06-26" - - flightNumber: "AA789" - date: "2025-06-26" - responses: - '200': - description: Multiple flight information - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/FlightInfo' - - /flights/tracking/status: - get: - tags: - - Flights - summary: Get flight tracking status - description: Get the status of all currently tracked flights - responses: - '200': - description: Flight tracking status - content: - application/json: - schema: - type: object - properties: - trackedFlights: - type: array - items: - type: object - properties: - flightKey: - type: string - vipName: - type: string - lastUpdate: - type: string - format: date-time - status: - type: string - - /admin/authenticate: - post: - tags: - - Admin - summary: Admin authentication - description: Authenticate admin user - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - password: - type: string - required: - - password - responses: - '200': - description: Authentication successful - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - '401': - description: Invalid password - - /admin/settings: - get: - tags: - - Admin - summary: Get admin settings - description: Retrieve current admin settings (requires authentication) - parameters: - - name: admin-auth - in: header - required: true - schema: - type: string - description: Admin authentication header - responses: - '200': - description: Admin settings - content: - application/json: - schema: - $ref: '#/components/schemas/AdminSettings' - '401': - description: Unauthorized - - post: - tags: - - Admin - summary: Update admin settings - description: Update admin settings (requires authentication) - parameters: - - name: admin-auth - in: header - required: true - schema: - type: string - description: Admin authentication header - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AdminSettings' - responses: - '200': - description: Settings updated successfully - '401': - description: Unauthorized - -components: - schemas: - VIP: - type: object - properties: - id: - type: string - description: Unique VIP identifier - name: - type: string - description: VIP's full name - organization: - type: string - description: VIP's organization or company - transportMode: - type: string - enum: [flight, self-driving] - description: Mode of transportation - flights: - type: array - items: - $ref: '#/components/schemas/Flight' - description: Flight information (for flight transport mode) - expectedArrival: - type: string - format: date-time - description: Expected arrival time (for self-driving mode) - needsAirportPickup: - type: boolean - description: Whether VIP needs airport pickup - needsVenueTransport: - type: boolean - description: Whether VIP needs venue transport - assignedDriverIds: - type: array - items: - type: string - description: List of assigned driver IDs - notes: - type: string - description: Additional notes about the VIP - schedule: - type: array - items: - $ref: '#/components/schemas/ScheduleEvent' - description: VIP's schedule (usually empty, fetched separately) - - VIPCreate: - type: object - required: - - name - - organization - - transportMode - properties: - name: - type: string - minLength: 1 - organization: - type: string - minLength: 1 - transportMode: - type: string - enum: [flight, self-driving] - flights: - type: array - items: - $ref: '#/components/schemas/Flight' - expectedArrival: - type: string - format: date-time - needsAirportPickup: - type: boolean - default: true - needsVenueTransport: - type: boolean - default: true - notes: - type: string - - Flight: - type: object - required: - - flightNumber - - flightDate - - segment - properties: - flightNumber: - type: string - description: Flight number (e.g., UA1234) - flightDate: - type: string - format: date - description: Flight date - segment: - type: integer - minimum: 1 - description: Flight segment number for connecting flights - validated: - type: boolean - description: Whether flight has been validated - validationData: - $ref: '#/components/schemas/FlightInfo' - - Driver: - type: object - properties: - id: - type: string - description: Unique driver identifier - name: - type: string - description: Driver's full name - phone: - type: string - description: Driver's phone number - currentLocation: - $ref: '#/components/schemas/Location' - assignedVipIds: - type: array - items: - type: string - description: List of assigned VIP IDs - - DriverCreate: - type: object - required: - - name - - phone - properties: - name: - type: string - minLength: 1 - phone: - type: string - minLength: 1 - currentLocation: - $ref: '#/components/schemas/Location' - - Location: - type: object - properties: - lat: - type: number - format: float - description: Latitude - lng: - type: number - format: float - description: Longitude - - ScheduleEvent: - type: object - properties: - id: - type: string - description: Unique event identifier - title: - type: string - description: Event title - location: - type: string - description: Event location - startTime: - type: string - format: date-time - description: Event start time - endTime: - type: string - format: date-time - description: Event end time - description: - type: string - description: Event description - assignedDriverId: - type: string - description: Assigned driver ID - status: - $ref: '#/components/schemas/EventStatus' - type: - $ref: '#/components/schemas/EventType' - - ScheduleEventCreate: - type: object - required: - - title - - location - - startTime - - endTime - - type - properties: - title: - type: string - minLength: 1 - location: - type: string - minLength: 1 - startTime: - type: string - format: date-time - endTime: - type: string - format: date-time - description: - type: string - type: - $ref: '#/components/schemas/EventType' - assignedDriverId: - type: string - - EventStatus: - type: string - enum: [scheduled, in-progress, completed, cancelled] - description: Current status of the event - - EventType: - type: string - enum: [transport, meeting, event, meal, accommodation] - description: Type of event - - FlightInfo: - type: object - properties: - flightNumber: - type: string - flightDate: - type: string - format: date - status: - type: string - enum: [scheduled, active, landed, cancelled, delayed] - airline: - type: string - aircraft: - type: string - departure: - $ref: '#/components/schemas/FlightLocation' - arrival: - $ref: '#/components/schemas/FlightLocation' - delay: - type: integer - description: Delay in minutes - lastUpdated: - type: string - format: date-time - source: - type: string - description: Data source (e.g., aviationstack) - - FlightLocation: - type: object - properties: - airport: - type: string - description: Airport code - airportName: - type: string - description: Full airport name - scheduled: - type: string - format: date-time - estimated: - type: string - format: date-time - actual: - type: string - format: date-time - terminal: - type: string - gate: - type: string - - AdminSettings: - type: object - properties: - apiKeys: - type: object - properties: - aviationStackKey: - type: string - description: Masked API key - googleMapsKey: - type: string - description: Masked API key - twilioKey: - type: string - description: Masked API key - systemSettings: - type: object - properties: - defaultPickupLocation: - type: string - defaultDropoffLocation: - type: string - timeZone: - type: string - notificationsEnabled: - type: boolean - - ValidationError: - type: object - properties: - error: - type: string - validationErrors: - type: array - items: - $ref: '#/components/schemas/ValidationMessage' - warnings: - type: array - items: - $ref: '#/components/schemas/ValidationWarning' - message: - type: string - - ValidationMessage: - type: object - properties: - field: - type: string - message: - type: string - code: - type: string - - ValidationWarning: - type: object - properties: - field: - type: string - message: - type: string - code: - type: string - - securitySchemes: - AdminAuth: - type: apiKey - in: header - name: admin-auth - description: Admin authentication header - -security: - - AdminAuth: [] diff --git a/frontend-old-20260125/public/silent-check-sso.html b/frontend-old-20260125/public/silent-check-sso.html deleted file mode 100644 index 29e06a3..0000000 --- a/frontend-old-20260125/public/silent-check-sso.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - Silent Check SSO - - - - - diff --git a/frontend-old-20260125/src/App.css b/frontend-old-20260125/src/App.css deleted file mode 100644 index 059347a..0000000 --- a/frontend-old-20260125/src/App.css +++ /dev/null @@ -1,190 +0,0 @@ -/* Modern App-specific styles using Tailwind utilities */ -@tailwind base; -@tailwind components; -@tailwind utilities; - -/* Enhanced button styles */ -@layer components { - .btn-modern { - @apply px-6 py-3 rounded-xl font-semibold text-sm transition-all duration-200 shadow-lg hover:shadow-xl transform hover:-translate-y-0.5; - } - - .btn-gradient-blue { - @apply bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white; - } - - .btn-gradient-green { - @apply bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 text-white; - } - - .btn-gradient-purple { - @apply bg-gradient-to-r from-purple-500 to-purple-600 hover:from-purple-600 hover:to-purple-700 text-white; - } - - .btn-gradient-amber { - @apply bg-gradient-to-r from-amber-500 to-amber-600 hover:from-amber-600 hover:to-amber-700 text-white; - } -} - -/* Status badges */ -@layer components { - .status-badge { - @apply inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold; - } - - .status-scheduled { - @apply bg-blue-100 text-blue-800 border border-blue-200; - } - - .status-in-progress { - @apply bg-amber-100 text-amber-800 border border-amber-200; - } - - .status-completed { - @apply bg-green-100 text-green-800 border border-green-200; - } - - .status-cancelled { - @apply bg-red-100 text-red-800 border border-red-200; - } -} - -/* Card enhancements */ -@layer components { - .card-modern { - @apply bg-white rounded-2xl shadow-lg border border-slate-200/60 overflow-hidden backdrop-blur-sm; - } - - .card-header { - @apply bg-gradient-to-r from-slate-50 to-slate-100 px-6 py-4 border-b border-slate-200/60; - } - - .card-content { - @apply p-6; - } -} - -/* Loading states */ -@layer components { - .loading-spinner { - @apply animate-spin rounded-full border-4 border-blue-600 border-t-transparent; - } - - .loading-text { - @apply text-slate-600 animate-pulse; - } - - .skeleton { - @apply animate-pulse bg-slate-200 rounded; - } -} - -/* Form enhancements */ -@layer components { - .form-modern { - @apply space-y-6; - } - - .form-group-modern { - @apply space-y-2; - } - - .form-label-modern { - @apply block text-sm font-semibold text-slate-700; - } - - .form-input-modern { - @apply w-full px-4 py-3 border border-slate-300 rounded-xl shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200; - } - - .form-select-modern { - @apply w-full px-4 py-3 border border-slate-300 rounded-xl shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white transition-all duration-200; - } -} - -/* Animation utilities */ -@layer utilities { - .animate-fade-in { - animation: fadeIn 0.5s ease-in-out; - } - - .animate-slide-up { - animation: slideUp 0.3s ease-out; - } - - .animate-scale-in { - animation: scaleIn 0.2s ease-out; - } -} - -@keyframes fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -@keyframes slideUp { - from { - transform: translateY(10px); - opacity: 0; - } - to { - transform: translateY(0); - opacity: 1; - } -} - -@keyframes scaleIn { - from { - transform: scale(0.95); - opacity: 0; - } - to { - transform: scale(1); - opacity: 1; - } -} - -/* Responsive utilities */ -@media (max-width: 768px) { - .mobile-stack { - @apply flex-col space-y-4 space-x-0; - } - - .mobile-full { - @apply w-full; - } - - .mobile-text-center { - @apply text-center; - } -} - -/* Glass morphism effect */ -@layer utilities { - .glass { - @apply bg-white/80 backdrop-blur-lg border border-white/20; - } - - .glass-dark { - @apply bg-slate-900/80 backdrop-blur-lg border border-slate-700/20; - } -} - -/* Hover effects */ -@layer utilities { - .hover-lift { - @apply transition-transform duration-200 hover:-translate-y-1; - } - - .hover-glow { - @apply transition-shadow duration-200 hover:shadow-2xl; - } - - .hover-scale { - @apply transition-transform duration-200 hover:scale-105; - } -} diff --git a/frontend-old-20260125/src/App.tsx b/frontend-old-20260125/src/App.tsx deleted file mode 100644 index d0456d0..0000000 --- a/frontend-old-20260125/src/App.tsx +++ /dev/null @@ -1,214 +0,0 @@ -import { useState, useEffect } from 'react'; -import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom'; -import { useKeycloak } from './contexts/KeycloakContext'; -import { apiCall } from './config/api'; -import VipList from './pages/VipList'; -import VipDetails from './pages/VipDetails'; -import DriverList from './pages/DriverList'; -import DriverDashboard from './pages/DriverDashboard'; -import Dashboard from './pages/Dashboard'; -import AdminDashboard from './pages/AdminDashboard'; -import UserManagement from './components/UserManagement'; -import Login from './components/Login'; -import PendingApproval from './pages/PendingApproval'; -import './App.css'; - -function App() { - const { authenticated, loading, logout, getToken, keycloak } = useKeycloak(); - const [user, setUser] = useState(null); - const [loadingUser, setLoadingUser] = useState(true); - - useEffect(() => { - if (authenticated && keycloak) { - // Fetch user details from backend - const fetchUser = async () => { - try { - const token = await getToken(); - - if (!token) { - setLoadingUser(false); - return; - } - - const response = await apiCall('/auth/me', { - headers: { - 'Authorization': `Bearer ${token}` - } - }); - - if (response.ok) { - const userData = await response.json(); - setUser(userData.user); - } else { - console.error('Failed to fetch user data'); - } - } catch (error) { - console.error('Error fetching user:', error); - } finally { - setLoadingUser(false); - } - }; - - fetchUser(); - } else if (!authenticated && !loading) { - setLoadingUser(false); - localStorage.removeItem('authToken'); - } - }, [authenticated, keycloak, loading, getToken]); - - const handleLogout = () => { - logout(); - }; - - // Show loading state - if (loading || (authenticated && loadingUser)) { - return ( -
-
-
- Loading VIP Coordinator... -
-
- ); - } - - // Show login if not authenticated - if (!authenticated) { - return ; - } - - // Show pending approval page if user is pending - if (user?.approval_status === 'pending') { - return ; - } - - // Show access denied if user was denied - if (user?.approval_status === 'denied') { - return ( -
-
-
-
🚫
-

Access Denied

-

- Your access to VIP Coordinator has been denied by an administrator. - Please contact your system administrator for more information. -

- -
-
-
- ); - } - - // Main app for approved users - return ( - -
- {/* Modern Navigation */} - - - {/* Main Content */} -
- - } /> - } /> - } /> - } /> - } /> - } /> - } /> - -
-
-
- ); -} - -export default App; diff --git a/frontend-old-20260125/src/api/auth0Client.ts b/frontend-old-20260125/src/api/auth0Client.ts deleted file mode 100644 index 48f52f1..0000000 --- a/frontend-old-20260125/src/api/auth0Client.ts +++ /dev/null @@ -1,41 +0,0 @@ -// Auth0-aware API client wrapper -// This file provides helper functions that automatically inject Auth0 tokens - -import { api } from './client'; - -// Token provider function - will be set by App.tsx -let tokenProvider: (() => Promise) | null = null; - -export function setTokenProvider(provider: () => Promise) { - tokenProvider = provider; -} - -// Wrapper that automatically adds Auth0 token to requests -async function makeAuthenticatedRequest( - requestFn: (headers: HeadersInit) => Promise -): Promise { - if (!tokenProvider) { - // Fallback to localStorage for non-Auth0 flows (shouldn't happen) - const token = localStorage.getItem('authToken'); - const headers = token ? { Authorization: `Bearer ${token}` } : {}; - return requestFn(headers); - } - - try { - const token = await tokenProvider(); - return requestFn({ Authorization: `Bearer ${token}` }); - } catch (error) { - console.error('Failed to get access token:', error); - throw error; - } -} - -// Re-export all API methods (they already handle authorization headers) -export { api, vipApi, driverApi, scheduleApi, authApi } from './client'; - -// Note: The original ApiClient in client.ts already reads from localStorage -// and adds the Authorization header. Auth0 SDK stores tokens in localStorage -// by default (cacheLocation: "localstorage"), so everything should work seamlessly. -// -// For more advanced use cases (e.g., using Auth0's memory cache or handling -// token refresh explicitly), you would use the tokenProvider approach above. diff --git a/frontend-old-20260125/src/api/client.ts b/frontend-old-20260125/src/api/client.ts deleted file mode 100644 index 1af8e55..0000000 --- a/frontend-old-20260125/src/api/client.ts +++ /dev/null @@ -1,109 +0,0 @@ -// Simplified API client that handles all the complexity in one place -// Use empty string for relative URLs when no API URL is specified -const API_BASE_URL = import.meta.env.VITE_API_URL || ''; - -class ApiClient { - private baseURL: string; - - constructor(baseURL: string) { - this.baseURL = baseURL; - } - - private getAuthHeaders(): HeadersInit { - const token = localStorage.getItem('authToken'); - return { - 'Content-Type': 'application/json', - ...(token && { Authorization: `Bearer ${token}` }) - }; - } - - private async handleResponse(response: Response): Promise { - if (!response.ok) { - const error = await response.json().catch(() => ({ error: response.statusText })); - throw new Error(error.error?.message || error.error || `Request failed: ${response.status}`); - } - return response.json(); - } - - // Generic request method - private async request(endpoint: string, options: RequestInit = {}): Promise { - const url = `${this.baseURL}${endpoint}`; - const response = await fetch(url, { - ...options, - headers: { - ...this.getAuthHeaders(), - ...options.headers - } - }); - return this.handleResponse(response); - } - - // Convenience methods - async get(endpoint: string): Promise { - return this.request(endpoint); - } - - async post(endpoint: string, data?: any): Promise { - return this.request(endpoint, { - method: 'POST', - body: data ? JSON.stringify(data) : undefined - }); - } - - async put(endpoint: string, data?: any): Promise { - return this.request(endpoint, { - method: 'PUT', - body: data ? JSON.stringify(data) : undefined - }); - } - - async delete(endpoint: string): Promise { - return this.request(endpoint, { method: 'DELETE' }); - } - - async patch(endpoint: string, data?: any): Promise { - return this.request(endpoint, { - method: 'PATCH', - body: data ? JSON.stringify(data) : undefined - }); - } -} - -// Export a singleton instance -export const api = new ApiClient(API_BASE_URL); - -// Export specific API methods for better type safety and convenience -export const vipApi = { - list: () => api.get('/vips'), - get: (id: string) => api.get(`/vips/${id}`), - create: (data: any) => api.post('/vips', data), - update: (id: string, data: any) => api.put(`/vips/${id}`, data), - delete: (id: string) => api.delete(`/vips/${id}`), - getSchedule: (id: string) => api.get(`/vips/${id}/schedule`) -}; - -export const driverApi = { - list: () => api.get('/drivers'), - get: (id: string) => api.get(`/drivers/${id}`), - create: (data: any) => api.post('/drivers', data), - update: (id: string, data: any) => api.put(`/drivers/${id}`, data), - delete: (id: string) => api.delete(`/drivers/${id}`), - getSchedule: (id: string) => api.get(`/drivers/${id}/schedule`) -}; - -export const scheduleApi = { - create: (vipId: string, data: any) => api.post(`/vips/${vipId}/schedule`, data), - update: (vipId: string, eventId: string, data: any) => - api.put(`/vips/${vipId}/schedule/${eventId}`, data), - delete: (vipId: string, eventId: string) => - api.delete(`/vips/${vipId}/schedule/${eventId}`), - updateStatus: (vipId: string, eventId: string, status: string) => - api.patch(`/vips/${vipId}/schedule/${eventId}/status`, { status }) -}; - -export const authApi = { - me: () => api.get('/auth/me'), - logout: () => api.post('/auth/logout'), - setup: () => api.get('/auth/setup'), - googleCallback: (code: string) => api.post('/auth/google/callback', { code }) -}; \ No newline at end of file diff --git a/frontend-old-20260125/src/components/AsyncErrorBoundary.tsx b/frontend-old-20260125/src/components/AsyncErrorBoundary.tsx deleted file mode 100644 index 19f2a8e..0000000 --- a/frontend-old-20260125/src/components/AsyncErrorBoundary.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import React, { Component, ReactNode } from 'react'; - -interface Props { - children: ReactNode; - onError?: (error: Error) => void; -} - -interface State { - hasError: boolean; - error: Error | null; -} - -export class AsyncErrorBoundary extends Component { - state: State = { - hasError: false, - error: null - }; - - static getDerivedStateFromError(error: Error): State { - return { - hasError: true, - error - }; - } - - componentDidCatch(error: Error) { - console.error('AsyncErrorBoundary caught an error:', error); - this.props.onError?.(error); - } - - retry = () => { - this.setState({ hasError: false, error: null }); - }; - - render() { - if (this.state.hasError) { - return ( -
-
- - - -

- Failed to load data -

-
-

- {this.state.error?.message || 'An unexpected error occurred'} -

- -
- ); - } - - return this.props.children; - } -} \ No newline at end of file diff --git a/frontend-old-20260125/src/components/DriverForm.tsx b/frontend-old-20260125/src/components/DriverForm.tsx deleted file mode 100644 index fed9558..0000000 --- a/frontend-old-20260125/src/components/DriverForm.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import React, { useState } from 'react'; - -interface DriverFormData { - name: string; - phone: string; - vehicleCapacity: number; -} - -interface DriverFormProps { - onSubmit: (driverData: DriverFormData) => void; - onCancel: () => void; -} - -const DriverForm: React.FC = ({ onSubmit, onCancel }) => { - const [formData, setFormData] = useState({ - name: '', - phone: '', - vehicleCapacity: 4 - }); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - onSubmit(formData); - }; - - const handleChange = (e: React.ChangeEvent) => { - const { name, value, type } = e.target; - setFormData(prev => ({ - ...prev, - [name]: type === 'number' || name === 'vehicleCapacity' ? parseInt(value) || 0 : value - })); - }; - - return ( -
-
- {/* Modal Header */} -
-

Add New Driver

-

Enter driver contact information

-
- - {/* Modal Body */} -
-
-
- - -
- -
- - -
- -
- - -

- šŸš— Select the maximum number of passengers this vehicle can accommodate -

-
- -
- - -
-
-
-
-
- ); -}; - -export default DriverForm; diff --git a/frontend-old-20260125/src/components/DriverSelector.tsx b/frontend-old-20260125/src/components/DriverSelector.tsx deleted file mode 100644 index be74821..0000000 --- a/frontend-old-20260125/src/components/DriverSelector.tsx +++ /dev/null @@ -1,371 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { useAuthToken } from '../hooks/useAuthToken'; -import { apiCall } from '../config/api'; - -interface DriverAvailability { - driverId: string; - driverName: string; - vehicleCapacity: number; - status: 'available' | 'scheduled' | 'overlapping' | 'tight_turnaround'; - assignmentCount: number; - conflicts: ConflictInfo[]; - currentAssignments: ScheduleEvent[]; -} - -interface ConflictInfo { - type: 'overlap' | 'tight_turnaround' | 'back_to_back'; - severity: 'low' | 'medium' | 'high'; - message: string; - conflictingEvent: ScheduleEvent; - timeDifference?: number; -} - -interface ScheduleEvent { - id: string; - title: string; - location: string; - startTime: string; - endTime: string; - assignedDriverId?: string; - vipId: string; - vipName: string; -} - -interface DriverSelectorProps { - selectedDriverId: string; - onDriverSelect: (driverId: string) => void; - eventTime: { - startTime: string; - endTime: string; - location: string; - }; -} - -const DriverSelector: React.FC = ({ - selectedDriverId, - onDriverSelect, - eventTime -}) => { - const token = useAuthToken(); - const [availability, setAvailability] = useState([]); - const [loading, setLoading] = useState(false); - const [showConflictModal, setShowConflictModal] = useState(false); - const [selectedDriver, setSelectedDriver] = useState(null); - - useEffect(() => { - if (token && eventTime.startTime && eventTime.endTime) { - checkDriverAvailability(); - } - }, [eventTime.startTime, eventTime.endTime, eventTime.location, token]); - - const checkDriverAvailability = async () => { - if (!token) return; - setLoading(true); - try { - const response = await apiCall('/drivers/availability', { - method: 'POST', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify(eventTime), - }); - - if (response.ok) { - const data = await response.json(); - setAvailability(data); - } - } catch (error) { - console.error('Error checking driver availability:', error); - } finally { - setLoading(false); - } - }; - - const getStatusIcon = (status: string) => { - switch (status) { - case 'available': return '🟢'; - case 'scheduled': return '🟔'; - case 'tight_turnaround': return '⚔'; - case 'overlapping': return 'šŸ”“'; - default: return '⚪'; - } - }; - - const getStatusColor = (status: string) => { - switch (status) { - case 'available': return 'bg-green-50 border-green-200 text-green-800'; - case 'scheduled': return 'bg-amber-50 border-amber-200 text-amber-800'; - case 'tight_turnaround': return 'bg-orange-50 border-orange-200 text-orange-800'; - case 'overlapping': return 'bg-red-50 border-red-200 text-red-800'; - default: return 'bg-slate-50 border-slate-200 text-slate-800'; - } - }; - - const getStatusText = (status: string) => { - switch (status) { - case 'available': return 'Available'; - case 'scheduled': return 'Busy'; - case 'tight_turnaround': return 'Tight Schedule'; - case 'overlapping': return 'Conflict'; - default: return 'Unknown'; - } - }; - - const handleDriverClick = (driver: DriverAvailability) => { - if (driver.conflicts.length > 0) { - setSelectedDriver(driver); - setShowConflictModal(true); - } else { - onDriverSelect(driver.driverId); - } - }; - - const confirmDriverAssignment = () => { - if (selectedDriver) { - onDriverSelect(selectedDriver.driverId); - setShowConflictModal(false); - setSelectedDriver(null); - } - }; - - const formatTime = (timeString: string) => { - return new Date(timeString).toLocaleString([], { - hour: '2-digit', - minute: '2-digit' - }); - }; - - if (loading) { - return ( -
-
-
- Checking driver availability... -
-
- ); - } - - return ( -
-

- šŸš— Assign Driver -

- - {availability.length === 0 ? ( -
-
- šŸš— -
-

No drivers available

-

Check the time and try again

-
- ) : ( -
- {availability.map((driver) => ( -
handleDriverClick(driver)} - > - {selectedDriverId === driver.driverId && ( -
- āœ“ -
- )} - -
-
-
- {getStatusIcon(driver.status)} -
-

{driver.driverName}

-
- - {getStatusText(driver.status)} - - - šŸš— {driver.vehicleCapacity} seats - - - {driver.assignmentCount} assignments - -
-
-
- - {driver.conflicts.length > 0 && ( -
- {driver.conflicts.map((conflict, index) => ( -
-
- - {conflict.type === 'overlap' ? 'šŸ”“' : '⚔'} - - - {conflict.type === 'overlap' ? 'Time Overlap' : 'Tight Turnaround'} - -
-

- {conflict.message} -

-
- ))} -
- )} - - {driver.currentAssignments.length > 0 && driver.conflicts.length === 0 && ( -
-

Next Assignment:

-

- {driver.currentAssignments[0]?.title} at {formatTime(driver.currentAssignments[0]?.startTime)} -

-
- )} -
- - {driver.conflicts.length > 0 && ( -
- - āš ļø CONFLICTS - -
- )} -
-
- ))} - - {selectedDriverId && ( - - )} -
- )} - - {/* Conflict Resolution Modal */} - {showConflictModal && selectedDriver && ( -
-
-
-

- āš ļø Driver Assignment Conflict -

-

- {selectedDriver.driverName} has scheduling conflicts that need your attention -

-
- -
- {/* Driver Info */} -
-
- šŸš— -
-

{selectedDriver.driverName}

-

- Vehicle Capacity: {selectedDriver.vehicleCapacity} passengers • - Current Assignments: {selectedDriver.assignmentCount} -

-
-
-
- - {/* Conflicts */} -
-

Scheduling Conflicts:

-
- {selectedDriver.conflicts.map((conflict, index) => ( -
-
- - {conflict.type === 'overlap' ? 'šŸ”“' : '⚔'} - - - {conflict.type === 'overlap' ? 'Time Overlap' : 'Tight Turnaround'} - -
-

- {conflict.message} -

-
- Conflicting event: {conflict.conflictingEvent.title}
- Time: {formatTime(conflict.conflictingEvent.startTime)} - {formatTime(conflict.conflictingEvent.endTime)}
- VIP: {conflict.conflictingEvent.vipName} -
-
- ))} -
-
- - {/* Current Schedule */} -
-

Current Schedule:

-
- {selectedDriver.currentAssignments.length === 0 ? ( -

No current assignments

- ) : ( -
- {selectedDriver.currentAssignments.map((assignment, index) => ( -
- - {assignment.title} - - ({formatTime(assignment.startTime)} - {formatTime(assignment.endTime)}) - - • {assignment.vipName} -
- ))} -
- )} -
-
-
- -
- - -
-
-
- )} -
- ); -}; - -export default DriverSelector; - diff --git a/frontend-old-20260125/src/components/EditDriverForm.tsx b/frontend-old-20260125/src/components/EditDriverForm.tsx deleted file mode 100644 index ee2ee6a..0000000 --- a/frontend-old-20260125/src/components/EditDriverForm.tsx +++ /dev/null @@ -1,188 +0,0 @@ -import { useState } from 'react'; - -interface Driver { - id: string; - name: string; - phone: string; - currentLocation: { lat: number; lng: number }; - assignedVipIds: string[]; - vehicleCapacity?: number; -} - -interface EditDriverFormProps { - driver: Driver; - onSubmit: (driverData: any) => void; - onCancel: () => void; -} - -const EditDriverForm: React.FC = ({ driver, onSubmit, onCancel }) => { - const [formData, setFormData] = useState({ - name: driver.name, - phone: driver.phone, - vehicleCapacity: driver.vehicleCapacity || 4, - currentLocation: { - lat: driver.currentLocation.lat, - lng: driver.currentLocation.lng - } - }); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - onSubmit({ - ...formData, - id: driver.id - }); - }; - - const handleChange = (e: React.ChangeEvent) => { - const { name, value } = e.target; - - if (name === 'lat' || name === 'lng') { - setFormData(prev => ({ - ...prev, - currentLocation: { - ...prev.currentLocation, - [name]: parseFloat(value) || 0 - } - })); - } else if (name === 'vehicleCapacity') { - setFormData(prev => ({ ...prev, [name]: parseInt(value) || 0 })); - } else { - setFormData(prev => ({ ...prev, [name]: value })); - } - }; - - return ( -
-
- {/* Modal Header */} -
-

Edit Driver

-

Update driver information for {driver.name}

-
- - {/* Modal Body */} -
-
- {/* Basic Information Section */} -
-
-

Basic Information

-
- -
-
- - -
- -
- - -
-
- -
- - -

- šŸš— Select the maximum number of passengers this vehicle can accommodate -

-
-
- - {/* Location Section */} -
-
-

Current Location

-
- -
-
- - -
- -
- - -
-
- -
-

- Current coordinates: {formData.currentLocation.lat.toFixed(6)}, {formData.currentLocation.lng.toFixed(6)} -

-

- You can use GPS coordinates or get them from a mapping service -

-
-
- -
- - -
-
-
-
-
- ); -}; - -export default EditDriverForm; diff --git a/frontend-old-20260125/src/components/EditVipForm.tsx b/frontend-old-20260125/src/components/EditVipForm.tsx deleted file mode 100644 index 9a81515..0000000 --- a/frontend-old-20260125/src/components/EditVipForm.tsx +++ /dev/null @@ -1,541 +0,0 @@ -import { useState } from 'react'; - -interface Flight { - flightNumber: string; - flightDate: string; - segment: number; - validated?: boolean; - validationData?: any; -} - -interface VipData { - id: string; - name: string; - organization: string; - transportMode: 'flight' | 'self-driving'; - flightNumber?: string; // Legacy - flightDate?: string; // Legacy - flights?: Flight[]; // New - expectedArrival?: string; - arrivalTime?: string; - needsAirportPickup?: boolean; - needsVenueTransport: boolean; - notes: string; -} - -interface EditVipFormProps { - vip: VipData; - onSubmit: (vipData: VipData) => void; - onCancel: () => void; -} - -const EditVipForm: React.FC = ({ vip, onSubmit, onCancel }) => { - // Convert legacy single flight to new format if needed - const initialFlights = vip.flights || (vip.flightNumber ? [{ - flightNumber: vip.flightNumber, - flightDate: vip.flightDate || '', - segment: 1 - }] : [{ flightNumber: '', flightDate: '', segment: 1 }]); - - const [formData, setFormData] = useState({ - id: vip.id, - name: vip.name, - organization: vip.organization, - transportMode: vip.transportMode || 'flight', - flights: initialFlights, - expectedArrival: vip.expectedArrival ? vip.expectedArrival.slice(0, 16) : '', - arrivalTime: vip.arrivalTime ? vip.arrivalTime.slice(0, 16) : '', - needsAirportPickup: vip.needsAirportPickup !== false, - needsVenueTransport: vip.needsVenueTransport !== false, - notes: vip.notes || '' - }); - - const [flightValidating, setFlightValidating] = useState<{ [key: number]: boolean }>({}); - const [flightErrors, setFlightErrors] = useState<{ [key: number]: string }>({}); - const [isSubmitting, setIsSubmitting] = useState(false); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setIsSubmitting(true); - - try { - // Only include flights with flight numbers - const validFlights = formData.flights?.filter(f => f.flightNumber) || []; - - await onSubmit({ - ...formData, - flights: validFlights.length > 0 ? validFlights : undefined - }); - } catch (error) { - console.error('Error updating VIP:', error); - } finally { - setIsSubmitting(false); - } - }; - - const handleChange = (e: React.ChangeEvent) => { - const { name, value, type } = e.target; - - if (type === 'checkbox') { - const checked = (e.target as HTMLInputElement).checked; - setFormData(prev => ({ - ...prev, - [name]: checked - })); - } else { - setFormData(prev => ({ - ...prev, - [name]: value - })); - } - }; - - const handleTransportModeChange = (mode: 'flight' | 'self-driving') => { - setFormData(prev => ({ - ...prev, - transportMode: mode, - flights: mode === 'flight' ? (prev.flights || [{ flightNumber: '', flightDate: '', segment: 1 }]) : undefined, - expectedArrival: mode === 'self-driving' ? prev.expectedArrival : '', - needsAirportPickup: mode === 'flight' ? true : false - })); - - // Clear flight errors when switching away from flight mode - if (mode !== 'flight') { - setFlightErrors({}); - } - }; - - const handleFlightChange = (index: number, field: 'flightNumber' | 'flightDate', value: string) => { - setFormData(prev => ({ - ...prev, - flights: prev.flights?.map((flight, i) => - i === index ? { ...flight, [field]: value, validated: false } : flight - ) || [] - })); - - // Clear validation for this flight when it changes - setFlightErrors(prev => ({ ...prev, [index]: '' })); - }; - - const addConnectingFlight = () => { - const currentFlights = formData.flights || []; - if (currentFlights.length < 3) { - setFormData(prev => ({ - ...prev, - flights: [...currentFlights, { - flightNumber: '', - flightDate: currentFlights[currentFlights.length - 1]?.flightDate || '', - segment: currentFlights.length + 1 - }] - })); - } - }; - - const removeConnectingFlight = (index: number) => { - setFormData(prev => ({ - ...prev, - flights: prev.flights?.filter((_, i) => i !== index).map((flight, i) => ({ - ...flight, - segment: i + 1 - })) || [] - })); - - // Clear errors for removed flight - setFlightErrors(prev => { - const newErrors = { ...prev }; - delete newErrors[index]; - return newErrors; - }); - }; - - const validateFlight = async (index: number) => { - const flight = formData.flights?.[index]; - if (!flight || !flight.flightNumber || !flight.flightDate) { - setFlightErrors(prev => ({ ...prev, [index]: 'Please enter flight number and date' })); - return; - } - - setFlightValidating(prev => ({ ...prev, [index]: true })); - setFlightErrors(prev => ({ ...prev, [index]: '' })); - - try { - const url = `/api/flights/${flight.flightNumber}?date=${flight.flightDate}`; - const response = await fetch(url); - - if (response.ok) { - const data = await response.json(); - - // Update flight with validation data - setFormData(prev => ({ - ...prev, - flights: prev.flights?.map((f, i) => - i === index ? { ...f, validated: true, validationData: data } : f - ) || [] - })); - - setFlightErrors(prev => ({ ...prev, [index]: '' })); - } else { - const errorData = await response.json(); - setFlightErrors(prev => ({ - ...prev, - [index]: errorData.error || 'Invalid flight number' - })); - } - } catch (error) { - setFlightErrors(prev => ({ - ...prev, - [index]: 'Error validating flight' - })); - } finally { - setFlightValidating(prev => ({ ...prev, [index]: false })); - } - }; - - - return ( -
-
-
-

- āœļø Edit VIP: {vip.name} -

-

Update VIP information and travel arrangements

-
- -
- {/* Basic Information */} -
-

- šŸ‘¤ Basic Information -

-
-
- - -
- -
- - -
-
-
- - {/* Transportation Mode */} -
-

- šŸš— Transportation -

-
-
- -
- - - -
-
-
-
- - {/* Flight Information */} - {formData.transportMode === 'flight' && formData.flights && ( -
-

- āœˆļø Flight Information -

-
- {formData.flights.map((flight, index) => ( -
-
-

- {index === 0 ? ( - <>āœˆļø Primary Flight - ) : ( - <>šŸ”„ Connecting Flight {index} - )} -

- {index > 0 && ( - - )} -
- -
-
- - handleFlightChange(index, 'flightNumber', e.target.value)} - className="w-full px-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors" - placeholder="e.g., AA123" - required={index === 0} - /> -
- -
- - handleFlightChange(index, 'flightDate', e.target.value)} - className="w-full px-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors" - required={index === 0} - min={new Date().toISOString().split('T')[0]} - /> -
-
- - - - {/* Flight Validation Results */} - {flightErrors[index] && ( -
-
- āŒ {flightErrors[index]} -
-
- )} - - {flight.validated && flight.validationData && ( -
-
- āœ… Valid Flight: {flight.validationData.airline || 'Flight'} - {flight.validationData.departure?.airport} → {flight.validationData.arrival?.airport} -
- {flight.validationData.flightDate !== flight.flightDate && ( -
- ā„¹ļø Live tracking starts 4 hours before departure on {new Date(flight.flightDate).toLocaleDateString()} -
- )} -
- )} -
- ))} - - {formData.flights.length < 3 && ( - - )} - -
- -
-
-
- )} - - {/* Self-Driving Information */} - {formData.transportMode === 'self-driving' && ( -
-

- šŸš— Arrival Information -

-
- - -
-
- )} - - {/* Transportation Options */} -
-

- 🚐 Transportation Options -

-
- -
-
- - {/* Additional Notes */} -
-

- šŸ“ Additional Notes -

-
- -