A full-stack mission dashboard for Artemis II with a static multi-page frontend, a Node.js/Express API backend, live telemetry polling, WebSocket push updates, automated mission/news aggregation, and a separate hidden admin console for manually managing media assets.
This repository is organized as two applications:
frontend/contains the public website and the hidden admin page.backend/contains the REST API, scheduled data collection jobs, media persistence, and WebSocket server.
The project is designed to present a "mission control" style experience around Artemis II. It combines live or near-live orbital data, mission timeline snapshots, Deep Space Network information, space weather, and curated media into one interface.
- What This Project Does
- Main Features
- Architecture Overview
- Repository Structure
- Tech Stack
- How Data Flows
- Local Development Setup
- Environment Variables
- Running the Project
- Public Pages
- Backend API
- WebSocket Feed
- Media Admin Workflow
- Manual Media Posting Rules
- Useful Scripts
- Deployment Notes
- Security Notes
- Known Limitations and Legacy Files
- Troubleshooting
The Artemis II Mission Tracker is a mission dashboard focused on the Artemis II flight profile and supporting context around the mission.
At runtime, it provides:
- live or fallback orbital telemetry
- mission elapsed time and mission phase tracking
- mission timeline and activity updates
- Deep Space Network visibility
- space weather summaries and event lists
- Artemis-related news aggregation from RSS and timeline highlights
- a public media center for images, videos, documents, and other files
- a hidden standalone admin console for authorized media publishing
The frontend is intentionally simple to host: it is a static site made from HTML, CSS, and plain JavaScript. The backend handles the dynamic work and persistence.
- Real-time dashboard with telemetry cards, mission timing, and timeline rendering
- WebSocket broadcasting for telemetry, DSN, weather, and timeline refreshes
- REST API for telemetry, history, news, timeline, weather, health, DSN, and media
- MongoDB-backed snapshot storage for telemetry, timeline, weather, DSN, news, and media
- In-memory caching layer to reduce repeated upstream fetches
- Automatic news aggregation from multiple Artemis-related feeds
- Automatic timeline/news/weather refresh via cron jobs
- Public media center with grouped content:
- images
- videos
- documents
- other downloadable files
- Manual admin posting for:
- Google Drive images
- Google Drive videos
- YouTube links
- direct
.mp4,.webm,.ogg,.mov,.m4vlinks - uploaded local image/video/document/other files
- PDFs, docs, spreadsheets, text files, JSON, XML, and similar assets
- Hidden admin access flow:
- not linked from the public UI
- separate page from the public media center
- temporary unlock via private keyboard shortcut
- login-backed posting session using
ADMIN_API_KEY
frontend/src/*.html + frontend/src/js/*
|
| fetch / WebSocket
v
backend/src/app.js
|
+--> REST routes (/api/v1/*)
+--> WebSocket server (/ws)
+--> cron scheduler
|
+--> services
|
+--> cache (in-memory)
+--> MongoDB via Mongoose
+--> external data sources
- AROW community relay
- JPL Horizons fallback
- NASA DONKI
- RSS feeds
- The frontend loads static HTML/JS/CSS from
frontend/src. - The frontend calls the backend REST API for initial data.
- The frontend connects to
/wsfor push updates. - The backend runs scheduled jobs to refresh telemetry, DSN, timeline, weather, and news.
- Fresh data is cached, optionally persisted to MongoDB, and broadcast to connected clients.
- The media center reads manually managed media from MongoDB plus bundled fallback content if the media collection is empty.
ARTEMIS-TRACKER/
|- frontend/
| `- src/
| |- index.html
| |- about.html
| |- crew.html
| |- crew-schedule.html
| |- spacecraft.html
| |- media.html
| |- sources.html
| |- admin.html
| |- css/style.css
| |- js/app.js
| |- js/media.js
| |- js/admin-access.js
| |- js/admin-console.js
| `- js/data.js
|
`- backend/
|- src/
| |- index.js
| |- app.js
| |- config/
| |- routes/
| |- services/
| |- fetchers/
| |- websocket/
| |- cache/
| |- db/
| | |- models/
| | `- queries/
| `- utils/
|- scripts/
|- package.json
`- .env.example
- HTML5
- CSS
- Vanilla JavaScript
- WebSocket client
- Static multi-page structure
- Node.js 22+
- Express 5
- Mongoose
- MongoDB
wsfor WebSocketsnode-cronfor scheduled refreshhelmet,cors,morganundicifor HTTP calls
- MongoDB for persisted snapshots and media metadata
- local filesystem storage under
backend/public/mediafor uploaded files - in-memory cache for fast transient reads
Telemetry is fetched through a fallback chain:
- AROW community consolidated endpoint
- direct community telemetry + AROW system endpoints
- JPL Horizons fallback
- interpolated mock telemetry
Real data snapshots are stored in MongoDB. Mock data is cached but intentionally not written to the database.
Timeline data is pulled from the community mission timeline feed, cached, stored in MongoDB, and broadcast to clients every 5 minutes.
Space weather combines:
- live solar data from the community relay when available
- NASA DONKI event feeds for solar flares, CMEs, geomagnetic storms, and SEP events
News is automatically fetched from:
- NASA Artemis RSS
- Spaceflight Now
- NASASpaceflight
- Ars Technica science feed
- mission timeline highlights from the community timeline API
Important: news is still automated. It is not manually posted through the admin console.
Media content is different from news:
- public viewers consume
GET /api/v1/media - admins can create new items through the hidden admin console
- URL-based items are saved as metadata in MongoDB
- uploaded files are stored under
backend/public/media/...and then referenced by MongoDB records
Make sure you have:
- Node.js 22 or newer
- npm
- a MongoDB connection string
- optionally a NASA API key
- a static file server for the frontend, or a hosting platform that can serve
frontend/src
git clone <your-repo-url>
cd ARTEMIS-TRACKERcd backend
npm installThere is no frontend package.json in this repo. The frontend is static and does not need an install step.
Copy backend/.env.example to backend/.env and fill in the values:
cp .env.example .envOn Windows PowerShell:
Copy-Item .env.example .envFrom backend/:
npm run devor:
npm startThe backend defaults to http://localhost:3001.
Because the frontend is plain static files, you can use any static server. Common options:
- VS Code Live Server
python -m http.servernpx serve frontend/src- Nginx / Apache / Vercel / Netlify
Example with Python from the repo root:
cd frontend/src
python -m http.server 3000Then open:
- frontend:
http://localhost:3000 - backend API:
http://localhost:3001
The frontend JavaScript already assumes:
http://localhost:3001when running on localhosthttps://artemis-tracker-mzav.onrender.comwhen not on localhost
If you use a different backend URL in development or production, update the hard-coded BACKEND_BASE values in:
frontend/src/js/app.jsfrontend/src/js/media.jsfrontend/src/js/admin-console.js
The backend reads configuration from backend/.env.
| Variable | Required | Purpose |
|---|---|---|
NODE_ENV |
No | Environment name, usually development or production. |
PORT |
No | Backend port. Defaults to 3001. |
LOG_LEVEL |
No | Logging level for backend logs. |
MONGODB_URI |
Yes | MongoDB connection string used by Mongoose. |
NASA_API_KEY |
Recommended | NASA API key for NASA-backed fetchers. |
COMMUNITY_API_BASE |
No | Base URL for community Artemis relay endpoints. |
COMMUNITY_API_TIMEOUT_MS |
No | Timeout for community API calls. |
ADMIN_API_KEY |
Yes | Admin password used to sign in to the hidden media console. |
ALLOWED_ORIGINS |
Yes for production | Comma-separated frontend origins allowed by CORS. |
Example:
NODE_ENV=development
PORT=3001
LOG_LEVEL=debug
MONGODB_URI=mongodb+srv://<username>:<password>@<cluster>.mongodb.net/?appName=<app_name>
NASA_API_KEY=YOUR_NASA_API_KEY
COMMUNITY_API_BASE=https://artemis.cdnspace.ca
COMMUNITY_API_TIMEOUT_MS=30000
ADMIN_API_KEY=replace_this_with_a_real_secret
ALLOWED_ORIGINS=http://localhost:3000,https://your-frontend-domain.comcd backend
npm run devcd backend
npm startServe frontend/src as a static site and make sure the origin is listed in ALLOWED_ORIGINS.
The public frontend includes these pages:
index.html- main mission dashboardabout.html- project/about pagecrew.html- crew overviewcrew-schedule.html- crew schedule viewspacecraft.html- Orion / SLS / subsystem overviewmedia.html- public media centersources.html- mission/data sources page
The admin page is separate:
admin.html- hidden admin console, not linked from the public UI
Public pages include the hidden admin shortcut script, but they do not expose any visible admin login UI.
Base path: /api/v1
| Method | Endpoint | Description |
|---|---|---|
GET |
/health |
Returns overall backend health and MongoDB status. |
| Method | Endpoint | Description |
|---|---|---|
GET |
/telemetry |
Returns current telemetry snapshot. |
GET |
/telemetry/history?hours=2 |
Returns telemetry history for the requested window. |
| Method | Endpoint | Description |
|---|---|---|
GET |
/timeline/current |
Returns current mission timeline snapshot. |
| Method | Endpoint | Description |
|---|---|---|
GET |
/dsn |
Returns current Deep Space Network data. |
| Method | Endpoint | Description |
|---|---|---|
GET |
/weather |
Returns current space weather summary and event lists. |
| Method | Endpoint | Description |
|---|---|---|
GET |
/news |
Returns latest Artemis-related news items. |
GET |
/news?limit=6 |
Returns a limited number of items. |
| Method | Endpoint | Description |
|---|---|---|
GET |
/media |
Returns the public media hub payload. |
POST |
/media/admin/login |
Signs in an admin and returns a temporary admin session token. |
GET |
/media/admin/session |
Validates the current admin session token. |
POST |
/media |
Creates a media item. Requires x-admin-token. |
{
"items": [],
"images": [],
"videos": [],
"documents": [],
"others": [],
"importantLinks": [],
"supportedTypes": ["image", "video", "document", "other"],
"maxUploadSizeBytes": 26214400,
"usingFallbackMedia": false,
"generatedAt": "2026-04-09T12:00:00.000Z"
}The backend upgrades connections on:
/ws
The server broadcasts channel-based messages with this shape:
{
"type": "telemetry",
"data": {},
"timestamp": 1710000000000
}Currently the frontend uses WebSocket updates for:
telemetryweatherdsntimeline
The server also sends initial state immediately after connection.
The admin console is intentionally separated from the public media center.
media.htmlshows only public media content- no admin button
- no visible login box
- no public link to the admin page
- Open any public page.
- Use the hidden shortcut:
- press
Ctrl + Alt + Shift + A - or type
artemisadminwhile not focused in an input
- press
- The browser is redirected to
admin.html. - The shortcut grants a short-lived unlock token in session storage.
- Only after that unlock is present does the login form appear.
- Log in using
ADMIN_API_KEY. - The backend returns a signed admin session token.
- Media posting uses
x-admin-tokenfor protected API requests.
- shortcut unlock lifetime: about 2 minutes
- admin session lifetime: about 8 hours
- sessions are stored in browser
sessionStorage - closing the browser tab clears the session storage state for that tab
This design keeps the admin UI out of the public browsing flow while still allowing an internal manual publishing workflow.
Important: the hidden shortcut is not the real security boundary. The actual protection is the backend session validation tied to ADMIN_API_KEY.
The media system supports both URL submissions and file uploads.
imagevideodocumentother
You can post:
- Google Drive image links
- Google Drive video links
- YouTube links
- direct video links such as
.mp4,.webm,.ogg,.mov,.m4v - direct document or file links
- backend-hosted
/public/...file URLs
Images:
.jpg.jpeg.png.gif.webp.svg.avif
Videos:
.mp4.webm.ogg.mov.m4v
Documents:
.pdf.doc.docx.txt.md.rtf.csv.json.xml.ppt.pptx.xls.xlsx
Other:
- any file that does not fit the image/video/document detection rules
- maximum upload size:
25 MB
{
"title": "Crew Photo",
"type": "image",
"category": "crew",
"description": "Official crew image from Google Drive.",
"url": "https://drive.google.com/file/d/FILE_ID/view"
}{
"title": "Press Kit PDF",
"type": "document",
"category": "press",
"description": "Launch press kit.",
"upload": {
"fileName": "press-kit.pdf",
"mimeType": "application/pdf",
"dataBase64": "<base64 data>"
}
}Uploaded files are written under:
backend/public/media/imagesbackend/public/media/videosbackend/public/media/documentsbackend/public/media/others
The backend serves these files from:
/public
So a saved file may be referenced like:
/public/media/documents/1710000000000-file.pdf
All scripts below run from backend/.
node scripts/seed_media.jsWhat it does:
- connects to MongoDB
- deletes all current media items
- inserts a starter set of items
node scripts/add_drive_media.js "Crew Photo" "image" "https://drive.google.com/file/d/FILE_ID/view" "Official crew image"Supported type values:
imagevideodocumentother
node scripts/move_media.jsThis script looks for old top-level images/ and videos/ folders and moves files into backend/public/media/....
For deployment, the backend needs:
- Node.js 22+
- access to MongoDB
- a valid
ADMIN_API_KEY - the frontend origin added to
ALLOWED_ORIGINS - persistent storage if you want uploaded media files to survive restarts
Important for uploads:
- if your host has ephemeral disk storage, uploaded files may disappear on redeploy/restart
- in that case, move media storage to persistent disk or object storage
The frontend can be deployed to any static host.
Before production deployment, verify:
- the backend URL in frontend JS points to your real API host
- the backend CORS config includes your frontend domain
/public/media/...files are reachable from the deployed backend
- Change
ADMIN_API_KEYimmediately for production. - Do not leave
ADMIN_API_KEY=change_me_in_production. - Treat the keyboard shortcut only as UI concealment, not true security.
- The hidden admin phrase
artemisadminis embedded infrontend/src/js/admin-access.js. Change it before public deployment if you continue using this approach. - The admin page includes
noindex,nofollow,noarchive, but that does not replace authentication. - Session tokens are HMAC-signed with
ADMIN_API_KEY, so rotating the admin key invalidates old sessions.
This repo is functional, but it still contains some older artifacts that are worth knowing about.
The frontend decides between:
http://localhost:3001https://artemis-tracker-mzav.onrender.com
There is no frontend environment-variable system in this repo right now.
frontend/src/js/app.js contains notification logic that tries to register /sw.js, but a matching sw.js file is not currently present in frontend/src.
That means notification-related behavior is incomplete until a service worker is added.
The active application startup connects to MongoDB through Mongoose.
Legacy PostgreSQL-related files still exist, including:
backend/src/db/pool.jsbackend/src/db/migrations/- the
pgdependency inbackend/package.json
These are currently not the primary runtime persistence path.
4. Hidden admin access is still a lightweight approach
The current admin solution is intentionally simple:
- shortcut unlock on the frontend
- password-based admin login
- signed session token
If you need stronger production security, consider moving to a full user/auth system with real roles, IP restrictions, audit logging, or SSO.
This is simple and works locally, but many cloud platforms use ephemeral filesystems.
For stronger production durability, use persistent volume storage or cloud object storage.
Check:
- backend is running on
localhost:3001 - frontend is being served from a real local server, not blocked by browser file-origin rules
- browser console for CORS or fetch errors
ALLOWED_ORIGINSincludes your frontend origin in production
Possible reasons:
- MongoDB is not connected
- media collection is empty
- database query failed and fallback media was used
Check:
- you used the shortcut on a public page first
- the redirect to
admin.htmlhappened in the same browser tab - session storage is enabled
- the 2-minute shortcut unlock did not expire
Check:
- backend is running
x-admin-tokensession is still valid- upload is under 25 MB
- uploaded file type matches the selected media type
- the backend process can write to
backend/public/media/...
Your hosting platform may be using ephemeral disk storage. Move uploads to persistent storage.
That is expected in the current design. News continues to be fetched automatically from timeline and RSS sources.
If you want to extend this project next, the most natural follow-up improvements are:
- replace the hidden shortcut with a proper auth system
- move uploaded media to cloud storage
- add frontend configuration for backend URL
- add tests for media posting and admin session behavior
- add a real service worker if browser notifications are required