Compare commits

..

4 Commits

Author SHA1 Message Date
github-actions[bot] bfe6664ac4 chore: bump version to 3.0.21 [skip ci] 2026-05-15 22:53:13 +00:00
Julien G. 117942f45e v3.0.21 Bug Fixes (#998)
* fix(journey): remove photo upload count limit and surface upload errors (#997)

Removes the arbitrary 10-file cap on journey entry photo uploads and 20-file
cap on gallery uploads. MulterErrors now return proper 4xx responses instead
of 500, and the client surfaces the server error message via toast rather than
silently trapping the user in the post editor overlay.

* fix(planner): remove correct assignment when place assigned to same day multiple times

When a place was assigned to the same day more than once, the "Remove from day"
button in PlaceInspector always deleted the first assignment (Array.find on
place.id) instead of the currently selected one. Now prefers selectedAssignmentId
when available.

Fixes #1005

* fix(map): enable 3D terrain for Mapbox outdoors style in trip planner

wantsTerrain() only matched satellite styles, so the outdoors-v12 style
was flat in the planner despite showing correct 3D terrain in the settings
preview. Added outdoors-v12 to the allowlist; marker drift is already
handled by syncMarkerAltitudes().

Fixes #1002

* 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.
2026-05-16 00:53:02 +02:00
Julien G. e7211325df Add asset.download permission to Photo Providers 2026-05-15 23:16:34 +02:00
github-actions[bot] 7e49f3467c chore: bump version to 3.0.20 [skip ci] 2026-05-13 08:35:23 +00:00
11 changed files with 67 additions and 15 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
apiVersion: v2
name: trek
version: 3.0.19
version: 3.0.21
description: Minimal Helm chart for TREK app
appVersion: "3.0.19"
appVersion: "3.0.21"
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "trek-client",
"version": "3.0.19",
"version": "3.0.21",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "trek-client",
"version": "3.0.19",
"version": "3.0.21",
"dependencies": {
"@react-pdf/renderer": "^4.3.2",
"axios": "^1.6.7",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "trek-client",
"version": "3.0.19",
"version": "3.0.21",
"private": true,
"type": "module",
"scripts": {
+6 -4
View File
@@ -8,13 +8,15 @@ export function isStandardFamily(style: string): boolean {
return style === 'mapbox://styles/mapbox/standard' || style === 'mapbox://styles/mapbox/standard-satellite'
}
// Terrain is only genuinely useful for the satellite imagery styles — on
// clean flat styles like streets/light/dark it nudges route lines onto
// the DEM while our HTML markers stay at Z=0, which causes the visible
// offset when the map is pitched. Restrict terrain to satellite.
// Terrain is only genuinely useful for styles that benefit from elevation
// data. On flat vector styles (streets/light/dark) it nudges route lines
// onto the DEM while HTML markers stay at Z=0, causing a visible drift
// when the map is pitched. Satellite and Outdoors are the intended styles
// for terrain; markers are re-pinned by syncMarkerAltitudes().
export function wantsTerrain(style: string): boolean {
return style === 'mapbox://styles/mapbox/satellite-v9'
|| style === 'mapbox://styles/mapbox/satellite-streets-v12'
|| style === 'mapbox://styles/mapbox/outdoors-v12'
}
// 3D can be added to every style now — the standard family has it built-in
@@ -169,7 +169,10 @@ export default function PlaceInspector({
const category = categories?.find(c => c.id === place.category_id)
const dayAssignments = selectedDayId ? (assignments[String(selectedDayId)] || []) : []
const assignmentInDay = selectedDayId ? dayAssignments.find(a => a.place?.id === place.id) : null
const assignmentInDay = selectedDayId
? ((selectedAssignmentId ? dayAssignments.find(a => a.id === selectedAssignmentId) : null)
?? dayAssignments.find(a => a.place?.id === place.id))
: null
const openingHours = googleDetails?.opening_hours || null
const openNow = googleDetails?.open_now ?? null
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "trek-server",
"version": "3.0.19",
"version": "3.0.21",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "trek-server",
"version": "3.0.19",
"version": "3.0.21",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.28.0",
"archiver": "^6.0.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "trek-server",
"version": "3.0.19",
"version": "3.0.21",
"main": "src/index.ts",
"scripts": {
"start": "node --import tsx src/index.ts",
+6 -1
View File
@@ -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<Response> {
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<string, string> ?? {}) },
});
}
// ── Interfaces ───────────────────────────────────────────────────────────────
+2 -1
View File
@@ -44,6 +44,7 @@ When generating the API key in Immich (**Account Settings → API Keys**), grant
| `asset.read` | Read photo metadata and search results |
| `asset.view` | Load thumbnails and preview images |
| `album.read` | List owned + shared albums and their contents |
| `asset.download` | Download the assets |
| `asset.upload` | *Only if you enable "Mirror journey photos to Immich on upload"* — push TREK uploads back to your library |
TREK never modifies or deletes anything in Immich, so no `update`, `delete`, or admin scopes are needed.
@@ -94,4 +95,4 @@ Once a provider is connected, you can browse and attach photos to your trips. Se
## See also
- [Admin-Addons](Admin-Addons)
- [Internal-Network-Access](Internal-Network-Access)
- [Internal-Network-Access](Internal-Network-Access)
+2
View File
@@ -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.
+39
View File
@@ -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:<PORT>`, 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/<PLACE_ID>" \
-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.