// Package client provides ZFS snapshot backup client functionality. // It handles creating snapshots and uploading them to a remote server. package client import ( "bufio" "os" "strings" ) // Config holds client-side configuration for connecting to the backup server. // Note: Storage type is determined by the server, not the client. type Config struct { // ClientID is the unique identifier for this client ClientID string `json:"client_id"` // APIKey is the authentication key for the server APIKey string `json:"api_key"` // ServerURL is the base URL of the backup server ServerURL string `json:"server_url"` // LocalDataset is the ZFS dataset to backup LocalDataset string `json:"local_dataset"` // Compress enables LZ4 compression for transfers Compress bool `json:"compress"` // S3Endpoint is the S3 endpoint URL (optional, for direct S3 uploads) S3Endpoint string `json:"s3_endpoint"` // S3Region is the AWS region S3Region string `json:"s3_region"` // S3Bucket is the S3 bucket name S3Bucket string `json:"s3_bucket"` // S3AccessKey is the AWS access key S3AccessKey string `json:"s3_access_key"` // S3SecretKey is the AWS secret key S3SecretKey string `json:"s3_secret_key"` } // LoadConfig loads client configuration from environment variables and .env file. // Environment variables take precedence over .env file values. func LoadConfig() *Config { // Load .env file if exists loadEnvFile(".env") return &Config{ ClientID: getEnv("CLIENT_ID", "client1"), APIKey: getEnv("API_KEY", "secret123"), ServerURL: getEnv("SERVER_URL", "http://backup-server:8080"), LocalDataset: getEnv("LOCAL_DATASET", "tank/data"), Compress: getEnv("COMPRESS", "true") == "true", S3Endpoint: getEnv("S3_ENDPOINT", ""), S3Region: getEnv("S3_REGION", "us-east-1"), S3Bucket: getEnv("S3_BUCKET", "zfs-backups"), S3AccessKey: getEnv("S3_ACCESS_KEY", ""), S3SecretKey: getEnv("S3_SECRET_KEY", ""), } } // loadEnvFile loads key=value pairs from a .env file. // Lines starting with # are treated as comments. // Values can be quoted or unquoted. func loadEnvFile(filename string) { file, err := os.Open(filename) if err != nil { return // .env file is optional } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) // Skip empty lines and comments if line == "" || strings.HasPrefix(line, "#") { continue } // Parse key=value parts := strings.SplitN(line, "=", 2) if len(parts) != 2 { continue } key := strings.TrimSpace(parts[0]) value := strings.TrimSpace(parts[1]) // Remove quotes if present if len(value) >= 2 && (value[0] == '"' || value[0] == '\'') { value = value[1 : len(value)-1] } // Only set if not already defined in environment if os.Getenv(key) == "" { os.Setenv(key, value) } } } // getEnv retrieves an environment variable with a default fallback value. func getEnv(key, defaultValue string) string { if value := os.Getenv(key); value != "" { return value } return defaultValue }