@@ -3,34 +3,19 @@ package ssm
33
44import (
55 "errors"
6+ "net/http"
67
78 "github.com/database64128/shadowsocks-go"
9+ "github.com/database64128/shadowsocks-go/api/internal/restapi"
810 "github.com/database64128/shadowsocks-go/cred"
911 "github.com/database64128/shadowsocks-go/stats"
10- "github.com/gofiber/fiber/v2"
1112)
1213
1314// StandardError is the standard error response.
1415type StandardError struct {
1516 Message string `json:"error"`
1617}
1718
18- // ServerInfo contains information about the API server.
19- type ServerInfo struct {
20- Name string `json:"server"`
21- APIVersion string `json:"apiVersion"`
22- }
23-
24- var serverInfo = ServerInfo {
25- Name : "shadowsocks-go " + shadowsocks .Version ,
26- APIVersion : "v1" ,
27- }
28-
29- // GetServerInfo returns information about the API server.
30- func GetServerInfo (c * fiber.Ctx ) error {
31- return c .JSON (& serverInfo )
32- }
33-
3419type managedServer struct {
3520 cms * cred.ManagedServer
3621 sc stats.Collector
@@ -58,131 +43,125 @@ func (sm *ServerManager) AddServer(name string, cms *cred.ManagedServer, sc stat
5843 sm .managedServerNames = append (sm .managedServerNames , name )
5944}
6045
61- // RegisterRoutes sets up routes for the /servers endpoint.
62- func (sm * ServerManager ) RegisterRoutes ( v1 fiber. Router ) {
63- v1 . Get ( "/servers" , sm .ListServers )
46+ // RegisterHandlers sets up handlers for the /servers endpoint.
47+ func (sm * ServerManager ) RegisterHandlers ( register func ( method string , path string , handler restapi. HandlerFunc ) ) {
48+ register ( http . MethodGet , "/servers" , sm .handleListServers )
6449
65- server := v1 .Group ("/servers/:server" , sm .ContextManagedServer )
66- server .Get ("" , GetServerInfo )
67- server .Get ("/stats" , sm .GetStats )
50+ register (http .MethodGet , "/servers/{server}" , sm .requireServerStats (handleGetServerInfo ))
51+ register (http .MethodGet , "/servers/{server}/stats" , sm .requireServerStats (handleGetStats ))
6852
69- users := server .Group ("/users" , sm .CheckMultiUserSupport )
70- users .Get ("" , sm .ListUsers )
71- users .Post ("" , sm .AddUser )
72- users .Get ("/:username" , sm .GetUser )
73- users .Patch ("/:username" , sm .UpdateUser )
74- users .Delete ("/:username" , sm .DeleteUser )
53+ register (http .MethodGet , "/servers/{server}/users" , sm .requireServerUsers (handleListUsers ))
54+ register (http .MethodPost , "/servers/{server}/users" , sm .requireServerUsers (handleAddUser ))
55+ register (http .MethodGet , "/servers/{server}/users/{username}" , sm .requireServerUsers (handleGetUser ))
56+ register (http .MethodPatch , "/servers/{server}/users/{username}" , sm .requireServerUsers (handleUpdateUser ))
57+ register (http .MethodDelete , "/servers/{server}/users/{username}" , sm .requireServerUsers (handleDeleteUser ))
7558}
7659
77- // ListServers lists all managed servers.
78- func (sm * ServerManager ) ListServers (c * fiber.Ctx ) error {
79- return c .JSON (& sm .managedServerNames )
60+ func (sm * ServerManager ) handleListServers (w http.ResponseWriter , _ * http.Request ) (int , error ) {
61+ return restapi .EncodeResponse (w , http .StatusOK , & sm .managedServerNames )
8062}
8163
82- // ContextManagedServer is a middleware for the servers group.
83- // It adds the server with the given name to the request context.
84- func (sm * ServerManager ) ContextManagedServer (c * fiber.Ctx ) error {
85- name := c .Params ("server" )
86- ms := sm .managedServers [name ]
87- if ms == nil {
88- return c .Status (fiber .StatusNotFound ).JSON (& StandardError {Message : "server not found" })
64+ func (sm * ServerManager ) requireServerStats (h func (http.ResponseWriter , * http.Request , stats.Collector ) (int , error )) restapi.HandlerFunc {
65+ return func (w http.ResponseWriter , r * http.Request ) (int , error ) {
66+ name := r .PathValue ("server" )
67+ ms := sm .managedServers [name ]
68+ if ms == nil {
69+ return restapi .EncodeResponse (w , http .StatusNotFound , StandardError {Message : "server not found" })
70+ }
71+ return h (w , r , ms .sc )
8972 }
90- c .Locals (0 , ms )
91- return c .Next ()
9273}
9374
94- // managedServerFromContext returns the managed server from the request context.
95- func managedServerFromContext (c * fiber.Ctx ) * managedServer {
96- return c .Locals (0 ).(* managedServer )
97- }
75+ var serverInfoJSON = []byte (`{"server":"shadowsocks-go ` + shadowsocks .Version + `","apiVersion":"v1"}` )
9876
99- // GetStats returns server traffic statistics.
100- func (sm * ServerManager ) GetStats (c * fiber.Ctx ) error {
101- ms := managedServerFromContext (c )
102- if c .QueryBool ("clear" ) {
103- return c .JSON (ms .sc .SnapshotAndReset ())
104- }
105- return c .JSON (ms .sc .Snapshot ())
77+ func handleGetServerInfo (w http.ResponseWriter , _ * http.Request , _ stats.Collector ) (int , error ) {
78+ w .Header ()["Content-Type" ] = []string {"application/json" }
79+ _ , err := w .Write (serverInfoJSON )
80+ return http .StatusOK , err
10681}
10782
108- // CheckMultiUserSupport is a middleware for the users group.
109- // It checks whether the selected server supports user management.
110- func ( sm * ServerManager ) CheckMultiUserSupport ( c * fiber. Ctx ) error {
111- ms := managedServerFromContext ( c )
112- if ms . cms == nil {
113- return c . Status ( fiber . StatusNotFound ). JSON ( & StandardError { Message : "The server does not support user management." } )
83+ func handleGetStats ( w http. ResponseWriter , r * http. Request , sc stats. Collector ) ( int , error ) {
84+ var serverStats stats. Server
85+ if v := r . URL . Query ()[ "clear" ]; len ( v ) == 1 && ( v [ 0 ] == "" || v [ 0 ] == "true" ) {
86+ serverStats = sc . SnapshotAndReset ( )
87+ } else {
88+ serverStats = sc . Snapshot ( )
11489 }
115- return c . Next ( )
90+ return restapi . EncodeResponse ( w , http . StatusOK , serverStats )
11691}
11792
118- // UserList contains a list of user credentials.
119- type UserList struct {
120- Users []cred.UserCredential `json:"users"`
93+ func (sm * ServerManager ) requireServerUsers (h func (http.ResponseWriter , * http.Request , * managedServer ) (int , error )) func (http.ResponseWriter , * http.Request ) (int , error ) {
94+ return func (w http.ResponseWriter , r * http.Request ) (int , error ) {
95+ name := r .PathValue ("server" )
96+ ms := sm .managedServers [name ]
97+ if ms == nil {
98+ return restapi .EncodeResponse (w , http .StatusNotFound , StandardError {Message : "server not found" })
99+ }
100+ if ms .cms == nil {
101+ return restapi .EncodeResponse (w , http .StatusNotFound , StandardError {Message : "The server does not support user management." })
102+ }
103+ return h (w , r , ms )
104+ }
121105}
122106
123- // ListUsers lists server users.
124- func (sm * ServerManager ) ListUsers (c * fiber.Ctx ) error {
125- ms := managedServerFromContext (c )
126- return c .JSON (& UserList {Users : ms .cms .Credentials ()})
107+ func handleListUsers (w http.ResponseWriter , _ * http.Request , ms * managedServer ) (int , error ) {
108+ type response struct {
109+ Users []cred.UserCredential `json:"users"`
110+ }
111+ return restapi .EncodeResponse (w , http .StatusOK , response {Users : ms .cms .Credentials ()})
127112}
128113
129- // AddUser adds a new user credential to the server.
130- func (sm * ServerManager ) AddUser (c * fiber.Ctx ) error {
114+ func handleAddUser (w http.ResponseWriter , r * http.Request , ms * managedServer ) (int , error ) {
131115 var uc cred.UserCredential
132- if err := c . BodyParser ( & uc ); err != nil {
133- return c . Status ( fiber .StatusBadRequest ). JSON ( & StandardError {Message : err .Error ()})
116+ if err := restapi . DecodeRequest ( r , & uc ); err != nil {
117+ return restapi . EncodeResponse ( w , http .StatusBadRequest , StandardError {Message : err .Error ()})
134118 }
135119
136- ms := managedServerFromContext (c )
137120 if err := ms .cms .AddCredential (uc .Name , uc .UPSK ); err != nil {
138- return c . Status ( fiber .StatusBadRequest ). JSON ( & StandardError {Message : err .Error ()})
121+ return restapi . EncodeResponse ( w , http .StatusBadRequest , StandardError {Message : err .Error ()})
139122 }
140- return c .JSON (& uc )
141- }
142123
143- // UserInfo contains information about a user.
144- type UserInfo struct {
145- cred.UserCredential
146- stats.Traffic
124+ return restapi .EncodeResponse (w , http .StatusOK , & uc )
147125}
148126
149- // GetUser returns information about a user.
150- func (sm * ServerManager ) GetUser (c * fiber.Ctx ) error {
151- ms := managedServerFromContext (c )
152- username := c .Params ("username" )
153- uc , ok := ms .cms .GetCredential (username )
127+ func handleGetUser (w http.ResponseWriter , r * http.Request , ms * managedServer ) (int , error ) {
128+ type response struct {
129+ cred.UserCredential
130+ stats.Traffic
131+ }
132+
133+ username := r .PathValue ("username" )
134+ userCred , ok := ms .cms .GetCredential (username )
154135 if ! ok {
155- return c . Status ( fiber .StatusNotFound ). JSON ( & StandardError {Message : "user not found" })
136+ return restapi . EncodeResponse ( w , http .StatusNotFound , StandardError {Message : "user not found" })
156137 }
157- return c .JSON (& UserInfo {uc , ms .sc .Snapshot ().Traffic })
138+
139+ return restapi .EncodeResponse (w , http .StatusOK , response {userCred , ms .sc .Snapshot ().Traffic })
158140}
159141
160- // UpdateUser updates a user's credential.
161- func (sm * ServerManager ) UpdateUser (c * fiber.Ctx ) error {
142+ func handleUpdateUser (w http.ResponseWriter , r * http.Request , ms * managedServer ) (int , error ) {
162143 var update struct {
163144 UPSK []byte `json:"uPSK"`
164145 }
165- if err := c . BodyParser ( & update ); err != nil {
166- return c . Status ( fiber .StatusBadRequest ). JSON ( & StandardError {Message : err .Error ()})
146+ if err := restapi . DecodeRequest ( r , & update ); err != nil {
147+ return restapi . EncodeResponse ( w , http .StatusBadRequest , StandardError {Message : err .Error ()})
167148 }
168149
169- ms := managedServerFromContext (c )
170- username := c .Params ("username" )
150+ username := r .PathValue ("username" )
171151 if err := ms .cms .UpdateCredential (username , update .UPSK ); err != nil {
172152 if errors .Is (err , cred .ErrNonexistentUser ) {
173- return c . Status ( fiber .StatusNotFound ). JSON ( & StandardError {Message : err .Error ()})
153+ return restapi . EncodeResponse ( w , http .StatusNotFound , StandardError {Message : err .Error ()})
174154 }
175- return c . Status ( fiber .StatusBadRequest ). JSON ( & StandardError {Message : err .Error ()})
155+ return restapi . EncodeResponse ( w , http .StatusBadRequest , StandardError {Message : err .Error ()})
176156 }
177- return c .SendStatus (fiber .StatusNoContent )
157+
158+ return restapi .EncodeResponse (w , http .StatusNoContent , nil )
178159}
179160
180- // DeleteUser deletes a user's credential.
181- func (sm * ServerManager ) DeleteUser (c * fiber.Ctx ) error {
182- ms := managedServerFromContext (c )
183- username := c .Params ("username" )
161+ func handleDeleteUser (w http.ResponseWriter , r * http.Request , ms * managedServer ) (int , error ) {
162+ username := r .PathValue ("username" )
184163 if err := ms .cms .DeleteCredential (username ); err != nil {
185- return c . Status ( fiber .StatusNotFound ). JSON ( & StandardError {Message : err .Error ()})
164+ return restapi . EncodeResponse ( w , http .StatusNotFound , StandardError {Message : err .Error ()})
186165 }
187- return c . SendStatus ( fiber .StatusNoContent )
166+ return restapi . EncodeResponse ( w , http .StatusNoContent , nil )
188167}
0 commit comments