mirror of
https://github.com/yhl452493373/frps-panel.git
synced 2026-04-04 06:16:59 +08:00
update readme;
optimize logic code;
This commit is contained in:
92
README.md
92
README.md
@@ -36,62 +36,60 @@ frp version >= v0.31.0
|
|||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
1. Create file `frps-panel.ini` including all support usernames and tokens.
|
1. Create file `frps-panel.toml` including common config.
|
||||||
|
|
||||||
```ini
|
```toml
|
||||||
|
#frps-panel.toml
|
||||||
[common]
|
[common]
|
||||||
;plugin listen ip
|
# frps panel config info
|
||||||
plugin_addr = 127.0.0.1
|
plugin_addr = "127.0.0.1" #aadr
|
||||||
;plugin listen port
|
|
||||||
plugin_port = 7200
|
plugin_port = 7200
|
||||||
;the username of manage ui,optional
|
#admin_user = "admin"
|
||||||
admin_user = admin
|
#admin_pwd = "admin"
|
||||||
;the password of manage ui,optional
|
# specified login state keep time
|
||||||
admin_pwd = admin
|
|
||||||
;specified login state keep time in seconds.0 - before the browser completely exit, don't need to re-login,greater than 0: when Idle time exceeds this value,you should re-login
|
|
||||||
admin_keep_time = 0
|
admin_keep_time = 0
|
||||||
|
|
||||||
; enable tls
|
# enable tls
|
||||||
tls_mode = false
|
tls_mode = false
|
||||||
; tls_cert_file = cert.crt
|
# tls_cert_file = cert.crt
|
||||||
; tls_key_file = cert.key
|
# tls_key_file = cert.key
|
||||||
|
|
||||||
; frp dashboard info
|
# frp dashboard info
|
||||||
dashboard_addr = 127.0.0.1
|
dashboard_addr = "127.0.0.1"
|
||||||
dashboard_port = 7500
|
dashboard_port = 7500
|
||||||
dashboard_user = admin
|
dashboard_user = "admin"
|
||||||
dashboard_pwd = admin
|
dashboard_pwd = "admin"
|
||||||
|
|
||||||
[users]
|
|
||||||
;user user1 with meta_token 123
|
|
||||||
user1 = 123
|
|
||||||
;user user2 with meta_token abc
|
|
||||||
user2 = abc
|
|
||||||
|
|
||||||
[ports]
|
|
||||||
;user1 can only use ports 8080,9090 to 9010 ,other ports will fail to create proxy (frpc can normally startup)
|
|
||||||
user1=8080,9090-9010
|
|
||||||
|
|
||||||
[domains]
|
|
||||||
;user1 can only use domain web01.user1.com ,other domain will fail to create proxy (frpc can normally startup)
|
|
||||||
user1=web01.user1.com
|
|
||||||
|
|
||||||
[subdomains]
|
|
||||||
;user1 can only use subdomain web01 ,other subdomain will fail to create proxy (frpc can normally startup)
|
|
||||||
user1=web01
|
|
||||||
|
|
||||||
[disabled]
|
|
||||||
;user2 is disabled,when frpc use this user to connect with frps,if frpc is not startup,it cannot startup,if it's already startup,it will always show error logs on console
|
|
||||||
user2 = disable
|
|
||||||
```
|
```
|
||||||
|
|
||||||
One user each line. Username and token are split by `=`.
|
2. Create file `frps-tokens.toml` to save users,it should be the same place with `frps-panel.toml`.this file will auto create by system.
|
||||||
|
|
||||||
2. Run frps-panel:
|
```toml
|
||||||
|
#frps-tokens.toml
|
||||||
|
[tokens]
|
||||||
|
[tokens.user1]
|
||||||
|
user = "user1"
|
||||||
|
token = "token1"
|
||||||
|
comment = "user1 with token1"
|
||||||
|
ports = [8080, "10000-10200"]
|
||||||
|
domains = ["web01.domain.com", "web02.domain.com"]
|
||||||
|
subdomains = ["web01", "web02"]
|
||||||
|
enable = true
|
||||||
|
[tokens.user2]
|
||||||
|
user = "user2"
|
||||||
|
token = "token2"
|
||||||
|
comment = "user2 with token2"
|
||||||
|
ports = [9080]
|
||||||
|
domains = ["web11.domain.com", "web12.domain.com"]
|
||||||
|
subdomains = ["web11", "web12"]
|
||||||
|
enable = false
|
||||||
|
```
|
||||||
|
|
||||||
`./frps-panel -c ./frps-panel.ini`
|
|
||||||
|
|
||||||
3. Register plugin in frps.
|
3. Run frps-panel:
|
||||||
|
|
||||||
|
`./frps-panel -c ./frps-panel.toml`
|
||||||
|
|
||||||
|
4. Register plugin in frps.
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
# frps.ini
|
# frps.ini
|
||||||
@@ -105,7 +103,7 @@ path = /handler
|
|||||||
ops = Login,NewWorkConn,NewUserConn,NewProxy,Ping
|
ops = Login,NewWorkConn,NewUserConn,NewProxy,Ping
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Specify username and meta_token in frpc configure file.
|
5. Specify username and meta_token in frpc configure file.
|
||||||
|
|
||||||
For user1:
|
For user1:
|
||||||
|
|
||||||
@@ -139,7 +137,7 @@ local_port = 22
|
|||||||
remote_port = 6000
|
remote_port = 6000
|
||||||
```
|
```
|
||||||
|
|
||||||
5. Manage your users in browser via: http://127.0.0.1:7200 or https://127.0.0.1:7200
|
6. Manage your users in browser via: http://127.0.0.1:7200 or https://127.0.0.1:7200
|
||||||
|
|
||||||
## Run as service
|
## Run as service
|
||||||
|
|
||||||
@@ -155,8 +153,8 @@ Wants = network.target
|
|||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type = simple
|
Type = simple
|
||||||
# config of frps-panel.ini,you should change the file path
|
# config of frps-panel.toml,you should change the file path
|
||||||
Environment=FRPS_PANEL_OPTS="-c /root/frps-panel/frps-panel.ini"
|
Environment=FRPS_PANEL_OPTS="-c /root/frps-panel/frps-panel.toml"
|
||||||
# command of run frps-panel,you should change the file path
|
# command of run frps-panel,you should change the file path
|
||||||
ExecStart = /root/frps-panel/frps-panel $FRPS_PANEL_OPTS
|
ExecStart = /root/frps-panel/frps-panel $FRPS_PANEL_OPTS
|
||||||
|
|
||||||
|
|||||||
92
README_zh.md
92
README_zh.md
@@ -37,63 +37,59 @@ frps-panel 会以一个单独的进程运行,并接收 frps 发送过来的 HT
|
|||||||
|
|
||||||
### 使用示例
|
### 使用示例
|
||||||
|
|
||||||
1. 创建 `frps-panel.ini` 文件,内容为所有支持的用户名和 token。
|
1. 创建 `frps-panel.toml` 文件,内容为基础配置。
|
||||||
|
|
||||||
```ini
|
```toml
|
||||||
|
# frps-panel.toml
|
||||||
[common]
|
[common]
|
||||||
;插件监听地址
|
# frps panel config info
|
||||||
;如果上面配置中 tls_mode = true, 则把plugin_addr的值改为 https://127.0.0.1:7200
|
plugin_addr = "127.0.0.1" #aadr
|
||||||
plugin_addr = 127.0.0.1
|
|
||||||
;插件端口
|
|
||||||
plugin_port = 7200
|
plugin_port = 7200
|
||||||
;插件管理页面账号,可选
|
#admin_user = "admin"
|
||||||
admin_user = admin
|
#admin_pwd = "admin"
|
||||||
;插件管理页面密码,与账号一起进行鉴权,可选
|
# specified login state keep time
|
||||||
admin_pwd = admin
|
|
||||||
;登录状态空闲时间(秒):0-浏览器完全退出前不用重新登录,大于0-空闲超过此时间则需要重新登录.
|
|
||||||
admin_keep_time = 0
|
admin_keep_time = 0
|
||||||
|
|
||||||
; frps 面板页面是否启用https访问,如果为true,则只能通过https访问
|
# enable tls
|
||||||
tls_mode = false
|
tls_mode = false
|
||||||
; tls_cert_file = cert.crt
|
# tls_cert_file = cert.crt
|
||||||
; tls_key_file = cert.key
|
# tls_key_file = cert.key
|
||||||
|
|
||||||
; frp服务器的看板页面信息,必须配置,且与frp服务器一致,否则无法获取服务器信息
|
# frp dashboard info
|
||||||
dashboard_addr = 127.0.0.1
|
dashboard_addr = "127.0.0.1"
|
||||||
dashboard_port = 7500
|
dashboard_port = 7500
|
||||||
dashboard_user = admin
|
dashboard_user = "admin"
|
||||||
dashboard_pwd = admin
|
dashboard_pwd = "admin"
|
||||||
|
|
||||||
[users]
|
|
||||||
;user1的meta_token为123
|
|
||||||
user1 = 123
|
|
||||||
;user2的meta_token为abc
|
|
||||||
user2 = abc
|
|
||||||
|
|
||||||
[ports]
|
|
||||||
;user1只能使用8080,9090到9010端口,其他端口则建立连接时返回失败(不影响客户端启动)
|
|
||||||
user1=8080,9090-9010
|
|
||||||
|
|
||||||
[domains]
|
|
||||||
;user1只能使用web01.yyy.zzz域名,配置了其他域名则建立连接时返回失败(不影响客户端启动)
|
|
||||||
user1=web01.user1.com
|
|
||||||
|
|
||||||
[subdomains]
|
|
||||||
;user1只能使用web01.xxx.yyy.zzz域名,配置了其他三级域名则建立连接时返回失败(不影响客户端启动)
|
|
||||||
user1=web01
|
|
||||||
|
|
||||||
[disabled]
|
|
||||||
;user2被禁用,frpc使用此账户与frps通信时,如果未启动则无法启动,如果已启动,则会一直打印错误日志
|
|
||||||
user2 = disable
|
|
||||||
```
|
```
|
||||||
|
|
||||||
每一个用户占一行,用户名和 token 之间以 `=` 号分隔。
|
2. 创建`frps-tokens.toml`文件,其内容为系统中的用户,该文件位置和`frps-panel.toml`相同。如不创建此文件,在增加用户时会自动创建。
|
||||||
|
|
||||||
2. 运行 frps-panel,指定监听地址以及 token 存储文件路径。
|
```toml
|
||||||
|
#frps-tokens.toml
|
||||||
|
[tokens]
|
||||||
|
[tokens.user1]
|
||||||
|
user = "user1"
|
||||||
|
token = "token1"
|
||||||
|
comment = "user1 with token1"
|
||||||
|
ports = [8080, "10000-10200"]
|
||||||
|
domains = ["web01.domain.com", "web02.domain.com"]
|
||||||
|
subdomains = ["web01", "web02"]
|
||||||
|
enable = true
|
||||||
|
[tokens.user2]
|
||||||
|
user = "user2"
|
||||||
|
token = "token2"
|
||||||
|
comment = "user2 with token2"
|
||||||
|
ports = [9080]
|
||||||
|
domains = ["web11.domain.com", "web12.domain.com"]
|
||||||
|
subdomains = ["web11", "web12"]
|
||||||
|
enable = false
|
||||||
|
```
|
||||||
|
|
||||||
`./frps-panel -c ./frps-panel.ini`
|
3. 运行 frps-panel,指定监听地址以及 token 存储文件路径。
|
||||||
|
|
||||||
3. 在 frps 的配置文件中注册插件,并启动。
|
`./frps-panel -c ./frps-panel.toml`
|
||||||
|
|
||||||
|
4. 在 frps 的配置文件中注册插件,并启动。
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
# frps.ini
|
# frps.ini
|
||||||
@@ -106,7 +102,7 @@ path = /handler
|
|||||||
ops = Login,NewWorkConn,NewUserConn,NewProxy,Ping
|
ops = Login,NewWorkConn,NewUserConn,NewProxy,Ping
|
||||||
```
|
```
|
||||||
|
|
||||||
4. 在 frpc 中指定用户名,在 meta 中指定 token,用户名以及 `meta_token` 的内容需要和之前创建的 token 文件匹配。
|
5. 在 frpc 中指定用户名,在 meta 中指定 token,用户名以及 `meta_token` 的内容需要和之前创建的 token 文件匹配。
|
||||||
|
|
||||||
user1 的配置:
|
user1 的配置:
|
||||||
|
|
||||||
@@ -140,7 +136,7 @@ local_port = 22
|
|||||||
remote_port = 6000
|
remote_port = 6000
|
||||||
```
|
```
|
||||||
|
|
||||||
5.浏览器中输入地址: http://127.0.0.1:7200 或 https://127.0.0.1:7200 进入管理页面进行用户管理
|
6.浏览器中输入地址: http://127.0.0.1:7200 或 https://127.0.0.1:7200 进入管理页面进行用户管理
|
||||||
|
|
||||||
## 以服务的形式运行
|
## 以服务的形式运行
|
||||||
|
|
||||||
@@ -156,8 +152,8 @@ Wants = network.target
|
|||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type = simple
|
Type = simple
|
||||||
# 启动frps-panel的配置文件路径,需修改为您的frps-panel.ini的路径
|
# 启动frps-panel的配置文件路径,需修改为您的frps-panel.toml的路径
|
||||||
Environment=FRPS_PANEL_OPTS="-c /root/frps-panel/frps-panel.ini"
|
Environment=FRPS_PANEL_OPTS="-c /root/frps-panel/frps-panel.toml"
|
||||||
# 启动frps-panel的命令,需修改为您的frps-panel的安装路径
|
# 启动frps-panel的命令,需修改为您的frps-panel的安装路径
|
||||||
ExecStart = /root/frps-panel/frps-panel $FRPS_PANEL_OPTS
|
ExecStart = /root/frps-panel/frps-panel $FRPS_PANEL_OPTS
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,11 @@
|
|||||||
border-color: #79bbff;
|
border-color: #79bbff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.layui-table-checked.layui-table-click,
|
||||||
|
.layui-table-checked.layui-table-hover {
|
||||||
|
background-color: #d9ecff;
|
||||||
|
}
|
||||||
|
|
||||||
.layui-nav-tree .layui-nav-child dd.layui-this,
|
.layui-nav-tree .layui-nav-child dd.layui-this,
|
||||||
.layui-nav-tree .layui-nav-child dd.layui-this a,
|
.layui-nav-tree .layui-nav-child dd.layui-this a,
|
||||||
.layui-nav-tree .layui-this,
|
.layui-nav-tree .layui-this,
|
||||||
@@ -206,6 +211,11 @@ section.proxy-list .proxy-info .layui-row .layui-row > div:first-child {
|
|||||||
border-color: #5f5f60;
|
border-color: #5f5f60;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.layui-table-checked.layui-table-click,
|
||||||
|
.layui-table-checked.layui-table-hover {
|
||||||
|
background-color: #5f5f60;
|
||||||
|
}
|
||||||
|
|
||||||
.layui-nav-tree .layui-nav-child dd.layui-this,
|
.layui-nav-tree .layui-nav-child dd.layui-this,
|
||||||
.layui-nav-tree .layui-nav-child dd.layui-this a,
|
.layui-nav-tree .layui-nav-child dd.layui-this a,
|
||||||
.layui-nav-tree .layui-this,
|
.layui-nav-tree .layui-this,
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ var loadUserList = (function ($) {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
valid: valid,
|
valid: valid,
|
||||||
trim: username
|
trim: username.trim()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ var loadUserList = (function ($) {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
valid: valid,
|
valid: valid,
|
||||||
trim: comment.replace(/[\n\t\r]/g, '')
|
trim: comment.trim().replace(/[\n\t\r]/g, '')
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +227,7 @@ var loadUserList = (function ($) {
|
|||||||
* set verify rule of layui.form
|
* set verify rule of layui.form
|
||||||
*/
|
*/
|
||||||
(function setFormVerifyRule() {
|
(function setFormVerifyRule() {
|
||||||
// layui.form.verify(verifyRules);
|
layui.form.verify(verifyRules);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -265,10 +265,10 @@ var loadUserList = (function ($) {
|
|||||||
{field: 'domains', title: i18n['AllowedDomains'], sort: true, edit: 'textarea'},
|
{field: 'domains', title: i18n['AllowedDomains'], sort: true, edit: 'textarea'},
|
||||||
{field: 'subdomains', title: i18n['AllowedSubdomains'], sort: true, edit: 'textarea'},
|
{field: 'subdomains', title: i18n['AllowedSubdomains'], sort: true, edit: 'textarea'},
|
||||||
{
|
{
|
||||||
field: 'status',
|
field: 'enable',
|
||||||
title: i18n['Status'],
|
title: i18n['Status'],
|
||||||
width: 100,
|
width: 100,
|
||||||
templet: '<span>{{d.status? "' + i18n['Enable'] + '":"' + i18n['Disable'] + '"}}</span>',
|
templet: '<span>{{d.enable? "' + i18n['Enable'] + '":"' + i18n['Disable'] + '"}}</span>',
|
||||||
sort: true
|
sort: true
|
||||||
},
|
},
|
||||||
{title: i18n['Operation'], width: 150, toolbar: '#userListOperationTemplate'}
|
{title: i18n['Operation'], width: 150, toolbar: '#userListOperationTemplate'}
|
||||||
@@ -371,6 +371,12 @@ var loadUserList = (function ($) {
|
|||||||
|
|
||||||
data.forEach(function (temp) {
|
data.forEach(function (temp) {
|
||||||
temp.ports = temp.ports.split(',')
|
temp.ports = temp.ports.split(',')
|
||||||
|
temp.ports.forEach(function (port, index) {
|
||||||
|
if (/^\d+$/.test(String(port))) {
|
||||||
|
temp.ports[index] = parseInt(String(port));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
temp.domains = temp.domains.split(',')
|
temp.domains = temp.domains.split(',')
|
||||||
temp.subdomains = temp.subdomains.split(',')
|
temp.subdomains = temp.subdomains.split(',')
|
||||||
});
|
});
|
||||||
@@ -395,6 +401,11 @@ var loadUserList = (function ($) {
|
|||||||
var data = obj.data;
|
var data = obj.data;
|
||||||
|
|
||||||
data.ports = data.ports.split(',')
|
data.ports = data.ports.split(',')
|
||||||
|
data.ports.forEach(function (port, index) {
|
||||||
|
if (/^\d+$/.test(String(port))) {
|
||||||
|
data.ports[index] = parseInt(String(port));
|
||||||
|
}
|
||||||
|
});
|
||||||
data.domains = data.domains.split(',')
|
data.domains = data.domains.split(',')
|
||||||
data.subdomains = data.subdomains.split(',')
|
data.subdomains = data.subdomains.split(',')
|
||||||
switch (obj.event) {
|
switch (obj.event) {
|
||||||
@@ -438,6 +449,11 @@ var loadUserList = (function ($) {
|
|||||||
var formData = layui.form.val('addUserForm');
|
var formData = layui.form.val('addUserForm');
|
||||||
if (formData.ports != null) {
|
if (formData.ports != null) {
|
||||||
formData.ports = formData.ports.split(',')
|
formData.ports = formData.ports.split(',')
|
||||||
|
formData.ports.forEach(function (port, index) {
|
||||||
|
if (/^\d+$/.test(String(port))) {
|
||||||
|
formData.ports[index] = parseInt(String(port));
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if (formData.domains != null) {
|
if (formData.domains != null) {
|
||||||
formData.domains = formData.domains.split(',')
|
formData.domains = formData.domains.split(',')
|
||||||
@@ -456,7 +472,7 @@ var loadUserList = (function ($) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* add user action
|
* add user action
|
||||||
* @param data {{user:string, token:string, comment:string, status:boolean, ports:[string], domains:[string], subdomains:[string]}} user data
|
* @param data {{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}} user data
|
||||||
* @param index popup index
|
* @param index popup index
|
||||||
*/
|
*/
|
||||||
function add(data, index) {
|
function add(data, index) {
|
||||||
@@ -485,10 +501,20 @@ var loadUserList = (function ($) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* update user action
|
* update user action
|
||||||
* @param before {{user:string, token:string, comment:string, status:boolean, ports:[string], domains:[string], subdomains:[string]}} data before update
|
* @param before {{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}} data before update
|
||||||
* @param after {{user:string, token:string, comment:string, status:boolean, ports:[string], domains:[string], subdomains:[string]}} data after update
|
* @param after {{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}} data after update
|
||||||
*/
|
*/
|
||||||
function update(before, after) {
|
function update(before, after) {
|
||||||
|
before.ports.forEach(function (port, index) {
|
||||||
|
if (/^\d+$/.test(String(port))) {
|
||||||
|
before.ports[index] = parseInt(String(port));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
after.ports.forEach(function (port, index) {
|
||||||
|
if (/^\d+$/.test(String(port)) && typeof port === "string") {
|
||||||
|
after.ports[index] = parseInt(String(port));
|
||||||
|
}
|
||||||
|
});
|
||||||
var loading = layui.layer.load();
|
var loading = layui.layer.load();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/update',
|
url: '/update',
|
||||||
@@ -513,7 +539,7 @@ var loadUserList = (function ($) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* batch remove user popup
|
* batch remove user popup
|
||||||
* @param data {[{user:string, token:string, comment:string, status:boolean, ports:[string], domains:[string], subdomains:[string]}]} user data list
|
* @param data {[{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}]} user data list
|
||||||
*/
|
*/
|
||||||
function batchRemovePopup(data) {
|
function batchRemovePopup(data) {
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
@@ -530,7 +556,7 @@ var loadUserList = (function ($) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* batch disable user popup
|
* batch disable user popup
|
||||||
* @param data {[{user:string, token:string, comment:string, status:boolean, ports:[string], domains:[string], subdomains:[string]}]} user data list
|
* @param data {[{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}]} user data list
|
||||||
*/
|
*/
|
||||||
function batchDisablePopup(data) {
|
function batchDisablePopup(data) {
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
@@ -547,7 +573,7 @@ var loadUserList = (function ($) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* batch enable user popup
|
* batch enable user popup
|
||||||
* @param data {[{user:string, token:string, comment:string, status:boolean, ports:[string], domains:[string], subdomains:[string]}]} user data list
|
* @param data {[{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}]} user data list
|
||||||
*/
|
*/
|
||||||
function batchEnablePopup(data) {
|
function batchEnablePopup(data) {
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
@@ -564,7 +590,7 @@ var loadUserList = (function ($) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* remove one user popup
|
* remove one user popup
|
||||||
* @param data {{user:string, token:string, comment:string, status:boolean, ports:[string], domains:[string], subdomains:[string]}} user data
|
* @param data {{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}} user data
|
||||||
*/
|
*/
|
||||||
function removePopup(data) {
|
function removePopup(data) {
|
||||||
layui.layer.confirm(i18n['ConfirmRemoveUser'], {
|
layui.layer.confirm(i18n['ConfirmRemoveUser'], {
|
||||||
@@ -577,7 +603,7 @@ var loadUserList = (function ($) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* disable one user popup
|
* disable one user popup
|
||||||
* @param data {{user:string, token:string, comment:string, status:boolean, ports:[string], domains:[string], subdomains:[string]}} user data
|
* @param data {{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}} user data
|
||||||
*/
|
*/
|
||||||
function disablePopup(data) {
|
function disablePopup(data) {
|
||||||
layui.layer.confirm(i18n['ConfirmDisableUser'], {
|
layui.layer.confirm(i18n['ConfirmDisableUser'], {
|
||||||
@@ -590,7 +616,7 @@ var loadUserList = (function ($) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* enable one user popup
|
* enable one user popup
|
||||||
* @param data {{user:string, token:string, comment:string, status:boolean, ports:[string], domains:[string], subdomains:[string]}} user data
|
* @param data {{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}} user data
|
||||||
*/
|
*/
|
||||||
function enablePopup(data) {
|
function enablePopup(data) {
|
||||||
layui.layer.confirm(i18n['ConfirmEnableUser'], {
|
layui.layer.confirm(i18n['ConfirmEnableUser'], {
|
||||||
@@ -604,7 +630,7 @@ var loadUserList = (function ($) {
|
|||||||
/**
|
/**
|
||||||
* operate actions
|
* operate actions
|
||||||
* @param type {apiType} action type
|
* @param type {apiType} action type
|
||||||
* @param data {[{user:string, token:string, comment:string, status:boolean, ports:[string], domains:[string], subdomains:[string]}]} user data list
|
* @param data {[{user:string, token:string, comment:string, enable:boolean, ports:[string|number], domains:[string], subdomains:[string]}]} user data list
|
||||||
* @param index popup index
|
* @param index popup index
|
||||||
*/
|
*/
|
||||||
function operate(type, data, index) {
|
function operate(type, data, index) {
|
||||||
|
|||||||
@@ -2,17 +2,17 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>${ .FrpsPanel }</title>
|
<title>${ .FrpsPanel }</title>
|
||||||
<link rel="stylesheet" href="./static/lib/layui/css/layui.css">
|
<link rel="stylesheet" href="./static/lib/layui/css/layui.css?v=${ .version }">
|
||||||
<link rel="stylesheet" href="./static/css/layui-theme-dark.css">
|
<link rel="stylesheet" href="./static/css/layui-theme-dark.css?v=${ .version }">
|
||||||
<link rel="stylesheet" href="./static/css/index.css">
|
<link rel="stylesheet" href="./static/css/index.css?v=${ .version }">
|
||||||
<link rel="stylesheet" href="./static/css/color.css">
|
<link rel="stylesheet" href="./static/css/color.css?v=${ .version }">
|
||||||
<script src="./static/lib/layui/layui.js"></script>
|
<script src="./static/lib/layui/layui.js?v=${ .version }"></script>
|
||||||
<script src="./static/lib/echarts.min.js"></script>
|
<script src="./static/lib/echarts.min.js?v=${ .version }"></script>
|
||||||
<script src="./static/lib/filesize.min.js"></script>
|
<script src="./static/lib/filesize.min.js?v=${ .version }"></script>
|
||||||
<script src="./static/js/index-server-info.js"></script>
|
<script src="./static/js/index-server-info.js?v=${ .version }"></script>
|
||||||
<script src="./static/js/index-user-list.js"></script>
|
<script src="./static/js/index-user-list.js?v=${ .version }"></script>
|
||||||
<script src="./static/js/index-proxy-list.js"></script>
|
<script src="./static/js/index-proxy-list.js?v=${ .version }"></script>
|
||||||
<script src="./static/js/index.js"></script>
|
<script src="./static/js/index.js?v=${ .version }"></script>
|
||||||
<style>
|
<style>
|
||||||
section.user-list .layui-table-cell:empty::after {
|
section.user-list .layui-table-cell:empty::after {
|
||||||
content: '${ .NotLimit }';
|
content: '${ .NotLimit }';
|
||||||
@@ -214,7 +214,7 @@
|
|||||||
<script type="text/html" id="userListOperationTemplate">
|
<script type="text/html" id="userListOperationTemplate">
|
||||||
<div class="layui-clear-space">
|
<div class="layui-clear-space">
|
||||||
<a class="layui-btn layui-btn-xs" lay-event="remove">${ .Remove }</a>
|
<a class="layui-btn layui-btn-xs" lay-event="remove">${ .Remove }</a>
|
||||||
{{# if (d.status) { }}
|
{{# if (d.enable) { }}
|
||||||
<a class="layui-btn layui-btn-xs" lay-event="disable">${ .Disable }</a>
|
<a class="layui-btn layui-btn-xs" lay-event="disable">${ .Disable }</a>
|
||||||
{{# } else { }}
|
{{# } else { }}
|
||||||
<a class="layui-btn layui-btn-xs" lay-event="enable">${ .Enable }</a>
|
<a class="layui-btn layui-btn-xs" lay-event="enable">${ .Enable }</a>
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
<html lang="">
|
<html lang="">
|
||||||
<head>
|
<head>
|
||||||
<title>Login</title>
|
<title>Login</title>
|
||||||
<link rel="stylesheet" href="./static/lib/layui/css/layui.css">
|
<link rel="stylesheet" href="./static/lib/layui/css/layui.css?v=${ .version }">
|
||||||
<link rel="stylesheet" href="./static/css/layui-theme-dark.css">
|
<link rel="stylesheet" href="./static/css/layui-theme-dark.css?v=${ .version }">
|
||||||
<link rel="stylesheet" href="./static/css/color.css">
|
<link rel="stylesheet" href="./static/css/color.css?v=${ .version }">
|
||||||
<link rel="stylesheet" href="./static/css/login.css">
|
<link rel="stylesheet" href="./static/css/login.css?v=${ .version }">
|
||||||
<script src="./static/lib/layui/layui.js"></script>
|
<script src="./static/lib/layui/layui.js?v=${ .version }"></script>
|
||||||
<script src="./static/js/login.js"></script>
|
<script src="./static/js/login.js?v=${ .version }"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="login-title">
|
<div class="login-title">
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"frps-panel/pkg/server"
|
"frps-panel/pkg/server"
|
||||||
"frps-panel/pkg/server/controller"
|
"frps-panel/pkg/server/controller"
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
@@ -11,7 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const version = "1.6.0"
|
const version = "1.7.0"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
showVersion bool
|
showVersion bool
|
||||||
@@ -79,8 +80,12 @@ func parseConfigFile(configFile, tokensFile string) (controller.HandleController
|
|||||||
|
|
||||||
_, err = toml.DecodeFile(tokensFile, &tokens)
|
_, err = toml.DecodeFile(tokensFile, &tokens)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
tokens = controller.Tokens{Tokens: make(map[string]controller.TokenInfo)}
|
||||||
|
} else {
|
||||||
log.Fatalf("decode token file %v error: %v", tokensFile, err)
|
log.Fatalf("decode token file %v error: %v", tokensFile, err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
common.Common.DashboardTls = strings.HasPrefix("https://", strings.ToLower(common.Common.DashboardAddr))
|
common.Common.DashboardTls = strings.HasPrefix("https://", strings.ToLower(common.Common.DashboardAddr))
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
[tokens]
|
|
||||||
[tokens.user1]
|
|
||||||
user = "user1"
|
|
||||||
token = "token1"
|
|
||||||
comment = "张三"
|
|
||||||
ports = [""]
|
|
||||||
domains = [""]
|
|
||||||
subdomains = [""]
|
|
||||||
status = true
|
|
||||||
[tokens.user2]
|
|
||||||
user = "user2"
|
|
||||||
token = "token2"
|
|
||||||
comment = "李四"
|
|
||||||
ports = [""]
|
|
||||||
domains = [""]
|
|
||||||
subdomains = [""]
|
|
||||||
status = true
|
|
||||||
@@ -290,7 +290,7 @@ func (c *HandleController) MakeQueryTokensFunc() func(context *gin.Context) {
|
|||||||
func (c *HandleController) MakeAddTokenFunc() func(context *gin.Context) {
|
func (c *HandleController) MakeAddTokenFunc() func(context *gin.Context) {
|
||||||
return func(context *gin.Context) {
|
return func(context *gin.Context) {
|
||||||
info := TokenInfo{
|
info := TokenInfo{
|
||||||
Status: true,
|
Enable: true,
|
||||||
}
|
}
|
||||||
response := OperationResponse{
|
response := OperationResponse{
|
||||||
Success: true,
|
Success: true,
|
||||||
@@ -315,7 +315,7 @@ func (c *HandleController) MakeAddTokenFunc() func(context *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
info.Comment = cleanString(info.Comment)
|
info.Comment = cleanString(info.Comment)
|
||||||
info.Ports = cleanStrings(info.Ports)
|
info.Ports = cleanPorts(info.Ports)
|
||||||
info.Domains = cleanStrings(info.Domains)
|
info.Domains = cleanStrings(info.Domains)
|
||||||
info.Subdomains = cleanStrings(info.Subdomains)
|
info.Subdomains = cleanStrings(info.Subdomains)
|
||||||
|
|
||||||
@@ -373,7 +373,7 @@ func (c *HandleController) MakeUpdateTokensFunc() func(context *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
after.Comment = cleanString(after.Comment)
|
after.Comment = cleanString(after.Comment)
|
||||||
after.Ports = cleanStrings(after.Ports)
|
after.Ports = cleanPorts(after.Ports)
|
||||||
after.Domains = cleanStrings(after.Domains)
|
after.Domains = cleanStrings(after.Domains)
|
||||||
after.Subdomains = cleanStrings(after.Subdomains)
|
after.Subdomains = cleanStrings(after.Subdomains)
|
||||||
|
|
||||||
@@ -467,7 +467,7 @@ func (c *HandleController) MakeDisableTokensFunc() func(context *gin.Context) {
|
|||||||
|
|
||||||
for _, user := range disable.Users {
|
for _, user := range disable.Users {
|
||||||
token := c.Tokens[user.User]
|
token := c.Tokens[user.User]
|
||||||
token.Status = false
|
token.Enable = false
|
||||||
c.Tokens[user.User] = token
|
c.Tokens[user.User] = token
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -515,7 +515,7 @@ func (c *HandleController) MakeEnableTokensFunc() func(context *gin.Context) {
|
|||||||
|
|
||||||
for _, user := range enable.Users {
|
for _, user := range enable.Users {
|
||||||
token := c.Tokens[user.User]
|
token := c.Tokens[user.User]
|
||||||
token.Status = true
|
token.Enable = true
|
||||||
c.Tokens[user.User] = token
|
c.Tokens[user.User] = token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ func (c *HandleController) JudgeToken(user string, token string) plugin.Response
|
|||||||
res.Reject = true
|
res.Reject = true
|
||||||
res.RejectReason = "user or meta token can not be empty"
|
res.RejectReason = "user or meta token can not be empty"
|
||||||
} else if info, exist := c.Tokens[user]; exist {
|
} else if info, exist := c.Tokens[user]; exist {
|
||||||
if !info.Status {
|
if !info.Enable {
|
||||||
res.Reject = true
|
res.Reject = true
|
||||||
res.RejectReason = fmt.Sprintf("user [%s] is disabled", user)
|
res.RejectReason = fmt.Sprintf("user [%s] is disabled", user)
|
||||||
} else {
|
} else {
|
||||||
@@ -77,7 +77,6 @@ func (c *HandleController) JudgePort(content *plugin.NewProxyContent) plugin.Res
|
|||||||
"tcp", "tcpmux", "udp", "http", "https",
|
"tcp", "tcpmux", "udp", "http", "https",
|
||||||
}
|
}
|
||||||
proxyType := content.ProxyType
|
proxyType := content.ProxyType
|
||||||
|
|
||||||
if stringContains(proxyType, supportProxyTypes) {
|
if stringContains(proxyType, supportProxyTypes) {
|
||||||
log.Printf("proxy type [%v] not support, plugin do nothing", proxyType)
|
log.Printf("proxy type [%v] not support, plugin do nothing", proxyType)
|
||||||
res.Unchange = true
|
res.Unchange = true
|
||||||
@@ -94,8 +93,9 @@ func (c *HandleController) JudgePort(content *plugin.NewProxyContent) plugin.Res
|
|||||||
portAllowed = false
|
portAllowed = false
|
||||||
if token, exist := c.Tokens[user]; exist {
|
if token, exist := c.Tokens[user]; exist {
|
||||||
for _, port := range token.Ports {
|
for _, port := range token.Ports {
|
||||||
if strings.Contains(port, "-") {
|
if str, ok := port.(string); ok {
|
||||||
allowedRanges := strings.Split(port, "-")
|
if strings.Contains(str, "-") {
|
||||||
|
allowedRanges := strings.Split(str, "-")
|
||||||
if len(allowedRanges) != 2 {
|
if len(allowedRanges) != 2 {
|
||||||
portErr = fmt.Errorf("user [%v] port range [%v] format error", user, port)
|
portErr = fmt.Errorf("user [%v] port range [%v] format error", user, port)
|
||||||
break
|
break
|
||||||
@@ -115,7 +115,7 @@ func (c *HandleController) JudgePort(content *plugin.NewProxyContent) plugin.Res
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
allowed, err := strconv.Atoi(port)
|
allowed, err := strconv.Atoi(str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
portErr = fmt.Errorf("user [%v] allowed port [%v] is not a number", user, port)
|
portErr = fmt.Errorf("user [%v] allowed port [%v] is not a number", user, port)
|
||||||
}
|
}
|
||||||
@@ -124,6 +124,14 @@ func (c *HandleController) JudgePort(content *plugin.NewProxyContent) plugin.Res
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
allowed := port
|
||||||
|
if allowed == userPort {
|
||||||
|
portAllowed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
portAllowed = true
|
portAllowed = true
|
||||||
|
|||||||
@@ -121,7 +121,8 @@ func (c *HandleController) verifyToken(token TokenInfo, operate int) OperationRe
|
|||||||
|
|
||||||
if validatePorts {
|
if validatePorts {
|
||||||
for _, port := range token.Ports {
|
for _, port := range token.Ports {
|
||||||
trimmedPort := trimString(port)
|
if str, ok := port.(string); ok {
|
||||||
|
trimmedPort := trimString(str)
|
||||||
if trimmedPort != "" && !portsFormatSingle.MatchString(trimmedPort) && !portsFormatRange.MatchString(trimmedPort) {
|
if trimmedPort != "" && !portsFormatSingle.MatchString(trimmedPort) && !portsFormatRange.MatchString(trimmedPort) {
|
||||||
response.Success = false
|
response.Success = false
|
||||||
response.Code = PortsFormatError
|
response.Code = PortsFormatError
|
||||||
@@ -131,6 +132,7 @@ func (c *HandleController) verifyToken(token TokenInfo, operate int) OperationRe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if validateDomains {
|
if validateDomains {
|
||||||
for _, domain := range token.Domains {
|
for _, domain := range token.Domains {
|
||||||
@@ -161,6 +163,19 @@ func (c *HandleController) verifyToken(token TokenInfo, operate int) OperationRe
|
|||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cleanPorts(ports []any) []any {
|
||||||
|
cleanedPorts := make([]any, len(ports))
|
||||||
|
for i, port := range ports {
|
||||||
|
if str, ok := port.(string); ok {
|
||||||
|
cleanedPorts[i] = cleanString(str)
|
||||||
|
} else {
|
||||||
|
//float64, for JSON numbers
|
||||||
|
cleanedPorts[i] = int(port.(float64))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cleanedPorts
|
||||||
|
}
|
||||||
|
|
||||||
func cleanStrings(originalStrings []string) []string {
|
func cleanStrings(originalStrings []string) []string {
|
||||||
cleanedStrings := make([]string, len(originalStrings))
|
cleanedStrings := make([]string, len(originalStrings))
|
||||||
for i, str := range originalStrings {
|
for i, str := range originalStrings {
|
||||||
@@ -170,7 +185,7 @@ func cleanStrings(originalStrings []string) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cleanString(originalString string) string {
|
func cleanString(originalString string) string {
|
||||||
return trimAllSpace.ReplaceAllString(originalString, "")
|
return trimString(originalString)
|
||||||
}
|
}
|
||||||
|
|
||||||
func stringContains(element string, data []string) bool {
|
func stringContains(element string, data []string) bool {
|
||||||
@@ -194,7 +209,9 @@ func (c *HandleController) saveToken() error {
|
|||||||
log.Printf("error to crate file %v: %v", c.TokensFile, err)
|
log.Printf("error to crate file %v: %v", c.TokensFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = toml.NewEncoder(tokenFile).Encode(tokensList(c.Tokens)); err != nil {
|
encoder := toml.NewEncoder(tokenFile)
|
||||||
|
encoder.Indent = " "
|
||||||
|
if err = encoder.Encode(tokensList(c.Tokens)); err != nil {
|
||||||
log.Printf("error to encode tokens: %v", err)
|
log.Printf("error to encode tokens: %v", err)
|
||||||
}
|
}
|
||||||
if err = tokenFile.Close(); err != nil {
|
if err = tokenFile.Close(); err != nil {
|
||||||
|
|||||||
@@ -84,10 +84,10 @@ type TokenInfo struct {
|
|||||||
User string `toml:"user" json:"user" form:"user"`
|
User string `toml:"user" json:"user" form:"user"`
|
||||||
Token string `toml:"token" json:"token" form:"token"`
|
Token string `toml:"token" json:"token" form:"token"`
|
||||||
Comment string `toml:"comment" json:"comment" form:"comment"`
|
Comment string `toml:"comment" json:"comment" form:"comment"`
|
||||||
Ports []string `toml:"ports" json:"ports" from:"ports"`
|
Ports []any `toml:"ports" json:"ports" from:"ports"`
|
||||||
Domains []string `toml:"domains" json:"domains" from:"domains"`
|
Domains []string `toml:"domains" json:"domains" from:"domains"`
|
||||||
Subdomains []string `toml:"subdomains" json:"subdomains" from:"subdomains"`
|
Subdomains []string `toml:"subdomains" json:"subdomains" from:"subdomains"`
|
||||||
Status bool `toml:"status" json:"status" form:"status"`
|
Enable bool `toml:"enable" json:"enable" form:"enable"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TokenResponse struct {
|
type TokenResponse struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user