github.com/database64128/shadowsocks-go@v1.10.2-0.20240315062903-143a773533f1/api/api.go (about) 1 package api 2 3 import ( 4 "context" 5 "errors" 6 7 v1 "github.com/database64128/shadowsocks-go/api/v1" 8 "github.com/database64128/shadowsocks-go/jsonhelper" 9 "github.com/gofiber/contrib/fiberzap/v2" 10 "github.com/gofiber/fiber/v2" 11 fiberlog "github.com/gofiber/fiber/v2/log" 12 "github.com/gofiber/fiber/v2/middleware/etag" 13 "github.com/gofiber/fiber/v2/middleware/pprof" 14 "go.uber.org/zap" 15 ) 16 17 // Config stores the configuration for the RESTful API. 18 type Config struct { 19 Enabled bool `json:"enabled"` 20 21 // Debug 22 DebugPprof bool `json:"debugPprof"` 23 24 // Reverse proxy 25 EnableTrustedProxyCheck bool `json:"enableTrustedProxyCheck"` 26 TrustedProxies []string `json:"trustedProxies"` 27 ProxyHeader string `json:"proxyHeader"` 28 29 // Listen 30 Listen string `json:"listen"` 31 CertFile string `json:"certFile"` 32 KeyFile string `json:"keyFile"` 33 ClientCertFile string `json:"clientCertFile"` 34 35 // Misc 36 SecretPath string `json:"secretPath"` 37 FiberConfigPath string `json:"fiberConfigPath"` 38 } 39 40 // Server returns a new API server from the config. 41 func (c *Config) Server(logger *zap.Logger) (*Server, *v1.ServerManager, error) { 42 if !c.Enabled { 43 return nil, nil, nil 44 } 45 46 fiberlog.SetLogger(fiberzap.NewLogger(fiberzap.LoggerConfig{ 47 SetLogger: logger, 48 })) 49 50 fc := fiber.Config{ 51 ProxyHeader: c.ProxyHeader, 52 DisableStartupMessage: true, 53 Network: "tcp", 54 EnableTrustedProxyCheck: c.EnableTrustedProxyCheck, 55 TrustedProxies: c.TrustedProxies, 56 } 57 58 if c.FiberConfigPath != "" { 59 if err := jsonhelper.LoadAndDecodeDisallowUnknownFields(c.FiberConfigPath, &fc); err != nil { 60 return nil, nil, err 61 } 62 } 63 64 app := fiber.New(fc) 65 66 app.Use(etag.New()) 67 68 app.Use(fiberzap.New(fiberzap.Config{ 69 Logger: logger, 70 Fields: []string{"latency", "status", "method", "url", "ip"}, 71 })) 72 73 if c.DebugPprof { 74 app.Use(pprof.New()) 75 } 76 77 var router fiber.Router = app 78 if c.SecretPath != "" { 79 router = app.Group(c.SecretPath) 80 } 81 82 sm := v1.Routes(router) 83 84 return &Server{ 85 logger: logger, 86 app: app, 87 listen: c.Listen, 88 certFile: c.CertFile, 89 keyFile: c.KeyFile, 90 clientCertFile: c.ClientCertFile, 91 }, sm, nil 92 } 93 94 // Server is the RESTful API server. 95 type Server struct { 96 logger *zap.Logger 97 app *fiber.App 98 listen string 99 certFile string 100 keyFile string 101 clientCertFile string 102 ctx context.Context 103 } 104 105 // String implements the service.Service String method. 106 func (s *Server) String() string { 107 return "API server" 108 } 109 110 // Start starts the API server. 111 func (s *Server) Start(ctx context.Context) error { 112 s.ctx = ctx 113 go func() { 114 var err error 115 switch { 116 case s.clientCertFile != "": 117 err = s.app.ListenMutualTLS(s.listen, s.certFile, s.keyFile, s.clientCertFile) 118 case s.certFile != "": 119 err = s.app.ListenTLS(s.listen, s.certFile, s.keyFile) 120 default: 121 err = s.app.Listen(s.listen) 122 } 123 if err != nil { 124 s.logger.Fatal("Failed to start API server", zap.Error(err)) 125 } 126 }() 127 return nil 128 } 129 130 // Stop stops the API server. 131 func (s *Server) Stop() error { 132 if err := s.app.ShutdownWithContext(s.ctx); err != nil { 133 if errors.Is(err, context.Canceled) { 134 return nil 135 } 136 if errors.Is(err, context.DeadlineExceeded) { 137 return nil 138 } 139 return err 140 } 141 return nil 142 }