diff --git a/api/api.go b/api/api.go index c92ef40..6e01717 100644 --- a/api/api.go +++ b/api/api.go @@ -169,6 +169,10 @@ func registerConfig(config *gin.RouterGroup) { config.PUT("/observers/:observer", updateObserver) config.DELETE("/observers/:observer", deleteObserver) + config.POST("/recorders", createRecorder) + config.PUT("/recorders/:recorder", updateRecorder) + config.DELETE("/recorders/:recorder", deleteRecorder) + config.POST("/sds", createSD) config.PUT("/sds/:sd", updateSD) config.DELETE("/sds/:sd", deleteSD) diff --git a/api/config.go b/api/config.go index 3cb09ea..0bfab47 100644 --- a/api/config.go +++ b/api/config.go @@ -144,7 +144,7 @@ func saveConfig(ctx *gin.Context) { if err != nil { writeError(ctx, &Error{ statusCode: http.StatusInternalServerError, - Code: 40005, + Code: ErrCodeSaveConfigFailed, Msg: fmt.Sprintf("create file: %s", err.Error()), }) return @@ -154,8 +154,8 @@ func saveConfig(ctx *gin.Context) { if err := config.Global().Write(f, req.Format); err != nil { writeError(ctx, &Error{ statusCode: http.StatusInternalServerError, - Code: 40006, - Msg: fmt.Sprintf("write: %s", err.Error()), + Code: ErrCodeSaveConfigFailed, + Msg: fmt.Sprintf("save config: %s", err.Error()), }) return } diff --git a/api/config_admission.go b/api/config_admission.go index 0be8c81..2c9caa9 100644 --- a/api/config_admission.go +++ b/api/config_admission.go @@ -1,7 +1,9 @@ package api import ( + "fmt" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/go-gost/x/config" @@ -35,15 +37,21 @@ func createAdmission(ctx *gin.Context) { var req createAdmissionRequest ctx.ShouldBindJSON(&req.Data) - if req.Data.Name == "" { - writeError(ctx, ErrInvalid) + name := strings.TrimSpace(req.Data.Name) + if name == "" { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "admission name is required")) + return + } + + if registry.AdmissionRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("admission %s already exists", name))) return } v := parser.ParseAdmission(&req.Data) - if err := registry.AdmissionRegistry().Register(req.Data.Name, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.AdmissionRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("admission %s already exists", name))) return } @@ -87,25 +95,27 @@ func updateAdmission(ctx *gin.Context) { ctx.ShouldBindUri(&req) ctx.ShouldBindJSON(&req.Data) - if !registry.AdmissionRegistry().IsRegistered(req.Admission) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Admission) + + if !registry.AdmissionRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("admission %s not found", name))) return } - req.Data.Name = req.Admission + req.Data.Name = name v := parser.ParseAdmission(&req.Data) - registry.AdmissionRegistry().Unregister(req.Admission) + registry.AdmissionRegistry().Unregister(name) - if err := registry.AdmissionRegistry().Register(req.Admission, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.AdmissionRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("admission %s already exists", name))) return } config.OnUpdate(func(c *config.Config) error { for i := range c.Admissions { - if c.Admissions[i].Name == req.Admission { + if c.Admissions[i].Name == name { c.Admissions[i] = &req.Data break } @@ -145,17 +155,19 @@ func deleteAdmission(ctx *gin.Context) { var req deleteAdmissionRequest ctx.ShouldBindUri(&req) - if !registry.AdmissionRegistry().IsRegistered(req.Admission) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Admission) + + if !registry.AdmissionRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("admission %s not found", name))) return } - registry.AdmissionRegistry().Unregister(req.Admission) + registry.AdmissionRegistry().Unregister(name) config.OnUpdate(func(c *config.Config) error { admissiones := c.Admissions c.Admissions = nil for _, s := range admissiones { - if s.Name == req.Admission { + if s.Name == name { continue } c.Admissions = append(c.Admissions, s) diff --git a/api/config_auther.go b/api/config_auther.go index 68264c4..41ba010 100644 --- a/api/config_auther.go +++ b/api/config_auther.go @@ -1,7 +1,9 @@ package api import ( + "fmt" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/go-gost/x/config" @@ -35,14 +37,20 @@ func createAuther(ctx *gin.Context) { var req createAutherRequest ctx.ShouldBindJSON(&req.Data) - if req.Data.Name == "" { - writeError(ctx, ErrInvalid) + name := strings.TrimSpace(req.Data.Name) + if name == "" { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "auther name is required")) + return + } + + if registry.AutherRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("auther %s already exists", name))) return } v := parser.ParseAuther(&req.Data) - if err := registry.AutherRegistry().Register(req.Data.Name, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.AutherRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("auther %s already exists", name))) return } @@ -86,24 +94,26 @@ func updateAuther(ctx *gin.Context) { ctx.ShouldBindUri(&req) ctx.ShouldBindJSON(&req.Data) - if !registry.AutherRegistry().IsRegistered(req.Auther) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Auther) + + if !registry.AutherRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("auther %s not found", name))) return } - req.Data.Name = req.Auther + req.Data.Name = name v := parser.ParseAuther(&req.Data) - registry.AutherRegistry().Unregister(req.Auther) + registry.AutherRegistry().Unregister(name) - if err := registry.AutherRegistry().Register(req.Auther, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.AutherRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("auther %s already exists", name))) return } config.OnUpdate(func(c *config.Config) error { for i := range c.Authers { - if c.Authers[i].Name == req.Auther { + if c.Authers[i].Name == name { c.Authers[i] = &req.Data break } @@ -143,17 +153,19 @@ func deleteAuther(ctx *gin.Context) { var req deleteAutherRequest ctx.ShouldBindUri(&req) - if !registry.AutherRegistry().IsRegistered(req.Auther) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Auther) + + if !registry.AutherRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("auther %s not found", name))) return } - registry.AutherRegistry().Unregister(req.Auther) + registry.AutherRegistry().Unregister(name) config.OnUpdate(func(c *config.Config) error { authers := c.Authers c.Authers = nil for _, s := range authers { - if s.Name == req.Auther { + if s.Name == name { continue } c.Authers = append(c.Authers, s) diff --git a/api/config_bypass.go b/api/config_bypass.go index 03aabbc..ae50c73 100644 --- a/api/config_bypass.go +++ b/api/config_bypass.go @@ -1,7 +1,9 @@ package api import ( + "fmt" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/go-gost/x/config" @@ -35,15 +37,21 @@ func createBypass(ctx *gin.Context) { var req createBypassRequest ctx.ShouldBindJSON(&req.Data) - if req.Data.Name == "" { - writeError(ctx, ErrInvalid) + name := strings.TrimSpace(req.Data.Name) + if name == "" { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "bypass name is required")) + return + } + + if registry.BypassRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("bypass %s already exists", name))) return } v := parser.ParseBypass(&req.Data) - if err := registry.BypassRegistry().Register(req.Data.Name, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.BypassRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("bypass %s already exists", name))) return } @@ -87,25 +95,27 @@ func updateBypass(ctx *gin.Context) { ctx.ShouldBindUri(&req) ctx.ShouldBindJSON(&req.Data) - if !registry.BypassRegistry().IsRegistered(req.Bypass) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Bypass) + + if !registry.BypassRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("bypass %s not found", name))) return } - req.Data.Name = req.Bypass + req.Data.Name = name v := parser.ParseBypass(&req.Data) - registry.BypassRegistry().Unregister(req.Bypass) + registry.BypassRegistry().Unregister(name) - if err := registry.BypassRegistry().Register(req.Bypass, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.BypassRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("bypass %s already exists", name))) return } config.OnUpdate(func(c *config.Config) error { for i := range c.Bypasses { - if c.Bypasses[i].Name == req.Bypass { + if c.Bypasses[i].Name == name { c.Bypasses[i] = &req.Data break } @@ -145,17 +155,19 @@ func deleteBypass(ctx *gin.Context) { var req deleteBypassRequest ctx.ShouldBindUri(&req) - if !registry.BypassRegistry().IsRegistered(req.Bypass) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Bypass) + + if !registry.BypassRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("bypass %s not found", name))) return } - registry.BypassRegistry().Unregister(req.Bypass) + registry.BypassRegistry().Unregister(name) config.OnUpdate(func(c *config.Config) error { bypasses := c.Bypasses c.Bypasses = nil for _, s := range bypasses { - if s.Name == req.Bypass { + if s.Name == name { continue } c.Bypasses = append(c.Bypasses, s) diff --git a/api/config_chain.go b/api/config_chain.go index d5eb33a..92ff66c 100644 --- a/api/config_chain.go +++ b/api/config_chain.go @@ -1,7 +1,9 @@ package api import ( + "fmt" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/go-gost/core/logger" @@ -36,19 +38,25 @@ func createChain(ctx *gin.Context) { var req createChainRequest ctx.ShouldBindJSON(&req.Data) - if req.Data.Name == "" { - writeError(ctx, ErrInvalid) + name := strings.TrimSpace(req.Data.Name) + if name == "" { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "chain name is required")) + return + } + + if registry.ChainRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("chain %s already exists", name))) return } v, err := parser.ParseChain(&req.Data, logger.Default()) if err != nil { - writeError(ctx, ErrCreate) + writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create chain %s failed: %s", name, err.Error()))) return } - if err := registry.ChainRegistry().Register(req.Data.Name, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.ChainRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("chain %s already exists", name))) return } @@ -93,29 +101,31 @@ func updateChain(ctx *gin.Context) { ctx.ShouldBindUri(&req) ctx.ShouldBindJSON(&req.Data) - if !registry.ChainRegistry().IsRegistered(req.Chain) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Chain) + + if !registry.ChainRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("chain %s not found", name))) return } - req.Data.Name = req.Chain + req.Data.Name = name v, err := parser.ParseChain(&req.Data, logger.Default()) if err != nil { - writeError(ctx, ErrCreate) + writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create chain %s failed: %s", name, err.Error()))) return } - registry.ChainRegistry().Unregister(req.Chain) + registry.ChainRegistry().Unregister(name) - if err := registry.ChainRegistry().Register(req.Chain, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.ChainRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("chain %s already exists", name))) return } config.OnUpdate(func(c *config.Config) error { for i := range c.Chains { - if c.Chains[i].Name == req.Chain { + if c.Chains[i].Name == name { c.Chains[i] = &req.Data break } @@ -155,17 +165,19 @@ func deleteChain(ctx *gin.Context) { var req deleteChainRequest ctx.ShouldBindUri(&req) - if !registry.ChainRegistry().IsRegistered(req.Chain) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Chain) + + if !registry.ChainRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("chain %s not found", name))) return } - registry.ChainRegistry().Unregister(req.Chain) + registry.ChainRegistry().Unregister(name) config.OnUpdate(func(c *config.Config) error { chains := c.Chains c.Chains = nil for _, s := range chains { - if s.Name == req.Chain { + if s.Name == name { continue } c.Chains = append(c.Chains, s) diff --git a/api/config_conn_limiter.go b/api/config_conn_limiter.go index 62e916a..863737b 100644 --- a/api/config_conn_limiter.go +++ b/api/config_conn_limiter.go @@ -1,7 +1,9 @@ package api import ( + "fmt" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/go-gost/x/config" @@ -35,15 +37,20 @@ func createConnLimiter(ctx *gin.Context) { var req createConnLimiterRequest ctx.ShouldBindJSON(&req.Data) - if req.Data.Name == "" { - writeError(ctx, ErrInvalid) + name := strings.TrimSpace(req.Data.Name) + if name == "" { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "limiter name is required")) + return + } + + if registry.ConnLimiterRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name))) return } v := parser.ParseConnLimiter(&req.Data) - - if err := registry.ConnLimiterRegistry().Register(req.Data.Name, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.ConnLimiterRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create limmiter %s failed: %s", name, err.Error()))) return } @@ -87,25 +94,27 @@ func updateConnLimiter(ctx *gin.Context) { ctx.ShouldBindUri(&req) ctx.ShouldBindJSON(&req.Data) - if !registry.ConnLimiterRegistry().IsRegistered(req.Limiter) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Limiter) + + if !registry.ConnLimiterRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name))) return } - req.Data.Name = req.Limiter + req.Data.Name = name v := parser.ParseConnLimiter(&req.Data) - registry.ConnLimiterRegistry().Unregister(req.Limiter) + registry.ConnLimiterRegistry().Unregister(name) - if err := registry.ConnLimiterRegistry().Register(req.Limiter, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.ConnLimiterRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name))) return } config.OnUpdate(func(c *config.Config) error { for i := range c.CLimiters { - if c.CLimiters[i].Name == req.Limiter { + if c.CLimiters[i].Name == name { c.CLimiters[i] = &req.Data break } @@ -145,17 +154,19 @@ func deleteConnLimiter(ctx *gin.Context) { var req deleteConnLimiterRequest ctx.ShouldBindUri(&req) - if !registry.ConnLimiterRegistry().IsRegistered(req.Limiter) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Limiter) + + if !registry.ConnLimiterRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name))) return } - registry.ConnLimiterRegistry().Unregister(req.Limiter) + registry.ConnLimiterRegistry().Unregister(name) config.OnUpdate(func(c *config.Config) error { limiteres := c.CLimiters c.CLimiters = nil for _, s := range limiteres { - if s.Name == req.Limiter { + if s.Name == name { continue } c.CLimiters = append(c.CLimiters, s) diff --git a/api/config_hop.go b/api/config_hop.go index 01f45f7..c6ee69a 100644 --- a/api/config_hop.go +++ b/api/config_hop.go @@ -1,7 +1,9 @@ package api import ( + "fmt" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/go-gost/core/logger" @@ -36,19 +38,25 @@ func createHop(ctx *gin.Context) { var req createHopRequest ctx.ShouldBindJSON(&req.Data) - if req.Data.Name == "" { - writeError(ctx, ErrInvalid) + name := strings.TrimSpace(req.Data.Name) + if name == "" { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "hop name is required")) + return + } + + if registry.HopRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("hop %s already exists", name))) return } v, err := parser.ParseHop(&req.Data, logger.Default()) if err != nil { - writeError(ctx, ErrCreate) + writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create hop %s failed: %s", name, err.Error()))) return } - if err := registry.HopRegistry().Register(req.Data.Name, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.HopRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("hop %s already exists", name))) return } @@ -93,29 +101,30 @@ func updateHop(ctx *gin.Context) { ctx.ShouldBindUri(&req) ctx.ShouldBindJSON(&req.Data) - if !registry.HopRegistry().IsRegistered(req.Hop) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Hop) + if !registry.HopRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("hop %s not found", name))) return } - req.Data.Name = req.Hop + req.Data.Name = name v, err := parser.ParseHop(&req.Data, logger.Default()) if err != nil { - writeError(ctx, ErrCreate) + writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create hop %s failed: %s", name, err.Error()))) return } - registry.HopRegistry().Unregister(req.Hop) + registry.HopRegistry().Unregister(name) - if err := registry.HopRegistry().Register(req.Hop, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.HopRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("hop %s already exists", name))) return } config.OnUpdate(func(c *config.Config) error { for i := range c.Hops { - if c.Hops[i].Name == req.Hop { + if c.Hops[i].Name == name { c.Hops[i] = &req.Data break } @@ -155,17 +164,19 @@ func deleteHop(ctx *gin.Context) { var req deleteHopRequest ctx.ShouldBindUri(&req) - if !registry.HopRegistry().IsRegistered(req.Hop) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Hop) + + if !registry.HopRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("hop %s not found", name))) return } - registry.HopRegistry().Unregister(req.Hop) + registry.HopRegistry().Unregister(name) config.OnUpdate(func(c *config.Config) error { hops := c.Hops c.Hops = nil for _, s := range hops { - if s.Name == req.Hop { + if s.Name == name { continue } c.Hops = append(c.Hops, s) diff --git a/api/config_hosts.go b/api/config_hosts.go index 7d65dbb..085ea56 100644 --- a/api/config_hosts.go +++ b/api/config_hosts.go @@ -1,7 +1,9 @@ package api import ( + "fmt" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/go-gost/x/config" @@ -35,15 +37,21 @@ func createHosts(ctx *gin.Context) { var req createHostsRequest ctx.ShouldBindJSON(&req.Data) - if req.Data.Name == "" { - writeError(ctx, ErrInvalid) + name := strings.TrimSpace(req.Data.Name) + if name == "" { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "hosts name is required")) + return + } + + if registry.HostsRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("hosts %s already exists", name))) return } v := parser.ParseHostMapper(&req.Data) - if err := registry.HostsRegistry().Register(req.Data.Name, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.HostsRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("hosts %s already exists", name))) return } @@ -87,25 +95,27 @@ func updateHosts(ctx *gin.Context) { ctx.ShouldBindUri(&req) ctx.ShouldBindJSON(&req.Data) - if !registry.HostsRegistry().IsRegistered(req.Hosts) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Hosts) + + if !registry.HostsRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("hosts %s not found", name))) return } - req.Data.Name = req.Hosts + req.Data.Name = name v := parser.ParseHostMapper(&req.Data) - registry.HostsRegistry().Unregister(req.Hosts) + registry.HostsRegistry().Unregister(name) - if err := registry.HostsRegistry().Register(req.Hosts, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.HostsRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("hosts %s already exists", name))) return } config.OnUpdate(func(c *config.Config) error { for i := range c.Hosts { - if c.Hosts[i].Name == req.Hosts { + if c.Hosts[i].Name == name { c.Hosts[i] = &req.Data break } @@ -145,17 +155,19 @@ func deleteHosts(ctx *gin.Context) { var req deleteHostsRequest ctx.ShouldBindUri(&req) - if !registry.HostsRegistry().IsRegistered(req.Hosts) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Hosts) + + if !registry.HostsRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("hosts %s not found", name))) return } - registry.HostsRegistry().Unregister(req.Hosts) + registry.HostsRegistry().Unregister(name) config.OnUpdate(func(c *config.Config) error { hosts := c.Hosts c.Hosts = nil for _, s := range hosts { - if s.Name == req.Hosts { + if s.Name == name { continue } c.Hosts = append(c.Hosts, s) diff --git a/api/config_ingress.go b/api/config_ingress.go index 5258ed5..ec75692 100644 --- a/api/config_ingress.go +++ b/api/config_ingress.go @@ -1,7 +1,9 @@ package api import ( + "fmt" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/go-gost/x/config" @@ -35,15 +37,21 @@ func createIngress(ctx *gin.Context) { var req createIngressRequest ctx.ShouldBindJSON(&req.Data) - if req.Data.Name == "" { - writeError(ctx, ErrInvalid) + name := strings.TrimSpace(req.Data.Name) + if name == "" { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "ingress name is required")) + return + } + + if registry.IngressRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("ingress %s already exists", name))) return } v := parser.ParseIngress(&req.Data) - if err := registry.IngressRegistry().Register(req.Data.Name, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.IngressRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("ingress %s already exists", name))) return } @@ -87,25 +95,27 @@ func updateIngress(ctx *gin.Context) { ctx.ShouldBindUri(&req) ctx.ShouldBindJSON(&req.Data) - if !registry.IngressRegistry().IsRegistered(req.Ingress) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Ingress) + + if !registry.IngressRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("ingress %s not found", name))) return } - req.Data.Name = req.Ingress + req.Data.Name = name v := parser.ParseIngress(&req.Data) - registry.IngressRegistry().Unregister(req.Ingress) + registry.IngressRegistry().Unregister(name) - if err := registry.IngressRegistry().Register(req.Ingress, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.IngressRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("ingress %s already exists", name))) return } config.OnUpdate(func(c *config.Config) error { for i := range c.Ingresses { - if c.Ingresses[i].Name == req.Ingress { + if c.Ingresses[i].Name == name { c.Ingresses[i] = &req.Data break } @@ -145,17 +155,19 @@ func deleteIngress(ctx *gin.Context) { var req deleteIngressRequest ctx.ShouldBindUri(&req) - if !registry.IngressRegistry().IsRegistered(req.Ingress) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Ingress) + + if !registry.IngressRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("ingress %s not found", name))) return } - registry.IngressRegistry().Unregister(req.Ingress) + registry.IngressRegistry().Unregister(name) config.OnUpdate(func(c *config.Config) error { ingresses := c.Ingresses c.Ingresses = nil for _, s := range ingresses { - if s.Name == req.Ingress { + if s.Name == name { continue } c.Ingresses = append(c.Ingresses, s) diff --git a/api/config_limiter.go b/api/config_limiter.go index 73ecaa7..2b31f3d 100644 --- a/api/config_limiter.go +++ b/api/config_limiter.go @@ -1,7 +1,9 @@ package api import ( + "fmt" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/go-gost/x/config" @@ -35,15 +37,21 @@ func createLimiter(ctx *gin.Context) { var req createLimiterRequest ctx.ShouldBindJSON(&req.Data) - if req.Data.Name == "" { - writeError(ctx, ErrInvalid) + name := strings.TrimSpace(req.Data.Name) + if name == "" { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "limiter name is required")) + return + } + + if registry.TrafficLimiterRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name))) return } v := parser.ParseTrafficLimiter(&req.Data) - if err := registry.TrafficLimiterRegistry().Register(req.Data.Name, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.TrafficLimiterRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name))) return } @@ -87,25 +95,27 @@ func updateLimiter(ctx *gin.Context) { ctx.ShouldBindUri(&req) ctx.ShouldBindJSON(&req.Data) - if !registry.TrafficLimiterRegistry().IsRegistered(req.Limiter) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Limiter) + + if !registry.TrafficLimiterRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name))) return } - req.Data.Name = req.Limiter + req.Data.Name = name v := parser.ParseTrafficLimiter(&req.Data) - registry.TrafficLimiterRegistry().Unregister(req.Limiter) + registry.TrafficLimiterRegistry().Unregister(name) - if err := registry.TrafficLimiterRegistry().Register(req.Limiter, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.TrafficLimiterRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name))) return } config.OnUpdate(func(c *config.Config) error { for i := range c.Limiters { - if c.Limiters[i].Name == req.Limiter { + if c.Limiters[i].Name == name { c.Limiters[i] = &req.Data break } @@ -145,17 +155,19 @@ func deleteLimiter(ctx *gin.Context) { var req deleteLimiterRequest ctx.ShouldBindUri(&req) - if !registry.TrafficLimiterRegistry().IsRegistered(req.Limiter) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Limiter) + + if !registry.TrafficLimiterRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name))) return } - registry.TrafficLimiterRegistry().Unregister(req.Limiter) + registry.TrafficLimiterRegistry().Unregister(name) config.OnUpdate(func(c *config.Config) error { limiteres := c.Limiters c.Limiters = nil for _, s := range limiteres { - if s.Name == req.Limiter { + if s.Name == name { continue } c.Limiters = append(c.Limiters, s) diff --git a/api/config_observer.go b/api/config_observer.go index 42ddece..b2c9965 100644 --- a/api/config_observer.go +++ b/api/config_observer.go @@ -1,7 +1,9 @@ package api import ( + "fmt" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/go-gost/x/config" @@ -35,15 +37,21 @@ func createObserver(ctx *gin.Context) { var req createObserverRequest ctx.ShouldBindJSON(&req.Data) - if req.Data.Name == "" { - writeError(ctx, ErrInvalid) + name := strings.TrimSpace(req.Data.Name) + if name == "" { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "observer name is required")) + return + } + + if registry.ObserverRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("observer %s already exists", name))) return } v := parser.ParseObserver(&req.Data) - if err := registry.ObserverRegistry().Register(req.Data.Name, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.ObserverRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("observer %s already exists", name))) return } @@ -87,25 +95,27 @@ func updateObserver(ctx *gin.Context) { ctx.ShouldBindUri(&req) ctx.ShouldBindJSON(&req.Data) - if !registry.ObserverRegistry().IsRegistered(req.Observer) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Observer) + + if !registry.ObserverRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("observer %s not found", name))) return } - req.Data.Name = req.Observer + req.Data.Name = name v := parser.ParseObserver(&req.Data) - registry.ObserverRegistry().Unregister(req.Observer) + registry.ObserverRegistry().Unregister(name) - if err := registry.ObserverRegistry().Register(req.Observer, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.ObserverRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("observer %s already exists", name))) return } config.OnUpdate(func(c *config.Config) error { for i := range c.Observers { - if c.Observers[i].Name == req.Observer { + if c.Observers[i].Name == name { c.Observers[i] = &req.Data break } @@ -145,17 +155,19 @@ func deleteObserver(ctx *gin.Context) { var req deleteObserverRequest ctx.ShouldBindUri(&req) - if !registry.ObserverRegistry().IsRegistered(req.Observer) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Observer) + + if !registry.ObserverRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("observer %s not found", name))) return } - registry.ObserverRegistry().Unregister(req.Observer) + registry.ObserverRegistry().Unregister(name) config.OnUpdate(func(c *config.Config) error { observers := c.Observers c.Observers = nil for _, s := range observers { - if s.Name == req.Observer { + if s.Name == name { continue } c.Observers = append(c.Observers, s) diff --git a/api/config_rate_limiter.go b/api/config_rate_limiter.go index e491e6e..5484f22 100644 --- a/api/config_rate_limiter.go +++ b/api/config_rate_limiter.go @@ -1,7 +1,9 @@ package api import ( + "fmt" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/go-gost/x/config" @@ -35,15 +37,21 @@ func createRateLimiter(ctx *gin.Context) { var req createRateLimiterRequest ctx.ShouldBindJSON(&req.Data) - if req.Data.Name == "" { - writeError(ctx, ErrInvalid) + name := strings.TrimSpace(req.Data.Name) + if name == "" { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "limiter name is required")) + return + } + + if registry.RateLimiterRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name))) return } v := parser.ParseRateLimiter(&req.Data) - if err := registry.RateLimiterRegistry().Register(req.Data.Name, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.RateLimiterRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name))) return } @@ -87,25 +95,27 @@ func updateRateLimiter(ctx *gin.Context) { ctx.ShouldBindUri(&req) ctx.ShouldBindJSON(&req.Data) - if !registry.RateLimiterRegistry().IsRegistered(req.Limiter) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Limiter) + + if !registry.RateLimiterRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name))) return } - req.Data.Name = req.Limiter + req.Data.Name = name v := parser.ParseRateLimiter(&req.Data) - registry.RateLimiterRegistry().Unregister(req.Limiter) + registry.RateLimiterRegistry().Unregister(name) - if err := registry.RateLimiterRegistry().Register(req.Limiter, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.RateLimiterRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name))) return } config.OnUpdate(func(c *config.Config) error { for i := range c.RLimiters { - if c.RLimiters[i].Name == req.Limiter { + if c.RLimiters[i].Name == name { c.RLimiters[i] = &req.Data break } @@ -145,17 +155,19 @@ func deleteRateLimiter(ctx *gin.Context) { var req deleteRateLimiterRequest ctx.ShouldBindUri(&req) - if !registry.RateLimiterRegistry().IsRegistered(req.Limiter) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Limiter) + + if !registry.RateLimiterRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name))) return } - registry.RateLimiterRegistry().Unregister(req.Limiter) + registry.RateLimiterRegistry().Unregister(name) config.OnUpdate(func(c *config.Config) error { limiteres := c.RLimiters c.RLimiters = nil for _, s := range limiteres { - if s.Name == req.Limiter { + if s.Name == name { continue } c.RLimiters = append(c.RLimiters, s) diff --git a/api/config_recorder.go b/api/config_recorder.go new file mode 100644 index 0000000..7e92091 --- /dev/null +++ b/api/config_recorder.go @@ -0,0 +1,180 @@ +package api + +import ( + "fmt" + "net/http" + "strings" + + "github.com/gin-gonic/gin" + "github.com/go-gost/x/config" + parser "github.com/go-gost/x/config/parsing/recorder" + "github.com/go-gost/x/registry" +) + +// swagger:parameters createRecorderRequest +type createRecorderRequest struct { + // in: body + Data config.RecorderConfig `json:"data"` +} + +// successful operation. +// swagger:response createRecorderResponse +type createRecorderResponse struct { + Data Response +} + +func createRecorder(ctx *gin.Context) { + // swagger:route POST /config/recorders Recorder createRecorderRequest + // + // Create a new recorder, the name of the recorder must be unique in recorder list. + // + // Security: + // basicAuth: [] + // + // Responses: + // 200: createRecorderResponse + + var req createRecorderRequest + ctx.ShouldBindJSON(&req.Data) + + name := strings.TrimSpace(req.Data.Name) + if name == "" { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "recorder name is required")) + return + } + + if registry.RecorderRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("recorder %s already exists", name))) + return + } + v := parser.ParseRecorder(&req.Data) + + if err := registry.RecorderRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("recorder %s already exists", name))) + return + } + + config.OnUpdate(func(c *config.Config) error { + c.Recorders = append(c.Recorders, &req.Data) + return nil + }) + + ctx.JSON(http.StatusOK, Response{ + Msg: "OK", + }) +} + +// swagger:parameters updateRecorderRequest +type updateRecorderRequest struct { + // in: path + // required: true + Recorder string `uri:"recorder" json:"recorder"` + // in: body + Data config.RecorderConfig `json:"data"` +} + +// successful operation. +// swagger:response updateRecorderResponse +type updateRecorderResponse struct { + Data Response +} + +func updateRecorder(ctx *gin.Context) { + // swagger:route PUT /config/recorders/{recorder} Recorder updateRecorderRequest + // + // Update recorder by name, the recorder must already exist. + // + // Security: + // basicAuth: [] + // + // Responses: + // 200: updateRecorderResponse + + var req updateRecorderRequest + ctx.ShouldBindUri(&req) + ctx.ShouldBindJSON(&req.Data) + + name := strings.TrimSpace(req.Recorder) + + if !registry.RecorderRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("recorder %s not found", name))) + return + } + + req.Data.Name = name + + v := parser.ParseRecorder(&req.Data) + + registry.RecorderRegistry().Unregister(name) + + if err := registry.RecorderRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("recorder %s already exists", name))) + return + } + + config.OnUpdate(func(c *config.Config) error { + for i := range c.Recorders { + if c.Recorders[i].Name == name { + c.Recorders[i] = &req.Data + break + } + } + return nil + }) + + ctx.JSON(http.StatusOK, Response{ + Msg: "OK", + }) +} + +// swagger:parameters deleteRecorderRequest +type deleteRecorderRequest struct { + // in: path + // required: true + Recorder string `uri:"recorder" json:"recorder"` +} + +// successful operation. +// swagger:response deleteRecorderResponse +type deleteRecorderResponse struct { + Data Response +} + +func deleteRecorder(ctx *gin.Context) { + // swagger:route DELETE /config/recorders/{recorder} Recorder deleteRecorderRequest + // + // Delete recorder by name. + // + // Security: + // basicAuth: [] + // + // Responses: + // 200: deleteRecorderResponse + + var req deleteRecorderRequest + ctx.ShouldBindUri(&req) + + name := strings.TrimSpace(req.Recorder) + + if !registry.RecorderRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("recorder %s not found", name))) + return + } + registry.RecorderRegistry().Unregister(name) + + config.OnUpdate(func(c *config.Config) error { + recorders := c.Recorders + c.Recorders = nil + for _, s := range recorders { + if s.Name == name { + continue + } + c.Recorders = append(c.Recorders, s) + } + return nil + }) + + ctx.JSON(http.StatusOK, Response{ + Msg: "OK", + }) +} diff --git a/api/config_resolver.go b/api/config_resolver.go index 485ce49..8a6e82f 100644 --- a/api/config_resolver.go +++ b/api/config_resolver.go @@ -1,7 +1,9 @@ package api import ( + "fmt" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/go-gost/x/config" @@ -35,19 +37,25 @@ func createResolver(ctx *gin.Context) { var req createResolverRequest ctx.ShouldBindJSON(&req.Data) - if req.Data.Name == "" { - writeError(ctx, ErrInvalid) + name := strings.TrimSpace(req.Data.Name) + if name == "" { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "resolver name is required")) + return + } + + if registry.ResolverRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("resolver %s already exists", name))) return } v, err := parser.ParseResolver(&req.Data) if err != nil { - writeError(ctx, ErrCreate) + writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create resolver %s failed: %s", name, err.Error()))) return } - if err := registry.ResolverRegistry().Register(req.Data.Name, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.ResolverRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("resolver %s already exists", name))) return } @@ -91,29 +99,31 @@ func updateResolver(ctx *gin.Context) { ctx.ShouldBindUri(&req) ctx.ShouldBindJSON(&req.Data) - if !registry.ResolverRegistry().IsRegistered(req.Resolver) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Resolver) + + if !registry.ResolverRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("resolver %s not found", name))) return } - req.Data.Name = req.Resolver + req.Data.Name = name v, err := parser.ParseResolver(&req.Data) if err != nil { - writeError(ctx, ErrCreate) + writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create resolver %s failed: %s", name, err.Error()))) return } - registry.ResolverRegistry().Unregister(req.Resolver) + registry.ResolverRegistry().Unregister(name) - if err := registry.ResolverRegistry().Register(req.Resolver, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.ResolverRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("resolver %s already exists", name))) return } config.OnUpdate(func(c *config.Config) error { for i := range c.Resolvers { - if c.Resolvers[i].Name == req.Resolver { + if c.Resolvers[i].Name == name { c.Resolvers[i] = &req.Data break } @@ -153,17 +163,19 @@ func deleteResolver(ctx *gin.Context) { var req deleteResolverRequest ctx.ShouldBindUri(&req) - if !registry.ResolverRegistry().IsRegistered(req.Resolver) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Resolver) + + if !registry.ResolverRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("resolver %s not found", name))) return } - registry.ResolverRegistry().Unregister(req.Resolver) + registry.ResolverRegistry().Unregister(name) config.OnUpdate(func(c *config.Config) error { resolvers := c.Resolvers c.Resolvers = nil for _, s := range resolvers { - if s.Name == req.Resolver { + if s.Name == name { continue } c.Resolvers = append(c.Resolvers, s) diff --git a/api/config_router.go b/api/config_router.go index 6d0c624..115dccc 100644 --- a/api/config_router.go +++ b/api/config_router.go @@ -1,7 +1,9 @@ package api import ( + "fmt" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/go-gost/x/config" @@ -35,15 +37,21 @@ func createRouter(ctx *gin.Context) { var req createRouterRequest ctx.ShouldBindJSON(&req.Data) - if req.Data.Name == "" { - writeError(ctx, ErrInvalid) + name := strings.TrimSpace(req.Data.Name) + if name == "" { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "router name is required")) + return + } + + if registry.RouterRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("router %s already exists", name))) return } v := parser.ParseRouter(&req.Data) - if err := registry.RouterRegistry().Register(req.Data.Name, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.RouterRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("router %s already exists", name))) return } @@ -87,25 +95,27 @@ func updateRouter(ctx *gin.Context) { ctx.ShouldBindUri(&req) ctx.ShouldBindJSON(&req.Data) - if !registry.RouterRegistry().IsRegistered(req.Router) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Router) + + if !registry.RouterRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("router %s not found", name))) return } - req.Data.Name = req.Router + req.Data.Name = name v := parser.ParseRouter(&req.Data) - registry.RouterRegistry().Unregister(req.Router) + registry.RouterRegistry().Unregister(name) - if err := registry.RouterRegistry().Register(req.Router, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.RouterRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("router %s already exists", name))) return } config.OnUpdate(func(c *config.Config) error { for i := range c.Routers { - if c.Routers[i].Name == req.Router { + if c.Routers[i].Name == name { c.Routers[i] = &req.Data break } @@ -145,17 +155,19 @@ func deleteRouter(ctx *gin.Context) { var req deleteRouterRequest ctx.ShouldBindUri(&req) - if !registry.RouterRegistry().IsRegistered(req.Router) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.Router) + + if !registry.RouterRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("router %s not found", name))) return } - registry.RouterRegistry().Unregister(req.Router) + registry.RouterRegistry().Unregister(name) config.OnUpdate(func(c *config.Config) error { routeres := c.Routers c.Routers = nil for _, s := range routeres { - if s.Name == req.Router { + if s.Name == name { continue } c.Routers = append(c.Routers, s) diff --git a/api/config_sd.go b/api/config_sd.go index 5036668..d625ce6 100644 --- a/api/config_sd.go +++ b/api/config_sd.go @@ -1,7 +1,9 @@ package api import ( + "fmt" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/go-gost/x/config" @@ -35,15 +37,21 @@ func createSD(ctx *gin.Context) { var req createSDRequest ctx.ShouldBindJSON(&req.Data) - if req.Data.Name == "" { - writeError(ctx, ErrInvalid) + name := strings.TrimSpace(req.Data.Name) + if name == "" { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "sd name is required")) + return + } + + if registry.SDRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("sd %s already exists", name))) return } v := parser.ParseSD(&req.Data) - if err := registry.SDRegistry().Register(req.Data.Name, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.SDRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("sd %s already exists", name))) return } @@ -87,25 +95,27 @@ func updateSD(ctx *gin.Context) { ctx.ShouldBindUri(&req) ctx.ShouldBindJSON(&req.Data) - if !registry.SDRegistry().IsRegistered(req.SD) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.SD) + + if !registry.SDRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("sd %s not found", name))) return } - req.Data.Name = req.SD + req.Data.Name = name v := parser.ParseSD(&req.Data) - registry.SDRegistry().Unregister(req.SD) + registry.SDRegistry().Unregister(name) - if err := registry.SDRegistry().Register(req.SD, v); err != nil { - writeError(ctx, ErrDup) + if err := registry.SDRegistry().Register(name, v); err != nil { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("sd %s already exists", name))) return } config.OnUpdate(func(c *config.Config) error { for i := range c.SDs { - if c.SDs[i].Name == req.SD { + if c.SDs[i].Name == name { c.SDs[i] = &req.Data break } @@ -145,17 +155,19 @@ func deleteSD(ctx *gin.Context) { var req deleteSDRequest ctx.ShouldBindUri(&req) - if !registry.SDRegistry().IsRegistered(req.SD) { - writeError(ctx, ErrNotFound) + name := strings.TrimSpace(req.SD) + + if !registry.SDRegistry().IsRegistered(name) { + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("sd %s not found", name))) return } - registry.SDRegistry().Unregister(req.SD) + registry.SDRegistry().Unregister(name) config.OnUpdate(func(c *config.Config) error { sds := c.SDs c.SDs = nil for _, s := range sds { - if s.Name == req.SD { + if s.Name == name { continue } c.SDs = append(c.SDs, s) diff --git a/api/config_service.go b/api/config_service.go index 32cb330..ebe0351 100644 --- a/api/config_service.go +++ b/api/config_service.go @@ -102,28 +102,28 @@ func updateService(ctx *gin.Context) { ctx.ShouldBindUri(&req) ctx.ShouldBindJSON(&req.Data) - service := strings.TrimSpace(req.Service) + name := strings.TrimSpace(req.Service) - old := registry.ServiceRegistry().Get(service) + old := registry.ServiceRegistry().Get(name) if old == nil { - writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("service %s not found", service))) + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("service %s not found", name))) return } old.Close() - req.Data.Name = service + req.Data.Name = name svc, err := parser.ParseService(&req.Data) if err != nil { - writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create service %s failed: %s", service, err.Error()))) + writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create service %s failed: %s", name, err.Error()))) return } - registry.ServiceRegistry().Unregister(req.Service) + registry.ServiceRegistry().Unregister(name) - if err := registry.ServiceRegistry().Register(req.Service, svc); err != nil { + if err := registry.ServiceRegistry().Register(name, svc); err != nil { svc.Close() - writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("service %s already exists", service))) + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("service %s already exists", name))) return } @@ -131,7 +131,7 @@ func updateService(ctx *gin.Context) { config.OnUpdate(func(c *config.Config) error { for i := range c.Services { - if c.Services[i].Name == req.Service { + if c.Services[i].Name == name { c.Services[i] = &req.Data break } @@ -171,22 +171,22 @@ func deleteService(ctx *gin.Context) { var req deleteServiceRequest ctx.ShouldBindUri(&req) - service := strings.TrimSpace(req.Service) + name := strings.TrimSpace(req.Service) - svc := registry.ServiceRegistry().Get(service) + svc := registry.ServiceRegistry().Get(name) if svc == nil { - writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("service %s not found", service))) + writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("service %s not found", name))) return } - registry.ServiceRegistry().Unregister(service) + registry.ServiceRegistry().Unregister(name) svc.Close() config.OnUpdate(func(c *config.Config) error { services := c.Services c.Services = nil for _, s := range services { - if s.Name == req.Service { + if s.Name == name { continue } c.Services = append(c.Services, s) diff --git a/api/middleware.go b/api/middleware.go index 27202c3..8a49037 100644 --- a/api/middleware.go +++ b/api/middleware.go @@ -37,7 +37,11 @@ func mwBasicAuth(auther auth.Authenticator) gin.HandlerFunc { u, p, _ := c.Request.BasicAuth() if _, ok := auther.Authenticate(c, u, p); !ok { c.Writer.Header().Set("WWW-Authenticate", "Basic") - c.AbortWithStatus(http.StatusUnauthorized) + c.JSON(http.StatusUnauthorized, Response{ + Code: http.StatusUnauthorized, + Msg: "Unauthorized", + }) + c.Abort() } } } diff --git a/api/swagger.yaml b/api/swagger.yaml index 95af895..e120dfb 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -306,7 +306,10 @@ definitions: type: string type: array x-go-name: Bypasses + filter: + $ref: '#/definitions/NodeFilterConfig' host: + description: DEPRECATED by filter.host type: string x-go-name: Host http: @@ -322,9 +325,11 @@ definitions: type: string x-go-name: Network path: + description: DEPRECATED by filter.path type: string x-go-name: Path protocol: + description: DEPRECATED by filter.protocol type: string x-go-name: Protocol tls: @@ -333,7 +338,12 @@ definitions: x-go-package: github.com/go-gost/x/config ForwarderConfig: properties: + hop: + description: the referenced hop name + type: string + x-go-name: Hop name: + description: DEPRECATED by hop field type: string x-go-name: Name nodes: @@ -705,8 +715,6 @@ definitions: addr: type: string x-go-name: Addr - auth: - $ref: '#/definitions/AuthConfig' bypass: type: string x-go-name: Bypass @@ -719,9 +727,8 @@ definitions: $ref: '#/definitions/ConnectorConfig' dialer: $ref: '#/definitions/DialerConfig' - host: - type: string - x-go-name: Host + filter: + $ref: '#/definitions/NodeFilterConfig' hosts: type: string x-go-name: Hosts @@ -740,12 +747,6 @@ definitions: network: type: string x-go-name: Network - path: - type: string - x-go-name: Path - protocol: - type: string - x-go-name: Protocol resolver: type: string x-go-name: Resolver @@ -755,6 +756,19 @@ definitions: $ref: '#/definitions/TLSNodeConfig' type: object x-go-package: github.com/go-gost/x/config + NodeFilterConfig: + properties: + host: + type: string + x-go-name: Host + path: + type: string + x-go-name: Path + protocol: + type: string + x-go-name: Protocol + type: object + x-go-package: github.com/go-gost/x/config ObserverConfig: properties: name: @@ -1762,6 +1776,64 @@ paths: summary: Update observer by name, the observer must already exist. tags: - Observer + /config/recorders: + post: + operationId: createRecorderRequest + parameters: + - in: body + name: data + schema: + $ref: '#/definitions/RecorderConfig' + x-go-name: Data + responses: + "200": + $ref: '#/responses/createRecorderResponse' + security: + - basicAuth: + - '[]' + summary: Create a new recorder, the name of the recorder must be unique in recorder list. + tags: + - Recorder + /config/recorders/{recorder}: + delete: + operationId: deleteRecorderRequest + parameters: + - in: path + name: recorder + required: true + type: string + x-go-name: Recorder + responses: + "200": + $ref: '#/responses/deleteRecorderResponse' + security: + - basicAuth: + - '[]' + summary: Delete recorder by name. + tags: + - Recorder + put: + operationId: updateRecorderRequest + parameters: + - in: path + name: recorder + required: true + type: string + x-go-name: Recorder + - in: body + name: data + schema: + $ref: '#/definitions/RecorderConfig' + x-go-name: Data + responses: + "200": + $ref: '#/responses/updateRecorderResponse' + security: + - basicAuth: + - '[]' + summary: Update recorder by name, the recorder must already exist. + tags: + - Recorder /config/resolvers: post: operationId: createResolverRequest @@ -2121,6 +2193,12 @@ responses: Data: {} schema: $ref: '#/definitions/Response' + createRecorderResponse: + description: successful operation. + headers: + Data: {} + schema: + $ref: '#/definitions/Response' createResolverResponse: description: successful operation. headers: @@ -2211,6 +2289,12 @@ responses: Data: {} schema: $ref: '#/definitions/Response' + deleteRecorderResponse: + description: successful operation. + headers: + Data: {} + schema: + $ref: '#/definitions/Response' deleteResolverResponse: description: successful operation. headers: @@ -2313,6 +2397,12 @@ responses: Data: {} schema: $ref: '#/definitions/Response' + updateRecorderResponse: + description: successful operation. + headers: + Data: {} + schema: + $ref: '#/definitions/Response' updateResolverResponse: description: successful operation. headers: diff --git a/config/config.go b/config/config.go index 312aca0..c97dfa8 100644 --- a/config/config.go +++ b/config/config.go @@ -297,9 +297,9 @@ type RedisRecorder struct { } type RecorderObject struct { - Name string `json:"name"` - Record string `json:"record"` - Metadata map[string]any + Name string `json:"name"` + Record string `json:"record"` + Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"` } type LimiterConfig struct { diff --git a/hop/plugin/grpc.go b/hop/plugin/grpc.go index 2b26ace..ed5671b 100644 --- a/hop/plugin/grpc.go +++ b/hop/plugin/grpc.go @@ -94,6 +94,10 @@ func (p *grpcPlugin) Select(ctx context.Context, opts ...hop.SelectOption) *chai } func (p *grpcPlugin) Close() error { + if p.conn == nil { + return nil + } + if closer, ok := p.conn.(io.Closer); ok { return closer.Close() } diff --git a/hosts/plugin/grpc.go b/hosts/plugin/grpc.go index 5585c07..3d76d83 100644 --- a/hosts/plugin/grpc.go +++ b/hosts/plugin/grpc.go @@ -71,6 +71,10 @@ func (p *grpcPlugin) Lookup(ctx context.Context, network, host string, opts ...h } func (p *grpcPlugin) Close() error { + if p.conn == nil { + return nil + } + if closer, ok := p.conn.(io.Closer); ok { return closer.Close() } diff --git a/ingress/plugin/grpc.go b/ingress/plugin/grpc.go index ffb6535..c062770 100644 --- a/ingress/plugin/grpc.go +++ b/ingress/plugin/grpc.go @@ -82,6 +82,10 @@ func (p *grpcPlugin) SetRule(ctx context.Context, rule *ingress.Rule, opts ...in } func (p *grpcPlugin) Close() error { + if p.conn == nil { + return nil + } + if closer, ok := p.conn.(io.Closer); ok { return closer.Close() } diff --git a/limiter/traffic/plugin/grpc.go b/limiter/traffic/plugin/grpc.go index dbe456b..c05a5e4 100644 --- a/limiter/traffic/plugin/grpc.go +++ b/limiter/traffic/plugin/grpc.go @@ -95,6 +95,10 @@ func (p *grpcPlugin) Out(ctx context.Context, key string, opts ...traffic.Option } func (p *grpcPlugin) Close() error { + if p.conn == nil { + return nil + } + if closer, ok := p.conn.(io.Closer); ok { return closer.Close() } diff --git a/observer/plugin/grpc.go b/observer/plugin/grpc.go index ecbad4b..4bdaabf 100644 --- a/observer/plugin/grpc.go +++ b/observer/plugin/grpc.go @@ -33,6 +33,7 @@ func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) observer.Obs conn, err := plugin.NewGRPCConn(addr, &options) if err != nil { log.Error(err) + return nil } p := &grpcPlugin{ diff --git a/observer/plugin/http.go b/observer/plugin/http.go index 321e35b..01e9a38 100644 --- a/observer/plugin/http.go +++ b/observer/plugin/http.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "net/http" + "strings" "github.com/go-gost/core/logger" "github.com/go-gost/core/observer" @@ -58,6 +59,9 @@ func NewHTTPPlugin(name string, url string, opts ...plugin.Option) observer.Obse opt(&options) } + if !strings.HasPrefix(url, "http") { + url = "http://" + url + } return &httpPlugin{ url: url, client: plugin.NewHTTPClient(&options), diff --git a/recorder/plugin/grpc.go b/recorder/plugin/grpc.go index 11fa028..5aa8544 100644 --- a/recorder/plugin/grpc.go +++ b/recorder/plugin/grpc.go @@ -69,6 +69,10 @@ func (p *grpcPlugin) Record(ctx context.Context, b []byte, opts ...recorder.Reco } func (p *grpcPlugin) Close() error { + if p.conn == nil { + return nil + } + if closer, ok := p.conn.(io.Closer); ok { return closer.Close() } diff --git a/resolver/plugin/grpc.go b/resolver/plugin/grpc.go index 1d18c00..c6cbdac 100644 --- a/resolver/plugin/grpc.go +++ b/resolver/plugin/grpc.go @@ -70,6 +70,10 @@ func (p *grpcPlugin) Resolve(ctx context.Context, network, host string, opts ... } func (p *grpcPlugin) Close() error { + if p.conn == nil { + return nil + } + if closer, ok := p.conn.(io.Closer); ok { return closer.Close() } diff --git a/router/plugin/grpc.go b/router/plugin/grpc.go index d966f34..f413d15 100644 --- a/router/plugin/grpc.go +++ b/router/plugin/grpc.go @@ -63,6 +63,10 @@ func (p *grpcPlugin) GetRoute(ctx context.Context, dst net.IP, opts ...router.Op } func (p *grpcPlugin) Close() error { + if p.conn == nil { + return nil + } + if closer, ok := p.conn.(io.Closer); ok { return closer.Close() } diff --git a/sd/plugin/grpc.go b/sd/plugin/grpc.go index 0b08f70..0ef7c01 100644 --- a/sd/plugin/grpc.go +++ b/sd/plugin/grpc.go @@ -127,6 +127,10 @@ func (p *grpcPlugin) Get(ctx context.Context, name string) ([]*sd.Service, error } func (p *grpcPlugin) Close() error { + if p.conn == nil { + return nil + } + if closer, ok := p.conn.(io.Closer); ok { return closer.Close() }