configui/handler.go

300 lines
6.4 KiB
Go

package configui
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strconv"
"kumoly.io/lib/ksrv"
)
func (cui *ConfigUI) ListFiles(w http.ResponseWriter, r *http.Request) {
data, err := json.Marshal(cui.Files)
if err != nil {
panic(err)
}
w.Write(data)
}
func (cui *ConfigUI) GetFile(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
file, err := cui.File(name)
if err != nil {
ksrv.Response(w, 404, []byte("file not found"))
return
}
data, err := file.Read()
if err != nil {
panic(err)
}
stat, err := os.Stat(file.Path)
if err != nil {
panic(err)
}
response, err := json.Marshal(map[string]string{
"path": file.Path,
"name": file.Name,
"cmd": file.Cmd,
"data": string(data),
"delta": strconv.Itoa(int(stat.Size())),
})
if err != nil {
panic(err)
}
w.Header().Set("Content-Type", "application/json")
w.Write(response)
}
func (cui *ConfigUI) GetDelta(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
delta, err := strconv.ParseInt(r.URL.Query().Get("delta"), 10, 64)
if err != nil {
panic(err)
}
file, err := cui.File(name)
if err != nil {
ksrv.Response(w, 404, []byte("file not found"))
return
}
f, err := os.Open(file.Path)
if err != nil {
panic(err)
}
defer f.Close()
stat, err := os.Stat(file.Path)
if err != nil {
panic(err)
}
if delta > stat.Size() {
panic(fmt.Errorf("delta(%d) is larger than file size(%d), the file might have been changed", delta, stat.Size()))
}
buf := make([]byte, stat.Size()-delta)
_, err = f.ReadAt(buf, delta)
if err != nil {
panic(err)
}
response, err := json.Marshal(map[string]string{
"path": file.Path,
"name": file.Name,
"cmd": file.Cmd,
"data": string(buf),
"delta": strconv.Itoa(int(stat.Size())),
})
if err != nil {
panic(err)
}
w.Header().Set("Content-Type", "application/json")
w.Write(response)
}
func (cui *ConfigUI) PostFile(w http.ResponseWriter, r *http.Request) {
data, err := ioutil.ReadAll(r.Body)
r.Body.Close()
if err != nil {
panic(err)
}
f := File{}
if err := json.Unmarshal(data, &f); err != nil {
panic(err)
}
file, err := cui.File(f.Name)
if err != nil {
ksrv.Response(w, 404, []byte("file not found"))
return
}
if err := file.Write([]byte(f.Data)); err != nil {
panic(err)
}
w.Write([]byte("ok"))
}
func (cui *ConfigUI) Apply(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
file, err := cui.File(name)
if err != nil {
ksrv.Response(w, 404, []byte("file not found"))
return
}
result, err := file.Do(cui.cmdTimeout, make(chan int))
if err != nil {
panic(err)
}
w.Write([]byte(result))
}
func (cui *ConfigUI) DoAction(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
for i, v := range cui.Actions {
if v.Name == name {
// limit running instance to one
if cap(cui.Actions[i].run) != 1 {
cui.Actions[i].run = make(chan struct{}, 1)
}
select {
case cui.Actions[i].run <- struct{}{}:
defer func() { <-cui.Actions[i].run }()
default:
panic(fmt.Errorf("another task of %s is running with pid: %d", name, v.pid))
}
file := &File{Name: name, Cmd: v.Cmd, owner: cui}
pid := make(chan int)
go func() {
cui.Actions[i].pid = <-pid
}()
result, err := file.Do(cui.cmdTimeout, pid)
if err != nil {
panic(err)
}
w.Write([]byte(result))
return
}
}
panic(fmt.Errorf("no action named: %v", name))
}
func (cui *ConfigUI) DoIntegration(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
for i, v := range cui.Integrations {
if v.Name == name {
// limit running instance to one
if cap(cui.Integrations[i].run) != 1 {
cui.Integrations[i].run = make(chan struct{}, 1)
}
select {
case cui.Integrations[i].run <- struct{}{}:
defer func() { <-cui.Integrations[i].run }()
default:
panic(fmt.Errorf("another task of %s is running", name))
}
result, err := v.Cmd()
if err != nil {
panic(err)
}
w.Write([]byte(result))
return
}
}
panic(fmt.Errorf("no integration named: %v", name))
}
func (cui *ConfigUI) Download(w http.ResponseWriter, r *http.Request) {
if name := r.URL.Query().Get("name"); name != "" {
if name == cui.AppName {
data, err := cui.Config()
if err != nil {
panic(err)
}
w.Header().Set(
"Content-Disposition", `
attachment; filename="`+cui.AppName+`.json"`,
)
w.Write(data)
return
}
file, err := cui.File(name)
if err != nil {
ksrv.Response(w, 404, []byte("file not found"))
return
}
data, err := file.Read()
if err != nil {
panic(err)
}
w.Header().Set("Content-Disposition", `attachment; filename="`+filepath.Base(file.Path)+`"`)
w.Write(data)
return
}
fs := []string{}
for _, i := range cui.fileIndex {
fs = append(fs, cui.Files[i].Path)
}
if cui.ConfigPath != "" {
fs = append(fs, cui.ConfigPath)
}
w.Header().Set("Content-Disposition", `attachment; filename="export.tar.gz"`)
bundle(w, fs, cui.AppName, false)
}
func (cui *ConfigUI) PostConfig(w http.ResponseWriter, r *http.Request) {
if cui.NoReconfig {
panic("system reconfig is disabled")
}
data, err := ioutil.ReadAll(r.Body)
r.Body.Close()
if err != nil {
panic(err)
}
err = cui.LoadConfig(string(data))
if err != nil {
panic(err)
}
if cui.ConfigPath != "" {
info, err := os.Stat(cui.ConfigPath)
if err != nil {
panic(err)
}
err = os.WriteFile(cui.ConfigPath, data, info.Mode())
if err != nil {
panic(err)
}
}
w.Write([]byte("ok"))
}
func (cui *ConfigUI) GetConfig(w http.ResponseWriter, r *http.Request) {
data, err := cui.Config()
if err != nil {
panic(err)
}
w.Write(data)
}
func (cui *ConfigUI) GetRunning(w http.ResponseWriter, r *http.Request) {
ksrv.JSON(w, cui.Onitachi)
}
func (cui *ConfigUI) OniStart(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
i, ok := cui.OniIndex[name]
if !ok {
panic(ErrorOniNotFound.New(name))
}
oni := cui.Onitachi[i]
switch oni.State {
case "":
oni.Init(cui)
case STARTED:
panic(ErrorOniHasStarted)
}
oni.Start()
w.Write([]byte("ok"))
}
func (cui *ConfigUI) OniStop(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
i, ok := cui.OniIndex[name]
if !ok {
panic(ErrorOniNotFound.New(name))
}
oni := cui.Onitachi[i]
switch oni.State {
case STARTED:
oni.Stop()
default:
panic(ErrorOniNotStarted.New(name))
}
oni.Start()
w.Write([]byte("ok"))
}