diff --git a/README.md b/README.md index 36c13e0..22bb593 100644 --- a/README.md +++ b/README.md @@ -318,7 +318,7 @@ If you prefer to manage the token outside the UI, set `CRON_TRIGGER_TOKEN` in yo A cron service calls the trigger endpoint on a schedule to pre-fetch fresh data before you open the dashboard. Without it the app still works — it downloads on demand when you visit — but you wait a few seconds. With it, data is always ready instantly. - **Service**: any free HTTP cron service (e.g. [cron-job.org](https://cron-job.org), GitHub Actions scheduled workflows, Render cron jobs) -- **Endpoint**: `/api/weather/refresh/trigger?key=` (shown in Settings after you generate the key) +- **Endpoint**: `/api/weather/refresh/trigger?key=` (shown in Settings after you generate the key) - **Key management**: generate, rotate, or revoke from **Settings → Automated Weather Refresh** (or set `CRON_TRIGGER_TOKEN` env var) - **Rate limit**: 3 requests/minute (separate from the manual refresh limit of 5/min) - **Recommended schedule**: once ~10:30 and once ~14:35 local time @@ -329,7 +329,7 @@ A cron service calls the trigger endpoint on a schedule to pre-fetch fresh data You can pair APD with an automation platform (e.g. [Make.com](https://make.com), [n8n](https://n8n.io), Zapier) to send an email digest after each scheduled refresh: - **Trigger**: schedule a second HTTP call ~5 minutes after the cron refresh -- **Content**: fetch the forecast from `/api/weather/refresh/trigger?key=` (shown in Settings after you generate the key) +- **Content**: fetch the forecast from `/api/weather/refresh/trigger?key=` (shown in Settings after you generate the key) - **Smart Email**: construct it from the `astrophotography_score` field to highlight clear nights (simple AI can be very effective here — e.g. "Tonight looks excellent for astrophotography with only 10% cloud cover, while tomorrow is poor with 80% clouds.") --- @@ -397,7 +397,7 @@ The app uses a single **master password** for access control. |----------|---------| | `GET /api/health` | Health check — also confirms Redis connectivity | | `GET /api/weather/summary` | LLM-friendly weather forecast JSON (for AI integrations) | -| `GET /api/weather/refresh/trigger?key=…` | Cron trigger — token-authenticated weather refresh (see Settings) | +| `GET/POST /api/weather/refresh/trigger?key=…` | Cron trigger — token-authenticated weather refresh (see Settings) | | `GET /api/auth/check` | Check authentication status | | `POST /api/auth/login` | Login with master password | | `POST /api/auth/logout` | Clear session | @@ -565,7 +565,7 @@ Both the weather dashboard and sky dashboard share a single observer location st |--------|----------|------|-------------| | `GET` | `/api/weather` | Yes | Cached weather data (auto-downloads if stale) | | `POST` | `/api/weather/refresh` | Yes | Force fresh download (manual/UI use) | -| `GET` | `/api/weather/refresh/trigger?key=…` | **Token** | Cron trigger — public, token-authenticated (3 req/min) | +| `GET/POST` | `/api/weather/refresh/trigger?key=…` | **Token** | Cron trigger — public, token-authenticated (3 req/min) | | `GET` | `/api/weather/status` | Yes | Download status only | | `GET` | `/api/weather/metoffice` | Yes | Cached Met Office data | | `GET` | `/api/weather/summary` | **No** | LLM-friendly processed forecast | diff --git a/server/index.js b/server/index.js index 48f7f9d..f4932fe 100644 --- a/server/index.js +++ b/server/index.js @@ -297,7 +297,7 @@ async function getCronTokenData() { return null; } -app.get("/api/weather/refresh/trigger", triggerLimiter, async (req, res) => { +const triggerHandler = [triggerLimiter, async (req, res) => { const key = typeof req.query.key === "string" ? req.query.key : ""; if (!key) { return res.status(401).json({ error: "Missing trigger key" }); @@ -335,7 +335,9 @@ app.get("/api/weather/refresh/trigger", triggerLimiter, async (req, res) => { console.error("Cron trigger refresh error:", error); res.status(500).json({ error: "Refresh failed" }); } -}); +}]; +app.get("/api/weather/refresh/trigger", ...triggerHandler); +app.post("/api/weather/refresh/trigger", ...triggerHandler); // ===== PROTECTED ROUTES — everything below requires auth ===== app.use("/api/*", requireAuth);