main.go
package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"os"
"strconv"
"strings"
"github.com/readysite/readysite/pkg/platform"
)
const usage = `
ReadySite - Connect to Production Servers
Usage:
connect [flags] [command...]
Flags:
--server <name> Connect to specific server by name (auto-selects if only one)
Examples:
connect # SSH (auto-selects single server)
connect --server app-main-1 # SSH to specific server
connect docker ps # Run command on server
connect docker logs -f app # Follow logs
`
// infraConfig defines infrastructure configuration.
type infraConfig struct {
Servers map[string]serverSpec `json:"servers"`
Instances map[string][]instance `json:"instances,omitempty"`
}
type serverSpec struct {
Size string `json:"size"`
}
// instance represents a deployed server instance.
type instance struct {
ID string `json:"id,omitempty"`
Name string `json:"name"`
IP string `json:"ip,omitempty"`
Region string `json:"region,omitempty"`
}
// loadInfraConfig loads infra.json from current directory.
func loadInfraConfig() (*infraConfig, error) {
data, err := os.ReadFile("infra.json")
if err != nil {
return nil, fmt.Errorf("read infra.json: %w", err)
}
var cfg infraConfig
if err := json.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("parse infra.json: %w", err)
}
return &cfg, nil
}
// allInstances returns all instances across all server types.
func (cfg *infraConfig) allInstances() []instance {
var all []instance
for _, instances := range cfg.Instances {
all = append(all, instances...)
}
return all
}
// getInstanceByName finds an instance by name across all server types.
func (cfg *infraConfig) getInstanceByName(name string) *instance {
for _, instances := range cfg.Instances {
for i := range instances {
if instances[i].Name == name {
return &instances[i]
}
}
}
return nil
}
// prompt displays a selection menu and returns the selected instance.
func prompt(instances []instance) (*instance, error) {
fmt.Println("\nMultiple servers found. Select one:")
fmt.Println()
for i, inst := range instances {
fmt.Printf(" [%d] %s (%s)\n", i+1, inst.Name, inst.IP)
}
fmt.Println()
fmt.Print("Enter choice: ")
var input string
if _, err := fmt.Scanln(&input); err != nil {
return nil, fmt.Errorf("read input: %w", err)
}
input = strings.TrimSpace(input)
choice, err := strconv.Atoi(input)
if err != nil || choice < 1 || choice > len(instances) {
return nil, fmt.Errorf("invalid selection: %s", input)
}
return &instances[choice-1], nil
}
func main() {
flag.Usage = func() { fmt.Print(usage) }
serverName := flag.String("server", "", "Connect to specific server by name")
flag.Parse()
args := flag.Args()
// Load infra.json from current directory
cfg, err := loadInfraConfig()
if err != nil {
log.Fatalf("load infra.json: %v", err)
}
instances := cfg.allInstances()
if len(instances) == 0 {
log.Fatal("no servers found in infra.json (run 'launch' first)")
}
// Select target server
var target *instance
if *serverName != "" {
target = cfg.getInstanceByName(*serverName)
if target == nil {
log.Fatalf("server %q not found", *serverName)
}
} else if len(instances) == 1 {
target = &instances[0]
} else {
target, err = prompt(instances)
if err != nil {
log.Fatal(err)
}
}
// Create platform server
server := &platform.Server{
ID: target.ID,
Name: target.Name,
IP: target.IP,
}
fmt.Printf("Connecting to %s (%s)...\n", target.Name, target.IP)
if len(args) > 0 {
// Run command with connected streams
if err := server.Connect(args...); err != nil {
os.Exit(1)
}
} else {
// Interactive SSH session
if err := server.Interactive(); err != nil {
log.Fatal("SSH connection failed:", err)
}
}
}