From ffa478611382f6f31fb8dcd36c62253adb55b92a Mon Sep 17 00:00:00 2001 From: Thomas Peetz Date: Thu, 8 Oct 2026 17:40:17 +0200 Subject: [PATCH] add server configuration --- kontor-gin/data.json | 38 ++++++++++++++++++ kontor-gin/internal/api/data.go | 29 +++++++++----- kontor-gin/internal/cmd/add-links.go | 6 +-- kontor-gin/internal/cmd/check.go | 42 +++++-------------- kontor-gin/internal/cmd/config.go | 32 +++++++++++++++ kontor-gin/internal/cmd/export.go | 46 ++++++--------------- kontor-gin/internal/cmd/init.go | 42 +++++++++++++++++++ kontor-gin/internal/cmd/root.go | 25 +++++------- kontor-gin/internal/cmd/sync.go | 10 ++++- kontor-gin/internal/config/config.go | 60 ++++++++++++++++++++++++++-- kontor-gin/internal/helpers/api.go | 59 +++++++++++++++++++++++++++ 11 files changed, 292 insertions(+), 97 deletions(-) create mode 100644 kontor-gin/data.json create mode 100644 kontor-gin/internal/cmd/config.go create mode 100644 kontor-gin/internal/cmd/init.go create mode 100644 kontor-gin/internal/helpers/api.go diff --git a/kontor-gin/data.json b/kontor-gin/data.json new file mode 100644 index 0000000..37300ef --- /dev/null +++ b/kontor-gin/data.json @@ -0,0 +1,38 @@ +{ + "sport": null, + "player": null, + "team": null, + "field_position": null, + "rooster": null, + "vendor": null, + "card_set": null, + "card": null, + "artist": null, + "publisher": null, + "worktype": null, + "comic": null, + "volume": null, + "story_arc": null, + "issue": null, + "trade_paperback": null, + "comic_work": null, + "issue_work": null, + "article": null, + "bookshelf_publisher": null, + "book": null, + "author": null, + "article_author": null, + "book_author": null, + "media_article": null, + "media_video": null, + "media_lofi": null, + "media_file": null, + "media_actor": null, + "media_actor_file": null, + "profile": null, + "permission": null, + "assignment": null, + "token": null, + "mail_account": null, + "mail": null +} \ No newline at end of file diff --git a/kontor-gin/internal/api/data.go b/kontor-gin/internal/api/data.go index f62bd63..7c3e2d6 100644 --- a/kontor-gin/internal/api/data.go +++ b/kontor-gin/internal/api/data.go @@ -3,6 +3,7 @@ package api import ( "encoding/json" "fmt" + "kontor-api-gin/internal/config" "kontor-api-gin/internal/models" "log/slog" "os" @@ -110,14 +111,14 @@ var ( var Registry map[string]any -func GetData(server, apiVersion string) (data *ExportData, err error) { +func GetData(server config.Server, apiVersion string) (data *ExportData, err error) { var login LoginResponse client := resty.New() - PostJSON(client, fmt.Sprintf("%s/%s/%s", server, "api", "login"), &login) + PostJSON(client, server, fmt.Sprintf("/%s/%s", "api", "login"), &login) for path, value := range Registry { - GetJSON(client, login.Token, fmt.Sprintf("%s/%s/%s", server, apiVersion, path), value) + GetJSON(client, login.Token, server, path, value) } export := ExportData{ Sport: Sports, @@ -176,7 +177,11 @@ func WriteJson(data *ExportData, fileName string) error { return nil } -func GetJSON(client *resty.Client, token string, url string, v any) { +func GetJSON(client *resty.Client, token string, server config.Server, path string, v any) { + url := fmt.Sprintf("%s/%s", server.Url, path) + if config.Verbose > 1 { + slog.Info("GetJSON", "url", url) + } _, err := client.R(). SetAuthToken(token). SetHeader("Accept", "application/json"). @@ -187,19 +192,25 @@ func GetJSON(client *resty.Client, token string, url string, v any) { } } -func PostJSON(client *resty.Client, url string, v any) { +func PostJSON(client *resty.Client, server config.Server, path string, v any) { + url := fmt.Sprintf("%s/%s", server.Url, path) + if config.Verbose > 1 { + slog.Info("PostJSON", "url", url) + } _, err := client.R(). SetBody(Login{ - Email: "thomas.peetz@thpeetz.de", - Password: "admin", + Email: server.Username, + Password: server.Password, }). SetHeader("Accept", "application/json"). SetResult(v). Post(url) if err != nil { - slog.Info("Health", "error", err.Error()) + slog.Info("PostJSON", "error", err.Error()) + } + if config.Verbose > 1 { + slog.Info(url, "token", v) } - slog.Info(url, "token", v) } func init() { diff --git a/kontor-gin/internal/cmd/add-links.go b/kontor-gin/internal/cmd/add-links.go index 811fb1c..f56324e 100644 --- a/kontor-gin/internal/cmd/add-links.go +++ b/kontor-gin/internal/cmd/add-links.go @@ -25,13 +25,13 @@ var addLinksCmd = &cobra.Command{ slog.Warn("add links failed", "error", err) os.Exit(1) } - var login LoginResponse + client := resty.New() + token := helpers.GetToken(client) for _, line := range lines { lofiAdd := models.MediaLofiAdd{Url: line} - client := resty.New() - PostJSON(client, fmt.Sprintf("%s/%s/%s", server, "api", "login"), &login) response, err := client.R(). SetBody(lofiAdd). + SetAuthToken(token). SetHeader("Accept", "application/json"). Post("http://localhost:8550/api/v1/media/lofi") if err != nil { diff --git a/kontor-gin/internal/cmd/check.go b/kontor-gin/internal/cmd/check.go index dc29356..45bbaf4 100644 --- a/kontor-gin/internal/cmd/check.go +++ b/kontor-gin/internal/cmd/check.go @@ -1,6 +1,9 @@ package cmd import ( + "kontor-api-gin/internal/api" + "kontor-api-gin/internal/config" + "kontor-api-gin/internal/helpers" "kontor-api-gin/internal/models" "log/slog" @@ -24,39 +27,16 @@ var checkCmd = &cobra.Command{ // Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { slog.Info("check kontor") - slog.Info("Verbosity level", "level", verbose) - var login LoginResponse + slog.Info("Verbosity level", "level", config.Verbose) client := resty.New() - _, err := client.R(). - SetBody(Login{ - Email: "thomas.peetz@thpeetz.de", - Password: "admin", - }). - SetHeader("Accept", "application/json"). - SetResult(&login). - Post("http://127.0.0.1:8550/api/login") - if err != nil { - slog.Info("Health", "error", err.Error()) - } - slog.Info("health", "token", login.Token) - - type ExportData struct { - Sport []models.SportDTO `json:"sport"` - Comic []models.ComicDTO `json:"comic"` - } - - var comics []models.ComicDTO - _, err = client.R(). - SetAuthToken(login.Token). - SetHeader("Accept", "application/json"). - SetResult(&comics). - Get("http://127.0.0.1:8550/api/v1/comics/comics") - if err != nil { - slog.Info("comics", "error", err.Error()) - } - // slog.Info("comics", "res", comics) - defer client.Close() + token := helpers.GetToken(client) + var comics []models.ComicDTO + localServer := config.Config.Server["local"] + + api.GetJSON(client, token, localServer, "comics/comics", &comics) + + slog.Info("comics", "count", len(comics)) slog.Info("kontor.check_kontor.check_duplicate_links") // check_duplicate_links(first_server, dirs) diff --git a/kontor-gin/internal/cmd/config.go b/kontor-gin/internal/cmd/config.go new file mode 100644 index 0000000..f1fd135 --- /dev/null +++ b/kontor-gin/internal/cmd/config.go @@ -0,0 +1,32 @@ +package cmd + +import ( + "kontor-api-gin/internal/config" + "log/slog" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +var configCmd = &cobra.Command{ + Use: "config", + TraverseChildren: true, + Short: "show current config", + Long: "show current config", + // Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + slog.Info("show config") + var config config.CliConfig + if err := viper.Unmarshal(&config); err != nil { + slog.Info("unable to decode config", "error", err.Error()) + } + for name, server := range config.Server { + slog.Info("show config", "server", name, "URL", server.Url) + } + slog.Info("show config", "config", config) + }, +} + +func init() { + rootCmd.AddCommand(configCmd) +} diff --git a/kontor-gin/internal/cmd/export.go b/kontor-gin/internal/cmd/export.go index f395b87..62b12aa 100644 --- a/kontor-gin/internal/cmd/export.go +++ b/kontor-gin/internal/cmd/export.go @@ -3,6 +3,8 @@ package cmd import ( "fmt" "kontor-api-gin/internal/api" + "kontor-api-gin/internal/config" + "kontor-api-gin/internal/helpers" "log/slog" "github.com/spf13/cobra" @@ -11,7 +13,7 @@ import ( ) var ( - server string + servername string filename string apiVersion string ) @@ -23,17 +25,19 @@ var exportCmd = &cobra.Command{ // Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { slog.Info("export kontor") - slog.Info("Verbosity level", "level", verbose) - slog.Info(fmt.Sprintf("Using server: %s", server)) - var login LoginResponse + slog.Info("Verbosity level", "level", config.Verbose) + slog.Info(fmt.Sprintf("Using server: %s", servername)) client := resty.New() - PostJSON(client, fmt.Sprintf("%s/%s/%s", server, "api", "login"), &login) + defer client.Close() + localServer := config.Config.Server[servername] + + token := helpers.GetToken(client) for path, value := range api.Registry { - GetJSON(client, login.Token, fmt.Sprintf("%s/%s/%s", server, apiVersion, path), value) + api.GetJSON(client, token, localServer, path, value) } - export, _ := api.GetData(server, apiVersion) + export, _ := api.GetData(localServer, apiVersion) err := api.WriteJson(export, filename) if err != nil { slog.Info("WriteJon failed", "Error:", err) @@ -42,36 +46,10 @@ var exportCmd = &cobra.Command{ }, } -func GetJSON(client *resty.Client, token string, url string, v any) { - _, err := client.R(). - SetAuthToken(token). - SetHeader("Accept", "application/json"). - SetResult(v). - Get(url) - if err != nil { - slog.Info(url, "error", err.Error()) - } -} - -func PostJSON(client *resty.Client, url string, v any) { - _, err := client.R(). - SetBody(Login{ - Email: "thomas.peetz@thpeetz.de", - Password: "admin", - }). - SetHeader("Accept", "application/json"). - SetResult(v). - Post(url) - if err != nil { - slog.Info("Health", "error", err.Error()) - } - slog.Info(url, "token", v) -} - func init() { rootCmd.AddCommand(exportCmd) - exportCmd.PersistentFlags().StringVarP(&server, "server", "s", "http://127.0.0.1:8550", "Define server to get Kontor items") + exportCmd.PersistentFlags().StringVarP(&servername, "servername", "s", "local", "Define server to get Kontor items") viper.BindPFlag("server", exportCmd.PersistentFlags().Lookup("server")) exportCmd.PersistentFlags().StringVarP(&filename, "file", "f", "/home/tpeetz/.sync/media/data-gin.json", "Filename to store data") viper.BindPFlag("file", exportCmd.PersistentFlags().Lookup("file")) diff --git a/kontor-gin/internal/cmd/init.go b/kontor-gin/internal/cmd/init.go new file mode 100644 index 0000000..372f216 --- /dev/null +++ b/kontor-gin/internal/cmd/init.go @@ -0,0 +1,42 @@ +package cmd + +import ( + "kontor-api-gin/internal/config" + "log/slog" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +var configInitcmd = &cobra.Command{ + Use: "init", + Short: "create config", + Long: "create config file with defaults", + // Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + slog.Info("create config") + config := config.CliConfig{ + Server: map[string]config.Server{ + "main": { + Url: "https://kontor-gin.thpeetz.de", + Username: "user", + Password: "password", + }, + "local": { + Url: "http://localhost:8550", + Username: "user", + Password: "password", + }, + }, + } + viper.Set("server", config.Server) + if err := viper.WriteConfig(); err != nil { + slog.Info("write config failed", "error", err.Error()) + } + }, +} + +func init() { + configInitcmd.Flags().BoolVarP(&config.Overwrite, "overwrite", "y", false, "overwrite existing config") + configCmd.AddCommand(configInitcmd) +} diff --git a/kontor-gin/internal/cmd/root.go b/kontor-gin/internal/cmd/root.go index 4d7303a..1797ae0 100644 --- a/kontor-gin/internal/cmd/root.go +++ b/kontor-gin/internal/cmd/root.go @@ -2,30 +2,25 @@ package cmd import ( "fmt" + "kontor-api-gin/internal/config" "log/slog" "os" "github.com/spf13/cobra" + "github.com/spf13/viper" ) -var cfgFile string -var verbose int - // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ - Use: "kontor-cli", - Short: "A brief description of your application", - Long: `A longer description that spans multiple lines and likely contains -examples and usage of using your application. For example: - -Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.`, + Use: "kontor-cli", + TraverseChildren: true, + Short: "A brief description of your application", + Long: ``, // Uncomment the following line if your bare application // has an action associated with it: Run: func(cmd *cobra.Command, args []string) { cmd.Help() - slog.Info("Verbosity level", "level", verbose) + slog.Info("Verbosity level", "level", config.Verbose) }, } @@ -37,6 +32,8 @@ func Execute() { } func init() { - rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "c", "config file (default is $HOME/.config/kli.yaml)") - rootCmd.PersistentFlags().CountVarP(&verbose, "verbose", "v", "increase verbosity (-v, -vv, -vvv)") + rootCmd.PersistentFlags().StringVarP(&config.CfgFile, "config", "c", "/home/tpeetz/.config/kli.yaml", "config file") + rootCmd.PersistentFlags().CountVarP(&config.Verbose, "verbose", "v", "increase verbosity (-v, -vv, -vvv)") + viper.BindPFlag("config", rootCmd.PersistentFlags().Lookup("config")) + cobra.OnInitialize(config.InitCliConfig) } diff --git a/kontor-gin/internal/cmd/sync.go b/kontor-gin/internal/cmd/sync.go index 3c29e77..a955784 100644 --- a/kontor-gin/internal/cmd/sync.go +++ b/kontor-gin/internal/cmd/sync.go @@ -2,10 +2,12 @@ package cmd import ( "kontor-api-gin/internal/api" + "kontor-api-gin/internal/config" "log/slog" "reflect" "github.com/spf13/cobra" + "github.com/spf13/viper" ) var syncCmd = &cobra.Command{ @@ -15,8 +17,12 @@ var syncCmd = &cobra.Command{ // Args: cobra.ExactArgs(2), Run: func(cmd *cobra.Command, args []string) { slog.Info("sync kontor") - sourceData, _ := api.GetData(server, apiVersion) - targetData, _ := api.GetData("https://kontor-gin.thpeetz.de", apiVersion) + apiVersion := viper.GetString("api") + mainServer := config.Config.Server["main"] + slog.Info("syncCmd#Run", "mainServer", mainServer) + localServer := config.Config.Server["local"] + sourceData, _ := api.GetData(mainServer, apiVersion) + targetData, _ := api.GetData(localServer, apiVersion) slog.Info("Size of targetData", "size", reflect.TypeOf(targetData).Size()) // find missing items in target and collect items not available in source for table, item_list := range sourceData.GetMap() { diff --git a/kontor-gin/internal/config/config.go b/kontor-gin/internal/config/config.go index 61b02cf..a11ad71 100644 --- a/kontor-gin/internal/config/config.go +++ b/kontor-gin/internal/config/config.go @@ -8,6 +8,53 @@ import ( "github.com/spf13/viper" ) +var ( + CfgFile string + Verbose int + Overwrite bool + Config CliConfig +) + +type Server struct { + Url string `mapstructure:"url"` + Username string `mapstructure:"username"` + Password string `mapstructure:"password"` +} + +type CliConfig struct { + Server map[string]Server `mapstructure:"server"` +} + +func InitCliConfig() { + slog.Info("InitCliConfig") + if CfgFile != "" { + viper.SetConfigFile(CfgFile) + } else { + viper.SetConfigName("kli") // without extension + viper.SetConfigType("yaml") + viper.AddConfigPath(".") // look for config in working directory + viper.AddConfigPath("/home/tpeetz/.config") + } + + // Read the configuration file + if err := viper.ReadInConfig(); err != nil { + // Handle the case where config file is not found + if _, ok := err.(viper.ConfigFileNotFoundError); ok { + slog.Info("No config file found, using defaults") + if err := viper.SafeWriteConfig(); err != nil { + slog.Info("error writing config file", "error", err.Error()) + } + } else { + slog.Info("error reading config file", "error", err.Error()) + } + } else { + slog.Info("initConfig", "Using config file: ", viper.ConfigFileUsed()) + if err := viper.Unmarshal(&Config); err != nil { + slog.Info("parsing of config file failed", "error", err.Error()) + } + } +} + func InitConfig() error { viper.SetDefault("db.host", "localhost") viper.SetDefault("db.port", 5432) @@ -20,7 +67,7 @@ func InitConfig() error { viper.SetConfigName("kontor-gin") viper.SetConfigType("yaml") viper.AddConfigPath(".") - viper.AddConfigPath("$HOME/.config") + viper.AddConfigPath("/home/tpeetz/.config") viper.AddConfigPath("/etc/kontor") viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) viper.AutomaticEnv() @@ -30,11 +77,16 @@ func InitConfig() error { // Handle the case where config file is not found if _, ok := err.(viper.ConfigFileNotFoundError); ok { slog.Info("No config file found, using defaults") - return nil + if err := viper.SafeWriteConfig(); err != nil { + slog.Info("error writing config file", "error", err.Error()) + return fmt.Errorf("error writing config file: %w", err) + } + } else { + return fmt.Errorf("error reading config file: %w", err) } - return fmt.Errorf("error reading config file: %w", err) + } else { + slog.Info("initConfig", "Using config file: ", viper.ConfigFileUsed()) } - slog.Info("initConfig", "Using config file: ", viper.ConfigFileUsed()) return nil } diff --git a/kontor-gin/internal/helpers/api.go b/kontor-gin/internal/helpers/api.go new file mode 100644 index 0000000..bf21880 --- /dev/null +++ b/kontor-gin/internal/helpers/api.go @@ -0,0 +1,59 @@ +package helpers + +import ( + "log/slog" + + "resty.dev/v3" +) + +type Login struct { + Email string + Password string +} + +type LoginResponse struct { + Token string +} + +func GetToken(client *resty.Client) (token string) { + var login LoginResponse + _, err := client.R(). + SetBody(Login{ + Email: "thomas.peetz@thpeetz.de", + Password: "admin", + }). + SetHeader("Accept", "application/json"). + SetResult(&login). + Post("http://127.0.0.1:8550/api/login") + if err != nil { + slog.Info("Health", "error", err.Error()) + } + slog.Info("health", "token", login.Token) + return login.Token +} + +func GetJSON(client *resty.Client, token string, url string, v any) { + _, err := client.R(). + SetAuthToken(token). + SetHeader("Accept", "application/json"). + SetResult(v). + Get(url) + if err != nil { + slog.Info(url, "error", err.Error()) + } +} + +func PostJSON(client *resty.Client, url string, v any) { + _, err := client.R(). + SetBody(Login{ + Email: "thomas.peetz@thpeetz.de", + Password: "admin", + }). + SetHeader("Accept", "application/json"). + SetResult(v). + Post(url) + if err != nil { + slog.Info("Health", "error", err.Error()) + } + slog.Info(url, "token", v) +}