Compare commits
1 Commits
863b489412
...
bc443cc320
Author | SHA1 | Date |
---|---|---|
Evan Chen | bc443cc320 |
|
@ -0,0 +1,118 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func matchIPGlob(ip, pattern string) bool {
|
||||
parts := strings.Split(pattern, ".")
|
||||
seg := strings.Split(ip, ".")
|
||||
for i, part := range parts {
|
||||
|
||||
// normalize pattern to 3 digits
|
||||
switch len(part) {
|
||||
case 1:
|
||||
if part == "*" {
|
||||
part = "***"
|
||||
} else {
|
||||
part = "00" + part
|
||||
}
|
||||
case 2:
|
||||
if strings.HasPrefix(part, "*") {
|
||||
part = "*" + part
|
||||
} else if strings.HasSuffix(part, "*") {
|
||||
part = part + "*"
|
||||
} else {
|
||||
part = "0" + part
|
||||
}
|
||||
}
|
||||
|
||||
// normalize ip to 3 digits
|
||||
switch len(seg[i]) {
|
||||
case 1:
|
||||
seg[i] = "00" + seg[i]
|
||||
case 2:
|
||||
seg[i] = "0" + seg[i]
|
||||
}
|
||||
|
||||
for j := range part {
|
||||
if string(part[j]) == "*" {
|
||||
continue
|
||||
}
|
||||
if part[j] != seg[i][j] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func bundle(buf io.Writer, paths []string, flat bool) error {
|
||||
gw := gzip.NewWriter(buf)
|
||||
defer gw.Close()
|
||||
tw := tar.NewWriter(gw)
|
||||
defer tw.Close()
|
||||
|
||||
for _, file := range paths {
|
||||
if err := func(file string) error {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
info, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
header, err := tar.FileInfoHeader(info, info.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Use full path as name (FileInfoHeader only takes the basename)
|
||||
// If we don't do this the directory strucuture would
|
||||
// not be preserved
|
||||
// https://golang.org/src/archive/tar/common.go?#L626
|
||||
if !flat {
|
||||
if !strings.HasPrefix(file, "/") {
|
||||
file = "configui/" + file
|
||||
} else {
|
||||
file = "configui" + file
|
||||
}
|
||||
header.Name = file
|
||||
}
|
||||
|
||||
// Write file header to the tar archive
|
||||
err = tw.WriteHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Copy file content to tar archive
|
||||
_, err = io.Copy(tw, f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
}(file); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var ext2mode map[string]string = map[string]string{
|
||||
"go": "golang",
|
||||
"log": "sh",
|
||||
"txt": "text",
|
||||
"yml": "yaml",
|
||||
"conf": "ini",
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package main
|
|
@ -0,0 +1,42 @@
|
|||
package configui
|
||||
|
||||
var APP_NAME = "ConfigUI"
|
||||
var UNIX_SHELL = "sh"
|
||||
var WIN_SHELL = "cmd"
|
||||
|
||||
type ConfigUI struct {
|
||||
ConfigPath string `json:"config_path"`
|
||||
|
||||
NoReconfig bool `json:"no_reconfig"`
|
||||
LogPath string `json:"log_path"`
|
||||
SilentSysOut bool `json:"silent_sys_out"`
|
||||
AllowIP string `json:"allow_ip"`
|
||||
|
||||
Files []*File `json:"files"`
|
||||
fileIndex map[string]int
|
||||
|
||||
// TODO
|
||||
ResultBellow bool `json:"result_bellow"`
|
||||
HideConfig bool `json:"hide_config"`
|
||||
}
|
||||
|
||||
func New() *ConfigUI {
|
||||
return &ConfigUI{
|
||||
fileIndex: map[string]int{},
|
||||
}
|
||||
}
|
||||
|
||||
func (cui *ConfigUI) Get(file_name string) (*File, error) {
|
||||
if file_name == APP_NAME {
|
||||
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (cui *ConfigUI) LoadConfig(confstr string) {
|
||||
|
||||
// construct fileIndex
|
||||
}
|
||||
func (cui *ConfigUI) GetConfig() []byte {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package configui
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type File struct {
|
||||
Path string `json:"path"`
|
||||
Name string `json:"name"`
|
||||
Action string `json:"action"`
|
||||
RO bool `json:"ro"`
|
||||
Lang string `json:"lang"`
|
||||
|
||||
// used for parsing post data
|
||||
Data string `json:"data"`
|
||||
|
||||
lock sync.RWMutex `json:"-"`
|
||||
}
|
||||
|
||||
func (f *File) Read() ([]byte, error) {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
data, err := os.ReadFile(f.Path)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (f *File) Write(data []byte) error {
|
||||
if f.RO {
|
||||
return errors.New("this file has readonly set")
|
||||
}
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
info, err := os.Stat(f.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(f.Path, data, info.Mode())
|
||||
}
|
||||
|
||||
func (f *File) Do() (string, error) {
|
||||
if f.Action == "" {
|
||||
return "", nil
|
||||
}
|
||||
timeout := time.After(2 * time.Second)
|
||||
cmd := &exec.Cmd{}
|
||||
if runtime.GOOS == "windows" {
|
||||
cmd = exec.Command(WIN_SHELL, "/c", f.Action)
|
||||
} else {
|
||||
exec.Command(UNIX_SHELL, "-c", f.Action)
|
||||
}
|
||||
var out []byte
|
||||
done := make(chan struct{}, 1)
|
||||
go func() {
|
||||
out, _ = cmd.CombinedOutput()
|
||||
// real cmd err is only pass down
|
||||
// if err != nil {
|
||||
// return string(out), err
|
||||
// }
|
||||
done <- struct{}{}
|
||||
}()
|
||||
select {
|
||||
case <-timeout:
|
||||
cmd.Process.Kill()
|
||||
return "", errors.New("command timeout")
|
||||
case <-done:
|
||||
return string(out), nil
|
||||
}
|
||||
}
|
||||
|
||||
func ReadConfig(confstr string) ([]File, error) {
|
||||
conf := []File{}
|
||||
err := json.Unmarshal([]byte(confstr), &conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range conf {
|
||||
if conf[i].Name == "" {
|
||||
conf[i].Name = conf[i].Path
|
||||
}
|
||||
}
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func GetFileMap(files []File) map[string]*File {
|
||||
fileMap := map[string]*File{}
|
||||
for i := range files {
|
||||
fileMap[files[i].Name] = &files[i]
|
||||
}
|
||||
return fileMap
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package configui
|
|
@ -11,16 +11,6 @@
|
|||
</span></span>
|
||||
</button>
|
||||
</p>
|
||||
<p class="control">
|
||||
<div class="select is-small">
|
||||
<select onchange="SetTabstop(this)">
|
||||
<option value="0">Tabstop</option>
|
||||
<option value="4">Tabstop 4</option>
|
||||
<option value="2">Tabstop 2</option>
|
||||
<option value="-1">Tab</option>
|
||||
</select>
|
||||
</div>
|
||||
</p>
|
||||
<p class="control">
|
||||
<button class="button is-small has-tooltip-arrow" id="toolFollow"
|
||||
data-tooltip="Follow file change (1s)" onclick="toolFollow()">
|
||||
|
@ -47,6 +37,16 @@
|
|||
</span></span>
|
||||
</button>
|
||||
</p>
|
||||
<p class="control">
|
||||
<div class="select is-small">
|
||||
<select onchange="SetTabstop(this)">
|
||||
<option value="0">Tabstop</option>
|
||||
<option value="4">Tabstop 4</option>
|
||||
<option value="2">Tabstop 2</option>
|
||||
<option value="-1">Tab</option>
|
||||
</select>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
119
util.go
119
util.go
|
@ -1,118 +1 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func matchIPGlob(ip, pattern string) bool {
|
||||
parts := strings.Split(pattern, ".")
|
||||
seg := strings.Split(ip, ".")
|
||||
for i, part := range parts {
|
||||
|
||||
// normalize pattern to 3 digits
|
||||
switch len(part) {
|
||||
case 1:
|
||||
if part == "*" {
|
||||
part = "***"
|
||||
} else {
|
||||
part = "00" + part
|
||||
}
|
||||
case 2:
|
||||
if strings.HasPrefix(part, "*") {
|
||||
part = "*" + part
|
||||
} else if strings.HasSuffix(part, "*") {
|
||||
part = part + "*"
|
||||
} else {
|
||||
part = "0" + part
|
||||
}
|
||||
}
|
||||
|
||||
// normalize ip to 3 digits
|
||||
switch len(seg[i]) {
|
||||
case 1:
|
||||
seg[i] = "00" + seg[i]
|
||||
case 2:
|
||||
seg[i] = "0" + seg[i]
|
||||
}
|
||||
|
||||
for j := range part {
|
||||
if string(part[j]) == "*" {
|
||||
continue
|
||||
}
|
||||
if part[j] != seg[i][j] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func bundle(buf io.Writer, paths []string, flat bool) error {
|
||||
gw := gzip.NewWriter(buf)
|
||||
defer gw.Close()
|
||||
tw := tar.NewWriter(gw)
|
||||
defer tw.Close()
|
||||
|
||||
for _, file := range paths {
|
||||
if err := func(file string) error {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
info, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
header, err := tar.FileInfoHeader(info, info.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Use full path as name (FileInfoHeader only takes the basename)
|
||||
// If we don't do this the directory strucuture would
|
||||
// not be preserved
|
||||
// https://golang.org/src/archive/tar/common.go?#L626
|
||||
if !flat {
|
||||
if !strings.HasPrefix(file, "/") {
|
||||
file = "configui/" + file
|
||||
} else {
|
||||
file = "configui" + file
|
||||
}
|
||||
header.Name = file
|
||||
}
|
||||
|
||||
// Write file header to the tar archive
|
||||
err = tw.WriteHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Copy file content to tar archive
|
||||
_, err = io.Copy(tw, f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
}(file); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var ext2mode map[string]string = map[string]string{
|
||||
"go": "golang",
|
||||
"log": "sh",
|
||||
"txt": "text",
|
||||
"yml": "yaml",
|
||||
"conf": "ini",
|
||||
}
|
||||
package configui
|
||||
|
|
Loading…
Reference in New Issue