No description
Find a file
2026-08-17 13:48:24 -04:00
.agents Update distance scored 2026-08-17 12:51:14 -04:00
apps Fix distances for real 2026-08-17 13:48:24 -04:00
docs Update dockerfile for scraper 2026-08-15 12:38:27 -04:00
.dockerignore first commit 2026-08-15 11:26:58 -04:00
.env.example first commit 2026-08-15 11:26:58 -04:00
.gitignore Fix distances for real 2026-08-17 13:48:24 -04:00
AGENTS.md Add distance score and sorting 2026-08-17 09:13:50 -04:00
docker-compose.yml first commit 2026-08-15 11:26:58 -04:00
package-lock.json first commit 2026-08-15 11:26:58 -04:00
package.json first commit 2026-08-15 11:26:58 -04:00
README.md first commit 2026-08-15 11:26:58 -04:00
sampledata.json Add offer status to cards 2026-08-15 12:47:52 -04:00
skills-lock.json first commit 2026-08-15 11:26:58 -04:00

MySchools Waitlist Monitor

Private LAN/Coolify-friendly monitor for NYC MySchools waitlist data. The scraper logs in once, fetches the waitlist endpoint, posts the payload to a local Fastify API, and the React dashboard updates over WebSocket.

Apps

  • apps/scraper: one-shot Node 20 scraper for cron.
  • apps/api: Fastify API and WebSocket broadcaster.
  • apps/web: Vite, React, and Tailwind dashboard.

Install

cp .env.example .env
npm install

Fill .env with your MySchools USERNAME and PASSWORD. Do not commit .env.

Run Locally

npm run dev:api
npm run dev:web
npm run scrape

The API defaults to http://localhost:3001. The dashboard defaults to http://localhost:5173 and connects to ws://localhost:3001/ws.

Cron

The scraper is designed to run once per hour to respect the site's rate limits.

0 * * * * cd /path/to/myschools-waitlist && npm run scrape >> /var/log/myschools-scrape.log 2>&1

Coolify Notes

Run the API and web app as separate services. Keep the scraper as a scheduled job/cron task with the same .env values and API_INGEST_URL pointing at the API service.

Dockerfiles are included for apps/api and apps/web; docker-compose.yml is provided for local container smoke tests. For Coolify, deploy each Dockerfile as its own service and set VITE_WS_URL/VITE_API_URL for the public or LAN API address before building the web image.

API Contract

  • GET /health: health check.
  • GET /waitlist: current in-memory waitlist state.
  • POST /ingest: scraper payload, either { data, fetchedAt } or a raw waitlist payload.
  • GET /ws: WebSocket stream. Messages are JSON objects with type: "state" and payload containing { data, fetchedAt, receivedAt }.