# 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/`](docs/) folder: - [**Architecture**](docs/ARCHITECTURE.md) -- system design, data flow, caching layers, database schema, external APIs - [**Operations**](docs/OPERATIONS.md) -- deployment, monitoring, troubleshooting, backup, scheduler management - [**API Reference**](docs/API.md) -- complete backend endpoint documentation with request/response examples - [**Development**](docs/DEVELOPMENT.md) -- 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](https://queue-times.com/en-US/pages/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 (Mar–Dec) | 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: ```bash curl -X POST http://localhost:3001/api/scrape/trigger?scope=today # scope: today | month | upcoming | full | force ``` --- ## Local Development **Prerequisites:** Node.js 22+, npm ```bash # Install frontend dependencies npm install # Install backend dependencies cd backend && npm install && cd .. ``` ### Start the backend ```bash 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 ```bash npm run dev ``` Open [http://localhost:3000](http://localhost:3000). Navigate weeks with the `←` / `→` buttons (or arrow keys); your selected week persists across visits via the `tcWeek` cookie. 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: ```bash npm run debug -- --park kingsisland --date 2026-06-15 ``` ### Run tests ```bash npm test ``` --- ## Deployment The app ships as two Docker images: ```bash 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 ```bash 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 |