mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-03-17 10:06:58 +00:00
- new `orama sandbox reset` deletes Hetzner resources (IPs, firewall, SSH key) and local config - interactive location/server type selection during `setup` - add Hetzner API methods for listing locations/types, deleting resources - update defaults to nbg1/cx23
154 lines
3.9 KiB
Go
154 lines
3.9 KiB
Go
package sandbox
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// Config holds sandbox configuration, stored at ~/.orama/sandbox.yaml.
|
|
type Config struct {
|
|
HetznerAPIToken string `yaml:"hetzner_api_token"`
|
|
Domain string `yaml:"domain"`
|
|
Location string `yaml:"location"` // Hetzner datacenter (default: fsn1)
|
|
ServerType string `yaml:"server_type"` // Hetzner server type (default: cx22)
|
|
FloatingIPs []FloatIP `yaml:"floating_ips"`
|
|
SSHKey SSHKeyConfig `yaml:"ssh_key"`
|
|
FirewallID int64 `yaml:"firewall_id,omitempty"` // Hetzner firewall resource ID
|
|
}
|
|
|
|
// FloatIP holds a Hetzner floating IP reference.
|
|
type FloatIP struct {
|
|
ID int64 `yaml:"id"`
|
|
IP string `yaml:"ip"`
|
|
}
|
|
|
|
// SSHKeyConfig holds SSH key paths and the Hetzner resource ID.
|
|
type SSHKeyConfig struct {
|
|
HetznerID int64 `yaml:"hetzner_id"`
|
|
PrivateKeyPath string `yaml:"private_key_path"`
|
|
PublicKeyPath string `yaml:"public_key_path"`
|
|
}
|
|
|
|
// configDir returns ~/.orama/, creating it if needed.
|
|
func configDir() (string, error) {
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return "", fmt.Errorf("get home directory: %w", err)
|
|
}
|
|
dir := filepath.Join(home, ".orama")
|
|
if err := os.MkdirAll(dir, 0700); err != nil {
|
|
return "", fmt.Errorf("create config directory: %w", err)
|
|
}
|
|
return dir, nil
|
|
}
|
|
|
|
// configPath returns the full path to ~/.orama/sandbox.yaml.
|
|
func configPath() (string, error) {
|
|
dir, err := configDir()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return filepath.Join(dir, "sandbox.yaml"), nil
|
|
}
|
|
|
|
// LoadConfig reads the sandbox config from ~/.orama/sandbox.yaml.
|
|
// Returns an error if the file doesn't exist (user must run setup first).
|
|
func LoadConfig() (*Config, error) {
|
|
path, err := configPath()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil, fmt.Errorf("sandbox not configured, run: orama sandbox setup")
|
|
}
|
|
return nil, fmt.Errorf("read config: %w", err)
|
|
}
|
|
|
|
var cfg Config
|
|
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
|
return nil, fmt.Errorf("parse config %s: %w", path, err)
|
|
}
|
|
|
|
if err := cfg.validate(); err != nil {
|
|
return nil, fmt.Errorf("invalid config: %w", err)
|
|
}
|
|
|
|
cfg.Defaults()
|
|
|
|
return &cfg, nil
|
|
}
|
|
|
|
// SaveConfig writes the sandbox config to ~/.orama/sandbox.yaml.
|
|
func SaveConfig(cfg *Config) error {
|
|
path, err := configPath()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
data, err := yaml.Marshal(cfg)
|
|
if err != nil {
|
|
return fmt.Errorf("marshal config: %w", err)
|
|
}
|
|
|
|
if err := os.WriteFile(path, data, 0600); err != nil {
|
|
return fmt.Errorf("write config: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// validate checks that required fields are present.
|
|
func (c *Config) validate() error {
|
|
if c.HetznerAPIToken == "" {
|
|
return fmt.Errorf("hetzner_api_token is required")
|
|
}
|
|
if c.Domain == "" {
|
|
return fmt.Errorf("domain is required")
|
|
}
|
|
if len(c.FloatingIPs) < 2 {
|
|
return fmt.Errorf("2 floating IPs required, got %d", len(c.FloatingIPs))
|
|
}
|
|
if c.SSHKey.PrivateKeyPath == "" {
|
|
return fmt.Errorf("ssh_key.private_key_path is required")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Defaults fills in default values for optional fields.
|
|
func (c *Config) Defaults() {
|
|
if c.Location == "" {
|
|
c.Location = "nbg1"
|
|
}
|
|
if c.ServerType == "" {
|
|
c.ServerType = "cx23"
|
|
}
|
|
}
|
|
|
|
// ExpandedPrivateKeyPath returns the absolute path to the SSH private key.
|
|
func (c *Config) ExpandedPrivateKeyPath() string {
|
|
return expandHome(c.SSHKey.PrivateKeyPath)
|
|
}
|
|
|
|
// ExpandedPublicKeyPath returns the absolute path to the SSH public key.
|
|
func (c *Config) ExpandedPublicKeyPath() string {
|
|
return expandHome(c.SSHKey.PublicKeyPath)
|
|
}
|
|
|
|
// expandHome replaces a leading ~ with the user's home directory.
|
|
func expandHome(path string) string {
|
|
if len(path) < 2 || path[:2] != "~/" {
|
|
return path
|
|
}
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return path
|
|
}
|
|
return filepath.Join(home, path[2:])
|
|
}
|