configui/main.go

209 lines
4.2 KiB
Go

package main
import (
"bytes"
"embed"
"encoding/json"
"errors"
"flag"
"fmt"
"html/template"
"io/ioutil"
"log"
"net/http"
"os"
"time"
"kumoly.io/tools/configui/configui"
)
//go:embed templates
var tmplFS embed.FS
var tmpl *template.Template
var (
cmdPath string
cmdAction string
cmdAlias string
cmdConfigPath string
cmdBind string
)
var files = map[string]*configui.File{}
func init() {
log.SetFlags(0)
flag.StringVar(&cmdConfigPath, "f", "", "path to config file")
flag.StringVar(&cmdPath, "p", "", "path to file, precedence over config")
flag.StringVar(&cmdAlias, "n", "", "alias of file")
flag.StringVar(&cmdAction, "c", "", "cmd to apply")
flag.StringVar(&cmdBind, "b", "localhost:8000", "address to bind")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: configui [options]\n")
flag.PrintDefaults()
}
}
func main() {
flag.Parse()
// setup values
if cmdPath != "" {
if cmdAlias == "" {
cmdAlias = cmdPath
}
files[cmdAlias] = &configui.File{
Path: cmdPath,
Alias: cmdAlias,
Action: cmdAction,
}
} else if cmdConfigPath == "" {
log.Fatalln(errors.New("no config found"))
} else {
conf, err := os.ReadFile(cmdConfigPath)
if err != nil {
log.Fatalln(err)
}
ftmp, err := configui.ReadConfig(string(conf))
if err != nil {
log.Fatalln(err)
}
files = configui.GetFileMap(ftmp)
}
// setup routes
mux := http.NewServeMux()
mux.HandleFunc("/api/files", func(rw http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
ListFiles(rw, r)
} else {
rw.WriteHeader(404)
}
})
mux.HandleFunc("/api/file", func(rw http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
GetFile(rw, r)
} else if r.Method == "POST" {
PostFile(rw, r)
} else {
rw.WriteHeader(404)
}
})
mux.HandleFunc("/api/apply", func(rw http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
Apply(rw, r)
} else {
rw.WriteHeader(404)
}
})
mux.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
buf := &bytes.Buffer{}
err := tmpl.ExecuteTemplate(buf, "home", nil)
if err != nil {
rw.WriteHeader(500)
rw.Write([]byte(err.Error()))
} else {
rw.Write(buf.Bytes())
}
})
tmpl = template.Must(template.New("").ParseFS(tmplFS, "templates/*.tmpl", "templates/**/*.tmpl"))
server := &http.Server{
Addr: cmdBind,
WriteTimeout: time.Second * 3,
ReadTimeout: time.Second * 30,
Handler: mux,
}
// start server
log.Println("Listening on ", cmdBind)
log.Fatal(server.ListenAndServe())
}
func ListFiles(rw http.ResponseWriter, r *http.Request) {
data, err := json.Marshal(files)
if err != nil {
rw.WriteHeader(500)
rw.Write([]byte(err.Error()))
return
}
rw.Write(data)
}
func GetFile(rw http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
file, ok := files[name]
if name == "" || !ok {
rw.WriteHeader(404)
rw.Write([]byte("file not found"))
return
}
data, err := file.Read()
if err != nil {
rw.WriteHeader(500)
rw.Write([]byte(err.Error()))
return
}
response, err := json.Marshal(map[string]string{
"path": file.Path,
"name": file.Alias,
"action": file.Action,
"data": string(data),
})
if err != nil {
rw.WriteHeader(500)
rw.Write([]byte(err.Error()))
return
}
rw.Header().Set("Content-Type", "application/json")
rw.Write(response)
}
func PostFile(rw http.ResponseWriter, r *http.Request) {
data, err := ioutil.ReadAll(r.Body)
r.Body.Close()
if err != nil {
rw.WriteHeader(500)
rw.Write([]byte(err.Error()))
return
}
f := configui.File{}
if err := json.Unmarshal(data, &f); err != nil {
rw.WriteHeader(500)
rw.Write([]byte(err.Error()))
return
}
file, ok := files[f.Alias]
if !ok {
rw.WriteHeader(404)
rw.Write([]byte("file not found"))
return
}
if err := file.Write([]byte(f.Data)); err != nil {
rw.WriteHeader(500)
rw.Write([]byte(err.Error()))
return
}
rw.Write([]byte("ok"))
}
func Apply(rw http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
file, ok := files[name]
if name == "" || !ok {
rw.WriteHeader(404)
rw.Write([]byte("file not found"))
return
}
result, err := file.Do()
if err != nil {
rw.WriteHeader(500)
rw.Write([]byte(err.Error()))
return
}
rw.Write([]byte(result))
}