π§ System Summary
- Single Cloudflare Worker (
worker.js) handles all requests for pub.ihnyc-rc.org - Static files served from
./publicvia Cloudflareβs Assets binding with SPA-style fallback (unknown paths βindex.html) /api/eventscan query up to three Notion databases and assembles a unified JSON payload- API responses cached at the edge for 2 minutes (
CACHE_TTL_SECONDS = 120) - No build step; plain HTML/CSS/JS for all static pages
Sources: worker.js, wrangler.toml
π§© Request Routing
flowchart TD REQ["Incoming Request"] --> WORKER["Cloudflare Worker (worker.js)"] WORKER --> IS_API{"/api/events?"} IS_API -- Yes --> CACHE_HIT{Edge Cache Hit?} CACHE_HIT -- Yes --> CACHED["Return Cached Response"] CACHE_HIT -- No --> NOTION["Query Notion APIs"] NOTION --> ASSEMBLE["Assemble JSON payload"] ASSEMBLE --> STORE["Store in edge cache (2 min TTL)"] STORE --> RETURN_API["Return JSON"] IS_API -- No --> ASSETS["Cloudflare Assets (./public)"] ASSETS --> FOUND{Asset found?} FOUND -- Yes --> SERVE["Serve static file"] FOUND -- No --> FALLBACK["Serve index.html (SPA fallback)"]
Sources: worker.js (fetch handler, CACHING section)
ποΈ Notion Databases
| Env Var | Purpose | Required |
|---|---|---|
NOTION_DATABASE_ID | Pub events β todayβs events and this weekβs events | Yes (for live data) |
NOTION_SHIFTS_DATABASE_ID | Pub tender shifts, operational timeframes, last-call config | No |
NOTION_ANNOUNCEMENTS_DATABASE_ID | Scrolling announcements shown on TV display | No |
NOTION_DATABASE_IDis required when live Notion data is enabled- If
NOTION_API_KEYis absent, the worker skips live Notion queries and returns placeholder data NOTION_API_KEYis required (set as a Wrangler secret, never inwrangler.toml)
Sources: worker.js (CONFIG section), wrangler.toml
π¦ /api/events Payload Assembly
When /api/events is called, the worker:
- Determines βtodayβ using the normal calendar date in
America/New_York(see what-counts-as-today) - Queries the Events DB for
todayEventsusing a broad date window, then narrows it to rows whose local Eastern start date equalstodayISO - Queries the Events DB again for
weekEventscovering calendar tomorrow through calendar day +7 - Queries the Announcements DB for TV-included announcements (if configured)
- Queries the Shifts DB for the currently active shift and on-duty pub tender (if configured)
- Assembles pub hours for the week from the Shifts DB (if configured)
- Returns a unified JSON object (see api)
Sources: worker.js (handleEventsApi, fetchFromNotion, fetchPubTenderFromNotion)
β‘ Caching
- Cache TTL: 2 minutes (
CACHE_TTL_SECONDS = 120) - Cache key: full request URL
- Cache stored in Cloudflareβs edge cache (not KV or D1)
- TV display client independently soft-refreshes every 2 minutes
- Hard
<meta http-equiv="refresh" content="300">fallback every 5 minutes in case JS hangs - Overnight serving-hours state on
/and/tv/is computed againstAmerica/New_York, not the device timezone, so post-midnight active shifts stay attached to their original shift day
Sources: worker.js (CACHING section), public/tv/index.html, public/tv/script.js (startRefreshCountdown)
ποΈ Static Site Structure
public/
βββ index.html # Landing page (pub hours widget, navigation)
βββ styles.css # Global styles (Jost + Playfair Display)
βββ assets/ # Logos and images
β βββ I-House_Emblem_Regular_RGB_Green.svg # Primary I-House logo / favicon
β βββ RC-transparent-background-logo.png # RC classification badge logo
β βββ avi_logo_bg.png # AVI Foodsystems logo
β βββ affectivetech_logo_bg_trans_logoonly.png # Affective Tech footer logo
βββ tv/ # TV signage display (primary feature)
β βββ index.html
β βββ styles.css
β βββ script.js
βββ left-light-av-panel/ # AV/lighting control panel
βββ menu/ # Placeholder pages
βββ events/
βββ announcements/
βββ pub-tenders/
βββ resources/
βββ tutorials/
Sources: public/ directory, wrangler.toml ([assets] binding)