mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-07-30 23:47:27 +02:00
feat: proxy backend API through Next.js using runtime BACKEND_URL
Previously, NEXT_PUBLIC_API_URL was a build-time Next.js variable, making
it impossible to configure the backend URL in docker-compose `environment`
without rebuilding the image.
This change introduces a proper server-side proxy:
- next.config.ts: adds a rewrite rule that forwards all /api/* requests
to BACKEND_URL (read at server startup, not baked at build time).
Defaults to http://localhost:8000 so local dev works without config.
- api.ts: API_BASE is now an empty string — all fetch calls use relative
/api/... paths, which the Next.js server proxies to the backend.
- docker-compose.yml: replaces NEXT_PUBLIC_API_URL build arg with a
runtime BACKEND_URL env var defaulting to http://backend:8000, using
Docker's internal networking. Port 8000 no longer needs to be exposed.
- README: updates Docker setup docs, standalone compose example, and
environment variable reference to reflect BACKEND_URL.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Former-commit-id: b4c9e78cdd
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
3c16071fcd
commit
e2a9ef9bbf
@@ -219,36 +219,76 @@ cd Shadowbroker
|
|||||||
|
|
||||||
Open `http://localhost:3000` to view the dashboard.
|
Open `http://localhost:3000` to view the dashboard.
|
||||||
|
|
||||||
> **Deploying publicly or on a LAN?** The frontend **auto-detects** the
|
> **Deploying publicly or on a LAN?** No configuration needed for most setups.
|
||||||
> backend — it uses your browser's hostname with port `8000`
|
> The frontend proxies all API calls through the Next.js server to `BACKEND_URL`,
|
||||||
> (e.g. if you visit `http://192.168.1.50:3000`, API calls go to
|
> which defaults to `http://backend:8000` (Docker internal networking).
|
||||||
> `http://192.168.1.50:8000`). **No configuration needed** for most setups.
|
> Port 8000 does not need to be exposed externally.
|
||||||
>
|
>
|
||||||
> If your backend runs on a **different port or host** (reverse proxy,
|
> If your backend runs on a **different host or port**, set `BACKEND_URL` at runtime — no rebuild required:
|
||||||
> custom Docker port mapping, separate server), set `NEXT_PUBLIC_API_URL`:
|
|
||||||
>
|
>
|
||||||
> ```bash
|
> ```bash
|
||||||
> # Linux / macOS
|
> # Linux / macOS
|
||||||
> NEXT_PUBLIC_API_URL=http://myserver.com:9096 docker-compose up -d --build
|
> BACKEND_URL=http://myserver.com:9096 docker-compose up -d
|
||||||
>
|
>
|
||||||
> # Podman (via compose.sh wrapper)
|
> # Podman (via compose.sh wrapper)
|
||||||
> NEXT_PUBLIC_API_URL=http://192.168.1.50:9096 ./compose.sh up -d --build
|
> BACKEND_URL=http://192.168.1.50:9096 ./compose.sh up -d
|
||||||
>
|
>
|
||||||
> # Windows (PowerShell)
|
> # Windows (PowerShell)
|
||||||
> $env:NEXT_PUBLIC_API_URL="http://myserver.com:9096"; docker-compose up -d --build
|
> $env:BACKEND_URL="http://myserver.com:9096"; docker-compose up -d
|
||||||
>
|
>
|
||||||
> # Or add to a .env file next to docker-compose.yml:
|
> # Or add to a .env file next to docker-compose.yml:
|
||||||
> # NEXT_PUBLIC_API_URL=http://myserver.com:9096
|
> # BACKEND_URL=http://myserver.com:9096
|
||||||
> ```
|
> ```
|
||||||
>
|
|
||||||
> This is a **build-time** variable (Next.js limitation) — it gets baked into
|
|
||||||
> the frontend during `npm run build`. Changing it requires a rebuild.
|
|
||||||
|
|
||||||
If you prefer to call the container engine directly, Podman users can run `podman compose up -d`, or force the wrapper to use Podman with `./compose.sh --engine podman up -d`.
|
If you prefer to call the container engine directly, Podman users can run `podman compose up -d`, or force the wrapper to use Podman with `./compose.sh --engine podman up -d`.
|
||||||
Depending on your local Podman configuration, `podman compose` may still delegate to an external compose provider while talking to the Podman socket.
|
Depending on your local Podman configuration, `podman compose` may still delegate to an external compose provider while talking to the Podman socket.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### 🐋 Standalone Deploy (Portainer, Uncloud, NAS, etc.)
|
||||||
|
|
||||||
|
No need to clone the repo. Use the pre-built images published to the GitHub Container Registry.
|
||||||
|
|
||||||
|
Create a `docker-compose.yml` with the following content and deploy it directly — paste it into Portainer's stack editor, `uncloud deploy`, or any Docker host:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
backend:
|
||||||
|
image: ghcr.io/bigbodycobain/shadowbroker-backend:latest
|
||||||
|
container_name: shadowbroker-backend
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
environment:
|
||||||
|
- AIS_API_KEY=your_aisstream_key # Required — get one free at aisstream.io
|
||||||
|
- OPENSKY_CLIENT_ID= # Optional — higher flight data rate limits
|
||||||
|
- OPENSKY_CLIENT_SECRET= # Optional — paired with Client ID above
|
||||||
|
- LTA_ACCOUNT_KEY= # Optional — Singapore CCTV cameras
|
||||||
|
- CORS_ORIGINS= # Optional — comma-separated allowed origins
|
||||||
|
volumes:
|
||||||
|
- backend_data:/app/data
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
image: ghcr.io/bigbodycobain/shadowbroker-frontend:latest
|
||||||
|
container_name: shadowbroker-frontend
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
- BACKEND_URL=http://backend:8000 # Docker internal networking — no rebuild needed
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
backend_data:
|
||||||
|
```
|
||||||
|
|
||||||
|
> **How it works:** The frontend container proxies all `/api/*` requests through the Next.js server to `BACKEND_URL` using Docker's internal networking. The browser only ever talks to port 3000 — port 8000 does not need to be exposed externally.
|
||||||
|
>
|
||||||
|
> `BACKEND_URL` is a plain runtime environment variable (not a build-time `NEXT_PUBLIC_*`), so you can change it in Portainer, Uncloud, or any compose editor without rebuilding the image. Set it to the address where your backend is reachable from inside the Docker network (e.g. `http://backend:8000`, `http://192.168.1.50:8000`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### 📦 Quick Start (No Code Required)
|
### 📦 Quick Start (No Code Required)
|
||||||
|
|
||||||
If you just want to run the dashboard without dealing with terminal commands:
|
If you just want to run the dashboard without dealing with terminal commands:
|
||||||
@@ -420,16 +460,13 @@ OPENSKY_CLIENT_SECRET=your_opensky_secret # OAuth2 — paired with Client ID
|
|||||||
LTA_ACCOUNT_KEY=your_lta_key # Singapore CCTV cameras
|
LTA_ACCOUNT_KEY=your_lta_key # Singapore CCTV cameras
|
||||||
```
|
```
|
||||||
|
|
||||||
### Frontend (optional)
|
### Frontend
|
||||||
|
|
||||||
| Variable | Where to set | Purpose |
|
| Variable | Where to set | Purpose |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `NEXT_PUBLIC_API_URL` | `.env` next to `docker-compose.yml`, or shell env | Override backend URL when deploying publicly or behind a reverse proxy. Leave unset for auto-detection. |
|
| `BACKEND_URL` | `environment` in `docker-compose.yml`, or shell env | URL the Next.js server uses to proxy API calls to the backend. Defaults to `http://backend:8000`. **Runtime variable — no rebuild needed.** |
|
||||||
|
|
||||||
**How auto-detection works:** When `NEXT_PUBLIC_API_URL` is not set, the frontend
|
**How it works:** The frontend proxies all `/api/*` requests through the Next.js server to `BACKEND_URL` using Docker's internal networking. Browsers only talk to port 3000; port 8000 never needs to be exposed externally. For local dev without Docker, `BACKEND_URL` defaults to `http://localhost:8000`.
|
||||||
reads `window.location.hostname` in the browser and calls `{protocol}//{hostname}:8000`.
|
|
||||||
This means the dashboard works on `localhost`, LAN IPs, and public domains without
|
|
||||||
any configuration — as long as the backend is reachable on port 8000 of the same host.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -21,13 +21,13 @@ services:
|
|||||||
frontend:
|
frontend:
|
||||||
build:
|
build:
|
||||||
context: ./frontend
|
context: ./frontend
|
||||||
args:
|
|
||||||
# Optional: set this to your backend's external URL if using custom ports
|
|
||||||
# e.g. http://192.168.1.50:9096 — leave empty to auto-detect from browser
|
|
||||||
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-}
|
|
||||||
container_name: shadowbroker-frontend
|
container_name: shadowbroker-frontend
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
# Points the Next.js server-side proxy at the backend container via Docker networking.
|
||||||
|
# Change this if your backend runs on a different host or port.
|
||||||
|
- BACKEND_URL=${BACKEND_URL:-http://backend:8000}
|
||||||
depends_on:
|
depends_on:
|
||||||
- backend
|
- backend
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import type { NextConfig } from "next";
|
import type { NextConfig } from "next";
|
||||||
|
|
||||||
|
// BACKEND_URL is a plain (non-NEXT_PUBLIC_) env var read at server startup —
|
||||||
|
// not baked at build time — so it can be set in docker-compose `environment`.
|
||||||
|
// Defaults to localhost for local dev where both services run on the same host.
|
||||||
|
const backendUrl = process.env.BACKEND_URL ?? "http://localhost:8000";
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
transpilePackages: ['react-map-gl', 'mapbox-gl', 'maplibre-gl'],
|
transpilePackages: ['react-map-gl', 'mapbox-gl', 'maplibre-gl'],
|
||||||
output: "standalone",
|
output: "standalone",
|
||||||
@@ -9,6 +14,14 @@ const nextConfig: NextConfig = {
|
|||||||
eslint: {
|
eslint: {
|
||||||
ignoreDuringBuilds: true,
|
ignoreDuringBuilds: true,
|
||||||
},
|
},
|
||||||
|
async rewrites() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
source: "/api/:path*",
|
||||||
|
destination: `${backendUrl}/api/:path*`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
|||||||
@@ -592,7 +592,7 @@ export default function Dashboard() {
|
|||||||
{backendStatus === 'disconnected' && (
|
{backendStatus === 'disconnected' && (
|
||||||
<div className="absolute top-0 left-0 right-0 z-[9000] flex items-center justify-center py-2 bg-red-950/90 border-b border-red-500/40 backdrop-blur-sm">
|
<div className="absolute top-0 left-0 right-0 z-[9000] flex items-center justify-center py-2 bg-red-950/90 border-b border-red-500/40 backdrop-blur-sm">
|
||||||
<span className="text-[10px] font-mono tracking-widest text-red-400">
|
<span className="text-[10px] font-mono tracking-widest text-red-400">
|
||||||
BACKEND OFFLINE — Cannot reach {API_BASE}. Start the backend server or check your connection.
|
BACKEND OFFLINE — Cannot reach backend server. Check that the backend container is running and BACKEND_URL is correct.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
+7
-28
@@ -1,28 +1,7 @@
|
|||||||
// NEXT_PUBLIC_* vars are baked at build time in Next.js, so setting them
|
// All API calls use relative paths (e.g. /api/flights).
|
||||||
// in docker-compose `environment` has no effect at runtime. Instead we
|
// Next.js rewrites them at the server level to BACKEND_URL (set in docker-compose
|
||||||
// auto-detect: use the browser's current hostname with a configurable port
|
// or .env.local for dev). This means:
|
||||||
// so the dashboard works on localhost, LAN IPs, and custom Docker port maps
|
// - No build-time baking of the backend URL into the client bundle
|
||||||
// without any code changes.
|
// - BACKEND_URL=http://backend:8000 works via Docker internal networking
|
||||||
//
|
// - Only port 3000 needs to be exposed externally
|
||||||
// Override order:
|
export const API_BASE = "";
|
||||||
// 1. Build-time NEXT_PUBLIC_API_URL (for advanced users who rebuild the image)
|
|
||||||
// 2. Runtime auto-detect from window.location.hostname + port 8000
|
|
||||||
|
|
||||||
function resolveApiBase(): string {
|
|
||||||
// Build-time override (works when image is rebuilt with the env var)
|
|
||||||
if (process.env.NEXT_PUBLIC_API_URL) {
|
|
||||||
return process.env.NEXT_PUBLIC_API_URL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server-side rendering: fall back to localhost
|
|
||||||
if (typeof window === "undefined") {
|
|
||||||
return "http://localhost:8000";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Client-side: use the same hostname the user is browsing on
|
|
||||||
const proto = window.location.protocol;
|
|
||||||
const host = window.location.hostname;
|
|
||||||
return `${proto}//${host}:8000`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const API_BASE = resolveApiBase();
|
|
||||||
|
|||||||
Reference in New Issue
Block a user