After two decades of astrophotography, and using pretty much every weather and sky chart tool out there, I grew frustrated as the tools were either too complex or simplistic.
So I made APD, Astrophotography Planning Dashboard, a unified web application for astrophotography planning. Combines weather forecasts from multiple sources (Meteoblue and Met Office via Open-Meteo) with an interactive visual sky chart (Aladin Lite + Telescopius) for framing and planning, and an endpoint dedicated to LLM-friendly weather summaries for AI integrations.
Two years ago I started building this for myself, and it evolved into my one-stop-shop for all my planning needs, from night & day (deep sky and solar) weather forecasts to a sky chart highly focused on visual framing and planning.
All packed into a universal web based app, easy to access from any device, with centralised data.
I've decided to open source it as I think it could be useful to other astrophotographers, and maybe build it into something even better. It will always be "An Opinionated Dashboard for Planning Astrophotography" β my personal vision of the perfect tool, but I'm excited to open it to contributions and suggestions from the community!
π Like what you see? This app is all about chasing stars β so why not give it one? A GitHub star helps others discover APD and keeps the motivation going. Cheers! β
- πΈ Screenshots
- π Quick Installation / Deployment (on Render.com)
- π» Quick Installation / Running locally on your computer
- β¨ Features
- π Self-hosting overview
- π Required services and API keys
- π Deployment Guide
- β° Automated Weather Refresh
- π Support / hosting help
- ποΈ Architecture
- π Authentication
- βοΈ Environment Variables
- π Local Development
- π€οΈ Weather Data Sources
- π Shared Observer Location
- π API Endpoints
- π File Structure
- π€ Contributing
- π License
APD is an online self-hosted free app. Instead of signing up on a hosted service, you spin up your own private copy of the app β on a free or cheap cloud platform β and get your own URL to access it.
You can deploy it directly fto Render.com (see next section), or use other cloud platforms like Railway, Fly.io, etc. (see the Deployment Guide section).
Render.com lets you deploy straight from this public repository URL β just point it at the address below, set your environment variables, and you're done.
- Create a free account at render.com
- Click New β Web Service, choose Connect a repository, and when prompted paste or search for:
https://github.com/giancarloerra/apd - Set the following:
- Build command:
npm ci --include=dev && npm run build - Start command:
npm start
- Build command:
- In the Environment tab, add these variables (enter each as a key-value pair, or click Add from .env to bulk-import from your local file):
UPSTASH_REDIS_REST_URLβ from your Upstash dashboard (check π Required services and API keys)UPSTASH_REDIS_REST_TOKENβ from your Upstash dashboard (check π Required services and API keys)METEOBLUE_API_KEYβ from your Meteoblue account (check π Required services and API keys)MASTER_PASSWORDβ a password to protect your dashboard (recommended)TELESCOPIUS_API_KEYβ from your Telescopius account (optional) (check π Required services and API keys)NODE_ENV=production
- Click Create Web Service β Render builds and deploys automatically.
β οΈ Security: If the app is exposed to the internet, always setMASTER_PASSWORDto a strong, unique password. Without it, anyone who discovers your URL can view your weather data and sky chart, change your API keys, and modify your stored settings. This is the only access control the app has β treat it like the master key to your dashboard.
Updates: Render can auto-deploy whenever this repository releases an update if you authorised the Render GitHub App to access it during setup. Otherwise a one-click manual redeploy is always available from the Render dashboard.
You can run APD entirely on your own machine β no cloud hosting needed. This is great if you prefer to run everything local or you're not comfortable with deploying to a cloud platform.
You will still need the external service accounts (Upstash Redis, Meteoblue API key β see π Required services and API keys) because those provide the weather data and database that the app relies on.
APD runs on Node.js (a free, cross-platform JavaScript runtime). You only need to install it once.
Windows
- Download the Node.js LTS installer from nodejs.org (the big green button).
- Run the installer β accept the defaults and click through.
- When finished, open Command Prompt (press
Win + R, typecmd, press Enter). - Verify the installation:
Both commands should print a version number (e.g.
node --version npm --versionv22.x.xand10.x.x).
macOS
- Download the Node.js LTS installer from nodejs.org (the big green button).
- Open the downloaded
.pkgfile and follow the prompts. - Open Terminal (press
Cmd + Space, typeTerminal, press Enter). - Verify the installation:
Both commands should print a version number.
node --version npm --version
Linux (Ubuntu / Debian)
- Open a terminal and run:
If the version is older than 18, use the NodeSource setup instead:
sudo apt update sudo apt install -y nodejs npm
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt install -y nodejs - Verify the installation:
node --version npm --version
-
Download the app: go to github.com/giancarloerra/apd, click the green Code button, then Download ZIP. Extract the ZIP to a folder of your choice (e.g. your Desktop or Documents folder).
-
Open a terminal in that folder:
- Windows: open the extracted
apd-mainfolder in File Explorer, click in the address bar, typecmd, and press Enter. - macOS: right-click the
apd-mainfolder and choose New Terminal at Folder (or open Terminal and typecdfollowed by dragging the folder into the Terminal window, then press Enter). - Linux: right-click the
apd-mainfolder and choose Open in Terminal (orcdto it manually).
- Windows: open the extracted
-
Install dependencies β run this command in the terminal:
npm installThis downloads all the libraries APD needs. It may take a minute.
-
Create your configuration file: in the
apd-mainfolder, find the file called.env.example, make a copy of it, and rename the copy to.env(just.env, nothing else). You can do this from the terminal:- Windows:
copy .env.example .env - macOS / Linux:
cp .env.example .env
- Windows:
-
Edit the
.envfile with any text editor (Notepad, TextEdit, etc.) and fill in your values:UPSTASH_REDIS_REST_URLβ the REST URL from your Upstash dashboardUPSTASH_REDIS_REST_TOKENβ the REST token from your Upstash dashboardMETEOBLUE_API_KEYβ your Meteoblue API keyMASTER_PASSWORDβ optional when running locally; leave blank if you're the only userTELESCOPIUS_API_KEYβ optional; only needed for the Sky Dashboard's Discover panel
Save the file.
Still in the same terminal, run:
npm run build
npm start
The first command builds the frontend (takes a few seconds). The second starts the server. You should see output like:
Server running on port 3001
Open your browser and go to http://localhost:3001 β APD is now running on your machine.
- To stop the server, press
Ctrl + Cin the terminal. - To start it again later, open a terminal in the same folder and run
npm start(you don't need to rebuild unless the app has been updated). - To update to a newer version, download the latest ZIP from GitHub, extract it over the same folder, run
npm installto pick up any new dependencies, thennpm run buildandnpm start.
- Dual-source forecasts β Meteoblue (primary) + Met Office via Open-Meteo, shown side-by-side for cross-validation
- Night-only filtering β displays only astronomically relevant hours (4 h before night β 10 AM after)
- Astrophotography score β 0β100 composite score per night per source, weighted from cloud cover (80%) and precipitation (20%); labels: Excellent / Very Good / Good / Fair / Poor / Very Poor
- 7-day expandable forecast β per-day rows with hourly cards showing cloud cover, temperature, precipitation, wind, humidity
- Cloud cover sparkline β at-a-glance bar chart of the night's cloud variation per row
- Weekly cloud cover heatmap β colour-coded grid of every night hour across the forecast week, with per-day score row
- Best night banner β highlights the highest-scoring night at a glance
- Moonlight β per-night average luminance (lux) and display percentage, colour-coded by impact
- Sky brightness & wind β shown inline per night row
- Solar astrophotography section β daytime cloud forecasts for H-alpha / solar imaging, with live SDO/HMI solar imagery and sunspot region images
- Weekly solar heatmap β same heatmap layout applied to daytime hours
- Auto / manual weather refresh β data downloads automatically on demand if stale; optional cron job pre-fetches twice daily; manual refresh also available from the header
- Download status indicator β shows last download time or in-progress state
- LLM-friendly summary endpoint β returns structured JSON optimised for AI/LLM consumption; always public (no auth), perfect for daily automation workflows β e.g. a scheduled job that reads the forecast and emails you if tonight or tomorrow night look promising
- Interactive sky map β Aladin Lite v3 with 17+ selectable surveys across Optical (DSS2, PanSTARRS, SDSS9, Mellinger, DECaPS, DESI Legacy), H-Alpha (VTSS, Fink, SHASSA), Infrared (2MASS, AllWISE, AKARI), UV / X-Ray / Gamma (GALEX UV, XMM, Fermi, ROSAT), and Radio (NVSS, GLEAM)
- Two modes β Framing (camera overlay tools) and Sky Chart (pure sky exploration)
- Camera FOV overlay β define sensor FOVs by width Γ height (degrees), saved to Redis; lock to any target
- Mosaic planner β overlay a configurable N Γ M tile grid with adjustable overlap percentage
- Camera angle offset β rotation slider for precise framing alignment
- Equatorial & Alt-Az mount support β FOV box aligned to RA/Dec or horizon depending on mount type
- Favorite targets β save and recall objects, synced to Redis; import directly from Telescopius lists
- Target search β jump to any object by name (e.g. M 42, IC 434)
- Time travel β step the sky forward/backward by hours, days, or months; reset to now
- Celestial overlays β constellation lines, names, star dots, star names, Moon (with phase info), planets
- Red night mode β full-screen red overlay for preserving dark adaptation
- Horizon line β with optional ground darkening and configurable opacity
- Alt-Az and equatorial grids β toggleable with shared opacity slider
- Deep Sky Objects β Messier, NGC, IC, LDN/B catalogs; filterable by type (galaxies, nebulae, dark nebulae, globular + open clusters, planetary nebulae); magnitude limit slider
- Star density & opacity β magnitude-limit and name-density sliders, plus separate constellation and star opacity controls
- Discover panel (Telescopius) β tonight's DSO highlights (filterable by type), target search with type/sort filters, personal Telescopius observing lists, solar system rise/set/transit times
- Live RA / Dec display β equatorial coordinates at map centre, updated in real time
APD is a self-hosted app (you can run it locally or privately host it for yourself for remote access). Instead of signing up on a hosted service, you spin up your own private copy of the app β on a free or cheap cloud platform β and get your own URL to access it.
You don't need to own a server or know how to manage infrastructure. Platforms like Render make it as simple as linking to a Github repository (another simple and free service), setting a few variables, set the password to protect it (so only you can access it), and clicking Deploy. Your data stays private, you control updates, and there are no subscription fees for the app itself.
| Service | What it's for | Cost |
|---|---|---|
| Render (or similar) | Runs the APD server in the cloud | Free tier available |
| Upstash Redis | Stores your settings: location, camera profiles, favourites, API keys | Free tier available |
| Meteoblue API | Weather forecast data (cloud cover, temperature, wind, etc.) | Free 1-year trial for personal use |
| Telescopius API (Optional) | Sky Dashboard Discover panel β DSO highlights, target search, observing lists | Patron-only |
| cron-job.org (Optional) | Triggers weather downloads twice daily (optional but recommended) | Free |
| GitHub (Optional) | Hosts your copy of the code; cloud platforms deploy from it | Free |
The next section walks through each service and how to get started.
Before you deploy, create accounts at the services below and gather the credentials you'll need.
APD stores your observer location, camera FOVs, favourite targets, and runtime-configured API keys in a Redis database. Upstash provides a serverless Redis with a permanently free tier β no credit card required.
- Sign up at upstash.com
- Click Create Database β choose Redis β pick any region close to you
- On the database detail page, copy:
- REST URL β this becomes
UPSTASH_REDIS_REST_URL - REST Token β this becomes
UPSTASH_REDIS_REST_TOKEN
- REST URL β this becomes
Free tier: 256 MB storage, 500,000 commands/month β well within personal use.
Meteoblue provides the primary weather forecast data. They offer a free 1-year API trial for non-commercial / personal use, with 5,000 API calls per year. APD uses roughly 2 calls per download run, so ~2,500 downloads before the limit is reached β enough for many years of twice-daily downloads.
- Create a free account at meteoblue.com
- Click your account icon (top right) β Account Overview
- Click the Weather API box and confirm non-commercial use to activate the free year
- Your API key appears in the API section of your account
After the free year you can purchase credit packages from the same page. Without a key the weather dashboard shows a placeholder message; no forecast data will load.
Used for the Sky Dashboard's Discover panel β tonight's DSO highlights, object search, and personal observing lists. All other sky dashboard features (sky map, FOV overlay, mosaic planner, star charts, etc.) work without it.
The Telescopius API is available to patrons and sponsors of the project. If you already support Telescopius:
- Log in at telescopius.com β account Settings β API Keys
- Generate a new key
- Enter the key in APD's Settings page at any time (saved to Redis β no redeploy needed)
If you don't have a Telescopius key, the sky dashboard still works fully β only the Discover panel stays hidden.
APD fetches fresh weather data automatically when you open the dashboard, but for proactive twice-daily updates (so the forecast is always ready when you need it), point a free HTTP cron job at your deployed URL. See Automated Weather Refresh for the full setup.
APD is a Node.js Express app that serves both the API and the built React frontend from a single process. Any platform that can run a persistent Node.js process and set environment variables will work.
| Platform | Notes | Cost |
|---|---|---|
| Render (recommended) | Zero-config, auto-deploy on push | Free tier (spins down after 15 min idle); ~$7/mo for always-on |
| Railway | Slightly faster cold starts | $5/mo Hobby plan (includes $5 usage credits) |
| Fly.io | More control, Docker-based, global regions | Free tier (limited); pay-as-you-go |
| DigitalOcean App Platform | Solid, predictable pricing | From ~$5/mo |
| Self-hosted VPS | Full control, cheapest at scale | Depends on provider |
- Create a new Web Service on render.com.
- No GitHub account / no fork: choose Connect a repository and paste
https://github.com/giancarloerra/apddirectly. - Fork first (for developers): fork this repo to your GitHub account (see Installing / using APD) and connect your fork.
- No GitHub account / no fork: choose Connect a repository and paste
- Connect your chosen repository.
- Set the following:
- Environment:
Node - Build command:
npm ci --include=dev && npm run build - Start command:
npm start - Node version:
22(or20+)
- Environment:
- Add environment variables under the Environment tab:
- Add each variable as a key-value pair, or click "Add from .env" to paste / import your local
.envfile in bulk (this is a Render dashboard feature, not a file upload β the variables are stored securely by Render). - Do not use Render's Secret Files for this β secret files are mounted at
/etc/secrets/and are not automatically loaded as environment variables.
- Add each variable as a key-value pair, or click "Add from .env" to paste / import your local
- Click Create Web Service β Render builds and deploys automatically.
β οΈ Security: If your instance is accessible from the internet (e.g. deployed on Render, Railway, Fly.io, or any public host), you must setMASTER_PASSWORDto a strong, unique password. Without it every route is open β anyone who discovers your URL can access your dashboard, change API keys, and modify stored settings. The password is the only access control the app provides. Use a randomly generated password of at least 16 characters and store it safely. Leaving the password unset is only appropriate when the app runs on a private/local network you fully trust.
On every push to main, Render will redeploy automatically.
- Create a new project on railway.app and choose Deploy from GitHub repo.
- Select your fork, or connect the upstream repo (
giancarloerra/apd) directly if you don't want to fork. Let Railway detect the Node.js service. - Set the same environment variables in the Railway Variables panel.
- Railway sets
PORTautomatically β no action needed.
| Variable | Required | Description |
|---|---|---|
UPSTASH_REDIS_REST_URL |
Yes | Upstash Redis REST URL (free tier at upstash.com) |
UPSTASH_REDIS_REST_TOKEN |
Yes | Upstash Redis REST token |
MASTER_PASSWORD |
No | Master password β if unset, all routes are open (fine for private/personal use behind a trusted network) |
SESSION_SECRET |
No | Secret for signing session tokens β auto-generated if unset, but set it explicitly in production so sessions survive restarts |
METEOBLUE_API_KEY |
No* | Meteoblue API key β without it, only Met Office data is shown; can also be set at runtime via Settings UI |
TELESCOPIUS_API_KEY |
No* | Telescopius API key β without it, sky chart DSO features are disabled; can also be set at runtime via Settings UI |
OBSERVER_LAT |
No | Default observer latitude (e.g. 52.6278) |
OBSERVER_LON |
No | Default observer longitude (e.g. -1.2983) |
OBSERVER_ALT |
No | Default observer altitude in metres (e.g. 25) |
PORT |
No | Server port β platforms set this automatically; default 3001 |
NODE_ENV |
No | Set to production to enable HTTPS redirect and secure cookies |
CORS_ORIGIN |
No | Comma-separated list of allowed origins in production (e.g. https://apd.example.com). Defaults to open in development, closed in production if not set. |
* API keys can be set via environment variable or through the Settings UI (where they are stored in Redis). The environment variable takes priority.
When you open the dashboard and the weather data is stale (older than 2:30 PM the previous day), APD will download fresh data automatically within that request β but you'll wait a few seconds while it fetches. A cron job pre-fetches the data on a fixed schedule so it's always ready the moment you open the app.
Step 1 β Generate a trigger key
Go to Settings β Automated Weather Refresh and click Generate Key. A unique URL will appear β copy it immediately (the key is only shown once). The URL looks like:
https://your-app.example.com/api/weather/refresh/trigger?key=<random-64-char-hex>
You can rotate or revoke the key at any time from the same Settings card.
Step 2 β Set up a cron service
Option A β cron-job.org (free, recommended)
- Sign up at cron-job.org.
- Create two jobs using the trigger URL you copied above.
- Suggested schedule:
30 9 * * *(09:30) and35 13 * * *(13:35).
Option B β GitHub Actions scheduled workflow
Add a workflow that calls the trigger URL on a schedule:
- run: curl -sf "${{ secrets.APD_TRIGGER_URL }}"Alternative β environment variable
If you prefer to manage the token outside the UI, set CRON_TRIGGER_TOKEN in your .env or hosting dashboard. The trigger URL is then: https://<your-app-url>/api/weather/refresh/trigger?key=<your-token-value>.
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, GitHub Actions scheduled workflows, Render cron jobs)
- Endpoint:
<your-app-url>/api/weather/refresh/trigger?key=<your-key>(shown in Settings after you generate the key) - Key management: generate, rotate, or revoke from Settings β Automated Weather Refresh (or set
CRON_TRIGGER_TOKENenv 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
- Timezone handling: the staleness check uses your server's local time β no extra config needed
You can pair APD with an automation platform (e.g. Make.com, n8n, 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
<your-app-url>/api/weather/refresh/trigger?key=<your-key>(shown in Settings after you generate the key) - Smart Email: construct it from the
astrophotography_scorefield 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.")
APD requires a few third-party accounts and some technical setup. If you need help, you can reach out at giancarloerra@gmail.com (I won't be able to help deploying this for free, but I can answer questions and point you in the right direction, or provide guidance for paid assistance).
- GitHub Issues β open a bug report or question for anything related to the app itself.
- GitHub Discussions β for general help, deployment questions, and ideas.
- Weather Dashboard: React 18 + TypeScript + Tailwind CSS (Vite build)
- Sky Dashboard: Self-contained HTML page with Aladin Lite v3.6.5 sky viewer, jQuery 3.7.1 (Aladin dependency)
- Icons: Lucide React
- Celestial data: Star, constellation, and DSO data from d3-celestial plus additional catalogs (SH2, LBN, vdB, HCG, Abell PNe) from VizieR/CDS (bundled locally in
public/data/celestial/) - Astronomy calculations: Simplified VSOP87/Meeus for planet positions, moon position and phase β approximate, optimised for visual display rather than astrometric precision
- Sky Dashboard persistence: UI settings (panel state, sliders, toggles) stored in
localStorage(astro-sky-dashboard-v1); FOVs and favorites stored in Redis via the API
- Runtime: Node.js with ES modules
- Framework: Express.js
- Persistent Storage: Upstash Redis (weather data, FOVs, favorites, API keys, observer location)
- Auth: HMAC-SHA256 session tokens in httpOnly cookies
| Service | Purpose |
|---|---|
| Upstash Redis | Stores weather data, FOVs, favorites, API keys, observer location β shared across deploys |
| Meteoblue API | Primary weather data (cloud cover, precipitation, temperature, wind, humidity, day/night) |
| Open-Meteo | Met Office UK model data (cloud cover, precipitation, temperature) |
| Telescopius API | DSO highlights, search, solar system data for the sky dashboard |
- Client requests weather data from the Express API
- Backend checks if cached data is fresh (< 24 hours old, after 2:30 PM yesterday)
- If stale, fetches from Meteoblue + Met Office APIs using the shared observer location
- Both datasets are cached to Redis and returned
- Frontend processes both datasets using Meteoblue's night/day detection for consistency
- All users share the same cached data
The app uses a single master password for access control.
- When
MASTER_PASSWORDis set in.env, all routes require authentication (except/api/health,/api/weather/summary, and/api/weather/refresh/trigger) - When
MASTER_PASSWORDis not set (or empty), auth is completely disabled β all routes are open - Sessions use HMAC-SHA256 signed tokens stored in httpOnly cookies (30-day expiry)
- The cron trigger endpoint uses a separate token-based auth (not session cookies) β see Automated Weather Refresh
- Login page served at
/login
| Endpoint | Purpose |
|---|---|
GET /api/health |
Health check β also confirms Redis connectivity |
GET /api/weather/summary |
LLM-friendly weather forecast JSON (for AI integrations) |
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 |
GET /login |
Login page |
All other /api/* routes, the weather dashboard (/weather), sky dashboard (/skychart.html), settings page (/settings), about page (/about), and all other non-root app paths.
To run without authentication (e.g., local development or future Electron app), simply leave MASTER_PASSWORD unset or empty in .env. The auth system is completely bypassed β no login page, no redirects, no cookie checks.
All configuration is via a .env file (see .env.example). The server loads it automatically when present; on cloud platforms, set env vars in the hosting dashboard instead.
Copy .env.example to .env and fill in your values:
cp .env.example .env| Variable | Required | Description |
|---|---|---|
UPSTASH_REDIS_REST_URL |
Yes | Upstash Redis REST URL β used for FOVs, favorites, API keys, location |
UPSTASH_REDIS_REST_TOKEN |
Yes | Upstash Redis REST token |
MASTER_PASSWORD |
No | Master password β if unset, auth is disabled |
SESSION_SECRET |
No | Secret for signing session tokens β auto-generated if unset |
CRON_TRIGGER_TOKEN |
No | Cron trigger token β if set, the trigger endpoint accepts this value; otherwise manage via Settings UI β Redis |
METEOBLUE_API_KEY |
No* | Meteoblue API key β without it, only Met Office data is shown; can also be set via UI β Redis |
TELESCOPIUS_API_KEY |
No* | Telescopius API key β without it, sky chart DSO features are disabled; can also be set via UI β Redis |
OBSERVER_LAT |
No | Default observer latitude (default: 52.6278) |
OBSERVER_LON |
No | Default observer longitude (default: -1.2983) |
OBSERVER_ALT |
No | Default observer altitude in metres (default: 25) |
PORT |
No | Server port (default: 3001) |
NODE_ENV |
No | Set to production for HTTPS redirect and secure cookies |
CORS_ORIGIN |
No | Comma-separated allowed origins in production (e.g. https://apd.example.com) |
* API keys can be provided via .env or stored in Redis through the UI. The .env value takes priority.
- Node.js 18+
- npm
- Upstash Redis account (free tier works)
git clone <repository-url>
cd apd
npm install
cp .env.example .env
# Edit .env with your credentialsnpm run devThis starts both:
- Frontend dev server on
http://localhost:5173(Vite, with HMR) - Backend server on
http://localhost:3001(nodemon, auto-restarts on changes)
Vite proxies /api requests to localhost:3001 automatically.
| Script | Description |
|---|---|
npm run dev |
Start frontend + backend in development |
npm run client:dev |
Start only the Vite frontend dev server |
npm run server:dev |
Start only the backend with nodemon |
npm run build |
Build frontend for production (outputs to dist/) |
npm start |
Start production server |
npm run lint |
Run ESLint |
npm run typecheck |
TypeScript type-check (no emit) |
npm test |
Run unit tests with Vitest |
npm run test:coverage |
Run tests with coverage report |
npm run release |
Bump version, update CHANGELOG, create GitHub release |
Unit tests run with Vitest and cover both server utilities and frontend logic:
src/utils/weatherUtils.test.tsβ Meteoblue data processing, scoring, and night detectionserver/utils/weatherProcessor.test.jsβ weather summary generation for the LLM endpoint
npm test # run tests
npm run test:coverage # run with coverage reportThe CI workflow (.github/workflows/ci.yml) runs on every push and pull request to main β it lints, type-checks, tests, and builds against Node.js 18, 20, and 22 in parallel.
Releases are automated via release-it. A GitHub Actions workflow (.github/workflows/release.yml) is triggered manually from the Actions tab β choose patch, minor, or major. It bumps package.json, updates CHANGELOG.md, tags the commit, and publishes a GitHub Release.
Contributions are welcome. See CONTRIBUTING.md for development setup, commit conventions, and pull request guidelines.
- Package:
basic-1h_clouds-1h_sunmoon_moonlight-1h - Strengths: Complete dataset with night/day detection, temperature, wind, humidity
- Usage: Primary calculations, night/day filtering, reference hours, statistics
- Model:
ukmo_global_deterministic_10km - Strengths: High-quality UK-specific forecasts
- Usage: Comparative forecasting, cross-validation
- Note: Used by Scope Nights β less precise but more optimistic than the 2km model
- Time Structure: Uses Meteoblue's time structure and night/day detection for consistency
- Display: Two separate sections showing identical layouts with different data sources
- Reference Hours: Both sources show 4 hours before night + hours until 10am after night
- Statistics: Each source calculates independent astrophotography scores
- Scoring difference: Meteoblue score uses cloud cover (80%) + precipitation (20%). Met Office score uses cloud cover only (Met Office reports precipitation in mm rather than %, so the precipitation penalty is not applied). Both are scaled 0β100.
Both the weather dashboard and sky dashboard share a single observer location stored in Redis.
- Default: Loaded from
OBSERVER_LAT/OBSERVER_LONenv vars - Runtime override: Set via UI in either dashboard β saved to Redis; the Settings page also provides a βUse my locationβ geolocation button
- API:
GET /PUT /api/locationβ shared by both dashboards - Weather API calls (Meteoblue and Open-Meteo) use the shared location dynamically
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/health |
No | Health check β confirms Redis connectivity |
GET |
/api/settings |
Yes | Aggregate settings: location, API key states, auth config, download status |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/weather |
Yes | Cached weather data (auto-downloads if stale) |
POST |
/api/weather/refresh |
Yes | Force fresh download (manual/UI use) |
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 |
GET |
/api/weather/meteoblue/key-status |
Yes | Check if Meteoblue key is configured |
PUT |
/api/weather/meteoblue/key |
Yes | Save Meteoblue API key to Redis |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/settings/cron-token |
Yes | Get trigger key status (configured, source, timestamps) |
POST |
/api/settings/cron-token |
Yes | Generate or rotate trigger key (returns raw key once) |
DELETE |
/api/settings/cron-token |
Yes | Revoke trigger key |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/location |
Yes | Get shared observer location |
PUT |
/api/location |
Yes | Update shared observer location |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/skychart/telescopius/highlights |
Yes | DSO highlights (cached 6h) |
GET |
/api/skychart/telescopius/search |
Yes | Target search |
GET |
/api/skychart/telescopius/solar-system |
Yes | Solar system times (cached 12h) |
GET |
/api/skychart/telescopius/lists |
Yes | Observing lists (cached 24h) |
GET |
/api/skychart/telescopius/lists/:id |
Yes | Single list details (cached 12h) |
GET |
/api/skychart/telescopius/pictures |
Yes | Astrophotography pictures search |
GET |
/api/skychart/telescopius/key-status |
Yes | Check Telescopius key status |
PUT |
/api/skychart/telescopius/key |
Yes | Save Telescopius API key to Redis |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/skychart/fovs |
Yes | Get saved FOV list |
PUT |
/api/skychart/fovs |
Yes | Save FOV list |
GET |
/api/skychart/favorites |
Yes | Get saved favorites |
PUT |
/api/skychart/favorites |
Yes | Save favorites |
Telescopius proxy routes are rate-limited to 30 requests/minute.
apd/
βββ server/ # Backend
β βββ index.js # Express server, auth, routes, Redis
β βββ services/
β β βββ weatherService.js # Weather fetching & caching
β βββ utils/
β βββ weatherProcessor.js # /api/weather/summary processing
β βββ weatherProcessor.test.js
βββ src/ # Frontend (React + TypeScript)
β βββ main.tsx # React entry point
β βββ App.tsx # Root component (routing + auth check)
β βββ index.css # Global styles (Tailwind base)
β βββ vite-env.d.ts # Vite client type declarations
β βββ components/
β β βββ Dashboard.tsx # Main weather dashboard
β β βββ LandingPage.tsx # Home / entry page with animated star field
β β βββ AboutPage.tsx # Feature guide and usage reference
β β βββ UnifiedDayRow.tsx # Day row with Meteoblue + Met Office side-by-side
β β βββ HourlyCard.tsx # Hourly forecast card
β β βββ WeeklyHeatmap.tsx # Weekly cloud cover heatmap grid
β β βββ DownloadStatus.tsx # Download status indicator
β βββ services/
β β βββ weatherService.ts # API client
β βββ types/
β β βββ weather.ts # TypeScript interfaces
β βββ utils/
β βββ weatherUtils.ts # Meteoblue data processing & scoring
β βββ weatherUtils.test.ts # Frontend unit tests
β βββ metOfficeUtils.ts # Met Office data processing & scoring
βββ public/
β βββ skychart.html # Sky dashboard (self-contained, Aladin Lite)
β βββ settings.html # Settings page (location, API keys, system status)
β βββ theme.css # Shared CSS theme (accent colour tokens)
β βββ logo.png # App logo / favicon
β βββ data/celestial/ # Star, constellation & DSO data (d3-celestial + VizieR catalogs)
βββ screenshots/ # README screenshots
βββ .github/
β βββ workflows/
β β βββ ci.yml # Lint, typecheck, test, build Β· Node 18/20/22
β β βββ release.yml # Automated release via release-it
β βββ ISSUE_TEMPLATE/ # Bug report & feature request templates
β βββ PULL_REQUEST_TEMPLATE.md
βββ .env.example # Template for .env
βββ .release-it.json # release-it config (conventional changelog)
βββ index.html # Vite entry HTML
βββ package.json
βββ vite.config.ts # Vite + Vitest configuration & dev proxy
βββ tsconfig.json # TypeScript project references root
βββ tsconfig.app.json # TypeScript config for the app source
βββ tsconfig.node.json # TypeScript config for Node/Vite tooling
βββ eslint.config.js # ESLint flat config
βββ tailwind.config.js # Tailwind CSS config
βββ postcss.config.js # PostCSS config (Tailwind + autoprefixer)
βββ CONTRIBUTING.md # Contribution guidelines
βββ CODE_OF_CONDUCT.md # Community code of conduct
βββ CHANGELOG.md # Auto-generated changelog (release-it)
βββ LICENSE # AGPL-3.0-or-later
βββ README.md
Copyright Β© 2026 Giancarlo Erra.
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
See LICENSE for the full license text.
This means:
- You can use, copy, modify, and distribute this software freely.
- If you run a modified version on a server and users interact with it over a network, you must make the modified source code available to those users.
- Any project that incorporates this code must also be released under AGPL-3.0-or-later.
APD includes or links to the following third-party components, each under its own license:
| Component | License | Notes |
|---|---|---|
| Aladin Lite v3 | GPL-3.0 | Loaded from CDS servers; compatible with AGPL-3.0 per Β§13 of both licenses |
| d3-celestial data files | BSD-3-Clause | Star, constellation, and DSO catalog data bundled in public/data/celestial/ |
| VizieR/CDS extra catalogs | Public astronomical data | SH2 (Sharpless 1959), LBN (Lynds 1965), B (Barnard 1927), LDN (Lynds 1962), vdB (van den Bergh 1966), HCG (Hickson 1993), Abell PNe (Acker+ 1992) β sourced from VizieR, CDS Strasbourg and bundled in public/data/celestial/catalogs_extra.json |
| Lucide icons | ISC | Icon library used in the React frontend |
| Google Fonts β Inter | SIL Open Font License 1.1 | Loaded from Google Fonts CDN |
| SDO/HMI solar imagery | Public domain | Hotlinked from Stanford JSOC; NASA/SDO data is in the public domain |
All npm dependencies (React, Express, Tailwind CSS, etc.) are licensed under MIT or ISC β see each package's own LICENSE file for details.












