mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-09-23 17:30:46 +02:00
Add runtime CARTO_API_KEY for basemap tiles
CARTO now requires an API key for its basemap tiles; without one every tile in the DEFAULT dark/light map carries an "API KEY REQUIRED" watermark. The tile URLs were hardcoded in mapStyles.ts with no way to supply a key, and because the frontend ships as a prebuilt image a NEXT_PUBLIC_ variable would be baked in empty for every Docker user. - New frontend-local route GET /api/basemap-config reads CARTO_API_KEY from the frontend container's environment at request time (same pattern as BACKEND_URL), so no image rebuild is needed. - useBasemapConfig() fetches it once per page load; MaplibreViewer builds the MapLibre style from it via buildBasemapStyle(theme, key) and defers the map's first style load until the config has settled, avoiding a burst of unkeyed tile requests followed by a style swap. - Tile URLs move to CARTO's documented rastertiles/ path with ?key= appended when configured. Unkeyed URLs serve byte-identical tiles to the old path, so deployments without a key behave exactly as before. - CARTO_API_KEY wired through docker-compose.yml and documented in .env.example, README (data source table + frontend env table) and docs/OUTBOUND_DATA.md. - Tests cover the route (unset / set / trimmed) and the style builder. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
a5fb1c392e
commit
8f169f1ecc
@@ -1,41 +1,54 @@
|
||||
export const darkStyle = {
|
||||
version: 8,
|
||||
glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf',
|
||||
sources: {
|
||||
'carto-dark': {
|
||||
type: 'raster',
|
||||
tiles: [
|
||||
'https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}@2x.png',
|
||||
'https://b.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}@2x.png',
|
||||
'https://c.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}@2x.png',
|
||||
'https://d.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}@2x.png',
|
||||
],
|
||||
tileSize: 256,
|
||||
},
|
||||
},
|
||||
layers: [
|
||||
{ id: 'carto-dark-layer', type: 'raster', source: 'carto-dark', minzoom: 0, maxzoom: 22 },
|
||||
{ id: 'imagery-ceiling', type: 'background', paint: { 'background-opacity': 0 } },
|
||||
],
|
||||
};
|
||||
/**
|
||||
* MapLibre basemap styles backed by CARTO raster tiles.
|
||||
*
|
||||
* CARTO now requires an API key for its basemap tiles; unkeyed requests
|
||||
* return tiles stamped with an "API KEY REQUIRED" watermark. The key is
|
||||
* read at request time by /api/basemap-config (CARTO_API_KEY on the
|
||||
* frontend container) and threaded in here via `buildBasemapStyle`, so
|
||||
* prebuilt Docker images pick it up without a rebuild.
|
||||
*
|
||||
* With no key configured the styles are unchanged from before, so existing
|
||||
* deployments keep working exactly as they did (watermark included).
|
||||
*/
|
||||
|
||||
export const lightStyle = {
|
||||
version: 8,
|
||||
glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf',
|
||||
sources: {
|
||||
'carto-light': {
|
||||
type: 'raster',
|
||||
tiles: [
|
||||
'https://a.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png',
|
||||
'https://b.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png',
|
||||
'https://c.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png',
|
||||
'https://d.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png',
|
||||
],
|
||||
tileSize: 256,
|
||||
},
|
||||
},
|
||||
layers: [
|
||||
{ id: 'carto-light-layer', type: 'raster', source: 'carto-light', minzoom: 0, maxzoom: 22 },
|
||||
{ id: 'imagery-ceiling', type: 'background', paint: { 'background-opacity': 0 } },
|
||||
],
|
||||
export type BasemapTheme = 'dark' | 'light';
|
||||
|
||||
const CARTO_SUBDOMAINS = ['a', 'b', 'c', 'd'] as const;
|
||||
const CARTO_RASTER_STYLE: Record<BasemapTheme, string> = {
|
||||
dark: 'dark_all',
|
||||
light: 'light_all',
|
||||
};
|
||||
const GLYPHS_URL = 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf';
|
||||
|
||||
/** Tile URL templates for a CARTO raster style, keyed when a key is supplied. */
|
||||
export function cartoTileUrls(theme: BasemapTheme, cartoApiKey?: string | null): string[] {
|
||||
const style = CARTO_RASTER_STYLE[theme];
|
||||
const key = (cartoApiKey || '').trim();
|
||||
const query = key ? `?key=${encodeURIComponent(key)}` : '';
|
||||
return CARTO_SUBDOMAINS.map(
|
||||
(s) => `https://${s}.basemaps.cartocdn.com/rastertiles/${style}/{z}/{x}/{y}@2x.png${query}`,
|
||||
);
|
||||
}
|
||||
|
||||
export function buildBasemapStyle(theme: BasemapTheme, cartoApiKey?: string | null) {
|
||||
const sourceId = `carto-${theme}`;
|
||||
return {
|
||||
version: 8,
|
||||
glyphs: GLYPHS_URL,
|
||||
sources: {
|
||||
[sourceId]: {
|
||||
type: 'raster',
|
||||
tiles: cartoTileUrls(theme, cartoApiKey),
|
||||
tileSize: 256,
|
||||
},
|
||||
},
|
||||
layers: [
|
||||
{ id: `${sourceId}-layer`, type: 'raster', source: sourceId, minzoom: 0, maxzoom: 22 },
|
||||
{ id: 'imagery-ceiling', type: 'background', paint: { 'background-opacity': 0 } },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Key-less defaults, kept for callers that do not need a CARTO key.
|
||||
export const darkStyle = buildBasemapStyle('dark');
|
||||
export const lightStyle = buildBasemapStyle('light');
|
||||
|
||||
Reference in New Issue
Block a user