diff --git a/README.md b/README.md index 09225f7..258ff5f 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,12 @@ sudo sh -c "curl -fsSL RELEASE_URL | tar -C /usr/local/bin/ -xz" "data": "" } ], + "Actions": [ + { + "name": "User", + "cmd": "whoami" + } + ], "result_bellow": false, "hide_config": false, "log_path": "access.log", @@ -59,6 +65,19 @@ sudo sh -c "curl -fsSL RELEASE_URL | tar -C /usr/local/bin/ -xz" `configui -f PATH/TO/CONFIG` +## Add integrations to ConfigUI + +```go +cui.Integrations = append(cui.Integrations, &configui.Integration{ + Name: "test", Description: "test", Cmd: func() (string, error) { + if rand.Int31n(40) > 20 { + return "ok", nil + } + return "", fmt.Errorf("error") + }, +}) +``` + ## Systemd ```ini diff --git a/app.go b/app.go index 5e33b2c..8cb4455 100644 --- a/app.go +++ b/app.go @@ -23,14 +23,16 @@ type Editor struct { } type Page struct { - AppName string - BaseUrl string - Version string - Build string - Files []ActiveFile - Error string - File ActiveFile - Editor Editor + AppName string + BaseUrl string + Actions []Action + Integrations []*Integration + Version string + Build string + Files []ActiveFile + Error string + File ActiveFile + Editor Editor Static bool HideConfig bool @@ -44,6 +46,12 @@ func (cui *ConfigUI) App(w http.ResponseWriter, r *http.Request) { } Files := []ActiveFile{} + // for i := range cui.Files { + // Files = append(Files, ActiveFile{ + // Name: cui.Files[i].Name, + // Path: cui.Files[i].Path, + // }) + // } for _, i := range cui.fileIndex { Files = append(Files, ActiveFile{ Name: cui.Files[i].Name, @@ -69,6 +77,8 @@ func (cui *ConfigUI) App(w http.ResponseWriter, r *http.Request) { Editor: Editor{ Platform: plat, }, + Actions: cui.Actions, + Integrations: cui.Integrations, Static: cui.NoReconfig, HideConfig: cui.HideConfig, ResultBellow: cui.ResultBellow, @@ -83,7 +93,7 @@ func (cui *ConfigUI) App(w http.ResponseWriter, r *http.Request) { if name == "" || err != nil { tmp, err = cui.Config() data.File.Name = cui.AppName - data.File.Path = ":mem:" + // data.File.Path = ":mem:" data.Editor.Lang = "json" if name != "" { data.Error = name + " not found\n" diff --git a/configui.go b/configui.go index bc2b6fd..c41ce1a 100644 --- a/configui.go +++ b/configui.go @@ -7,6 +7,7 @@ import ( "fmt" "html/template" "os" + "strings" "sync" "time" @@ -18,7 +19,7 @@ import ( var UNIX_SHELL = "sh" var WIN_SHELL = "cmd" -const version = "v0.1.9" +const version = "v0.1.10" //go:embed templates var tmplFS embed.FS @@ -32,6 +33,17 @@ var Ext2Mode map[string]string = map[string]string{ "md": "markdown", } +type Action struct { + Name string `json:"name"` + Cmd string `json:"cmd"` +} + +type Integration struct { + Name string `json:"name"` + Description string `json:"description"` + Cmd func() (string, error) `json:"-"` +} + type ConfigUI struct { AppName string `json:"app_name"` Prod bool `json:"production"` @@ -43,8 +55,10 @@ type ConfigUI struct { CmdTimeout string `json:"timeout"` cmdTimeout time.Duration - Files []*File `json:"files"` - fileIndex map[string]int + Files []*File `json:"files"` + fileIndex map[string]int + Actions []Action `json:"actions"` + Integrations []*Integration `json:"integrations"` ResultBellow bool `json:"result_bellow"` HideConfig bool `json:"hide_config"` @@ -55,11 +69,11 @@ type ConfigUI struct { TmplFS embed.FS `json:"-"` tmpl *engine.Engine - PublicFS embed.FS `json:"-"` - log *klog.Logger `json:"-"` - ksrv_log *klog.Logger `json:"-"` - f *os.File `json:"-"` - configLock sync.Mutex `json:"-"` + PublicFS embed.FS `json:"-"` + log *klog.Logger + ksrv_log *klog.Logger + f *os.File + configLock sync.Mutex } func New() *ConfigUI { @@ -71,12 +85,16 @@ func New() *ConfigUI { } return items }, + "normal": func(name string) string { + return strings.ReplaceAll(name, " ", "-") + }, }).ParseFS(tmplFS, "templates/*.tmpl", "templates/**/*.tmpl")) return &ConfigUI{ fileIndex: map[string]int{}, Prod: true, AppName: "ConfigUI", BaseUrl: "/", + Actions: []Action{{Name: "User", Cmd: "whoami"}}, PublicFS: public.FS, TmplFS: tmplFS, tmpl: tmpl, @@ -133,6 +151,13 @@ func (cui *ConfigUI) LoadConfig(confstr string) error { cui.NoReconfig = tmpConf.NoReconfig cui.ResultBellow = tmpConf.ResultBellow + cui.Actions = tmpConf.Actions + for i := range cui.Actions { + if cui.Actions[i].Name == "" { + cui.Actions[i].Name = cui.Actions[i].Cmd + } + } + cui.AppName = tmpConf.AppName if cui.AppName == "" { cui.AppName = "ConfigUI" diff --git a/handler.go b/handler.go index 88b6455..c593c45 100644 --- a/handler.go +++ b/handler.go @@ -128,6 +128,37 @@ func (cui *ConfigUI) Apply(w http.ResponseWriter, r *http.Request) { w.Write([]byte(result)) } +func (cui *ConfigUI) DoAction(w http.ResponseWriter, r *http.Request) { + name := r.URL.Query().Get("name") + for _, v := range cui.Actions { + if v.Name == name { + file := &File{Name: name, Action: v.Cmd, owner: cui} + 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 _, v := range cui.Integrations { + if v.Name == 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 { diff --git a/server.go b/server.go index 18e5dc5..5d7a19d 100644 --- a/server.go +++ b/server.go @@ -84,6 +84,20 @@ func (cui *ConfigUI) mux() *http.ServeMux { w.WriteHeader(404) } }) + cuiR.HandleFunc("/api/action", func(w http.ResponseWriter, r *http.Request) { + if r.Method == "POST" { + cui.DoAction(w, r) + } else { + w.WriteHeader(404) + } + }) + cuiR.HandleFunc("/api/integration", func(w http.ResponseWriter, r *http.Request) { + if r.Method == "POST" { + cui.DoIntegration(w, r) + } else { + w.WriteHeader(404) + } + }) cuiR.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.FS(cui.PublicFS)))) cuiR.HandleFunc("/", cui.App) return cuiR diff --git a/templates/base/script.tmpl b/templates/base/script.tmpl index 7f7332c..1f1d6ed 100644 --- a/templates/base/script.tmpl +++ b/templates/base/script.tmpl @@ -75,6 +75,19 @@ async function FileApply(){ } } +async function DoAction(type, name){ + const res = await fetch('{{.BaseUrl}}api/'+type+'?name='+ name, { + method: 'POST', + }); + if(!res.ok){ + const result = await Catch(res) + result_editor.session.setValue(result) + return + } + const result = await res.text() + result_editor.session.setValue(result) +} + async function FileDelta(){ if (Active == '{{.AppName}}') { await FileGet() diff --git a/templates/home.tmpl b/templates/home.tmpl index ce88924..ed4b159 100644 --- a/templates/home.tmpl +++ b/templates/home.tmpl @@ -18,6 +18,32 @@ var Active = "{{.File.Name}}";