From: Banana Date: Sun, 16 Jun 2024 12:47:18 +0000 (+0200) Subject: starting fresh with the go client X-Git-Tag: 1.1~7 X-Git-Url: http://91.132.146.200/gitweb/?a=commitdiff_plain;h=089338c1aa744073983edec430e8a7c9ee5c7c4f;p=scientia.git starting fresh with the go client Signed-off-by: Banana --- diff --git a/CHANGELOG b/CHANGELOG index 8ccc507..610a341 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,5 @@ 1.x Atlas - - + i18n. See upgrade-from-1.0.txt for more details. + + i18n. See upgrade-from-1.0.md for more details. + PHP 8.1 stuff + Search + Updated clients. A cli client written in Go. diff --git a/client/go-cli/Makefile b/client/go-cli/Makefile deleted file mode 100644 index b8ab31a..0000000 --- a/client/go-cli/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -all: - @echo "Options are: build scientia-cli scientia-cli-linux-amd64 scientia-cli-windows-amd64 scientia-cli-darwin-amd64 clean buildall" - -scientia-cli: - @echo "Building for local os/arch..." - go build -o scientia-cli - -scientia-cli-linux-amd64: - @echo "Building for linux/amd64 arch..." - GOOS=linux GOARCH=amd64 go build -o scientia-cli-linux-amd64 - -scientia-cli-windows-amd64: - @echo "Building for windows/amd64 arch..." - GOOS=windows GOARCH=amd64 go build -o scientia-cli-windows-amd64 - -scientia-cli-darwin-amd64: - @echo "Building for macOS/amd64 arch..." - GOOS=darwin GOARCH=amd64 go build -o scientia-cli-darwin-amd64 - -build: - @echo "Building for local os/arch..." - go build -o scientia-cli-`go env GOOS`-`go env GOARCH` - -buildall: scientia-cli-linux-amd64 scientia-cli-windows-amd64 scientia-cli-darwin-amd64 - @echo "Building for linux/amd64, windows/amd64 and macOS/amd64 arch..." - -clean: - rm -f scientia-cli scientia-cli-darwin-amd64 scientia-cli-windows-amd64 scientia-cli-linux-amd64 \ No newline at end of file diff --git a/client/go-cli/README.md b/client/go-cli/README.md deleted file mode 100644 index 04ff93f..0000000 --- a/client/go-cli/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# scienta go cli client - -This is a terminal cli client written in go to be used with scientia -https://://www.bananas-playground.net/projekt/scientia/ - -!WARNING! -This is a very simple, with limited experience written, go program. -Use at own risk and feel free to improve. - -# Howto build - -Nothing special, just use the provided Makefile or directly -"go build -o scientia-cli" to use your current os/arch settings. - -# Usage - -At first usage you need to create the config and the individual secret. -Run $scientia-cli -create-config-file to create the default config file. -The path to the config file is printed. -Change the host address and update your server it with the secret, which is randomly created. - -## Create - -Read from a file `$ scientia-cli file.txt` or piped `$ cat file.txt | scientia-cli` - -# Commandline arguments - -## Optinal - -+ `-create-config-file` Create default config file -+ `-debug` Print debug infos -+ `-verbose` Produce verbose output diff --git a/client/go-cli/go.mod b/client/go-cli/go.mod deleted file mode 100644 index 5a0b7ac..0000000 --- a/client/go-cli/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module scientia/go-client-cli - -go 1.20 - -require gopkg.in/yaml.v2 v2.4.0 diff --git a/client/go-cli/go.sum b/client/go-cli/go.sum deleted file mode 100644 index dd0bc19..0000000 --- a/client/go-cli/go.sum +++ /dev/null @@ -1,4 +0,0 @@ -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/client/go-cli/scientia-cli.go b/client/go-cli/scientia-cli.go deleted file mode 100644 index cebde96..0000000 --- a/client/go-cli/scientia-cli.go +++ /dev/null @@ -1,271 +0,0 @@ -package main - -import ( - "bytes" - "encoding/json" - "errors" - "flag" - "fmt" - "io" - "log" - "math/rand" - "net/http" - "os" - - "gopkg.in/yaml.v2" -) - -/** - * scientia - * - * Copyright 2023 Johannes Keßler - * - * https://www.bananas-playground.net/projekt/scientia/ - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the COMMON DEVELOPMENT AND DISTRIBUTION LICENSE - * - * You should have received a copy of the - * COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 - * along with this program. If not, see http://www.sun.com/cddl/cddl.html - */ - -const website = "https://www.bananas-playground.net/projekt/scientia/" -const version = "1.0" - -// used for non-existing default config -const letters = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-_" - -// command line parameters -var optsVerbose bool -var optsCreateConfig bool -var optsDebug bool - -// config -var cfg Config - -// config file struct -type Config struct { - Endpoint struct { - Host string `yaml:"host"` - Secret string `yaml:"secret"` - } `yaml:"endpoint"` -} - -// post json struct -type PayloadJson struct { - Asl string `json:"asl"` - Data string `json:"data"` -} - -// response json struct -type Response struct { - Message string `json:"message"` - Status int `json:"status"` -} - -/** - * Main - */ -func main() { - - // https://cli.urfave.org/v2/examples/arguments/ - // https://github.com/dnote/dnote - //https://github.com/spf13/cobra/blob/main/site/content/user_guide.md - - // parse commandline parameters - flag.BoolVar(&optsVerbose, "verbose", false, "Produce verbose output") - flag.BoolVar(&optsCreateConfig, "create-config-file", false, "Create default config file") - flag.BoolVar(&optsDebug, "debug", false, "Print debug infos") - flag.Parse() - if optsDebug { - fmt.Println("verbose:", optsVerbose) - fmt.Println("create-config-file:", optsCreateConfig) - fmt.Println("debug:", optsDebug) - } - - // load the config and populate Config - loadConfig() - - // get the payload - payload := getInput() - if optsDebug { - log.Println(payload) - } - - // do the upload and get the response - responseString := uploadCall(payload) - response := Response{} - json.Unmarshal([]byte(responseString), &response) - - // print the result and link to the pasty - fmt.Printf("Status: %d\n", response.Status) - fmt.Printf("Message: %s\n", response.Message) -} - -/** - * Check and display error with additional message - */ -func errorCheck(e error, msg string) { - if e != nil { - log.Fatal(msg, " ; Errrormsg: ", e) - } -} - -/** - * just a random string - */ -func randStringBytes(n int) string { - b := make([]byte, n) - for i := range b { - b[i] = letters[rand.Intn(len(letters))] - } - return string(b) -} - -/** - * load or even create a default config - * $HOME/.scientia.yaml - */ -func loadConfig() { - homeDir, err := os.UserHomeDir() - errorCheck(err, "No $HOME directory available?") - if optsVerbose { - log.Printf("Your $HOME: %s \n", homeDir) - } - - var configFile = homeDir + "/.scientia.yaml" - - if _, err := os.Stat(configFile); errors.Is(err, os.ErrNotExist) { - log.Printf("Config file not found: %s \n", configFile) - - if optsCreateConfig { - log.Printf("Creating new default config file: %s \n", configFile) - - newConfig, err := os.Create(configFile) - errorCheck(err, "Can not create config file!") - defer newConfig.Close() - - _, err = fmt.Fprintf(newConfig, "# scientia go client config file.\n") - errorCheck(err, "Can not write to new config file") - fmt.Fprintf(newConfig, "# See %s for more details.\n", website) - fmt.Fprintf(newConfig, "# Version: %s\n", version) - fmt.Fprintf(newConfig, "endpoint:\n") - fmt.Fprintf(newConfig, " host: http://your-scientia-endpoi.nt/api.php\n") - fmt.Fprintf(newConfig, " secret: %s\n", randStringBytes(50)) - - log.Fatalf("New default config file created: - %s - Edit and launch again!", configFile) - } - } - - existingConfigFile, err := os.Open(configFile) - errorCheck(err, "Can not open config file. Did you create one with -create-config-file?") - defer existingConfigFile.Close() - if optsVerbose { - log.Printf("Reading config file: %s \n", configFile) - } - - var decoder = yaml.NewDecoder(existingConfigFile) - err = decoder.Decode(&cfg) - errorCheck(err, "Can not decode config file") - - if cfg.Endpoint.Host == "" || cfg.Endpoint.Secret == "" { - log.Fatal("Empty config?") - } - - if optsDebug { - log.Println(cfg.Endpoint.Host) - log.Println(cfg.Endpoint.Secret) - } -} - -/** - * Do a http POST call to the defined endpoint - * and upload the payload - * Return response body as string - */ -func uploadCall(payload string) string { - - if optsVerbose { - log.Println("Starting to upload data") - } - if optsDebug { - log.Println(payload) - } - if len(payload) == 0 { - log.Fatal("Nothing provided to upload") - } - - payloadStruct := PayloadJson{ - Asl: cfg.Endpoint.Secret, - Data: payload, - } - - jsonData, err := json.Marshal(payloadStruct) - errorCheck(err, "Can not create json payload") - - req, err := http.NewRequest(http.MethodPost, cfg.Endpoint.Host, bytes.NewBuffer(jsonData)) - errorCheck(err, "Can not create http request") - // We need to set the content type from the writer, it includes necessary boundary as well - req.Header.Set("Content-Type", "application/json; charset=UTF-8") - req.Header.Set("User-Agent", "scientiaAgent/1.0") - - // Do the request - client := &http.Client{} - response, err := client.Do(req) - errorCheck(err, "POST request failed") - - responseBody, err := io.ReadAll(response.Body) - errorCheck(err, "Can not read response body") - - if optsVerbose { - log.Println("Request done") - } - if optsDebug { - log.Printf("Response status code: %d\n", response.StatusCode) - log.Printf("Response headers: %#v\n", response.Header) - log.Println(string(responseBody)) - } - - return string(responseBody) -} - -/** - * check if file is provided as commandline argument - * or piped into - * return the read data as string - */ -func getInput() string { - if optsVerbose { - log.Println("Getting input") - } - - var inputString string - - if filename := flag.Arg(0); filename != "" { - if optsVerbose { - log.Println("Read from file argument") - } - - bytes, err := os.ReadFile(filename) - errorCheck(err, "Error opening file") - inputString = string(bytes) - } else { - stat, _ := os.Stdin.Stat() - if (stat.Mode() & os.ModeCharDevice) == 0 { - if optsVerbose { - log.Println("data is being piped") - } - - bytes, _ := io.ReadAll(os.Stdin) - inputString = string(bytes) - } - } - - if len(inputString) == 0 { - log.Fatal("Nothing provided to upload") - } - - return inputString -} diff --git a/client/go-cli/scientia/Makefile b/client/go-cli/scientia/Makefile new file mode 100644 index 0000000..550711d --- /dev/null +++ b/client/go-cli/scientia/Makefile @@ -0,0 +1,11 @@ +all: scientia + +scientia: + @echo "Building for local os/arch..." + go build -o scientia + chmod u+x scientia + +devbuild: clean scientia + +clean: + rm -f scientia diff --git a/client/go-cli/scientia/cmd/config.go b/client/go-cli/scientia/cmd/config.go new file mode 100644 index 0000000..42ee9c9 --- /dev/null +++ b/client/go-cli/scientia/cmd/config.go @@ -0,0 +1,23 @@ +package cmd + +import ( + "os" + + "github.com/spf13/cobra" +) + +var configCmd = &cobra.Command { + Use: "config", + Short: "Modify config", + Long: "Read, edit and initialize scientia configuration.", + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + cmd.Help() + os.Exit(0) + } + }, +} + +func init() { + rootCmd.AddCommand(configCmd) +} diff --git a/client/go-cli/scientia/cmd/config_init.go b/client/go-cli/scientia/cmd/config_init.go new file mode 100644 index 0000000..5fef19a --- /dev/null +++ b/client/go-cli/scientia/cmd/config_init.go @@ -0,0 +1,58 @@ +package cmd + +import ( + "errors" + "fmt" + "os" + + "github.com/kirsle/configdir" + "github.com/spf13/cobra" + + Helper "scientia/lib" +) + +var configInitCmd = &cobra.Command { + Use: "init", + Short: "Initialize config", + Long: `Read, edit and initialize scientia configuration`, + Run: func(cmd *cobra.Command, args []string) { + initConfig() + }, +} + +func init() { + configCmd.AddCommand(configInitCmd) +} + + +func initConfig() { + configPath := configdir.LocalConfig("scientia") + err := configdir.MakePath(configPath) // Ensure it exists. + Helper.ErrorCheck(err, "No $HOME/.config/scientia directory available?") + var configFile = configPath + "/.scientia.yaml" + + if FlagDebug { + fmt.Printf("Local user config path: %s\n", configPath) + fmt.Printf("Local user config file: %s\n", configFile) + } + + if _, err := os.Stat(configFile); errors.Is(err, os.ErrNotExist) { + fmt.Printf("Creating new default config file: %s \n", configFile) + + newConfig, err := os.Create(configFile) + Helper.ErrorCheck(err, "Can not create config file!") + defer newConfig.Close() + + _, err = fmt.Fprintf(newConfig, "# scientia go client config file.\n") + Helper.ErrorCheck(err, "Can not write to new config file") + fmt.Fprintf(newConfig, "# See %s for more details.\n", Helper.Website) + fmt.Fprintf(newConfig, "# Version: %s\n", Helper.Version) + fmt.Fprintf(newConfig, "endpoint:\n") + fmt.Fprintf(newConfig, " host: http://your-scientia-endpoi.nt/api.php\n") + fmt.Fprintf(newConfig, " secret: %s\n", Helper.RandStringBytes(50)) + + } else { + fmt.Printf("Config file exists.: %s \n", configFile) + fmt.Println("User read to display or edit to modify the config file.") + } +} diff --git a/client/go-cli/scientia/cmd/root.go b/client/go-cli/scientia/cmd/root.go new file mode 100644 index 0000000..e9a0d79 --- /dev/null +++ b/client/go-cli/scientia/cmd/root.go @@ -0,0 +1,44 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" +) + +// FlagVerbose is a global flag +var FlagVerbose bool + +// FlagDebug is a global flag +var FlagDebug bool + +// The rootCmd +var rootCmd = &cobra.Command { + Use: "scientia", + Short: "scientia client", + Long: `cognizance, insight, knowledge. +A client to scientia. +More information: https://www.bananas-playground.net/projekt/scientia/`, + Run: func(cmd *cobra.Command, args []string) { + + // display help if no arguments are given + if len(args) == 0 { + cmd.Help() + os.Exit(0) + } + }, +} + +func init() { + // add global flags + rootCmd.PersistentFlags().BoolVarP(&FlagVerbose, "verbose", "v", false, "verbose output") + rootCmd.PersistentFlags().BoolVarP(&FlagDebug, "debug", "d", false, "debug output") +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} diff --git a/client/go-cli/scientia/go.mod b/client/go-cli/scientia/go.mod new file mode 100644 index 0000000..b46d0b3 --- /dev/null +++ b/client/go-cli/scientia/go.mod @@ -0,0 +1,10 @@ +module scientia + +go 1.22.4 + +require ( + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f // indirect + github.com/spf13/cobra v1.8.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect +) diff --git a/client/go-cli/scientia/go.sum b/client/go-cli/scientia/go.sum new file mode 100644 index 0000000..8d8834d --- /dev/null +++ b/client/go-cli/scientia/go.sum @@ -0,0 +1,12 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f h1:dKccXx7xA56UNqOcFIbuqFjAWPVtP688j5QMgmo6OHU= +github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f/go.mod h1:4rEELDSfUAlBSyUjPG0JnaNGjf13JySHFeRdD/3dLP0= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/client/go-cli/scientia/lib/helpers.go b/client/go-cli/scientia/lib/helpers.go new file mode 100644 index 0000000..c221475 --- /dev/null +++ b/client/go-cli/scientia/lib/helpers.go @@ -0,0 +1,26 @@ +package lib + +import ( + "log" + "math/rand" +) + +const Version string = "1.0" +const Website string = "https://www.bananas-playground.net/projekt/scientia/" + +// ErrorCheck if error then display it with an addition message +func ErrorCheck(e error, msg string) { + if e != nil { + log.Fatal(msg, " ; Message: ", e) + } +} + +// RandStringBytes creates just a random string +func RandStringBytes(n int) string { + var letters = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-_" + b := make([]byte, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return string(b) +} diff --git a/client/go-cli/scientia/main.go b/client/go-cli/scientia/main.go new file mode 100644 index 0000000..05eada2 --- /dev/null +++ b/client/go-cli/scientia/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "scientia/cmd" +) + +func main() { + cmd.Execute() +} diff --git a/client/go-cli/scientia/scientia b/client/go-cli/scientia/scientia new file mode 100755 index 0000000..0749306 Binary files /dev/null and b/client/go-cli/scientia/scientia differ diff --git a/client/go-cli_/Makefile b/client/go-cli_/Makefile new file mode 100644 index 0000000..b8ab31a --- /dev/null +++ b/client/go-cli_/Makefile @@ -0,0 +1,28 @@ +all: + @echo "Options are: build scientia-cli scientia-cli-linux-amd64 scientia-cli-windows-amd64 scientia-cli-darwin-amd64 clean buildall" + +scientia-cli: + @echo "Building for local os/arch..." + go build -o scientia-cli + +scientia-cli-linux-amd64: + @echo "Building for linux/amd64 arch..." + GOOS=linux GOARCH=amd64 go build -o scientia-cli-linux-amd64 + +scientia-cli-windows-amd64: + @echo "Building for windows/amd64 arch..." + GOOS=windows GOARCH=amd64 go build -o scientia-cli-windows-amd64 + +scientia-cli-darwin-amd64: + @echo "Building for macOS/amd64 arch..." + GOOS=darwin GOARCH=amd64 go build -o scientia-cli-darwin-amd64 + +build: + @echo "Building for local os/arch..." + go build -o scientia-cli-`go env GOOS`-`go env GOARCH` + +buildall: scientia-cli-linux-amd64 scientia-cli-windows-amd64 scientia-cli-darwin-amd64 + @echo "Building for linux/amd64, windows/amd64 and macOS/amd64 arch..." + +clean: + rm -f scientia-cli scientia-cli-darwin-amd64 scientia-cli-windows-amd64 scientia-cli-linux-amd64 \ No newline at end of file diff --git a/client/go-cli_/README.md b/client/go-cli_/README.md new file mode 100644 index 0000000..04ff93f --- /dev/null +++ b/client/go-cli_/README.md @@ -0,0 +1,32 @@ +# scienta go cli client + +This is a terminal cli client written in go to be used with scientia +https://://www.bananas-playground.net/projekt/scientia/ + +!WARNING! +This is a very simple, with limited experience written, go program. +Use at own risk and feel free to improve. + +# Howto build + +Nothing special, just use the provided Makefile or directly +"go build -o scientia-cli" to use your current os/arch settings. + +# Usage + +At first usage you need to create the config and the individual secret. +Run $scientia-cli -create-config-file to create the default config file. +The path to the config file is printed. +Change the host address and update your server it with the secret, which is randomly created. + +## Create + +Read from a file `$ scientia-cli file.txt` or piped `$ cat file.txt | scientia-cli` + +# Commandline arguments + +## Optinal + ++ `-create-config-file` Create default config file ++ `-debug` Print debug infos ++ `-verbose` Produce verbose output diff --git a/client/go-cli_/go.mod b/client/go-cli_/go.mod new file mode 100644 index 0000000..5a0b7ac --- /dev/null +++ b/client/go-cli_/go.mod @@ -0,0 +1,5 @@ +module scientia/go-client-cli + +go 1.20 + +require gopkg.in/yaml.v2 v2.4.0 diff --git a/client/go-cli_/go.sum b/client/go-cli_/go.sum new file mode 100644 index 0000000..dd0bc19 --- /dev/null +++ b/client/go-cli_/go.sum @@ -0,0 +1,4 @@ +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/client/go-cli_/scientia-cli.go b/client/go-cli_/scientia-cli.go new file mode 100644 index 0000000..ef999a8 --- /dev/null +++ b/client/go-cli_/scientia-cli.go @@ -0,0 +1,271 @@ +package main + +import ( + "bytes" + "encoding/json" + "errors" + "flag" + "fmt" + "io" + "log" + "math/rand" + "net/http" + "os" + + "gopkg.in/yaml.v2" +) + +/** + * scientia + * + * Copyright 2023 - 2024 Johannes Keßler + * + * https://www.bananas-playground.net/projekt/scientia/ + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the COMMON DEVELOPMENT AND DISTRIBUTION LICENSE + * + * You should have received a copy of the + * COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 + * along with this program. If not, see http://www.sun.com/cddl/cddl.html + */ + +const website = "https://www.bananas-playground.net/projekt/scientia/" +const version = "1.0" + +// used for non-existing default config +const letters = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-_" + +// command line parameters +var optsVerbose bool +var optsCreateConfig bool +var optsDebug bool + +// config +var cfg Config + +// config file struct +type Config struct { + Endpoint struct { + Host string `yaml:"host"` + Secret string `yaml:"secret"` + } `yaml:"endpoint"` +} + +// post json struct +type PayloadJson struct { + Asl string `json:"asl"` + Data string `json:"data"` +} + +// response json struct +type Response struct { + Message string `json:"message"` + Status int `json:"status"` +} + +/** + * Main + */ +func main() { + + // https://cli.urfave.org/v2/examples/arguments/ + // https://github.com/dnote/dnote + //https://github.com/spf13/cobra/blob/main/site/content/user_guide.md + + // parse commandline parameters + flag.BoolVar(&optsVerbose, "verbose", false, "Produce verbose output") + flag.BoolVar(&optsCreateConfig, "create-config-file", false, "Create default config file") + flag.BoolVar(&optsDebug, "debug", false, "Print debug infos") + flag.Parse() + if optsDebug { + fmt.Println("verbose:", optsVerbose) + fmt.Println("create-config-file:", optsCreateConfig) + fmt.Println("debug:", optsDebug) + } + + // load the config and populate Config + loadConfig() + + // get the payload + payload := getInput() + if optsDebug { + log.Println(payload) + } + + // do the upload and get the response + responseString := uploadCall(payload) + response := Response{} + json.Unmarshal([]byte(responseString), &response) + + // print the result and link to the pasty + fmt.Printf("Status: %d\n", response.Status) + fmt.Printf("Message: %s\n", response.Message) +} + +/** + * Check and display error with additional message + */ +func errorCheck(e error, msg string) { + if e != nil { + log.Fatal(msg, " ; Errrormsg: ", e) + } +} + +/** + * just a random string + */ +func randStringBytes(n int) string { + b := make([]byte, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return string(b) +} + +/** + * load or even create a default config + * $HOME/.scientia.yaml + */ +func loadConfig() { + homeDir, err := os.UserHomeDir() + errorCheck(err, "No $HOME directory available?") + if optsVerbose { + log.Printf("Your $HOME: %s \n", homeDir) + } + + var configFile = homeDir + "/.scientia.yaml" + + if _, err := os.Stat(configFile); errors.Is(err, os.ErrNotExist) { + log.Printf("Config file not found: %s \n", configFile) + + if optsCreateConfig { + log.Printf("Creating new default config file: %s \n", configFile) + + newConfig, err := os.Create(configFile) + errorCheck(err, "Can not create config file!") + defer newConfig.Close() + + _, err = fmt.Fprintf(newConfig, "# scientia go client config file.\n") + errorCheck(err, "Can not write to new config file") + fmt.Fprintf(newConfig, "# See %s for more details.\n", website) + fmt.Fprintf(newConfig, "# Version: %s\n", version) + fmt.Fprintf(newConfig, "endpoint:\n") + fmt.Fprintf(newConfig, " host: http://your-scientia-endpoi.nt/api.php\n") + fmt.Fprintf(newConfig, " secret: %s\n", randStringBytes(50)) + + log.Fatalf("New default config file created: - %s - Edit and launch again!", configFile) + } + } + + existingConfigFile, err := os.Open(configFile) + errorCheck(err, "Can not open config file. Did you create one with -create-config-file?") + defer existingConfigFile.Close() + if optsVerbose { + log.Printf("Reading config file: %s \n", configFile) + } + + var decoder = yaml.NewDecoder(existingConfigFile) + err = decoder.Decode(&cfg) + errorCheck(err, "Can not decode config file") + + if cfg.Endpoint.Host == "" || cfg.Endpoint.Secret == "" { + log.Fatal("Empty config?") + } + + if optsDebug { + log.Println(cfg.Endpoint.Host) + log.Println(cfg.Endpoint.Secret) + } +} + +/** + * Do a http POST call to the defined endpoint + * and upload the payload + * Return response body as string + */ +func uploadCall(payload string) string { + + if optsVerbose { + log.Println("Starting to upload data") + } + if optsDebug { + log.Println(payload) + } + if len(payload) == 0 { + log.Fatal("Nothing provided to upload") + } + + payloadStruct := PayloadJson{ + Asl: cfg.Endpoint.Secret, + Data: payload, + } + + jsonData, err := json.Marshal(payloadStruct) + errorCheck(err, "Can not create json payload") + + req, err := http.NewRequest(http.MethodPost, cfg.Endpoint.Host, bytes.NewBuffer(jsonData)) + errorCheck(err, "Can not create http request") + // We need to set the content type from the writer, it includes necessary boundary as well + req.Header.Set("Content-Type", "application/json; charset=UTF-8") + req.Header.Set("User-Agent", "scientiaAgent/1.0") + + // Do the request + client := &http.Client{} + response, err := client.Do(req) + errorCheck(err, "POST request failed") + + responseBody, err := io.ReadAll(response.Body) + errorCheck(err, "Can not read response body") + + if optsVerbose { + log.Println("Request done") + } + if optsDebug { + log.Printf("Response status code: %d\n", response.StatusCode) + log.Printf("Response headers: %#v\n", response.Header) + log.Println(string(responseBody)) + } + + return string(responseBody) +} + +/** + * check if file is provided as commandline argument + * or piped into + * return the read data as string + */ +func getInput() string { + if optsVerbose { + log.Println("Getting input") + } + + var inputString string + + if filename := flag.Arg(0); filename != "" { + if optsVerbose { + log.Println("Read from file argument") + } + + bytes, err := os.ReadFile(filename) + errorCheck(err, "Error opening file") + inputString = string(bytes) + } else { + stat, _ := os.Stdin.Stat() + if (stat.Mode() & os.ModeCharDevice) == 0 { + if optsVerbose { + log.Println("data is being piped") + } + + bytes, _ := io.ReadAll(os.Stdin) + inputString = string(bytes) + } + } + + if len(inputString) == 0 { + log.Fatal("Nothing provided to upload") + } + + return inputString +}