74 lines
2.6 KiB
Markdown
74 lines
2.6 KiB
Markdown
# Trip Planner
|
|
|
|
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)
|
|
|
|
## Quick Start
|
|
|
|
### Running Locally with Docker Compose
|
|
|
|
```bash
|
|
docker-compose up -d
|
|
```
|
|
|
|
This starts:
|
|
- `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 on port 5432
|
|
- `redis`: Redis 7-alpine on port 6379
|
|
- `watchtower`: nickfedor/watchtower for automatic container updates (interval: 60s)
|
|
|
|
### Running the API Server Directly
|
|
|
|
```bash
|
|
go run ./cmd/api
|
|
```
|
|
|
|
### Running Cron Jobs
|
|
|
|
```bash
|
|
go run ./cmd/cron
|
|
```
|
|
|
|
## Deployment
|
|
|
|
### Docker Deployment
|
|
|
|
The project uses a multi-stage Dockerfile:
|
|
- **Builder stage**: `golang:1.26-alpine` to build `api` and `cron` binaries
|
|
- **Runtime stage**: `alpine:latest` with minimal footprint (~27MB)
|
|
|
|
### 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
|
|
|
|
- `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)
|