Compare commits

...

102 Commits

Author SHA1 Message Date
刘祥超
0ff8aecbea commit components.js 2023-06-18 19:51:47 +08:00
刘祥超
e6cae84764 优化文字提示 2023-06-18 16:19:16 +08:00
刘祥超
2d2d2931f1 commit components.js 2023-06-17 21:53:08 +08:00
刘祥超
69ad5e7d1f 安装mysql时预先安装 numactl-libs 2023-06-17 21:53:00 +08:00
刘祥超
97abc6aeb9 缓存条件类型增加“URL通配符” 2023-06-16 11:34:39 +08:00
刘祥超
8cf37dd37c 优化文字提示 2023-06-16 09:54:51 +08:00
刘祥超
e11d091ac5 优化错误提示 2023-06-16 08:17:16 +08:00
刘祥超
e952dfcedd 提交components.js 2023-06-15 15:25:01 +08:00
刘祥超
2565d5cab2 缓存条件增加"强制返回区间内容"选项 2023-06-15 15:15:11 +08:00
刘祥超
e4e66c74bc 更新Components.js 2023-06-13 20:52:46 +08:00
刘祥超
6607c41823 实现自动下载升级版本 2023-06-13 20:52:37 +08:00
刘祥超
43570f20b5 优化OSS源站相关代码 2023-06-13 15:40:40 +08:00
刘祥超
9ea7a93206 优化左侧主菜单显示 2023-06-12 19:50:53 +08:00
刘祥超
dec4922fe5 优化左侧主菜单显示 2023-06-12 19:46:37 +08:00
刘祥超
ef1f95b347 IP列表可以搜索单个IP 2023-06-12 18:29:11 +08:00
刘祥超
7317312f68 版本号改为1.2.0 2023-06-12 14:42:48 +08:00
刘祥超
2965e8df65 commit components.js 2023-06-12 11:16:56 +08:00
刘祥超
4fdcc7fae9 优化缓存条件添加表单 2023-06-10 17:58:03 +08:00
刘祥超
6be8f7539d 缓存条件类型支持“全站” 2023-06-10 17:35:45 +08:00
刘祥超
a6de65fe39 迁移后API地址确认页面提示更详细的API节点配置错误 2023-06-10 16:31:20 +08:00
刘祥超
fc5dae5a8e 优化源站列表界面 2023-06-09 17:46:15 +08:00
刘祥超
db69454d32 压缩utils.js 2023-06-09 17:29:14 +08:00
刘祥超
c824335b0e 增加备用音频 2023-06-09 17:28:49 +08:00
刘祥超
1f1ce11078 优化OSS相关代码 2023-06-08 17:50:22 +08:00
刘祥超
0ec1571d37 初步实现对象存储源站 2023-06-07 17:24:56 +08:00
刘祥超
407e8b52a5 允许在集群设置 -- “网站设置” 中设置节点IP访问显示的内容 2023-06-05 19:26:55 +08:00
刘祥超
1be13a151d 网站全局设置增加“强制Ln请求“选项 2023-06-05 17:05:42 +08:00
刘祥超
4d9d417d7a 增加<dns-resolvers-config-box>组件 2023-06-05 12:33:30 +08:00
刘祥超
aaff8f0c4a 将API设置URL从/api改为/settings/api 2023-06-05 09:41:52 +08:00
刘祥超
8c064daf3f “服务分组”改为“网站分组” 2023-06-04 10:30:57 +08:00
刘祥超
b0b3dae147 增加并发任务函数 2023-06-03 11:13:05 +08:00
刘祥超
2ea2be43d3 更新components.js 2023-06-03 09:09:18 +08:00
刘祥超
81f7364e93 改进文字提示 2023-06-02 16:33:24 +08:00
刘祥超
c530869530 优化代码 2023-06-01 18:07:46 +08:00
刘祥超
586ccd90ec 初步实现HTTP3 2023-06-01 17:44:39 +08:00
刘祥超
ece237c49f 增加不能登录的敏感区域 2023-06-01 15:56:45 +08:00
刘祥超
11da5ca98c 线图坐标增加xColorFunc参数 2023-05-29 13:56:32 +08:00
刘祥超
276a68bda6 Update Dockerfile 2023-05-29 09:08:59 +08:00
刘祥超
fba89d197a 优化交互 2023-05-28 20:24:58 +08:00
刘祥超
b3259e2489 commit components.js 2023-05-28 19:22:05 +08:00
刘祥超
880704dda0 优化WAF“跳转‘动作显示 2023-05-28 17:22:20 +08:00
刘祥超
d5e92e9c09 WAF增加“跳转”动作 2023-05-28 17:11:45 +08:00
刘祥超
df2f5692bd 版本号改为1.1.0 2023-05-28 16:06:05 +08:00
刘祥超
dacc99b8b6 优化文字 2023-05-28 15:17:33 +08:00
刘祥超
0e8ade3b61 手动执行健康检查时提示用户当前集群尚未部署网站 2023-05-28 15:06:53 +08:00
刘祥超
fe2d8f6261 优化界面 2023-05-28 14:41:38 +08:00
刘祥超
927381039b 优化界面 2023-05-28 11:31:59 +08:00
刘祥超
6b95947acb 集群列表页也增加“创建节点”链接 2023-05-27 19:55:06 +08:00
刘祥超
b05e53ce58 登录界面屏蔽个别敏感区域 2023-05-27 18:12:40 +08:00
刘祥超
a2546582f2 优化界面 2023-05-27 17:11:18 +08:00
刘祥超
a023b3401e 优化界面 2023-05-27 17:05:17 +08:00
刘祥超
07142e9872 删除EdgePlus 2023-05-27 16:46:30 +08:00
刘祥超
f5fea6c34d 创建节点表单显示剩余配额 2023-05-27 15:33:08 +08:00
刘祥超
5eb0fb3422 正则表达式填写多行时提示用户需要转换成竖杠(|)符号 2023-05-27 14:44:39 +08:00
刘祥超
fa915e7c35 部分“服务”文字改为“网站” 2023-05-26 11:55:21 +08:00
刘祥超
2b7905d31c 优化文字和界面 2023-05-26 10:52:45 +08:00
刘祥超
9cbbf37add 优化界面 2023-05-26 10:31:03 +08:00
刘祥超
642a0c3b0d 优化界面 2023-05-25 17:42:22 +08:00
刘祥超
ff47a19250 优化界面 2023-05-25 17:29:55 +08:00
刘祥超
1fc1a3fbe3 WAF国家/地区封禁、省份封禁增加例外URL、限制URL 2023-05-25 12:02:19 +08:00
刘祥超
996172bd69 改进界面 2023-05-25 10:59:18 +08:00
刘祥超
df038314ef commit components.js 2023-05-24 17:21:05 +08:00
刘祥超
67881c2e34 网站全局设置中增加“自动匹配证书”选项 2023-05-24 17:20:28 +08:00
刘祥超
9c691a17b2 优化集群菜单 2023-05-23 19:50:19 +08:00
刘祥超
54df86a4a2 优化文字提示 2023-05-23 19:15:35 +08:00
刘祥超
70aff759d7 优化文字显示 2023-05-23 11:25:25 +08:00
刘祥超
d903cc6e3b 优化源站列表专属域名显示 2023-05-23 11:25:13 +08:00
刘祥超
c520271ab5 实现自定义页面组件 2023-05-22 17:30:46 +08:00
刘祥超
f6936224d9 调整集群设置菜单位置 2023-05-22 09:49:56 +08:00
刘祥超
283984a6a6 HTTP Header中支持设置非标Header 2023-05-19 19:52:10 +08:00
刘祥超
c4fc09f72a 优化HTTP Header页面 2023-05-19 17:40:00 +08:00
刘祥超
efdbabfa04 优化HTTP Header页面文字提示 2023-05-19 17:34:54 +08:00
刘祥超
a43af333fd 优化文字提示 2023-05-19 16:51:49 +08:00
刘祥超
1f5894ff82 HTTP Header - CORS跨域设置增加多个选项 2023-05-19 16:32:28 +08:00
刘祥超
79b4054e31 在节点列表显示租期、是否为备用节点等信息 2023-05-19 11:11:34 +08:00
刘祥超
376d7d0c78 修改部分代码的联系方式 2023-05-19 11:09:57 +08:00
刘祥超
c8b85330ed 删除QQ群信息 2023-05-17 19:22:56 +08:00
刘祥超
c65dffee13 实现基础的智能调度 2023-05-17 18:41:27 +08:00
刘祥超
275320ad9b 如果管理系统前端有反向代理,则不要自动从HTTP跳转到HTTPS 2023-05-07 09:28:18 +08:00
刘祥超
f71cdf9065 防盗链增加”同时检查Origin选项“ 2023-05-02 17:05:35 +08:00
刘祥超
8e0a239de9 优化WAF CC2规则说明文字 2023-05-02 17:04:40 +08:00
刘祥超
ed2b95bc3f 集群设置中“服务设置”改为“网站设置” 2023-04-26 14:04:49 +08:00
刘祥超
2e23ee4c66 优化健康检查界面 2023-04-26 10:48:44 +08:00
刘祥超
58def52e30 数据库手动清理页面增加按表名和按占用空间排序/优化数据库相关界面 2023-04-26 09:49:28 +08:00
刘祥超
aea5ef3b68 Update Dockerfile 2023-04-24 10:29:31 +08:00
刘祥超
f2e14cf0a6 版本号修改为1.0.4 2023-04-24 10:17:05 +08:00
刘祥超
bc9b6d1595 为集群选择DNS域名时自动排序并不显示已下线域名 2023-04-24 09:38:57 +08:00
刘祥超
fd11b62390 更新components.js 2023-04-23 20:13:23 +08:00
刘祥超
8db00a5ab5 远程升级API节点时自动上传边缘节点安装文件 2023-04-23 19:48:07 +08:00
刘祥超
a6757e9374 申请ACME证书时可以指定平台用户 2023-04-23 15:01:43 +08:00
刘祥超
e0fe385404 证书申请任务可以使用用户筛选 2023-04-23 11:03:07 +08:00
刘祥超
f9f4258ec1 证书列表可以使用用户筛选 2023-04-23 10:44:37 +08:00
刘祥超
f939d563ad 选择证书时,如果证书列表为空,则提示可以搜索未指定用户证书 2023-04-22 12:04:04 +08:00
刘祥超
fad03add54 “网站服务”改为“网站”、“网站列表” 2023-04-21 17:51:25 +08:00
刘祥超
9ab6dab081 SESSION读取失败时不强制重新登录 2023-04-21 15:39:51 +08:00
刘祥超
5a55268830 修复在HTTP/1.x下开多个窗口访问非常慢的问题 2023-04-19 21:03:46 +08:00
刘祥超
fc3769239d 节点列表页CPU、内存使用比例增加百分号 2023-04-19 19:26:49 +08:00
刘祥超
c9fb3153eb 安全设置增加“检查客户端指纹"和"检查客户端区域"选项 2023-04-19 18:25:10 +08:00
刘祥超
a31f9ed9c5 节点列表负载数据如果是0,则显示0.00 2023-04-18 11:23:49 +08:00
刘祥超
2f75828ba4 优化滚动条在firefox上的显示 2023-04-11 16:33:22 +08:00
刘祥超
89679ec358 版本号改为1.1.0 2023-04-10 21:03:41 +08:00
刘祥超
98f77f52df Update Dockerfile 2023-04-10 09:20:59 +08:00
296 changed files with 5755 additions and 1174 deletions

View File

@@ -54,7 +54,7 @@
* [管理平台](https://github.com/TeaOSLab/EdgeAdmin)
## 联系我们
有什么问题和建议都可以加入QQ群 `659832182` 或者 [Telegram群](https://t.me/+5kVCMGxQhZxiODY9)
有什么问题和建议都可以加入 [Telegram群](https://t.me/+5kVCMGxQhZxiODY9)
## 企业版
* [GoEdge企业版](https://goedge.cn/commercial) - 功能更强大的CDN系统

View File

@@ -1 +1 @@
这个目录下我们列举了所有需要公开声明的第三方License如果有遗漏烦请告知 iwind.liu@gmail.com。再次感谢这些开源软件项目和贡献人员
这个目录下我们列举了所有需要公开声明的第三方License如果有遗漏烦请告知 goedge.cdn@gmail.com。再次感谢这些开源软件项目和贡献人员

View File

@@ -1,7 +1,7 @@
FROM alpine:latest
LABEL maintainer="iwind.liu@gmail.com"
LABEL maintainer="goedge.cdn@gmail.com"
ENV TZ "Asia/Shanghai"
ENV VERSION 1.0.0
ENV VERSION 1.1.0
ENV ROOT_DIR /usr/local/goedge
ENV TAR_FILE edge-admin-linux-amd64-plus-v${VERSION}.zip
ENV TAR_URL "https://dl.goedge.cn/edge/v${VERSION}/edge-admin-linux-amd64-plus-v${VERSION}.zip"

View File

@@ -167,7 +167,7 @@ func AllModuleMaps() []maps.Map {
"url": "/dashboard",
},
{
"name": "网站服务",
"name": "网站列表",
"code": AdminModuleCodeServer,
"url": "/servers",
},

View File

@@ -29,7 +29,7 @@ func LoadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
return nil, err
}
v := reflect.Indirect(reflect.ValueOf(config)).Interface().(systemconfigs.SecurityConfig)
var v = reflect.Indirect(reflect.ValueOf(config)).Interface().(systemconfigs.SecurityConfig)
return &v, nil
}
@@ -83,7 +83,12 @@ func loadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
return sharedSecurityConfig, nil
}
config := &systemconfigs.SecurityConfig{}
var config = &systemconfigs.SecurityConfig{
Frame: FrameSameOrigin,
AllowLocal: true,
CheckClientFingerprint: false,
CheckClientRegion: true,
}
err = json.Unmarshal(resp.ValueJSON, config)
if err != nil {
logs.Println("[SECURITY_MANAGER]" + err.Error())
@@ -100,7 +105,9 @@ func loadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
func defaultSecurityConfig() *systemconfigs.SecurityConfig {
return &systemconfigs.SecurityConfig{
Frame: FrameSameOrigin,
AllowLocal: true,
Frame: FrameSameOrigin,
AllowLocal: true,
CheckClientFingerprint: false,
CheckClientRegion: true,
}
}

View File

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

View File

@@ -85,6 +85,9 @@ func (this *AdminNode) Run() {
// 启动API节点
this.startAPINode()
// 启动IP库
this.startIPLibrary()
// 启动Web服务
sessionManager, err := NewSessionManager()
if err != nil {

View File

@@ -0,0 +1,18 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package nodes
import (
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"github.com/iwind/TeaGo/logs"
)
// 启动IP库
func (this *AdminNode) startIPLibrary() {
logs.Println("[NODE]initializing ip library ...")
err := iplibrary.InitDefault()
if err != nil {
logs.Println("[NODE]initialize ip library failed: "+err.Error())
}
}

View File

@@ -40,6 +40,7 @@ func (this *SessionManager) Read(sid string) map[string]string {
resp, err := rpcClient.LoginSessionRPC().FindLoginSession(rpcClient.Context(0), &pb.FindLoginSessionRequest{Sid: sid})
if err != nil {
logs.Println("SESSION", "read '"+sid+"' failed: "+err.Error())
result["@error"] = err.Error()
return result
}

View File

@@ -2,6 +2,9 @@ package rpc
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"strings"
"sync"
)
@@ -28,3 +31,39 @@ func SharedRPC() (*RPCClient, error) {
sharedRPC = client
return sharedRPC, nil
}
// IsConnError 是否为连接错误
func IsConnError(err error) bool {
if err == nil {
return false
}
// 检查是否为连接错误
statusErr, ok := status.FromError(err)
if ok {
var errorCode = statusErr.Code()
return errorCode == codes.Unavailable || errorCode == codes.Canceled
}
if strings.Contains(err.Error(), "code = Canceled") {
return true
}
return false
}
// IsUnimplementedError 检查是否为未实现错误
func IsUnimplementedError(err error) bool {
if err == nil {
return false
}
statusErr, ok := status.FromError(err)
if ok {
if statusErr.Code() == codes.Unimplemented {
return true
}
}
return false
}

View File

@@ -119,7 +119,7 @@ func (this *CheckUpdatesTask) Loop() error {
var vMap = maps.NewMap(version)
if vMap.GetString("code") == "admin" {
var latestVersion = vMap.GetString("version")
if stringutil.VersionCompare(teaconst.Version, latestVersion) < 0 {
if stringutil.VersionCompare(teaconst.Version, latestVersion) < 0 && (len(config.IgnoredVersion) == 0 || stringutil.VersionCompare(latestVersion, config.IgnoredVersion) > 0) {
teaconst.NewVersionCode = latestVersion
teaconst.NewVersionDownloadURL = dlHost + vMap.GetString("url")
return nil

View File

@@ -0,0 +1,79 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package apinodeutils
import (
"crypto/md5"
"fmt"
"io"
"os"
)
// DeployFile 部署文件描述
type DeployFile struct {
OS string
Arch string
Version string
Path string
}
// Sum 计算概要
func (this *DeployFile) Sum() (string, error) {
fp, err := os.Open(this.Path)
if err != nil {
return "", err
}
defer func() {
_ = fp.Close()
}()
m := md5.New()
buffer := make([]byte, 128*1024)
for {
n, err := fp.Read(buffer)
if err != nil {
if err == io.EOF {
break
}
return "", err
}
_, err = m.Write(buffer[:n])
if err != nil {
return "", err
}
}
sum := m.Sum(nil)
return fmt.Sprintf("%x", sum), nil
}
// Read 读取一个片段数据
func (this *DeployFile) Read(offset int64) (data []byte, newOffset int64, err error) {
fp, err := os.Open(this.Path)
if err != nil {
return nil, offset, err
}
defer func() {
_ = fp.Close()
}()
stat, err := fp.Stat()
if err != nil {
return nil, offset, err
}
if offset >= stat.Size() {
return nil, offset, io.EOF
}
_, err = fp.Seek(offset, io.SeekStart)
if err != nil {
return nil, offset, err
}
buffer := make([]byte, 128*1024)
n, err := fp.Read(buffer)
if err != nil {
return nil, offset, err
}
return buffer[:n], offset + int64(n), nil
}

View File

@@ -0,0 +1,96 @@
package apinodeutils
import (
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/files"
stringutil "github.com/iwind/TeaGo/utils/string"
"regexp"
)
// DeployManager 节点部署文件管理器
// 如果节点部署文件有变化需要重启API节点以便于生效
type DeployManager struct {
dir string
}
// NewDeployManager 获取新节点部署文件管理器
func NewDeployManager() *DeployManager {
var manager = &DeployManager{
dir: Tea.Root + "/edge-api/deploy",
}
manager.LoadNodeFiles()
manager.LoadNSNodeFiles()
return manager
}
// LoadNodeFiles 加载所有边缘节点文件
func (this *DeployManager) LoadNodeFiles() []*DeployFile {
var keyMap = map[string]*DeployFile{} // key => File
var reg = regexp.MustCompile(`^edge-node-(\w+)-(\w+)-v([0-9.]+)\.zip$`)
for _, file := range files.NewFile(this.dir).List() {
var name = file.Name()
if !reg.MatchString(name) {
continue
}
var matches = reg.FindStringSubmatch(name)
var osName = matches[1]
var arch = matches[2]
var version = matches[3]
var key = osName + "_" + arch
oldFile, ok := keyMap[key]
if ok && stringutil.VersionCompare(oldFile.Version, version) > 0 {
continue
}
keyMap[key] = &DeployFile{
OS: osName,
Arch: arch,
Version: version,
Path: file.Path(),
}
}
var result = []*DeployFile{}
for _, v := range keyMap {
result = append(result, v)
}
return result
}
// LoadNSNodeFiles 加载所有NS节点安装文件
func (this *DeployManager) LoadNSNodeFiles() []*DeployFile {
var keyMap = map[string]*DeployFile{} // key => File
var reg = regexp.MustCompile(`^edge-dns-(\w+)-(\w+)-v([0-9.]+)\.zip$`)
for _, file := range files.NewFile(this.dir).List() {
var name = file.Name()
if !reg.MatchString(name) {
continue
}
var matches = reg.FindStringSubmatch(name)
var osName = matches[1]
var arch = matches[2]
var version = matches[3]
var key = osName + "_" + arch
oldFile, ok := keyMap[key]
if ok && stringutil.VersionCompare(oldFile.Version, version) > 0 {
continue
}
keyMap[key] = &DeployFile{
OS: osName,
Arch: arch,
Version: version,
Path: file.Path(),
}
}
var result = []*DeployFile{}
for _, v := range keyMap {
result = append(result, v)
}
return result
}

View File

@@ -4,6 +4,7 @@ package apinodeutils
import (
"compress/gzip"
"context"
"crypto/md5"
"errors"
"fmt"
@@ -62,7 +63,36 @@ func (this *Upgrader) Upgrade() error {
if err != nil {
return err
}
versionResp, err := rpcClient.APINodeRPC().FindCurrentAPINodeVersion(sharedClient.Context(0), &pb.FindCurrentAPINodeVersionRequest{})
// 升级边缘节点
err = this.upgradeNodes(sharedClient.Context(0), rpcClient)
if err != nil {
return err
}
// 升级NS节点
err = this.upgradeNSNodes(sharedClient.Context(0), rpcClient)
if err != nil {
return err
}
// 升级API节点
err = this.upgradeAPINode(sharedClient.Context(0), rpcClient)
if err != nil {
return errors.New("upgrade api node failed: " + err.Error())
}
return nil
}
// Progress 查看升级进程
func (this *Upgrader) Progress() *Progress {
return this.progress
}
// 升级API节点
func (this *Upgrader) upgradeAPINode(ctx context.Context, rpcClient *rpc.RPCClient) error {
versionResp, err := rpcClient.APINodeRPC().FindCurrentAPINodeVersion(ctx, &pb.FindCurrentAPINodeVersionRequest{})
if err != nil {
return err
}
@@ -77,7 +107,7 @@ func (this *Upgrader) Upgrade() error {
return errors.New(reason)
}
localVersion, err := localVersion()
localVersion, err := lookupLocalVersion()
if err != nil {
return errors.New("lookup version failed: " + err.Error())
}
@@ -196,6 +226,124 @@ func (this *Upgrader) Upgrade() error {
return nil
}
func (this *Upgrader) Progress() *Progress {
return this.progress
// 升级边缘节点
func (this *Upgrader) upgradeNodes(ctx context.Context, rpcClient *rpc.RPCClient) error {
// 本地的
var manager = NewDeployManager()
var localFileMap = map[string]*DeployFile{} // os_arch => *DeployFile
for _, deployFile := range manager.LoadNodeFiles() {
localFileMap[deployFile.OS+"_"+deployFile.Arch] = deployFile
}
remoteFilesResp, err := rpcClient.APINodeRPC().FindLatestDeployFiles(ctx, &pb.FindLatestDeployFilesRequest{})
if err != nil {
return err
}
var remoteFileMap = map[string]*pb.FindLatestDeployFilesResponse_DeployFile{} // os_arch => *DeployFile
for _, nodeFile := range remoteFilesResp.NodeDeployFiles {
remoteFileMap[nodeFile.Os+"_"+nodeFile.Arch] = nodeFile
}
// 对比版本
for key, deployFile := range localFileMap {
remoteDeployFile, ok := remoteFileMap[key]
if !ok || stringutil.VersionCompare(remoteDeployFile.Version, deployFile.Version) < 0 {
err = this.uploadNodeDeployFile(ctx, rpcClient, deployFile.Path)
if err != nil {
return errors.New("upload deploy file '" + filepath.Base(deployFile.Path) + "' failed: " + err.Error())
}
}
}
return nil
}
// 升级NS节点
func (this *Upgrader) upgradeNSNodes(ctx context.Context, rpcClient *rpc.RPCClient) error {
// 本地的
var manager = NewDeployManager()
var localFileMap = map[string]*DeployFile{} // os_arch => *DeployFile
for _, deployFile := range manager.LoadNSNodeFiles() {
localFileMap[deployFile.OS+"_"+deployFile.Arch] = deployFile
}
remoteFilesResp, err := rpcClient.APINodeRPC().FindLatestDeployFiles(ctx, &pb.FindLatestDeployFilesRequest{})
if err != nil {
return err
}
var remoteFileMap = map[string]*pb.FindLatestDeployFilesResponse_DeployFile{} // os_arch => *DeployFile
for _, nodeFile := range remoteFilesResp.NsNodeDeployFiles {
remoteFileMap[nodeFile.Os+"_"+nodeFile.Arch] = nodeFile
}
// 对比版本
for key, deployFile := range localFileMap {
remoteDeployFile, ok := remoteFileMap[key]
if !ok || stringutil.VersionCompare(remoteDeployFile.Version, deployFile.Version) < 0 {
err = this.uploadNodeDeployFile(ctx, rpcClient, deployFile.Path)
if err != nil {
return errors.New("upload deploy file '" + filepath.Base(deployFile.Path) + "' failed: " + err.Error())
}
}
}
return nil
}
// 上传节点文件
func (this *Upgrader) uploadNodeDeployFile(ctx context.Context, rpcClient *rpc.RPCClient, path string) error {
fp, err := os.Open(path)
if err != nil {
return err
}
defer func() {
_ = fp.Close()
}()
var buf = make([]byte, 128*4096)
var isFirst = true
var hash = md5.New()
for {
n, err := fp.Read(buf)
if n > 0 {
hash.Write(buf[:n])
_, uploadErr := rpcClient.APINodeRPC().UploadDeployFileToAPINode(ctx, &pb.UploadDeployFileToAPINodeRequest{
Filename: filepath.Base(path),
Sum: "",
ChunkData: buf[:n],
IsFirstChunk: isFirst,
IsLastChunk: false,
})
if uploadErr != nil {
return uploadErr
}
isFirst = false
}
if err != nil {
if err == io.EOF {
err = nil
_, uploadErr := rpcClient.APINodeRPC().UploadDeployFileToAPINode(ctx, &pb.UploadDeployFileToAPINodeRequest{
Filename: filepath.Base(path),
Sum: fmt.Sprintf("%x", hash.Sum(nil)),
ChunkData: nil,
IsFirstChunk: false,
IsLastChunk: true,
})
if uploadErr != nil {
return uploadErr
}
break
}
return err
}
}
return nil
}

View File

@@ -39,7 +39,7 @@ func CanUpgrade(apiVersion string, osName string, arch string) (canUpgrade bool,
return false, "is directory"
}
localVersion, err := localVersion()
localVersion, err := lookupLocalVersion()
if err != nil {
return false, "lookup version failed: " + err.Error()
}
@@ -53,9 +53,7 @@ func CanUpgrade(apiVersion string, osName string, arch string) (canUpgrade bool,
return true, ""
}
func localVersion() (string, error) {
func lookupLocalVersion() (string, error) {
var cmd = exec.Command(apiExe(), "-V")
var output = &bytes.Buffer{}
cmd.Stdout = output
@@ -74,7 +72,6 @@ func localVersion() (string, error) {
return localVersion, nil
}
func apiExe() string {
return Tea.Root + "/edge-api/bin/edge-api"
}
}

View File

@@ -75,7 +75,7 @@ func FormatCount(count int64) string {
return fmt.Sprintf("%.1fB", float32(count)/1000/1000/1000)
}
func FormatFloat(f interface{}, decimal int) string {
func FormatFloat(f any, decimal int) string {
if f == nil {
return ""
}
@@ -101,10 +101,29 @@ func FormatFloat(f interface{}, decimal int) string {
return ""
}
func FormatFloat2(f interface{}) string {
func FormatFloat2(f any) string {
return FormatFloat(f, 2)
}
// PadFloatZero 为浮点型数字字符串填充足够的0
func PadFloatZero(s string, countZero int) string {
if countZero <= 0 {
return s
}
if len(s) == 0 {
s = "0"
}
var index = strings.Index(s, ".")
if index < 0 {
return s + "." + strings.Repeat("0", countZero)
}
var decimalLen = len(s) - 1 - index
if decimalLen < countZero {
return s + strings.Repeat("0", countZero-decimalLen)
}
return s
}
var decimalReg = regexp.MustCompile(`^(\d+\.\d+)([a-zA-Z]+)?$`)
// TrimZeroSuffix 去除小数数字尾部多余的0

View File

@@ -4,6 +4,7 @@ package numberutils_test
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/iwind/TeaGo/assert"
"testing"
)
@@ -49,6 +50,24 @@ func TestFormatFloat(t *testing.T) {
t.Log(numberutils.FormatFloat(-221745.12, 2))
}
func TestFormatFloat2(t *testing.T) {
t.Log(numberutils.FormatFloat2(0))
t.Log(numberutils.FormatFloat2(0.0))
t.Log(numberutils.FormatFloat2(1.23456))
t.Log(numberutils.FormatFloat2(1.0))
}
func TestPadFloatZero(t *testing.T) {
var a = assert.NewAssertion(t)
a.IsTrue(numberutils.PadFloatZero("1", 0) == "1")
a.IsTrue(numberutils.PadFloatZero("1", 2) == "1.00")
a.IsTrue(numberutils.PadFloatZero("1.1", 2) == "1.10")
a.IsTrue(numberutils.PadFloatZero("1.12", 2) == "1.12")
a.IsTrue(numberutils.PadFloatZero("1.123", 2) == "1.123")
a.IsTrue(numberutils.PadFloatZero("10000.123", 2) == "10000.123")
a.IsTrue(numberutils.PadFloatZero("", 2) == "0.00")
}
func TestTrimZeroSuffix(t *testing.T) {
for _, s := range []string{
"1",

View File

@@ -0,0 +1,56 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package taskutils
import (
"errors"
"reflect"
"sync"
)
func RunConcurrent(tasks any, concurrent int, f func(task any)) error {
if tasks == nil {
return nil
}
var tasksValue = reflect.ValueOf(tasks)
if tasksValue.Type().Kind() != reflect.Slice {
return errors.New("ony works for slice")
}
var countTasks = tasksValue.Len()
if countTasks == 0 {
return nil
}
if concurrent <= 0 {
concurrent = 8
}
if concurrent > countTasks {
concurrent = countTasks
}
var taskChan = make(chan any, countTasks)
for i := 0; i < countTasks; i++ {
taskChan <- tasksValue.Index(i).Interface()
}
var wg = &sync.WaitGroup{}
wg.Add(concurrent)
for i := 0; i < concurrent; i++ {
go func() {
defer wg.Done()
for {
select {
case task := <-taskChan:
f(task)
default:
return
}
}
}()
}
wg.Wait()
return nil
}

View File

@@ -0,0 +1,17 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package taskutils_test
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils/taskutils"
"testing"
)
func TestRunConcurrent(t *testing.T) {
err := taskutils.RunConcurrent([]string{"a", "b", "c", "d", "e"}, 3, func(task any) {
t.Log("run", task)
})
if err != nil {
t.Fatal(err)
}
}

View File

@@ -2,37 +2,49 @@ package actionutils
import (
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type TabItem struct {
Name string `json:"name"`
SubName string `json:"subName"`
URL string `json:"url"`
Icon string `json:"icon"`
IsActive bool `json:"isActive"`
IsRight bool `json:"isRight"`
IsTitle bool `json:"isTitle"`
IsDisabled bool `json:"isDisabled"`
}
// Tabbar Tabbar定义
type Tabbar struct {
items []maps.Map
items []*TabItem
}
// NewTabbar 获取新对象
func NewTabbar() *Tabbar {
return &Tabbar{
items: []maps.Map{},
items: []*TabItem{},
}
}
// Add 添加菜单项
func (this *Tabbar) Add(name string, subName string, url string, icon string, active bool) maps.Map {
m := maps.Map{
"name": name,
"subName": subName,
"url": url,
"icon": icon,
"active": active,
"right": false,
func (this *Tabbar) Add(name string, subName string, url string, icon string, active bool) *TabItem {
var m = &TabItem{
Name: name,
SubName: subName,
URL: url,
Icon: icon,
IsActive: active,
IsRight: false,
IsTitle: false,
IsDisabled: false,
}
this.items = append(this.items, m)
return m
}
// Items 取得所有的Items
func (this *Tabbar) Items() []maps.Map {
func (this *Tabbar) Items() []*TabItem {
return this.items
}

View File

@@ -24,10 +24,19 @@ import (
// Fail 提示服务器错误信息
func Fail(action actions.ActionWrapper, err error) {
if err != nil {
logs.Println("[" + reflect.TypeOf(action).String() + "]" + findStack(err.Error()))
if err == nil {
err = errors.New("unknown error")
}
action.Object().Fail(teaconst.ErrServer + "" + err.Error() + "")
logs.Println("[" + reflect.TypeOf(action).String() + "]" + findStack(err.Error()))
_, _, isLocalAPI, issuesHTML := parseAPIErr(action, err)
if isLocalAPI && len(issuesHTML) > 0 {
action.Object().Fail(teaconst.ErrServer + "" + err.Error() + ";最近一次错误提示:" + issuesHTML + "")
} else {
action.Object().Fail(teaconst.ErrServer + "" + err.Error() + "")
}
}
// FailPage 提示页面错误信息
@@ -38,67 +47,11 @@ func FailPage(action actions.ActionWrapper, err error) {
logs.Println("[" + reflect.TypeOf(action).String() + "]" + findStack(err.Error()))
// 当前API终端地址
var apiEndpoints = []string{}
apiConfig, apiConfigErr := configs.LoadAPIConfig()
if apiConfigErr == nil && apiConfig != nil {
apiEndpoints = append(apiEndpoints, apiConfig.RPC.Endpoints...)
}
var isRPCConnError bool
err, isRPCConnError = rpcerrors.HumanError(err, apiEndpoints, Tea.ConfigFile("api.yaml"))
var apiNodeIsStarting = false
var apiNodeProgress = ""
if isRPCConnError {
// API节点是否正在启动
var sock = gosock.NewTmpSock("edge-api")
reply, err := sock.SendTimeout(&gosock.Command{
Code: "starting",
Params: nil,
}, 1*time.Second)
if err == nil && reply != nil {
var params = maps.NewMap(reply.Params)
if params.GetBool("isStarting") {
apiNodeIsStarting = true
var progressMap = params.GetMap("progress")
apiNodeProgress = progressMap.GetString("description")
}
}
}
action.Object().ResponseWriter.WriteHeader(http.StatusInternalServerError)
if len(action.Object().Request.Header.Get("X-Requested-With")) > 0 {
action.Object().WriteString(teaconst.ErrServer)
} else {
// 本地的一些错误提示
var isLocalAPI = false
if isRPCConnError {
host, _, hostErr := net.SplitHostPort(action.Object().Request.Host)
if hostErr == nil {
for _, endpoint := range apiEndpoints {
if strings.HasPrefix(endpoint, "http://"+host) || strings.HasPrefix(endpoint, "https://"+host) || strings.HasPrefix(endpoint, host) {
isLocalAPI = true
break
}
}
}
}
var issuesHTML = ""
if isLocalAPI {
// 读取本地API节点的issues
issuesData, issuesErr := os.ReadFile(Tea.Root + "/edge-api/logs/issues.log")
if issuesErr == nil {
var issueMaps = []maps.Map{}
issuesErr = json.Unmarshal(issuesData, &issueMaps)
if issuesErr == nil && len(issueMaps) > 0 {
var issueMap = issueMaps[0]
issuesHTML = "本地API节点启动错误" + issueMap.GetString("message") + ",处理建议:" + issueMap.GetString("suggestion")
}
}
}
apiNodeIsStarting, apiNodeProgress, _, issuesHTML := parseAPIErr(action, err)
var html = `<!DOCTYPE html>
<html>
<head>
@@ -187,3 +140,61 @@ func findStack(err string) string {
return err
}
// 分析API节点的错误信息
func parseAPIErr(action actions.ActionWrapper, err error) (apiNodeIsStarting bool, apiNodeProgress string, isLocalAPI bool, issuesHTML string) {
// 当前API终端地址
var apiEndpoints = []string{}
apiConfig, apiConfigErr := configs.LoadAPIConfig()
if apiConfigErr == nil && apiConfig != nil {
apiEndpoints = append(apiEndpoints, apiConfig.RPC.Endpoints...)
}
var isRPCConnError bool
err, isRPCConnError = rpcerrors.HumanError(err, apiEndpoints, Tea.ConfigFile("api.yaml"))
if isRPCConnError {
// API节点是否正在启动
var sock = gosock.NewTmpSock("edge-api")
reply, err := sock.SendTimeout(&gosock.Command{
Code: "starting",
Params: nil,
}, 1*time.Second)
if err == nil && reply != nil {
var params = maps.NewMap(reply.Params)
if params.GetBool("isStarting") {
apiNodeIsStarting = true
var progressMap = params.GetMap("progress")
apiNodeProgress = progressMap.GetString("description")
}
}
}
// 本地的一些错误提示
if isRPCConnError {
host, _, hostErr := net.SplitHostPort(action.Object().Request.Host)
if hostErr == nil {
for _, endpoint := range apiEndpoints {
if strings.HasPrefix(endpoint, "http://"+host) || strings.HasPrefix(endpoint, "https://"+host) || strings.HasPrefix(endpoint, host) {
isLocalAPI = true
break
}
}
}
}
if isLocalAPI {
// 读取本地API节点的issues
issuesData, issuesErr := os.ReadFile(Tea.Root + "/edge-api/logs/issues.log")
if issuesErr == nil {
var issueMaps = []maps.Map{}
issuesErr = json.Unmarshal(issuesData, &issueMaps)
if issuesErr == nil && len(issueMaps) > 0 {
var issueMap = issueMaps[0]
issuesHTML = "本地API节点启动错误" + issueMap.GetString("message") + ",处理建议:" + issueMap.GetString("suggestion")
}
}
}
return
}

View File

@@ -39,6 +39,17 @@ func (this *CreateBatchAction) RunGet(params struct {
}
this.Data["leftMenuItems"] = leftMenuItems
// 限额
maxNodes, leftNodes, err := this.findNodesQuota()
if err != nil {
this.ErrorPage(err)
return
}
this.Data["quota"] = maps.Map{
"maxNodes": maxNodes,
"leftNodes": leftNodes,
}
this.Show()
}

View File

@@ -0,0 +1,8 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package cluster
func (this *CreateBatchAction) findNodesQuota() (maxNodes int32, leftNodes int32, err error) {
return
}

View File

@@ -30,6 +30,11 @@ func (this *CreateNodeAction) Init() {
func (this *CreateNodeAction) RunGet(params struct {
ClusterId int64
}) {
if params.ClusterId <= 0 {
this.RedirectURL("/clusters")
return
}
var leftMenuItems = []maps.Map{
{
"name": "单个创建",
@@ -92,6 +97,17 @@ func (this *CreateNodeAction) RunGet(params struct {
// 安装文件下载
this.Data["installerFiles"] = clusterutils.ListInstallerFiles()
// 限额
maxNodes, leftNodes, err := this.findNodesQuota()
if err != nil {
this.ErrorPage(err)
return
}
this.Data["quota"] = maps.Map{
"maxNodes": maxNodes,
"leftNodes": leftNodes,
}
this.Show()
}

View File

@@ -0,0 +1,8 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package cluster
func (this *CreateNodeAction) findNodesQuota() (maxNodes int32, leftNodes int32, err error) {
return
}

View File

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

View File

@@ -187,6 +187,8 @@ func (this *DetailAction) RunGet(params struct {
"route": route.Name,
"value": addr.Ip,
"clusterName": cluster.Name,
"isBackup": dnsInfo.IsBackupForCluster || dnsInfo.IsBackupForGroup,
"isOffline": dnsInfo.IsOffline,
})
}
}
@@ -343,25 +345,29 @@ func (this *DetailAction) RunGet(params struct {
}
this.Data["node"] = maps.Map{
"id": node.Id,
"name": node.Name,
"ipAddresses": ipAddressMaps,
"cluster": clusterMap,
"secondaryClusters": secondaryClustersMaps,
"login": loginMap,
"installDir": node.InstallDir,
"isInstalled": node.IsInstalled,
"uniqueId": node.UniqueId,
"secret": node.Secret,
"maxCPU": node.MaxCPU,
"isOn": node.IsOn,
"records": recordMaps,
"routes": routeMaps,
"level": node.Level,
"levelInfo": nodeconfigs.FindNodeLevel(int(node.Level)),
"lnAddrs": lnAddrs,
"enableIPLists": node.EnableIPLists,
"apiNodeAddrs": apiNodeAddrStrings,
"id": node.Id,
"name": node.Name,
"ipAddresses": ipAddressMaps,
"cluster": clusterMap,
"secondaryClusters": secondaryClustersMaps,
"login": loginMap,
"installDir": node.InstallDir,
"isInstalled": node.IsInstalled,
"uniqueId": node.UniqueId,
"secret": node.Secret,
"maxCPU": node.MaxCPU,
"isOn": node.IsOn,
"records": recordMaps,
"routes": routeMaps,
"level": node.Level,
"levelInfo": nodeconfigs.FindNodeLevel(int(node.Level)),
"lnAddrs": lnAddrs,
"enableIPLists": node.EnableIPLists,
"apiNodeAddrs": apiNodeAddrStrings,
"offlineDay": node.OfflineDay,
"isOffline": len(node.OfflineDay) > 0 && node.OfflineDay < timeutil.Format("Ymd"),
"isBackupForCluster": node.IsBackupForCluster,
"isBackupForGroup": node.IsBackupForGroup,
"status": maps.Map{
"isActive": status.IsActive,

View File

@@ -209,6 +209,8 @@ func (this *NodesAction) RunGet(params struct {
"isInstalled": node.IsInstalled,
"isOn": node.IsOn,
"isUp": node.IsUp,
"isBackup": node.IsBackupForCluster || node.IsBackupForGroup,
"offlineDay": node.OfflineDay,
"installStatus": maps.Map{
"isRunning": node.InstallStatus.IsRunning,
"isFinished": node.InstallStatus.IsFinished,
@@ -225,7 +227,7 @@ func (this *NodesAction) RunGet(params struct {
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
"trafficInBytes": status.TrafficInBytes,
"trafficOutBytes": status.TrafficOutBytes,
"load1m": numberutils.FormatFloat2(status.Load1m),
"load1m": numberutils.PadFloatZero(numberutils.FormatFloat2(status.Load1m), 2),
"countConnections": status.ConnectionCount,
},
"cluster": maps.Map{

View File

@@ -64,6 +64,7 @@ p { color: grey; }
</body>
</html>`
}
this.Data["httpAllDomainMismatchActionContentHTML"] = httpAllDomainMismatchActionContentHTML
this.Show()
@@ -77,8 +78,12 @@ func (this *IndexAction) RunPost(params struct {
HttpAllAllowMismatchDomainsJSON []byte
HttpAllAllowNodeIP bool
HttpAllDefaultDomain string
HttpAllNodeIPPageHTML string
HttpAllNodeIPShowPage bool
HttpAllForceLnRequest bool
HttpAllSupportsLowVersionHTTP bool
HttpAllSupportsLowVersionHTTP bool
HttpAllMatchCertFromAllServers bool
HttpAccessLogEnableRequestHeaders bool
HttpAccessLogEnableResponseHeaders bool
@@ -135,9 +140,13 @@ func (this *IndexAction) RunPost(params struct {
config.HTTPAll.AllowMismatchDomains = allowMismatchDomains
config.HTTPAll.AllowNodeIP = params.HttpAllAllowNodeIP
config.HTTPAll.DefaultDomain = params.HttpAllDefaultDomain
config.HTTPAll.NodeIPShowPage = params.HttpAllNodeIPShowPage
config.HTTPAll.NodeIPPageHTML = params.HttpAllNodeIPPageHTML
// HTTP All
config.HTTPAll.SupportsLowVersionHTTP = params.HttpAllSupportsLowVersionHTTP
config.HTTPAll.MatchCertFromAllServers = params.HttpAllMatchCertFromAllServers
config.HTTPAll.ForceLnRequest = params.HttpAllForceLnRequest
// 访问日志
config.HTTPAccessLog.EnableRequestHeaders = params.HttpAccessLogEnableRequestHeaders

View File

@@ -15,7 +15,17 @@ func (this *RunPopupAction) Init() {
this.Nav("", "", "")
}
func (this *RunPopupAction) RunGet(params struct{}) {
func (this *RunPopupAction) RunGet(params struct {
ClusterId int64
}) {
// 检查是否已部署服务
countServersResp, err := this.RPC().ServerRPC().CountAllEnabledServersWithNodeClusterId(this.AdminContext(), &pb.CountAllEnabledServersWithNodeClusterIdRequest{NodeClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["hasServers"] = countServersResp.Count > 0
this.Show()
}

View File

@@ -24,6 +24,7 @@ func init() {
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
Helper(clusters.NewClusterHelper()).
Prefix("/clusters/cluster/settings").
Data("teaSubMenu", "cluster").
GetPost("", new(IndexAction)).
// 健康检查

View File

@@ -29,9 +29,9 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
action.Data["teaMenu"] = "clusters"
selectedTabbar := action.Data.GetString("mainTab")
clusterId := action.ParamInt64("clusterId")
clusterIdString := strconv.FormatInt(clusterId, 10)
var selectedTabbar = action.Data.GetString("mainTab")
var clusterId = action.ParamInt64("clusterId")
var clusterIdString = strconv.FormatInt(clusterId, 10)
action.Data["clusterId"] = clusterId
if clusterId > 0 {
@@ -57,14 +57,45 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
return
}
var nodeId = action.ParamInt64("nodeId")
var isInCluster = nodeId <= 0
var tabbar = actionutils.NewTabbar()
tabbar.Add("集群列表", "", "/clusters", "", false)
if teaconst.IsPlus {
tabbar.Add("集群看板", "", "/clusters/cluster/boards?clusterId="+clusterIdString, "chart line area", selectedTabbar == "board")
{
var url = "/clusters"
if !isInCluster {
url = "/clusters/cluster/nodes?clusterId=" + clusterIdString
}
tabbar.Add("", "", url, "arrow left", false)
}
{
var url = "/clusters/cluster?clusterId=" + clusterIdString
if !isInCluster {
url = "/clusters/cluster/nodes?clusterId=" + clusterIdString
}
var item = tabbar.Add(cluster.Name, "", url, "angle right", true)
item.IsTitle = true
}
if teaconst.IsPlus {
{
var item = tabbar.Add("集群看板", "", "/clusters/cluster/boards?clusterId="+clusterIdString, "chart line area", selectedTabbar == "board")
item.IsDisabled = !isInCluster
}
}
{
var item = tabbar.Add("节点列表", "", "/clusters/cluster/nodes?clusterId="+clusterIdString, "server", selectedTabbar == "node")
item.IsDisabled = !isInCluster
}
{
var item = tabbar.Add("集群设置", "", "/clusters/cluster/settings?clusterId="+clusterIdString, "setting", selectedTabbar == "setting")
item.IsDisabled = !isInCluster
}
{
var item = tabbar.Add("删除集群", "", "/clusters/cluster/delete?clusterId="+clusterIdString, "trash", selectedTabbar == "delete")
item.IsDisabled = !isInCluster
}
tabbar.Add("集群节点", "", "/clusters/cluster/nodes?clusterId="+clusterIdString, "server", selectedTabbar == "node")
tabbar.Add("集群设置", "", "/clusters/cluster/settings?clusterId="+clusterIdString, "setting", selectedTabbar == "setting")
tabbar.Add("删除集群", "", "/clusters/cluster/delete?clusterId="+clusterIdString, "trash", selectedTabbar == "delete")
actionutils.SetTabbar(action, tabbar)
// 左侧菜单
@@ -97,14 +128,39 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, info *pb.F
"isActive": selectedItem == "basic",
"isOn": true,
})
items = append(items, maps.Map{
"name": "缓存设置",
"name": "DNS设置",
"url": "/clusters/cluster/settings/dns?clusterId=" + clusterId,
"isActive": selectedItem == "dns",
"isOn": cluster.DnsDomainId > 0 || len(cluster.DnsName) > 0,
})
items = append(items, maps.Map{
"name": "健康检查",
"url": "/clusters/cluster/settings/health?clusterId=" + clusterId,
"isActive": selectedItem == "health",
"isOn": info != nil && info.HealthCheckIsOn,
})
items = append(items, maps.Map{
"name": "-",
})
items = append(items, maps.Map{
"name": "网站设置",
"url": "/clusters/cluster/settings/global-server-config?clusterId=" + clusterId,
"isActive": selectedItem == "globalServerConfig",
"isOn": true,
})
items = append(items, maps.Map{
"name": "缓存策略",
"url": "/clusters/cluster/settings/cache?clusterId=" + clusterId,
"isActive": selectedItem == "cache",
"isOn": cluster.HttpCachePolicyId > 0,
})
items = append(items, maps.Map{
"name": "WAF设置",
"name": "WAF策略",
"url": "/clusters/cluster/settings/waf?clusterId=" + clusterId,
"isActive": selectedItem == "waf",
"isOn": cluster.HttpFirewallPolicyId > 0,
@@ -132,20 +188,6 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, info *pb.F
"isActive": false,
})
items = append(items, maps.Map{
"name": "DNS设置",
"url": "/clusters/cluster/settings/dns?clusterId=" + clusterId,
"isActive": selectedItem == "dns",
"isOn": cluster.DnsDomainId > 0 || len(cluster.DnsName) > 0,
})
items = append(items, maps.Map{
"name": "健康检查",
"url": "/clusters/cluster/settings/health?clusterId=" + clusterId,
"isActive": selectedItem == "health",
"isOn": info != nil && info.HealthCheckIsOn,
})
items = append(items, maps.Map{
"name": "DDoS防护",
"url": "/clusters/cluster/settings/ddos-protection?clusterId=" + clusterId,
@@ -153,13 +195,6 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, info *pb.F
"isOn": info != nil && info.HasDDoSProtection,
})
items = append(items, maps.Map{
"name": "服务设置",
"url": "/clusters/cluster/settings/global-server-config?clusterId=" + clusterId,
"isActive": selectedItem == "globalServerConfig",
"isOn": true,
})
items = append(items, maps.Map{
"name": "-",
})

View File

@@ -0,0 +1,52 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package clusters
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/types"
)
type CreateNodeAction struct {
actionutils.ParentAction
}
func (this *CreateNodeAction) Init() {
this.Nav("", "cluster", "createNode")
}
func (this *CreateNodeAction) RunGet(params struct{}) {
// 集群总数
totalClustersResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClusters(this.AdminContext(), &pb.CountAllEnabledNodeClustersRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["totalNodeClusters"] = totalClustersResp.Count
// 节点总数
totalNodesResp, err := this.RPC().NodeRPC().CountAllEnabledNodes(this.AdminContext(), &pb.CountAllEnabledNodesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["totalNodes"] = totalNodesResp.Count
// 如果只有一个默认集群,那么直接跳转到集群
clustersResp, err := this.RPC().NodeClusterRPC().ListEnabledNodeClusters(this.AdminContext(), &pb.ListEnabledNodeClustersRequest{
Offset: 0,
Size: 2,
Keyword: "",
})
if err != nil {
this.ErrorPage(err)
return
}
if len(clustersResp.NodeClusters) == 1 {
this.RedirectURL("/clusters/cluster/createNode?clusterId=" + types.String(clustersResp.NodeClusters[0].Id))
return
}
this.Show()
}

View File

@@ -13,9 +13,11 @@ func init() {
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
Helper(clusterutils.NewClustersHelper()).
Data("teaMenu", "clusters").
Data("teaSubMenu", "cluster").
Prefix("/clusters").
Get("", new(IndexAction)).
GetPost("/create", new(CreateAction)).
GetPost("/createNode", new(CreateNodeAction)).
Post("/pin", new(PinAction)).
Get("/nodes", new(NodesAction)).

View File

@@ -211,6 +211,8 @@ func (this *NodesAction) RunGet(params struct {
"isInstalled": node.IsInstalled,
"isOn": node.IsOn,
"isUp": node.IsUp,
"isBackup": node.IsBackupForCluster || node.IsBackupForGroup,
"offlineDay": node.OfflineDay,
"installStatus": maps.Map{
"isRunning": node.InstallStatus.IsRunning,
"isFinished": node.InstallStatus.IsFinished,
@@ -222,12 +224,12 @@ func (this *NodesAction) RunGet(params struct {
"updatedAt": status.UpdatedAt,
"hostname": status.Hostname,
"cpuUsage": status.CPUUsage,
"cpuUsageText": numberutils.FormatFloat2(status.CPUUsage * 100),
"cpuUsageText": numberutils.FormatFloat2(status.CPUUsage*100) + "%",
"memUsage": status.MemoryUsage,
"memUsageText": numberutils.FormatFloat2(status.MemoryUsage * 100),
"memUsageText": numberutils.FormatFloat2(status.MemoryUsage*100) + "%",
"trafficInBytes": status.TrafficInBytes,
"trafficOutBytes": status.TrafficOutBytes,
"load1m": numberutils.FormatFloat2(status.Load1m),
"load1m": numberutils.PadFloatZero(numberutils.FormatFloat2(status.Load1m), 2),
"countConnections": status.ConnectionCount,
},
"cluster": maps.Map{

View File

@@ -15,6 +15,9 @@ func (this *CheckAction) RunPost(params struct {
HasError bool
IsUpdated bool
}) {
var isStream = this.Request.ProtoMajor >= 2
this.Data["isStream"] = isStream
var maxTries = 10
for i := 0; i < maxTries; i++ {
resp, err := this.RPC().NodeTaskRPC().ExistsNodeTasks(this.AdminContext(), &pb.ExistsNodeTasksRequest{
@@ -26,7 +29,7 @@ func (this *CheckAction) RunPost(params struct {
}
// 如果没有数据变化,继续查询
if i < maxTries-1 && params.IsUpdated && resp.ExistTasks == params.IsDoing && resp.ExistError == params.HasError {
if i < maxTries-1 && params.IsUpdated && resp.ExistTasks == params.IsDoing && resp.ExistError == params.HasError && isStream {
time.Sleep(3 * time.Second)
continue
}

View File

@@ -136,6 +136,8 @@ func (this *ClusterAction) RunGet(params struct {
"clusterId": node.NodeClusterId,
"isResolved": isResolved,
"isInstalled": isInstalled,
"isBackup": node.IsBackupForCluster || node.IsBackupForGroup,
"isOffline": node.IsOffline,
})
}
} else {
@@ -171,6 +173,8 @@ func (this *ClusterAction) RunGet(params struct {
"clusterId": node.NodeClusterId,
"isResolved": isResolved,
"isInstalled": isInstalled,
"isBackup": node.IsBackupForCluster || node.IsBackupForGroup,
"isOffline": node.IsOffline,
})
}
}

View File

@@ -4,6 +4,7 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
"sort"
)
// DomainOptionsAction 域名列表选项
@@ -21,10 +22,18 @@ func (this *DomainOptionsAction) RunPost(params struct {
this.ErrorPage(err)
return
}
domainMaps := []maps.Map{}
// 排序
if len(domainsResp.DnsDomains) > 0 {
sort.Slice(domainsResp.DnsDomains, func(i, j int) bool {
return domainsResp.DnsDomains[i].Name < domainsResp.DnsDomains[j].Name
})
}
var domainMaps = []maps.Map{}
for _, domain := range domainsResp.DnsDomains {
// 未开启或者已删除的先跳过
if !domain.IsOn || domain.IsDeleted {
if !domain.IsOn || domain.IsDeleted || !domain.IsUp {
continue
}

View File

@@ -16,6 +16,7 @@ func init() {
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeDNS)).
Helper(new(Helper)).
Prefix("/dns").
Data("teaSubMenu", "cluster").
Get("", new(IndexAction)).
GetPost("/updateClusterPopup", new(UpdateClusterPopupAction)).
Post("/providerOptions", new(ProviderOptionsAction)).

View File

@@ -15,6 +15,9 @@ func (this *CheckAction) RunPost(params struct {
HasError bool
IsUpdated bool
}) {
var isStream = this.Request.ProtoMajor >= 2
this.Data["isStream"] = isStream
var maxTries = 10
for i := 0; i < maxTries; i++ {
resp, err := this.RPC().DNSTaskRPC().ExistsDNSTasks(this.AdminContext(), &pb.ExistsDNSTasksRequest{})
@@ -24,7 +27,7 @@ func (this *CheckAction) RunPost(params struct {
}
// 如果没有数据变化,继续查询
if i < maxTries-1 && params.IsUpdated && resp.ExistTasks == params.IsDoing && resp.ExistError == params.HasError {
if i < maxTries-1 && params.IsUpdated && resp.ExistTasks == params.IsDoing && resp.ExistError == params.HasError && isStream {
time.Sleep(3 * time.Second)
continue
}

View File

@@ -13,15 +13,19 @@ import (
adminserverutils "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/server/admin-server-utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/types"
stringutil "github.com/iwind/TeaGo/utils/string"
"net"
"time"
)
const regionDenyMessage = "当前软件系统暂时不为你所在的区域提供服务。"
type IndexAction struct {
actionutils.ParentAction
}
@@ -36,6 +40,11 @@ func (this *IndexAction) RunGet(params struct {
Auth *helpers.UserShouldAuth
}) {
if !this.checkRegion() {
this.WriteString(regionDenyMessage)
return
}
// 是否自动从HTTP跳转到HTTPS
if this.Request.TLS == nil {
httpsPort, _ := adminserverutils.ReadServerHTTPS()
@@ -50,8 +59,11 @@ func (this *IndexAction) RunGet(params struct {
newHost += ":" + types.String(httpsPort)
}
this.RedirectURL("https://" + newHost + this.Request.RequestURI)
return
// 如果没有前端反向代理,则跳转
if len(this.Request.Header.Get("X-Forwarded-For")) == 0 && len(this.Request.Header.Get("X-Real-Ip")) == 0 {
this.RedirectURL("https://" + newHost + this.Request.RequestURI)
return
}
}
}
@@ -122,6 +134,11 @@ func (this *IndexAction) RunPost(params struct {
Auth *helpers.UserShouldAuth
CSRF *actionutils.CSRF
}) {
if !this.checkRegion() {
this.Fail(regionDenyMessage)
return
}
params.Must.
Field("username", params.Username).
Require("请输入用户名").
@@ -213,3 +230,13 @@ func (this *IndexAction) RunPost(params struct {
this.Success()
}
// 检查登录区域
func (this *IndexAction) checkRegion() bool {
var ip = this.RequestRemoteIP()
var result = iplibrary.LookupIP(ip)
if result != nil && result.IsOk() && result.CountryId() > 0 && lists.ContainsInt64([]int64{9, 10}, result.CountryId()) {
return false
}
return true
}

View File

@@ -4,16 +4,41 @@ package loginutils
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"github.com/iwind/TeaGo/actions"
stringutil "github.com/iwind/TeaGo/utils/string"
"net"
"net/http"
)
// CalculateClientFingerprint 计算客户端指纹
func CalculateClientFingerprint(action *actions.ActionObject) string {
return stringutil.Md5(action.RequestRemoteIP() + "@" + action.Request.UserAgent())
return stringutil.Md5(RemoteIP(action) + "@" + action.Request.UserAgent())
}
// RemoteIP 获取客户端IP
// TODO 将来增加是否使用代理设置即从X-Real-IP中获取IP
func RemoteIP(action *actions.ActionObject) string {
ip, _, _ := net.SplitHostPort(action.Request.RemoteAddr)
return ip
}
// LookupIPRegion 查找登录区域
func LookupIPRegion(ip string) string {
if len(ip) == 0 {
return ""
}
var result = iplibrary.LookupIP(ip)
if result != nil && result.IsOk() {
// 这里不需要网络运营商信息
return result.CountryName() + "@" + result.ProvinceName() + "@" + result.CityName() + "@" + result.TownName()
}
return ""
}
// SetCookie 设置Cookie
func SetCookie(action *actions.ActionObject, remember bool) {
if remember {
var cookie = &http.Cookie{
@@ -44,6 +69,7 @@ func SetCookie(action *actions.ActionObject, remember bool) {
}
}
// UnsetCookie 重置Cookie
func UnsetCookie(action *actions.ActionObject) {
cookie := &http.Cookie{
Name: teaconst.CookieSID,

View File

@@ -8,6 +8,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"net/url"
"regexp"
@@ -27,6 +28,8 @@ func (this *AddOriginPopupAction) RunGet(params struct {
}) {
this.Data["serverType"] = params.ServerType
this.getOSSHook()
this.Show()
}
@@ -40,50 +43,93 @@ func (this *AddOriginPopupAction) RunPost(params struct {
Must *actions.Must
}) {
params.Must.
Field("addr", params.Addr).
Require("请输入源站地址")
var addr = params.Addr
// 是否是完整的地址
if (params.Protocol == "http" || params.Protocol == "https") && regexp.MustCompile(`^(http|https)://`).MatchString(addr) {
u, err := url.Parse(addr)
if err == nil {
addr = u.Host
}
ossConfig, goNext, err := this.postOSSHook(params.Protocol)
if err != nil {
this.ErrorPage(err)
return
}
if !goNext {
return
}
addr = regexp.MustCompile(`\s+`).ReplaceAllString(addr, "")
var portIndex = strings.LastIndex(addr, ":")
if portIndex < 0 {
if params.Protocol == "http" {
addr += ":80"
} else if params.Protocol == "https" {
addr += ":443"
} else {
this.Fail("地址中需要带有端口")
}
portIndex = strings.LastIndex(addr, ":")
// 初始化
var pbAddr = &pb.NetworkAddress{
Protocol: params.Protocol,
}
var host = addr[:portIndex]
var port = addr[portIndex+1:]
var addrConfig = &serverconfigs.NetworkAddressConfig{
Protocol: serverconfigs.Protocol(params.Protocol),
}
var ossJSON []byte
// 检查端口号
if port == "0" {
this.Fail("端口号不能为0")
}
if !configutils.HasVariables(port) {
// 必须是整数
if !regexp.MustCompile(`^\d+$`).MatchString(port) {
this.Fail("端口号只能为整数")
if ossConfig != nil { // OSS
ossJSON, err = json.Marshal(ossConfig)
if err != nil {
this.ErrorPage(err)
return
}
var portInt = types.Int(port)
if portInt == 0 {
err = ossConfig.Init()
if err != nil {
this.Fail("校验OSS配置时出错" + err.Error())
return
}
} else { // 普通源站
params.Must.
Field("addr", params.Addr).
Require("请输入源站地址")
var addr = params.Addr
// 是否是完整的地址
if (params.Protocol == "http" || params.Protocol == "https") && regexp.MustCompile(`^(http|https)://`).MatchString(addr) {
u, err := url.Parse(addr)
if err == nil {
addr = u.Host
}
}
addr = regexp.MustCompile(`\s+`).ReplaceAllString(addr, "")
var portIndex = strings.LastIndex(addr, ":")
if portIndex < 0 {
if params.Protocol == "http" {
addr += ":80"
} else if params.Protocol == "https" {
addr += ":443"
} else {
this.Fail("地址中需要带有端口")
}
portIndex = strings.LastIndex(addr, ":")
}
var host = addr[:portIndex]
var port = addr[portIndex+1:]
// 检查端口号
if port == "0" {
this.Fail("端口号不能为0")
}
if portInt > 65535 {
this.Fail("端口号不能大于65535")
if !configutils.HasVariables(port) {
// 必须是整数
if !regexp.MustCompile(`^\d+$`).MatchString(port) {
this.Fail("端口号只能为整数")
}
var portInt = types.Int(port)
if portInt == 0 {
this.Fail("端口号不能为0")
}
if portInt > 65535 {
this.Fail("端口号不能大于65535")
}
}
pbAddr = &pb.NetworkAddress{
Protocol: params.Protocol,
Host: host,
PortRange: port,
}
addrConfig = &serverconfigs.NetworkAddressConfig{
Protocol: serverconfigs.Protocol(params.Protocol),
Host: host,
PortRange: port,
}
}
@@ -103,12 +149,9 @@ func (this *AddOriginPopupAction) RunPost(params struct {
}
resp, err := this.RPC().OriginRPC().CreateOrigin(this.AdminContext(), &pb.CreateOriginRequest{
Name: "",
Addr: &pb.NetworkAddress{
Protocol: params.Protocol,
Host: host,
PortRange: port,
},
Name: "",
Addr: pbAddr,
OssJSON: ossJSON,
Description: "",
Weight: 10,
IsOn: true,
@@ -124,14 +167,16 @@ func (this *AddOriginPopupAction) RunPost(params struct {
var origin = &serverconfigs.OriginConfig{
Id: resp.OriginId,
IsOn: true,
Addr: &serverconfigs.NetworkAddressConfig{
Protocol: serverconfigs.Protocol(params.Protocol),
Host: host,
PortRange: port,
},
Addr: addrConfig,
OSS: ossConfig,
}
this.Data["origin"] = origin
this.Data["origin"] = maps.Map{
"id": resp.OriginId,
"isOn": true,
"addr": addrConfig,
"addrSummary": origin.AddrSummary(),
}
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "创建源站 %d", resp.OriginId)

View File

@@ -0,0 +1,20 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package servers
import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ossconfigs"
"github.com/iwind/TeaGo/maps"
)
func (this *AddOriginPopupAction) getOSSHook() {
this.Data["ossTypes"] = []maps.Map{}
this.Data["ossBucketParams"] = []maps.Map{}
this.Data["ossForm"] = ""
}
func (this *AddOriginPopupAction) postOSSHook(protocol string) (config *ossconfigs.OSSConfig, goNext bool, err error) {
goNext = true
return
}

View File

@@ -18,31 +18,6 @@ func (this *CreateAction) Init() {
}
func (this *CreateAction) RunGet(params struct{}) {
// 获取所有可用的用户
usersResp, err := this.RPC().ACMEUserRPC().FindAllACMEUsers(this.AdminContext(), &pb.FindAllACMEUsersRequest{
AdminId: this.AdminId(),
UserId: 0,
})
if err != nil {
this.ErrorPage(err)
return
}
userMaps := []maps.Map{}
for _, user := range usersResp.AcmeUsers {
description := user.Description
if len(description) > 0 {
description = "" + description + ""
}
userMaps = append(userMaps, maps.Map{
"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 {
@@ -81,14 +56,15 @@ func (this *CreateAction) RunGet(params struct{}) {
}
func (this *CreateAction) RunPost(params struct {
TaskId int64
AuthType string
AcmeUserId int64
DnsProviderId int64
DnsDomain string
Domains []string
AutoRenew bool
AuthURL string
PlatformUserId int64
TaskId int64
AuthType string
AcmeUserId int64
DnsProviderId int64
DnsDomain string
Domains []string
AutoRenew bool
AuthURL string
Must *actions.Must
}) {
@@ -117,7 +93,7 @@ func (this *CreateAction) RunPost(params struct {
if len(params.Domains) == 0 {
this.Fail("请输入证书域名列表")
}
realDomains := []string{}
var realDomains = []string{}
for _, domain := range params.Domains {
domain = strings.ToLower(domain)
if params.AuthType == "dns" { // DNS认证
@@ -134,6 +110,7 @@ func (this *CreateAction) RunPost(params struct {
if params.TaskId == 0 {
createResp, err := this.RPC().ACMETaskRPC().CreateACMETask(this.AdminContext(), &pb.CreateACMETaskRequest{
UserId: params.PlatformUserId,
AuthType: params.AuthType,
AcmeUserId: params.AcmeUserId,
DnsProviderId: params.DnsProviderId,

View File

@@ -17,22 +17,48 @@ func (this *IndexAction) Init() {
}
func (this *IndexAction) RunGet(params struct {
UserId int64
Type string
Keyword string
}) {
this.Data["type"] = params.Type
this.Data["keyword"] = params.Keyword
countAll := int64(0)
countAvailable := int64(0)
countExpired := int64(0)
count7Days := int64(0)
count30Days := int64(0)
// 当前用户
this.Data["searchingUserId"] = params.UserId
var userMap = maps.Map{
"id": 0,
"username": "",
"fullname": "",
}
if params.UserId > 0 {
userResp, err := this.RPC().UserRPC().FindEnabledUser(this.AdminContext(), &pb.FindEnabledUserRequest{UserId: params.UserId})
if err != nil {
this.ErrorPage(err)
return
}
var user = userResp.User
if user != nil {
userMap = maps.Map{
"id": user.Id,
"username": user.Username,
"fullname": user.Fullname,
}
}
}
this.Data["user"] = userMap
var countAll int64
var countAvailable int64
var countExpired int64
var count7Days int64
var count30Days int64
// 计算数量
{
// all
resp, err := this.RPC().ACMETaskRPC().CountAllEnabledACMETasks(this.AdminContext(), &pb.CountAllEnabledACMETasksRequest{
UserId: params.UserId,
Keyword: params.Keyword,
})
if err != nil {
@@ -43,6 +69,7 @@ func (this *IndexAction) RunGet(params struct {
// available
resp, err = this.RPC().ACMETaskRPC().CountAllEnabledACMETasks(this.AdminContext(), &pb.CountAllEnabledACMETasksRequest{
UserId: params.UserId,
IsAvailable: true,
Keyword: params.Keyword,
})
@@ -54,6 +81,7 @@ func (this *IndexAction) RunGet(params struct {
// expired
resp, err = this.RPC().ACMETaskRPC().CountAllEnabledACMETasks(this.AdminContext(), &pb.CountAllEnabledACMETasksRequest{
UserId: params.UserId,
IsExpired: true,
Keyword: params.Keyword,
})
@@ -65,6 +93,7 @@ func (this *IndexAction) RunGet(params struct {
// expire in 7 days
resp, err = this.RPC().ACMETaskRPC().CountAllEnabledACMETasks(this.AdminContext(), &pb.CountAllEnabledACMETasksRequest{
UserId: params.UserId,
ExpiringDays: 7,
Keyword: params.Keyword,
})
@@ -76,6 +105,7 @@ func (this *IndexAction) RunGet(params struct {
// expire in 30 days
resp, err = this.RPC().ACMETaskRPC().CountAllEnabledACMETasks(this.AdminContext(), &pb.CountAllEnabledACMETasksRequest{
UserId: params.UserId,
ExpiringDays: 30,
Keyword: params.Keyword,
})
@@ -100,25 +130,51 @@ func (this *IndexAction) RunGet(params struct {
case "":
page = this.NewPage(countAll)
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.AdminContext(), &pb.ListEnabledACMETasksRequest{
UserId: params.UserId,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
})
case "available":
page = this.NewPage(countAvailable)
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.AdminContext(), &pb.ListEnabledACMETasksRequest{IsAvailable: true, Offset: page.Offset, Size: page.Size, Keyword: params.Keyword})
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.AdminContext(), &pb.ListEnabledACMETasksRequest{
UserId: params.UserId,
IsAvailable: true,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
})
case "expired":
page = this.NewPage(countExpired)
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.AdminContext(), &pb.ListEnabledACMETasksRequest{IsExpired: true, Offset: page.Offset, Size: page.Size, Keyword: params.Keyword})
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.AdminContext(), &pb.ListEnabledACMETasksRequest{
UserId: params.UserId,
IsExpired: true,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
})
case "7days":
page = this.NewPage(count7Days)
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.AdminContext(), &pb.ListEnabledACMETasksRequest{ExpiringDays: 7, Offset: page.Offset, Size: page.Size, Keyword: params.Keyword})
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.AdminContext(), &pb.ListEnabledACMETasksRequest{
UserId: params.UserId,
ExpiringDays: 7,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
})
case "30days":
page = this.NewPage(count30Days)
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.AdminContext(), &pb.ListEnabledACMETasksRequest{ExpiringDays: 30, Offset: page.Offset, Size: page.Size, Keyword: params.Keyword})
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.AdminContext(), &pb.ListEnabledACMETasksRequest{
UserId: params.UserId,
ExpiringDays: 30,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
})
default:
page = this.NewPage(countAll)
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.AdminContext(), &pb.ListEnabledACMETasksRequest{
UserId: params.UserId,
Keyword: params.Keyword,
Offset: page.Offset,
Size: page.Size,
@@ -131,7 +187,7 @@ func (this *IndexAction) RunGet(params struct {
this.Data["page"] = page.AsHTML()
taskMaps := []maps.Map{}
var taskMaps = []maps.Map{}
for _, task := range tasksResp.AcmeTasks {
if task.AcmeUser == nil {
continue

View File

@@ -12,6 +12,8 @@ type RunAction struct {
func (this *RunAction) RunPost(params struct {
TaskId int64
}) {
defer this.CreateLogInfo("执行ACME任务 %d", params.TaskId)
runResp, err := this.RPC().ACMETaskRPC().RunACMETask(this.AdminContext(), &pb.RunACMETaskRequest{AcmeTaskId: params.TaskId})
if err != nil {
this.ErrorPage(err)

View File

@@ -0,0 +1,44 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package acme
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type UserOptionsAction struct {
actionutils.ParentAction
}
func (this *UserOptionsAction) RunPost(params struct {
PlatformUserId int64
}) {
// 获取所有可用的用户
usersResp, err := this.RPC().ACMEUserRPC().FindAllACMEUsers(this.AdminContext(), &pb.FindAllACMEUsersRequest{
AdminId: 0,
UserId: params.PlatformUserId,
})
if err != nil {
this.ErrorPage(err)
return
}
var userMaps = []maps.Map{}
for _, user := range usersResp.AcmeUsers {
description := user.Description
if len(description) > 0 {
description = "" + description + ""
}
userMaps = append(userMaps, maps.Map{
"id": user.Id,
"description": description,
"email": user.Email,
"providerCode": user.AcmeProviderCode,
})
}
this.Data["users"] = userMaps
this.Success()
}

View File

@@ -16,10 +16,30 @@ func (this *CreatePopupAction) Init() {
}
func (this *CreatePopupAction) RunGet(params struct {
ProviderCode string
PlatformUserId int64
ProviderCode string
}) {
this.Data["platformUserId"] = params.PlatformUserId
this.Data["providerCode"] = params.ProviderCode
// 平台用户信息
this.Data["platformUser"] = nil
if params.PlatformUserId > 0 {
platformUserResp, err := this.RPC().UserRPC().FindEnabledUser(this.AdminContext(), &pb.FindEnabledUserRequest{UserId: params.PlatformUserId})
if err != nil {
this.ErrorPage(err)
return
}
var platformUser = platformUserResp.User
if platformUser != nil {
this.Data["platformUser"] = maps.Map{
"id": platformUser.Id,
"username": platformUser.Username,
"fullname": platformUser.Fullname,
}
}
}
// 服务商
providersResp, err := this.RPC().ACMEProviderRPC().FindAllACMEProviders(this.AdminContext(), &pb.FindAllACMEProvidersRequest{})
if err != nil {
@@ -40,10 +60,11 @@ func (this *CreatePopupAction) RunGet(params struct {
}
func (this *CreatePopupAction) RunPost(params struct {
Email string
ProviderCode string
AccountId int64
Description string
PlatformUserId int64
Email string
ProviderCode string
AccountId int64
Description string
Must *actions.Must
CSRF *actionutils.CSRF
@@ -85,6 +106,7 @@ func (this *CreatePopupAction) RunPost(params struct {
}
createResp, err := this.RPC().ACMEUserRPC().CreateACMEUser(this.AdminContext(), &pb.CreateACMEUserRequest{
UserId: params.PlatformUserId,
Email: params.Email,
Description: params.Description,
AcmeProviderCode: params.ProviderCode,

View File

@@ -19,12 +19,37 @@ func (this *IndexAction) Init() {
}
func (this *IndexAction) RunGet(params struct {
UserId int64
Type string
Keyword string
}) {
this.Data["type"] = params.Type
this.Data["keyword"] = params.Keyword
// 当前用户
this.Data["searchingUserId"] = params.UserId
var userMap = maps.Map{
"id": 0,
"username": "",
"fullname": "",
}
if params.UserId > 0 {
userResp, err := this.RPC().UserRPC().FindEnabledUser(this.AdminContext(), &pb.FindEnabledUserRequest{UserId: params.UserId})
if err != nil {
this.ErrorPage(err)
return
}
var user = userResp.User
if user != nil {
userMap = maps.Map{
"id": user.Id,
"username": user.Username,
"fullname": user.Fullname,
}
}
}
this.Data["user"] = userMap
var countAll = int64(0)
var countCA = int64(0)
var countAvailable = int64(0)
@@ -36,6 +61,7 @@ func (this *IndexAction) RunGet(params struct {
{
// all
resp, err := this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
UserId: params.UserId,
Keyword: params.Keyword,
})
if err != nil {
@@ -46,6 +72,7 @@ func (this *IndexAction) RunGet(params struct {
// CA
resp, err = this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
UserId: params.UserId,
IsCA: true,
Keyword: params.Keyword,
})
@@ -57,6 +84,7 @@ func (this *IndexAction) RunGet(params struct {
// available
resp, err = this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
UserId: params.UserId,
IsAvailable: true,
Keyword: params.Keyword,
})
@@ -68,6 +96,7 @@ func (this *IndexAction) RunGet(params struct {
// expired
resp, err = this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
UserId: params.UserId,
IsExpired: true,
Keyword: params.Keyword,
})
@@ -79,6 +108,7 @@ func (this *IndexAction) RunGet(params struct {
// expire in 7 days
resp, err = this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
UserId: params.UserId,
ExpiringDays: 7,
Keyword: params.Keyword,
})
@@ -90,6 +120,7 @@ func (this *IndexAction) RunGet(params struct {
// expire in 30 days
resp, err = this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
UserId: params.UserId,
ExpiringDays: 30,
Keyword: params.Keyword,
})
@@ -115,28 +146,60 @@ func (this *IndexAction) RunGet(params struct {
case "":
page = this.NewPage(countAll)
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
UserId: params.UserId,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
})
case "ca":
page = this.NewPage(countCA)
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{IsCA: true, Offset: page.Offset, Size: page.Size, Keyword: params.Keyword})
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
UserId: params.UserId,
IsCA: true,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
})
case "available":
page = this.NewPage(countAvailable)
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{IsAvailable: true, Offset: page.Offset, Size: page.Size, Keyword: params.Keyword})
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
UserId: params.UserId,
IsAvailable: true,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
})
case "expired":
page = this.NewPage(countExpired)
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{IsExpired: true, Offset: page.Offset, Size: page.Size, Keyword: params.Keyword})
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
UserId: params.UserId,
IsExpired: true,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
})
case "7days":
page = this.NewPage(count7Days)
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{ExpiringDays: 7, Offset: page.Offset, Size: page.Size, Keyword: params.Keyword})
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
UserId: params.UserId,
ExpiringDays: 7,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
})
case "30days":
page = this.NewPage(count30Days)
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{ExpiringDays: 30, Offset: page.Offset, Size: page.Size, Keyword: params.Keyword})
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
UserId: params.UserId,
ExpiringDays: 30,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
})
default:
page = this.NewPage(countAll)
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
UserId: params.UserId,
Keyword: params.Keyword,
Offset: page.Offset,
Size: page.Size,
@@ -158,7 +221,9 @@ func (this *IndexAction) RunGet(params struct {
var certMaps = []maps.Map{}
var nowTime = time.Now().Unix()
for _, certConfig := range certConfigs {
countServersResp, err := this.RPC().ServerRPC().CountAllEnabledServersWithSSLCertId(this.AdminContext(), &pb.CountAllEnabledServersWithSSLCertIdRequest{SslCertId: certConfig.Id})
countServersResp, err := this.RPC().ServerRPC().CountAllEnabledServersWithSSLCertId(this.AdminContext(), &pb.CountAllEnabledServersWithSSLCertIdRequest{
SslCertId: certConfig.Id,
})
if err != nil {
this.ErrorPage(err)
return

View File

@@ -41,6 +41,7 @@ func init() {
Post("/run", new(acme.RunAction)).
GetPost("/updateTaskPopup", new(acme.UpdateTaskPopupAction)).
Post("/deleteTask", new(acme.DeleteTaskAction)).
Post("/userOptions", new(acme.UserOptionsAction)).
// ACME用户
Prefix("/servers/certs/acme/users").

View File

@@ -34,6 +34,8 @@ func (this *SelectPopupAction) RunGet(params struct {
SelectedCertIds string
Keyword string
}) {
this.Data["searchingServerId"] = params.ServerId
// 服务相关
if params.ServerId > 0 {
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.AdminContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: params.ServerId})
@@ -66,7 +68,8 @@ func (this *SelectPopupAction) RunGet(params struct {
}
// 用户相关
this.Data["userId"] = params.UserId
this.Data["userId"] = params.UserId // 可变
this.Data["searchingUserId"] = params.UserId
// 域名搜索相关
var url = this.Request.URL.Path
@@ -82,6 +85,7 @@ func (this *SelectPopupAction) RunGet(params struct {
if len(searchingDomains) > maxDomains {
searchingDomains = searchingDomains[:maxDomains]
}
this.Data["allSearchingDomains"] = params.SearchingDomains
this.Data["searchingDomains"] = searchingDomains
this.Data["keyword"] = params.Keyword

View File

@@ -45,7 +45,7 @@ func KeyFailReason(reasonCode string) string {
case "requireDomain":
return "找不到Key对应的域名"
case "requireServer":
return "找不到Key对应的网站服务"
return "找不到Key对应的网站"
case "requireUser":
return "该域名不属于当前用户"
case "requireClusterId":

View File

@@ -64,7 +64,7 @@ func (this *IndexAction) RunPost(params struct {
DefaultDomain string
}) {
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "保存网站服务全局配置")
defer this.CreateLog(oplogs.LevelInfo, "保存网站全局配置")
if len(params.GlobalConfigJSON) == 0 {
this.Fail("错误的配置信息,请刷新当前页面后重试")

View File

@@ -238,7 +238,8 @@ func (this *CreateAction) RunPost(params struct {
}
sslPolicyIdResp, err := this.RPC().SSLPolicyRPC().CreateSSLPolicy(this.AdminContext(), &pb.CreateSSLPolicyRequest{
Http2Enabled: false, // 默认值
Http2Enabled: false, // 默认值
Http3Enabled: false,
MinVersion: "TLS 1.1", // 默认值
SslCertsJSON: certRefsJSON,
HstsJSON: nil,
@@ -387,7 +388,7 @@ func (this *CreateAction) RunPost(params struct {
AdminId: this.AdminId(),
Type: params.ServerType,
Name: params.Name,
ServerNamesJSON: params.ServerNames,
ServerNamesJSON: params.ServerNames,
Description: params.Description,
NodeClusterId: clusterId,
IncludeNodesJSON: includeNodesJSON,

View File

@@ -42,7 +42,7 @@ func (this *CreatePopupAction) RunPost(params struct {
}
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "创建服务分组 %d", createResp.ServerGroupId)
defer this.CreateLog(oplogs.LevelInfo, "创建网站分组 %d", createResp.ServerGroupId)
this.Success()
}

View File

@@ -14,7 +14,7 @@ func (this *DeleteAction) RunPost(params struct {
GroupId int64
}) {
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "删除代理服务分组 %d", params.GroupId)
defer this.CreateLog(oplogs.LevelInfo, "删除代理网站分组 %d", params.GroupId)
// 检查是否正在使用
countResp, err := this.RPC().ServerRPC().CountAllEnabledServersWithServerGroupId(this.AdminContext(), &pb.CountAllEnabledServersWithServerGroupIdRequest{ServerGroupId: params.GroupId})

View File

@@ -59,7 +59,8 @@ func (this *IndexAction) RunGet(params struct {
var m = maps.Map{
"id": originConfig.Id,
"weight": originConfig.Weight,
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
"addr": originConfig.AddrSummary(),
"isOSS": originConfig.IsOSS(),
"name": originConfig.Name,
"isOn": originConfig.IsOn,
"domains": originConfig.Domains,
@@ -76,7 +77,8 @@ func (this *IndexAction) RunGet(params struct {
var m = maps.Map{
"id": originConfig.Id,
"weight": originConfig.Weight,
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
"addr": originConfig.AddrSummary(),
"isOSS": originConfig.IsOSS(),
"name": originConfig.Name,
"isOn": originConfig.IsOn,
"domains": originConfig.Domains,

View File

@@ -59,7 +59,8 @@ func (this *IndexAction) RunGet(params struct {
m := maps.Map{
"id": originConfig.Id,
"weight": originConfig.Weight,
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
"addr": originConfig.AddrSummary(),
"isOSS": originConfig.IsOSS(),
"name": originConfig.Name,
"isOn": originConfig.IsOn,
"domains": originConfig.Domains,
@@ -76,7 +77,8 @@ func (this *IndexAction) RunGet(params struct {
m := maps.Map{
"id": originConfig.Id,
"weight": originConfig.Weight,
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
"addr": originConfig.AddrSummary(),
"isOSS": originConfig.IsOSS(),
"name": originConfig.Name,
"isOn": originConfig.IsOn,
"domains": originConfig.Domains,

View File

@@ -59,7 +59,8 @@ func (this *IndexAction) RunGet(params struct {
var m = maps.Map{
"id": originConfig.Id,
"weight": originConfig.Weight,
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
"addr": originConfig.AddrSummary(),
"isOSS": originConfig.IsOSS(),
"name": originConfig.Name,
"isOn": originConfig.IsOn,
"domains": originConfig.Domains,
@@ -76,7 +77,8 @@ func (this *IndexAction) RunGet(params struct {
var m = maps.Map{
"id": originConfig.Id,
"weight": originConfig.Weight,
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
"addr": originConfig.AddrSummary(),
"isOSS": originConfig.IsOSS(),
"name": originConfig.Name,
"isOn": originConfig.IsOn,
"domains": originConfig.Domains,

View File

@@ -41,7 +41,7 @@ func (this *UpdateAction) RunPost(params struct {
Must *actions.Must
}) {
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "修改代理服务分组 %d", params.GroupId)
defer this.CreateLog(oplogs.LevelInfo, "修改网站分组 %d", params.GroupId)
params.Must.
Field("name", params.Name).

View File

@@ -14,7 +14,7 @@ func (this *SortAction) RunPost(params struct {
GroupIds []int64
}) {
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "修改服务分组排序")
defer this.CreateLog(oplogs.LevelInfo, "修改网站分组排序")
_, err := this.RPC().ServerGroupRPC().UpdateServerGroupOrders(this.AdminContext(), &pb.UpdateServerGroupOrdersRequest{ServerGroupIds: params.GroupIds})
if err != nil {

View File

@@ -21,6 +21,7 @@ func (this *IndexAction) Init() {
}
func (this *IndexAction) RunGet(params struct {
Ip string
Keyword string
GlobalOnly bool
Unread bool
@@ -40,6 +41,7 @@ func (this *IndexAction) RunGet(params struct {
Unread: true,
EventLevel: params.EventLevel,
ListType: params.ListType,
Ip: params.Ip,
})
if err != nil {
this.ErrorPage(err)
@@ -53,6 +55,7 @@ func (this *IndexAction) RunGet(params struct {
Unread: params.Unread,
EventLevel: params.EventLevel,
ListType: params.ListType,
Ip: params.Ip,
})
if err != nil {
this.ErrorPage(err)
@@ -68,6 +71,7 @@ func (this *IndexAction) RunGet(params struct {
Unread: params.Unread,
EventLevel: params.EventLevel,
ListType: params.ListType,
Ip: params.Ip,
Offset: page.Offset,
Size: page.Size,
})

View File

@@ -45,14 +45,14 @@ func (this *CreateDeletePopupAction) RunPost(params struct {
this.ErrorPage(err)
return
}
policyConfig := &shared.HTTPHeaderPolicy{}
var policyConfig = &shared.HTTPHeaderPolicy{}
err = json.Unmarshal(policyConfigResp.HttpHeaderPolicyJSON, policyConfig)
if err != nil {
this.ErrorPage(err)
return
}
deleteHeaders := policyConfig.DeleteHeaders
var deleteHeaders = policyConfig.DeleteHeaders
deleteHeaders = append(deleteHeaders, params.Name)
_, err = this.RPC().HTTPHeaderPolicyRPC().UpdateHTTPHeaderPolicyDeletingHeaders(this.AdminContext(), &pb.UpdateHTTPHeaderPolicyDeletingHeadersRequest{
HttpHeaderPolicyId: params.HeaderPolicyId,

View File

@@ -0,0 +1,67 @@
package headers
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/actions"
)
type CreateNonStandardPopupAction struct {
actionutils.ParentAction
}
func (this *CreateNonStandardPopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreateNonStandardPopupAction) RunGet(params struct {
HeaderPolicyId int64
Type string
}) {
this.Data["headerPolicyId"] = params.HeaderPolicyId
this.Data["type"] = params.Type
this.Show()
}
func (this *CreateNonStandardPopupAction) RunPost(params struct {
HeaderPolicyId int64
Name string
Must *actions.Must
}) {
// 日志
defer this.CreateLog(oplogs.LevelInfo, "添加非标的Header HeaderPolicyId: %d, Name: %s", params.HeaderPolicyId, params.Name)
params.Must.
Field("name", params.Name).
Require("名称不能为空")
policyConfigResp, err := this.RPC().HTTPHeaderPolicyRPC().FindEnabledHTTPHeaderPolicyConfig(this.AdminContext(), &pb.FindEnabledHTTPHeaderPolicyConfigRequest{HttpHeaderPolicyId: params.HeaderPolicyId})
if err != nil {
this.ErrorPage(err)
return
}
var policyConfig = &shared.HTTPHeaderPolicy{}
err = json.Unmarshal(policyConfigResp.HttpHeaderPolicyJSON, policyConfig)
if err != nil {
this.ErrorPage(err)
return
}
var nonStandardHeaders = policyConfig.NonStandardHeaders
nonStandardHeaders = append(nonStandardHeaders, params.Name)
_, err = this.RPC().HTTPHeaderPolicyRPC().UpdateHTTPHeaderPolicyNonStandardHeaders(this.AdminContext(), &pb.UpdateHTTPHeaderPolicyNonStandardHeadersRequest{
HttpHeaderPolicyId: params.HeaderPolicyId,
HeaderNames: nonStandardHeaders,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -17,22 +17,22 @@ func (this *DeleteDeletingHeaderAction) RunPost(params struct {
HeaderName string
}) {
// 日志
defer this.CreateLog(oplogs.LevelInfo, "删除需要删除的请求HeaderHeaderPolicyId:%d, HeaderName:%s", params.HeaderPolicyId, params.HeaderName)
defer this.CreateLog(oplogs.LevelInfo, "删除需要删除的HeaderHeaderPolicyId:%d, HeaderName:%s", params.HeaderPolicyId, params.HeaderName)
policyConfigResp, err := this.RPC().HTTPHeaderPolicyRPC().FindEnabledHTTPHeaderPolicyConfig(this.AdminContext(), &pb.FindEnabledHTTPHeaderPolicyConfigRequest{HttpHeaderPolicyId: params.HeaderPolicyId})
if err != nil {
this.ErrorPage(err)
return
}
policyConfigJSON := policyConfigResp.HttpHeaderPolicyJSON
policyConfig := &shared.HTTPHeaderPolicy{}
var policyConfigJSON = policyConfigResp.HttpHeaderPolicyJSON
var policyConfig = &shared.HTTPHeaderPolicy{}
err = json.Unmarshal(policyConfigJSON, policyConfig)
if err != nil {
this.ErrorPage(err)
return
}
headerNames := []string{}
var headerNames = []string{}
for _, h := range policyConfig.DeleteHeaders {
if h == params.HeaderName {
continue

View File

@@ -0,0 +1,52 @@
package headers
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
)
type DeleteNonStandardHeaderAction struct {
actionutils.ParentAction
}
func (this *DeleteNonStandardHeaderAction) RunPost(params struct {
HeaderPolicyId int64
HeaderName string
}) {
// 日志
defer this.CreateLog(oplogs.LevelInfo, "删除需要非标的HeaderHeaderPolicyId:%d, HeaderName:%s", params.HeaderPolicyId, params.HeaderName)
policyConfigResp, err := this.RPC().HTTPHeaderPolicyRPC().FindEnabledHTTPHeaderPolicyConfig(this.AdminContext(), &pb.FindEnabledHTTPHeaderPolicyConfigRequest{HttpHeaderPolicyId: params.HeaderPolicyId})
if err != nil {
this.ErrorPage(err)
return
}
var policyConfigJSON = policyConfigResp.HttpHeaderPolicyJSON
var policyConfig = &shared.HTTPHeaderPolicy{}
err = json.Unmarshal(policyConfigJSON, policyConfig)
if err != nil {
this.ErrorPage(err)
return
}
var headerNames = []string{}
for _, h := range policyConfig.NonStandardHeaders {
if h == params.HeaderName {
continue
}
headerNames = append(headerNames, h)
}
_, err = this.RPC().HTTPHeaderPolicyRPC().UpdateHTTPHeaderPolicyNonStandardHeaders(this.AdminContext(), &pb.UpdateHTTPHeaderPolicyNonStandardHeadersRequest{
HttpHeaderPolicyId: params.HeaderPolicyId,
HeaderNames: headerNames,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -18,6 +18,8 @@ func init() {
GetPost("/updateSetPopup", new(UpdateSetPopupAction)).
GetPost("/createDeletePopup", new(CreateDeletePopupAction)).
Post("/deleteDeletingHeader", new(DeleteDeletingHeaderAction)).
GetPost("/createNonStandardPopup", new(CreateNonStandardPopupAction)).
Post("/deleteNonStandardHeader", new(DeleteNonStandardHeaderAction)).
Post("/delete", new(DeleteAction)).
GetPost("/updateCORSPopup", new(UpdateCORSPopupAction)).
EndAll()

View File

@@ -49,7 +49,7 @@ func (this *UpdateCORSPopupAction) RunPost(params struct {
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var config = &shared.HTTPCORSHeaderConfig{}
var config = shared.NewHTTPCORSHeaderConfig()
err := json.Unmarshal(params.CorsJSON, config)
if err != nil {
this.Fail("配置校验失败:" + err.Error())

View File

@@ -85,11 +85,23 @@ func (this *IndexAction) RunGet(params struct {
}
}
// 当前集群是否支持HTTP/3
if server.NodeCluster == nil {
this.ErrorPage(errors.New("no node cluster for the server"))
return
}
supportsHTTP3, err := this.checkSupportsHTTP3(server.NodeCluster.Id)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["serverType"] = server.Type
this.Data["httpsConfig"] = maps.Map{
"isOn": httpsConfig.IsOn,
"addresses": httpsConfig.Listen,
"sslPolicy": sslPolicy,
"isOn": httpsConfig.IsOn,
"addresses": httpsConfig.Listen,
"sslPolicy": sslPolicy,
"supportsHTTP3": supportsHTTP3,
}
this.Show()
@@ -175,6 +187,7 @@ func (this *IndexAction) RunPost(params struct {
_, err := this.RPC().SSLPolicyRPC().UpdateSSLPolicy(this.AdminContext(), &pb.UpdateSSLPolicyRequest{
SslPolicyId: sslPolicyId,
Http2Enabled: sslPolicy.HTTP2Enabled,
Http3Enabled: sslPolicy.HTTP3Enabled,
MinVersion: sslPolicy.MinVersion,
SslCertsJSON: certsJSON,
HstsJSON: hstsJSON,
@@ -191,6 +204,7 @@ func (this *IndexAction) RunPost(params struct {
} else {
resp, err := this.RPC().SSLPolicyRPC().CreateSSLPolicy(this.AdminContext(), &pb.CreateSSLPolicyRequest{
Http2Enabled: sslPolicy.HTTP2Enabled,
Http3Enabled: sslPolicy.HTTP3Enabled,
MinVersion: sslPolicy.MinVersion,
SslCertsJSON: certsJSON,
HstsJSON: hstsJSON,

View File

@@ -0,0 +1,8 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package https
func (this *IndexAction) checkSupportsHTTP3(clusterId int64) (bool, error) {
return false, nil
}

View File

@@ -38,6 +38,7 @@ func (this *IndexAction) RunGet(params struct {
AllowEmpty: true,
AllowSameDomain: true,
AllowDomains: nil,
CheckOrigin: true,
}
}

View File

@@ -60,7 +60,8 @@ func (this *IndexAction) RunGet(params struct {
var m = maps.Map{
"id": originConfig.Id,
"weight": originConfig.Weight,
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
"addr": originConfig.AddrSummary(),
"isOSS": originConfig.IsOSS(),
"name": originConfig.Name,
"isOn": originConfig.IsOn,
"domains": originConfig.Domains,
@@ -77,7 +78,8 @@ func (this *IndexAction) RunGet(params struct {
var m = maps.Map{
"id": originConfig.Id,
"weight": originConfig.Weight,
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
"addr": originConfig.AddrSummary(),
"isOSS": originConfig.IsOSS(),
"name": originConfig.Name,
"isOn": originConfig.IsOn,
"domains": originConfig.Domains,

View File

@@ -47,6 +47,9 @@ func (this *AddPopupAction) RunGet(params struct {
// 是否为HTTP
this.Data["isHTTP"] = serverType == "httpProxy" || serverType == "httpWeb"
// OSS
this.getOSSHook()
this.Show()
}
@@ -76,104 +79,142 @@ func (this *AddPopupAction) RunPost(params struct {
Must *actions.Must
}) {
params.Must.
Field("addr", params.Addr).
Require("请输入源站地址")
var addr = params.Addr
// 是否是完整的地址
if (params.Protocol == "http" || params.Protocol == "https") && regexp.MustCompile(`^(http|https)://`).MatchString(addr) {
u, err := url.Parse(addr)
if err == nil {
addr = u.Host
}
}
addr = strings.ReplaceAll(addr, "", ":")
addr = regexp.MustCompile(`\s+`).ReplaceAllString(addr, "")
var portIndex = strings.LastIndex(addr, ":")
if portIndex < 0 {
if params.Protocol == "http" {
addr += ":80"
} else if params.Protocol == "https" {
addr += ":443"
} else {
this.FailField("addr", "源站地址中需要带有端口")
}
portIndex = strings.LastIndex(addr, ":")
}
var host = addr[:portIndex]
var port = addr[portIndex+1:]
// 检查端口号
if port == "0" {
this.FailField("addr", "源站端口号不能为0")
}
if !configutils.HasVariables(port) {
// 必须是整数
if !regexp.MustCompile(`^\d+$`).MatchString(port) {
this.FailField("addr", "源站端口号只能为整数")
}
var portInt = types.Int(port)
if portInt == 0 {
this.FailField("addr", "源站端口号不能为0")
}
if portInt > 65535 {
this.FailField("addr", "源站端口号不能大于65535")
}
}
connTimeoutJSON, err := (&shared.TimeDuration{
Count: int64(params.ConnTimeout),
Unit: shared.TimeDurationUnitSecond,
}).AsJSON()
ossConfig, goNext, err := this.postOSSHook(params.Protocol)
if err != nil {
this.ErrorPage(err)
return
}
readTimeoutJSON, err := (&shared.TimeDuration{
Count: int64(params.ReadTimeout),
Unit: shared.TimeDurationUnitSecond,
}).AsJSON()
if err != nil {
this.ErrorPage(err)
if !goNext {
return
}
idleTimeoutJSON, err := (&shared.TimeDuration{
Count: int64(params.IdleTimeout),
Unit: shared.TimeDurationUnitSecond,
}).AsJSON()
if err != nil {
this.ErrorPage(err)
return
// 初始化
var pbAddr = &pb.NetworkAddress{
Protocol: params.Protocol,
}
var connTimeoutJSON []byte
var readTimeoutJSON []byte
var idleTimeoutJSON []byte
var certRefJSON []byte
// 证书
var certIds = []int64{}
if len(params.CertIdsJSON) > 0 {
err = json.Unmarshal(params.CertIdsJSON, &certIds)
var ossJSON []byte = nil
if ossConfig != nil { // OSS
ossJSON, err = json.Marshal(ossConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
var certRefJSON []byte
if len(certIds) > 0 {
var certId = certIds[0]
if certId > 0 {
var certRef = &sslconfigs.SSLCertRef{
IsOn: true,
CertId: certId,
err = ossConfig.Init()
if err != nil {
this.Fail("校验OSS配置时出错" + err.Error())
return
}
} else { // 普通源站
params.Must.
Field("addr", params.Addr).
Require("请输入源站地址")
var addr = params.Addr
// 是否是完整的地址
if (params.Protocol == "http" || params.Protocol == "https") && regexp.MustCompile(`^(http|https)://`).MatchString(addr) {
u, err := url.Parse(addr)
if err == nil {
addr = u.Host
}
certRefJSON, err = json.Marshal(certRef)
}
addr = strings.ReplaceAll(addr, "", ":")
addr = regexp.MustCompile(`\s+`).ReplaceAllString(addr, "")
var portIndex = strings.LastIndex(addr, ":")
if portIndex < 0 {
if params.Protocol == "http" {
addr += ":80"
} else if params.Protocol == "https" {
addr += ":443"
} else {
this.FailField("addr", "源站地址中需要带有端口")
}
portIndex = strings.LastIndex(addr, ":")
}
var host = addr[:portIndex]
var port = addr[portIndex+1:]
// 检查端口号
if port == "0" {
this.FailField("addr", "源站端口号不能为0")
}
if !configutils.HasVariables(port) {
// 必须是整数
if !regexp.MustCompile(`^\d+$`).MatchString(port) {
this.FailField("addr", "源站端口号只能为整数")
}
var portInt = types.Int(port)
if portInt == 0 {
this.FailField("addr", "源站端口号不能为0")
}
if portInt > 65535 {
this.FailField("addr", "源站端口号不能大于65535")
}
}
connTimeoutJSON, err = (&shared.TimeDuration{
Count: int64(params.ConnTimeout),
Unit: shared.TimeDurationUnitSecond,
}).AsJSON()
if err != nil {
this.ErrorPage(err)
return
}
readTimeoutJSON, err = (&shared.TimeDuration{
Count: int64(params.ReadTimeout),
Unit: shared.TimeDurationUnitSecond,
}).AsJSON()
if err != nil {
this.ErrorPage(err)
return
}
idleTimeoutJSON, err = (&shared.TimeDuration{
Count: int64(params.IdleTimeout),
Unit: shared.TimeDurationUnitSecond,
}).AsJSON()
if err != nil {
this.ErrorPage(err)
return
}
// 证书
var certIds = []int64{}
if len(params.CertIdsJSON) > 0 {
err = json.Unmarshal(params.CertIdsJSON, &certIds)
if err != nil {
this.ErrorPage(err)
return
}
}
if len(certIds) > 0 {
var certId = certIds[0]
if certId > 0 {
var certRef = &sslconfigs.SSLCertRef{
IsOn: true,
CertId: certId,
}
certRefJSON, err = json.Marshal(certRef)
if err != nil {
this.ErrorPage(err)
return
}
}
}
pbAddr = &pb.NetworkAddress{
Protocol: params.Protocol,
Host: host,
PortRange: port,
}
}
// 专属域名
@@ -192,12 +233,9 @@ func (this *AddPopupAction) RunPost(params struct {
}
createResp, err := this.RPC().OriginRPC().CreateOrigin(this.AdminContext(), &pb.CreateOriginRequest{
Name: params.Name,
Addr: &pb.NetworkAddress{
Protocol: params.Protocol,
Host: host,
PortRange: port,
},
Name: params.Name,
Addr: pbAddr,
OssJSON: ossJSON,
Description: params.Description,
Weight: params.Weight,
IsOn: params.IsOn,

View File

@@ -0,0 +1,20 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package origins
import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ossconfigs"
"github.com/iwind/TeaGo/maps"
)
func (this *AddPopupAction) getOSSHook() {
this.Data["ossTypes"] = []maps.Map{}
this.Data["ossBucketParams"] = []maps.Map{}
this.Data["ossForm"] = ""
}
func (this *AddPopupAction) postOSSHook(protocol string) (config *ossconfigs.OSSConfig, goNext bool, err error) {
goNext = true
return
}

View File

@@ -94,10 +94,14 @@ func (this *UpdatePopupAction) RunGet(params struct {
config.Cert.KeyData = nil
}
var addr = ""
if len(config.Addr.Host) > 0 && len(config.Addr.PortRange) > 0 {
addr = config.Addr.Host + ":" + config.Addr.PortRange
}
this.Data["origin"] = maps.Map{
"id": config.Id,
"protocol": config.Addr.Protocol,
"addr": config.Addr.Host + ":" + config.Addr.PortRange,
"addr": addr,
"weight": config.Weight,
"name": config.Name,
"description": config.Description,
@@ -111,8 +115,12 @@ func (this *UpdatePopupAction) RunGet(params struct {
"domains": config.Domains,
"host": config.RequestHost,
"followPort": config.FollowPort,
"oss": config.OSS,
}
// OSS
this.getOSSHook()
this.Show()
}
@@ -143,103 +151,144 @@ func (this *UpdatePopupAction) RunPost(params struct {
Must *actions.Must
}) {
params.Must.
Field("addr", params.Addr).
Require("请输入源站地址")
var addr = params.Addr
// 是否是完整的地址
if (params.Protocol == "http" || params.Protocol == "https") && regexp.MustCompile(`^(http|https)://`).MatchString(addr) {
u, err := url.Parse(addr)
if err == nil {
addr = u.Host
}
}
addr = strings.ReplaceAll(addr, "", ":")
addr = regexp.MustCompile(`\s+`).ReplaceAllString(addr, "")
portIndex := strings.LastIndex(addr, ":")
if portIndex < 0 {
if params.Protocol == "http" {
addr += ":80"
} else if params.Protocol == "https" {
addr += ":443"
} else {
this.FailField("addr", "源站地址中需要带有端口")
}
portIndex = strings.LastIndex(addr, ":")
}
var host = addr[:portIndex]
var port = addr[portIndex+1:]
// 检查端口号
if port == "0" {
this.FailField("addr", "源站端口号不能为0")
}
if !configutils.HasVariables(port) {
// 必须是整数
if !regexp.MustCompile(`^\d+$`).MatchString(port) {
this.FailField("addr", "源站端口号只能为整数")
}
var portInt = types.Int(port)
if portInt == 0 {
this.FailField("addr", "源站端口号不能为0")
}
if portInt > 65535 {
this.FailField("addr", "源站端口号不能大于65535")
}
}
connTimeoutJSON, err := (&shared.TimeDuration{
Count: int64(params.ConnTimeout),
Unit: shared.TimeDurationUnitSecond,
}).AsJSON()
ossConfig, goNext, err := this.postOSSHook(params.Protocol)
if err != nil {
this.ErrorPage(err)
return
}
readTimeoutJSON, err := (&shared.TimeDuration{
Count: int64(params.ReadTimeout),
Unit: shared.TimeDurationUnitSecond,
}).AsJSON()
if err != nil {
this.ErrorPage(err)
if !goNext {
return
}
idleTimeoutJSON, err := (&shared.TimeDuration{
Count: int64(params.IdleTimeout),
Unit: shared.TimeDurationUnitSecond,
}).AsJSON()
if err != nil {
this.ErrorPage(err)
return
var ossJSON []byte = nil
var connTimeoutJSON []byte
var readTimeoutJSON []byte
var idleTimeoutJSON []byte
var certRefJSON []byte
var pbAddr = &pb.NetworkAddress{
Protocol: params.Protocol,
}
// 证书
var certIds = []int64{}
if len(params.CertIdsJSON) > 0 {
err = json.Unmarshal(params.CertIdsJSON, &certIds)
if ossConfig != nil { // OSS
ossJSON, err = json.Marshal(ossConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
var certRefJSON []byte
if len(certIds) > 0 {
var certId = certIds[0]
if certId > 0 {
var certRef = &sslconfigs.SSLCertRef{
IsOn: true,
CertId: certId,
err = ossConfig.Init()
if err != nil {
this.Fail("校验OSS配置时出错" + err.Error())
return
}
} else { // 普通源站
params.Must.
Field("addr", params.Addr).
Require("请输入源站地址")
var addr = params.Addr
// 是否是完整的地址
if (params.Protocol == "http" || params.Protocol == "https") && regexp.MustCompile(`^(http|https)://`).MatchString(addr) {
u, err := url.Parse(addr)
if err == nil {
addr = u.Host
}
certRefJSON, err = json.Marshal(certRef)
}
addr = strings.ReplaceAll(addr, "", ":")
addr = regexp.MustCompile(`\s+`).ReplaceAllString(addr, "")
portIndex := strings.LastIndex(addr, ":")
if portIndex < 0 {
if params.Protocol == "http" {
addr += ":80"
} else if params.Protocol == "https" {
addr += ":443"
} else {
this.FailField("addr", "源站地址中需要带有端口")
}
portIndex = strings.LastIndex(addr, ":")
}
var host = addr[:portIndex]
var port = addr[portIndex+1:]
// 检查端口号
if port == "0" {
this.FailField("addr", "源站端口号不能为0")
return
}
if !configutils.HasVariables(port) {
// 必须是整数
if !regexp.MustCompile(`^\d+$`).MatchString(port) {
this.FailField("addr", "源站端口号只能为整数")
return
}
var portInt = types.Int(port)
if portInt == 0 {
this.FailField("addr", "源站端口号不能为0")
return
}
if portInt > 65535 {
this.FailField("addr", "源站端口号不能大于65535")
return
}
}
pbAddr = &pb.NetworkAddress{
Protocol: params.Protocol,
Host: host,
PortRange: port,
}
connTimeoutJSON, err = (&shared.TimeDuration{
Count: int64(params.ConnTimeout),
Unit: shared.TimeDurationUnitSecond,
}).AsJSON()
if err != nil {
this.ErrorPage(err)
return
}
readTimeoutJSON, err = (&shared.TimeDuration{
Count: int64(params.ReadTimeout),
Unit: shared.TimeDurationUnitSecond,
}).AsJSON()
if err != nil {
this.ErrorPage(err)
return
}
idleTimeoutJSON, err = (&shared.TimeDuration{
Count: int64(params.IdleTimeout),
Unit: shared.TimeDurationUnitSecond,
}).AsJSON()
if err != nil {
this.ErrorPage(err)
return
}
// 证书
var certIds = []int64{}
if len(params.CertIdsJSON) > 0 {
err = json.Unmarshal(params.CertIdsJSON, &certIds)
if err != nil {
this.ErrorPage(err)
return
}
}
if len(certIds) > 0 {
var certId = certIds[0]
if certId > 0 {
var certRef = &sslconfigs.SSLCertRef{
IsOn: true,
CertId: certId,
}
certRefJSON, err = json.Marshal(certRef)
if err != nil {
this.ErrorPage(err)
return
}
}
}
}
// 专属域名
@@ -258,13 +307,10 @@ func (this *UpdatePopupAction) RunPost(params struct {
}
_, err = this.RPC().OriginRPC().UpdateOrigin(this.AdminContext(), &pb.UpdateOriginRequest{
OriginId: params.OriginId,
Name: params.Name,
Addr: &pb.NetworkAddress{
Protocol: params.Protocol,
Host: host,
PortRange: port,
},
OriginId: params.OriginId,
Name: params.Name,
Addr: pbAddr,
OssJSON: ossJSON,
Description: params.Description,
Weight: params.Weight,
IsOn: params.IsOn,

View File

@@ -0,0 +1,20 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package origins
import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ossconfigs"
"github.com/iwind/TeaGo/maps"
)
func (this *UpdatePopupAction) getOSSHook() {
this.Data["ossTypes"] = []maps.Map{}
this.Data["ossBucketParams"] = []maps.Map{}
this.Data["ossForm"] = ""
}
func (this *UpdatePopupAction) postOSSHook(protocol string) (config *ossconfigs.OSSConfig, goNext bool, err error) {
goNext = true
return
}

View File

@@ -40,6 +40,7 @@ func (this *IndexAction) RunGet(params struct {
AllowEmpty: true,
AllowSameDomain: true,
AllowDomains: nil,
CheckOrigin: true,
}
}

View File

@@ -87,7 +87,8 @@ func (this *IndexAction) RunGet(params struct {
var m = maps.Map{
"id": originConfig.Id,
"weight": originConfig.Weight,
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
"addr": originConfig.AddrSummary(),
"isOSS": originConfig.IsOSS(),
"name": originConfig.Name,
"isOn": originConfig.IsOn,
"domains": originConfig.Domains,
@@ -104,7 +105,8 @@ func (this *IndexAction) RunGet(params struct {
var m = maps.Map{
"id": originConfig.Id,
"weight": originConfig.Weight,
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
"addr": originConfig.AddrSummary(),
"isOSS": originConfig.IsOSS(),
"name": originConfig.Name,
"isOn": originConfig.IsOn,
"domains": originConfig.Domains,

View File

@@ -128,6 +128,7 @@ func (this *IndexAction) RunPost(params struct {
_, err := this.RPC().SSLPolicyRPC().UpdateSSLPolicy(this.AdminContext(), &pb.UpdateSSLPolicyRequest{
SslPolicyId: sslPolicyId,
Http2Enabled: sslPolicy.HTTP2Enabled,
Http3Enabled: sslPolicy.HTTP3Enabled,
MinVersion: sslPolicy.MinVersion,
SslCertsJSON: certsJSON,
HstsJSON: hstsJSON,
@@ -144,6 +145,7 @@ func (this *IndexAction) RunPost(params struct {
} else {
resp, err := this.RPC().SSLPolicyRPC().CreateSSLPolicy(this.AdminContext(), &pb.CreateSSLPolicyRequest{
Http2Enabled: sslPolicy.HTTP2Enabled,
Http3Enabled: sslPolicy.HTTP3Enabled,
MinVersion: sslPolicy.MinVersion,
SslCertsJSON: certsJSON,
HstsJSON: hstsJSON,

View File

@@ -7,6 +7,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
@@ -41,7 +42,7 @@ func (this *CountriesAction) RunGet(params struct {
this.NotFound("firewallPolicy", params.FirewallPolicyId)
return
}
selectedCountryIds := []int64{}
var selectedCountryIds = []int64{}
if policyConfig.Inbound != nil && policyConfig.Inbound.Region != nil {
selectedCountryIds = policyConfig.Inbound.Region.DenyCountryIds
}
@@ -51,7 +52,7 @@ func (this *CountriesAction) RunGet(params struct {
this.ErrorPage(err)
return
}
countryMaps := []maps.Map{}
var countryMaps = []maps.Map{}
for _, country := range countriesResp.RegionCountries {
countryMaps = append(countryMaps, maps.Map{
"id": country.Id,
@@ -62,6 +63,18 @@ func (this *CountriesAction) RunGet(params struct {
}
this.Data["countries"] = countryMaps
// except & only URL Patterns
this.Data["exceptURLPatterns"] = []*shared.URLPattern{}
this.Data["onlyURLPatterns"] = []*shared.URLPattern{}
if policyConfig.Inbound != nil && policyConfig.Inbound.Region != nil {
if len(policyConfig.Inbound.Region.CountryExceptURLPatterns) > 0 {
this.Data["exceptURLPatterns"] = policyConfig.Inbound.Region.CountryExceptURLPatterns
}
if len(policyConfig.Inbound.Region.CountryOnlyURLPatterns) > 0 {
this.Data["onlyURLPatterns"] = policyConfig.Inbound.Region.CountryOnlyURLPatterns
}
}
// WAF是否启用
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), params.ServerId)
if err != nil {
@@ -77,6 +90,9 @@ func (this *CountriesAction) RunPost(params struct {
FirewallPolicyId int64
CountryIds []int64
ExceptURLPatternsJSON []byte
OnlyURLPatternsJSON []byte
Must *actions.Must
}) {
// 日志
@@ -102,6 +118,34 @@ func (this *CountriesAction) RunPost(params struct {
}
policyConfig.Inbound.Region.DenyCountryIds = params.CountryIds
// 例外URL
var exceptURLPatterns = []*shared.URLPattern{}
if len(params.ExceptURLPatternsJSON) > 0 {
err = json.Unmarshal(params.ExceptURLPatternsJSON, &exceptURLPatterns)
if err != nil {
this.Fail("校验例外URL参数失败" + err.Error())
return
}
}
policyConfig.Inbound.Region.CountryExceptURLPatterns = exceptURLPatterns
// 限制URL
var onlyURLPatterns = []*shared.URLPattern{}
if len(params.OnlyURLPatternsJSON) > 0 {
err = json.Unmarshal(params.OnlyURLPatternsJSON, &onlyURLPatterns)
if err != nil {
this.Fail("校验限制URL参数失败" + err.Error())
return
}
}
policyConfig.Inbound.Region.CountryOnlyURLPatterns = onlyURLPatterns
err = policyConfig.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())
return
}
inboundJSON, err := json.Marshal(policyConfig.Inbound)
if err != nil {
this.ErrorPage(err)

View File

@@ -7,6 +7,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
@@ -41,7 +42,7 @@ func (this *ProvincesAction) RunGet(params struct {
this.NotFound("firewallPolicy", params.FirewallPolicyId)
return
}
selectedProvinceIds := []int64{}
var selectedProvinceIds = []int64{}
if policyConfig.Inbound != nil && policyConfig.Inbound.Region != nil {
selectedProvinceIds = policyConfig.Inbound.Region.DenyProvinceIds
}
@@ -53,7 +54,7 @@ func (this *ProvincesAction) RunGet(params struct {
this.ErrorPage(err)
return
}
provinceMaps := []maps.Map{}
var provinceMaps = []maps.Map{}
for _, province := range provincesResp.RegionProvinces {
provinceMaps = append(provinceMaps, maps.Map{
"id": province.Id,
@@ -63,6 +64,18 @@ func (this *ProvincesAction) RunGet(params struct {
}
this.Data["provinces"] = provinceMaps
// except & only URL Patterns
this.Data["exceptURLPatterns"] = []*shared.URLPattern{}
this.Data["onlyURLPatterns"] = []*shared.URLPattern{}
if policyConfig.Inbound != nil && policyConfig.Inbound.Region != nil {
if len(policyConfig.Inbound.Region.ProvinceExceptURLPatterns) > 0 {
this.Data["exceptURLPatterns"] = policyConfig.Inbound.Region.ProvinceExceptURLPatterns
}
if len(policyConfig.Inbound.Region.ProvinceOnlyURLPatterns) > 0 {
this.Data["onlyURLPatterns"] = policyConfig.Inbound.Region.ProvinceOnlyURLPatterns
}
}
// WAF是否启用
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), params.ServerId)
if err != nil {
@@ -78,6 +91,9 @@ func (this *ProvincesAction) RunPost(params struct {
FirewallPolicyId int64
ProvinceIds []int64
ExceptURLPatternsJSON []byte
OnlyURLPatternsJSON []byte
Must *actions.Must
}) {
// 日志
@@ -103,6 +119,34 @@ func (this *ProvincesAction) RunPost(params struct {
}
policyConfig.Inbound.Region.DenyProvinceIds = params.ProvinceIds
// 例外URL
var exceptURLPatterns = []*shared.URLPattern{}
if len(params.ExceptURLPatternsJSON) > 0 {
err = json.Unmarshal(params.ExceptURLPatternsJSON, &exceptURLPatterns)
if err != nil {
this.Fail("校验例外URL参数失败" + err.Error())
return
}
}
policyConfig.Inbound.Region.ProvinceExceptURLPatterns = exceptURLPatterns
// 限制URL
var onlyURLPatterns = []*shared.URLPattern{}
if len(params.OnlyURLPatternsJSON) > 0 {
err = json.Unmarshal(params.OnlyURLPatternsJSON, &onlyURLPatterns)
if err != nil {
this.Fail("校验限制URL参数失败" + err.Error())
return
}
}
policyConfig.Inbound.Region.ProvinceOnlyURLPatterns = onlyURLPatterns
err = policyConfig.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())
return
}
inboundJSON, err := json.Marshal(policyConfig.Inbound)
if err != nil {
this.ErrorPage(err)

View File

@@ -106,8 +106,13 @@ func (this *ServerHelper) createLeftMenu(action *actions.ActionObject) {
// TABBAR
selectedTabbar, _ := action.Data["mainTab"]
tabbar := actionutils.NewTabbar()
tabbar.Add("服务列表", "", "/servers", "", false)
var tabbar = actionutils.NewTabbar()
tabbar.Add("", "", "/servers", "left arrow", false)
if len(serverConfig.Name) > 0 {
var item = tabbar.Add(serverConfig.Name, "", "/servers/server?serverId="+serverIdString, "angle right", true)
item.IsTitle = true
}
if teaconst.IsPlus {
tabbar.Add("看板", "", "/servers/server/boards?serverId="+serverIdString, "dashboard", selectedTabbar == "board")
}

View File

@@ -2,7 +2,7 @@ package api
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/api/node"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/api/node"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
@@ -14,7 +14,7 @@ func init() {
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)).
Helper(NewHelper()).
Helper(settingutils.NewAdvancedHelper("apiNodes")).
Prefix("/api").
Prefix("/settings/api").
Get("", new(IndexAction)).
Get("/methodStats", new(MethodStatsAction)).
GetPost("/node/createPopup", new(node.CreatePopupAction)).

View File

@@ -12,7 +12,7 @@ func init() {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)).
Helper(settingutils.NewAdvancedHelper("apiNodes")).
Prefix("/api/node").
Prefix("/settings/api/node").
// 这里不受Helper的约束
GetPost("/createAddrPopup", new(CreateAddrPopupAction)).

View File

@@ -6,6 +6,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"sort"
)
type CleanAction struct {
@@ -16,11 +17,20 @@ func (this *CleanAction) Init() {
this.Nav("", "", "clean")
}
func (this *CleanAction) RunGet(params struct{}) {
func (this *CleanAction) RunGet(params struct {
OrderTable string
OrderSize string
}) {
this.Data["orderTable"] = params.OrderTable
this.Data["orderSize"] = params.OrderSize
this.Show()
}
func (this *CleanAction) RunPost(params struct {
OrderTable string
OrderSize string
Must *actions.Must
}) {
tablesResp, err := this.RPC().DBRPC().FindAllDBTables(this.AdminContext(), &pb.FindAllDBTablesRequest{})
@@ -28,9 +38,33 @@ func (this *CleanAction) RunPost(params struct {
this.ErrorPage(err)
return
}
var tables = tablesResp.DbTables
tableMaps := []maps.Map{}
for _, table := range tablesResp.DbTables {
// 排序
switch params.OrderTable {
case "asc":
sort.Slice(tables, func(i, j int) bool {
return tables[i].Name < tables[j].Name
})
case "desc":
sort.Slice(tables, func(i, j int) bool {
return tables[i].Name > tables[j].Name
})
}
switch params.OrderSize {
case "asc":
sort.Slice(tables, func(i, j int) bool {
return tables[i].DataLength+tables[i].IndexLength < tables[j].DataLength+tables[j].IndexLength
})
case "desc":
sort.Slice(tables, func(i, j int) bool {
return tables[i].DataLength+tables[i].IndexLength > tables[j].DataLength+tables[j].IndexLength
})
}
var tableMaps = []maps.Map{}
for _, table := range tables {
if !table.IsBaseTable || (!table.CanClean && !table.CanDelete) {
continue
}

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