Initial commit: TravelHub multi-tenant flight booking platform

Angular + Go + PostgreSQL sample with strict per-tenant isolation.

- Backend: Go API (basic auth, tenant-scoped repository layer, pgx pool)
- Frontend: Angular 22 SPA (search, checkout, bookings, dashboard)
- Database: schema, seed data and demo bookings migrations
- Tests: 311 across Go (integration), Angular (unit) and Playwright (e2e),
  plus a generated evidence report with PDF export
- Security: whitebox VAPT report under security/

Secrets (.env, tokens, DB credentials) are git-ignored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
nilesh.p
2026-09-13 11:38:01 +05:30
co-authored by Claude Opus 4.8
commit 4d3eb18a6c
129 changed files with 25424 additions and 0 deletions
+143
View File
@@ -0,0 +1,143 @@
// Package models holds the API-facing representations of TravelHub data.
// JSON field names are camelCase so the Angular client can consume them
// without a translation layer.
package models
import (
"time"
"github.com/google/uuid"
)
// Tenant is one travel agency using the platform.
type Tenant struct {
ID uuid.UUID `json:"id"`
Slug string `json:"slug"`
Name string `json:"name"`
Status string `json:"status"`
CreatedAt time.Time `json:"createdAt"`
}
// User belongs to exactly one tenant. Email is unique per tenant, not globally,
// so the same person can hold accounts at several agencies.
type User struct {
ID uuid.UUID `json:"id"`
TenantID uuid.UUID `json:"tenantId"`
Email string `json:"email"`
FullName string `json:"fullName"`
Role string `json:"role"`
CreatedAt time.Time `json:"createdAt"`
// PasswordHash never leaves the server.
PasswordHash string `json:"-"`
}
// Airport is global reference data, shared by every tenant.
type Airport struct {
ID uuid.UUID `json:"id"`
IATACode string `json:"iataCode"`
Name string `json:"name"`
City string `json:"city"`
Country string `json:"country"`
Timezone string `json:"timezone"`
}
// Airline is global reference data, shared by every tenant.
type Airline struct {
ID uuid.UUID `json:"id"`
IATACode string `json:"iataCode"`
Name string `json:"name"`
LogoURL string `json:"logoUrl,omitempty"`
}
// Flight is tenant-scoped inventory: each agency loads its own fares, so the
// same physical route carries a different price per tenant.
type Flight struct {
ID uuid.UUID `json:"id"`
FlightNumber string `json:"flightNumber"`
Airline Airline `json:"airline"`
Origin Airport `json:"origin"`
Destination Airport `json:"destination"`
DepartureTime time.Time `json:"departureTime"`
ArrivalTime time.Time `json:"arrivalTime"`
DurationMinutes int `json:"durationMinutes"`
Aircraft string `json:"aircraft"`
CabinClass string `json:"cabinClass"`
BasePrice float64 `json:"basePrice"`
Currency string `json:"currency"`
SeatsTotal int `json:"seatsTotal"`
SeatsAvailable int `json:"seatsAvailable"`
Status string `json:"status"`
}
// Passenger is one traveller on a booking.
type Passenger struct {
ID uuid.UUID `json:"id"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
DateOfBirth *time.Time `json:"dateOfBirth,omitempty"`
PassportNumber string `json:"passportNumber,omitempty"`
SeatNumber string `json:"seatNumber,omitempty"`
}
// Booking is a confirmed sale against a flight, owned by one tenant.
type Booking struct {
ID uuid.UUID `json:"id"`
BookingReference string `json:"bookingReference"`
Flight Flight `json:"flight"`
Passengers []Passenger `json:"passengers"`
PassengerCount int `json:"passengerCount"`
TotalAmount float64 `json:"totalAmount"`
Currency string `json:"currency"`
Status string `json:"status"`
ContactEmail string `json:"contactEmail"`
ContactPhone string `json:"contactPhone,omitempty"`
BookedBy string `json:"bookedBy"`
CreatedAt time.Time `json:"createdAt"`
}
// FlightSearch carries the filters accepted by GET /api/flights.
type FlightSearch struct {
Origin string // IATA code, optional
Destination string // IATA code, optional
DepartDate *time.Time // matches the whole calendar day, optional
CabinClass string // optional
Passengers int // minimum seats that must still be available
SortBy string // price | duration | departure
Limit int
Offset int
}
// PassengerInput is one traveller as supplied by the client. Dates arrive as
// YYYY-MM-DD strings and are parsed when the booking is written.
type PassengerInput struct {
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
DateOfBirth string `json:"dateOfBirth"`
PassportNumber string `json:"passportNumber"`
}
// CreateBookingRequest is the POST /api/bookings payload.
type CreateBookingRequest struct {
FlightID uuid.UUID `json:"flightId"`
ContactEmail string `json:"contactEmail"`
ContactPhone string `json:"contactPhone"`
Passengers []PassengerInput `json:"passengers"`
}
// DashboardStats powers the agency overview screen.
type DashboardStats struct {
TotalBookings int `json:"totalBookings"`
ConfirmedBookings int `json:"confirmedBookings"`
CancelledBookings int `json:"cancelledBookings"`
TotalRevenue float64 `json:"totalRevenue"`
Currency string `json:"currency"`
FlightsInStock int `json:"flightsInStock"`
SeatsSold int `json:"seatsSold"`
RecentBookings []Booking `json:"recentBookings"`
TopRoutes []struct {
Route string `json:"route"`
Bookings int `json:"bookings"`
Revenue float64 `json:"revenue"`
} `json:"topRoutes"`
}