Compare commits

..

123 Commits

Author SHA1 Message Date
刘祥超
d8393481a4 修改域名变更时是否需要审核的初始状态 2021-11-25 12:10:23 +08:00
刘祥超
70d3202a2c 服务增加是否合并URL中的多余分隔符选项 2021-11-24 14:49:42 +08:00
刘祥超
c7f1bbc03d 版本号改为0.3.6 2021-11-24 14:03:56 +08:00
刘祥超
61a55cb3f4 优化命名 2021-11-24 11:58:01 +08:00
刘祥超
6a484af775 将版本修改为0.3.5.2 2021-11-24 10:29:59 +08:00
刘祥超
3074d41cf2 改进源站专属域名的文字提示和交互 2021-11-22 18:46:08 +08:00
刘祥超
54199058e3 修复服务无法创建的Bug 2021-11-22 14:34:32 +08:00
刘祥超
87a533791b 版本改为0.3.5.1 2021-11-22 14:34:20 +08:00
刘祥超
c3109bb2c6 编译时生成components.js 2021-11-22 12:08:53 +08:00
刘祥超
031cb836d2 安装时等API节点启动完毕后才进行下一步,避免因为未启动完整而导致的错误 2021-11-21 19:26:05 +08:00
刘祥超
aa0a9134cb 迁移后确认API节点界面可以跳转到安装界面 2021-11-21 19:25:42 +08:00
刘祥超
749eac74fe 当迁移了管理平台后,自动跳转到确认API配置页 2021-11-21 15:57:13 +08:00
刘祥超
ac39908737 优化删除IP时成功消息提示时间 2021-11-21 09:45:11 +08:00
刘祥超
bb8f4bf488 增加批量删除IP名单中的IP的功能 2021-11-21 09:43:14 +08:00
刘祥超
db0d157a74 修复时间输入组件时间戳总是多一秒的Bug 2021-11-21 08:44:03 +08:00
刘祥超
bd8e1bbe71 优化RPC客户端锁 2021-11-20 19:17:52 +08:00
刘祥超
7aba898cf5 优化文字 2021-11-20 19:17:16 +08:00
刘祥超
baf039755f 实现迁移辅助功能(系统设置 -- 高级设置 -- 迁移) 2021-11-20 18:58:58 +08:00
刘祥超
70977f7d80 IP地址“健康检查失败”阈值改为“健康检查结果” 2021-11-18 14:47:48 +08:00
刘祥超
884342d6af 节点IP阈值增加节点健康检查失败 2021-11-18 14:30:46 +08:00
刘祥超
411b0fb4c2 修复看板--事件中无法单条已读操作的Bug 2021-11-18 08:52:29 +08:00
刘祥超
8053fb2399 IP名单中显示已过期标签 2021-11-17 21:18:33 +08:00
刘祥超
ed42dcab9c IP名单增加是否只显示自动拦截名单选项 2021-11-17 20:25:31 +08:00
刘祥超
866b5b0f2f IP名单中增加搜索按钮 2021-11-17 20:15:37 +08:00
刘祥超
5834a1a0fa 增加全局查看、检索IP功能 2021-11-17 19:50:52 +08:00
刘祥超
8c6d845603 将公用IP名单默认改成全局名单,自动应用于所有服务 2021-11-17 16:14:37 +08:00
刘祥超
667f363f3c WAF block动作默认封锁范围为global 2021-11-16 19:17:08 +08:00
刘祥超
e209ff38d9 IP名单中显示IP创建时相关的服务、WAF策略等信息 2021-11-16 16:11:29 +08:00
刘祥超
ea915993b6 节点运行日志中显示相关服务链接 2021-11-15 16:51:39 +08:00
刘祥超
72d0468c6a IP名单中的IP增加添加日期显示 2021-11-15 11:31:54 +08:00
刘祥超
35ae13b1c3 优化缓存配置界面 2021-11-14 16:21:04 +08:00
刘祥超
1d9460f565 优化RPC连接 2021-11-10 22:22:27 +08:00
刘祥超
e9a3ed71b4 将带宽限制改为流量限制 2021-11-09 17:36:38 +08:00
刘祥超
e344e5b7e6 支持套餐相关操作 2021-11-09 15:36:18 +08:00
刘祥超
e814064403 编译时删除.js.map文件 2021-11-09 14:19:42 +08:00
刘祥超
f5aeb5cbcd 支持购买套餐/续费套餐/用户账户操作等 2021-11-08 20:52:02 +08:00
刘祥超
f41164b892 删除不需要的文件 2021-11-07 09:11:30 +08:00
刘祥超
03073c8364 删除不需要的文件 2021-11-07 08:42:11 +08:00
刘祥超
a359bff531 安装时自动检查服务器上安装的MySQL 2021-11-06 18:35:22 +08:00
刘祥超
3789ac6433 域名解析中可以删除和恢复某个域名 2021-11-06 16:23:38 +08:00
刘祥超
9f53f59f18 SSH登录支持Passphrase 2021-11-06 15:31:07 +08:00
刘祥超
521bd746e3 当证书被API节点或者用户节点使用时不允许删除/规范命名 2021-11-05 17:56:17 +08:00
刘祥超
3d8e43a42b 修改文字 2021-11-05 17:13:43 +08:00
刘祥超
9452e1852d 规范API命名 2021-11-05 15:34:48 +08:00
刘祥超
3920d24af6 只有一个可用的API节点时不允许删除 2021-11-05 15:29:37 +08:00
刘祥超
3af11e6ba8 修改版本号为0.3.5 2021-11-05 14:59:10 +08:00
刘祥超
7bcde46d49 修复缓存条件可能无法保存的Bug 2021-11-04 11:23:29 +08:00
刘祥超
0b73041718 支持info指令查询PID、版本号等信息 2021-11-04 11:13:54 +08:00
刘祥超
dcdc0cb8c1 修复无法设置缓存条件的Bug 2021-11-04 11:12:59 +08:00
刘祥超
ad6ac1aad6 优化安装程序 2021-11-01 21:09:52 +08:00
刘祥超
249dc6accd 修改版本为0.3.4 2021-11-01 10:45:41 +08:00
刘祥超
939e5999ca 优化细节 2021-10-30 22:33:56 +08:00
刘祥超
1f91e57d56 增加套餐相关代码 2021-10-29 14:02:01 +08:00
刘祥超
81e749dc60 支持gif转webp 2021-10-29 12:21:52 +08:00
刘祥超
241b2afda8 Update .gitignore 2021-10-26 20:43:59 +08:00
刘祥超
94cc29f227 删除不必要的文件 2021-10-26 20:43:47 +08:00
刘祥超
6d6659eee1 WAF增加显示网页动作 2021-10-25 19:40:28 +08:00
刘祥超
5220be0775 优化代码 2021-10-25 19:01:56 +08:00
刘祥超
07ebbf0863 WAF模板中有新的规则时,可以在界面上收到提醒并点击加入 2021-10-25 12:02:03 +08:00
刘祥超
b60c767fc5 创建网站服务时增加缓存、WAF、从上级代理中读取IP等选项 2021-10-25 09:06:23 +08:00
刘祥超
371c3b78c3 DNS记录名支持下划线 2021-10-25 09:05:26 +08:00
刘祥超
6a3aa219d5 优化HTTP客户端IP配置交互 2021-10-22 14:40:39 +08:00
刘祥超
df586ddfdd 优化代码 2021-10-22 13:57:04 +08:00
刘祥超
f3b2bbfec0 删除不需要的文件 2021-10-22 13:20:02 +08:00
刘祥超
3ea2114798 IP名单列表可以搜索关键词 2021-10-22 12:38:52 +08:00
刘祥超
809cf70e0e 可以在IP名单中搜索IP 2021-10-22 12:19:02 +08:00
刘祥超
390619535f 实现单个服务的带宽限制(商业版) 2021-10-21 17:10:08 +08:00
刘祥超
3392ac1fa8 优化交互 2021-10-20 09:59:08 +08:00
刘祥超
b09d94abbe 网站服务--访问日志增加服务链接 2021-10-19 16:32:08 +08:00
刘祥超
03ac01d21f 健康检查支持UserAgent和是否基础请求设置 2021-10-19 16:31:45 +08:00
刘祥超
12b1d785e5 增加防盗链规则参数 2021-10-19 11:38:56 +08:00
刘祥超
71c58e9d2e WAF阻止动作增加封锁范围 2021-10-18 20:09:06 +08:00
刘祥超
13c2997a52 内容压缩支持对已压缩内容重新压缩 2021-10-18 16:49:19 +08:00
刘祥超
47b840cac9 优化文字提示 2021-10-18 16:49:04 +08:00
刘祥超
3f7f243f50 默认的内容压缩算法从gzip改为brotli 2021-10-18 11:59:50 +08:00
刘祥超
1d1e83b18d 增加PURGE某个URL缓存功能 2021-10-17 20:22:57 +08:00
刘祥超
6f3a602c76 优化分页条数修改/在弹窗下不运行某些任务 2021-10-16 12:45:56 +08:00
刘祥超
afb7a4c6a7 修复选择集群弹窗无法修改分页条数的问题 2021-10-16 12:45:25 +08:00
刘祥超
d0c950d4ca 优化界面 2021-10-16 12:06:55 +08:00
刘祥超
63ee7d5211 支持任意域名通过CNAME访问服务(开启选项后)/可以重新生成服务CNAME 2021-10-16 12:03:21 +08:00
刘祥超
6b0d875745 优化界面显示 2021-10-16 10:29:14 +08:00
刘祥超
a47a9b9c0c 删除CodeMirror中没用的代码 2021-10-16 10:26:14 +08:00
刘祥超
27040a3e5c 节点运行日志增加本页已读 2021-10-15 13:05:02 +08:00
刘祥超
c7a8a40e22 运行日志显示未读的日志数量 2021-10-15 12:54:23 +08:00
刘祥超
408de6af63 优化文字提示 2021-10-15 09:42:55 +08:00
刘祥超
8a324afaa1 数据看板增加事件列表(商业版) 2021-10-14 17:29:30 +08:00
刘祥超
54bc4cede0 修复无法编译amd64以外架构的Bug 2021-10-13 18:06:38 +08:00
刘祥超
4cfbea80b0 支持PROXY Protocol 2021-10-12 20:18:29 +08:00
刘祥超
64cb8286bd 集群非上海时区的在列表里显示时区 2021-10-12 14:39:13 +08:00
刘祥超
3e92e0afc6 优化修改时区交互 2021-10-12 11:49:26 +08:00
刘祥超
8a91308280 可以在集群中指定节点时区 2021-10-12 11:43:53 +08:00
刘祥超
3da861d71e 选择线路的时候关键词可以搜索域名 2021-10-12 08:41:02 +08:00
刘祥超
3566e18e99 WebP压缩支持ico 2021-10-11 14:51:50 +08:00
刘祥超
6e608e627a WebP默认mimeTypes从image/*改为image/png等 2021-10-11 13:56:51 +08:00
刘祥超
65f7fb979b 修改版本为0.3.3 2021-10-11 13:56:20 +08:00
刘祥超
558b5e14f1 优化细节 2021-10-10 20:17:40 +08:00
刘祥超
a5ee2dd03b 页面底部增加GoEdge官网和文档链接 2021-10-10 16:38:45 +08:00
刘祥超
9e9fe78b8d 优化升级提示文字 2021-10-10 16:33:27 +08:00
刘祥超
265e126faf TCP、TLS、UDP支持端口范围 2021-10-10 16:30:21 +08:00
刘祥超
af440e5c5b 服务分组增加特殊页面设置 2021-10-10 10:52:58 +08:00
刘祥超
32b8c91113 特殊页面可以直接使用HTML 2021-10-10 10:35:14 +08:00
刘祥超
dbc60ccca4 证书上传时可以选择输入文本内容 2021-10-09 17:30:05 +08:00
刘祥超
d5b5af5d3a 数据看板-WAF看板增加节点拦截排行和域名拦截排行 2021-10-09 16:01:17 +08:00
刘祥超
8c1bd3bc4e 增加新的界面风格theme4, theme5 2021-10-09 11:50:02 +08:00
刘祥超
8f638186a3 在服务看板中可以切换到附近的服务 2021-10-08 14:36:57 +08:00
刘祥超
0b5a27e674 支持更多的分组全局设置功能 2021-10-07 16:47:14 +08:00
刘祥超
870f1aaaec WAF模式从pass改为bypass 2021-10-07 13:55:00 +08:00
刘祥超
23cb4dcbe5 服务支持自定义访客IP地址获取方式 2021-10-06 11:40:24 +08:00
刘祥超
4f125b4244 添加源站时自动去除专属域名中的末尾斜杠 2021-10-06 09:29:44 +08:00
刘祥超
2b9de7938f 在服务设置里也显示WAF策略的模式 2021-10-06 09:08:09 +08:00
刘祥超
75bb07184f ACME使用EAB申请的账号只能绑定一个用户 2021-10-03 14:43:29 +08:00
刘祥超
1fb491d2e1 ACME证书增加ZeroSSL支持 2021-10-03 13:09:49 +08:00
刘祥超
0bc8bdd841 支持自动转换图像文件为WebP 2021-10-01 16:24:42 +08:00
刘祥超
71d4e2626e 自建DNS改成智能DNS 2021-09-30 13:20:50 +08:00
刘祥超
0b26cbdd01 WAF策略增加观察模式和通过模式 2021-09-30 11:30:36 +08:00
刘祥超
ca72b3c18b 内容压缩支持brotli和deflate 2021-09-29 20:12:27 +08:00
刘祥超
8676f2711b 在WAF规则产生错误时给予提示 2021-09-27 10:11:37 +08:00
刘祥超
788a86bdcf 看板增加离线节点数字 2021-09-27 09:23:48 +08:00
刘祥超
0df6b4b220 优化安装界面--设置管理员账号的交互 2021-09-26 15:08:53 +08:00
刘祥超
f14dcd5c28 缓存条件增加最小内容尺寸配置 2021-09-26 15:01:52 +08:00
刘祥超
f86548e046 版本改为0.3.2 2021-09-26 10:09:51 +08:00
刘祥超
9dea11ab11 节点设置中不显示阈值设置 2021-09-25 19:57:22 +08:00
刘祥超
00749f806c 优化编译脚本 2021-09-25 19:57:10 +08:00
508 changed files with 26843 additions and 4171 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
*_plus.go
*-plus.sh
*_plus.html
*_plus.js
*@plus.js

View File

@@ -58,13 +58,20 @@ function build() {
rm -f $(basename $EDGE_API_ZIP_FILE)
cd -
# generate files
echo "generating files ..."
go run -tags $TAG $ROOT/../cmd/edge-admin/main.go generate
# build
echo "building "${NAME}" ..."
env GOOS=$OS GOARCH=$GOARCH go build -tags $TAG -ldflags="-s -w" -o $DIST/bin/${NAME} $ROOT/../cmd/edge-admin/main.go
env GOOS=$OS GOARCH=$ARCH go build -tags $TAG -ldflags="-s -w" -o $DIST/bin/${NAME} $ROOT/../cmd/edge-admin/main.go
# delete hidden files
find $DIST -name ".DS_Store" -delete
find $DIST -name ".gitignore" -delete
find $DIST -name "*.less" -delete
find $DIST -name "*.css.map" -delete
find $DIST -name "*.js.map" -delete
# zip
echo "zip files ..."

View File

@@ -5,6 +5,7 @@ import (
"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/web"
_ "github.com/iwind/TeaGo/bootstrap"
@@ -70,6 +71,13 @@ func main() {
}
fmt.Println("change demo mode successfully")
})
app.On("generate", func() {
err := gen.Generate()
if err != nil {
fmt.Println("generate failed: " + err.Error())
return
}
})
app.Run(func() {
adminNode := nodes.NewAdminNode()
adminNode.Run()

6
go.mod
View File

@@ -10,9 +10,11 @@ require (
github.com/go-sql-driver/mysql v1.5.0
github.com/go-yaml/yaml v2.1.0+incompatible
github.com/google/go-cmp v0.5.6 // indirect
github.com/iwind/TeaGo v0.0.0-20210720011303-fc255c995afa
github.com/iwind/gosock v0.0.0-20210722083328-12b2d66abec3
github.com/iwind/TeaGo v0.0.0-20211026123858-7de7a21cad24
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4
github.com/json-iterator/go v1.1.12 // indirect
github.com/miekg/dns v1.1.35
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/tealeg/xlsx/v3 v3.2.3
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119

14
go.sum
View File

@@ -62,12 +62,18 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iwind/TeaGo v0.0.0-20210628135026-38575a4ab060/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/TeaGo v0.0.0-20210720011303-fc255c995afa h1:woN88uEmRRUNFD7pRZEtX9heDcjFn0ClMxjF5ButKow=
github.com/iwind/TeaGo v0.0.0-20210720011303-fc255c995afa/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/TeaGo v0.0.0-20210831140440-a2a442471b13 h1:HuEJ5xJfujW1Q6rNDhOu5LQXEBB2qLPah3jYslT8Gz4=
github.com/iwind/TeaGo v0.0.0-20210831140440-a2a442471b13/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/TeaGo v0.0.0-20211026123858-7de7a21cad24 h1:1cGulkD2SNJJRok5OKwyhP/Ddm+PgSWKOupn0cR36/A=
github.com/iwind/TeaGo v0.0.0-20211026123858-7de7a21cad24/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/gosock v0.0.0-20210722083328-12b2d66abec3 h1:aBSonas7vFcgTj9u96/bWGILGv1ZbUSTLiOzcI1ZT6c=
github.com/iwind/gosock v0.0.0-20210722083328-12b2d66abec3/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4 h1:VWGsCqTzObdlbf7UUE3oceIpcEKi4C/YBUszQXk118A=
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -77,8 +83,12 @@ github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs=
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=

View File

@@ -19,6 +19,7 @@ const (
AdminModuleCodeAdmin AdminModuleCode = "admin" // 系统用户
AdminModuleCodeUser AdminModuleCode = "user" // 平台用户
AdminModuleCodeFinance AdminModuleCode = "finance" // 财务
AdminModuleCodePlan AdminModuleCode = "plan" // 套餐
AdminModuleCodeLog AdminModuleCode = "log" // 日志
AdminModuleCodeSetting AdminModuleCode = "setting" // 设置
AdminModuleCodeCommon AdminModuleCode = "common" // 只要登录就可以访问的模块
@@ -182,7 +183,7 @@ func AllModuleMaps() []maps.Map {
}
if teaconst.IsPlus {
m = append(m, maps.Map{
"name": "自建DNS",
"name": "智能DNS",
"code": AdminModuleCodeNS,
"url": "/ns",
})
@@ -203,6 +204,11 @@ func AllModuleMaps() []maps.Map {
"code": AdminModuleCodeFinance,
"url": "/finance",
},
{
"name": "套餐管理",
"code": AdminModuleCodePlan,
"url": "/plans",
},
{
"name": "日志审计",
"code": AdminModuleCodeLog,

View File

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

129
internal/gen/generate.go Normal file
View File

@@ -0,0 +1,129 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package gen
import (
"bytes"
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/conds/condutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/files"
"github.com/iwind/TeaGo/logs"
"io"
"os"
"path/filepath"
)
func Generate() error {
err := generateComponentsJSFile()
if err != nil {
return errors.New("generate 'components.js' failed: " + err.Error())
}
return nil
}
// 生成Javascript文件
func generateComponentsJSFile() error {
var buffer = bytes.NewBuffer([]byte{})
var webRoot string
if Tea.IsTesting() {
webRoot = Tea.Root + "/../web/public/js/components/"
} else {
webRoot = Tea.Root + "/web/public/js/components/"
}
f := files.NewFile(webRoot)
f.Range(func(file *files.File) {
if !file.IsFile() {
return
}
if file.Ext() != ".js" {
return
}
data, err := file.ReadAll()
if err != nil {
logs.Error(err)
return
}
buffer.Write(data)
buffer.Write([]byte{'\n', '\n'})
})
// 条件组件
typesJSON, err := json.Marshal(condutils.ReadAllAvailableCondTypes())
if err != nil {
logs.Println("ComponentsAction marshal request cond types failed: " + err.Error())
} else {
buffer.WriteString("window.REQUEST_COND_COMPONENTS = ")
buffer.Write(typesJSON)
buffer.Write([]byte{'\n', '\n'})
}
// 条件操作符
requestOperatorsJSON, err := json.Marshal(shared.AllRequestOperators())
if err != nil {
logs.Println("ComponentsAction marshal request operators failed: " + err.Error())
} else {
buffer.WriteString("window.REQUEST_COND_OPERATORS = ")
buffer.Write(requestOperatorsJSON)
buffer.Write([]byte{'\n', '\n'})
}
// 请求变量
requestVariablesJSON, err := json.Marshal(shared.DefaultRequestVariables())
if err != nil {
logs.Println("ComponentsAction marshal request variables failed: " + err.Error())
} else {
buffer.WriteString("window.REQUEST_VARIABLES = ")
buffer.Write(requestVariablesJSON)
buffer.Write([]byte{'\n', '\n'})
}
// 指标
metricHTTPKeysJSON, err := json.Marshal(serverconfigs.FindAllMetricKeyDefinitions(serverconfigs.MetricItemCategoryHTTP))
if err != nil {
logs.Println("ComponentsAction marshal metric http keys failed: " + err.Error())
} else {
buffer.WriteString("window.METRIC_HTTP_KEYS = ")
buffer.Write(metricHTTPKeysJSON)
buffer.Write([]byte{'\n', '\n'})
}
// IP地址阈值项目
ipAddrThresholdItemsJSON, err := json.Marshal(nodeconfigs.FindAllIPAddressThresholdItems())
if err != nil {
logs.Println("ComponentsAction marshal ip addr threshold items failed: " + err.Error())
} else {
buffer.WriteString("window.IP_ADDR_THRESHOLD_ITEMS = ")
buffer.Write(ipAddrThresholdItemsJSON)
buffer.Write([]byte{'\n', '\n'})
}
// IP地址阈值动作
ipAddrThresholdActionsJSON, err := json.Marshal(nodeconfigs.FindAllIPAddressThresholdActions())
if err != nil {
logs.Println("ComponentsAction marshal ip addr threshold actions failed: " + err.Error())
} else {
buffer.WriteString("window.IP_ADDR_THRESHOLD_ACTIONS = ")
buffer.Write(ipAddrThresholdActionsJSON)
buffer.Write([]byte{'\n', '\n'})
}
fp, err := os.OpenFile(filepath.Clean(Tea.PublicFile("/js/components.js")), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777)
if err != nil {
return err
}
_, err = io.Copy(fp, buffer)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,13 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package gen
import "testing"
func TestGenerate(t *testing.T) {
err := Generate()
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}

View File

@@ -326,6 +326,16 @@ func (this *AdminNode) listenSock() error {
case "demo":
teaconst.IsDemoMode = !teaconst.IsDemoMode
_ = cmd.ReplyOk()
case "info":
exePath, _ := os.Executable()
_ = cmd.Reply(&gosock.Command{
Code: "info",
Params: map[string]interface{}{
"pid": os.Getpid(),
"version": teaconst.Version,
"path": exePath,
},
})
}
})

View File

@@ -27,11 +27,11 @@ type RPCClient struct {
apiConfig *configs.APIConfig
conns []*grpc.ClientConn
locker sync.Mutex
locker sync.RWMutex
}
// NewRPCClient 构造新的RPC客户端
func NewRPCClient(apiConfig *configs.APIConfig) (*RPCClient, error) {
func NewRPCClient(apiConfig *configs.APIConfig, isPrimary bool) (*RPCClient, error) {
if apiConfig == nil {
return nil, errors.New("api config should not be nil")
}
@@ -46,7 +46,9 @@ func NewRPCClient(apiConfig *configs.APIConfig) (*RPCClient, error) {
}
// 设置RPC
dao.SetRPC(client)
if isPrimary {
dao.SetRPC(client)
}
return client, nil
}
@@ -364,6 +366,14 @@ func (this *RPCClient) ACMETaskRPC() pb.ACMETaskServiceClient {
return pb.NewACMETaskServiceClient(this.pickConn())
}
func (this *RPCClient) ACMEProviderRPC() pb.ACMEProviderServiceClient {
return pb.NewACMEProviderServiceClient(this.pickConn())
}
func (this *RPCClient) ACMEProviderAccountRPC() pb.ACMEProviderAccountServiceClient {
return pb.NewACMEProviderAccountServiceClient(this.pickConn())
}
func (this *RPCClient) UserRPC() pb.UserServiceClient {
return pb.NewUserServiceClient(this.pickConn())
}
@@ -372,6 +382,18 @@ func (this *RPCClient) UserBillRPC() pb.UserBillServiceClient {
return pb.NewUserBillServiceClient(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())
}
@@ -456,6 +478,14 @@ 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())
}
// Context 构造Admin上下文
func (this *RPCClient) Context(adminId int64) context.Context {
ctx := context.Background()
@@ -505,7 +535,11 @@ func (this *RPCClient) APIContext(apiNodeId int64) context.Context {
// UpdateConfig 修改配置
func (this *RPCClient) UpdateConfig(config *configs.APIConfig) error {
this.apiConfig = config
return this.init()
this.locker.Lock()
err := this.init()
this.locker.Unlock()
return err
}
// 初始化
@@ -583,3 +617,20 @@ func (this *RPCClient) pickConn() *grpc.ClientConn {
return this.conns[rands.Int(0, len(this.conns)-1)]
}
// Close 关闭
func (this *RPCClient) Close() error {
this.locker.Lock()
defer this.locker.Unlock()
var lastErr error
for _, conn := range this.conns {
var err = conn.Close()
if err != nil {
lastErr = err
continue
}
}
return lastErr
}

View File

@@ -20,7 +20,7 @@ func SharedRPC() (*RPCClient, error) {
if err != nil {
return nil, err
}
client, err := NewRPCClient(config)
client, err := NewRPCClient(config, true)
if err != nil {
return nil, err
}

View File

@@ -2,6 +2,8 @@ package setup
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"os"
)
var isConfigured bool
@@ -16,3 +18,16 @@ func IsConfigured() bool {
isConfigured = err == nil
return isConfigured
}
// IsNewInstalled IsNew 检查是否新安装
func IsNewInstalled() bool {
homeDir, err := os.UserHomeDir()
if err != nil {
return false
}
_, err = os.Stat(homeDir + "/." + teaconst.ProcessName + "/api.yaml")
if err != nil {
return true
}
return false
}

View File

@@ -66,7 +66,7 @@ func (this *SyncAPINodesTask) Loop() error {
}
newEndpoints := []string{}
for _, node := range resp.Nodes {
for _, node := range resp.ApiNodes {
if !node.IsOn {
continue
}

View File

@@ -1,6 +1,7 @@
package utils
import (
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/miekg/dns"
)
@@ -16,13 +17,19 @@ func LookupCNAME(host string) (string, error) {
m.SetQuestion(host+".", dns.TypeCNAME)
m.RecursionDesired = true
r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
if err != nil {
return "", err
}
if len(r.Answer) == 0 {
return "", nil
}
return r.Answer[0].(*dns.CNAME).Target, nil
var lastErr error
for _, serverAddr := range config.Servers {
r, _, err := c.Exchange(m, configutils.QuoteIP(serverAddr)+":"+config.Port)
if err != nil {
lastErr = err
continue
}
if len(r.Answer) == 0 {
continue
}
return r.Answer[0].(*dns.CNAME).Target, nil
}
return "", lastErr
}

View File

@@ -19,6 +19,8 @@ type ParentAction struct {
actions.ActionObject
rpcClient *rpc.RPCClient
ctx context.Context
}
// Parent 可以调用自身的一个简便方法
@@ -117,6 +119,10 @@ func (this *ParentAction) RPC() *rpc.RPCClient {
// AdminContext 获取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 {
@@ -125,7 +131,8 @@ func (this *ParentAction) AdminContext() context.Context {
}
this.rpcClient = rpcClient
}
return this.rpcClient.Context(this.AdminId())
this.ctx = this.rpcClient.Context(this.AdminId())
return this.ctx
}
// ViewData 视图里可以使用的数据

View File

@@ -13,16 +13,35 @@ type DeleteAction struct {
func (this *DeleteAction) RunPost(params struct {
NodeId int64
}) {
// TODO 检查权限
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "删除API节点 %d", params.NodeId)
_, err := this.RPC().APINodeRPC().DeleteAPINode(this.AdminContext(), &pb.DeleteAPINodeRequest{NodeId: params.NodeId})
// 检查是否是唯一的节点
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
var apiNode = nodeResp.ApiNode
if apiNode == nil {
this.Success()
}
if apiNode.IsOn {
countResp, err := this.RPC().APINodeRPC().CountAllEnabledAndOnAPINodes(this.AdminContext(), &pb.CountAllEnabledAndOnAPINodesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
if countResp.Count == 1 {
this.Fail("无法删除此节点必须保留至少一个可用的API节点")
}
}
_, err = this.RPC().APINodeRPC().DeleteAPINode(this.AdminContext(), &pb.DeleteAPINodeRequest{ApiNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "删除API节点 %d", params.NodeId)
this.Success()
}

View File

@@ -41,7 +41,7 @@ func (this *IndexAction) RunGet(params struct{}) {
return
}
for _, node := range nodesResp.Nodes {
for _, node := range nodesResp.ApiNodes {
// 状态
status := &nodeconfigs.NodeStatus{}
if len(node.StatusJSON) > 0 {

View File

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

View File

@@ -20,12 +20,12 @@ func (this *IndexAction) Init() {
func (this *IndexAction) RunGet(params struct {
NodeId int64
}) {
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{NodeId: params.NodeId})
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
node := nodeResp.Node
node := nodeResp.ApiNode
if node == nil {
this.NotFound("apiNode", params.NodeId)
return

View File

@@ -21,12 +21,12 @@ func (this *InstallAction) RunGet(params struct {
NodeId int64
}) {
// API节点信息
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{NodeId: params.NodeId})
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
node := nodeResp.Node
node := nodeResp.ApiNode
if node == nil {
this.NotFound("apiNode", params.NodeId)
return

View File

@@ -31,12 +31,12 @@ func (this *LogsAction) RunGet(params struct {
this.Data["keyword"] = params.Keyword
this.Data["level"] = params.Level
apiNodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{NodeId: params.NodeId})
apiNodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
apiNode := apiNodeResp.Node
apiNode := apiNodeResp.ApiNode
if apiNode == nil {
this.NotFound("apiNode", params.NodeId)
return

View File

@@ -23,13 +23,13 @@ func (this *UpdateAction) RunGet(params struct {
NodeId int64
}) {
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{
NodeId: params.NodeId,
ApiNodeId: params.NodeId,
})
if err != nil {
this.ErrorPage(err)
return
}
node := nodeResp.Node
node := nodeResp.ApiNode
if node == nil {
this.WriteString("要操作的节点不存在")
return
@@ -281,7 +281,7 @@ func (this *UpdateAction) RunPost(params struct {
}
_, err = this.RPC().APINodeRPC().UpdateAPINode(this.AdminContext(), &pb.UpdateAPINodeRequest{
NodeId: params.NodeId,
ApiNodeId: params.NodeId,
Name: params.Name,
Description: params.Description,
HttpJSON: httpJSON,

View File

@@ -75,7 +75,7 @@ func (this *CreateNodeAction) RunGet(params struct {
this.ErrorPage(err)
return
}
apiNodes := apiNodesResp.Nodes
apiNodes := apiNodesResp.ApiNodes
apiEndpoints := []string{}
for _, apiNode := range apiNodes {
if !apiNode.IsOn {

View File

@@ -19,6 +19,7 @@ func init() {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
Helper(clusters.NewClusterHelper()).
Data("teaMenu", "clusters").
Prefix("/clusters/cluster").
Get("", new(IndexAction)).
Get("/nodes", new(NodesAction)).

View File

@@ -49,7 +49,7 @@ func (this *InstallNodesAction) RunGet(params struct {
this.ErrorPage(err)
return
}
for _, apiNode := range apiNodesResp.Nodes {
for _, apiNode := range apiNodesResp.ApiNodes {
if !apiNode.IsOn {
continue
}

View File

@@ -70,7 +70,7 @@ func (this *InstallAction) RunGet(params struct {
this.ErrorPage(err)
return
}
apiNodes := apiNodesResp.Nodes
apiNodes := apiNodesResp.ApiNodes
apiEndpoints := []string{}
for _, apiNode := range apiNodes {
if !apiNode.IsOn {

View File

@@ -69,6 +69,20 @@ func (this *LogsAction) RunGet(params struct {
logs := []maps.Map{}
for _, log := range logsResp.NodeLogs {
// 服务信息
var serverMap = maps.Map{"id": 0}
if log.ServerId > 0 {
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.AdminContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: log.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var server = serverResp.Server
if server != nil {
serverMap = maps.Map{"id": server.Id, "name": server.Name}
}
}
logs = append(logs, maps.Map{
"tag": log.Tag,
"description": log.Description,
@@ -76,6 +90,7 @@ func (this *LogsAction) RunGet(params struct {
"level": log.Level,
"isToday": timeutil.FormatTime("Y-m-d", log.CreatedAt) == timeutil.Format("Y-m-d"),
"count": log.Count,
"server": serverMap,
})
}
this.Data["logs"] = logs

View File

@@ -4,6 +4,7 @@ package nodeutils
import (
"errors"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
@@ -49,7 +50,7 @@ func InitNodeInfo(parentAction *actionutils.ParentAction, nodeId int64) (*pb.Nod
var query = "clusterId=" + types.String(clusterId) + "&nodeId=" + types.String(nodeId)
var menuItem = parentAction.Data.GetString("secondMenuItem")
parentAction.Data["leftMenuItems"] = []maps.Map{
var menuItems = []maps.Map{
{
"name": "基础设置",
"url": prefix + "/update?" + query,
@@ -65,11 +66,17 @@ func InitNodeInfo(parentAction *actionutils.ParentAction, nodeId int64) (*pb.Nod
"url": prefix + "/settings/cache?" + query,
"isActive": menuItem == "cache",
},
{
"name": "阈值设置",
"url": prefix + "/settings/thresholds?" + query,
"isActive": menuItem == "threshold",
},
}
if teaconst.IsPlus {
menuItems = append(menuItems, []maps.Map{
{
"name": "阈值设置",
"url": prefix + "/settings/thresholds?" + query,
"isActive": menuItem == "threshold",
},
}...)
}
menuItems = append(menuItems, []maps.Map{
{
"name": "SSH设置",
"url": prefix + "/settings/ssh?" + query,
@@ -80,7 +87,8 @@ func InitNodeInfo(parentAction *actionutils.ParentAction, nodeId int64) (*pb.Nod
"url": prefix + "/settings/system?" + query,
"isActive": menuItem == "system",
},
}
}...)
parentAction.Data["leftMenuItems"] = menuItems
return nodeResp.Node, nil
}

View File

@@ -4,6 +4,7 @@ import (
"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"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
@@ -21,6 +22,7 @@ func (this *IndexAction) Init() {
func (this *IndexAction) RunGet(params struct {
ClusterId int64
}) {
// 基本信息
clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
@@ -53,10 +55,20 @@ func (this *IndexAction) RunGet(params struct {
}
this.Data["grant"] = grantMap
// 时区
this.Data["timeZoneGroups"] = nodeconfigs.FindAllTimeZoneGroups()
this.Data["timeZoneLocations"] = nodeconfigs.FindAllTimeZoneLocations()
if len(cluster.TimeZone) == 0 {
cluster.TimeZone = nodeconfigs.DefaultTimeZoneLocation
}
this.Data["timeZoneLocation"] = nodeconfigs.FindTimeZoneLocation(cluster.TimeZone)
this.Data["cluster"] = maps.Map{
"id": cluster.Id,
"name": cluster.Name,
"installDir": cluster.InstallDir,
"timeZone": cluster.TimeZone,
}
this.Show()
@@ -68,6 +80,7 @@ func (this *IndexAction) RunPost(params struct {
Name string
GrantId int64
InstallDir string
TimeZone string
Must *actions.Must
}) {
@@ -83,6 +96,7 @@ func (this *IndexAction) RunPost(params struct {
Name: params.Name,
NodeGrantId: params.GrantId,
InstallDir: params.InstallDir,
TimeZone: params.TimeZone,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -12,7 +12,8 @@ func NewClustersHelper() *ClustersHelper {
return &ClustersHelper{}
}
func (this *ClustersHelper) BeforeAction(action *actions.ActionObject) {
func (this *ClustersHelper) BeforeAction(actionPtr actions.ActionWrapper) {
var action = actionPtr.Object()
if action.Request.Method != http.MethodGet {
return
}

View File

@@ -28,6 +28,7 @@ func (this *CreateAction) RunPost(params struct {
Username string
Password string
PrivateKey string
Passphrase string
Description string
Must *actions.Must
@@ -58,6 +59,7 @@ func (this *CreateAction) RunPost(params struct {
Username: params.Username,
Password: params.Password,
PrivateKey: params.PrivateKey,
Passphrase: params.Passphrase,
Description: params.Description,
NodeId: 0,
})

View File

@@ -29,6 +29,7 @@ func (this *CreatePopupAction) RunPost(params struct {
Username string
Password string
PrivateKey string
Passphrase string
Description string
Must *actions.Must
@@ -59,6 +60,7 @@ func (this *CreatePopupAction) RunPost(params struct {
Username: params.Username,
Password: params.Password,
PrivateKey: params.PrivateKey,
Passphrase: params.Passphrase,
Description: params.Description,
NodeId: 0,
})

View File

@@ -40,6 +40,7 @@ func (this *GrantAction) RunGet(params struct {
"username": grant.Username,
"password": strings.Repeat("*", len(grant.Password)),
"privateKey": grant.PrivateKey,
"passphrase": strings.Repeat("*", len(grant.Passphrase)),
"description": grant.Description,
"su": grant.Su,
}

View File

@@ -12,6 +12,7 @@ func init() {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
Helper(clusterutils.NewClustersHelper()).
Data("teaMenu", "clusters").
Data("teaSubMenu", "grant").
Prefix("/clusters/grants").

View File

@@ -43,6 +43,7 @@ func (this *UpdateAction) RunGet(params struct {
"username": grant.Username,
"password": grant.Password,
"privateKey": grant.PrivateKey,
"passphrase": grant.Passphrase,
"description": grant.Description,
"su": grant.Su,
}
@@ -57,6 +58,7 @@ func (this *UpdateAction) RunPost(params struct {
Username string
Password string
PrivateKey string
Passphrase string
Description string
Must *actions.Must
@@ -93,6 +95,7 @@ func (this *UpdateAction) RunPost(params struct {
Username: params.Username,
Password: params.Password,
PrivateKey: params.PrivateKey,
Passphrase: params.Passphrase,
Description: params.Description,
NodeId: 0,
})

View File

@@ -43,6 +43,7 @@ func (this *UpdatePopupAction) RunGet(params struct {
"password": grant.Password,
"description": grant.Description,
"privateKey": grant.PrivateKey,
"passphrase": grant.Passphrase,
}
this.Show()
@@ -56,6 +57,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
Username string
Password string
PrivateKey string
Passphrase string
Description string
Must *actions.Must
@@ -91,6 +93,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
Username: params.Username,
Password: params.Password,
PrivateKey: params.PrivateKey,
Passphrase: params.Passphrase,
Description: params.Description,
NodeId: params.NodeId,
})

View File

@@ -121,6 +121,10 @@ func (this *IndexAction) RunGet(params struct {
this.ErrorPage(err)
}
if cluster.TimeZone == nodeconfigs.DefaultTimeZoneLocation {
cluster.TimeZone = ""
}
clusterMaps = append(clusterMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
@@ -132,6 +136,7 @@ func (this *IndexAction) RunGet(params struct {
"dnsName": cluster.DnsName,
"dnsDomainName": dnsDomainName,
"countServers": countServersResp.Count,
"timeZone": cluster.TimeZone,
})
}
}

View File

@@ -12,6 +12,7 @@ func init() {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
Helper(clusterutils.NewClustersHelper()).
Data("teaMenu", "clusters").
Prefix("/clusters").
Get("", new(IndexAction)).
GetPost("/create", new(CreateAction)).

View File

@@ -2,6 +2,7 @@ package logs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
@@ -12,6 +13,11 @@ type IndexAction struct {
}
func (this *IndexAction) Init() {
if this.ParamString("type") == "unread" {
this.FirstMenu("unread")
} else {
this.FirstMenu("index")
}
}
func (this *IndexAction) RunGet(params struct {
@@ -19,19 +25,34 @@ func (this *IndexAction) RunGet(params struct {
DayTo string
Keyword string
Level string
Type string
}) {
this.Data["dayFrom"] = params.DayFrom
this.Data["dayTo"] = params.DayTo
this.Data["keyword"] = params.Keyword
this.Data["level"] = params.Level
this.Data["type"] = params.Type
// 未读数量
countUnreadResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
Role: nodeconfigs.NodeRoleNode,
IsUnread: true,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["countUnreadLogs"] = countUnreadResp.Count
// 日志数量
countResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
NodeId: 0,
Role: "node",
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
NodeId: 0,
Role: nodeconfigs.NodeRoleNode,
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
IsUnread: params.Type == "unread",
})
if err != nil {
this.ErrorPage(err)
@@ -42,14 +63,15 @@ func (this *IndexAction) RunGet(params struct {
this.Data["page"] = page.AsHTML()
logsResp, err := this.RPC().NodeLogRPC().ListNodeLogs(this.AdminContext(), &pb.ListNodeLogsRequest{
NodeId: 0,
Role: "node",
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
Offset: page.Offset,
Size: page.Size,
NodeId: 0,
Role: nodeconfigs.NodeRoleNode,
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
IsUnread: params.Type == "unread",
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
@@ -68,13 +90,29 @@ func (this *IndexAction) RunGet(params struct {
continue
}
// 服务信息
var serverMap = maps.Map{"id": 0}
if log.ServerId > 0 {
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.AdminContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: log.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var server = serverResp.Server
if server != nil {
serverMap = maps.Map{"id": server.Id, "name": server.Name}
}
}
logs = append(logs, maps.Map{
"id": log.Id,
"tag": log.Tag,
"description": log.Description,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", log.CreatedAt),
"level": log.Level,
"isToday": timeutil.FormatTime("Y-m-d", log.CreatedAt) == timeutil.Format("Y-m-d"),
"count": log.Count,
"isRead": log.IsRead,
"node": maps.Map{
"id": node.Id,
"cluster": maps.Map{
@@ -83,6 +121,7 @@ func (this *IndexAction) RunGet(params struct {
},
"name": node.Name,
},
"server": serverMap,
})
}
this.Data["logs"] = logs

View File

@@ -14,6 +14,8 @@ func init() {
Data("teaSubMenu", "log").
Prefix("/clusters/logs").
Get("", new(IndexAction)).
Post("/readLogs", new(ReadLogsAction)).
Post("/readAllLogs", new(ReadAllLogsAction)).
EndAll()
})
}

View File

@@ -0,0 +1,24 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package logs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type ReadAllLogsAction struct {
actionutils.ParentAction
}
func (this *ReadAllLogsAction) RunPost(params struct {
LogIds []int64
}) {
_, err := this.RPC().NodeLogRPC().UpdateAllNodeLogsRead(this.AdminContext(), &pb.UpdateAllNodeLogsReadRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,26 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package logs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type ReadLogsAction struct {
actionutils.ParentAction
}
func (this *ReadLogsAction) RunPost(params struct {
LogIds []int64
}) {
_, err := this.RPC().NodeLogRPC().UpdateNodeLogsRead(this.AdminContext(), &pb.UpdateNodeLogsReadRequest{
NodeLogIds: params.LogIds,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -21,6 +21,7 @@ func (this *SelectPopupAction) Init() {
func (this *SelectPopupAction) RunGet(params struct {
SelectedClusterIds string
Keyword string
PageSize int64
}) {
this.Data["keyword"] = params.Keyword
@@ -36,6 +37,9 @@ func (this *SelectPopupAction) RunGet(params struct {
var count = countResp.Count
var page = this.NewPage(count)
page.Size = 6
if params.PageSize > 0 {
page.Size = params.PageSize
}
this.Data["page"] = page.AsHTML()
clustersResp, err := this.RPC().NodeClusterRPC().ListEnabledNodeClusters(this.AdminContext(), &pb.ListEnabledNodeClustersRequest{

View File

@@ -55,13 +55,16 @@ func (this *IndexAction) RunPost(params struct{}) {
this.Data["dashboard"] = maps.Map{
"defaultClusterId": resp.DefaultNodeClusterId,
"countServers": resp.CountServers,
"countNodeClusters": resp.CountNodeClusters,
"countNodes": resp.CountNodes,
"countUsers": resp.CountUsers,
"countAPINodes": resp.CountAPINodes,
"countDBNodes": resp.CountDBNodes,
"countUserNodes": resp.CountUserNodes,
"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,
"canGoServers": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeServer),
"canGoNodes": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeNode),

View File

@@ -6,7 +6,7 @@ import (
"github.com/iwind/TeaGo/maps"
)
// 域名列表选项
// DomainOptionsAction 域名列表选项
type DomainOptionsAction struct {
actionutils.ParentAction
}
@@ -23,8 +23,8 @@ func (this *DomainOptionsAction) RunPost(params struct {
}
domainMaps := []maps.Map{}
for _, domain := range domainsResp.DnsDomains {
// 未开启的先跳过
if !domain.IsOn {
// 未开启或者已删除的先跳过
if !domain.IsOn || domain.IsDeleted {
continue
}

View File

@@ -77,7 +77,7 @@ func ValidateRecordName(name string) bool {
strings.HasSuffix(piece, "-") ||
strings.Contains(piece, "--") ||
len(piece) > 63 ||
!regexp.MustCompile(`^[a-z0-9-]+$`).MatchString(piece) {
!regexp.MustCompile(`^[_a-z0-9-]+$`).MatchString(piece) {
return false
}
}

View File

@@ -0,0 +1,27 @@
package domains
import (
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type RecoverAction struct {
actionutils.ParentAction
}
func (this *RecoverAction) RunPost(params struct {
DomainId int64
}) {
// 记录日志
defer this.CreateLog(oplogs.LevelInfo, "从DNS服务商中恢复域名 %d", params.DomainId)
// 执行恢复
_, err := this.RPC().DNSDomainRPC().RecoverDNSDomain(this.AdminContext(), &pb.RecoverDNSDomainRequest{DnsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -52,7 +52,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
// TODO 检查DomainId
// 记录日志
defer this.CreateLog(oplogs.LevelInfo, "修改管理域名到DNS服务商 %d", params.DomainId)
defer this.CreateLog(oplogs.LevelInfo, "修改DNS服务商域名 %d", params.DomainId)
params.Must.
Field("name", params.Name).

View File

@@ -43,6 +43,7 @@ func init() {
GetPost("/createPopup", new(domains.CreatePopupAction)).
GetPost("/updatePopup", new(domains.UpdatePopupAction)).
Post("/delete", new(domains.DeleteAction)).
Post("/recover", new(domains.RecoverAction)).
Post("/sync", new(domains.SyncAction)).
Get("/routesPopup", new(domains.RoutesPopupAction)).
GetPost("/selectPopup", new(domains.SelectPopupAction)).

View File

@@ -83,6 +83,7 @@ func (this *ProviderAction) RunGet(params struct {
"name": domain.Name,
"isOn": domain.IsOn,
"isUp": domain.IsUp,
"isDeleted": domain.IsDeleted,
"dataUpdatedTime": dataUpdatedTime,
"countRoutes": len(domain.Routes),
"countServerRecords": domain.CountServerRecords,

View File

@@ -1,38 +0,0 @@
package bills
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type GenerateAction struct {
actionutils.ParentAction
}
func (this *GenerateAction) Init() {
this.Nav("", "", "generate")
}
func (this *GenerateAction) RunGet(params struct{}) {
this.Data["month"] = timeutil.Format("Ym", time.Now().AddDate(0, -1, 0))
this.Show()
}
func (this *GenerateAction) RunPost(params struct {
Month string
Must *actions.Must
}) {
defer this.CreateLogInfo("手动生成上个月(" + params.Month + ")账单")
_, err := this.RPC().UserBillRPC().GenerateAllUserBills(this.AdminContext(), &pb.GenerateAllUserBillsRequest{Month: params.Month})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -1,65 +0,0 @@
package bills
import (
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "index")
}
func (this *IndexAction) RunGet(params struct {
PaidFlag int32 `default:"-1"`
UserId int64
Month string
}) {
countResp, err := this.RPC().UserBillRPC().CountAllUserBills(this.AdminContext(), &pb.CountAllUserBillsRequest{})
if err != nil {
this.ErrorPage(err)
return
}
page := this.NewPage(countResp.Count)
this.Data["page"] = page.AsHTML()
billsResp, err := this.RPC().UserBillRPC().ListUserBills(this.AdminContext(), &pb.ListUserBillsRequest{
PaidFlag: params.PaidFlag,
UserId: params.UserId,
Month: params.Month,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
billMaps := []maps.Map{}
for _, bill := range billsResp.UserBills {
var userMap maps.Map = nil
if bill.User != nil {
userMap = maps.Map{
"id": bill.User.Id,
"fullname": bill.User.Fullname,
}
}
billMaps = append(billMaps, maps.Map{
"id": bill.Id,
"isPaid": bill.IsPaid,
"month": bill.Month,
"amount": fmt.Sprintf("%.2f", bill.Amount),
"typeName": bill.TypeName,
"user": userMap,
"description": bill.Description,
})
}
this.Data["bills"] = billMaps
this.Show()
}

View File

@@ -1,22 +0,0 @@
package bills
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.AdminModuleCodeFinance)).
Data("teaMenu", "finance").
// 财务管理
Prefix("/finance/bills").
Get("", new(IndexAction)).
GetPost("/generate", new(GenerateAction)).
EndAll()
})
}

View File

@@ -43,6 +43,12 @@ func (this *IndexAction) RunGet(params struct {
return
}
//// 是否新安装
if setup.IsNewInstalled() {
this.RedirectURL("/setup/confirm")
return
}
// 已登录跳转到dashboard
if params.Auth.IsUser() {
this.RedirectURL("/dashboard")

View File

@@ -48,6 +48,7 @@ func SendMessageToCluster(ctx context.Context, clusterId int64, code string, msg
wg := &sync.WaitGroup{}
wg.Add(len(nodes))
for _, node := range nodes {
// TODO 检查是否在线
@@ -68,7 +69,7 @@ func SendMessageToCluster(ctx context.Context, clusterId int64, code string, msg
apiNodeId := node.ConnectedAPINodeIds[0]
rpcClient, ok := rpcMap[apiNodeId]
if !ok {
apiNodeResp, err := defaultRPCClient.APINodeRPC().FindEnabledAPINode(ctx, &pb.FindEnabledAPINodeRequest{NodeId: apiNodeId})
apiNodeResp, err := defaultRPCClient.APINodeRPC().FindEnabledAPINode(ctx, &pb.FindEnabledAPINodeRequest{ApiNodeId: apiNodeId})
if err != nil {
locker.Lock()
results = append(results, &MessageResult{
@@ -82,7 +83,7 @@ func SendMessageToCluster(ctx context.Context, clusterId int64, code string, msg
continue
}
if apiNodeResp.Node == nil {
if apiNodeResp.ApiNode == nil {
locker.Lock()
results = append(results, &MessageResult{
NodeId: node.Id,
@@ -94,7 +95,7 @@ func SendMessageToCluster(ctx context.Context, clusterId int64, code string, msg
wg.Done()
continue
}
apiNode := apiNodeResp.Node
apiNode := apiNodeResp.ApiNode
apiRPCClient, err := rpc.NewRPCClient(&configs.APIConfig{
RPC: struct {
@@ -104,7 +105,7 @@ func SendMessageToCluster(ctx context.Context, clusterId int64, code string, msg
},
NodeId: apiNode.UniqueId,
Secret: apiNode.Secret,
})
}, false)
if err != nil {
locker.Lock()
results = append(results, &MessageResult{
@@ -162,6 +163,11 @@ func SendMessageToCluster(ctx context.Context, clusterId int64, code string, msg
})
}
// 关闭RPC
for _, rpcClient := range rpcMap {
_ = rpcClient.Close()
}
return
}
@@ -243,7 +249,7 @@ func SendMessageToNodeIds(ctx context.Context, nodeIds []int64, code string, msg
apiNodeId := node.ConnectedAPINodeIds[0]
rpcClient, ok := rpcMap[apiNodeId]
if !ok {
apiNodeResp, err := defaultRPCClient.APINodeRPC().FindEnabledAPINode(ctx, &pb.FindEnabledAPINodeRequest{NodeId: apiNodeId})
apiNodeResp, err := defaultRPCClient.APINodeRPC().FindEnabledAPINode(ctx, &pb.FindEnabledAPINodeRequest{ApiNodeId: apiNodeId})
if err != nil {
locker.Lock()
results = append(results, &MessageResult{
@@ -257,7 +263,7 @@ func SendMessageToNodeIds(ctx context.Context, nodeIds []int64, code string, msg
continue
}
if apiNodeResp.Node == nil {
if apiNodeResp.ApiNode == nil {
locker.Lock()
results = append(results, &MessageResult{
NodeId: node.Id,
@@ -269,7 +275,7 @@ func SendMessageToNodeIds(ctx context.Context, nodeIds []int64, code string, msg
wg.Done()
continue
}
apiNode := apiNodeResp.Node
apiNode := apiNodeResp.ApiNode
apiRPCClient, err := rpc.NewRPCClient(&configs.APIConfig{
RPC: struct {
@@ -279,7 +285,7 @@ func SendMessageToNodeIds(ctx context.Context, nodeIds []int64, code string, msg
},
NodeId: apiNode.UniqueId,
Secret: apiNode.Secret,
})
}, false)
if err != nil {
locker.Lock()
results = append(results, &MessageResult{
@@ -337,5 +343,10 @@ func SendMessageToNodeIds(ctx context.Context, nodeIds []int64, code string, msg
})
}
// 关闭RPC
for _, rpcClient := range rpcMap {
_ = rpcClient.Close()
}
return
}

View File

@@ -1,38 +0,0 @@
package cluster
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/cluster/node"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Helper(new(clusterutils.ClusterHelper)).
Data("teaMenu", "ns").
Data("teaSubMenu", "cluster").
Prefix("/ns/clusters/cluster").
Get("", new(IndexAction)).
GetPost("/delete", new(DeleteAction)).
GetPost("/createNode", new(CreateNodeAction)).
Post("/deleteNode", new(DeleteNodeAction)).
Get("/upgradeRemote", new(UpgradeRemoteAction)).
GetPost("/updateNodeSSH", new(UpdateNodeSSHAction)).
// 节点相关
Prefix("/ns/clusters/cluster/node").
Get("", new(node.IndexAction)).
Get("/logs", new(node.LogsAction)).
GetPost("/update", new(node.UpdateAction)).
GetPost("/install", new(node.InstallAction)).
Post("/status", new(node.StatusAction)).
Post("/updateInstallStatus", new(node.UpdateInstallStatusAction)).
Post("/start", new(node.StartAction)).
Post("/stop", new(node.StopAction)).
EndAll()
})
}

View File

@@ -73,7 +73,7 @@ func (this *InstallAction) RunGet(params struct {
this.ErrorPage(err)
return
}
apiNodes := apiNodesResp.Nodes
apiNodes := apiNodesResp.ApiNodes
apiEndpoints := []string{}
for _, apiNode := range apiNodes {
if !apiNode.IsOn {

View File

@@ -1,21 +0,0 @@
package accessLog
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Helper(new(clusterutils.ClusterHelper)).
Data("teaMenu", "ns").
Data("teaSubMenu", "cluster").
Prefix("/ns/clusters/cluster/settings/accessLog").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -1,21 +0,0 @@
package cluster
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Helper(new(clusterutils.ClusterHelper)).
Data("teaMenu", "ns").
Data("teaSubMenu", "cluster").
Prefix("/ns/clusters/cluster/settings").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -1,21 +0,0 @@
package recursion
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Helper(new(clusterutils.ClusterHelper)).
Data("teaMenu", "ns").
Data("teaSubMenu", "cluster").
Prefix("/ns/clusters/cluster/settings/recursion").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -1,21 +0,0 @@
package clusters
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.AdminModuleCodeNS)).
Data("teaMenu", "ns").
Data("teaSubMenu", "cluster").
Prefix("/ns/clusters").
Get("", new(IndexAction)).
GetPost("/create", new(CreateAction)).
Post("/options", new(OptionsAction)).
EndAll()
})
}

View File

@@ -1,19 +0,0 @@
package logs
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.AdminModuleCodeNS)).
Data("teaMenu", "ns").
Data("teaSubMenu", "log").
Prefix("/ns/clusters/logs").
Get("", new(IndexAction)).
EndAll()
})
}

View File

@@ -1,25 +0,0 @@
package clusters
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.AdminModuleCodeNS)).
Data("teaMenu", "ns").
Data("teaSubMenu", "route").
Prefix("/ns/routes").
Get("", new(IndexAction)).
Get("/route", new(RouteAction)).
GetPost("/createPopup", new(CreatePopupAction)).
GetPost("/updatePopup", new(UpdatePopupAction)).
Post("/delete", new(DeleteAction)).
Post("/sort", new(SortAction)).
Post("/options", new(OptionsAction)).
EndAll()
})
}

View File

@@ -1,23 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accesslogs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/settings/settingutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Helper(new(settingutils.Helper)).
Data("teaMenu", "ns").
Data("teaSubMenu", "setting").
Prefix("/ns/settings/accesslogs").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -1,19 +0,0 @@
package users
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.AdminModuleCodeNS)).
Data("teaMenu", "ns").
Data("teaSubMenu", "domain").
Prefix("/ns/users").
Post("/options", new(OptionsAction)).
EndAll()
})
}

View File

@@ -42,7 +42,7 @@ func (this *UpdateHostsAction) RunPost(params struct {
},
NodeId: params.NodeId,
Secret: params.NodeSecret,
})
}, false)
if err != nil {
this.FailField("host", "测试API节点时出错请检查配置错误信息"+err.Error())
}
@@ -51,6 +51,10 @@ func (this *UpdateHostsAction) RunPost(params struct {
this.FailField("host", "无法连接此API节点错误信息"+err.Error())
}
defer func() {
_ = client.Close()
}()
// 获取管理员节点信息
apiTokensResp, err := client.APITokenRPC().FindAllEnabledAPITokens(client.APIContext(0), &pb.FindAllEnabledAPITokensRequest{Role: "admin"})
if err != nil {
@@ -69,7 +73,7 @@ func (this *UpdateHostsAction) RunPost(params struct {
this.Fail("获取API节点列表失败错误信息" + err.Error())
}
var endpoints = []string{}
for _, node := range nodesResp.Nodes {
for _, node := range nodesResp.ApiNodes {
if !node.IsOn {
continue
}
@@ -144,7 +148,7 @@ func (this *UpdateHostsAction) RunPost(params struct {
// 保存
_, err = client.APINodeRPC().UpdateAPINode(client.Context(0), &pb.UpdateAPINodeRequest{
NodeId: node.Id,
ApiNodeId: node.Id,
Name: node.Name,
Description: node.Description,
HttpJSON: node.HttpJSON,

View File

@@ -49,10 +49,15 @@ func (this *ValidateApiAction) RunPost(params struct {
},
NodeId: params.NodeId,
Secret: params.NodeSecret,
})
}, false)
if err != nil {
this.FailField("host", "测试API节点时出错请检查配置错误信息"+err.Error())
}
defer func() {
_ = client.Close()
}()
_, err = client.APINodeRPC().FindCurrentAPINodeVersion(client.APIContext(0), &pb.FindCurrentAPINodeVersionRequest{})
if err != nil {
this.FailField("host", "无法连接此API节点错误信息"+err.Error())
@@ -64,7 +69,7 @@ func (this *ValidateApiAction) RunPost(params struct {
this.Fail("获取API节点列表失败错误信息" + err.Error())
}
var hosts = []string{}
for _, node := range nodesResp.Nodes {
for _, node := range nodesResp.ApiNodes {
if !node.IsOn {
continue
}

View File

@@ -1,26 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package ipbox
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.AdminModuleCodeServer)).
Prefix("/servers/accesslogs").
Data("teaMenu", "servers").
Data("teaSubMenu", "accesslog").
Get("", new(IndexAction)).
GetPost("/createPopup", new(CreatePopupAction)).
Get("/policy", new(PolicyAction)).
GetPost("/test", new(TestAction)).
GetPost("/update", new(UpdateAction)).
Post("/delete", new(DeleteAction)).
EndAll()
})
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"regexp"
"strings"
)
@@ -18,9 +19,10 @@ func (this *AddPortPopupAction) Init() {
}
func (this *AddPortPopupAction) RunGet(params struct {
ServerType string
Protocol string
From string
ServerType string
Protocol string
From string
SupportRange bool
}) {
this.Data["from"] = params.From
@@ -36,10 +38,14 @@ func (this *AddPortPopupAction) RunGet(params struct {
}
this.Data["protocols"] = protocols
this.Data["supportRange"] = params.SupportRange
this.Show()
}
func (this *AddPortPopupAction) RunPost(params struct {
SupportRange bool
Protocol string
Address string
@@ -50,26 +56,69 @@ func (this *AddPortPopupAction) RunPost(params struct {
"protocol": params.Protocol,
"host": "",
"portRange": "",
"minPort": 0,
"maxPort": 0,
}
// TODO 判断端口不能小于1
// TODO 判断端口号不能大于65535
digitRegexp := regexp.MustCompile(`^\d+$`)
if digitRegexp.MatchString(params.Address) {
addr["portRange"] = params.Address
} else if strings.Contains(params.Address, ":") {
var portRegexp = regexp.MustCompile(`^\d+$`)
if portRegexp.MatchString(params.Address) { // 单个端口
addr["portRange"] = this.checkPort(params.Address)
} else if params.SupportRange && regexp.MustCompile(`^\d+\s*-\s*\d+$`).MatchString(params.Address) { // Port1-Port2
addr["portRange"], addr["minPort"], addr["maxPort"] = this.checkPortRange(params.Address)
} else if strings.Contains(params.Address, ":") { // IP:Port
index := strings.LastIndex(params.Address, ":")
addr["host"] = strings.TrimSpace(params.Address[:index])
port := strings.TrimSpace(params.Address[index+1:])
if !digitRegexp.MatchString(port) {
this.Fail("端口只能是一个数字")
if portRegexp.MatchString(port) {
addr["portRange"] = this.checkPort(port)
} else if params.SupportRange && regexp.MustCompile(`^\d+\s*-\s*\d+$`).MatchString(port) { // Port1-Port2
addr["portRange"], addr["minPort"], addr["maxPort"] = this.checkPortRange(port)
} else {
this.FailField("address", "请输入正确的端口或者网络地址")
}
addr["portRange"] = port
} else {
this.Fail("请输入正确的端口或者网络地址")
this.FailField("address", "请输入正确的端口或者网络地址")
}
this.Data["address"] = addr
this.Success()
}
func (this *AddPortPopupAction) checkPort(port string) (portRange string) {
var intPort = types.Int(port)
if intPort < 1 {
this.FailField("address", "端口号不能小于1")
}
if intPort > 65535 {
this.FailField("address", "端口号不能大于65535")
}
return port
}
func (this *AddPortPopupAction) checkPortRange(port string) (portRange string, minPort int, maxPort int) {
var pieces = strings.Split(port, "-")
var piece1 = strings.TrimSpace(pieces[0])
var piece2 = strings.TrimSpace(pieces[1])
var port1 = types.Int(piece1)
var port2 = types.Int(piece2)
if port1 < 1 {
this.FailField("address", "端口号不能小于1")
}
if port1 > 65535 {
this.FailField("address", "端口号不能大于65535")
}
if port2 < 1 {
this.FailField("address", "端口号不能小于1")
}
if port2 > 65535 {
this.FailField("address", "端口号不能大于65535")
}
if port1 > port2 {
port1, port2 = port2, port1
}
return types.String(port1) + "-" + types.String(port2), port1, port2
}

View File

@@ -0,0 +1,100 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accounts
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 CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
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 {
this.ErrorPage(err)
return
}
var providerMaps = []maps.Map{}
for _, provider := range providersResp.AcmeProviders {
providerMaps = append(providerMaps, maps.Map{
"name": provider.Name,
"code": provider.Code,
"description": provider.Description,
"requireEAB": provider.RequireEAB,
"eabDescription": provider.EabDescription,
})
}
this.Data["providers"] = providerMaps
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
Name string
ProviderCode string
EabKid string
EabKey string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var accountId int64
defer func() {
this.CreateLogInfo("创建ACME服务商账号 %d", accountId)
}()
params.Must.
Field("name", params.Name).
Require("请输入账号名称").
Field("providerCode", params.ProviderCode).
Require("请选择服务商")
providerResp, err := this.RPC().ACMEProviderRPC().FindACMEProviderWithCode(this.AdminContext(), &pb.FindACMEProviderWithCodeRequest{AcmeProviderCode: params.ProviderCode})
if err != nil {
this.ErrorPage(err)
return
}
var provider = providerResp.AcmeProvider
if provider == nil {
this.Fail("请选择服务商")
}
if provider.RequireEAB {
params.Must.
Field("eabKid", params.EabKid).
Require("请输入EAB Kid").
Field("eabKey", params.EabKey).
Require("请输入EAB HMAC Key")
}
createResp, err := this.RPC().ACMEProviderAccountRPC().CreateACMEProviderAccount(this.AdminContext(), &pb.CreateACMEProviderAccountRequest{
Name: params.Name,
ProviderCode: params.ProviderCode,
EabKid: params.EabKid,
EabKey: params.EabKey,
})
if err != nil {
this.ErrorPage(err)
return
}
accountId = createResp.AcmeProviderAccountId
this.Success()
}

View File

@@ -0,0 +1,26 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accounts
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 {
AccountId int64
}) {
defer this.CreateLogInfo("删除ACME服务商账号 %d", params.AccountId)
_, err := this.RPC().ACMEProviderAccountRPC().DeleteACMEProviderAccount(this.AdminContext(), &pb.DeleteACMEProviderAccountRequest{AcmeProviderAccountId: params.AccountId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,61 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accounts
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "account")
}
func (this *IndexAction) RunGet(params struct{}) {
countResp, err := this.RPC().ACMEProviderAccountRPC().CountAllEnabledACMEProviderAccounts(this.AdminContext(), &pb.CountAllEnabledACMEProviderAccountsRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var count = countResp.Count
var page = this.NewPage(count)
this.Data["page"] = page.AsHTML()
accountsResp, err := this.RPC().ACMEProviderAccountRPC().ListEnabledACMEProviderAccounts(this.AdminContext(), &pb.ListEnabledACMEProviderAccountsRequest{
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var accountMaps = []maps.Map{}
for _, account := range accountsResp.AcmeProviderAccounts {
var providerMap maps.Map
if account.AcmeProvider != nil {
providerMap = maps.Map{
"name": account.AcmeProvider.Name,
"code": account.AcmeProvider.Code,
"requireEAB": account.AcmeProvider.RequireEAB,
}
}
accountMaps = append(accountMaps, maps.Map{
"id": account.Id,
"isOn": account.IsOn,
"name": account.Name,
"eabKid": account.EabKid,
"eabKey": account.EabKey,
"provider": providerMap,
})
}
this.Data["accounts"] = accountMaps
this.Show()
}

View File

@@ -0,0 +1,108 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accounts
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 {
AccountId int64
}) {
// 账号信息
accountResp, err := this.RPC().ACMEProviderAccountRPC().FindEnabledACMEProviderAccount(this.AdminContext(), &pb.FindEnabledACMEProviderAccountRequest{AcmeProviderAccountId: params.AccountId})
if err != nil {
this.ErrorPage(err)
return
}
var account = accountResp.AcmeProviderAccount
if account == nil {
this.NotFound("ACMEProviderAccount", params.AccountId)
return
}
var providerMap maps.Map
if account.AcmeProvider != nil {
providerMap = maps.Map{
"name": account.AcmeProvider.Name,
"code": account.AcmeProvider.Code,
"description": account.AcmeProvider.Description,
"eabDescription": account.AcmeProvider.EabDescription,
"requireEAB": account.AcmeProvider.RequireEAB,
}
}
this.Data["account"] = maps.Map{
"id": account.Id,
"name": account.Name,
"isOn": account.IsOn,
"providerCode": account.ProviderCode,
"eabKid": account.EabKid,
"eabKey": account.EabKey,
"provider": providerMap,
}
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
AccountId int64
Name string
ProviderCode string
EabKid string
EabKey string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo("修改ACME服务商账号 %d", params.AccountId)
params.Must.
Field("name", params.Name).
Require("请输入账号名称").
Field("providerCode", params.ProviderCode).
Require("请选择服务商")
providerResp, err := this.RPC().ACMEProviderRPC().FindACMEProviderWithCode(this.AdminContext(), &pb.FindACMEProviderWithCodeRequest{AcmeProviderCode: params.ProviderCode})
if err != nil {
this.ErrorPage(err)
return
}
var provider = providerResp.AcmeProvider
if provider == nil {
this.Fail("请选择服务商")
}
if provider.RequireEAB {
params.Must.
Field("eabKid", params.EabKid).
Require("请输入EAB Kid").
Field("eabKey", params.EabKey).
Require("请输入EAB HMAC Key")
}
_, err = this.RPC().ACMEProviderAccountRPC().UpdateACMEProviderAccount(this.AdminContext(), &pb.UpdateACMEProviderAccountRequest{
AcmeProviderAccountId: params.AccountId,
Name: params.Name,
EabKid: params.EabKid,
EabKey: params.EabKey,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -35,15 +35,31 @@ func (this *CreateAction) RunGet(params struct{}) {
}
userMaps = append(userMaps, maps.Map{
"id": user.Id,
"description": description,
"email": user.Email,
"id": user.Id,
"description": description,
"email": user.Email,
"providerCode": user.AcmeProviderCode,
})
}
this.Data["users"] = userMaps
// 证书服务商
providersResp, err := this.RPC().ACMEProviderRPC().FindAllACMEProviders(this.AdminContext(), &pb.FindAllACMEProvidersRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var providerMaps = []maps.Map{}
for _, provider := range providersResp.AcmeProviders {
providerMaps = append(providerMaps, maps.Map{
"name": provider.Name,
"code": provider.Code,
})
}
this.Data["providers"] = providerMaps
// 域名解析服务商
providersResp, err := this.RPC().DNSProviderRPC().FindAllEnabledDNSProviders(this.AdminContext(), &pb.FindAllEnabledDNSProvidersRequest{
dnsProvidersResp, err := this.RPC().DNSProviderRPC().FindAllEnabledDNSProviders(this.AdminContext(), &pb.FindAllEnabledDNSProvidersRequest{
AdminId: this.AdminId(),
UserId: 0,
})
@@ -51,15 +67,15 @@ func (this *CreateAction) RunGet(params struct{}) {
this.ErrorPage(err)
return
}
providerMaps := []maps.Map{}
for _, provider := range providersResp.DnsProviders {
providerMaps = append(providerMaps, maps.Map{
dnsProviderMaps := []maps.Map{}
for _, provider := range dnsProvidersResp.DnsProviders {
dnsProviderMaps = append(dnsProviderMaps, maps.Map{
"id": provider.Id,
"name": provider.Name,
"typeName": provider.TypeName,
})
}
this.Data["providers"] = providerMaps
this.Data["dnsProviders"] = dnsProviderMaps
this.Show()
}

View File

@@ -136,6 +136,26 @@ func (this *IndexAction) RunGet(params struct {
if task.AcmeUser == nil {
continue
}
// 服务商
var providerMap maps.Map
if task.AcmeUser.AcmeProvider != nil {
providerMap = maps.Map{
"name": task.AcmeUser.AcmeProvider.Name,
"code": task.AcmeUser.AcmeProvider.Code,
}
}
// 账号
var accountMap maps.Map
if task.AcmeUser.AcmeProviderAccount != nil {
accountMap = maps.Map{
"id": task.AcmeUser.AcmeProviderAccount.Id,
"name": task.AcmeUser.AcmeProviderAccount.Name,
}
}
// DNS服务商
dnsProviderMap := maps.Map{}
if task.AuthType == "dns" && task.DnsProvider != nil {
dnsProviderMap = maps.Map{
@@ -170,8 +190,10 @@ func (this *IndexAction) RunGet(params struct {
"id": task.Id,
"authType": task.AuthType,
"acmeUser": maps.Map{
"id": task.AcmeUser.Id,
"email": task.AcmeUser.Email,
"id": task.AcmeUser.Id,
"email": task.AcmeUser.Email,
"provider": providerMap,
"account": accountMap,
},
"dnsProvider": dnsProviderMap,
"dnsDomain": task.DnsDomain,

View File

@@ -0,0 +1,33 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package users
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type AccountsWithCodeAction struct {
actionutils.ParentAction
}
func (this *AccountsWithCodeAction) RunPost(params struct {
Code string
}) {
accountsResp, err := this.RPC().ACMEProviderAccountRPC().FindAllACMEProviderAccountsWithProviderCode(this.AdminContext(), &pb.FindAllACMEProviderAccountsWithProviderCodeRequest{AcmeProviderCode: params.Code})
if err != nil {
this.ErrorPage(err)
return
}
var accountMaps = []maps.Map{}
for _, account := range accountsResp.AcmeProviderAccounts {
accountMaps = append(accountMaps, maps.Map{
"id": account.Id,
"name": account.Name,
})
}
this.Data["accounts"] = accountMaps
this.Success()
}

View File

@@ -16,12 +16,30 @@ func (this *CreatePopupAction) Init() {
}
func (this *CreatePopupAction) RunGet(params struct{}) {
// 服务商
providersResp, err := this.RPC().ACMEProviderRPC().FindAllACMEProviders(this.AdminContext(), &pb.FindAllACMEProvidersRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var providerMaps = []maps.Map{}
for _, provider := range providersResp.AcmeProviders {
providerMaps = append(providerMaps, maps.Map{
"code": provider.Code,
"name": provider.Name,
"requireEAB": provider.RequireEAB,
})
}
this.Data["providers"] = providerMaps
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
Email string
Description string
Email string
ProviderCode string
AccountId int64
Description string
Must *actions.Must
CSRF *actionutils.CSRF
@@ -29,11 +47,44 @@ func (this *CreatePopupAction) RunPost(params struct {
params.Must.
Field("email", params.Email).
Require("请输入邮箱").
Email("请输入正确的邮箱格式")
Email("请输入正确的邮箱格式").
Field("providerCode", params.ProviderCode).
Require("请选择所属服务商")
providerResp, err := this.RPC().ACMEProviderRPC().FindACMEProviderWithCode(this.AdminContext(), &pb.FindACMEProviderWithCodeRequest{
AcmeProviderCode: params.ProviderCode,
})
if err != nil {
this.ErrorPage(err)
return
}
if providerResp.AcmeProvider == nil {
this.Fail("找不到要选择的证书")
}
if providerResp.AcmeProvider.RequireEAB {
if params.AccountId <= 0 {
this.Fail("此服务商要求必须选择或创建服务商账号")
}
// 同一个账号只能有一个用户
countResp, err := this.RPC().ACMEUserRPC().
CountACMEUsers(this.AdminContext(), &pb.CountAcmeUsersRequest{
AcmeProviderAccountId: params.AccountId,
})
if err != nil {
this.ErrorPage(err)
return
}
if countResp.Count > 0 {
this.Fail("此服务商账号已被别的用户使用,请换成别的账号")
}
}
createResp, err := this.RPC().ACMEUserRPC().CreateACMEUser(this.AdminContext(), &pb.CreateACMEUserRequest{
Email: params.Email,
Description: params.Description,
Email: params.Email,
Description: params.Description,
AcmeProviderCode: params.ProviderCode,
AcmeProviderAccountId: params.AccountId,
})
if err != nil {
this.ErrorPage(err)
@@ -42,9 +93,10 @@ func (this *CreatePopupAction) RunPost(params struct {
// 返回数据
this.Data["acmeUser"] = maps.Map{
"id": createResp.AcmeUserId,
"description": params.Description,
"email": params.Email,
"id": createResp.AcmeUserId,
"description": params.Description,
"email": params.Email,
"providerCode": params.ProviderCode,
}
// 日志

View File

@@ -40,11 +40,31 @@ func (this *IndexAction) RunGet(params struct{}) {
}
userMaps := []maps.Map{}
for _, user := range usersResp.AcmeUsers {
// 服务商
var providerMap maps.Map
if user.AcmeProvider != nil {
providerMap = maps.Map{
"name": user.AcmeProvider.Name,
"code": user.AcmeProvider.Code,
}
}
// 账号
var accountMap maps.Map
if user.AcmeProviderAccount != nil {
accountMap = maps.Map{
"id": user.AcmeProviderAccount.Id,
"name": user.AcmeProviderAccount.Name,
}
}
userMaps = append(userMaps, maps.Map{
"id": user.Id,
"email": user.Email,
"description": user.Description,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", user.CreatedAt),
"provider": providerMap,
"account": accountMap,
})
}
this.Data["users"] = userMaps

View File

@@ -29,10 +29,30 @@ func (this *UpdatePopupAction) RunGet(params struct {
return
}
// 服务商
var providerMap maps.Map
if user.AcmeProvider != nil {
providerMap = maps.Map{
"name": user.AcmeProvider.Name,
"code": user.AcmeProvider.Code,
}
}
// 账号
var accountMap maps.Map
if user.AcmeProviderAccount != nil {
accountMap = maps.Map{
"id": user.AcmeProviderAccount.Id,
"name": user.AcmeProviderAccount.Name,
}
}
this.Data["user"] = maps.Map{
"id": user.Id,
"email": user.Email,
"description": user.Description,
"provider": providerMap,
"account": accountMap,
}
this.Show()

View File

@@ -16,7 +16,7 @@ func (this *DeleteAction) RunPost(params struct {
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "删除SSL证书 %d", params.CertId)
// 是否正在被使用
// 是否正在被服务使用
countResp, err := this.RPC().ServerRPC().CountAllEnabledServersWithSSLCertId(this.AdminContext(), &pb.CountAllEnabledServersWithSSLCertIdRequest{SslCertId: params.CertId})
if err != nil {
this.ErrorPage(err)
@@ -26,6 +26,26 @@ func (this *DeleteAction) RunPost(params struct {
this.Fail("此证书正在被某些服务引用,请先修改服务后再删除。")
}
// 是否正在被API节点使用
countResp, err = this.RPC().APINodeRPC().CountAllEnabledAPINodesWithSSLCertId(this.AdminContext(), &pb.CountAllEnabledAPINodesWithSSLCertIdRequest{SslCertId: params.CertId})
if err != nil {
this.ErrorPage(err)
return
}
if countResp.Count > 0 {
this.Fail("此证书正在被某些API节点引用请先修改API节点后再删除")
}
// 是否正在被用户节点使用
countResp, err = this.RPC().UserNodeRPC().CountAllEnabledUserNodesWithSSLCertId(this.AdminContext(), &pb.CountAllEnabledUserNodesWithSSLCertIdRequest{SslCertId: params.CertId})
if err != nil {
this.ErrorPage(err)
return
}
if countResp.Count > 0 {
this.Fail("此证书正在被某些用户节点引用,请先修改用户节点后再删除")
}
_, err = this.RPC().SSLCertRPC().DeleteSSLCert(this.AdminContext(), &pb.DeleteSSLCertRequest{SslCertId: params.CertId})
if err != nil {
this.ErrorPage(err)

View File

@@ -3,6 +3,7 @@ package certs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/certs/acme"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/certs/acme/accounts"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/certs/acme/users"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
@@ -13,9 +14,7 @@ func init() {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeServer)).
Helper(NewHelper()).
Data("teaSubMenu", "cert").
Prefix("/servers/certs").
Data("leftMenuItem", "cert").
Get("", new(IndexAction)).
@@ -31,7 +30,7 @@ func init() {
Get("/selectPopup", new(SelectPopupAction)).
Get("/datajs", new(DatajsAction)).
// ACME
// ACME任务
Prefix("/servers/certs/acme").
Data("leftMenuItem", "acme").
Get("", new(acme.IndexAction)).
@@ -40,13 +39,23 @@ func init() {
GetPost("/updateTaskPopup", new(acme.UpdateTaskPopupAction)).
Post("/deleteTask", new(acme.DeleteTaskAction)).
// ACME用户
Prefix("/servers/certs/acme/users").
Get("", new(users.IndexAction)).
GetPost("/createPopup", new(users.CreatePopupAction)).
GetPost("/updatePopup", new(users.UpdatePopupAction)).
Post("/delete", new(users.DeleteAction)).
GetPost("/selectPopup", new(users.SelectPopupAction)).
Post("/accountsWithCode", new(users.AccountsWithCodeAction)).
// ACME账号
Prefix("/servers/certs/acme/accounts").
Get("", new(accounts.IndexAction)).
GetPost("/createPopup", new(accounts.CreatePopupAction)).
GetPost("/updatePopup", new(accounts.UpdatePopupAction)).
Post("/delete", new(accounts.DeleteAction)).
//
EndAll()
})
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/iwind/TeaGo/actions"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type UpdatePopupAction struct {
@@ -45,6 +46,8 @@ func (this *UpdatePopupAction) RunGet(params struct {
func (this *UpdatePopupAction) RunPost(params struct {
CertId int64
TextMode bool
Name string
IsCA bool
Description string
@@ -53,6 +56,9 @@ func (this *UpdatePopupAction) RunPost(params struct {
CertFile *actions.File
KeyFile *actions.File
CertText string
KeyText string
Must *actions.Must
}) {
// 创建日志
@@ -82,18 +88,31 @@ func (this *UpdatePopupAction) RunPost(params struct {
Field("name", params.Name).
Require("请输入证书说明")
if params.CertFile != nil {
certConfig.CertData, err = params.CertFile.Read()
if err != nil {
this.Fail("读取证书文件内容错误,请重新上传")
if params.TextMode {
if len(params.CertText) > 0 {
certConfig.CertData = []byte(params.CertText)
}
}
if !params.IsCA {
if params.KeyFile != nil {
certConfig.KeyData, err = params.KeyFile.Read()
if !params.IsCA {
if len(params.KeyText) > 0 {
certConfig.KeyData = []byte(params.KeyText)
}
}
} else {
if params.CertFile != nil {
certConfig.CertData, err = params.CertFile.Read()
if err != nil {
this.Fail("读取密钥文件内容错误,请重新上传")
this.FailField("certFile", "读取证书文件内容错误,请重新上传")
}
}
if !params.IsCA {
if params.KeyFile != nil {
certConfig.KeyData, err = params.KeyFile.Read()
if err != nil {
this.FailField("keyFile", "读取私钥文件内容错误,请重新上传")
}
}
}
}
@@ -109,6 +128,10 @@ func (this *UpdatePopupAction) RunPost(params struct {
}
}
if len(timeutil.Format("Y", certConfig.TimeEnd())) != 4 {
this.Fail("证书格式错误:无法读取到证书有效期")
}
// 保存
_, err = this.RPC().SSLCertRPC().UpdateSSLCert(this.AdminContext(), &pb.UpdateSSLCertRequest{
SslCertId: params.CertId,

View File

@@ -7,6 +7,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/iwind/TeaGo/actions"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type UploadPopupAction struct {
@@ -22,6 +23,7 @@ func (this *UploadPopupAction) RunGet(params struct{}) {
}
func (this *UploadPopupAction) RunPost(params struct {
TextMode bool
Name string
IsCA bool
Description string
@@ -30,6 +32,9 @@ func (this *UploadPopupAction) RunPost(params struct {
CertFile *actions.File
KeyFile *actions.File
CertText string
KeyText string
Must *actions.Must
}) {
params.Must.
@@ -39,22 +44,37 @@ func (this *UploadPopupAction) RunPost(params struct {
certData := []byte{}
keyData := []byte{}
if params.CertFile == nil {
this.Fail("请选择要上传的证书文件")
}
var err error
certData, err = params.CertFile.Read()
if err != nil {
this.Fail("读取证书文件内容错误,请重新上传")
}
if params.TextMode {
if len(params.CertText) == 0 {
this.FailField("certText", "请输入证书内容")
}
if !params.IsCA {
if params.KeyFile == nil {
this.Fail("请选择要上传的私钥文件")
} else {
keyData, err = params.KeyFile.Read()
if err != nil {
this.Fail("读取密钥文件内容错误,请重新上传")
if !params.IsCA {
if len(params.KeyText) == 0 {
this.FailField("keyText", "请输入私钥内容")
}
}
certData = []byte(params.CertText)
keyData = []byte(params.KeyText)
} else {
if params.CertFile == nil {
this.FailField("certFile", "请选择要上传的证书文件")
}
var err error
certData, err = params.CertFile.Read()
if err != nil {
this.FailField("certFile", "读取证书文件内容错误,请重新上传")
}
if !params.IsCA {
if params.KeyFile == nil {
this.FailField("keyFile", "请选择要上传的私钥文件")
} else {
keyData, err = params.KeyFile.Read()
if err != nil {
this.FailField("keyFile", "读取密钥文件内容错误,请重新上传")
}
}
}
}
@@ -65,7 +85,7 @@ func (this *UploadPopupAction) RunPost(params struct {
CertData: certData,
KeyData: keyData,
}
err = sslConfig.Init()
err := sslConfig.Init()
if err != nil {
if params.IsCA {
this.Fail("证书校验错误:" + err.Error())
@@ -73,6 +93,9 @@ func (this *UploadPopupAction) RunPost(params struct {
this.Fail("证书或密钥校验错误:" + err.Error())
}
}
if len(timeutil.Format("Y", sslConfig.TimeEnd())) != 4 {
this.Fail("证书格式错误:无法读取到证书有效期")
}
// 保存
createResp, err := this.RPC().SSLCertRPC().CreateSSLCert(this.AdminContext(), &pb.CreateSSLCertRequest{

View File

@@ -33,7 +33,7 @@ func (this *IndexAction) RunGet(params struct{}) {
globalConfig := &serverconfigs.GlobalConfig{}
// 默认值
globalConfig.HTTPAll.DomainAuditingIsOn = true
globalConfig.HTTPAll.DomainAuditingIsOn = false
if len(valueJSON) > 0 {
err = json.Unmarshal(valueJSON, globalConfig)

View File

@@ -73,6 +73,14 @@ func (this *GroupAction) RunGet(params struct {
"name": set.Name,
"rules": lists.Map(set.Rules, func(k int, v interface{}) interface{} {
rule := v.(*firewallconfigs.HTTPFirewallRule)
// 校验
var errString = ""
var err = rule.Init()
if err != nil {
errString = err.Error()
}
return maps.Map{
"param": rule.Param,
"paramFilters": rule.ParamFilters,
@@ -81,6 +89,7 @@ func (this *GroupAction) RunGet(params struct {
"isCaseInsensitive": rule.IsCaseInsensitive,
"isComposed": firewallconfigs.CheckCheckpointIsComposed(rule.Prefix()),
"checkpointOptions": rule.CheckpointOptions,
"err": errString,
}
}),
"isOn": set.IsOn,

View File

@@ -70,10 +70,17 @@ func (this *IndexAction) RunGet(params struct {
}
countClusters := countClustersResp.Count
// mode
if len(policy.Mode) == 0 {
policy.Mode = firewallconfigs.FirewallModeDefend
}
policyMaps = append(policyMaps, maps.Map{
"id": policy.Id,
"isOn": policy.IsOn,
"name": policy.Name,
"mode": policy.Mode,
"modeInfo": firewallconfigs.FindFirewallMode(policy.Mode),
"countInbound": countInbound,
"countOutbound": countOutbound,
"countClusters": countClusters,

View File

@@ -19,6 +19,7 @@ func init() {
GetPost("/createPopup", new(CreatePopupAction)).
Post("/delete", new(DeleteAction)).
Get("/policy", new(PolicyAction)).
Post("/upgradeTemplate", new(UpgradeTemplateAction)).
Get("/groups", new(GroupsAction)).
Get("/group", new(GroupAction)).
Get("/log", new(LogAction)).

View File

@@ -59,14 +59,56 @@ func (this *ListsAction) RunGet(params struct {
expiredTime = timeutil.FormatTime("Y-m-d H:i:s", item.ExpiredAt)
}
// policy
var sourcePolicyMap = maps.Map{"id": 0}
if item.SourceHTTPFirewallPolicy != nil {
sourcePolicyMap = maps.Map{
"id": item.SourceHTTPFirewallPolicy.Id,
"name": item.SourceHTTPFirewallPolicy.Name,
"serverId": item.SourceHTTPFirewallPolicy.ServerId,
}
}
// group
var sourceGroupMap = maps.Map{"id": 0}
if item.SourceHTTPFirewallRuleGroup != nil {
sourceGroupMap = maps.Map{
"id": item.SourceHTTPFirewallRuleGroup.Id,
"name": item.SourceHTTPFirewallRuleGroup.Name,
}
}
// set
var sourceSetMap = maps.Map{"id": 0}
if item.SourceHTTPFirewallRuleSet != nil {
sourceSetMap = maps.Map{
"id": item.SourceHTTPFirewallRuleSet.Id,
"name": item.SourceHTTPFirewallRuleSet.Name,
}
}
// server
var sourceServerMap = maps.Map{"id": 0}
if item.SourceServer != nil {
sourceServerMap = maps.Map{
"id": item.SourceServer.Id,
"name": item.SourceServer.Name,
}
}
itemMaps = append(itemMaps, maps.Map{
"id": item.Id,
"ipFrom": item.IpFrom,
"ipTo": item.IpTo,
"createdTime": timeutil.FormatTime("Y-m-d", item.CreatedAt),
"expiredTime": expiredTime,
"reason": item.Reason,
"type": item.Type,
"eventLevelName": firewallconfigs.FindFirewallEventLevelName(item.EventLevel),
"sourcePolicy": sourcePolicyMap,
"sourceGroup": sourceGroupMap,
"sourceSet": sourceSetMap,
"sourceServer": sourceServerMap,
})
}
this.Data["items"] = itemMaps

View File

@@ -60,6 +60,7 @@ func (this *TestAction) RunPost(params struct {
"ipFrom": resp.IpItem.IpFrom,
"ipTo": resp.IpItem.IpTo,
"reason": resp.IpItem.Reason,
"createdTime": timeutil.FormatTime("Y-m-d", resp.IpItem.CreatedAt),
"expiredAt": resp.IpItem.ExpiredAt,
"expiredTime": timeutil.FormatTime("Y-m-d H:i:s", resp.IpItem.ExpiredAt),
"type": resp.IpItem.Type,

View File

@@ -4,6 +4,7 @@ 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/firewallconfigs"
"github.com/iwind/TeaGo/maps"
)
@@ -46,11 +47,44 @@ func (this *PolicyAction) RunGet(params struct {
}
}
// 检查是否有升级
var templatePolicy = firewallconfigs.HTTPFirewallTemplate()
var upgradeItems = []string{}
if templatePolicy.Inbound != nil {
for _, group := range templatePolicy.Inbound.Groups {
if len(group.Code) == 0 {
continue
}
var oldGroup = firewallPolicy.FindRuleGroupWithCode(group.Code)
if oldGroup == nil {
upgradeItems = append(upgradeItems, group.Name)
continue
}
for _, set := range group.Sets {
if len(set.Code) == 0 {
continue
}
var oldSet = oldGroup.FindRuleSetWithCode(set.Code)
if oldSet == nil {
upgradeItems = append(upgradeItems, group.Name+" -- "+set.Name)
continue
}
}
}
}
this.Data["upgradeItems"] = upgradeItems
// 模式
if len(firewallPolicy.Mode) == 0 {
firewallPolicy.Mode = firewallconfigs.FirewallModeDefend
}
this.Data["firewallPolicy"] = maps.Map{
"id": firewallPolicy.Id,
"name": firewallPolicy.Name,
"isOn": firewallPolicy.IsOn,
"description": firewallPolicy.Description,
"mode": firewallPolicy.Mode,
"modeInfo": firewallconfigs.FindFirewallMode(firewallPolicy.Mode),
"groups": internalGroups,
"blockOptions": firewallPolicy.BlockOptions,
}

View File

@@ -1,6 +1,7 @@
package waf
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
@@ -37,14 +38,22 @@ func (this *UpdateAction) RunGet(params struct {
StatusCode: http.StatusForbidden,
Body: "Blocked By WAF",
URL: "",
Timeout: 60,
}
}
// mode
if len(firewallPolicy.Mode) == 0 {
firewallPolicy.Mode = firewallconfigs.FirewallModeDefend
}
this.Data["modes"] = firewallconfigs.FindAllFirewallModes()
this.Data["firewallPolicy"] = maps.Map{
"id": firewallPolicy.Id,
"name": firewallPolicy.Name,
"description": firewallPolicy.Description,
"isOn": firewallPolicy.IsOn,
"mode": firewallPolicy.Mode,
"blockOptions": firewallPolicy.BlockOptions,
}
@@ -77,6 +86,7 @@ func (this *UpdateAction) RunPost(params struct {
BlockOptionsJSON []byte
Description string
IsOn bool
Mode string
Must *actions.Must
}) {
@@ -87,13 +97,21 @@ func (this *UpdateAction) RunPost(params struct {
Field("name", params.Name).
Require("请输入策略名称")
_, err := this.RPC().HTTPFirewallPolicyRPC().UpdateHTTPFirewallPolicy(this.AdminContext(), &pb.UpdateHTTPFirewallPolicyRequest{
// 校验JSON
var blockOptions = &firewallconfigs.HTTPFirewallBlockAction{}
err := json.Unmarshal(params.BlockOptionsJSON, blockOptions)
if err != nil {
this.Fail("拦截动作参数校验失败:" + err.Error())
}
_, err = this.RPC().HTTPFirewallPolicyRPC().UpdateHTTPFirewallPolicy(this.AdminContext(), &pb.UpdateHTTPFirewallPolicyRequest{
HttpFirewallPolicyId: params.FirewallPolicyId,
IsOn: params.IsOn,
Name: params.Name,
Description: params.Description,
FirewallGroupCodes: params.GroupCodes,
BlockOptionsJSON: params.BlockOptionsJSON,
Mode: params.Mode,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -0,0 +1,124 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package waf
import (
"encoding/json"
"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/firewallconfigs"
)
type UpgradeTemplateAction struct {
actionutils.ParentAction
}
func (this *UpgradeTemplateAction) RunPost(params struct {
PolicyId int64
}) {
defer this.CreateLogInfo("升级WAF %d 内置规则", params.PolicyId)
policy, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicyConfig(this.AdminContext(), params.PolicyId)
if err != nil {
this.ErrorPage(err)
return
}
if policy == nil {
this.NotFound("firewallPolicy", params.PolicyId)
return
}
// 检查是否有升级
var templatePolicy = firewallconfigs.HTTPFirewallTemplate()
if templatePolicy.Inbound != nil {
for _, group := range templatePolicy.Inbound.Groups {
if len(group.Code) == 0 {
continue
}
var oldGroup = policy.FindRuleGroupWithCode(group.Code)
if oldGroup == nil {
createGroupResp, err := this.RPC().HTTPFirewallRuleGroupRPC().CreateHTTPFirewallRuleGroup(this.AdminContext(), &pb.CreateHTTPFirewallRuleGroupRequest{
IsOn: true,
Name: group.Name,
Code: group.Code,
Description: group.Description,
})
if err != nil {
this.ErrorPage(err)
return
}
var groupId = createGroupResp.FirewallRuleGroupId
policy.Inbound.GroupRefs = append(policy.Inbound.GroupRefs, &firewallconfigs.HTTPFirewallRuleGroupRef{
IsOn: true,
GroupId: groupId,
})
for _, set := range group.Sets {
setJSON, err := json.Marshal(set)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPFirewallRuleGroupRPC().AddHTTPFirewallRuleGroupSet(this.AdminContext(), &pb.AddHTTPFirewallRuleGroupSetRequest{
FirewallRuleGroupId: groupId,
FirewallRuleSetConfigJSON: setJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
}
continue
}
for _, set := range group.Sets {
if len(set.Code) == 0 {
continue
}
var oldSet = oldGroup.FindRuleSetWithCode(set.Code)
if oldSet == nil {
setJSON, err := json.Marshal(set)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPFirewallRuleGroupRPC().AddHTTPFirewallRuleGroupSet(this.AdminContext(), &pb.AddHTTPFirewallRuleGroupSetRequest{
FirewallRuleGroupId: oldGroup.Id,
FirewallRuleSetConfigJSON: setJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
continue
}
}
}
}
// 保存inbound
inboundJSON, err := policy.InboundJSON()
if err != nil {
this.ErrorPage(err)
return
}
outboundJSON, err := policy.OutboundJSON()
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPFirewallPolicyRPC().UpdateHTTPFirewallPolicyGroups(this.AdminContext(), &pb.UpdateHTTPFirewallPolicyGroupsRequest{
HttpFirewallPolicyId: params.PolicyId,
InboundJSON: inboundJSON,
OutboundJSON: outboundJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/logs"
@@ -56,14 +57,26 @@ func (this *CreateAction) RunGet(params struct{}) {
// 服务类型
this.Data["serverTypes"] = serverconfigs.AllServerTypes()
// 检查是否有用户
countUsersResp, err := this.RPC().UserRPC().CountAllEnabledUsers(this.AdminContext(), &pb.CountAllEnabledUsersRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["hasUsers"] = countUsersResp.Count > 0
this.Show()
}
func (this *CreateAction) RunPost(params struct {
Name string
Description string
ClusterId int64
GroupIds []int64
UserId int64
UserPlanId int64
ClusterId int64
GroupIds []int64
ServerType string
Addresses string
@@ -71,8 +84,11 @@ func (this *CreateAction) RunPost(params struct {
CertIdsJSON []byte
Origins string
AccessLogIsOn bool
WebsocketIsOn bool
AccessLogIsOn bool
WebsocketIsOn bool
CacheIsOn bool
WafIsOn bool
RemoteAddrIsOn bool
WebRoot string
@@ -82,11 +98,24 @@ func (this *CreateAction) RunPost(params struct {
Field("name", params.Name).
Require("请输入服务名称")
if params.ClusterId <= 0 {
this.Fail("请选择部署的集群")
var clusterId = params.ClusterId
// 用户
var userId = params.UserId
if userId > 0 {
clusterIdResp, err := this.RPC().UserRPC().FindUserNodeClusterId(this.AdminContext(), &pb.FindUserNodeClusterIdRequest{UserId: userId})
if err != nil {
this.ErrorPage(err)
return
}
clusterId = clusterIdResp.NodeClusterId
if clusterId <= 0 {
this.Fail("请选择部署的集群")
}
}
// TODO 验证集群ID
// 套餐
var userPlanId = params.UserPlanId
// 端口地址
var httpConfig *serverconfigs.HTTPProtocolConfig = nil
@@ -256,7 +285,7 @@ func (this *CreateAction) RunPost(params struct {
if len(allServerNames) > 0 {
dupResp, err := this.RPC().ServerRPC().CheckServerNameDuplicationInNodeCluster(this.AdminContext(), &pb.CheckServerNameDuplicationInNodeClusterRequest{
ServerNames: allServerNames,
NodeClusterId: params.ClusterId,
NodeClusterId: clusterId,
})
if err != nil {
this.ErrorPage(err)
@@ -335,7 +364,7 @@ func (this *CreateAction) RunPost(params struct {
this.ErrorPage(err)
return
}
webId = webResp.WebId
webId = webResp.HttpWebId
}
// 包含条件
@@ -355,13 +384,14 @@ func (this *CreateAction) RunPost(params struct {
}
req := &pb.CreateServerRequest{
UserId: 0,
UserId: userId,
UserPlanId: userPlanId,
AdminId: this.AdminId(),
Type: params.ServerType,
Name: params.Name,
ServerNamesJON: []byte(params.ServerNames),
Description: params.Description,
NodeClusterId: params.ClusterId,
NodeClusterId: clusterId,
IncludeNodesJSON: includeNodesJSON,
ExcludeNodesJSON: excludeNodesJSON,
WebId: webId,
@@ -432,7 +462,7 @@ func (this *CreateAction) RunPost(params struct {
// 访问日志
if params.AccessLogIsOn {
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebAccessLog(this.AdminContext(), &pb.UpdateHTTPWebAccessLogRequest{
WebId: webConfig.Id,
HttpWebId: webConfig.Id,
AccessLogJSON: []byte(`{
"isPrior": false,
"isOn": true,
@@ -471,7 +501,7 @@ func (this *CreateAction) RunPost(params struct {
} else {
websocketId := createWebSocketResp.WebsocketId
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebWebsocket(this.AdminContext(), &pb.UpdateHTTPWebWebsocketRequest{
WebId: webConfig.Id,
HttpWebId: webConfig.Id,
WebsocketJSON: []byte(` {
"isPrior": false,
"isOn": true,
@@ -483,6 +513,75 @@ func (this *CreateAction) RunPost(params struct {
}
}
}
// cache
if params.CacheIsOn {
var cacheConfig = &serverconfigs.HTTPCacheConfig{
IsPrior: false,
IsOn: true,
AddStatusHeader: true,
PurgeIsOn: false,
PurgeKey: "",
CacheRefs: []*serverconfigs.HTTPCacheRef{},
}
cacheConfigJSON, err := json.Marshal(cacheConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebCache(this.AdminContext(), &pb.UpdateHTTPWebCacheRequest{
HttpWebId: webConfig.Id,
CacheJSON: cacheConfigJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
}
// waf
if params.WafIsOn {
var firewallRef = &firewallconfigs.HTTPFirewallRef{
IsPrior: false,
IsOn: true,
FirewallPolicyId: 0,
}
firewallRefJSON, err := json.Marshal(firewallRef)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebFirewall(this.AdminContext(), &pb.UpdateHTTPWebFirewallRequest{
HttpWebId: webConfig.Id,
FirewallJSON: firewallRefJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
}
// remoteAddr
var remoteAddrConfig = &serverconfigs.HTTPRemoteAddrConfig{
IsOn: true,
Value: "${rawRemoteAddr}",
}
if params.RemoteAddrIsOn {
remoteAddrConfig.Value = "${remoteAddr}"
}
remoteAddrConfigJSON, err := json.Marshal(remoteAddrConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebRemoteAddr(this.AdminContext(), &pb.UpdateHTTPWebRemoteAddrRequest{
HttpWebId: webConfig.Id,
RemoteAddrJSON: remoteAddrConfigJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
}
}

View File

@@ -4,6 +4,7 @@ package servergrouputils
import (
"errors"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
@@ -35,75 +36,115 @@ func InitGroup(parent *actionutils.ParentAction, groupId int64, menuItem string)
}
var urlPrefix = "/servers/groups/group/settings"
parent.Data["leftMenuItems"] = []maps.Map{
/**{
"name": "Web设置",
"url": urlPrefix + "/web?groupId=" + types.String(groupId),
"isActive": menuItem == "web",
},**/
var leftMenuItems = []maps.Map{
{
"name": "HTTP反向代理",
"name": "HTTP代理",
"url": urlPrefix + "/httpReverseProxy?groupId=" + types.String(groupId),
"isActive": menuItem == "httpReverseProxy",
"isOn": configInfoResp.HasHTTPReverseProxy,
},
{
"name": "TCP反向代理",
"name": "TCP代理",
"url": urlPrefix + "/tcpReverseProxy?groupId=" + types.String(groupId),
"isActive": menuItem == "tcpReverseProxy",
"isOn": configInfoResp.HasTCPReverseProxy,
},
{
"name": "UDP反向代理",
"name": "UDP代理",
"url": urlPrefix + "/udpReverseProxy?groupId=" + types.String(groupId),
"isActive": menuItem == "udpReverseProxy",
"isOn": configInfoResp.HasUDPReverseProxy,
},
/**{
"name": "-",
"url": "",
},
{
"name": "WAF",
"url": urlPrefix + "/waf?groupId=" + types.String(groupId),
"isActive": menuItem == "waf",
},
{
"name": "缓存",
"url": urlPrefix + "/cache?groupId=" + types.String(groupId),
"isActive": menuItem == "cache",
},
{
"name": "访问日志",
"url": urlPrefix + "/accessLog?groupId=" + types.String(groupId),
"isActive": menuItem == "accessLog",
},
{
"name": "统计",
"url": urlPrefix + "/stat?groupId=" + types.String(groupId),
"isActive": menuItem == "stat",
},
{
"name": "Gzip压缩",
"url": urlPrefix + "/gzip?groupId=" + types.String(groupId),
"isActive": menuItem == "gzip",
},
{
"name": "特殊页面",
"url": urlPrefix + "/pages?groupId=" + types.String(groupId),
"isActive": menuItem == "page",
},
{
"name": "HTTP Header",
"url": urlPrefix + "/headers?groupId=" + types.String(groupId),
"isActive": menuItem == "header",
},
{
"name": "Websocket",
"url": urlPrefix + "/websocket?groupId=" + types.String(groupId),
"isActive": menuItem == "websocket",
},**/
}
if teaconst.IsPlus {
leftMenuItems = append([]maps.Map{
{
"name": "Web设置",
"url": urlPrefix + "/web?groupId=" + types.String(groupId),
"isActive": menuItem == "web",
"isOn": configInfoResp.HasRootConfig,
},
}, leftMenuItems...)
leftMenuItems = append(leftMenuItems, []maps.Map{
{
"name": "-",
"url": "",
},
{
"name": "WAF",
"url": urlPrefix + "/waf?groupId=" + types.String(groupId),
"isActive": menuItem == "waf",
"isOn": configInfoResp.HasWAFConfig,
},
{
"name": "缓存",
"url": urlPrefix + "//cache?groupId=" + types.String(groupId),
"isActive": menuItem == "cache",
"isOn": configInfoResp.HasCacheConfig,
},
{
"name": "字符编码",
"url": urlPrefix + "/charset?groupId=" + types.String(groupId),
"isActive": menuItem == "charset",
"isOn": configInfoResp.HasCharsetConfig,
},
{
"name": "访问日志",
"url": urlPrefix + "/accessLog?groupId=" + types.String(groupId),
"isActive": menuItem == "accessLog",
"isOn": configInfoResp.HasAccessLogConfig,
},
{
"name": "统计",
"url": urlPrefix + "/stat?groupId=" + types.String(groupId),
"isActive": menuItem == "stat",
"isOn": configInfoResp.HasStatConfig,
},
{
"name": "内容压缩",
"url": urlPrefix + "/compression?groupId=" + types.String(groupId),
"isActive": menuItem == "compression",
"isOn": configInfoResp.HasCompressionConfig,
},
{
"name": "特殊页面",
"url": urlPrefix + "/pages?groupId=" + types.String(groupId),
"isActive": menuItem == "pages",
"isOn": configInfoResp.HasPagesConfig,
},
{
"name": "HTTP Header",
"url": urlPrefix + "/headers?groupId=" + types.String(groupId),
"isActive": menuItem == "headers",
"isOn": configInfoResp.HasRequestHeadersConfig || configInfoResp.HasResponseHeadersConfig,
},
{
"name": "Websocket",
"url": urlPrefix + "/websocket?groupId=" + types.String(groupId),
"isActive": menuItem == "websocket",
"isOn": configInfoResp.HasWebsocketConfig,
},
{
"name": "WebP",
"url": urlPrefix + "/webp?groupId=" + types.String(groupId),
"isActive": menuItem == "webp",
"isOn": configInfoResp.HasWebPConfig,
},
}...)
}
leftMenuItems = append(leftMenuItems, maps.Map{
"name": "-",
"url": "",
})
leftMenuItems = append(leftMenuItems, maps.Map{
"name": "访客IP地址",
"url": urlPrefix + "/remoteAddr?groupId=" + types.String(groupId),
"isActive": menuItem == "remoteAddr",
"isOn": configInfoResp.HasRemoteAddrConfig,
})
parent.Data["leftMenuItems"] = leftMenuItems
}
return group, nil

View File

@@ -0,0 +1,68 @@
package accessLog
import (
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/groups/group/servergrouputils"
"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("accessLog")
}
func (this *IndexAction) RunGet(params struct {
GroupId int64
}) {
_, err := servergrouputils.InitGroup(this.Parent(), params.GroupId, "accessLog")
if err != nil {
this.ErrorPage(err)
return
}
// 获取配置
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerGroupId(this.AdminContext(), params.GroupId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
this.Data["accessLogConfig"] = webConfig.AccessLogRef
// 通用变量
this.Data["fields"] = serverconfigs.HTTPAccessLogFields
this.Data["defaultFieldCodes"] = serverconfigs.HTTPAccessLogDefaultFieldsCodes
this.Show()
}
func (this *IndexAction) RunPost(params struct {
WebId int64
AccessLogJSON []byte
Must *actions.Must
}) {
// 日志
defer this.CreateLog(oplogs.LevelInfo, "修改Web %d 的访问日志设置", params.WebId)
// TODO 检查参数
_, err := this.RPC().HTTPWebRPC().UpdateHTTPWebAccessLog(this.AdminContext(), &pb.UpdateHTTPWebAccessLogRequest{
HttpWebId: params.WebId,
AccessLogJSON: params.AccessLogJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -1,4 +1,4 @@
package test
package accessLog
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
@@ -9,12 +9,11 @@ import (
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Data("teaMenu", "ns").
Data("teaSubMenu", "test").
Prefix("/ns/test").
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeServer)).
Data("teaMenu", "servers").
Data("teaSubMenu", "group").
Prefix("/servers/groups/group/settings/accessLog").
GetPost("", new(IndexAction)).
Post("/nodeOptions", new(NodeOptionsAction)).
EndAll()
})
}

View File

@@ -0,0 +1,88 @@
package cache
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/servers/groups/group/servergrouputils"
"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("cache")
}
func (this *IndexAction) RunGet(params struct {
GroupId int64
}) {
_, err := servergrouputils.InitGroup(this.Parent(), params.GroupId, "cache")
if err != nil {
this.ErrorPage(err)
return
}
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerGroupId(this.AdminContext(), params.GroupId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
this.Data["cacheConfig"] = webConfig.Cache
this.Data["cachePolicy"] = nil
this.Show()
}
func (this *IndexAction) RunPost(params struct {
WebId int64
CacheJSON []byte
Must *actions.Must
}) {
// 日志
defer this.CreateLog(oplogs.LevelInfo, "修改Web %d 的缓存设置", params.WebId)
// 校验配置
cacheConfig := &serverconfigs.HTTPCacheConfig{}
err := json.Unmarshal(params.CacheJSON, cacheConfig)
if err != nil {
this.ErrorPage(err)
return
}
err = cacheConfig.Init()
if err != nil {
this.Fail("检查配置失败:" + err.Error())
}
// 去除不必要的部分
for _, cacheRef := range cacheConfig.CacheRefs {
cacheRef.CachePolicy = nil
}
cacheJSON, err := json.Marshal(cacheConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebCache(this.AdminContext(), &pb.UpdateHTTPWebCacheRequest{
HttpWebId: params.WebId,
CacheJSON: cacheJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,21 @@
package cache
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.AdminModuleCodeServer)).
Data("teaMenu", "servers").
Data("teaSubMenu", "group").
Prefix("/servers/groups/group/settings/cache").
GetPost("", new(IndexAction)).
GetPost("/purge", new(PurgeAction)).
GetPost("/preheat", new(PreheatAction)).
EndAll()
})
}

View File

@@ -0,0 +1,143 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/messageconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"strings"
)
type PreheatAction struct {
actionutils.ParentAction
}
func (this *PreheatAction) Init() {
this.Nav("", "setting", "preheat")
this.SecondMenu("cache")
}
func (this *PreheatAction) RunGet(params struct {
ServerId int64
}) {
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
this.Data["webConfig"] = webConfig
this.Show()
}
func (this *PreheatAction) RunPost(params struct {
ServerId int64
WebId int64
Keys string
Must *actions.Must
}) {
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "预热服务 %d 缓存", params.ServerId)
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithId(this.AdminContext(), params.WebId)
if err != nil {
this.ErrorPage(err)
return
}
if webConfig == nil {
this.NotFound("httpWeb", params.WebId)
return
}
var cache = webConfig.Cache
if cache == nil || !cache.IsOn {
this.Fail("当前没有开启缓存")
}
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.AdminContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var server = serverResp.Server
if server == nil || server.NodeCluster == nil {
this.NotFound("server", params.ServerId)
return
}
var clusterId = server.NodeCluster.Id
clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: clusterId})
if err != nil {
this.ErrorPage(err)
return
}
var cluster = clusterResp.NodeCluster
if cluster == nil {
this.NotFound("nodeCluster", clusterId)
return
}
var cachePolicyId = cluster.HttpCachePolicyId
if cachePolicyId == 0 {
this.Fail("当前集群没有设置缓存策略")
}
cachePolicyResp, err := this.RPC().HTTPCachePolicyRPC().FindEnabledHTTPCachePolicyConfig(this.AdminContext(), &pb.FindEnabledHTTPCachePolicyConfigRequest{HttpCachePolicyId: cachePolicyId})
if err != nil {
this.ErrorPage(err)
return
}
cachePolicyJSON := cachePolicyResp.HttpCachePolicyJSON
if len(cachePolicyJSON) == 0 {
this.Fail("找不到要操作的缓存策略")
}
if len(params.Keys) == 0 {
this.Fail("请输入要预热的Key列表")
}
realKeys := []string{}
for _, key := range strings.Split(params.Keys, "\n") {
key = strings.TrimSpace(key)
if len(key) == 0 {
continue
}
if lists.ContainsString(realKeys, key) {
continue
}
realKeys = append(realKeys, key)
}
// 发送命令
msg := &messageconfigs.PreheatCacheMessage{
CachePolicyJSON: cachePolicyJSON,
Keys: realKeys,
}
results, err := nodeutils.SendMessageToCluster(this.AdminContext(), clusterId, messageconfigs.MessageCodePreheatCache, msg, 300)
if err != nil {
this.ErrorPage(err)
return
}
isAllOk := true
for _, result := range results {
if !result.IsOK {
isAllOk = false
break
}
}
this.Data["isAllOk"] = isAllOk
this.Data["results"] = results
this.Success()
}

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