6447db3008
Build and Deploy / Build & Push (push) Successful in 1m7s
The home page no longer reads ?week=YYYY-MM-DD from the URL. Selected week lives in the tcWeek cookie, set via a server action that revalidates the home page so the next render reflects it. The URL stays at "/" regardless of which week the user is viewing. WeekNav prev/next/today buttons (and the arrow-key bindings) call the server action directly — no router.refresh dance, no client-side cookie write. BackToCalendarLink drops its localStorage-based href reconstruction and just links to "/" since the cookie already remembers the right week across navigations. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
167 lines
5.9 KiB
Markdown
167 lines
5.9 KiB
Markdown
# 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 |
|