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 }