initial commit
This commit is contained in:
commit
a4f37af597
6 changed files with 398 additions and 0 deletions
238
main.go
Normal file
238
main.go
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
APIUrl string `json:"api_url"`
|
||||
InsecureSkipVerify bool `json:"insecure_skip_verify"`
|
||||
}
|
||||
|
||||
var configPath = filepath.Join(os.Getenv("HOME"), ".klog", "config.json")
|
||||
|
||||
func saveConfig(cfg Config) error {
|
||||
data, err := json.MarshalIndent(cfg, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.MkdirAll(filepath.Dir(configPath), 0700)
|
||||
return ioutil.WriteFile(configPath, data, 0600)
|
||||
}
|
||||
|
||||
func loadConfig() (Config, error) {
|
||||
var cfg Config
|
||||
data, err := ioutil.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
err = json.Unmarshal(data, &cfg)
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
func buildAPIEndpoint(baseURL, method string) string {
|
||||
// ensure /api.php?method=... is appended correctly
|
||||
if strings.HasSuffix(baseURL, "/") {
|
||||
return fmt.Sprintf("%sapi.php?method=%s", baseURL, method)
|
||||
}
|
||||
return fmt.Sprintf("%s/api.php?method=%s", baseURL, method)
|
||||
}
|
||||
|
||||
|
||||
|
||||
func postFormRequest(method string, formValues url.Values) error {
|
||||
cfg, err := loadConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load config: %w", err)
|
||||
}
|
||||
|
||||
endpoint := buildAPIEndpoint(cfg.APIUrl, method)
|
||||
|
||||
req, err := http.NewRequest("POST", endpoint, strings.NewReader(formValues.Encode()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+cfg.AccessToken)
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
client := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: cfg.InsecureSkipVerify},
|
||||
},
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, _ := ioutil.ReadAll(resp.Body)
|
||||
|
||||
// Try to parse as JSON array first
|
||||
var arr []map[string]interface{}
|
||||
if err := json.Unmarshal(respBody, &arr); err == nil {
|
||||
if len(arr) == 0 {
|
||||
fmt.Println("(no data)")
|
||||
return nil
|
||||
}
|
||||
printTable(arr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Try to parse as single object
|
||||
var obj map[string]interface{}
|
||||
if err := json.Unmarshal(respBody, &obj); err == nil {
|
||||
printTable([]map[string]interface{}{obj})
|
||||
return nil
|
||||
}
|
||||
|
||||
// fallback: print raw
|
||||
fmt.Println(string(respBody))
|
||||
return nil
|
||||
}
|
||||
|
||||
func printTable(data []map[string]interface{}) {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
|
||||
// print headers
|
||||
for k := range data[0] {
|
||||
fmt.Fprintf(w, "%s\t", k)
|
||||
}
|
||||
fmt.Fprintln(w)
|
||||
|
||||
// print separator
|
||||
for range data[0] {
|
||||
fmt.Fprintf(w, "--------\t")
|
||||
}
|
||||
fmt.Fprintln(w)
|
||||
|
||||
// print rows
|
||||
for _, row := range data {
|
||||
for _, v := range row {
|
||||
fmt.Fprintf(w, "%v\t", v)
|
||||
}
|
||||
fmt.Fprintln(w)
|
||||
}
|
||||
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
|
||||
func main() {
|
||||
var rootCmd = &cobra.Command{Use: "klog"}
|
||||
|
||||
// config command
|
||||
var configCmd = &cobra.Command{Use: "config", Short: "Configure klog settings"}
|
||||
|
||||
var configTokenCmd = &cobra.Command{
|
||||
Use: "token [token]",
|
||||
Short: "Save access token",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg, _ := loadConfig()
|
||||
cfg.AccessToken = args[0]
|
||||
return saveConfig(cfg)
|
||||
},
|
||||
}
|
||||
|
||||
var configURLCmd = &cobra.Command{
|
||||
Use: "url [url]",
|
||||
Short: "Save API base URL",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg, _ := loadConfig()
|
||||
cfg.APIUrl = args[0]
|
||||
return saveConfig(cfg)
|
||||
},
|
||||
}
|
||||
|
||||
var configInsecureCmd = &cobra.Command{
|
||||
Use: "insecure [true|false]",
|
||||
Short: "Ignore self-signed certificates",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg, _ := loadConfig()
|
||||
val := strings.ToLower(args[0])
|
||||
cfg.InsecureSkipVerify = val == "true" || val == "1" || val == "yes"
|
||||
return saveConfig(cfg)
|
||||
},
|
||||
}
|
||||
|
||||
var configShowCmd = &cobra.Command{
|
||||
Use: "show",
|
||||
Short: "Show current configuration",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := loadConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Access Token: %s\nAPI URL: %s\nIgnore Self-Signed Certs: %v\n", cfg.AccessToken, cfg.APIUrl, cfg.InsecureSkipVerify)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
configCmd.AddCommand(configTokenCmd, configURLCmd, configInsecureCmd, configShowCmd)
|
||||
|
||||
// coffee command
|
||||
var cCmd = &cobra.Command{
|
||||
Use: "c",
|
||||
Aliases: []string{"k", "coffee", "kaffee"},
|
||||
Short: "Log a coffee",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return postFormRequest("coffee", nil)
|
||||
},
|
||||
}
|
||||
|
||||
var balanceCmd = &cobra.Command{
|
||||
Use: "balance",
|
||||
Short: "Show my balance",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return postFormRequest("balance", nil)
|
||||
},
|
||||
}
|
||||
|
||||
// brew command
|
||||
var planTime int
|
||||
var notify bool
|
||||
var plan bool
|
||||
var brewCmd = &cobra.Command{
|
||||
Use: "brew [waterIn]",
|
||||
Short: "Log a brew with parameters",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
cmd.Help()
|
||||
return nil
|
||||
}
|
||||
form := url.Values{}
|
||||
form.Set("waterIn", args[0])
|
||||
form.Set("notify", strconv.FormatBool(notify))
|
||||
form.Set("plan", strconv.FormatBool(plan))
|
||||
if plan {
|
||||
form.Set("planTime", strconv.Itoa(planTime))
|
||||
}
|
||||
return postFormRequest("brew", form)
|
||||
},
|
||||
}
|
||||
brewCmd.Flags().IntVar(&planTime, "planTime", 8, "Planning time in minutes (e.g. 4, 5, 6, 7, 8) (relevant only if plan=true)")
|
||||
brewCmd.Flags().BoolVar(¬ify, "notify", true, "Enable/disable notifications")
|
||||
brewCmd.Flags().BoolVar(&plan, "plan", true, "Enable/disable planning")
|
||||
|
||||
rootCmd.AddCommand(configCmd, balanceCmd, cCmd, brewCmd)
|
||||
rootCmd.Execute()
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue