feat: add a Docker install path

Vite's dev server is the whole app here — it serves the client and runs every
API proxy as middleware — so one container running `npm run dev` covers it, and
HOST/PORT are already env-driven.

Three details are deliberate:

- The port publishes to 127.0.0.1. Docker's usual "4173:4173" binds every host
  interface, which would silently make the README's LAN warning the default for
  a server that brokers configured provider keys.
- The repo is bind-mounted, so keys saved from Provider Settings, .gev-cache
  and .gev-logs persist on the host exactly as they do under `npm run dev`.
  node_modules stays in the image so a host install cannot shadow the
  Linux-built native deps.
- The container runs as the host user, since everything it writes lands in
  that bind mount. Vite's dep optimizer still needs to write inside the image's
  node_modules, hence the two loosened directories.

Puppeteer's Chromium download is skipped: it is only needed by `npm run
test:track`, which the README points at the host.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
SM-26 2026-09-09 08:43:36 +02:00
parent 759652207f
commit debd5b8631
No known key found for this signature in database
5 changed files with 87 additions and 0 deletions

6
.dockerignore Normal file
View File

@ -0,0 +1,6 @@
node_modules
.git
.env
.gev-cache
.gev-logs
dist

View File

@ -5,6 +5,14 @@ of current runtime behavior, see [`docs/CURRENT-STATE.md`](docs/CURRENT-STATE.md
## [Unreleased]
### Added
- **Docker install path.** `docker compose up` builds the pinned Node image and
serves the app on `http://localhost:4173` with no local Node install. The repo
is bind-mounted and the container runs as the host user, so keys saved from
Provider Settings, `.gev-cache`, and `.gev-logs` persist on the host exactly
as they do under `npm run dev`.
### Fixed
- Mapped-site outages show their scheduled retry countdown and distinguish

28
Dockerfile Normal file
View File

@ -0,0 +1,28 @@
# syntax=docker/dockerfile:1
#
# The dev server is the whole app: Vite serves the client AND runs the API
# proxies (ADS-B, AIS, Overpass, OpenAI Realtime, ...) as middleware, so there
# is nothing else to containerise. See compose.yaml for the usual entry point.
FROM node:24-slim
# Puppeteer is only used by the QA scripts; skip its ~150MB Chromium download.
ENV PUPPETEER_SKIP_DOWNLOAD=1
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# compose runs the container as the host user so bind-mounted files stay
# yours, but node_modules is baked in as root. Vite only needs to write its
# dep-optimizer cache, so open up those two directories rather than chowning
# a few hundred MB to a UID we cannot know at build time.
RUN mkdir -p node_modules/.vite && chmod 777 node_modules node_modules/.vite
COPY . .
# Bind to all interfaces, otherwise the published port hits nothing. Vite also
# relaxes allowedHosts when HOST is 0.0.0.0, which is what lets the browser
# reach it as "localhost".
ENV HOST=0.0.0.0
ENV PORT=4173
EXPOSE 4173
CMD ["npm", "run", "dev"]

View File

@ -133,6 +133,30 @@ See [docs/PERFORMANCE.md](docs/PERFORMANCE.md).
**macOS shortcut:** `./scripts/dev-fresh.sh` clears the Vite cache and pulls any
configured keys straight from the Keychain. It starts keyless too.
### Path 3 — Docker
No Node install needed; the container publishes the same dev server.
```bash
git clone https://github.com/bilawalsidhu/gods-eye-view.git
cd gods-eye-view
docker compose up
```
Open **`http://localhost:4173`** as above. The repo is bind-mounted, so keys you
paste into Provider Settings persist in your host `.env` and source edits still
hot-reload. The container binds `0.0.0.0` *inside* the container, but the port
is published to `127.0.0.1` only, so the default matches the native one: nobody
else can reach your server. Widening that mapping puts you under
[Sharing an instance](#-sharing-an-instance) — keys and all.
`node_modules` deliberately stays inside the image, so it survives in an
anonymous volume across restarts: after a `git pull` that changes dependencies,
recreate it with `docker compose down -v && docker compose up --build`. The
image also skips Puppeteer's Chromium download, which keeps it small but leaves
`npm run test:track` to the host — `npm run build` and `npm test` run fine in
the container.
### Then power it up — in the app, not in a file
Keys are upgrades, not prerequisites. When you want one, click the **POWER UP**

21
compose.yaml Normal file
View File

@ -0,0 +1,21 @@
services:
gods-eye-view:
build: .
# Everything the app writes (.env with your keys, .gev-cache, .gev-logs)
# lands in the bind-mounted repo, so run as you, not root. UID/GID are
# unset on Docker Desktop, where the 1000 fallback is ignored anyway.
user: "${UID:-1000}:${GID:-1000}"
ports:
# Publish to loopback only. Docker's default ("4173:4173") would bind
# every host interface, which quietly turns the LAN warning in the README
# into the default: this server brokers whatever provider keys you have
# configured to anyone who can reach it. Widen this deliberately, not by
# accident.
- "127.0.0.1:4173:4173"
volumes:
# Bind the source so edits hot-reload and so keys pasted into the setup
# UI land in the .env on your host instead of vanishing with the
# container. node_modules stays in the image: sharp and friends are
# compiled for Linux and must not be shadowed by a host install.
- .:/app
- /app/node_modules