123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- package ssmapi
- import (
- C "github.com/sagernet/sing-box/constant"
- "github.com/sagernet/sing/common"
- "net/http"
- "github.com/go-chi/chi/v5"
- "github.com/go-chi/render"
- )
- func (s *Server) setupRoutes(r chi.Router) {
- r.Group(func(r chi.Router) {
- r.Get("/", s.getServerInfo)
- r.Get("/nodes", s.getNodes)
- r.Post("/nodes", s.addNode)
- r.Get("/nodes/{id}", s.getNode)
- r.Put("/nodes/{id}", s.updateNode)
- r.Delete("/nodes/{id}", s.deleteNode)
- r.Get("/users", s.listUser)
- r.Post("/users", s.addUser)
- r.Get("/users/{username}", s.getUser)
- r.Put("/users/{username}", s.updateUser)
- r.Delete("/users/{username}", s.deleteUser)
- r.Get("/stats", s.getStats)
- })
- }
- func (s *Server) getServerInfo(writer http.ResponseWriter, request *http.Request) {
- render.JSON(writer, request, render.M{
- "server": "sing-box",
- "apiVersion": "v1",
- "_sing_box_version": C.Version,
- })
- }
- func (s *Server) getNodes(writer http.ResponseWriter, request *http.Request) {
- var response struct {
- Protocols []string `json:"protocols"`
- Shadowsocks []ShadowsocksNodeObject `json:"shadowsocks,omitempty"`
- }
- for _, node := range s.nodes {
- if !common.Contains(response.Protocols, node.Protocol()) {
- response.Protocols = append(response.Protocols, node.Protocol())
- }
- switch node.Protocol() {
- case C.TypeShadowsocks:
- response.Shadowsocks = append(response.Shadowsocks, node.Shadowsocks())
- }
- }
- render.JSON(writer, request, &response)
- }
- func (s *Server) addNode(writer http.ResponseWriter, request *http.Request) {
- writer.WriteHeader(http.StatusNotImplemented)
- }
- func (s *Server) getNode(writer http.ResponseWriter, request *http.Request) {
- nodeID := chi.URLParam(request, "id")
- if nodeID == "" {
- writer.WriteHeader(http.StatusBadRequest)
- return
- }
- for _, node := range s.nodes {
- if nodeID == node.ID() {
- render.JSON(writer, request, render.M{
- node.Protocol(): node.Object(),
- })
- return
- }
- }
- }
- func (s *Server) updateNode(writer http.ResponseWriter, request *http.Request) {
- writer.WriteHeader(http.StatusNotImplemented)
- }
- func (s *Server) deleteNode(writer http.ResponseWriter, request *http.Request) {
- writer.WriteHeader(http.StatusNotImplemented)
- }
- type SSMUserObject struct {
- UserName string `json:"username"`
- Password string `json:"uPSK,omitempty"`
- DownlinkBytes int64 `json:"downlinkBytes"`
- UplinkBytes int64 `json:"uplinkBytes"`
- DownlinkPackets int64 `json:"downlinkPackets"`
- UplinkPackets int64 `json:"uplinkPackets"`
- TCPSessions int64 `json:"tcpSessions"`
- UDPSessions int64 `json:"udpSessions"`
- }
- func (s *Server) listUser(writer http.ResponseWriter, request *http.Request) {
- render.JSON(writer, request, render.M{
- "users": s.userManager.List(),
- })
- }
- func (s *Server) addUser(writer http.ResponseWriter, request *http.Request) {
- var addRequest struct {
- UserName string `json:"username"`
- Password string `json:"uPSK"`
- }
- err := render.DecodeJSON(request.Body, &addRequest)
- if err != nil {
- render.Status(request, http.StatusBadRequest)
- render.PlainText(writer, request, err.Error())
- return
- }
- err = s.userManager.Add(addRequest.UserName, addRequest.Password)
- if err != nil {
- render.Status(request, http.StatusBadRequest)
- render.PlainText(writer, request, err.Error())
- return
- }
- writer.WriteHeader(http.StatusCreated)
- }
- func (s *Server) getUser(writer http.ResponseWriter, request *http.Request) {
- userName := chi.URLParam(request, "username")
- if userName == "" {
- writer.WriteHeader(http.StatusBadRequest)
- return
- }
- uPSK, loaded := s.userManager.Get(userName)
- if !loaded {
- writer.WriteHeader(http.StatusNotFound)
- return
- }
- user := SSMUserObject{
- UserName: userName,
- Password: uPSK,
- }
- s.trafficManager.ReadUser(&user)
- render.JSON(writer, request, user)
- }
- func (s *Server) updateUser(writer http.ResponseWriter, request *http.Request) {
- userName := chi.URLParam(request, "username")
- if userName == "" {
- writer.WriteHeader(http.StatusBadRequest)
- return
- }
- var updateRequest struct {
- Password string `json:"uPSK"`
- }
- err := render.DecodeJSON(request.Body, &updateRequest)
- if err != nil {
- render.Status(request, http.StatusBadRequest)
- render.PlainText(writer, request, err.Error())
- return
- }
- _, loaded := s.userManager.Get(userName)
- if !loaded {
- writer.WriteHeader(http.StatusNotFound)
- return
- }
- err = s.userManager.Update(userName, updateRequest.Password)
- if err != nil {
- render.Status(request, http.StatusBadRequest)
- render.PlainText(writer, request, err.Error())
- return
- }
- writer.WriteHeader(http.StatusNoContent)
- }
- func (s *Server) deleteUser(writer http.ResponseWriter, request *http.Request) {
- userName := chi.URLParam(request, "username")
- if userName == "" {
- writer.WriteHeader(http.StatusBadRequest)
- return
- }
- _, loaded := s.userManager.Get(userName)
- if !loaded {
- writer.WriteHeader(http.StatusNotFound)
- return
- }
- err := s.userManager.Delete(userName)
- if err != nil {
- render.Status(request, http.StatusBadRequest)
- render.PlainText(writer, request, err.Error())
- return
- }
- writer.WriteHeader(http.StatusNoContent)
- }
- func (s *Server) getStats(writer http.ResponseWriter, request *http.Request) {
- requireClear := chi.URLParam(request, "clear") == "true"
- users := s.userManager.List()
- s.trafficManager.ReadUsers(users)
- for i := range users {
- users[i].Password = ""
- }
- uplinkBytes, downlinkBytes, uplinkPackets, downlinkPackets, tcpSessions, udpSessions := s.trafficManager.ReadGlobal()
- if requireClear {
- s.trafficManager.Clear()
- }
- render.JSON(writer, request, render.M{
- "uplinkBytes": uplinkBytes,
- "downlinkBytes": downlinkBytes,
- "uplinkPackets": uplinkPackets,
- "downlinkPackets": downlinkPackets,
- "tcpSessions": tcpSessions,
- "udpSessions": udpSessions,
- "users": users,
- })
- }
|