Travel Panel: the core travel management platform
FastAPI backend, Next.js operator portal, and B2B partner portal powering Moon Holidays end to end
Overview
Travel Panel is the core system at Moon Holidays. Not a single repo, not a single service, but the orchestrator that every other product either feeds data into or consumes data from. When I say "core", I mean it literally: nothing else in the Moon Holidays engineering organization ships without touching Travel Panel. I designed and built it from zero in December 2022 and have led it since. The code carries a proprietary license in my name going back to 2023.
The Travel Panel ecosystem
Travel Panel is the hub. Six other products connect to it and depend on it:
| Product | What it does | How it connects |
|---|---|---|
| Travel Panel operator portal | Internal admin dashboard used daily by the whole ops team | Direct client of the FastAPI backend |
| Travel Panel B2B portal | External partner agencies browse and book inventory | Direct client with partner-scoped permissions |
| TravelOffer | Customer-facing booking flow for end travelers (itineraries, flights, hotels, transportation, payments) | Reads inventory and pricing from Travel Panel, writes bookings back |
| Live Deck | Wall-mounted call-center TV dashboard, controlled and fed data by Travel Panel | Consumes metrics and live data from Travel Panel plus Aircall telephony |
| Vercel Controller | Invalidates CDN cache and triggers redeploys when Travel Panel data changes | Travel Panel calls it whenever content updates require a frontend refresh |
| StaySync | B2B hotel allotment, availability checks, and hardblock management | Syncs with Travel Panel for inventory and availability |
| Travel Panel WebSocket Server | Real-time internal messaging between operators, notifications, chat | Consumes Travel Panel events via Redis pub/sub, fans them out to connected clients |
| Moon Support Hub | Enterprise support ticketing platform | Built alongside the core platform; shares the same identity and notification layer |
Each of those systems has its own repo and its own case study in this portfolio. They all share one thing: Travel Panel is upstream of them.
The three core repositories
- travelpanel-fastapi — the Python backend. The heart of the system. Serves everything downstream.
- travelpanel-nextjs — the internal operator portal. A full admin dashboard used every day by the whole operations team.
- travelpanel-b2b-nextjs — the newer B2B partner portal for external travel agencies who resell Moon Holidays inventory.
What it manages
Travel Panel is a complete travel management system for travel agencies and tour operators:
- Hotels — listings, room inventory, availability, contract management, hotel ID uniqueness system, positional updates for room order preservation
- Attractions — activity listings, availability, ticket/service breakdown, multi-currency support
- Flights — domestic and international, baggage details, operator images, per-passenger pricing
- Transportation — transfers, rentals, transit services with grouped currency display, route details
- Financial tools — transactions v2, refunds, reports, analytics, reconciliation, multi-gateway payment processing
- Customer relationship management — profiles, history, preferences, communication history
- Itinerary builder — custom multi-product itineraries for customers
- User management — role-based access across agents and administrators
- File manager — centralized S3-backed file storage and management
- Multi-brand + multilingual — serves multiple brands under the group, in English, Hebrew, and other languages
Architecture
Backend (travelpanel-fastapi)
Python and FastAPI. Strong typing with Pydantic, automatic OpenAPI schema generation, async by default. Uvicorn with workers in production.
MongoDB as the primary data store (Atlas in production, local Docker in dev). The travel domain is document-shaped by nature: itineraries are nested trees of offers, passengers, rooms, and extras. Connection manager follows the singleton-with-global-caching pattern so serverless cold starts stay cheap.
Valkey cluster — a 6-node Redis-compatible in-memory data store. Used for caching, pub/sub, session storage, and distributed locks. Switched from single-node Redis to the cluster because horizontal scale across multiple FastAPI workers needed shared state that can tolerate writer failover.
AWS MemoryDB cluster in production, for the Redis-compatible workloads that need managed persistence and automatic failover. The dev stack uses local Valkey; prod uses MemoryDB behind the same client code.
Firebase Admin SDK for JWT authentication, bridging the operator portal, the B2B portal, and every downstream product (WebSocket server, Live Deck, Support Hub).
Docker everywhere. Dev and prod both run in Docker Compose. Nothing in the codebase assumes a specific cloud provider. dev.sh automates the full local setup including an optional database restore from a dated backup. Production runs with docker-compose build --no-cache && docker-compose up -d.
Integrations: Live Agent customer support (via thaitours.ladesk.com/api/v3), Firebase for file storage, and adapter-isolated integrations with Agoda, Booking.com, and every other supplier / vendor connected to the platform.
Testing: pytest against a running FastAPI server. For a smaller open-source FastAPI reference I maintain, see FastAPI DocShield (auth-protected docs endpoints) and Dark Theme Auth FastAPI Server (full JWT + Redis template).
Operator portal (travelpanel-nextjs)
Next.js 15.3.4 with React, Material-UI v7, Emotion for CSS-in-JS, and a mature state layer combining React Query (TanStack Query v5), Context API, and Redux. The API layer is Axios with retry capabilities.
Auth: Firebase Auth with role-based controls. JWT sessions. Multiple auth contexts for Firebase, JWT, and fallbacks.
Forms: Formik with Yup/Zod validation.
Reporting and visualization: ApexCharts and Recharts, Mapbox for geographic UI.
Payments: Stripe and Omise — two processors in parallel for regional coverage. The portal handles gateway selection per transaction.
Content: Quill rich-text editor for operators who write long-form updates.
Storage: AWS S3 for file management (operator uploads, customer documents, hotel media).
External integrations wired in via NEXT_PUBLIC_ flags: OpenAI (for AI-assisted workflows), LiveAgent, Salesforce, Google Ads, Google Analytics 4, Aircall call analytics, Mapbox, feature flags for maintenance mode and analytics toggles.
Testing: Vitest + React Testing Library. 236 test cases across unit, component, and integration layers. FormFieldsBuilder logic alone has 56 tests covering key props, permissions, field formatting, and business logic. Refunds and Transactions SearchFilter components each have 25 tests covering toggle buttons, themes, and accessibility.
61 API endpoints documented in the API implementation guide. The system-features summary covers 15 distinct patterns (TanStack Query guide, hotel API implementation, WebSocket system, table-with-filters pattern, settings dialog patterns, Google Ads OAuth setup, and more).
Three-tier deployment flexibility: default Docker config, Node.js 24 Alpine (lightweight), Bun runtime (high-performance). Each uses different container names so multiple versions can run simultaneously.
License: © 2023-2025 George Khananaev, proprietary. "Owner & Lead Developer" per the README contributors section.
B2B partner portal (travelpanel-b2b-nextjs)
The newest piece. A partner portal for external travel agencies running on the Bun runtime (oven/bun:1-alpine), making it the first Moon Holidays service to go production on Bun. It has a polyglot data layer unique in the portfolio: MongoDB 8 for document data, PostgreSQL 18 for relational records (partner agreements, commission structures, financial ledgers), and Redis 8 for caching and session state. Docker Compose development with named volume persistence and hot reload via source code bind-mounts.
AWS infrastructure
Travel Panel runs on AWS in production. The stack is deliberately cloud-native where it helps and deliberately cloud-agnostic where it matters:
- Application Load Balancer — terminates TLS, routes traffic to the FastAPI backend and the Next.js frontends, health-checks every service, and handles SSL certificate rotation automatically via ACM.
- AWS MemoryDB for Redis — a managed Redis-compatible cluster with multi-AZ failover and durability. The dev stack runs a local Valkey cluster for the same client code; prod points at MemoryDB. Client code never changes between environments.
- CloudFront CDN — caches static assets, images, and API responses where appropriate. Origin shield in front of S3 buckets reduces egress cost and smooths traffic spikes.
- S3 buckets — file storage for operator uploads, customer documents, hotel media, invoices, receipts, and signed PDF output. Lifecycle policies move cold content to cheaper tiers automatically.
- MongoDB Atlas — primary document store, multi-region replica set, with the FastAPI backend connected via private network.
- Secrets Manager / Parameter Store — every credential and API key lives here, not in env files.
- CloudWatch — metrics, logs, and alarms for the whole platform. Every service publishes structured JSON logs.
- Route 53 — DNS and health-check-based failover.
When Travel Panel content changes — a new offer published, a hotel updated, inventory adjusted — the backend calls the Vercel Controller service to purge CloudFront cache for the affected paths and trigger a redeploy of the dependent frontends. That way the CDN, the Vercel deployments, and the Travel Panel data stay coherent without manual intervention.
The dev stack still runs in Docker Compose and is infrastructure-agnostic. Nothing in the codebase is hard-coded to AWS: the container image that runs in production on MemoryDB + S3 + CloudFront is the same container image that runs in dev against local Valkey and local file storage. Moving to GCP or Oracle Cloud is a config change, not a refactor.
The integration layer
Travel Panel is connected to dozens of external services. Each integration is isolated behind an adapter so failures do not cascade and swapping a vendor stays a localized change.
Agoda, Booking.com, and supplier inventory APIs. Google Cloud services. Cloudflare. Twilio for SMS and voice. Firebase for push notifications, file storage, and real-time features. AWS for S3, Cognito, and infrastructure. Real-time currency exchange feeds. Stripe and Omise for payment processing. Live Agent for customer support workflows. OpenAI for AI-assisted operator tools. Google Ads API with OAuth2 for campaign analytics. Salesforce CRM analytics. GA4 analytics. Aircall for phone analytics.
Documentation discipline
Travel Panel carries an unusually thorough documentation set for an internal platform, stored alongside the code: API Implementation Guide (43KB), MongoDB Implementation Guide (31KB), Theme and Design Guide (38KB), Testing Guide (44KB), File Structure Guide (31KB), NEXT.js API Best Practices (69KB), plus 16 feature-specific guides and 23 section-specific guides. This is deliberate. When a team grows, tribal knowledge breaks. Writing things down once saves days of onboarding per engineer.
Operational reality
Travel Panel has been running in production since I started at Moon Holidays in late 2022. Every product in the suite has been added on top of it: the TravelOffer customer booking flow, the Live Deck call-center dashboard, Moon Support Hub, StaySync for B2B allotment management, the real-time WebSocket server, the Vercel deployment controller. Each one of those exists because Travel Panel made it cheap to add a new product instead of a new platform.
What I learned building this
Get the data model right on day one. Every shortcut taken in the tenancy model or the core entity shapes costs ten times as much to undo later. Travel Panel is multi-tenant from the first commit: every record belongs to a tenant, every query filters by tenant, every background job scopes to tenant. That discipline paid back immediately and continues to pay back every time a new product needs to share the data layer.
Own your integration boundary. Every external API is a liability the moment your business depends on it. Put it behind an adapter you control, test the adapter, and wrap every call with retries, timeouts, and observability. You will thank yourself the first time an upstream provider has a bad day.
Treat the platform as a product. Internal tools that will be used every day by real operators deserve the same design discipline as customer-facing software. The operator portal at Moon Holidays is not a back-office CRUD app. It is the product the company runs on, and it reflects that investment.
Write the docs as you go. Travel Panel's docs folder is as big as some projects' source trees. The payoff is that new engineers can onboard by reading instead of asking, and architectural decisions are captured while the reasoning is still fresh.
Tech stack
Backend: Python, FastAPI, Pydantic, MongoDB, Valkey cluster (6 nodes, Redis-compatible), AWS MemoryDB for production, Firebase Admin SDK, Uvicorn, Docker, Docker Compose, pytest.
Operator portal: Next.js 15.3.4, React, Material-UI v7, Emotion, TanStack Query v5, Redux, Context API, Axios, Firebase Auth, Formik, Yup, Zod, ApexCharts, Recharts, Mapbox, Stripe, Omise, Quill, AWS S3, Vitest, React Testing Library.
B2B portal: Next.js, Bun runtime, MongoDB 8, PostgreSQL 18, Redis 8, Docker Compose.
Shared infrastructure: multi-tenant data model, role-based access control, infrastructure-agnostic deployments, external integration layer isolated behind adapters, 74+ pages of internal architecture documentation.
