238 lines
5.5 KiB
Go
238 lines
5.5 KiB
Go
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()
|
|
}
|
|
|