josh aa46cc1b3d
Build and Deploy / Build & Push (push) Successful in 3m37s
fix: run startup scrape only when database is empty
Restores the startup scrape removed in deb8e41, gated on
getParkDayCount() < 50 so warm restarts don't hammer the API.
Cold containers (e.g. after the volume mount fix) populate
immediately instead of waiting up to 24h for tier-4 cron.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 14:45:57 -04:00

Thoosie Calendar

A week-by-week calendar showing operating hours for all Six Flags Entertainment Group theme parks — including the former Cedar Fair parks. Data is fetched from the Six Flags internal API via a backend service and stored in SQLite. Click any park to see its full month calendar and live ride status with current wait times.

Parks

24 theme parks across the US, Canada, and Mexico, grouped by region:

Region Parks
Northeast Great Adventure (NJ), New England (MA), Great Escape (NY), Darien Lake (NY), Dorney Park (PA), Canada's Wonderland (ON)
Southeast Over Georgia, Carowinds (NC), Kings Dominion (VA)
Midwest Great America (IL), St. Louis (MO), Cedar Point (OH), Kings Island (OH), Valleyfair (MN), Worlds of Fun (MO), Michigan's Adventure (MI)
Texas & South Over Texas, Fiesta Texas (TX), Frontier City (OK)
West & International Magic Mountain (CA), Discovery Kingdom (CA), Knott's Berry Farm (CA), California's Great America (CA), Mexico

Documentation

Detailed docs live in the docs/ folder:

  • Architecture -- system design, data flow, caching layers, database schema, external APIs
  • Operations -- deployment, monitoring, troubleshooting, backup, scheduler management
  • API Reference -- complete backend endpoint documentation with request/response examples
  • Development -- local setup, project structure, adding parks, testing, code conventions

Architecture

The app runs as two containers:

Container Port Purpose
web 3000 Next.js frontend — pure presentation layer, fetches all data from the backend API
backend 3001 Hono API server — owns the SQLite database, runs tiered cron scheduling, handles all external API calls

The frontend makes no direct database or external API calls. All data flows through the backend.

Tech Stack

  • Next.js 15 — App Router, Server Components, standalone output
  • Tailwind CSS v4@theme {} CSS variables, no config file
  • Hono — lightweight TypeScript API framework for the backend
  • SQLite via better-sqlite3 — owned exclusively by the backend
  • node-cron — tiered scheduling (hourly → daily) for data freshness
  • Six Flags CloudFront API — park operating hours and ride schedules
  • Queue-Times.com API — live ride open/closed status and wait times

Ride Status

The park detail page shows ride open/closed status using a two-tier approach:

  1. Live data (Queue-Times.com) — when a park is operating, ride status and wait times are fetched from the Queue-Times.com API and cached for 5 minutes. All 24 parks are mapped. Displays a Live badge with per-ride wait times.

  2. Schedule fallback (Six Flags API) — when Queue-Times data is unavailable, the app falls back to the nearest upcoming date from the Six Flags schedule API as an approximation.

Roller Coaster Filter

When live data is shown, a Coasters only toggle filters to roller coasters. Coaster lists are hardcoded in lib/coaster-data.ts.

Data Refresh

The backend runs a tiered scraping schedule via node-cron:

Tier Schedule Scope
1 Hourly (MarDec) Today's hours for all parks
2 Every 6 hours Current month for all parks
3 Twice daily (3 AM, 3 PM) Current + next month
4 Daily at 3 AM Full year (respects 72h staleness window)

Past dates are never overwritten. The hourly tier compares live data against the database before writing — unchanged data is skipped.

A manual trigger is available via the backend API:

curl -X POST http://localhost:3001/api/scrape/trigger?scope=today
# scope: today | month | upcoming | full | force

Local Development

Prerequisites: Node.js 22+, npm

# Install frontend dependencies
npm install

# Install backend dependencies
cd backend && npm install && cd ..

Start the backend

cd backend
npm run dev

The backend starts on port 3001, initializes the database, and begins the cron schedule. On first run it creates an empty database — the schedulers will populate it automatically, or trigger a manual scrape.

Start the frontend

npm run dev

Open http://localhost:3000. Navigate weeks with the / buttons, or pass ?week=YYYY-MM-DD directly. Click any park name to open its detail page.

Debug a specific park + date

Inspect raw API data and parsed output for any park and date:

npm run debug -- --park kingsisland --date 2026-06-15

Run tests

npm test

Deployment

The app ships as two Docker images:

docker compose up -d

Images are built and pushed automatically by CI on every push to main.

Environment variables

web:

Variable Default Description
BACKEND_URL http://backend:3001 Backend API base URL (Docker internal networking)

backend:

Variable Default Description
TZ UTC Timezone for cron schedules (e.g. America/New_York)
PARK_HOURS_STALENESS_HOURS 72 Hours before park schedule data is re-fetched

Updating

docker compose pull && docker compose up -d

Backend API endpoints

Endpoint Description
GET /api/calendar/week?start=YYYY-MM-DD Week calendar for all parks
GET /api/calendar/:parkId/month?month=YYYY-MM Month calendar for one park
GET /api/parks/:id/rides Live rides or schedule fallback
GET /api/parks Park list with metadata
GET /api/status Health check, scrape timestamps, DB stats
POST /api/scrape/trigger?scope=... Manual scrape trigger
S
Description
No description provided
Readme 862 KiB
Languages
TypeScript 96.7%
CSS 2%
Dockerfile 1.1%
JavaScript 0.2%