Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe28fc5623 | ||
|
|
8401948dc0 | ||
|
|
4413eb3571 | ||
|
|
fbadd44af5 | ||
|
|
e6488d3f1b | ||
|
|
e4ecc57825 | ||
|
|
c5e90c9e6c | ||
|
|
c8c6d17c9c | ||
|
|
dd87729455 | ||
|
|
e4d1caa4b9 | ||
|
|
1fdc0fc63a | ||
|
|
d09fc2fe91 | ||
|
|
70915e6e0e | ||
|
|
012a18ceb2 | ||
|
|
6ca0044dc3 | ||
|
|
9096585a34 | ||
|
|
11855f0917 |
6
Makefile
@@ -1,8 +1,12 @@
|
|||||||
export GO111MODULE=on
|
export GO111MODULE=on
|
||||||
|
export CGO_ENABLED=0
|
||||||
|
export GOOS=linux
|
||||||
|
export GOARCH=amd64
|
||||||
|
|
||||||
build: frps-multiuser
|
build: frps-multiuser
|
||||||
cp ./config/frps-multiuser.ini ./bin/frps-multiuser.ini
|
cp ./config/frps-multiuser.ini ./bin/frps-multiuser.ini
|
||||||
cp -r ./assets/ ./bin/assets/
|
cp -r ./assets/ ./bin/assets/
|
||||||
|
|
||||||
frps-multiuser:
|
frps-multiuser:
|
||||||
go build -o ./bin/frps-multiuser ./cmd/fp-multiuser
|
rm -rf ./bin
|
||||||
|
go build -o ./bin/frps-multiuser ./cmd/frps-multiuser
|
||||||
|
|||||||
@@ -9,16 +9,16 @@ copy: build
|
|||||||
cp -r ./assets/ ./release/assets/
|
cp -r ./assets/ ./release/assets/
|
||||||
|
|
||||||
build:
|
build:
|
||||||
env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-darwin-amd64 ./cmd/fp-multiuser
|
env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-darwin-amd64 ./cmd/frps-multiuser
|
||||||
env CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-freebsd-386 ./cmd/fp-multiuser
|
env CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-freebsd-386 ./cmd/frps-multiuser
|
||||||
env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-freebsd-amd64 ./cmd/fp-multiuser
|
env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-freebsd-amd64 ./cmd/frps-multiuser
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-linux-386 ./cmd/fp-multiuser
|
env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-linux-386 ./cmd/frps-multiuser
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-linux-amd64 ./cmd/fp-multiuser
|
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-linux-amd64 ./cmd/frps-multiuser
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-linux-arm ./cmd/fp-multiuser
|
env CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-linux-arm ./cmd/frps-multiuser
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-linux-arm64 ./cmd/fp-multiuser
|
env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-linux-arm64 ./cmd/frps-multiuser
|
||||||
env CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-windows-386.exe ./cmd/fp-multiuser
|
env CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-windows-386.exe ./cmd/frps-multiuser
|
||||||
env CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-windows-amd64.exe ./cmd/fp-multiuser
|
env CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-windows-amd64.exe ./cmd/frps-multiuser
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-linux-mips64 ./cmd/fp-multiuser
|
env CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-linux-mips64 ./cmd/frps-multiuser
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-linux-mips64le ./cmd/fp-multiuser
|
env CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-linux-mips64le ./cmd/frps-multiuser
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-linux-mips ./cmd/fp-multiuser
|
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-linux-mips ./cmd/frps-multiuser
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-linux-mipsle ./cmd/fp-multiuser
|
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./release/frps-multiuser-linux-mipsle ./cmd/frps-multiuser
|
||||||
38
README.md
@@ -1,5 +1,7 @@
|
|||||||
# frps-multiuser
|
# frps-multiuser
|
||||||
|
|
||||||
|
[README](README.md) | [中文文档](README_zh.md)
|
||||||
|
|
||||||
frp server plugin to support multiple users for [frp](https://github.com/fatedier/frp).
|
frp server plugin to support multiple users for [frp](https://github.com/fatedier/frp).
|
||||||
|
|
||||||
frps-multiuser will run as one single process and accept HTTP requests from frps.
|
frps-multiuser will run as one single process and accept HTTP requests from frps.
|
||||||
@@ -9,7 +11,7 @@ frps-multiuser will run as one single process and accept HTTP requests from frps
|
|||||||

|

|
||||||

|

|
||||||
|
|
||||||
## update notes
|
## Update Notes
|
||||||
|
|
||||||
+ **the default tokens file is frps-multiuser.ini now,ini file support comment**
|
+ **the default tokens file is frps-multiuser.ini now,ini file support comment**
|
||||||
+ **remove `-l`,it configure in `frps-multiuser.ini` now**
|
+ **remove `-l`,it configure in `frps-multiuser.ini` now**
|
||||||
@@ -21,13 +23,14 @@ frps-multiuser will run as one single process and accept HTTP requests from frps
|
|||||||
+ **you can limit `ports`,`domains` and `subdomains` for each user now**
|
+ **you can limit `ports`,`domains` and `subdomains` for each user now**
|
||||||
|
|
||||||
***when a user is dynamic been `remove` or `disable`,it will take some time to be effective***
|
***when a user is dynamic been `remove` or `disable`,it will take some time to be effective***
|
||||||
***the limit of `ports`、`domains`、`subdomains` only effective at `NewProxy`***
|
|
||||||
|
|
||||||
[README](README.md) | [中文文档](README_zh.md)
|
***the limit of `ports`、`domains`、`subdomains` only effective at `NewProxy`***
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Support multiple user authentication by tokens saved in file.
|
* Support multiple user authentication by tokens saved in file.
|
||||||
|
* Support dynamic `add`,`remove`,`disable` or `enable` user
|
||||||
|
* Limit `ports`,`domains` and `subdomains` for each user
|
||||||
|
|
||||||
### Download
|
### Download
|
||||||
|
|
||||||
@@ -128,8 +131,36 @@ local_port = 22
|
|||||||
remote_port = 6000
|
remote_port = 6000
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Run as service
|
||||||
|
|
||||||
|
this example is for `ubuntu` and with `root` user
|
||||||
|
|
||||||
|
+ 1.unzip `frps-multiuser.zip` to dir `/root/frps-multiuser`
|
||||||
|
+ 2.touch a file with command `touch frps-multiuser.service` in dir `/root/frps-multiuser`.the file content is:
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description = frp multiuser service
|
||||||
|
After = network.target syslog.target
|
||||||
|
Wants = network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type = simple
|
||||||
|
# config of frps-multiuser.ini,you should change the file path
|
||||||
|
Environment=FRPS_MULTIUSER_OPTS="-c /root/frps-multiuser/frps-multiuser.ini"
|
||||||
|
# command of run frps-multiuser,you should change the file path
|
||||||
|
ExecStart = /root/frps-multiuser/frps-multiuser $FRPS_MULTIUSER_OPTS
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy = multi-user.target
|
||||||
|
```
|
||||||
|
+ 3.copy `frps-multiuser.service` to `/etc/systemd/system/` with command `cp /root/frps-multiuser.service /etc/systemd/system/`
|
||||||
|
+ 4.reload service with command `systemctl daemon-reload`
|
||||||
|
+ 5.start service with command `service frps-multiuser start`
|
||||||
|
|
||||||
## Issues & Ideas
|
## Issues & Ideas
|
||||||
|
|
||||||
|
___If you want visit mange ui from internet, you should change `plugin_addr` to `0.0.0.0`___
|
||||||
|
|
||||||
If you have any issues or ideas, put it on [issues](https://github.com/yhl452493373/frps-multiuser/issues). I will try my best to achieve it.
|
If you have any issues or ideas, put it on [issues](https://github.com/yhl452493373/frps-multiuser/issues). I will try my best to achieve it.
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
@@ -137,3 +168,4 @@ If you have any issues or ideas, put it on [issues](https://github.com/yhl452493
|
|||||||
+ [frp](https://github.com/fatedier/frp)
|
+ [frp](https://github.com/fatedier/frp)
|
||||||
+ [fp-multiuser](https://github.com/gofrp/fp-multiuser)
|
+ [fp-multiuser](https://github.com/gofrp/fp-multiuser)
|
||||||
+ [layui](https://github.com/layui/layui)
|
+ [layui](https://github.com/layui/layui)
|
||||||
|
+ [layui-theme-dark](https://github.com/Sight-wcg/layui-theme-dark)
|
||||||
|
|||||||
32
README_zh.md
@@ -23,11 +23,14 @@ frps-multiuser 会以一个单独的进程运行,并接收 frps 发送过来
|
|||||||
+ **新增对用户的`端口`、`域名`、`二级域名`进行限制**
|
+ **新增对用户的`端口`、`域名`、`二级域名`进行限制**
|
||||||
|
|
||||||
***用户被`删除`或`禁用`后,不会马上生效,需要等一段时间***
|
***用户被`删除`或`禁用`后,不会马上生效,需要等一段时间***
|
||||||
|
|
||||||
***用户`端口`、`域名`、`二级域名`限制仅在建立新连接(`NewProxy`)时生效***
|
***用户`端口`、`域名`、`二级域名`限制仅在建立新连接(`NewProxy`)时生效***
|
||||||
|
|
||||||
### 功能
|
### 功能
|
||||||
|
|
||||||
* 通过配置文件配置所有支持的用户名和 Token,只允许匹配的 frpc 客户端登录。
|
* 通过配置文件配置所有支持的用户名和 Token,只允许匹配的 frpc 客户端登录。
|
||||||
|
* 动态`添加`、`删除`、`禁用`、`启用`用户
|
||||||
|
* 对每个用户进行`端口`、`域名`、`二级域名`限制
|
||||||
|
|
||||||
### 下载
|
### 下载
|
||||||
|
|
||||||
@@ -128,8 +131,36 @@ local_port = 22
|
|||||||
remote_port = 6000
|
remote_port = 6000
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 以服务的形式运行
|
||||||
|
|
||||||
|
本实例是在 `ubuntu` 下, 以 `root` 用户执操作
|
||||||
|
|
||||||
|
+ 1、解压 `frps-multiuser.zip` 到目录 `/root/frps-multiuser`
|
||||||
|
+ 2、在目录 `/root/frps-multiuser` 下 用命令创建文件:`touch frps-multiuser.service`。创建后修改文件内容:
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description = frp multiuser service
|
||||||
|
After = network.target syslog.target
|
||||||
|
Wants = network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type = simple
|
||||||
|
# 启动frps-multiuser的配置文件路径,需修改为您的frps-multiuser.ini的路径
|
||||||
|
Environment=FRPS_MULTIUSER_OPTS="-c /root/frps-multiuser/frps-multiuser.ini"
|
||||||
|
# 启动frps-multiuser的命令,需修改为您的frps-multiuser的安装路径
|
||||||
|
ExecStart = /root/frps-multiuser/frps-multiuser $FRPS_MULTIUSER_OPTS
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy = multi-user.target
|
||||||
|
```
|
||||||
|
+ 3、复制服务文件: `cp /root/frps-multiuser.service /etc/systemd/system/`
|
||||||
|
+ 4、重载服务: `systemctl daemon-reload`
|
||||||
|
+ 5、启动服务: `service frps-multiuser start`
|
||||||
|
|
||||||
## 使用
|
## 使用
|
||||||
|
|
||||||
|
___如果要从外网访问管理界面, 需要把配置中的 `plugin_addr` 改为 `0.0.0.0`___
|
||||||
|
|
||||||
如果使用中有问题或者有其他想法,在[issues](https://github.com/yhl452493373/frps-multiuser/issues)上提出来。 如果我能搞定的话,我尽量搞。
|
如果使用中有问题或者有其他想法,在[issues](https://github.com/yhl452493373/frps-multiuser/issues)上提出来。 如果我能搞定的话,我尽量搞。
|
||||||
|
|
||||||
## 致谢
|
## 致谢
|
||||||
@@ -137,3 +168,4 @@ remote_port = 6000
|
|||||||
+ [frp](https://github.com/fatedier/frp)
|
+ [frp](https://github.com/fatedier/frp)
|
||||||
+ [fp-multiuser](https://github.com/gofrp/fp-multiuser)
|
+ [fp-multiuser](https://github.com/gofrp/fp-multiuser)
|
||||||
+ [layui](https://github.com/layui/layui)
|
+ [layui](https://github.com/layui/layui)
|
||||||
|
+ [layui-theme-dark](https://github.com/Sight-wcg/layui-theme-dark)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"User Manage": "User Manage",
|
"User Manage": "User Manage",
|
||||||
|
"frps multiuser": "frps multiuser",
|
||||||
"User": "User",
|
"User": "User",
|
||||||
"Token": "Token",
|
"Token": "Token",
|
||||||
"Notes": "Notes",
|
"Notes": "Notes",
|
||||||
@@ -29,7 +30,8 @@
|
|||||||
"Other error": "Other error",
|
"Other error": "Other error",
|
||||||
"Param error": "Param error",
|
"Param error": "Param error",
|
||||||
"User exist": "User exist",
|
"User exist": "User exist",
|
||||||
"Token cannot be empty": "Token cannot be empty",
|
"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",
|
"Please check at least one user": "Please Check at least one user",
|
||||||
"Operation confirm": "Operation confirm",
|
"Operation confirm": "Operation confirm",
|
||||||
"Empty data": "Empty data",
|
"Empty data": "Empty data",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"User Manage": "用户管理",
|
"User Manage": "用户管理",
|
||||||
|
"frps multiuser": "frps用户管理",
|
||||||
"User": "用户名(user)",
|
"User": "用户名(user)",
|
||||||
"Token": "凭证(meta_token)",
|
"Token": "凭证(meta_token)",
|
||||||
"Notes": "备注",
|
"Notes": "备注",
|
||||||
@@ -29,7 +30,8 @@
|
|||||||
"Other error": "其他异常",
|
"Other error": "其他异常",
|
||||||
"Param error": "参数异常",
|
"Param error": "参数异常",
|
||||||
"User exist": "用户已经存在",
|
"User exist": "用户已经存在",
|
||||||
"Token cannot be empty": "Token 不能为空",
|
"User format error": "用户不能为空或包含空格。只允许英文数字、字母、下划线",
|
||||||
|
"Token format error": "Token不能为空或包含空格。允许的特殊符号:_!@#$%^&*()",
|
||||||
"Please check at least one user": "请选中需要操作的用户",
|
"Please check at least one user": "请选中需要操作的用户",
|
||||||
"Operation confirm": "操作确认",
|
"Operation confirm": "操作确认",
|
||||||
"Empty data": "无数据",
|
"Empty data": "无数据",
|
||||||
@@ -38,7 +40,7 @@
|
|||||||
"Allowed domains": "允许域名",
|
"Allowed domains": "允许域名",
|
||||||
"Please input allowed domains": "请输入允许使用的域名,如:web01.domain.com,web02.domain.com",
|
"Please input allowed domains": "请输入允许使用的域名,如:web01.domain.com,web02.domain.com",
|
||||||
"Allowed subdomains": "允许子域名",
|
"Allowed subdomains": "允许子域名",
|
||||||
"Please input allowed subdomains": "请输入允许使用的端口,如:web01,web02",
|
"Please input allowed subdomains": "请输入允许使用的子域名,如:web01,web02",
|
||||||
"Ports is invalid": "端口不正确",
|
"Ports is invalid": "端口不正确",
|
||||||
"Domains is invalid": "域名不正确",
|
"Domains is invalid": "域名不正确",
|
||||||
"Subdomains is invalid": "子域名不正确",
|
"Subdomains is invalid": "子域名不正确",
|
||||||
|
|||||||
128
assets/static/css/index-color.css
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
header {
|
||||||
|
background-color: #58b7ff;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-btn {
|
||||||
|
background-color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-layer-btn .layui-layer-btn0 {
|
||||||
|
background-color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-btn-primary {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-btn-primary:hover {
|
||||||
|
border-color: #79bbff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-input:focus,
|
||||||
|
.layui-textarea:focus {
|
||||||
|
border-color: #79bbff !important;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-form-danger + .layui-form-select .layui-input,
|
||||||
|
.layui-form-danger:focus {
|
||||||
|
border-color: #ff5722 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-laypage .layui-laypage-curr .layui-laypage-em {
|
||||||
|
background-color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-laypage input:focus, .layui-laypage select:focus {
|
||||||
|
border-color: #79bbff !important;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-table-view .layui-table td[data-edit]:hover:after {
|
||||||
|
border-color: #79bbff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-form-checkbox[lay-skin=primary]:hover > i {
|
||||||
|
border-color: #79bbff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-form-checked[lay-skin=primary] > i {
|
||||||
|
background-color: #409eff;
|
||||||
|
border-color: #409eff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-table-checked {
|
||||||
|
background-color: #ecf5ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-table-checked:hover {
|
||||||
|
background-color: #d9ecff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-table-cell-c:hover{
|
||||||
|
border-color: #79bbff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
header {
|
||||||
|
background-color: #395c74;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-btn {
|
||||||
|
background-color: #4f80a1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-layer-btn .layui-layer-btn0 {
|
||||||
|
background-color: #4f80a1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-btn-primary {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: #484849;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-btn-primary:hover {
|
||||||
|
border-color: #5f5f60;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-input:focus,
|
||||||
|
.layui-textarea:focus {
|
||||||
|
border-color: #5f5f60 !important;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-laypage .layui-laypage-curr .layui-laypage-em {
|
||||||
|
background-color: #4f80a1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-laypage input:focus, .layui-laypage select:focus {
|
||||||
|
border-color: #5f5f60 !important;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-table-view .layui-table td[data-edit]:hover:after {
|
||||||
|
border-color: #5f5f60;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-form-checkbox[lay-skin=primary]:hover > i {
|
||||||
|
border-color: #5f5f60;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-form-checked[lay-skin=primary] > i {
|
||||||
|
background-color: #484849;
|
||||||
|
border-color: #484849 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-table-checked {
|
||||||
|
background-color: rgba(255, 255, 255, .04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-table-checked:hover {
|
||||||
|
background-color: rgba(255, 255, 255, .08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-table-cell-c:hover{
|
||||||
|
border-color: #5f5f60;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,18 @@
|
|||||||
body {
|
html, body {
|
||||||
padding: 15px;
|
padding: 0;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
header .title {
|
||||||
|
padding: 10px 15px;
|
||||||
|
line-height: 40px;
|
||||||
|
font-size: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
padding: 15px 15px 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
#searchForm input {
|
#searchForm input {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
@@ -44,10 +54,14 @@ body {
|
|||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.layui-btn-container{
|
.layui-btn-container {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.layui-layer-btn > a[class^=layui-layer-btn]{
|
.layui-layer-btn > a[class^=layui-layer-btn] {
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.layui-table-page {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
/* https://github.com/Sight-wcg/layui-theme-dark */
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root {
|
:root {
|
||||||
/* =====色板===== */
|
/* =====色板===== */
|
||||||
@@ -2048,28 +2047,37 @@
|
|||||||
background-color: var(--lay-color-bg-3)
|
background-color: var(--lay-color-bg-3)
|
||||||
}
|
}
|
||||||
|
|
||||||
.layui-layer-imgnext, .layui-layer-imgprev {
|
.layui-layer-photos {
|
||||||
|
background: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-layer-photos-prev, .layui-layer-photos-next {
|
||||||
color: var(--lay-color-gray-9)
|
color: var(--lay-color-gray-9)
|
||||||
}
|
}
|
||||||
|
|
||||||
.layui-layer-imgnext:hover, .layui-layer-imgprev:hover {
|
.layui-layer-photos-prev:hover, .layui-layer-photos-next:hover {
|
||||||
color: var(--lay-color-gray-8)
|
color: var(--lay-color-text-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
.layui-layer-imgbar {
|
.layui-layer-photos-toolbar {
|
||||||
background-color: #333 \9;
|
background-color: #333 \9;
|
||||||
background-color: var(--lay-color-bg-5);
|
background-color: var(--lay-color-bg-5);
|
||||||
color: var(--lay-color-text-1)
|
color: var(--lay-color-text-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
.layui-layer-imgtit * {
|
.layui-layer-photos-toolbar * {
|
||||||
color: var(--lay-color-text-1)
|
color: var(--lay-color-text-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
.layui-layer-imgtit a:hover {
|
.layui-layer-photos-toolbar a:hover {
|
||||||
color: var(--lay-color-text-2)
|
color: var(--lay-color-text-2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.layui-layer-photos-header > span:hover {
|
||||||
|
background-color: var(--lay-color-fill-2)
|
||||||
|
}
|
||||||
|
|
||||||
.layui-layer-tips i.layui-layer-TipsB, .layui-layer-tips i.layui-layer-TipsT {
|
.layui-layer-tips i.layui-layer-TipsB, .layui-layer-tips i.layui-layer-TipsT {
|
||||||
border-right-color: var(--lay-color-bg-5)
|
border-right-color: var(--lay-color-bg-5)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,36 @@ $(function () {
|
|||||||
Disable: 3
|
Disable: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* verify user value
|
||||||
|
* @param username
|
||||||
|
*/
|
||||||
|
function verifyUser(username) {
|
||||||
|
var valid = true;
|
||||||
|
if (username.trim() === '' || !/^\w+$/.test(username)) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
valid: valid,
|
||||||
|
trim: username
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* verify token value
|
||||||
|
* @param token
|
||||||
|
*/
|
||||||
|
function verifyToken(token) {
|
||||||
|
var valid = true;
|
||||||
|
if (token.trim() === '' || !/^[\w!@#$%^&*()]+$/.test(token)) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
valid: valid,
|
||||||
|
trim: token.trim()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* verify comment is valid
|
* verify comment is valid
|
||||||
* @param comment
|
* @param comment
|
||||||
@@ -120,52 +150,90 @@ $(function () {
|
|||||||
function langLoaded(lang) {
|
function langLoaded(lang) {
|
||||||
//set verify rules
|
//set verify rules
|
||||||
var verifyRules = {
|
var verifyRules = {
|
||||||
|
user: function (value, item) {
|
||||||
|
var result = verifyUser(value);
|
||||||
|
if (!result.valid) {
|
||||||
|
return lang['UserFormatError'];
|
||||||
|
}
|
||||||
|
if (item != null) {
|
||||||
|
if (typeof item === "function") {
|
||||||
|
item && item(result.trim);
|
||||||
|
} else {
|
||||||
|
$(item).val(result.trim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
token: function (value, item) {
|
||||||
|
var result = verifyToken(value);
|
||||||
|
if (!result.valid) {
|
||||||
|
return lang['TokenFormatError'];
|
||||||
|
}
|
||||||
|
if (item != null) {
|
||||||
|
if (typeof item === "function") {
|
||||||
|
item && item(result.trim);
|
||||||
|
} else {
|
||||||
|
$(item).val(result.trim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
comment: function (value, item) {
|
comment: function (value, item) {
|
||||||
var result = verifyComment(value);
|
var result = verifyComment(value);
|
||||||
if (!result.valid) {
|
if (!result.valid) {
|
||||||
return lang['CommentInvalid'];
|
return lang['CommentInvalid'];
|
||||||
}
|
}
|
||||||
|
if (item != null) {
|
||||||
if (typeof item === "function") {
|
if (typeof item === "function") {
|
||||||
item && item(result.trim);
|
item && item(result.trim);
|
||||||
} else {
|
} else {
|
||||||
$(item).val(result.trim);
|
$(item).val(result.trim);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
ports: function (value, item) {
|
ports: function (value, item) {
|
||||||
var result = verifyPorts(value);
|
var result = verifyPorts(value);
|
||||||
if (!result.valid) {
|
if (!result.valid) {
|
||||||
return lang['PortsInvalid'];
|
return lang['PortsInvalid'];
|
||||||
}
|
}
|
||||||
|
if (item != null) {
|
||||||
if (typeof item === "function") {
|
if (typeof item === "function") {
|
||||||
item && item(result.trim);
|
item && item(result.trim);
|
||||||
} else {
|
} else {
|
||||||
$(item).val(result.trim);
|
$(item).val(result.trim);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
domains: function (value, item) {
|
domains: function (value, item) {
|
||||||
var result = verifyDomains(value);
|
var result = verifyDomains(value);
|
||||||
if (!result.valid) {
|
if (!result.valid) {
|
||||||
return lang['DomainsInvalid'];
|
return lang['DomainsInvalid'];
|
||||||
}
|
}
|
||||||
|
if (item != null) {
|
||||||
if (typeof item === "function") {
|
if (typeof item === "function") {
|
||||||
item && item(result.trim);
|
item && item(result.trim);
|
||||||
} else {
|
} else {
|
||||||
$(item).val(result.trim);
|
$(item).val(result.trim);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
subdomains: function (value, item) {
|
subdomains: function (value, item) {
|
||||||
var result = verifySubdomains(value);
|
var result = verifySubdomains(value);
|
||||||
if (!result.valid) {
|
if (!result.valid) {
|
||||||
return lang['SubdomainsInvalid'];
|
return lang['SubdomainsInvalid'];
|
||||||
}
|
}
|
||||||
|
if (item != null) {
|
||||||
if (typeof item === "function") {
|
if (typeof item === "function") {
|
||||||
item && item(result.trim);
|
item && item(result.trim);
|
||||||
} else {
|
} else {
|
||||||
$(item).val(result.trim);
|
$(item).val(result.trim);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
layui.form.verify(verifyRules);
|
|
||||||
|
layui.form.set({
|
||||||
|
verIncludeRequired: true,
|
||||||
|
verify: verifyRules
|
||||||
|
});
|
||||||
|
|
||||||
layui.table.render({
|
layui.table.render({
|
||||||
elem: '#tokenTable',
|
elem: '#tokenTable',
|
||||||
@@ -217,8 +285,11 @@ $(function () {
|
|||||||
var after = $.extend(true, {}, obj.data);
|
var after = $.extend(true, {}, obj.data);
|
||||||
var verifyMsg = false;
|
var verifyMsg = false;
|
||||||
if (field === 'token') {
|
if (field === 'token') {
|
||||||
if (value.trim() === '') {
|
verifyMsg = verifyRules.token(value, function (trim) {
|
||||||
layui.layer.msg(lang['TokenEmpty'])
|
updateTableField(obj, field, trim)
|
||||||
|
});
|
||||||
|
if (verifyMsg) {
|
||||||
|
layui.layer.msg(verifyMsg);
|
||||||
return obj.reedit();
|
return obj.reedit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,7 +389,7 @@ $(function () {
|
|||||||
btn: [lang['Confirm'], lang['Cancel']],
|
btn: [lang['Confirm'], lang['Cancel']],
|
||||||
btn1: function (index) {
|
btn1: function (index) {
|
||||||
if (layui.form.validate('#addUserForm')) {
|
if (layui.form.validate('#addUserForm')) {
|
||||||
add(layui.form.val('addUserForm'), index)
|
add(layui.form.val('addUserForm'), index);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
btn2: function (index) {
|
btn2: function (index) {
|
||||||
@@ -540,6 +611,12 @@ $(function () {
|
|||||||
reason = lang['ParamError'];
|
reason = lang['ParamError'];
|
||||||
else if (result.code === 2)
|
else if (result.code === 2)
|
||||||
reason = lang['UserExist'];
|
reason = lang['UserExist'];
|
||||||
|
else if (result.code === 3)
|
||||||
|
reason = lang['ParamError'];
|
||||||
|
else if (result.code === 4)
|
||||||
|
reason = lang['UserFormatError'];
|
||||||
|
else if (result.code === 5)
|
||||||
|
reason = lang['TokenFormatError'];
|
||||||
layui.layer.msg(lang['OperateFailed'] + ',' + reason)
|
layui.layer.msg(lang['OperateFailed'] + ',' + reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<link rel="stylesheet" href="./static/layui/css/layui.css">
|
<link rel="stylesheet" href="./static/layui/css/layui.css">
|
||||||
<link rel="stylesheet" href="./static/css/layui-theme-dark.css">
|
<link rel="stylesheet" href="./static/css/layui-theme-dark.css">
|
||||||
<link rel="stylesheet" href="./static/css/index.css">
|
<link rel="stylesheet" href="./static/css/index.css">
|
||||||
|
<link rel="stylesheet" href="./static/css/index-color.css">
|
||||||
<script src="./static/layui/layui.js"></script>
|
<script src="./static/layui/layui.js"></script>
|
||||||
<script src="./static/js/index.js"></script>
|
<script src="./static/js/index.js"></script>
|
||||||
<style>
|
<style>
|
||||||
@@ -18,13 +19,17 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<form class="layui-form layui-row layui-col-space16" id="searchForm" lay-filter="searchForm">
|
<header>
|
||||||
|
<div class="title">${ .FrpsMultiuser }</div>
|
||||||
|
</header>
|
||||||
|
<section>
|
||||||
|
<form class="layui-form layui-row layui-col-space16" id="searchForm" lay-filter="searchForm">
|
||||||
<div class="layui-col-md3">
|
<div class="layui-col-md3">
|
||||||
<div class="layui-input-wrap">
|
<div class="layui-input-wrap">
|
||||||
<div class="layui-input-prefix">
|
<div class="layui-input-prefix">
|
||||||
<i class="layui-icon layui-icon-username"></i>
|
<i class="layui-icon layui-icon-username"></i>
|
||||||
</div>
|
</div>
|
||||||
<input type="text" name="user" value="" placeholder="${ .User }" class="layui-input" autocomplete="off"
|
<input type="text" name="user" placeholder="${ .User }" class="layui-input" autocomplete="off"
|
||||||
lay-affix="clear">
|
lay-affix="clear">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -52,8 +57,10 @@
|
|||||||
<button class="layui-btn layui-btn-sm layui-btn-primary" type="reset" id="resetBtn">${ .Reset }</button>
|
<button class="layui-btn layui-btn-sm layui-btn-primary" type="reset" id="resetBtn">${ .Reset }</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<table id="tokenTable" lay-filter="tokenTable"></table>
|
<table id="tokenTable" lay-filter="tokenTable"></table>
|
||||||
|
</section>
|
||||||
|
|
||||||
<script type="text/html" id="toolbarTemplate">
|
<script type="text/html" id="toolbarTemplate">
|
||||||
<div class="layui-btn-container">
|
<div class="layui-btn-container">
|
||||||
<button class="layui-btn layui-btn-sm" lay-event="add">${ .NewUser }</button>
|
<button class="layui-btn layui-btn-sm" lay-event="add">${ .NewUser }</button>
|
||||||
@@ -73,21 +80,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
<script type="text/html" id="addTemplate">
|
<script type="text/html" id="addTemplate">
|
||||||
<div class="layui-form" id="addUserForm" lay-filter="addUserForm">
|
<form class="layui-form" id="addUserForm" lay-filter="addUserForm">
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">${ .User }</label>
|
<label class="layui-form-label">${ .User }</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="text" name="user" placeholder="${ .PleaseInputUserAccount }" autocomplete="off"
|
<input type="text" name="user" lay-verify="user" placeholder="${ .PleaseInputUserAccount }"
|
||||||
class="layui-input"
|
autocomplete="off" class="layui-input"/>
|
||||||
lay-verify="required">
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">${ .Token }</label>
|
<label class="layui-form-label">${ .Token }</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="text" name="token" lay-verify="required" placeholder="${ .PleaseInputUserToken }"
|
<input type="text" name="token" lay-verify="token" placeholder="${ .PleaseInputUserToken }"
|
||||||
class="layui-input"
|
autocomplete="off" class="layui-input"/>
|
||||||
autocomplete="off">
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item layui-form-text">
|
<div class="layui-form-item layui-form-text">
|
||||||
@@ -118,7 +123,7 @@
|
|||||||
autocomplete="off" class="layui-textarea"></textarea>
|
autocomplete="off" class="layui-textarea"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -33,12 +34,21 @@ var rootCmd = &cobra.Command{
|
|||||||
log.Println(version)
|
log.Println(version)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
executable, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error get program path: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rootDir := filepath.Dir(executable)
|
||||||
|
|
||||||
common, tokens, ports, domains, subdomains, iniFile, err := ParseConfigFile(configFile)
|
common, tokens, ports, domains, subdomains, iniFile, err := ParseConfigFile(configFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("fail to start frps-multiuser : %v", err)
|
log.Printf("fail to start frps-multiuser : %v", err)
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
s, err := server.New(controller.HandleController{
|
s, err := server.New(
|
||||||
|
rootDir,
|
||||||
|
controller.HandleController{
|
||||||
CommonInfo: common,
|
CommonInfo: common,
|
||||||
Tokens: tokens,
|
Tokens: tokens,
|
||||||
Ports: ports,
|
Ports: ports,
|
||||||
@@ -19,8 +19,12 @@ const (
|
|||||||
ParamError = 1
|
ParamError = 1
|
||||||
UserExist = 2
|
UserExist = 2
|
||||||
SaveError = 3
|
SaveError = 3
|
||||||
|
UserFormatError = 4
|
||||||
|
TokenFormatError = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var UserFormatReg = regexp.MustCompile("^\\w+$")
|
||||||
|
var TokenFormatReg = regexp.MustCompile("^[\\w!@#$%^&*()]+$")
|
||||||
var TrimAllSpaceReg = regexp.MustCompile("[\\n\\t\\r\\s]")
|
var TrimAllSpaceReg = regexp.MustCompile("[\\n\\t\\r\\s]")
|
||||||
var TrimBreakLineReg = regexp.MustCompile("[\\n\\t\\r]")
|
var TrimBreakLineReg = regexp.MustCompile("[\\n\\t\\r]")
|
||||||
|
|
||||||
@@ -160,6 +164,7 @@ func (c *HandleController) MakeManagerFunc() func(context *gin.Context) {
|
|||||||
return func(context *gin.Context) {
|
return func(context *gin.Context) {
|
||||||
context.HTML(http.StatusOK, "index.html", gin.H{
|
context.HTML(http.StatusOK, "index.html", gin.H{
|
||||||
"UserManage": ginI18n.MustGetMessage(context, "User Manage"),
|
"UserManage": ginI18n.MustGetMessage(context, "User Manage"),
|
||||||
|
"FrpsMultiuser": ginI18n.MustGetMessage(context, "frps multiuser"),
|
||||||
"User": ginI18n.MustGetMessage(context, "User"),
|
"User": ginI18n.MustGetMessage(context, "User"),
|
||||||
"Token": ginI18n.MustGetMessage(context, "Token"),
|
"Token": ginI18n.MustGetMessage(context, "Token"),
|
||||||
"Notes": ginI18n.MustGetMessage(context, "Notes"),
|
"Notes": ginI18n.MustGetMessage(context, "Notes"),
|
||||||
@@ -210,7 +215,8 @@ func (c *HandleController) MakeLangFunc() func(context *gin.Context) {
|
|||||||
"OperateError": ginI18n.MustGetMessage(context, "Operate error"),
|
"OperateError": ginI18n.MustGetMessage(context, "Operate error"),
|
||||||
"OperateFailed": ginI18n.MustGetMessage(context, "Operate failed"),
|
"OperateFailed": ginI18n.MustGetMessage(context, "Operate failed"),
|
||||||
"UserExist": ginI18n.MustGetMessage(context, "User exist"),
|
"UserExist": ginI18n.MustGetMessage(context, "User exist"),
|
||||||
"TokenEmpty": ginI18n.MustGetMessage(context, "Token cannot be empty"),
|
"UserFormatError": ginI18n.MustGetMessage(context, "User format error"),
|
||||||
|
"TokenFormatError": ginI18n.MustGetMessage(context, "Token format error"),
|
||||||
"ShouldCheckUser": ginI18n.MustGetMessage(context, "Please check at least one user"),
|
"ShouldCheckUser": ginI18n.MustGetMessage(context, "Please check at least one user"),
|
||||||
"OperationConfirm": ginI18n.MustGetMessage(context, "Operation confirm"),
|
"OperationConfirm": ginI18n.MustGetMessage(context, "Operation confirm"),
|
||||||
"EmptyData": ginI18n.MustGetMessage(context, "Empty data"),
|
"EmptyData": ginI18n.MustGetMessage(context, "Empty data"),
|
||||||
@@ -221,6 +227,7 @@ func (c *HandleController) MakeLangFunc() func(context *gin.Context) {
|
|||||||
"DomainsInvalid": ginI18n.MustGetMessage(context, "Domains is invalid"),
|
"DomainsInvalid": ginI18n.MustGetMessage(context, "Domains is invalid"),
|
||||||
"SubdomainsInvalid": ginI18n.MustGetMessage(context, "Subdomains is invalid"),
|
"SubdomainsInvalid": ginI18n.MustGetMessage(context, "Subdomains is invalid"),
|
||||||
"CommentInvalid": ginI18n.MustGetMessage(context, "Comment is invalid"),
|
"CommentInvalid": ginI18n.MustGetMessage(context, "Comment is invalid"),
|
||||||
|
"ParamError": ginI18n.MustGetMessage(context, "Param error"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -313,6 +320,14 @@ func (c *HandleController) MakeAddTokenFunc() func(context *gin.Context) {
|
|||||||
context.JSON(http.StatusOK, &response)
|
context.JSON(http.StatusOK, &response)
|
||||||
return
|
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 {
|
if _, exist := c.Tokens[info.User]; exist {
|
||||||
log.Printf("user add failed, user [%v] exist", info.User)
|
log.Printf("user add failed, user [%v] exist", info.User)
|
||||||
response.Success = false
|
response.Success = false
|
||||||
@@ -321,6 +336,16 @@ func (c *HandleController) MakeAddTokenFunc() func(context *gin.Context) {
|
|||||||
context.JSON(http.StatusOK, &response)
|
context.JSON(http.StatusOK, &response)
|
||||||
return
|
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)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
replaceSpaceToken := TrimAllSpaceReg.ReplaceAllString(info.Token, "")
|
||||||
|
info.Token = replaceSpaceToken
|
||||||
c.Tokens[info.User] = info
|
c.Tokens[info.User] = info
|
||||||
|
|
||||||
usersSection, _ := c.IniFile.GetSection("users")
|
usersSection, _ := c.IniFile.GetSection("users")
|
||||||
@@ -388,7 +413,18 @@ func (c *HandleController) MakeUpdateTokensFunc() func(context *gin.Context) {
|
|||||||
comment := TrimBreakLineReg.ReplaceAllString(after.Comment, "")
|
comment := TrimBreakLineReg.ReplaceAllString(after.Comment, "")
|
||||||
after.Comment = comment
|
after.Comment = comment
|
||||||
key.Comment = comment
|
key.Comment = comment
|
||||||
key.SetValue(after.Token)
|
|
||||||
|
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)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
replaceSpaceToken := TrimAllSpaceReg.ReplaceAllString(after.Token, "")
|
||||||
|
after.Token = replaceSpaceToken
|
||||||
|
key.SetValue(replaceSpaceToken)
|
||||||
|
|
||||||
if before.Ports != after.Ports {
|
if before.Ports != after.Ports {
|
||||||
portsSection, _ := c.IniFile.GetSection("ports")
|
portsSection, _ := c.IniFile.GetSection("ports")
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -24,9 +26,15 @@ func NewHandleController(config *HandleController) *HandleController {
|
|||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *HandleController) Register(engine *gin.Engine) {
|
func (c *HandleController) Register(rootDir string, engine *gin.Engine) {
|
||||||
|
assets := filepath.Join(rootDir, "assets")
|
||||||
|
_, err := os.Stat(assets)
|
||||||
|
if err != nil && !os.IsExist(err) {
|
||||||
|
assets = "./assets"
|
||||||
|
}
|
||||||
|
|
||||||
engine.Delims("${", "}")
|
engine.Delims("${", "}")
|
||||||
engine.LoadHTMLGlob("./assets/templates/*")
|
engine.LoadHTMLGlob(filepath.Join(assets, "templates/*"))
|
||||||
engine.POST("/handler", c.MakeHandlerFunc())
|
engine.POST("/handler", c.MakeHandlerFunc())
|
||||||
|
|
||||||
var group *gin.RouterGroup
|
var group *gin.RouterGroup
|
||||||
@@ -37,7 +45,7 @@ func (c *HandleController) Register(engine *gin.Engine) {
|
|||||||
} else {
|
} else {
|
||||||
group = engine.Group("/")
|
group = engine.Group("/")
|
||||||
}
|
}
|
||||||
group.Static("/static", "./assets/static")
|
group.Static("/static", filepath.Join(assets, "static"))
|
||||||
group.GET("/", c.MakeManagerFunc())
|
group.GET("/", c.MakeManagerFunc())
|
||||||
group.GET("/lang", c.MakeLangFunc())
|
group.GET("/lang", c.MakeLangFunc())
|
||||||
group.GET("/tokens", c.MakeQueryTokensFunc())
|
group.GET("/tokens", c.MakeQueryTokensFunc())
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -22,12 +23,14 @@ type Server struct {
|
|||||||
cfg controller.HandleController
|
cfg controller.HandleController
|
||||||
s *http.Server
|
s *http.Server
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
|
rootDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(cfg controller.HandleController) (*Server, error) {
|
func New(rootDir string, cfg controller.HandleController) (*Server, error) {
|
||||||
s := &Server{
|
s := &Server{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
|
rootDir: rootDir,
|
||||||
}
|
}
|
||||||
if err := s.init(); err != nil {
|
if err := s.init(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -74,14 +77,10 @@ func LoadSupportLanguage(dir string) ([]language.Tag, error) {
|
|||||||
var tags []language.Tag
|
var tags []language.Tag
|
||||||
|
|
||||||
files, err := os.Open(dir)
|
files, err := os.Open(dir)
|
||||||
if err != nil {
|
|
||||||
log.Printf("error opening directory: %v", err)
|
|
||||||
return tags, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fileList, err := files.Readdir(-1)
|
fileList, err := files.Readdir(-1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error reading directory: %v", err)
|
log.Printf("error read lang directory: %v", err)
|
||||||
return tags, err
|
return tags, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,15 +102,21 @@ func LoadSupportLanguage(dir string) ([]language.Tag, error) {
|
|||||||
return tags, nil
|
return tags, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GinI18nLocalize() gin.HandlerFunc {
|
func GinI18nLocalize(rootDir string) gin.HandlerFunc {
|
||||||
dir := "./assets/lang"
|
assets := filepath.Join(rootDir, "assets")
|
||||||
tags, err := LoadSupportLanguage(dir)
|
_, err := os.Stat(assets)
|
||||||
|
if err != nil && !os.IsExist(err) {
|
||||||
|
assets = "./assets"
|
||||||
|
}
|
||||||
|
lang := filepath.Join(assets, "lang")
|
||||||
|
tags, err := LoadSupportLanguage(lang)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("language file is not found: %v", err)
|
log.Panicf("language file is not found: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ginI18n.Localize(
|
return ginI18n.Localize(
|
||||||
ginI18n.WithBundle(&ginI18n.BundleCfg{
|
ginI18n.WithBundle(&ginI18n.BundleCfg{
|
||||||
RootPath: dir,
|
RootPath: lang,
|
||||||
AcceptLanguage: tags,
|
AcceptLanguage: tags,
|
||||||
DefaultLanguage: language.Chinese,
|
DefaultLanguage: language.Chinese,
|
||||||
FormatBundleFile: "json",
|
FormatBundleFile: "json",
|
||||||
@@ -133,10 +138,10 @@ func GinI18nLocalize() gin.HandlerFunc {
|
|||||||
func (s *Server) initHTTPServer() error {
|
func (s *Server) initHTTPServer() error {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
engine := gin.New()
|
engine := gin.New()
|
||||||
engine.Use(GinI18nLocalize())
|
engine.Use(GinI18nLocalize(s.rootDir))
|
||||||
s.s = &http.Server{
|
s.s = &http.Server{
|
||||||
Handler: engine,
|
Handler: engine,
|
||||||
}
|
}
|
||||||
controller.NewHandleController(&s.cfg).Register(engine)
|
controller.NewHandleController(&s.cfg).Register(s.rootDir, engine)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 147 KiB After Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 327 KiB After Width: | Height: | Size: 236 KiB |
|
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 134 KiB |