A full-stack airline operations platform built with React, Express.js, and MySQL. It provides an Admin Panel for managing flights, airplanes, airports, schedules, clients, and bookings — and a Customer Panel for browsing flights, booking tickets, and leaving reviews.
- Prerequisites
- 1. Database Setup
- 2. Server Setup
- 3. Client Setup
- 4. Running the App
- 5. Deployment (Free)
- 6. Manual Testing Guide
- Default Dev Credentials
- Project Structure
- Tech Stack
Make sure the following are installed on your machine before starting:
| Tool | Version | Check |
|---|---|---|
| Node.js | v16+ | node --version |
| npm | v8+ | npm --version |
| MySQL | v8+ | mysql --version |
| Git | any | git --version |
Open your MySQL shell (or MySQL Workbench) and run:
CREATE DATABASE airport_management;
USE airport_management;This creates all the tables and views. Run this first, before the seeds.
mysql -u root -p airport_management < server/schema.sqlOr paste the contents of server/schema.sql directly into MySQL Workbench and execute.
This populates all tables with sample data including admin accounts and test users.
mysql -u root -p airport_management < server/seeds.sqlNote: All passwords in the seed file are already bcrypt-hashed. See Default Dev Credentials for the plaintext values to use when logging in.
cd servernpm installCopy the example env file and fill in your values:
cp .env.example .envOpen server/.env and update it:
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=your_mysql_password_here
DB_NAME=airport_management
DB_SSL=false
JWT_SECRET=replace_this_with_a_long_random_string_at_least_32_chars
PORT=5000
ALLOWED_ORIGIN=http://localhost:3000Important:
JWT_SECRETmust be a long random string. You can generate one with:node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
cd clientnpm installcp .env.example .envThe default client/.env should contain:
REACT_APP_API_URL=http://localhost:5000Change this if your backend runs on a different port or host.
Open two terminals — one for the server, one for the client.
Terminal 1 — Start the backend:
cd server
npm run devYou should see:
Server running on port 5000
Terminal 2 — Start the frontend:
cd client
npm startThe browser will open automatically at http://localhost:3000.
This project is configured for a fully free demo deployment using:
| Layer | Platform | Notes |
|---|---|---|
| Frontend | Vercel | Free forever, global CDN |
| Backend | Render | Free 750 hrs/month, sleeps after 15 min idle |
| Database | Aiven | Free MySQL tier, always-on |
Cold start warning: Render's free tier sleeps after 15 minutes of inactivity. The first request after idle takes ~30–50 seconds. This is acceptable for demo use. The
/healthendpoint can be used to pre-warm the server.
- Sign up at aiven.io → Create service → MySQL → Free plan
- Once running, go to the service Overview tab and note:
- Host →
DB_HOST - Port →
DB_PORT(typically a non-standard port like13306) - User →
DB_USER - Password →
DB_PASSWORD - Database name →
DB_NAME(default:defaultdb)
- Host →
- Connect via MySQL Workbench using the credentials above — set SSL mode to Required
- Run
server/schema.sqlthenserver/seeds.sqlin order
- Sign up at render.com → New Web Service → connect your GitHub repo
- Configure:
- Root Directory:
server - Build Command:
npm install - Start Command:
node index.js - Plan: Free
- Root Directory:
- Add environment variables:
| Key | Value |
|---|---|
DB_HOST |
(from Aiven) |
DB_PORT |
(from Aiven) |
DB_USER |
(from Aiven) |
DB_PASSWORD |
(from Aiven) |
DB_NAME |
defaultdb |
DB_SSL |
true |
JWT_SECRET |
(generate with openssl rand -hex 32) |
ALLOWED_ORIGIN |
(set after Vercel deploy in Step 3) |
PORT |
5000 |
- Deploy and copy your Render URL (e.g.
https://your-app.onrender.com)
- Sign up at vercel.com → Add New Project → import your GitHub repo
- Configure:
- Root Directory:
client - Framework Preset: Create React App
- Root Directory:
- Add environment variable:
| Key | Value |
|---|---|
REACT_APP_API_URL |
Your Render URL from Step 2 |
- Deploy and copy your Vercel URL (e.g.
https://your-app.vercel.app)
Go back to your Render service → Environment → set ALLOWED_ORIGIN to your Vercel URL. Render redeploys automatically.
# Should return { "status": "ok", "timestamp": "..." }
curl https://your-app.onrender.com/healthThen open your Vercel URL and test login.
Vercel Analytics is pre-configured in this project (@vercel/analytics). Once deployed to Vercel, visit your project dashboard → Analytics tab to see page views, visitors, and top routes. It is a no-op in local development.
curl http://localhost:5000/healthExpected: {"status":"ok","timestamp":"..."}
- Go to
http://localhost:3000/signin - Enter username:
Ahmad, password:fast123 - You should be redirected to the Admin Panel
- Open DevTools → Application → Cookies — you should see an
admin_tokencookie
- While logged out, navigate directly to
http://localhost:3000/AdminPanel - You should be redirected to
/signinimmediately - Same for
/Client,/Flight,/Booking, etc.
Once logged in as admin:
- Clients: Sidebar → Clients → Add, Edit, View, Delete a client
- Airplanes: Sidebar → Airplanes → Add a new airplane with ID and max seats
- Schedules: Sidebar → Schedules → Add a schedule with departure/arrival times
- Flights: Sidebar → Flights → Add a flight (select schedule, status, airplane)
- Tickets: Sidebar → Tickets → Edit existing tickets
- Bookings: Sidebar → Bookings → View all bookings
- Go to
http://localhost:3000/sign-up - Fill in all fields (use any email not already in the seed data)
- Submit — you should see "Registered Successfully"
- Go to
http://localhost:3000/customer-signin - Log in with the email and password you just registered
- You should land on your Customer Panel
- Log in as a customer
- Click Book Flight in the customer navbar
- Select departure airport, arrival airport, departure date, return date, class, and price
- Click Find Flight — you should see matching available flights
- Select a flight → proceed to Invoice → confirm
- You should receive a boarding pass
Test that the API rejects unauthenticated requests:
# Should return 401
curl -X DELETE http://localhost:5000/removeSearch
# Should return 401
curl http://localhost:5000/api/get
# Should return 401
curl http://localhost:5000/booking/getstatsHit the login endpoint 6 times quickly:
for i in {1..6}; do
curl -s -o /dev/null -w "%{http_code}\n" \
-X POST http://localhost:5000/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"wrong","password":"wrong"}'
doneExpected: first 5 return 401, the 6th returns 429 Too Many Requests.
- While logged in as admin, click Logout
- You should be redirected to
/signin - Try navigating to
/AdminPanel— you should be redirected back to/signin - Check DevTools → Cookies —
admin_tokenshould be gone
These are for local development only. Change all passwords before deploying.
| Username | Password |
|---|---|
| Ahmad | fast123 |
| Faheem | notfast123 |
| Mohsin | yesfast123 |
All seed customers share the password: clientpass123
mohsinalimirza@gmail.com |
ahmadaleem@hotmail.com |
wahajjaved@gmail.com |
asfeenhakani@gmail.com |
Or register a new account at /sign-up.
AirOpsManager/
├── client/ # React frontend (CRA)
│ ├── .env # Frontend env (REACT_APP_API_URL)
│ ├── .env.example
│ └── src/
│ ├── api/
│ │ └── client.js # Axios instance (baseURL from env)
│ ├── contexts/
│ │ └── AuthContext.js # JWT auth state, login/logout
│ ├── components/
│ │ ├── ProtectedRoute.js # Redirects unauthenticated admins
│ │ ├── CustomerRoute.js # Redirects unauthenticated customers
│ │ ├── Navbar/
│ │ ├── CustomerNavbar/
│ │ └── Pages/ # All 40+ page components
│ └── App.js # Routes with lazy loading, auth guards, analytics
│
└── server/ # Express.js backend
├── .env # DB credentials + JWT secret (never commit)
├── .env.example # Safe template to commit
├── schema.sql # CREATE TABLE statements only
├── seeds.sql # INSERT statements with hashed passwords
├── index.js # App setup
├── db/
│ └── pool.js # MySQL connection pool (SSL-aware)
├── middleware/
│ ├── auth.js # JWT cookie verification
│ ├── rateLimiter.js # Brute-force protection
│ └── validate.js # Input validation chains
└── routes/
├── auth.js # POST /auth/login, /customerlogin, /signup, /logout, /me
├── clients.js # GET/POST/PUT/DELETE /api/...
├── airplanes.js
├── schedules.js
├── flights.js
├── flightStatus.js
├── airports.js
├── gates.js
├── tickets.js
├── bookings.js
├── reviews.js
└── search.js # Flight search + booking flow
| Layer | Technology |
|---|---|
| Frontend | React 18, React Router v5, Tailwind CSS |
| Analytics | Vercel Analytics (@vercel/analytics) |
| Forms | react-hook-form, express-validator |
| HTTP Client | Axios (via centralized api/client.js) |
| Auth | JWT (jsonwebtoken) in HttpOnly cookies |
| Backend | Express.js (Node.js) |
| Security | Helmet, CORS, express-rate-limit, bcryptjs |
| Database | MySQL 8 (raw SQL, mysql2 driver) |
| Dev Tools | nodemon, dotenv |
| Hosting | Vercel (frontend), Render (backend), Aiven (MySQL) |
"Cannot connect to database"
- Make sure MySQL is running:
sudo service mysql start(Linux) or start MySQL from System Preferences (Mac) - Double-check
DB_PASSWORDinserver/.envmatches your MySQL root password
"Invalid credentials" on login
- Make sure you ran
server/seeds.sql— without it there are no admin accounts in the database - Passwords are case-sensitive
"CORS error" in browser console
- Make sure
ALLOWED_ORIGINinserver/.envmatches exactly what's in your browser URL bar (including the port) - On Render, confirm
ALLOWED_ORIGINis set to your exact Vercel URL with no trailing slash
Frontend shows blank page
- Make sure
REACT_APP_API_URLinclient/.envmatches where your backend is running - CRA requires you to restart
npm startafter changing.envfiles
Port 5000 already in use
- Change
PORT=5001inserver/.envand updateREACT_APP_API_URL=http://localhost:5001inclient/.env
Render backend is slow to respond (first request)
- This is expected on the free tier — Render spins down services after 15 minutes of inactivity. The first request after idle takes ~30–50 seconds to wake up.
Aiven SSL connection error
- Confirm
DB_SSL=trueis set in your Render environment variables - Confirm
DB_PORTmatches the port shown in Aiven's Overview tab (not the default 3306)