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
shared/src/journey/journey.schema.ts– Contains the Zod schemas definingjourneyProviderPhotosRequestSchemaand other validation structures for the Journey API.wiki/Photo-Providers.md– Documentation covering admin toggle locations, user configuration UI fields, and provider-specific setup instructions.wiki/Admin-Addons.md– Explains how the Journey addon appears in the admin panel and where provider activation toggles reside.src/journey/journey.controller.ts– Implements the REST endpoints receiving photo attachment payloads and validating them against schemas.src/journey/photo-proxy.controller.ts– Handles streaming image data from external providers through TREK’s secure proxy.wiki/Internal-Network-Access.md– Provides configuration notes for enabling outbound access when providers are hosted on private LANs usingALLOW_INTERNAL_NETWORK.
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
journeyProviderPhotosRequestSchemadefined inshared/src/journey/journey.schema.tsbefore 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-proxyendpoint, hiding real provider URLs from end users and browsers. - Optional mirroring functionality keeps Immich libraries synchronized with Journey uploads through automated
asset.uploadcalls.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →