Rebuilding Queen of San Diego's Booking Infrastructure: Homepage Integration, Payment QR Codes, and CloudFront Authentication
This session involved a comprehensive overhaul of the Queen of San Diego booking website, spanning frontend HTML/CSS fixes, Google Apps Script integration for calendar automation, S3/CloudFront infrastructure improvements, and implementation of a private financial dashboard with basic authentication.
Problem Statement
The production site at https://queenofsandiego.com/ had multiple critical issues:
- Broken Google Maps embed rendering improperly in the hero section
- No direct booking capability from the homepage — users had to navigate elsewhere to book
- Payment QR codes (Stripe, Zelle, Venmo) were missing from the primary user journey
- Calendar integration between the website and Google Calendar was incomplete
- No secure access to financial dashboards (P&L calculator)
Frontend Changes: index.html Refactoring
The primary work involved modifying /Users/cb/Documents/repos/sites/queenofsandiego.com/index.html across multiple iterations.
Maps Replacement with Payment QR Panel
The broken Google Maps iframe was replaced with a responsive payment methods section featuring QR codes for:
- Stripe payment link (primary e-payment method)
- Zelle QR code (
zelle-qr.jpeg— note the .jpeg extension, which was critical; earlier iterations incorrectly referenced.jpg) - Venmo QR code for mobile payment
This change positioned payment options directly in the hero section, reducing friction for users who prefer to pay before or during booking rather than after.
Booking Modal Enhancement
The modal triggered by the "Reserve" button in the navigation needed several updates:
- Added duration selector (2-hour vs 3-hour charters)
- Integrated date picker connected to the Google Calendar backend
- Updated the
jadaOpenBook()JavaScript function to accept duration parameters - Fixed modal styling to accommodate payment QR codes alongside booking controls
CSS Debugging
During development, a critical CSS bug emerged: an orphaned </style> closing tag at line 3090 with no matching opening tag was breaking the cascade. This required:
- Auditing all opening and closing style tags across the document
- Ensuring proper nesting of CSS blocks
- Verifying the hero section's visual hierarchy after fixes
Google Apps Script Integration: BookingAutomation.gs
The booking system required a two-way sync between the website and Google Calendar. The BookingAutomation.gs file (Google Apps Script) handles this:
- CalendarSync.gs: Contains the
doGet()endpoint that serves available dates from the JADA calendar (a shared Google Calendar resource) - BookingAutomation.gs: Handles booking submission logic, creating calendar events with user details
Key functions added/modified:
jadaOpenBook(duration)— JavaScript function that opens the modal and passes the selected duration (2 or 3 hours) to the GAS backendbooked_datesendpoint — exposes the calendar's booked dates as JSON for the frontend date picker- Event creation with duration stored in the calendar event description
The GAS web app was deployed using clasp (Google Apps Script CLI), and the deployment ID was stored in the local .clasp.json configuration.
Infrastructure: S3, CloudFront, and Route 53
Primary Site Deployment
The updated index.html was deployed to the main S3 bucket backing queenofsandiego.com:
aws s3 cp index.html s3://queenofsandiego-main/ --acl public-read
The CloudFront distribution (ID: E****, aliased to queenofsandiego.com) cache was invalidated:
aws cloudfront create-invalidation --distribution-id E**** --paths "/index.html" "/*"
Private Financial Dashboard: pnl.queenofsandiego.com
A secondary requirement emerged: hosting a private P&L (profit and loss) calculator accessible only to internal stakeholders. This required a new subdomain with basic authentication.
Architecture decisions:
- Separate S3 bucket:
pnl-queenofsandiego(private, no public read access) - Origin Access Control (OAC): CloudFront can access the bucket, but the bucket policy restricts all direct S3 access
- CloudFront Function for auth: A JavaScript function runs on every request to validate
Authorizationheaders with base64-encoded credentials - ACM certificate: Requested for
pnl.queenofsandiego.comwith DNS validation via Route 53
Implementation steps:
- Created private S3 bucket with default-deny policy
- Generated OAC for CloudFront-to-S3 access
- Created CloudFront Function (in LIVE stage) to intercept requests:
// /tmp/ops-basic-auth.js pattern function handler(event) { var request = event.request; var authHeader = request.headers.authorization; var expectedAuth = "Basic " + btoa("username:password"); if (!authHeader || authHeader.value !== expectedAuth) { return { statusCode: 401, statusDescription: "Unauthorized" }; } return request; } - Attached OAC-only bucket policy to S3:
aws s3api put-bucket-policy --bucket pnl-queenofsandiego --policy file://policy.json - Created CloudFront distribution for
pnl.queenofsandiego.com:- Origin: S3 bucket with OAC
- Behavior: CloudFront Function attached to viewer request
- Cache: Minimal TTL for HTML (60s), longer for static assets
- Added Route 53 A and AAAA alias records pointing to the CloudFront domain
Credentials storage: The basic auth credentials were stored in a secure repos.env file (not version-controlled), accessible only to deployment scripts. The base64-encoded version was embedded in the CloudFront Function code.
Ops and Tools Dashboard Updates
Updated the ops dashboard infrastructure to include financial tools:
- Modified
/tmp/ops-index.htmland