separate frontend repository
This commit is contained in:
+343
@@ -0,0 +1,343 @@
|
||||
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,68 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"s-ui/database/model"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const (
|
||||
loginUser = "LOGIN_USER"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gob.Register(model.User{})
|
||||
}
|
||||
|
||||
func SetLoginUser(c *gin.Context, userName string, maxAge int) error {
|
||||
options := sessions.Options{
|
||||
Path: "/",
|
||||
Secure: false,
|
||||
}
|
||||
if maxAge > 0 {
|
||||
options.MaxAge = maxAge * 60
|
||||
}
|
||||
|
||||
s := sessions.Default(c)
|
||||
s.Set(loginUser, userName)
|
||||
s.Options(options)
|
||||
|
||||
return s.Save()
|
||||
}
|
||||
|
||||
func SetMaxAge(c *gin.Context) error {
|
||||
s := sessions.Default(c)
|
||||
s.Options(sessions.Options{
|
||||
Path: "/",
|
||||
})
|
||||
return s.Save()
|
||||
}
|
||||
|
||||
func GetLoginUser(c *gin.Context) string {
|
||||
s := sessions.Default(c)
|
||||
obj := s.Get(loginUser)
|
||||
if obj == nil {
|
||||
return ""
|
||||
}
|
||||
objStr, ok := obj.(string)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return objStr
|
||||
}
|
||||
|
||||
func IsLogin(c *gin.Context) bool {
|
||||
return GetLoginUser(c) != ""
|
||||
}
|
||||
|
||||
func ClearSession(c *gin.Context) {
|
||||
s := sessions.Default(c)
|
||||
s.Clear()
|
||||
s.Options(sessions.Options{
|
||||
Path: "/",
|
||||
MaxAge: -1,
|
||||
})
|
||||
s.Save()
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"s-ui/logger"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Msg struct {
|
||||
Success bool `json:"success"`
|
||||
Msg string `json:"msg"`
|
||||
Obj interface{} `json:"obj"`
|
||||
}
|
||||
|
||||
func getRemoteIp(c *gin.Context) string {
|
||||
value := c.GetHeader("X-Forwarded-For")
|
||||
if value != "" {
|
||||
ips := strings.Split(value, ",")
|
||||
return ips[0]
|
||||
} else {
|
||||
addr := c.Request.RemoteAddr
|
||||
ip, _, _ := net.SplitHostPort(addr)
|
||||
return ip
|
||||
}
|
||||
}
|
||||
|
||||
func getHostname(c *gin.Context) string {
|
||||
host := c.Request.Host
|
||||
if colonIndex := strings.LastIndex(host, ":"); colonIndex != -1 {
|
||||
host, _, _ = net.SplitHostPort(c.Request.Host)
|
||||
}
|
||||
return host
|
||||
}
|
||||
|
||||
func jsonMsg(c *gin.Context, msg string, err error) {
|
||||
jsonMsgObj(c, msg, nil, err)
|
||||
}
|
||||
|
||||
func jsonObj(c *gin.Context, obj interface{}, err error) {
|
||||
jsonMsgObj(c, "", obj, err)
|
||||
}
|
||||
|
||||
func jsonMsgObj(c *gin.Context, msg string, obj interface{}, err error) {
|
||||
m := Msg{
|
||||
Obj: obj,
|
||||
}
|
||||
if err == nil {
|
||||
m.Success = true
|
||||
if msg != "" {
|
||||
m.Msg = msg
|
||||
}
|
||||
} else {
|
||||
m.Success = false
|
||||
m.Msg = msg + ": " + err.Error()
|
||||
logger.Warning("failed :", err)
|
||||
}
|
||||
c.JSON(http.StatusOK, m)
|
||||
}
|
||||
|
||||
func pureJsonMsg(c *gin.Context, success bool, msg string) {
|
||||
if success {
|
||||
c.JSON(http.StatusOK, Msg{
|
||||
Success: true,
|
||||
Msg: msg,
|
||||
})
|
||||
} else {
|
||||
c.JSON(http.StatusOK, Msg{
|
||||
Success: false,
|
||||
Msg: msg,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func checkLogin(c *gin.Context) {
|
||||
if !IsLogin(c) {
|
||||
if c.GetHeader("X-Requested-With") == "XMLHttpRequest" {
|
||||
pureJsonMsg(c, false, "Invalid login")
|
||||
} else {
|
||||
c.Redirect(http.StatusTemporaryRedirect, "/login")
|
||||
}
|
||||
c.Abort()
|
||||
} else {
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user