Deploying a CloudFront-Backed S3 Receipt Submission Portal for quickdumpnow.com/books
Overview
This session involved deploying a new receipt management interface for a trailer rental business at https://quickdumpnow.com/books, modernizing the port sheet automation pipeline for Maria's charter business, and resolving CloudFront caching behavior to serve dynamic content correctly. The work demonstrates practical patterns for S3+CloudFront static site hosting, OAuth2 token refresh workflows, and Google Sheets API integration for business operations data.
What Was Done
- Deployed a new receipt submission page to
quickdumpnow.com/bookswith proper S3 object keys and CloudFront invalidation - Updated
robots.txtto block crawler access to the receipts area - Refactored the port sheet automation pipeline (
jada_port_sheet.py) to read from the correct Google Sheets tabs - Built a new OAuth2 reauthentication utility (
reauth_jada_calendar.py) to refresh expired Google Calendar tokens - Pushed refreshed credentials to AWS Lambda for the port sheet automation function
- Tested the end-to-end pipeline with a new charter entry ($1845.72 for Joseph Zurek)
Technical Details: S3 and CloudFront Configuration
The quickdumpnow.com domain is hosted on S3 with CloudFront distribution serving as the origin. The deployment process revealed a common CloudFront gotcha: custom error responses configured on the distribution.
S3 Object Keys and Pretty URLs
To serve https://quickdumpnow.com/books as a directory-style URL (not /books.html), we uploaded the HTML file to two S3 keys:
s3://quickdumpnow.com/books/index.html
s3://quickdumpnow.com/books
The first key is the actual HTML file location. The second key is a "bare" object that CloudFront serves when a user requests /books without trailing slash. This pattern avoids 301 redirects and keeps the URL clean.
CloudFront Distribution Configuration
The CloudFront distribution for quickdumpnow.com had a custom error response rule:
- Distribution ID: Verified via AWS CLI to confirm custom error responses
- Error Handling: 404 responses were being redirected to the homepage instead of returning the actual 404 status
- Origin: S3 bucket (
quickdumpnow.com) with standard S3 website endpoint configuration
This behavior initially masked the missing S3 object — requests to /books were returning 404 from S3, which CloudFront's error response rule converted to a redirect to /, making it appear the books page didn't exist.
Cache Invalidation
After uploading the books page, we invalidated the CloudFront cache for two paths:
/books
/books/
/robots.txt
CloudFront invalidations are typically propagated within 30–60 seconds globally. The /books/ path was included to ensure both the trailing-slash and non-trailing-slash URLs were refreshed.
S3 Deployment Commands
aws s3 cp books/index.html s3://quickdumpnow.com/books/index.html --content-type "text/html"
aws s3 cp books/index.html s3://quickdumpnow.com/books --content-type "text/html"
aws s3 cp robots.txt s3://quickdumpnow.com/robots.txt --content-type "text/plain"
aws cloudfront create-invalidation --distribution-id DIST_ID --paths "/books" "/books/" "/robots.txt"
Port Sheet Automation Pipeline
The port sheet system automates charter and rental logging for Maria's sailing business into a Google Sheet called "JADA Port Log 2026." The pipeline consists of:
File Structure
/Users/cb/Documents/repos/tools/jada_port_sheet.py— Main automation script that reads data from Google Calendar and appends entries to the Port Log sheet/Users/cb/Documents/repos/tools/reauth_jada_calendar.py— OAuth2 token refresh utility for Google Calendar API/Users/cb/Documents/repos/queenofsandiego.com/PortSheetReporter.gs— Google Apps Script that may trigger or validate port sheet data
Data Flow
Google Calendar (charter events)
↓
jada_port_sheet.py (reads events, formats entries)
↓
Google Sheets API (appends to Port Log)
↓
JADA Port Log 2026 sheet (multiple tabs: Template, March, April, etc.)
Sheet Structure
The JADA Port Log 2026 spreadsheet uses multiple tabs, one per month:
- Template tab: Contains headers and formatting rules (not directly written to)
- March tab: Existing charter entries for March 2026
- April tab: Created during this session for April 2026 entries
Each entry row contains columns for charter name, captain, boat, dates, and financial details. For the new Zurek charter, we appended:
Joseph Zurek | [Captain] | [Boat] | [Dates] | $1845.72 | [Notes]
OAuth2 Token Refresh Implementation
The Google Calendar API credentials had expired, causing the port sheet pipeline to fail. Rather than requiring manual re-authentication through the Google Cloud Console, we built an automated refresh mechanism.
reauth_jada_calendar.py Design
This utility:
- Reads the existing refresh token from a local credentials file
- Uses the OAuth2 client ID and secret to request a new access token
- Saves the refreshed token back to disk
- Pushes the new token to AWS Lambda environment variables
The script handles the local OAuth2 server flow (listening on port 8765) if a full re-authentication is needed, but in this case it only required refreshing the existing refresh token.
Port Binding and Process Management
During development, port 8765 was occupied by a stale Python process. Rather than killing the entire process, we identified and terminated the specific process:
lsof -i :8765
kill -9 PID
This freed the port for the OAuth2 callback server without disrupting other services.
Lambda Deployment
The refreshed Google Calendar token was pushed to the AWS Lambda function that runs the port sheet automation:
aws lambda update-function-configuration \
--function-name jada-port-sheet-automation \
--environment Variables={GCAL