打造 status.live:以可見資料新鮮度呈現滑雪纜車、道路及網絡攝影機狀況
Building status.live: ski lifts, roads, and webcams with visible data freshness
status.live 正在開發一個免費網站,供用戶查閲滑雪纜車、道路、網絡攝影機等戶外狀況,並呈現資料狀態及更新時間。滑雪場目錄目前收錄 284 個度假村,網站亦提供唯讀 JSON API 和 MCP 工具。系統以共享快照減少重複讀取上游資料,並讓各資料區段分別顯示新鮮度及錯誤狀態。
A ski lift can be running while the access road is restricted. A webcam can respond successfully while showing an old image. An empty lift board can mean the resort has stopped publishing its winter report.
These are different states, but a simple “open” or “closed” badge can hide the distinction.
I’m building status.live, a free website for checking ski lifts, road conditions, webcams, park alerts, campground opening schedules, and surf conditions. It also exposes a read-only JSON API and MCP tools. The ski catalog currently includes 284 resorts; available data and freshness vary by source.
Here are four decisions that have shaped the implementation, including a map mistake that made an otherwise valid build hard to use.
1. Missing data needs its own state
A successful HTTP response doesn’t necessarily contain a useful lift report. A parser can return an empty list because the resort has removed its seasonal board, changed its markup, or genuinely has nothing listed.
The data model needs to preserve that uncertainty. A simplified version looks like this:
type LiveSection<T> = {
state: 'reported' | 'none_listed' | 'source_error' | 'loading';
as_of: string | null;
age_s: number | null;
stale: boolean;
items: T[];
source: { name: string; url?: string };
};
This is an illustrative contract rather than a complete response schema. The important part is that an empty collection and a failed source read are distinguishable from a reported closure.
There are also two useful times to keep separate: when a source says an event happened, and when the application successfully checked it. Refreshing an HTML page should not make an old upstream observation look new.
When a source fails, a last-good snapshot can still be useful. It needs to retain its timestamp and carry a stale flag. Once that snapshot is too old to be useful, the interface should expose the missing data.
2. Share upstream reads across visitors
Fetching every source while rendering every page makes upstream traffic depend on how popular the site becomes.
For Colorado roads, the scheduled ingestion path is:
CDOT / COtrip statewide feeds
-> scheduled ingestion
-> shared snapshots
-> resort pages, park pages, JSON API, MCP
The normal schedule reads three feeds—road conditions, incidents, and planned events—once every ten minutes. That is 432 scheduled upstream calls per day, rather than three calls for every visitor or resort page.
A shared cooldown handles HTTP 429 responses and respects Retry-After. Failed refreshes preserve last-good data with its original timestamp. Visitors read the stored snapshots rather than independently retrying the upstream service.
Other sources use caches suited to their update frequency and terms. The application runs on Cloudflare Workers, with KV for shared snapshots and D1 for catalog and monitoring records. A global cache matters here: a cache confined to one data center can still produce repeated upstream reads from different regions.
3. Let one section fail without inventing an answer
Lift status, roads, weather, and webcams have different sources and refresh cycles. A weather-service timeout should not erase an available lift report.
The page and API keep those sections separate. A section can be loading or unavailable while other sections remain useful. Each live section carries its own freshness information; one timestamp for the entire page would conceal those differences.
The distinctions also extend to what the data means:
- Weather and snow forecasts are model estimates, separate from measured observations.
- Campground opening schedules are separate from campsite reservation availability.
- No matching road report is different from an affirmative report that a road is clear.
The same principle applies to an AI assistant consuming the MCP tools. The assistant needs the source and uncertainty, not just a number it can confidently repeat.
4. A generated map still needs a human orientation check
The resort maps combine OpenStreetMap runs and lifts with elevation and, where available, reusable aerial imagery. The Swiss example below credits swisstopo, Copernicus EU-DEM, and OpenStreetMap contributors. It is a generated map, not the resort’s official piste map.
Gstaad exposed a usability problem. It spans separate ski sectors, and choosing orientation from the aggregate direction of the lifts put the main village toward the top. The terrain and lines rendered correctly, but the map was difficult to understand.
The revised 2D map places Gstaad village in the lower part, marks it explicitly, and labels the separate sectors. The build now checks the village’s vertical position when a village marker is configured.
Gstaad’s three main mapped ski areas. Glacier 3000 and the peripheral lifts on its broader status board are outside this map; the resort page also links to the official map.
That review has become part of adding each new resort: verify boundaries, compare lift names, check the village position, and inspect labels at the size readers will actually see. Some resort pages still have official-map links while their generated maps are being added.
Try the data
For a concrete API example:
curl --fail --silent \
'https://status.live/api/v1/ski/resorts/palisades?wait_ms=1500'
Inspect each section’s state and freshness before using its values. With a response time budget, slower sections may still be loading.
The API reference includes interactive examples, and the source-health page shows the monitoring side of the project. For AI clients, setup instructions are at status.live/connect.
The website requires no account. Personal and non-commercial API use is free; reuse and source-credit requirements are described in the terms.
If you build with live outdoor data, I’d be interested in how you present stale information without making people dig for it. Feedback on the resort pages—especially unclear states or map labels—is welcome too.
來源:DEV Community · dev.to
