# RideRegion

RideRegion is an open-source platform for operating a regional bike-sharing network across multiple cities, campuses, and transit districts. It provides a unified API and web dashboard for managing stations, bicycles, memberships, trips, pricing, and maintenance operations.

The project is designed for networks that combine docked bikes, e-bikes, and virtual parking zones while exposing real-time availability through the General Bikeshare Feed Specification (GBFS).

## Features

- Real-time bike and dock availability
- Support for docked bikes, e-bikes, and virtual stations
- Rider registration and membership management
- Trip creation, completion, pricing, and payment tracking
- Station capacity and service-area management
- Maintenance reporting and fleet redistribution workflows
- Role-based access for operators, technicians, and administrators
- GBFS-compatible public data feeds
- REST API and browser-based operations dashboard
- PostgreSQL persistence with optional Redis caching
- Configurable fares, operating hours, and regional policies
- Docker-based local development environment

## Requirements

- Node.js 20 or later
- PostgreSQL 15 or later
- Redis 7 or later
- npm 10 or later

Docker Compose can provide PostgreSQL and Redis for local development.

## Installation

Clone the repository and install dependencies:

```bash
git clone https://github.com/example/ride-region.git
cd ride-region
npm install
```

Create a local environment file:

```bash
cp .env.example .env
```

Start the supporting services:

```bash
docker compose up -d postgres redis
```

Run database migrations and load sample stations:

```bash
npm run db:migrate
npm run db:seed
```

Start the API and dashboard in development mode:

```bash
npm run dev
```

The dashboard will be available at `http://localhost:3000`, and the API will be available at `http://localhost:4000`.

## Usage

### Sign in to the operations dashboard

After seeding the development database, sign in with:

```text
Email: operator@example.test
Password: change-me
```

Change or remove this account before deploying the application.

### Check station availability

```bash
curl http://localhost:4000/api/v1/stations
```

Filter stations by region:

```bash
curl "http://localhost:4000/api/v1/stations?region=lake-district"
```

### Start a trip

```bash
curl -X POST http://localhost:4000/api/v1/trips \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "bikeId": "bike-1042",
    "originStationId": "station-central-01"
  }'
```

### End a trip

```bash
curl -X POST http://localhost:4000/api/v1/trips/trip-123/end \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "destinationStationId": "station-riverside-03"
  }'
```

### Access GBFS feeds

The feed index is published at:

```text
http://localhost:4000/gbfs/gbfs.json
```

### Run tests

```bash
npm test
```

Run API integration tests:

```bash
npm run test:integration
```

## Configuration

RideRegion reads configuration from environment variables. Values in `.env` are loaded automatically during local development.

| Variable | Default | Description |
| --- | --- | --- |
| `NODE_ENV` | `development` | Application environment |
| `API_PORT` | `4000` | API server port |
| `WEB_PORT` | `3000` | Dashboard development-server port |
| `PUBLIC_API_URL` | `http://localhost:4000` | Public URL used by the dashboard and GBFS feeds |
| `DATABASE_URL` | — | PostgreSQL connection string |
| `REDIS_URL` | `redis://localhost:6379` | Redis connection string |
| `JWT_SECRET` | — | Secret used to sign access tokens |
| `JWT_EXPIRES_IN` | `15m` | Access-token lifetime |
| `NETWORK_ID` | `ride-region` | Stable identifier for the bike-sharing network |
| `NETWORK_NAME` | `RideRegion` | Public network name |
| `DEFAULT_TIMEZONE` | `America/Toronto` | Timezone used for schedules and reports |
| `DEFAULT_CURRENCY` | `CAD` | ISO 4217 currency code |
| `GBFS_ENABLED` | `true` | Enables public GBFS endpoints |
| `GBFS_CACHE_SECONDS` | `30` | GBFS response cache duration |
| `TRIP_RESERVATION_MINUTES` | `10` | Time a reserved bike remains unavailable |
| `MAINTENANCE_MODE` | `false` | Prevents new trips while allowing active trips to end |
| `LOG_LEVEL` | `info` | Logging verbosity |

Example configuration:

```dotenv
NODE_ENV=development
API_PORT=4000
WEB_PORT=3000
PUBLIC_API_URL=http://localhost:4000

DATABASE_URL=postgresql://rideregion:rideregion@localhost:5432/rideregion
REDIS_URL=redis://localhost:6379
JWT_SECRET=replace-with-a-long-random-value

NETWORK_ID=ride-region
NETWORK_NAME=RideRegion
DEFAULT_TIMEZONE=America/Toronto
DEFAULT_CURRENCY=CAD

GBFS_ENABLED=true
GBFS_CACHE_SECONDS=30
TRIP_RESERVATION_MINUTES=10
MAINTENANCE_MODE=false
LOG_LEVEL=debug
```

Regional settings, fare rules, station groups, and operating hours are stored in `config/network.yml`:

```yaml
regions:
  - id: lake-district
    name: Lake District
    operatingHours:
      weekdays: "05:00-01:00"
      weekends: "06:00-02:00"

fares:
  currency: CAD
  unlockFee: 1.00
  standardBikePerMinute: 0.15
  electricBikePerMinute: 0.30
  dailyCap: 24.00

policies:
  minimumRiderAge: 16
  maximumTripHours: 24
  allowOutOfStationReturns: false
```

Restart the application after changing environment variables or network configuration.

## Production

Build the API and dashboard:

```bash
npm run build
```

Apply pending database migrations:

```bash
npm run db:migrate
```

Start the production server:

```bash
npm start
```

Production deployments should use HTTPS, a managed PostgreSQL database, a persistent Redis instance, and a unique `JWT_SECRET`. Database backups and monitoring should be configured before accepting rider trips.

## License

Licensed under the Apache License 2.0. See `LICENSE` for details.