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  }