2021-10-23 04:56:24 +00:00
|
|
|
package configui
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2021-11-03 08:21:49 +00:00
|
|
|
"fmt"
|
2021-10-23 04:56:24 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2021-11-03 08:21:49 +00:00
|
|
|
"strconv"
|
2021-11-03 19:35:51 +00:00
|
|
|
|
|
|
|
"kumoly.io/lib/ksrv"
|
2021-10-23 04:56:24 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
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 {
|
2021-11-03 19:35:51 +00:00
|
|
|
ksrv.Response(w, 404, []byte("file not found"))
|
2021-10-23 04:56:24 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
data, err := file.Read()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2021-11-03 08:21:49 +00:00
|
|
|
stat, err := os.Stat(file.Path)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2021-10-23 04:56:24 +00:00
|
|
|
response, err := json.Marshal(map[string]string{
|
2021-11-11 02:21:10 +00:00
|
|
|
"path": file.Path,
|
|
|
|
"name": file.Name,
|
|
|
|
"cmd": file.Cmd,
|
|
|
|
"data": string(data),
|
|
|
|
"delta": strconv.Itoa(int(stat.Size())),
|
2021-11-03 08:21:49 +00:00
|
|
|
})
|
|
|
|
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 {
|
2021-11-03 19:35:51 +00:00
|
|
|
ksrv.Response(w, 404, []byte("file not found"))
|
2021-11-03 08:21:49 +00:00
|
|
|
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() {
|
2021-11-04 02:01:20 +00:00
|
|
|
panic(fmt.Errorf("delta(%d) is larger than file size(%d), the file might have been changed", delta, stat.Size()))
|
2021-11-03 08:21:49 +00:00
|
|
|
}
|
|
|
|
buf := make([]byte, stat.Size()-delta)
|
|
|
|
_, err = f.ReadAt(buf, delta)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
response, err := json.Marshal(map[string]string{
|
2021-11-11 02:21:10 +00:00
|
|
|
"path": file.Path,
|
|
|
|
"name": file.Name,
|
|
|
|
"cmd": file.Cmd,
|
|
|
|
"data": string(buf),
|
|
|
|
"delta": strconv.Itoa(int(stat.Size())),
|
2021-10-23 04:56:24 +00:00
|
|
|
})
|
|
|
|
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 {
|
2021-11-03 19:35:51 +00:00
|
|
|
ksrv.Response(w, 404, []byte("file not found"))
|
2021-10-23 04:56:24 +00:00
|
|
|
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 {
|
2021-11-03 19:35:51 +00:00
|
|
|
ksrv.Response(w, 404, []byte("file not found"))
|
2021-10-23 04:56:24 +00:00
|
|
|
return
|
|
|
|
}
|
2021-11-12 15:09:32 +00:00
|
|
|
result, err := file.Do(cui.cmdTimeout, make(chan int))
|
2021-10-23 04:56:24 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
w.Write([]byte(result))
|
|
|
|
}
|
|
|
|
|
2021-11-09 03:27:58 +00:00
|
|
|
func (cui *ConfigUI) DoAction(w http.ResponseWriter, r *http.Request) {
|
|
|
|
name := r.URL.Query().Get("name")
|
2021-11-11 01:58:36 +00:00
|
|
|
for i, v := range cui.Actions {
|
2021-11-09 03:27:58 +00:00
|
|
|
if v.Name == name {
|
2021-11-11 01:58:36 +00:00
|
|
|
|
|
|
|
// 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:
|
2021-11-12 15:09:32 +00:00
|
|
|
panic(fmt.Errorf("another task of %s is running with pid: %d", name, v.pid))
|
2021-11-11 01:58:36 +00:00
|
|
|
}
|
|
|
|
|
2021-11-12 15:09:32 +00:00
|
|
|
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)
|
2021-11-09 03:27:58 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2021-11-12 15:09:32 +00:00
|
|
|
w.Write([]byte(result))
|
|
|
|
|
2021-11-09 03:27:58 +00:00
|
|
|
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")
|
2021-11-11 01:58:36 +00:00
|
|
|
for i, v := range cui.Integrations {
|
2021-11-09 03:27:58 +00:00
|
|
|
if v.Name == name {
|
2021-11-11 01:58:36 +00:00
|
|
|
|
|
|
|
// 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))
|
|
|
|
}
|
|
|
|
|
2021-11-09 03:27:58 +00:00
|
|
|
result, err := v.Cmd()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
w.Write([]byte(result))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
panic(fmt.Errorf("no integration named: %v", name))
|
|
|
|
}
|
|
|
|
|
2021-10-23 04:56:24 +00:00
|
|
|
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 {
|
2021-11-03 19:35:51 +00:00
|
|
|
ksrv.Response(w, 404, []byte("file not found"))
|
2021-10-23 04:56:24 +00:00
|
|
|
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)
|
|
|
|
}
|
2021-11-12 09:25:44 +00:00
|
|
|
|
|
|
|
func (cui *ConfigUI) GetRunning(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ksrv.JSON(w, cui.Onitachi)
|
|
|
|
}
|
2021-11-12 15:09:32 +00:00
|
|
|
|
|
|
|
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"))
|
|
|
|
}
|