- Create README.md with project overview, quick start, Docker deployment, CI/CD info - Update CLAUDE.md with Deployment section - Fix cmd/api/main.go to use REDIS_ADDR env var with fallback - Fix Dockerfile to use golang:1.22-alpine instead of golang:1.26-alpine - Fix .gitea/workflows/deploy.yml to include Docker registry prefix in image tags
209 lines
7.4 KiB
Markdown
209 lines
7.4 KiB
Markdown
# CLAUDE.md
|
||
|
||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||
|
||
## Overview
|
||
|
||
This is a multimodal trip planning service that uses Yandex.Schedules API to provide routing across different transport modes (planes, trains, buses). The service implements lazy graph expansion to work within API quota constraints and provides route visualization on maps.
|
||
|
||
## Technology Stack
|
||
|
||
- **Backend**: Go
|
||
- **Database**: PostgreSQL (with PostGIS optional for geometry)
|
||
- **Cache/Queue**: Redis (native TTL)
|
||
- **Frontend Map**: Leaflet + OpenStreetMap tiles
|
||
- **Task Scheduler**: cron (internal `cmd/cron` or system cron)
|
||
|
||
## Project Structure
|
||
|
||
```
|
||
/cmd
|
||
/api — HTTP server
|
||
/cron — Reference data updates, station status detection
|
||
/internal
|
||
/yandex — Yandex API client, rate limiter, retries, circuit breaker
|
||
/cache — Interface + Redis implementation (cache-aside), user preferences
|
||
/storage — PostgreSQL repositories, transfer rules, station neighbors
|
||
/routing — Graph, search algorithm, MCT rules, search cache, route status
|
||
/airports — Neighboring stations, closure detection
|
||
/geo — GeoJSON assembly for maps
|
||
/metrics — Observability metrics (cache hits/misses, API quota, circuit breaker, search duration)
|
||
```
|
||
|
||
## Common Development Commands
|
||
|
||
### Running the API Server
|
||
```bash
|
||
go run ./cmd/api
|
||
```
|
||
|
||
### Running Cron Jobs
|
||
```bash
|
||
go run ./cmd/cron
|
||
```
|
||
|
||
### Running Tests
|
||
```bash
|
||
# Run all tests
|
||
go test ./...
|
||
|
||
# Run tests for a specific package
|
||
go test ./internal/routing
|
||
|
||
# Run tests with coverage
|
||
go test ./... -cover
|
||
```
|
||
|
||
### Database Migrations
|
||
*(Assuming standard Go migration tools)*
|
||
```bash
|
||
# Apply migrations
|
||
goose up
|
||
|
||
# Rollback migration
|
||
goose down
|
||
|
||
# Check migration status
|
||
goose status
|
||
```
|
||
|
||
### Code Formatting
|
||
```bash
|
||
# Format Go code
|
||
go fmt ./...
|
||
|
||
# Check for formatting issues
|
||
go vet ./...
|
||
```
|
||
|
||
## Deployment
|
||
|
||
### Docker Deployment
|
||
|
||
The project uses a multi-stage Dockerfile:
|
||
- **Builder stage**: `golang:1.22-alpine` to build `api` and `cron` binaries
|
||
- **Runtime stage**: `alpine:latest` with minimal footprint (~27MB)
|
||
|
||
### Docker Compose
|
||
|
||
The `docker-compose.yml` includes:
|
||
- `api` service: Go HTTP server on port 8080
|
||
- `cron` service: Go cron binary for reference data updates and station status detection
|
||
- `postgres`: PostgreSQL 15-alpine with `postgres_data` volume
|
||
- `redis`: Redis 7-alpine with `redis_data` volume
|
||
- `watchtower`: nickfedor/watchtower for automatic container updates (interval: 60s)
|
||
|
||
### CI/CD Pipeline
|
||
|
||
Gitea Actions workflow (`.gitea/workflows/deploy.yml`) provides:
|
||
- Checkout code
|
||
- Setup Go 1.22+
|
||
- Go modules cache
|
||
- Lint with golangci-lint v1.54.2
|
||
- Run tests with race detector: `go test -v -race ./...`
|
||
- Docker Buildx setup
|
||
- Docker login to registry
|
||
- Build and push Docker image with tags: `latest` and commit SHA
|
||
|
||
### API Endpoints (from specification)
|
||
|
||
- `GET /v1/cities?query=` — City autocomplete
|
||
- `GET /v1/cities/{id}/stations` — City stations (including neighbors if main closed)
|
||
- `POST /v1/routes/search` — Search for routes (Pareto-optimal results)
|
||
- `GET /v1/routes/{search_id}/{route_id}/geojson` — Get route geometry for map
|
||
- `GET /v1/stations/{id}/status` — Station status
|
||
- `POST /internal/admin/stations/{id}/status` — Manual station status override (requires auth)
|
||
- `GET /v1/preferences/saved-cities?user_id=` — Get user's saved cities
|
||
- `POST /v1/preferences/saved-cities?user_id=` — Add a city to user's saved cities
|
||
- `DELETE /v1/preferences/saved-cities/{city_code}?user_id=` — Remove a city from user's saved cities
|
||
- `GET /v1/preferences/search-history?user_id=` — Get user's search history
|
||
- `POST /v1/preferences/search-history?user_id=` — Add a search to user's history
|
||
- `GET /metrics` — Get observability metrics (cache hit rates, API quota, circuit breaker trips, search duration)
|
||
|
||
## Key Architectural Features
|
||
|
||
### 1. Lazy Graph Expansion
|
||
Due to Yandex.Schedules API limitations (no full timetable dump), the service uses:
|
||
- Hub stations (major transport nodes) as anchor points
|
||
- BFS/Dijkstra with depth limiting (4-5 transfers max)
|
||
- On-demand `/search` requests only for relevant station pairs
|
||
- Aggressive caching to minimize API calls
|
||
|
||
### 2. Caching Strategy
|
||
Multi-layer TTL approach:
|
||
- City/station directory: 30 days (Postgres + Redis hot cache)
|
||
- `/nearest_stations`: 30 days (static coordinates)
|
||
- `/search`: 2-6 hours (near-term), 7 days (far-term dates)
|
||
- `/schedule` (for closure detection): 1 day
|
||
- `/thread`: Not cached or 1-5 min TTL (real-time status)
|
||
|
||
### 3. Multimodal Routing
|
||
- Graph edges: Real (actual scheduled trips) and Synthetic (city<74><79>↔airport transfers)
|
||
- Transport type stored as attribute for display/filtering
|
||
- Transfer rules based on node type, city tier, and check-in type
|
||
- Pareto-front ranking (time, transfers, cost) rather than single "optimal" route
|
||
|
||
### 4. Station Closure Detection
|
||
- Daily cron job checks `/schedule` for each monitored station
|
||
- N consecutive days of zero trips triggers closure status (N=3 recommended)
|
||
- Automatic fallback to neighboring stations when closed
|
||
- Immediate reactivation when trips resume
|
||
|
||
### 5. Map Visualization
|
||
- Routes served as pre-built GeoJSON FeatureCollections
|
||
- Real segments: Solid lines (color by transport type)
|
||
- Synthetic segments: Dashed lines
|
||
- Transfer points: Markers with popup info (connection time, type)
|
||
- Frontend: Leaflet + OSM tiles (no vendor lock-in)
|
||
|
||
### 6. User Preferences Cache
|
||
- Saved cities and search history stored per user in Redis
|
||
- 7-day TTL for preference data
|
||
- Accessed via `/v1/preferences/` endpoints
|
||
|
||
### 7. Observability Metrics
|
||
- Cache hit/miss counts per layer (cache, search, cache_aside)
|
||
- API quota remaining tracking
|
||
- Circuit breaker trip counts
|
||
- Search count and duration histogram (avg in milliseconds)
|
||
- Available via `GET /metrics` endpoint
|
||
|
||
### 8. Search Cache Service
|
||
- Cache-aside pattern for Yandex `/search` API calls
|
||
- Near-term dates: 3-hour TTL
|
||
- Far-term dates: 7-day TTL
|
||
- Reduces API quota consumption through aggressive caching
|
||
|
||
### 9. Transfer Rules / MCT System
|
||
- Minimum Connection Time rules stored in `transfer_rules` table
|
||
- Rule keys include: `airport_internal`, `airport_internal_through`, `airport_internal_separate`, `station_internal`, `airport_to_city`
|
||
- Base MCT is 30 minutes (1800 seconds)
|
||
- Rule keys with suffixes (e.g., `_through`, `_separate`) match base keys
|
||
|
||
### 10. Route Change Notifications
|
||
- `CheckAndRescheduleRoute` checks for significant route changes
|
||
- Detects cancellations (edge duration > 1 day) or major delays (duration > 2x normal)
|
||
- Re-searches route when changes detected, returns updated itinerary
|
||
|
||
## Development Guidelines
|
||
|
||
### Error Handling
|
||
- Use circuit breaker pattern in `/internal/yandex` for API protection
|
||
- Degrade gracefully to stale cache when API unavailable
|
||
- Always provide clear error messages to users
|
||
|
||
### Testing
|
||
- Unit test routing logic with synthetic timetable fixtures
|
||
- Mock external API calls in tests
|
||
- Test cache-aside patterns thoroughly
|
||
- Validate MCT (Minimum Connection Time) calculations
|
||
|
||
### Performance
|
||
- Target: <3-5s for cached routes, <15s for cold cache with multiple segments
|
||
- Monitor: Cache hit rates, API quota consumption, circuit breaker trips
|
||
- Provide progress indicators for long-running searches
|
||
|
||
### Security
|
||
- Validate all inputs (especially for admin endpoints)
|
||
- Protect admin endpoints with proper authentication
|
||
- Never store API keys or secrets in code/repository |