detached 468d3ad

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
468d3ad4a43affbf1ef5a02b03ea949cb309dd0c
author
bnema · · 2023-10-24 09:36 UTC
parent
402d8ce
signature
unsigned
5 files · +151 -13
M cmd/cli/main.go +1 -1
@@ -6,6 +6,6 @@ import (
66 )
77
88 func main() {
9- a := app.NewServerApp()
9+ a := app.NewClientApp()
1010 cli.Execute(a)
1111 }
@@ -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 {
4646 }
4747
4848 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"`
5253 }
5354
5455 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 (
1111 "github.com/bnema/gordon/internal/common"
1212 )
1313
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-
2014 // PrepareJSONPayload marshals the payload into JSON
2115 func PrepareJSONPayload(payload common.Payload) ([]byte, error) {
2216 return json.Marshal(payload)
@@ -24,6 +18,7 @@ func PrepareJSONPayload(payload common.Payload) ([]byte, error) {
2418
2519 // CreateNewRequest creates a new HTTP request
2620 func CreateNewRequest(method, url string, body []byte) (*http.Request, error) {
21+
2722 return http.NewRequest(method, url, bytes.NewBuffer(body))
2823 }
2924
@@ -45,8 +40,7 @@ type Response struct {
4540
4641 // SendHTTPRequest sends the HTTP request
4742 func SendHTTPRequest(a *app.App, rp *common.RequestPayload, endpoint string) (*Response, error) {
48- apiUrl := GenerateAPIURL(a)
49-
43+ apiUrl := a.Config.Http.BackendURL + "/api"
5044 token := a.Config.General.GordonToken
5145
5246 // Prepare the entire RequestPayload, not just the inner Payload
@@ -54,7 +48,6 @@ func SendHTTPRequest(a *app.App, rp *common.RequestPayload, endpoint string) (*R
5448 if err != nil {
5549 return nil, fmt.Errorf("failed to marshal payload: %w", err)
5650 }
57-
5851 // Create a new request
5952 req, err := CreateNewRequest("GET", apiUrl+endpoint, jsonPayload)
6053 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 JSON14 // 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 request19 // 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 request41 // 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.GordonToken44 token := a.Config.General.GordonToken
51 45
52 // Prepare the entire RequestPayload, not just the inner Payload46 // 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 request51 // 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 {

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close