add reload for web api

This commit is contained in:
ginuerzh
2025-01-10 21:39:21 +08:00
parent e480039691
commit aa7528fc1e
11 changed files with 858 additions and 145 deletions
+13 -83
View File
@@ -2,13 +2,11 @@ package api
import (
"embed"
"net"
"net/http"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/go-gost/core/auth"
"github.com/go-gost/core/service"
)
var (
@@ -21,55 +19,17 @@ type Response struct {
Msg string `json:"msg,omitempty"`
}
type options struct {
accessLog bool
pathPrefix string
auther auth.Authenticator
type Options struct {
AccessLog bool
PathPrefix string
Auther auth.Authenticator
}
type Option func(*options)
func PathPrefixOption(pathPrefix string) Option {
return func(o *options) {
o.pathPrefix = pathPrefix
}
}
func AccessLogOption(enable bool) Option {
return func(o *options) {
o.accessLog = enable
}
}
func AutherOption(auther auth.Authenticator) Option {
return func(o *options) {
o.auther = auther
}
}
type server struct {
s *http.Server
ln net.Listener
cclose chan struct{}
}
func NewService(network, addr string, opts ...Option) (service.Service, error) {
if network == "" {
network = "tcp"
}
ln, err := net.Listen(network, addr)
if err != nil {
return nil, err
func Register(r *gin.Engine, opts *Options) {
if opts == nil {
opts = &Options{}
}
var options options
for _, opt := range opts {
opt(&options)
}
gin.SetMode(gin.ReleaseMode)
r := gin.New()
r.Use(
cors.New((cors.Config{
AllowAllOrigins: true,
@@ -79,55 +39,25 @@ func NewService(network, addr string, opts ...Option) (service.Service, error) {
})),
gin.Recovery(),
)
if options.accessLog {
if opts.AccessLog {
r.Use(mwLogger())
}
router := r.Group("")
if options.pathPrefix != "" {
router = router.Group(options.pathPrefix)
if opts.PathPrefix != "" {
router = router.Group(opts.PathPrefix)
}
router.StaticFS("/docs", http.FS(swaggerDoc))
config := router.Group("/config")
config.Use(mwBasicAuth(options.auther))
registerConfig(config)
config.Use(mwBasicAuth(opts.Auther))
return &server{
s: &http.Server{
Handler: r,
},
ln: ln,
cclose: make(chan struct{}),
}, nil
}
func (s *server) Serve() error {
return s.s.Serve(s.ln)
}
func (s *server) Addr() net.Addr {
return s.ln.Addr()
}
func (s *server) Close() error {
return s.s.Close()
}
func (s *server) IsClosed() bool {
select {
case <-s.cclose:
return true
default:
return false
}
}
func registerConfig(config *gin.RouterGroup) {
config.GET("", getConfig)
config.POST("", saveConfig)
config.POST("/reload", reloadConfig)
config.POST("/services", createService)
config.PUT("/services/:service", updateService)
config.DELETE("/services/:service", deleteService)
+56
View File
@@ -0,0 +1,56 @@
package api
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/go-gost/x/config"
"github.com/go-gost/x/config/loader"
"github.com/go-gost/x/config/parsing/parser"
"github.com/go-gost/x/registry"
)
// swagger:parameters reloadConfigRequest
type reloadConfigRequest struct{}
// successful operation.
// swagger:response reloadConfigResponse
type reloadConfigResponse struct {
Data Response
}
func reloadConfig(ctx *gin.Context) {
// swagger:route POST /config/reload Reload reloadConfigRequest
//
// Hot reload config.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: reloadConfigResponse
cfg, err := parser.Parse()
if err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, err.Error()))
return
}
config.Set(cfg)
if err := loader.Load(cfg); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, err.Error()))
return
}
for _, svc := range registry.ServiceRegistry().GetAll() {
svc := svc
go func() {
svc.Serve()
}()
}
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
+96
View File
@@ -0,0 +1,96 @@
package service
import (
"net"
"net/http"
"github.com/gin-gonic/gin"
"github.com/go-gost/core/auth"
"github.com/go-gost/core/service"
"github.com/go-gost/x/api"
)
type options struct {
accessLog bool
pathPrefix string
auther auth.Authenticator
}
type Option func(*options)
func PathPrefixOption(pathPrefix string) Option {
return func(o *options) {
o.pathPrefix = pathPrefix
}
}
func AccessLogOption(enable bool) Option {
return func(o *options) {
o.accessLog = enable
}
}
func AutherOption(auther auth.Authenticator) Option {
return func(o *options) {
o.auther = auther
}
}
type server struct {
s *http.Server
ln net.Listener
cclose chan struct{}
}
func NewService(network, addr string, opts ...Option) (service.Service, error) {
if network == "" {
network = "tcp"
}
ln, err := net.Listen(network, addr)
if err != nil {
return nil, err
}
var options options
for _, opt := range opts {
opt(&options)
}
gin.SetMode(gin.ReleaseMode)
r := gin.New()
api.Register(r, &api.Options{
AccessLog: options.accessLog,
PathPrefix: options.pathPrefix,
Auther: options.auther,
})
return &server{
s: &http.Server{
Handler: r,
},
ln: ln,
cclose: make(chan struct{}),
}, nil
}
func (s *server) Serve() error {
return s.s.Serve(s.ln)
}
func (s *server) Addr() net.Addr {
return s.ln.Addr()
}
func (s *server) Close() error {
return s.s.Close()
}
func (s *server) IsClosed() bool {
select {
case <-s.cclose:
return true
default:
return false
}
}
+103 -8
View File
@@ -41,7 +41,7 @@ definitions:
reload:
$ref: '#/definitions/Duration'
reverse:
description: DEPRECATED by whitelist since beta.4
description: 'Deprecated: use whitelist instead'
type: boolean
x-go-name: Reverse
whitelist:
@@ -102,7 +102,7 @@ definitions:
reload:
$ref: '#/definitions/Duration'
reverse:
description: DEPRECATED by whitelist since beta.4
description: 'Deprecated: use whitelist instead'
type: boolean
x-go-name: Reverse
whitelist:
@@ -309,11 +309,13 @@ definitions:
filter:
$ref: '#/definitions/NodeFilterConfig'
host:
description: DEPRECATED by filter.host
description: 'Deprecated: use matcher instead'
type: string
x-go-name: Host
http:
$ref: '#/definitions/HTTPNodeConfig'
matcher:
$ref: '#/definitions/NodeMatcherConfig'
metadata:
additionalProperties: {}
type: object
@@ -325,11 +327,11 @@ definitions:
type: string
x-go-name: Network
path:
description: DEPRECATED by filter.path
description: 'Deprecated: use matcher instead'
type: string
x-go-name: Path
protocol:
description: DEPRECATED by filter.protocol
description: 'Deprecated: use matcher instead'
type: string
x-go-name: Protocol
tls:
@@ -343,7 +345,7 @@ definitions:
type: string
x-go-name: Hop
name:
description: DEPRECATED by hop field
description: 'Deprecated: use hop instead'
type: string
x-go-name: Name
nodes:
@@ -355,6 +357,17 @@ definitions:
$ref: '#/definitions/SelectorConfig'
type: object
x-go-package: github.com/go-gost/x/config
HTTPBodyRewriteConfig:
properties:
Match:
type: string
Replacement:
type: string
Type:
description: filter by MIME types
type: string
type: object
x-go-package: github.com/go-gost/x/config
HTTPLoader:
properties:
timeout:
@@ -371,20 +384,52 @@ definitions:
header:
additionalProperties:
type: string
description: 'Deprecated: use requestHeader instead'
type: object
x-go-name: Header
host:
description: rewrite host header
type: string
x-go-name: Host
requestHeader:
additionalProperties:
type: string
description: additional request header
type: object
x-go-name: RequestHeader
responseHeader:
additionalProperties:
type: string
description: additional response header
type: object
x-go-name: ResponseHeader
rewrite:
description: 'Deprecated: use rewriteURL instead'
items:
$ref: '#/definitions/HTTPURLRewriteConfig'
type: array
x-go-name: Rewrite
rewriteBody:
description: rewrite response body
items:
$ref: '#/definitions/HTTPBodyRewriteConfig'
type: array
x-go-name: RewriteBody
rewriteURL:
description: rewrite URL
items:
$ref: '#/definitions/HTTPURLRewriteConfig'
type: array
x-go-name: RewriteURL
type: object
x-go-package: github.com/go-gost/x/config
HTTPRecorder:
properties:
header:
additionalProperties:
type: string
type: object
x-go-name: Header
timeout:
$ref: '#/definitions/Duration'
url:
@@ -458,6 +503,10 @@ definitions:
interface:
type: string
x-go-name: Interface
metadata:
additionalProperties: {}
type: object
x-go-name: Metadata
name:
type: string
x-go-name: Name
@@ -737,6 +786,8 @@ definitions:
interface:
type: string
x-go-name: Interface
matcher:
$ref: '#/definitions/NodeMatcherConfig'
metadata:
additionalProperties: {}
type: object
@@ -744,6 +795,9 @@ definitions:
name:
type: string
x-go-name: Name
netns:
type: string
x-go-name: Netns
network:
type: string
x-go-name: Network
@@ -769,6 +823,17 @@ definitions:
x-go-name: Protocol
type: object
x-go-package: github.com/go-gost/x/config
NodeMatcherConfig:
properties:
priority:
format: int64
type: integer
x-go-name: Priority
rule:
type: string
x-go-name: Rule
type: object
x-go-package: github.com/go-gost/x/config
ObserverConfig:
properties:
name:
@@ -821,9 +886,10 @@ definitions:
x-go-package: github.com/go-gost/x/config
RecorderObject:
properties:
Metadata:
metadata:
additionalProperties: {}
type: object
x-go-name: Metadata
name:
type: string
x-go-name: Name
@@ -850,6 +916,9 @@ definitions:
type:
type: string
x-go-name: Type
username:
type: string
x-go-name: Username
type: object
x-go-package: github.com/go-gost/x/config
RedisRecorder:
@@ -870,6 +939,9 @@ definitions:
type:
type: string
x-go-name: Type
username:
type: string
x-go-name: Username
type: object
x-go-package: github.com/go-gost/x/config
ResolverConfig:
@@ -983,7 +1055,7 @@ definitions:
type: string
x-go-name: Hosts
interface:
description: DEPRECATED by metadata.interface since beta.5
description: 'Deprecated: use metadata.interface instead'
type: string
x-go-name: Interface
limiter:
@@ -1139,6 +1211,11 @@ definitions:
x-go-package: github.com/go-gost/x/config
TLSOptions:
properties:
alpn:
items:
type: string
type: array
x-go-name: ALPN
cipherSuites:
items:
type: string
@@ -1834,6 +1911,18 @@ paths:
summary: Update recorder by name, the recorder must already exist.
tags:
- Recorder
/config/reload:
post:
operationId: reloadConfigRequest
responses:
"200":
$ref: '#/responses/reloadConfigResponse'
security:
- basicAuth:
- '[]'
summary: Hot reload config.
tags:
- Reload
/config/resolvers:
post:
operationId: createResolverRequest
@@ -2325,6 +2414,12 @@ responses:
Config: {}
schema:
$ref: '#/definitions/Config'
reloadConfigResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
saveConfigResponse:
description: successful operation.
headers: