Compare commits

...

141 Commits

Author SHA1 Message Date
刘祥超
42148a66bd 改进文字提示 2022-11-27 22:00:35 +08:00
刘祥超
96878715bf 优化界面 2022-11-26 19:04:55 +08:00
刘祥超
1a5f3342e7 优化文字提示 2022-11-26 15:28:01 +08:00
刘祥超
3613d13a2b 优化文字说明 2022-11-25 19:05:11 +08:00
刘祥超
7786140d85 优化代码 2022-11-24 17:20:08 +08:00
刘祥超
3a23b57f1b 优化智能DNS访问日志组件 2022-11-24 10:24:04 +08:00
刘祥超
5a6e6fba69 节点详情显示API连接状况 2022-11-22 11:27:48 +08:00
刘祥超
910b3a6162 在节点详情中显示API节点地址 2022-11-21 21:07:51 +08:00
刘祥超
0dc19bed45 节点可以单独设置所使用的API节点地址 2022-11-21 19:54:54 +08:00
刘祥超
a7bdb64301 记录名第一段允许通配符 2022-11-20 20:08:03 +08:00
刘祥超
4739072a85 Update go.mod 2022-11-18 17:37:25 +08:00
刘祥超
07bdae2488 安装时检测本地数据库时增加更多的候选密码 2022-11-18 17:07:12 +08:00
刘祥超
b84035d821 优化代码 2022-11-18 17:02:38 +08:00
刘祥超
dc0a7b9dae 优化文字提示 2022-11-18 15:45:13 +08:00
刘祥超
2937bd8de0 提交components.js 2022-11-17 17:34:21 +08:00
刘祥超
37315ef4d9 优化代码 2022-11-17 10:41:56 +08:00
刘祥超
1986fece07 优化代码 2022-11-17 10:40:57 +08:00
刘祥超
16b1657f35 优化WAF规则相关界面 2022-11-16 15:43:08 +08:00
刘祥超
c115c62cd9 去掉服务CNAME末尾的点符号,防止误解 2022-11-16 14:44:07 +08:00
刘祥超
d2df7f8d5b 节点列表页带宽使用bps显示 2022-11-16 12:02:56 +08:00
刘祥超
6cb79864e6 边缘节点支持设置多个缓存目录 2022-11-15 20:35:45 +08:00
刘祥超
982d28c7b4 docker build增加--no-cache参数 2022-11-14 15:01:13 +08:00
刘祥超
0f57516fdc Dockerfile: 使用alpine:latest代替alpine:edge 2022-11-14 14:49:19 +08:00
刘祥超
75a89defcb 构建Dockerfile时从官网下载最新版本安装包 2022-11-14 10:01:22 +08:00
刘祥超
22a6c52060 增加Docker镜像构建脚本 2022-11-13 18:46:30 +08:00
刘祥超
37607e4a41 安装过程显示更详细内容 2022-11-11 21:47:36 +08:00
刘祥超
5936155998 优化文字提示 2022-11-11 17:46:10 +08:00
刘祥超
8d76de935f 优化文字提示 2022-11-10 15:07:15 +08:00
刘祥超
9baa530064 优化文字提示 2022-11-09 18:20:18 +08:00
刘祥超
103414b338 优化<checkbox>组件 2022-11-09 17:50:22 +08:00
刘祥超
72fe68ebfe 访问日志搜索method:XXX和requestMethod:XXX方法 2022-11-09 11:58:39 +08:00
刘祥超
cfed31958b 优化文字提示 2022-11-08 09:22:09 +08:00
刘祥超
3d5fca2d36 调整默认压缩的mime types 2022-11-08 09:18:13 +08:00
刘祥超
a5710286ec 优化代码 2022-11-06 20:34:19 +08:00
刘祥超
d0ce0c6c58 优化界面 2022-11-06 15:16:21 +08:00
刘祥超
779e2cf0f2 域名跳转增加忽略跳转前端口选项 2022-11-04 20:59:20 +08:00
刘祥超
2108474777 删除不必要的文件 2022-11-04 15:19:30 +08:00
刘祥超
e25e0f1747 删除不必要的文件 2022-11-04 15:01:36 +08:00
刘祥超
e8e74b639c 优化datepicker组件 2022-11-04 14:35:32 +08:00
刘祥超
a14fcd1e50 添加和修改API节点时,检查HTTP API端口是否和GRPC端口冲突 2022-11-04 12:04:07 +08:00
刘祥超
485c0e0891 修复系统用户和平台用户access key无法禁用和删除的问题 2022-11-04 11:06:45 +08:00
刘祥超
00a19e9d43 时钟同步增加是否检查chrony选项 2022-11-03 14:58:32 +08:00
刘祥超
67d0dc0783 版本修改为0.5.8 2022-11-02 15:10:47 +08:00
刘祥超
3718c35842 提交components.js 2022-11-02 15:10:39 +08:00
刘祥超
567ffc80b6 优化<domains-box>组件 2022-11-02 09:58:59 +08:00
刘祥超
5d15a08ac8 防盗链使用<domains-box>组件 2022-11-02 09:47:17 +08:00
刘祥超
2b84037346 修改一处注释 2022-11-01 21:16:48 +08:00
刘祥超
536f11e617 修复datetime组件时间戳生成错误 2022-10-31 11:08:36 +08:00
刘祥超
5d367a384e 修改版本为0.5.7 2022-10-31 10:39:10 +08:00
刘祥超
04933e6bf0 重置配置的同时也重置本地API节点的配置 2022-10-30 20:06:59 +08:00
刘祥超
12abe9aa69 优化界面显示 2022-10-30 20:06:38 +08:00
刘祥超
cba642a4bc 多处域名列表支持批量输入 2022-10-28 17:59:46 +08:00
刘祥超
9fce0ac0aa 优化API节点界面 2022-10-27 19:45:03 +08:00
刘祥超
681812b619 优化申请证书界面 2022-10-27 19:44:50 +08:00
刘祥超
6d0be57698 更新components.js 2022-10-27 11:19:10 +08:00
刘祥超
1d521602e1 节点SSH登录自动使用集群设置 2022-10-26 19:23:50 +08:00
刘祥超
2f67e7937a 集群全局服务配置中增加多个访问日志相关选项 2022-10-26 17:49:25 +08:00
刘祥超
e0078a42dc URL跳转中增加域名跳转、端口跳转 2022-10-26 16:06:50 +08:00
刘祥超
3ec875d49d 节点设置中增加“通过IP名单”选项 2022-10-26 10:41:42 +08:00
刘祥超
35028d1310 开源版本Dashboard不再显示用户节点相关信息 2022-10-26 09:51:09 +08:00
刘祥超
7ba3d7c4bb 修复窄屏显示时有些内容无法隐藏的问题 2022-10-26 09:30:41 +08:00
刘祥超
c05e64098c 更新components.js 2022-10-25 14:36:40 +08:00
刘祥超
e82ee56a2c IP名单支持模糊查询 2022-10-25 10:15:17 +08:00
刘祥超
5681b61aea 在选择区域弹出框中可以不选区域直接确定返回 2022-10-25 09:43:31 +08:00
刘祥超
d6ce7eab25 修复域名解析--集群中单节点多IP时无法修改IP的Bug 2022-10-24 16:33:56 +08:00
刘祥超
bf597fe41c 添加、修改、删除HTTP Header时增加通用Header提示 2022-10-24 15:42:18 +08:00
刘祥超
6a920f964f 集群服务设置--访问日志中可以设置是否只记录通用Header 2022-10-24 14:39:32 +08:00
刘祥超
977b66ba4e 防盗链功能增加禁止的来源域名 2022-10-24 10:20:44 +08:00
刘祥超
4659c29358 优化界面显示 2022-10-23 11:44:09 +08:00
刘祥超
e3bc95b275 管理安全设置中域名列表支持通配符 2022-10-21 18:36:33 +08:00
刘祥超
bf51255e13 优化区域列表界面 2022-10-20 15:17:03 +08:00
刘祥超
915fe6837b 区域设置中增加节点快速设置区域的功能 2022-10-20 15:11:26 +08:00
刘祥超
d1237215c0 更新components.js 2022-10-20 11:07:07 +08:00
刘祥超
8ba5cfdfa6 优化代码 2022-10-19 19:55:51 +08:00
刘祥超
0d1097425d 优化界面和文字 2022-10-18 10:31:20 +08:00
刘祥超
0789a9ecc8 优化数字格式化 2022-10-16 09:36:36 +08:00
刘祥超
400f764b74 优化数字格式化 2022-10-15 19:33:07 +08:00
刘祥超
46e036e3a4 删除代码 2022-10-15 16:58:45 +08:00
刘祥超
17e6264af8 删除不需要的代码 2022-10-14 09:57:24 +08:00
刘祥超
468b6ae125 添加源站的时候也提示8443和8080对应的协议 2022-10-07 09:01:22 +08:00
刘祥超
e5f5ee4f6a 提交components.js 2022-10-03 21:11:02 +08:00
刘祥超
3695082ec2 服务列表页带宽使用比特代替字节 2022-10-03 19:25:20 +08:00
刘祥超
f384d86014 版本修改为0.5.6 2022-10-01 08:49:00 +08:00
刘祥超
34bf5028c3 删除不必要的代码 2022-10-01 07:16:37 +08:00
刘祥超
8b727aa939 提交components.js 2022-09-30 18:36:46 +08:00
刘祥超
9432600de6 优化网站服务列表页在海量域名时的加载速度 2022-09-30 13:48:16 +08:00
刘祥超
bb790ec687 文件下载链接找不到对应元素时不提示错误 2022-09-30 09:39:59 +08:00
刘祥超
8bad658d7c 版本调整为v0.5.5 2022-09-28 18:57:47 +08:00
刘祥超
a66cc9c08a 删除不必要的代码 2022-09-28 17:38:11 +08:00
刘祥超
0dc24fb342 systemd服务增加BEGIN INIT INFO 2022-09-28 08:17:32 +08:00
刘祥超
b965ac6232 版本修改为0.5.4 2022-09-27 08:05:50 +08:00
刘祥超
08d61013c8 删除多于的文件 2022-09-27 08:04:35 +08:00
刘祥超
7796f65814 将版本修改为0.5.4 2022-09-26 15:16:53 +08:00
刘祥超
02a3afb9ad 版本修改为0.5.3.1 2022-09-26 13:02:35 +08:00
刘祥超
15a11c384b 优化代码 2022-09-25 20:34:43 +08:00
刘祥超
57b8496d89 更新components.js 2022-09-25 10:34:12 +08:00
刘祥超
7bbe44f5d2 优化代码 2022-09-25 10:33:10 +08:00
刘祥超
ac11ab0431 缓存条件增加”忽略URI参数“选项 2022-09-24 15:13:35 +08:00
刘祥超
12e352964c 默认支持206 Partial Content 2022-09-24 14:40:34 +08:00
刘祥超
7daefc4384 优化代码 2022-09-24 14:06:41 +08:00
刘祥超
29541becd0 添加Header的时候自动去除Header后多余的冒号 2022-09-23 19:00:51 +08:00
刘祥超
ef6dc82f88 优化代码 2022-09-23 11:09:21 +08:00
刘祥超
6d66d93180 智能DNS访问日志增加只记录失败查询选项 2022-09-22 18:41:43 +08:00
刘祥超
3ded17b920 优化防盗链选项说明文字 2022-09-22 17:55:58 +08:00
刘祥超
d35651f570 增加防盗链功能 2022-09-22 16:33:35 +08:00
刘祥超
235300d034 修改反向代理等文字 2022-09-22 15:27:23 +08:00
刘祥超
2f7ebf5166 优化代码 2022-09-21 13:49:02 +08:00
刘祥超
4819cc87c6 删除不必要的文件 2022-09-20 18:22:13 +08:00
刘祥超
c5da383d1e 更新components.js 2022-09-19 09:50:03 +08:00
刘祥超
2da17329fa 创建节点时尝试自动从节点名称中读取IP 2022-09-18 14:57:37 +08:00
刘祥超
70d84acf76 在节点手动安装页显示节点安装文件下载链接 2022-09-18 12:38:32 +08:00
刘祥超
fef99a0023 优化代码/DNS域名增加分页 2022-09-18 10:22:58 +08:00
刘祥超
76d0935e8f 可以设置是否自动安装nftables 2022-09-17 21:04:45 +08:00
刘祥超
42dfe5ada9 缓存的URL类型文字从“前缀”改成“目录” 2022-09-17 19:26:27 +08:00
刘祥超
ef136c25e1 域名解析域名和记录名中支持中文 2022-09-17 16:50:25 +08:00
刘祥超
4251635a8a 集群增加是否远程启动选项 2022-09-17 15:11:40 +08:00
刘祥超
13f00104dd 健康检查设置域名时检查域名是否存在 2022-09-17 11:38:04 +08:00
刘祥超
b0b82b29c1 集群设置--DNS设置页显示DNS账号名 2022-09-17 10:59:18 +08:00
刘祥超
e0d43ad3d9 节点即使离线后仍然在运行状态中显示版本、主程序位置等信息 2022-09-17 10:25:40 +08:00
刘祥超
ae15115af7 创建集群时增加“只允许绑定的域名访问”选项 2022-09-16 19:33:57 +08:00
刘祥超
a2890c6cb0 集群设置中增加服务设置 2022-09-16 18:41:04 +08:00
刘祥超
2b00ece07f 访问日志中增加源站状态码 2022-09-16 10:07:21 +08:00
刘祥超
2e3c34571f 删除不必要的文件 2022-09-15 19:03:30 +08:00
刘祥超
b8b10b5176 集群增加自动同步时钟选项 2022-09-15 15:57:53 +08:00
刘祥超
e6e62bbd24 域名和记录名中可以使用大写 2022-09-14 19:48:23 +08:00
刘祥超
05e9bbf1d6 添加域名窗口中提示可以添加泛域名 2022-09-14 09:24:27 +08:00
刘祥超
bf94c2395f 可以隐藏CDN功能菜单 2022-09-13 19:54:31 +08:00
刘祥超
4a749ca345 优化GRPC参数 2022-09-13 10:48:54 +08:00
刘祥超
80ffe6c9a3 实现DNS域名验证 2022-09-10 16:13:31 +08:00
刘祥超
5414b5a8f9 优化提示文字 2022-09-09 10:33:16 +08:00
刘祥超
e3ba85a326 修改管理界面设置中的时区时同时也会应用到API节点 2022-09-09 10:27:10 +08:00
刘祥超
154fa69dbb 优化安装时跟端口相关的提示 2022-09-08 19:45:23 +08:00
刘祥超
76c44973e9 域名解析增加EdgeDNS API 2022-09-08 19:36:37 +08:00
刘祥超
26fdc82cc1 创建集群的时候可以设置DNS记录的默认TTL 2022-09-08 11:02:19 +08:00
刘祥超
dcc587182c 优化文字提示 2022-09-08 11:00:44 +08:00
刘祥超
7cee1ff5ff 访问日志里以标签的形式显示中文域名 2022-09-07 17:02:40 +08:00
刘祥超
78b8e3bf0b API节点在启动时提示“API节点正在启动,请耐心等待完成” 2022-09-07 15:57:06 +08:00
刘祥超
bd4c014c12 增加edge-admin upgrade命令 2022-09-06 21:19:37 +08:00
刘祥超
daa263cd68 将版本修改为0.5.3 2022-09-06 09:24:02 +08:00
刘祥超
e2965d39af 将版本修改为0.5.2.1 2022-09-05 16:04:51 +08:00
刘祥超
8279d15ebb 将版本修改为0.5.3 2022-09-05 11:02:17 +08:00
356 changed files with 6792 additions and 6072 deletions

View File

@@ -1,23 +1,30 @@
package main
import (
"bytes"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/apps"
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/gen"
"github.com/TeaOSLab/EdgeAdmin/internal/nodes"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web"
"github.com/iwind/TeaGo/Tea"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/gosock/pkg/gosock"
"log"
"os"
"os/exec"
"time"
)
func main() {
app := apps.NewAppCmd().
var app = apps.NewAppCmd().
Version(teaconst.Version).
Product(teaconst.ProductName).
Usage(teaconst.ProcessName+" [-v|start|stop|restart|service|daemon|reset|recover|demo]").
Usage(teaconst.ProcessName+" [-v|start|stop|restart|service|daemon|reset|recover|demo|upgrade]").
Usage(teaconst.ProcessName+" [dev|prod]").
Option("-h", "show this help").
Option("-v", "show version").
@@ -30,7 +37,8 @@ func main() {
Option("recover", "enter recovery mode").
Option("demo", "switch to demo mode").
Option("dev", "switch to 'dev' mode").
Option("prod", "switch to 'prod' mode")
Option("prod", "switch to 'prod' mode").
Option("upgrade", "upgrade from official site")
app.On("daemon", func() {
nodes.NewAdminNode().Daemon()
@@ -49,6 +57,20 @@ func main() {
fmt.Println("[ERROR]reset failed: " + err.Error())
return
}
// reset local api
var apiNodeExe = Tea.Root + "/edge-api/bin/edge-api"
_, err = os.Stat(apiNodeExe)
if err == nil {
var cmd = exec.Command(apiNodeExe, "reset")
var stderr = &bytes.Buffer{}
cmd.Stderr = stderr
err = cmd.Run()
if err != nil {
fmt.Println("reset api node failed: " + stderr.String())
}
}
fmt.Println("done")
})
app.On("recover", func() {
@@ -115,8 +137,42 @@ func main() {
fmt.Println("switch to '" + env + "' ok")
}
})
app.On("upgrade", func() {
var manager = utils.NewUpgradeManager("admin")
log.Println("checking latest version ...")
var ticker = time.NewTicker(1 * time.Second)
go func() {
var lastProgress float32 = 0
var isStarted = false
for range ticker.C {
if manager.IsDownloading() {
if !isStarted {
log.Println("start downloading v" + manager.NewVersion() + " ...")
isStarted = true
}
var progress = manager.Progress()
if progress >= 0 {
if progress == 0 || progress == 1 || progress-lastProgress >= 0.1 {
lastProgress = progress
log.Println(fmt.Sprintf("%.2f%%", manager.Progress()*100))
}
}
} else {
break
}
}
}()
err := manager.Start()
if err != nil {
log.Println("upgrade failed: " + err.Error())
return
}
log.Println("finished!")
log.Println("restarting ...")
app.RunRestart()
})
app.Run(func() {
adminNode := nodes.NewAdminNode()
var adminNode = nodes.NewAdminNode()
adminNode.Run()
})
}

1
docker/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.zip

36
docker/Dockerfile Normal file
View File

@@ -0,0 +1,36 @@
FROM alpine:latest
LABEL maintainer="iwind.liu@gmail.com"
ENV TZ "Asia/Shanghai"
ENV VERSION 0.5.7
ENV ROOT_DIR /usr/local/goedge
ENV TAR_FILE edge-admin-linux-amd64-plus-v${VERSION}.zip
ENV TAR_URL "https://dl.goedge.cn/edge/v${VERSION}/edge-admin-linux-amd64-plus-v${VERSION}.zip"
RUN apk add --no-cache tzdata
RUN apk add wget
RUN mkdir ${ROOT_DIR}; \
cd ${ROOT_DIR}; \
wget ${TAR_URL} -O ${TAR_FILE}; \
apk add unzip; \
unzip ${TAR_FILE}; \
rm -f ${TAR_FILE}
RUN apk add mysql mysql-client; \
sed -e "s/\[mysqld\]/\[mysqld\]\n\ndatadir=\/var\/lib\/mysql\nport=3306\ninnodb_flush_log_at_trx_commit=2\nmax_connections=256\nmax_prepared_stmt_count=65535\nbinlog_cache_size=1M\nbinlog_stmt_cache_size=1M\nthread_cache_size=32\nbinlog_expire_logs_seconds=1209600\n\n/" /etc/my.cnf > /tmp/my.cnf; \
cp /tmp/my.cnf /etc/my.cnf; \
sed -e "s/skip-networking/#skip-networking/" /etc/my.cnf.d/mariadb-server.cnf > /tmp/mariadb-server.cnf; \
cp /tmp/mariadb-server.cnf /etc/my.cnf.d/mariadb-server.cnf; \
mysql_install_db --user=mysql
RUN mysqld_safe --user=mysql & \
sleep 5; \
mysql -uroot -hlocalhost --execute="ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';"
RUN echo -e "#!/usr/bin/env sh\n\nmysqld_safe --user=mysql &\n/usr/local/goedge/edge-admin/bin/edge-admin\n" > ${ROOT_DIR}/run.sh; \
chmod u+x ${ROOT_DIR}/run.sh
EXPOSE 7788
EXPOSE 8001
EXPOSE 3306
ENTRYPOINT [ "/usr/local/goedge/run.sh" ]

5
docker/build.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/usr/bin/env bash
VERSION=latest
docker build --no-cache -t goedge/edge-admin:${VERSION} .

5
docker/run.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/usr/bin/env bash
VERSION=latest
docker run -d -p 7788:7788 -p 8001:8001 -p 3306:3306 --name edge-admin goedge/edge-admin:${VERSION}

View File

@@ -123,7 +123,7 @@ func (this *AppCmd) On(arg string, callback func()) {
// Run 运行
func (this *AppCmd) Run(main func()) {
// 获取参数
args := os.Args[1:]
var args = os.Args[1:]
if len(args) > 0 {
switch args[0] {
case "-v", "version", "-version", "--version":
@@ -139,7 +139,7 @@ func (this *AppCmd) Run(main func()) {
this.runStop()
return
case "restart":
this.runRestart()
this.RunRestart()
return
case "status":
this.runStatus()
@@ -160,7 +160,7 @@ func (this *AppCmd) Run(main func()) {
}
// 日志
writer := new(LogWriter)
var writer = new(LogWriter)
writer.Init()
logs.SetWriter(writer)
@@ -210,7 +210,7 @@ func (this *AppCmd) runStop() {
}
// 重启
func (this *AppCmd) runRestart() {
func (this *AppCmd) RunRestart() {
this.runStop()
time.Sleep(1 * time.Second)
this.runStart()

View File

@@ -6,7 +6,7 @@ import (
)
func TestLoadAdminModuleMapping(t *testing.T) {
m, err := LoadAdminModuleMapping()
m, err := loadAdminModuleMapping()
if err != nil {
t.Fatal(err)
}

View File

@@ -9,7 +9,7 @@ import (
func TestLoadUIConfig(t *testing.T) {
for i := 0; i < 10; i++ {
before := time.Now()
config, err := LoadUIConfig()
config, err := LoadAdminUIConfig()
if err != nil {
t.Fatal(err)
}
@@ -20,7 +20,7 @@ func TestLoadUIConfig(t *testing.T) {
func TestLoadUIConfig2(t *testing.T) {
for i := 0; i < 10; i++ {
config, err := LoadUIConfig()
config, err := LoadAdminUIConfig()
if err != nil {
t.Fatal(err)
}

View File

@@ -1,96 +0,0 @@
package configloaders
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/iwind/TeaGo/logs"
"reflect"
)
var sharedUserUIConfig *systemconfigs.UserUIConfig = nil
const (
UserUISettingName = "userUIConfig"
)
func LoadUserUIConfig() (*systemconfigs.UserUIConfig, error) {
locker.Lock()
defer locker.Unlock()
config, err := loadUserUIConfig()
if err != nil {
return nil, err
}
v := reflect.Indirect(reflect.ValueOf(config)).Interface().(systemconfigs.UserUIConfig)
return &v, nil
}
func UpdateUserUIConfig(uiConfig *systemconfigs.UserUIConfig) error {
locker.Lock()
defer locker.Unlock()
var rpcClient, err = rpc.SharedRPC()
if err != nil {
return err
}
valueJSON, err := json.Marshal(uiConfig)
if err != nil {
return err
}
_, err = rpcClient.SysSettingRPC().UpdateSysSetting(rpcClient.Context(0), &pb.UpdateSysSettingRequest{
Code: UserUISettingName,
ValueJSON: valueJSON,
})
if err != nil {
return err
}
sharedUserUIConfig = uiConfig
return nil
}
func loadUserUIConfig() (*systemconfigs.UserUIConfig, error) {
if sharedUserUIConfig != nil {
return sharedUserUIConfig, nil
}
var rpcClient, err = rpc.SharedRPC()
if err != nil {
return nil, err
}
resp, err := rpcClient.SysSettingRPC().ReadSysSetting(rpcClient.Context(0), &pb.ReadSysSettingRequest{
Code: UserUISettingName,
})
if err != nil {
return nil, err
}
if len(resp.ValueJSON) == 0 {
sharedUserUIConfig = defaultUserUIConfig()
return sharedUserUIConfig, nil
}
config := &systemconfigs.UserUIConfig{}
err = json.Unmarshal(resp.ValueJSON, config)
if err != nil {
logs.Println("[UI_MANAGER]" + err.Error())
sharedUserUIConfig = defaultUserUIConfig()
return sharedUserUIConfig, nil
}
sharedUserUIConfig = config
return sharedUserUIConfig, nil
}
func defaultUserUIConfig() *systemconfigs.UserUIConfig {
return &systemconfigs.UserUIConfig{
ProductName: "GoEdge",
UserSystemName: "GoEdge用户系统",
ShowOpenSourceInfo: true,
ShowVersion: true,
ShowFinance: true,
BandwidthUnit: systemconfigs.BandwidthUnitBit,
ShowBandwidthCharts: true,
ShowTrafficCharts: true,
}
}

View File

@@ -61,10 +61,11 @@ func LoadAPIConfig() (*APIConfig, error) {
// ResetAPIConfig 重置配置
func ResetAPIConfig() error {
filename := "api.yaml"
var filename = "api.yaml"
// 重置 configs/api.yaml
{
configFile := Tea.ConfigFile(filename)
var configFile = Tea.ConfigFile(filename)
stat, err := os.Stat(configFile)
if err == nil && !stat.IsDir() {
err = os.Remove(configFile)
@@ -77,7 +78,7 @@ func ResetAPIConfig() error {
// 重置 ~/.edge-admin/api.yaml
homeDir, homeErr := os.UserHomeDir()
if homeErr == nil {
configFile := homeDir + "/." + teaconst.ProcessName + "/" + filename
var configFile = homeDir + "/." + teaconst.ProcessName + "/" + filename
stat, err := os.Stat(configFile)
if err == nil && !stat.IsDir() {
err = os.Remove(configFile)
@@ -89,7 +90,7 @@ func ResetAPIConfig() error {
// 重置 /etc/edge-admin/api.yaml
{
configFile := "/etc/" + teaconst.ProcessName + "/" + filename
var configFile = "/etc/" + teaconst.ProcessName + "/" + filename
stat, err := os.Stat(configFile)
if err == nil && !stat.IsDir() {
err = os.Remove(configFile)

View File

@@ -14,13 +14,7 @@ func TestLoadAPIConfig(t *testing.T) {
}
func TestAPIConfig_WriteFile(t *testing.T) {
config := &APIConfig{
RPC: struct {
Endpoints []string `yaml:"endpoints"`
}{},
NodeId: "1",
Secret: "2",
}
config := &APIConfig{}
err := config.WriteFile("/tmp/api_config.yaml")
if err != nil {
t.Fatal(err)

View File

@@ -1,9 +1,9 @@
package teaconst
const (
Version = "0.5.2"
Version = "0.5.8"
APINodeVersion = "0.5.2"
APINodeVersion = "0.5.8"
ProductName = "Edge Admin"
ProcessName = "edge-admin"

View File

@@ -1,9 +1,9 @@
package nodes
import (
"errors"
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/errors"
"github.com/TeaOSLab/EdgeAdmin/internal/events"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/iwind/TeaGo"
@@ -90,7 +90,7 @@ func (this *AdminNode) Run() {
EndAll().
Session(sessions.NewFileSessionManager(86400, secret), teaconst.CookieSID).
ReadHeaderTimeout(3 * time.Second).
ReadTimeout(600 * time.Second).
ReadTimeout(1200 * time.Second).
Start()
}

View File

@@ -98,10 +98,6 @@ func (this *RPCClient) NodeRegionRPC() pb.NodeRegionServiceClient {
return pb.NewNodeRegionServiceClient(this.pickConn())
}
func (this *RPCClient) NodePriceItemRPC() pb.NodePriceItemServiceClient {
return pb.NewNodePriceItemServiceClient(this.pickConn())
}
func (this *RPCClient) NodeIPAddressRPC() pb.NodeIPAddressServiceClient {
return pb.NewNodeIPAddressServiceClient(this.pickConn())
}
@@ -174,18 +170,10 @@ func (this *RPCClient) APIMethodStatRPC() pb.APIMethodStatServiceClient {
return pb.NewAPIMethodStatServiceClient(this.pickConn())
}
func (this *RPCClient) UserNodeRPC() pb.UserNodeServiceClient {
return pb.NewUserNodeServiceClient(this.pickConn())
}
func (this *RPCClient) DBNodeRPC() pb.DBNodeServiceClient {
return pb.NewDBNodeServiceClient(this.pickConn())
}
func (this *RPCClient) MonitorNodeRPC() pb.MonitorNodeServiceClient {
return pb.NewMonitorNodeServiceClient(this.pickConn())
}
func (this *RPCClient) DBRPC() pb.DBServiceClient {
return pb.NewDBServiceClient(this.pickConn())
}
@@ -407,26 +395,6 @@ func (this *RPCClient) UserRPC() pb.UserServiceClient {
return pb.NewUserServiceClient(this.pickConn())
}
func (this *RPCClient) UserBillRPC() pb.UserBillServiceClient {
return pb.NewUserBillServiceClient(this.pickConn())
}
func (this *RPCClient) ServerBillRPC() pb.ServerBillServiceClient {
return pb.NewServerBillServiceClient(this.pickConn())
}
func (this *RPCClient) UserAccountRPC() pb.UserAccountServiceClient {
return pb.NewUserAccountServiceClient(this.pickConn())
}
func (this *RPCClient) UserAccountLogRPC() pb.UserAccountLogServiceClient {
return pb.NewUserAccountLogServiceClient(this.pickConn())
}
func (this *RPCClient) UserAccountDailyStatRPC() pb.UserAccountDailyStatServiceClient {
return pb.NewUserAccountDailyStatServiceClient(this.pickConn())
}
func (this *RPCClient) UserAccessKeyRPC() pb.UserAccessKeyServiceClient {
return pb.NewUserAccessKeyServiceClient(this.pickConn())
}
@@ -483,14 +451,6 @@ func (this *RPCClient) ServerStatBoardChartRPC() pb.ServerStatBoardChartServiceC
return pb.NewServerStatBoardChartServiceClient(this.pickConn())
}
func (this *RPCClient) PlanRPC() pb.PlanServiceClient {
return pb.NewPlanServiceClient(this.pickConn())
}
func (this *RPCClient) UserPlanRPC() pb.UserPlanServiceClient {
return pb.NewUserPlanServiceClient(this.pickConn())
}
func (this *RPCClient) TrafficDailyStatRPC() pb.TrafficDailyStatServiceClient {
return pb.NewTrafficDailyStatServiceClient(this.pickConn())
}
@@ -611,41 +571,30 @@ func (this *RPCClient) pickConn() *grpc.ClientConn {
defer this.locker.Unlock()
// 检查连接状态
if len(this.conns) > 0 {
var availableConns = []*grpc.ClientConn{}
for _, state := range []connectivity.State{connectivity.Ready, connectivity.Idle, connectivity.Connecting} {
var countConns = len(this.conns)
if countConns > 0 {
if countConns == 1 {
return this.conns[0]
}
for _, state := range []connectivity.State{
connectivity.Ready,
connectivity.Idle,
connectivity.Connecting,
connectivity.TransientFailure,
} {
var availableConns = []*grpc.ClientConn{}
for _, conn := range this.conns {
if conn.GetState() == state {
availableConns = append(availableConns, conn)
}
}
if len(availableConns) > 0 {
break
return this.randConn(availableConns)
}
}
if len(availableConns) > 0 {
return availableConns[rands.Int(0, len(availableConns)-1)]
}
// 关闭
for _, conn := range this.conns {
_ = conn.Close()
}
}
// 重新初始化
err := this.init()
if err != nil {
// 错误提示已经在构造对象时打印过,所以这里不再重复打印
return nil
}
if len(this.conns) == 0 {
return nil
}
return this.conns[rands.Int(0, len(this.conns)-1)]
return this.randConn(this.conns)
}
// Close 关闭
@@ -679,3 +628,14 @@ func (this *RPCClient) localIPAddrs() []string {
}
return localIPAddrs
}
func (this *RPCClient) randConn(conns []*grpc.ClientConn) *grpc.ClientConn {
var l = len(conns)
if l == 0 {
return nil
}
if l == 1 {
return conns[0]
}
return conns[rands.Int(0, l-1)]
}

View File

@@ -14,6 +14,7 @@ import (
"github.com/iwind/TeaGo/logs"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"net/url"
"sort"
"strings"
@@ -143,8 +144,9 @@ func (this *SyncAPINodesTask) testEndpoints(endpoints []string) bool {
cancel()
}()
var conn *grpc.ClientConn
if u.Scheme == "http" {
conn, err = grpc.DialContext(ctx, u.Host, grpc.WithInsecure(), grpc.WithBlock())
conn, err = grpc.DialContext(ctx, u.Host, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
} else if u.Scheme == "https" {
conn, err = grpc.DialContext(ctx, u.Host, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
InsecureSkipVerify: true,

12
internal/utils/email.go Normal file
View File

@@ -0,0 +1,12 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package utils
import "regexp"
var emailReg = regexp.MustCompile(`(?i)^[a-z\d]+([._+-]*[a-z\d]+)*@([a-z\d]+[a-z\d-]*[a-z\d]+\.)+[a-z\d]+$`)
// ValidateEmail 校验电子邮箱格式
func ValidateEmail(email string) bool {
return emailReg.MatchString(email)
}

View File

@@ -0,0 +1,22 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package utils_test
import (
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/iwind/TeaGo/assert"
"testing"
)
func TestValidateEmail(t *testing.T) {
var a = assert.NewAssertion(t)
a.IsTrue(utils.ValidateEmail("aaaa@gmail.com"))
a.IsTrue(utils.ValidateEmail("a.b@gmail.com"))
a.IsTrue(utils.ValidateEmail("a.b.c.d@gmail.com"))
a.IsTrue(utils.ValidateEmail("aaaa@gmail.com.cn"))
a.IsTrue(utils.ValidateEmail("hello.world.123@gmail.123.com"))
a.IsTrue(utils.ValidateEmail("10000@qq.com"))
a.IsFalse(utils.ValidateEmail("aaaa.@gmail.com"))
a.IsFalse(utils.ValidateEmail("aaaa@gmail"))
a.IsFalse(utils.ValidateEmail("aaaa@123"))
}

View File

@@ -3,7 +3,9 @@ package numberutils
import (
"fmt"
"github.com/iwind/TeaGo/types"
"regexp"
"strconv"
"strings"
)
func FormatInt64(value int64) string {
@@ -28,17 +30,35 @@ func FormatBytes(bytes int64) string {
if bytes < Pow1024(1) {
return FormatInt64(bytes) + "B"
} else if bytes < Pow1024(2) {
return fmt.Sprintf("%.2fKB", float64(bytes)/float64(Pow1024(1)))
return TrimZeroSuffix(fmt.Sprintf("%.2fKB", float64(bytes)/float64(Pow1024(1))))
} else if bytes < Pow1024(3) {
return fmt.Sprintf("%.2fMB", float64(bytes)/float64(Pow1024(2)))
return TrimZeroSuffix(fmt.Sprintf("%.2fMB", float64(bytes)/float64(Pow1024(2))))
} else if bytes < Pow1024(4) {
return fmt.Sprintf("%.2fGB", float64(bytes)/float64(Pow1024(3)))
return TrimZeroSuffix(fmt.Sprintf("%.2fGB", float64(bytes)/float64(Pow1024(3))))
} else if bytes < Pow1024(5) {
return fmt.Sprintf("%.2fTB", float64(bytes)/float64(Pow1024(4)))
return TrimZeroSuffix(fmt.Sprintf("%.2fTB", float64(bytes)/float64(Pow1024(4))))
} else if bytes < Pow1024(6) {
return fmt.Sprintf("%.2fPB", float64(bytes)/float64(Pow1024(5)))
return TrimZeroSuffix(fmt.Sprintf("%.2fPB", float64(bytes)/float64(Pow1024(5))))
} else {
return fmt.Sprintf("%.2fEB", float64(bytes)/float64(Pow1024(6)))
return TrimZeroSuffix(fmt.Sprintf("%.2fEB", float64(bytes)/float64(Pow1024(6))))
}
}
func FormatBits(bits int64) string {
if bits < Pow1024(1) {
return FormatInt64(bits) + "bps"
} else if bits < Pow1024(2) {
return TrimZeroSuffix(fmt.Sprintf("%.4fKbps", float64(bits)/float64(Pow1024(1))))
} else if bits < Pow1024(3) {
return TrimZeroSuffix(fmt.Sprintf("%.4fMbps", float64(bits)/float64(Pow1024(2))))
} else if bits < Pow1024(4) {
return TrimZeroSuffix(fmt.Sprintf("%.4fGbps", float64(bits)/float64(Pow1024(3))))
} else if bits < Pow1024(5) {
return TrimZeroSuffix(fmt.Sprintf("%.4fTbps", float64(bits)/float64(Pow1024(4))))
} else if bits < Pow1024(6) {
return TrimZeroSuffix(fmt.Sprintf("%.4fPbps", float64(bits)/float64(Pow1024(5))))
} else {
return TrimZeroSuffix(fmt.Sprintf("%.4fEbps", float64(bits)/float64(Pow1024(6))))
}
}
@@ -62,9 +82,19 @@ func FormatFloat(f interface{}, decimal int) string {
switch x := f.(type) {
case float32, float64:
var s = fmt.Sprintf("%."+types.String(decimal)+"f", x)
// 分隔
var dotIndex = strings.Index(s, ".")
if dotIndex > 0 {
var d = s[:dotIndex]
var f2 = s[dotIndex:]
f2 = strings.TrimRight(strings.TrimRight(f2, "0"), ".")
return formatDigit(d) + f2
}
return s
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
return types.String(x)
return formatDigit(types.String(x))
case string:
return x
}
@@ -74,3 +104,44 @@ func FormatFloat(f interface{}, decimal int) string {
func FormatFloat2(f interface{}) string {
return FormatFloat(f, 2)
}
var decimalReg = regexp.MustCompile(`^(\d+\.\d+)([a-zA-Z]+)?$`)
// TrimZeroSuffix 去除小数数字尾部多余的0
func TrimZeroSuffix(s string) string {
var matches = decimalReg.FindStringSubmatch(s)
if len(matches) < 3 {
return s
}
return strings.TrimRight(strings.TrimRight(matches[1], "0"), ".") + matches[2]
}
func formatDigit(d string) string {
if len(d) == 0 {
return d
}
var prefix = ""
if d[0] < '0' || d[0] > '9' {
prefix = d[:1]
d = d[1:]
}
var l = len(d)
if l > 3 {
var pieces = l / 3
var commIndex = l - pieces*3
var d2 = ""
if commIndex > 0 {
d2 = d[:commIndex] + ", "
}
for i := 0; i < pieces; i++ {
d2 += d[commIndex+i*3 : commIndex+i*3+3]
if i != pieces-1 {
d2 += ", "
}
}
return prefix + d2
}
return prefix + d
}

View File

@@ -33,4 +33,35 @@ func TestFormatFloat(t *testing.T) {
t.Log(numberutils.FormatFloat(100.23456, 2))
t.Log(numberutils.FormatFloat(100.000023, 2))
t.Log(numberutils.FormatFloat(100.012, 2))
t.Log(numberutils.FormatFloat(123.012, 2))
t.Log(numberutils.FormatFloat(1234.012, 2))
t.Log(numberutils.FormatFloat(12345.012, 2))
t.Log(numberutils.FormatFloat(123456.012, 2))
t.Log(numberutils.FormatFloat(1234567.012, 2))
t.Log(numberutils.FormatFloat(12345678.012, 2))
t.Log(numberutils.FormatFloat(123456789.012, 2))
t.Log(numberutils.FormatFloat(1234567890.012, 2))
t.Log(numberutils.FormatFloat(123, 2))
t.Log(numberutils.FormatFloat(1234, 2))
t.Log(numberutils.FormatFloat(1234.00001, 4))
t.Log(numberutils.FormatFloat(1234.56700, 4))
t.Log(numberutils.FormatFloat(-1234.56700, 2))
t.Log(numberutils.FormatFloat(-221745.12, 2))
}
func TestTrimZeroSuffix(t *testing.T) {
for _, s := range []string{
"1",
"1.0000",
"1.10",
"100",
"100.0000",
"100.0",
"100.0123",
"100.0010",
"100.000KB",
"100.010MB",
} {
t.Log(s, "=>", numberutils.TrimZeroSuffix(s))
}
}

View File

@@ -111,7 +111,8 @@ func (this *ServiceManager) installSystemdService(systemd, exePath string, args
shortName := teaconst.SystemdServiceName
longName := "GoEdge Admin" // TODO 将来可以修改
desc := `# Provides: ` + shortName + `
desc := `### BEGIN INIT INFO
# Provides: ` + shortName + `
# Required-Start: $all
# Required-Stop:
# Default-Start: 2 3 4 5

55
internal/utils/time.go Normal file
View File

@@ -0,0 +1,55 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package utils
import (
"errors"
"fmt"
"github.com/iwind/TeaGo/types"
"regexp"
)
// RangeTimes 计算时间点
func RangeTimes(timeFrom string, timeTo string, everyMinutes int32) (result []string, err error) {
if everyMinutes <= 0 {
return nil, errors.New("invalid 'everyMinutes'")
}
var reg = regexp.MustCompile(`^\d{4}$`)
if !reg.MatchString(timeFrom) {
return nil, errors.New("invalid timeFrom '" + timeFrom + "'")
}
if !reg.MatchString(timeTo) {
return nil, errors.New("invalid timeTo '" + timeTo + "'")
}
if timeFrom > timeTo {
// swap
timeFrom, timeTo = timeTo, timeFrom
}
var everyMinutesInt = int(everyMinutes)
var fromHour = types.Int(timeFrom[:2])
var fromMinute = types.Int(timeFrom[2:])
var toHour = types.Int(timeTo[:2])
var toMinute = types.Int(timeTo[2:])
if fromMinute%everyMinutesInt == 0 {
result = append(result, timeFrom)
}
for {
fromMinute += everyMinutesInt
if fromMinute > 59 {
fromHour += fromMinute / 60
fromMinute = fromMinute % 60
}
if fromHour > toHour || (fromHour == toHour && fromMinute > toMinute) {
break
}
result = append(result, fmt.Sprintf("%02d%02d", fromHour, fromMinute))
}
return
}

View File

@@ -0,0 +1,292 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package utils
import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
stringutil "github.com/iwind/TeaGo/utils/string"
"io"
"net/http"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"
)
type UpgradeFileWriter struct {
rawWriter io.Writer
written int64
}
func NewUpgradeFileWriter(rawWriter io.Writer) *UpgradeFileWriter {
return &UpgradeFileWriter{rawWriter: rawWriter}
}
func (this *UpgradeFileWriter) Write(p []byte) (n int, err error) {
n, err = this.rawWriter.Write(p)
this.written += int64(n)
return
}
func (this *UpgradeFileWriter) TotalWritten() int64 {
return this.written
}
type UpgradeManager struct {
client *http.Client
component string
newVersion string
contentLength int64
isDownloading bool
writer *UpgradeFileWriter
body io.ReadCloser
isCancelled bool
}
func NewUpgradeManager(component string) *UpgradeManager {
return &UpgradeManager{
component: component,
client: &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
CheckRedirect: nil,
Jar: nil,
Timeout: 30 * time.Minute,
},
}
}
func (this *UpgradeManager) Start() error {
if this.isDownloading {
return errors.New("another process is running")
}
this.isDownloading = true
defer func() {
this.client.CloseIdleConnections()
this.isDownloading = false
}()
// 检查unzip
unzipExe, _ := exec.LookPath("unzip")
if len(unzipExe) == 0 {
// TODO install unzip automatically or pack with a static 'unzip' file
return errors.New("can not find 'unzip' command")
}
// 检查cp
cpExe, _ := exec.LookPath("cp")
if len(cpExe) == 0 {
return errors.New("can not find 'cp' command")
}
// 检查新版本
var downloadURL = ""
{
var url = teaconst.UpdatesURL
var osName = runtime.GOOS
if Tea.IsTesting() && osName == "darwin" {
osName = "linux"
}
url = strings.ReplaceAll(url, "${os}", osName)
url = strings.ReplaceAll(url, "${arch}", runtime.GOARCH)
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return errors.New("create url request failed: " + err.Error())
}
resp, err := this.client.Do(req)
if err != nil {
return errors.New("read latest version failed: " + err.Error())
}
defer func() {
_ = resp.Body.Close()
}()
if resp.StatusCode != http.StatusOK {
return errors.New("read latest version failed: invalid response code '" + types.String(resp.StatusCode) + "'")
}
data, err := io.ReadAll(resp.Body)
if err != nil {
return errors.New("read latest version failed: " + err.Error())
}
var m = maps.Map{}
err = json.Unmarshal(data, &m)
if err != nil {
return errors.New("invalid response data: " + err.Error() + ", origin data: " + string(data))
}
var code = m.GetInt("code")
if code != 200 {
return errors.New(m.GetString("message"))
}
var dataMap = m.GetMap("data")
var downloadHost = dataMap.GetString("host")
var versions = dataMap.GetSlice("versions")
var downloadPath = ""
for _, component := range versions {
var componentMap = maps.NewMap(component)
if componentMap.Has("version") {
if componentMap.GetString("code") == this.component {
var version = componentMap.GetString("version")
if stringutil.VersionCompare(version, teaconst.Version) > 0 {
this.newVersion = version
downloadPath = componentMap.GetString("url")
break
}
}
}
}
if len(downloadPath) == 0 {
return errors.New("no latest version to download")
}
downloadURL = downloadHost + downloadPath
}
{
req, err := http.NewRequest(http.MethodGet, downloadURL, nil)
if err != nil {
return errors.New("create download request failed: " + err.Error())
}
resp, err := this.client.Do(req)
if err != nil {
return errors.New("download failed: " + downloadURL + ": " + err.Error())
}
defer func() {
_ = resp.Body.Close()
}()
if resp.StatusCode != http.StatusOK {
return errors.New("download failed: " + downloadURL + ": invalid response code '" + types.String(resp.StatusCode) + "'")
}
this.contentLength = resp.ContentLength
this.body = resp.Body
// download to tmp
var tmpDir = os.TempDir()
var filename = filepath.Base(downloadURL)
var destFile = tmpDir + "/" + filename
_ = os.Remove(destFile)
fp, err := os.Create(destFile)
if err != nil {
return errors.New("create file failed: " + err.Error())
}
defer func() {
// 删除安装文件
_ = os.Remove(destFile)
}()
this.writer = NewUpgradeFileWriter(fp)
_, err = io.Copy(this.writer, resp.Body)
if err != nil {
_ = fp.Close()
if this.isCancelled {
return nil
}
return errors.New("download failed: " + err.Error())
}
_ = fp.Close()
// unzip
var unzipDir = tmpDir + "/edge-" + this.component + "-tmp"
stat, err := os.Stat(unzipDir)
if err == nil && stat.IsDir() {
err = os.RemoveAll(unzipDir)
if err != nil {
return errors.New("remove old dir '" + unzipDir + "' failed: " + err.Error())
}
}
var unzipCmd = exec.Command(unzipExe, "-q", "-o", destFile, "-d", unzipDir)
var unzipStderr = &bytes.Buffer{}
unzipCmd.Stderr = unzipStderr
err = unzipCmd.Run()
if err != nil {
return errors.New("unzip installation file failed: " + err.Error() + ": " + unzipStderr.String())
}
installationFiles, err := filepath.Glob(unzipDir + "/edge-" + this.component + "/*")
if err != nil {
return errors.New("lookup installation files failed: " + err.Error())
}
// cp to target dir
currentExe, err := os.Executable()
if err != nil {
return errors.New("reveal current executable file path failed: " + err.Error())
}
var targetDir = filepath.Dir(filepath.Dir(currentExe))
if !Tea.IsTesting() {
for _, installationFile := range installationFiles {
var cpCmd = exec.Command(cpExe, "-R", "-f", installationFile, targetDir)
var cpStderr = &bytes.Buffer{}
cpCmd.Stderr = cpStderr
err = cpCmd.Run()
if err != nil {
return errors.New("overwrite installation files failed: '" + cpCmd.String() + "': " + cpStderr.String())
}
}
}
// remove tmp
_ = os.RemoveAll(unzipDir)
}
return nil
}
func (this *UpgradeManager) IsDownloading() bool {
return this.isDownloading
}
func (this *UpgradeManager) Progress() float32 {
if this.contentLength <= 0 {
return -1
}
if this.writer == nil {
return -1
}
return float32(this.writer.TotalWritten()) / float32(this.contentLength)
}
func (this *UpgradeManager) NewVersion() string {
return this.newVersion
}
func (this *UpgradeManager) Cancel() error {
this.isCancelled = true
this.isDownloading = false
if this.body != nil {
_ = this.body.Close()
}
return nil
}

View File

@@ -0,0 +1,35 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package utils_test
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"testing"
"time"
)
func TestNewUpgradeManager(t *testing.T) {
var manager = utils.NewUpgradeManager("admin")
var ticker = time.NewTicker(2 * time.Second)
go func() {
for range ticker.C {
if manager.IsDownloading() {
t.Logf("%.2f%%", manager.Progress()*100)
}
}
}()
/**go func() {
time.Sleep(5 * time.Second)
if manager.IsDownloading() {
t.Log("cancel downloading")
_ = manager.Cancel()
}
}()**/
err := manager.Start()
if err != nil {
t.Fatal(err)
}
}

View File

@@ -49,7 +49,11 @@ func (this *ParentAction) ErrorText(err string) {
}
func (this *ParentAction) NotFound(name string, itemId int64) {
this.ErrorPage(errors.New(name + " id: '" + strconv.FormatInt(itemId, 10) + "' is not found"))
if itemId > 0 {
this.ErrorPage(errors.New(name + " id: '" + strconv.FormatInt(itemId, 10) + "' is not found"))
} else {
this.ErrorPage(errors.New(name + " is not found"))
}
}
func (this *ParentAction) NewPage(total int64, size ...int64) *Page {
@@ -126,11 +130,8 @@ func (this *ParentAction) RPC() *rpc.RPCClient {
}
// AdminContext 获取Context
// 每个请求的context都必须是一个新的实例
func (this *ParentAction) AdminContext() context.Context {
if this.ctx != nil {
return this.ctx
}
if this.rpcClient == nil {
rpcClient, err := rpc.SharedRPC()
if err != nil {

View File

@@ -11,6 +11,7 @@ import (
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/gosock/pkg/gosock"
"net"
"net/http"
"os"
@@ -18,6 +19,7 @@ import (
"reflect"
"runtime"
"strings"
"time"
)
// Fail 提示服务器错误信息
@@ -45,6 +47,25 @@ func FailPage(action actions.ActionWrapper, err error) {
var isRPCConnError bool
err, isRPCConnError = rpcerrors.HumanError(err, apiEndpoints, Tea.ConfigFile("api.yaml"))
var apiNodeIsStarting = false
var apiNodeProgress = ""
if isRPCConnError {
// API节点是否正在启动
var sock = gosock.NewTmpSock("edge-api")
reply, err := sock.SendTimeout(&gosock.Command{
Code: "starting",
Params: nil,
}, 1*time.Second)
if err == nil && reply != nil {
var params = maps.NewMap(reply.Params)
if params.GetBool("isStarting") {
apiNodeIsStarting = true
var progressMap = params.GetMap("progress")
apiNodeProgress = progressMap.GetString("description")
}
}
}
action.Object().ResponseWriter.WriteHeader(http.StatusInternalServerError)
if len(action.Object().Request.Header.Get("X-Requested-With")) > 0 {
@@ -90,14 +111,25 @@ func FailPage(action actions.ActionWrapper, err error) {
</head>
<body>
<div style="background: #eee; border: 1px #ccc solid; padding: 10px; font-size: 12px; line-height: 1.8">
` + teaconst.ErrServer + `
`
if apiNodeIsStarting { // API节点正在启动
html += "<div class=\"red\">API节点正在启动请耐心等待完成"
if len(apiNodeProgress) > 0 {
html += "" + apiNodeProgress
}
html += "</div>"
} else {
html += teaconst.ErrServer + `
<div>可以通过查看 <strong><em>$安装目录/logs/run.log</em></strong> 日志文件查看具体的错误提示。</div>
<hr/>
<div class="red">Error: ` + err.Error() + `</div>`
if len(issuesHTML) > 0 {
html += ` <hr/>
if len(issuesHTML) > 0 {
html += ` <hr/>
<div class="red">` + issuesHTML + `</div>`
}
}
action.Object().WriteString(html + `

View File

@@ -22,7 +22,7 @@ func init() {
Post("/options", new(OptionsAction)).
// AccessKeys
Prefix("/admins/accessKeys").
Prefix("/admins/accesskeys").
Get("", new(accesskeys.IndexAction)).
GetPost("/createPopup", new(accesskeys.CreatePopupAction)).
Post("/delete", new(accesskeys.DeleteAction)).

View File

@@ -25,6 +25,7 @@ func (this *DeleteAction) RunPost(params struct {
var apiNode = nodeResp.ApiNode
if apiNode == nil {
this.Success()
return
}
if apiNode.IsOn {
countResp, err := this.RPC().APINodeRPC().CountAllEnabledAndOnAPINodes(this.AdminContext(), &pb.CountAllEnabledAndOnAPINodesRequest{})

View File

@@ -4,10 +4,12 @@ import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
)
type CreatePopupAction struct {
@@ -40,11 +42,11 @@ func (this *CreatePopupAction) RunPost(params struct {
Field("name", params.Name).
Require("请输入API节点名称")
httpConfig := &serverconfigs.HTTPProtocolConfig{}
httpsConfig := &serverconfigs.HTTPSProtocolConfig{}
var httpConfig = &serverconfigs.HTTPProtocolConfig{}
var httpsConfig = &serverconfigs.HTTPSProtocolConfig{}
// 监听地址
listens := []*serverconfigs.NetworkAddressConfig{}
var listens = []*serverconfigs.NetworkAddressConfig{}
err := json.Unmarshal(params.ListensJSON, &listens)
if err != nil {
this.ErrorPage(err)
@@ -64,15 +66,19 @@ func (this *CreatePopupAction) RunPost(params struct {
}
// Rest监听地址
restHTTPConfig := &serverconfigs.HTTPProtocolConfig{}
restHTTPSConfig := &serverconfigs.HTTPSProtocolConfig{}
var restHTTPConfig = &serverconfigs.HTTPProtocolConfig{}
var restHTTPSConfig = &serverconfigs.HTTPSProtocolConfig{}
if params.RestIsOn {
restListens := []*serverconfigs.NetworkAddressConfig{}
var restListens = []*serverconfigs.NetworkAddressConfig{}
err = json.Unmarshal(params.RestListensJSON, &restListens)
if err != nil {
this.ErrorPage(err)
return
}
if len(restListens) == 0 {
this.Fail("请至少添加一个HTTP API监听端口")
return
}
for _, addr := range restListens {
if addr.Protocol.IsHTTPFamily() {
restHTTPConfig.IsOn = true
@@ -82,10 +88,35 @@ func (this *CreatePopupAction) RunPost(params struct {
restHTTPSConfig.Listen = append(restHTTPSConfig.Listen, addr)
}
}
// 是否有端口冲突
var rpcAddresses = []string{}
for _, listen := range listens {
err := listen.Init()
if err != nil {
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
return
}
rpcAddresses = append(rpcAddresses, listen.Addresses()...)
}
for _, listen := range restListens {
err := listen.Init()
if err != nil {
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
return
}
for _, address := range listen.Addresses() {
if lists.ContainsString(rpcAddresses, address) {
this.Fail("HTTP API地址 '" + address + "' 和 GRPC地址冲突请修改后提交")
return
}
}
}
}
// 证书
certIds := []int64{}
var certIds = []int64{}
if len(params.CertIdsJSON) > 0 {
err = json.Unmarshal(params.CertIdsJSON, &certIds)
if err != nil {
@@ -97,7 +128,7 @@ func (this *CreatePopupAction) RunPost(params struct {
this.Fail("请添加至少一个证书")
}
certRefs := []*sslconfigs.SSLCertRef{}
var certRefs = []*sslconfigs.SSLCertRef{}
for _, certId := range certIds {
certRefs = append(certRefs, &sslconfigs.SSLCertRef{
IsOn: true,
@@ -131,7 +162,7 @@ func (this *CreatePopupAction) RunPost(params struct {
}
// 访问地址
accessAddrs := []*serverconfigs.NetworkAddressConfig{}
var accessAddrs = []*serverconfigs.NetworkAddressConfig{}
err = json.Unmarshal(params.AccessAddrsJSON, &accessAddrs)
if err != nil {
this.ErrorPage(err)

View File

@@ -4,10 +4,12 @@ import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
)
@@ -175,12 +177,16 @@ func (this *UpdateAction) RunPost(params struct {
var restHTTPConfig = &serverconfigs.HTTPProtocolConfig{}
var restHTTPSConfig = &serverconfigs.HTTPSProtocolConfig{}
if params.RestIsOn {
restListens := []*serverconfigs.NetworkAddressConfig{}
var restListens = []*serverconfigs.NetworkAddressConfig{}
err = json.Unmarshal(params.RestListensJSON, &restListens)
if err != nil {
this.ErrorPage(err)
return
}
if len(restListens) == 0 {
this.Fail("请至少添加一个HTTP API监听端口")
return
}
for _, addr := range restListens {
if addr.Protocol.IsHTTPFamily() {
restHTTPConfig.IsOn = true
@@ -190,6 +196,31 @@ func (this *UpdateAction) RunPost(params struct {
restHTTPSConfig.Listen = append(restHTTPSConfig.Listen, addr)
}
}
// 是否有端口冲突
var rpcAddresses = []string{}
for _, listen := range listens {
err := listen.Init()
if err != nil {
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
return
}
rpcAddresses = append(rpcAddresses, listen.Addresses()...)
}
for _, listen := range restListens {
err := listen.Init()
if err != nil {
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
return
}
for _, address := range listen.Addresses() {
if lists.ContainsString(rpcAddresses, address) {
this.Fail("HTTP API地址 '" + address + "' 和 GRPC地址冲突请修改后提交")
return
}
}
}
}
// 证书

View File

@@ -5,11 +5,14 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"net"
"regexp"
"strconv"
"strings"
)
@@ -27,7 +30,7 @@ func (this *CreateNodeAction) Init() {
func (this *CreateNodeAction) RunGet(params struct {
ClusterId int64
}) {
leftMenuItems := []maps.Map{
var leftMenuItems = []maps.Map{
{
"name": "单个创建",
"url": "/clusters/cluster/createNode?clusterId=" + strconv.FormatInt(params.ClusterId, 10),
@@ -47,7 +50,7 @@ func (this *CreateNodeAction) RunGet(params struct {
this.ErrorPage(err)
return
}
dnsRouteMaps := []maps.Map{}
var dnsRouteMaps = []maps.Map{}
this.Data["dnsDomainId"] = 0
if clusterDNSResp.Domain != nil {
domainId := clusterDNSResp.Domain.Id
@@ -76,8 +79,8 @@ func (this *CreateNodeAction) RunGet(params struct {
this.ErrorPage(err)
return
}
apiNodes := apiNodesResp.ApiNodes
apiEndpoints := []string{}
var apiNodes = apiNodesResp.ApiNodes
var apiEndpoints = []string{}
for _, apiNode := range apiNodes {
if !apiNode.IsOn {
continue
@@ -86,6 +89,9 @@ func (this *CreateNodeAction) RunGet(params struct {
}
this.Data["apiEndpoints"] = "\"" + strings.Join(apiEndpoints, "\", \"") + "\""
// 安装文件下载
this.Data["installerFiles"] = clusterutils.ListInstallerFiles()
this.Show()
}
@@ -118,7 +124,7 @@ func (this *CreateNodeAction) RunPost(params struct {
}
// IP地址
ipAddresses := []maps.Map{}
var ipAddresses = []maps.Map{}
if len(params.IpAddressesJSON) > 0 {
err := json.Unmarshal(params.IpAddressesJSON, &ipAddresses)
if err != nil {
@@ -127,10 +133,29 @@ func (this *CreateNodeAction) RunPost(params struct {
}
}
if len(ipAddresses) == 0 {
this.Fail("请至少输入一个IP地址")
// 检查Name中是否包含IP
var ipv4Reg = regexp.MustCompile(`\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}`)
var ipMatches = ipv4Reg.FindStringSubmatch(params.Name)
if len(ipMatches) > 0 {
var nodeIP = ipMatches[0]
if net.ParseIP(nodeIP) != nil {
ipAddresses = []maps.Map{
{
"ip": nodeIP,
"canAccess": true,
"isOn": true,
"isUp": true,
},
}
}
}
if len(ipAddresses) == 0 {
this.Fail("请至少输入一个IP地址")
}
}
dnsRouteCodes := []string{}
var dnsRouteCodes = []string{}
if len(params.DnsRoutesJSON) > 0 {
err := json.Unmarshal(params.DnsRoutesJSON, &dnsRouteCodes)
if err != nil {
@@ -140,7 +165,7 @@ func (this *CreateNodeAction) RunPost(params struct {
}
// TODO 检查登录授权
loginInfo := &pb.NodeLogin{
var loginInfo = &pb.NodeLogin{
Id: 0,
Name: "SSH",
Type: "ssh",
@@ -165,7 +190,7 @@ func (this *CreateNodeAction) RunPost(params struct {
this.ErrorPage(err)
return
}
nodeId := createResp.NodeId
var nodeId = createResp.NodeId
// IP地址
var resultIPAddresses = []string{}

View File

@@ -0,0 +1,70 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package cluster
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/types"
"io"
"net/http"
"os"
"regexp"
)
type DownloadInstallerAction struct {
actionutils.ParentAction
}
func (this *DownloadInstallerAction) Init() {
this.Nav("", "", "")
}
func (this *DownloadInstallerAction) RunGet(params struct {
Name string
}) {
if len(params.Name) == 0 {
this.ResponseWriter.WriteHeader(http.StatusNotFound)
this.WriteString("file not found")
return
}
// 检查文件名
// 以防止路径穿越等风险
if !regexp.MustCompile(`^[a-zA-Z0-9.-]+$`).MatchString(params.Name) {
this.ResponseWriter.WriteHeader(http.StatusNotFound)
this.WriteString("file not found")
return
}
var zipFile = Tea.Root + "/edge-api/deploy/" + params.Name
fp, err := os.OpenFile(zipFile, os.O_RDWR, 0444)
if err != nil {
if os.IsNotExist(err) {
this.ResponseWriter.WriteHeader(http.StatusNotFound)
this.WriteString("file not found")
return
}
this.ResponseWriter.WriteHeader(http.StatusInternalServerError)
this.WriteString("file can not be opened")
return
}
defer func() {
_ = fp.Close()
}()
stat, err := fp.Stat()
if err != nil {
this.ResponseWriter.WriteHeader(http.StatusInternalServerError)
this.WriteString("file can not be opened")
return
}
this.AddHeader("Content-Disposition", "attachment; filename=\""+params.Name+"\";")
this.AddHeader("Content-Type", "application/zip")
this.AddHeader("Content-Length", types.String(stat.Size()))
_, _ = io.Copy(this.ResponseWriter, fp)
}

View File

@@ -48,7 +48,7 @@ func (this *CreatePopupAction) RunPost(params struct {
}
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "创建节点分组", createResp.NodeGroupId)
defer this.CreateLog(oplogs.LevelInfo, "创建节点分组 %d", createResp.NodeGroupId)
this.Success()
}

View File

@@ -35,6 +35,7 @@ func init() {
GetPost("/updateNodeSSH", new(UpdateNodeSSHAction)).
GetPost("/installManual", new(InstallManualAction)).
Post("/suggestLoginPorts", new(SuggestLoginPortsAction)).
Get("/downloadInstaller", new(DownloadInstallerAction)).
// 节点相关
Prefix("/clusters/cluster/node").

View File

@@ -10,6 +10,7 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/ipAddresses/ipaddressutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
@@ -300,6 +301,22 @@ func (this *DetailAction) RunGet(params struct {
lnAddrs = []string{}
}
// API节点地址
var apiNodeAddrStrings = []string{}
var apiNodeAddrs = []*serverconfigs.NetworkAddressConfig{}
if len(node.ApiNodeAddrsJSON) > 0 {
err = json.Unmarshal(node.ApiNodeAddrsJSON, &apiNodeAddrs)
if err != nil {
this.ErrorPage(err)
return
}
for _, addr := range apiNodeAddrs {
if addr.Init() == nil {
apiNodeAddrStrings = append(apiNodeAddrStrings, addr.FullAddresses()...)
}
}
}
this.Data["node"] = maps.Map{
"id": node.Id,
"name": node.Name,
@@ -318,6 +335,8 @@ func (this *DetailAction) RunGet(params struct {
"level": node.Level,
"levelInfo": nodeconfigs.FindNodeLevel(int(node.Level)),
"lnAddrs": lnAddrs,
"enableIPLists": node.EnableIPLists,
"apiNodeAddrs": apiNodeAddrStrings,
"status": maps.Map{
"isActive": status.IsActive,
@@ -337,6 +356,8 @@ func (this *DetailAction) RunGet(params struct {
"cacheTotalDiskSize": numberutils.FormatBytes(status.CacheTotalDiskSize),
"cacheTotalMemorySize": numberutils.FormatBytes(status.CacheTotalMemorySize),
"exePath": status.ExePath,
"apiSuccessPercent": status.APISuccessPercent,
"apiAvgCostSeconds": status.APIAvgCostSeconds,
},
"group": groupMap,

View File

@@ -1,12 +1,16 @@
package node
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/clusterutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"path/filepath"
"strings"
)
@@ -32,6 +36,20 @@ func (this *InstallAction) RunGet(params struct {
return
}
// 最近运行目录
var exeRoot = ""
if len(node.StatusJSON) > 0 {
var nodeStatus = &nodeconfigs.NodeStatus{}
err = json.Unmarshal(node.StatusJSON, nodeStatus)
if err == nil {
var exePath = nodeStatus.ExePath
if len(exePath) > 0 {
exeRoot = filepath.Dir(filepath.Dir(exePath))
}
}
}
this.Data["exeRoot"] = exeRoot
// 安装信息
if node.InstallStatus != nil {
this.Data["installStatus"] = maps.Map{
@@ -70,7 +88,7 @@ func (this *InstallAction) RunGet(params struct {
this.ErrorPage(err)
return
}
apiNodes := apiNodesResp.ApiNodes
var apiNodes = apiNodesResp.ApiNodes
apiEndpoints := []string{}
for _, apiNode := range apiNodes {
if !apiNode.IsOn {
@@ -87,6 +105,10 @@ func (this *InstallAction) RunGet(params struct {
nodeMap["secret"] = node.Secret
nodeMap["cluster"] = clusterMap
// 安装文件
var installerFiles = clusterutils.ListInstallerFiles()
this.Data["installerFiles"] = installerFiles
this.Show()
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
@@ -57,9 +58,19 @@ func (this *IndexAction) RunGet(params struct {
}
}
var diskSubDirs = []*serverconfigs.CacheDir{}
if len(node.CacheDiskSubDirsJSON) > 0 {
err = json.Unmarshal(node.CacheDiskSubDirsJSON, &diskSubDirs)
if err != nil {
this.ErrorPage(err)
return
}
}
var nodeMap = this.Data["node"].(maps.Map)
nodeMap["maxCacheDiskCapacity"] = maxCacheDiskCapacity
nodeMap["cacheDiskDir"] = node.CacheDiskDir
nodeMap["cacheDiskSubDirs"] = diskSubDirs
nodeMap["maxCacheMemoryCapacity"] = maxCacheMemoryCapacity
this.Show()
@@ -69,6 +80,7 @@ func (this *IndexAction) RunPost(params struct {
NodeId int64
MaxCacheDiskCapacityJSON []byte
CacheDiskDir string
CacheDiskSubDirsJSON []byte
MaxCacheMemoryCapacityJSON []byte
Must *actions.Must
@@ -105,10 +117,20 @@ func (this *IndexAction) RunPost(params struct {
}
}
if len(params.CacheDiskSubDirsJSON) > 0 {
var cacheSubDirs = []*serverconfigs.CacheDir{}
err := json.Unmarshal(params.CacheDiskSubDirsJSON, &cacheSubDirs)
if err != nil {
this.ErrorPage(err)
return
}
}
_, err := this.RPC().NodeRPC().UpdateNodeCache(this.AdminContext(), &pb.UpdateNodeCacheRequest{
NodeId: params.NodeId,
MaxCacheDiskCapacity: pbMaxCacheDiskCapacity,
CacheDiskDir: params.CacheDiskDir,
CacheDiskSubDirsJSON: params.CacheDiskSubDirsJSON,
MaxCacheMemoryCapacity: pbMaxCacheMemoryCapacity,
})
if err != nil {

View File

@@ -8,6 +8,7 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
@@ -50,6 +51,22 @@ func (this *IndexAction) RunGet(params struct {
}
this.Data["dnsResolverConfig"] = dnsResolverConfig
// API相关
apiConfigResp, err := this.RPC().NodeRPC().FindNodeAPIConfig(this.AdminContext(), &pb.FindNodeAPIConfigRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
var apiNodeAddrs = []*serverconfigs.NetworkAddressConfig{}
if len(apiConfigResp.ApiNodeAddrsJSON) > 0 {
err = json.Unmarshal(apiConfigResp.ApiNodeAddrsJSON, &apiNodeAddrs)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["apiNodeAddrs"] = apiNodeAddrs
this.Show()
}
@@ -59,6 +76,8 @@ func (this *IndexAction) RunPost(params struct {
DnsResolverJSON []byte
ApiNodeAddrsJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
@@ -68,6 +87,7 @@ func (this *IndexAction) RunPost(params struct {
this.Fail("CPU线程数不能小于0")
}
// 系统设置
_, err := this.RPC().NodeRPC().UpdateNodeSystem(this.AdminContext(), &pb.UpdateNodeSystemRequest{
NodeId: params.NodeId,
MaxCPU: params.MaxCPU,
@@ -77,6 +97,7 @@ func (this *IndexAction) RunPost(params struct {
return
}
// DNS解析设置
var dnsResolverConfig = nodeconfigs.DefaultDNSResolverConfig()
err = json.Unmarshal(params.DnsResolverJSON, dnsResolverConfig)
if err != nil {
@@ -98,5 +119,28 @@ func (this *IndexAction) RunPost(params struct {
return
}
// API节点设置
var apiNodeAddrs = []*serverconfigs.NetworkAddressConfig{}
if len(params.ApiNodeAddrsJSON) > 0 {
err = json.Unmarshal(params.ApiNodeAddrsJSON, &apiNodeAddrs)
if err != nil {
this.Fail("API节点地址校验错误" + err.Error())
}
for _, addr := range apiNodeAddrs {
err = addr.Init()
if err != nil {
this.Fail("API节点地址校验错误" + err.Error())
}
}
}
_, err = this.RPC().NodeRPC().UpdateNodeAPIConfig(this.AdminContext(), &pb.UpdateNodeAPIConfigRequest{
NodeId: params.NodeId,
ApiNodeAddrsJSON: params.ApiNodeAddrsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -100,14 +100,15 @@ func (this *UpdateAction) RunGet(params struct {
}
var nodeMap = maps.Map{
"id": node.Id,
"name": node.Name,
"ipAddresses": ipAddressMaps,
"cluster": clusterMap,
"isOn": node.IsOn,
"group": groupMap,
"region": regionMap,
"level": node.Level,
"id": node.Id,
"name": node.Name,
"ipAddresses": ipAddressMaps,
"cluster": clusterMap,
"isOn": node.IsOn,
"group": groupMap,
"region": regionMap,
"level": node.Level,
"enableIPLists": node.EnableIPLists,
}
if node.LnAddrs == nil {
@@ -157,6 +158,7 @@ func (this *UpdateAction) RunPost(params struct {
IsOn bool
Level int32
LnAddrs []string
EnableIPLists bool
Must *actions.Must
}) {
@@ -234,6 +236,7 @@ func (this *UpdateAction) RunPost(params struct {
IsOn: params.IsOn,
Level: params.Level,
LnAddrs: lnAddrs,
EnableIPLists: params.EnableIPLists,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -6,6 +6,7 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
@@ -41,10 +42,23 @@ func (this *IndexAction) RunGet(params struct {
this.Data["dnsName"] = dnsInfoResp.Name
this.Data["nodesAutoSync"] = dnsInfoResp.NodesAutoSync
this.Data["serversAutoSync"] = dnsInfoResp.ServersAutoSync
var domainProviderMap = maps.Map{
"id": 0,
"name": "",
}
if dnsInfoResp.Domain != nil {
this.Data["domainId"] = dnsInfoResp.Domain.Id
this.Data["domainName"] = dnsInfoResp.Domain.Name
if dnsInfoResp.Provider != nil {
domainProviderMap = maps.Map{
"id": dnsInfoResp.Provider.Id,
"name": dnsInfoResp.Provider.Name,
}
}
}
this.Data["domainProvider"] = domainProviderMap
if len(dnsInfoResp.CnameRecords) == 0 {
this.Data["cnameRecords"] = []string{}

View File

@@ -0,0 +1,160 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package globalServerConfig
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "")
this.SecondMenu("globalServerConfig")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
}) {
configResp, err := this.RPC().NodeClusterRPC().FindNodeClusterGlobalServerConfig(this.AdminContext(), &pb.FindNodeClusterGlobalServerConfigRequest{NodeClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
var configJSON = configResp.GlobalServerConfigJSON
var config = serverconfigs.DefaultGlobalServerConfig()
if len(configJSON) > 0 {
err = json.Unmarshal(configJSON, config)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["config"] = config
var httpAllDomainMismatchActionContentHTML = ""
if config.HTTPAll.DomainMismatchAction != nil {
httpAllDomainMismatchActionContentHTML = config.HTTPAll.DomainMismatchAction.Options.GetString("contentHTML")
} else {
httpAllDomainMismatchActionContentHTML = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>404 not found</title>
<style>
* { font-family: Roboto, system-ui, sans-serif; }
h3, p { text-align: center; }
p { color: grey; }
</style>
</head>
<body>
<h3>Error: 404 Page Not Found</h3>
<h3>找不到您要访问的页面。</h3>
<p>原因:找不到当前访问域名对应的网站,请联系网站管理员。</p>
</body>
</html>`
}
this.Data["httpAllDomainMismatchActionContentHTML"] = httpAllDomainMismatchActionContentHTML
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ClusterId int64
HttpAllMatchDomainStrictly bool
HttpAllDomainMismatchActionContentHTML string
HttpAllAllowMismatchDomainsJSON []byte
HttpAllAllowNodeIP bool
HttpAllDefaultDomain string
HttpAccessLogEnableRequestHeaders bool
HttpAccessLogEnableResponseHeaders bool
HttpAccessLogCommonRequestHeadersOnly bool
HttpAccessLogEnableCookies bool
LogRecordServerError bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo("修改集群 %d 全局配置", params.ClusterId)
configResp, err := this.RPC().NodeClusterRPC().FindNodeClusterGlobalServerConfig(this.AdminContext(), &pb.FindNodeClusterGlobalServerConfigRequest{NodeClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
var configJSON = configResp.GlobalServerConfigJSON
var config = serverconfigs.DefaultGlobalServerConfig()
if len(configJSON) > 0 {
err = json.Unmarshal(configJSON, config)
if err != nil {
this.ErrorPage(err)
return
}
}
config.HTTPAll.MatchDomainStrictly = params.HttpAllMatchDomainStrictly
config.HTTPAll.DomainMismatchAction = &serverconfigs.DomainMismatchAction{
Code: serverconfigs.DomainMismatchActionPage,
Options: maps.Map{
"statusCode": 404,
"contentHTML": params.HttpAllDomainMismatchActionContentHTML,
},
}
var allowMismatchDomains = []string{}
if len(params.HttpAllAllowMismatchDomainsJSON) > 0 {
err = json.Unmarshal(params.HttpAllAllowMismatchDomainsJSON, &allowMismatchDomains)
if err != nil {
this.ErrorPage(err)
return
}
}
config.HTTPAll.AllowMismatchDomains = allowMismatchDomains
config.HTTPAll.AllowNodeIP = params.HttpAllAllowNodeIP
config.HTTPAll.DefaultDomain = params.HttpAllDefaultDomain
config.HTTPAccessLog.EnableRequestHeaders = params.HttpAccessLogEnableRequestHeaders
config.HTTPAccessLog.EnableResponseHeaders = params.HttpAccessLogEnableResponseHeaders
config.HTTPAccessLog.CommonRequestHeadersOnly = params.HttpAccessLogCommonRequestHeadersOnly
config.HTTPAccessLog.EnableCookies = params.HttpAccessLogEnableCookies
config.Log.RecordServerError = params.LogRecordServerError
err = config.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())
return
}
configJSON, err = json.Marshal(config)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().NodeClusterRPC().UpdateNodeClusterGlobalServerConfig(this.AdminContext(), &pb.UpdateNodeClusterGlobalServerConfigRequest{
NodeClusterId: params.ClusterId,
GlobalServerConfigJSON: configJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,52 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package health
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"net"
"strings"
)
type CheckDomainAction struct {
actionutils.ParentAction
}
func (this *CheckDomainAction) RunPost(params struct {
Host string
ClusterId int64
}) {
this.Data["isOk"] = true // 默认为TRUE
var host = params.Host
if len(host) > 0 &&
!strings.Contains(host, "{") /** 包含变量 **/ {
h, _, err := net.SplitHostPort(host)
if err == nil && len(h) > 0 {
host = h
}
// 是否为IP
if net.ParseIP(host) != nil {
this.Success()
return
}
host = strings.ToLower(host)
resp, err := this.RPC().ServerRPC().CheckServerNameDuplicationInNodeCluster(this.AdminContext(), &pb.CheckServerNameDuplicationInNodeClusterRequest{
NodeClusterId: params.ClusterId,
ServerNames: []string{host},
SupportWildcard: true,
})
if err != nil {
this.ErrorPage(err)
return
}
if len(resp.DuplicatedServerNames) == 0 {
this.Data["isOk"] = false
}
}
this.Success()
}

View File

@@ -1,6 +1,7 @@
package settings
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
@@ -44,7 +45,7 @@ func (this *IndexAction) RunGet(params struct {
this.ErrorPage(err)
return
}
grant := grantResp.NodeGrant
var grant = grantResp.NodeGrant
if grant != nil {
grantMap = maps.Map{
"id": grant.Id,
@@ -65,13 +66,40 @@ func (this *IndexAction) RunGet(params struct {
}
this.Data["timeZoneLocation"] = nodeconfigs.FindTimeZoneLocation(cluster.TimeZone)
// 时钟
var clockConfig = nodeconfigs.DefaultClockConfig()
if len(cluster.ClockJSON) > 0 {
err = json.Unmarshal(cluster.ClockJSON, clockConfig)
if err != nil {
this.ErrorPage(err)
return
}
if clockConfig == nil {
clockConfig = nodeconfigs.DefaultClockConfig()
}
}
// SSH参数
var sshParams = nodeconfigs.DefaultSSHParams()
if len(cluster.SshParamsJSON) > 0 {
err = json.Unmarshal(cluster.SshParamsJSON, sshParams)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["cluster"] = maps.Map{
"id": cluster.Id,
"name": cluster.Name,
"installDir": cluster.InstallDir,
"timeZone": cluster.TimeZone,
"nodeMaxThreads": cluster.NodeMaxThreads,
"autoOpenPorts": cluster.AutoOpenPorts,
"id": cluster.Id,
"name": cluster.Name,
"installDir": cluster.InstallDir,
"timeZone": cluster.TimeZone,
"nodeMaxThreads": cluster.NodeMaxThreads,
"autoOpenPorts": cluster.AutoOpenPorts,
"clock": clockConfig,
"autoRemoteStart": cluster.AutoRemoteStart,
"autoInstallNftables": cluster.AutoInstallNftables,
"sshParams": sshParams,
}
// 默认值
@@ -84,13 +112,19 @@ func (this *IndexAction) RunGet(params struct {
// RunPost 保存设置
func (this *IndexAction) RunPost(params struct {
ClusterId int64
Name string
GrantId int64
InstallDir string
TimeZone string
NodeMaxThreads int32
AutoOpenPorts bool
ClusterId int64
Name string
GrantId int64
SshParamsPort int
InstallDir string
TimeZone string
NodeMaxThreads int32
AutoOpenPorts bool
ClockAutoSync bool
ClockServer string
ClockCheckChrony bool
AutoRemoteStart bool
AutoInstallNftables bool
Must *actions.Must
}) {
@@ -108,14 +142,44 @@ func (this *IndexAction) RunPost(params struct {
Lte(int64(nodeconfigs.DefaultMaxThreadsMax), "单节点最大线程数最大值不能大于"+types.String(nodeconfigs.DefaultMaxThreadsMax))
}
_, err := this.RPC().NodeClusterRPC().UpdateNodeCluster(this.AdminContext(), &pb.UpdateNodeClusterRequest{
NodeClusterId: params.ClusterId,
Name: params.Name,
NodeGrantId: params.GrantId,
InstallDir: params.InstallDir,
TimeZone: params.TimeZone,
NodeMaxThreads: params.NodeMaxThreads,
AutoOpenPorts: params.AutoOpenPorts,
// ssh
var sshParams = nodeconfigs.DefaultSSHParams()
sshParams.Port = params.SshParamsPort
sshParamsJSON, err := json.Marshal(sshParams)
if err != nil {
this.ErrorPage(err)
return
}
// clock
var clockConfig = nodeconfigs.DefaultClockConfig()
clockConfig.AutoSync = params.ClockAutoSync
clockConfig.Server = params.ClockServer
clockConfig.CheckChrony = params.ClockCheckChrony
clockConfigJSON, err := json.Marshal(clockConfig)
if err != nil {
this.ErrorPage(err)
return
}
err = clockConfig.Init()
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().NodeClusterRPC().UpdateNodeCluster(this.AdminContext(), &pb.UpdateNodeClusterRequest{
NodeClusterId: params.ClusterId,
Name: params.Name,
NodeGrantId: params.GrantId,
InstallDir: params.InstallDir,
TimeZone: params.TimeZone,
NodeMaxThreads: params.NodeMaxThreads,
AutoOpenPorts: params.AutoOpenPorts,
ClockJSON: clockConfigJSON,
AutoRemoteStart: params.AutoRemoteStart,
AutoInstallNftables: params.AutoInstallNftables,
SshParamsJSON: sshParamsJSON,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -6,6 +6,7 @@ import (
ddosProtection "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/ddos-protection"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/dns"
firewallActions "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/firewall-actions"
globalServerConfig "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/global-server-config"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/health"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/metrics"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/services"
@@ -28,6 +29,7 @@ func init() {
// 健康检查
GetPost("/health", new(health.IndexAction)).
GetPost("/health/runPopup", new(health.RunPopupAction)).
Post("/health/checkDomain", new(health.CheckDomainAction)).
// 缓存
GetPost("/cache", new(cache.IndexAction)).
@@ -71,6 +73,10 @@ func init() {
GetPost("", new(ddosProtection.IndexAction)).
GetPost("/status", new(ddosProtection.StatusAction)).
// 全局服务配置
Prefix("/clusters/cluster/settings/global-server-config").
GetPost("", new(globalServerConfig.IndexAction)).
//
EndAll()
})

View File

@@ -60,7 +60,7 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
var tabbar = actionutils.NewTabbar()
tabbar.Add("集群列表", "", "/clusters", "", false)
if teaconst.IsPlus {
tabbar.Add("集群看板", "", "/clusters/cluster/boards?clusterId="+clusterIdString, "board", selectedTabbar == "board")
tabbar.Add("集群看板", "", "/clusters/cluster/boards?clusterId="+clusterIdString, "chart line area", selectedTabbar == "board")
}
tabbar.Add("集群节点", "", "/clusters/cluster/nodes?clusterId="+clusterIdString, "server", selectedTabbar == "node")
tabbar.Add("集群设置", "", "/clusters/cluster/settings?clusterId="+clusterIdString, "setting", selectedTabbar == "setting")
@@ -153,6 +153,12 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, info *pb.F
"isOn": info != nil && info.HasDDoSProtection,
})
items = append(items, maps.Map{
"name": "服务设置",
"url": "/clusters/cluster/settings/global-server-config?clusterId=" + clusterId,
"isActive": selectedItem == "globalServerConfig",
})
items = append(items, maps.Map{
"name": "-",
})

View File

@@ -0,0 +1,66 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package clusterutils
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/iwind/TeaGo/Tea"
"path/filepath"
"regexp"
"sort"
"strings"
)
type installerFile struct {
Name string `json:"name"`
OS string `json:"os"`
Arch string `json:"arch"`
Version string `json:"version"`
}
func ListInstallerFiles() []*installerFile {
var dir = Tea.Root + "/edge-api/deploy"
matches, err := filepath.Glob(dir + "/edge-node-*.zip")
if err != nil {
return nil
}
var result = []*installerFile{}
var reg = regexp.MustCompile(`^edge-node-(\w+)-(\w+)-v([\w.]+)\.zip$`)
for _, match := range matches {
var baseName = filepath.Base(match)
var subMatches = reg.FindStringSubmatch(baseName)
if len(subMatches) >= 4 {
var osName = subMatches[1]
if len(osName) > 0 {
osName = strings.ToUpper(osName[:1]) + osName[1:]
}
var arch = subMatches[2]
if arch == "amd64" {
arch = "x86_64"
}
var version = subMatches[3]
if version != teaconst.Version { // 只能下载当前版本
continue
}
result = append(result, &installerFile{
Name: subMatches[0],
OS: osName,
Arch: arch,
Version: version,
})
}
}
// 排序将x86_64排在最上面
if len(result) > 0 {
sort.Slice(result, func(i, j int) bool {
return result[i].Arch == "x86_64"
})
}
return result
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
)
@@ -55,14 +56,19 @@ func (this *CreateAction) RunPost(params struct {
// WAF策略
HttpFirewallPolicyId int64
// 服务配置
MatchDomainStrictly bool
// SSH相关
GrantId int64
InstallDir string
SystemdServiceIsOn bool
GrantId int64
InstallDir string
SystemdServiceIsOn bool
AutoInstallNftables bool
// DNS相关
DnsDomainId int64
DnsName string
DnsTTL int32
Must *actions.Must
}) {
@@ -92,8 +98,17 @@ func (this *CreateAction) RunPost(params struct {
// TODO 检查DnsDomainId的有效性
// 全局服务配置
var globalServerConfig = serverconfigs.DefaultGlobalServerConfig()
globalServerConfig.HTTPAll.MatchDomainStrictly = params.MatchDomainStrictly
globalServerConfigJSON, err := json.Marshal(globalServerConfig)
if err != nil {
this.ErrorPage(err)
return
}
// 系统服务
systemServices := map[string]interface{}{}
var systemServices = map[string]any{}
if params.SystemdServiceIsOn {
systemServices[nodeconfigs.SystemServiceTypeSystemd] = &nodeconfigs.SystemdServiceConfig{
IsOn: true,
@@ -106,14 +121,17 @@ func (this *CreateAction) RunPost(params struct {
}
createResp, err := this.RPC().NodeClusterRPC().CreateNodeCluster(this.AdminContext(), &pb.CreateNodeClusterRequest{
Name: params.Name,
NodeGrantId: params.GrantId,
InstallDir: params.InstallDir,
DnsDomainId: params.DnsDomainId,
DnsName: params.DnsName,
HttpCachePolicyId: params.CachePolicyId,
HttpFirewallPolicyId: params.HttpFirewallPolicyId,
SystemServicesJSON: systemServicesJSON,
Name: params.Name,
NodeGrantId: params.GrantId,
InstallDir: params.InstallDir,
DnsDomainId: params.DnsDomainId,
DnsName: params.DnsName,
DnsTTL: params.DnsTTL,
HttpCachePolicyId: params.CachePolicyId,
HttpFirewallPolicyId: params.HttpFirewallPolicyId,
SystemServicesJSON: systemServicesJSON,
GlobalServerConfigJSON: globalServerConfigJSON,
AutoInstallNftables: params.AutoInstallNftables,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -11,7 +11,7 @@ type IndexAction struct {
}
func (this *IndexAction) Init() {
this.Nav("", "", "region")
this.Nav("", "", "index")
}
func (this *IndexAction) RunGet(params struct{}) {
@@ -20,7 +20,7 @@ func (this *IndexAction) RunGet(params struct{}) {
this.ErrorPage(err)
return
}
regionMaps := []maps.Map{}
var regionMaps = []maps.Map{}
for _, region := range regionsResp.NodeRegions {
countNodesResp, err := this.RPC().NodeRPC().CountAllEnabledNodesWithNodeRegionId(this.AdminContext(), &pb.CountAllEnabledNodesWithNodeRegionIdRequest{NodeRegionId: region.Id})
if err != nil {

View File

@@ -18,9 +18,13 @@ func init() {
GetPost("/updatePopup", new(UpdatePopupAction)).
Post("/delete", new(DeleteAction)).
Post("/sort", new(SortAction)).
Get("/nodes", new(NodesAction)).
GetPost("/updateNodeRegionPopup", new(UpdateNodeRegionPopupAction)).
//
GetPost("/selectPopup", new(SelectPopupAction)).
GetPost("/prices", new(PricesAction)).
GetPost("/updatePricePopup", new(UpdatePricePopupAction)).
//
EndAll()
})
}

View File

@@ -1,50 +0,0 @@
package items
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions/regionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct{}) {
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
Name string
BitsFrom int64
BitsTo int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
params.Must.
Field("name", params.Name).
Require("请输入名称").
Field("bitsFrom", params.BitsFrom).
Gte(0, "请输入不小于0的整数").
Field("bitsTo", params.BitsTo).
Gte(0, "请输入不小于0的整数")
createResp, err := this.RPC().NodePriceItemRPC().CreateNodePriceItem(this.AdminContext(), &pb.CreateNodePriceItemRequest{
Name: params.Name,
Type: regionutils.PriceTypeTraffic,
BitsFrom: params.BitsFrom * 1000 * 1000,
BitsTo: params.BitsTo * 1000 * 1000,
})
if err != nil {
this.ErrorPage(err)
return
}
defer this.CreateLogInfo("创建流量价格项目", createResp.NodePriceItemId)
this.Success()
}

View File

@@ -1,24 +0,0 @@
package items
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
ItemId int64
}) {
defer this.CreateLogInfo("删除流量价格项目 %d", params.ItemId)
_, err := this.RPC().NodePriceItemRPC().DeleteNodePriceItem(this.AdminContext(), &pb.DeleteNodePriceItemRequest{NodePriceItemId: params.ItemId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -1,21 +0,0 @@
package items
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
Data("teaMenu", "clusters").
Data("teaSubMenu", "region").
Prefix("/clusters/regions/items").
GetPost("/createPopup", new(CreatePopupAction)).
GetPost("/updatePopup", new(UpdatePopupAction)).
Post("/delete", new(DeleteAction)).
EndAll()
})
}

View File

@@ -1,73 +0,0 @@
package items
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
ItemId int64
}) {
itemResp, err := this.RPC().NodePriceItemRPC().FindEnabledNodePriceItem(this.AdminContext(), &pb.FindEnabledNodePriceItemRequest{NodePriceItemId: params.ItemId})
if err != nil {
this.ErrorPage(err)
return
}
item := itemResp.NodePriceItem
if item == nil {
this.NotFound("nodePriceItem", params.ItemId)
return
}
this.Data["item"] = maps.Map{
"id": item.Id,
"name": item.Name,
"bitsFrom": item.BitsFrom,
"bitsTo": item.BitsTo,
}
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
ItemId int64
Name string
BitsFrom int64
BitsTo int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo("修改流量价格项目", params.ItemId)
params.Must.
Field("name", params.Name).
Require("请输入名称").
Field("bitsFrom", params.BitsFrom).
Gte(0, "请输入不小于0的整数").
Field("bitsTo", params.BitsTo).
Gte(0, "请输入不小于0的整数")
_, err := this.RPC().NodePriceItemRPC().UpdateNodePriceItem(this.AdminContext(), &pb.UpdateNodePriceItemRequest{
NodePriceItemId: params.ItemId,
Name: params.Name,
BitsFrom: params.BitsFrom * 1000 * 1000,
BitsTo: params.BitsTo * 1000 * 1000,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,92 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package regions
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type NodesAction struct {
actionutils.ParentAction
}
func (this *NodesAction) Init() {
this.Nav("", "", "node")
}
func (this *NodesAction) RunGet(params struct {
RegionId int64
}) {
this.Data["regionId"] = params.RegionId
// 所有区域
regionsResp, err := this.RPC().NodeRegionRPC().FindAllAvailableNodeRegions(this.AdminContext(), &pb.FindAllAvailableNodeRegionsRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var regionMaps = []maps.Map{}
for _, region := range regionsResp.NodeRegions {
regionMaps = append(regionMaps, maps.Map{
"id": region.Id,
"name": region.Name,
})
}
this.Data["regions"] = regionMaps
// 节点数量
countResp, err := this.RPC().NodeRPC().CountAllNodeRegionInfo(this.AdminContext(), &pb.CountAllNodeRegionInfoRequest{NodeRegionId: params.RegionId})
if err != nil {
this.ErrorPage(err)
return
}
var page = this.NewPage(countResp.Count)
this.Data["page"] = page.AsHTML()
// 节点列表
var hasNodesWithoutRegion = false
nodesResp, err := this.RPC().NodeRPC().ListNodeRegionInfo(this.AdminContext(), &pb.ListNodeRegionInfoRequest{
NodeRegionId: params.RegionId,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var nodeMaps = []maps.Map{}
for _, node := range nodesResp.InfoList {
// region
var regionMap maps.Map
if node.NodeRegion != nil {
regionMap = maps.Map{
"id": node.NodeRegion.Id,
"name": node.NodeRegion.Name,
}
} else {
hasNodesWithoutRegion = true
}
// cluster
var clusterMap maps.Map
if node.NodeCluster != nil {
clusterMap = maps.Map{
"id": node.NodeCluster.Id,
"name": node.NodeCluster.Name,
}
}
nodeMaps = append(nodeMaps, maps.Map{
"id": node.Id,
"name": node.Name,
"region": regionMap,
"cluster": clusterMap,
})
}
this.Data["nodes"] = nodeMaps
this.Data["hasNodesWithoutRegion"] = hasNodesWithoutRegion
this.Show()
}

View File

@@ -1,86 +0,0 @@
package regions
import (
"encoding/json"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions/regionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type PricesAction struct {
actionutils.ParentAction
}
func (this *PricesAction) Init() {
this.Nav("", "", "price")
}
func (this *PricesAction) RunGet(params struct{}) {
// 所有价格项目
itemsResp, err := this.RPC().NodePriceItemRPC().FindAllAvailableNodePriceItems(this.AdminContext(), &pb.FindAllAvailableNodePriceItemsRequest{Type: regionutils.PriceTypeTraffic})
if err != nil {
this.ErrorPage(err)
return
}
itemMaps := []maps.Map{}
for _, item := range itemsResp.NodePriceItems {
itemMaps = append(itemMaps, maps.Map{
"id": item.Id,
"name": item.Name,
"bitsFromString": this.formatBits(item.BitsFrom),
"bitsToString": this.formatBits(item.BitsTo),
})
}
this.Data["items"] = itemMaps
// 所有区域
regionsResp, err := this.RPC().NodeRegionRPC().FindAllEnabledNodeRegions(this.AdminContext(), &pb.FindAllEnabledNodeRegionsRequest{})
if err != nil {
this.ErrorPage(err)
return
}
regionMaps := []maps.Map{}
for _, region := range regionsResp.NodeRegions {
pricesMap := map[string]float32{}
if len(region.PricesJSON) > 0 {
err = json.Unmarshal(region.PricesJSON, &pricesMap)
if err != nil {
this.ErrorPage(err)
return
}
}
regionMaps = append(regionMaps, maps.Map{
"id": region.Id,
"isOn": region.IsOn,
"name": region.Name,
"prices": pricesMap,
})
}
this.Data["regions"] = regionMaps
this.Show()
}
func (this *PricesAction) formatBits(bits int64) string {
sizeHuman := ""
if bits < 1000 {
sizeHuman = numberutils.FormatInt64(bits) + "BPS"
} else if bits < 1_000_000 {
sizeHuman = fmt.Sprintf("%.2fKBPS", float64(bits)/1000)
} else if bits < 1_000_000_000 {
sizeHuman = fmt.Sprintf("%.2fMBPS", float64(bits)/1000/1000)
} else if bits < 1_000_000_000_000 {
sizeHuman = fmt.Sprintf("%.2fGBPS", float64(bits)/1000/1000/1000)
} else if bits < 1_000_000_000_000_000 {
sizeHuman = fmt.Sprintf("%.2fTBPS", float64(bits)/1000/1000/1000/1000)
} else if bits < 1_000_000_000_000_000_000 {
sizeHuman = fmt.Sprintf("%.2fPBPS", float64(bits)/1000/1000/1000/1000/1000)
} else {
sizeHuman = fmt.Sprintf("%.2fEBPS", float64(bits)/1000/1000/1000/1000/1000/1000)
}
return sizeHuman
}

View File

@@ -1,5 +0,0 @@
package regionutils
const (
PriceTypeTraffic = "traffic"
)

View File

@@ -40,12 +40,18 @@ func (this *SelectPopupAction) RunPost(params struct {
Must *actions.Must
CSRF *actionutils.CSRF
}) {
if params.RegionId <= 0 {
this.Data["region"] = nil
this.Success()
return
}
regionResp, err := this.RPC().NodeRegionRPC().FindEnabledNodeRegion(this.AdminContext(), &pb.FindEnabledNodeRegionRequest{NodeRegionId: params.RegionId})
if err != nil {
this.ErrorPage(err)
return
}
region := regionResp.NodeRegion
var region = regionResp.NodeRegion
if region == nil {
this.NotFound("nodeRegion", params.RegionId)
return

View File

@@ -0,0 +1,97 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package regions
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdateNodeRegionPopupAction struct {
actionutils.ParentAction
}
func (this *UpdateNodeRegionPopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdateNodeRegionPopupAction) RunGet(params struct {
NodeId int64
RegionId int64
}) {
// node
nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
var node = nodeResp.Node
if node == nil {
this.NotFound("node", params.NodeId)
return
}
this.Data["node"] = maps.Map{
"id": node.Id,
"name": node.Name,
}
// region
this.Data["region"] = maps.Map{
"id": 0,
"name": "",
}
if params.RegionId > 0 {
regionResp, err := this.RPC().NodeRegionRPC().FindEnabledNodeRegion(this.AdminContext(), &pb.FindEnabledNodeRegionRequest{NodeRegionId: params.RegionId})
if err != nil {
this.ErrorPage(err)
return
}
var region = regionResp.NodeRegion
if region != nil {
this.Data["region"] = maps.Map{
"id": region.Id,
"name": region.Name,
}
}
}
// all regions
regionsResp, err := this.RPC().NodeRegionRPC().FindAllAvailableNodeRegions(this.AdminContext(), &pb.FindAllAvailableNodeRegionsRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var regionMaps = []maps.Map{}
for _, region := range regionsResp.NodeRegions {
regionMaps = append(regionMaps, maps.Map{
"id": region.Id,
"name": region.Name,
})
}
this.Data["regions"] = regionMaps
this.Show()
}
func (this *UpdateNodeRegionPopupAction) RunPost(params struct {
NodeId int64
RegionId int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo("修改节点 %d 区域到 %d", params.RegionId)
_, err := this.RPC().NodeRPC().UpdateNodeRegionInfo(this.AdminContext(), &pb.UpdateNodeRegionInfoRequest{
NodeId: params.NodeId,
NodeRegionId: params.RegionId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -1,94 +0,0 @@
package regions
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdatePricePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePricePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePricePopupAction) RunGet(params struct {
RegionId int64
ItemId int64
}) {
// 区域
regionResp, err := this.RPC().NodeRegionRPC().FindEnabledNodeRegion(this.AdminContext(), &pb.FindEnabledNodeRegionRequest{NodeRegionId: params.RegionId})
if err != nil {
this.ErrorPage(err)
return
}
region := regionResp.NodeRegion
if region == nil {
this.NotFound("nodeRegion", params.RegionId)
return
}
this.Data["region"] = maps.Map{
"id": region.Id,
"isOn": region.IsOn,
"name": region.Name,
}
// 当前价格
pricesMap := map[string]float32{}
if len(region.PricesJSON) > 0 {
err = json.Unmarshal(region.PricesJSON, &pricesMap)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["price"] = pricesMap[numberutils.FormatInt64(params.ItemId)]
// 价格项
itemResp, err := this.RPC().NodePriceItemRPC().FindEnabledNodePriceItem(this.AdminContext(), &pb.FindEnabledNodePriceItemRequest{NodePriceItemId: params.ItemId})
if err != nil {
this.ErrorPage(err)
return
}
item := itemResp.NodePriceItem
if item == nil {
this.NotFound("nodePriceItem", params.ItemId)
return
}
this.Data["item"] = maps.Map{
"id": item.Id,
"name": item.Name,
"bitsFrom": item.BitsFrom,
"bitsTo": item.BitsTo,
}
this.Show()
}
func (this *UpdatePricePopupAction) RunPost(params struct {
RegionId int64
ItemId int64
Price float32
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo("修改区域 %d-价格项 %d 的价格", params.RegionId, params.ItemId)
_, err := this.RPC().NodeRegionRPC().UpdateNodeRegionPrice(this.AdminContext(), &pb.UpdateNodeRegionPriceRequest{
NodeRegionId: params.RegionId,
NodeItemId: params.ItemId,
Price: params.Price,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -76,16 +76,14 @@ func (this *IndexAction) RunPost(params struct{}) {
this.Data["dashboard"] = maps.Map{
"defaultClusterId": resp.DefaultNodeClusterId,
"countServers": resp.CountServers,
"countNodeClusters": resp.CountNodeClusters,
"countNodes": resp.CountNodes,
"countOfflineNodes": resp.CountOfflineNodes,
"countUsers": resp.CountUsers,
"countAPINodes": resp.CountAPINodes,
"countOfflineAPINodes": resp.CountOfflineAPINodes,
"countDBNodes": resp.CountDBNodes,
"countUserNodes": resp.CountUserNodes,
"countOfflineUserNodes": resp.CountOfflineUserNodes,
"countServers": resp.CountServers,
"countNodeClusters": resp.CountNodeClusters,
"countNodes": resp.CountNodes,
"countOfflineNodes": resp.CountOfflineNodes,
"countUsers": resp.CountUsers,
"countAPINodes": resp.CountAPINodes,
"countOfflineAPINodes": resp.CountOfflineAPINodes,
"countDBNodes": resp.CountDBNodes,
"canGoServers": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeServer),
"canGoNodes": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeNode),

View File

@@ -43,7 +43,7 @@ func (this *ClusterAction) RunGet(params struct {
}
var defaultRoute = dnsResp.DefaultRoute
domainName := ""
dnsMap := maps.Map{
var dnsMap = maps.Map{
"dnsName": dnsResp.Name,
"domainId": 0,
"domainName": "",
@@ -76,14 +76,14 @@ func (this *ClusterAction) RunGet(params struct {
this.ErrorPage(err)
return
}
nodeMaps := []maps.Map{}
var nodeMaps = []maps.Map{}
for _, node := range nodesResp.Nodes {
if len(node.Routes) > 0 {
for _, route := range node.Routes {
// 检查是否已解析
isResolved := false
var isResolved = false
if cluster.DnsDomainId > 0 && len(cluster.DnsName) > 0 && len(node.IpAddr) > 0 {
recordType := "A"
var recordType = "A"
if utils.IsIPv6(node.IpAddr) {
recordType = "AAAA"
}
@@ -102,9 +102,10 @@ func (this *ClusterAction) RunGet(params struct {
}
nodeMaps = append(nodeMaps, maps.Map{
"id": node.Id,
"name": node.Name,
"ipAddr": node.IpAddr,
"id": node.Id,
"name": node.Name,
"ipAddr": node.IpAddr,
"ipAddrId": node.NodeIPAddressId,
"route": maps.Map{
"name": route.Name,
"code": route.Code,
@@ -117,7 +118,7 @@ func (this *ClusterAction) RunGet(params struct {
// 默认线路
var isResolved = false
if len(defaultRoute) > 0 {
recordType := "A"
var recordType = "A"
if utils.IsIPv6(node.IpAddr) {
recordType = "AAAA"
}
@@ -135,9 +136,10 @@ func (this *ClusterAction) RunGet(params struct {
isResolved = checkResp.IsOk
}
nodeMaps = append(nodeMaps, maps.Map{
"id": node.Id,
"name": node.Name,
"ipAddr": node.IpAddr,
"id": node.Id,
"name": node.Name,
"ipAddr": node.IpAddr,
"ipAddrId": node.NodeIPAddressId,
"route": maps.Map{
"name": "",
"code": "",
@@ -155,7 +157,7 @@ func (this *ClusterAction) RunGet(params struct {
this.ErrorPage(err)
return
}
serverMaps := []maps.Map{}
var serverMaps = []maps.Map{}
for _, server := range serversResp.Servers {
// 检查是否已解析
isResolved := false
@@ -198,7 +200,7 @@ func (this *ClusterAction) RunGet(params struct {
this.ErrorPage(err)
return
}
issueMaps := []maps.Map{}
var issueMaps = []maps.Map{}
for _, issue := range issuesResp.Issues {
issueMaps = append(issueMaps, maps.Map{
"target": issue.Target,
@@ -218,7 +220,7 @@ func (this *ClusterAction) RunGet(params struct {
this.ErrorPage(err)
return
}
taskMaps := []maps.Map{}
var taskMaps = []maps.Map{}
for _, task := range resp.DnsTasks {
var clusterMap maps.Map = nil
var nodeMap maps.Map = nil

View File

@@ -14,7 +14,7 @@ type DomainOptionsAction struct {
func (this *DomainOptionsAction) RunPost(params struct {
ProviderId int64
}) {
domainsResp, err := this.RPC().DNSDomainRPC().FindAllEnabledBasicDNSDomainsWithDNSProviderId(this.AdminContext(), &pb.FindAllEnabledBasicDNSDomainsWithDNSProviderIdRequest{
domainsResp, err := this.RPC().DNSDomainRPC().FindAllBasicDNSDomainsWithDNSProviderId(this.AdminContext(), &pb.FindAllBasicDNSDomainsWithDNSProviderIdRequest{
DnsProviderId: params.ProviderId,
})
if err != nil {

View File

@@ -18,7 +18,7 @@ func (this *ClustersPopupAction) RunGet(params struct {
DomainId int64
}) {
// 域名信息
domainResp, err := this.RPC().DNSDomainRPC().FindEnabledBasicDNSDomain(this.AdminContext(), &pb.FindEnabledBasicDNSDomainRequest{
domainResp, err := this.RPC().DNSDomainRPC().FindBasicDNSDomain(this.AdminContext(), &pb.FindBasicDNSDomainRequest{
DnsDomainId: params.DomainId,
})
if err != nil {

View File

@@ -17,9 +17,9 @@ func ValidateDomainFormat(domain string) bool {
if piece == "-" ||
strings.HasPrefix(piece, "-") ||
strings.HasSuffix(piece, "-") ||
//strings.Contains(piece, "--") ||
len(piece) > 63 ||
!regexp.MustCompile(`^[a-z0-9-]+$`).MatchString(piece) {
// 我们允许中文、大写字母、下划线,防止有些特殊场景下需要
!regexp.MustCompile(`^[\p{Han}_a-zA-Z0-9-]+$`).MatchString(piece) {
return false
}
}
@@ -71,13 +71,17 @@ func ValidateRecordName(name string) bool {
}
pieces := strings.Split(name, ".")
for _, piece := range pieces {
for index, piece := range pieces {
if index == 0 && piece == "*" {
continue
}
if piece == "-" ||
strings.HasPrefix(piece, "-") ||
strings.HasSuffix(piece, "-") ||
//strings.Contains(piece, "--") ||
len(piece) > 63 ||
!regexp.MustCompile(`^[_a-z0-9-]+$`).MatchString(piece) {
// 我们允许中文、大写字母、下划线,防止有些特殊场景下需要
!regexp.MustCompile(`^[\p{Han}_a-zA-Z0-9-]+$`).MatchString(piece) {
return false
}
}

View File

@@ -19,7 +19,7 @@ func (this *NodesPopupAction) RunGet(params struct {
DomainId int64
}) {
// 域名信息
domainResp, err := this.RPC().DNSDomainRPC().FindEnabledBasicDNSDomain(this.AdminContext(), &pb.FindEnabledBasicDNSDomainRequest{
domainResp, err := this.RPC().DNSDomainRPC().FindBasicDNSDomain(this.AdminContext(), &pb.FindBasicDNSDomainRequest{
DnsDomainId: params.DomainId,
})
if err != nil {

View File

@@ -25,12 +25,12 @@ func (this *SelectPopupAction) RunGet(params struct {
// 域名信息
if params.DomainId > 0 {
domainResp, err := this.RPC().DNSDomainRPC().FindEnabledDNSDomain(this.AdminContext(), &pb.FindEnabledDNSDomainRequest{DnsDomainId: params.DomainId})
domainResp, err := this.RPC().DNSDomainRPC().FindDNSDomain(this.AdminContext(), &pb.FindDNSDomainRequest{DnsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
domain := domainResp.DnsDomain
var domain = domainResp.DnsDomain
if domain != nil {
this.Data["domainId"] = domain.Id
this.Data["domainName"] = domain.Name
@@ -53,7 +53,7 @@ func (this *SelectPopupAction) RunGet(params struct {
this.ErrorPage(err)
return
}
providerTypeMaps := []maps.Map{}
var providerTypeMaps = []maps.Map{}
for _, providerType := range providerTypesResp.ProviderTypes {
providerTypeMaps = append(providerTypeMaps, maps.Map{
"name": providerType.Name,
@@ -73,15 +73,29 @@ func (this *SelectPopupAction) RunPost(params struct {
}) {
this.Data["domainId"] = params.DomainId
this.Data["domainName"] = ""
this.Data["providerName"] = ""
if params.DomainId > 0 {
domainResp, err := this.RPC().DNSDomainRPC().FindEnabledDNSDomain(this.AdminContext(), &pb.FindEnabledDNSDomainRequest{DnsDomainId: params.DomainId})
domainResp, err := this.RPC().DNSDomainRPC().FindDNSDomain(this.AdminContext(), &pb.FindDNSDomainRequest{DnsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
if domainResp.DnsDomain != nil {
this.Data["domainName"] = domainResp.DnsDomain.Name
// 服务商名称
var providerId = domainResp.DnsDomain.ProviderId
if providerId > 0 {
providerResp, err := this.RPC().DNSProviderRPC().FindEnabledDNSProvider(this.AdminContext(), &pb.FindEnabledDNSProviderRequest{DnsProviderId: providerId})
if err != nil {
this.ErrorPage(err)
return
}
if providerResp.DnsProvider != nil {
this.Data["providerName"] = providerResp.DnsProvider.Name
}
}
} else {
this.Data["domainId"] = 0
}

View File

@@ -18,7 +18,7 @@ func (this *ServersPopupAction) RunGet(params struct {
DomainId int64
}) {
// 域名信息
domainResp, err := this.RPC().DNSDomainRPC().FindEnabledBasicDNSDomain(this.AdminContext(), &pb.FindEnabledBasicDNSDomainRequest{
domainResp, err := this.RPC().DNSDomainRPC().FindBasicDNSDomain(this.AdminContext(), &pb.FindBasicDNSDomainRequest{
DnsDomainId: params.DomainId,
})
if err != nil {

View File

@@ -21,7 +21,7 @@ func (this *UpdatePopupAction) Init() {
func (this *UpdatePopupAction) RunGet(params struct {
DomainId int64
}) {
domainResp, err := this.RPC().DNSDomainRPC().FindEnabledDNSDomain(this.AdminContext(), &pb.FindEnabledDNSDomainRequest{DnsDomainId: params.DomainId})
domainResp, err := this.RPC().DNSDomainRPC().FindDNSDomain(this.AdminContext(), &pb.FindDNSDomainRequest{DnsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return

View File

@@ -47,7 +47,7 @@ func (this *IndexAction) RunGet(params struct {
providerTypeName := ""
if cluster.DnsDomainId > 0 {
domainResp, err := this.RPC().DNSDomainRPC().FindEnabledBasicDNSDomain(this.AdminContext(), &pb.FindEnabledBasicDNSDomainRequest{DnsDomainId: domainId})
domainResp, err := this.RPC().DNSDomainRPC().FindBasicDNSDomain(this.AdminContext(), &pb.FindBasicDNSDomainRequest{DnsDomainId: domainId})
if err != nil {
this.ErrorPage(err)
return

View File

@@ -22,12 +22,14 @@ func (this *UpdateNodePopupAction) Init() {
func (this *UpdateNodePopupAction) RunGet(params struct {
ClusterId int64
NodeId int64
IpAddrId int64
}) {
this.Data["nodeId"] = params.NodeId
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{
NodeId: params.NodeId,
NodeClusterId: params.ClusterId,
NodeIPAddrId: params.IpAddrId,
})
if err != nil {
this.ErrorPage(err)
@@ -39,12 +41,13 @@ func (this *UpdateNodePopupAction) RunGet(params struct {
return
}
this.Data["ipAddr"] = dnsInfo.IpAddr
this.Data["ipAddrId"] = dnsInfo.NodeIPAddressId
this.Data["routes"] = domainutils.ConvertRoutesToMaps(dnsInfo)
this.Data["domainId"] = dnsInfo.DnsDomainId
this.Data["domainName"] = dnsInfo.DnsDomainName
// 读取所有线路
allRouteMaps := []maps.Map{}
var allRouteMaps = []maps.Map{}
if dnsInfo.DnsDomainId > 0 {
routesResp, err := this.RPC().DNSDomainRPC().FindAllDNSDomainRoutes(this.AdminContext(), &pb.FindAllDNSDomainRoutesRequest{DnsDomainId: dnsInfo.DnsDomainId})
if err != nil {
@@ -75,6 +78,7 @@ func (this *UpdateNodePopupAction) RunGet(params struct {
func (this *UpdateNodePopupAction) RunPost(params struct {
NodeId int64
IpAddr string
IpAddrId int64
DomainId int64
DnsRoutesJSON []byte
@@ -84,7 +88,7 @@ func (this *UpdateNodePopupAction) RunPost(params struct {
// 操作日志
defer this.CreateLog(oplogs.LevelInfo, "修改节点 %d 的DNS设置", params.NodeId)
routes := []string{}
var routes = []string{}
if len(params.DnsRoutesJSON) > 0 {
err := json.Unmarshal(params.DnsRoutesJSON, &routes)
if err != nil {
@@ -103,10 +107,11 @@ func (this *UpdateNodePopupAction) RunPost(params struct {
// 执行修改
_, err := this.RPC().NodeRPC().UpdateNodeDNS(this.AdminContext(), &pb.UpdateNodeDNSRequest{
NodeId: params.NodeId,
IpAddr: params.IpAddr,
DnsDomainId: params.DomainId,
Routes: routes,
NodeId: params.NodeId,
IpAddr: params.IpAddr,
NodeIPAddressId: params.IpAddrId,
DnsDomainId: params.DomainId,
Routes: routes,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -73,6 +73,12 @@ func (this *CreatePopupAction) RunPost(params struct {
ParamCustomHTTPURL string
ParamCustomHTTPSecret string
// EdgeDNS API
ParamEdgeDNSAPIRole string
ParamEdgeDNSAPIHost string
ParamEdgeDNSAPIAccessKeyId string
ParamEdgeDNSAPIAccessKeySecret string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
@@ -121,6 +127,20 @@ func (this *CreatePopupAction) RunPost(params struct {
Email("请输入正确格式的邮箱地址")
apiParams["apiKey"] = params.ParamCloudFlareAPIKey
apiParams["email"] = params.ParamCloudFlareEmail
case "edgeDNSAPI":
params.Must.
Field("paramEdgeDNSAPIHost", params.ParamEdgeDNSAPIHost).
Require("请输入API地址").
Field("paramEdgeDNSAPIRole", params.ParamEdgeDNSAPIRole).
Require("请选择AccessKey类型").
Field("paramEdgeDNSAPIAccessKeyId", params.ParamEdgeDNSAPIAccessKeyId).
Require("请输入AccessKey ID").
Field("paramEdgeDNSAPIAccessKeySecret", params.ParamEdgeDNSAPIAccessKeySecret).
Require("请输入AccessKey密钥")
apiParams["host"] = params.ParamEdgeDNSAPIHost
apiParams["role"] = params.ParamEdgeDNSAPIRole
apiParams["accessKeyId"] = params.ParamEdgeDNSAPIAccessKeyId
apiParams["accessKeySecret"] = params.ParamEdgeDNSAPIAccessKeySecret
case "customHTTP":
params.Must.
Field("paramCustomHTTPURL", params.ParamCustomHTTPURL).

View File

@@ -59,20 +59,20 @@ func (this *IndexAction) RunGet(params struct {
}
var providerMaps = []maps.Map{}
for _, provider := range providersResp.DnsProviders {
dataUpdatedTime := ""
var dataUpdatedTime = ""
if provider.DataUpdatedAt > 0 {
dataUpdatedTime = timeutil.FormatTime("Y-m-d H:i:s", provider.DataUpdatedAt)
}
// 域名
countDomainsResp, err := this.RPC().DNSDomainRPC().CountAllEnabledDNSDomainsWithDNSProviderId(this.AdminContext(), &pb.CountAllEnabledDNSDomainsWithDNSProviderIdRequest{
countDomainsResp, err := this.RPC().DNSDomainRPC().CountAllDNSDomainsWithDNSProviderId(this.AdminContext(), &pb.CountAllDNSDomainsWithDNSProviderIdRequest{
DnsProviderId: provider.Id,
})
if err != nil {
this.ErrorPage(err)
return
}
countDomains := countDomainsResp.Count
var countDomains = countDomainsResp.Count
providerMaps = append(providerMaps, maps.Map{
"id": provider.Id,

View File

@@ -18,19 +18,24 @@ func (this *ProviderAction) Init() {
func (this *ProviderAction) RunGet(params struct {
ProviderId int64
Page int
Filter string
}) {
this.Data["pageNo"] = params.Page
this.Data["filter"] = params.Filter
providerResp, err := this.RPC().DNSProviderRPC().FindEnabledDNSProvider(this.AdminContext(), &pb.FindEnabledDNSProviderRequest{DnsProviderId: params.ProviderId})
if err != nil {
this.ErrorPage(err)
return
}
provider := providerResp.DnsProvider
var provider = providerResp.DnsProvider
if provider == nil {
this.NotFound("dnsProvider", params.ProviderId)
return
}
apiParams := maps.Map{}
var apiParams = maps.Map{}
if len(provider.ApiParamsJSON) > 0 {
err = json.Unmarshal(provider.ApiParamsJSON, &apiParams)
if err != nil {
@@ -55,13 +60,33 @@ func (this *ProviderAction) RunGet(params struct {
"localEdgeDNS": localEdgeDNSMap,
}
// 域名
domainsResp, err := this.RPC().DNSDomainRPC().FindAllEnabledDNSDomainsWithDNSProviderId(this.AdminContext(), &pb.FindAllEnabledDNSDomainsWithDNSProviderIdRequest{DnsProviderId: provider.Id})
// 域名数量
countDomainsResp, err := this.RPC().DNSDomainRPC().CountAllDNSDomainsWithDNSProviderId(this.AdminContext(), &pb.CountAllDNSDomainsWithDNSProviderIdRequest{
DnsProviderId: params.ProviderId,
IsDeleted: params.Filter == "deleted",
IsDown: params.Filter == "down",
})
if err != nil {
this.ErrorPage(err)
return
}
domainMaps := []maps.Map{}
var countDomains = countDomainsResp.Count
var page = this.NewPage(countDomains)
this.Data["page"] = page.AsHTML()
// 域名
domainsResp, err := this.RPC().DNSDomainRPC().ListBasicDNSDomainsWithDNSProviderId(this.AdminContext(), &pb.ListBasicDNSDomainsWithDNSProviderIdRequest{
DnsProviderId: params.ProviderId,
IsDeleted: params.Filter == "deleted",
IsDown: params.Filter == "down",
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var domainMaps = []maps.Map{}
for _, domain := range domainsResp.DnsDomains {
dataUpdatedTime := ""
if domain.DataUpdatedAt > 0 {

View File

@@ -100,6 +100,12 @@ func (this *UpdatePopupAction) RunPost(params struct {
ParamCustomHTTPURL string
ParamCustomHTTPSecret string
// EdgeDNS API
ParamEdgeDNSAPIHost string
ParamEdgeDNSAPIRole string
ParamEdgeDNSAPIAccessKeyId string
ParamEdgeDNSAPIAccessKeySecret string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
@@ -150,6 +156,20 @@ func (this *UpdatePopupAction) RunPost(params struct {
Email("请输入正确格式的邮箱地址")
apiParams["apiKey"] = params.ParamCloudFlareAPIKey
apiParams["email"] = params.ParamCloudFlareEmail
case "edgeDNSAPI":
params.Must.
Field("paramEdgeDNSAPIHost", params.ParamEdgeDNSAPIHost).
Require("请输入API地址").
Field("paramEdgeDNSAPIRole", params.ParamEdgeDNSAPIRole).
Require("请选择AccessKey类型").
Field("paramEdgeDNSAPIAccessKeyId", params.ParamEdgeDNSAPIAccessKeyId).
Require("请输入AccessKey ID").
Field("paramEdgeDNSAPIAccessKeySecret", params.ParamEdgeDNSAPIAccessKeySecret).
Require("请输入AccessKey密钥")
apiParams["host"] = params.ParamEdgeDNSAPIHost
apiParams["role"] = params.ParamEdgeDNSAPIRole
apiParams["accessKeyId"] = params.ParamEdgeDNSAPIAccessKeyId
apiParams["accessKeySecret"] = params.ParamEdgeDNSAPIAccessKeySecret
case "customHTTP":
params.Must.
Field("paramCustomHTTPURL", params.ParamCustomHTTPURL).

View File

@@ -1,6 +1,7 @@
package acme
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
@@ -25,7 +26,7 @@ func (this *UpdateTaskPopupAction) RunGet(params struct {
this.ErrorPage(err)
return
}
task := taskResp.AcmeTask
var task = taskResp.AcmeTask
if task == nil {
this.NotFound("acmeTask", params.TaskId)
return
@@ -74,7 +75,7 @@ func (this *UpdateTaskPopupAction) RunGet(params struct {
this.ErrorPage(err)
return
}
providerMaps := []maps.Map{}
var providerMaps = []maps.Map{}
for _, provider := range providersResp.DnsProviders {
providerMaps = append(providerMaps, maps.Map{
"id": provider.Id,
@@ -93,7 +94,7 @@ func (this *UpdateTaskPopupAction) RunPost(params struct {
AcmeUserId int64
DnsProviderId int64
DnsDomain string
Domains []string
DomainsJSON []byte
AutoRenew bool
AuthURL string
@@ -123,11 +124,20 @@ func (this *UpdateTaskPopupAction) RunPost(params struct {
}
}
if len(params.Domains) == 0 {
var domains = []string{}
if len(params.DomainsJSON) > 0 {
err := json.Unmarshal(params.DomainsJSON, &domains)
if err != nil {
this.Fail("解析域名数据失败:" + err.Error())
return
}
}
if len(domains) == 0 {
this.Fail("请输入证书域名列表")
}
realDomains := []string{}
for _, domain := range params.Domains {
var realDomains = []string{}
for _, domain := range domains {
domain = strings.ToLower(domain)
if params.AuthType == "dns" {
if !strings.HasSuffix(domain, "."+dnsDomain) && domain != dnsDomain {

View File

@@ -15,7 +15,11 @@ func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct{}) {
func (this *CreatePopupAction) RunGet(params struct {
ProviderCode string
}) {
this.Data["providerCode"] = params.ProviderCode
// 服务商
providersResp, err := this.RPC().ACMEProviderRPC().FindAllACMEProviders(this.AdminContext(), &pb.FindAllACMEProvidersRequest{})
if err != nil {

View File

@@ -30,9 +30,10 @@ func (this *SelectPopupAction) RunGet(params struct {
// TODO 列出常用和最新的证书供用户选择
this.Data["keyword"] = params.Keyword
this.Data["selectedCertIds"] = params.SelectedCertIds
// 已经选择的证书
selectedCertIds := []string{}
var selectedCertIds = []string{}
if len(params.SelectedCertIds) > 0 {
selectedCertIds = strings.Split(params.SelectedCertIds, ",")
}

View File

@@ -7,7 +7,6 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
const (
@@ -65,7 +64,7 @@ func (this *IndexAction) RunPost(params struct {
DefaultDomain string
}) {
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "保存代理服务全局配置")
defer this.CreateLog(oplogs.LevelInfo, "保存网站服务全局配置")
if len(params.GlobalConfigJSON) == 0 {
this.Fail("错误的配置信息,请刷新当前页面后重试")
@@ -84,27 +83,6 @@ func (this *IndexAction) RunPost(params struct {
allowMismatchDomains = append(allowMismatchDomains, domain)
}
}
globalConfig.HTTPAll.AllowMismatchDomains = allowMismatchDomains
// 不匹配域名的动作
switch params.DomainMismatchAction {
case "close":
globalConfig.HTTPAll.DomainMismatchAction = &serverconfigs.DomainMismatchAction{
Code: "close",
Options: nil,
}
case "page":
if params.DomainMismatchActionPageStatusCode <= 0 {
params.DomainMismatchActionPageStatusCode = 404
}
globalConfig.HTTPAll.DomainMismatchAction = &serverconfigs.DomainMismatchAction{
Code: "page",
Options: maps.Map{
"statusCode": params.DomainMismatchActionPageStatusCode,
"contentHTML": params.DomainMismatchActionPageContentHTML,
},
}
}
// TCP端口范围
if params.TcpAllPortRangeMin < 1024 {

View File

@@ -32,6 +32,7 @@ func (this *CreateRulePopupAction) RunGet(params struct {
"params": checkpoint.Params,
"options": checkpoint.Options,
"isComposed": checkpoint.IsComposed,
"dataType": checkpoint.DataType,
})
}
}

View File

@@ -80,9 +80,9 @@ func (this *UpdateIPPopupAction) RunPost(params struct {
var ipToLong uint64
if len(params.IpTo) > 0 && !utils.IsIPv4(params.IpTo) {
ipToLong = utils.IP2Long(params.IpTo)
this.Fail("请输入正确的结束IP")
}
ipToLong = utils.IP2Long(params.IpTo)
if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
params.IpTo, params.IpFrom = params.IpFrom, params.IpTo

View File

@@ -109,6 +109,10 @@ func (this *SettingAction) RunPost(params struct {
FollowRedirects: reverseProxyConfig.FollowRedirects,
ProxyProtocolJSON: proxyProtocolJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -24,5 +24,4 @@ func (this *IndexAction) RunGet(params struct {
} else {
this.RedirectURL("/servers/groups/group/settings/httpReverseProxy?groupId=" + types.String(params.GroupId))
}
return
}

View File

@@ -109,6 +109,10 @@ func (this *SettingAction) RunPost(params struct {
FollowRedirects: reverseProxyConfig.FollowRedirects,
ProxyProtocolJSON: proxyProtocolJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -96,8 +96,12 @@ func (this *SettingAction) RunPost(params struct {
StripPrefix: reverseProxyConfig.StripPrefix,
AutoFlush: reverseProxyConfig.AutoFlush,
AddHeaders: reverseProxyConfig.AddHeaders,
FollowRedirects: reverseProxyConfig.FollowRedirects,
FollowRedirects: reverseProxyConfig.FollowRedirects,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -79,9 +79,9 @@ func (this *UpdateIPPopupAction) RunPost(params struct {
var ipToLong uint64
if len(params.IpTo) > 0 && !utils.IsIPv4(params.IpTo) {
ipToLong = utils.IP2Long(params.IpTo)
this.Fail("请输入正确的结束IP")
}
ipToLong = utils.IP2Long(params.IpTo)
if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
params.IpTo, params.IpFrom = params.IpFrom, params.IpTo

View File

@@ -0,0 +1,26 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package headers
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
)
type OptionsAction struct {
actionutils.ParentAction
}
func (this *OptionsAction) RunPost(params struct {
Type string
}) {
if params.Type == "request" {
this.Data["headers"] = serverconfigs.AllHTTPCommonRequestHeaders
} else if params.Type == "response" {
this.Data["headers"] = serverconfigs.AllHTTPCommonResponseHeaders
} else {
this.Data["headers"] = []string{}
}
this.Success()
}

View File

@@ -3,7 +3,6 @@ package servers
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
@@ -90,15 +89,16 @@ func (this *IndexAction) RunGet(params struct {
// 服务列表
serversResp, err := this.RPC().ServerRPC().ListEnabledServersMatch(this.AdminContext(), &pb.ListEnabledServersMatchRequest{
Offset: page.Offset,
Size: page.Size,
NodeClusterId: params.ClusterId,
ServerGroupId: params.GroupId,
Keyword: params.Keyword,
AuditingFlag: params.AuditingFlag,
TrafficOutDesc: params.TrafficOutOrder == "desc",
TrafficOutAsc: params.TrafficOutOrder == "asc",
UserId: params.UserId,
Offset: page.Offset,
Size: page.Size,
NodeClusterId: params.ClusterId,
ServerGroupId: params.GroupId,
Keyword: params.Keyword,
AuditingFlag: params.AuditingFlag,
TrafficOutDesc: params.TrafficOutOrder == "desc",
TrafficOutAsc: params.TrafficOutOrder == "asc",
UserId: params.UserId,
IgnoreServerNames: true,
})
if err != nil {
this.ErrorPage(err)
@@ -176,27 +176,35 @@ func (this *IndexAction) RunGet(params struct {
}
// 域名列表
var serverNames = []*serverconfigs.ServerNameConfig{}
if server.IsAuditing || (server.AuditingResult != nil && !server.AuditingResult.IsOk) {
server.ServerNamesJSON = server.AuditingServerNamesJSON
if len(config.ServerNames) == 0 {
// 审核中的域名
if len(server.ServerNamesJSON) > 0 {
var serverNames = []*serverconfigs.ServerNameConfig{}
err = json.Unmarshal(server.ServerNamesJSON, &serverNames)
if err != nil {
this.ErrorPage(err)
return
}
config.ServerNames = serverNames
}
}
}
var auditingIsOk = true
if !server.IsAuditing && server.AuditingResult != nil && !server.AuditingResult.IsOk {
auditingIsOk = false
}
if len(server.ServerNamesJSON) > 0 {
err = json.Unmarshal(server.ServerNamesJSON, &serverNames)
if err != nil {
this.ErrorPage(err)
return
var firstServerName = ""
for _, serverNameConfig := range config.ServerNames {
if len(serverNameConfig.Name) > 0 {
firstServerName = serverNameConfig.Name
break
}
}
var countServerNames = 0
for _, serverName := range serverNames {
if len(serverName.SubNames) == 0 {
countServerNames++
} else {
countServerNames += len(serverName.SubNames)
if len(serverNameConfig.SubNames) > 0 {
firstServerName = serverNameConfig.SubNames[0]
break
}
}
@@ -216,9 +224,9 @@ func (this *IndexAction) RunGet(params struct {
}
// 统计数据
var bandwidth = ""
var bandwidthBits int64 = 0
if server.BandwidthBytes > 0 {
bandwidth = numberutils.FormatBytes(server.BandwidthBytes)
bandwidthBits = server.BandwidthBytes * 8
}
serverMaps = append(serverMaps, maps.Map{
@@ -232,13 +240,13 @@ func (this *IndexAction) RunGet(params struct {
"ports": portMaps,
"serverTypeName": serverconfigs.FindServerType(server.Type).GetString("name"),
"groups": groupMaps,
"serverNames": serverNames,
"countServerNames": countServerNames,
"firstServerName": firstServerName,
"countServerNames": server.CountServerNames,
"isAuditing": server.IsAuditing,
"auditingIsOk": auditingIsOk,
"user": userMap,
"auditingTime": auditingTime,
"bandwidth": bandwidth,
"bandwidthBits": bandwidthBits,
})
}
this.Data["servers"] = serverMaps

View File

@@ -2,6 +2,7 @@ package servers
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/headers"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/users"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
@@ -26,10 +27,13 @@ func init() {
Get("/serverNamesPopup", new(ServerNamesPopupAction)).
Post("/status", new(StatusAction)).
//
// user
Post("/users/options", new(users.OptionsAction)).
Post("/users/plans", new(users.PlansAction)).
// header
Post("/headers/options", new(headers.OptionsAction)).
//
EndAll()
})

View File

@@ -97,9 +97,9 @@ func (this *CreateIPPopupAction) RunPost(params struct {
var ipToLong uint64
if len(params.IpTo) > 0 && !utils.IsIPv4(params.IpTo) {
ipToLong = utils.IP2Long(params.IpTo)
this.Fail("请输入正确的结束IP")
}
ipToLong = utils.IP2Long(params.IpTo)
if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
params.IpTo, params.IpFrom = params.IpFrom, params.IpTo

View File

@@ -21,21 +21,21 @@ func (this *IndexAction) Init() {
}
func (this *IndexAction) RunGet(params struct {
Ip string
Keyword string
GlobalOnly bool
Unread bool
EventLevel string
ListType string
}) {
this.Data["type"] = ""
this.Data["ip"] = params.Ip
this.Data["keyword"] = params.Keyword
this.Data["globalOnly"] = params.GlobalOnly
this.Data["unread"] = params.Unread
this.Data["eventLevel"] = params.EventLevel
this.Data["listType"] = params.ListType
countUnreadResp, err := this.RPC().IPItemRPC().CountAllEnabledIPItems(this.AdminContext(), &pb.CountAllEnabledIPItemsRequest{
Ip: params.Ip,
Keyword: params.Keyword,
GlobalOnly: params.GlobalOnly,
Unread: true,
EventLevel: params.EventLevel,
@@ -48,7 +48,7 @@ func (this *IndexAction) RunGet(params struct {
this.Data["countUnread"] = countUnreadResp.Count
countResp, err := this.RPC().IPItemRPC().CountAllEnabledIPItems(this.AdminContext(), &pb.CountAllEnabledIPItemsRequest{
Ip: params.Ip,
Keyword: params.Keyword,
GlobalOnly: params.GlobalOnly,
Unread: params.Unread,
EventLevel: params.EventLevel,
@@ -63,7 +63,7 @@ func (this *IndexAction) RunGet(params struct {
this.Data["page"] = page.AsHTML()
itemsResp, err := this.RPC().IPItemRPC().ListAllEnabledIPItems(this.AdminContext(), &pb.ListAllEnabledIPItemsRequest{
Ip: params.Ip,
Keyword: params.Keyword,
GlobalOnly: params.GlobalOnly,
Unread: params.Unread,
EventLevel: params.EventLevel,

View File

@@ -79,9 +79,9 @@ func (this *UpdateIPPopupAction) RunPost(params struct {
var ipToLong uint64
if len(params.IpTo) > 0 && !utils.IsIPv4(params.IpTo) {
ipToLong = utils.IP2Long(params.IpTo)
this.Fail("请输入正确的结束IP")
}
ipToLong = utils.IP2Long(params.IpTo)
if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
params.IpTo, params.IpFrom = params.IpFrom, params.IpTo

View File

@@ -41,9 +41,6 @@ func (this *UpdateRefsAction) RunPost(params struct {
// 校验配置
var cacheConfig = webConfig.Cache
if webConfig == nil {
this.Success()
}
var refs = []*serverconfigs.HTTPCacheRef{}
err = json.Unmarshal(params.RefsJSON, &refs)

View File

@@ -19,8 +19,10 @@ func (this *CreateDeletePopupAction) Init() {
func (this *CreateDeletePopupAction) RunGet(params struct {
HeaderPolicyId int64
Type string
}) {
this.Data["headerPolicyId"] = params.HeaderPolicyId
this.Data["type"] = params.Type
this.Show()
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/actions"
"strings"
)
type CreateSetPopupAction struct {
@@ -45,6 +46,8 @@ func (this *CreateSetPopupAction) RunPost(params struct {
// 日志
defer this.CreateLog(oplogs.LevelInfo, "设置请求HeaderHeaderPolicyId:%d, Name:%s, Value:%s", params.HeaderPolicyId, params.Name, params.Value)
params.Name = strings.TrimSuffix(params.Name, ":")
params.Must.
Field("name", params.Name).
Require("请输入Header名称")

View File

@@ -8,7 +8,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
)
// 删除Header
// DeleteAction 删除Header
type DeleteAction struct {
actionutils.ParentAction
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/actions"
"strings"
)
type UpdateSetPopupAction struct {
@@ -60,6 +61,8 @@ func (this *UpdateSetPopupAction) RunPost(params struct {
// 日志
defer this.CreateLog(oplogs.LevelInfo, "修改设置请求HeaderHeaderPolicyId:%d, Name:%s, Value:%s", params.HeaderId, params.Name, params.Value)
params.Name = strings.TrimSuffix(params.Name, ":")
params.Must.
Field("name", params.Name).
Require("请输入Header名称")

View File

@@ -65,34 +65,7 @@ func (this *IndexAction) RunGet(params struct {
}
// 套餐
var userPlanMap = maps.Map{"id": server.UserPlanId, "dayTo": "", "plan": maps.Map{}}
if server.UserPlanId > 0 {
userPlanResp, err := this.RPC().UserPlanRPC().FindEnabledUserPlan(this.AdminContext(), &pb.FindEnabledUserPlanRequest{UserPlanId: server.UserPlanId})
if err != nil {
this.ErrorPage(err)
return
}
var userPlan = userPlanResp.UserPlan
if userPlan != nil {
planResp, err := this.RPC().PlanRPC().FindEnabledPlan(this.AdminContext(), &pb.FindEnabledPlanRequest{PlanId: userPlan.PlanId})
if err != nil {
this.ErrorPage(err)
return
}
var plan = planResp.Plan
if plan != nil {
userPlanMap = maps.Map{
"id": userPlan.Id,
"dayTo": userPlan.DayTo,
"plan": maps.Map{
"id": plan.Id,
"name": plan.Name,
},
}
}
}
}
this.Data["userPlan"] = userPlanMap
this.initUserPlan(server)
// 集群
clusterId := int64(0)

View File

@@ -0,0 +1,14 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package settings
import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
func (this *IndexAction) initUserPlan(server *pb.Server) {
var userPlanMap = maps.Map{"id": server.UserPlanId, "dayTo": "", "plan": maps.Map{}}
this.Data["userPlan"] = userPlanMap
}

View File

@@ -103,6 +103,12 @@ func (this *LocationHelper) createMenus(serverIdString string, locationIdString
"isActive": secondMenuItem == "access",
"isOn": locationConfig != nil && locationConfig.Web != nil && locationConfig.Web.Auth != nil && locationConfig.Web.Auth.IsPrior,
})
menuItems = append(menuItems, maps.Map{
"name": "防盗链",
"url": "/servers/server/settings/locations/referers?serverId=" + serverIdString + "&locationId=" + locationIdString,
"isActive": secondMenuItem == "referer",
"isOn": locationConfig.Web != nil && locationConfig.Web.Referers != nil && locationConfig.Web.Referers.IsPrior,
})
menuItems = append(menuItems, maps.Map{
"name": "字符编码",
"url": "/servers/server/settings/locations/charset?serverId=" + serverIdString + "&locationId=" + locationIdString,

View File

@@ -0,0 +1,68 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package uam
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("referer")
}
func (this *IndexAction) RunGet(params struct {
LocationId int64
}) {
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithLocationId(this.AdminContext(), params.LocationId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
var referersConfig = webConfig.Referers
if referersConfig == nil {
referersConfig = &serverconfigs.ReferersConfig{
IsPrior: false,
IsOn: false,
AllowEmpty: true,
AllowSameDomain: true,
AllowDomains: nil,
}
}
this.Data["referersConfig"] = referersConfig
this.Show()
}
func (this *IndexAction) RunPost(params struct {
WebId int64
ReferersJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo("修改Web %d 防盗链设置", params.WebId)
_, err := this.RPC().HTTPWebRPC().UpdateHTTPWebReferers(this.AdminContext(), &pb.UpdateHTTPWebReferersRequest{
HttpWebId: params.WebId,
ReferersJSON: params.ReferersJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,22 @@
package uam
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/locations/locationutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeServer)).
Helper(locationutils.NewLocationHelper()).
Helper(serverutils.NewServerHelper()).
Data("tinyMenuItem", "referer").
Prefix("/servers/server/settings/locations/referers").
GetPost("", new(IndexAction)).
EndAll()
})
}

Some files were not shown because too many files have changed in this diff Show More