Separate config file logic for the server and the client
- The client now read the config file from the home user directory (.config/Gordon) - Create the file if not exist - If BackendURL and GordonToken are not present in the config file, a prompt will ask the user to enter them - Switch from ServerApp to ClientApp in main.go - Add BackendURL to HttpConfig struct in app.go - Remove GenerateAPIURL function, use BackendURL directly in http.go
- commit
- befdbe344198563bf0b238589a0d54d7c5e112a1
- author
- bnema · · 2023-10-24 09:36 UTC
- parent
- 1a8b60f
- signature
- unsigned
5 files · +151 -13
M cmd/cli/main.go +1 -1
| @@ -6,6 +6,6 @@ import ( | |||
| 6 | 6 | ) | |
| 7 | 7 | ||
| 8 | 8 | func main() { | |
| 9 | - | a := app.NewServerApp() | |
| 9 | + | a := app.NewClientApp() | |
| 10 | 10 | cli.Execute(a) | |
| 11 | 11 | } | |
| @@ -6,6 +6,6 @@ import ( | |||||
| 6 | ) | 6 | ) | ||
| 7 | 7 | ||||
| 8 | func main() { | 8 | func main() { | ||
| 9 | - | a := app.NewServerApp() | 9 | + | a := app.NewClientApp() |
| 10 | cli.Execute(a) | 10 | cli.Execute(a) | ||
| 11 | } | 11 | } | ||
M internal/app/app.go +4 -3
| @@ -46,9 +46,10 @@ type GeneralConfig struct { | |||
| 46 | 46 | } | |
| 47 | 47 | ||
| 48 | 48 | type HttpConfig struct { | |
| 49 | - | Port int `yaml:"port"` | |
| 50 | - | TopDomain string `yaml:"topDomain"` | |
| 51 | - | SubDomain string `yaml:"subDomain"` | |
| 49 | + | Port int `yaml:"port"` | |
| 50 | + | TopDomain string `yaml:"topDomain"` | |
| 51 | + | SubDomain string `yaml:"subDomain"` | |
| 52 | + | BackendURL string `yaml:"backendURL"` | |
| 52 | 53 | } | |
| 53 | 54 | ||
| 54 | 55 | type AdminConfig struct { | |
| @@ -46,9 +46,10 @@ type GeneralConfig struct { | |||||
| 46 | } | 46 | } | ||
| 47 | 47 | ||||
| 48 | type HttpConfig struct { | 48 | type HttpConfig struct { | ||
| 49 | - | Port int `yaml:"port"` | 49 | + | Port int `yaml:"port"` |
| 50 | - | TopDomain string `yaml:"topDomain"` | 50 | + | TopDomain string `yaml:"topDomain"` |
| 51 | - | SubDomain string `yaml:"subDomain"` | 51 | + | SubDomain string `yaml:"subDomain"` |
| 52 | + | BackendURL string `yaml:"backendURL"` | |||
| 52 | } | 53 | } | ||
| 53 | 54 | ||||
| 54 | type AdminConfig struct { | 55 | type AdminConfig struct { | ||
A internal/app/newclient.go +128 -0
| @@ -0,0 +1,128 @@ | |||
| 1 | + | package app | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "fmt" | |
| 5 | + | "log" | |
| 6 | + | "os" | |
| 7 | + | "os/user" | |
| 8 | + | "path/filepath" | |
| 9 | + | "runtime" | |
| 10 | + | ||
| 11 | + | "gopkg.in/yaml.v3" | |
| 12 | + | ) | |
| 13 | + | ||
| 14 | + | func NewClientApp() *App { | |
| 15 | + | // Initialize AppConfig | |
| 16 | + | config := &Config{} | |
| 17 | + | config, err := config.LoadClientConfig() | |
| 18 | + | if err != nil { | |
| 19 | + | log.Fatalf("Failed to load configuration: %v", err) | |
| 20 | + | } | |
| 21 | + | ||
| 22 | + | // Initialize App | |
| 23 | + | a := &App{ | |
| 24 | + | Config: *config, | |
| 25 | + | } | |
| 26 | + | return a | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | func (config *Config) LoadClientConfig() (*Config, error) { | |
| 30 | + | configFileName := "config.yml" | |
| 31 | + | usr, err := user.Current() | |
| 32 | + | if err != nil { | |
| 33 | + | return nil, fmt.Errorf("error getting user's home directory: %w", err) | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | var configDir string | |
| 37 | + | ||
| 38 | + | if runtime.GOOS == "windows" { | |
| 39 | + | configDir = filepath.Join(usr.HomeDir, "AppData", "Roaming", "Gordon") | |
| 40 | + | } else { | |
| 41 | + | configDir = filepath.Join(usr.HomeDir, ".config", "Gordon") | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | if err := os.MkdirAll(configDir, 0755); err != nil { | |
| 45 | + | return nil, fmt.Errorf("error creating configuration directory: %w", err) | |
| 46 | + | } | |
| 47 | + | ||
| 48 | + | configFilePath := filepath.Join(configDir, configFileName) | |
| 49 | + | ||
| 50 | + | if _, err := os.Stat(configFilePath); os.IsNotExist(err) { | |
| 51 | + | fmt.Printf("Config file not found, creating it at %s\n", configFilePath) | |
| 52 | + | defaultConfig := []byte("some: default\ndata: here\n") | |
| 53 | + | if err := os.WriteFile(configFilePath, defaultConfig, 0666); err != nil { | |
| 54 | + | return nil, fmt.Errorf("error writing default configuration: %w", err) | |
| 55 | + | } | |
| 56 | + | } else if err != nil { | |
| 57 | + | return nil, fmt.Errorf("error checking configuration file: %w", err) | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | // Read and unmarshal the config file | |
| 61 | + | configData, err := os.ReadFile(configFilePath) | |
| 62 | + | if err != nil { | |
| 63 | + | return nil, fmt.Errorf("error reading configuration file: %w", err) | |
| 64 | + | } | |
| 65 | + | ||
| 66 | + | err = yaml.Unmarshal(configData, &config) | |
| 67 | + | if err != nil { | |
| 68 | + | return nil, fmt.Errorf("error unmarshalling configuration file: %w", err) | |
| 69 | + | } | |
| 70 | + | ||
| 71 | + | // New logic for backend URL and token | |
| 72 | + | needsSave := false | |
| 73 | + | ||
| 74 | + | if config.Http.BackendURL == "" { | |
| 75 | + | fmt.Println("Please enter the backend URL:") | |
| 76 | + | var backendURL string | |
| 77 | + | fmt.Scanln(&backendURL) | |
| 78 | + | config.Http.BackendURL = backendURL | |
| 79 | + | needsSave = true | |
| 80 | + | } | |
| 81 | + | ||
| 82 | + | if config.General.GordonToken == "" { | |
| 83 | + | fmt.Println("Please enter the token:") | |
| 84 | + | var token string | |
| 85 | + | fmt.Scanln(&token) | |
| 86 | + | config.General.GordonToken = token | |
| 87 | + | needsSave = true | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | if needsSave { | |
| 91 | + | err := config.SaveConfig() // Assuming SaveConfig is a method that saves the config to file | |
| 92 | + | if err != nil { | |
| 93 | + | return nil, fmt.Errorf("error saving configuration file: %w", err) | |
| 94 | + | } | |
| 95 | + | } | |
| 96 | + | ||
| 97 | + | return config, nil | |
| 98 | + | } | |
| 99 | + | ||
| 100 | + | func (config *Config) SaveConfig() error { | |
| 101 | + | usr, err := user.Current() | |
| 102 | + | if err != nil { | |
| 103 | + | return fmt.Errorf("error getting user's home directory: %w", err) | |
| 104 | + | } | |
| 105 | + | ||
| 106 | + | var configDir string | |
| 107 | + | if runtime.GOOS == "windows" { | |
| 108 | + | configDir = filepath.Join(usr.HomeDir, "AppData", "Roaming", "Gordon") | |
| 109 | + | } else { | |
| 110 | + | configDir = filepath.Join(usr.HomeDir, ".config", "Gordon") | |
| 111 | + | } | |
| 112 | + | ||
| 113 | + | configFilePath := filepath.Join(configDir, "config.yml") | |
| 114 | + | ||
| 115 | + | // Marshal the config struct into YAML | |
| 116 | + | data, err := yaml.Marshal(config) | |
| 117 | + | if err != nil { | |
| 118 | + | return fmt.Errorf("failed to marshal config: %v", err) | |
| 119 | + | } | |
| 120 | + | ||
| 121 | + | // Write the data to the config file | |
| 122 | + | err = os.WriteFile(configFilePath, data, 0644) | |
| 123 | + | if err != nil { | |
| 124 | + | return fmt.Errorf("failed to write to config file: %v", err) | |
| 125 | + | } | |
| 126 | + | ||
| 127 | + | return nil | |
| 128 | + | } | |
| @@ -0,0 +1,128 @@ | |||||
| 1 | + | package app | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "fmt" | |||
| 5 | + | "log" | |||
| 6 | + | "os" | |||
| 7 | + | "os/user" | |||
| 8 | + | "path/filepath" | |||
| 9 | + | "runtime" | |||
| 10 | + | ||||
| 11 | + | "gopkg.in/yaml.v3" | |||
| 12 | + | ) | |||
| 13 | + | ||||
| 14 | + | func NewClientApp() *App { | |||
| 15 | + | // Initialize AppConfig | |||
| 16 | + | config := &Config{} | |||
| 17 | + | config, err := config.LoadClientConfig() | |||
| 18 | + | if err != nil { | |||
| 19 | + | log.Fatalf("Failed to load configuration: %v", err) | |||
| 20 | + | } | |||
| 21 | + | ||||
| 22 | + | // Initialize App | |||
| 23 | + | a := &App{ | |||
| 24 | + | Config: *config, | |||
| 25 | + | } | |||
| 26 | + | return a | |||
| 27 | + | } | |||
| 28 | + | ||||
| 29 | + | func (config *Config) LoadClientConfig() (*Config, error) { | |||
| 30 | + | configFileName := "config.yml" | |||
| 31 | + | usr, err := user.Current() | |||
| 32 | + | if err != nil { | |||
| 33 | + | return nil, fmt.Errorf("error getting user's home directory: %w", err) | |||
| 34 | + | } | |||
| 35 | + | ||||
| 36 | + | var configDir string | |||
| 37 | + | ||||
| 38 | + | if runtime.GOOS == "windows" { | |||
| 39 | + | configDir = filepath.Join(usr.HomeDir, "AppData", "Roaming", "Gordon") | |||
| 40 | + | } else { | |||
| 41 | + | configDir = filepath.Join(usr.HomeDir, ".config", "Gordon") | |||
| 42 | + | } | |||
| 43 | + | ||||
| 44 | + | if err := os.MkdirAll(configDir, 0755); err != nil { | |||
| 45 | + | return nil, fmt.Errorf("error creating configuration directory: %w", err) | |||
| 46 | + | } | |||
| 47 | + | ||||
| 48 | + | configFilePath := filepath.Join(configDir, configFileName) | |||
| 49 | + | ||||
| 50 | + | if _, err := os.Stat(configFilePath); os.IsNotExist(err) { | |||
| 51 | + | fmt.Printf("Config file not found, creating it at %s\n", configFilePath) | |||
| 52 | + | defaultConfig := []byte("some: default\ndata: here\n") | |||
| 53 | + | if err := os.WriteFile(configFilePath, defaultConfig, 0666); err != nil { | |||
| 54 | + | return nil, fmt.Errorf("error writing default configuration: %w", err) | |||
| 55 | + | } | |||
| 56 | + | } else if err != nil { | |||
| 57 | + | return nil, fmt.Errorf("error checking configuration file: %w", err) | |||
| 58 | + | } | |||
| 59 | + | ||||
| 60 | + | // Read and unmarshal the config file | |||
| 61 | + | configData, err := os.ReadFile(configFilePath) | |||
| 62 | + | if err != nil { | |||
| 63 | + | return nil, fmt.Errorf("error reading configuration file: %w", err) | |||
| 64 | + | } | |||
| 65 | + | ||||
| 66 | + | err = yaml.Unmarshal(configData, &config) | |||
| 67 | + | if err != nil { | |||
| 68 | + | return nil, fmt.Errorf("error unmarshalling configuration file: %w", err) | |||
| 69 | + | } | |||
| 70 | + | ||||
| 71 | + | // New logic for backend URL and token | |||
| 72 | + | needsSave := false | |||
| 73 | + | ||||
| 74 | + | if config.Http.BackendURL == "" { | |||
| 75 | + | fmt.Println("Please enter the backend URL:") | |||
| 76 | + | var backendURL string | |||
| 77 | + | fmt.Scanln(&backendURL) | |||
| 78 | + | config.Http.BackendURL = backendURL | |||
| 79 | + | needsSave = true | |||
| 80 | + | } | |||
| 81 | + | ||||
| 82 | + | if config.General.GordonToken == "" { | |||
| 83 | + | fmt.Println("Please enter the token:") | |||
| 84 | + | var token string | |||
| 85 | + | fmt.Scanln(&token) | |||
| 86 | + | config.General.GordonToken = token | |||
| 87 | + | needsSave = true | |||
| 88 | + | } | |||
| 89 | + | ||||
| 90 | + | if needsSave { | |||
| 91 | + | err := config.SaveConfig() // Assuming SaveConfig is a method that saves the config to file | |||
| 92 | + | if err != nil { | |||
| 93 | + | return nil, fmt.Errorf("error saving configuration file: %w", err) | |||
| 94 | + | } | |||
| 95 | + | } | |||
| 96 | + | ||||
| 97 | + | return config, nil | |||
| 98 | + | } | |||
| 99 | + | ||||
| 100 | + | func (config *Config) SaveConfig() error { | |||
| 101 | + | usr, err := user.Current() | |||
| 102 | + | if err != nil { | |||
| 103 | + | return fmt.Errorf("error getting user's home directory: %w", err) | |||
| 104 | + | } | |||
| 105 | + | ||||
| 106 | + | var configDir string | |||
| 107 | + | if runtime.GOOS == "windows" { | |||
| 108 | + | configDir = filepath.Join(usr.HomeDir, "AppData", "Roaming", "Gordon") | |||
| 109 | + | } else { | |||
| 110 | + | configDir = filepath.Join(usr.HomeDir, ".config", "Gordon") | |||
| 111 | + | } | |||
| 112 | + | ||||
| 113 | + | configFilePath := filepath.Join(configDir, "config.yml") | |||
| 114 | + | ||||
| 115 | + | // Marshal the config struct into YAML | |||
| 116 | + | data, err := yaml.Marshal(config) | |||
| 117 | + | if err != nil { | |||
| 118 | + | return fmt.Errorf("failed to marshal config: %v", err) | |||
| 119 | + | } | |||
| 120 | + | ||||
| 121 | + | // Write the data to the config file | |||
| 122 | + | err = os.WriteFile(configFilePath, data, 0644) | |||
| 123 | + | if err != nil { | |||
| 124 | + | return fmt.Errorf("failed to write to config file: %v", err) | |||
| 125 | + | } | |||
| 126 | + | ||||
| 127 | + | return nil | |||
| 128 | + | } | |||
A internal/cli/handler/configfile.go +16 -0
| @@ -0,0 +1,16 @@ | |||
| 1 | + | package handler | |
| 2 | + | ||
| 3 | + | import "github.com/bnema/gordon/internal/app" | |
| 4 | + | ||
| 5 | + | // I need to handle the case of the backend URL not being present in the config file | |
| 6 | + | func IsBackendURLPresent(a *app.App) bool { | |
| 7 | + | // since config.yml is loaded from newclient.go, I can access the backend URL from the app struct | |
| 8 | + | ||
| 9 | + | if a.Config.Http.BackendURL == "" { | |
| 10 | + | return false | |
| 11 | + | } | |
| 12 | + | ||
| 13 | + | return true | |
| 14 | + | } | |
| 15 | + | ||
| 16 | + | // I Need to handle the case the token is not present in the config file | |
| @@ -0,0 +1,16 @@ | |||||
| 1 | + | package handler | |||
| 2 | + | ||||
| 3 | + | import "github.com/bnema/gordon/internal/app" | |||
| 4 | + | ||||
| 5 | + | // I need to handle the case of the backend URL not being present in the config file | |||
| 6 | + | func IsBackendURLPresent(a *app.App) bool { | |||
| 7 | + | // since config.yml is loaded from newclient.go, I can access the backend URL from the app struct | |||
| 8 | + | ||||
| 9 | + | if a.Config.Http.BackendURL == "" { | |||
| 10 | + | return false | |||
| 11 | + | } | |||
| 12 | + | ||||
| 13 | + | return true | |||
| 14 | + | } | |||
| 15 | + | ||||
| 16 | + | // I Need to handle the case the token is not present in the config file | |||
M internal/cli/handler/http.go +2 -9
| @@ -11,12 +11,6 @@ import ( | |||
| 11 | 11 | "github.com/bnema/gordon/internal/common" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | - | // GenerateAPIURL creates the API URL | |
| 15 | - | func GenerateAPIURL(a *app.App) string { | |
| 16 | - | fmt.Println("Generating API URL:", a.Config.GenerateAPIURL()) | |
| 17 | - | return a.Config.GenerateAPIURL() | |
| 18 | - | } | |
| 19 | - | ||
| 20 | 14 | // PrepareJSONPayload marshals the payload into JSON | |
| 21 | 15 | func PrepareJSONPayload(payload common.Payload) ([]byte, error) { | |
| 22 | 16 | return json.Marshal(payload) | |
| @@ -24,6 +18,7 @@ func PrepareJSONPayload(payload common.Payload) ([]byte, error) { | |||
| 24 | 18 | ||
| 25 | 19 | // CreateNewRequest creates a new HTTP request | |
| 26 | 20 | func CreateNewRequest(method, url string, body []byte) (*http.Request, error) { | |
| 21 | + | ||
| 27 | 22 | return http.NewRequest(method, url, bytes.NewBuffer(body)) | |
| 28 | 23 | } | |
| 29 | 24 | ||
| @@ -45,8 +40,7 @@ type Response struct { | |||
| 45 | 40 | ||
| 46 | 41 | // SendHTTPRequest sends the HTTP request | |
| 47 | 42 | func SendHTTPRequest(a *app.App, rp *common.RequestPayload, endpoint string) (*Response, error) { | |
| 48 | - | apiUrl := GenerateAPIURL(a) | |
| 49 | - | ||
| 43 | + | apiUrl := a.Config.Http.BackendURL + "/api" | |
| 50 | 44 | token := a.Config.General.GordonToken | |
| 51 | 45 | ||
| 52 | 46 | // Prepare the entire RequestPayload, not just the inner Payload | |
| @@ -54,7 +48,6 @@ func SendHTTPRequest(a *app.App, rp *common.RequestPayload, endpoint string) (*R | |||
| 54 | 48 | if err != nil { | |
| 55 | 49 | return nil, fmt.Errorf("failed to marshal payload: %w", err) | |
| 56 | 50 | } | |
| 57 | - | ||
| 58 | 51 | // Create a new request | |
| 59 | 52 | req, err := CreateNewRequest("GET", apiUrl+endpoint, jsonPayload) | |
| 60 | 53 | if err != nil { | |
| @@ -11,12 +11,6 @@ import ( | |||||
| 11 | "github.com/bnema/gordon/internal/common" | 11 | "github.com/bnema/gordon/internal/common" | ||
| 12 | ) | 12 | ) | ||
| 13 | 13 | ||||
| 14 | - | // GenerateAPIURL creates the API URL | |||
| 15 | - | func GenerateAPIURL(a *app.App) string { | |||
| 16 | - | fmt.Println("Generating API URL:", a.Config.GenerateAPIURL()) | |||
| 17 | - | return a.Config.GenerateAPIURL() | |||
| 18 | - | } | |||
| 19 | - | ||||
| 20 | // PrepareJSONPayload marshals the payload into JSON | 14 | // PrepareJSONPayload marshals the payload into JSON | ||
| 21 | func PrepareJSONPayload(payload common.Payload) ([]byte, error) { | 15 | func PrepareJSONPayload(payload common.Payload) ([]byte, error) { | ||
| 22 | return json.Marshal(payload) | 16 | return json.Marshal(payload) | ||
| @@ -24,6 +18,7 @@ func PrepareJSONPayload(payload common.Payload) ([]byte, error) { | |||||
| 24 | 18 | ||||
| 25 | // CreateNewRequest creates a new HTTP request | 19 | // CreateNewRequest creates a new HTTP request | ||
| 26 | func CreateNewRequest(method, url string, body []byte) (*http.Request, error) { | 20 | func CreateNewRequest(method, url string, body []byte) (*http.Request, error) { | ||
| 21 | + | ||||
| 27 | return http.NewRequest(method, url, bytes.NewBuffer(body)) | 22 | return http.NewRequest(method, url, bytes.NewBuffer(body)) | ||
| 28 | } | 23 | } | ||
| 29 | 24 | ||||
| @@ -45,8 +40,7 @@ type Response struct { | |||||
| 45 | 40 | ||||
| 46 | // SendHTTPRequest sends the HTTP request | 41 | // SendHTTPRequest sends the HTTP request | ||
| 47 | func SendHTTPRequest(a *app.App, rp *common.RequestPayload, endpoint string) (*Response, error) { | 42 | func SendHTTPRequest(a *app.App, rp *common.RequestPayload, endpoint string) (*Response, error) { | ||
| 48 | - | apiUrl := GenerateAPIURL(a) | 43 | + | apiUrl := a.Config.Http.BackendURL + "/api" |
| 49 | - | ||||
| 50 | token := a.Config.General.GordonToken | 44 | token := a.Config.General.GordonToken | ||
| 51 | 45 | ||||
| 52 | // Prepare the entire RequestPayload, not just the inner Payload | 46 | // Prepare the entire RequestPayload, not just the inner Payload | ||
| @@ -54,7 +48,6 @@ func SendHTTPRequest(a *app.App, rp *common.RequestPayload, endpoint string) (*R | |||||
| 54 | if err != nil { | 48 | if err != nil { | ||
| 55 | return nil, fmt.Errorf("failed to marshal payload: %w", err) | 49 | return nil, fmt.Errorf("failed to marshal payload: %w", err) | ||
| 56 | } | 50 | } | ||
| 57 | - | ||||
| 58 | // Create a new request | 51 | // Create a new request | ||
| 59 | req, err := CreateNewRequest("GET", apiUrl+endpoint, jsonPayload) | 52 | req, err := CreateNewRequest("GET", apiUrl+endpoint, jsonPayload) | ||
| 60 | if err != nil { | 53 | if err != nil { | ||