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