new api with token
This commit is contained in:
-343
@@ -1,343 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"s-ui/database"
|
|
||||||
"s-ui/logger"
|
|
||||||
"s-ui/service"
|
|
||||||
"s-ui/util"
|
|
||||||
"s-ui/util/common"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
type APIHandler struct {
|
|
||||||
service.SettingService
|
|
||||||
service.UserService
|
|
||||||
service.ConfigService
|
|
||||||
service.ClientService
|
|
||||||
service.TlsService
|
|
||||||
service.InboundService
|
|
||||||
service.OutboundService
|
|
||||||
service.EndpointService
|
|
||||||
service.PanelService
|
|
||||||
service.StatsService
|
|
||||||
service.ServerService
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAPIHandler(g *gin.RouterGroup) {
|
|
||||||
a := &APIHandler{}
|
|
||||||
a.initRouter(g)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *APIHandler) initRouter(g *gin.RouterGroup) {
|
|
||||||
g.Use(func(c *gin.Context) {
|
|
||||||
path := c.Request.URL.Path
|
|
||||||
if !strings.HasSuffix(path, "login") && !strings.HasSuffix(path, "logout") {
|
|
||||||
checkLogin(c)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
g.POST("/:postAction", a.postHandler)
|
|
||||||
g.GET("/:getAction", a.getHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *APIHandler) postHandler(c *gin.Context) {
|
|
||||||
var err error
|
|
||||||
action := c.Param("postAction")
|
|
||||||
remoteIP := getRemoteIp(c)
|
|
||||||
loginUser := GetLoginUser(c)
|
|
||||||
hostname := getHostname(c)
|
|
||||||
|
|
||||||
switch action {
|
|
||||||
case "login":
|
|
||||||
loginUser, err := a.UserService.Login(c.Request.FormValue("user"), c.Request.FormValue("pass"), remoteIP)
|
|
||||||
if err != nil {
|
|
||||||
jsonMsg(c, "", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionMaxAge, err := a.SettingService.GetSessionMaxAge()
|
|
||||||
if err != nil {
|
|
||||||
logger.Infof("Unable to get session's max age from DB")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = SetLoginUser(c, loginUser, sessionMaxAge)
|
|
||||||
if err == nil {
|
|
||||||
logger.Info("user ", loginUser, " login success")
|
|
||||||
} else {
|
|
||||||
logger.Warning("login failed: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonMsg(c, "", nil)
|
|
||||||
case "changePass":
|
|
||||||
id := c.Request.FormValue("id")
|
|
||||||
oldPass := c.Request.FormValue("oldPass")
|
|
||||||
newUsername := c.Request.FormValue("newUsername")
|
|
||||||
newPass := c.Request.FormValue("newPass")
|
|
||||||
err = a.UserService.ChangePass(id, oldPass, newUsername, newPass)
|
|
||||||
if err == nil {
|
|
||||||
logger.Info("change user credentials success")
|
|
||||||
jsonMsg(c, "save", nil)
|
|
||||||
} else {
|
|
||||||
logger.Warning("change user credentials failed:", err)
|
|
||||||
jsonMsg(c, "", err)
|
|
||||||
}
|
|
||||||
case "save":
|
|
||||||
obj := c.Request.FormValue("object")
|
|
||||||
act := c.Request.FormValue("action")
|
|
||||||
data := c.Request.FormValue("data")
|
|
||||||
initUsers := c.Request.FormValue("initUsers")
|
|
||||||
objs, err := a.ConfigService.Save(obj, act, json.RawMessage(data), initUsers, loginUser, hostname)
|
|
||||||
if err != nil {
|
|
||||||
jsonMsg(c, "save", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = a.loadPartialData(c, objs)
|
|
||||||
if err != nil {
|
|
||||||
jsonMsg(c, obj, err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
case "restartApp":
|
|
||||||
err = a.PanelService.RestartPanel(3)
|
|
||||||
jsonMsg(c, "restartApp", err)
|
|
||||||
case "restartSb":
|
|
||||||
err = a.ConfigService.RestartCore()
|
|
||||||
jsonMsg(c, "restartSb", err)
|
|
||||||
case "linkConvert":
|
|
||||||
link := c.Request.FormValue("link")
|
|
||||||
result, _, err := util.GetOutbound(link, 0)
|
|
||||||
jsonObj(c, result, err)
|
|
||||||
case "importdb":
|
|
||||||
file, _, err := c.Request.FormFile("db")
|
|
||||||
if err != nil {
|
|
||||||
jsonMsg(c, "", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
err = database.ImportDB(file)
|
|
||||||
jsonMsg(c, "", err)
|
|
||||||
default:
|
|
||||||
jsonMsg(c, "failed", common.NewError("unknown action: ", action))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *APIHandler) getHandler(c *gin.Context) {
|
|
||||||
action := c.Param("getAction")
|
|
||||||
|
|
||||||
switch action {
|
|
||||||
case "logout":
|
|
||||||
loginUser := GetLoginUser(c)
|
|
||||||
if loginUser != "" {
|
|
||||||
logger.Infof("user %s logout", loginUser)
|
|
||||||
}
|
|
||||||
ClearSession(c)
|
|
||||||
jsonMsg(c, "", nil)
|
|
||||||
case "load":
|
|
||||||
data, err := a.loadData(c)
|
|
||||||
if err != nil {
|
|
||||||
jsonMsg(c, "", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
jsonObj(c, data, nil)
|
|
||||||
case "inbounds", "outbounds", "endpoints", "tls", "clients", "config":
|
|
||||||
err := a.loadPartialData(c, []string{action})
|
|
||||||
if err != nil {
|
|
||||||
jsonMsg(c, action, err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
case "users":
|
|
||||||
users, err := a.UserService.GetUsers()
|
|
||||||
if err != nil {
|
|
||||||
jsonMsg(c, "", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
jsonObj(c, *users, nil)
|
|
||||||
case "settings":
|
|
||||||
data, err := a.SettingService.GetAllSetting()
|
|
||||||
if err != nil {
|
|
||||||
jsonMsg(c, "", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
jsonObj(c, data, err)
|
|
||||||
case "stats":
|
|
||||||
resource := c.Query("resource")
|
|
||||||
tag := c.Query("tag")
|
|
||||||
limit, err := strconv.Atoi(c.Query("limit"))
|
|
||||||
if err != nil {
|
|
||||||
limit = 100
|
|
||||||
}
|
|
||||||
data, err := a.StatsService.GetStats(resource, tag, limit)
|
|
||||||
if err != nil {
|
|
||||||
jsonMsg(c, "", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
jsonObj(c, data, err)
|
|
||||||
case "status":
|
|
||||||
request := c.Query("r")
|
|
||||||
result := a.ServerService.GetStatus(request)
|
|
||||||
jsonObj(c, result, nil)
|
|
||||||
case "onlines":
|
|
||||||
onlines, err := a.StatsService.GetOnlines()
|
|
||||||
jsonObj(c, onlines, err)
|
|
||||||
case "logs":
|
|
||||||
count := c.Query("c")
|
|
||||||
level := c.Query("l")
|
|
||||||
logs := a.ServerService.GetLogs(count, level)
|
|
||||||
jsonObj(c, logs, nil)
|
|
||||||
case "changes":
|
|
||||||
actor := c.Query("a")
|
|
||||||
chngKey := c.Query("k")
|
|
||||||
count := c.Query("c")
|
|
||||||
changes := a.ConfigService.GetChanges(actor, chngKey, count)
|
|
||||||
jsonObj(c, changes, nil)
|
|
||||||
case "keypairs":
|
|
||||||
kType := c.Query("k")
|
|
||||||
options := c.Query("o")
|
|
||||||
keypair := a.ServerService.GenKeypair(kType, options)
|
|
||||||
jsonObj(c, keypair, nil)
|
|
||||||
case "getdb":
|
|
||||||
exclude := c.Query("exclude")
|
|
||||||
db, err := database.GetDb(exclude)
|
|
||||||
if err != nil {
|
|
||||||
jsonMsg(c, "", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.Header("Content-Type", "application/octet-stream")
|
|
||||||
c.Header("Content-Disposition", "attachment; filename=s-ui_"+time.Now().Format("20060102-150405")+".db")
|
|
||||||
c.Writer.Write(db)
|
|
||||||
default:
|
|
||||||
jsonMsg(c, "failed", common.NewError("unknown action: ", action))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *APIHandler) loadData(c *gin.Context) (interface{}, error) {
|
|
||||||
data := make(map[string]interface{}, 0)
|
|
||||||
lu := c.Query("lu")
|
|
||||||
isUpdated, err := a.ConfigService.CheckChanges(lu)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
onlines, err := a.StatsService.GetOnlines()
|
|
||||||
|
|
||||||
sysInfo := a.ServerService.GetSingboxInfo()
|
|
||||||
if sysInfo["running"] == false {
|
|
||||||
logs := a.ServerService.GetLogs("1", "debug")
|
|
||||||
if len(logs) > 0 {
|
|
||||||
data["lastLog"] = logs[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if isUpdated {
|
|
||||||
config, err := a.SettingService.GetConfig()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
clients, err := a.ClientService.GetAll()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
tlsConfigs, err := a.TlsService.GetAll()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
inbounds, err := a.InboundService.GetAll()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
outbounds, err := a.OutboundService.GetAll()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
endpoints, err := a.EndpointService.GetAll()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
subURI, err := a.SettingService.GetFinalSubURI(strings.Split(c.Request.Host, ":")[0])
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
data["config"] = json.RawMessage(config)
|
|
||||||
data["clients"] = clients
|
|
||||||
data["tls"] = tlsConfigs
|
|
||||||
data["inbounds"] = inbounds
|
|
||||||
data["outbounds"] = outbounds
|
|
||||||
data["endpoints"] = endpoints
|
|
||||||
data["subURI"] = subURI
|
|
||||||
data["onlines"] = onlines
|
|
||||||
} else {
|
|
||||||
data["onlines"] = onlines
|
|
||||||
}
|
|
||||||
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *APIHandler) loadPartialData(c *gin.Context, objs []string) error {
|
|
||||||
data := make(map[string]interface{}, 0)
|
|
||||||
id := c.Query("id")
|
|
||||||
|
|
||||||
for _, obj := range objs {
|
|
||||||
switch obj {
|
|
||||||
case "inbounds":
|
|
||||||
inbounds, err := a.InboundService.Get(id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data[obj] = inbounds
|
|
||||||
case "outbounds":
|
|
||||||
outbounds, err := a.OutboundService.GetAll()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data[obj] = outbounds
|
|
||||||
case "endpoints":
|
|
||||||
endpoints, err := a.EndpointService.GetAll()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data[obj] = endpoints
|
|
||||||
case "tls":
|
|
||||||
tlsConfigs, err := a.TlsService.GetAll()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data[obj] = tlsConfigs
|
|
||||||
case "clients":
|
|
||||||
clients, err := a.ClientService.Get(id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data[obj] = clients
|
|
||||||
case "config":
|
|
||||||
config, err := a.SettingService.GetConfig()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data[obj] = json.RawMessage(config)
|
|
||||||
case "settings":
|
|
||||||
settings, err := a.SettingService.GetAllSetting()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data[obj] = settings
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonObj(c, data, nil)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *APIHandler) postActions(c *gin.Context) (string, json.RawMessage, error) {
|
|
||||||
var data map[string]json.RawMessage
|
|
||||||
err := c.ShouldBind(&data)
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
return string(data["action"]), data["data"], nil
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"s-ui/util/common"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type APIHandler struct {
|
||||||
|
ApiService
|
||||||
|
apiv2 *APIv2Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAPIHandler(g *gin.RouterGroup, a2 *APIv2Handler) {
|
||||||
|
a := &APIHandler{
|
||||||
|
apiv2: a2,
|
||||||
|
}
|
||||||
|
a.initRouter(g)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *APIHandler) initRouter(g *gin.RouterGroup) {
|
||||||
|
g.Use(func(c *gin.Context) {
|
||||||
|
path := c.Request.URL.Path
|
||||||
|
if !strings.HasSuffix(path, "login") && !strings.HasSuffix(path, "logout") {
|
||||||
|
checkLogin(c)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
g.POST("/:postAction", a.postHandler)
|
||||||
|
g.GET("/:getAction", a.getHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *APIHandler) postHandler(c *gin.Context) {
|
||||||
|
loginUser := GetLoginUser(c)
|
||||||
|
action := c.Param("postAction")
|
||||||
|
|
||||||
|
switch action {
|
||||||
|
case "login":
|
||||||
|
a.ApiService.Login(c)
|
||||||
|
case "changePass":
|
||||||
|
a.ApiService.ChangePass(c)
|
||||||
|
case "save":
|
||||||
|
a.ApiService.Save(c, loginUser)
|
||||||
|
case "restartApp":
|
||||||
|
a.ApiService.RestartApp(c)
|
||||||
|
case "restartSb":
|
||||||
|
a.ApiService.RestartSb(c)
|
||||||
|
case "linkConvert":
|
||||||
|
a.ApiService.LinkConvert(c)
|
||||||
|
case "importdb":
|
||||||
|
a.ApiService.ImportDb(c)
|
||||||
|
case "addToken":
|
||||||
|
a.ApiService.AddToken(c)
|
||||||
|
a.apiv2.ReloadTokens()
|
||||||
|
case "deleteToken":
|
||||||
|
a.ApiService.DeleteToken(c)
|
||||||
|
a.apiv2.ReloadTokens()
|
||||||
|
default:
|
||||||
|
jsonMsg(c, "failed", common.NewError("unknown action: ", action))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *APIHandler) getHandler(c *gin.Context) {
|
||||||
|
action := c.Param("getAction")
|
||||||
|
|
||||||
|
switch action {
|
||||||
|
case "logout":
|
||||||
|
a.ApiService.Logout(c)
|
||||||
|
case "load":
|
||||||
|
a.ApiService.LoadData(c)
|
||||||
|
case "inbounds", "outbounds", "endpoints", "tls", "clients", "config":
|
||||||
|
err := a.ApiService.LoadPartialData(c, []string{action})
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(c, action, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case "users":
|
||||||
|
a.ApiService.GetUsers(c)
|
||||||
|
case "settings":
|
||||||
|
a.ApiService.GetSettings(c)
|
||||||
|
case "stats":
|
||||||
|
a.ApiService.GetStats(c)
|
||||||
|
case "status":
|
||||||
|
a.ApiService.GetStatus(c)
|
||||||
|
case "onlines":
|
||||||
|
a.ApiService.GetOnlines(c)
|
||||||
|
case "logs":
|
||||||
|
a.ApiService.GetLogs(c)
|
||||||
|
case "changes":
|
||||||
|
a.ApiService.CheckChanges(c)
|
||||||
|
case "keypairs":
|
||||||
|
a.ApiService.GetKeypairs(c)
|
||||||
|
case "getdb":
|
||||||
|
a.ApiService.GetDb(c)
|
||||||
|
case "tokens":
|
||||||
|
a.ApiService.GetTokens(c)
|
||||||
|
default:
|
||||||
|
jsonMsg(c, "failed", common.NewError("unknown action: ", action))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,363 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"s-ui/database"
|
||||||
|
"s-ui/logger"
|
||||||
|
"s-ui/service"
|
||||||
|
"s-ui/util"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ApiService struct {
|
||||||
|
service.SettingService
|
||||||
|
service.UserService
|
||||||
|
service.ConfigService
|
||||||
|
service.ClientService
|
||||||
|
service.TlsService
|
||||||
|
service.InboundService
|
||||||
|
service.OutboundService
|
||||||
|
service.EndpointService
|
||||||
|
service.PanelService
|
||||||
|
service.StatsService
|
||||||
|
service.ServerService
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) LoadData(c *gin.Context) {
|
||||||
|
data, err := a.getData(c)
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(c, "", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
jsonObj(c, data, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) getData(c *gin.Context) (interface{}, error) {
|
||||||
|
data := make(map[string]interface{}, 0)
|
||||||
|
lu := c.Query("lu")
|
||||||
|
isUpdated, err := a.ConfigService.CheckChanges(lu)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
onlines, err := a.StatsService.GetOnlines()
|
||||||
|
|
||||||
|
sysInfo := a.ServerService.GetSingboxInfo()
|
||||||
|
if sysInfo["running"] == false {
|
||||||
|
logs := a.ServerService.GetLogs("1", "debug")
|
||||||
|
if len(logs) > 0 {
|
||||||
|
data["lastLog"] = logs[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if isUpdated {
|
||||||
|
config, err := a.SettingService.GetConfig()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
clients, err := a.ClientService.GetAll()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
tlsConfigs, err := a.TlsService.GetAll()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
inbounds, err := a.InboundService.GetAll()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
outbounds, err := a.OutboundService.GetAll()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
endpoints, err := a.EndpointService.GetAll()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
subURI, err := a.SettingService.GetFinalSubURI(strings.Split(c.Request.Host, ":")[0])
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
data["config"] = json.RawMessage(config)
|
||||||
|
data["clients"] = clients
|
||||||
|
data["tls"] = tlsConfigs
|
||||||
|
data["inbounds"] = inbounds
|
||||||
|
data["outbounds"] = outbounds
|
||||||
|
data["endpoints"] = endpoints
|
||||||
|
data["subURI"] = subURI
|
||||||
|
data["onlines"] = onlines
|
||||||
|
} else {
|
||||||
|
data["onlines"] = onlines
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) LoadPartialData(c *gin.Context, objs []string) error {
|
||||||
|
data := make(map[string]interface{}, 0)
|
||||||
|
id := c.Query("id")
|
||||||
|
|
||||||
|
for _, obj := range objs {
|
||||||
|
switch obj {
|
||||||
|
case "inbounds":
|
||||||
|
inbounds, err := a.InboundService.Get(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data[obj] = inbounds
|
||||||
|
case "outbounds":
|
||||||
|
outbounds, err := a.OutboundService.GetAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data[obj] = outbounds
|
||||||
|
case "endpoints":
|
||||||
|
endpoints, err := a.EndpointService.GetAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data[obj] = endpoints
|
||||||
|
case "tls":
|
||||||
|
tlsConfigs, err := a.TlsService.GetAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data[obj] = tlsConfigs
|
||||||
|
case "clients":
|
||||||
|
clients, err := a.ClientService.Get(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data[obj] = clients
|
||||||
|
case "config":
|
||||||
|
config, err := a.SettingService.GetConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data[obj] = json.RawMessage(config)
|
||||||
|
case "settings":
|
||||||
|
settings, err := a.SettingService.GetAllSetting()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data[obj] = settings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonObj(c, data, nil)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) GetUsers(c *gin.Context) {
|
||||||
|
users, err := a.UserService.GetUsers()
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(c, "", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
jsonObj(c, *users, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) GetSettings(c *gin.Context) {
|
||||||
|
data, err := a.SettingService.GetAllSetting()
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(c, "", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
jsonObj(c, data, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) GetStats(c *gin.Context) {
|
||||||
|
resource := c.Query("resource")
|
||||||
|
tag := c.Query("tag")
|
||||||
|
limit, err := strconv.Atoi(c.Query("limit"))
|
||||||
|
if err != nil {
|
||||||
|
limit = 100
|
||||||
|
}
|
||||||
|
data, err := a.StatsService.GetStats(resource, tag, limit)
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(c, "", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
jsonObj(c, data, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) GetStatus(c *gin.Context) {
|
||||||
|
request := c.Query("r")
|
||||||
|
result := a.ServerService.GetStatus(request)
|
||||||
|
jsonObj(c, result, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) GetOnlines(c *gin.Context) {
|
||||||
|
onlines, err := a.StatsService.GetOnlines()
|
||||||
|
jsonObj(c, onlines, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) GetLogs(c *gin.Context) {
|
||||||
|
count := c.Query("c")
|
||||||
|
level := c.Query("l")
|
||||||
|
logs := a.ServerService.GetLogs(count, level)
|
||||||
|
jsonObj(c, logs, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) CheckChanges(c *gin.Context) {
|
||||||
|
actor := c.Query("a")
|
||||||
|
chngKey := c.Query("k")
|
||||||
|
count := c.Query("c")
|
||||||
|
changes := a.ConfigService.GetChanges(actor, chngKey, count)
|
||||||
|
jsonObj(c, changes, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) GetKeypairs(c *gin.Context) {
|
||||||
|
kType := c.Query("k")
|
||||||
|
options := c.Query("o")
|
||||||
|
keypair := a.ServerService.GenKeypair(kType, options)
|
||||||
|
jsonObj(c, keypair, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) GetDb(c *gin.Context) {
|
||||||
|
exclude := c.Query("exclude")
|
||||||
|
db, err := database.GetDb(exclude)
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(c, "", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Header("Content-Type", "application/octet-stream")
|
||||||
|
c.Header("Content-Disposition", "attachment; filename=s-ui_"+time.Now().Format("20060102-150405")+".db")
|
||||||
|
c.Writer.Write(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) postActions(c *gin.Context) (string, json.RawMessage, error) {
|
||||||
|
var data map[string]json.RawMessage
|
||||||
|
err := c.ShouldBind(&data)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
return string(data["action"]), data["data"], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) Login(c *gin.Context) {
|
||||||
|
remoteIP := getRemoteIp(c)
|
||||||
|
loginUser, err := a.UserService.Login(c.Request.FormValue("user"), c.Request.FormValue("pass"), remoteIP)
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(c, "", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionMaxAge, err := a.SettingService.GetSessionMaxAge()
|
||||||
|
if err != nil {
|
||||||
|
logger.Infof("Unable to get session's max age from DB")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = SetLoginUser(c, loginUser, sessionMaxAge)
|
||||||
|
if err == nil {
|
||||||
|
logger.Info("user ", loginUser, " login success")
|
||||||
|
} else {
|
||||||
|
logger.Warning("login failed: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonMsg(c, "", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) ChangePass(c *gin.Context) {
|
||||||
|
id := c.Request.FormValue("id")
|
||||||
|
oldPass := c.Request.FormValue("oldPass")
|
||||||
|
newUsername := c.Request.FormValue("newUsername")
|
||||||
|
newPass := c.Request.FormValue("newPass")
|
||||||
|
err := a.UserService.ChangePass(id, oldPass, newUsername, newPass)
|
||||||
|
if err == nil {
|
||||||
|
logger.Info("change user credentials success")
|
||||||
|
jsonMsg(c, "save", nil)
|
||||||
|
} else {
|
||||||
|
logger.Warning("change user credentials failed:", err)
|
||||||
|
jsonMsg(c, "", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) Save(c *gin.Context, loginUser string) {
|
||||||
|
hostname := getHostname(c)
|
||||||
|
obj := c.Request.FormValue("object")
|
||||||
|
act := c.Request.FormValue("action")
|
||||||
|
data := c.Request.FormValue("data")
|
||||||
|
initUsers := c.Request.FormValue("initUsers")
|
||||||
|
objs, err := a.ConfigService.Save(obj, act, json.RawMessage(data), initUsers, loginUser, hostname)
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(c, "save", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = a.LoadPartialData(c, objs)
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(c, obj, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) RestartApp(c *gin.Context) {
|
||||||
|
err := a.PanelService.RestartPanel(3)
|
||||||
|
jsonMsg(c, "restartApp", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) RestartSb(c *gin.Context) {
|
||||||
|
err := a.ConfigService.RestartCore()
|
||||||
|
jsonMsg(c, "restartSb", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) LinkConvert(c *gin.Context) {
|
||||||
|
link := c.Request.FormValue("link")
|
||||||
|
result, _, err := util.GetOutbound(link, 0)
|
||||||
|
jsonObj(c, result, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) ImportDb(c *gin.Context) {
|
||||||
|
file, _, err := c.Request.FormFile("db")
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(c, "", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
err = database.ImportDB(file)
|
||||||
|
jsonMsg(c, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) Logout(c *gin.Context) {
|
||||||
|
loginUser := GetLoginUser(c)
|
||||||
|
if loginUser != "" {
|
||||||
|
logger.Infof("user %s logout", loginUser)
|
||||||
|
}
|
||||||
|
ClearSession(c)
|
||||||
|
jsonMsg(c, "", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) LoadTokens() ([]byte, error) {
|
||||||
|
return a.UserService.LoadTokens()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) GetTokens(c *gin.Context) {
|
||||||
|
loginUser := GetLoginUser(c)
|
||||||
|
tokens, err := a.UserService.GetUserTokens(loginUser)
|
||||||
|
jsonObj(c, tokens, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) AddToken(c *gin.Context) {
|
||||||
|
loginUser := GetLoginUser(c)
|
||||||
|
expiry := c.Request.FormValue("expiry")
|
||||||
|
expiryInt, err := strconv.ParseInt(expiry, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(c, "", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
desc := c.Request.FormValue("desc")
|
||||||
|
token, err := a.UserService.AddToken(loginUser, expiryInt, desc)
|
||||||
|
jsonObj(c, token, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiService) DeleteToken(c *gin.Context) {
|
||||||
|
tokenId := c.Request.FormValue("id")
|
||||||
|
err := a.UserService.DeleteToken(tokenId)
|
||||||
|
jsonMsg(c, "", err)
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"s-ui/logger"
|
||||||
|
"s-ui/util/common"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TokenInMemory struct {
|
||||||
|
Token string
|
||||||
|
Expiry int64
|
||||||
|
Username string
|
||||||
|
}
|
||||||
|
|
||||||
|
type APIv2Handler struct {
|
||||||
|
ApiService
|
||||||
|
tokens *[]TokenInMemory
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAPIv2Handler(g *gin.RouterGroup) *APIv2Handler {
|
||||||
|
a := &APIv2Handler{}
|
||||||
|
a.ReloadTokens()
|
||||||
|
a.initRouter(g)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *APIv2Handler) initRouter(g *gin.RouterGroup) {
|
||||||
|
g.Use(func(c *gin.Context) {
|
||||||
|
a.checkToken(c)
|
||||||
|
})
|
||||||
|
g.POST("/:postAction", a.postHandler)
|
||||||
|
g.GET("/:getAction", a.getHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *APIv2Handler) postHandler(c *gin.Context) {
|
||||||
|
username := a.findUsername(c)
|
||||||
|
action := c.Param("postAction")
|
||||||
|
|
||||||
|
switch action {
|
||||||
|
case "save":
|
||||||
|
a.ApiService.Save(c, username)
|
||||||
|
case "restartApp":
|
||||||
|
a.ApiService.RestartApp(c)
|
||||||
|
case "restartSb":
|
||||||
|
a.ApiService.RestartSb(c)
|
||||||
|
case "linkConvert":
|
||||||
|
a.ApiService.LinkConvert(c)
|
||||||
|
case "importdb":
|
||||||
|
a.ApiService.ImportDb(c)
|
||||||
|
default:
|
||||||
|
jsonMsg(c, "failed", common.NewError("unknown action: ", action))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *APIv2Handler) getHandler(c *gin.Context) {
|
||||||
|
action := c.Param("getAction")
|
||||||
|
|
||||||
|
switch action {
|
||||||
|
case "load":
|
||||||
|
a.ApiService.LoadData(c)
|
||||||
|
case "inbounds", "outbounds", "endpoints", "tls", "clients", "config":
|
||||||
|
err := a.ApiService.LoadPartialData(c, []string{action})
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(c, action, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case "users":
|
||||||
|
a.ApiService.GetUsers(c)
|
||||||
|
case "settings":
|
||||||
|
a.ApiService.GetSettings(c)
|
||||||
|
case "stats":
|
||||||
|
a.ApiService.GetStats(c)
|
||||||
|
case "status":
|
||||||
|
a.ApiService.GetStatus(c)
|
||||||
|
case "onlines":
|
||||||
|
a.ApiService.GetOnlines(c)
|
||||||
|
case "logs":
|
||||||
|
a.ApiService.GetLogs(c)
|
||||||
|
case "changes":
|
||||||
|
a.ApiService.CheckChanges(c)
|
||||||
|
case "keypairs":
|
||||||
|
a.ApiService.GetKeypairs(c)
|
||||||
|
case "getdb":
|
||||||
|
a.ApiService.GetDb(c)
|
||||||
|
default:
|
||||||
|
jsonMsg(c, "failed", common.NewError("unknown action: ", action))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *APIv2Handler) findUsername(c *gin.Context) string {
|
||||||
|
token := c.Request.Header.Get("Token")
|
||||||
|
for index, t := range *a.tokens {
|
||||||
|
if t.Expiry > 0 && t.Expiry < time.Now().Unix() {
|
||||||
|
(*a.tokens) = append((*a.tokens)[:index], (*a.tokens)[index+1:]...)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if t.Token == token {
|
||||||
|
return t.Username
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *APIv2Handler) checkToken(c *gin.Context) {
|
||||||
|
username := a.findUsername(c)
|
||||||
|
if username != "" {
|
||||||
|
c.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
jsonMsg(c, "", common.NewError("invalid token"))
|
||||||
|
c.Abort()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *APIv2Handler) ReloadTokens() {
|
||||||
|
tokens, err := a.ApiService.LoadTokens()
|
||||||
|
if err == nil {
|
||||||
|
var newTokens []TokenInMemory
|
||||||
|
err = json.Unmarshal(tokens, &newTokens)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("unable to load tokens: ", err)
|
||||||
|
}
|
||||||
|
a.tokens = &newTokens
|
||||||
|
} else {
|
||||||
|
logger.Error("unable to load tokens: ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -79,6 +79,7 @@ func InitDB(dbPath string) error {
|
|||||||
&model.Outbound{},
|
&model.Outbound{},
|
||||||
&model.Endpoint{},
|
&model.Endpoint{},
|
||||||
&model.User{},
|
&model.User{},
|
||||||
|
&model.Tokens{},
|
||||||
&model.Stats{},
|
&model.Stats{},
|
||||||
&model.Client{},
|
&model.Client{},
|
||||||
&model.Changes{},
|
&model.Changes{},
|
||||||
|
|||||||
@@ -54,3 +54,12 @@ type Changes struct {
|
|||||||
Action string `json:"action"`
|
Action string `json:"action"`
|
||||||
Obj json.RawMessage `json:"obj"`
|
Obj json.RawMessage `json:"obj"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Tokens struct {
|
||||||
|
Id uint `json:"id" form:"id" gorm:"primaryKey;autoIncrement"`
|
||||||
|
Desc string `json:"desc" form:"desc"`
|
||||||
|
Token string `json:"token" form:"token"`
|
||||||
|
Expiry int64 `json:"expiry" form:"expiry"`
|
||||||
|
UserId uint `json:"userId" form:"userId"`
|
||||||
|
User *User `json:"user" gorm:"foreignKey:UserId;references:Id"`
|
||||||
|
}
|
||||||
|
|||||||
+7
-1
@@ -146,7 +146,10 @@ func (s *ServerService) GetSystemInfo() map[string]interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServerService) GetLogs(count string, level string) []string {
|
func (s *ServerService) GetLogs(count string, level string) []string {
|
||||||
c, _ := strconv.Atoi(count)
|
c, err := strconv.Atoi(count)
|
||||||
|
if err != nil {
|
||||||
|
c = 10
|
||||||
|
}
|
||||||
return logger.GetLogs(c, level)
|
return logger.GetLogs(c, level)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,6 +174,9 @@ func (s *ServerService) GenKeypair(keyType string, options string) []string {
|
|||||||
|
|
||||||
func (s *ServerService) generateECHKeyPair(options string) []string {
|
func (s *ServerService) generateECHKeyPair(options string) []string {
|
||||||
parts := strings.Split(options, ",")
|
parts := strings.Split(options, ",")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return []string{"Failed to generate ECH keypair: ", "invalid options"}
|
||||||
|
}
|
||||||
configPem, keyPem, err := tls.ECHKeygenDefault(parts[0], parts[1] == "true")
|
configPem, keyPem, err := tls.ECHKeygenDefault(parts[0], parts[1] == "true")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{"Failed to generate ECH keypair: ", err.Error()}
|
return []string{"Failed to generate ECH keypair: ", err.Error()}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"s-ui/database"
|
"s-ui/database"
|
||||||
"s-ui/database/model"
|
"s-ui/database/model"
|
||||||
"s-ui/logger"
|
"s-ui/logger"
|
||||||
@@ -99,3 +100,61 @@ func (s *UserService) ChangePass(id string, oldPass string, newUser string, newP
|
|||||||
user.Password = newPass
|
user.Password = newPass
|
||||||
return db.Save(user).Error
|
return db.Save(user).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *UserService) LoadTokens() ([]byte, error) {
|
||||||
|
db := database.GetDB()
|
||||||
|
var tokens []model.Tokens
|
||||||
|
err := db.Model(model.Tokens{}).Preload("User").Where("expiry == 0 or expiry > ?", time.Now().Unix()).Find(&tokens).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var result []map[string]interface{}
|
||||||
|
for _, t := range tokens {
|
||||||
|
result = append(result, map[string]interface{}{
|
||||||
|
"token": t.Token,
|
||||||
|
"expiry": t.Expiry,
|
||||||
|
"username": t.User.Username,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
jsonResult, _ := json.MarshalIndent(result, "", " ")
|
||||||
|
return jsonResult, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UserService) GetUserTokens(username string) (*[]model.Tokens, error) {
|
||||||
|
db := database.GetDB()
|
||||||
|
var token []model.Tokens
|
||||||
|
err := db.Model(model.Tokens{}).Select("id,desc,'****' as token,expiry,user_id").Where("user_id = (select id from users where username = ?)", username).Find(&token).Error
|
||||||
|
if err != nil && !database.IsNotFound(err) {
|
||||||
|
println(err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UserService) AddToken(username string, expiry int64, desc string) (string, error) {
|
||||||
|
db := database.GetDB()
|
||||||
|
var userId uint
|
||||||
|
err := db.Model(model.User{}).Where("username = ?", username).Select("id").Scan(&userId).Error
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if expiry > 0 {
|
||||||
|
expiry = expiry*86400 + time.Now().Unix()
|
||||||
|
}
|
||||||
|
token := &model.Tokens{
|
||||||
|
Token: common.Random(32),
|
||||||
|
Desc: desc,
|
||||||
|
Expiry: expiry,
|
||||||
|
UserId: userId,
|
||||||
|
}
|
||||||
|
err = db.Create(token).Error
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return token.Token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UserService) DeleteToken(id string) error {
|
||||||
|
db := database.GetDB()
|
||||||
|
return db.Model(model.Tokens{}).Where("id = ?", id).Delete(&model.Tokens{}).Error
|
||||||
|
}
|
||||||
|
|||||||
+4
-1
@@ -102,8 +102,11 @@ func (s *Server) initRouter() (*gin.Engine, error) {
|
|||||||
|
|
||||||
engine.StaticFS(assetsBasePath, http.FS(assetsFS))
|
engine.StaticFS(assetsBasePath, http.FS(assetsFS))
|
||||||
|
|
||||||
|
group_apiv2 := engine.Group(base_url + "apiv2")
|
||||||
|
apiv2 := api.NewAPIv2Handler(group_apiv2)
|
||||||
|
|
||||||
group_api := engine.Group(base_url + "api")
|
group_api := engine.Group(base_url + "api")
|
||||||
api.NewAPIHandler(group_api)
|
api.NewAPIHandler(group_api, apiv2)
|
||||||
|
|
||||||
// Serve index.html as the entry point
|
// Serve index.html as the entry point
|
||||||
// Handle all other routes by serving index.html
|
// Handle all other routes by serving index.html
|
||||||
|
|||||||
Reference in New Issue
Block a user