Compare commits
1 Commits
6d9aa92e7e
...
85b5416db5
Author | SHA1 | Date |
---|---|---|
Evan Chen | 85b5416db5 |
19
README.md
19
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
|
||||
|
|
28
app.go
28
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"
|
||||
|
|
41
configui.go
41
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"
|
||||
|
|
31
handler.go
31
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 {
|
||||
|
|
14
server.go
14
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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -18,6 +18,32 @@ var Active = "{{.File.Name}}";
|
|||
<div class="box">
|
||||
{{template "components/menu" .}}
|
||||
</div>
|
||||
{{if .Actions}}
|
||||
<div class="box">
|
||||
<p class="menu-label has-text-left">Actions</p>
|
||||
<div class="buttons are-small">
|
||||
{{- range .Actions -}}
|
||||
<button class="button has-tooltip-arrow" id="actbtn-{{.Name|normal}}"
|
||||
data-tooltip="{{.Cmd}}" onclick="toolDoAction('{{.Name}}')">
|
||||
<span>{{- .Name -}}</span>
|
||||
</button>
|
||||
{{- end -}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Integrations}}
|
||||
<div class="box">
|
||||
<p class="menu-label has-text-left">Integrations</p>
|
||||
<div class="buttons are-small">
|
||||
{{- range .Integrations -}}
|
||||
<button class="button has-tooltip-arrow" id="itgbtn-{{.Name|normal}}"
|
||||
data-tooltip="{{.Description}}" onclick="toolDoIntegration('{{.Name}}')">
|
||||
<span>{{- .Name -}}</span>
|
||||
</button>
|
||||
{{- end -}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="box has-text-centered">
|
||||
<a href="{{.BaseUrl}}api/export" class="button is-small">Export Files</a>
|
||||
</div>
|
||||
|
@ -37,6 +63,28 @@ var Active = "{{.File.Name}}";
|
|||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
async function toolDoAction(name){
|
||||
let el = document.getElementById('actbtn-'+name.replaceAll(" ","-"));
|
||||
el.classList.add("is-loading")
|
||||
el.classList.remove("has-tooltip-arrow")
|
||||
await DoAction('action',name)
|
||||
el.classList.remove("is-loading")
|
||||
el.classList.add("has-tooltip-arrow")
|
||||
{{if not .ResultBellow}}ResultViewTog(){{end}}
|
||||
}
|
||||
|
||||
async function toolDoIntegration(name){
|
||||
let el = document.getElementById('itgbtn-'+name.replaceAll(" ","-"));
|
||||
el.classList.add("is-loading")
|
||||
el.classList.remove("has-tooltip-arrow")
|
||||
await DoAction('integration',name)
|
||||
el.classList.remove("is-loading")
|
||||
el.classList.add("has-tooltip-arrow")
|
||||
{{if not .ResultBellow}}ResultViewTog(){{end}}
|
||||
}
|
||||
</script>
|
||||
{{if not .ResultBellow}}
|
||||
{{template "components/result" .}}
|
||||
{{end}}
|
||||
|
|
Loading…
Reference in New Issue