How the Journey Travel Journal Addon Integrates with Photo Providers in TREK

The Journey addon connects to external photo providers like Immich and Synology Photos through a secure three-layer architecture that encrypts credentials at rest, validates requests against the journeyProviderPhotosRequestSchema, and streams images through TREK's proxy endpoint to hide provider URLs from clients.

The TREK platform includes a Journey travel journal addon that allows users to embed photos from external libraries directly into their travel entries. This integration supports popular self-hosted solutions while maintaining strict security through encrypted credential storage and a proxy-based request flow. According to the mauriceboe/TREK source code, the connection flows through admin configuration, user authentication, and a validated API contract that ensures seamless yet secure photo access.

Add-on Configuration and Provider Setup

The integration begins with administrative activation followed by individual user configuration. This two-step process ensures that photo providers are only available when explicitly enabled and that credentials remain encrypted throughout the lifecycle.

Admin-Level Activation

An administrator enables the Journey addon and toggles specific photo providers under Admin → Add-ons → Journey. When Immich or Synology Photos are activated, TREK exposes a Photo Providers section in each user’s Settings → Integrations panel. This toggle system ensures that backend services only attempt connections to providers that have been explicitly approved for the instance.

User Credential Management

Each user stores provider connection data—including URL endpoints, API keys, and passwords—directly in TREK’s database. The system encrypts these credentials at rest and never transmits them back to the browser. For Immich, the API key is encrypted, while Synology Photos credentials include encrypted DSM passwords. This approach ensures that even if the client is compromised, provider authentication tokens remain inaccessible.

API Contract and Request Validation

The frontend communicates with photo providers through a strictly validated schema that defines the shape of every photo request.

The JourneyProviderPhotosRequestSchema

All photo attachment requests must conform to the journeyProviderPhotosRequestSchema defined in shared/src/journey/journey.schema.ts. This Zod schema validates the payload before any provider interaction occurs, ensuring that only properly formatted requests reach the service layer.

A typical validated payload includes:

{
  "provider": "immich",
  "asset_id": "a1b2c3d4",
  "caption": "Sunset at the beach"
}

Endpoint Structure

The client sends photo requests to /api/journeys/{journeyId}/photos, where the controller validates the body against journeyProviderPhotosRequestSchema. After validation, the request passes to journeyService, which orchestrates the provider communication and returns a proxy URL rather than the direct provider link.

Provider Communication and Data Flow

Once validated, the backend service layer handles the specific protocols required by each photo provider, abstracting the differences behind a unified interface.

Immich Integration

For Immich providers, the service uses the stored API key to authenticate against the Immich server. It calls the timeline.read, asset.read, and asset.view endpoints to retrieve thumbnail and full-size URLs. The service then streams this content through TREK’s proxy, ensuring the real Immich host remains hidden from the client.

Synology Photos Integration

When connecting to Synology Photos, the service first authenticates with the stored DSM credentials to obtain a session token. It then queries the Synology Photos REST API using this token. Like Immich requests, the resulting image data flows through TREK’s proxy endpoint rather than returning direct Synology URLs to the browser.

The Proxy Layer

TREK implements a secure proxy at /api/public/journey/photo-proxy that streams images from external providers. The service generates temporary URLs like https://trek.example.com/api/public/journey/photo-proxy?provider=immich&asset_id=a1b2c3d4, which the client loads directly. This architecture prevents exposure of internal network addresses and provider-specific endpoints to end users.

Code Implementation Examples

The following examples demonstrate the frontend request construction, backend validation, and proxy streaming implementation.

Frontend Photo Attachment

React components use TanStack Query to send validated payloads to the Journey API:

// Example in a React component
import { useMutation } from '@tanstack/react-query';
import axios from 'axios';

type AddPhotoPayload = {
  provider: string;          // "immich" | "synologyphotos"
  asset_id?: string;         // single asset
  asset_ids?: (string | number)[]; // multiple assets
  caption?: string;
};

const addPhoto = async (journeyId: string, payload: AddPhotoPayload) => {
  await axios.post(
    `/api/journeys/${journeyId}/photos`,
    payload
  );
};

// Usage
addPhoto('12345', {
  provider: 'immich',
  asset_id: 'a1b2c3d4',
  caption: 'Sunrise over the Alps',
});

Backend Validation

The controller in src/journey/journey.controller.ts validates incoming requests before processing:

// In src/journey/journey.controller.ts
import { journeyProviderPhotosRequestSchema } from '../../shared/src/journey/journey.schema';

export const addJourneyPhoto = async (req, res) => {
  const parseResult = journeyProviderPhotosRequestSchema.safeParse(req.body);
  if (!parseResult.success) {
    return res.status(400).json({ error: 'Invalid payload' });
  }
  const validated = parseResult.data;
  const result = await journeyService.attachPhoto(req.params.journeyId, validated, req.user.id);
  return res.json(result); // contains proxy URL
};

Proxy Streaming

The photo proxy controller handles the actual streaming from external providers:

// In src/journey/photo-proxy.controller.ts
export const photoProxy = async (req, res) => {
  const { provider, asset_id } = req.query as { provider: string; asset_id: string };
  const stream = await photoProviderService.getAssetStream(provider, asset_id);
  stream.pipe(res);
};

Optional Photo Mirroring

When administrators enable the “Mirror journey photos to Immich on upload” option, TREK synchronizes uploaded images bidirectionally. After a successful local upload, the system issues an asset.upload call to the Immich instance, maintaining parity between the Journey journal and the external photo library.

Key Files and Resources

Summary

  • The Journey addon integrates with Immich and Synology Photos through a three-layer architecture: admin configuration, encrypted user credentials, and validated API contracts.
  • All photo requests must validate against journeyProviderPhotosRequestSchema defined in shared/src/journey/journey.schema.ts before processing.
  • TREK encrypts provider credentials at rest and never exposes them to the client, ensuring security even if frontend sessions are compromised.
  • Images stream through TREK’s /api/public/journey/photo-proxy endpoint, hiding real provider URLs from end users and browsers.
  • Optional mirroring functionality keeps Immich libraries synchronized with Journey uploads through automated asset.upload calls.

Frequently Asked Questions

How does TREK keep my photo provider credentials secure?

TREK encrypts all provider credentials at rest using database-level encryption. API keys for Immich and passwords for Synology Photos are never transmitted back to the browser or exposed in client-side code. The backend service decrypts these values only when making server-to-server requests to fetch image metadata.

Can I use multiple photo providers simultaneously with the Journey addon?

Yes. Administrators can enable both Immich and Synology Photos in the Journey addon settings. Users then see both providers available in their Settings → Integrations panel and can configure credentials for each. When creating journal entries, users select which provider to pull assets from for each individual photo attachment.

Why does TREK use a proxy instead of returning direct URLs to my Immich server?

The proxy architecture at /api/public/journey/photo-proxy prevents exposure of internal network addresses and authentication tokens. This design allows TREK to validate user permissions before serving images, supports providers hosted on private LANs (configured via ALLOW_INTERNAL_NETWORK), and ensures that temporary access tokens or session cookies remain server-side.

What happens if my photo provider is offline when I view a Journey entry?

If the Immich or Synology Photos instance is unavailable, the proxy request will fail and TREK will return an error status for that specific image. The Journey entry itself remains accessible, but the photo attachment will display a broken image or error state depending on the frontend implementation. The system does not cache full-resolution images locally unless the optional mirroring feature is enabled.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →