# Turbine

A modular service framework.

## Launchpad (Process Manager)

The `launchpad` is a CLI tool located in `pkg/launchpad` designed to simplify development and production workflows for the entire Turbine project. It acts as a process manager that can run all defined services concurrently for development and generate a `docker-compose.yml` file for production deployments.

### Configuration (`launchpad.toml`)

The launchpad is configured via a `launchpad.toml` file in the project root. This file defines all the services, their configurations, and the networks they use.

**`launchpad.toml` example:**
```toml
# Defines variables required by the configuration.
# These should be supplied in a .env file.
[variables]
required = ["CACHE_PASSWORD", "QUEUE_PASSWORD", "RING_IMAGE", "RING_PORT"]

# Defines docker networks.
[networks]
aspire = {}

# Service definitions
[[services]]
name = "cache"
type = "docker" # For third-party docker images
[services.prod]
image = "docker.io/library/redis:7.4"
command = ["/bin/sh", "-c", "redis-server --requirepass $$REDIS_PASSWORD"]
environment = ["REDIS_PASSWORD=${CACHE_PASSWORD}"]
expose = ["6379"]
networks = ["aspire"]

[[services]]
name = "ring"
type = "dotnet"
path = "../turbine-dotnet-services/ring"
[services.dev]
command = "dotnet watch run"
[services.prod]
image = "${RING_IMAGE}"
environment = [
    "HTTP_PORTS=${RING_PORT}",
    "ConnectionStrings__cache=cache:6379,password=${CACHE_PASSWORD}",
]
volumes = ["./keys:/app/keys", "./settings/ring.json:/app/appsettings.json"]
expose = ["${RING_PORT}", "5001"]
networks = ["aspire"]
depends_on = ["cache", "queue"]
```

### Environment Variables (`.env`)

For the `deploy` command to work, you must create a `.env` file in the project root containing the variables defined in `launchpad.toml`. An example is provided in `.env.example`.

### Commands

To use the launchpad, run its `main.go` file with one of the following commands:

#### Development (`dev`)
Starts all services defined in `launchpad.toml` in development mode. Each service runs in a separate process, and their logs are streamed to the console with colored prefixes. A single `Ctrl+C` will gracefully shut down all services.

```bash
go run ./pkg/launchpad/main.go dev
```

#### Production Deployment (`deploy`)
Generates a `docker-compose.yml` file in the project root based on the `prod` configuration of all services in `launchpad.toml`. It substitutes variables from your `.env` file. This file can be used to build and run all services as Docker containers.

```bash
go run ./pkg/launchpad/main.go deploy
```


## Registrar

The Registrar is the service discovery system of the DysonNetwork.
Here are a port to the Golang in order to support other Golang services.

To use the system, try build with these API:

```go
package main

import (
	"log"
	"os"
	"os/signal"
	"syscall"

	"yourmodule/registry"
)

func main() {
	endpoints := []string{"localhost:2379"}

	registrar, err := registry.NewServiceRegistrar(endpoints)
	if err != nil {
		log.Fatalf("Error creating registrar: %v", err)
	}

	serviceName := "orders"
	host := "10.0.0.5"
	port := 5000
	ttl := int64(30)

	err = registrar.Register(serviceName, "http", "instance-1", host, port, ttl)
	if err != nil {
		log.Fatalf("Register error: %v", err)
	}
	log.Println("Service registered")

	// Wait for termination
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
	<-stop

	err = registrar.Deregister()
	if err != nil {
		log.Printf("Deregister error: %v", err)
	} else {
		log.Println("Service deregistered")
	}
}
```

## Gateway

The gateway is the entry point for all requests. It uses the `registrar` to discover services in real-time from etcd and forwards requests to the appropriate service instances.

### Features
- **Service Discovery**: Automatically discovers `http` services from etcd.
- **Dynamic Routing**: Maintains an in-memory routing table that is automatically updated when services are added or removed.
- **Request Proxying**: Forwards incoming requests to the correct service instance based on the URL path (`/<service-name>/...`).
- **Load Balancing**: Implements round-robin load balancing across service instances.
- **Route Overrides**: Allows for custom routing rules to be defined in the configuration file.

### Configuration

The gateway is configured via a `settings.toml` file located in the same directory.

```toml
# The address the gateway will listen on
listen = ":8080"

# ETCD configuration for service discovery
[etcd]
endpoints = ["127.0.0.1:2379"]
# Set to true if your etcd server does not use TLS
insecure = true

# Custom route overrides
# The key is the incoming path prefix.
# The value is the destination in the format "/<service_name>/<path_prefix>"
[routes]
"/websocket" = "/chatter/ws"
```

## Config Service

The config service provides a centralized location for other services to fetch their configuration. This is useful for managing connection strings, feature flags, and other shared parameters without hardcoding them into each service.

### Usage
The config service reads a `shared_config.toml` file from its own directory (`pkg/config`) and serves it as a JSON object over a simple HTTP endpoint.

To retrieve the configuration, other services can make a GET request to the gateway at `/config`. The gateway will route the request to an available instance of the config service.

**Example with curl:**
```bash
curl http://localhost:8080/config
```

**Expected Response (JSON):**
```json
{
  "database": {
    "connection_string": "postgres://user:password@db-host:5432/mydatabase?sslmode=require"
  },
  "redis": {
    "address": "redis-host:6379"
  }
}
```

