refact: use klog and ksrv
continuous-integration/drone/tag Build is passing
Details
continuous-integration/drone/tag Build is passing
Details
parent
cff4c13b78
commit
239be22094
|
@ -3,11 +3,11 @@ package main
|
|||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
log "kumoly.io/lib/klog"
|
||||
|
||||
"kumoly.io/tools/configui"
|
||||
)
|
||||
|
||||
|
@ -55,17 +55,6 @@ func main() {
|
|||
|
||||
cui := configui.New()
|
||||
|
||||
// setup logging
|
||||
if flagLogFile != "" {
|
||||
f, err := os.OpenFile(flagLogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
log.Fatalf("Error opening file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
mwriter := io.MultiWriter(f, os.Stderr)
|
||||
log.SetOutput(mwriter)
|
||||
}
|
||||
|
||||
// setup values
|
||||
if flagPath != "" {
|
||||
if flagName == "" {
|
||||
|
@ -77,18 +66,21 @@ func main() {
|
|||
Action: flagAction,
|
||||
}
|
||||
if err := cui.AppendFile(file); err != nil {
|
||||
log.Fatalln(err)
|
||||
log.Error(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else if flagConfigPath == "" {
|
||||
log.Println("no config specified")
|
||||
log.Error("no config specified")
|
||||
} else {
|
||||
conf, err := os.ReadFile(flagConfigPath)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
log.Error(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
cui.LoadConfig(string(conf))
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
log.Error(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,6 +106,10 @@ func main() {
|
|||
}
|
||||
|
||||
// start server
|
||||
log.Println("Listening on", flagBind)
|
||||
log.Fatal(server.ListenAndServe())
|
||||
log.Info("Listening on ", flagBind)
|
||||
err := server.ListenAndServe()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
|
71
configui.go
71
configui.go
|
@ -1,13 +1,17 @@
|
|||
package configui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"kumoly.io/lib/klog"
|
||||
"kumoly.io/tools/configui/public"
|
||||
)
|
||||
|
||||
|
@ -30,6 +34,7 @@ var Ext2Mode map[string]string = map[string]string{
|
|||
|
||||
type ConfigUI struct {
|
||||
AppName string `json:"app_name"`
|
||||
Prod bool `json:"production"`
|
||||
BaseUrl string `json:"base_url"`
|
||||
ConfigPath string `json:"config_path"`
|
||||
|
||||
|
@ -46,11 +51,14 @@ type ConfigUI struct {
|
|||
|
||||
// Should be in main app
|
||||
LogPath string `json:"log_path"`
|
||||
SilentSysOut bool `json:"silent_sys_out"`
|
||||
LogLevel int `json:"log_level"`
|
||||
|
||||
TmplFS embed.FS `json:"-"`
|
||||
tmpl *template.Template
|
||||
PublicFS embed.FS `json:"-"`
|
||||
log *klog.Logger `json:"-"`
|
||||
ksrv_log *klog.Logger `json:"-"`
|
||||
f *os.File `json:"-"`
|
||||
}
|
||||
|
||||
func New() *ConfigUI {
|
||||
|
@ -65,6 +73,7 @@ func New() *ConfigUI {
|
|||
}).ParseFS(tmplFS, "templates/*.tmpl", "templates/**/*.tmpl"))
|
||||
return &ConfigUI{
|
||||
fileIndex: map[string]int{},
|
||||
Prod: true,
|
||||
AppName: "ConfigUI",
|
||||
BaseUrl: "/",
|
||||
PublicFS: public.FS,
|
||||
|
@ -72,6 +81,8 @@ func New() *ConfigUI {
|
|||
tmpl: tmpl,
|
||||
CmdTimeout: "10s",
|
||||
cmdTimeout: time.Second * 10,
|
||||
LogLevel: klog.Lerror | klog.Linfo,
|
||||
log: klog.Sub("ConfigUI"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,6 +92,7 @@ func (cui *ConfigUI) File(file_name string) (*File, error) {
|
|||
Path: cui.ConfigPath,
|
||||
Name: cui.AppName,
|
||||
Lang: "json",
|
||||
owner: cui,
|
||||
}, nil
|
||||
}
|
||||
index, ok := cui.fileIndex[file_name]
|
||||
|
@ -103,6 +115,7 @@ func (cui *ConfigUI) LoadConfig(confstr string) error {
|
|||
if f.Name == "" {
|
||||
f.Name = f.Path
|
||||
}
|
||||
f.owner = cui
|
||||
tmpIndex[f.Name] = i
|
||||
}
|
||||
|
||||
|
@ -110,15 +123,21 @@ func (cui *ConfigUI) LoadConfig(confstr string) error {
|
|||
cui.fileIndex = tmpIndex
|
||||
cui.Files = tmpConf.Files
|
||||
cui.AllowIP = tmpConf.AllowIP
|
||||
cui.Prod = tmpConf.Prod
|
||||
cui.ConfigPath = tmpConf.ConfigPath
|
||||
cui.HideConfig = tmpConf.HideConfig
|
||||
cui.LogPath = tmpConf.LogPath
|
||||
cui.NoReconfig = tmpConf.NoReconfig
|
||||
|
||||
cui.AppName = tmpConf.AppName
|
||||
if cui.AppName == "" {
|
||||
cui.AppName = "ConfigUI"
|
||||
}
|
||||
|
||||
cui.BaseUrl = tmpConf.BaseUrl
|
||||
if cui.BaseUrl == "" {
|
||||
cui.BaseUrl = "/"
|
||||
}
|
||||
|
||||
ct, err := time.ParseDuration(tmpConf.CmdTimeout)
|
||||
if err != nil || cui.CmdTimeout == "" {
|
||||
cui.CmdTimeout = "10s"
|
||||
|
@ -128,13 +147,48 @@ func (cui *ConfigUI) LoadConfig(confstr string) error {
|
|||
cui.cmdTimeout = ct
|
||||
}
|
||||
|
||||
cui.log = klog.Sub(cui.AppName)
|
||||
|
||||
// NOT implemented
|
||||
cui.ResultBellow = tmpConf.ResultBellow
|
||||
cui.SilentSysOut = tmpConf.SilentSysOut
|
||||
|
||||
cui.LogLevel = tmpConf.LogLevel
|
||||
cui.LogPath = tmpConf.LogPath
|
||||
cui.setLog()
|
||||
// fmt.Printf("%+v", cui)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cui *ConfigUI) setLog() {
|
||||
var err error
|
||||
if cui.f != nil {
|
||||
cui.f.Close()
|
||||
}
|
||||
if cui.LogPath != "" {
|
||||
cui.f, err = os.OpenFile(cui.LogPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
cui.log.Error(err)
|
||||
}
|
||||
cui.log.SetErrOutput(cui.f)
|
||||
cui.log.SetOutput(cui.f)
|
||||
cui.log.Reload()
|
||||
if cui.ksrv_log != nil {
|
||||
cui.ksrv_log.SetErrOutput(cui.f)
|
||||
cui.ksrv_log.SetOutput(cui.f)
|
||||
cui.ksrv_log.Reload()
|
||||
}
|
||||
} else {
|
||||
cui.log.SetErrOutput(os.Stderr)
|
||||
cui.log.SetOutput(os.Stderr)
|
||||
cui.log.Reload()
|
||||
if cui.ksrv_log != nil {
|
||||
cui.ksrv_log.SetErrOutput(os.Stderr)
|
||||
cui.ksrv_log.SetOutput(os.Stderr)
|
||||
cui.ksrv_log.Reload()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cui *ConfigUI) Config() ([]byte, error) {
|
||||
return json.MarshalIndent(cui, "", " ")
|
||||
}
|
||||
|
@ -143,6 +197,7 @@ func (cui *ConfigUI) AppendFile(file *File) error {
|
|||
if file.Name == "" {
|
||||
file.Name = file.Path
|
||||
}
|
||||
file.owner = cui
|
||||
i, ok := cui.fileIndex[file.Name]
|
||||
if ok {
|
||||
return fmt.Errorf("%v already exists at %d", file.Name, i)
|
||||
|
@ -151,3 +206,13 @@ func (cui *ConfigUI) AppendFile(file *File) error {
|
|||
cui.Files = append(cui.Files, file)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cui *ConfigUI) Parse(w http.ResponseWriter, name string, data interface{}) error {
|
||||
buf := &bytes.Buffer{}
|
||||
err := cui.tmpl.ExecuteTemplate(buf, "home", data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = w.Write(buf.Bytes())
|
||||
return err
|
||||
}
|
||||
|
|
33
file.go
33
file.go
|
@ -1,9 +1,7 @@
|
|||
package configui
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
|
@ -26,6 +24,7 @@ type File struct {
|
|||
Data string `json:"data"`
|
||||
|
||||
lock sync.RWMutex `json:"-"`
|
||||
owner *ConfigUI `json:"-"`
|
||||
}
|
||||
|
||||
func (f *File) Read() ([]byte, error) {
|
||||
|
@ -33,7 +32,7 @@ func (f *File) Read() ([]byte, error) {
|
|||
defer f.lock.RUnlock()
|
||||
data, err := os.ReadFile(f.Path)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
f.owner.log.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
|
@ -64,7 +63,7 @@ func (f *File) Do(CmdTimeout time.Duration) (string, error) {
|
|||
} else {
|
||||
cmd = exec.Command(UNIX_SHELL, "-c", f.Action)
|
||||
}
|
||||
log.Println("DO: ", f.Action)
|
||||
f.owner.log.Info("DO: ", f.Action)
|
||||
done := make(chan string, 1)
|
||||
go func() {
|
||||
out, _ := cmd.CombinedOutput()
|
||||
|
@ -77,32 +76,10 @@ func (f *File) Do(CmdTimeout time.Duration) (string, error) {
|
|||
select {
|
||||
case <-time.After(CmdTimeout):
|
||||
cmd.Process.Kill()
|
||||
log.Println("timeout")
|
||||
f.owner.log.Error("timeout")
|
||||
return "", errors.New("command timeout")
|
||||
case out := <-done:
|
||||
log.Printf("\n%v", out)
|
||||
f.owner.log.Info("\n", out)
|
||||
return 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
|
||||
}
|
||||
|
|
10
go.mod
10
go.mod
|
@ -1,3 +1,13 @@
|
|||
module kumoly.io/tools/configui
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
kumoly.io/lib/klog v0.1.9
|
||||
kumoly.io/lib/ksrv v0.1.4
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
|
||||
)
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
kumoly.io/lib/klog v0.1.9 h1:rS9PPqfyBIIfeQlPSuMv+7StGPiFVuAdp04HDwwDY3E=
|
||||
kumoly.io/lib/klog v0.1.9/go.mod h1:Snm+c1xRrh/RbXsxQf7UGYbAJGPcIa6bEEN+CmzJh7M=
|
||||
kumoly.io/lib/ksrv v0.1.4 h1:8zbslRwdNWHw5Wm1PiyDLr6Mu4xxjz0FTW4u8dZ6ZeI=
|
||||
kumoly.io/lib/ksrv v0.1.4/go.mod h1:eKfhJR5mOqlQZAy5EUI+Avfxirx2/nNW79r+Ki2C18k=
|
12
handler.go
12
handler.go
|
@ -8,6 +8,8 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"kumoly.io/lib/ksrv"
|
||||
)
|
||||
|
||||
func (cui *ConfigUI) ListFiles(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -22,7 +24,7 @@ func (cui *ConfigUI) GetFile(w http.ResponseWriter, r *http.Request) {
|
|||
name := r.URL.Query().Get("name")
|
||||
file, err := cui.File(name)
|
||||
if err != nil {
|
||||
response(w, 404, []byte("file not found"))
|
||||
ksrv.Response(w, 404, []byte("file not found"))
|
||||
return
|
||||
}
|
||||
data, err := file.Read()
|
||||
|
@ -55,7 +57,7 @@ func (cui *ConfigUI) GetDelta(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
file, err := cui.File(name)
|
||||
if err != nil {
|
||||
response(w, 404, []byte("file not found"))
|
||||
ksrv.Response(w, 404, []byte("file not found"))
|
||||
return
|
||||
}
|
||||
f, err := os.Open(file.Path)
|
||||
|
@ -106,7 +108,7 @@ func (cui *ConfigUI) PostFile(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
file, err := cui.File(f.Name)
|
||||
if err != nil {
|
||||
response(w, 404, []byte("file not found"))
|
||||
ksrv.Response(w, 404, []byte("file not found"))
|
||||
return
|
||||
}
|
||||
if err := file.Write([]byte(f.Data)); err != nil {
|
||||
|
@ -119,7 +121,7 @@ func (cui *ConfigUI) Apply(w http.ResponseWriter, r *http.Request) {
|
|||
name := r.URL.Query().Get("name")
|
||||
file, err := cui.File(name)
|
||||
if err != nil {
|
||||
response(w, 404, []byte("file not found"))
|
||||
ksrv.Response(w, 404, []byte("file not found"))
|
||||
return
|
||||
}
|
||||
result, err := file.Do(cui.cmdTimeout)
|
||||
|
@ -145,7 +147,7 @@ func (cui *ConfigUI) Download(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
file, err := cui.File(name)
|
||||
if err != nil {
|
||||
response(w, 404, []byte("file not found"))
|
||||
ksrv.Response(w, 404, []byte("file not found"))
|
||||
return
|
||||
}
|
||||
data, err := file.Read()
|
||||
|
|
69
netutil.go
69
netutil.go
|
@ -1,69 +0,0 @@
|
|||
package configui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type CuiResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
StatueCode int
|
||||
}
|
||||
|
||||
func (w *CuiResponseWriter) WriteHeader(statusCode int) {
|
||||
if w.StatueCode != 0 {
|
||||
return
|
||||
}
|
||||
w.StatueCode = statusCode
|
||||
w.ResponseWriter.WriteHeader(statusCode)
|
||||
}
|
||||
|
||||
func (w *CuiResponseWriter) Write(body []byte) (int, error) {
|
||||
if w.StatueCode == 0 {
|
||||
w.WriteHeader(200)
|
||||
}
|
||||
return w.ResponseWriter.Write(body)
|
||||
}
|
||||
|
||||
func response(w http.ResponseWriter, status int, body []byte) (int, error) {
|
||||
w.WriteHeader(status)
|
||||
return w.Write(body)
|
||||
}
|
||||
|
||||
func abort(w http.ResponseWriter, err interface{}) (int, error) {
|
||||
log.Println(err)
|
||||
switch v := err.(type) {
|
||||
case int:
|
||||
w.WriteHeader(v)
|
||||
return w.Write([]byte(strconv.Itoa(v)))
|
||||
case string:
|
||||
w.WriteHeader(500)
|
||||
return w.Write([]byte(v))
|
||||
case error:
|
||||
w.WriteHeader(500)
|
||||
return w.Write([]byte(v.Error()))
|
||||
default:
|
||||
w.WriteHeader(500)
|
||||
return w.Write([]byte(strconv.Itoa(500)))
|
||||
}
|
||||
}
|
||||
|
||||
func catch(rw *CuiResponseWriter, r *http.Request) {
|
||||
ex := recover()
|
||||
if ex != nil {
|
||||
abort(rw, ex)
|
||||
log.Printf("%s %s %d %s %s\n", GetIP(r), r.Method, rw.StatueCode, r.URL, r.Header.Get("User-Agent"))
|
||||
}
|
||||
}
|
||||
|
||||
func (cui *ConfigUI) Parse(w http.ResponseWriter, name string, data interface{}) error {
|
||||
buf := &bytes.Buffer{}
|
||||
err := cui.tmpl.ExecuteTemplate(buf, "home", data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = w.Write(buf.Bytes())
|
||||
return err
|
||||
}
|
30
server.go
30
server.go
|
@ -1,9 +1,10 @@
|
|||
package configui
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"kumoly.io/lib/ksrv"
|
||||
)
|
||||
|
||||
func (cui *ConfigUI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -11,25 +12,28 @@ func (cui *ConfigUI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (cui *ConfigUI) middleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
// log.Println(r.URL)
|
||||
rw := &CuiResponseWriter{w, 0}
|
||||
defer catch(rw, r)
|
||||
ip := GetIP(r)
|
||||
k := ksrv.New()
|
||||
cui.ksrv_log = k.GetLogger()
|
||||
cui.setLog()
|
||||
k.SetNoLogCondition(func(r *http.Request) bool {
|
||||
if strings.HasPrefix(r.URL.Path, "/public") || r.URL.Query().Get("f") == "true" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
return k.Middleware(
|
||||
http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
if cui.AllowIP != "" {
|
||||
if !matchIPGlob(ip, cui.AllowIP) {
|
||||
ip := ksrv.GetIP(r)
|
||||
if !ksrv.MatchIPGlob(ip, cui.AllowIP) {
|
||||
rw.WriteHeader(403)
|
||||
panic("permission denied")
|
||||
}
|
||||
}
|
||||
next.ServeHTTP(rw, r)
|
||||
//logging
|
||||
if !strings.HasPrefix(r.URL.Path, "/public") && r.URL.Query().Get("f") != "true" {
|
||||
log.Printf("%s %s %d %s %s\n", ip, r.Method, rw.StatueCode, r.URL, r.Header.Get("User-Agent"))
|
||||
}
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
func (cui *ConfigUI) mux() *http.ServeMux {
|
||||
|
|
62
util.go
62
util.go
|
@ -4,55 +4,10 @@ import (
|
|||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"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, rootDir string, flat bool) error {
|
||||
gw := gzip.NewWriter(buf)
|
||||
defer gw.Close()
|
||||
|
@ -110,20 +65,3 @@ func bundle(buf io.Writer, paths []string, rootDir string, flat bool) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetIP(r *http.Request) string {
|
||||
ip := r.Header.Get("X-Real-Ip")
|
||||
if ip == "" {
|
||||
ips := r.Header.Get("X-Forwarded-For")
|
||||
ipArr := strings.Split(ips, ",")
|
||||
ip = strings.Trim(ipArr[len(ipArr)-1], " ")
|
||||
}
|
||||
if ip == "" {
|
||||
var err error
|
||||
ip, _, err = net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
ip = r.RemoteAddr
|
||||
}
|
||||
}
|
||||
return ip
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue