Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
476ff91bf8 | ||
|
|
ac6b10489e | ||
|
|
9352e1837f | ||
|
|
ddec102d18 | ||
|
|
0d50b9b0cc | ||
|
|
20b4b47eea | ||
|
|
ee8396c760 | ||
|
|
c3fa6a753a | ||
|
|
fbe4de2e94 | ||
|
|
f5c7108799 | ||
|
|
5825a7e654 | ||
|
|
a6911117af | ||
|
|
b428db4f5e | ||
|
|
e4145a2059 | ||
|
|
af13357985 | ||
|
|
1b949bd056 |
@@ -1,7 +1,7 @@
|
|||||||
package teaconst
|
package teaconst
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "1.0.0"
|
Version = "1.0.4"
|
||||||
|
|
||||||
ProductName = "Edge API"
|
ProductName = "Edge API"
|
||||||
ProcessName = "edge-api"
|
ProcessName = "edge-api"
|
||||||
@@ -18,7 +18,7 @@ const (
|
|||||||
|
|
||||||
// 其他节点版本号,用来检测是否有需要升级的节点
|
// 其他节点版本号,用来检测是否有需要升级的节点
|
||||||
|
|
||||||
NodeVersion = "1.0.0"
|
NodeVersion = "1.0.4"
|
||||||
|
|
||||||
// SQLVersion SQL版本号
|
// SQLVersion SQL版本号
|
||||||
SQLVersion = "11"
|
SQLVersion = "11"
|
||||||
|
|||||||
@@ -131,6 +131,8 @@ func (this *ACMEUserDAO) CountACMEUsersWithAdminId(tx *dbs.Tx, adminId int64, us
|
|||||||
}
|
}
|
||||||
if userId > 0 {
|
if userId > 0 {
|
||||||
query.Attr("userId", userId)
|
query.Attr("userId", userId)
|
||||||
|
} else {
|
||||||
|
query.Attr("userId", 0)
|
||||||
}
|
}
|
||||||
if accountId > 0 {
|
if accountId > 0 {
|
||||||
query.Attr("accountId", accountId)
|
query.Attr("accountId", accountId)
|
||||||
@@ -149,6 +151,8 @@ func (this *ACMEUserDAO) ListACMEUsers(tx *dbs.Tx, adminId int64, userId int64,
|
|||||||
}
|
}
|
||||||
if userId > 0 {
|
if userId > 0 {
|
||||||
query.Attr("userId", userId)
|
query.Attr("userId", userId)
|
||||||
|
} else {
|
||||||
|
query.Attr("userId", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = query.
|
_, err = query.
|
||||||
|
|||||||
@@ -237,7 +237,7 @@ func (this *HTTPAccessLogDAO) CreateHTTPAccessLog(tx *dbs.Tx, dao *HTTPAccessLog
|
|||||||
if len(accessLog.TimeISO8601) > 10 {
|
if len(accessLog.TimeISO8601) > 10 {
|
||||||
day = strings.ReplaceAll(accessLog.TimeISO8601[:10], "-", "")
|
day = strings.ReplaceAll(accessLog.TimeISO8601[:10], "-", "")
|
||||||
} else {
|
} else {
|
||||||
timeutil.FormatTime("Ymd", accessLog.Timestamp)
|
day = timeutil.FormatTime("Ymd", accessLog.Timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
tableDef, err := SharedHTTPAccessLogManager.FindLastTable(dao.Instance, day, true)
|
tableDef, err := SharedHTTPAccessLogManager.FindLastTable(dao.Instance, day, true)
|
||||||
|
|||||||
@@ -416,10 +416,10 @@ func (this *IPItemDAO) ListIPItemsWithListId(tx *dbs.Tx, listId int64, sourceUse
|
|||||||
// ListIPItemsAfterVersion 根据版本号查找IP列表
|
// ListIPItemsAfterVersion 根据版本号查找IP列表
|
||||||
func (this *IPItemDAO) ListIPItemsAfterVersion(tx *dbs.Tx, version int64, size int64) (result []*IPItem, err error) {
|
func (this *IPItemDAO) ListIPItemsAfterVersion(tx *dbs.Tx, version int64, size int64) (result []*IPItem, err error) {
|
||||||
_, err = this.Query(tx).
|
_, err = this.Query(tx).
|
||||||
|
UseIndex("version").
|
||||||
// 这里不要设置状态参数,因为我们要知道哪些是删除的
|
// 这里不要设置状态参数,因为我们要知道哪些是删除的
|
||||||
Gt("version", version).
|
Gt("version", version).
|
||||||
Asc("version").
|
Asc("version").
|
||||||
Asc("id").
|
|
||||||
Limit(size).
|
Limit(size).
|
||||||
Slice(&result).
|
Slice(&result).
|
||||||
FindAll()
|
FindAll()
|
||||||
|
|||||||
@@ -356,7 +356,7 @@ func (this *NodeDAO) ListEnabledNodesMatch(tx *dbs.Tx,
|
|||||||
|
|
||||||
// 关键词
|
// 关键词
|
||||||
if len(keyword) > 0 {
|
if len(keyword) > 0 {
|
||||||
query.Where("(name LIKE :keyword OR JSON_EXTRACT(status,'$.hostname') LIKE :keyword OR id IN (SELECT nodeId FROM "+SharedNodeIPAddressDAO.Table+" WHERE ip LIKE :keyword))").
|
query.Where("(name LIKE :keyword OR JSON_EXTRACT(status,'$.hostname') LIKE :keyword OR "+this.Table+".id IN (SELECT nodeId FROM "+SharedNodeIPAddressDAO.Table+" WHERE ip LIKE :keyword))").
|
||||||
Param("keyword", dbutils.QuoteLike(keyword))
|
Param("keyword", dbutils.QuoteLike(keyword))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -684,7 +684,7 @@ func (this *SSLCertDAO) buildDomainSearchingQuery(query *dbs.Query, domains []st
|
|||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
domainMap[domain] = true
|
domainMap[domain] = true
|
||||||
}
|
}
|
||||||
var reg = regexp.MustCompile(`^[\w.-]+$`) // 为了下面的SQL语句安全先不支持其他字符
|
var reg = regexp.MustCompile(`^[\w*.-]+$`) // 为了下面的SQL语句安全先不支持其他字符
|
||||||
for domain := range domainMap {
|
for domain := range domainMap {
|
||||||
if !reg.MatchString(domain) {
|
if !reg.MatchString(domain) {
|
||||||
continue
|
continue
|
||||||
@@ -729,7 +729,9 @@ func (this *SSLCertDAO) buildDomainSearchingQuery(query *dbs.Query, domains []st
|
|||||||
|
|
||||||
sqlPieces = append(sqlPieces, "JSON_CONTAINS(dnsNames, '"+string(domainJSON)+"')")
|
sqlPieces = append(sqlPieces, "JSON_CONTAINS(dnsNames, '"+string(domainJSON)+"')")
|
||||||
}
|
}
|
||||||
query.Where("(" + strings.Join(sqlPieces, " OR ") + ")")
|
if len(sqlPieces) > 0 {
|
||||||
|
query.Where("(" + strings.Join(sqlPieces, " OR ") + ")")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package dnspod
|
||||||
|
|
||||||
|
type CustomLineGroupListResponse struct {
|
||||||
|
BaseResponse
|
||||||
|
|
||||||
|
Data struct {
|
||||||
|
LineGroups []struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
} `json:"line_groups"`
|
||||||
|
Info struct {
|
||||||
|
NowTotal int `json:"now_total"`
|
||||||
|
Total int `json:"total"`
|
||||||
|
} `json:"info"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
@@ -164,6 +164,53 @@ func (this *DNSPodProvider) GetRoutes(domain string) (routes []*dnstypes.Route,
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 自定义线路分组
|
||||||
|
var groupsPerPage = 100
|
||||||
|
var customLineGroupListResponse = new(dnspod.CustomLineGroupListResponse)
|
||||||
|
err = this.doAPI("/Custom.Line.Group.List", map[string]string{
|
||||||
|
"domain": domain,
|
||||||
|
"offset": "0",
|
||||||
|
"length": types.String(groupsPerPage),
|
||||||
|
}, customLineGroupListResponse)
|
||||||
|
if err != nil {
|
||||||
|
// 忽略自定义分组错误
|
||||||
|
err = nil
|
||||||
|
} else {
|
||||||
|
if len(customLineGroupListResponse.Data.LineGroups) > 0 {
|
||||||
|
for _, lineGroup := range customLineGroupListResponse.Data.LineGroups {
|
||||||
|
routes = append(routes, &dnstypes.Route{
|
||||||
|
Name: "分组:" + lineGroup.Name,
|
||||||
|
Code: lineGroup.Name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if customLineGroupListResponse.Data.Info.Total > customLineGroupListResponse.Data.Info.NowTotal {
|
||||||
|
for page := 1; page <= 100; /** 最多100页 **/ page++ {
|
||||||
|
err = this.doAPI("/Custom.Line.Group.List", map[string]string{
|
||||||
|
"domain": domain,
|
||||||
|
"offset": types.String(page * groupsPerPage),
|
||||||
|
"length": types.String(groupsPerPage),
|
||||||
|
}, customLineGroupListResponse)
|
||||||
|
if err != nil {
|
||||||
|
// 忽略错误
|
||||||
|
err = nil
|
||||||
|
} else {
|
||||||
|
if len(customLineGroupListResponse.Data.LineGroups) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, lineGroup := range customLineGroupListResponse.Data.LineGroups {
|
||||||
|
routes = append(routes, &dnstypes.Route{
|
||||||
|
Name: "分组:" + lineGroup.Name,
|
||||||
|
Code: lineGroup.Name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return routes, nil
|
return routes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"github.com/iwind/TeaGo/files"
|
"github.com/iwind/TeaGo/files"
|
||||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var SharedDeployManager = NewDeployManager()
|
var SharedDeployManager = NewDeployManager()
|
||||||
@@ -12,9 +13,12 @@ var SharedDeployManager = NewDeployManager()
|
|||||||
// DeployManager 节点部署文件管理器
|
// DeployManager 节点部署文件管理器
|
||||||
// 如果节点部署文件有变化,需要重启API节点以便于生效
|
// 如果节点部署文件有变化,需要重启API节点以便于生效
|
||||||
type DeployManager struct {
|
type DeployManager struct {
|
||||||
dir string
|
dir string
|
||||||
|
|
||||||
nodeFiles []*DeployFile
|
nodeFiles []*DeployFile
|
||||||
nsNodeFiles []*DeployFile
|
nsNodeFiles []*DeployFile
|
||||||
|
|
||||||
|
locker sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDeployManager 获取新节点部署文件管理器
|
// NewDeployManager 获取新节点部署文件管理器
|
||||||
@@ -29,24 +33,27 @@ func NewDeployManager() *DeployManager {
|
|||||||
|
|
||||||
// LoadNodeFiles 加载所有边缘节点文件
|
// LoadNodeFiles 加载所有边缘节点文件
|
||||||
func (this *DeployManager) LoadNodeFiles() []*DeployFile {
|
func (this *DeployManager) LoadNodeFiles() []*DeployFile {
|
||||||
|
this.locker.Lock()
|
||||||
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
if len(this.nodeFiles) > 0 {
|
if len(this.nodeFiles) > 0 {
|
||||||
return this.nodeFiles
|
return this.nodeFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
keyMap := map[string]*DeployFile{} // key => File
|
var keyMap = map[string]*DeployFile{} // key => File
|
||||||
|
|
||||||
reg := regexp.MustCompile(`^edge-node-(\w+)-(\w+)-v([0-9.]+)\.zip$`)
|
var reg = regexp.MustCompile(`^edge-node-(\w+)-(\w+)-v([0-9.]+)\.zip$`)
|
||||||
for _, file := range files.NewFile(this.dir).List() {
|
for _, file := range files.NewFile(this.dir).List() {
|
||||||
name := file.Name()
|
var name = file.Name()
|
||||||
if !reg.MatchString(name) {
|
if !reg.MatchString(name) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
matches := reg.FindStringSubmatch(name)
|
var matches = reg.FindStringSubmatch(name)
|
||||||
osName := matches[1]
|
var osName = matches[1]
|
||||||
arch := matches[2]
|
var arch = matches[2]
|
||||||
version := matches[3]
|
var version = matches[3]
|
||||||
|
|
||||||
key := osName + "_" + arch
|
var key = osName + "_" + arch
|
||||||
oldFile, ok := keyMap[key]
|
oldFile, ok := keyMap[key]
|
||||||
if ok && stringutil.VersionCompare(oldFile.Version, version) > 0 {
|
if ok && stringutil.VersionCompare(oldFile.Version, version) > 0 {
|
||||||
continue
|
continue
|
||||||
@@ -59,7 +66,7 @@ func (this *DeployManager) LoadNodeFiles() []*DeployFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result := []*DeployFile{}
|
var result = []*DeployFile{}
|
||||||
for _, v := range keyMap {
|
for _, v := range keyMap {
|
||||||
result = append(result, v)
|
result = append(result, v)
|
||||||
}
|
}
|
||||||
@@ -81,24 +88,27 @@ func (this *DeployManager) FindNodeFile(os string, arch string) *DeployFile {
|
|||||||
|
|
||||||
// LoadNSNodeFiles 加载所有NS节点安装文件
|
// LoadNSNodeFiles 加载所有NS节点安装文件
|
||||||
func (this *DeployManager) LoadNSNodeFiles() []*DeployFile {
|
func (this *DeployManager) LoadNSNodeFiles() []*DeployFile {
|
||||||
|
this.locker.Lock()
|
||||||
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
if len(this.nsNodeFiles) > 0 {
|
if len(this.nsNodeFiles) > 0 {
|
||||||
return this.nsNodeFiles
|
return this.nsNodeFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
keyMap := map[string]*DeployFile{} // key => File
|
var keyMap = map[string]*DeployFile{} // key => File
|
||||||
|
|
||||||
reg := regexp.MustCompile(`^edge-dns-(\w+)-(\w+)-v([0-9.]+)\.zip$`)
|
var reg = regexp.MustCompile(`^edge-dns-(\w+)-(\w+)-v([0-9.]+)\.zip$`)
|
||||||
for _, file := range files.NewFile(this.dir).List() {
|
for _, file := range files.NewFile(this.dir).List() {
|
||||||
name := file.Name()
|
var name = file.Name()
|
||||||
if !reg.MatchString(name) {
|
if !reg.MatchString(name) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
matches := reg.FindStringSubmatch(name)
|
var matches = reg.FindStringSubmatch(name)
|
||||||
osName := matches[1]
|
var osName = matches[1]
|
||||||
arch := matches[2]
|
var arch = matches[2]
|
||||||
version := matches[3]
|
var version = matches[3]
|
||||||
|
|
||||||
key := osName + "_" + arch
|
var key = osName + "_" + arch
|
||||||
oldFile, ok := keyMap[key]
|
oldFile, ok := keyMap[key]
|
||||||
if ok && stringutil.VersionCompare(oldFile.Version, version) > 0 {
|
if ok && stringutil.VersionCompare(oldFile.Version, version) > 0 {
|
||||||
continue
|
continue
|
||||||
@@ -111,7 +121,7 @@ func (this *DeployManager) LoadNSNodeFiles() []*DeployFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result := []*DeployFile{}
|
var result = []*DeployFile{}
|
||||||
for _, v := range keyMap {
|
for _, v := range keyMap {
|
||||||
result = append(result, v)
|
result = append(result, v)
|
||||||
}
|
}
|
||||||
@@ -130,3 +140,12 @@ func (this *DeployManager) FindNSNodeFile(os string, arch string) *DeployFile {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reload 重置缓存
|
||||||
|
func (this *DeployManager) Reload() {
|
||||||
|
this.locker.Lock()
|
||||||
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
|
this.nodeFiles = nil
|
||||||
|
this.nsNodeFiles = nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build !windows
|
||||||
// +build !windows
|
// +build !windows
|
||||||
|
|
||||||
package nodes
|
package nodes
|
||||||
@@ -31,7 +32,7 @@ func (this *NodeStatusExecutor) updateMem(status *nodeconfigs.NodeStatus) {
|
|||||||
if minFreeMemory > 1<<30 {
|
if minFreeMemory > 1<<30 {
|
||||||
minFreeMemory = 1 << 30
|
minFreeMemory = 1 << 30
|
||||||
}
|
}
|
||||||
if stat.Free < minFreeMemory {
|
if stat.Available > 0 && stat.Available < minFreeMemory {
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
debug.FreeOSMemory()
|
debug.FreeOSMemory()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,6 +232,12 @@ func (this *ACMETaskService) CreateACMETask(ctx context.Context, req *pb.CreateA
|
|||||||
req.AuthType = acme.AuthTypeDNS
|
req.AuthType = acme.AuthTypeDNS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if adminId > 0 {
|
||||||
|
if req.UserId > 0 {
|
||||||
|
userId = req.UserId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var tx = this.NullTx()
|
var tx = this.NullTx()
|
||||||
taskId, err := acmemodels.SharedACMETaskDAO.CreateACMETask(tx, adminId, userId, req.AuthType, req.AcmeUserId, req.DnsProviderId, req.DnsDomain, req.Domains, req.AutoRenew, req.AuthURL)
|
taskId, err := acmemodels.SharedACMETaskDAO.CreateACMETask(tx, adminId, userId, req.AuthType, req.AcmeUserId, req.DnsProviderId, req.DnsDomain, req.Domains, req.AutoRenew, req.AuthURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ func (this *ACMEUserService) CreateACMEUser(ctx context.Context, req *pb.CreateA
|
|||||||
|
|
||||||
var tx = this.NullTx()
|
var tx = this.NullTx()
|
||||||
|
|
||||||
|
if adminId > 0 {
|
||||||
|
if req.UserId > 0 {
|
||||||
|
userId = req.UserId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
acmeUserId, err := acmemodels.SharedACMEUserDAO.CreateACMEUser(tx, adminId, userId, req.AcmeProviderCode, req.AcmeProviderAccountId, req.Email, req.Description)
|
acmeUserId, err := acmemodels.SharedACMEUserDAO.CreateACMEUser(tx, adminId, userId, req.AcmeProviderCode, req.AcmeProviderAccountId, req.Email, req.Description)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/installers"
|
||||||
rpcutils "github.com/TeaOSLab/EdgeAPI/internal/rpc/utils"
|
rpcutils "github.com/TeaOSLab/EdgeAPI/internal/rpc/utils"
|
||||||
executils "github.com/TeaOSLab/EdgeAPI/internal/utils/exec"
|
executils "github.com/TeaOSLab/EdgeAPI/internal/utils/exec"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
@@ -481,3 +482,106 @@ func (this *APINodeService) UploadAPINodeFile(ctx context.Context, req *pb.Uploa
|
|||||||
|
|
||||||
return &pb.UploadAPINodeFileResponse{}, nil
|
return &pb.UploadAPINodeFileResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UploadDeployFileToAPINode 上传节点安装文件
|
||||||
|
func (this *APINodeService) UploadDeployFileToAPINode(ctx context.Context, req *pb.UploadDeployFileToAPINodeRequest) (*pb.RPCSuccess, error) {
|
||||||
|
_, err := this.ValidateAdmin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetDir = Tea.Root + "/deploy/"
|
||||||
|
var targetTmpFile = targetDir + "/" + req.Filename + ".tmp"
|
||||||
|
var targetFile = targetDir + "/" + req.Filename
|
||||||
|
|
||||||
|
if req.IsFirstChunk {
|
||||||
|
_ = os.Remove(targetTmpFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.ChunkData) > 0 {
|
||||||
|
err = func() error {
|
||||||
|
var flags = os.O_CREATE | os.O_WRONLY
|
||||||
|
if req.IsFirstChunk {
|
||||||
|
flags |= os.O_TRUNC
|
||||||
|
} else {
|
||||||
|
flags |= os.O_APPEND
|
||||||
|
}
|
||||||
|
fp, err := os.OpenFile(targetTmpFile, flags, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = fp.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, err = fp.Write(req.ChunkData)
|
||||||
|
return err
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("write file failed: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.IsLastChunk {
|
||||||
|
// 检查SUM
|
||||||
|
fp, err := os.Open(targetTmpFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var hash = md5.New()
|
||||||
|
_, err = io.Copy(hash, fp)
|
||||||
|
_ = fp.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var tmpSum = fmt.Sprintf("%x", hash.Sum(nil))
|
||||||
|
if tmpSum != req.Sum {
|
||||||
|
_ = os.Remove(targetTmpFile)
|
||||||
|
return nil, errors.New("check sum failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正式改名
|
||||||
|
err = os.Rename(targetTmpFile, targetFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("rename failed: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重载数据
|
||||||
|
installers.SharedDeployManager.Reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.Success()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLatestDeployFiles 查找已有节点安装文件信息
|
||||||
|
func (this *APINodeService) FindLatestDeployFiles(ctx context.Context, req *pb.FindLatestDeployFilesRequest) (*pb.FindLatestDeployFilesResponse, error) {
|
||||||
|
_, err := this.ValidateAdmin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pbNodeFiles = []*pb.FindLatestDeployFilesResponse_DeployFile{}
|
||||||
|
var nodeFiles = installers.SharedDeployManager.LoadNodeFiles()
|
||||||
|
for _, nodeFile := range nodeFiles {
|
||||||
|
pbNodeFiles = append(pbNodeFiles, &pb.FindLatestDeployFilesResponse_DeployFile{
|
||||||
|
Os: nodeFile.OS,
|
||||||
|
Arch: nodeFile.Arch,
|
||||||
|
Version: nodeFile.Version,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var pbNSNodeFiles = []*pb.FindLatestDeployFilesResponse_DeployFile{}
|
||||||
|
var nsNodeFiles = installers.SharedDeployManager.LoadNSNodeFiles()
|
||||||
|
for _, nodeFile := range nsNodeFiles {
|
||||||
|
pbNSNodeFiles = append(pbNSNodeFiles, &pb.FindLatestDeployFilesResponse_DeployFile{
|
||||||
|
Os: nodeFile.OS,
|
||||||
|
Arch: nodeFile.Arch,
|
||||||
|
Version: nodeFile.Version,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pb.FindLatestDeployFilesResponse{
|
||||||
|
NodeDeployFiles: pbNodeFiles,
|
||||||
|
NsNodeDeployFiles: pbNSNodeFiles,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,8 +29,29 @@ func (this *HTTPCacheTaskService) CreateHTTPCacheTask(ctx context.Context, req *
|
|||||||
var tx = this.NullTx()
|
var tx = this.NullTx()
|
||||||
|
|
||||||
// 检查操作类型
|
// 检查操作类型
|
||||||
|
if len(req.Type) == 0 {
|
||||||
|
return nil, errors.New("require 'type' parameter")
|
||||||
|
}
|
||||||
if req.Type != models.HTTPCacheTaskTypePurge && req.Type != models.HTTPCacheTaskTypeFetch {
|
if req.Type != models.HTTPCacheTaskTypePurge && req.Type != models.HTTPCacheTaskTypeFetch {
|
||||||
return nil, errors.New("invalid type '" + req.Type + "'")
|
return nil, errors.New("'type' must be 'purge' or 'fetch'")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查Key类型
|
||||||
|
if len(req.KeyType) == 0 {
|
||||||
|
return nil, errors.New("require 'keyType' parameter")
|
||||||
|
}
|
||||||
|
if req.KeyType != "key" && req.KeyType != "prefix" {
|
||||||
|
return nil, errors.New("'keyType' must be 'key' or 'prefix'")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预热只能是Key
|
||||||
|
if req.Type == models.HTTPCacheTaskTypeFetch && req.KeyType != "key" {
|
||||||
|
return nil, errors.New("'keyType' should be 'key' when fetching cache")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查key是否为空
|
||||||
|
if len(req.Keys) == 0 {
|
||||||
|
return nil, errors.New("'keys' should not be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查Key数量
|
// 检查Key数量
|
||||||
|
|||||||
@@ -195,6 +195,9 @@ func (this *IPLibraryService) LookupIPRegion(ctx context.Context, req *pb.Lookup
|
|||||||
Isp: result.ProviderName(),
|
Isp: result.ProviderName(),
|
||||||
CountryId: result.CountryId(),
|
CountryId: result.CountryId(),
|
||||||
ProvinceId: result.ProvinceId(),
|
ProvinceId: result.ProvinceId(),
|
||||||
|
CityId: result.CityId(),
|
||||||
|
TownId: result.TownId(),
|
||||||
|
ProviderId: result.ProviderId(),
|
||||||
Summary: result.Summary(),
|
Summary: result.Summary(),
|
||||||
}}, nil
|
}}, nil
|
||||||
}
|
}
|
||||||
@@ -213,12 +216,17 @@ func (this *IPLibraryService) LookupIPRegions(ctx context.Context, req *pb.Looku
|
|||||||
var info = iplibrary.LookupIP(ip)
|
var info = iplibrary.LookupIP(ip)
|
||||||
if info != nil && info.IsOk() {
|
if info != nil && info.IsOk() {
|
||||||
result[ip] = &pb.IPRegion{
|
result[ip] = &pb.IPRegion{
|
||||||
Country: info.CountryName(),
|
Country: info.CountryName(),
|
||||||
Region: "",
|
Region: "",
|
||||||
Province: info.ProvinceName(),
|
Province: info.ProvinceName(),
|
||||||
City: info.CityName(),
|
City: info.CityName(),
|
||||||
Isp: info.ProviderName(),
|
Isp: info.ProviderName(),
|
||||||
Summary: info.Summary(),
|
CountryId: info.CountryId(),
|
||||||
|
ProvinceId: info.ProvinceId(),
|
||||||
|
CityId: info.CityId(),
|
||||||
|
TownId: info.TownId(),
|
||||||
|
ProviderId: info.ProviderId(),
|
||||||
|
Summary: info.Summary(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,7 @@ package setup
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"encoding/json"
|
|
||||||
"github.com/iwind/TeaGo/logs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed sql.json
|
//go:embed sql.json
|
||||||
var sqlData []byte
|
var sqlData []byte
|
||||||
|
|
||||||
func init() {
|
|
||||||
err := json.Unmarshal(sqlData, LatestSQLResult)
|
|
||||||
if err != nil {
|
|
||||||
logs.Println("[ERROR]load sql failed: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -62184,7 +62184,7 @@
|
|||||||
"name": "edgeIPItems",
|
"name": "edgeIPItems",
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"charset": "utf8mb4_general_ci",
|
"charset": "utf8mb4_general_ci",
|
||||||
"definition": "CREATE TABLE `edgeIPItems` (\n `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',\n `listId` int(11) unsigned DEFAULT '0' COMMENT '所属名单ID',\n `type` varchar(64) DEFAULT 'ipv4' COMMENT '类型',\n `ipFrom` varchar(64) DEFAULT NULL COMMENT '开始IP',\n `ipTo` varchar(64) DEFAULT NULL COMMENT '结束IP',\n `ipFromLong` bigint(20) unsigned DEFAULT '0' COMMENT '开始IP整型',\n `ipToLong` bigint(20) unsigned DEFAULT '0' COMMENT '结束IP整型',\n `version` bigint(20) unsigned DEFAULT '0' COMMENT '版本',\n `createdAt` bigint(11) unsigned DEFAULT '0' COMMENT '创建时间',\n `updatedAt` bigint(11) unsigned DEFAULT '0' COMMENT '修改时间',\n `reason` varchar(255) DEFAULT NULL COMMENT '加入说明',\n `eventLevel` varchar(64) DEFAULT NULL COMMENT '事件级别',\n `state` tinyint(1) unsigned DEFAULT '1' COMMENT '状态',\n `expiredAt` bigint(11) unsigned DEFAULT '0' COMMENT '过期时间',\n `serverId` int(11) unsigned DEFAULT '0' COMMENT '有效范围服务ID',\n `nodeId` int(11) unsigned DEFAULT '0' COMMENT '有效范围节点ID',\n `sourceNodeId` int(11) unsigned DEFAULT '0' COMMENT '来源节点ID',\n `sourceServerId` int(11) unsigned DEFAULT '0' COMMENT '来源服务ID',\n `sourceHTTPFirewallPolicyId` int(11) unsigned DEFAULT '0' COMMENT '来源策略ID',\n `sourceHTTPFirewallRuleGroupId` int(11) unsigned DEFAULT '0' COMMENT '来源规则集分组ID',\n `sourceHTTPFirewallRuleSetId` int(11) unsigned DEFAULT '0' COMMENT '来源规则集ID',\n `sourceUserId` bigint(11) unsigned DEFAULT '0' COMMENT '用户ID',\n `isRead` tinyint(1) unsigned DEFAULT '1' COMMENT '是否已读',\n PRIMARY KEY (`id`),\n KEY `listId` (`listId`),\n KEY `version_expiredAt` (`version`,`expiredAt`) USING BTREE,\n KEY `ipFrom` (`ipFrom`),\n KEY `serverId` (`serverId`),\n KEY `expiredAt_state` (`expiredAt`,`state`) USING BTREE,\n KEY `isRead` (`expiredAt`,`isRead`) USING BTREE,\n KEY `createdAt` (`createdAt`),\n KEY `sourceUserId` (`sourceUserId`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='IP'",
|
"definition": "CREATE TABLE `edgeIPItems` (\n `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',\n `listId` int(11) unsigned DEFAULT '0' COMMENT '所属名单ID',\n `type` varchar(64) DEFAULT 'ipv4' COMMENT '类型',\n `ipFrom` varchar(64) DEFAULT NULL COMMENT '开始IP',\n `ipTo` varchar(64) DEFAULT NULL COMMENT '结束IP',\n `ipFromLong` bigint(20) unsigned DEFAULT '0' COMMENT '开始IP整型',\n `ipToLong` bigint(20) unsigned DEFAULT '0' COMMENT '结束IP整型',\n `version` bigint(20) unsigned DEFAULT '0' COMMENT '版本',\n `createdAt` bigint(11) unsigned DEFAULT '0' COMMENT '创建时间',\n `updatedAt` bigint(11) unsigned DEFAULT '0' COMMENT '修改时间',\n `reason` varchar(255) DEFAULT NULL COMMENT '加入说明',\n `eventLevel` varchar(64) DEFAULT NULL COMMENT '事件级别',\n `state` tinyint(1) unsigned DEFAULT '1' COMMENT '状态',\n `expiredAt` bigint(11) unsigned DEFAULT '0' COMMENT '过期时间',\n `serverId` int(11) unsigned DEFAULT '0' COMMENT '有效范围服务ID',\n `nodeId` int(11) unsigned DEFAULT '0' COMMENT '有效范围节点ID',\n `sourceNodeId` int(11) unsigned DEFAULT '0' COMMENT '来源节点ID',\n `sourceServerId` int(11) unsigned DEFAULT '0' COMMENT '来源服务ID',\n `sourceHTTPFirewallPolicyId` int(11) unsigned DEFAULT '0' COMMENT '来源策略ID',\n `sourceHTTPFirewallRuleGroupId` int(11) unsigned DEFAULT '0' COMMENT '来源规则集分组ID',\n `sourceHTTPFirewallRuleSetId` int(11) unsigned DEFAULT '0' COMMENT '来源规则集ID',\n `sourceUserId` bigint(11) unsigned DEFAULT '0' COMMENT '用户ID',\n `isRead` tinyint(1) unsigned DEFAULT '1' COMMENT '是否已读',\n PRIMARY KEY (`id`),\n KEY `listId` (`listId`),\n KEY `ipFrom` (`ipFrom`),\n KEY `serverId` (`serverId`),\n KEY `expiredAt_state` (`expiredAt`,`state`) USING BTREE,\n KEY `isRead` (`expiredAt`,`isRead`) USING BTREE,\n KEY `createdAt` (`createdAt`),\n KEY `sourceUserId` (`sourceUserId`),\n KEY `version` (`version`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='IP'",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"name": "id",
|
"name": "id",
|
||||||
@@ -62288,10 +62288,6 @@
|
|||||||
"name": "listId",
|
"name": "listId",
|
||||||
"definition": "KEY `listId` (`listId`) USING BTREE"
|
"definition": "KEY `listId` (`listId`) USING BTREE"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "version_expiredAt",
|
|
||||||
"definition": "KEY `version_expiredAt` (`version`,`expiredAt`) USING BTREE"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "ipFrom",
|
"name": "ipFrom",
|
||||||
"definition": "KEY `ipFrom` (`ipFrom`) USING BTREE"
|
"definition": "KEY `ipFrom` (`ipFrom`) USING BTREE"
|
||||||
@@ -62315,6 +62311,10 @@
|
|||||||
{
|
{
|
||||||
"name": "sourceUserId",
|
"name": "sourceUserId",
|
||||||
"definition": "KEY `sourceUserId` (`sourceUserId`) USING BTREE"
|
"definition": "KEY `sourceUserId` (`sourceUserId`) USING BTREE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "version",
|
||||||
|
"definition": "KEY `version` (`version`) USING BTREE"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"records": []
|
"records": []
|
||||||
|
|||||||
@@ -12,14 +12,13 @@ import (
|
|||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"github.com/iwind/TeaGo/rands"
|
"github.com/iwind/TeaGo/rands"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
|
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var LatestSQLResult = &SQLDumpResult{}
|
|
||||||
|
|
||||||
// SQLExecutor 安装或升级SQL执行器
|
// SQLExecutor 安装或升级SQL执行器
|
||||||
type SQLExecutor struct {
|
type SQLExecutor struct {
|
||||||
dbConfig *dbs.DBConfig
|
dbConfig *dbs.DBConfig
|
||||||
@@ -65,7 +64,14 @@ func (this *SQLExecutor) Run(showLog bool) error {
|
|||||||
if this.logWriter != nil {
|
if this.logWriter != nil {
|
||||||
showLog = true
|
showLog = true
|
||||||
}
|
}
|
||||||
_, err = sqlDump.Apply(db, LatestSQLResult, showLog)
|
|
||||||
|
var sqlResult = &SQLDumpResult{}
|
||||||
|
err = json.Unmarshal(sqlData, sqlResult)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("decode sql data failed: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = sqlDump.Apply(db, sqlResult, showLog)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -81,8 +87,14 @@ func (this *SQLExecutor) Run(showLog bool) error {
|
|||||||
|
|
||||||
// 检查数据
|
// 检查数据
|
||||||
func (this *SQLExecutor) checkData(db *dbs.DB) error {
|
func (this *SQLExecutor) checkData(db *dbs.DB) error {
|
||||||
|
// 检查初始化用户
|
||||||
|
err := this.checkUser(db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// 检查管理员平台节点
|
// 检查管理员平台节点
|
||||||
err := this.checkAdminNode(db)
|
err = this.checkAdminNode(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -132,6 +144,20 @@ func (this *SQLExecutor) checkData(db *dbs.DB) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建初始用户
|
||||||
|
func (this *SQLExecutor) checkUser(db *dbs.DB) error {
|
||||||
|
one, err := db.FindOne("SELECT id FROM edgeUsers LIMIT 1")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(one) > 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec("INSERT INTO edgeUsers (`username`, `password`, `fullname`, `isOn`, `state`, `createdAt`) VALUES (?, ?, ?, ?, ?, ?)", "USER-"+rands.HexString(10), stringutil.Md5(rands.HexString(32)), "默认用户", 1, 1, time.Now().Unix())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// 检查管理员平台节点
|
// 检查管理员平台节点
|
||||||
func (this *SQLExecutor) checkAdminNode(db *dbs.DB) error {
|
func (this *SQLExecutor) checkAdminNode(db *dbs.DB) error {
|
||||||
stmt, err := db.Prepare("SELECT COUNT(*) FROM edgeAPITokens WHERE role='admin'")
|
stmt, err := db.Prepare("SELECT COUNT(*) FROM edgeAPITokens WHERE role='admin'")
|
||||||
|
|||||||
Reference in New Issue
Block a user