Trip Planner¶
AI-powered travel planning application with interactive maps and itinerary management.
Quick Links¶
Production¶
- App: https://travel.bogocat.com (via Caddy reverse proxy)
- Namespace:
trip-planner - Ingress: nginx class, hostname
travel.bogocat.com
Development¶
- Dev Server: http://localhost:3000 (run from host)
- Host Path:
/root/projects/trip-planner - tmux Session:
tmux attach -t trip-planner
Overview¶
Trip Planner helps users plan and organize travel itineraries with AI assistance, interactive maps, and collaborative features.
Key Features¶
- AI Chat Interface: Natural language trip planning with Claude (Anthropic)
- Interactive Maps: Mapbox integration for location visualization
- Itinerary Management: Day-by-day planning with locations and activities
- Location Management: Add, edit, and organize trip locations
- GPX Import: Import hiking/biking routes from GPX files
- Mobile Navigation: Responsive bottom navigation for mobile devices
- Today View: Active trip detection with "what's next" display, countdowns, and one-tap navigation
Tech Stack¶
- Framework: Next.js 16 (App Router)
- UI Library: React 19
- Styling: Tailwind CSS v4
- Database: Supabase (PostgreSQL, k8s shared instance)
- Authentication: Authentik SSO via NextAuth v5 (dual auth mode)
- Maps: Mapbox GL JS + react-map-gl
- AI: @jakecelentano/ai-providers (npm) + Anthropic Claude
- Deployment: Kubernetes (k3s) with NGINX Ingress + Caddy
Authentication¶
Dual Auth Mode¶
Trip Planner uses the standard Authentik SSO pattern with dual auth mode:
AUTH_MODE=none- Development mode, no login requiredAUTH_MODE=authentik- Production mode, requires Authentik SSO
Supabase Auth Bridge¶
Since Authentik uses SHA256 hashes for user IDs (not UUIDs), a custom auth bridge generates Supabase-compatible JWTs:
File: lib/auth/supabase-bridge.ts
// Usage in server components/API routes:
const { user, supabase } = await getAuthContextWithRLS()
if (!user || !supabase) {
redirect("/login")
}
// supabase client has RLS-enabled JWT with user's Authentik ID
const { data } = await supabase.from("trips").select("*")
RLS Policies¶
RLS policies use auth.jwt() ->> 'sub' instead of auth.uid() to match Authentik's sub claim format:
Database¶
Schema¶
Tables organized in the trip_planner PostgreSQL schema:
-- Main tables
trip_planner.trips -- user_id is TEXT (Authentik hash)
trip_planner.itineraries
trip_planner.locations
trip_planner.constraints
trip_planner.chat_messages
trip_planner.chat_sessions
Supabase Instance: Shared Kubernetes Supabase (supabase namespace)
Client Configuration:
See: Supabase Multi-App Architecture
Deployment¶
Deploy Script¶
# Full deployment
/root/tower-fleet/scripts/deploy-trip-planner.sh -y
# Or step by step:
cd /root/projects/trip-planner
AUTH_MODE=authentik npm run build
docker build -t trip-planner:v1.x.x .
docker tag trip-planner:v1.x.x 10.89.97.201:30500/trip-planner:v1.x.x
docker push 10.89.97.201:30500/trip-planner:v1.x.x
kubectl set image deployment/trip-planner trip-planner=10.89.97.201:30500/trip-planner:v1.x.x -n trip-planner
Dockerfile Notes¶
The Dockerfile must set AUTH_MODE=authentik during build:
Container Registry¶
Registry: 10.89.97.201:30500
Images:
- 10.89.97.201:30500/trip-planner:latest
- 10.89.97.201:30500/trip-planner:v1.x.x
Environment Variables¶
Development (.env.local)¶
AUTH_MODE=none # or authentik for testing SSO
NEXT_PUBLIC_SUPABASE_URL=http://10.89.97.214:8000
NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon-key>
SUPABASE_SERVICE_ROLE_KEY=<service-role-key>
SUPABASE_JWT_SECRET=<jwt-secret>
ANTHROPIC_API_KEY=<api-key>
# For AUTH_MODE=authentik:
AUTH_SECRET=<random-secret>
AUTHENTIK_CLIENT_ID=<client-id>
AUTHENTIK_CLIENT_SECRET=<client-secret>
AUTHENTIK_ISSUER=https://auth.bogocat.com/application/o/trip-planner/
Production (k8s Secrets)¶
All sensitive values stored in trip-planner-secrets SealedSecret:
- AUTH_MODE, AUTH_SECRET, AUTH_URL, AUTH_TRUST_HOST
- AUTHENTIK_CLIENT_ID, AUTHENTIK_CLIENT_SECRET, AUTHENTIK_ISSUER
- SUPABASE_JWT_SECRET, SUPABASE_SERVICE_ROLE_KEY
- ANTHROPIC_API_KEY
Dependencies¶
@jakecelentano/ai-providers¶
Custom npm package for AI provider abstraction:
Published: https://www.npmjs.com/package/@jakecelentano/ai-providers
Provides unified interface for Anthropic and OpenRouter APIs.
Troubleshooting¶
"Dev Mode" showing in production¶
Check that:
1. AUTH_MODE=authentik is in k8s secret
2. SessionProvider wraps the app in layout.tsx
3. /api/auth/mode returns {"mode":"authentik"}
Trips not loading (RLS errors)¶
- Verify user_id column is TEXT type (not UUID)
- Check RLS policies use
auth.jwt() ->> 'sub' - Ensure SUPABASE_JWT_SECRET matches Supabase's JWT secret
Docker build fails¶
See: Docker AppArmor Issue on PVE9
Related Documentation¶
Last Updated: 2025-12-23 Status: Active Version: v1.1.0 Production URL: https://travel.bogocat.com