Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ MAIL_DEFAULT_SENDER=no-reply@example.com
CELERY_BROKER_URL=redis://redis:6379/0
CELERY_RESULT_BACKEND=redis://redis:6379/0

# --- Frontend (NEXT_PUBLIC_* vars are baked into browser-side code) ---
# API base URL the frontend calls. In local dev, requests go through nginx on
# localhost, so the default below is what you want.
NEXT_PUBLIC_DEVELOP_BACKEND_URL=http://localhost
# MapTiler style URLs for the basemap layers (need a free MapTiler API key,
# e.g. https://api.maptiler.com/maps/streets-v2/style.json?key=YOUR_KEY).
# Optional: the app boots without them; the basemap is just blank.
NEXT_PUBLIC_DEVELOP_MAPTILE_STREET=
NEXT_PUBLIC_DEVELOP_MAPTILE_SATELITE=
NEXT_PUBLIC_DEVELOP_MAPTILE_DARK=

# --- Ports ---
DEVELOP_BACKEND_PORT=8000
DEVELOP_FRONTEND_PORT=3000
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ fmt: ## Apply ruff formatting in Docker
$(TEST_COMPOSE) run --rm --no-deps test sh -c "uv sync --frozen && uv run ruff format ."

seed: ## Seed a dev org + verified user + sample filing (stack must be up)
$(COMPOSE) exec backend python scripts/seed.py
$(COMPOSE) exec -e PYTHONPATH=/app backend python scripts/seed.py

grant-admin: ## Grant platform-admin to a user: make grant-admin email=you@example.com
$(COMPOSE) exec backend python scripts/grant_platform_admin.py $(email)
$(COMPOSE) exec -e PYTHONPATH=/app backend python scripts/grant_platform_admin.py $(email)

migrate: ## Apply DB migrations (alembic upgrade head)
$(COMPOSE) exec backend alembic upgrade head
Expand Down
21 changes: 17 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,11 @@ services:
volumes:
- ./back-end:/app
depends_on:
- redis

db:
condition: service_healthy
redis:
condition: service_started

backend:
image: ${DOCKER_IMAGE_BACKEND}
build:
Expand Down Expand Up @@ -86,14 +89,24 @@ services:
volumes:
- ./back-end:/app
depends_on:
- db
- redis
db:
condition: service_healthy
redis:
condition: service_started
ports:
- ${DEVELOP_BACKEND_PORT}:${DEVELOP_BACKEND_PORT}

frontend:
image: ${DOCKER_IMAGE_FRONTEND}
build: ./front-end
environment:
# API base URL the browser-side code calls; defaults to nginx on localhost.
- NEXT_PUBLIC_DEVELOP_BACKEND_URL=${NEXT_PUBLIC_DEVELOP_BACKEND_URL:-http://localhost}
# MapTiler basemap style URLs (optional — the app boots without them,
# the basemap is just blank until a key is configured).
- NEXT_PUBLIC_DEVELOP_MAPTILE_STREET=${NEXT_PUBLIC_DEVELOP_MAPTILE_STREET:-}
- NEXT_PUBLIC_DEVELOP_MAPTILE_SATELITE=${NEXT_PUBLIC_DEVELOP_MAPTILE_SATELITE:-}
- NEXT_PUBLIC_DEVELOP_MAPTILE_DARK=${NEXT_PUBLIC_DEVELOP_MAPTILE_DARK:-}
volumes:
- ./front-end:/app
depends_on:
Expand Down
45 changes: 45 additions & 0 deletions front-end/__tests__/settings-basemap-fallback.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* The basemap settings must fall back to a keyless OSM raster style when the
* MapTiler env vars are unset, so the map renders in dev without a key.
* With a key configured, the style URL passes through unchanged.
*/

const loadSettings = (env) => {
let mod;
jest.isolateModules(() => {
const prev = {};
for (const [k, v] of Object.entries(env)) {
prev[k] = process.env[k];
if (v === undefined) delete process.env[k];
else process.env[k] = v;
}
mod = require("../utils/settings");
for (const [k, v] of Object.entries(prev)) {
if (v === undefined) delete process.env[k];
else process.env[k] = v;
}
});
return mod;
};

describe("basemap fallback", () => {
it("falls back to an OSM raster style object when no key is set", () => {
const s = loadSettings({
NEXT_PUBLIC_DEVELOP_MAPTILE_STREET: "",
NEXT_PUBLIC_DEVELOP_MAPTILE_SATELITE: undefined,
NEXT_PUBLIC_DEVELOP_MAPTILE_DARK: undefined,
});
for (const style of [s.maptile_street, s.maptile_satelite, s.maptile_dark]) {
expect(typeof style).toBe("object");
expect(style.version).toBe(8);
expect(style.sources.osm.type).toBe("raster");
expect(style.sources.osm.tiles[0]).toContain("openstreetmap.org");
}
});

it("passes the configured style URL through unchanged", () => {
const url = "https://api.maptiler.com/maps/streets/style.json?key=abc";
const s = loadSettings({ NEXT_PUBLIC_DEVELOP_MAPTILE_STREET: url });
expect(s.maptile_street).toBe(url);
});
});
23 changes: 20 additions & 3 deletions front-end/utils/settings.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
// Keyless OSM raster basemap, used whenever a MapTiler style URL isn't
// configured — the map then renders without any API key (dev/demo). MapLibre
// accepts either a style URL string or a style object, so consumers don't care
// which one they get.
const osmFallbackStyle = {
version: 8,
sources: {
osm: {
type: "raster",
tiles: ["https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"],
tileSize: 256,
attribution: "© OpenStreetMap contributors",
},
},
layers: [{ id: "osm", type: "raster", source: "osm" }],
};

export const backend_url = process.env.NEXT_PUBLIC_DEVELOP_BACKEND_URL;
export const maptile_street = process.env.NEXT_PUBLIC_DEVELOP_MAPTILE_STREET;
export const maptile_satelite = process.env.NEXT_PUBLIC_DEVELOP_MAPTILE_SATELITE;
export const maptile_dark = process.env.NEXT_PUBLIC_DEVELOP_MAPTILE_DARK;
export const maptile_street = process.env.NEXT_PUBLIC_DEVELOP_MAPTILE_STREET || osmFallbackStyle;
export const maptile_satelite = process.env.NEXT_PUBLIC_DEVELOP_MAPTILE_SATELITE || osmFallbackStyle;
export const maptile_dark = process.env.NEXT_PUBLIC_DEVELOP_MAPTILE_DARK || osmFallbackStyle;
Loading