github.com/database64128/shadowsocks-go@v1.10.2-0.20240315062903-143a773533f1/api/v1/servers.go (about)

     1  package v1
     2  
     3  import (
     4  	"errors"
     5  
     6  	"github.com/database64128/shadowsocks-go/cred"
     7  	"github.com/database64128/shadowsocks-go/stats"
     8  	"github.com/gofiber/fiber/v2"
     9  )
    10  
    11  // ServerInfo contains information about the API server.
    12  type ServerInfo struct {
    13  	Name       string `json:"server"`
    14  	APIVersion string `json:"apiVersion"`
    15  }
    16  
    17  var serverInfo = ServerInfo{
    18  	Name:       "shadowsocks-go",
    19  	APIVersion: "v1",
    20  }
    21  
    22  // GetServerInfo returns information about the API server.
    23  func GetServerInfo(c *fiber.Ctx) error {
    24  	return c.JSON(&serverInfo)
    25  }
    26  
    27  type managedServer struct {
    28  	cms *cred.ManagedServer
    29  	sc  stats.Collector
    30  }
    31  
    32  // ServerManager handles server management API requests.
    33  type ServerManager struct {
    34  	managedServers     map[string]*managedServer
    35  	managedServerNames []string
    36  }
    37  
    38  // NewServerManager returns a new server manager.
    39  func NewServerManager() *ServerManager {
    40  	return &ServerManager{
    41  		managedServers: make(map[string]*managedServer),
    42  	}
    43  }
    44  
    45  // AddServer adds a server to the server manager.
    46  func (sm *ServerManager) AddServer(name string, cms *cred.ManagedServer, sc stats.Collector) {
    47  	sm.managedServers[name] = &managedServer{
    48  		cms: cms,
    49  		sc:  sc,
    50  	}
    51  	sm.managedServerNames = append(sm.managedServerNames, name)
    52  }
    53  
    54  // Routes sets up routes for the /v1/servers endpoint.
    55  func (sm *ServerManager) Routes(v1 fiber.Router) {
    56  	v1.Get("/servers", sm.ListServers)
    57  
    58  	server := v1.Group("/servers/:server", sm.ContextManagedServer)
    59  	server.Get("", GetServerInfo)
    60  	server.Get("/stats", sm.GetStats)
    61  
    62  	users := server.Group("/users", sm.CheckMultiUserSupport)
    63  	users.Get("", sm.ListUsers)
    64  	users.Post("", sm.AddUser)
    65  	users.Get("/:username", sm.GetUser)
    66  	users.Patch("/:username", sm.UpdateUser)
    67  	users.Delete("/:username", sm.DeleteUser)
    68  }
    69  
    70  // ListServers lists all managed servers.
    71  func (sm *ServerManager) ListServers(c *fiber.Ctx) error {
    72  	return c.JSON(&sm.managedServerNames)
    73  }
    74  
    75  // ContextManagedServer is a middleware for the servers group.
    76  // It adds the server with the given name to the request context.
    77  func (sm *ServerManager) ContextManagedServer(c *fiber.Ctx) error {
    78  	name := c.Params("server")
    79  	ms := sm.managedServers[name]
    80  	if ms == nil {
    81  		return c.Status(fiber.StatusNotFound).JSON(&StandardError{Message: "server not found"})
    82  	}
    83  	c.Locals(0, ms)
    84  	return c.Next()
    85  }
    86  
    87  // managedServerFromContext returns the managed server from the request context.
    88  func managedServerFromContext(c *fiber.Ctx) *managedServer {
    89  	return c.Locals(0).(*managedServer)
    90  }
    91  
    92  // GetStats returns server traffic statistics.
    93  func (sm *ServerManager) GetStats(c *fiber.Ctx) error {
    94  	ms := managedServerFromContext(c)
    95  	if c.QueryBool("clear") {
    96  		return c.JSON(ms.sc.SnapshotAndReset())
    97  	}
    98  	return c.JSON(ms.sc.Snapshot())
    99  }
   100  
   101  // CheckMultiUserSupport is a middleware for the users group.
   102  // It checks whether the selected server supports user management.
   103  func (sm *ServerManager) CheckMultiUserSupport(c *fiber.Ctx) error {
   104  	ms := managedServerFromContext(c)
   105  	if ms.cms == nil {
   106  		return c.Status(fiber.StatusNotFound).JSON(&StandardError{Message: "The server does not support user management."})
   107  	}
   108  	return c.Next()
   109  }
   110  
   111  // UserList contains a list of user credentials.
   112  type UserList struct {
   113  	Users []cred.UserCredential `json:"users"`
   114  }
   115  
   116  // ListUsers lists server users.
   117  func (sm *ServerManager) ListUsers(c *fiber.Ctx) error {
   118  	ms := managedServerFromContext(c)
   119  	return c.JSON(&UserList{Users: ms.cms.Credentials()})
   120  }
   121  
   122  // AddUser adds a new user credential to the server.
   123  func (sm *ServerManager) AddUser(c *fiber.Ctx) error {
   124  	var uc cred.UserCredential
   125  	if err := c.BodyParser(&uc); err != nil {
   126  		return c.Status(fiber.StatusBadRequest).JSON(&StandardError{Message: err.Error()})
   127  	}
   128  
   129  	ms := managedServerFromContext(c)
   130  	if err := ms.cms.AddCredential(uc.Name, uc.UPSK); err != nil {
   131  		return c.Status(fiber.StatusBadRequest).JSON(&StandardError{Message: err.Error()})
   132  	}
   133  	return c.JSON(&uc)
   134  }
   135  
   136  // UserInfo contains information about a user.
   137  type UserInfo struct {
   138  	cred.UserCredential
   139  	stats.Traffic
   140  }
   141  
   142  // GetUser returns information about a user.
   143  func (sm *ServerManager) GetUser(c *fiber.Ctx) error {
   144  	ms := managedServerFromContext(c)
   145  	username := c.Params("username")
   146  	uc, ok := ms.cms.GetCredential(username)
   147  	if !ok {
   148  		return c.Status(fiber.StatusNotFound).JSON(&StandardError{Message: "user not found"})
   149  	}
   150  	return c.JSON(&UserInfo{uc, ms.sc.Snapshot().Traffic})
   151  }
   152  
   153  // UpdateUser updates a user's credential.
   154  func (sm *ServerManager) UpdateUser(c *fiber.Ctx) error {
   155  	var update struct {
   156  		UPSK []byte `json:"uPSK"`
   157  	}
   158  	if err := c.BodyParser(&update); err != nil {
   159  		return c.Status(fiber.StatusBadRequest).JSON(&StandardError{Message: err.Error()})
   160  	}
   161  
   162  	ms := managedServerFromContext(c)
   163  	username := c.Params("username")
   164  	if err := ms.cms.UpdateCredential(username, update.UPSK); err != nil {
   165  		if errors.Is(err, cred.ErrNonexistentUser) {
   166  			return c.Status(fiber.StatusNotFound).JSON(&StandardError{Message: err.Error()})
   167  		}
   168  		return c.Status(fiber.StatusBadRequest).JSON(&StandardError{Message: err.Error()})
   169  	}
   170  	return c.SendStatus(fiber.StatusNoContent)
   171  }
   172  
   173  // DeleteUser deletes a user's credential.
   174  func (sm *ServerManager) DeleteUser(c *fiber.Ctx) error {
   175  	ms := managedServerFromContext(c)
   176  	username := c.Params("username")
   177  	if err := ms.cms.DeleteCredential(username); err != nil {
   178  		return c.Status(fiber.StatusNotFound).JSON(&StandardError{Message: err.Error()})
   179  	}
   180  	return c.SendStatus(fiber.StatusNoContent)
   181  }