package configui import ( "encoding/json" "fmt" "io/ioutil" "net/http" "os" "path/filepath" "strconv" "time" "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) 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, Action: v.Cmd, owner: cui} go func() { <-time.After(time.Millisecond * 10) cui.Actions[i].pid = file.pid }() result, err := file.Do(cui.cmdTimeout) 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) }