# 77th EV Calendar Notify Service A TypeScript + Node.js service that polls the 77th JSOC events API, exposes its events through a REST API, stores them in SQLite, publishes an ICS feed, and sends change notifications through an Apprise server. ## Requirements - Node.js 22.9 or newer - npm ## Run ```sh npm install Copy-Item .env.example .env npm run dev ``` The service requests `https://77th-jsoc.com/service.php?action=get_events` for each calendar month touched by today through the next 60 days. It sets `year`, `month`, and `timezone=-120` on every request and polls every 15 minutes. The API's `uid` is the event identity in SQLite, the REST API, and ICS `UID`; the event title remains the display summary. Each successful poll stores source events in `data/events.sqlite` (or the path configured by `DB_PATH`). When opening the older title-keyed database schema, the service preserves its old table under an `events_legacy_title_*` name and starts a silent UID-based baseline. If the events table is empty, the poll is treated as an initial snapshot and sends no notifications. Later additions, changes, and removals are queued durably in SQLite and sent through Apprise. Failed sends remain queued for retry. Events removed from the source remain in SQLite as cancelled ICS entries. ## Apprise and notification configuration Copy `.env.example` to `.env` and set the Apprise host, ntfy host/topic, and Discord webhook. `dc_webhook` accepts `WEBHOOK_ID/WEBHOOK_TOKEN` or the full Discord webhook URL. ntfy credentials are optional, but set both username and password if authentication is required. Leave `notification_mock=false` to send; set it to `true` to log and consume notifications without sending. The service posts a JSON payload to Apprise `/notify` with both the ntfy and Discord URLs. Apprise supports multiple destination URLs in one request. [Apprise API](https://github.com/caronc/apprise-api/blob/master/README.md) A `.env` file is optional at startup; without one, the server still runs, but notification sends will remain queued until the notification settings are configured. `PORT` defaults to `3000`; `DB_PATH` defaults to `./data/events.sqlite`. For production, run `npm run build` followed by `npm start`, and keep the process running with your preferred process manager or service host. ## Docker Build and start the calendar service and Apprise with Docker Compose: ```sh Copy-Item .env.example .env # Edit .env and configure Apprise, ntfy, and Discord settings. docker compose up -d --build ``` The calendar service listens on port `3000`; set `HOST_PORT` in the shell or Compose `.env` file to change the host port. Apprise listens on the private Compose network at `apprise:8000` and is not published directly to the internet. SQLite data is stored in the persistent `event-data` Docker volume. Follow both services with `docker compose logs -f`, stop them with `docker compose down`, and remove the database volume only when you intend to delete the stored events and notification history (`docker compose down -v`). To build and run without Compose: ```sh docker build -t 77th-ev-cal-ntfy-srv . docker run -d --name 77th-ev-cal-ntfy-srv --restart unless-stopped \ --env-file .env -p 3000:3000 -v event-data:/app/data \ 77th-ev-cal-ntfy-srv ``` ## Endpoints - `GET /health` — service and polling status - `GET /api/events` — latest active events and polling status - `POST /api/refresh` — trigger an immediate poll - `GET /calendar.ics` — active events and cancellation entries for calendar subscription