Just a few more functions to rough in before we begin iterating. I'm going to bed now

This commit is contained in:
VetheonGames 2024-03-27 03:26:20 -06:00
parent 303670e43a
commit 9c19c402c0
11 changed files with 672 additions and 84 deletions

View File

@ -1,82 +1,76 @@
package cmd
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"pixelridgesoftworks.com/BackGo/config"
"reflect"
"strconv"
"strings"
)
const configDir = "/etc/PixelRidge/BackupGo"
const configFile = "config.json"
const configFilePath = "/etc/PixelRidge/BackGo/config.json"
// Config represents the application's configuration
type Config struct {
S3ConnectionInfo string `json:"s3_connection_info"`
// Add other configuration options here, all as strings or booleans
}
// LoadConfig loads the configuration from the JSON file
func LoadConfig() (*Config, error) {
configPath := filepath.Join(configDir, configFile)
file, err := os.Open(configPath)
// OpenConfigInNano opens the configuration file in the nano editor.
func OpenConfigInNano() error {
cmd := exec.Command("nano", configFilePath)
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
err := cmd.Run()
if err != nil {
return nil, err
return fmt.Errorf("error opening config in nano: %v", err)
}
defer file.Close()
config := &Config{}
decoder := json.NewDecoder(file)
if err := decoder.Decode(config); err != nil {
return nil, err
}
return config, nil
}
// SaveConfig saves the modified configuration back to the JSON file
func SaveConfig(config *Config) error {
configPath := filepath.Join(configDir, configFile)
file, err := os.Create(configPath)
if err != nil {
return err
}
defer file.Close()
encoder := json.NewEncoder(file)
if err := encoder.Encode(config); err != nil {
return err
}
return nil
}
// EditConfigOption allows editing a specific configuration option from the CLI
func EditConfigOption(optionName, newValue string) error {
config, err := LoadConfig()
// Load the current configuration from file
cfg, err := config.LoadConfigFromFile()
if err != nil {
return err
return fmt.Errorf("loading config: %v", err)
}
// Reflect or a simple switch can be used to match the optionName to Config fields
switch optionName {
case "s3_connection_info":
config.S3ConnectionInfo = newValue
// Add cases for other options here
default:
// Use reflection to find and set the field in the Config struct
rConfig := reflect.ValueOf(cfg).Elem()
field := rConfig.FieldByNameFunc(func(s string) bool {
return strings.EqualFold(s, optionName)
})
if !field.IsValid() {
return fmt.Errorf("unknown configuration option: %s", optionName)
}
return SaveConfig(config)
// Ensure the field can be set
if !field.CanSet() {
return fmt.Errorf("cannot set configuration option: %s", optionName)
}
// OpenConfigInNano opens the configuration file in the nano editor
func OpenConfigInNano() error {
configPath := filepath.Join(configDir, configFile)
cmd := exec.Command("nano", configPath)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
// Convert and set the field value based on its kind
switch field.Kind() {
case reflect.String:
field.SetString(newValue)
case reflect.Bool:
boolVal, err := strconv.ParseBool(newValue)
if err != nil {
return fmt.Errorf("invalid value for %s: %v", optionName, err)
}
field.SetBool(boolVal)
case reflect.Int:
intVal, err := strconv.Atoi(newValue)
if err != nil {
return fmt.Errorf("invalid value for %s: %v", optionName, err)
}
// Additional checks can be implemented here for specific int fields like LogLevel
field.SetInt(int64(intVal))
default:
return fmt.Errorf("unsupported configuration type for: %s", optionName)
}
// Save the updated configuration back to the file
if err := config.SaveConfigToFile(cfg); err != nil {
return fmt.Errorf("saving config: %v", err)
}
return nil
}

View File

@ -77,16 +77,19 @@ func init() {
configCmd := NewCommand("config", "Configuration management", nil)
editCmd := NewCommand("edit", "Edit a configuration option", func(cmd *Command, args []string) {
// Ensure the flag is parsed before trying to access it
cmd.FlagSet.Parse(args)
configOption := cmd.FlagSet.Lookup("config-option").Value.String()
if configOption == "" {
fmt.Println("No config option specified")
return
}
newValue := cmd.FlagSet.Arg(0) // Assumes the new value is the first argument after flags
if newValue == "" {
// The new value should be the first argument after the flags
if len(cmd.FlagSet.Args()) == 0 {
fmt.Println("No new value specified for the config option")
return
}
newValue := cmd.FlagSet.Arg(0)
if err := EditConfigOption(configOption, newValue); err != nil {
fmt.Println("Error editing config option:", err)
return

View File

@ -0,0 +1,177 @@
package config
import (
"encoding/json"
"fmt"
"log"
"os"
"reflect"
"strconv"
"strings"
)
const configFilePath = "/etc/PixelRidge/BackGo/config.json"
type LogLevel int
const (
LogLevelDebug LogLevel = iota
LogLevelInfo
LogLevelWarning
LogLevelError
)
type Config struct {
LogLevel LogLevel
S3Enabled bool
S3ConnectionAddress string
S3AuthorizationInfo string
S3Region string
S3BucketInfo string
SMBEnabled bool
SMBConnectionAddress string
SMBAuthorizationInfo string
B2Enabled bool
B2ConnectionAddress string
B2AuthorizationInfo string
B2BucketInfo string
LocalStorageEnabled bool
LocalStorageDirectory string
APIServerBindAddress string
APIServerPort string
TransportServerBindAddress string
TransportServerPort string
Nodes []string
}
func LoadConfig() *Config {
return &Config{
LogLevel: getEnvAsLogLevel("LOG_LEVEL", LogLevelInfo),
S3Enabled: getEnvAsBool("S3_ENABLED", false),
S3ConnectionAddress: os.Getenv("S3_CONNECTION_ADDRESS"),
S3AuthorizationInfo: os.Getenv("S3_AUTHORIZATION_INFO"),
S3Region: os.Getenv("S3_REGION"),
S3BucketInfo: os.Getenv("S3_BUCKET_INFO"),
SMBEnabled: getEnvAsBool("SMB_ENABLED", false),
SMBConnectionAddress: os.Getenv("SMB_CONNECTION_ADDRESS"),
SMBAuthorizationInfo: os.Getenv("SMB_AUTHORIZATION_INFO"),
B2Enabled: getEnvAsBool("BACKBLAZE_B2_ENABLED", false),
B2ConnectionAddress: os.Getenv("B2_CONNECTION_ADDRESS"),
B2AuthorizationInfo: os.Getenv("B2_AUTHORIZATION_INFO"),
B2BucketInfo: os.Getenv("BACKBLAZE_B2_BUCKET_INFO"),
LocalStorageEnabled: getEnvAsBool("LOCAL_STORAGE_ENABLED", false),
LocalStorageDirectory: os.Getenv("LOCAL_STORAGE_DIRECTORY"),
APIServerBindAddress: os.Getenv("API_SERVER_BIND_ADDRESS"),
APIServerPort: os.Getenv("API_SERVER_PORT"),
TransportServerBindAddress: os.Getenv("TRANSPORT_SERVER_BIND_ADDRESS"),
TransportServerPort: os.Getenv("TRANSPORT_SERVER_PORT"),
Nodes: getEnvAsSlice("NODES", ",", []string{}),
}
}
func getEnvAsBool(name string, defaultVal bool) bool {
valStr := os.Getenv(name)
if valStr == "" {
return defaultVal
}
val, err := strconv.ParseBool(valStr)
if err != nil {
log.Printf("Error parsing boolean for %s: %v\n", name, err)
return defaultVal
}
return val
}
func getEnvAsSlice(name, sep string, defaultVal []string) []string {
valStr := os.Getenv(name)
if valStr == "" {
return defaultVal
}
return strings.Split(valStr, sep)
}
func getEnvAsLogLevel(name string, defaultVal LogLevel) LogLevel {
valStr := os.Getenv(name)
if valStr == "" {
return defaultVal
}
if strings.EqualFold(valStr, "debug") {
return LogLevelDebug
} else if strings.EqualFold(valStr, "info") {
return LogLevelInfo
} else if strings.EqualFold(valStr, "warning") {
return LogLevelWarning
} else if strings.EqualFold(valStr, "error") {
return LogLevelError
}
log.Printf("Unknown LOG_LEVEL '%s', defaulting to INFO.\n", valStr)
return LogLevelInfo
}
func UpdateConfig(key, value string) error {
config, err := LoadConfigFromFile()
if err != nil {
return fmt.Errorf("loading config: %v", err)
}
rConfig := reflect.ValueOf(config).Elem()
field := rConfig.FieldByNameFunc(func(s string) bool {
return strings.EqualFold(s, key)
})
if !field.IsValid() {
return fmt.Errorf("unknown configuration key: %s", key)
}
if !field.CanSet() {
return fmt.Errorf("cannot set configuration key: %s", key)
}
switch field.Kind() {
case reflect.String:
field.SetString(value)
case reflect.Bool:
boolVal, err := strconv.ParseBool(value)
if err != nil {
return fmt.Errorf("parsing boolean value for %s: %v", key, err)
}
field.SetBool(boolVal)
case reflect.Int:
intVal, err := strconv.Atoi(value)
if err != nil {
return fmt.Errorf("parsing integer value for %s: %v", key, err)
}
field.SetInt(int64(intVal))
default:
return fmt.Errorf("unsupported configuration type for: %s", key)
}
return SaveConfigToFile(config)
}
func LoadConfigFromFile() (*Config, error) {
file, err := os.ReadFile(configFilePath)
if err != nil {
return nil, err
}
var config Config
if err := json.Unmarshal(file, &config); err != nil {
return nil, err
}
return &config, nil
}
func SaveConfigToFile(config *Config) error {
data, err := json.MarshalIndent(config, "", " ")
if err != nil {
return err
}
return os.WriteFile(configFilePath, data, 0644)
}
func ReloadConfig() {
fmt.Println("Configuration reloaded")
}

View File

@ -0,0 +1,64 @@
package config
import (
"fmt"
"os"
"time"
)
// SetDefaultConfig sets the default configuration by writing to environment variables
// and create a default configuration file.
func SetDefaultConfig() error {
// Define default values with explanations
defaults := map[string]string{
// Logging level: DEBUG, INFO, WARNING, ERROR
"LOG_LEVEL": "INFO",
// API server configuration
"API_SERVER_BIND_ADDRESS": "0.0.0.0", // IP address to bind the API server
"API_SERVER_PORT": "6678", // Port for the API server
// Transport server configuration for internal communication
"TRANSPORT_SERVER_BIND_ADDRESS": "0.0.0.0", // IP address for the transport server
"TRANSPORT_SERVER_PORT": "6679", // Port for the transport server
// S3 storage configuration
"S3_ENABLED": "false", // Enable S3 storage (true/false)
"S3_CONNECTION_ADDRESS": "", // S3 connection address in the format {IP}:{PORT}
"S3_AUTHORIZATION_INFO": "", // S3 authorization information (e.g., access key and secret key)
"S3_REGION": "us-east-1", // AWS region for S3
"S3_BUCKET_INFO": "", // S3 bucket name
// SMB storage configuration
"SMB_ENABLED": "false", // Enable SMB storage (true/false)
"SMB_CONNECTION_ADDRESS": "", // SMB connection address in the format {IP}:{PORT}
"SMB_AUTHORIZATION_INFO": "", // SMB authorization information (e.g., username and password)
// BackBlaze B2 storage configuration
"B2_ENABLED": "false", // Enable BackBlaze B2 storage (true/false)
"B2_CONNECTION_ADDRESS":"", // BackBlaze B2 connection address in the format {IP}:{PORT}
"B2_AUTHORIZATION_INFO":"", // BackBlaze B2 authorization information
"B2_BUCKET_INFO": "", // BackBlaze B2 bucket name
// Local storage configuration
"LOCAL_STORAGE_ENABLED": "true", // Enable local storage (true/false)
"LOCAL_STORAGE_DIRECTORY": fmt.Sprintf("/etc/PixelRidge/BackGo/local-data-storage/%s-%d", os.Getenv("HOSTNAME"), time.Now().Unix()), // Path for local storage directory
// Nodes configuration for cluster setup
"NODES": "", // Comma-separated list of node addresses in the cluster
}
// Write defaults to environment variables
for key, value := range defaults {
if err := os.Setenv(key, value); err != nil {
return fmt.Errorf("failed to set default config for %s: %v", key, err)
}
}
// write these defaults to a file for persistence
// write to a JSON or YAML file
return nil
}
// include a function here to write the configuration to a file

29
go.mod
View File

@ -1,3 +1,32 @@
module pixelridgesoftworks.com/BackGo
go 1.22.0
require (
github.com/aws/aws-sdk-go-v2/config v1.27.9
github.com/aws/aws-sdk-go-v2/service/s3 v1.53.0
github.com/hirochachacha/go-smb2 v1.1.0
golang.org/x/crypto v0.21.0
)
require (
github.com/aws/aws-sdk-go-v2 v1.26.0 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.9 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.4 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.4 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.6 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.6 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.20.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.28.5 // indirect
github.com/aws/smithy-go v1.20.1 // indirect
github.com/geoffgarside/ber v1.1.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/text v0.14.0 // indirect
)

52
go.sum Normal file
View File

@ -0,0 +1,52 @@
github.com/aws/aws-sdk-go-v2 v1.26.0 h1:/Ce4OCiM3EkpW7Y+xUnfAFpchU78K7/Ug01sZni9PgA=
github.com/aws/aws-sdk-go-v2 v1.26.0/go.mod h1:35hUlJVYd+M++iLI3ALmVwMOyRYMmRqUXpTtRGW+K9I=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1 h1:gTK2uhtAPtFcdRRJilZPx8uJLL2J85xK11nKtWL0wfU=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1/go.mod h1:sxpLb+nZk7tIfCWChfd+h4QwHNUR57d8hA1cleTkjJo=
github.com/aws/aws-sdk-go-v2/config v1.27.9 h1:gRx/NwpNEFSk+yQlgmk1bmxxvQ5TyJ76CWXs9XScTqg=
github.com/aws/aws-sdk-go-v2/config v1.27.9/go.mod h1:dK1FQfpwpql83kbD873E9vz4FyAxuJtR22wzoXn3qq0=
github.com/aws/aws-sdk-go-v2/credentials v1.17.9 h1:N8s0/7yW+h8qR8WaRlPQeJ6czVMNQVNtNdUqf6cItao=
github.com/aws/aws-sdk-go-v2/credentials v1.17.9/go.mod h1:446YhIdmSV0Jf/SLafGZalQo+xr2iw7/fzXGDPTU1yQ=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.0 h1:af5YzcLf80tv4Em4jWVD75lpnOHSBkPUZxZfGkrI3HI=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.0/go.mod h1:nQ3how7DMnFMWiU1SpECohgC82fpn4cKZ875NDMmwtA=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.4 h1:0ScVK/4qZ8CIW0k8jOeFVsyS/sAiXpYxRBLolMkuLQM=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.4/go.mod h1:84KyjNZdHC6QZW08nfHI6yZgPd+qRgaWcYsyLUo3QY8=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.4 h1:sHmMWWX5E7guWEFQ9SVo6A3S4xpPrWnd77a6y4WM6PU=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.4/go.mod h1:WjpDrhWisWOIoS9n3nk67A3Ll1vfULJ9Kq6h29HTD48=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.4 h1:SIkD6T4zGQ+1YIit22wi37CGNkrE7mXV1vNA5VpI3TI=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.4/go.mod h1:XfeqbsG0HNedNs0GT+ju4Bs+pFAwsrlzcRdMvdNVf5s=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 h1:EyBZibRTVAs6ECHZOw5/wlylS9OcTzwyjeQMudmREjE=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1/go.mod h1:JKpmtYhhPs7D97NL/ltqz7yCkERFW5dOlHyVl66ZYF8=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.6 h1:NkHCgg0Ck86c5PTOzBZ0JRccI51suJDg5lgFtxBu1ek=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.6/go.mod h1:mjTpxjC8v4SeINTngrnKFgm2QUi+Jm+etTbCxh8W4uU=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.6 h1:b+E7zIUHMmcB4Dckjpkapoy47W6C9QBv/zoUP+Hn8Kc=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.6/go.mod h1:S2fNV0rxrP78NhPbCZeQgY8H9jdDMeGtwcfZIRxzBqU=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.4 h1:uDj2K47EM1reAYU9jVlQ1M5YENI1u6a/TxJpf6AeOLA=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.4/go.mod h1:XKCODf4RKHppc96c2EZBGV/oCUC7OClxAo2MEyg4pIk=
github.com/aws/aws-sdk-go-v2/service/s3 v1.53.0 h1:r3o2YsgW9zRcIP3Q0WCmttFVhTuugeKIvT5z9xDspc0=
github.com/aws/aws-sdk-go-v2/service/s3 v1.53.0/go.mod h1:w2E4f8PUfNtyjfL6Iu+mWI96FGttE03z3UdNcUEC4tA=
github.com/aws/aws-sdk-go-v2/service/sso v1.20.3 h1:mnbuWHOcM70/OFUlZZ5rcdfA8PflGXXiefU/O+1S3+8=
github.com/aws/aws-sdk-go-v2/service/sso v1.20.3/go.mod h1:5HFu51Elk+4oRBZVxmHrSds5jFXmFj8C3w7DVF2gnrs=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.3 h1:uLq0BKatTmDzWa/Nu4WO0M1AaQDaPpwTKAeByEc6WFM=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.3/go.mod h1:b+qdhjnxj8GSR6t5YfphOffeoQSQ1KmpoVVuBn+PWxs=
github.com/aws/aws-sdk-go-v2/service/sts v1.28.5 h1:J/PpTf/hllOjx8Xu9DMflff3FajfLxqM5+tepvVXmxg=
github.com/aws/aws-sdk-go-v2/service/sts v1.28.5/go.mod h1:0ih0Z83YDH/QeQ6Ori2yGE2XvWYv/Xm+cZc01LC6oK0=
github.com/aws/smithy-go v1.20.1 h1:4SZlSlMr36UEqC7XOyRVb27XMeZubNcBNN+9IgEPIQw=
github.com/aws/smithy-go v1.20.1/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/geoffgarside/ber v1.1.0 h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w=
github.com/geoffgarside/ber v1.1.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc=
github.com/hirochachacha/go-smb2 v1.1.0 h1:b6hs9qKIql9eVXAiN0M2wSFY5xnhbHAQoCwRKbaRTZI=
github.com/hirochachacha/go-smb2 v1.1.0/go.mod h1:8F1A4d5EZzrGu5R7PU163UcMRDJQl4FtcxjBfsY8TZE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=

View File

@ -0,0 +1,51 @@
package backup
import (
"crypto/tls"
"fmt"
"log"
"net/http"
"golang.org/x/crypto/acme/autocert"
)
// GenerateCertForDomain generates an SSL certificate for a domain using Let's Encrypt
func GenerateCertForDomain(domain string) {
certManager := autocert.Manager{
Prompt: autocert.AcceptTOS,
Cache: autocert.DirCache("certs"), // Stores certificates in ./certs
HostPolicy: autocert.HostWhitelist(domain), // Replace with your domain
}
server := &http.Server{
Addr: ":https", // Default HTTPS port
TLSConfig: &tls.Config{
GetCertificate: certManager.GetCertificate,
},
}
// Redirect HTTP to HTTPS
go http.ListenAndServe(":http", certManager.HTTPHandler(nil))
log.Printf("Starting HTTPS server for %s\n", domain)
log.Fatal(server.ListenAndServeTLS("", "")) // Keys are provided by Let's Encrypt
}
// HandleJoinRequest handles a join request from a node
func HandleJoinRequest(w http.ResponseWriter, r *http.Request) {
// TODO: Implement join request logic here
fmt.Fprintf(w, "Join request received\n")
}
// SendCommandToNode sends a CLI command to a specified node in the cluster
func SendCommandToNode(nodeAddress, command string) {
// TODO: Implement command sending logic here
fmt.Printf("Sending command '%s' to node %s\n", command, nodeAddress)
}
// StartMasterServer starts the master API server that listens for join requests and other commands
func StartMasterServer(domain string) {
http.HandleFunc("/join", HandleJoinRequest) // Endpoint for nodes to join the cluster
// Generate SSL certificate and start HTTPS server
GenerateCertForDomain(domain)
}

View File

@ -0,0 +1,54 @@
package backup
import (
"context"
"fmt"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
// S3Client wraps the AWS S3 client
type S3Client struct {
Client *s3.Client
Bucket string
}
// NewS3Client initializes and returns a new S3 client
func NewS3Client(bucket string) (*S3Client, error) {
cfg, err := config.LoadDefaultConfig(context.TODO(),
config.WithRegion("us-west-2"), // Specify your region
)
if err != nil {
return nil, fmt.Errorf("unable to load SDK config, %w", err)
}
s3Client := s3.NewFromConfig(cfg)
return &S3Client{
Client: s3Client,
Bucket: bucket,
}, nil
}
// UploadFile uploads a file to the specified S3 bucket
func (c *S3Client) UploadFile(key string, filePath string) error {
// Implementation for uploading file to S3
// need to open the file and use PutObject API call
return nil
}
// DeleteFile deletes a file from the specified S3 bucket
func (c *S3Client) DeleteFile(key string) error {
// Implementation for deleting file from S3 using DeleteObject API call
return nil
}
// ListFiles lists all files (backups) in the specified S3 bucket
func (c *S3Client) ListFiles() ([]string, error) {
// Implementation for listing files in S3 bucket using ListObjectsV2 API call
return nil, nil
}
// SetBucket sets the S3 bucket for the client
func (c *S3Client) SetBucket(bucket string) {
c.Bucket = bucket
}

View File

@ -0,0 +1,115 @@
package backup
import (
"fmt"
"io"
"net"
"os"
"github.com/hirochachacha/go-smb2"
)
// SMBClient wraps the SMB client and session
type SMBClient struct {
Share string
Session *smb2.Session
}
// NewSMBClient initializes and returns a new SMB client
func NewSMBClient(server, user, pass, domain, share string) (*SMBClient, error) {
conn, err := net.Dial("tcp", server)
if err != nil {
return nil, fmt.Errorf("failed to dial: %v", err)
}
defer conn.Close()
d := &smb2.Dialer{
Initiator: &smb2.NTLMInitiator{
User: user,
Password: pass,
Domain: domain,
},
}
session, err := d.Dial(conn)
if err != nil {
return nil, fmt.Errorf("failed to start SMB session: %v", err)
}
return &SMBClient{
Share: share,
Session: session,
}, nil
}
// UploadFile uploads a file to the specified SMB share
func (c *SMBClient) UploadFile(remotePath, localPath string) error {
fs, err := c.Session.Mount(c.Share)
if err != nil {
return fmt.Errorf("failed to mount share: %v", err)
}
defer fs.Umount()
localFile, err := os.Open(localPath)
if err != nil {
return fmt.Errorf("failed to open local file: %v", err)
}
defer localFile.Close()
remoteFile, err := fs.Create(remotePath)
if err != nil {
return fmt.Errorf("failed to create remote file: %v", err)
}
defer remoteFile.Close()
_, err = io.Copy(remoteFile, localFile)
if err != nil {
return fmt.Errorf("failed to write to remote file: %v", err)
}
return nil
}
// DeleteFile deletes a file from the specified SMB share
func (c *SMBClient) DeleteFile(remotePath string) error {
fs, err := c.Session.Mount(c.Share)
if err != nil {
return fmt.Errorf("failed to mount share: %v", err)
}
defer fs.Umount()
err = fs.Remove(remotePath)
if err != nil {
return fmt.Errorf("failed to delete remote file: %v", err)
}
return nil
}
// ListFiles lists all files (backups) in the specified SMB share
func (c *SMBClient) ListFiles(remoteDir string) ([]string, error) {
fs, err := c.Session.Mount(c.Share)
if err != nil {
return nil, fmt.Errorf("failed to mount share: %v", err)
}
defer fs.Umount()
entries, err := fs.ReadDir(remoteDir)
if err != nil {
return nil, fmt.Errorf("failed to read directory: %v", err)
}
var files []string
for _, entry := range entries {
if !entry.IsDir() {
files = append(files, entry.Name())
}
}
return files, nil
}
// SetShare sets the SMB share for the client
func (c *SMBClient) SetShare(share string) {
c.Share = share
}

View File

@ -0,0 +1,45 @@
package logger
import (
"io"
"log"
"os"
)
// Logger holds the log package's Logger instance.
type Logger struct {
*log.Logger
}
// NewLogger creates a new Logger instance. By default, it logs to stdout.
func NewLogger() *Logger {
return &Logger{
Logger: log.New(os.Stdout, "", log.LstdFlags),
}
}
// LogToFile configures the logger to log to a specified file.
func (l *Logger) LogToFile(filePath string) error {
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
return err
}
l.SetOutput(file)
return nil
}
// LogToStdout configures the logger to log to stdout.
func (l *Logger) LogToStdout() {
l.SetOutput(os.Stdout)
}
// LogToBoth configures the logger to log to both a file and stdout.
func (l *Logger) LogToBoth(filePath string) error {
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
return err
}
multi := io.MultiWriter(file, os.Stdout)
l.SetOutput(multi)
return nil
}

View File

@ -0,0 +1,4 @@
// Package utils contains utility functions for the application.
package utils
// This file serves as a placeholder for future utility methods.