mirror of
https://github.com/VaalaCat/frp-panel.git
synced 2025-09-26 19:31:18 +08:00
feat: rbac and temp node
This commit is contained in:
@@ -1,13 +1,14 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/VaalaCat/frp-panel/conf"
|
"github.com/VaalaCat/frp-panel/conf"
|
||||||
|
"github.com/VaalaCat/frp-panel/defs"
|
||||||
"github.com/VaalaCat/frp-panel/middleware"
|
"github.com/VaalaCat/frp-panel/middleware"
|
||||||
|
"github.com/VaalaCat/frp-panel/models"
|
||||||
"github.com/VaalaCat/frp-panel/pb"
|
"github.com/VaalaCat/frp-panel/pb"
|
||||||
"github.com/VaalaCat/frp-panel/services/app"
|
"github.com/VaalaCat/frp-panel/services/app"
|
||||||
"github.com/VaalaCat/frp-panel/services/dao"
|
"github.com/VaalaCat/frp-panel/services/dao"
|
||||||
|
"github.com/VaalaCat/frp-panel/utils/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
func LoginHandler(ctx *app.Context, req *pb.LoginRequest) (*pb.LoginResponse, error) {
|
func LoginHandler(ctx *app.Context, req *pb.LoginRequest) (*pb.LoginResponse, error) {
|
||||||
@@ -24,7 +25,28 @@ func LoginHandler(ctx *app.Context, req *pb.LoginRequest) (*pb.LoginResponse, er
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenStr := conf.GetCommonJWT(ctx.GetApp().GetConfig(), fmt.Sprint(user.GetUserID()))
|
userCount, err := dao.NewQuery(ctx).AdminCountUsers()
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger(ctx).WithError(err).Error("get user count failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if userCount == 1 && user.GetSafeUserInfo().Role != defs.UserRole_Admin {
|
||||||
|
userEntity, ok := user.(models.User)
|
||||||
|
if !ok {
|
||||||
|
logger.Logger(ctx).Errorf("trans user entity failed, invalid user entity")
|
||||||
|
return &pb.LoginResponse{
|
||||||
|
Status: &pb.Status{Code: pb.RespCode_RESP_CODE_INVALID, Message: "invalid user"},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
userEntity.Role = defs.UserRole_Admin
|
||||||
|
|
||||||
|
dao.NewQuery(ctx).AdminUpdateUser(&models.UserEntity{
|
||||||
|
UserID: user.GetUserID(),
|
||||||
|
}, userEntity.UserEntity)
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenStr := conf.GetJWTWithAllPermission(ctx.GetApp().GetConfig(), user.GetUserID())
|
||||||
|
|
||||||
ginCtx := ctx.GetGinCtx()
|
ginCtx := ctx.GetGinCtx()
|
||||||
middleware.PushTokenStr(ginCtx, ctx.GetApp(), tokenStr)
|
middleware.PushTokenStr(ginCtx, ctx.GetApp(), tokenStr)
|
||||||
|
@@ -3,6 +3,7 @@ package auth
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/VaalaCat/frp-panel/defs"
|
||||||
"github.com/VaalaCat/frp-panel/models"
|
"github.com/VaalaCat/frp-panel/models"
|
||||||
"github.com/VaalaCat/frp-panel/pb"
|
"github.com/VaalaCat/frp-panel/pb"
|
||||||
"github.com/VaalaCat/frp-panel/services/app"
|
"github.com/VaalaCat/frp-panel/services/app"
|
||||||
@@ -47,10 +48,14 @@ func RegisterHandler(c *app.Context, req *pb.RegisterRequest) (*pb.RegisterRespo
|
|||||||
Password: hashedPassword,
|
Password: hashedPassword,
|
||||||
Email: email,
|
Email: email,
|
||||||
Status: models.STATUS_NORMAL,
|
Status: models.STATUS_NORMAL,
|
||||||
Role: models.ROLE_NORMAL,
|
Role: defs.UserRole_Normal,
|
||||||
Token: uuid.New().String(),
|
Token: uuid.New().String(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if userCount == 0 {
|
||||||
|
newUser.Role = defs.UserRole_Admin
|
||||||
|
}
|
||||||
|
|
||||||
err = dao.NewQuery(c).CreateUser(newUser)
|
err = dao.NewQuery(c).CreateUser(newUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &pb.RegisterResponse{
|
return &pb.RegisterResponse{
|
||||||
|
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/VaalaCat/frp-panel/services/app"
|
"github.com/VaalaCat/frp-panel/services/app"
|
||||||
"github.com/VaalaCat/frp-panel/services/dao"
|
"github.com/VaalaCat/frp-panel/services/dao"
|
||||||
"github.com/VaalaCat/frp-panel/utils"
|
"github.com/VaalaCat/frp-panel/utils"
|
||||||
|
"github.com/VaalaCat/frp-panel/utils/logger"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,6 +29,8 @@ func InitClientHandler(c *app.Context, req *pb.InitClientRequest) (*pb.InitClien
|
|||||||
|
|
||||||
globalClientID := app.GlobalClientID(userInfo.GetUserName(), "c", userClientID)
|
globalClientID := app.GlobalClientID(userInfo.GetUserName(), "c", userClientID)
|
||||||
|
|
||||||
|
logger.Logger(c).Infof("start to init client, request:[%s], transformed global client id:[%s]", req.String(), globalClientID)
|
||||||
|
|
||||||
if err := dao.NewQuery(c).CreateClient(userInfo,
|
if err := dao.NewQuery(c).CreateClient(userInfo,
|
||||||
&models.ClientEntity{
|
&models.ClientEntity{
|
||||||
ClientID: globalClientID,
|
ClientID: globalClientID,
|
||||||
@@ -35,6 +38,7 @@ func InitClientHandler(c *app.Context, req *pb.InitClientRequest) (*pb.InitClien
|
|||||||
UserID: userInfo.GetUserID(),
|
UserID: userInfo.GetUserID(),
|
||||||
ConnectSecret: uuid.New().String(),
|
ConnectSecret: uuid.New().String(),
|
||||||
IsShadow: true,
|
IsShadow: true,
|
||||||
|
Ephemeral: req.GetEphemeral(),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return &pb.InitClientResponse{Status: &pb.Status{Code: pb.RespCode_RESP_CODE_INVALID, Message: err.Error()}}, err
|
return &pb.InitClientResponse{Status: &pb.Status{Code: pb.RespCode_RESP_CODE_INVALID, Message: err.Error()}}, err
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/VaalaCat/frp-panel/common"
|
"github.com/VaalaCat/frp-panel/common"
|
||||||
"github.com/VaalaCat/frp-panel/models"
|
"github.com/VaalaCat/frp-panel/models"
|
||||||
"github.com/VaalaCat/frp-panel/pb"
|
"github.com/VaalaCat/frp-panel/pb"
|
||||||
@@ -31,6 +33,10 @@ func GetClientHandler(ctx *app.Context, req *pb.GetClientRequest) (*pb.GetClient
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(clientID, ".") {
|
||||||
|
clientID = app.GlobalClientID(userInfo.GetUserName(), "c", clientID)
|
||||||
|
}
|
||||||
|
|
||||||
respCli := &pb.Client{}
|
respCli := &pb.Client{}
|
||||||
if len(serverID) == 0 {
|
if len(serverID) == 0 {
|
||||||
client, err := dao.NewQuery(ctx).GetClientByClientID(userInfo, clientID)
|
client, err := dao.NewQuery(ctx).GetClientByClientID(userInfo, clientID)
|
||||||
@@ -50,6 +56,8 @@ func GetClientHandler(ctx *app.Context, req *pb.GetClientRequest) (*pb.GetClient
|
|||||||
Stopped: lo.ToPtr(client.Stopped),
|
Stopped: lo.ToPtr(client.Stopped),
|
||||||
Comment: lo.ToPtr(client.Comment),
|
Comment: lo.ToPtr(client.Comment),
|
||||||
ClientIds: clientIDs,
|
ClientIds: clientIDs,
|
||||||
|
Ephemeral: &client.Ephemeral,
|
||||||
|
// LastSeenAt: lo.ToPtr(client.LastSeenAt.UnixMilli()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
client, err := dao.NewQuery(ctx).GetClientByFilter(userInfo, &models.ClientEntity{
|
client, err := dao.NewQuery(ctx).GetClientByFilter(userInfo, &models.ClientEntity{
|
||||||
@@ -73,9 +81,13 @@ func GetClientHandler(ctx *app.Context, req *pb.GetClientRequest) (*pb.GetClient
|
|||||||
ServerId: lo.ToPtr(client.ServerID),
|
ServerId: lo.ToPtr(client.ServerID),
|
||||||
Stopped: lo.ToPtr(client.Stopped),
|
Stopped: lo.ToPtr(client.Stopped),
|
||||||
Comment: lo.ToPtr(client.Comment),
|
Comment: lo.ToPtr(client.Comment),
|
||||||
FrpsUrl: lo.ToPtr(client.FRPsUrl),
|
FrpsUrl: lo.ToPtr(client.FrpsUrl),
|
||||||
|
Ephemeral: &client.Ephemeral,
|
||||||
ClientIds: nil,
|
ClientIds: nil,
|
||||||
}
|
}
|
||||||
|
if client.LastSeenAt != nil {
|
||||||
|
respCli.LastSeenAt = lo.ToPtr(client.LastSeenAt.UnixMilli())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &pb.GetClientResponse{
|
return &pb.GetClientResponse{
|
||||||
|
@@ -59,7 +59,7 @@ func ListClientsHandler(ctx *app.Context, req *pb.ListClientsRequest) (*pb.ListC
|
|||||||
logger.Logger(ctx).Errorf("get client ids in shadow by client id error: %v", err)
|
logger.Logger(ctx).Errorf("get client ids in shadow by client id error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &pb.Client{
|
respCli := &pb.Client{
|
||||||
Id: lo.ToPtr(c.ClientID),
|
Id: lo.ToPtr(c.ClientID),
|
||||||
Secret: lo.ToPtr(c.ConnectSecret),
|
Secret: lo.ToPtr(c.ConnectSecret),
|
||||||
Config: lo.ToPtr(string(c.ConfigContent)),
|
Config: lo.ToPtr(string(c.ConfigContent)),
|
||||||
@@ -67,7 +67,12 @@ func ListClientsHandler(ctx *app.Context, req *pb.ListClientsRequest) (*pb.ListC
|
|||||||
Stopped: lo.ToPtr(c.Stopped),
|
Stopped: lo.ToPtr(c.Stopped),
|
||||||
Comment: lo.ToPtr(c.Comment),
|
Comment: lo.ToPtr(c.Comment),
|
||||||
ClientIds: clientIDs,
|
ClientIds: clientIDs,
|
||||||
|
Ephemeral: lo.ToPtr(c.Ephemeral),
|
||||||
}
|
}
|
||||||
|
if c.LastSeenAt != nil {
|
||||||
|
respCli.LastSeenAt = lo.ToPtr(c.LastSeenAt.UnixMilli())
|
||||||
|
}
|
||||||
|
return respCli
|
||||||
})
|
})
|
||||||
|
|
||||||
return &pb.ListClientsResponse{
|
return &pb.ListClientsResponse{
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/VaalaCat/frp-panel/models"
|
"github.com/VaalaCat/frp-panel/models"
|
||||||
"github.com/VaalaCat/frp-panel/pb"
|
"github.com/VaalaCat/frp-panel/pb"
|
||||||
"github.com/VaalaCat/frp-panel/services/app"
|
"github.com/VaalaCat/frp-panel/services/app"
|
||||||
@@ -23,6 +21,11 @@ func RPCPullConfig(ctx *app.Context, req *pb.PullClientConfigReq) (*pb.PullClien
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := dao.NewQuery(ctx).AdminUpdateClientLastSeen(cli.ClientID); err != nil {
|
||||||
|
logger.Logger(ctx).WithError(err).Errorf("update client last_seen_at time error, req:[%s] clientId:[%s]",
|
||||||
|
req.String(), cli.ClientID)
|
||||||
|
}
|
||||||
|
|
||||||
if cli.IsShadow {
|
if cli.IsShadow {
|
||||||
proxies, err := dao.NewQuery(ctx).AdminListProxyConfigsWithFilters(&models.ProxyConfigEntity{
|
proxies, err := dao.NewQuery(ctx).AdminListProxyConfigsWithFilters(&models.ProxyConfigEntity{
|
||||||
OriginClientID: cli.ClientID,
|
OriginClientID: cli.ClientID,
|
||||||
@@ -34,7 +37,12 @@ func RPCPullConfig(ctx *app.Context, req *pb.PullClientConfigReq) (*pb.PullClien
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cli.Stopped && cli.IsShadow {
|
if cli.Stopped && cli.IsShadow {
|
||||||
return nil, fmt.Errorf("client is stopped")
|
return &pb.PullClientConfigResp{
|
||||||
|
Client: &pb.Client{
|
||||||
|
Id: lo.ToPtr(cli.ClientID),
|
||||||
|
Stopped: lo.ToPtr(true),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return &pb.PullClientConfigResp{
|
return &pb.PullClientConfigResp{
|
||||||
|
@@ -102,7 +102,7 @@ func UpdateFrpcHander(c *app.Context, req *pb.UpdateFRPCRequest) (*pb.UpdateFRPC
|
|||||||
cliCfg.ServerPort = srvConf.BindPort
|
cliCfg.ServerPort = srvConf.BindPort
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(req.GetFrpsUrl()) > 0 || len(cli.FRPsUrl) > 0 {
|
if len(req.GetFrpsUrl()) > 0 || len(cli.FrpsUrl) > 0 {
|
||||||
// 有一个有就需要覆盖,优先请求的url
|
// 有一个有就需要覆盖,优先请求的url
|
||||||
var (
|
var (
|
||||||
parsedFrpsUrl *url.URL
|
parsedFrpsUrl *url.URL
|
||||||
@@ -120,21 +120,21 @@ func UpdateFrpcHander(c *app.Context, req *pb.UpdateFRPCRequest) (*pb.UpdateFRPC
|
|||||||
urlToParse = req.GetFrpsUrl()
|
urlToParse = req.GetFrpsUrl()
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(cli.FRPsUrl) > 0 && parsedFrpsUrl == nil {
|
if len(cli.FrpsUrl) > 0 && parsedFrpsUrl == nil {
|
||||||
parsedFrpsUrl, err = ValidateFrpsUrl(cli.FRPsUrl)
|
parsedFrpsUrl, err = ValidateFrpsUrl(cli.FrpsUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Logger(c).WithError(err).Errorf("invalid old frps url, url: [%s]", cli.FRPsUrl)
|
logger.Logger(c).WithError(err).Errorf("invalid old frps url, url: [%s]", cli.FrpsUrl)
|
||||||
return &pb.UpdateFRPCResponse{
|
return &pb.UpdateFRPCResponse{
|
||||||
Status: &pb.Status{Code: pb.RespCode_RESP_CODE_INVALID, Message: err.Error()},
|
Status: &pb.Status{Code: pb.RespCode_RESP_CODE_INVALID, Message: err.Error()},
|
||||||
}, err
|
}, err
|
||||||
}
|
}
|
||||||
urlToParse = cli.FRPsUrl
|
urlToParse = cli.FrpsUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
cliCfg.ServerAddr = parsedFrpsUrl.Hostname()
|
cliCfg.ServerAddr = parsedFrpsUrl.Hostname()
|
||||||
cliCfg.ServerPort = cast.ToInt(parsedFrpsUrl.Port())
|
cliCfg.ServerPort = cast.ToInt(parsedFrpsUrl.Port())
|
||||||
cliCfg.Transport.Protocol = parsedFrpsUrl.Scheme
|
cliCfg.Transport.Protocol = parsedFrpsUrl.Scheme
|
||||||
cli.FRPsUrl = urlToParse
|
cli.FrpsUrl = urlToParse
|
||||||
}
|
}
|
||||||
|
|
||||||
cliCfg.User = userInfo.GetUserName()
|
cliCfg.User = userInfo.GetUserName()
|
||||||
|
@@ -27,52 +27,51 @@ func ConfigureRouter(appInstance app.Application, router *gin.Engine) {
|
|||||||
router.POST("/auth", auth.MakeGinHandlerFunc(appInstance, auth.HandleLogin))
|
router.POST("/auth", auth.MakeGinHandlerFunc(appInstance, auth.HandleLogin))
|
||||||
|
|
||||||
api := router.Group("/api")
|
api := router.Group("/api")
|
||||||
v1 := api.Group("/v1")
|
api.POST("/v1/auth/cert", app.Wrapper(appInstance, auth.GetClientCert))
|
||||||
|
api.POST("/v1/auth/login", app.Wrapper(appInstance, auth.LoginHandler))
|
||||||
|
api.POST("/v1/auth/register", app.Wrapper(appInstance, auth.RegisterHandler))
|
||||||
|
api.GET("/v1/auth/logout", auth.RemoveJWTHandler(appInstance))
|
||||||
|
|
||||||
|
v1 := api.Group("/v1", middleware.JWTAuth(appInstance), middleware.AuthCtx(appInstance), middleware.RBAC(appInstance))
|
||||||
{
|
{
|
||||||
authRouter := v1.Group("/auth")
|
userRouter := v1.Group("/user")
|
||||||
{
|
|
||||||
authRouter.POST("/login", app.Wrapper(appInstance, auth.LoginHandler))
|
|
||||||
authRouter.POST("/register", app.Wrapper(appInstance, auth.RegisterHandler))
|
|
||||||
authRouter.GET("/logout", auth.RemoveJWTHandler(appInstance))
|
|
||||||
authRouter.POST("/cert", app.Wrapper(appInstance, auth.GetClientCert))
|
|
||||||
}
|
|
||||||
userRouter := v1.Group("/user", middleware.JWTAuth(appInstance), middleware.AuthCtx(appInstance))
|
|
||||||
{
|
{
|
||||||
userRouter.POST("/get", app.Wrapper(appInstance, user.GetUserInfoHandler))
|
userRouter.POST("/get", app.Wrapper(appInstance, user.GetUserInfoHandler))
|
||||||
userRouter.POST("/update", app.Wrapper(appInstance, user.UpdateUserInfoHander))
|
userRouter.POST("/update", app.Wrapper(appInstance, user.UpdateUserInfoHander))
|
||||||
|
userRouter.POST("/sign-token", app.Wrapper(appInstance, user.SignTokenHandler))
|
||||||
}
|
}
|
||||||
platformRouter := v1.Group("/platform", middleware.JWTAuth(appInstance), middleware.AuthCtx(appInstance))
|
platformRouter := v1.Group("/platform")
|
||||||
{
|
{
|
||||||
platformRouter.GET("/baseinfo", platform.GetPlatformInfo(appInstance))
|
platformRouter.GET("/baseinfo", platform.GetPlatformInfo(appInstance))
|
||||||
platformRouter.POST("/clientsstatus", app.Wrapper(appInstance, platform.GetClientsStatus))
|
platformRouter.POST("/clientsstatus", app.Wrapper(appInstance, platform.GetClientsStatus))
|
||||||
}
|
}
|
||||||
clientRouter := v1.Group("/client", middleware.JWTAuth(appInstance), middleware.AuthCtx(appInstance))
|
clientRouter := v1.Group("/client")
|
||||||
{
|
{
|
||||||
clientRouter.POST("/get", app.Wrapper(appInstance, client.GetClientHandler))
|
clientRouter.POST("/get", app.Wrapper(appInstance, client.GetClientHandler))
|
||||||
clientRouter.POST("/init", app.Wrapper(appInstance, client.InitClientHandler))
|
clientRouter.POST("/init", app.Wrapper(appInstance, client.InitClientHandler))
|
||||||
clientRouter.POST("/delete", app.Wrapper(appInstance, client.DeleteClientHandler))
|
clientRouter.POST("/delete", app.Wrapper(appInstance, client.DeleteClientHandler))
|
||||||
clientRouter.POST("/list", app.Wrapper(appInstance, client.ListClientsHandler))
|
clientRouter.POST("/list", app.Wrapper(appInstance, client.ListClientsHandler))
|
||||||
}
|
}
|
||||||
serverRouter := v1.Group("/server", middleware.JWTAuth(appInstance), middleware.AuthCtx(appInstance))
|
serverRouter := v1.Group("/server")
|
||||||
{
|
{
|
||||||
serverRouter.POST("/get", app.Wrapper(appInstance, server.GetServerHandler))
|
serverRouter.POST("/get", app.Wrapper(appInstance, server.GetServerHandler))
|
||||||
serverRouter.POST("/init", app.Wrapper(appInstance, server.InitServerHandler))
|
serverRouter.POST("/init", app.Wrapper(appInstance, server.InitServerHandler))
|
||||||
serverRouter.POST("/delete", app.Wrapper(appInstance, server.DeleteServerHandler))
|
serverRouter.POST("/delete", app.Wrapper(appInstance, server.DeleteServerHandler))
|
||||||
serverRouter.POST("/list", app.Wrapper(appInstance, server.ListServersHandler))
|
serverRouter.POST("/list", app.Wrapper(appInstance, server.ListServersHandler))
|
||||||
}
|
}
|
||||||
frpcRouter := v1.Group("/frpc", middleware.JWTAuth(appInstance), middleware.AuthCtx(appInstance))
|
frpcRouter := v1.Group("/frpc")
|
||||||
{
|
{
|
||||||
frpcRouter.POST("/update", app.Wrapper(appInstance, client.UpdateFrpcHander))
|
frpcRouter.POST("/update", app.Wrapper(appInstance, client.UpdateFrpcHander))
|
||||||
frpcRouter.POST("/delete", app.Wrapper(appInstance, client.RemoveFrpcHandler))
|
frpcRouter.POST("/delete", app.Wrapper(appInstance, client.RemoveFrpcHandler))
|
||||||
frpcRouter.POST("/stop", app.Wrapper(appInstance, client.StopFRPCHandler))
|
frpcRouter.POST("/stop", app.Wrapper(appInstance, client.StopFRPCHandler))
|
||||||
frpcRouter.POST("/start", app.Wrapper(appInstance, client.StartFRPCHandler))
|
frpcRouter.POST("/start", app.Wrapper(appInstance, client.StartFRPCHandler))
|
||||||
}
|
}
|
||||||
frpsRouter := v1.Group("/frps", middleware.JWTAuth(appInstance), middleware.AuthCtx(appInstance))
|
frpsRouter := v1.Group("/frps")
|
||||||
{
|
{
|
||||||
frpsRouter.POST("/update", app.Wrapper(appInstance, server.UpdateFrpsHander))
|
frpsRouter.POST("/update", app.Wrapper(appInstance, server.UpdateFrpsHander))
|
||||||
frpsRouter.POST("/delete", app.Wrapper(appInstance, server.RemoveFrpsHandler))
|
frpsRouter.POST("/delete", app.Wrapper(appInstance, server.RemoveFrpsHandler))
|
||||||
}
|
}
|
||||||
proxyRouter := v1.Group("/proxy", middleware.JWTAuth(appInstance), middleware.AuthCtx(appInstance))
|
proxyRouter := v1.Group("/proxy")
|
||||||
{
|
{
|
||||||
proxyRouter.POST("/get_by_cid", app.Wrapper(appInstance, proxy.GetProxyStatsByClientID))
|
proxyRouter.POST("/get_by_cid", app.Wrapper(appInstance, proxy.GetProxyStatsByClientID))
|
||||||
proxyRouter.POST("/get_by_sid", app.Wrapper(appInstance, proxy.GetProxyStatsByServerID))
|
proxyRouter.POST("/get_by_sid", app.Wrapper(appInstance, proxy.GetProxyStatsByServerID))
|
||||||
@@ -82,7 +81,7 @@ func ConfigureRouter(appInstance app.Application, router *gin.Engine) {
|
|||||||
proxyRouter.POST("/delete_config", app.Wrapper(appInstance, proxy.DeleteProxyConfig))
|
proxyRouter.POST("/delete_config", app.Wrapper(appInstance, proxy.DeleteProxyConfig))
|
||||||
proxyRouter.POST("/get_config", app.Wrapper(appInstance, proxy.GetProxyConfig))
|
proxyRouter.POST("/get_config", app.Wrapper(appInstance, proxy.GetProxyConfig))
|
||||||
}
|
}
|
||||||
v1.GET("/pty/:clientID", middleware.JWTAuth(appInstance), middleware.AuthCtx(appInstance), shell.PTYHandler(appInstance))
|
v1.GET("/pty/:clientID", shell.PTYHandler(appInstance))
|
||||||
v1.GET("/log", middleware.JWTAuth(appInstance), middleware.AuthCtx(appInstance), streamlog.GetLogHandler(appInstance))
|
v1.GET("/log", streamlog.GetLogHandler(appInstance))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -39,7 +39,7 @@ func GetServerHandler(c *app.Context, req *pb.GetServerRequest) (*pb.GetServerRe
|
|||||||
Secret: lo.ToPtr(serverEntity.ConnectSecret),
|
Secret: lo.ToPtr(serverEntity.ConnectSecret),
|
||||||
Comment: lo.ToPtr(serverEntity.Comment),
|
Comment: lo.ToPtr(serverEntity.Comment),
|
||||||
Ip: lo.ToPtr(serverEntity.ServerIP),
|
Ip: lo.ToPtr(serverEntity.ServerIP),
|
||||||
FrpsUrls: serverEntity.FRPsUrls,
|
FrpsUrls: serverEntity.FrpsUrls,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@@ -54,7 +54,7 @@ func ListServersHandler(c *app.Context, req *pb.ListServersRequest) (*pb.ListSer
|
|||||||
Secret: lo.ToPtr(c.ConnectSecret),
|
Secret: lo.ToPtr(c.ConnectSecret),
|
||||||
Ip: lo.ToPtr(c.ServerIP),
|
Ip: lo.ToPtr(c.ServerIP),
|
||||||
Comment: lo.ToPtr(c.Comment),
|
Comment: lo.ToPtr(c.Comment),
|
||||||
FrpsUrls: c.FRPsUrls,
|
FrpsUrls: c.FrpsUrls,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
Total: lo.ToPtr(int32(serverCounts)),
|
Total: lo.ToPtr(int32(serverCounts)),
|
||||||
|
@@ -53,7 +53,7 @@ func UpdateFrpsHander(c *app.Context, req *pb.UpdateFRPSRequest) (*pb.UpdateFRPS
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(req.GetFrpsUrls()) > 0 {
|
if len(req.GetFrpsUrls()) > 0 {
|
||||||
srv.FRPsUrls = req.GetFrpsUrls()
|
srv.FrpsUrls = req.GetFrpsUrls()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := dao.NewQuery(c).UpdateServer(userInfo, srv); err != nil {
|
if err := dao.NewQuery(c).UpdateServer(userInfo, srv); err != nil {
|
||||||
|
45
biz/master/user/sign_token.go
Normal file
45
biz/master/user/sign_token.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/VaalaCat/frp-panel/common"
|
||||||
|
"github.com/VaalaCat/frp-panel/conf"
|
||||||
|
"github.com/VaalaCat/frp-panel/defs"
|
||||||
|
"github.com/VaalaCat/frp-panel/pb"
|
||||||
|
"github.com/VaalaCat/frp-panel/services/app"
|
||||||
|
"github.com/VaalaCat/frp-panel/utils"
|
||||||
|
"github.com/VaalaCat/frp-panel/utils/logger"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SignTokenHandler(ctx *app.Context, req *pb.SignTokenRequest) (*pb.SignTokenResponse, error) {
|
||||||
|
var (
|
||||||
|
userInfo = common.GetUserInfo(ctx)
|
||||||
|
permissions = req.GetPermissions()
|
||||||
|
expiresIn = req.GetExpiresIn()
|
||||||
|
cfg = ctx.GetApp().GetConfig()
|
||||||
|
)
|
||||||
|
|
||||||
|
token, err := utils.GetJwtTokenFromMap(conf.JWTSecret(cfg),
|
||||||
|
time.Now().Unix(),
|
||||||
|
int64(expiresIn),
|
||||||
|
map[string]interface{}{
|
||||||
|
defs.UserIDKey: userInfo.GetUserID(),
|
||||||
|
defs.TokenPayloadKey_Permissions: permissions,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger(ctx).WithError(err).Errorf("get jwt token failed, req: [%s]", req.String())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Logger(ctx).Infof("get jwt token success, req: [%s]", req.String())
|
||||||
|
|
||||||
|
return &pb.SignTokenResponse{
|
||||||
|
Token: lo.ToPtr(token),
|
||||||
|
Status: &pb.Status{
|
||||||
|
Code: pb.RespCode_RESP_CODE_SUCCESS,
|
||||||
|
Message: "ok",
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
@@ -1,16 +1,23 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"embed"
|
||||||
|
|
||||||
|
"github.com/VaalaCat/frp-panel/cmd/frpp/shared"
|
||||||
"github.com/VaalaCat/frp-panel/utils/logger"
|
"github.com/VaalaCat/frp-panel/utils/logger"
|
||||||
"github.com/fatedier/golib/crypto"
|
"github.com/fatedier/golib/crypto"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:embed all:out
|
||||||
|
var fs embed.FS
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
crypto.DefaultSalt = "frp"
|
crypto.DefaultSalt = "frp"
|
||||||
logger.InitLogger()
|
logger.InitLogger()
|
||||||
cobra.MousetrapHelpText = ""
|
cobra.MousetrapHelpText = ""
|
||||||
rootCmd := buildCommand()
|
|
||||||
setMasterCommandIfNonePresent(rootCmd)
|
rootCmd := shared.BuildCommand(fs)
|
||||||
|
shared.SetMasterCommandIfNonePresent(rootCmd)
|
||||||
rootCmd.Execute()
|
rootCmd.Execute()
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package shared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
@@ -1,7 +1,8 @@
|
|||||||
package main
|
package shared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"embed"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@@ -30,9 +31,10 @@ type CommonArgs struct {
|
|||||||
RpcPort *int
|
RpcPort *int
|
||||||
ApiPort *int
|
ApiPort *int
|
||||||
ApiScheme *string
|
ApiScheme *string
|
||||||
|
JoinToken *string
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildCommand() *cobra.Command {
|
func BuildCommand(fs embed.FS) *cobra.Command {
|
||||||
cfg := conf.NewConfig()
|
cfg := conf.NewConfig()
|
||||||
|
|
||||||
logger.UpdateLoggerOpt(
|
logger.UpdateLoggerOpt(
|
||||||
@@ -41,7 +43,7 @@ func buildCommand() *cobra.Command {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return NewRootCmd(
|
return NewRootCmd(
|
||||||
NewMasterCmd(cfg),
|
NewMasterCmd(cfg, fs),
|
||||||
NewClientCmd(cfg),
|
NewClientCmd(cfg),
|
||||||
NewServerCmd(cfg),
|
NewServerCmd(cfg),
|
||||||
NewJoinCmd(),
|
NewJoinCmd(),
|
||||||
@@ -59,6 +61,7 @@ func AddCommonFlags(commonCmd *cobra.Command) {
|
|||||||
commonCmd.Flags().StringP("id", "i", "", "client id")
|
commonCmd.Flags().StringP("id", "i", "", "client id")
|
||||||
commonCmd.Flags().String("rpc-url", "", "rpc url, master rpc url, scheme can be grpc/ws/wss://hostname:port")
|
commonCmd.Flags().String("rpc-url", "", "rpc url, master rpc url, scheme can be grpc/ws/wss://hostname:port")
|
||||||
commonCmd.Flags().String("api-url", "", "api url, master api url, scheme can be http/https://hostname:port")
|
commonCmd.Flags().String("api-url", "", "api url, master api url, scheme can be http/https://hostname:port")
|
||||||
|
commonCmd.Flags().StringP("join-token", "j", "", "your token from master, auto join with out webui")
|
||||||
|
|
||||||
// deprecated start
|
// deprecated start
|
||||||
commonCmd.Flags().StringP("app", "a", "", "app secret")
|
commonCmd.Flags().StringP("app", "a", "", "app secret")
|
||||||
@@ -109,12 +112,11 @@ func GetCommonArgs(cmd *cobra.Command) CommonArgs {
|
|||||||
commonArgs.ApiScheme = &apiScheme
|
commonArgs.ApiScheme = &apiScheme
|
||||||
}
|
}
|
||||||
|
|
||||||
return commonArgs
|
if joinToken, err := cmd.Flags().GetString("join-token"); err == nil {
|
||||||
}
|
commonArgs.JoinToken = &joinToken
|
||||||
|
}
|
||||||
|
|
||||||
type JoinArgs struct {
|
return commonArgs
|
||||||
CommonArgs
|
|
||||||
JoinToken *string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewJoinCmd() *cobra.Command {
|
func NewJoinCmd() *cobra.Command {
|
||||||
@@ -122,29 +124,25 @@ func NewJoinCmd() *cobra.Command {
|
|||||||
Use: "join [-j join token] [-r rpc host] [-p api port] [-e api scheme]",
|
Use: "join [-j join token] [-r rpc host] [-p api port] [-e api scheme]",
|
||||||
Short: "join to master with token, save param to config",
|
Short: "join to master with token, save param to config",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
ctx := context.Background()
|
||||||
commonArgs := GetCommonArgs(cmd)
|
commonArgs := GetCommonArgs(cmd)
|
||||||
|
|
||||||
warnDepParam(cmd)
|
warnDepParam(cmd)
|
||||||
|
|
||||||
joinArgs := &JoinArgs{
|
cli, err := JoinMaster(conf.NewConfig(), commonArgs)
|
||||||
CommonArgs: commonArgs,
|
if err != nil {
|
||||||
|
logger.Logger(ctx).Fatalf("join master failed: %s", err.Error())
|
||||||
}
|
}
|
||||||
if joinToken, err := cmd.Flags().GetString("join-token"); err == nil {
|
saveConfig(ctx, cli, commonArgs)
|
||||||
joinArgs.JoinToken = &joinToken
|
|
||||||
}
|
|
||||||
|
|
||||||
appInstance := app.NewApp()
|
|
||||||
pullRunConfig(appInstance, joinArgs)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
joinCmd.Flags().StringP("join-token", "j", "", "your token from master")
|
|
||||||
AddCommonFlags(joinCmd)
|
AddCommonFlags(joinCmd)
|
||||||
|
|
||||||
return joinCmd
|
return joinCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMasterCmd(cfg conf.Config) *cobra.Command {
|
func NewMasterCmd(cfg conf.Config, fs embed.FS) *cobra.Command {
|
||||||
return &cobra.Command{
|
return &cobra.Command{
|
||||||
Use: "master",
|
Use: "master",
|
||||||
Short: "run frp-panel manager",
|
Short: "run frp-panel manager",
|
||||||
@@ -159,6 +157,8 @@ func NewMasterCmd(cfg conf.Config) *cobra.Command {
|
|||||||
fx.Supply(
|
fx.Supply(
|
||||||
CommonArgs{},
|
CommonArgs{},
|
||||||
fx.Annotate(cfg, fx.ResultTags(`name:"originConfig"`)),
|
fx.Annotate(cfg, fx.ResultTags(`name:"originConfig"`)),
|
||||||
|
fs,
|
||||||
|
defs.AppRole_Master,
|
||||||
),
|
),
|
||||||
fx.Provide(fx.Annotate(NewDefaultServerConfig, fx.ResultTags(`name:"defaultServerConfig"`))),
|
fx.Provide(fx.Annotate(NewDefaultServerConfig, fx.ResultTags(`name:"defaultServerConfig"`))),
|
||||||
fx.Invoke(NewConfigPrinter),
|
fx.Invoke(NewConfigPrinter),
|
||||||
@@ -202,6 +202,7 @@ func NewClientCmd(cfg conf.Config) *cobra.Command {
|
|||||||
fx.Supply(
|
fx.Supply(
|
||||||
commonArgs,
|
commonArgs,
|
||||||
fx.Annotate(cfg, fx.ResultTags(`name:"originConfig"`)),
|
fx.Annotate(cfg, fx.ResultTags(`name:"originConfig"`)),
|
||||||
|
defs.AppRole_Client,
|
||||||
),
|
),
|
||||||
fx.Invoke(NewConfigPrinter),
|
fx.Invoke(NewConfigPrinter),
|
||||||
fx.Invoke(runClient),
|
fx.Invoke(runClient),
|
||||||
@@ -246,6 +247,7 @@ func NewServerCmd(cfg conf.Config) *cobra.Command {
|
|||||||
fx.Supply(
|
fx.Supply(
|
||||||
commonArgs,
|
commonArgs,
|
||||||
fx.Annotate(cfg, fx.ResultTags(`name:"originConfig"`)),
|
fx.Annotate(cfg, fx.ResultTags(`name:"originConfig"`)),
|
||||||
|
defs.AppRole_Server,
|
||||||
),
|
),
|
||||||
fx.Invoke(runServer),
|
fx.Invoke(runServer),
|
||||||
}
|
}
|
||||||
@@ -403,7 +405,7 @@ func warnDepParam(cmd *cobra.Command) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setMasterCommandIfNonePresent(rootCmd *cobra.Command) {
|
func SetMasterCommandIfNonePresent(rootCmd *cobra.Command) {
|
||||||
cmd, _, err := rootCmd.Find(os.Args[1:])
|
cmd, _, err := rootCmd.Find(os.Args[1:])
|
||||||
if err == nil && cmd.Use == rootCmd.Use && cmd.Flags().Parse(os.Args[1:]) != pflag.ErrHelp {
|
if err == nil && cmd.Use == rootCmd.Use && cmd.Flags().Parse(os.Args[1:]) != pflag.ErrHelp {
|
||||||
args := append([]string{"master"}, os.Args[1:]...)
|
args := append([]string{"master"}, os.Args[1:]...)
|
||||||
@@ -411,96 +413,120 @@ func setMasterCommandIfNonePresent(rootCmd *cobra.Command) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func pullRunConfig(appInstance app.Application, joinArgs *JoinArgs) {
|
func SetClientCommandIfNonePresent(rootCmd *cobra.Command) {
|
||||||
|
cmd, _, err := rootCmd.Find(os.Args[1:])
|
||||||
|
if err == nil && cmd.Use == rootCmd.Use && cmd.Flags().Parse(os.Args[1:]) != pflag.ErrHelp {
|
||||||
|
args := append([]string{"client"}, os.Args[1:]...)
|
||||||
|
rootCmd.SetArgs(args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func JoinMaster(cfg conf.Config, joinArgs CommonArgs) (*pb.Client, error) {
|
||||||
c := context.Background()
|
c := context.Background()
|
||||||
if err := checkPullParams(joinArgs); err != nil {
|
if err := checkPullParams(joinArgs); err != nil {
|
||||||
logger.Logger(c).Errorf("check pull params failed: %s", err.Error())
|
logger.Logger(c).Errorf("check pull params failed: %s", err.Error())
|
||||||
return
|
return nil, err
|
||||||
}
|
|
||||||
|
|
||||||
if err := utils.EnsureDirectoryExists(defs.SysEnvPath); err != nil {
|
|
||||||
logger.Logger(c).Errorf("ensure directory failed: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var clientID string
|
var clientID string
|
||||||
|
|
||||||
if cliID := joinArgs.ClientID; cliID == nil || len(*cliID) == 0 {
|
if cliID := joinArgs.ClientID; cliID == nil || len(*cliID) == 0 {
|
||||||
clientID = utils.GetHostnameWithIP()
|
clientID = utils.GetHostnameWithIP()
|
||||||
|
} else {
|
||||||
|
clientID = *cliID
|
||||||
}
|
}
|
||||||
|
|
||||||
clientID = utils.MakeClientIDPermited(clientID)
|
clientID = utils.MakeClientIDPermited(clientID)
|
||||||
patchConfig(appInstance, joinArgs.CommonArgs)
|
|
||||||
|
|
||||||
initResp, err := rpc.InitClient(appInstance, clientID, *joinArgs.JoinToken)
|
logger.Logger(c).Infof("join master with param, clientId:[%s] joinArgs:[%s]", clientID, utils.MarshalForJson(joinArgs))
|
||||||
if err != nil {
|
|
||||||
logger.Logger(c).Errorf("init client failed: %s", err.Error())
|
// 检测是否存在已有的client
|
||||||
return
|
clientResp, err := rpc.GetClient(cfg, clientID, *joinArgs.JoinToken)
|
||||||
}
|
if err != nil || clientResp == nil || clientResp.GetStatus().GetCode() != pb.RespCode_RESP_CODE_SUCCESS {
|
||||||
if initResp == nil {
|
logger.Logger(c).Infof("client [%s] not found, try to init client", clientID)
|
||||||
logger.Logger(c).Errorf("init resp is nil")
|
|
||||||
return
|
// 创建短期client
|
||||||
}
|
initResp, err := rpc.InitClient(cfg, clientID, *joinArgs.JoinToken, true)
|
||||||
if initResp.GetStatus().GetCode() != pb.RespCode_RESP_CODE_SUCCESS {
|
if err != nil {
|
||||||
logger.Logger(c).Errorf("init client failed with status: %s", initResp.GetStatus().GetMessage())
|
logger.Logger(c).Errorf("init client failed: %s", err.Error())
|
||||||
return
|
return nil, err
|
||||||
|
}
|
||||||
|
if initResp == nil {
|
||||||
|
logger.Logger(c).Errorf("init resp is nil")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if initResp.GetStatus().GetCode() != pb.RespCode_RESP_CODE_SUCCESS {
|
||||||
|
logger.Logger(c).Errorf("init client failed with status: %s", initResp.GetStatus().GetMessage())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
clientID = initResp.GetClientId()
|
||||||
|
clientResp, err = rpc.GetClient(cfg, clientID, *joinArgs.JoinToken)
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger(c).Errorf("get client failed: %s", err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clientID = initResp.GetClientId()
|
|
||||||
clientResp, err := rpc.GetClient(appInstance, clientID, *joinArgs.JoinToken)
|
|
||||||
if err != nil {
|
|
||||||
logger.Logger(c).Errorf("get client failed: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if clientResp == nil {
|
if clientResp == nil {
|
||||||
logger.Logger(c).Errorf("client resp is nil")
|
logger.Logger(c).Errorf("client resp is nil")
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
if clientResp.GetStatus().GetCode() != pb.RespCode_RESP_CODE_SUCCESS {
|
if clientResp.GetStatus().GetCode() != pb.RespCode_RESP_CODE_SUCCESS {
|
||||||
logger.Logger(c).Errorf("client resp code is not success: %s", clientResp.GetStatus().GetMessage())
|
logger.Logger(c).Errorf("client resp code is not success: %s", clientResp.GetStatus().GetMessage())
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
client := clientResp.GetClient()
|
client := clientResp.GetClient()
|
||||||
if client == nil {
|
if client == nil {
|
||||||
logger.Logger(c).Errorf("client is nil")
|
logger.Logger(c).Errorf("client is nil")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveConfig(ctx context.Context, cli *pb.Client, joinArgs CommonArgs) {
|
||||||
|
if err := utils.EnsureDirectoryExists(defs.SysEnvPath); err != nil {
|
||||||
|
logger.Logger(ctx).Errorf("ensure directory failed: %s", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
envMap, err := godotenv.Read(defs.SysEnvPath)
|
envMap, err := godotenv.Read(defs.SysEnvPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
envMap = make(map[string]string)
|
envMap = make(map[string]string)
|
||||||
logger.Logger(c).Warnf("read env file failed, try to create: %s", err.Error())
|
logger.Logger(ctx).Warnf("read env file failed, try to create: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
envMap[defs.EnvClientID] = clientID
|
envMap[defs.EnvClientID] = cli.GetId()
|
||||||
envMap[defs.EnvClientSecret] = client.GetSecret()
|
envMap[defs.EnvClientSecret] = cli.GetSecret()
|
||||||
envMap[defs.EnvClientAPIUrl] = *joinArgs.ApiUrl
|
envMap[defs.EnvClientAPIUrl] = *joinArgs.ApiUrl
|
||||||
envMap[defs.EnvClientRPCUrl] = *joinArgs.RpcUrl
|
envMap[defs.EnvClientRPCUrl] = *joinArgs.RpcUrl
|
||||||
|
|
||||||
if err = godotenv.Write(envMap, defs.SysEnvPath); err != nil {
|
if err = godotenv.Write(envMap, defs.SysEnvPath); err != nil {
|
||||||
logger.Logger(c).Errorf("write env file failed: %s", err.Error())
|
logger.Logger(ctx).Errorf("write env file failed: %s", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logger.Logger(c).Infof("config saved to env file: %s, you can use `frp-panel client` without args to run client,\n\nconfig is: [%v]", defs.SysEnvPath, envMap)
|
logger.Logger(ctx).Infof("config saved to env file: %s, you can use `frp-panel client` without args to run client,\n\nconfig is: [%v]",
|
||||||
|
defs.SysEnvPath, envMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkPullParams(joinArgs *JoinArgs) error {
|
func checkPullParams(joinArgs CommonArgs) error {
|
||||||
if joinToken := joinArgs.JoinToken; joinToken != nil && len(*joinToken) == 0 {
|
if joinToken := joinArgs.JoinToken; joinToken != nil && len(*joinToken) == 0 {
|
||||||
return errors.New("join token is empty")
|
return errors.New("join token is empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
if apiUrl := joinArgs.ApiUrl; apiUrl == nil || len(*apiUrl) == 0 {
|
var (
|
||||||
if apiHost := joinArgs.ApiHost; apiHost == nil || len(*apiHost) == 0 {
|
apiUrlAvaliable = joinArgs.ApiUrl != nil && len(*joinArgs.ApiUrl) > 0
|
||||||
return errors.New("api host is empty")
|
rpcUrlAvaliable = joinArgs.RpcUrl != nil && len(*joinArgs.RpcUrl) > 0
|
||||||
}
|
)
|
||||||
if apiScheme := joinArgs.ApiScheme; apiScheme == nil || len(*apiScheme) == 0 {
|
|
||||||
return errors.New("api scheme is empty")
|
if !apiUrlAvaliable {
|
||||||
}
|
return errors.New("api url is empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
if apiPort := joinArgs.ApiPort; apiPort == nil || *apiPort == 0 {
|
if !rpcUrlAvaliable {
|
||||||
return errors.New("api port is empty")
|
return errors.New("rpc url is empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package shared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -35,6 +35,7 @@ type runMasterParam struct {
|
|||||||
TaskManager watcher.Client `name:"masterTaskManager"`
|
TaskManager watcher.Client `name:"masterTaskManager"`
|
||||||
WsListener *wsgrpc.WSListener
|
WsListener *wsgrpc.WSListener
|
||||||
DefaultServerConfig conf.Config `name:"defaultServerConfig"`
|
DefaultServerConfig conf.Config `name:"defaultServerConfig"`
|
||||||
|
PermManager app.PermissionManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func runMaster(param runMasterParam) {
|
func runMaster(param runMasterParam) {
|
@@ -1,13 +1,14 @@
|
|||||||
package main
|
package shared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
clientMod = fx.Module("cmd.client", fx.Provide(
|
clientMod = fx.Module("cmd.client",
|
||||||
fx.Annotate(NewWatcher, fx.ResultTags(`name:"clientTaskManager"`)),
|
fx.Provide(
|
||||||
))
|
fx.Annotate(NewWatcher, fx.ResultTags(`name:"clientTaskManager"`)),
|
||||||
|
))
|
||||||
|
|
||||||
serverMod = fx.Module("cmd.server", fx.Provide(
|
serverMod = fx.Module("cmd.server", fx.Provide(
|
||||||
fx.Annotate(NewServerAPI, fx.ResultTags(`name:"serverApiService"`)),
|
fx.Annotate(NewServerAPI, fx.ResultTags(`name:"serverApiService"`)),
|
||||||
@@ -16,12 +17,13 @@ var (
|
|||||||
))
|
))
|
||||||
|
|
||||||
masterMod = fx.Module("cmd.master", fx.Provide(
|
masterMod = fx.Module("cmd.master", fx.Provide(
|
||||||
|
NewPermissionManager,
|
||||||
|
NewEnforcer,
|
||||||
NewListenerOptions,
|
NewListenerOptions,
|
||||||
NewDBManager,
|
NewDBManager,
|
||||||
NewWSListener,
|
NewWSListener,
|
||||||
NewMasterTLSConfig,
|
NewMasterTLSConfig,
|
||||||
NewWSUpgrader,
|
NewWSUpgrader,
|
||||||
NewFs,
|
|
||||||
NewClientLogManager,
|
NewClientLogManager,
|
||||||
fx.Annotate(NewWatcher, fx.ResultTags(`name:"masterTaskManager"`)),
|
fx.Annotate(NewWatcher, fx.ResultTags(`name:"masterTaskManager"`)),
|
||||||
fx.Annotate(NewMasterRouter, fx.ResultTags(`name:"masterRouter"`)),
|
fx.Annotate(NewMasterRouter, fx.ResultTags(`name:"masterRouter"`)),
|
||||||
@@ -34,11 +36,12 @@ var (
|
|||||||
))
|
))
|
||||||
|
|
||||||
commonMod = fx.Module("common", fx.Provide(
|
commonMod = fx.Module("common", fx.Provide(
|
||||||
NewPatchedConfig,
|
|
||||||
NewLogHookManager,
|
NewLogHookManager,
|
||||||
NewPTYManager,
|
NewPTYManager,
|
||||||
NewBaseApp,
|
NewBaseApp,
|
||||||
NewContext,
|
NewContext,
|
||||||
NewClientsManager,
|
NewClientsManager,
|
||||||
|
NewAutoJoin,
|
||||||
|
fx.Annotate(NewPatchedConfig, fx.ResultTags(`name:"argsPatchedConfig"`)),
|
||||||
))
|
))
|
||||||
)
|
)
|
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package shared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/VaalaCat/frp-panel/biz/master/streamlog"
|
"github.com/VaalaCat/frp-panel/biz/master/streamlog"
|
||||||
bizserver "github.com/VaalaCat/frp-panel/biz/server"
|
bizserver "github.com/VaalaCat/frp-panel/biz/server"
|
||||||
"github.com/VaalaCat/frp-panel/conf"
|
"github.com/VaalaCat/frp-panel/conf"
|
||||||
|
"github.com/VaalaCat/frp-panel/defs"
|
||||||
"github.com/VaalaCat/frp-panel/models"
|
"github.com/VaalaCat/frp-panel/models"
|
||||||
"github.com/VaalaCat/frp-panel/pb"
|
"github.com/VaalaCat/frp-panel/pb"
|
||||||
"github.com/VaalaCat/frp-panel/services/api"
|
"github.com/VaalaCat/frp-panel/services/api"
|
||||||
@@ -22,11 +23,13 @@ import (
|
|||||||
"github.com/VaalaCat/frp-panel/services/dao"
|
"github.com/VaalaCat/frp-panel/services/dao"
|
||||||
"github.com/VaalaCat/frp-panel/services/master"
|
"github.com/VaalaCat/frp-panel/services/master"
|
||||||
"github.com/VaalaCat/frp-panel/services/mux"
|
"github.com/VaalaCat/frp-panel/services/mux"
|
||||||
|
"github.com/VaalaCat/frp-panel/services/rbac"
|
||||||
"github.com/VaalaCat/frp-panel/services/rpc"
|
"github.com/VaalaCat/frp-panel/services/rpc"
|
||||||
"github.com/VaalaCat/frp-panel/services/watcher"
|
"github.com/VaalaCat/frp-panel/services/watcher"
|
||||||
"github.com/VaalaCat/frp-panel/utils"
|
"github.com/VaalaCat/frp-panel/utils"
|
||||||
"github.com/VaalaCat/frp-panel/utils/logger"
|
"github.com/VaalaCat/frp-panel/utils/logger"
|
||||||
"github.com/VaalaCat/frp-panel/utils/wsgrpc"
|
"github.com/VaalaCat/frp-panel/utils/wsgrpc"
|
||||||
|
"github.com/casbin/casbin/v2"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/glebarez/sqlite"
|
"github.com/glebarez/sqlite"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
@@ -38,9 +41,6 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed all:out
|
|
||||||
var fs embed.FS
|
|
||||||
|
|
||||||
func NewLogHookManager() app.StreamLogHookMgr {
|
func NewLogHookManager() app.StreamLogHookMgr {
|
||||||
return &bizcommon.HookMgr{}
|
return &bizcommon.HookMgr{}
|
||||||
}
|
}
|
||||||
@@ -70,10 +70,6 @@ func NewClientsManager() app.ClientsManager {
|
|||||||
return rpc.NewClientsManager()
|
return rpc.NewClientsManager()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFs() embed.FS {
|
|
||||||
return fs
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPatchedConfig(param struct {
|
func NewPatchedConfig(param struct {
|
||||||
fx.In
|
fx.In
|
||||||
|
|
||||||
@@ -96,7 +92,7 @@ func NewClientLogManager() app.ClientLogManager {
|
|||||||
|
|
||||||
func NewDBManager(ctx *app.Context, appInstance app.Application) app.DBManager {
|
func NewDBManager(ctx *app.Context, appInstance app.Application) app.DBManager {
|
||||||
logger.Logger(ctx).Infof("start to init database, type: %s", appInstance.GetConfig().DB.Type)
|
logger.Logger(ctx).Infof("start to init database, type: %s", appInstance.GetConfig().DB.Type)
|
||||||
mgr := models.NewDBManager(nil, appInstance.GetConfig().DB.Type)
|
mgr := models.NewDBManager(appInstance.GetConfig().DB.Type)
|
||||||
appInstance.SetDBManager(mgr)
|
appInstance.SetDBManager(mgr)
|
||||||
|
|
||||||
if appInstance.GetConfig().IsDebug {
|
if appInstance.GetConfig().IsDebug {
|
||||||
@@ -104,7 +100,7 @@ func NewDBManager(ctx *app.Context, appInstance app.Application) app.DBManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch appInstance.GetConfig().DB.Type {
|
switch appInstance.GetConfig().DB.Type {
|
||||||
case "sqlite3":
|
case defs.DBTypeSQLite3:
|
||||||
if err := utils.EnsureDirectoryExists(appInstance.GetConfig().DB.DSN); err != nil {
|
if err := utils.EnsureDirectoryExists(appInstance.GetConfig().DB.DSN); err != nil {
|
||||||
logger.Logger(ctx).WithError(err).Warnf("ensure directory failed, data location: [%s], keep data in current directory",
|
logger.Logger(ctx).WithError(err).Warnf("ensure directory failed, data location: [%s], keep data in current directory",
|
||||||
appInstance.GetConfig().DB.DSN)
|
appInstance.GetConfig().DB.DSN)
|
||||||
@@ -117,27 +113,34 @@ func NewDBManager(ctx *app.Context, appInstance app.Application) app.DBManager {
|
|||||||
if sqlitedb, err := gorm.Open(sqlite.Open(appInstance.GetConfig().DB.DSN), &gorm.Config{}); err != nil {
|
if sqlitedb, err := gorm.Open(sqlite.Open(appInstance.GetConfig().DB.DSN), &gorm.Config{}); err != nil {
|
||||||
logger.Logger(ctx).Panic(err)
|
logger.Logger(ctx).Panic(err)
|
||||||
} else {
|
} else {
|
||||||
appInstance.GetDBManager().SetDB("sqlite3", sqlitedb)
|
appInstance.GetDBManager().SetDB(defs.DBTypeSQLite3, defs.DBRoleDefault, sqlitedb)
|
||||||
logger.Logger(ctx).Infof("init database success, data location: [%s]", appInstance.GetConfig().DB.DSN)
|
logger.Logger(ctx).Infof("init database success, data location: [%s]", appInstance.GetConfig().DB.DSN)
|
||||||
}
|
}
|
||||||
case "mysql":
|
case defs.DBTypeMysql:
|
||||||
if mysqlDB, err := gorm.Open(mysql.Open(appInstance.GetConfig().DB.DSN), &gorm.Config{}); err != nil {
|
if mysqlDB, err := gorm.Open(mysql.Open(appInstance.GetConfig().DB.DSN), &gorm.Config{}); err != nil {
|
||||||
logger.Logger(ctx).Panic(err)
|
logger.Logger(ctx).Panic(err)
|
||||||
} else {
|
} else {
|
||||||
appInstance.GetDBManager().SetDB("mysql", mysqlDB)
|
appInstance.GetDBManager().SetDB(defs.DBTypeMysql, defs.DBRoleDefault, mysqlDB)
|
||||||
logger.Logger(ctx).Infof("init database success, data type: [%s]", "mysql")
|
logger.Logger(ctx).Infof("init database success, data type: [%s]", "mysql")
|
||||||
}
|
}
|
||||||
case "postgres":
|
case defs.DBTypePostgres:
|
||||||
if postgresDB, err := gorm.Open(postgres.Open(appInstance.GetConfig().DB.DSN), &gorm.Config{}); err != nil {
|
if postgresDB, err := gorm.Open(postgres.Open(appInstance.GetConfig().DB.DSN), &gorm.Config{}); err != nil {
|
||||||
logger.Logger(ctx).Panic(err)
|
logger.Logger(ctx).Panic(err)
|
||||||
} else {
|
} else {
|
||||||
appInstance.GetDBManager().SetDB("postgres", postgresDB)
|
appInstance.GetDBManager().SetDB(defs.DBTypePostgres, defs.DBRoleDefault, postgresDB)
|
||||||
logger.Logger(ctx).Infof("init database success, data type: [%s]", "postgres")
|
logger.Logger(ctx).Infof("init database success, data type: [%s]", "postgres")
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
logger.Logger(ctx).Panicf("currently unsupported database type: %s", appInstance.GetConfig().DB.Type)
|
logger.Logger(ctx).Panicf("currently unsupported database type: %s", appInstance.GetConfig().DB.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memoryDB, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger(ctx).Panic(err)
|
||||||
|
}
|
||||||
|
appInstance.GetDBManager().SetDB(defs.DBTypeSQLite3, defs.DBRoleRam, memoryDB)
|
||||||
|
logger.Logger(ctx).Infof("init memory database success")
|
||||||
|
|
||||||
appInstance.GetDBManager().Init()
|
appInstance.GetDBManager().Init()
|
||||||
return mgr
|
return mgr
|
||||||
}
|
}
|
||||||
@@ -279,7 +282,94 @@ func NewDefaultServerConfig(ctx *app.Context) conf.Config {
|
|||||||
|
|
||||||
const splitter = "\n--------------------------------------------\n"
|
const splitter = "\n--------------------------------------------\n"
|
||||||
|
|
||||||
func NewConfigPrinter(ctx *app.Context, config conf.Config) {
|
func NewConfigPrinter(param struct {
|
||||||
|
fx.In
|
||||||
|
|
||||||
|
Ctx *app.Context
|
||||||
|
Config conf.Config
|
||||||
|
}) {
|
||||||
|
var (
|
||||||
|
ctx = param.Ctx
|
||||||
|
config = param.Config
|
||||||
|
)
|
||||||
logger.Logger(ctx).Infof("%srunning config is: %s%s", splitter, config.PrintStr(), splitter)
|
logger.Logger(ctx).Infof("%srunning config is: %s%s", splitter, config.PrintStr(), splitter)
|
||||||
logger.Logger(ctx).Infof("%scurrent version: \n%s%s", splitter, conf.GetVersion().String(), splitter)
|
logger.Logger(ctx).Infof("%scurrent version: \n%s%s", splitter, conf.GetVersion().String(), splitter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewAutoJoin(param struct {
|
||||||
|
fx.In
|
||||||
|
|
||||||
|
Role defs.AppRole
|
||||||
|
Ctx *app.Context
|
||||||
|
Cfg conf.Config `name:"argsPatchedConfig"`
|
||||||
|
CommonArgs CommonArgs
|
||||||
|
}) conf.Config {
|
||||||
|
var (
|
||||||
|
ctx = param.Ctx
|
||||||
|
clientID = param.Cfg.Client.ID
|
||||||
|
clientSecret = param.Cfg.Client.Secret
|
||||||
|
autoJoin = false
|
||||||
|
appInstance = param.Ctx.GetApp()
|
||||||
|
)
|
||||||
|
|
||||||
|
appInstance.SetConfig(param.Cfg)
|
||||||
|
|
||||||
|
if param.Role != defs.AppRole_Client {
|
||||||
|
return param.Cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户不输入clientID和clientSecret时,使用autoJoin
|
||||||
|
if len(clientSecret) == 0 || len(clientID) == 0 {
|
||||||
|
if param.CommonArgs.JoinToken != nil && len(*param.CommonArgs.JoinToken) > 0 {
|
||||||
|
autoJoin = true
|
||||||
|
} else {
|
||||||
|
if len(clientSecret) == 0 {
|
||||||
|
logger.Logger(ctx).Fatal("client secret cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(clientID) == 0 {
|
||||||
|
logger.Logger(ctx).Fatal("client id cannot be empty")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if autoJoin {
|
||||||
|
logger.Logger(ctx).Infof("start to try join master, clientID: [%s], clientSecret: [%s]", clientID, clientSecret)
|
||||||
|
cli, err := JoinMaster(param.Cfg, param.CommonArgs)
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger(ctx).Fatalf("join master failed: %s", err.Error())
|
||||||
|
}
|
||||||
|
logger.Logger(ctx).Infof("join master success, clientID: [%s], clientInfo: [%s]", cli.GetId(), cli.String())
|
||||||
|
tmpCfg := appInstance.GetConfig()
|
||||||
|
tmpCfg.Client.ID = cli.GetId()
|
||||||
|
tmpCfg.Client.Secret = cli.GetSecret()
|
||||||
|
appInstance.SetConfig(tmpCfg)
|
||||||
|
}
|
||||||
|
return appInstance.GetConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPermissionManager(param struct {
|
||||||
|
fx.In
|
||||||
|
|
||||||
|
Enforcer *casbin.Enforcer
|
||||||
|
AppInstance app.Application
|
||||||
|
}) app.PermissionManager {
|
||||||
|
permMgr := rbac.NewPermManager(param.Enforcer)
|
||||||
|
param.AppInstance.SetPermManager(permMgr)
|
||||||
|
return permMgr
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEnforcer(param struct {
|
||||||
|
fx.In
|
||||||
|
|
||||||
|
Ctx *app.Context
|
||||||
|
DBmanager app.DBManager
|
||||||
|
AppInstance app.Application
|
||||||
|
}) *casbin.Enforcer {
|
||||||
|
e, err := rbac.InitializeCasbin(param.Ctx, param.DBmanager.GetDefaultDB())
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger(param.Ctx).WithError(err).Fatal("initialize casbin failed")
|
||||||
|
}
|
||||||
|
param.AppInstance.SetEnforcer(e)
|
||||||
|
return e
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package shared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package shared
|
||||||
|
|
||||||
// func serverThings() {
|
// func serverThings() {
|
||||||
// time.Sleep(5 * time.Second)
|
// time.Sleep(5 * time.Second)
|
@@ -1,72 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
bizclient "github.com/VaalaCat/frp-panel/biz/client"
|
|
||||||
"github.com/VaalaCat/frp-panel/defs"
|
|
||||||
"github.com/VaalaCat/frp-panel/pb"
|
|
||||||
"github.com/VaalaCat/frp-panel/services/app"
|
|
||||||
"github.com/VaalaCat/frp-panel/services/rpc"
|
|
||||||
"github.com/VaalaCat/frp-panel/services/rpcclient"
|
|
||||||
"github.com/VaalaCat/frp-panel/services/tunnel"
|
|
||||||
"github.com/VaalaCat/frp-panel/services/watcher"
|
|
||||||
"github.com/VaalaCat/frp-panel/utils"
|
|
||||||
"github.com/VaalaCat/frp-panel/utils/logger"
|
|
||||||
"github.com/sourcegraph/conc"
|
|
||||||
)
|
|
||||||
|
|
||||||
func runClient(appInstance app.Application) {
|
|
||||||
var (
|
|
||||||
c = context.Background()
|
|
||||||
clientID = appInstance.GetConfig().Client.ID
|
|
||||||
clientSecret = appInstance.GetConfig().Client.Secret
|
|
||||||
ctx = context.Background()
|
|
||||||
)
|
|
||||||
logger.Logger(c).Infof("start to run client")
|
|
||||||
if len(clientSecret) == 0 {
|
|
||||||
logger.Logger(ctx).Fatal("client secret cannot be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(clientID) == 0 {
|
|
||||||
logger.Logger(ctx).Fatal("client id cannot be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
cred, err := utils.TLSClientCertNoValidate(rpc.GetClientCert(appInstance, clientID, clientSecret, pb.ClientType_CLIENT_TYPE_FRPC))
|
|
||||||
if err != nil {
|
|
||||||
logger.Logger(ctx).Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
appInstance.SetRPCCred(cred)
|
|
||||||
appInstance.SetMasterCli(rpc.NewMasterCli(appInstance))
|
|
||||||
appInstance.SetClientController(tunnel.NewClientController())
|
|
||||||
|
|
||||||
r := rpcclient.NewClientRPCHandler(
|
|
||||||
appInstance,
|
|
||||||
clientID,
|
|
||||||
clientSecret,
|
|
||||||
pb.Event_EVENT_REGISTER_CLIENT,
|
|
||||||
bizclient.HandleServerMessage,
|
|
||||||
)
|
|
||||||
appInstance.SetClientRPCHandler(r)
|
|
||||||
|
|
||||||
w := watcher.NewClient()
|
|
||||||
w.AddDurationTask(defs.PullConfigDuration, bizclient.PullConfig, clientID, clientSecret)
|
|
||||||
|
|
||||||
initClientOnce(appInstance, clientID, clientSecret)
|
|
||||||
|
|
||||||
defer w.Stop()
|
|
||||||
defer r.Stop()
|
|
||||||
|
|
||||||
var wg conc.WaitGroup
|
|
||||||
wg.Go(r.Run)
|
|
||||||
wg.Go(w.Run)
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
func initClientOnce(appInstance app.Application, clientID, clientSecret string) {
|
|
||||||
err := bizclient.PullConfig(appInstance, clientID, clientSecret)
|
|
||||||
if err != nil {
|
|
||||||
logger.Logger(context.Background()).WithError(err).Errorf("cannot pull client config, wait for retry")
|
|
||||||
}
|
|
||||||
}
|
|
319
cmd/frppc/cmd.go
319
cmd/frppc/cmd.go
@@ -1,319 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/VaalaCat/frp-panel/conf"
|
|
||||||
"github.com/VaalaCat/frp-panel/defs"
|
|
||||||
"github.com/VaalaCat/frp-panel/pb"
|
|
||||||
"github.com/VaalaCat/frp-panel/services/app"
|
|
||||||
"github.com/VaalaCat/frp-panel/services/rpc"
|
|
||||||
"github.com/VaalaCat/frp-panel/utils"
|
|
||||||
"github.com/VaalaCat/frp-panel/utils/logger"
|
|
||||||
"github.com/joho/godotenv"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
clientCmd *cobra.Command
|
|
||||||
rootCmd *cobra.Command
|
|
||||||
)
|
|
||||||
|
|
||||||
func initCommand(appInstance app.Application) {
|
|
||||||
rootCmd = &cobra.Command{
|
|
||||||
Use: "frp-panel",
|
|
||||||
Short: "frp-panel is a frp panel QwQ",
|
|
||||||
}
|
|
||||||
CmdListWithFlag := initCmdWithFlag(appInstance)
|
|
||||||
CmdListWithoutFlag := initCmdWithoutFlag(appInstance)
|
|
||||||
rootCmd.AddCommand(CmdListWithFlag...)
|
|
||||||
rootCmd.AddCommand(CmdListWithoutFlag...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func initCmdWithFlag(appInstance app.Application) []*cobra.Command {
|
|
||||||
var (
|
|
||||||
clientSecret string
|
|
||||||
clientID string
|
|
||||||
rpcHost string
|
|
||||||
apiHost string
|
|
||||||
rpcPort int
|
|
||||||
apiPort int
|
|
||||||
apiScheme string
|
|
||||||
joinToken string
|
|
||||||
rpcUrl string
|
|
||||||
apiUrl string
|
|
||||||
)
|
|
||||||
|
|
||||||
clientCmd = &cobra.Command{
|
|
||||||
Use: "client [-s client secret] [-i client id] [-t api host] [-r rpc host] [-c rpc port] [-p api port]",
|
|
||||||
Short: "run managed frpc",
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
run := func() {
|
|
||||||
patchConfig(appInstance,
|
|
||||||
apiHost, rpcHost,
|
|
||||||
clientID, clientSecret,
|
|
||||||
apiScheme, rpcPort, apiPort,
|
|
||||||
apiUrl, rpcUrl)
|
|
||||||
runClient(appInstance)
|
|
||||||
}
|
|
||||||
if srv, err := utils.CreateSystemService(args, run); err != nil {
|
|
||||||
run()
|
|
||||||
} else {
|
|
||||||
srv.Run()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
joinCmd := &cobra.Command{
|
|
||||||
Use: "join [-j join token] [-r rpc host] [-p api port] [-e api scheme]",
|
|
||||||
Short: "join to master with token, save param to config",
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
pullRunConfig(appInstance,
|
|
||||||
joinToken, rpcHost, apiScheme, rpcPort, apiPort, clientID, apiHost, apiUrl, rpcUrl)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
clientCmd.Flags().StringVarP(&clientSecret, "secret", "s", "", "client secret")
|
|
||||||
clientCmd.Flags().StringVarP(&clientID, "id", "i", "", "client id")
|
|
||||||
|
|
||||||
clientCmd.Flags().StringVar(&rpcUrl, "rpc-url", "", "rpc url, master rpc url, scheme can be grpc/ws/wss://hostname:port")
|
|
||||||
|
|
||||||
clientCmd.Flags().StringVar(&apiUrl, "api-url", "", "api url, master api url, scheme can be http/https://hostname:port")
|
|
||||||
|
|
||||||
// deprecated start
|
|
||||||
clientCmd.Flags().StringVarP(&rpcHost, "rpc", "r", "", "deprecated, use --rpc-url instead, rpc host, canbe ip or domain")
|
|
||||||
clientCmd.Flags().StringVarP(&apiHost, "api", "t", "", "deprecated, use --api-url instead, api host, canbe ip or domain")
|
|
||||||
clientCmd.Flags().IntVarP(&rpcPort, "rpc-port", "c", 0, "deprecated, use --rpc-url instead, rpc port, master rpc port, scheme is grpc")
|
|
||||||
clientCmd.Flags().IntVarP(&apiPort, "api-port", "p", 0, "deprecated, use --api-url instead, api port, master api port, scheme is http/https")
|
|
||||||
clientCmd.Flags().StringVarP(&apiScheme, "api-scheme", "e", "", "deprecated, use --api-url instead, api scheme, master api scheme, scheme is http/https")
|
|
||||||
joinCmd.Flags().IntVarP(&rpcPort, "rpc-port", "c", 0, "deprecated, use --rpc-url instead, rpc port, master rpc port, scheme is grpc")
|
|
||||||
joinCmd.Flags().IntVarP(&apiPort, "api-port", "p", 0, "deprecated, use --api-url instead, api port, master api port, scheme is http/https")
|
|
||||||
joinCmd.Flags().StringVarP(&rpcHost, "rpc", "r", "", "deprecated, use --rpc-url instead, rpc host, canbe ip or domain")
|
|
||||||
joinCmd.Flags().StringVarP(&apiHost, "api", "t", "", "deprecated, use --api-url instead, api host, canbe ip or domain")
|
|
||||||
joinCmd.Flags().StringVarP(&apiScheme, "api-scheme", "e", "", "deprecated, use --api-url instead, api scheme, master api scheme, scheme is http/https")
|
|
||||||
// deprecated end
|
|
||||||
|
|
||||||
joinCmd.Flags().StringVarP(&joinToken, "join-token", "j", "", "your token from master")
|
|
||||||
joinCmd.Flags().StringVarP(&clientID, "id", "i", "", "client id")
|
|
||||||
joinCmd.Flags().StringVar(&rpcUrl, "rpc-url", "", "rpc url, master rpc url, scheme can be grpc/ws/wss://hostname:port")
|
|
||||||
joinCmd.Flags().StringVar(&apiUrl, "api-url", "", "api url, master api url, scheme can be http/https://hostname:port")
|
|
||||||
|
|
||||||
return []*cobra.Command{clientCmd, joinCmd}
|
|
||||||
}
|
|
||||||
|
|
||||||
func initCmdWithoutFlag(appInstance app.Application) []*cobra.Command {
|
|
||||||
|
|
||||||
installServiceCmd := &cobra.Command{
|
|
||||||
Use: "install",
|
|
||||||
Short: "install frp-panel as service",
|
|
||||||
DisableFlagParsing: true,
|
|
||||||
DisableFlagsInUseLine: true,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
utils.ControlSystemService(args, "install", func() {})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
uninstallServiceCmd := &cobra.Command{
|
|
||||||
Use: "uninstall",
|
|
||||||
Short: "uninstall frp-panel service",
|
|
||||||
DisableFlagParsing: true,
|
|
||||||
DisableFlagsInUseLine: true,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
utils.ControlSystemService(args, "uninstall", func() {})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
startServiceCmd := &cobra.Command{
|
|
||||||
Use: "start",
|
|
||||||
Short: "start frp-panel service",
|
|
||||||
DisableFlagParsing: true,
|
|
||||||
DisableFlagsInUseLine: true,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
utils.ControlSystemService(args, "start", func() {})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
stopServiceCmd := &cobra.Command{
|
|
||||||
Use: "stop",
|
|
||||||
Short: "stop frp-panel service",
|
|
||||||
DisableFlagParsing: true,
|
|
||||||
DisableFlagsInUseLine: true,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
utils.ControlSystemService(args, "stop", func() {})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
restartServiceCmd := &cobra.Command{
|
|
||||||
Use: "restart",
|
|
||||||
Short: "restart frp-panel service",
|
|
||||||
DisableFlagParsing: true,
|
|
||||||
DisableFlagsInUseLine: true,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
utils.ControlSystemService(args, "restart", func() {})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
versionCmd := &cobra.Command{
|
|
||||||
Use: "version",
|
|
||||||
Short: "Print the version info of frp-panel",
|
|
||||||
Long: `All software has versions. This is frp-panel's`,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
fmt.Println(conf.GetVersion().String())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return []*cobra.Command{
|
|
||||||
installServiceCmd,
|
|
||||||
uninstallServiceCmd,
|
|
||||||
startServiceCmd,
|
|
||||||
stopServiceCmd,
|
|
||||||
restartServiceCmd,
|
|
||||||
versionCmd,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func patchConfig(appInstance app.Application, apiHost, rpcHost, clientID, clientSecret, apiScheme string, rpcPort, apiPort int, apiUrl, rpcUrl string) {
|
|
||||||
c := context.Background()
|
|
||||||
tmpCfg := appInstance.GetConfig()
|
|
||||||
if len(rpcHost) != 0 {
|
|
||||||
tmpCfg.Master.RPCHost = rpcHost
|
|
||||||
tmpCfg.Master.APIHost = rpcHost
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(apiHost) != 0 {
|
|
||||||
tmpCfg.Master.APIHost = apiHost
|
|
||||||
}
|
|
||||||
|
|
||||||
if rpcPort != 0 {
|
|
||||||
tmpCfg.Master.RPCPort = rpcPort
|
|
||||||
}
|
|
||||||
if apiPort != 0 {
|
|
||||||
tmpCfg.Master.APIPort = apiPort
|
|
||||||
}
|
|
||||||
if len(apiScheme) != 0 {
|
|
||||||
tmpCfg.Master.APIScheme = apiScheme
|
|
||||||
}
|
|
||||||
if len(clientID) != 0 {
|
|
||||||
tmpCfg.Client.ID = clientID
|
|
||||||
}
|
|
||||||
if len(clientSecret) != 0 {
|
|
||||||
tmpCfg.Client.Secret = clientSecret
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(apiUrl) != 0 {
|
|
||||||
tmpCfg.Client.APIUrl = apiUrl
|
|
||||||
}
|
|
||||||
if len(rpcUrl) != 0 {
|
|
||||||
tmpCfg.Client.RPCUrl = rpcUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
if rpcPort != 0 || apiPort != 0 || len(apiScheme) != 0 || len(rpcHost) != 0 || len(apiHost) != 0 {
|
|
||||||
logger.Logger(c).Warnf("deprecatedenv configs !!! rpc host: %s, rpc port: %d, api host: %s, api port: %d, api scheme: %s",
|
|
||||||
tmpCfg.Master.RPCHost, tmpCfg.Master.RPCPort,
|
|
||||||
tmpCfg.Master.APIHost, tmpCfg.Master.APIPort,
|
|
||||||
tmpCfg.Master.APIScheme)
|
|
||||||
}
|
|
||||||
logger.Logger(c).Infof("env config, api url: %s, rpc url: %s", tmpCfg.Client.APIUrl, tmpCfg.Client.RPCUrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setMasterCommandIfNonePresent() {
|
|
||||||
cmd, _, err := rootCmd.Find(os.Args[1:])
|
|
||||||
if err == nil && cmd.Use == rootCmd.Use && cmd.Flags().Parse(os.Args[1:]) != pflag.ErrHelp {
|
|
||||||
args := append([]string{"master"}, os.Args[1:]...)
|
|
||||||
rootCmd.SetArgs(args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func pullRunConfig(appInstance app.Application, joinToken, rpcHost, apiScheme string, rpcPort, apiPort int, clientID, apiHost string, apiUrl, rpcUrl string) {
|
|
||||||
c := context.Background()
|
|
||||||
if err := checkPullParams(joinToken, apiHost, apiScheme, apiPort, apiUrl); err != nil {
|
|
||||||
logger.Logger(c).Errorf("check pull params failed: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := utils.EnsureDirectoryExists(defs.SysEnvPath); err != nil {
|
|
||||||
logger.Logger(c).Errorf("ensure directory failed: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(clientID) == 0 {
|
|
||||||
clientID = utils.GetHostnameWithIP()
|
|
||||||
}
|
|
||||||
|
|
||||||
clientID = utils.MakeClientIDPermited(clientID)
|
|
||||||
patchConfig(appInstance, apiHost, rpcHost, "", "", apiScheme, rpcPort, apiPort, apiUrl, rpcUrl)
|
|
||||||
|
|
||||||
initResp, err := rpc.InitClient(appInstance, clientID, joinToken)
|
|
||||||
if err != nil {
|
|
||||||
logger.Logger(c).Errorf("init client failed: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if initResp == nil {
|
|
||||||
logger.Logger(c).Errorf("init resp is nil")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if initResp.GetStatus().GetCode() != pb.RespCode_RESP_CODE_SUCCESS {
|
|
||||||
logger.Logger(c).Errorf("init client failed with status: %s", initResp.GetStatus().GetMessage())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
clientID = initResp.GetClientId()
|
|
||||||
clientResp, err := rpc.GetClient(appInstance, clientID, joinToken)
|
|
||||||
if err != nil {
|
|
||||||
logger.Logger(c).Errorf("get client failed: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if clientResp == nil {
|
|
||||||
logger.Logger(c).Errorf("client resp is nil")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if clientResp.GetStatus().GetCode() != pb.RespCode_RESP_CODE_SUCCESS {
|
|
||||||
logger.Logger(c).Errorf("client resp code is not success: %s", clientResp.GetStatus().GetMessage())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client := clientResp.GetClient()
|
|
||||||
if client == nil {
|
|
||||||
logger.Logger(c).Errorf("client is nil")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
envMap, err := godotenv.Read(defs.SysEnvPath)
|
|
||||||
if err != nil {
|
|
||||||
envMap = make(map[string]string)
|
|
||||||
logger.Logger(c).Warnf("read env file failed, try to create: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
envMap[defs.EnvClientID] = clientID
|
|
||||||
envMap[defs.EnvClientSecret] = client.GetSecret()
|
|
||||||
envMap[defs.EnvClientAPIUrl] = apiUrl
|
|
||||||
envMap[defs.EnvClientRPCUrl] = rpcUrl
|
|
||||||
|
|
||||||
if err = godotenv.Write(envMap, defs.SysEnvPath); err != nil {
|
|
||||||
logger.Logger(c).Errorf("write env file failed: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.Logger(c).Infof("config saved to env file: %s, you can use `frp-panel client` without args to run client,\n\nconfig is: [%v]", defs.SysEnvPath, envMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkPullParams(joinToken, apiHost, apiScheme string, apiPort int, apiUrl string) error {
|
|
||||||
if len(joinToken) == 0 {
|
|
||||||
return errors.New("join token is empty")
|
|
||||||
}
|
|
||||||
if len(apiUrl) == 0 {
|
|
||||||
if len(apiHost) == 0 {
|
|
||||||
return errors.New("api host is empty")
|
|
||||||
}
|
|
||||||
if len(apiScheme) == 0 {
|
|
||||||
return errors.New("api scheme is empty")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if apiPort == 0 {
|
|
||||||
return errors.New("api port is empty")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@@ -1,13 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"github.com/VaalaCat/frp-panel/cmd/frpp/shared"
|
||||||
|
|
||||||
"github.com/VaalaCat/frp-panel/biz/common"
|
|
||||||
"github.com/VaalaCat/frp-panel/biz/master/shell"
|
|
||||||
"github.com/VaalaCat/frp-panel/conf"
|
"github.com/VaalaCat/frp-panel/conf"
|
||||||
"github.com/VaalaCat/frp-panel/services/app"
|
|
||||||
"github.com/VaalaCat/frp-panel/services/rpc"
|
|
||||||
"github.com/VaalaCat/frp-panel/utils/logger"
|
"github.com/VaalaCat/frp-panel/utils/logger"
|
||||||
"github.com/fatedier/golib/crypto"
|
"github.com/fatedier/golib/crypto"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -15,20 +10,20 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
crypto.DefaultSalt = "frp"
|
crypto.DefaultSalt = "frp"
|
||||||
|
|
||||||
logger.InitLogger()
|
logger.InitLogger()
|
||||||
cobra.MousetrapHelpText = ""
|
cobra.MousetrapHelpText = ""
|
||||||
cfg := conf.NewConfig()
|
|
||||||
|
|
||||||
appInstance := app.NewApp()
|
rootCmd := shared.NewRootCmd(
|
||||||
appInstance.SetConfig(cfg)
|
shared.NewClientCmd(conf.NewConfig()),
|
||||||
appInstance.SetClientsManager(rpc.NewClientsManager())
|
shared.NewJoinCmd(),
|
||||||
appInstance.SetStreamLogHookMgr(&common.HookMgr{})
|
shared.NewInstallServiceCmd(),
|
||||||
appInstance.SetShellPTYMgr(shell.NewPTYMgr())
|
shared.NewUninstallServiceCmd(),
|
||||||
appInstance.SetClientRecvMap(&sync.Map{})
|
shared.NewStartServiceCmd(),
|
||||||
|
shared.NewStopServiceCmd(),
|
||||||
|
shared.NewRestartServiceCmd(),
|
||||||
|
shared.NewVersionCmd(),
|
||||||
|
)
|
||||||
|
|
||||||
initCommand(appInstance)
|
shared.SetClientCommandIfNonePresent(rootCmd)
|
||||||
|
|
||||||
setMasterCommandIfNonePresent()
|
|
||||||
rootCmd.Execute()
|
rootCmd.Execute()
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@ package common
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/VaalaCat/frp-panel/defs"
|
"github.com/VaalaCat/frp-panel/defs"
|
||||||
"github.com/VaalaCat/frp-panel/models"
|
"github.com/VaalaCat/frp-panel/models"
|
||||||
@@ -20,3 +21,27 @@ func GetUserInfo(c context.Context) models.UserInfo {
|
|||||||
|
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetTokenPermission(c context.Context) ([]defs.APIPermission, error) {
|
||||||
|
val := c.Value(defs.TokenPayloadKey_Permissions)
|
||||||
|
if val == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
perms := []defs.APIPermission{}
|
||||||
|
err = json.Unmarshal(raw, &perms)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return perms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTokenString(c context.Context) string {
|
||||||
|
return c.Value(defs.TokenKey).(string)
|
||||||
|
}
|
||||||
|
@@ -24,7 +24,7 @@ type ReqType interface {
|
|||||||
pb.StartFRPCRequest | pb.StopFRPCRequest | pb.StartFRPSRequest | pb.StopFRPSRequest |
|
pb.StartFRPCRequest | pb.StopFRPCRequest | pb.StartFRPSRequest | pb.StopFRPSRequest |
|
||||||
pb.GetProxyStatsByClientIDRequest | pb.GetProxyStatsByServerIDRequest |
|
pb.GetProxyStatsByClientIDRequest | pb.GetProxyStatsByServerIDRequest |
|
||||||
pb.CreateProxyConfigRequest | pb.ListProxyConfigsRequest | pb.UpdateProxyConfigRequest |
|
pb.CreateProxyConfigRequest | pb.ListProxyConfigsRequest | pb.UpdateProxyConfigRequest |
|
||||||
pb.DeleteProxyConfigRequest | pb.GetProxyConfigRequest
|
pb.DeleteProxyConfigRequest | pb.GetProxyConfigRequest | pb.SignTokenRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetProtoRequest[T ReqType](c *gin.Context) (r *T, err error) {
|
func GetProtoRequest[T ReqType](c *gin.Context) (r *T, err error) {
|
||||||
|
@@ -25,7 +25,7 @@ type RespType interface {
|
|||||||
pb.StartFRPCResponse | pb.StopFRPCResponse | pb.StartFRPSResponse | pb.StopFRPSResponse |
|
pb.StartFRPCResponse | pb.StopFRPCResponse | pb.StartFRPSResponse | pb.StopFRPSResponse |
|
||||||
pb.GetProxyStatsByClientIDResponse | pb.GetProxyStatsByServerIDResponse |
|
pb.GetProxyStatsByClientIDResponse | pb.GetProxyStatsByServerIDResponse |
|
||||||
pb.CreateProxyConfigResponse | pb.ListProxyConfigsResponse | pb.UpdateProxyConfigResponse |
|
pb.CreateProxyConfigResponse | pb.ListProxyConfigsResponse | pb.UpdateProxyConfigResponse |
|
||||||
pb.DeleteProxyConfigResponse | pb.GetProxyConfigResponse
|
pb.DeleteProxyConfigResponse | pb.GetProxyConfigResponse | pb.SignTokenResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
func OKResp[T RespType](c *gin.Context, origin *T) {
|
func OKResp[T RespType](c *gin.Context, origin *T) {
|
||||||
|
@@ -58,19 +58,38 @@ func FRPsAuthOption(cfg Config, isDefault bool) v1.HTTPPluginOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCommonJWT(cfg Config, uid string) string {
|
func GetJWTWithPayload(cfg Config, uid int, payload map[string]interface{}) (string, error) {
|
||||||
token, _ := utils.GetJwtTokenFromMap(JWTSecret(cfg),
|
payload[defs.UserIDKey] = uid
|
||||||
|
return utils.GetJwtTokenFromMap(JWTSecret(cfg),
|
||||||
time.Now().Unix(),
|
time.Now().Unix(),
|
||||||
int64(cfg.App.CookieAge),
|
int64(cfg.App.CookieAge),
|
||||||
map[string]string{defs.UserIDKey: uid})
|
payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetJWTWithAllPermission(cfg Config, uid int) string {
|
||||||
|
token, _ := GetJWTWithPayload(cfg, uid, map[string]interface{}{
|
||||||
|
defs.TokenPayloadKey_Permissions: AllPermission(),
|
||||||
|
})
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCommonJWTWithExpireTime(cfg Config, uid string, expSec int) string {
|
func AllPermission() []defs.APIPermission {
|
||||||
|
return []defs.APIPermission{{
|
||||||
|
Method: "*",
|
||||||
|
Path: "*",
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCommonJWT(cfg Config, uid int) string {
|
||||||
|
token, _ := GetJWTWithPayload(cfg, uid, map[string]interface{}{})
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCommonJWTWithExpireTime(cfg Config, uid int64, expSec int) string {
|
||||||
token, _ := utils.GetJwtTokenFromMap(JWTSecret(cfg),
|
token, _ := utils.GetJwtTokenFromMap(JWTSecret(cfg),
|
||||||
time.Now().Unix(),
|
time.Now().Unix(),
|
||||||
int64(expSec),
|
int64(expSec),
|
||||||
map[string]string{defs.UserIDKey: uid})
|
map[string]interface{}{defs.UserIDKey: uid})
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -54,6 +54,14 @@ const (
|
|||||||
CliTypeClient = "client"
|
CliTypeClient = "client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type AppRole string
|
||||||
|
|
||||||
|
const (
|
||||||
|
AppRole_Client AppRole = CliTypeClient
|
||||||
|
AppRole_Server AppRole = CliTypeServer
|
||||||
|
AppRole_Master AppRole = "master"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultServerID = "default"
|
DefaultServerID = "default"
|
||||||
DefaultAdminUserID = 1
|
DefaultAdminUserID = 1
|
||||||
@@ -77,3 +85,27 @@ const (
|
|||||||
EnvClientAPIUrl = "CLIENT_API_URL"
|
EnvClientAPIUrl = "CLIENT_API_URL"
|
||||||
EnvClientRPCUrl = "CLIENT_RPC_URL"
|
EnvClientRPCUrl = "CLIENT_RPC_URL"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DBRoleDefault = "default"
|
||||||
|
DBRoleRam = "ram"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DBTypeSQLite3 = "sqlite3"
|
||||||
|
DBTypeMysql = "mysql"
|
||||||
|
DBTypePostgres = "postgres"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
UserRole_Admin = "admin"
|
||||||
|
UserRole_Normal = "normal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TokenStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
TokenStatusActive TokenStatus = "active"
|
||||||
|
TokenStatusInactive TokenStatus = "inactive"
|
||||||
|
TokenStatusRevoked TokenStatus = "revoked"
|
||||||
|
)
|
||||||
|
1
defs/error.go
Normal file
1
defs/error.go
Normal file
@@ -0,0 +1 @@
|
|||||||
|
package defs
|
43
defs/rbac_consts.go
Normal file
43
defs/rbac_consts.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package defs
|
||||||
|
|
||||||
|
type RBACAction string
|
||||||
|
|
||||||
|
const (
|
||||||
|
RBACActionCreate RBACAction = "create"
|
||||||
|
RBACActionRead RBACAction = "read"
|
||||||
|
RBACActionUpdate RBACAction = "update"
|
||||||
|
RBACActionDelete RBACAction = "delete"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RBACObj string
|
||||||
|
|
||||||
|
const (
|
||||||
|
RBACObjServer RBACObj = "server"
|
||||||
|
RBACObjClient RBACObj = "client"
|
||||||
|
RBACObjUser RBACObj = "user"
|
||||||
|
RBACObjGroup RBACObj = "group"
|
||||||
|
RBACObjAPI RBACObj = "api"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RBACSubject string
|
||||||
|
|
||||||
|
const (
|
||||||
|
RBACSubjectUser RBACSubject = "user"
|
||||||
|
RBACSubjectGroup RBACSubject = "group"
|
||||||
|
RBACSubjectToken RBACSubject = "token"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RBACDomain string
|
||||||
|
|
||||||
|
const (
|
||||||
|
RBACDomainTenant RBACDomain = "tenant"
|
||||||
|
)
|
||||||
|
|
||||||
|
type APIPermission struct {
|
||||||
|
Method string `json:"method"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
TokenPayloadKey_Permissions = "permissions"
|
||||||
|
)
|
@@ -19,19 +19,19 @@ Client 推荐使用 docker 部署,直接部署在客户机中,可以通过
|
|||||||
点击对应客户端的 `ID (点击查看安装命令)` 一列,弹出不同系统的安装命令,粘贴到对应终端即可安装,这里以 Linux 为例
|
点击对应客户端的 `ID (点击查看安装命令)` 一列,弹出不同系统的安装命令,粘贴到对应终端即可安装,这里以 Linux 为例
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -sSL https://raw.githubusercontent.com/VaalaCat/frp-panel/main/install.sh | bash -s -- client -s abc -i user.s.client1 -a 123123 -r frpp-rpc.example.com -c 9001 -p 9000 -e http
|
curl -fSL https://raw.githubusercontent.com/VaalaCat/frp-panel/main/install.sh | bash -s -- client -s abc -i user.s.client1 -a 123123 -r frpp-rpc.example.com -c 9001 -p 9000 -e http
|
||||||
```
|
```
|
||||||
|
|
||||||
如果你在国内,可以增加github加速到安装脚本前
|
如果你在国内,可以增加github加速到安装脚本前
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -sSL https://ghfast.top/https://raw.githubusercontent.com/VaalaCat/frp-panel/main/install.sh | bash -s -- client -s abc -i user.s.client1 -a 123123 -r frpp-rpc.example.com -c 9001 -p 9000 -e http
|
curl -fSL https://ghfast.top/https://raw.githubusercontent.com/VaalaCat/frp-panel/main/install.sh | bash -s -- client -s abc -i user.s.client1 -a 123123 -r frpp-rpc.example.com -c 9001 -p 9000 -e http
|
||||||
```
|
```
|
||||||
|
|
||||||
注意,如果你使用 反向代理 TLS,需要修改这行命令类似如下:
|
注意,如果你使用 反向代理 TLS,需要修改这行命令类似如下:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -sSL https://ghfast.top/https://raw.githubusercontent.com/VaalaCat/frp-panel/main/install.sh | bash -s -- frp-panel client -s abc -i user.s.client1 -a 123123 -t frpp.example.com -r frpp-rpc.example.com -c 443 -p 443 -e https
|
curl -fSL https://ghfast.top/https://raw.githubusercontent.com/VaalaCat/frp-panel/main/install.sh | bash -s -- frp-panel client -s abc -i user.s.client1 -a 123123 -t frpp.example.com -r frpp-rpc.example.com -c 443 -p 443 -e https
|
||||||
```
|
```
|
||||||
|
|
||||||
## Docker Compose 部署
|
## Docker Compose 部署
|
||||||
|
20
go.mod
20
go.mod
@@ -6,6 +6,8 @@ toolchain go1.24.1
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/UserExistsError/conpty v0.1.4
|
github.com/UserExistsError/conpty v0.1.4
|
||||||
|
github.com/casbin/casbin/v2 v2.105.0
|
||||||
|
github.com/casbin/gorm-adapter/v3 v3.32.0
|
||||||
github.com/coocood/freecache v1.2.4
|
github.com/coocood/freecache v1.2.4
|
||||||
github.com/creack/pty v1.1.24
|
github.com/creack/pty v1.1.24
|
||||||
github.com/fatedier/frp v0.62.0
|
github.com/fatedier/frp v0.62.0
|
||||||
@@ -37,9 +39,9 @@ require (
|
|||||||
golang.org/x/net v0.39.0
|
golang.org/x/net v0.39.0
|
||||||
google.golang.org/grpc v1.67.1
|
google.golang.org/grpc v1.67.1
|
||||||
google.golang.org/protobuf v1.36.5
|
google.golang.org/protobuf v1.36.5
|
||||||
gorm.io/driver/mysql v1.5.2
|
gorm.io/driver/mysql v1.5.7
|
||||||
gorm.io/driver/postgres v1.5.4
|
gorm.io/driver/postgres v1.5.9
|
||||||
gorm.io/gorm v1.25.5
|
gorm.io/gorm v1.25.12
|
||||||
k8s.io/apimachinery v0.28.8
|
k8s.io/apimachinery v0.28.8
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -49,7 +51,9 @@ require (
|
|||||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 // indirect
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect
|
||||||
github.com/bytedance/sonic v1.9.1 // indirect
|
github.com/bytedance/sonic v1.9.1 // indirect
|
||||||
|
github.com/casbin/govaluate v1.3.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||||
github.com/cloudflare/circl v1.6.1 // indirect
|
github.com/cloudflare/circl v1.6.1 // indirect
|
||||||
@@ -68,6 +72,9 @@ require (
|
|||||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||||
|
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||||
|
github.com/golang/mock v1.7.0-rc.1 // indirect
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 // indirect
|
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 // indirect
|
||||||
github.com/gorilla/mux v1.8.1 // indirect
|
github.com/gorilla/mux v1.8.1 // indirect
|
||||||
@@ -77,7 +84,8 @@ require (
|
|||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||||
github.com/jackc/pgx/v5 v5.4.3 // indirect
|
github.com/jackc/pgx/v5 v5.5.5 // indirect
|
||||||
|
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/jonboulle/clockwork v0.4.0 // indirect
|
github.com/jonboulle/clockwork v0.4.0 // indirect
|
||||||
@@ -88,6 +96,7 @@ require (
|
|||||||
github.com/leodido/go-urn v1.2.4 // indirect
|
github.com/leodido/go-urn v1.2.4 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
|
github.com/microsoft/go-mssqldb v1.6.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.23.4 // indirect
|
github.com/onsi/ginkgo/v2 v2.23.4 // indirect
|
||||||
@@ -110,7 +119,6 @@ require (
|
|||||||
github.com/refraction-networking/utls v1.6.7 // indirect
|
github.com/refraction-networking/utls v1.6.7 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
|
||||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 // indirect
|
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 // indirect
|
||||||
github.com/stretchr/objx v0.5.2 // indirect
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
github.com/stretchr/testify v1.10.0 // indirect
|
github.com/stretchr/testify v1.10.0 // indirect
|
||||||
@@ -145,6 +153,8 @@ require (
|
|||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
gorm.io/driver/sqlserver v1.5.3 // indirect
|
||||||
|
gorm.io/plugin/dbresolver v1.5.3 // indirect
|
||||||
gvisor.dev/gvisor v0.0.0-20250425231648-60ec4e7a009d // indirect
|
gvisor.dev/gvisor v0.0.0-20250425231648-60ec4e7a009d // indirect
|
||||||
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect
|
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect
|
||||||
modernc.org/libc v1.22.5 // indirect
|
modernc.org/libc v1.22.5 // indirect
|
||||||
|
107
go.sum
107
go.sum
@@ -1,6 +1,24 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 h1:/iHxaJhsFr0+xVFfbMr5vxz848jyiWuIEDhYq3y5odY=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0 h1:yfJe15aSwEQ6Oo6J+gdfdulPNoZ3TEhmbhLIoxZcA+U=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0/go.mod h1:Q28U+75mpCaSCDowNEmhIo/rmgdkqmkmzI7N6TGR4UY=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0 h1:T028gtTPiYt/RMUfs8nVsAL7FDQrfLlrm/NnRG/zcC4=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0/go.mod h1:cw4zVQgBby0Z5f2v0itn6se2dDP17nTjbZFXW5uPyHA=
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||||
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
|
||||||
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0 h1:HCc0+LpPfpCKs6LGGLAhwBARt9632unrVcI6i8s/8os=
|
||||||
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
|
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
|
||||||
@@ -13,9 +31,17 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
|
|||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
|
||||||
|
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||||
|
github.com/casbin/casbin/v2 v2.105.0 h1:dLj5P6pLApBRat9SADGiLxLZjiDPvA1bsPkyV4PGx6I=
|
||||||
|
github.com/casbin/casbin/v2 v2.105.0/go.mod h1:Ee33aqGrmES+GNL17L0h9X28wXuo829wnNUnS0edAco=
|
||||||
|
github.com/casbin/gorm-adapter/v3 v3.32.0 h1:Au+IOILBIE9clox5BJhI2nA3p9t7Ep1ePlupdGbGfus=
|
||||||
|
github.com/casbin/gorm-adapter/v3 v3.32.0/go.mod h1:Zre/H8p17mpv5U3EaWgPoxLILLdXO3gHW5aoQQpUDZI=
|
||||||
|
github.com/casbin/govaluate v1.3.0 h1:VA0eSY0M2lA86dYd5kPPuNZMUD9QkWnOCnavGrw9myc=
|
||||||
|
github.com/casbin/govaluate v1.3.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
@@ -38,6 +64,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
|
||||||
|
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE=
|
github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE=
|
||||||
@@ -90,10 +118,20 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
|
|||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
|
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||||
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
|
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||||
|
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||||
|
github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U=
|
||||||
|
github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
@@ -117,10 +155,13 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX
|
|||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 h1:gD0vax+4I+mAj+jEChEf25Ia07Jq7kYOFO5PPhAxFl4=
|
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 h1:gD0vax+4I+mAj+jEChEf25Ia07Jq7kYOFO5PPhAxFl4=
|
||||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
||||||
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||||
|
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||||
|
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
@@ -128,6 +169,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY
|
|||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||||
github.com/iamacarpet/go-winpty v1.0.4 h1:r42xaLLRZcUqjX6vHZeHos2haACfWkooOJTnFdogBfI=
|
github.com/iamacarpet/go-winpty v1.0.4 h1:r42xaLLRZcUqjX6vHZeHos2haACfWkooOJTnFdogBfI=
|
||||||
@@ -142,10 +185,18 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
|
|||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY=
|
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
|
||||||
github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
|
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
github.com/jackpal/gateway v1.0.16 h1:mTBRuHSW8qviVqX7kXnxKevqlfS/OA01ys6k6fxSX7w=
|
github.com/jackpal/gateway v1.0.16 h1:mTBRuHSW8qviVqX7kXnxKevqlfS/OA01ys6k6fxSX7w=
|
||||||
github.com/jackpal/gateway v1.0.16/go.mod h1:IOn1OUbso/cGYmnCBZbCEqhNCLSz0xxdtIpUpri5/nA=
|
github.com/jackpal/gateway v1.0.16/go.mod h1:IOn1OUbso/cGYmnCBZbCEqhNCLSz0xxdtIpUpri5/nA=
|
||||||
|
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||||
|
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
|
||||||
|
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
|
||||||
|
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
|
||||||
|
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
|
||||||
|
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
||||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
@@ -170,20 +221,28 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
|||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||||
|
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
|
||||||
|
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/microsoft/go-mssqldb v1.6.0 h1:mM3gYdVwEPFrlg/Dvr2DNVEgYFG7L42l+dGc67NNNpc=
|
||||||
|
github.com/microsoft/go-mssqldb v1.6.0/go.mod h1:00mDtPbeQCRGC1HwOOR5K/gr30P1NcEG0vx6Kbv2aJU=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
||||||
|
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||||
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
|
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
|
||||||
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
|
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
|
||||||
github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU=
|
github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU=
|
||||||
@@ -202,6 +261,8 @@ github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouAN
|
|||||||
github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
|
github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
|
||||||
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
|
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
|
||||||
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
|
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
|
||||||
|
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||||
|
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
@@ -297,6 +358,7 @@ github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lL
|
|||||||
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0=
|
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0=
|
||||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
|
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
@@ -318,10 +380,14 @@ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUu
|
|||||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||||
|
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||||
|
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||||
@@ -331,6 +397,7 @@ golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudw
|
|||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||||
@@ -341,10 +408,14 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
|
|||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||||
@@ -356,6 +427,7 @@ golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT
|
|||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||||
@@ -369,7 +441,10 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@@ -385,6 +460,7 @@ golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
|||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||||
@@ -393,10 +469,13 @@ golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
|||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||||
@@ -405,14 +484,18 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
|
|||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
||||||
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
||||||
@@ -442,6 +525,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
|
|||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
@@ -449,13 +533,18 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs=
|
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
|
||||||
gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8=
|
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
||||||
gorm.io/driver/postgres v1.5.4 h1:Iyrp9Meh3GmbSuyIAGyjkN+n9K+GHX9b9MqsTL4EJCo=
|
gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8=
|
||||||
gorm.io/driver/postgres v1.5.4/go.mod h1:Bgo89+h0CRcdA33Y6frlaHHVuTdOf87pmyzwW9C/BH0=
|
gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
|
||||||
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
gorm.io/driver/sqlserver v1.5.3 h1:rjupPS4PVw+rjJkfvr8jn2lJ8BMhT4UW5FwuJY0P3Z0=
|
||||||
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
|
gorm.io/driver/sqlserver v1.5.3/go.mod h1:B+CZ0/7oFJ6tAlefsKoyxdgDCXJKSgwS2bMOQZT0I00=
|
||||||
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||||
|
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||||
|
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||||
|
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||||
|
gorm.io/plugin/dbresolver v1.5.3 h1:wFwINGZZmttuu9h7XpvbDHd8Lf9bb8GNzp/NpAMV2wU=
|
||||||
|
gorm.io/plugin/dbresolver v1.5.3/go.mod h1:TSrVhaUg2DZAWP3PrHlDlITEJmNOkL0tFTjvTEsQ4XE=
|
||||||
gvisor.dev/gvisor v0.0.0-20250425231648-60ec4e7a009d h1:cCKla0V7sa6eixh74LtGQXakTu5QJEzkcX7DzNRhFOE=
|
gvisor.dev/gvisor v0.0.0-20250425231648-60ec4e7a009d h1:cCKla0V7sa6eixh74LtGQXakTu5QJEzkcX7DzNRhFOE=
|
||||||
gvisor.dev/gvisor v0.0.0-20250425231648-60ec4e7a009d/go.mod h1:3r5CMtNQMKIvBlrmM9xWUNamjKBYPOWyXOjmg5Kts3g=
|
gvisor.dev/gvisor v0.0.0-20250425231648-60ec4e7a009d/go.mod h1:3r5CMtNQMKIvBlrmM9xWUNamjKBYPOWyXOjmg5Kts3g=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
@@ -22,4 +22,19 @@ message RegisterRequest {
|
|||||||
|
|
||||||
message RegisterResponse {
|
message RegisterResponse {
|
||||||
optional common.Status status = 1;
|
optional common.Status status = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message APIPermission {
|
||||||
|
optional string method = 1;
|
||||||
|
optional string path = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SignTokenRequest {
|
||||||
|
optional int64 expires_in = 1;
|
||||||
|
repeated APIPermission permissions = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SignTokenResponse {
|
||||||
|
optional common.Status status = 1;
|
||||||
|
optional string token = 2;
|
||||||
}
|
}
|
@@ -7,6 +7,7 @@ option go_package="../pb";
|
|||||||
|
|
||||||
message InitClientRequest {
|
message InitClientRequest {
|
||||||
optional string client_id = 1;
|
optional string client_id = 1;
|
||||||
|
optional bool ephemeral = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message InitClientResponse {
|
message InitClientResponse {
|
||||||
@@ -145,4 +146,4 @@ message GetProxyConfigResponse {
|
|||||||
optional common.Status status = 1;
|
optional common.Status status = 1;
|
||||||
optional common.ProxyConfig proxy_config = 2;
|
optional common.ProxyConfig proxy_config = 2;
|
||||||
optional common.ProxyWorkingStatus working_status = 3;
|
optional common.ProxyWorkingStatus working_status = 3;
|
||||||
}
|
}
|
||||||
|
@@ -43,6 +43,8 @@ message Client {
|
|||||||
repeated string client_ids = 8; // some client can connected to more than one server, make a shadow client to handle this
|
repeated string client_ids = 8; // some client can connected to more than one server, make a shadow client to handle this
|
||||||
optional string origin_client_id = 9;
|
optional string origin_client_id = 9;
|
||||||
optional string frps_url = 10; // 客户端用于连接frps的url,解决 frp 在 CDN 后的问题,格式类似 [tcp/ws/wss/quic/kcp]://example.com:7000
|
optional string frps_url = 10; // 客户端用于连接frps的url,解决 frp 在 CDN 后的问题,格式类似 [tcp/ws/wss/quic/kcp]://example.com:7000
|
||||||
|
optional bool ephemeral = 11; // 是否临时节点
|
||||||
|
optional int64 last_seen_at = 12; // 最后一次心跳时间戳
|
||||||
}
|
}
|
||||||
|
|
||||||
message Server {
|
message Server {
|
||||||
@@ -93,4 +95,4 @@ message ProxyWorkingStatus {
|
|||||||
optional string status = 3;
|
optional string status = 3;
|
||||||
optional string err = 4;
|
optional string err = 4;
|
||||||
optional string remote_addr = 5;
|
optional string remote_addr = 5;
|
||||||
}
|
}
|
||||||
|
@@ -6,9 +6,9 @@ import (
|
|||||||
"github.com/VaalaCat/frp-panel/models"
|
"github.com/VaalaCat/frp-panel/models"
|
||||||
"github.com/VaalaCat/frp-panel/services/app"
|
"github.com/VaalaCat/frp-panel/services/app"
|
||||||
"github.com/VaalaCat/frp-panel/services/dao"
|
"github.com/VaalaCat/frp-panel/services/dao"
|
||||||
"github.com/VaalaCat/frp-panel/utils"
|
|
||||||
"github.com/VaalaCat/frp-panel/utils/logger"
|
"github.com/VaalaCat/frp-panel/utils/logger"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/spf13/cast"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AuthCtx(appInstance app.Application) func(*gin.Context) {
|
func AuthCtx(appInstance app.Application) func(*gin.Context) {
|
||||||
@@ -22,9 +22,9 @@ func AuthCtx(appInstance app.Application) func(*gin.Context) {
|
|||||||
logger.Logger(c).Info("finish auth user middleware")
|
logger.Logger(c).Info("finish auth user middleware")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
userID, ok := utils.GetValue[int](c, defs.UserIDKey)
|
userID, err := cast.ToIntE(c.Value(defs.UserIDKey))
|
||||||
if !ok {
|
if err != nil {
|
||||||
logger.Logger(c).Errorf("invalid user id")
|
logger.Logger(c).WithError(err).Errorf("invalid user id: %v", c.Value(defs.UserIDKey))
|
||||||
common.ErrUnAuthorized(c, "token invalid")
|
common.ErrUnAuthorized(c, "token invalid")
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
@@ -61,7 +61,7 @@ func AuthCtx(appInstance app.Application) func(*gin.Context) {
|
|||||||
|
|
||||||
func AuthAdmin(c *gin.Context) {
|
func AuthAdmin(c *gin.Context) {
|
||||||
u := common.GetUserInfo(c)
|
u := common.GetUserInfo(c)
|
||||||
if u != nil && u.GetRole() == models.ROLE_ADMIN {
|
if u != nil && u.GetRole() == defs.UserRole_Admin {
|
||||||
common.ErrUnAuthorized(c, "permission denied")
|
common.ErrUnAuthorized(c, "permission denied")
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
|
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/VaalaCat/frp-panel/utils/logger"
|
"github.com/VaalaCat/frp-panel/utils/logger"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
"github.com/spf13/cast"
|
||||||
)
|
)
|
||||||
|
|
||||||
// JWTMAuth check if authed and set uid to context
|
// JWTMAuth check if authed and set uid to context
|
||||||
@@ -30,14 +31,14 @@ func JWTAuth(appInstance app.Application) func(c *gin.Context) {
|
|||||||
c.Set(k, v)
|
c.Set(k, v)
|
||||||
}
|
}
|
||||||
logger.Logger(c).Infof("query auth success")
|
logger.Logger(c).Infof("query auth success")
|
||||||
if err = resignAndPatchCtxJWT(c, appInstance, t, tokenStr); err != nil {
|
if err = resignAndPatchCtxJWT(c, appInstance, cast.ToInt(t[defs.UserIDKey]), t, tokenStr); err != nil {
|
||||||
logger.Logger(c).WithError(err).Errorf("resign jwt error")
|
logger.Logger(c).WithError(err).Errorf("resign jwt error")
|
||||||
common.ErrUnAuthorized(c, "resign jwt error")
|
common.ErrUnAuthorized(c, "resign jwt error")
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.Next()
|
c.Next()
|
||||||
SetToken(c, appInstance, utils.ToStr(t[defs.UserIDKey]))
|
SetToken(c, appInstance, cast.ToInt(t[defs.UserIDKey]), t)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logger.Logger(c).Infof("query auth failed")
|
logger.Logger(c).Infof("query auth failed")
|
||||||
@@ -50,7 +51,7 @@ func JWTAuth(appInstance app.Application) func(c *gin.Context) {
|
|||||||
c.Set(k, v)
|
c.Set(k, v)
|
||||||
}
|
}
|
||||||
logger.Logger(c).Infof("cookie auth success")
|
logger.Logger(c).Infof("cookie auth success")
|
||||||
if err = resignAndPatchCtxJWT(c, appInstance, t, cookieToken); err != nil {
|
if err = resignAndPatchCtxJWT(c, appInstance, cast.ToInt(t[defs.UserIDKey]), t, cookieToken); err != nil {
|
||||||
logger.Logger(c).WithError(err).Errorf("resign jwt error")
|
logger.Logger(c).WithError(err).Errorf("resign jwt error")
|
||||||
common.ErrUnAuthorized(c, "resign jwt error")
|
common.ErrUnAuthorized(c, "resign jwt error")
|
||||||
c.Abort()
|
c.Abort()
|
||||||
@@ -80,7 +81,7 @@ func JWTAuth(appInstance app.Application) func(c *gin.Context) {
|
|||||||
c.Set(k, v)
|
c.Set(k, v)
|
||||||
}
|
}
|
||||||
logger.Logger(c).Infof("header auth success")
|
logger.Logger(c).Infof("header auth success")
|
||||||
if err = resignAndPatchCtxJWT(c, appInstance, t, tokenStr); err != nil {
|
if err = resignAndPatchCtxJWT(c, appInstance, cast.ToInt(t[defs.UserIDKey]), t, tokenStr); err != nil {
|
||||||
logger.Logger(c).WithError(err).Errorf("resign jwt error")
|
logger.Logger(c).WithError(err).Errorf("resign jwt error")
|
||||||
common.ErrUnAuthorized(c, "resign jwt error")
|
common.ErrUnAuthorized(c, "resign jwt error")
|
||||||
c.Abort()
|
c.Abort()
|
||||||
@@ -94,7 +95,7 @@ func JWTAuth(appInstance app.Application) func(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func resignAndPatchCtxJWT(c *gin.Context, appInstance app.Application, t jwt.MapClaims, tokenStr string) error {
|
func resignAndPatchCtxJWT(c *gin.Context, appInstance app.Application, userID int, t jwt.MapClaims, tokenStr string) error {
|
||||||
tokenExpire, _ := t.GetExpirationTime()
|
tokenExpire, _ := t.GetExpirationTime()
|
||||||
now := time.Now().Add(time.Duration(appInstance.GetConfig().App.CookieAge/2) * time.Second)
|
now := time.Now().Add(time.Duration(appInstance.GetConfig().App.CookieAge/2) * time.Second)
|
||||||
if now.Before(tokenExpire.Time) {
|
if now.Before(tokenExpire.Time) {
|
||||||
@@ -103,47 +104,32 @@ func resignAndPatchCtxJWT(c *gin.Context, appInstance app.Application, t jwt.Map
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := utils.GetJwtTokenFromMap(conf.JWTSecret(appInstance.GetConfig()),
|
tokenStr, err := SetToken(c, appInstance, userID, t)
|
||||||
time.Now().Unix(),
|
|
||||||
int64(appInstance.GetConfig().App.CookieAge),
|
|
||||||
map[string]string{defs.UserIDKey: utils.ToStr(t[defs.UserIDKey])})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Set(defs.TokenKey, tokenStr)
|
|
||||||
logger.Logger(c).WithError(err).Errorf("resign jwt error")
|
logger.Logger(c).WithError(err).Errorf("resign jwt error")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PushTokenStr(c, appInstance, tokenStr)
|
||||||
|
|
||||||
logger.Logger(c).Infof("jwt going to expire, resigning token")
|
logger.Logger(c).Infof("jwt going to expire, resigning token")
|
||||||
c.Header(defs.SetAuthorizationKey, token)
|
|
||||||
c.SetCookie(appInstance.GetConfig().App.CookieName,
|
|
||||||
token,
|
|
||||||
appInstance.GetConfig().App.CookieAge,
|
|
||||||
appInstance.GetConfig().App.CookiePath,
|
|
||||||
appInstance.GetConfig().App.CookieDomain,
|
|
||||||
appInstance.GetConfig().App.CookieSecure,
|
|
||||||
appInstance.GetConfig().App.CookieHTTPOnly)
|
|
||||||
c.Set(defs.TokenKey, token)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetToken(c *gin.Context, appInstance app.Application, uid string) (string, error) {
|
// SetToken 设置新token并写入ctx
|
||||||
logger.Logger(c).Infof("set token for uid:[%s]", uid)
|
func SetToken(c *gin.Context, appInstance app.Application, userID int, payload jwt.MapClaims) (string, error) {
|
||||||
token, err := utils.GetJwtTokenFromMap(conf.JWTSecret(appInstance.GetConfig()),
|
logger.Logger(c).Debugf("set token for userID:[%d]", userID)
|
||||||
time.Now().Unix(),
|
if payload == nil {
|
||||||
int64(appInstance.GetConfig().App.CookieAge),
|
payload = make(map[string]interface{})
|
||||||
map[string]string{defs.UserIDKey: uid})
|
}
|
||||||
|
|
||||||
|
payload[defs.UserIDKey] = userID
|
||||||
|
|
||||||
|
token, err := conf.GetJWTWithPayload(appInstance.GetConfig(), userID, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
c.SetCookie(appInstance.GetConfig().App.CookieName,
|
|
||||||
token,
|
|
||||||
appInstance.GetConfig().App.CookieAge,
|
|
||||||
appInstance.GetConfig().App.CookiePath,
|
|
||||||
appInstance.GetConfig().App.CookieDomain,
|
|
||||||
appInstance.GetConfig().App.CookieSecure,
|
|
||||||
appInstance.GetConfig().App.CookieHTTPOnly)
|
|
||||||
c.Set(defs.TokenKey, token)
|
c.Set(defs.TokenKey, token)
|
||||||
c.Header(defs.SetAuthorizationKey, token)
|
|
||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
82
middleware/rbac.go
Normal file
82
middleware/rbac.go
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/VaalaCat/frp-panel/common"
|
||||||
|
"github.com/VaalaCat/frp-panel/pb"
|
||||||
|
"github.com/VaalaCat/frp-panel/services/app"
|
||||||
|
"github.com/VaalaCat/frp-panel/utils"
|
||||||
|
"github.com/VaalaCat/frp-panel/utils/logger"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RBAC(appInstance app.Application) func(*gin.Context) {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
// appCtx := app.NewContext(c, appInstance)
|
||||||
|
perms, err := common.GetTokenPermission(c)
|
||||||
|
userInfo := common.GetUserInfo(c)
|
||||||
|
token := common.GetTokenString(c)
|
||||||
|
path := c.Request.URL.Path
|
||||||
|
method := c.Request.Method
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger(c).WithError(err).Errorf("get token permission error, token: [%s], userInfo:[%s]", token, utils.MarshalForJson(userInfo.GetSafeUserInfo()))
|
||||||
|
common.ErrResp(c, &pb.CommonResponse{Status: &pb.Status{Code: pb.RespCode_RESP_CODE_INVALID, Message: err.Error()}}, err.Error())
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(perms) == 0 {
|
||||||
|
logger.Logger(c).WithError(err).Errorf("user has no permission, token: [%s], userInfo:[%s]", token, utils.MarshalForJson(userInfo.GetSafeUserInfo()))
|
||||||
|
common.ErrResp(c, &pb.CommonResponse{Status: &pb.Status{Code: pb.RespCode_RESP_CODE_INVALID, Message: "user has no permission"}}, "user has no permission")
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, perm := range perms {
|
||||||
|
if ruleMatched(ruleMatchParam{
|
||||||
|
RuleMethod: perm.Method,
|
||||||
|
RulePath: perm.Path,
|
||||||
|
RequestPath: path,
|
||||||
|
RequestMethod: method,
|
||||||
|
}) {
|
||||||
|
logger.Logger(c).Infof("user has api permission, continue")
|
||||||
|
c.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Logger(c).Errorf("user has no permission, perms: %s, userInfo: [%s], ", utils.MarshalForJson(perms), utils.MarshalForJson(userInfo.GetSafeUserInfo()))
|
||||||
|
common.ErrResp(c, &pb.CommonResponse{Status: &pb.Status{Code: pb.RespCode_RESP_CODE_INVALID, Message: "user has no permission"}}, "user has no permission")
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ruleMatchParam struct {
|
||||||
|
RuleMethod string
|
||||||
|
RulePath string
|
||||||
|
RequestPath string
|
||||||
|
RequestMethod string
|
||||||
|
}
|
||||||
|
|
||||||
|
func ruleMatched(param ruleMatchParam) bool {
|
||||||
|
methodMatch := false
|
||||||
|
if param.RuleMethod == param.RequestMethod || param.RuleMethod == "*" {
|
||||||
|
methodMatch = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !methodMatch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
pathMatch := false
|
||||||
|
if param.RulePath == "*" {
|
||||||
|
pathMatch = true
|
||||||
|
} else {
|
||||||
|
pathMatch = regexp.MustCompile(param.RulePath).MatchString(param.RequestPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathMatch
|
||||||
|
}
|
@@ -4,7 +4,7 @@ import "gorm.io/gorm"
|
|||||||
|
|
||||||
type Cert struct {
|
type Cert struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
Name string `gorm:"uniqueIndex"`
|
Name string `gorm:"type:varchar(255);uniqueIndex"`
|
||||||
CertFile []byte
|
CertFile []byte
|
||||||
KeyFile []byte
|
KeyFile []byte
|
||||||
CaFile []byte
|
CaFile []byte
|
||||||
|
@@ -25,10 +25,13 @@ type ClientEntity struct {
|
|||||||
Comment string `json:"comment"`
|
Comment string `json:"comment"`
|
||||||
IsShadow bool `json:"is_shadow" gorm:"index"`
|
IsShadow bool `json:"is_shadow" gorm:"index"`
|
||||||
OriginClientID string `json:"origin_client_id" gorm:"index"`
|
OriginClientID string `json:"origin_client_id" gorm:"index"`
|
||||||
FRPsUrl string `json:"frps_url" gorm:"index"`
|
FrpsUrl string `json:"frps_url" gorm:"index"`
|
||||||
CreatedAt time.Time
|
Ephemeral bool `json:"ephemeral" gorm:"index"`
|
||||||
UpdatedAt time.Time
|
|
||||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
LastSeenAt *time.Time `json:"last_seen_at" gorm:"index"`
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Client) TableName() string {
|
func (*Client) TableName() string {
|
||||||
|
89
models/db.go
89
models/db.go
@@ -3,79 +3,80 @@ package models
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/VaalaCat/frp-panel/defs"
|
||||||
"github.com/VaalaCat/frp-panel/utils/logger"
|
"github.com/VaalaCat/frp-panel/utils/logger"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DBManager interface {
|
|
||||||
GetDB(dbType string) *gorm.DB
|
|
||||||
GetDefaultDB() *gorm.DB
|
|
||||||
SetDB(dbType string, db *gorm.DB)
|
|
||||||
RemoveDB(dbType string)
|
|
||||||
SetDebug(bool)
|
|
||||||
Init()
|
|
||||||
}
|
|
||||||
|
|
||||||
type dbManagerImpl struct {
|
type dbManagerImpl struct {
|
||||||
DBs map[string]*gorm.DB // key: db type
|
DBs map[string]map[string]*gorm.DB // map[db type]map[db role]*gorm.DB
|
||||||
defaultDBType string
|
defaultDBType string
|
||||||
debug bool
|
debug bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbm *dbManagerImpl) Init() {
|
func (dbm *dbManagerImpl) Init() {
|
||||||
for _, db := range dbm.DBs {
|
for _, dbGroup := range dbm.DBs {
|
||||||
if err := db.AutoMigrate(&Client{}); err != nil {
|
for _, db := range dbGroup {
|
||||||
logger.Logger(context.Background()).WithError(err).Fatalf("cannot init db table [%s]", (&Client{}).TableName())
|
if err := db.AutoMigrate(&Client{}); err != nil {
|
||||||
}
|
logger.Logger(context.Background()).WithError(err).Fatalf("cannot init db table [%s]", (&Client{}).TableName())
|
||||||
if err := db.AutoMigrate(&User{}); err != nil {
|
}
|
||||||
logger.Logger(context.Background()).WithError(err).Fatalf("cannot init db table [%s]", (&User{}).TableName())
|
if err := db.AutoMigrate(&User{}); err != nil {
|
||||||
}
|
logger.Logger(context.Background()).WithError(err).Fatalf("cannot init db table [%s]", (&User{}).TableName())
|
||||||
if err := db.AutoMigrate(&Server{}); err != nil {
|
}
|
||||||
logger.Logger(context.Background()).WithError(err).Fatalf("cannot init db table [%s]", (&Server{}).TableName())
|
if err := db.AutoMigrate(&Server{}); err != nil {
|
||||||
}
|
logger.Logger(context.Background()).WithError(err).Fatalf("cannot init db table [%s]", (&Server{}).TableName())
|
||||||
if err := db.AutoMigrate(&Cert{}); err != nil {
|
}
|
||||||
logger.Logger(context.Background()).WithError(err).Fatalf("cannot init db table [%s]", (&Cert{}).TableName())
|
if err := db.AutoMigrate(&Cert{}); err != nil {
|
||||||
}
|
logger.Logger(context.Background()).WithError(err).Fatalf("cannot init db table [%s]", (&Cert{}).TableName())
|
||||||
if err := db.AutoMigrate(&ProxyStats{}); err != nil {
|
}
|
||||||
logger.Logger(context.Background()).WithError(err).Fatalf("cannot init db table [%s]", (&ProxyStats{}).TableName())
|
if err := db.AutoMigrate(&ProxyStats{}); err != nil {
|
||||||
}
|
logger.Logger(context.Background()).WithError(err).Fatalf("cannot init db table [%s]", (&ProxyStats{}).TableName())
|
||||||
if err := db.AutoMigrate(&HistoryProxyStats{}); err != nil {
|
}
|
||||||
logger.Logger(context.Background()).WithError(err).Fatalf("cannot init db table [%s]", (&HistoryProxyStats{}).TableName())
|
if err := db.AutoMigrate(&HistoryProxyStats{}); err != nil {
|
||||||
}
|
logger.Logger(context.Background()).WithError(err).Fatalf("cannot init db table [%s]", (&HistoryProxyStats{}).TableName())
|
||||||
if err := db.AutoMigrate(&ProxyConfig{}); err != nil {
|
}
|
||||||
logger.Logger(context.Background()).WithError(err).Fatalf("cannot init db table [%s]", (&ProxyConfig{}).TableName())
|
if err := db.AutoMigrate(&ProxyConfig{}); err != nil {
|
||||||
|
logger.Logger(context.Background()).WithError(err).Fatalf("cannot init db table [%s]", (&ProxyConfig{}).TableName())
|
||||||
|
}
|
||||||
|
if err := db.AutoMigrate(&UserGroup{}); err != nil {
|
||||||
|
logger.Logger(context.Background()).WithError(err).Fatalf("cannot init db table [%s]", (&UserGroup{}).TableName())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDBManager(dbs map[string]*gorm.DB, defaultDBType string) *dbManagerImpl {
|
func NewDBManager(defaultDBType string) *dbManagerImpl {
|
||||||
if dbs == nil {
|
dbs := map[string]map[string]*gorm.DB{}
|
||||||
dbs = make(map[string]*gorm.DB)
|
|
||||||
}
|
|
||||||
return &dbManagerImpl{
|
return &dbManagerImpl{
|
||||||
DBs: dbs,
|
DBs: dbs,
|
||||||
defaultDBType: defaultDBType,
|
defaultDBType: defaultDBType,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbm *dbManagerImpl) GetDB(dbType string) *gorm.DB {
|
func (dbm *dbManagerImpl) GetDB(dbType string, dbRole string) *gorm.DB {
|
||||||
return dbm.DBs[dbType]
|
return dbm.DBs[dbType][dbRole]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbm *dbManagerImpl) SetDB(dbType string, db *gorm.DB) {
|
func (dbm *dbManagerImpl) SetDB(dbType string, dbRole string, db *gorm.DB) {
|
||||||
dbm.DBs[dbType] = db
|
if dbm.DBs[dbType] == nil {
|
||||||
|
dbm.DBs[dbType] = map[string]*gorm.DB{}
|
||||||
|
}
|
||||||
|
dbm.DBs[dbType][dbRole] = db
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbm *dbManagerImpl) RemoveDB(dbType string) {
|
func (dbm *dbManagerImpl) RemoveDB(dbType string, dbRole string) {
|
||||||
delete(dbm.DBs, dbType)
|
if dbm.DBs[dbType] == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete(dbm.DBs[dbType], dbRole)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbm *dbManagerImpl) GetDefaultDB() *gorm.DB {
|
func (dbm *dbManagerImpl) GetDefaultDB() *gorm.DB {
|
||||||
db := dbm.DBs[dbm.defaultDBType]
|
dbGroup := dbm.DBs[dbm.defaultDBType]
|
||||||
if dbm.debug {
|
if dbm.debug {
|
||||||
return db.Debug()
|
return dbGroup[defs.DBRoleDefault].Debug()
|
||||||
}
|
}
|
||||||
return db
|
return dbGroup[defs.DBRoleDefault]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbm *dbManagerImpl) SetDebug(debug bool) {
|
func (dbm *dbManagerImpl) SetDebug(debug bool) {
|
||||||
|
@@ -1,9 +1,6 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
const (
|
|
||||||
ROLE_ADMIN = "admin"
|
|
||||||
ROLE_NORMAL = "normal"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
STATUS_UNKNOWN = iota
|
STATUS_UNKNOWN = iota
|
||||||
|
@@ -16,13 +16,13 @@ type Server struct {
|
|||||||
|
|
||||||
type ServerEntity struct {
|
type ServerEntity struct {
|
||||||
ServerID string `json:"client_id" gorm:"uniqueIndex;not null;primaryKey"`
|
ServerID string `json:"client_id" gorm:"uniqueIndex;not null;primaryKey"`
|
||||||
TenantID int `json:"tenant_id" gorm:"not null"`
|
TenantID int `json:"tenant_id" gorm:"not null,index"`
|
||||||
UserID int `json:"user_id" gorm:"not null"`
|
UserID int `json:"user_id" gorm:"not null"`
|
||||||
ServerIP string `json:"server_ip"`
|
ServerIP string `json:"server_ip"`
|
||||||
ConfigContent []byte `json:"config_content"`
|
ConfigContent []byte `json:"config_content"`
|
||||||
ConnectSecret string `json:"connect_secret" gorm:"not null"`
|
ConnectSecret string `json:"connect_secret" gorm:"not null"`
|
||||||
Comment string `json:"comment"`
|
Comment string `json:"comment"`
|
||||||
FRPsUrls GormArray[string] `json:"frps_urls"`
|
FrpsUrls GormArray[string] `json:"frps_urls"`
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/VaalaCat/frp-panel/defs"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,9 +31,9 @@ var _ UserInfo = (*UserEntity)(nil)
|
|||||||
|
|
||||||
type UserEntity struct {
|
type UserEntity struct {
|
||||||
UserID int `json:"user_id" gorm:"primaryKey"`
|
UserID int `json:"user_id" gorm:"primaryKey"`
|
||||||
UserName string `json:"user_name" gorm:"uniqueIndex;not null"`
|
UserName string `json:"user_name" gorm:"type:varchar(255);uniqueIndex;not null"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Email string `json:"email" gorm:"uniqueIndex;not null"`
|
Email string `json:"email" gorm:"type:varchar(255);uniqueIndex;not null"`
|
||||||
Status int `json:"status"`
|
Status int `json:"status"`
|
||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
TenantID int `json:"tenant_id"`
|
TenantID int `json:"tenant_id"`
|
||||||
@@ -40,6 +41,8 @@ type UserEntity struct {
|
|||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||||
|
|
||||||
|
Groups []*UserGroup `json:"groups,omitempty" gorm:"many2many:user_group_memberships;"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserEntity) GetUserID() int {
|
func (u *UserEntity) GetUserID() int {
|
||||||
@@ -99,7 +102,7 @@ func (u *UserEntity) Valid() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserEntity) IsAdmin() bool {
|
func (u *UserEntity) IsAdmin() bool {
|
||||||
return u.Role == ROLE_ADMIN
|
return u.Role == defs.UserRole_Admin
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) TableName() string {
|
func (u *User) TableName() string {
|
||||||
|
18
models/user_group.go
Normal file
18
models/user_group.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type UserGroup struct {
|
||||||
|
GroupID string `json:"group_id" gorm:"primaryKey"`
|
||||||
|
GroupName string `json:"group_name" gorm:"type:varchar(255);uniqueIndex:idx_group_tenant_name;not null"`
|
||||||
|
TenantID int `json:"tenant_id" gorm:"uniqueIndex:idx_group_tenant_name;not null"`
|
||||||
|
Comment string `json:"comment"`
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
|
||||||
|
Users []*User `json:"users,omitempty" gorm:"many2many:user_group_memberships;"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserGroup) TableName() string {
|
||||||
|
return "user_groups"
|
||||||
|
}
|
@@ -229,6 +229,162 @@ func (x *RegisterResponse) GetStatus() *Status {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type APIPermission struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Method *string `protobuf:"bytes,1,opt,name=method,proto3,oneof" json:"method,omitempty"`
|
||||||
|
Path *string `protobuf:"bytes,2,opt,name=path,proto3,oneof" json:"path,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *APIPermission) Reset() {
|
||||||
|
*x = APIPermission{}
|
||||||
|
mi := &file_api_auth_proto_msgTypes[4]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *APIPermission) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*APIPermission) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *APIPermission) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_api_auth_proto_msgTypes[4]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use APIPermission.ProtoReflect.Descriptor instead.
|
||||||
|
func (*APIPermission) Descriptor() ([]byte, []int) {
|
||||||
|
return file_api_auth_proto_rawDescGZIP(), []int{4}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *APIPermission) GetMethod() string {
|
||||||
|
if x != nil && x.Method != nil {
|
||||||
|
return *x.Method
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *APIPermission) GetPath() string {
|
||||||
|
if x != nil && x.Path != nil {
|
||||||
|
return *x.Path
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignTokenRequest struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
ExpiresIn *int64 `protobuf:"varint,1,opt,name=expires_in,json=expiresIn,proto3,oneof" json:"expires_in,omitempty"`
|
||||||
|
Permissions []*APIPermission `protobuf:"bytes,2,rep,name=permissions,proto3" json:"permissions,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SignTokenRequest) Reset() {
|
||||||
|
*x = SignTokenRequest{}
|
||||||
|
mi := &file_api_auth_proto_msgTypes[5]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SignTokenRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SignTokenRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *SignTokenRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_api_auth_proto_msgTypes[5]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use SignTokenRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*SignTokenRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_api_auth_proto_rawDescGZIP(), []int{5}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SignTokenRequest) GetExpiresIn() int64 {
|
||||||
|
if x != nil && x.ExpiresIn != nil {
|
||||||
|
return *x.ExpiresIn
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SignTokenRequest) GetPermissions() []*APIPermission {
|
||||||
|
if x != nil {
|
||||||
|
return x.Permissions
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignTokenResponse struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Status *Status `protobuf:"bytes,1,opt,name=status,proto3,oneof" json:"status,omitempty"`
|
||||||
|
Token *string `protobuf:"bytes,2,opt,name=token,proto3,oneof" json:"token,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SignTokenResponse) Reset() {
|
||||||
|
*x = SignTokenResponse{}
|
||||||
|
mi := &file_api_auth_proto_msgTypes[6]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SignTokenResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SignTokenResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *SignTokenResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_api_auth_proto_msgTypes[6]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use SignTokenResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*SignTokenResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_api_auth_proto_rawDescGZIP(), []int{6}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SignTokenResponse) GetStatus() *Status {
|
||||||
|
if x != nil {
|
||||||
|
return x.Status
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SignTokenResponse) GetToken() string {
|
||||||
|
if x != nil && x.Token != nil {
|
||||||
|
return *x.Token
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
var File_api_auth_proto protoreflect.FileDescriptor
|
var File_api_auth_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
const file_api_auth_proto_rawDesc = "" +
|
const file_api_auth_proto_rawDesc = "" +
|
||||||
@@ -253,7 +409,22 @@ const file_api_auth_proto_rawDesc = "" +
|
|||||||
"\x06_email\"J\n" +
|
"\x06_email\"J\n" +
|
||||||
"\x10RegisterResponse\x12+\n" +
|
"\x10RegisterResponse\x12+\n" +
|
||||||
"\x06status\x18\x01 \x01(\v2\x0e.common.StatusH\x00R\x06status\x88\x01\x01B\t\n" +
|
"\x06status\x18\x01 \x01(\v2\x0e.common.StatusH\x00R\x06status\x88\x01\x01B\t\n" +
|
||||||
"\a_statusB\aZ\x05../pbb\x06proto3"
|
"\a_status\"Y\n" +
|
||||||
|
"\rAPIPermission\x12\x1b\n" +
|
||||||
|
"\x06method\x18\x01 \x01(\tH\x00R\x06method\x88\x01\x01\x12\x17\n" +
|
||||||
|
"\x04path\x18\x02 \x01(\tH\x01R\x04path\x88\x01\x01B\t\n" +
|
||||||
|
"\a_methodB\a\n" +
|
||||||
|
"\x05_path\"\x80\x01\n" +
|
||||||
|
"\x10SignTokenRequest\x12\"\n" +
|
||||||
|
"\n" +
|
||||||
|
"expires_in\x18\x01 \x01(\x03H\x00R\texpiresIn\x88\x01\x01\x129\n" +
|
||||||
|
"\vpermissions\x18\x02 \x03(\v2\x17.api_auth.APIPermissionR\vpermissionsB\r\n" +
|
||||||
|
"\v_expires_in\"p\n" +
|
||||||
|
"\x11SignTokenResponse\x12+\n" +
|
||||||
|
"\x06status\x18\x01 \x01(\v2\x0e.common.StatusH\x00R\x06status\x88\x01\x01\x12\x19\n" +
|
||||||
|
"\x05token\x18\x02 \x01(\tH\x01R\x05token\x88\x01\x01B\t\n" +
|
||||||
|
"\a_statusB\b\n" +
|
||||||
|
"\x06_tokenB\aZ\x05../pbb\x06proto3"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
file_api_auth_proto_rawDescOnce sync.Once
|
file_api_auth_proto_rawDescOnce sync.Once
|
||||||
@@ -267,22 +438,27 @@ func file_api_auth_proto_rawDescGZIP() []byte {
|
|||||||
return file_api_auth_proto_rawDescData
|
return file_api_auth_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_api_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
var file_api_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||||
var file_api_auth_proto_goTypes = []any{
|
var file_api_auth_proto_goTypes = []any{
|
||||||
(*LoginRequest)(nil), // 0: api_auth.LoginRequest
|
(*LoginRequest)(nil), // 0: api_auth.LoginRequest
|
||||||
(*LoginResponse)(nil), // 1: api_auth.LoginResponse
|
(*LoginResponse)(nil), // 1: api_auth.LoginResponse
|
||||||
(*RegisterRequest)(nil), // 2: api_auth.RegisterRequest
|
(*RegisterRequest)(nil), // 2: api_auth.RegisterRequest
|
||||||
(*RegisterResponse)(nil), // 3: api_auth.RegisterResponse
|
(*RegisterResponse)(nil), // 3: api_auth.RegisterResponse
|
||||||
(*Status)(nil), // 4: common.Status
|
(*APIPermission)(nil), // 4: api_auth.APIPermission
|
||||||
|
(*SignTokenRequest)(nil), // 5: api_auth.SignTokenRequest
|
||||||
|
(*SignTokenResponse)(nil), // 6: api_auth.SignTokenResponse
|
||||||
|
(*Status)(nil), // 7: common.Status
|
||||||
}
|
}
|
||||||
var file_api_auth_proto_depIdxs = []int32{
|
var file_api_auth_proto_depIdxs = []int32{
|
||||||
4, // 0: api_auth.LoginResponse.status:type_name -> common.Status
|
7, // 0: api_auth.LoginResponse.status:type_name -> common.Status
|
||||||
4, // 1: api_auth.RegisterResponse.status:type_name -> common.Status
|
7, // 1: api_auth.RegisterResponse.status:type_name -> common.Status
|
||||||
2, // [2:2] is the sub-list for method output_type
|
4, // 2: api_auth.SignTokenRequest.permissions:type_name -> api_auth.APIPermission
|
||||||
2, // [2:2] is the sub-list for method input_type
|
7, // 3: api_auth.SignTokenResponse.status:type_name -> common.Status
|
||||||
2, // [2:2] is the sub-list for extension type_name
|
4, // [4:4] is the sub-list for method output_type
|
||||||
2, // [2:2] is the sub-list for extension extendee
|
4, // [4:4] is the sub-list for method input_type
|
||||||
0, // [0:2] is the sub-list for field type_name
|
4, // [4:4] is the sub-list for extension type_name
|
||||||
|
4, // [4:4] is the sub-list for extension extendee
|
||||||
|
0, // [0:4] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_api_auth_proto_init() }
|
func init() { file_api_auth_proto_init() }
|
||||||
@@ -295,13 +471,16 @@ func file_api_auth_proto_init() {
|
|||||||
file_api_auth_proto_msgTypes[1].OneofWrappers = []any{}
|
file_api_auth_proto_msgTypes[1].OneofWrappers = []any{}
|
||||||
file_api_auth_proto_msgTypes[2].OneofWrappers = []any{}
|
file_api_auth_proto_msgTypes[2].OneofWrappers = []any{}
|
||||||
file_api_auth_proto_msgTypes[3].OneofWrappers = []any{}
|
file_api_auth_proto_msgTypes[3].OneofWrappers = []any{}
|
||||||
|
file_api_auth_proto_msgTypes[4].OneofWrappers = []any{}
|
||||||
|
file_api_auth_proto_msgTypes[5].OneofWrappers = []any{}
|
||||||
|
file_api_auth_proto_msgTypes[6].OneofWrappers = []any{}
|
||||||
type x struct{}
|
type x struct{}
|
||||||
out := protoimpl.TypeBuilder{
|
out := protoimpl.TypeBuilder{
|
||||||
File: protoimpl.DescBuilder{
|
File: protoimpl.DescBuilder{
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_auth_proto_rawDesc), len(file_api_auth_proto_rawDesc)),
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_auth_proto_rawDesc), len(file_api_auth_proto_rawDesc)),
|
||||||
NumEnums: 0,
|
NumEnums: 0,
|
||||||
NumMessages: 4,
|
NumMessages: 7,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
},
|
},
|
||||||
|
@@ -24,6 +24,7 @@ const (
|
|||||||
type InitClientRequest struct {
|
type InitClientRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
ClientId *string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3,oneof" json:"client_id,omitempty"`
|
ClientId *string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3,oneof" json:"client_id,omitempty"`
|
||||||
|
Ephemeral *bool `protobuf:"varint,2,opt,name=ephemeral,proto3,oneof" json:"ephemeral,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -65,6 +66,13 @@ func (x *InitClientRequest) GetClientId() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *InitClientRequest) GetEphemeral() bool {
|
||||||
|
if x != nil && x.Ephemeral != nil {
|
||||||
|
return *x.Ephemeral
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type InitClientResponse struct {
|
type InitClientResponse struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Status *Status `protobuf:"bytes,1,opt,name=status,proto3,oneof" json:"status,omitempty"`
|
Status *Status `protobuf:"bytes,1,opt,name=status,proto3,oneof" json:"status,omitempty"`
|
||||||
@@ -1498,11 +1506,14 @@ var File_api_client_proto protoreflect.FileDescriptor
|
|||||||
const file_api_client_proto_rawDesc = "" +
|
const file_api_client_proto_rawDesc = "" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"\x10api_client.proto\x12\n" +
|
"\x10api_client.proto\x12\n" +
|
||||||
"api_client\x1a\fcommon.proto\"C\n" +
|
"api_client\x1a\fcommon.proto\"t\n" +
|
||||||
"\x11InitClientRequest\x12 \n" +
|
"\x11InitClientRequest\x12 \n" +
|
||||||
"\tclient_id\x18\x01 \x01(\tH\x00R\bclientId\x88\x01\x01B\f\n" +
|
"\tclient_id\x18\x01 \x01(\tH\x00R\bclientId\x88\x01\x01\x12!\n" +
|
||||||
|
"\tephemeral\x18\x02 \x01(\bH\x01R\tephemeral\x88\x01\x01B\f\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"_client_id\"|\n" +
|
"_client_idB\f\n" +
|
||||||
|
"\n" +
|
||||||
|
"_ephemeral\"|\n" +
|
||||||
"\x12InitClientResponse\x12+\n" +
|
"\x12InitClientResponse\x12+\n" +
|
||||||
"\x06status\x18\x01 \x01(\v2\x0e.common.StatusH\x00R\x06status\x88\x01\x01\x12 \n" +
|
"\x06status\x18\x01 \x01(\v2\x0e.common.StatusH\x00R\x06status\x88\x01\x01\x12 \n" +
|
||||||
"\tclient_id\x18\x02 \x01(\tH\x01R\bclientId\x88\x01\x01B\t\n" +
|
"\tclient_id\x18\x02 \x01(\tH\x01R\bclientId\x88\x01\x01B\t\n" +
|
||||||
|
@@ -289,7 +289,9 @@ type Client struct {
|
|||||||
Stopped *bool `protobuf:"varint,7,opt,name=stopped,proto3,oneof" json:"stopped,omitempty"`
|
Stopped *bool `protobuf:"varint,7,opt,name=stopped,proto3,oneof" json:"stopped,omitempty"`
|
||||||
ClientIds []string `protobuf:"bytes,8,rep,name=client_ids,json=clientIds,proto3" json:"client_ids,omitempty"` // some client can connected to more than one server, make a shadow client to handle this
|
ClientIds []string `protobuf:"bytes,8,rep,name=client_ids,json=clientIds,proto3" json:"client_ids,omitempty"` // some client can connected to more than one server, make a shadow client to handle this
|
||||||
OriginClientId *string `protobuf:"bytes,9,opt,name=origin_client_id,json=originClientId,proto3,oneof" json:"origin_client_id,omitempty"`
|
OriginClientId *string `protobuf:"bytes,9,opt,name=origin_client_id,json=originClientId,proto3,oneof" json:"origin_client_id,omitempty"`
|
||||||
FrpsUrl *string `protobuf:"bytes,10,opt,name=frps_url,json=frpsUrl,proto3,oneof" json:"frps_url,omitempty"` // 客户端用于连接frps的url,解决 frp 在 CDN 后的问题,格式类似 [tcp/ws/wss/quic/kcp]://example.com:7000
|
FrpsUrl *string `protobuf:"bytes,10,opt,name=frps_url,json=frpsUrl,proto3,oneof" json:"frps_url,omitempty"` // 客户端用于连接frps的url,解决 frp 在 CDN 后的问题,格式类似 [tcp/ws/wss/quic/kcp]://example.com:7000
|
||||||
|
Ephemeral *bool `protobuf:"varint,11,opt,name=ephemeral,proto3,oneof" json:"ephemeral,omitempty"` // 是否临时节点
|
||||||
|
LastSeenAt *int64 `protobuf:"varint,12,opt,name=last_seen_at,json=lastSeenAt,proto3,oneof" json:"last_seen_at,omitempty"` // 最后一次心跳时间戳
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -387,6 +389,20 @@ func (x *Client) GetFrpsUrl() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Client) GetEphemeral() bool {
|
||||||
|
if x != nil && x.Ephemeral != nil {
|
||||||
|
return *x.Ephemeral
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Client) GetLastSeenAt() int64 {
|
||||||
|
if x != nil && x.LastSeenAt != nil {
|
||||||
|
return *x.LastSeenAt
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Id *string `protobuf:"bytes,1,opt,name=id,proto3,oneof" json:"id,omitempty"`
|
Id *string `protobuf:"bytes,1,opt,name=id,proto3,oneof" json:"id,omitempty"`
|
||||||
@@ -862,7 +878,7 @@ const file_common_proto_rawDesc = "" +
|
|||||||
"\x06status\x18\x01 \x01(\v2\x0e.common.StatusH\x00R\x06status\x88\x01\x01\x12\x17\n" +
|
"\x06status\x18\x01 \x01(\v2\x0e.common.StatusH\x00R\x06status\x88\x01\x01\x12\x17\n" +
|
||||||
"\x04data\x18\x02 \x01(\tH\x01R\x04data\x88\x01\x01B\t\n" +
|
"\x04data\x18\x02 \x01(\tH\x01R\x04data\x88\x01\x01B\t\n" +
|
||||||
"\a_statusB\a\n" +
|
"\a_statusB\a\n" +
|
||||||
"\x05_data\"\x8a\x03\n" +
|
"\x05_data\"\xf3\x03\n" +
|
||||||
"\x06Client\x12\x13\n" +
|
"\x06Client\x12\x13\n" +
|
||||||
"\x02id\x18\x01 \x01(\tH\x00R\x02id\x88\x01\x01\x12\x1b\n" +
|
"\x02id\x18\x01 \x01(\tH\x00R\x02id\x88\x01\x01\x12\x1b\n" +
|
||||||
"\x06secret\x18\x02 \x01(\tH\x01R\x06secret\x88\x01\x01\x12\x1b\n" +
|
"\x06secret\x18\x02 \x01(\tH\x01R\x06secret\x88\x01\x01\x12\x1b\n" +
|
||||||
@@ -874,7 +890,10 @@ const file_common_proto_rawDesc = "" +
|
|||||||
"client_ids\x18\b \x03(\tR\tclientIds\x12-\n" +
|
"client_ids\x18\b \x03(\tR\tclientIds\x12-\n" +
|
||||||
"\x10origin_client_id\x18\t \x01(\tH\x06R\x0eoriginClientId\x88\x01\x01\x12\x1e\n" +
|
"\x10origin_client_id\x18\t \x01(\tH\x06R\x0eoriginClientId\x88\x01\x01\x12\x1e\n" +
|
||||||
"\bfrps_url\x18\n" +
|
"\bfrps_url\x18\n" +
|
||||||
" \x01(\tH\aR\afrpsUrl\x88\x01\x01B\x05\n" +
|
" \x01(\tH\aR\afrpsUrl\x88\x01\x01\x12!\n" +
|
||||||
|
"\tephemeral\x18\v \x01(\bH\bR\tephemeral\x88\x01\x01\x12%\n" +
|
||||||
|
"\flast_seen_at\x18\f \x01(\x03H\tR\n" +
|
||||||
|
"lastSeenAt\x88\x01\x01B\x05\n" +
|
||||||
"\x03_idB\t\n" +
|
"\x03_idB\t\n" +
|
||||||
"\a_secretB\t\n" +
|
"\a_secretB\t\n" +
|
||||||
"\a_configB\n" +
|
"\a_configB\n" +
|
||||||
@@ -885,7 +904,10 @@ const file_common_proto_rawDesc = "" +
|
|||||||
"\n" +
|
"\n" +
|
||||||
"\b_stoppedB\x13\n" +
|
"\b_stoppedB\x13\n" +
|
||||||
"\x11_origin_client_idB\v\n" +
|
"\x11_origin_client_idB\v\n" +
|
||||||
"\t_frps_url\"\xd8\x01\n" +
|
"\t_frps_urlB\f\n" +
|
||||||
|
"\n" +
|
||||||
|
"_ephemeralB\x0f\n" +
|
||||||
|
"\r_last_seen_at\"\xd8\x01\n" +
|
||||||
"\x06Server\x12\x13\n" +
|
"\x06Server\x12\x13\n" +
|
||||||
"\x02id\x18\x01 \x01(\tH\x00R\x02id\x88\x01\x01\x12\x1b\n" +
|
"\x02id\x18\x01 \x01(\tH\x00R\x02id\x88\x01\x01\x12\x1b\n" +
|
||||||
"\x06secret\x18\x02 \x01(\tH\x01R\x06secret\x88\x01\x01\x12\x13\n" +
|
"\x06secret\x18\x02 \x01(\tH\x01R\x06secret\x88\x01\x01\x12\x13\n" +
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/VaalaCat/frp-panel/conf"
|
"github.com/VaalaCat/frp-panel/conf"
|
||||||
|
"github.com/casbin/casbin/v2"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,6 +23,39 @@ type application struct {
|
|||||||
serverController ServerController
|
serverController ServerController
|
||||||
rpcCred credentials.TransportCredentials
|
rpcCred credentials.TransportCredentials
|
||||||
conf conf.Config
|
conf conf.Config
|
||||||
|
currentRole string
|
||||||
|
permManager PermissionManager
|
||||||
|
enforcer *casbin.Enforcer
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEnforcer implements Application.
|
||||||
|
func (a *application) GetEnforcer() *casbin.Enforcer {
|
||||||
|
return a.enforcer
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEnforcer implements Application.
|
||||||
|
func (a *application) SetEnforcer(c *casbin.Enforcer) {
|
||||||
|
a.enforcer = c
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPermManager implements Application.
|
||||||
|
func (a *application) GetPermManager() PermissionManager {
|
||||||
|
return a.permManager
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPermManager implements Application.
|
||||||
|
func (a *application) SetPermManager(p PermissionManager) {
|
||||||
|
a.permManager = p
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrentRole implements Application.
|
||||||
|
func (a *application) GetCurrentRole() string {
|
||||||
|
return a.currentRole
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCurrentRole implements Application.
|
||||||
|
func (a *application) SetCurrentRole(r string) {
|
||||||
|
a.currentRole = r
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetClientCred implements Application.
|
// GetClientCred implements Application.
|
||||||
|
@@ -5,6 +5,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/VaalaCat/frp-panel/conf"
|
"github.com/VaalaCat/frp-panel/conf"
|
||||||
|
"github.com/casbin/casbin/v2"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
)
|
)
|
||||||
@@ -36,6 +37,12 @@ type Application interface {
|
|||||||
SetConfig(conf.Config)
|
SetConfig(conf.Config)
|
||||||
GetRPCCred() credentials.TransportCredentials
|
GetRPCCred() credentials.TransportCredentials
|
||||||
SetRPCCred(credentials.TransportCredentials)
|
SetRPCCred(credentials.TransportCredentials)
|
||||||
|
GetCurrentRole() string
|
||||||
|
SetCurrentRole(string)
|
||||||
|
GetEnforcer() *casbin.Enforcer
|
||||||
|
SetEnforcer(*casbin.Enforcer)
|
||||||
|
GetPermManager() PermissionManager
|
||||||
|
SetPermManager(PermissionManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Context struct {
|
type Context struct {
|
||||||
|
@@ -4,8 +4,10 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/VaalaCat/frp-panel/defs"
|
||||||
"github.com/VaalaCat/frp-panel/pb"
|
"github.com/VaalaCat/frp-panel/pb"
|
||||||
"github.com/VaalaCat/frp-panel/utils"
|
"github.com/VaalaCat/frp-panel/utils"
|
||||||
|
"github.com/casbin/casbin/v2"
|
||||||
|
|
||||||
"github.com/fatedier/frp/client/proxy"
|
"github.com/fatedier/frp/client/proxy"
|
||||||
v1 "github.com/fatedier/frp/pkg/config/v1"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
@@ -66,10 +68,10 @@ type ClientLogManager interface {
|
|||||||
|
|
||||||
// models/db.go
|
// models/db.go
|
||||||
type DBManager interface {
|
type DBManager interface {
|
||||||
GetDB(dbType string) *gorm.DB
|
GetDB(dbType string, dbRole string) *gorm.DB
|
||||||
GetDefaultDB() *gorm.DB
|
GetDefaultDB() *gorm.DB
|
||||||
SetDB(dbType string, db *gorm.DB)
|
SetDB(dbType string, dbRole string, db *gorm.DB)
|
||||||
RemoveDB(dbType string)
|
RemoveDB(dbType string, dbRole string)
|
||||||
SetDebug(bool)
|
SetDebug(bool)
|
||||||
Init()
|
Init()
|
||||||
}
|
}
|
||||||
@@ -157,3 +159,15 @@ type ServerHandler interface {
|
|||||||
type MasterClient interface {
|
type MasterClient interface {
|
||||||
Call() pb.MasterClient
|
Call() pb.MasterClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// services/rbac/perm_manager.go
|
||||||
|
type PermissionManager interface {
|
||||||
|
AddUserToGroup(userID int, groupID string, tenantID int) (bool, error)
|
||||||
|
CheckPermission(userID int, objType defs.RBACObj, objID string, action defs.RBACAction, tenantID int) (bool, error)
|
||||||
|
Enforcer() *casbin.Enforcer
|
||||||
|
GrantGroupPermission(groupID string, objType defs.RBACObj, objID string, action defs.RBACAction, tenantID int) (bool, error)
|
||||||
|
GrantUserPermission(userID int, objType defs.RBACObj, objID string, action defs.RBACAction, tenantID int) (bool, error)
|
||||||
|
RemoveUserFromGroup(userID int, groupID string, tenantID int) (bool, error)
|
||||||
|
RevokeGroupPermission(groupID string, objType defs.RBACObj, objID string, action defs.RBACAction, tenantID int) (bool, error)
|
||||||
|
RevokeUserPermission(userID int, objType defs.RBACObj, objID string, action defs.RBACAction, tenantID int) (bool, error)
|
||||||
|
}
|
||||||
|
@@ -2,6 +2,7 @@ package dao
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/VaalaCat/frp-panel/models"
|
"github.com/VaalaCat/frp-panel/models"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
@@ -175,8 +176,6 @@ func (q *queryImpl) ListClients(userInfo models.UserInfo, page, pageSize int) ([
|
|||||||
Where(
|
Where(
|
||||||
db.Where(
|
db.Where(
|
||||||
normalClientFilter(db),
|
normalClientFilter(db),
|
||||||
).Or(
|
|
||||||
"is_shadow = ?", true,
|
|
||||||
),
|
),
|
||||||
).Offset(offset).Limit(pageSize).Find(&clients).Error
|
).Offset(offset).Limit(pageSize).Find(&clients).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -331,10 +330,30 @@ func (q *queryImpl) AdminGetClientIDsInShadowByClientID(clientID string) ([]stri
|
|||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *queryImpl) AdminUpdateClientLastSeen(clientID string) error {
|
||||||
|
db := q.ctx.GetApp().GetDBManager().GetDefaultDB()
|
||||||
|
return db.Model(&models.Client{
|
||||||
|
ClientEntity: &models.ClientEntity{
|
||||||
|
ClientID: clientID,
|
||||||
|
}}).Update("last_seen_at", time.Now()).Error
|
||||||
|
}
|
||||||
|
|
||||||
func normalClientFilter(db *gorm.DB) *gorm.DB {
|
func normalClientFilter(db *gorm.DB) *gorm.DB {
|
||||||
// 1. 没shadow过的老client
|
// 1. 没shadow过的老client
|
||||||
// 2. shadow过的shadow client
|
// 2. shadow过的shadow client
|
||||||
return db.Where("origin_client_id is NULL").
|
// 3. 非临时节点
|
||||||
Or("is_shadow = ?", true).
|
return db.Where(
|
||||||
Or("LENGTH(origin_client_id) = ?", 0)
|
db.Where("origin_client_id is NULL").
|
||||||
|
Or("is_shadow = ?", true).
|
||||||
|
Or("LENGTH(origin_client_id) = ?", 0),
|
||||||
|
).
|
||||||
|
Where(
|
||||||
|
db.Where(
|
||||||
|
db.Where("ephemeral is NULL").
|
||||||
|
Or("ephemeral = ?", false),
|
||||||
|
).Or(
|
||||||
|
db.Where("ephemeral = ?", true).
|
||||||
|
Where("last_seen_at > ?", time.Now().Add(-5*time.Minute)),
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@@ -62,6 +62,20 @@ func (q *queryImpl) UpdateUser(userInfo models.UserInfo, user *models.UserEntity
|
|||||||
}).Error
|
}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *queryImpl) AdminUpdateUser(userInfo models.UserInfo, user *models.UserEntity) error {
|
||||||
|
db := q.ctx.GetApp().GetDBManager().GetDefaultDB()
|
||||||
|
user.UserID = userInfo.GetUserID()
|
||||||
|
return db.Model(&models.User{}).Where(
|
||||||
|
&models.User{
|
||||||
|
UserEntity: &models.UserEntity{
|
||||||
|
UserID: userInfo.GetUserID(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
).Save(&models.User{
|
||||||
|
UserEntity: user,
|
||||||
|
}).Error
|
||||||
|
}
|
||||||
|
|
||||||
func (q *queryImpl) GetUserByUserName(userName string) (*models.UserEntity, error) {
|
func (q *queryImpl) GetUserByUserName(userName string) (*models.UserEntity, error) {
|
||||||
if userName == "" {
|
if userName == "" {
|
||||||
return nil, fmt.Errorf("invalid user name")
|
return nil, fmt.Errorf("invalid user name")
|
||||||
|
45
services/dao/user_group.go
Normal file
45
services/dao/user_group.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package dao
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/VaalaCat/frp-panel/defs"
|
||||||
|
"github.com/VaalaCat/frp-panel/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (q *queryImpl) CreateGroup(userInfo models.UserInfo, groupID, groupName, comment string) (*models.UserGroup, error) {
|
||||||
|
if groupID == "" || groupName == "" {
|
||||||
|
return nil, fmt.Errorf("invalid group id or group name")
|
||||||
|
}
|
||||||
|
|
||||||
|
if userInfo.GetRole() != defs.UserRole_Admin {
|
||||||
|
return nil, fmt.Errorf("only admin can create group")
|
||||||
|
}
|
||||||
|
|
||||||
|
db := q.ctx.GetApp().GetDBManager().GetDefaultDB()
|
||||||
|
|
||||||
|
g := &models.UserGroup{
|
||||||
|
TenantID: userInfo.GetTenantID(),
|
||||||
|
GroupID: groupID,
|
||||||
|
GroupName: groupName,
|
||||||
|
Comment: comment,
|
||||||
|
}
|
||||||
|
err := db.Create(g).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return g, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *queryImpl) DeleteGroup(userInfo models.UserInfo, groupID string) error {
|
||||||
|
if userInfo.GetRole() != defs.UserRole_Admin {
|
||||||
|
return fmt.Errorf("only admin can delete group")
|
||||||
|
}
|
||||||
|
|
||||||
|
db := q.ctx.GetApp().GetDBManager().GetDefaultDB()
|
||||||
|
return db.Unscoped().Where(&models.UserGroup{
|
||||||
|
TenantID: userInfo.GetTenantID(),
|
||||||
|
GroupID: groupID,
|
||||||
|
}).Delete(&models.UserGroup{}).Error
|
||||||
|
}
|
@@ -134,6 +134,12 @@ func (s *server) ServerSend(sender pb.Master_ServerSendServer) error {
|
|||||||
return fmt.Errorf("invalid secret, %s id: [%s]", req.GetEvent().String(), req.GetClientId())
|
return fmt.Errorf("invalid secret, %s id: [%s]", req.GetEvent().String(), req.GetClientId())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cliType == defs.CliTypeClient {
|
||||||
|
if err := dao.NewQuery(ctx).AdminUpdateClientLastSeen(req.GetClientId()); err != nil {
|
||||||
|
logger.Logger(ctx).Errorf("cannot update client last seen, %s id: [%s]", req.GetEvent().String(), req.GetClientId())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s.appInstance.GetClientsManager().Set(req.GetClientId(), cliType, sender)
|
s.appInstance.GetClientsManager().Set(req.GetClientId(), cliType, sender)
|
||||||
done = rpc.Recv(s.appInstance, req.GetClientId())
|
done = rpc.Recv(s.appInstance, req.GetClientId())
|
||||||
sender.Send(&pb.ServerMessage{
|
sender.Send(&pb.ServerMessage{
|
||||||
|
43
services/rbac/init.go
Normal file
43
services/rbac/init.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package rbac
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/VaalaCat/frp-panel/services/app"
|
||||||
|
"github.com/VaalaCat/frp-panel/utils/logger"
|
||||||
|
"github.com/casbin/casbin/v2"
|
||||||
|
"github.com/casbin/casbin/v2/model"
|
||||||
|
gormadapter "github.com/casbin/gorm-adapter/v3"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitializeCasbin(ctx *app.Context, db *gorm.DB) (*casbin.Enforcer, error) {
|
||||||
|
adapter, err := gormadapter.NewAdapterByDB(db)
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger(ctx).Fatalf("error creating Casbin GORM adapter: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the Casbin model from file
|
||||||
|
m, err := model.NewModelFromString(RBAC_MODEL)
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger(ctx).Fatalf("error loading Casbin model: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the Casbin enforcer
|
||||||
|
enforcer, err := casbin.NewEnforcer(m, adapter)
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger(ctx).Fatalf("error creating Casbin enforcer: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = enforcer.LoadPolicy()
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger(ctx).WithError(err).Warnf("could not load Casbin policy from DB")
|
||||||
|
// return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
enforcer.EnableAutoSave(true)
|
||||||
|
|
||||||
|
logger.Logger(ctx).Infof("Casbin Enforcer initialized successfully.")
|
||||||
|
return enforcer, nil
|
||||||
|
}
|
82
services/rbac/perm_manager.go
Normal file
82
services/rbac/perm_manager.go
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
package rbac
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/VaalaCat/frp-panel/defs"
|
||||||
|
"github.com/casbin/casbin/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type permManager struct {
|
||||||
|
enforcer *casbin.Enforcer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *permManager) Enforcer() *casbin.Enforcer {
|
||||||
|
return pm.enforcer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPermManager(enforcer *casbin.Enforcer) *permManager {
|
||||||
|
return &permManager{
|
||||||
|
enforcer: enforcer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func identity[T defs.RBACObj | defs.RBACSubject | defs.RBACDomain, U string | int | uint | int64](rType T, objID U) string {
|
||||||
|
return string(rType) + ":" + fmt.Sprint(objID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *permManager) GrantGroupPermission(groupID string, objType defs.RBACObj, objID string, action defs.RBACAction, tenantID int) (bool, error) {
|
||||||
|
groupSubject := identity(defs.RBACSubjectGroup, groupID)
|
||||||
|
objSubject := identity(objType, objID)
|
||||||
|
domain := identity(defs.RBACDomainTenant, tenantID)
|
||||||
|
|
||||||
|
return pm.enforcer.AddPolicy(groupSubject, objSubject, string(action), domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *permManager) RevokeGroupPermission(groupID string, objType defs.RBACObj, objID string, action defs.RBACAction, tenantID int) (bool, error) {
|
||||||
|
groupSubject := identity(defs.RBACSubjectGroup, groupID)
|
||||||
|
objSubject := identity(objType, objID)
|
||||||
|
domain := identity(defs.RBACDomainTenant, tenantID)
|
||||||
|
|
||||||
|
return pm.enforcer.RemovePolicy(groupSubject, objSubject, string(action), domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *permManager) GrantUserPermission(userID int, objType defs.RBACObj, objID string, action defs.RBACAction, tenantID int) (bool, error) {
|
||||||
|
userSubject := identity(defs.RBACSubjectUser, userID)
|
||||||
|
objSubject := identity(objType, objID)
|
||||||
|
domain := identity(defs.RBACDomainTenant, tenantID)
|
||||||
|
|
||||||
|
return pm.enforcer.AddPolicy(userSubject, objSubject, string(action), domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *permManager) RevokeUserPermission(userID int, objType defs.RBACObj, objID string, action defs.RBACAction, tenantID int) (bool, error) {
|
||||||
|
userSubject := identity(defs.RBACSubjectUser, userID)
|
||||||
|
objSubject := identity(objType, objID)
|
||||||
|
domain := identity(defs.RBACDomainTenant, tenantID)
|
||||||
|
|
||||||
|
return pm.enforcer.RemovePolicy(userSubject, objSubject, string(action), domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *permManager) CheckPermission(userID int, objType defs.RBACObj, objID string, action defs.RBACAction, tenantID int) (bool, error) {
|
||||||
|
userSubject := identity(defs.RBACSubjectUser, userID)
|
||||||
|
objSubject := identity(objType, objID)
|
||||||
|
domain := identity(defs.RBACDomainTenant, tenantID)
|
||||||
|
|
||||||
|
return pm.enforcer.Enforce(userSubject, objSubject, string(action), domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *permManager) AddUserToGroup(userID int, groupID string, tenantID int) (bool, error) {
|
||||||
|
userSub := identity(defs.RBACSubjectUser, userID)
|
||||||
|
groupSub := identity(defs.RBACSubjectGroup, groupID)
|
||||||
|
domain := identity(defs.RBACDomainTenant, tenantID)
|
||||||
|
|
||||||
|
return pm.enforcer.AddGroupingPolicy(userSub, groupSub, domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *permManager) RemoveUserFromGroup(userID int, groupID string, tenantID int) (bool, error) {
|
||||||
|
userSub := identity(defs.RBACSubjectUser, userID)
|
||||||
|
groupSub := identity(defs.RBACSubjectGroup, groupID)
|
||||||
|
domain := identity(defs.RBACDomainTenant, tenantID)
|
||||||
|
|
||||||
|
return pm.enforcer.RemoveGroupingPolicy(userSub, groupSub, domain)
|
||||||
|
}
|
17
services/rbac/rbac_models.go
Normal file
17
services/rbac/rbac_models.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package rbac
|
||||||
|
|
||||||
|
const RBAC_MODEL = `
|
||||||
|
[request_definition]
|
||||||
|
r = sub, obj, act, dom
|
||||||
|
|
||||||
|
[policy_definition]
|
||||||
|
p = sub, obj, act, dom
|
||||||
|
|
||||||
|
[role_definition]
|
||||||
|
g = _, _, dom
|
||||||
|
|
||||||
|
[policy_effect]
|
||||||
|
e = some(where (p.eft == allow))
|
||||||
|
|
||||||
|
[matchers]
|
||||||
|
m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act`
|
@@ -105,13 +105,14 @@ func GetClientCert(appInstance app.Application, clientID, clientSecret string, c
|
|||||||
return resp.Cert
|
return resp.Cert
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitClient(appInstance app.Application, clientID, joinToken string) (*pb.InitClientResponse, error) {
|
func InitClient(cfg conf.Config, clientID, joinToken string, ephemeral bool) (*pb.InitClientResponse, error) {
|
||||||
apiEndpoint := conf.GetAPIURL(appInstance.GetConfig())
|
apiEndpoint := conf.GetAPIURL(cfg)
|
||||||
|
|
||||||
c := httpCli()
|
c := httpCli()
|
||||||
|
|
||||||
rawReq, err := proto.Marshal(&pb.InitClientRequest{
|
rawReq, err := proto.Marshal(&pb.InitClientRequest{
|
||||||
ClientId: &clientID,
|
ClientId: &clientID,
|
||||||
|
Ephemeral: &ephemeral,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -132,8 +133,8 @@ func InitClient(appInstance app.Application, clientID, joinToken string) (*pb.In
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetClient(appInstance app.Application, clientID, joinToken string) (*pb.GetClientResponse, error) {
|
func GetClient(cfg conf.Config, clientID, joinToken string) (*pb.GetClientResponse, error) {
|
||||||
apiEndpoint := conf.GetAPIURL(appInstance.GetConfig())
|
apiEndpoint := conf.GetAPIURL(cfg)
|
||||||
c := httpCli()
|
c := httpCli()
|
||||||
|
|
||||||
rawReq, err := proto.Marshal(&pb.GetClientRequest{
|
rawReq, err := proto.Marshal(&pb.GetClientRequest{
|
||||||
|
@@ -18,7 +18,7 @@ func GetValue[T any](c context.Context, key string) (T, bool) {
|
|||||||
return v, true
|
return v, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func getValue[T any](c context.Context, key string) (interface{}, bool) {
|
func getValue[T any](c context.Context, key string) (any, bool) {
|
||||||
val := c.Value(key)
|
val := c.Value(key)
|
||||||
if val == nil {
|
if val == nil {
|
||||||
return *new(T), false
|
return *new(T), false
|
||||||
|
11
utils/json.go
Normal file
11
utils/json.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
func MarshalForJson(v any) string {
|
||||||
|
ret, err := json.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(ret)
|
||||||
|
}
|
@@ -24,7 +24,7 @@ func GetJwtToken(secretKey string, iat, seconds int64, payload string) (string,
|
|||||||
// @iat: 时间戳
|
// @iat: 时间戳
|
||||||
// @seconds: 过期时间,单位秒
|
// @seconds: 过期时间,单位秒
|
||||||
// @payload: 数据载体
|
// @payload: 数据载体
|
||||||
func GetJwtTokenFromMap(secretKey string, iat, seconds int64, payload map[string]string) (string, error) {
|
func GetJwtTokenFromMap(secretKey string, iat, seconds int64, payload map[string]interface{}) (string, error) {
|
||||||
claims := make(jwt.MapClaims)
|
claims := make(jwt.MapClaims)
|
||||||
claims["exp"] = iat + seconds
|
claims["exp"] = iat + seconds
|
||||||
claims["iat"] = iat
|
claims["iat"] = iat
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import http from '@/api/http'
|
import http from '@/api/http'
|
||||||
import { API_PATH } from '@/lib/consts'
|
import { API_PATH } from '@/lib/consts'
|
||||||
|
import { SignTokenRequest, SignTokenResponse } from '@/lib/pb/api_auth'
|
||||||
import {
|
import {
|
||||||
GetUserInfoRequest,
|
GetUserInfoRequest,
|
||||||
GetUserInfoResponse,
|
GetUserInfoResponse,
|
||||||
@@ -20,3 +21,8 @@ export const updateUserInfo = async (req: UpdateUserInfoRequest) => {
|
|||||||
const res = await http.post(API_PATH + '/user/update', UpdateUserInfoRequest.toJson(req))
|
const res = await http.post(API_PATH + '/user/update', UpdateUserInfoRequest.toJson(req))
|
||||||
return UpdateUserInfoResponse.fromJson((res.data as BaseResponse).body)
|
return UpdateUserInfoResponse.fromJson((res.data as BaseResponse).body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const signToken = async (req: SignTokenRequest) => {
|
||||||
|
const res = await http.post(API_PATH + '/user/sign-token', SignTokenRequest.toJson(req))
|
||||||
|
return SignTokenResponse.fromJson((res.data as BaseResponse).body)
|
||||||
|
}
|
@@ -227,6 +227,10 @@ export const ClientInfo = ({ client }: { client: ClientTableSchema }) => {
|
|||||||
{`需要升级!`}
|
{`需要升级!`}
|
||||||
</Badge>
|
</Badge>
|
||||||
}
|
}
|
||||||
|
{client.originClient.ephemeral && <Badge variant={"secondary"} className={`p-2 border font-mono w-fit text-nowrap rounded-full h-6`}>
|
||||||
|
{`临时`}
|
||||||
|
</Badge>}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -1,14 +1,18 @@
|
|||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import React from 'react'
|
import React, { useState } from 'react'
|
||||||
import { JoinCommandStr } from '@/lib/consts'
|
import { JoinCommandStr } from '@/lib/consts'
|
||||||
import { useStore } from '@nanostores/react'
|
import { useStore } from '@nanostores/react'
|
||||||
import { $platformInfo, $token } from '@/store/user'
|
import { $platformInfo } from '@/store/user'
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { signToken } from '@/api/user'
|
||||||
|
import { useQuery } from '@tanstack/react-query'
|
||||||
|
import { toast } from 'sonner'
|
||||||
|
import { RespCode } from '@/lib/pb/common'
|
||||||
|
|
||||||
export const ClientJoinButton = () => {
|
export const ClientJoinButton = () => {
|
||||||
const platformInfo = useStore($platformInfo)
|
const platformInfo = useStore($platformInfo)
|
||||||
const token = useStore($token)
|
const [joinToken, setJoinToken] = useState<undefined | string>(undefined)
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
@@ -19,6 +23,25 @@ export const ClientJoinButton = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleNewToken = async () => {
|
||||||
|
try {
|
||||||
|
const resp = await signToken({
|
||||||
|
expiresIn: BigInt(1000000000),
|
||||||
|
permissions: [
|
||||||
|
{ method: 'POST', path: '/api/v1/client/get', },
|
||||||
|
{ method: 'POST', path: '/api/v1/client/init', },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
if (!resp || !resp.status || resp.status.code !== RespCode.SUCCESS) {
|
||||||
|
toast.error('server error')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setJoinToken(resp.token)
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(JSON.stringify(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
@@ -32,24 +55,36 @@ export const ClientJoinButton = () => {
|
|||||||
{t('client.join.description')} (<a className='text-blue-500' href='https://github.com/VaalaCat/frp-panel/releases' target="_blank" rel="noopener noreferrer">{t('common.download')}</a>)
|
{t('client.join.description')} (<a className='text-blue-500' href='https://github.com/VaalaCat/frp-panel/releases' target="_blank" rel="noopener noreferrer">{t('common.download')}</a>)
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{token != undefined && <div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
<pre className="bg-muted p-3 rounded-md font-mono text-sm overflow-x-auto whitespace-pre-wrap break-all">
|
{joinToken != undefined && <>
|
||||||
{JoinCommandStr(platformInfo, token)}
|
<pre className="bg-muted p-3 rounded-md font-mono text-sm overflow-x-auto whitespace-pre-wrap break-all">
|
||||||
</pre>
|
{JoinCommandStr(platformInfo, joinToken)}
|
||||||
|
</pre>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
className="w-full"
|
||||||
|
onClick={() => {
|
||||||
|
if (joinToken) {
|
||||||
|
navigator.clipboard.writeText(JoinCommandStr(platformInfo, joinToken))
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={!platformInfo}
|
||||||
|
>
|
||||||
|
{t('common.copy')}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
}
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
onClick={() => {
|
onClick={handleNewToken}
|
||||||
if (token) {
|
|
||||||
navigator.clipboard.writeText(JoinCommandStr(platformInfo, token))
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
disabled={!platformInfo}
|
disabled={!platformInfo}
|
||||||
>
|
>
|
||||||
{t('common.copy')}
|
{t('client.join.sign_token')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
@@ -5,5 +5,15 @@ export function NeedUpgrade(version: ClientVersion | undefined) {
|
|||||||
if (!version.gitVersion) return false
|
if (!version.gitVersion) return false
|
||||||
const versionString = version?.gitVersion
|
const versionString = version?.gitVersion
|
||||||
const [a, b, c] = versionString.split('.')
|
const [a, b, c] = versionString.split('.')
|
||||||
return Number(b) < 1
|
if (Number(b) < 1) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(Number(a), Number(b), Number(c))
|
||||||
|
|
||||||
|
if (a=='v0' && Number(b)<=1 && Number(c) <= 10) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
@@ -258,7 +258,8 @@
|
|||||||
"button": "Batch Configuration",
|
"button": "Batch Configuration",
|
||||||
"title": "Batch Configuration",
|
"title": "Batch Configuration",
|
||||||
"description": "Download the binary files in advance, run the following command, and the client will automatically generate and save the configuration file.",
|
"description": "Download the binary files in advance, run the following command, and the client will automatically generate and save the configuration file.",
|
||||||
"copy": "Copy Command"
|
"copy": "Copy Command",
|
||||||
|
"sign_token": "Generate Token"
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
"title": "Start Command",
|
"title": "Start Command",
|
||||||
|
@@ -263,7 +263,8 @@
|
|||||||
"button": "批量配置",
|
"button": "批量配置",
|
||||||
"title": "批量配置",
|
"title": "批量配置",
|
||||||
"description": "提前下载好二进制文件,运行以下命令,client 将自动生成配置文件保存",
|
"description": "提前下载好二进制文件,运行以下命令,client 将自动生成配置文件保存",
|
||||||
"copy": "复制命令"
|
"copy": "复制命令",
|
||||||
|
"sign_token": "生成令牌"
|
||||||
},
|
},
|
||||||
"actions_menu": {
|
"actions_menu": {
|
||||||
"title": "客户端操作",
|
"title": "客户端操作",
|
||||||
|
@@ -78,7 +78,7 @@ export const LinuxInstallCommand = <T extends Client | Server>(
|
|||||||
item: T,
|
item: T,
|
||||||
info: GetPlatformInfoResponse,
|
info: GetPlatformInfoResponse,
|
||||||
) => {
|
) => {
|
||||||
return `curl -sSL https://raw.githubusercontent.com/VaalaCat/frp-panel/main/install.sh | bash -s --${ExecCommandStr(type, item, info, ' ')}`
|
return `curl -fSL https://raw.githubusercontent.com/VaalaCat/frp-panel/main/install.sh | bash -s --${ExecCommandStr(type, item, info, ' ')}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ClientEnvFile = <T extends Client | Server>(
|
export const ClientEnvFile = <T extends Client | Server>(
|
||||||
|
@@ -63,6 +63,45 @@ export interface RegisterResponse {
|
|||||||
*/
|
*/
|
||||||
status?: Status;
|
status?: Status;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @generated from protobuf message api_auth.APIPermission
|
||||||
|
*/
|
||||||
|
export interface APIPermission {
|
||||||
|
/**
|
||||||
|
* @generated from protobuf field: optional string method = 1;
|
||||||
|
*/
|
||||||
|
method?: string;
|
||||||
|
/**
|
||||||
|
* @generated from protobuf field: optional string path = 2;
|
||||||
|
*/
|
||||||
|
path?: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @generated from protobuf message api_auth.SignTokenRequest
|
||||||
|
*/
|
||||||
|
export interface SignTokenRequest {
|
||||||
|
/**
|
||||||
|
* @generated from protobuf field: optional int64 expires_in = 1;
|
||||||
|
*/
|
||||||
|
expiresIn?: bigint;
|
||||||
|
/**
|
||||||
|
* @generated from protobuf field: repeated api_auth.APIPermission permissions = 2;
|
||||||
|
*/
|
||||||
|
permissions: APIPermission[];
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @generated from protobuf message api_auth.SignTokenResponse
|
||||||
|
*/
|
||||||
|
export interface SignTokenResponse {
|
||||||
|
/**
|
||||||
|
* @generated from protobuf field: optional common.Status status = 1;
|
||||||
|
*/
|
||||||
|
status?: Status;
|
||||||
|
/**
|
||||||
|
* @generated from protobuf field: optional string token = 2;
|
||||||
|
*/
|
||||||
|
token?: string;
|
||||||
|
}
|
||||||
// @generated message type with reflection information, may provide speed optimized methods
|
// @generated message type with reflection information, may provide speed optimized methods
|
||||||
class LoginRequest$Type extends MessageType<LoginRequest> {
|
class LoginRequest$Type extends MessageType<LoginRequest> {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -275,3 +314,163 @@ class RegisterResponse$Type extends MessageType<RegisterResponse> {
|
|||||||
* @generated MessageType for protobuf message api_auth.RegisterResponse
|
* @generated MessageType for protobuf message api_auth.RegisterResponse
|
||||||
*/
|
*/
|
||||||
export const RegisterResponse = new RegisterResponse$Type();
|
export const RegisterResponse = new RegisterResponse$Type();
|
||||||
|
// @generated message type with reflection information, may provide speed optimized methods
|
||||||
|
class APIPermission$Type extends MessageType<APIPermission> {
|
||||||
|
constructor() {
|
||||||
|
super("api_auth.APIPermission", [
|
||||||
|
{ no: 1, name: "method", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ },
|
||||||
|
{ no: 2, name: "path", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ }
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
create(value?: PartialMessage<APIPermission>): APIPermission {
|
||||||
|
const message = globalThis.Object.create((this.messagePrototype!));
|
||||||
|
if (value !== undefined)
|
||||||
|
reflectionMergePartial<APIPermission>(this, message, value);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: APIPermission): APIPermission {
|
||||||
|
let message = target ?? this.create(), end = reader.pos + length;
|
||||||
|
while (reader.pos < end) {
|
||||||
|
let [fieldNo, wireType] = reader.tag();
|
||||||
|
switch (fieldNo) {
|
||||||
|
case /* optional string method */ 1:
|
||||||
|
message.method = reader.string();
|
||||||
|
break;
|
||||||
|
case /* optional string path */ 2:
|
||||||
|
message.path = reader.string();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
let u = options.readUnknownField;
|
||||||
|
if (u === "throw")
|
||||||
|
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
|
||||||
|
let d = reader.skip(wireType);
|
||||||
|
if (u !== false)
|
||||||
|
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
internalBinaryWrite(message: APIPermission, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
|
||||||
|
/* optional string method = 1; */
|
||||||
|
if (message.method !== undefined)
|
||||||
|
writer.tag(1, WireType.LengthDelimited).string(message.method);
|
||||||
|
/* optional string path = 2; */
|
||||||
|
if (message.path !== undefined)
|
||||||
|
writer.tag(2, WireType.LengthDelimited).string(message.path);
|
||||||
|
let u = options.writeUnknownFields;
|
||||||
|
if (u !== false)
|
||||||
|
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
|
||||||
|
return writer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @generated MessageType for protobuf message api_auth.APIPermission
|
||||||
|
*/
|
||||||
|
export const APIPermission = new APIPermission$Type();
|
||||||
|
// @generated message type with reflection information, may provide speed optimized methods
|
||||||
|
class SignTokenRequest$Type extends MessageType<SignTokenRequest> {
|
||||||
|
constructor() {
|
||||||
|
super("api_auth.SignTokenRequest", [
|
||||||
|
{ no: 1, name: "expires_in", kind: "scalar", opt: true, T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ },
|
||||||
|
{ no: 2, name: "permissions", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => APIPermission }
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
create(value?: PartialMessage<SignTokenRequest>): SignTokenRequest {
|
||||||
|
const message = globalThis.Object.create((this.messagePrototype!));
|
||||||
|
message.permissions = [];
|
||||||
|
if (value !== undefined)
|
||||||
|
reflectionMergePartial<SignTokenRequest>(this, message, value);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: SignTokenRequest): SignTokenRequest {
|
||||||
|
let message = target ?? this.create(), end = reader.pos + length;
|
||||||
|
while (reader.pos < end) {
|
||||||
|
let [fieldNo, wireType] = reader.tag();
|
||||||
|
switch (fieldNo) {
|
||||||
|
case /* optional int64 expires_in */ 1:
|
||||||
|
message.expiresIn = reader.int64().toBigInt();
|
||||||
|
break;
|
||||||
|
case /* repeated api_auth.APIPermission permissions */ 2:
|
||||||
|
message.permissions.push(APIPermission.internalBinaryRead(reader, reader.uint32(), options));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
let u = options.readUnknownField;
|
||||||
|
if (u === "throw")
|
||||||
|
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
|
||||||
|
let d = reader.skip(wireType);
|
||||||
|
if (u !== false)
|
||||||
|
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
internalBinaryWrite(message: SignTokenRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
|
||||||
|
/* optional int64 expires_in = 1; */
|
||||||
|
if (message.expiresIn !== undefined)
|
||||||
|
writer.tag(1, WireType.Varint).int64(message.expiresIn);
|
||||||
|
/* repeated api_auth.APIPermission permissions = 2; */
|
||||||
|
for (let i = 0; i < message.permissions.length; i++)
|
||||||
|
APIPermission.internalBinaryWrite(message.permissions[i], writer.tag(2, WireType.LengthDelimited).fork(), options).join();
|
||||||
|
let u = options.writeUnknownFields;
|
||||||
|
if (u !== false)
|
||||||
|
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
|
||||||
|
return writer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @generated MessageType for protobuf message api_auth.SignTokenRequest
|
||||||
|
*/
|
||||||
|
export const SignTokenRequest = new SignTokenRequest$Type();
|
||||||
|
// @generated message type with reflection information, may provide speed optimized methods
|
||||||
|
class SignTokenResponse$Type extends MessageType<SignTokenResponse> {
|
||||||
|
constructor() {
|
||||||
|
super("api_auth.SignTokenResponse", [
|
||||||
|
{ no: 1, name: "status", kind: "message", T: () => Status },
|
||||||
|
{ no: 2, name: "token", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ }
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
create(value?: PartialMessage<SignTokenResponse>): SignTokenResponse {
|
||||||
|
const message = globalThis.Object.create((this.messagePrototype!));
|
||||||
|
if (value !== undefined)
|
||||||
|
reflectionMergePartial<SignTokenResponse>(this, message, value);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: SignTokenResponse): SignTokenResponse {
|
||||||
|
let message = target ?? this.create(), end = reader.pos + length;
|
||||||
|
while (reader.pos < end) {
|
||||||
|
let [fieldNo, wireType] = reader.tag();
|
||||||
|
switch (fieldNo) {
|
||||||
|
case /* optional common.Status status */ 1:
|
||||||
|
message.status = Status.internalBinaryRead(reader, reader.uint32(), options, message.status);
|
||||||
|
break;
|
||||||
|
case /* optional string token */ 2:
|
||||||
|
message.token = reader.string();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
let u = options.readUnknownField;
|
||||||
|
if (u === "throw")
|
||||||
|
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
|
||||||
|
let d = reader.skip(wireType);
|
||||||
|
if (u !== false)
|
||||||
|
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
internalBinaryWrite(message: SignTokenResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
|
||||||
|
/* optional common.Status status = 1; */
|
||||||
|
if (message.status)
|
||||||
|
Status.internalBinaryWrite(message.status, writer.tag(1, WireType.LengthDelimited).fork(), options).join();
|
||||||
|
/* optional string token = 2; */
|
||||||
|
if (message.token !== undefined)
|
||||||
|
writer.tag(2, WireType.LengthDelimited).string(message.token);
|
||||||
|
let u = options.writeUnknownFields;
|
||||||
|
if (u !== false)
|
||||||
|
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
|
||||||
|
return writer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @generated MessageType for protobuf message api_auth.SignTokenResponse
|
||||||
|
*/
|
||||||
|
export const SignTokenResponse = new SignTokenResponse$Type();
|
||||||
|
@@ -23,6 +23,10 @@ export interface InitClientRequest {
|
|||||||
* @generated from protobuf field: optional string client_id = 1;
|
* @generated from protobuf field: optional string client_id = 1;
|
||||||
*/
|
*/
|
||||||
clientId?: string;
|
clientId?: string;
|
||||||
|
/**
|
||||||
|
* @generated from protobuf field: optional bool ephemeral = 2;
|
||||||
|
*/
|
||||||
|
ephemeral?: boolean;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @generated from protobuf message api_client.InitClientResponse
|
* @generated from protobuf message api_client.InitClientResponse
|
||||||
@@ -391,7 +395,8 @@ export interface GetProxyConfigResponse {
|
|||||||
class InitClientRequest$Type extends MessageType<InitClientRequest> {
|
class InitClientRequest$Type extends MessageType<InitClientRequest> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("api_client.InitClientRequest", [
|
super("api_client.InitClientRequest", [
|
||||||
{ no: 1, name: "client_id", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ }
|
{ no: 1, name: "client_id", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ },
|
||||||
|
{ no: 2, name: "ephemeral", kind: "scalar", opt: true, T: 8 /*ScalarType.BOOL*/ }
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
create(value?: PartialMessage<InitClientRequest>): InitClientRequest {
|
create(value?: PartialMessage<InitClientRequest>): InitClientRequest {
|
||||||
@@ -408,6 +413,9 @@ class InitClientRequest$Type extends MessageType<InitClientRequest> {
|
|||||||
case /* optional string client_id */ 1:
|
case /* optional string client_id */ 1:
|
||||||
message.clientId = reader.string();
|
message.clientId = reader.string();
|
||||||
break;
|
break;
|
||||||
|
case /* optional bool ephemeral */ 2:
|
||||||
|
message.ephemeral = reader.bool();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
let u = options.readUnknownField;
|
let u = options.readUnknownField;
|
||||||
if (u === "throw")
|
if (u === "throw")
|
||||||
@@ -423,6 +431,9 @@ class InitClientRequest$Type extends MessageType<InitClientRequest> {
|
|||||||
/* optional string client_id = 1; */
|
/* optional string client_id = 1; */
|
||||||
if (message.clientId !== undefined)
|
if (message.clientId !== undefined)
|
||||||
writer.tag(1, WireType.LengthDelimited).string(message.clientId);
|
writer.tag(1, WireType.LengthDelimited).string(message.clientId);
|
||||||
|
/* optional bool ephemeral = 2; */
|
||||||
|
if (message.ephemeral !== undefined)
|
||||||
|
writer.tag(2, WireType.Varint).bool(message.ephemeral);
|
||||||
let u = options.writeUnknownFields;
|
let u = options.writeUnknownFields;
|
||||||
if (u !== false)
|
if (u !== false)
|
||||||
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
|
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
|
||||||
|
@@ -85,6 +85,14 @@ export interface Client {
|
|||||||
* @generated from protobuf field: optional string frps_url = 10;
|
* @generated from protobuf field: optional string frps_url = 10;
|
||||||
*/
|
*/
|
||||||
frpsUrl?: string; // 客户端用于连接frps的url,解决 frp 在 CDN 后的问题,格式类似 [tcp/ws/wss/quic/kcp]://example.com:7000
|
frpsUrl?: string; // 客户端用于连接frps的url,解决 frp 在 CDN 后的问题,格式类似 [tcp/ws/wss/quic/kcp]://example.com:7000
|
||||||
|
/**
|
||||||
|
* @generated from protobuf field: optional bool ephemeral = 11;
|
||||||
|
*/
|
||||||
|
ephemeral?: boolean; // 是否临时节点
|
||||||
|
/**
|
||||||
|
* @generated from protobuf field: optional int64 last_seen_at = 12;
|
||||||
|
*/
|
||||||
|
lastSeenAt?: bigint; // 最后一次心跳时间戳
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @generated from protobuf message common.Server
|
* @generated from protobuf message common.Server
|
||||||
@@ -467,7 +475,9 @@ class Client$Type extends MessageType<Client> {
|
|||||||
{ no: 7, name: "stopped", kind: "scalar", opt: true, T: 8 /*ScalarType.BOOL*/ },
|
{ no: 7, name: "stopped", kind: "scalar", opt: true, T: 8 /*ScalarType.BOOL*/ },
|
||||||
{ no: 8, name: "client_ids", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ },
|
{ no: 8, name: "client_ids", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ },
|
||||||
{ no: 9, name: "origin_client_id", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ },
|
{ no: 9, name: "origin_client_id", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ },
|
||||||
{ no: 10, name: "frps_url", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ }
|
{ no: 10, name: "frps_url", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ },
|
||||||
|
{ no: 11, name: "ephemeral", kind: "scalar", opt: true, T: 8 /*ScalarType.BOOL*/ },
|
||||||
|
{ no: 12, name: "last_seen_at", kind: "scalar", opt: true, T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ }
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
create(value?: PartialMessage<Client>): Client {
|
create(value?: PartialMessage<Client>): Client {
|
||||||
@@ -509,6 +519,12 @@ class Client$Type extends MessageType<Client> {
|
|||||||
case /* optional string frps_url */ 10:
|
case /* optional string frps_url */ 10:
|
||||||
message.frpsUrl = reader.string();
|
message.frpsUrl = reader.string();
|
||||||
break;
|
break;
|
||||||
|
case /* optional bool ephemeral */ 11:
|
||||||
|
message.ephemeral = reader.bool();
|
||||||
|
break;
|
||||||
|
case /* optional int64 last_seen_at */ 12:
|
||||||
|
message.lastSeenAt = reader.int64().toBigInt();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
let u = options.readUnknownField;
|
let u = options.readUnknownField;
|
||||||
if (u === "throw")
|
if (u === "throw")
|
||||||
@@ -548,6 +564,12 @@ class Client$Type extends MessageType<Client> {
|
|||||||
/* optional string frps_url = 10; */
|
/* optional string frps_url = 10; */
|
||||||
if (message.frpsUrl !== undefined)
|
if (message.frpsUrl !== undefined)
|
||||||
writer.tag(10, WireType.LengthDelimited).string(message.frpsUrl);
|
writer.tag(10, WireType.LengthDelimited).string(message.frpsUrl);
|
||||||
|
/* optional bool ephemeral = 11; */
|
||||||
|
if (message.ephemeral !== undefined)
|
||||||
|
writer.tag(11, WireType.Varint).bool(message.ephemeral);
|
||||||
|
/* optional int64 last_seen_at = 12; */
|
||||||
|
if (message.lastSeenAt !== undefined)
|
||||||
|
writer.tag(12, WireType.Varint).int64(message.lastSeenAt);
|
||||||
let u = options.writeUnknownFields;
|
let u = options.writeUnknownFields;
|
||||||
if (u !== false)
|
if (u !== false)
|
||||||
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
|
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
|
||||||
|
Reference in New Issue
Block a user