Beginning of the CLI client.
- Create endpoint(s) on the backend to handle cli client requests - Add a middleware token who compare from the one in the config.yaml - Create basic CLI client with cobra - Create payload structure - Create basic hello world command
- commit
- 48a7c9467171cb431dfe75c78cf60b48d435aaee
- author
- bnema · · 2023-10-23 17:24 UTC
- parent
- 4191a13
- signature
- unsigned
20 files · +361 -189
M cmd/cli/main.go +3 -2
| @@ -1,10 +1,11 @@ | |||
| 1 | 1 | package main | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | + | "github.com/bnema/gordon/internal/app" | |
| 4 | 5 | "github.com/bnema/gordon/internal/cli" | |
| 5 | 6 | ) | |
| 6 | 7 | ||
| 7 | 8 | func main() { | |
| 8 | - | cli.InitCli() | |
| 9 | - | ||
| 9 | + | a := app.NewServerApp() | |
| 10 | + | cli.Execute(a) | |
| 10 | 11 | } | |
| @@ -1,10 +1,11 @@ | |||||
| 1 | package main | 1 | package main | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | + | "github.com/bnema/gordon/internal/app" | |||
| 4 | "github.com/bnema/gordon/internal/cli" | 5 | "github.com/bnema/gordon/internal/cli" | ||
| 5 | ) | 6 | ) | ||
| 6 | 7 | ||||
| 7 | func main() { | 8 | func main() { | ||
| 8 | - | cli.InitCli() | 9 | + | a := app.NewServerApp() |
| 9 | - | 10 | + | cli.Execute(a) | |
| 10 | } | 11 | } | ||
M cmd/srv/main.go +1 -1
| @@ -21,7 +21,7 @@ func cleanup(a *app.App) { | |||
| 21 | 21 | } | |
| 22 | 22 | ||
| 23 | 23 | func main() { | |
| 24 | - | a := app.NewApp() | |
| 24 | + | a := app.NewServerApp() | |
| 25 | 25 | ||
| 26 | 26 | _, err := app.InitializeDB(a) | |
| 27 | 27 | if err != nil { | |
| @@ -21,7 +21,7 @@ func cleanup(a *app.App) { | |||||
| 21 | } | 21 | } | ||
| 22 | 22 | ||||
| 23 | func main() { | 23 | func main() { | ||
| 24 | - | a := app.NewApp() | 24 | + | a := app.NewServerApp() |
| 25 | 25 | ||||
| 26 | _, err := app.InitializeDB(a) | 26 | _, err := app.InitializeDB(a) | ||
| 27 | if err != nil { | 27 | if err != nil { | ||
M go.mod +3 -13
| @@ -4,7 +4,6 @@ go 1.21.3 | |||
| 4 | 4 | ||
| 5 | 5 | require ( | |
| 6 | 6 | github.com/PuerkitoBio/goquery v1.8.1 | |
| 7 | - | github.com/charmbracelet/bubbletea v0.24.2 | |
| 8 | 7 | github.com/docker/docker v24.0.6+incompatible | |
| 9 | 8 | github.com/docker/go-connections v0.4.0 | |
| 10 | 9 | github.com/gorilla/sessions v1.2.1 | |
| @@ -13,24 +12,15 @@ require ( | |||
| 13 | 12 | github.com/labstack/echo/v4 v4.11.1 | |
| 14 | 13 | github.com/mattn/go-sqlite3 v1.14.17 | |
| 15 | 14 | github.com/microcosm-cc/bluemonday v1.0.25 | |
| 15 | + | github.com/spf13/cobra v1.7.0 | |
| 16 | 16 | gopkg.in/yaml.v3 v3.0.1 | |
| 17 | 17 | ) | |
| 18 | 18 | ||
| 19 | 19 | require ( | |
| 20 | - | github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect | |
| 21 | - | github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect | |
| 20 | + | github.com/inconshreveable/mousetrap v1.1.0 // indirect | |
| 22 | 21 | github.com/kr/text v0.2.0 // indirect | |
| 23 | - | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect | |
| 24 | - | github.com/mattn/go-localereader v0.0.1 // indirect | |
| 25 | - | github.com/mattn/go-runewidth v0.0.14 // indirect | |
| 26 | - | github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect | |
| 27 | - | github.com/muesli/cancelreader v0.2.2 // indirect | |
| 28 | - | github.com/muesli/reflow v0.3.0 // indirect | |
| 29 | - | github.com/muesli/termenv v0.15.1 // indirect | |
| 30 | - | github.com/rivo/uniseg v0.2.0 // indirect | |
| 22 | + | github.com/spf13/pflag v1.0.5 // indirect | |
| 31 | 23 | github.com/stretchr/testify v1.8.2 // indirect | |
| 32 | - | golang.org/x/sync v0.4.0 // indirect | |
| 33 | - | golang.org/x/term v0.13.0 // indirect | |
| 34 | 24 | ) | |
| 35 | 25 | ||
| 36 | 26 | require ( | |
| @@ -4,7 +4,6 @@ go 1.21.3 | |||||
| 4 | 4 | ||||
| 5 | require ( | 5 | require ( | ||
| 6 | github.com/PuerkitoBio/goquery v1.8.1 | 6 | github.com/PuerkitoBio/goquery v1.8.1 | ||
| 7 | - | github.com/charmbracelet/bubbletea v0.24.2 | |||
| 8 | github.com/docker/docker v24.0.6+incompatible | 7 | github.com/docker/docker v24.0.6+incompatible | ||
| 9 | github.com/docker/go-connections v0.4.0 | 8 | github.com/docker/go-connections v0.4.0 | ||
| 10 | github.com/gorilla/sessions v1.2.1 | 9 | github.com/gorilla/sessions v1.2.1 | ||
| @@ -13,24 +12,15 @@ require ( | |||||
| 13 | github.com/labstack/echo/v4 v4.11.1 | 12 | github.com/labstack/echo/v4 v4.11.1 | ||
| 14 | github.com/mattn/go-sqlite3 v1.14.17 | 13 | github.com/mattn/go-sqlite3 v1.14.17 | ||
| 15 | github.com/microcosm-cc/bluemonday v1.0.25 | 14 | github.com/microcosm-cc/bluemonday v1.0.25 | ||
| 15 | + | github.com/spf13/cobra v1.7.0 | |||
| 16 | gopkg.in/yaml.v3 v3.0.1 | 16 | gopkg.in/yaml.v3 v3.0.1 | ||
| 17 | ) | 17 | ) | ||
| 18 | 18 | ||||
| 19 | require ( | 19 | require ( | ||
| 20 | - | github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect | 20 | + | github.com/inconshreveable/mousetrap v1.1.0 // indirect |
| 21 | - | github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect | |||
| 22 | github.com/kr/text v0.2.0 // indirect | 21 | github.com/kr/text v0.2.0 // indirect | ||
| 23 | - | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect | 22 | + | github.com/spf13/pflag v1.0.5 // indirect |
| 24 | - | github.com/mattn/go-localereader v0.0.1 // indirect | |||
| 25 | - | github.com/mattn/go-runewidth v0.0.14 // indirect | |||
| 26 | - | github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect | |||
| 27 | - | github.com/muesli/cancelreader v0.2.2 // indirect | |||
| 28 | - | github.com/muesli/reflow v0.3.0 // indirect | |||
| 29 | - | github.com/muesli/termenv v0.15.1 // indirect | |||
| 30 | - | github.com/rivo/uniseg v0.2.0 // indirect | |||
| 31 | github.com/stretchr/testify v1.8.2 // indirect | 23 | github.com/stretchr/testify v1.8.2 // indirect | ||
| 32 | - | golang.org/x/sync v0.4.0 // indirect | |||
| 33 | - | golang.org/x/term v0.13.0 // indirect | |||
| 34 | ) | 24 | ) | ||
| 35 | 25 | ||||
| 36 | require ( | 26 | require ( | ||
M go.sum +8 -27 generated file: collapsed
M internal/app/app.go +29 -48
| @@ -3,14 +3,10 @@ package app | |||
| 3 | 3 | import ( | |
| 4 | 4 | "database/sql" | |
| 5 | 5 | "fmt" | |
| 6 | - | "io" | |
| 7 | 6 | "io/fs" | |
| 8 | - | "log" | |
| 9 | 7 | "os" | |
| 10 | 8 | ||
| 11 | 9 | "github.com/bnema/gordon/internal/db" | |
| 12 | - | "github.com/bnema/gordon/internal/templating" | |
| 13 | - | "github.com/bnema/gordon/internal/webui" | |
| 14 | 10 | "github.com/bnema/gordon/pkg/docker" | |
| 15 | 11 | _ "github.com/joho/godotenv/autoload" | |
| 16 | 12 | "gopkg.in/yaml.v3" | |
| @@ -97,50 +93,6 @@ func LoadConfig(config *Config) (*Config, error) { | |||
| 97 | 93 | return config, nil | |
| 98 | 94 | } | |
| 99 | 95 | ||
| 100 | - | func NewApp() *App { | |
| 101 | - | // Initialize AppConfig | |
| 102 | - | config := &Config{} | |
| 103 | - | _, err := LoadConfig(config) | |
| 104 | - | if err != nil { | |
| 105 | - | log.Fatalf("Failed to load configuration: %v", err) | |
| 106 | - | } | |
| 107 | - | // Append build dir to storage dir | |
| 108 | - | config.General.StorageDir = fmt.Sprintf("%s/%s", config.General.BuildDir, config.General.StorageDir) | |
| 109 | - | ||
| 110 | - | // Open the strings.yml file containing the strings for the current language | |
| 111 | - | file, err := templating.TemplateFS.Open("locstrings.yml") | |
| 112 | - | if err != nil { | |
| 113 | - | log.Fatalf("Failed to open strings.yml: %v", err) | |
| 114 | - | } | |
| 115 | - | ||
| 116 | - | // Read the file content into a byte slice | |
| 117 | - | bytes, err := io.ReadAll(file) | |
| 118 | - | if err != nil { | |
| 119 | - | log.Fatalf("Failed to read strings.yml: %v", err) | |
| 120 | - | } | |
| 121 | - | ||
| 122 | - | file.Close() | |
| 123 | - | ||
| 124 | - | // Initialize App | |
| 125 | - | a := &App{ | |
| 126 | - | TemplateFS: templating.TemplateFS, | |
| 127 | - | PublicFS: webui.PublicFS, | |
| 128 | - | LocYML: bytes, | |
| 129 | - | DBDir: DBDir, | |
| 130 | - | DBFilename: DBFilename, | |
| 131 | - | Config: *config, | |
| 132 | - | } | |
| 133 | - | ||
| 134 | - | // DBTables: DBTables{ | |
| 135 | - | // User: db.User{}, | |
| 136 | - | // Account: db.Account{}, | |
| 137 | - | // Sessions: db.Sessions{}, | |
| 138 | - | // Provider: db.Provider{}, | |
| 139 | - | ||
| 140 | - | OauthCallbackURL = config.GenerateOauthCallbackURL() | |
| 141 | - | return a | |
| 142 | - | } | |
| 143 | - | ||
| 144 | 96 | // NewDockerConfig creates and returns a new Docker client configuration | |
| 145 | 97 | func (config *Config) NewDockerConfig() *docker.Config { | |
| 146 | 98 | if config.ContainerEngine.Podman { | |
| @@ -175,6 +127,25 @@ func (config *Config) GenerateOauthCallbackURL() string { | |||
| 175 | 127 | return fmt.Sprintf("%s://%s%s%s/login/oauth/callback", scheme, domain, port, config.Admin.Path) | |
| 176 | 128 | } | |
| 177 | 129 | ||
| 130 | + | func (config *Config) GenerateAPIURL() string { | |
| 131 | + | var scheme, port string | |
| 132 | + | ||
| 133 | + | if config.General.RunEnv == "dev" { | |
| 134 | + | scheme = "http" | |
| 135 | + | port = fmt.Sprintf(":%d", config.Http.Port) | |
| 136 | + | } else { // Assuming "prod" | |
| 137 | + | scheme = "https" | |
| 138 | + | port = "" | |
| 139 | + | } | |
| 140 | + | ||
| 141 | + | domain := config.Http.TopDomain | |
| 142 | + | if config.Http.SubDomain != "" { | |
| 143 | + | domain = fmt.Sprintf("%s.%s", config.Http.SubDomain, config.Http.TopDomain) | |
| 144 | + | } | |
| 145 | + | ||
| 146 | + | return fmt.Sprintf("%s://%s%s/api", scheme, domain, port) | |
| 147 | + | } | |
| 148 | + | ||
| 178 | 149 | // UpdateConfig updates the config file | |
| 179 | 150 | func (config *Config) UpdateConfig() error { | |
| 180 | 151 | // Marshal the config struct into YAML | |
| @@ -191,3 +162,13 @@ func (config *Config) UpdateConfig() error { | |||
| 191 | 162 | ||
| 192 | 163 | return nil | |
| 193 | 164 | } | |
| 165 | + | ||
| 166 | + | func (config *Config) GetToken() (string, error) { | |
| 167 | + | token := config.General.GordonToken | |
| 168 | + | if token == "" { | |
| 169 | + | return "", fmt.Errorf("no token found in config.yml") | |
| 170 | + | } | |
| 171 | + | ||
| 172 | + | return token, nil | |
| 173 | + | ||
| 174 | + | } | |
| @@ -3,14 +3,10 @@ package app | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "database/sql" | 4 | "database/sql" | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | - | "io" | |||
| 7 | "io/fs" | 6 | "io/fs" | ||
| 8 | - | "log" | |||
| 9 | "os" | 7 | "os" | ||
| 10 | 8 | ||||
| 11 | "github.com/bnema/gordon/internal/db" | 9 | "github.com/bnema/gordon/internal/db" | ||
| 12 | - | "github.com/bnema/gordon/internal/templating" | |||
| 13 | - | "github.com/bnema/gordon/internal/webui" | |||
| 14 | "github.com/bnema/gordon/pkg/docker" | 10 | "github.com/bnema/gordon/pkg/docker" | ||
| 15 | _ "github.com/joho/godotenv/autoload" | 11 | _ "github.com/joho/godotenv/autoload" | ||
| 16 | "gopkg.in/yaml.v3" | 12 | "gopkg.in/yaml.v3" | ||
| @@ -97,50 +93,6 @@ func LoadConfig(config *Config) (*Config, error) { | |||||
| 97 | return config, nil | 93 | return config, nil | ||
| 98 | } | 94 | } | ||
| 99 | 95 | ||||
| 100 | - | func NewApp() *App { | |||
| 101 | - | // Initialize AppConfig | |||
| 102 | - | config := &Config{} | |||
| 103 | - | _, err := LoadConfig(config) | |||
| 104 | - | if err != nil { | |||
| 105 | - | log.Fatalf("Failed to load configuration: %v", err) | |||
| 106 | - | } | |||
| 107 | - | // Append build dir to storage dir | |||
| 108 | - | config.General.StorageDir = fmt.Sprintf("%s/%s", config.General.BuildDir, config.General.StorageDir) | |||
| 109 | - | ||||
| 110 | - | // Open the strings.yml file containing the strings for the current language | |||
| 111 | - | file, err := templating.TemplateFS.Open("locstrings.yml") | |||
| 112 | - | if err != nil { | |||
| 113 | - | log.Fatalf("Failed to open strings.yml: %v", err) | |||
| 114 | - | } | |||
| 115 | - | ||||
| 116 | - | // Read the file content into a byte slice | |||
| 117 | - | bytes, err := io.ReadAll(file) | |||
| 118 | - | if err != nil { | |||
| 119 | - | log.Fatalf("Failed to read strings.yml: %v", err) | |||
| 120 | - | } | |||
| 121 | - | ||||
| 122 | - | file.Close() | |||
| 123 | - | ||||
| 124 | - | // Initialize App | |||
| 125 | - | a := &App{ | |||
| 126 | - | TemplateFS: templating.TemplateFS, | |||
| 127 | - | PublicFS: webui.PublicFS, | |||
| 128 | - | LocYML: bytes, | |||
| 129 | - | DBDir: DBDir, | |||
| 130 | - | DBFilename: DBFilename, | |||
| 131 | - | Config: *config, | |||
| 132 | - | } | |||
| 133 | - | ||||
| 134 | - | // DBTables: DBTables{ | |||
| 135 | - | // User: db.User{}, | |||
| 136 | - | // Account: db.Account{}, | |||
| 137 | - | // Sessions: db.Sessions{}, | |||
| 138 | - | // Provider: db.Provider{}, | |||
| 139 | - | ||||
| 140 | - | OauthCallbackURL = config.GenerateOauthCallbackURL() | |||
| 141 | - | return a | |||
| 142 | - | } | |||
| 143 | - | ||||
| 144 | // NewDockerConfig creates and returns a new Docker client configuration | 96 | // NewDockerConfig creates and returns a new Docker client configuration | ||
| 145 | func (config *Config) NewDockerConfig() *docker.Config { | 97 | func (config *Config) NewDockerConfig() *docker.Config { | ||
| 146 | if config.ContainerEngine.Podman { | 98 | if config.ContainerEngine.Podman { | ||
| @@ -175,6 +127,25 @@ func (config *Config) GenerateOauthCallbackURL() string { | |||||
| 175 | return fmt.Sprintf("%s://%s%s%s/login/oauth/callback", scheme, domain, port, config.Admin.Path) | 127 | return fmt.Sprintf("%s://%s%s%s/login/oauth/callback", scheme, domain, port, config.Admin.Path) | ||
| 176 | } | 128 | } | ||
| 177 | 129 | ||||
| 130 | + | func (config *Config) GenerateAPIURL() string { | |||
| 131 | + | var scheme, port string | |||
| 132 | + | ||||
| 133 | + | if config.General.RunEnv == "dev" { | |||
| 134 | + | scheme = "http" | |||
| 135 | + | port = fmt.Sprintf(":%d", config.Http.Port) | |||
| 136 | + | } else { // Assuming "prod" | |||
| 137 | + | scheme = "https" | |||
| 138 | + | port = "" | |||
| 139 | + | } | |||
| 140 | + | ||||
| 141 | + | domain := config.Http.TopDomain | |||
| 142 | + | if config.Http.SubDomain != "" { | |||
| 143 | + | domain = fmt.Sprintf("%s.%s", config.Http.SubDomain, config.Http.TopDomain) | |||
| 144 | + | } | |||
| 145 | + | ||||
| 146 | + | return fmt.Sprintf("%s://%s%s/api", scheme, domain, port) | |||
| 147 | + | } | |||
| 148 | + | ||||
| 178 | // UpdateConfig updates the config file | 149 | // UpdateConfig updates the config file | ||
| 179 | func (config *Config) UpdateConfig() error { | 150 | func (config *Config) UpdateConfig() error { | ||
| 180 | // Marshal the config struct into YAML | 151 | // Marshal the config struct into YAML | ||
| @@ -191,3 +162,13 @@ func (config *Config) UpdateConfig() error { | |||||
| 191 | 162 | ||||
| 192 | return nil | 163 | return nil | ||
| 193 | } | 164 | } | ||
| 165 | + | ||||
| 166 | + | func (config *Config) GetToken() (string, error) { | |||
| 167 | + | token := config.General.GordonToken | |||
| 168 | + | if token == "" { | |||
| 169 | + | return "", fmt.Errorf("no token found in config.yml") | |||
| 170 | + | } | |||
| 171 | + | ||||
| 172 | + | return token, nil | |||
| 173 | + | ||||
| 174 | + | } | |||
A internal/app/newserver.go +48 -0
| @@ -0,0 +1,48 @@ | |||
| 1 | + | package app | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "fmt" | |
| 5 | + | "io" | |
| 6 | + | "log" | |
| 7 | + | ||
| 8 | + | "github.com/bnema/gordon/internal/templating" | |
| 9 | + | "github.com/bnema/gordon/internal/webui" | |
| 10 | + | ) | |
| 11 | + | ||
| 12 | + | func NewServerApp() *App { | |
| 13 | + | // Initialize AppConfig | |
| 14 | + | config := &Config{} | |
| 15 | + | _, err := LoadConfig(config) | |
| 16 | + | if err != nil { | |
| 17 | + | log.Fatalf("Failed to load configuration: %v", err) | |
| 18 | + | } | |
| 19 | + | // Append build dir to storage dir | |
| 20 | + | config.General.StorageDir = fmt.Sprintf("%s/%s", config.General.BuildDir, config.General.StorageDir) | |
| 21 | + | ||
| 22 | + | // Open the strings.yml file containing the strings for the current language | |
| 23 | + | file, err := templating.TemplateFS.Open("locstrings.yml") | |
| 24 | + | if err != nil { | |
| 25 | + | log.Fatalf("Failed to open strings.yml: %v", err) | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | // Read the file content into a byte slice | |
| 29 | + | bytes, err := io.ReadAll(file) | |
| 30 | + | if err != nil { | |
| 31 | + | log.Fatalf("Failed to read strings.yml: %v", err) | |
| 32 | + | } | |
| 33 | + | ||
| 34 | + | file.Close() | |
| 35 | + | ||
| 36 | + | // Initialize App | |
| 37 | + | a := &App{ | |
| 38 | + | TemplateFS: templating.TemplateFS, | |
| 39 | + | PublicFS: webui.PublicFS, | |
| 40 | + | LocYML: bytes, | |
| 41 | + | DBDir: DBDir, | |
| 42 | + | DBFilename: DBFilename, | |
| 43 | + | Config: *config, | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | OauthCallbackURL = config.GenerateOauthCallbackURL() | |
| 47 | + | return a | |
| 48 | + | } | |
| @@ -0,0 +1,48 @@ | |||||
| 1 | + | package app | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "fmt" | |||
| 5 | + | "io" | |||
| 6 | + | "log" | |||
| 7 | + | ||||
| 8 | + | "github.com/bnema/gordon/internal/templating" | |||
| 9 | + | "github.com/bnema/gordon/internal/webui" | |||
| 10 | + | ) | |||
| 11 | + | ||||
| 12 | + | func NewServerApp() *App { | |||
| 13 | + | // Initialize AppConfig | |||
| 14 | + | config := &Config{} | |||
| 15 | + | _, err := LoadConfig(config) | |||
| 16 | + | if err != nil { | |||
| 17 | + | log.Fatalf("Failed to load configuration: %v", err) | |||
| 18 | + | } | |||
| 19 | + | // Append build dir to storage dir | |||
| 20 | + | config.General.StorageDir = fmt.Sprintf("%s/%s", config.General.BuildDir, config.General.StorageDir) | |||
| 21 | + | ||||
| 22 | + | // Open the strings.yml file containing the strings for the current language | |||
| 23 | + | file, err := templating.TemplateFS.Open("locstrings.yml") | |||
| 24 | + | if err != nil { | |||
| 25 | + | log.Fatalf("Failed to open strings.yml: %v", err) | |||
| 26 | + | } | |||
| 27 | + | ||||
| 28 | + | // Read the file content into a byte slice | |||
| 29 | + | bytes, err := io.ReadAll(file) | |||
| 30 | + | if err != nil { | |||
| 31 | + | log.Fatalf("Failed to read strings.yml: %v", err) | |||
| 32 | + | } | |||
| 33 | + | ||||
| 34 | + | file.Close() | |||
| 35 | + | ||||
| 36 | + | // Initialize App | |||
| 37 | + | a := &App{ | |||
| 38 | + | TemplateFS: templating.TemplateFS, | |||
| 39 | + | PublicFS: webui.PublicFS, | |||
| 40 | + | LocYML: bytes, | |||
| 41 | + | DBDir: DBDir, | |||
| 42 | + | DBFilename: DBFilename, | |||
| 43 | + | Config: *config, | |||
| 44 | + | } | |||
| 45 | + | ||||
| 46 | + | OauthCallbackURL = config.GenerateOauthCallbackURL() | |||
| 47 | + | return a | |||
| 48 | + | } | |||
M internal/cli/cli.go +9 -9
| @@ -1,18 +1,18 @@ | |||
| 1 | 1 | package cli | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | - | "log" | |
| 5 | - | ||
| 4 | + | "github.com/bnema/gordon/internal/app" | |
| 6 | 5 | "github.com/bnema/gordon/internal/cli/cmd" | |
| 7 | - | "github.com/bnema/gordon/internal/cli/mvu" | |
| 8 | - | tea "github.com/charmbracelet/bubbletea" | |
| 6 | + | "github.com/spf13/cobra" | |
| 9 | 7 | ) | |
| 10 | 8 | ||
| 9 | + | var rootCmd = &cobra.Command{Use: "gordon"} | |
| 11 | 10 | ||
| 11 | + | func InitializeCommands(a *app.App) { | |
| 12 | + | rootCmd.AddCommand(cmd.NewHelloCommand(a)) | |
| 13 | + | } | |
| 12 | 14 | ||
| 13 | - | func InitCli(a *app.App, m *mvu.Model)) { | |
| 14 | - | p := tea.NewProgram(Init, mvu.Init, mvu.Update, mvu.View) | |
| 15 | - | if _, err := p.Run(); err != nil { | |
| 16 | - | log.Fatal(err) | |
| 17 | - | } | |
| 15 | + | func Execute(a *app.App) { | |
| 16 | + | InitializeCommands(a) | |
| 17 | + | cobra.CheckErr(rootCmd.Execute()) | |
| 18 | 18 | } | |
| @@ -1,18 +1,18 @@ | |||||
| 1 | package cli | 1 | package cli | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | - | "log" | 4 | + | "github.com/bnema/gordon/internal/app" |
| 5 | - | ||||
| 6 | "github.com/bnema/gordon/internal/cli/cmd" | 5 | "github.com/bnema/gordon/internal/cli/cmd" | ||
| 7 | - | "github.com/bnema/gordon/internal/cli/mvu" | 6 | + | "github.com/spf13/cobra" |
| 8 | - | tea "github.com/charmbracelet/bubbletea" | |||
| 9 | ) | 7 | ) | ||
| 10 | 8 | ||||
| 9 | + | var rootCmd = &cobra.Command{Use: "gordon"} | |||
| 11 | 10 | ||||
| 11 | + | func InitializeCommands(a *app.App) { | |||
| 12 | + | rootCmd.AddCommand(cmd.NewHelloCommand(a)) | |||
| 13 | + | } | |||
| 12 | 14 | ||||
| 13 | - | func InitCli(a *app.App, m *mvu.Model)) { | 15 | + | func Execute(a *app.App) { |
| 14 | - | p := tea.NewProgram(Init, mvu.Init, mvu.Update, mvu.View) | 16 | + | InitializeCommands(a) |
| 15 | - | if _, err := p.Run(); err != nil { | 17 | + | cobra.CheckErr(rootCmd.Execute()) |
| 16 | - | log.Fatal(err) | |||
| 17 | - | } | |||
| 18 | } | 18 | } | ||
R internal/cli/cmd/update.go → internal/cli/cmd/_update.go +0 -0 renamed, 100% similar
renamed, 100% similar
M internal/cli/cmd/checkserver.go +0 -27
| @@ -1,28 +1,1 @@ | |||
| 1 | 1 | package cmd | |
| 2 | - | ||
| 3 | - | import ( | |
| 4 | - | "fmt" | |
| 5 | - | "net/http" | |
| 6 | - | "time" | |
| 7 | - | ||
| 8 | - | "github.com/bnema/gordon/internal/app" | |
| 9 | - | tea "github.com/charmbracelet/bubbletea" | |
| 10 | - | ) | |
| 11 | - | ||
| 12 | - | type statusMsg int | |
| 13 | - | type timeout time.Duration | |
| 14 | - | ||
| 15 | - | func CheckServer(a *app.App) tea.Msg { | |
| 16 | - | url := a.Config.Admin.Path | |
| 17 | - | c := &http.Client{ | |
| 18 | - | Timeout: 10 * time.Second, | |
| 19 | - | } | |
| 20 | - | res, err := c.Get(url) | |
| 21 | - | if err != nil { | |
| 22 | - | fmt.Println(err) | |
| 23 | - | return mvu.ErrMsg{Err: err} | |
| 24 | - | } | |
| 25 | - | defer res.Body.Close() // nolint:errcheck | |
| 26 | - | ||
| 27 | - | return statusMsg(res.StatusCode) | |
| 28 | - | } | |
| @@ -1,28 +1,1 @@ | |||||
| 1 | package cmd | 1 | package cmd | ||
| 2 | - | ||||
| 3 | - | import ( | |||
| 4 | - | "fmt" | |||
| 5 | - | "net/http" | |||
| 6 | - | "time" | |||
| 7 | - | ||||
| 8 | - | "github.com/bnema/gordon/internal/app" | |||
| 9 | - | tea "github.com/charmbracelet/bubbletea" | |||
| 10 | - | ) | |||
| 11 | - | ||||
| 12 | - | type statusMsg int | |||
| 13 | - | type timeout time.Duration | |||
| 14 | - | ||||
| 15 | - | func CheckServer(a *app.App) tea.Msg { | |||
| 16 | - | url := a.Config.Admin.Path | |||
| 17 | - | c := &http.Client{ | |||
| 18 | - | Timeout: 10 * time.Second, | |||
| 19 | - | } | |||
| 20 | - | res, err := c.Get(url) | |||
| 21 | - | if err != nil { | |||
| 22 | - | fmt.Println(err) | |||
| 23 | - | return mvu.ErrMsg{Err: err} | |||
| 24 | - | } | |||
| 25 | - | defer res.Body.Close() // nolint:errcheck | |||
| 26 | - | ||||
| 27 | - | return statusMsg(res.StatusCode) | |||
| 28 | - | } | |||
A internal/cli/cmd/hello.go +33 -0
| @@ -0,0 +1,33 @@ | |||
| 1 | + | package cmd | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "fmt" | |
| 5 | + | ||
| 6 | + | "github.com/bnema/gordon/internal/app" | |
| 7 | + | "github.com/bnema/gordon/internal/cli/handler" | |
| 8 | + | "github.com/bnema/gordon/internal/common" | |
| 9 | + | "github.com/spf13/cobra" | |
| 10 | + | ) | |
| 11 | + | ||
| 12 | + | func NewHelloCommand(a *app.App) *cobra.Command { | |
| 13 | + | return &cobra.Command{ | |
| 14 | + | Use: "hello", | |
| 15 | + | Short: "Send a hello request to the backend", | |
| 16 | + | Run: func(cmd *cobra.Command, args []string) { | |
| 17 | + | data := map[string]interface{}{ | |
| 18 | + | "message": "Hello, World!", | |
| 19 | + | } | |
| 20 | + | payload, err := common.CreatePayload("hello", data) | |
| 21 | + | if err != nil { | |
| 22 | + | fmt.Println("Error creating payload:", err) | |
| 23 | + | return | |
| 24 | + | } | |
| 25 | + | ||
| 26 | + | // Use your handler.SendHTTPRequest function here | |
| 27 | + | // Assuming that the SendHTTPRequest is moved to a package that can be imported here | |
| 28 | + | handler.SendHTTPRequest(a, payload) | |
| 29 | + | ||
| 30 | + | fmt.Printf("Sending payload to backend: %+v\n", payload) | |
| 31 | + | }, | |
| 32 | + | } | |
| 33 | + | } | |
| @@ -0,0 +1,33 @@ | |||||
| 1 | + | package cmd | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "fmt" | |||
| 5 | + | ||||
| 6 | + | "github.com/bnema/gordon/internal/app" | |||
| 7 | + | "github.com/bnema/gordon/internal/cli/handler" | |||
| 8 | + | "github.com/bnema/gordon/internal/common" | |||
| 9 | + | "github.com/spf13/cobra" | |||
| 10 | + | ) | |||
| 11 | + | ||||
| 12 | + | func NewHelloCommand(a *app.App) *cobra.Command { | |||
| 13 | + | return &cobra.Command{ | |||
| 14 | + | Use: "hello", | |||
| 15 | + | Short: "Send a hello request to the backend", | |||
| 16 | + | Run: func(cmd *cobra.Command, args []string) { | |||
| 17 | + | data := map[string]interface{}{ | |||
| 18 | + | "message": "Hello, World!", | |||
| 19 | + | } | |||
| 20 | + | payload, err := common.CreatePayload("hello", data) | |||
| 21 | + | if err != nil { | |||
| 22 | + | fmt.Println("Error creating payload:", err) | |||
| 23 | + | return | |||
| 24 | + | } | |||
| 25 | + | ||||
| 26 | + | // Use your handler.SendHTTPRequest function here | |||
| 27 | + | // Assuming that the SendHTTPRequest is moved to a package that can be imported here | |||
| 28 | + | handler.SendHTTPRequest(a, payload) | |||
| 29 | + | ||||
| 30 | + | fmt.Printf("Sending payload to backend: %+v\n", payload) | |||
| 31 | + | }, | |||
| 32 | + | } | |||
| 33 | + | } | |||
M internal/cli/cmd/serve.go +18 -0
| @@ -1,3 +1,21 @@ | |||
| 1 | 1 | // the serve command is used to start the gordon server | |
| 2 | 2 | // Path: internal/cli/cmd/serve.go | |
| 3 | 3 | package cmd | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "github.com/bnema/gordon/internal/app" | |
| 7 | + | "github.com/bnema/gordon/internal/cli/handler" | |
| 8 | + | "github.com/spf13/cobra" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | // execute cmd/srv/main.go main function | |
| 12 | + | ||
| 13 | + | func NewServeCommand(a *app.App) *cobra.Command { | |
| 14 | + | return &cobra.Command{ | |
| 15 | + | Use: "serve", | |
| 16 | + | Short: "Start the gordon server", | |
| 17 | + | Run: func(cmd *cobra.Command, args []string) { | |
| 18 | + | handler.StartServer(a) | |
| 19 | + | }, | |
| 20 | + | } | |
| 21 | + | } | |
| @@ -1,3 +1,21 @@ | |||||
| 1 | // the serve command is used to start the gordon server | 1 | // the serve command is used to start the gordon server | ||
| 2 | // Path: internal/cli/cmd/serve.go | 2 | // Path: internal/cli/cmd/serve.go | ||
| 3 | package cmd | 3 | package cmd | ||
| 4 | + | ||||
| 5 | + | import ( | |||
| 6 | + | "github.com/bnema/gordon/internal/app" | |||
| 7 | + | "github.com/bnema/gordon/internal/cli/handler" | |||
| 8 | + | "github.com/spf13/cobra" | |||
| 9 | + | ) | |||
| 10 | + | ||||
| 11 | + | // execute cmd/srv/main.go main function | |||
| 12 | + | ||||
| 13 | + | func NewServeCommand(a *app.App) *cobra.Command { | |||
| 14 | + | return &cobra.Command{ | |||
| 15 | + | Use: "serve", | |||
| 16 | + | Short: "Start the gordon server", | |||
| 17 | + | Run: func(cmd *cobra.Command, args []string) { | |||
| 18 | + | handler.StartServer(a) | |||
| 19 | + | }, | |||
| 20 | + | } | |||
| 21 | + | } | |||
A internal/cli/handler/http.go +70 -0
| @@ -0,0 +1,70 @@ | |||
| 1 | + | package handler | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "bytes" | |
| 5 | + | "encoding/json" | |
| 6 | + | "fmt" | |
| 7 | + | "net/http" | |
| 8 | + | ||
| 9 | + | "github.com/bnema/gordon/internal/app" | |
| 10 | + | "github.com/bnema/gordon/internal/common" | |
| 11 | + | ) | |
| 12 | + | ||
| 13 | + | // GenerateAPIURL creates the API URL | |
| 14 | + | func GenerateAPIURL(a *app.App) string { | |
| 15 | + | fmt.Println("Generating API URL:", a.Config.GenerateAPIURL()) | |
| 16 | + | return a.Config.GenerateAPIURL() | |
| 17 | + | } | |
| 18 | + | ||
| 19 | + | // PrepareJSONPayload marshals the payload into JSON | |
| 20 | + | func PrepareJSONPayload(payload common.Payload) ([]byte, error) { | |
| 21 | + | return json.Marshal(payload) | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | // CreateNewRequest creates a new HTTP request | |
| 25 | + | func CreateNewRequest(method, url string, body []byte) (*http.Request, error) { | |
| 26 | + | return http.NewRequest(method, url, bytes.NewBuffer(body)) | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | // SetRequestHeaders sets the headers for the request | |
| 30 | + | func SetRequestHeaders(req *http.Request, token string) { | |
| 31 | + | req.Header.Set("Content-Type", "application/json") | |
| 32 | + | req.Header.Set("Authorization", "Bearer "+token) | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | // SendRequest sends the HTTP request and returns the response | |
| 36 | + | func SendRequest(req *http.Request) (*http.Response, error) { | |
| 37 | + | client := &http.Client{} | |
| 38 | + | return client.Do(req) | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | // SendHTTPRequest sends the HTTP request | |
| 42 | + | func SendHTTPRequest(a *app.App, payload common.Payload) { | |
| 43 | + | apiUrl := GenerateAPIURL(a) | |
| 44 | + | fmt.Println("Sending request to:", apiUrl) | |
| 45 | + | ||
| 46 | + | token := a.Config.General.GordonToken | |
| 47 | + | ||
| 48 | + | jsonPayload, err := PrepareJSONPayload(payload) | |
| 49 | + | if err != nil { | |
| 50 | + | fmt.Println("Error preparing JSON payload:", err) | |
| 51 | + | return | |
| 52 | + | } | |
| 53 | + | ||
| 54 | + | req, err := CreateNewRequest("POST", apiUrl, jsonPayload) | |
| 55 | + | if err != nil { | |
| 56 | + | fmt.Println("Error creating new HTTP request:", err) | |
| 57 | + | return | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | SetRequestHeaders(req, token) | |
| 61 | + | ||
| 62 | + | resp, err := SendRequest(req) | |
| 63 | + | if err != nil { | |
| 64 | + | fmt.Println("Error sending HTTP request:", err) | |
| 65 | + | return | |
| 66 | + | } | |
| 67 | + | defer resp.Body.Close() | |
| 68 | + | ||
| 69 | + | fmt.Println("Response Status:", resp.Status) | |
| 70 | + | } | |
| @@ -0,0 +1,70 @@ | |||||
| 1 | + | package handler | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "bytes" | |||
| 5 | + | "encoding/json" | |||
| 6 | + | "fmt" | |||
| 7 | + | "net/http" | |||
| 8 | + | ||||
| 9 | + | "github.com/bnema/gordon/internal/app" | |||
| 10 | + | "github.com/bnema/gordon/internal/common" | |||
| 11 | + | ) | |||
| 12 | + | ||||
| 13 | + | // GenerateAPIURL creates the API URL | |||
| 14 | + | func GenerateAPIURL(a *app.App) string { | |||
| 15 | + | fmt.Println("Generating API URL:", a.Config.GenerateAPIURL()) | |||
| 16 | + | return a.Config.GenerateAPIURL() | |||
| 17 | + | } | |||
| 18 | + | ||||
| 19 | + | // PrepareJSONPayload marshals the payload into JSON | |||
| 20 | + | func PrepareJSONPayload(payload common.Payload) ([]byte, error) { | |||
| 21 | + | return json.Marshal(payload) | |||
| 22 | + | } | |||
| 23 | + | ||||
| 24 | + | // CreateNewRequest creates a new HTTP request | |||
| 25 | + | func CreateNewRequest(method, url string, body []byte) (*http.Request, error) { | |||
| 26 | + | return http.NewRequest(method, url, bytes.NewBuffer(body)) | |||
| 27 | + | } | |||
| 28 | + | ||||
| 29 | + | // SetRequestHeaders sets the headers for the request | |||
| 30 | + | func SetRequestHeaders(req *http.Request, token string) { | |||
| 31 | + | req.Header.Set("Content-Type", "application/json") | |||
| 32 | + | req.Header.Set("Authorization", "Bearer "+token) | |||
| 33 | + | } | |||
| 34 | + | ||||
| 35 | + | // SendRequest sends the HTTP request and returns the response | |||
| 36 | + | func SendRequest(req *http.Request) (*http.Response, error) { | |||
| 37 | + | client := &http.Client{} | |||
| 38 | + | return client.Do(req) | |||
| 39 | + | } | |||
| 40 | + | ||||
| 41 | + | // SendHTTPRequest sends the HTTP request | |||
| 42 | + | func SendHTTPRequest(a *app.App, payload common.Payload) { | |||
| 43 | + | apiUrl := GenerateAPIURL(a) | |||
| 44 | + | fmt.Println("Sending request to:", apiUrl) | |||
| 45 | + | ||||
| 46 | + | token := a.Config.General.GordonToken | |||
| 47 | + | ||||
| 48 | + | jsonPayload, err := PrepareJSONPayload(payload) | |||
| 49 | + | if err != nil { | |||
| 50 | + | fmt.Println("Error preparing JSON payload:", err) | |||
| 51 | + | return | |||
| 52 | + | } | |||
| 53 | + | ||||
| 54 | + | req, err := CreateNewRequest("POST", apiUrl, jsonPayload) | |||
| 55 | + | if err != nil { | |||
| 56 | + | fmt.Println("Error creating new HTTP request:", err) | |||
| 57 | + | return | |||
| 58 | + | } | |||
| 59 | + | ||||
| 60 | + | SetRequestHeaders(req, token) | |||
| 61 | + | ||||
| 62 | + | resp, err := SendRequest(req) | |||
| 63 | + | if err != nil { | |||
| 64 | + | fmt.Println("Error sending HTTP request:", err) | |||
| 65 | + | return | |||
| 66 | + | } | |||
| 67 | + | defer resp.Body.Close() | |||
| 68 | + | ||||
| 69 | + | fmt.Println("Response Status:", resp.Status) | |||
| 70 | + | } | |||
A internal/cli/handler/startserver.go +9 -0
| @@ -0,0 +1,9 @@ | |||
| 1 | + | package handler | |
| 2 | + | ||
| 3 | + | import "github.com/bnema/gordon/internal/app" | |
| 4 | + | ||
| 5 | + | // execute cmd/srv/main.go main function | |
| 6 | + | ||
| 7 | + | func StartServer(a *app.App) error { | |
| 8 | + | return nil | |
| 9 | + | } | |
| @@ -0,0 +1,9 @@ | |||||
| 1 | + | package handler | |||
| 2 | + | ||||
| 3 | + | import "github.com/bnema/gordon/internal/app" | |||
| 4 | + | ||||
| 5 | + | // execute cmd/srv/main.go main function | |||
| 6 | + | ||||
| 7 | + | func StartServer(a *app.App) error { | |||
| 8 | + | return nil | |||
| 9 | + | } | |||
D internal/cli/mvu/mvu.go +0 -59
| @@ -1,59 +0,0 @@ | |||
| 1 | - | package mvu | |
| 2 | - | ||
| 3 | - | import ( | |
| 4 | - | "fmt" | |
| 5 | - | "net/http" | |
| 6 | - | ||
| 7 | - | tea "github.com/charmbracelet/bubbletea" | |
| 8 | - | ) | |
| 9 | - | ||
| 10 | - | type Model struct { | |
| 11 | - | status int | |
| 12 | - | err error | |
| 13 | - | url string | |
| 14 | - | } | |
| 15 | - | ||
| 16 | - | type statusMsg int | |
| 17 | - | ||
| 18 | - | type ErrMsg struct { | |
| 19 | - | Err error | |
| 20 | - | } | |
| 21 | - | ||
| 22 | - | func (m Model) Init() tea.Cmd { | |
| 23 | - | return nil | |
| 24 | - | } | |
| 25 | - | ||
| 26 | - | func Error(e ErrMsg) string { return e.Err.Error() } | |
| 27 | - | ||
| 28 | - | func Update(msg tea.Msg, m Model) (tea.Model, tea.Cmd) { | |
| 29 | - | switch msg := msg.(type) { | |
| 30 | - | case tea.KeyMsg: | |
| 31 | - | switch msg.String() { | |
| 32 | - | case "q", "ctrl+c", "esc": | |
| 33 | - | return m, tea.Quit | |
| 34 | - | default: | |
| 35 | - | return nil | |
| 36 | - | } | |
| 37 | - | ||
| 38 | - | case statusMsg: | |
| 39 | - | m.status = int(msg) | |
| 40 | - | return m, tea.Quit | |
| 41 | - | ||
| 42 | - | case ErrMsg: | |
| 43 | - | m.err = msg | |
| 44 | - | return nil | |
| 45 | - | ||
| 46 | - | default: | |
| 47 | - | return nil | |
| 48 | - | } | |
| 49 | - | } | |
| 50 | - | ||
| 51 | - | func (m Model) View() string { | |
| 52 | - | s := fmt.Sprintf("Checking %s...", m.url) | |
| 53 | - | if m.err != nil { | |
| 54 | - | s += fmt.Sprintf("something went wrong: %s", m.err) | |
| 55 | - | } else if m.status != 0 { | |
| 56 | - | s += fmt.Sprintf("%d %s", m.status, http.StatusText(m.status)) | |
| 57 | - | } | |
| 58 | - | return s + "\n" | |
| 59 | - | } | |
| @@ -1,59 +0,0 @@ | |||||
| 1 | - | package mvu | |||
| 2 | - | ||||
| 3 | - | import ( | |||
| 4 | - | "fmt" | |||
| 5 | - | "net/http" | |||
| 6 | - | ||||
| 7 | - | tea "github.com/charmbracelet/bubbletea" | |||
| 8 | - | ) | |||
| 9 | - | ||||
| 10 | - | type Model struct { | |||
| 11 | - | status int | |||
| 12 | - | err error | |||
| 13 | - | url string | |||
| 14 | - | } | |||
| 15 | - | ||||
| 16 | - | type statusMsg int | |||
| 17 | - | ||||
| 18 | - | type ErrMsg struct { | |||
| 19 | - | Err error | |||
| 20 | - | } | |||
| 21 | - | ||||
| 22 | - | func (m Model) Init() tea.Cmd { | |||
| 23 | - | return nil | |||
| 24 | - | } | |||
| 25 | - | ||||
| 26 | - | func Error(e ErrMsg) string { return e.Err.Error() } | |||
| 27 | - | ||||
| 28 | - | func Update(msg tea.Msg, m Model) (tea.Model, tea.Cmd) { | |||
| 29 | - | switch msg := msg.(type) { | |||
| 30 | - | case tea.KeyMsg: | |||
| 31 | - | switch msg.String() { | |||
| 32 | - | case "q", "ctrl+c", "esc": | |||
| 33 | - | return m, tea.Quit | |||
| 34 | - | default: | |||
| 35 | - | return nil | |||
| 36 | - | } | |||
| 37 | - | ||||
| 38 | - | case statusMsg: | |||
| 39 | - | m.status = int(msg) | |||
| 40 | - | return m, tea.Quit | |||
| 41 | - | ||||
| 42 | - | case ErrMsg: | |||
| 43 | - | m.err = msg | |||
| 44 | - | return nil | |||
| 45 | - | ||||
| 46 | - | default: | |||
| 47 | - | return nil | |||
| 48 | - | } | |||
| 49 | - | } | |||
| 50 | - | ||||
| 51 | - | func (m Model) View() string { | |||
| 52 | - | s := fmt.Sprintf("Checking %s...", m.url) | |||
| 53 | - | if m.err != nil { | |||
| 54 | - | s += fmt.Sprintf("something went wrong: %s", m.err) | |||
| 55 | - | } else if m.status != 0 { | |||
| 56 | - | s += fmt.Sprintf("%d %s", m.status, http.StatusText(m.status)) | |||
| 57 | - | } | |||
| 58 | - | return s + "\n" | |||
| 59 | - | } | |||
A internal/common/payload.go +61 -0
| @@ -0,0 +1,61 @@ | |||
| 1 | + | package common | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "errors" | |
| 5 | + | ) | |
| 6 | + | ||
| 7 | + | // RequestPayload holds the type and payload of a client request | |
| 8 | + | type RequestPayload struct { | |
| 9 | + | Type string `json:"type"` | |
| 10 | + | Payload Payload `json:"payload"` | |
| 11 | + | } | |
| 12 | + | ||
| 13 | + | // Request is the client request type | |
| 14 | + | type Request struct { | |
| 15 | + | Token string `json:"token"` | |
| 16 | + | Request RequestPayload `json:"request"` | |
| 17 | + | } | |
| 18 | + | ||
| 19 | + | // Payload interface exposes the type and allows for extensible payloads | |
| 20 | + | type Payload interface { | |
| 21 | + | GetType() string | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | // HelloWorldPayload for testing | |
| 25 | + | type HelloWorldPayload struct { | |
| 26 | + | Message string `json:"message"` | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | // GetType implementation for HelloWorldPayload | |
| 30 | + | func (p HelloWorldPayload) GetType() string { | |
| 31 | + | return "hello" | |
| 32 | + | } | |
| 33 | + | ||
| 34 | + | // PushPayload for pushing container images | |
| 35 | + | type PushPayload struct { | |
| 36 | + | Image string `json:"image"` | |
| 37 | + | Tag string `json:"tag"` | |
| 38 | + | } | |
| 39 | + | ||
| 40 | + | // GetType implementation for PushPayload | |
| 41 | + | func (p PushPayload) GetType() string { | |
| 42 | + | return "push" | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | // CreatePayload is a factory function to create a Payload based on the type | |
| 46 | + | func CreatePayload(payloadType string, data map[string]interface{}) (Payload, error) { | |
| 47 | + | switch payloadType { | |
| 48 | + | case "hello": | |
| 49 | + | message, ok := data["message"].(string) | |
| 50 | + | if !ok { | |
| 51 | + | return nil, errors.New("invalid data for HelloWorldPayload") | |
| 52 | + | } | |
| 53 | + | return HelloWorldPayload{Message: message}, nil | |
| 54 | + | case "push": | |
| 55 | + | // TODO: implement | |
| 56 | + | default: | |
| 57 | + | return nil, errors.New("unknown payload type") | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | return nil, nil | |
| 61 | + | } | |
| @@ -0,0 +1,61 @@ | |||||
| 1 | + | package common | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "errors" | |||
| 5 | + | ) | |||
| 6 | + | ||||
| 7 | + | // RequestPayload holds the type and payload of a client request | |||
| 8 | + | type RequestPayload struct { | |||
| 9 | + | Type string `json:"type"` | |||
| 10 | + | Payload Payload `json:"payload"` | |||
| 11 | + | } | |||
| 12 | + | ||||
| 13 | + | // Request is the client request type | |||
| 14 | + | type Request struct { | |||
| 15 | + | Token string `json:"token"` | |||
| 16 | + | Request RequestPayload `json:"request"` | |||
| 17 | + | } | |||
| 18 | + | ||||
| 19 | + | // Payload interface exposes the type and allows for extensible payloads | |||
| 20 | + | type Payload interface { | |||
| 21 | + | GetType() string | |||
| 22 | + | } | |||
| 23 | + | ||||
| 24 | + | // HelloWorldPayload for testing | |||
| 25 | + | type HelloWorldPayload struct { | |||
| 26 | + | Message string `json:"message"` | |||
| 27 | + | } | |||
| 28 | + | ||||
| 29 | + | // GetType implementation for HelloWorldPayload | |||
| 30 | + | func (p HelloWorldPayload) GetType() string { | |||
| 31 | + | return "hello" | |||
| 32 | + | } | |||
| 33 | + | ||||
| 34 | + | // PushPayload for pushing container images | |||
| 35 | + | type PushPayload struct { | |||
| 36 | + | Image string `json:"image"` | |||
| 37 | + | Tag string `json:"tag"` | |||
| 38 | + | } | |||
| 39 | + | ||||
| 40 | + | // GetType implementation for PushPayload | |||
| 41 | + | func (p PushPayload) GetType() string { | |||
| 42 | + | return "push" | |||
| 43 | + | } | |||
| 44 | + | ||||
| 45 | + | // CreatePayload is a factory function to create a Payload based on the type | |||
| 46 | + | func CreatePayload(payloadType string, data map[string]interface{}) (Payload, error) { | |||
| 47 | + | switch payloadType { | |||
| 48 | + | case "hello": | |||
| 49 | + | message, ok := data["message"].(string) | |||
| 50 | + | if !ok { | |||
| 51 | + | return nil, errors.New("invalid data for HelloWorldPayload") | |||
| 52 | + | } | |||
| 53 | + | return HelloWorldPayload{Message: message}, nil | |||
| 54 | + | case "push": | |||
| 55 | + | // TODO: implement | |||
| 56 | + | default: | |||
| 57 | + | return nil, errors.New("unknown payload type") | |||
| 58 | + | } | |||
| 59 | + | ||||
| 60 | + | return nil, nil | |||
| 61 | + | } | |||
A internal/httpserve/handler/endpoints.go +14 -0
| @@ -0,0 +1,14 @@ | |||
| 1 | + | package handler | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "net/http" | |
| 5 | + | ||
| 6 | + | "github.com/bnema/gordon/internal/app" | |
| 7 | + | "github.com/labstack/echo/v4" | |
| 8 | + | ) | |
| 9 | + | ||
| 10 | + | // Handle GET on /api endpoint | |
| 11 | + | ||
| 12 | + | func GetHello(c echo.Context, a *app.App) error { | |
| 13 | + | return c.JSON(http.StatusOK, "Hello, World!") | |
| 14 | + | } | |
| @@ -0,0 +1,14 @@ | |||||
| 1 | + | package handler | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "net/http" | |||
| 5 | + | ||||
| 6 | + | "github.com/bnema/gordon/internal/app" | |||
| 7 | + | "github.com/labstack/echo/v4" | |||
| 8 | + | ) | |||
| 9 | + | ||||
| 10 | + | // Handle GET on /api endpoint | |||
| 11 | + | ||||
| 12 | + | func GetHello(c echo.Context, a *app.App) error { | |||
| 13 | + | return c.JSON(http.StatusOK, "Hello, World!") | |||
| 14 | + | } | |||
M internal/httpserve/handler/oauth.go +2 -2
| @@ -21,7 +21,7 @@ import ( | |||
| 21 | 21 | var urlToken string | |
| 22 | 22 | ||
| 23 | 23 | // compareGordonToken compares the token from the URL query parameter with the one from the config.yml | |
| 24 | - | func compareGordonToken(c echo.Context, a *app.App) error { | |
| 24 | + | func CompareGordonToken(c echo.Context, a *app.App) error { | |
| 25 | 25 | configToken := a.Config.General.GordonToken | |
| 26 | 26 | if urlToken != configToken { | |
| 27 | 27 | // if token is not present or does not match the one from the config.yml | |
| @@ -158,7 +158,7 @@ func handleUser(c echo.Context, a *app.App, accessToken, browserInfo string, use | |||
| 158 | 158 | ||
| 159 | 159 | if !userExists { | |
| 160 | 160 | // if it is a new user creation we compare the gordon token | |
| 161 | - | err := compareGordonToken(c, a) | |
| 161 | + | err := CompareGordonToken(c, a) | |
| 162 | 162 | if err != nil { | |
| 163 | 163 | return nil, echo.NewHTTPError(http.StatusUnauthorized, "Token is empty or not valid") | |
| 164 | 164 | } | |
| @@ -21,7 +21,7 @@ import ( | |||||
| 21 | var urlToken string | 21 | var urlToken string | ||
| 22 | 22 | ||||
| 23 | // compareGordonToken compares the token from the URL query parameter with the one from the config.yml | 23 | // compareGordonToken compares the token from the URL query parameter with the one from the config.yml | ||
| 24 | - | func compareGordonToken(c echo.Context, a *app.App) error { | 24 | + | func CompareGordonToken(c echo.Context, a *app.App) error { |
| 25 | configToken := a.Config.General.GordonToken | 25 | configToken := a.Config.General.GordonToken | ||
| 26 | if urlToken != configToken { | 26 | if urlToken != configToken { | ||
| 27 | // if token is not present or does not match the one from the config.yml | 27 | // if token is not present or does not match the one from the config.yml | ||
| @@ -158,7 +158,7 @@ func handleUser(c echo.Context, a *app.App, accessToken, browserInfo string, use | |||||
| 158 | 158 | ||||
| 159 | if !userExists { | 159 | if !userExists { | ||
| 160 | // if it is a new user creation we compare the gordon token | 160 | // if it is a new user creation we compare the gordon token | ||
| 161 | - | err := compareGordonToken(c, a) | 161 | + | err := CompareGordonToken(c, a) |
| 162 | if err != nil { | 162 | if err != nil { | ||
| 163 | return nil, echo.NewHTTPError(http.StatusUnauthorized, "Token is empty or not valid") | 163 | return nil, echo.NewHTTPError(http.StatusUnauthorized, "Token is empty or not valid") | ||
| 164 | } | 164 | } | ||
M internal/httpserve/middleware/commondata.go +1 -1
| @@ -8,7 +8,7 @@ import ( | |||
| 8 | 8 | func SetCommonDataMiddleware(a *app.App) echo.MiddlewareFunc { | |
| 9 | 9 | return func(next echo.HandlerFunc) echo.HandlerFunc { | |
| 10 | 10 | return func(c echo.Context) error { | |
| 11 | - | // Will pass data to the renderer | |
| 11 | + | // TODO: set common data | |
| 12 | 12 | return next(c) | |
| 13 | 13 | } | |
| 14 | 14 | } | |
| @@ -8,7 +8,7 @@ import ( | |||||
| 8 | func SetCommonDataMiddleware(a *app.App) echo.MiddlewareFunc { | 8 | func SetCommonDataMiddleware(a *app.App) echo.MiddlewareFunc { | ||
| 9 | return func(next echo.HandlerFunc) echo.HandlerFunc { | 9 | return func(next echo.HandlerFunc) echo.HandlerFunc { | ||
| 10 | return func(c echo.Context) error { | 10 | return func(c echo.Context) error { | ||
| 11 | - | // Will pass data to the renderer | 11 | + | // TODO: set common data |
| 12 | return next(c) | 12 | return next(c) | ||
| 13 | } | 13 | } | ||
| 14 | } | 14 | } | ||
A internal/httpserve/middleware/requiretoken.go +40 -0
| @@ -0,0 +1,40 @@ | |||
| 1 | + | package middleware | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "fmt" | |
| 5 | + | "strings" | |
| 6 | + | ||
| 7 | + | "github.com/bnema/gordon/internal/app" | |
| 8 | + | "github.com/labstack/echo/v4" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | // RequireToken is a middleware that checks for a valid token in the request | |
| 12 | + | func RequireToken(a *app.App) echo.MiddlewareFunc { | |
| 13 | + | return func(next echo.HandlerFunc) echo.HandlerFunc { | |
| 14 | + | return func(c echo.Context) error { | |
| 15 | + | if err := validateToken(c, a); err != nil { | |
| 16 | + | return err | |
| 17 | + | } | |
| 18 | + | return next(c) | |
| 19 | + | } | |
| 20 | + | } | |
| 21 | + | } | |
| 22 | + | ||
| 23 | + | func validateToken(c echo.Context, a *app.App) error { | |
| 24 | + | token := c.Request().Header.Get("Authorization") | |
| 25 | + | if token == "" { | |
| 26 | + | return echo.ErrUnauthorized | |
| 27 | + | } | |
| 28 | + | fmt.Println(token) | |
| 29 | + | token = strings.Replace(token, "Bearer ", "", 1) | |
| 30 | + | configToken, err := a.Config.GetToken() | |
| 31 | + | if err != nil { | |
| 32 | + | return fmt.Errorf("failed to get token from config: %w", err) | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | if token != configToken { | |
| 36 | + | return echo.ErrUnauthorized | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | return nil | |
| 40 | + | } | |
| @@ -0,0 +1,40 @@ | |||||
| 1 | + | package middleware | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "fmt" | |||
| 5 | + | "strings" | |||
| 6 | + | ||||
| 7 | + | "github.com/bnema/gordon/internal/app" | |||
| 8 | + | "github.com/labstack/echo/v4" | |||
| 9 | + | ) | |||
| 10 | + | ||||
| 11 | + | // RequireToken is a middleware that checks for a valid token in the request | |||
| 12 | + | func RequireToken(a *app.App) echo.MiddlewareFunc { | |||
| 13 | + | return func(next echo.HandlerFunc) echo.HandlerFunc { | |||
| 14 | + | return func(c echo.Context) error { | |||
| 15 | + | if err := validateToken(c, a); err != nil { | |||
| 16 | + | return err | |||
| 17 | + | } | |||
| 18 | + | return next(c) | |||
| 19 | + | } | |||
| 20 | + | } | |||
| 21 | + | } | |||
| 22 | + | ||||
| 23 | + | func validateToken(c echo.Context, a *app.App) error { | |||
| 24 | + | token := c.Request().Header.Get("Authorization") | |||
| 25 | + | if token == "" { | |||
| 26 | + | return echo.ErrUnauthorized | |||
| 27 | + | } | |||
| 28 | + | fmt.Println(token) | |||
| 29 | + | token = strings.Replace(token, "Bearer ", "", 1) | |||
| 30 | + | configToken, err := a.Config.GetToken() | |||
| 31 | + | if err != nil { | |||
| 32 | + | return fmt.Errorf("failed to get token from config: %w", err) | |||
| 33 | + | } | |||
| 34 | + | ||||
| 35 | + | if token != configToken { | |||
| 36 | + | return echo.ErrUnauthorized | |||
| 37 | + | } | |||
| 38 | + | ||||
| 39 | + | return nil | |||
| 40 | + | } | |||
M internal/httpserve/router.go +12 -0
| @@ -10,11 +10,14 @@ import ( | |||
| 10 | 10 | ||
| 11 | 11 | // RegisterRoutes registers all routes for the application | |
| 12 | 12 | func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo { | |
| 13 | + | // --- Register API endpoints --- // | |
| 14 | + | bindAPIEndpoints(e, a) | |
| 13 | 15 | AdminPath := a.Config.Admin.Path | |
| 14 | 16 | // SetCommonDataMiddleware will pass data to the renderer | |
| 15 | 17 | e.Use(middleware.SetCommonDataMiddleware(a)) | |
| 16 | 18 | // Error handler middleware | |
| 17 | 19 | e.Use(middleware.ErrorHandler) | |
| 20 | + | ||
| 18 | 21 | // Initiate the session middleware | |
| 19 | 22 | e.Use(middleware.InitSessionMiddleware()) | |
| 20 | 23 | // Use middlewares | |
| @@ -38,6 +41,15 @@ func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo { | |||
| 38 | 41 | return e | |
| 39 | 42 | } | |
| 40 | 43 | ||
| 44 | + | // bindAPIEndpoints expose /api endpoints and speaks only JSON | |
| 45 | + | func bindAPIEndpoints(e *echo.Echo, a *app.App) { | |
| 46 | + | apiGroup := e.Group("/api") | |
| 47 | + | apiGroup.Use(middleware.RequireToken(a)) | |
| 48 | + | apiGroup.GET("/hello", func(c echo.Context) error { | |
| 49 | + | return handler.GetHello(c, a) | |
| 50 | + | }) | |
| 51 | + | } | |
| 52 | + | ||
| 41 | 53 | // bindStaticRoute bind static path | |
| 42 | 54 | func bindStaticRoute(e *echo.Echo, a *app.App, path string) { | |
| 43 | 55 | e.GET(path, func(c echo.Context) error { | |
| @@ -10,11 +10,14 @@ import ( | |||||
| 10 | 10 | ||||
| 11 | // RegisterRoutes registers all routes for the application | 11 | // RegisterRoutes registers all routes for the application | ||
| 12 | func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo { | 12 | func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo { | ||
| 13 | + | // --- Register API endpoints --- // | |||
| 14 | + | bindAPIEndpoints(e, a) | |||
| 13 | AdminPath := a.Config.Admin.Path | 15 | AdminPath := a.Config.Admin.Path | ||
| 14 | // SetCommonDataMiddleware will pass data to the renderer | 16 | // SetCommonDataMiddleware will pass data to the renderer | ||
| 15 | e.Use(middleware.SetCommonDataMiddleware(a)) | 17 | e.Use(middleware.SetCommonDataMiddleware(a)) | ||
| 16 | // Error handler middleware | 18 | // Error handler middleware | ||
| 17 | e.Use(middleware.ErrorHandler) | 19 | e.Use(middleware.ErrorHandler) | ||
| 20 | + | ||||
| 18 | // Initiate the session middleware | 21 | // Initiate the session middleware | ||
| 19 | e.Use(middleware.InitSessionMiddleware()) | 22 | e.Use(middleware.InitSessionMiddleware()) | ||
| 20 | // Use middlewares | 23 | // Use middlewares | ||
| @@ -38,6 +41,15 @@ func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo { | |||||
| 38 | return e | 41 | return e | ||
| 39 | } | 42 | } | ||
| 40 | 43 | ||||
| 44 | + | // bindAPIEndpoints expose /api endpoints and speaks only JSON | |||
| 45 | + | func bindAPIEndpoints(e *echo.Echo, a *app.App) { | |||
| 46 | + | apiGroup := e.Group("/api") | |||
| 47 | + | apiGroup.Use(middleware.RequireToken(a)) | |||
| 48 | + | apiGroup.GET("/hello", func(c echo.Context) error { | |||
| 49 | + | return handler.GetHello(c, a) | |||
| 50 | + | }) | |||
| 51 | + | } | |||
| 52 | + | ||||
| 41 | // bindStaticRoute bind static path | 53 | // bindStaticRoute bind static path | ||
| 42 | func bindStaticRoute(e *echo.Echo, a *app.App, path string) { | 54 | func bindStaticRoute(e *echo.Echo, a *app.App, path string) { | ||
| 43 | e.GET(path, func(c echo.Context) error { | 55 | e.GET(path, func(c echo.Context) error { | ||