A static site listing on-duty ("nöbetçi") pharmacies in Turkish provinces, built with Astro. Pharmacy data is scraped daily from official provincial chamber of pharmacists pages into static JSON, so the deployed site needs no backend.
The scraping approach (selectors, parsing logic) is ported from the earlier
Gleam implementation on this repository's main branch, a scraper for the same
Antalya source page using headless Chrome. This version uses a plain fetch +
cheerio instead, since the source pages turned out
to be server-rendered and don't require JS execution.
src/
data/ scraped CityData JSON, one file per city (e.g. antalya.json)
lib/
cities.ts registry of known cities (slug, name, source URL)
types.ts Pharmacy / CityData contract shared with the UI
pages/ Astro pages (UI is owned separately from the scraper)
scripts/
scrape.ts scraper entrypoint, run per city
| Command | Action |
|---|---|
npm install |
Install dependencies |
npm run scrape -- <slug> |
Scrape a city (e.g. npm run scrape -- antalya) and write src/data/<slug>.json |
npm run dev |
Start local dev server |
npm run build |
Build the static site to ./dist/ |
npm run preview |
Preview the production build locally |
There are two deploy workflows, both deploying dist/ to Cloudflare Pages
(project eczane) via wrangler:
-
.github/workflows/deploy.ymlruns on push tomainand onworkflow_dispatch. It scrapes fresh data into the working tree, builds, and deploys. -
.github/workflows/update-data.ymlruns daily (and onworkflow_dispatch). It scrapes fresh data into the working tree, builds, and deploys.
Both workflows use the same CLOUDFLARE_API_TOKEN secret and CLOUDFLARE_ACCOUNT_ID repository variable.
Scraped JSON under src/data/ is generated at deploy time and is
git-ignored — never commit it as a baseline: a stale committed snapshot gets
shipped to prod if a build ever runs before the scraper (seen live
2026-09-09: a merge regressed prod to August data).
- Add an entry to
src/lib/cities.tswith aslug, displayname, and the province's on-duty pharmacy source URL. - Inspect the source page's HTML and adjust the selectors in
scripts/scrape.tsif the markup differs from Antalya's (.ilce,.ilcebas,.nobetciDivstructure). - Run
npm run scrape -- <slug>and verifysrc/data/<slug>.jsonlooks correct (plausible pharmacy count, mostly non-null coordinates). The file is git-ignored; it only needs to exist locally for dev/build. - If you want the daily CI refresh to cover the new city, add a corresponding
npm run scrape -- <slug>line to.github/workflows/update-data.yml.