A rowing race application API built with Go and PostgreSQL that allows users to compete against each other using Concept2 rowing machines.
- User Management: Registration, authentication, and email verification
- Friend System: Invite friends after racing together
- Race Management: Create, join, and participate in rowing races
- Real-time Racing: Track progress and race completion
- Race History: View past races and statistics
- JWT Authentication: Secure API access
- Go 1.24+
- PostgreSQL 17+
- Docker (optional)
The application uses YAML configuration. Copy config.yaml.example to config.yaml and update with your values:
# Database Configuration
database:
url: "postgres://username:password@localhost:5432/ergracer?sslmode=disable"
# JWT Configuration
jwt:
secret: "your-super-secret-jwt-key-change-in-production"
# Mailgun Configuration (for email verification)
mailgun:
domain: "yourdomain.mailgun.org"
api_key: "your-mailgun-api-key"
from_email: "noreply@yourdomain.com"
from_name: "ErgRacer Team"
# Application Configuration
app:
url: "http://localhost:8080"
port: 8080Environment Variables:
CONFIG_FILE_PATH(optional) - Path to config file (defaults toconfig.yaml)
- Clone and setup:
git clone <repository>
cd ergracer-api
go mod download- Setup configuration:
cp config.yaml.example config.yaml
# Edit config.yaml with your database and SMTP settings- Run the application:
For development with hot reloading (recommended): note: this requires docker for the postgres container.
make freshPOST /api/v1/auth/register
Content-Type: application/json
{
"email": "user@example.com",
"username": "username",
"password": "password123"
}POST /api/v1/auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "password123"
}
Response:
{
"access_token": "jwt_token_here",
"refresh_token": "refresh_token_here",
"user": {...}
}POST /api/v1/auth/refresh
Content-Type: application/json
{
"refresh_token": "refresh_token_here"
}
Response:
{
"access_token": "new_jwt_token",
"refresh_token": "new_refresh_token"
}GET /api/v1/auth/verify-email?token=verification_tokenGET /api/v1/profile
Authorization: Bearer <jwt_token>POST /api/v1/friends/invite
Authorization: Bearer <jwt_token>
Content-Type: application/json
{
"friend_id": 123
}POST /api/v1/friends/accept/123
Authorization: Bearer <jwt_token>GET /api/v1/friends
Authorization: Bearer <jwt_token>GET /api/v1/friends/invitations
Authorization: Bearer <jwt_token>POST /api/v1/races
Authorization: Bearer <jwt_token>
Content-Type: application/json
{
"distance": 2000
}POST /api/v1/races/join
Authorization: Bearer <jwt_token>
Content-Type: application/json
{
"race_uuid": "race-uuid-here"
}GET /api/v1/races/{uuid}
Authorization: Bearer <jwt_token>POST /api/v1/races/{raceId}/ready
Authorization: Bearer <jwt_token>
Content-Type: application/json
{
"ready": true
}POST /api/v1/races/{raceId}/progress
Authorization: Bearer <jwt_token>
Content-Type: application/json
{
"distance": 1500
}POST /api/v1/races/{raceId}/start
Authorization: Bearer <jwt_token>GET /api/v1/history
Authorization: Bearer <jwt_token>- Create Race: User creates a race with specified distance
- Join Race: Other users join using the race UUID
- Ready Up: All participants mark themselves as ready
- Countdown: 10-second countdown begins when all are ready
- Race Start: Race becomes active, participants can submit progress
- Progress Updates: Users submit their rowing distance
- Completion: Users are marked finished when they reach the target distance
- Results: Pace and positions calculated automatically
- id, email, username, password_hash
- email_verified, email_verify_token
- created_at, updated_at
- user_id, friend_id, status (pending/accepted)
- created_at, accepted_at
- id, uuid, distance, status, created_by
- created_at, started_at, finished_at, countdown_at
- race_id, user_id, status, current_distance
- finished_at, pace, position, joined_at
- race_id, user_id, distance, timestamp
- user_id, refresh_token_hash, device_type
- user_agent, ip_address, expires_at
- created_at, updated_at
go test ./...go build -o ergracer-apigolangci-lint run- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
MIT License - see LICENSE file for details.