diff --git a/assets/lang/en.json b/assets/lang/en.json
index 14e3cb5..8e72149 100644
--- a/assets/lang/en.json
+++ b/assets/lang/en.json
@@ -29,6 +29,7 @@
"Other error": "Other error",
"Param error": "Param error",
"User exist": "User exist",
+ "User not exist": "User not exist",
"User format error": "User cannot be empty or include space char. It only allowed alphanumeric and underline.",
"Token format error": "Token cannot be empty or include space char. It allow include those special char: _!@#$%^&*()",
"Please check at least one user": "Please Check at least one user",
diff --git a/assets/lang/zh.json b/assets/lang/zh.json
index 0b359be..845749e 100644
--- a/assets/lang/zh.json
+++ b/assets/lang/zh.json
@@ -29,6 +29,7 @@
"Other error": "其他异常",
"Param error": "参数异常",
"User exist": "用户已经存在",
+ "User not exist": "用户不存在",
"User format error": "用户不能为空或包含空格。只允许英文数字、字母、下划线",
"Token format error": "Token不能为空或包含空格。允许的特殊符号:_!@#$%^&*()",
"Please check at least one user": "请选中需要操作的用户",
diff --git a/assets/static/css/color.css b/assets/static/css/color.css
index dcc7d9a..a637f31 100644
--- a/assets/static/css/color.css
+++ b/assets/static/css/color.css
@@ -46,6 +46,14 @@
border-color: #79bbff;
}
+.layui-form-checkbox[lay-skin=primary] > .layui-icon-indeterminate:before {
+ background-color: #79bbff;
+}
+
+.layui-form-checkbox[lay-skin=primary]>.layui-icon-indeterminate{
+ border-color: #79bbff;
+}
+
.layui-form-checked[lay-skin=primary] > i {
background-color: #409eff;
border-color: #409eff !important;
@@ -173,6 +181,14 @@ section.proxy-list .proxy-info .layui-row .layui-row > div:first-child {
border-color: #5f5f60;
}
+ .layui-form-checkbox[lay-skin=primary] > .layui-icon-indeterminate:before {
+ background-color: #5f5f60;
+ }
+
+ .layui-form-checkbox[lay-skin=primary]>.layui-icon-indeterminate{
+ border-color: #5f5f60;
+ }
+
.layui-form-checked[lay-skin=primary] > i {
background-color: #484849;
border-color: #484849 !important;
diff --git a/assets/static/js/index-user-list.js b/assets/static/js/index-user-list.js
index 80ef350..c317f10 100644
--- a/assets/static/js/index-user-list.js
+++ b/assets/static/js/index-user-list.js
@@ -184,7 +184,7 @@ var loadUserList = (function ($) {
if (domains.trim() !== '') {
try {
domains.split(',').forEach(function (domain) {
- if (!/^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62}){1,3}$/.test(domain.trim())) {
+ if (!/^([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\.)+[a-zA-Z]{2,}$/.test(domain.trim())) {
valid = false;
throw 'break';
}
@@ -209,7 +209,7 @@ var loadUserList = (function ($) {
if (subdomains.trim() !== '') {
try {
subdomains.split(',').forEach(function (subdomain) {
- if (!/^[a-zA-z0-9][a-zA-Z0-9-]{0,19}$/.test(subdomain.trim())) {
+ if (!/^[a-zA-z0-9][a-zA-z0-9-]{0,19}$/.test(subdomain.trim())) {
valid = false;
throw 'break';
}
@@ -227,7 +227,7 @@ var loadUserList = (function ($) {
* set verify rule of layui.form
*/
(function setFormVerifyRule() {
- layui.form.verify(verifyRules);
+ // layui.form.verify(verifyRules);
})();
/**
@@ -251,7 +251,9 @@ var loadUserList = (function ($) {
where: {},
dataType: 'json',
editTrigger: 'dblclick',
- page: navigator.language.indexOf("zh") !== -1,
+ page: {
+ layout: navigator.language.indexOf("zh") === -1 ? ['first', 'prev', 'next', 'last'] : ['prev', 'page', 'next', 'skip', 'count', 'limit']
+ },
toolbar: '#userListToolbarTemplate',
defaultToolbar: false,
cols: [[
@@ -270,7 +272,14 @@ var loadUserList = (function ($) {
sort: true
},
{title: i18n['Operation'], width: 150, toolbar: '#userListOperationTemplate'}
- ]]
+ ]],
+ parseData: function (res) {
+ res.data.forEach(function (data) {
+ data.ports = data.ports.join(',');
+ data.domains = data.domains.join(',');
+ data.subdomains = data.subdomains.join(',');
+ });
+ }
});
bindFormEvent();
@@ -348,7 +357,6 @@ var loadUserList = (function ($) {
before.ports = before.ports.split(',')
before.domains = before.domains.split(',')
before.subdomains = before.subdomains.split(',')
-
after.ports = after.ports.split(',')
after.domains = after.domains.split(',')
after.subdomains = after.subdomains.split(',')
@@ -359,8 +367,14 @@ var loadUserList = (function ($) {
layui.table.on('toolbar(tokenTable)', function (obj) {
var id = obj.config.id;
var checkStatus = layui.table.checkStatus(id);
-
var data = checkStatus.data;
+
+ data.forEach(function (temp) {
+ temp.ports = temp.ports.split(',')
+ temp.domains = temp.domains.split(',')
+ temp.subdomains = temp.subdomains.split(',')
+ });
+
switch (obj.event) {
case 'add':
addPopup();
@@ -379,6 +393,10 @@ var loadUserList = (function ($) {
layui.table.on('tool(tokenTable)', function (obj) {
var data = obj.data;
+
+ data.ports = data.ports.split(',')
+ data.domains = data.domains.split(',')
+ data.subdomains = data.subdomains.split(',')
switch (obj.event) {
case 'remove':
removePopup(data);
@@ -418,9 +436,15 @@ var loadUserList = (function ($) {
btn1: function (index) {
if (layui.form.validate('#addUserForm')) {
var formData = layui.form.val('addUserForm');
- formData.ports = formData.ports.split(',')
- formData.domains = formData.domains.split(',')
- formData.subdomains = formData.subdomains.split(',')
+ if (formData.ports != null) {
+ formData.ports = formData.ports.split(',')
+ }
+ if (formData.domains != null) {
+ formData.domains = formData.domains.split(',')
+ }
+ if (formData.subdomains != null) {
+ formData.subdomains = formData.subdomains.split(',')
+ }
add(formData, index);
}
},
@@ -638,17 +662,27 @@ var loadUserList = (function ($) {
* @param result
*/
function errorMsg(result) {
- var reason = i18n['Other Error'];
+ var reason = i18n['OtherError'];
if (result.code === 1)
reason = i18n['ParamError'];
else if (result.code === 2)
reason = i18n['UserExist'];
else if (result.code === 3)
- reason = i18n['ParamError'];
+ reason = i18n['UserNotExist'];
else if (result.code === 4)
- reason = i18n['UserFormatError'];
+ reason = i18n['ParamError'];
else if (result.code === 5)
+ reason = i18n['UserFormatError'];
+ else if (result.code === 6)
reason = i18n['TokenFormatError'];
+ else if (result.code === 7)
+ reason = i18n['CommentInvalid'];
+ else if (result.code === 8)
+ reason = i18n['PortsInvalid'];
+ else if (result.code === 9)
+ reason = i18n['DomainsInvalid'];
+ else if (result.code === 10)
+ reason = i18n['SubdomainsInvalid'];
layui.layer.msg(i18n['OperateFailed'] + ',' + reason)
}
diff --git a/assets/templates/index.html b/assets/templates/index.html
index 2f026f5..5953d21 100644
--- a/assets/templates/index.html
+++ b/assets/templates/index.html
@@ -1,5 +1,5 @@
-
+
${ .FrpsPanel }
diff --git a/cmd/frps-panel/cmd.go b/cmd/frps-panel/cmd.go
index 6d69ca3..513fe55 100644
--- a/cmd/frps-panel/cmd.go
+++ b/cmd/frps-panel/cmd.go
@@ -70,34 +70,22 @@ func Execute() {
}
func parseConfigFile(configFile, tokensFile string) (controller.HandleController, server.TLS, error) {
- var config controller.Config
- _, err := toml.DecodeFile(configFile, &config)
+ var common controller.Common
+ var tokens controller.Tokens
+ _, err := toml.DecodeFile(configFile, &common)
if err != nil {
log.Fatalf("decode config file %v error: %v", configFile, err)
}
- _, err = toml.DecodeFile(tokensFile, &config)
+ _, err = toml.DecodeFile(tokensFile, &tokens)
if err != nil {
log.Fatalf("decode token file %v error: %v", tokensFile, err)
}
- config.Common.DashboardTls = strings.HasPrefix("https://", strings.ToLower(config.Common.DashboardAddr))
-
- //f, err := os.Create("/Volumes/Work/Git Sources/frps-panel/config/frps-panel-new.toml")
- //if err != nil {
- // log.Fatal(err)
- //}
- //if err := toml.NewEncoder(f).Encode(config); err != nil {
- // // failed to encode
- // log.Fatal(err)
- //}
- //if err := f.Close(); err != nil {
- // // failed to close the file
- // log.Fatal(err)
- //}
+ common.Common.DashboardTls = strings.HasPrefix("https://", strings.ToLower(common.Common.DashboardAddr))
tls := server.TLS{
- Enable: config.Common.TlsMode,
+ Enable: common.Common.TlsMode,
Protocol: "HTTP",
}
@@ -112,8 +100,8 @@ func parseConfigFile(configFile, tokensFile string) (controller.HandleController
}
return controller.HandleController{
- CommonInfo: config.Common,
- Tokens: config.Tokens.Tokens,
+ CommonInfo: common.Common,
+ Tokens: tokens.Tokens,
Version: version,
ConfigFile: configFile,
TokensFile: tokensFile,
diff --git a/config/frps-panel.ini b/config/frps-panel.ini
deleted file mode 100644
index 3d1e3bc..0000000
--- a/config/frps-panel.ini
+++ /dev/null
@@ -1,36 +0,0 @@
-; basic options
-[common]
-; frps panel config info
-plugin_addr = 127.0.0.1
-plugin_port = 7200
-admin_user = admin
-admin_pwd = admin
-; specified login state keep time
-admin_keep_time = 0
-
-; enable tls
-tls_mode = false
-; tls_cert_file = cert.crt
-; tls_key_file = cert.key
-
-; frp dashboard info
-dashboard_addr = 127.0.0.1
-dashboard_port = 7500
-dashboard_user = admin
-dashboard_pwd = admin
-
-; user tokens
-[users]
-user1 = token1
-
-; user been disabled
-[disabled]
-
-; user allowed ports. it will be used when a new proxy connect on
-[ports]
-
-; user allowed domains. it will be used when a new proxy connect on
-[domains]
-
-; user allowed subdomains. it will be used when a new proxy connect on
-[subdomains]
diff --git a/config/frps-tokens.toml b/config/frps-tokens.toml
index b5b565a..a69f66b 100644
--- a/config/frps-tokens.toml
+++ b/config/frps-tokens.toml
@@ -3,15 +3,15 @@
user = "user1"
token = "token1"
comment = "张三"
- ports = ["1", "2", "3", "10-100"]
- domains = ["aaa.com", "bbb.com"]
- subdomains = ["a", "b"]
+ ports = [""]
+ domains = [""]
+ subdomains = [""]
status = true
[tokens.user2]
user = "user2"
token = "token2"
comment = "李四"
- ports = ["11", "22", "33", "110-200"]
- domains = ["ccc.com", "ddd.com"]
- subdomains = ["c", "d"]
+ ports = [""]
+ domains = [""]
+ subdomains = [""]
status = true
diff --git a/pkg/server/controller/authorizer.go b/pkg/server/controller/authorizer.go
index 75a98ee..34f062a 100644
--- a/pkg/server/controller/authorizer.go
+++ b/pkg/server/controller/authorizer.go
@@ -12,7 +12,7 @@ import (
func (c *HandleController) BasicAuth() gin.HandlerFunc {
return func(context *gin.Context) {
- if strings.TrimSpace(c.CommonInfo.AdminUser) == "" || strings.TrimSpace(c.CommonInfo.AdminPwd) == "" {
+ if trimString(c.CommonInfo.AdminUser) == "" || trimString(c.CommonInfo.AdminPwd) == "" {
if context.Request.RequestURI == LoginUrl {
context.Redirect(http.StatusTemporaryRedirect, LoginSuccessUrl)
}
@@ -54,7 +54,7 @@ func (c *HandleController) BasicAuth() gin.HandlerFunc {
}
func (c *HandleController) LoginAuth(username, password string, context *gin.Context) bool {
- if strings.TrimSpace(c.CommonInfo.AdminUser) == "" || strings.TrimSpace(c.CommonInfo.AdminPwd) == "" {
+ if trimString(c.CommonInfo.AdminUser) == "" || trimString(c.CommonInfo.AdminPwd) == "" {
return true
}
diff --git a/pkg/server/controller/controller.go b/pkg/server/controller/controller.go
index 2abbafa..e82f84c 100644
--- a/pkg/server/controller/controller.go
+++ b/pkg/server/controller/controller.go
@@ -5,14 +5,12 @@ import (
"encoding/json"
"errors"
"fmt"
- "github.com/BurntSushi/toml"
plugin "github.com/fatedier/frp/pkg/plugin/server"
ginI18n "github.com/gin-contrib/i18n"
"github.com/gin-gonic/gin"
"io"
"log"
"net/http"
- "os"
"sort"
"strconv"
"strings"
@@ -123,7 +121,7 @@ func (c *HandleController) MakeIndexFunc() func(context *gin.Context) {
return func(context *gin.Context) {
context.HTML(http.StatusOK, "index.html", gin.H{
"version": c.Version,
- "showExit": strings.TrimSpace(c.CommonInfo.AdminUser) != "" && strings.TrimSpace(c.CommonInfo.AdminPwd) != "",
+ "showExit": trimString(c.CommonInfo.AdminUser) != "" && trimString(c.CommonInfo.AdminPwd) != "",
"FrpsPanel": ginI18n.MustGetMessage(context, "Frps Panel"),
"User": ginI18n.MustGetMessage(context, "User"),
"Token": ginI18n.MustGetMessage(context, "Token"),
@@ -206,6 +204,7 @@ func (c *HandleController) MakeLangFunc() func(context *gin.Context) {
"OperateError": ginI18n.MustGetMessage(context, "Operate error"),
"OperateFailed": ginI18n.MustGetMessage(context, "Operate failed"),
"UserExist": ginI18n.MustGetMessage(context, "User exist"),
+ "UserNotExist": ginI18n.MustGetMessage(context, "User not exist"),
"UserFormatError": ginI18n.MustGetMessage(context, "User format error"),
"TokenFormatError": ginI18n.MustGetMessage(context, "Token format error"),
"ShouldCheckUser": ginI18n.MustGetMessage(context, "Please check at least one user"),
@@ -220,6 +219,7 @@ func (c *HandleController) MakeLangFunc() func(context *gin.Context) {
"SubdomainsInvalid": ginI18n.MustGetMessage(context, "Subdomains is invalid"),
"CommentInvalid": ginI18n.MustGetMessage(context, "Comment is invalid"),
"ParamError": ginI18n.MustGetMessage(context, "Param error"),
+ "OtherError": ginI18n.MustGetMessage(context, "Other error"),
"Name": ginI18n.MustGetMessage(context, "Name"),
"Port": ginI18n.MustGetMessage(context, "Port"),
"Connections": ginI18n.MustGetMessage(context, "Connections"),
@@ -287,52 +287,6 @@ func (c *HandleController) MakeQueryTokensFunc() func(context *gin.Context) {
}
}
-func filter(main TokenInfo, sub TokenInfo) bool {
- replaceSpaceUser := TrimAllSpaceReg.ReplaceAllString(sub.User, "")
- if len(replaceSpaceUser) != 0 {
- if !strings.Contains(main.User, replaceSpaceUser) {
- return false
- }
- }
-
- replaceSpaceToken := TrimAllSpaceReg.ReplaceAllString(sub.Token, "")
- if len(replaceSpaceToken) != 0 {
- if !strings.Contains(main.Token, replaceSpaceToken) {
- return false
- }
- }
-
- replaceSpaceComment := TrimAllSpaceReg.ReplaceAllString(sub.Comment, "")
- if len(replaceSpaceComment) != 0 {
- if !strings.Contains(main.Comment, replaceSpaceComment) {
- return false
- }
- }
- return true
-}
-
-func TokensList(tokens map[string]TokenInfo) Tokens {
- return Tokens{
- tokens,
- }
-}
-
-func (c *HandleController) SaveToken() error {
- tokenFile, err := os.Create(c.TokensFile)
- if err != nil {
- log.Printf("error to crate file %v: %v", c.TokensFile, err)
- }
-
- if err = toml.NewEncoder(tokenFile).Encode(TokensList(c.Tokens)); err != nil {
- log.Printf("error to encode tokens: %v", err)
- }
- if err = tokenFile.Close(); err != nil {
- log.Printf("error to close file %v: %v", c.TokensFile, err)
- }
-
- return err
-}
-
func (c *HandleController) MakeAddTokenFunc() func(context *gin.Context) {
return func(context *gin.Context) {
info := TokenInfo{
@@ -345,46 +299,34 @@ func (c *HandleController) MakeAddTokenFunc() func(context *gin.Context) {
}
err := context.BindJSON(&info)
if err != nil {
- log.Printf("user add failed, param error : %v", err)
response.Success = false
response.Code = ParamError
- response.Message = "user add failed, param error "
+ response.Message = fmt.Sprintf("user add failed, param error : %v", err)
+ log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
- if !UserFormatReg.MatchString(info.User) {
- log.Printf("user add failed, user format error")
- response.Success = false
- response.Code = UserFormatError
- response.Message = fmt.Sprintf("user add failed, user format error")
- context.JSON(http.StatusOK, &response)
- return
- }
- if _, exist := c.Tokens[info.User]; exist {
- log.Printf("user add failed, user [%v] exist", info.User)
- response.Success = false
- response.Code = UserExist
- response.Message = fmt.Sprintf("user add failed, user [%s] exist ", info.User)
- context.JSON(http.StatusOK, &response)
- return
- }
- if !TokenFormatReg.MatchString(info.Token) {
- log.Printf("user add failed, token format error")
- response.Success = false
- response.Code = TokenFormatError
- response.Message = fmt.Sprintf("user add failed, token format error")
- context.JSON(http.StatusOK, &response)
+
+ result := c.verifyToken(info, TOKEN_ADD)
+
+ if !result.Success {
+ context.JSON(http.StatusOK, &result)
return
}
+ info.Comment = cleanString(info.Comment)
+ info.Ports = cleanStrings(info.Ports)
+ info.Domains = cleanStrings(info.Domains)
+ info.Subdomains = cleanStrings(info.Subdomains)
+
c.Tokens[info.User] = info
- err = c.SaveToken()
+ err = c.saveToken()
if err != nil {
- log.Printf("add failed, error : %v", err)
response.Success = false
response.Code = SaveError
- response.Message = "user add failed"
+ response.Message = fmt.Sprintf("add failed, error : %v", err)
+ log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
@@ -403,10 +345,10 @@ func (c *HandleController) MakeUpdateTokensFunc() func(context *gin.Context) {
update := TokenUpdate{}
err := context.BindJSON(&update)
if err != nil {
- log.Printf("update failed, param error : %v", err)
response.Success = false
response.Code = ParamError
- response.Message = "user update failed, param error"
+ response.Message = fmt.Sprintf("update failed, param error : %v", err)
+ log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
@@ -414,32 +356,35 @@ func (c *HandleController) MakeUpdateTokensFunc() func(context *gin.Context) {
before := update.Before
after := update.After
- if after.User != before.User {
- log.Printf("update failed, user not match")
+ if before.User != after.User {
response.Success = false
response.Code = ParamError
- response.Message = "update failed, user not match"
+ response.Message = fmt.Sprintf("update failed, user should be same : before -> %v, after -> %v", before.User, after.User)
+ log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
- if !TokenFormatReg.MatchString(after.Token) {
- log.Printf("update failed, token format error")
- response.Success = false
- response.Code = TokenFormatError
- response.Message = "user update failed, token format error"
- context.JSON(http.StatusOK, &response)
+ result := c.verifyToken(after, TOKEN_UPDATE)
+
+ if !result.Success {
+ context.JSON(http.StatusOK, &result)
return
}
+ after.Comment = cleanString(after.Comment)
+ after.Ports = cleanStrings(after.Ports)
+ after.Domains = cleanStrings(after.Domains)
+ after.Subdomains = cleanStrings(after.Subdomains)
+
c.Tokens[after.User] = after
- err = c.SaveToken()
+ err = c.saveToken()
if err != nil {
- log.Printf("user update failed, error : %v", err)
response.Success = false
response.Code = SaveError
- response.Message = "user update failed"
+ response.Message = fmt.Sprintf("user update failed, error : %v", err)
+ log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
@@ -458,24 +403,33 @@ func (c *HandleController) MakeRemoveTokensFunc() func(context *gin.Context) {
remove := TokenRemove{}
err := context.BindJSON(&remove)
if err != nil {
- log.Printf("user remove failed, param error : %v", err)
response.Success = false
response.Code = ParamError
- response.Message = "user remove failed, param error "
+ response.Message = fmt.Sprintf("user remove failed, param error : %v", err)
+ log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
+ for _, user := range remove.Users {
+ result := c.verifyToken(user, TOKEN_REMOVE)
+
+ if !result.Success {
+ context.JSON(http.StatusOK, &result)
+ return
+ }
+ }
+
for _, user := range remove.Users {
delete(c.Tokens, user.User)
}
- err = c.SaveToken()
+ err = c.saveToken()
if err != nil {
- log.Printf("user update failed, error : %v", err)
response.Success = false
response.Code = SaveError
- response.Message = "user update failed"
+ response.Message = fmt.Sprintf("user update failed, error : %v", err)
+ log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
@@ -494,27 +448,36 @@ func (c *HandleController) MakeDisableTokensFunc() func(context *gin.Context) {
disable := TokenDisable{}
err := context.BindJSON(&disable)
if err != nil {
- log.Printf("disable failed, param error : %v", err)
response.Success = false
response.Code = ParamError
- response.Message = "disable failed, param error "
+ response.Message = fmt.Sprintf("disable failed, param error : %v", err)
+ log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
+ for _, user := range disable.Users {
+ result := c.verifyToken(user, TOKEN_DISABLE)
+
+ if !result.Success {
+ context.JSON(http.StatusOK, &result)
+ return
+ }
+ }
+
for _, user := range disable.Users {
token := c.Tokens[user.User]
token.Status = false
c.Tokens[user.User] = token
}
- err = c.SaveToken()
+ err = c.saveToken()
if err != nil {
- log.Printf("disable failed, error : %v", err)
response.Success = false
response.Code = SaveError
- response.Message = "disable failed"
+ response.Message = fmt.Sprintf("disable failed, error : %v", err)
+ log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
@@ -533,21 +496,30 @@ func (c *HandleController) MakeEnableTokensFunc() func(context *gin.Context) {
enable := TokenEnable{}
err := context.BindJSON(&enable)
if err != nil {
- log.Printf("enable failed, param error : %v", err)
response.Success = false
response.Code = ParamError
- response.Message = "enable failed, param error "
+ response.Message = fmt.Sprintf("enable failed, param error : %v", err)
+ log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
+ for _, user := range enable.Users {
+ result := c.verifyToken(user, TOKEN_ENABLE)
+
+ if !result.Success {
+ context.JSON(http.StatusOK, &result)
+ return
+ }
+ }
+
for _, user := range enable.Users {
token := c.Tokens[user.User]
token.Status = true
c.Tokens[user.User] = token
}
- err = c.SaveToken()
+ err = c.saveToken()
if err != nil {
log.Printf("enable failed, error : %v", err)
@@ -588,7 +560,7 @@ func (c *HandleController) MakeProxyFunc() func(context *gin.Context) {
request, _ := http.NewRequest("GET", requestUrl, nil)
username := c.CommonInfo.DashboardUser
password := c.CommonInfo.DashboardPwd
- if len(strings.TrimSpace(username)) != 0 && len(strings.TrimSpace(password)) != 0 {
+ if trimString(username) != "" && trimString(password) != "" {
request.SetBasicAuth(username, password)
log.Printf("Proxy to %s", requestUrl)
}
diff --git a/pkg/server/controller/handler.go b/pkg/server/controller/handler.go
index 735d500..9114d48 100644
--- a/pkg/server/controller/handler.go
+++ b/pkg/server/controller/handler.go
@@ -78,7 +78,7 @@ func (c *HandleController) JudgePort(content *plugin.NewProxyContent) plugin.Res
}
proxyType := content.ProxyType
- if StringIndexOf(proxyType, supportProxyTypes) == -1 {
+ if stringContains(proxyType, supportProxyTypes) {
log.Printf("proxy type [%v] not support, plugin do nothing", proxyType)
res.Unchange = true
return res
@@ -100,12 +100,12 @@ func (c *HandleController) JudgePort(content *plugin.NewProxyContent) plugin.Res
portErr = fmt.Errorf("user [%v] port range [%v] format error", user, port)
break
}
- start, err := strconv.Atoi(strings.TrimSpace(allowedRanges[0]))
+ start, err := strconv.Atoi(trimString(allowedRanges[0]))
if err != nil {
portErr = fmt.Errorf("user [%v] port rang [%v] start port [%v] is not a number", user, port, allowedRanges[0])
break
}
- end, err := strconv.Atoi(strings.TrimSpace(allowedRanges[1]))
+ end, err := strconv.Atoi(trimString(allowedRanges[1]))
if err != nil {
portErr = fmt.Errorf("user [%v] port rang [%v] end port [%v] is not a number", user, port, allowedRanges[0])
break
@@ -141,7 +141,7 @@ func (c *HandleController) JudgePort(content *plugin.NewProxyContent) plugin.Res
if portAllowed {
if token, exist := c.Tokens[user]; exist {
for _, userDomain := range userDomains {
- if StringIndexOf(userDomain, token.Domains) == -1 {
+ if stringContains(userDomain, token.Domains) {
domainAllowed = false
break
}
@@ -183,12 +183,3 @@ func (c *HandleController) JudgePort(content *plugin.NewProxyContent) plugin.Res
}
return res
}
-
-func StringIndexOf(element string, data []string) int {
- for k, v := range data {
- if element == v {
- return k
- }
- }
- return -1
-}
diff --git a/pkg/server/controller/utils.go b/pkg/server/controller/utils.go
new file mode 100644
index 0000000..6181e4f
--- /dev/null
+++ b/pkg/server/controller/utils.go
@@ -0,0 +1,205 @@
+package controller
+
+import (
+ "fmt"
+ "github.com/BurntSushi/toml"
+ "log"
+ "os"
+ "strings"
+)
+
+func filter(main TokenInfo, sub TokenInfo) bool {
+ replaceSpaceUser := trimAllSpace.ReplaceAllString(sub.User, "")
+ if len(replaceSpaceUser) != 0 {
+ if !strings.Contains(main.User, replaceSpaceUser) {
+ return false
+ }
+ }
+
+ replaceSpaceToken := trimAllSpace.ReplaceAllString(sub.Token, "")
+ if len(replaceSpaceToken) != 0 {
+ if !strings.Contains(main.Token, replaceSpaceToken) {
+ return false
+ }
+ }
+
+ replaceSpaceComment := trimAllSpace.ReplaceAllString(sub.Comment, "")
+ if len(replaceSpaceComment) != 0 {
+ if !strings.Contains(main.Comment, replaceSpaceComment) {
+ return false
+ }
+ }
+ return true
+}
+
+func trimString(str string) string {
+ return strings.TrimSpace(str)
+}
+
+func (c *HandleController) verifyToken(token TokenInfo, operate int) OperationResponse {
+ response := OperationResponse{
+ Success: true,
+ Code: Success,
+ Message: "operate success",
+ }
+
+ var (
+ validateExist = false
+ validateNotExist = false
+ validateUser = false
+ validateToken = false
+ validateComment = false
+ validatePorts = false
+ validateDomains = false
+ validateSubdomains = false
+ )
+
+ if operate == TOKEN_ADD {
+ validateExist = true
+ validateUser = true
+ validateToken = true
+ validateComment = true
+ validatePorts = true
+ validateDomains = true
+ validateSubdomains = true
+ } else if operate == TOKEN_UPDATE {
+ validateNotExist = true
+ validateUser = true
+ validateToken = true
+ validateComment = true
+ validatePorts = true
+ validateDomains = true
+ validateSubdomains = true
+ } else if operate == TOKEN_ENABLE || operate == TOKEN_DISABLE || operate == TOKEN_REMOVE {
+ validateNotExist = true
+ }
+
+ if validateUser && !userFormat.MatchString(token.User) {
+ response.Success = false
+ response.Code = UserFormatError
+ response.Message = fmt.Sprintf("operate failed, user [%s] format error", token.User)
+ log.Printf(response.Message)
+ return response
+ }
+
+ if validateExist {
+ if _, exist := c.Tokens[token.User]; exist {
+ response.Success = false
+ response.Code = UserExist
+ response.Message = fmt.Sprintf("operate failed, user [%s] exist ", token.User)
+ log.Printf(response.Message)
+ return response
+ }
+ }
+
+ if validateNotExist {
+ if _, exist := c.Tokens[token.User]; !exist {
+ response.Success = false
+ response.Code = UserNotExist
+ response.Message = fmt.Sprintf("operate failed, user [%s] not exist ", token.User)
+ log.Printf(response.Message)
+ return response
+ }
+ }
+
+ if validateToken && !tokenFormat.MatchString(token.Token) {
+ response.Success = false
+ response.Code = TokenFormatError
+ response.Message = fmt.Sprintf("operate failed, token [%s] format error", token.Token)
+ log.Printf(response.Message)
+ return response
+ }
+
+ trimmedComment := trimString(token.Comment)
+ if validateComment && trimmedComment != "" && commentFormat.MatchString(trimmedComment) {
+ response.Success = false
+ response.Code = CommentFormatError
+ response.Message = fmt.Sprintf("operate failed, comment [%s] format error", token.Comment)
+ log.Printf(response.Message)
+ return response
+ }
+
+ if validatePorts {
+ for _, port := range token.Ports {
+ trimmedPort := trimString(port)
+ if trimmedPort != "" && !portsFormatSingle.MatchString(trimmedPort) && !portsFormatRange.MatchString(trimmedPort) {
+ response.Success = false
+ response.Code = PortsFormatError
+ response.Message = fmt.Sprintf("operate failed, ports [%v] format error", token.Ports)
+ log.Printf(response.Message)
+ return response
+ }
+ }
+ }
+
+ if validateDomains {
+ for _, domain := range token.Domains {
+ trimmedDomain := trimString(domain)
+ if trimmedDomain != "" && !domainFormat.MatchString(trimmedDomain) {
+ response.Success = false
+ response.Code = DomainsFormatError
+ response.Message = fmt.Sprintf("operate failed, domains [%v] format error", token.Domains)
+ log.Printf(response.Message)
+ return response
+ }
+ }
+ }
+
+ if validateSubdomains {
+ for _, subdomain := range token.Subdomains {
+ trimmedSubdomain := trimString(subdomain)
+ if trimmedSubdomain != "" && !subdomainFormat.MatchString(trimmedSubdomain) {
+ response.Success = false
+ response.Code = SubdomainsFormatError
+ response.Message = fmt.Sprintf("operate failed, subdomains [%v] format error", token.Subdomains)
+ log.Printf(response.Message)
+ return response
+ }
+ }
+ }
+
+ return response
+}
+
+func cleanStrings(originalStrings []string) []string {
+ cleanedStrings := make([]string, len(originalStrings))
+ for i, str := range originalStrings {
+ cleanedStrings[i] = cleanString(str)
+ }
+ return cleanedStrings
+}
+
+func cleanString(originalString string) string {
+ return trimAllSpace.ReplaceAllString(originalString, "")
+}
+
+func stringContains(element string, data []string) bool {
+ for _, v := range data {
+ if element == v {
+ return true
+ }
+ }
+ return false
+}
+
+func tokensList(tokens map[string]TokenInfo) Tokens {
+ return Tokens{
+ tokens,
+ }
+}
+
+func (c *HandleController) saveToken() error {
+ tokenFile, err := os.Create(c.TokensFile)
+ if err != nil {
+ log.Printf("error to crate file %v: %v", c.TokensFile, err)
+ }
+
+ if err = toml.NewEncoder(tokenFile).Encode(tokensList(c.Tokens)); err != nil {
+ log.Printf("error to encode tokens: %v", err)
+ }
+ if err = tokenFile.Close(); err != nil {
+ log.Printf("error to close file %v: %v", c.TokensFile, err)
+ }
+
+ return err
+}
diff --git a/pkg/server/controller/variables.go b/pkg/server/controller/variables.go
index 0d40038..07d074b 100644
--- a/pkg/server/controller/variables.go
+++ b/pkg/server/controller/variables.go
@@ -5,14 +5,29 @@ import (
)
const (
- Success = 0
- ParamError = 1
- UserExist = 2
- SaveError = 3
- UserFormatError = 4
- TokenFormatError = 5
- FrpServerError = 6
+ Success int = iota
+ ParamError
+ UserExist
+ UserNotExist
+ SaveError
+ UserFormatError
+ TokenFormatError
+ CommentFormatError
+ PortsFormatError
+ DomainsFormatError
+ SubdomainsFormatError
+ FrpServerError
+)
+const (
+ TOKEN_ADD int = iota
+ TOKEN_UPDATE
+ TOKEN_REMOVE
+ TOKEN_ENABLE
+ TOKEN_DISABLE
+)
+
+const (
SessionName = "GOSESSION"
AuthName = "_PANEL_AUTH"
LoginUrl = "/login"
@@ -22,9 +37,14 @@ const (
)
var (
- UserFormatReg = regexp.MustCompile("^\\w+$")
- TokenFormatReg = regexp.MustCompile("^[\\w!@#$%^&*()]+$")
- TrimAllSpaceReg = regexp.MustCompile("[\\n\\t\\r\\s]")
+ userFormat = regexp.MustCompile("^\\w+$")
+ tokenFormat = regexp.MustCompile("^[\\w!@#$%^&*()]+$")
+ commentFormat = regexp.MustCompile("[\\n\\t\\r]")
+ portsFormatSingle = regexp.MustCompile("^\\s*\\d{1,5}\\s*$")
+ portsFormatRange = regexp.MustCompile("^\\s*\\d{1,5}\\s*-\\s*\\d{1,5}\\s*$")
+ domainFormat = regexp.MustCompile("^([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\\.)+[a-zA-Z]{2,}$")
+ subdomainFormat = regexp.MustCompile("^[a-zA-z0-9-]{1,20}$")
+ trimAllSpace = regexp.MustCompile("[\\n\\t\\r\\s]")
)
type Response struct {
@@ -36,9 +56,8 @@ type HTTPError struct {
Err error
}
-type Config struct {
+type Common struct {
Common CommonInfo
- Tokens
}
type CommonInfo struct {