From f82646e14a9d00ebd9c735ff95e97054c5b92f05 Mon Sep 17 00:00:00 2001 From: jubnl Date: Sat, 16 May 2026 00:09:31 +0200 Subject: [PATCH] fix(maps): send Referer header on Google API calls when APP_URL is set Supports HTTP referrer restrictions on GCP API keys. Documents the restriction types and photo troubleshooting steps in the wiki. --- server/src/services/mapsService.ts | 7 +++++- wiki/Places-and-Search.md | 2 ++ wiki/Troubleshooting.md | 39 ++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/server/src/services/mapsService.ts b/server/src/services/mapsService.ts index 00151fe2..c336135c 100644 --- a/server/src/services/mapsService.ts +++ b/server/src/services/mapsService.ts @@ -1,6 +1,7 @@ import { db } from '../db/database'; import { decrypt_api_key } from './apiKeyCrypto'; import { checkSsrf } from '../utils/ssrfGuard'; +import { getAppUrl } from './notifications'; // ── Google API call counter ─────────────────────────────────────────────────── @@ -12,7 +13,11 @@ export function resetGoogleApiCallCount(): void { googleApiCallCount = 0; } function googleFetch(endpoint: string, label: string, init?: RequestInit): Promise { googleApiCallCount++; console.debug(`[Google API] #${googleApiCallCount} ${label} → ${endpoint}`); - return fetch(endpoint, init); + const referer = process.env.APP_URL ? getAppUrl() : undefined; + return fetch(endpoint, { + ...init, + headers: { ...(referer ? { Referer: referer } : {}), ...(init?.headers as Record ?? {}) }, + }); } // ── Interfaces ─────────────────────────────────────────────────────────────── diff --git a/wiki/Places-and-Search.md b/wiki/Places-and-Search.md index efc49453..8a336294 100644 --- a/wiki/Places-and-Search.md +++ b/wiki/Places-and-Search.md @@ -21,6 +21,8 @@ Type in the search box at the top of the form. After 2 or more characters, with When a key is present, the autocomplete uses the Google Places API, which can return ratings, opening hours, photos, and phone numbers from Google's database. +> **API key restrictions:** TREK calls the Google Places API from the server, not the browser. If you apply **HTTP referrers** restrictions to your key in Google Cloud Console, you must also set `APP_URL` in your environment — TREK sends it as the `Referer` header on every outbound Google API request. Without it, Google will reject all server-side calls with `REQUEST_DENIED`. For server-side deployments, **IP address** restrictions are simpler and require no extra configuration. See [Troubleshooting](Troubleshooting) if photos are missing after adding a key. + ### Without a Google Maps API key TREK falls back to OpenStreetMap (Nominatim) automatically — no API key needed. A notice appears above the search box explaining that OpenStreetMap is in use and that photos, ratings, and opening hours are unavailable. Results include name, address, and coordinates. diff --git a/wiki/Troubleshooting.md b/wiki/Troubleshooting.md index 7b7a6d03..dae924c8 100644 --- a/wiki/Troubleshooting.md +++ b/wiki/Troubleshooting.md @@ -223,6 +223,45 @@ If `ALLOWED_ORIGINS` is not set, TREK allows all origins (development default). --- +## Place photos not loading / place thumbnail shows default map pin (Google Maps API key configured) + +**Cause:** When a Google Maps API key is set, TREK fetches photo references and image bytes from the Google Places API on the server side. If the server-side call is rejected or returns no photos, the `/place-photo/:id` endpoint returns 404 and the place falls back to the default map-pin thumbnail. The most common causes are: + +1. **HTTP referrer restriction on the API key.** Google Cloud Console lets you restrict a key to specific HTTP referrers. Because TREK calls Google from the server (not the browser), it sends a `Referer` header derived from `APP_URL`. If `APP_URL` is not set, the fallback is `http://localhost:`, which will not match any domain whitelist in GCP. + +2. **Wrong key restriction type.** API keys restricted by **HTTP referrers** are designed for browser-side JavaScript. For a self-hosted server application, use **IP address** restrictions instead — add the public IP of your TREK server and no `APP_URL` configuration is needed. + +3. **Places API (New) not enabled.** The key must have **Places API (New)** enabled in Google Cloud Console under APIs & Services → Enabled APIs. Enabling only the legacy Places API is not sufficient. + +4. **Billing not set up.** Google requires a billing account to be linked to the project even within the free tier. Without it, photo and details requests return `REQUEST_DENIED`. + +**Fix for HTTP referrer restriction:** + +Set `APP_URL` to the public URL of your instance and add that URL (or its domain with a wildcard, e.g. `https://trek.example.com/*`) to the allowed referrers in GCP: + +```yaml +environment: + - APP_URL=https://trek.example.com +``` + +**Fix for wrong restriction type:** + +Switch the key's "Application restrictions" from **HTTP referrers** to **IP addresses** in Google Cloud Console, and add your server's public IP. No `APP_URL` change needed. + +**Verifying the issue:** + +Run the following curl command using your key to check whether Google returns photo references: + +```bash +curl "https://places.googleapis.com/v1/places/" \ + -H "X-Goog-Api-Key: YOUR_API_KEY" \ + -H "X-Goog-FieldMask: photos" +``` + +If the response is `{}` or `{"error": {...}}`, the key or its restrictions are blocking the request. If it returns a `photos` array, the key is valid and the issue is elsewhere. + +--- + ## MCP OAuth flow does not initiate / "Connect" redirects but authentication never starts **Cause:** TREK builds the OAuth 2.1 redirect URI from `APP_URL`. If `APP_URL` is not set, the authorization URL is constructed from a localhost fallback that external clients (Claude.ai, Claude Desktop) cannot reach, so the OAuth handshake never completes.