Initial commit (code only without large binaries)
This commit is contained in:
16
EdgeAdmin/internal/web/actions/default/settings/advanced.go
Normal file
16
EdgeAdmin/internal/web/actions/default/settings/advanced.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package settings
|
||||
|
||||
import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
|
||||
type AdvancedAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *AdvancedAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *AdvancedAction) RunGet(params struct{}) {
|
||||
// 跳转到高级设置的第一个Tab
|
||||
this.RedirectURL("/settings/database")
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
type DeleteAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DeleteAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
}) {
|
||||
// 创建日志
|
||||
defer this.CreateLogInfo(codes.APINode_LogDeleteAPINode, params.NodeId)
|
||||
|
||||
// 检查是否是唯一的节点
|
||||
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var apiNode = nodeResp.ApiNode
|
||||
if apiNode == nil {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
if apiNode.IsOn {
|
||||
countResp, err := this.RPC().APINodeRPC().CountAllEnabledAndOnAPINodes(this.AdminContext(), &pb.CountAllEnabledAndOnAPINodesRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if countResp.Count == 1 {
|
||||
this.Fail("无法删除此节点:必须保留至少一个可用的API节点")
|
||||
}
|
||||
}
|
||||
|
||||
_, err = this.RPC().APINodeRPC().DeleteAPINode(this.AdminContext(), &pb.DeleteAPINodeRequest{ApiNodeId: params.NodeId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
type Helper struct {
|
||||
}
|
||||
|
||||
func NewHelper() *Helper {
|
||||
return &Helper{}
|
||||
}
|
||||
|
||||
func (this *Helper) BeforeAction(action *actions.ActionObject) {
|
||||
}
|
||||
130
EdgeAdmin/internal/web/actions/default/settings/api/index.go
Normal file
130
EdgeAdmin/internal/web/actions/default/settings/api/index.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/apinodeutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "node", "index")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
countResp, err := this.RPC().APINodeRPC().CountAllEnabledAPINodes(this.AdminContext(), &pb.CountAllEnabledAPINodesRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var count = countResp.Count
|
||||
var page = this.NewPage(count)
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
var nodeMaps = []maps.Map{}
|
||||
if count > 0 {
|
||||
nodesResp, err := this.RPC().APINodeRPC().ListEnabledAPINodes(this.AdminContext(), &pb.ListEnabledAPINodesRequest{
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, node := range nodesResp.ApiNodes {
|
||||
// 状态
|
||||
var status = &nodeconfigs.NodeStatus{}
|
||||
if len(node.StatusJSON) > 0 {
|
||||
err = json.Unmarshal(node.StatusJSON, &status)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
continue
|
||||
}
|
||||
status.IsActive = status.IsActive && time.Now().Unix()-status.UpdatedAt <= 60 // N秒之内认为活跃
|
||||
}
|
||||
|
||||
// Rest地址
|
||||
var restAccessAddrs = []string{}
|
||||
if node.RestIsOn {
|
||||
if len(node.RestHTTPJSON) > 0 {
|
||||
httpConfig := &serverconfigs.HTTPProtocolConfig{}
|
||||
err = json.Unmarshal(node.RestHTTPJSON, httpConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
_ = httpConfig.Init()
|
||||
if httpConfig.IsOn && len(httpConfig.Listen) > 0 {
|
||||
for _, listen := range httpConfig.Listen {
|
||||
restAccessAddrs = append(restAccessAddrs, listen.FullAddresses()...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(node.RestHTTPSJSON) > 0 {
|
||||
httpsConfig := &serverconfigs.HTTPSProtocolConfig{}
|
||||
err = json.Unmarshal(node.RestHTTPSJSON, httpsConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
_ = httpsConfig.Init(context.TODO())
|
||||
if httpsConfig.IsOn && len(httpsConfig.Listen) > 0 {
|
||||
restAccessAddrs = append(restAccessAddrs, httpsConfig.FullAddresses()...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var shouldUpgrade = status.IsActive && len(status.BuildVersion) > 0 && stringutil.VersionCompare(teaconst.APINodeVersion, status.BuildVersion) > 0
|
||||
canUpgrade, _ := apinodeutils.CanUpgrade(status.BuildVersion, status.OS, status.Arch)
|
||||
|
||||
nodeMaps = append(nodeMaps, maps.Map{
|
||||
"id": node.Id,
|
||||
"isOn": node.IsOn,
|
||||
"name": node.Name,
|
||||
"accessAddrs": node.AccessAddrs,
|
||||
"restAccessAddrs": restAccessAddrs,
|
||||
"isPrimary": node.IsPrimary,
|
||||
"status": maps.Map{
|
||||
"isActive": status.IsActive,
|
||||
"updatedAt": status.UpdatedAt,
|
||||
"hostname": status.Hostname,
|
||||
"cpuUsage": status.CPUUsage,
|
||||
"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
|
||||
"memUsage": status.MemoryUsage,
|
||||
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
|
||||
"buildVersion": status.BuildVersion,
|
||||
"latestVersion": teaconst.APINodeVersion,
|
||||
"shouldUpgrade": shouldUpgrade,
|
||||
"canUpgrade": shouldUpgrade && canUpgrade,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
this.Data["nodes"] = nodeMaps
|
||||
|
||||
// 检查是否有调试数据
|
||||
countMethodStatsResp, err := this.RPC().APIMethodStatRPC().CountAPIMethodStatsWithDay(this.AdminContext(), &pb.CountAPIMethodStatsWithDayRequest{Day: timeutil.Format("Ymd")})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["hasMethodStats"] = countMethodStatsResp.Count > 0
|
||||
|
||||
this.Show()
|
||||
}
|
||||
24
EdgeAdmin/internal/web/actions/default/settings/api/init.go
Normal file
24
EdgeAdmin/internal/web/actions/default/settings/api/init.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"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"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)).
|
||||
Helper(NewHelper()).
|
||||
Helper(settingutils.NewAdvancedHelper("apiNodes")).
|
||||
Prefix("/settings/api").
|
||||
Get("", new(IndexAction)).
|
||||
Get("/methodStats", new(MethodStatsAction)).
|
||||
GetPost("/node/createPopup", new(node.CreatePopupAction)).
|
||||
Post("/delete", new(DeleteAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type MethodStatsAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *MethodStatsAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *MethodStatsAction) RunGet(params struct {
|
||||
Order string
|
||||
Method string
|
||||
Tag string
|
||||
}) {
|
||||
this.Data["order"] = params.Order
|
||||
this.Data["method"] = params.Method
|
||||
this.Data["tag"] = params.Tag
|
||||
|
||||
statsResp, err := this.RPC().APIMethodStatRPC().FindAPIMethodStatsWithDay(this.AdminContext(), &pb.FindAPIMethodStatsWithDayRequest{Day: timeutil.Format("Ymd")})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var pbStats = statsResp.ApiMethodStats
|
||||
|
||||
switch params.Order {
|
||||
case "method":
|
||||
sort.Slice(pbStats, func(i, j int) bool {
|
||||
return pbStats[i].Method < pbStats[j].Method
|
||||
})
|
||||
case "costMs.desc":
|
||||
sort.Slice(pbStats, func(i, j int) bool {
|
||||
return pbStats[i].CostMs > pbStats[j].CostMs
|
||||
})
|
||||
case "peekMs.desc":
|
||||
sort.Slice(pbStats, func(i, j int) bool {
|
||||
return pbStats[i].PeekMs > pbStats[j].PeekMs
|
||||
})
|
||||
case "calls.desc":
|
||||
sort.Slice(pbStats, func(i, j int) bool {
|
||||
return pbStats[i].CountCalls > pbStats[j].CountCalls
|
||||
})
|
||||
}
|
||||
|
||||
var statMaps = []maps.Map{}
|
||||
for _, stat := range pbStats {
|
||||
if len(params.Method) > 0 && !strings.Contains(strings.ToLower(stat.Method), strings.ToLower(params.Method)) {
|
||||
continue
|
||||
}
|
||||
if len(params.Tag) > 0 && !strings.Contains(strings.ToLower(stat.Tag), strings.ToLower(params.Tag)) {
|
||||
continue
|
||||
}
|
||||
|
||||
statMaps = append(statMaps, maps.Map{
|
||||
"id": stat.Id,
|
||||
"method": stat.Method,
|
||||
"tag": stat.Tag,
|
||||
"costMs": stat.CostMs,
|
||||
"peekMs": stat.PeekMs,
|
||||
"countCalls": stat.CountCalls,
|
||||
})
|
||||
}
|
||||
this.Data["stats"] = statMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CreateAddrPopupAction 添加地址
|
||||
type CreateAddrPopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CreateAddrPopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *CreateAddrPopupAction) RunGet(params struct {
|
||||
}) {
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *CreateAddrPopupAction) RunPost(params struct {
|
||||
Protocol string
|
||||
Addr string
|
||||
Must *actions.Must
|
||||
}) {
|
||||
params.Must.
|
||||
Field("addr", params.Addr).
|
||||
Require("请输入访问地址")
|
||||
|
||||
// 兼容URL
|
||||
if regexp.MustCompile(`^(?i)(http|https)://`).MatchString(params.Addr) {
|
||||
u, err := url.Parse(params.Addr)
|
||||
if err != nil {
|
||||
this.FailField("addr", "错误的访问地址,不需要添加http://或https://")
|
||||
}
|
||||
params.Addr = u.Host
|
||||
}
|
||||
|
||||
// 自动添加端口
|
||||
if !strings.Contains(params.Addr, ":") {
|
||||
switch params.Protocol {
|
||||
case "http":
|
||||
params.Addr += ":80"
|
||||
case "https":
|
||||
params.Addr += ":443"
|
||||
}
|
||||
}
|
||||
|
||||
host, port, err := net.SplitHostPort(params.Addr)
|
||||
if err != nil {
|
||||
this.FailField("addr", "错误的访问地址")
|
||||
}
|
||||
|
||||
var addrConfig = &serverconfigs.NetworkAddressConfig{
|
||||
Protocol: serverconfigs.Protocol(params.Protocol),
|
||||
Host: host,
|
||||
PortRange: port,
|
||||
}
|
||||
this.Data["addr"] = addrConfig
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
)
|
||||
|
||||
type CreatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) Init() {
|
||||
this.Nav("", "node", "create")
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunGet(params struct{}) {
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunPost(params struct {
|
||||
Name string
|
||||
Description string
|
||||
ListensJSON []byte
|
||||
CertIdsJSON []byte
|
||||
AccessAddrsJSON []byte
|
||||
|
||||
RestIsOn bool
|
||||
RestListensJSON []byte
|
||||
|
||||
IsOn bool
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入API节点名称")
|
||||
|
||||
var httpConfig = &serverconfigs.HTTPProtocolConfig{}
|
||||
var httpsConfig = &serverconfigs.HTTPSProtocolConfig{}
|
||||
|
||||
// 监听地址
|
||||
var listens = []*serverconfigs.NetworkAddressConfig{}
|
||||
err := json.Unmarshal(params.ListensJSON, &listens)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if len(listens) == 0 {
|
||||
this.Fail("请添加至少一个进程监听地址")
|
||||
}
|
||||
for _, addr := range listens {
|
||||
if addr.Protocol.IsHTTPFamily() {
|
||||
httpConfig.IsOn = true
|
||||
httpConfig.Listen = append(httpConfig.Listen, addr)
|
||||
} else if addr.Protocol.IsHTTPSFamily() {
|
||||
httpsConfig.IsOn = true
|
||||
httpsConfig.Listen = append(httpsConfig.Listen, addr)
|
||||
}
|
||||
}
|
||||
|
||||
// Rest监听地址
|
||||
var restHTTPConfig = &serverconfigs.HTTPProtocolConfig{}
|
||||
var restHTTPSConfig = &serverconfigs.HTTPSProtocolConfig{}
|
||||
if params.RestIsOn {
|
||||
var restListens = []*serverconfigs.NetworkAddressConfig{}
|
||||
err = json.Unmarshal(params.RestListensJSON, &restListens)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if len(restListens) == 0 {
|
||||
this.Fail("请至少添加一个HTTP API监听端口")
|
||||
return
|
||||
}
|
||||
for _, addr := range restListens {
|
||||
if addr.Protocol.IsHTTPFamily() {
|
||||
restHTTPConfig.IsOn = true
|
||||
restHTTPConfig.Listen = append(restHTTPConfig.Listen, addr)
|
||||
} else if addr.Protocol.IsHTTPSFamily() {
|
||||
restHTTPSConfig.IsOn = true
|
||||
restHTTPSConfig.Listen = append(restHTTPSConfig.Listen, addr)
|
||||
}
|
||||
}
|
||||
|
||||
// 是否有端口冲突
|
||||
var rpcAddresses = []string{}
|
||||
for _, listen := range listens {
|
||||
err := listen.Init()
|
||||
if err != nil {
|
||||
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
|
||||
return
|
||||
}
|
||||
rpcAddresses = append(rpcAddresses, listen.Addresses()...)
|
||||
}
|
||||
|
||||
for _, listen := range restListens {
|
||||
err := listen.Init()
|
||||
if err != nil {
|
||||
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
|
||||
return
|
||||
}
|
||||
for _, address := range listen.Addresses() {
|
||||
if lists.ContainsString(rpcAddresses, address) {
|
||||
this.Fail("HTTP API地址 '" + address + "' 和 GRPC地址冲突,请修改后提交")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 证书
|
||||
var certIds = []int64{}
|
||||
if len(params.CertIdsJSON) > 0 {
|
||||
err = json.Unmarshal(params.CertIdsJSON, &certIds)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if ((httpsConfig.IsOn && len(httpsConfig.Listen) > 0) || (restHTTPSConfig.IsOn && len(httpsConfig.Listen) > 0)) && len(certIds) == 0 {
|
||||
this.Fail("请添加至少一个证书")
|
||||
}
|
||||
|
||||
var certRefs = []*sslconfigs.SSLCertRef{}
|
||||
for _, certId := range certIds {
|
||||
certRefs = append(certRefs, &sslconfigs.SSLCertRef{
|
||||
IsOn: true,
|
||||
CertId: certId,
|
||||
})
|
||||
}
|
||||
certRefsJSON, err := json.Marshal(certRefs)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 创建策略
|
||||
if len(certIds) > 0 {
|
||||
sslPolicyCreateResp, err := this.RPC().SSLPolicyRPC().CreateSSLPolicy(this.AdminContext(), &pb.CreateSSLPolicyRequest{
|
||||
SslCertsJSON: certRefsJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
sslPolicyId := sslPolicyCreateResp.SslPolicyId
|
||||
httpsConfig.SSLPolicyRef = &sslconfigs.SSLPolicyRef{
|
||||
IsOn: true,
|
||||
SSLPolicyId: sslPolicyId,
|
||||
}
|
||||
restHTTPSConfig.SSLPolicyRef = &sslconfigs.SSLPolicyRef{
|
||||
IsOn: true,
|
||||
SSLPolicyId: sslPolicyId,
|
||||
}
|
||||
}
|
||||
|
||||
// 访问地址
|
||||
var accessAddrs = []*serverconfigs.NetworkAddressConfig{}
|
||||
err = json.Unmarshal(params.AccessAddrsJSON, &accessAddrs)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if len(accessAddrs) == 0 {
|
||||
this.Fail("请添加至少一个外部访问地址")
|
||||
}
|
||||
|
||||
httpJSON, err := json.Marshal(httpConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
httpsJSON, err := json.Marshal(httpsConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
restHTTPJSON, err := json.Marshal(restHTTPConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
restHTTPSJSON, err := json.Marshal(restHTTPSConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
createResp, err := this.RPC().APINodeRPC().CreateAPINode(this.AdminContext(), &pb.CreateAPINodeRequest{
|
||||
Name: params.Name,
|
||||
Description: params.Description,
|
||||
HttpJSON: httpJSON,
|
||||
HttpsJSON: httpsJSON,
|
||||
RestIsOn: params.RestIsOn,
|
||||
RestHTTPJSON: restHTTPJSON,
|
||||
RestHTTPSJSON: restHTTPSJSON,
|
||||
AccessAddrsJSON: params.AccessAddrsJSON,
|
||||
IsOn: params.IsOn,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 创建日志
|
||||
defer this.CreateLogInfo(codes.APINode_LogCreateAPINode, createResp.ApiNodeId)
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Helper struct {
|
||||
}
|
||||
|
||||
func NewHelper() *Helper {
|
||||
return &Helper{}
|
||||
}
|
||||
|
||||
func (this *Helper) BeforeAction(action *actions.ActionObject) (goNext bool) {
|
||||
if action.Request.Method != http.MethodGet {
|
||||
return true
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"time"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "index")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
NodeId int64
|
||||
}) {
|
||||
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var node = nodeResp.ApiNode
|
||||
if node == nil {
|
||||
this.NotFound("apiNode", params.NodeId)
|
||||
return
|
||||
}
|
||||
|
||||
// 监听地址
|
||||
var hasHTTPS = false
|
||||
var httpConfig = &serverconfigs.HTTPProtocolConfig{}
|
||||
if len(node.HttpJSON) > 0 {
|
||||
err = json.Unmarshal(node.HttpJSON, httpConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
var httpsConfig = &serverconfigs.HTTPSProtocolConfig{}
|
||||
if len(node.HttpsJSON) > 0 {
|
||||
err = json.Unmarshal(node.HttpsJSON, httpsConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
hasHTTPS = len(httpsConfig.Listen) > 0
|
||||
}
|
||||
|
||||
// 监听地址
|
||||
var listens = []*serverconfigs.NetworkAddressConfig{}
|
||||
listens = append(listens, httpConfig.Listen...)
|
||||
listens = append(listens, httpsConfig.Listen...)
|
||||
|
||||
// 证书信息
|
||||
var certs = []*sslconfigs.SSLCertConfig{}
|
||||
if httpsConfig.SSLPolicyRef != nil && httpsConfig.SSLPolicyRef.SSLPolicyId > 0 {
|
||||
sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.AdminContext(), &pb.FindEnabledSSLPolicyConfigRequest{
|
||||
SslPolicyId: httpsConfig.SSLPolicyRef.SSLPolicyId,
|
||||
IgnoreData: true,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var sslPolicyConfigJSON = sslPolicyConfigResp.SslPolicyJSON
|
||||
if len(sslPolicyConfigJSON) > 0 {
|
||||
var sslPolicy = &sslconfigs.SSLPolicy{}
|
||||
err = json.Unmarshal(sslPolicyConfigJSON, sslPolicy)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
certs = sslPolicy.Certs
|
||||
}
|
||||
}
|
||||
|
||||
// 访问地址
|
||||
var accessAddrs = []*serverconfigs.NetworkAddressConfig{}
|
||||
if len(node.AccessAddrsJSON) > 0 {
|
||||
err = json.Unmarshal(node.AccessAddrsJSON, &accessAddrs)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Rest地址
|
||||
var restAccessAddrs = []*serverconfigs.NetworkAddressConfig{}
|
||||
if node.RestIsOn {
|
||||
if len(node.RestHTTPJSON) > 0 {
|
||||
var httpConfig = &serverconfigs.HTTPProtocolConfig{}
|
||||
err = json.Unmarshal(node.RestHTTPJSON, httpConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if httpConfig.IsOn && len(httpConfig.Listen) > 0 {
|
||||
restAccessAddrs = append(restAccessAddrs, httpConfig.Listen...)
|
||||
}
|
||||
}
|
||||
|
||||
if len(node.RestHTTPSJSON) > 0 {
|
||||
var httpsConfig = &serverconfigs.HTTPSProtocolConfig{}
|
||||
err = json.Unmarshal(node.RestHTTPSJSON, httpsConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if httpsConfig.IsOn && len(httpsConfig.Listen) > 0 {
|
||||
restAccessAddrs = append(restAccessAddrs, httpsConfig.Listen...)
|
||||
}
|
||||
|
||||
if !hasHTTPS {
|
||||
hasHTTPS = len(httpsConfig.Listen) > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 状态
|
||||
var status = &nodeconfigs.NodeStatus{}
|
||||
var statusIsValid = false
|
||||
this.Data["newVersion"] = ""
|
||||
if len(node.StatusJSON) > 0 {
|
||||
err = json.Unmarshal(node.StatusJSON, &status)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if status.UpdatedAt >= time.Now().Unix()-300 {
|
||||
statusIsValid = true
|
||||
|
||||
// 是否为新版本
|
||||
if stringutil.VersionCompare(status.BuildVersion, teaconst.APINodeVersion) < 0 {
|
||||
this.Data["newVersion"] = teaconst.APINodeVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["node"] = maps.Map{
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
"description": node.Description,
|
||||
"isOn": node.IsOn,
|
||||
"listens": listens,
|
||||
"accessAddrs": accessAddrs,
|
||||
"restIsOn": node.RestIsOn,
|
||||
"restAccessAddrs": restAccessAddrs,
|
||||
"hasHTTPS": hasHTTPS,
|
||||
"certs": certs,
|
||||
"isPrimary": node.IsPrimary,
|
||||
"statusIsValid": statusIsValid,
|
||||
"status": maps.Map{
|
||||
"isActive": status.IsActive,
|
||||
"updatedAt": status.UpdatedAt,
|
||||
"hostname": status.Hostname,
|
||||
"cpuUsage": status.CPUUsage,
|
||||
"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
|
||||
"memUsage": status.MemoryUsage,
|
||||
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
|
||||
"connectionCount": status.ConnectionCount,
|
||||
"buildVersion": status.BuildVersion,
|
||||
"cpuPhysicalCount": status.CPUPhysicalCount,
|
||||
"cpuLogicalCount": status.CPULogicalCount,
|
||||
"load1m": numberutils.FormatFloat2(status.Load1m),
|
||||
"load5m": numberutils.FormatFloat2(status.Load5m),
|
||||
"load15m": numberutils.FormatFloat2(status.Load15m),
|
||||
"cacheTotalDiskSize": numberutils.FormatBytes(status.CacheTotalDiskSize),
|
||||
"cacheTotalMemorySize": numberutils.FormatBytes(status.CacheTotalMemorySize),
|
||||
"exePath": status.ExePath,
|
||||
},
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)).
|
||||
Helper(settingutils.NewAdvancedHelper("apiNodes")).
|
||||
Prefix("/settings/api/node").
|
||||
|
||||
// 这里不受Helper的约束
|
||||
GetPost("/createAddrPopup", new(CreateAddrPopupAction)).
|
||||
GetPost("/updateAddrPopup", new(UpdateAddrPopupAction)).
|
||||
|
||||
// 节点相关
|
||||
Helper(NewHelper()).
|
||||
Get("", new(IndexAction)).
|
||||
GetPost("/update", new(UpdateAction)).
|
||||
Get("/install", new(InstallAction)).
|
||||
Get("/logs", new(LogsAction)).
|
||||
GetPost("/upgradePopup", new(UpgradePopupAction)).
|
||||
Post("/upgradeCheck", new(UpgradeCheckAction)).
|
||||
|
||||
//
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"gopkg.in/yaml.v3"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type InstallAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *InstallAction) Init() {
|
||||
this.Nav("", "", "install")
|
||||
}
|
||||
|
||||
func (this *InstallAction) RunGet(params struct {
|
||||
NodeId int64
|
||||
}) {
|
||||
// API节点信息
|
||||
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var node = nodeResp.ApiNode
|
||||
if node == nil {
|
||||
this.NotFound("apiNode", params.NodeId)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["node"] = maps.Map{
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
"uniqueId": node.UniqueId,
|
||||
"secret": node.Secret,
|
||||
}
|
||||
|
||||
// 数据库配置
|
||||
var dbConfigMap = maps.Map{
|
||||
"config": "",
|
||||
"error": "",
|
||||
"isNotFound": false,
|
||||
}
|
||||
var dbConfigFile = Tea.ConfigFile("api_db.yaml")
|
||||
data, err := os.ReadFile(dbConfigFile)
|
||||
dbConfigMap["config"] = string(data)
|
||||
var config = &configs.SimpleDBConfig{}
|
||||
err = yaml.Unmarshal(data, config)
|
||||
if err == nil && len(config.Host) > 0 {
|
||||
var password = config.Password
|
||||
if len(password) > 0 {
|
||||
password = strings.Repeat("*", len(password))
|
||||
}
|
||||
config.Password = password
|
||||
tmpConf, _ := yaml.Marshal(config)
|
||||
|
||||
dbConfigMap["config"] = string(tmpConf)
|
||||
} else {
|
||||
var config = &dbs.Config{}
|
||||
err := yaml.Unmarshal(data, config)
|
||||
if err != nil {
|
||||
this.Data["error"] = "parse config file failed: api_db.yaml: " + err.Error()
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
this.Data["error"] = "parse config file failed: api_db.yaml: " + err.Error()
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
|
||||
if config.DBs == nil {
|
||||
this.Data["error"] = "no database configured in config file: api_db.yaml"
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
|
||||
for key, db := range config.DBs {
|
||||
var dbConfig *dbs.DBConfig
|
||||
dbConfig = db
|
||||
|
||||
if dbConfig == nil {
|
||||
this.Data["error"] = "no database configured in config file: api_db.yaml"
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
var dsn = dbConfig.Dsn
|
||||
cfg, err := mysql.ParseDSN(dsn)
|
||||
if err != nil {
|
||||
this.Data["error"] = "parse dsn error: " + err.Error()
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
var password = cfg.Passwd
|
||||
if len(password) > 0 {
|
||||
password = strings.Repeat("*", len(password))
|
||||
}
|
||||
|
||||
var host = cfg.Addr
|
||||
var port = "3306"
|
||||
var index = strings.LastIndex(host, ":")
|
||||
if index > 0 {
|
||||
port = host[index+1:]
|
||||
host = host[:index]
|
||||
}
|
||||
dbConfig.Dsn = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&timeout=30s", cfg.User, password, host, port, cfg.DBName)
|
||||
config.DBs[key] = dbConfig
|
||||
//fmt.Println(host, port, cfg.User, password, cfg.DBName)
|
||||
}
|
||||
tmpConf, _ := yaml.Marshal(config)
|
||||
dbConfigMap["config"] = string(tmpConf)
|
||||
|
||||
}
|
||||
if err != nil {
|
||||
dbConfigMap["error"] = err.Error()
|
||||
dbConfigMap["isNotFound"] = os.IsNotExist(err)
|
||||
}
|
||||
this.Data["dbConfig"] = dbConfigMap
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
)
|
||||
|
||||
type LogsAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *LogsAction) Init() {
|
||||
this.Nav("", "node", "log")
|
||||
this.SecondMenu("nodes")
|
||||
}
|
||||
|
||||
func (this *LogsAction) RunGet(params struct {
|
||||
NodeId int64
|
||||
|
||||
DayFrom string
|
||||
DayTo string
|
||||
Keyword string
|
||||
Level string
|
||||
}) {
|
||||
this.Data["nodeId"] = params.NodeId
|
||||
this.Data["dayFrom"] = params.DayFrom
|
||||
this.Data["dayTo"] = params.DayTo
|
||||
this.Data["keyword"] = params.Keyword
|
||||
this.Data["level"] = params.Level
|
||||
|
||||
apiNodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
apiNode := apiNodeResp.ApiNode
|
||||
if apiNode == nil {
|
||||
this.NotFound("apiNode", params.NodeId)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["node"] = maps.Map{
|
||||
"id": apiNode.Id,
|
||||
"name": apiNode.Name,
|
||||
}
|
||||
|
||||
countResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
|
||||
Role: nodeconfigs.NodeRoleAPI,
|
||||
NodeId: params.NodeId,
|
||||
DayFrom: params.DayFrom,
|
||||
DayTo: params.DayTo,
|
||||
Keyword: params.Keyword,
|
||||
Level: params.Level,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var count = countResp.Count
|
||||
var page = this.NewPage(count, 20)
|
||||
|
||||
logsResp, err := this.RPC().NodeLogRPC().ListNodeLogs(this.AdminContext(), &pb.ListNodeLogsRequest{
|
||||
NodeId: params.NodeId,
|
||||
Role: nodeconfigs.NodeRoleAPI,
|
||||
DayFrom: params.DayFrom,
|
||||
DayTo: params.DayTo,
|
||||
Keyword: params.Keyword,
|
||||
Level: params.Level,
|
||||
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var logs = []maps.Map{}
|
||||
for _, log := range logsResp.NodeLogs {
|
||||
logs = append(logs, maps.Map{
|
||||
"tag": log.Tag,
|
||||
"description": log.Description,
|
||||
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", log.CreatedAt),
|
||||
"level": log.Level,
|
||||
"isToday": timeutil.FormatTime("Y-m-d", log.CreatedAt) == timeutil.Format("Y-m-d"),
|
||||
})
|
||||
}
|
||||
this.Data["logs"] = logs
|
||||
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type UpdateAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdateAction) Init() {
|
||||
this.Nav("", "", "update")
|
||||
}
|
||||
|
||||
func (this *UpdateAction) RunGet(params struct {
|
||||
NodeId int64
|
||||
}) {
|
||||
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{
|
||||
ApiNodeId: params.NodeId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var node = nodeResp.ApiNode
|
||||
if node == nil {
|
||||
this.WriteString("要操作的节点不存在")
|
||||
return
|
||||
}
|
||||
|
||||
var httpConfig = &serverconfigs.HTTPProtocolConfig{}
|
||||
if len(node.HttpJSON) > 0 {
|
||||
err = json.Unmarshal(node.HttpJSON, httpConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
var httpsConfig = &serverconfigs.HTTPSProtocolConfig{}
|
||||
if len(node.HttpsJSON) > 0 {
|
||||
err = json.Unmarshal(node.HttpsJSON, httpsConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 监听地址
|
||||
var listens = []*serverconfigs.NetworkAddressConfig{}
|
||||
listens = append(listens, httpConfig.Listen...)
|
||||
listens = append(listens, httpsConfig.Listen...)
|
||||
|
||||
var restHTTPConfig = &serverconfigs.HTTPProtocolConfig{}
|
||||
if len(node.RestHTTPJSON) > 0 {
|
||||
err = json.Unmarshal(node.RestHTTPJSON, restHTTPConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
var restHTTPSConfig = &serverconfigs.HTTPSProtocolConfig{}
|
||||
if len(node.RestHTTPSJSON) > 0 {
|
||||
err = json.Unmarshal(node.RestHTTPSJSON, restHTTPSConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 监听地址
|
||||
var restListens = []*serverconfigs.NetworkAddressConfig{}
|
||||
restListens = append(restListens, restHTTPConfig.Listen...)
|
||||
restListens = append(restListens, restHTTPSConfig.Listen...)
|
||||
|
||||
// 证书信息
|
||||
var certs = []*sslconfigs.SSLCertConfig{}
|
||||
var sslPolicyId = int64(0)
|
||||
if httpsConfig.SSLPolicyRef != nil && httpsConfig.SSLPolicyRef.SSLPolicyId > 0 {
|
||||
sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.AdminContext(), &pb.FindEnabledSSLPolicyConfigRequest{
|
||||
SslPolicyId: httpsConfig.SSLPolicyRef.SSLPolicyId,
|
||||
IgnoreData: true,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var sslPolicyConfigJSON = sslPolicyConfigResp.SslPolicyJSON
|
||||
if len(sslPolicyConfigJSON) > 0 {
|
||||
sslPolicyId = httpsConfig.SSLPolicyRef.SSLPolicyId
|
||||
|
||||
sslPolicy := &sslconfigs.SSLPolicy{}
|
||||
err = json.Unmarshal(sslPolicyConfigJSON, sslPolicy)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
certs = sslPolicy.Certs
|
||||
}
|
||||
}
|
||||
|
||||
var accessAddrs = []*serverconfigs.NetworkAddressConfig{}
|
||||
if len(node.AccessAddrsJSON) > 0 {
|
||||
err = json.Unmarshal(node.AccessAddrsJSON, &accessAddrs)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["node"] = maps.Map{
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
"description": node.Description,
|
||||
"isOn": node.IsOn,
|
||||
"listens": listens,
|
||||
"restIsOn": node.RestIsOn,
|
||||
"restListens": restListens,
|
||||
"certs": certs,
|
||||
"sslPolicyId": sslPolicyId,
|
||||
"accessAddrs": accessAddrs,
|
||||
"isPrimary": node.IsPrimary,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
// RunPost 保存基础设置
|
||||
func (this *UpdateAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
Name string
|
||||
SslPolicyId int64
|
||||
ListensJSON []byte
|
||||
RestIsOn bool
|
||||
RestListensJSON []byte
|
||||
CertIdsJSON []byte
|
||||
AccessAddrsJSON []byte
|
||||
Description string
|
||||
IsOn bool
|
||||
IsPrimary bool
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入API节点名称")
|
||||
|
||||
var httpConfig = &serverconfigs.HTTPProtocolConfig{}
|
||||
var httpsConfig = &serverconfigs.HTTPSProtocolConfig{}
|
||||
|
||||
// 监听地址
|
||||
var listens = []*serverconfigs.NetworkAddressConfig{}
|
||||
err := json.Unmarshal(params.ListensJSON, &listens)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if len(listens) == 0 {
|
||||
this.Fail("请添加至少一个进程监听地址")
|
||||
}
|
||||
for _, addr := range listens {
|
||||
if addr.Protocol.IsHTTPFamily() {
|
||||
httpConfig.IsOn = true
|
||||
httpConfig.Listen = append(httpConfig.Listen, addr)
|
||||
} else if addr.Protocol.IsHTTPSFamily() {
|
||||
httpsConfig.IsOn = true
|
||||
httpsConfig.Listen = append(httpsConfig.Listen, addr)
|
||||
}
|
||||
}
|
||||
|
||||
// Rest监听地址
|
||||
var restHTTPConfig = &serverconfigs.HTTPProtocolConfig{}
|
||||
var restHTTPSConfig = &serverconfigs.HTTPSProtocolConfig{}
|
||||
if params.RestIsOn {
|
||||
var restListens = []*serverconfigs.NetworkAddressConfig{}
|
||||
err = json.Unmarshal(params.RestListensJSON, &restListens)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if len(restListens) == 0 {
|
||||
this.Fail("请至少添加一个HTTP API监听端口")
|
||||
return
|
||||
}
|
||||
for _, addr := range restListens {
|
||||
if addr.Protocol.IsHTTPFamily() {
|
||||
restHTTPConfig.IsOn = true
|
||||
restHTTPConfig.Listen = append(restHTTPConfig.Listen, addr)
|
||||
} else if addr.Protocol.IsHTTPSFamily() {
|
||||
restHTTPSConfig.IsOn = true
|
||||
restHTTPSConfig.Listen = append(restHTTPSConfig.Listen, addr)
|
||||
}
|
||||
}
|
||||
|
||||
// 是否有端口冲突
|
||||
var rpcAddresses = []string{}
|
||||
for _, listen := range listens {
|
||||
err := listen.Init()
|
||||
if err != nil {
|
||||
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
|
||||
return
|
||||
}
|
||||
rpcAddresses = append(rpcAddresses, listen.Addresses()...)
|
||||
}
|
||||
|
||||
for _, listen := range restListens {
|
||||
err := listen.Init()
|
||||
if err != nil {
|
||||
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
|
||||
return
|
||||
}
|
||||
for _, address := range listen.Addresses() {
|
||||
if lists.ContainsString(rpcAddresses, address) {
|
||||
this.Fail("HTTP API地址 '" + address + "' 和 GRPC地址冲突,请修改后提交")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 证书
|
||||
var certIds = []int64{}
|
||||
if len(params.CertIdsJSON) > 0 {
|
||||
err = json.Unmarshal(params.CertIdsJSON, &certIds)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if ((httpsConfig.IsOn && len(httpsConfig.Listen) > 0) || (restHTTPSConfig.IsOn && len(httpsConfig.Listen) > 0)) && len(certIds) == 0 {
|
||||
this.Fail("请添加至少一个证书")
|
||||
}
|
||||
|
||||
var certRefs = []*sslconfigs.SSLCertRef{}
|
||||
for _, certId := range certIds {
|
||||
certRefs = append(certRefs, &sslconfigs.SSLCertRef{
|
||||
IsOn: true,
|
||||
CertId: certId,
|
||||
})
|
||||
}
|
||||
certRefsJSON, err := json.Marshal(certRefs)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 创建策略
|
||||
var sslPolicyId = params.SslPolicyId
|
||||
if sslPolicyId == 0 {
|
||||
if len(certIds) > 0 {
|
||||
sslPolicyCreateResp, err := this.RPC().SSLPolicyRPC().CreateSSLPolicy(this.AdminContext(), &pb.CreateSSLPolicyRequest{
|
||||
SslCertsJSON: certRefsJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
sslPolicyId = sslPolicyCreateResp.SslPolicyId
|
||||
}
|
||||
} else {
|
||||
_, err = this.RPC().SSLPolicyRPC().UpdateSSLPolicy(this.AdminContext(), &pb.UpdateSSLPolicyRequest{
|
||||
SslPolicyId: sslPolicyId,
|
||||
SslCertsJSON: certRefsJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
httpsConfig.SSLPolicyRef = &sslconfigs.SSLPolicyRef{
|
||||
IsOn: true,
|
||||
SSLPolicyId: sslPolicyId,
|
||||
}
|
||||
restHTTPSConfig.SSLPolicyRef = &sslconfigs.SSLPolicyRef{
|
||||
IsOn: true,
|
||||
SSLPolicyId: sslPolicyId,
|
||||
}
|
||||
|
||||
// 访问地址
|
||||
var accessAddrs = []*serverconfigs.NetworkAddressConfig{}
|
||||
err = json.Unmarshal(params.AccessAddrsJSON, &accessAddrs)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if len(accessAddrs) == 0 {
|
||||
this.Fail("请添加至少一个外部访问地址")
|
||||
}
|
||||
|
||||
httpJSON, err := json.Marshal(httpConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
httpsJSON, err := json.Marshal(httpsConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
restHTTPJSON, err := json.Marshal(restHTTPConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
restHTTPSJSON, err := json.Marshal(restHTTPSConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = this.RPC().APINodeRPC().UpdateAPINode(this.AdminContext(), &pb.UpdateAPINodeRequest{
|
||||
ApiNodeId: params.NodeId,
|
||||
Name: params.Name,
|
||||
Description: params.Description,
|
||||
HttpJSON: httpJSON,
|
||||
HttpsJSON: httpsJSON,
|
||||
RestIsOn: params.RestIsOn,
|
||||
RestHTTPJSON: restHTTPJSON,
|
||||
RestHTTPSJSON: restHTTPSJSON,
|
||||
AccessAddrsJSON: params.AccessAddrsJSON,
|
||||
IsOn: params.IsOn,
|
||||
IsPrimary: params.IsPrimary,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 创建日志
|
||||
defer this.CreateLogInfo(codes.APINode_LogUpdateAPINode, params.NodeId)
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type UpdateAddrPopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdateAddrPopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdateAddrPopupAction) RunGet(params struct{}) {
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdateAddrPopupAction) RunPost(params struct {
|
||||
Protocol string
|
||||
Addr string
|
||||
Must *actions.Must
|
||||
}) {
|
||||
params.Must.
|
||||
Field("addr", params.Addr).
|
||||
Require("请输入访问地址")
|
||||
|
||||
// 兼容URL
|
||||
if regexp.MustCompile(`^(?i)(http|https)://`).MatchString(params.Addr) {
|
||||
u, err := url.Parse(params.Addr)
|
||||
if err != nil {
|
||||
this.FailField("addr", "错误的访问地址,不需要添加http://或https://")
|
||||
}
|
||||
params.Addr = u.Host
|
||||
}
|
||||
|
||||
// 自动添加端口
|
||||
if !strings.Contains(params.Addr, ":") {
|
||||
switch params.Protocol {
|
||||
case "http":
|
||||
params.Addr += ":80"
|
||||
case "https":
|
||||
params.Addr += ":443"
|
||||
}
|
||||
}
|
||||
|
||||
host, port, err := net.SplitHostPort(params.Addr)
|
||||
if err != nil {
|
||||
this.FailField("addr", "错误的访问地址")
|
||||
}
|
||||
|
||||
addrConfig := &serverconfigs.NetworkAddressConfig{
|
||||
Protocol: serverconfigs.Protocol(params.Protocol),
|
||||
Host: host,
|
||||
PortRange: port,
|
||||
}
|
||||
this.Data["addr"] = addrConfig
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package node
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
// UpgradeCheckAction 检查升级结果
|
||||
type UpgradeCheckAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpgradeCheckAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpgradeCheckAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
}) {
|
||||
this.Data["isOk"] = false
|
||||
|
||||
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
|
||||
if err != nil {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
var node = nodeResp.ApiNode
|
||||
if node == nil || len(node.AccessAddrs) == 0 {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
apiConfig, err := configs.LoadAPIConfig()
|
||||
if err != nil {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
var newAPIConfig = apiConfig.Clone()
|
||||
newAPIConfig.RPCEndpoints = node.AccessAddrs
|
||||
rpcClient, err := rpc.NewRPCClient(newAPIConfig, false)
|
||||
if err != nil {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
versionResp, err := rpcClient.APINodeRPC().FindCurrentAPINodeVersion(rpcClient.Context(0), &pb.FindCurrentAPINodeVersionRequest{})
|
||||
if err != nil {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
if versionResp.Version != teaconst.Version {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["isOk"] = true
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package node
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/apinodeutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type UpgradePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpgradePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpgradePopupAction) RunGet(params struct {
|
||||
NodeId int64
|
||||
}) {
|
||||
this.Data["nodeId"] = params.NodeId
|
||||
this.Data["nodeName"] = ""
|
||||
this.Data["currentVersion"] = ""
|
||||
this.Data["latestVersion"] = ""
|
||||
this.Data["result"] = ""
|
||||
this.Data["resultIsOk"] = true
|
||||
this.Data["canUpgrade"] = false
|
||||
this.Data["isUpgrading"] = false
|
||||
|
||||
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var node = nodeResp.ApiNode
|
||||
if node == nil {
|
||||
this.Data["result"] = "要升级的节点不存在"
|
||||
this.Data["resultIsOk"] = false
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
this.Data["nodeName"] = node.Name + " / [" + strings.Join(node.AccessAddrs, ", ") + "]"
|
||||
|
||||
// 节点状态
|
||||
var status = &nodeconfigs.NodeStatus{}
|
||||
if len(node.StatusJSON) > 0 {
|
||||
err = json.Unmarshal(node.StatusJSON, &status)
|
||||
if err != nil {
|
||||
this.ErrorPage(errors.New("decode status failed: " + err.Error()))
|
||||
return
|
||||
}
|
||||
this.Data["currentVersion"] = status.BuildVersion
|
||||
} else {
|
||||
this.Data["result"] = "无法检测到节点当前版本"
|
||||
this.Data["resultIsOk"] = false
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
this.Data["latestVersion"] = teaconst.APINodeVersion
|
||||
|
||||
if status.IsActive && len(status.BuildVersion) > 0 {
|
||||
canUpgrade, reason := apinodeutils.CanUpgrade(status.BuildVersion, status.OS, status.Arch)
|
||||
if !canUpgrade {
|
||||
this.Data["result"] = reason
|
||||
this.Data["resultIsOk"] = false
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
this.Data["canUpgrade"] = true
|
||||
this.Data["result"] = "等待升级"
|
||||
this.Data["resultIsOk"] = true
|
||||
} else {
|
||||
this.Data["result"] = "当前节点非连接状态无法远程升级"
|
||||
this.Data["resultIsOk"] = false
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
|
||||
// 是否正在升级
|
||||
var oldUpgrader = apinodeutils.SharedManager.FindUpgrader(params.NodeId)
|
||||
if oldUpgrader != nil {
|
||||
this.Data["result"] = "正在升级中..."
|
||||
this.Data["resultIsOk"] = false
|
||||
this.Data["isUpgrading"] = true
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpgradePopupAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
var manager = apinodeutils.SharedManager
|
||||
|
||||
var oldUpgrader = manager.FindUpgrader(params.NodeId)
|
||||
if oldUpgrader != nil {
|
||||
this.Fail("正在升级中,无需重复提交 ...")
|
||||
return
|
||||
}
|
||||
|
||||
var upgrader = apinodeutils.NewUpgrader(params.NodeId)
|
||||
manager.AddUpgrader(upgrader)
|
||||
defer func() {
|
||||
manager.RemoveUpgrader(upgrader)
|
||||
}()
|
||||
|
||||
err := upgrader.Upgrade()
|
||||
if err != nil {
|
||||
this.Fail("升级失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
//go:build plus
|
||||
|
||||
package authority
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
plusutils "github.com/TeaOSLab/EdgePlus/pkg/utils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
type ActivateAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *ActivateAction) Init() {
|
||||
this.Nav("", "", "activate")
|
||||
}
|
||||
|
||||
func (this *ActivateAction) RunGet(params struct{}) {
|
||||
// 选中左侧菜单
|
||||
this.Data["teaSubMenu"] = "authority"
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *ActivateAction) RunPost(params struct {
|
||||
Key string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
requestCode, err := plusutils.GenerateRequestCode()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(params.Key) == 0 {
|
||||
this.FailField("key", "请输入注册码")
|
||||
}
|
||||
|
||||
// 本地检查
|
||||
key, err := plusutils.DecodeKey([]byte(params.Key))
|
||||
if err != nil {
|
||||
this.Fail("请输入正确的注册码")
|
||||
return
|
||||
}
|
||||
if key.Method != plusutils.MethodRemote {
|
||||
if len(key.RequestCode) > 0 { // 使用注册码激活
|
||||
ok, errorCode := plusutils.ValidateRequestCode(key.RequestCode)
|
||||
if !ok {
|
||||
this.Fail("无法在当前服务器上使用此注册码,请联系软件开发者,错误代号:" + errorCode)
|
||||
return
|
||||
}
|
||||
} else if len(key.MacAddresses) > 0 { // 使用MAC地址激活
|
||||
for _, address := range key.MacAddresses {
|
||||
if !plus.ValidateMac(address) {
|
||||
this.Fail("无法在当前服务器上使用此注册码,请联系软件开发者")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := this.RPC().AuthorityKeyRPC().ValidateAuthorityKey(this.AdminContext(), &pb.ValidateAuthorityKeyRequest{
|
||||
Key: params.Key,
|
||||
RequestCode: requestCode,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsOk {
|
||||
_, err := this.RPC().AuthorityKeyRPC().UpdateAuthorityKey(this.AdminContext(), &pb.UpdateAuthorityKeyRequest{
|
||||
Value: params.Key,
|
||||
RequestCode: requestCode,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 显示菜单
|
||||
_ = configloaders.ReloadAdminUIConfig()
|
||||
|
||||
this.Success()
|
||||
} else {
|
||||
this.FailField("key", "无法激活:"+resp.Error)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
//go:build plus
|
||||
|
||||
package authority
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
plusutils "github.com/TeaOSLab/EdgePlus/pkg/utils"
|
||||
)
|
||||
|
||||
type ApplyAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *ApplyAction) Init() {
|
||||
this.Nav("", "", "apply")
|
||||
}
|
||||
|
||||
func (this *ApplyAction) RunGet(params struct{}) {
|
||||
// 选中左侧菜单
|
||||
this.Data["teaSubMenu"] = "authority"
|
||||
|
||||
requestCode, err := plusutils.GenerateRequestCode()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["requestCode"] = requestCode
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
//go:build plus
|
||||
|
||||
package authority
|
||||
|
||||
import (
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
plusutils "github.com/TeaOSLab/EdgePlus/pkg/utils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("authority", "", "index")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
// 选中左侧菜单
|
||||
this.Data["teaSubMenu"] = "authority"
|
||||
|
||||
this.Data["key"] = nil
|
||||
this.Data["plusErr"] = ""
|
||||
|
||||
keyResp, err := this.RPC().AuthorityKeyRPC().ReadAuthorityKey(this.AdminContext(), &pb.ReadAuthorityKeyRequest{})
|
||||
if err != nil {
|
||||
this.Data["plusErr"] = err.Error()
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
var keyMap maps.Map = nil
|
||||
teaconst.IsPlus = false
|
||||
plus.UpdateComponents("", []string{})
|
||||
plus.ErrString = ""
|
||||
|
||||
var key = keyResp.AuthorityKey
|
||||
if key != nil {
|
||||
if len(key.MacAddresses) == 0 {
|
||||
key.MacAddresses = []string{}
|
||||
}
|
||||
if len(key.Components) == 0 {
|
||||
key.Components = []string{}
|
||||
}
|
||||
|
||||
var isActive = len(key.DayTo) > 0 && key.DayTo >= timeutil.Format("Y-m-d")
|
||||
var method = ""
|
||||
if isActive {
|
||||
nativeKey, err := plusutils.DecodeKey([]byte(key.Value))
|
||||
var isOk = true
|
||||
if err != nil {
|
||||
teaconst.IsPlus = false
|
||||
} else if len(nativeKey.RequestCode) > 0 { // 使用申请码
|
||||
method = nativeKey.Method
|
||||
isOk, _ = plusutils.ValidateRequestCode(nativeKey.RequestCode)
|
||||
} else if len(nativeKey.MacAddresses) > 0 { // MAC地址
|
||||
for _, address := range nativeKey.MacAddresses {
|
||||
if !plus.ValidateMac(address) {
|
||||
isOk = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if isOk {
|
||||
teaconst.IsPlus = true
|
||||
plus.UpdateComponents(key.Edition, key.Components)
|
||||
} else {
|
||||
teaconst.IsPlus = false
|
||||
plus.UpdateComponents("", []string{})
|
||||
plus.ErrString = "当前管理平台所在服务器环境与商业版认证分发时发生了变化,因此无法使用商业版功能。如果你正在迁移管理平台,请联系开发者重新申请新的注册码。"
|
||||
}
|
||||
}
|
||||
|
||||
var isExpiring = isActive && key.DayTo < timeutil.Format("Y-m-d", time.Now().AddDate(0, 0, 7))
|
||||
|
||||
// 终身授权
|
||||
var dayTo = key.DayTo
|
||||
if dayTo >= "2050-01-01" {
|
||||
dayTo = "终身授权"
|
||||
}
|
||||
|
||||
keyMap = maps.Map{
|
||||
"dayFrom": key.DayFrom,
|
||||
"dayTo": dayTo,
|
||||
"hostname": key.Hostname,
|
||||
"company": key.Company,
|
||||
"nodes": key.Nodes,
|
||||
"components": key.Components,
|
||||
"editionName": plusutils.EditionName(key.Edition),
|
||||
"isExpired": !isActive,
|
||||
"isExpiring": isExpiring,
|
||||
"updatedTime": timeutil.FormatTime("Y-m-d H:i:s", keyResp.AuthorityKey.UpdatedAt),
|
||||
"method": method,
|
||||
}
|
||||
}
|
||||
this.Data["key"] = keyMap
|
||||
|
||||
this.Data["plusErr"] = plus.ErrString
|
||||
|
||||
// 配额
|
||||
quotaResp, err := this.RPC().AuthorityKeyRPC().FindAuthorityQuota(this.AdminContext(), &pb.FindAuthorityQuotaRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["quota"] = maps.Map{
|
||||
"maxNodes": quotaResp.MaxNodes,
|
||||
"countNodes": quotaResp.CountNodes,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
//go:build plus
|
||||
|
||||
package authority
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)).
|
||||
Helper(settingutils.NewAdvancedHelper("authority")).
|
||||
Prefix("/settings/authority").
|
||||
Get("", new(IndexAction)).
|
||||
Get("/apply", new(ApplyAction)).
|
||||
GetPost("/activate", new(ActivateAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package backup
|
||||
|
||||
import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package backup
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)).
|
||||
Helper(settingutils.NewAdvancedHelper("backup")).
|
||||
Prefix("/settings/backup").
|
||||
Get("", new(IndexAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package browser
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type UpdatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunGet(params struct {
|
||||
DataId string
|
||||
}) {
|
||||
browserResp, err := this.RPC().FormalClientBrowserRPC().FindFormalClientBrowserWithDataId(this.AdminContext(), &pb.FindFormalClientBrowserWithDataIdRequest{DataId: params.DataId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var browser = browserResp.FormalClientBrowser
|
||||
if browser == nil {
|
||||
this.NotFound("formalClientBrowser", 0)
|
||||
return
|
||||
}
|
||||
if browser.Codes == nil {
|
||||
browser.Codes = []string{}
|
||||
}
|
||||
this.Data["browser"] = maps.Map{
|
||||
"id": browser.Id,
|
||||
"name": browser.Name,
|
||||
"codes": browser.Codes,
|
||||
"dataId": browser.DataId,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunPost(params struct {
|
||||
BrowserId int64
|
||||
Name string
|
||||
Codes []string
|
||||
DataId string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.ClientBrowser_LogUpdateClientBrowser, params.BrowserId)
|
||||
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入浏览器名称").
|
||||
Field("dataId", params.DataId).
|
||||
Require("请输入数据ID")
|
||||
|
||||
if len(params.Codes) == 0 {
|
||||
params.Codes = []string{params.Name}
|
||||
}
|
||||
|
||||
// 检查dataId
|
||||
browserResp, err := this.RPC().FormalClientBrowserRPC().FindFormalClientBrowserWithDataId(this.AdminContext(), &pb.FindFormalClientBrowserWithDataIdRequest{DataId: params.DataId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if browserResp.FormalClientBrowser != nil && browserResp.FormalClientBrowser.Id != params.BrowserId {
|
||||
this.Fail("该数据ID已经被 '" + browserResp.FormalClientBrowser.Name + "'所使用,请换一个")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = this.RPC().FormalClientBrowserRPC().UpdateFormalClientBrowser(this.AdminContext(), &pb.UpdateFormalClientBrowserRequest{
|
||||
FormalClientBrowserId: params.BrowserId,
|
||||
Name: params.Name,
|
||||
Codes: params.Codes,
|
||||
DataId: params.DataId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package clientbrowsers
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
type CreatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunGet(params struct{}) {
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunPost(params struct {
|
||||
Name string
|
||||
Codes []string
|
||||
DataId string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.ClientBrowser_LogCreateBrowser, params.Name)
|
||||
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入浏览器名称").
|
||||
Field("dataId", params.DataId).
|
||||
Require("请输入数据ID")
|
||||
|
||||
if len(params.Codes) == 0 {
|
||||
params.Codes = []string{params.Name}
|
||||
}
|
||||
|
||||
// 检查dataId
|
||||
browserResp, err := this.RPC().FormalClientBrowserRPC().FindFormalClientBrowserWithDataId(this.AdminContext(), &pb.FindFormalClientBrowserWithDataIdRequest{DataId: params.DataId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if browserResp.FormalClientBrowser != nil {
|
||||
this.Fail("该数据ID已经被 '" + browserResp.FormalClientBrowser.Name + "'所使用,请换一个")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = this.RPC().FormalClientBrowserRPC().CreateFormalClientBrowser(this.AdminContext(), &pb.CreateFormalClientBrowserRequest{
|
||||
Name: params.Name,
|
||||
Codes: params.Codes,
|
||||
DataId: params.DataId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package clientbrowsers
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
Keyword string
|
||||
}) {
|
||||
this.Data["keyword"] = params.Keyword
|
||||
|
||||
countResp, err := this.RPC().FormalClientBrowserRPC().CountFormalClientBrowsers(this.AdminContext(), &pb.CountFormalClientBrowsersRequest{
|
||||
Keyword: params.Keyword,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var page = this.NewPage(countResp.Count)
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
browsersResp, err := this.RPC().FormalClientBrowserRPC().ListFormalClientBrowsers(this.AdminContext(), &pb.ListFormalClientBrowsersRequest{
|
||||
Keyword: params.Keyword,
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var browserMaps = []maps.Map{}
|
||||
for _, browser := range browsersResp.FormalClientBrowsers {
|
||||
var codes = browser.Codes
|
||||
if codes == nil {
|
||||
codes = []string{}
|
||||
}
|
||||
browserMaps = append(browserMaps, maps.Map{
|
||||
"id": browser.Id,
|
||||
"name": browser.Name,
|
||||
"codes": codes,
|
||||
"dataId": browser.DataId,
|
||||
})
|
||||
}
|
||||
this.Data["browsers"] = browserMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//go:build plus
|
||||
|
||||
package clientbrowsers
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/client-browsers/browser"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(plus.NewBasicHelper()).
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)).
|
||||
Helper(settingutils.NewAdvancedHelper("clientBrowser")).
|
||||
|
||||
//
|
||||
Prefix("/settings/client-browsers").
|
||||
Get("", new(IndexAction)).
|
||||
GetPost("/createPopup", new(CreatePopupAction)).
|
||||
|
||||
//
|
||||
Prefix("/settings/client-browsers/browser").
|
||||
GetPost("/updatePopup", new(browser.UpdatePopupAction)).
|
||||
|
||||
//
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package clientsystems
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
type CreatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunGet(params struct{}) {
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunPost(params struct {
|
||||
Name string
|
||||
Codes []string
|
||||
DataId string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.ClientSystem_LogCreateSystem, params.Name)
|
||||
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入操作系统名称").
|
||||
Field("dataId", params.DataId).
|
||||
Require("请输入数据ID")
|
||||
|
||||
if len(params.Codes) == 0 {
|
||||
params.Codes = []string{params.Name}
|
||||
}
|
||||
|
||||
// 检查dataId
|
||||
systemResp, err := this.RPC().FormalClientSystemRPC().FindFormalClientSystemWithDataId(this.AdminContext(), &pb.FindFormalClientSystemWithDataIdRequest{DataId: params.DataId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if systemResp.FormalClientSystem != nil {
|
||||
this.Fail("该数据ID已经被 '" + systemResp.FormalClientSystem.Name + "'所使用,请换一个")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = this.RPC().FormalClientSystemRPC().CreateFormalClientSystem(this.AdminContext(), &pb.CreateFormalClientSystemRequest{
|
||||
Name: params.Name,
|
||||
Codes: params.Codes,
|
||||
DataId: params.DataId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package clientsystems
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
Keyword string
|
||||
}) {
|
||||
this.Data["keyword"] = params.Keyword
|
||||
|
||||
countResp, err := this.RPC().FormalClientSystemRPC().CountFormalClientSystems(this.AdminContext(), &pb.CountFormalClientSystemsRequest{
|
||||
Keyword: params.Keyword,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var page = this.NewPage(countResp.Count)
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
systemsResp, err := this.RPC().FormalClientSystemRPC().ListFormalClientSystems(this.AdminContext(), &pb.ListFormalClientSystemsRequest{
|
||||
Keyword: params.Keyword,
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var systemMaps = []maps.Map{}
|
||||
for _, system := range systemsResp.FormalClientSystems {
|
||||
var codes = system.Codes
|
||||
if codes == nil {
|
||||
codes = []string{}
|
||||
}
|
||||
systemMaps = append(systemMaps, maps.Map{
|
||||
"id": system.Id,
|
||||
"name": system.Name,
|
||||
"codes": codes,
|
||||
"dataId": system.DataId,
|
||||
})
|
||||
}
|
||||
this.Data["systems"] = systemMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//go:build plus
|
||||
|
||||
package clientsystems
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/client-systems/system"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(plus.NewBasicHelper()).
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)).
|
||||
Helper(settingutils.NewAdvancedHelper("clientSystem")).
|
||||
|
||||
//
|
||||
Prefix("/settings/client-systems").
|
||||
Get("", new(IndexAction)).
|
||||
GetPost("/createPopup", new(CreatePopupAction)).
|
||||
|
||||
//
|
||||
Prefix("/settings/client-systems/system").
|
||||
GetPost("/updatePopup", new(system.UpdatePopupAction)).
|
||||
|
||||
//
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type UpdatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunGet(params struct {
|
||||
DataId string
|
||||
}) {
|
||||
systemResp, err := this.RPC().FormalClientSystemRPC().FindFormalClientSystemWithDataId(this.AdminContext(), &pb.FindFormalClientSystemWithDataIdRequest{DataId: params.DataId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var system = systemResp.FormalClientSystem
|
||||
if system == nil {
|
||||
this.NotFound("formalClientSystem", 0)
|
||||
return
|
||||
}
|
||||
if system.Codes == nil {
|
||||
system.Codes = []string{}
|
||||
}
|
||||
this.Data["system"] = maps.Map{
|
||||
"id": system.Id,
|
||||
"name": system.Name,
|
||||
"codes": system.Codes,
|
||||
"dataId": system.DataId,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunPost(params struct {
|
||||
SystemId int64
|
||||
Name string
|
||||
Codes []string
|
||||
DataId string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.ClientSystem_LogUpdateClientSystem, params.SystemId)
|
||||
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入操作系统名称").
|
||||
Field("dataId", params.DataId).
|
||||
Require("请输入数据ID")
|
||||
|
||||
if len(params.Codes) == 0 {
|
||||
params.Codes = []string{params.Name}
|
||||
}
|
||||
|
||||
// 检查dataId
|
||||
systemResp, err := this.RPC().FormalClientSystemRPC().FindFormalClientSystemWithDataId(this.AdminContext(), &pb.FindFormalClientSystemWithDataIdRequest{DataId: params.DataId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if systemResp.FormalClientSystem != nil && systemResp.FormalClientSystem.Id != params.SystemId {
|
||||
this.Fail("该数据ID已经被 '" + systemResp.FormalClientSystem.Name + "'所使用,请换一个")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = this.RPC().FormalClientSystemRPC().UpdateFormalClientSystem(this.AdminContext(), &pb.UpdateFormalClientSystemRequest{
|
||||
FormalClientSystemId: params.SystemId,
|
||||
Name: params.Name,
|
||||
Codes: params.Codes,
|
||||
DataId: params.DataId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type CleanAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CleanAction) Init() {
|
||||
this.Nav("", "", "clean")
|
||||
}
|
||||
|
||||
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{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var tables = 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
|
||||
}
|
||||
tableMaps = append(tableMaps, maps.Map{
|
||||
"name": table.Name,
|
||||
"rows": table.Rows,
|
||||
"size": numberutils.FormatBytes(table.DataLength + table.IndexLength),
|
||||
"canDelete": table.CanDelete,
|
||||
"canClean": table.CanClean,
|
||||
"comment": table.Comment,
|
||||
})
|
||||
}
|
||||
this.Data["tables"] = tableMaps
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
type CleanSettingAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CleanSettingAction) Init() {
|
||||
this.Nav("", "", "cleanSetting")
|
||||
}
|
||||
|
||||
func (this *CleanSettingAction) RunGet(params struct{}) {
|
||||
// 读取设置
|
||||
configResp, err := this.RPC().SysSettingRPC().ReadSysSetting(this.AdminContext(), &pb.ReadSysSettingRequest{Code: systemconfigs.SettingCodeDatabaseConfigSetting})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var config = systemconfigs.NewDatabaseConfig()
|
||||
if len(configResp.ValueJSON) > 0 {
|
||||
err = json.Unmarshal(configResp.ValueJSON, config)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
this.Data["config"] = config
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *CleanSettingAction) RunPost(params struct {
|
||||
ServerAccessLogCleanDays int
|
||||
ServerBandwidthStatCleanDays int
|
||||
UserBandwidthStatCleanDays int
|
||||
UserPlanBandwidthStatCleanDays int
|
||||
ServerDailyStatCleanDays int
|
||||
ServerDomainHourlyStatCleanDays int
|
||||
TrafficDailyStatCleanDays int
|
||||
TrafficHourlyStatCleanDays int
|
||||
NodeClusterTrafficDailyStatCleanDays int
|
||||
NodeTrafficDailyStatCleanDays int
|
||||
NodeTrafficHourlyStatCleanDays int
|
||||
HttpCacheTaskCleanDays int
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.Database_LogUpdateCleanDays)
|
||||
|
||||
// 读取设置
|
||||
configResp, err := this.RPC().SysSettingRPC().ReadSysSetting(this.AdminContext(), &pb.ReadSysSettingRequest{Code: systemconfigs.SettingCodeDatabaseConfigSetting})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var config = systemconfigs.NewDatabaseConfig()
|
||||
if len(configResp.ValueJSON) > 0 {
|
||||
err = json.Unmarshal(configResp.ValueJSON, config)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if params.ServerAccessLogCleanDays < 0 {
|
||||
params.ServerAccessLogCleanDays = 0
|
||||
}
|
||||
config.ServerAccessLog.Clean.Days = params.ServerAccessLogCleanDays
|
||||
|
||||
if params.ServerBandwidthStatCleanDays < 0 {
|
||||
params.ServerBandwidthStatCleanDays = 0
|
||||
}
|
||||
config.ServerBandwidthStat.Clean.Days = params.ServerBandwidthStatCleanDays
|
||||
|
||||
if params.UserBandwidthStatCleanDays < 0 {
|
||||
params.UserBandwidthStatCleanDays = 0
|
||||
}
|
||||
config.UserBandwidthStat.Clean.Days = params.UserBandwidthStatCleanDays
|
||||
|
||||
if params.UserPlanBandwidthStatCleanDays < 0 {
|
||||
params.UserPlanBandwidthStatCleanDays = 0
|
||||
}
|
||||
config.UserPlanBandwidthStat.Clean.Days = params.UserPlanBandwidthStatCleanDays
|
||||
|
||||
if params.ServerDailyStatCleanDays < 0 {
|
||||
params.ServerDailyStatCleanDays = 0
|
||||
}
|
||||
config.ServerDailyStat.Clean.Days = params.ServerDailyStatCleanDays
|
||||
|
||||
if params.ServerDomainHourlyStatCleanDays < 0 {
|
||||
params.ServerDomainHourlyStatCleanDays = 0
|
||||
}
|
||||
config.ServerDomainHourlyStat.Clean.Days = params.ServerDomainHourlyStatCleanDays
|
||||
|
||||
if params.TrafficDailyStatCleanDays < 0 {
|
||||
params.TrafficDailyStatCleanDays = 0
|
||||
}
|
||||
config.TrafficDailyStat.Clean.Days = params.TrafficDailyStatCleanDays
|
||||
|
||||
if params.TrafficHourlyStatCleanDays < 0 {
|
||||
params.TrafficHourlyStatCleanDays = 0
|
||||
}
|
||||
config.TrafficHourlyStat.Clean.Days = params.TrafficHourlyStatCleanDays
|
||||
|
||||
if params.NodeClusterTrafficDailyStatCleanDays < 0 {
|
||||
params.NodeClusterTrafficDailyStatCleanDays = 0
|
||||
}
|
||||
config.NodeClusterTrafficDailyStat.Clean.Days = params.NodeClusterTrafficDailyStatCleanDays
|
||||
|
||||
if params.NodeTrafficDailyStatCleanDays < 0 {
|
||||
params.NodeTrafficDailyStatCleanDays = 0
|
||||
}
|
||||
config.NodeTrafficDailyStat.Clean.Days = params.NodeTrafficDailyStatCleanDays
|
||||
|
||||
if params.NodeTrafficHourlyStatCleanDays < 0 {
|
||||
params.NodeTrafficHourlyStatCleanDays = 0
|
||||
}
|
||||
config.NodeTrafficHourlyStat.Clean.Days = params.NodeTrafficHourlyStatCleanDays
|
||||
|
||||
if params.HttpCacheTaskCleanDays < 0 {
|
||||
params.HttpCacheTaskCleanDays = 0
|
||||
}
|
||||
config.HTTPCacheTask.Clean.Days = params.HttpCacheTaskCleanDays
|
||||
|
||||
configJSON, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
_, err = this.RPC().SysSettingRPC().UpdateSysSetting(this.AdminContext(), &pb.UpdateSysSettingRequest{
|
||||
Code: systemconfigs.SettingCodeDatabaseConfigSetting,
|
||||
ValueJSON: configJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
type DeleteTableAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DeleteTableAction) RunPost(params struct {
|
||||
Table string
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.Database_LogDeleteTable, params.Table)
|
||||
|
||||
_, err := this.RPC().DBRPC().DeleteDBTable(this.AdminContext(), &pb.DeleteDBTableRequest{DbTable: params.Table})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"gopkg.in/yaml.v3"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "index")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
this.Data["error"] = ""
|
||||
|
||||
var configFile = Tea.ConfigFile("api_db.yaml")
|
||||
data, err := os.ReadFile(configFile)
|
||||
if err != nil {
|
||||
this.Data["error"] = "read config file failed: api_db.yaml: " + err.Error()
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
|
||||
// new config
|
||||
var config = &configs.SimpleDBConfig{}
|
||||
err = yaml.Unmarshal(data, config)
|
||||
if err == nil && len(config.Host) > 0 {
|
||||
host, port, splitErr := net.SplitHostPort(config.Host)
|
||||
if splitErr != nil {
|
||||
port = "3306"
|
||||
}
|
||||
var password = config.Password
|
||||
if len(password) > 0 {
|
||||
password = strings.Repeat("*", len(password))
|
||||
}
|
||||
this.Data["dbConfig"] = maps.Map{
|
||||
"host": host,
|
||||
"port": port,
|
||||
"username": config.User,
|
||||
"password": password,
|
||||
"database": config.Database,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
|
||||
this.parseOldConfig(data)
|
||||
}
|
||||
|
||||
func (this *IndexAction) parseOldConfig(data []byte) {
|
||||
var config = &dbs.Config{}
|
||||
err := yaml.Unmarshal(data, config)
|
||||
if err != nil {
|
||||
this.Data["error"] = "parse config file failed: api_db.yaml: " + err.Error()
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
|
||||
if config.DBs == nil {
|
||||
this.Data["error"] = "no database configured in config file: api_db.yaml"
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
|
||||
var dbConfig *dbs.DBConfig
|
||||
for _, db := range config.DBs {
|
||||
dbConfig = db
|
||||
break
|
||||
}
|
||||
if dbConfig == nil {
|
||||
this.Data["error"] = "no database configured in config file: api_db.yaml"
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
var dsn = dbConfig.Dsn
|
||||
cfg, err := mysql.ParseDSN(dsn)
|
||||
if err != nil {
|
||||
this.Data["error"] = "parse dsn error: " + err.Error()
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
|
||||
var host = cfg.Addr
|
||||
var port = "3306"
|
||||
var index = strings.LastIndex(host, ":")
|
||||
if index > 0 {
|
||||
port = host[index+1:]
|
||||
host = host[:index]
|
||||
}
|
||||
|
||||
var password = cfg.Passwd
|
||||
if len(password) > 0 {
|
||||
password = strings.Repeat("*", len(password))
|
||||
}
|
||||
|
||||
this.Data["dbConfig"] = maps.Map{
|
||||
"host": host,
|
||||
"port": port,
|
||||
"username": cfg.User,
|
||||
"password": password,
|
||||
"database": cfg.DBName,
|
||||
}
|
||||
|
||||
// TODO 测试连接
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)).
|
||||
Helper(settingutils.NewAdvancedHelper("database")).
|
||||
Prefix("/settings/database").
|
||||
Get("", new(IndexAction)).
|
||||
GetPost("/update", new(UpdateAction)).
|
||||
GetPost("/clean", new(CleanAction)).
|
||||
GetPost("/cleanSetting", new(CleanSettingAction)).
|
||||
GetPost("/truncateTable", new(TruncateTableAction)).
|
||||
GetPost("/deleteTable", new(DeleteTableAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
type TruncateTableAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *TruncateTableAction) RunPost(params struct {
|
||||
Table string
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.Database_LogTruncateTable, params.Table)
|
||||
|
||||
_, err := this.RPC().DBRPC().TruncateDBTable(this.AdminContext(), &pb.TruncateDBTableRequest{DbTable: params.Table})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"gopkg.in/yaml.v3"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type UpdateAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdateAction) Init() {
|
||||
this.Nav("", "", "update")
|
||||
}
|
||||
|
||||
func (this *UpdateAction) RunGet(params struct{}) {
|
||||
this.Data["dbConfig"] = maps.Map{
|
||||
"host": "",
|
||||
"port": "",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"database": "",
|
||||
}
|
||||
|
||||
configFile := Tea.ConfigFile("api_db.yaml")
|
||||
data, err := os.ReadFile(configFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// new config
|
||||
var config = &configs.SimpleDBConfig{}
|
||||
err = yaml.Unmarshal(data, config)
|
||||
if err == nil && len(config.Host) > 0 {
|
||||
host, port, splitErr := net.SplitHostPort(config.Host)
|
||||
if splitErr != nil {
|
||||
port = "3306"
|
||||
}
|
||||
|
||||
this.Data["dbConfig"] = maps.Map{
|
||||
"host": host,
|
||||
"port": port,
|
||||
"username": config.User,
|
||||
"password": config.Password,
|
||||
"database": config.Database,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
|
||||
this.parseOldConfig(data)
|
||||
}
|
||||
|
||||
func (this *UpdateAction) RunPost(params struct {
|
||||
Host string
|
||||
Port int32
|
||||
Database string
|
||||
Username string
|
||||
Password string
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.Database_LogUpdateAPINodeDatabaseConfig)
|
||||
|
||||
params.Must.
|
||||
Field("host", params.Host).
|
||||
Require("请输入主机地址").
|
||||
Expect(func() (message string, success bool) {
|
||||
// 是否为IP
|
||||
if net.ParseIP(params.Host) != nil {
|
||||
success = true
|
||||
return
|
||||
}
|
||||
if !regexp.MustCompile(`^[\w.-]+$`).MatchString(params.Host) {
|
||||
message = "主机地址中不能包含特殊字符"
|
||||
success = false
|
||||
return
|
||||
}
|
||||
success = true
|
||||
return
|
||||
}).
|
||||
Field("port", params.Port).
|
||||
Gt(0, "端口需要大于0").
|
||||
Lt(65535, "端口需要小于65535").
|
||||
Field("database", params.Database).
|
||||
Require("请输入数据库名称").
|
||||
Match(`^[\w\.-]+$`, "数据库名称中不能包含特殊字符").
|
||||
Field("username", params.Username).
|
||||
Require("请输入连接数据库的用户名").
|
||||
Match(`^[\w\.-]+$`, "用户名中不能包含特殊字符")
|
||||
|
||||
var config = &configs.SimpleDBConfig{
|
||||
User: params.Username,
|
||||
Password: params.Password,
|
||||
Database: params.Database,
|
||||
Host: configutils.QuoteIP(params.Host) + ":" + fmt.Sprintf("%d", params.Port),
|
||||
}
|
||||
configYAML, err := yaml.Marshal(config)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 保存
|
||||
|
||||
var configFile = Tea.ConfigFile("api_db.yaml")
|
||||
err = os.WriteFile(configFile, configYAML, 0666)
|
||||
if err != nil {
|
||||
this.Fail("保存配置失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// TODO 思考是否让本地的API节点生效
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
func (this *UpdateAction) parseOldConfig(data []byte) {
|
||||
var config = &dbs.Config{}
|
||||
err := yaml.Unmarshal(data, config)
|
||||
if err != nil {
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
|
||||
if config.DBs == nil {
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
|
||||
var dbConfig *dbs.DBConfig
|
||||
for _, db := range config.DBs {
|
||||
dbConfig = db
|
||||
break
|
||||
}
|
||||
if dbConfig == nil {
|
||||
this.Data["dbConfig"] = maps.Map{
|
||||
"host": "",
|
||||
"port": "",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"database": "",
|
||||
}
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
|
||||
var dsn = dbConfig.Dsn
|
||||
cfg, err := mysql.ParseDSN(dsn)
|
||||
if err != nil {
|
||||
this.Data["dbConfig"] = maps.Map{
|
||||
"host": "",
|
||||
"port": "",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"database": "",
|
||||
}
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
|
||||
var host = cfg.Addr
|
||||
var port = "3306"
|
||||
var index = strings.LastIndex(cfg.Addr, ":")
|
||||
if index > 0 {
|
||||
host = cfg.Addr[:index]
|
||||
port = cfg.Addr[index+1:]
|
||||
}
|
||||
|
||||
this.Data["dbConfig"] = maps.Map{
|
||||
"host": host,
|
||||
"port": port,
|
||||
"username": cfg.User,
|
||||
"password": cfg.Passwd,
|
||||
"database": cfg.DBName,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
14
EdgeAdmin/internal/web/actions/default/settings/helper.go
Normal file
14
EdgeAdmin/internal/web/actions/default/settings/helper.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package settings
|
||||
|
||||
import "github.com/iwind/TeaGo/actions"
|
||||
|
||||
type Helper struct {
|
||||
}
|
||||
|
||||
func NewHelper() *Helper {
|
||||
return &Helper{}
|
||||
}
|
||||
|
||||
func (this *Helper) BeforeAction(action *actions.ActionObject) {
|
||||
action.Data["teaMenu"] = "settings"
|
||||
}
|
||||
17
EdgeAdmin/internal/web/actions/default/settings/index.go
Normal file
17
EdgeAdmin/internal/web/actions/default/settings/index.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
this.RedirectURL("/settings/server")
|
||||
}
|
||||
19
EdgeAdmin/internal/web/actions/default/settings/init.go
Normal file
19
EdgeAdmin/internal/web/actions/default/settings/init.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)).
|
||||
Helper(NewHelper()).
|
||||
Prefix("/settings").
|
||||
Get("", new(IndexAction)).
|
||||
Get("/advanced", new(AdvancedAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package cities
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/iplibraryutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "city")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
CountryId int64
|
||||
ProvinceId int64
|
||||
}) {
|
||||
// 检查权限
|
||||
if !iplibraryutils.CanAccess() {
|
||||
return
|
||||
}
|
||||
this.Data["canAccess"] = true
|
||||
|
||||
if params.CountryId <= 0 || params.ProvinceId <= 0 {
|
||||
params.CountryId = 1 // china
|
||||
params.ProvinceId = 1 // beijing
|
||||
}
|
||||
|
||||
this.Data["countryId"] = params.CountryId
|
||||
this.Data["provinceId"] = params.ProvinceId
|
||||
|
||||
// 所有国家/地区
|
||||
countriesResp, err := this.RPC().RegionCountryRPC().FindAllRegionCountries(this.AdminContext(), &pb.FindAllRegionCountriesRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var countryMaps = []maps.Map{}
|
||||
for _, country := range countriesResp.RegionCountries {
|
||||
countryMaps = append(countryMaps, maps.Map{
|
||||
"id": country.Id,
|
||||
"name": country.DisplayName,
|
||||
})
|
||||
}
|
||||
this.Data["countries"] = countryMaps
|
||||
|
||||
// 城市列表
|
||||
citiesResp, err := this.RPC().RegionCityRPC().FindAllRegionCitiesWithRegionProvinceId(this.AdminContext(), &pb.FindAllRegionCitiesWithRegionProvinceIdRequest{
|
||||
RegionProvinceId: params.ProvinceId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var cityMaps = []maps.Map{}
|
||||
for _, city := range citiesResp.RegionCities {
|
||||
if city.Codes == nil {
|
||||
city.Codes = []string{}
|
||||
}
|
||||
if city.CustomCodes == nil {
|
||||
city.CustomCodes = []string{}
|
||||
}
|
||||
cityMaps = append(cityMaps, maps.Map{
|
||||
"id": city.Id,
|
||||
"name": city.Name,
|
||||
"codes": city.Codes,
|
||||
"customName": city.CustomName,
|
||||
"customCodes": city.CustomCodes,
|
||||
})
|
||||
}
|
||||
this.Data["cities"] = cityMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package cities
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type ProvinceOptionsAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *ProvinceOptionsAction) RunPost(params struct {
|
||||
CountryId int64
|
||||
}) {
|
||||
provincesResp, err := this.RPC().RegionProvinceRPC().FindAllRegionProvincesWithRegionCountryId(this.AdminContext(), &pb.FindAllRegionProvincesWithRegionCountryIdRequest{
|
||||
RegionCountryId: params.CountryId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var provinceMaps = []maps.Map{}
|
||||
for _, province := range provincesResp.RegionProvinces {
|
||||
provinceMaps = append(provinceMaps, maps.Map{
|
||||
"id": province.Id,
|
||||
"name": province.DisplayName,
|
||||
})
|
||||
}
|
||||
this.Data["provinces"] = provinceMaps
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package cities
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type UpdatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunGet(params struct {
|
||||
CityId int64
|
||||
}) {
|
||||
cityResp, err := this.RPC().RegionCityRPC().FindRegionCity(this.AdminContext(), &pb.FindRegionCityRequest{RegionCityId: params.CityId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var city = cityResp.RegionCity
|
||||
if city == nil {
|
||||
this.NotFound("regionCity", params.CityId)
|
||||
return
|
||||
}
|
||||
if city.Codes == nil {
|
||||
city.Codes = []string{}
|
||||
}
|
||||
if city.CustomCodes == nil {
|
||||
city.CustomCodes = []string{}
|
||||
}
|
||||
this.Data["city"] = maps.Map{
|
||||
"id": city.Id,
|
||||
"name": city.Name,
|
||||
"codes": city.Codes,
|
||||
"customName": city.CustomName,
|
||||
"customCodes": city.CustomCodes,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunPost(params struct {
|
||||
CityId int64
|
||||
CustomName string
|
||||
CustomCodes []string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.RegionCity_LogUpdateRegionCityCustom, params.CityId)
|
||||
|
||||
_, err := this.RPC().RegionCityRPC().UpdateRegionCityCustom(this.AdminContext(), &pb.UpdateRegionCityCustomRequest{
|
||||
RegionCityId: params.CityId,
|
||||
CustomName: params.CustomName,
|
||||
CustomCodes: params.CustomCodes,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package countries
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/iplibraryutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "country")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
// 检查权限
|
||||
if !iplibraryutils.CanAccess() {
|
||||
return
|
||||
}
|
||||
this.Data["canAccess"] = true
|
||||
|
||||
countriesResp, err := this.RPC().RegionCountryRPC().FindAllRegionCountries(this.AdminContext(), &pb.FindAllRegionCountriesRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var countryMaps = []maps.Map{}
|
||||
for _, country := range countriesResp.RegionCountries {
|
||||
if country.Codes == nil {
|
||||
country.Codes = []string{}
|
||||
}
|
||||
if country.CustomCodes == nil {
|
||||
country.CustomCodes = []string{}
|
||||
}
|
||||
countryMaps = append(countryMaps, maps.Map{
|
||||
"id": country.Id,
|
||||
"name": country.Name,
|
||||
"codes": country.Codes,
|
||||
"customName": country.CustomName,
|
||||
"customCodes": country.CustomCodes,
|
||||
})
|
||||
}
|
||||
this.Data["countries"] = countryMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package countries
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type UpdatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunGet(params struct {
|
||||
CountryId int64
|
||||
}) {
|
||||
countryResp, err := this.RPC().RegionCountryRPC().FindRegionCountry(this.AdminContext(), &pb.FindRegionCountryRequest{
|
||||
RegionCountryId: params.CountryId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var country = countryResp.RegionCountry
|
||||
if country == nil {
|
||||
this.NotFound("regionCountry", params.CountryId)
|
||||
return
|
||||
}
|
||||
if country.Codes == nil {
|
||||
country.Codes = []string{}
|
||||
}
|
||||
if country.CustomCodes == nil {
|
||||
country.CustomCodes = []string{}
|
||||
}
|
||||
this.Data["country"] = maps.Map{
|
||||
"id": country.Id,
|
||||
"name": country.Name,
|
||||
"codes": country.Codes,
|
||||
"customName": country.CustomName,
|
||||
"customCodes": country.CustomCodes,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunPost(params struct {
|
||||
CountryId int64
|
||||
CustomName string
|
||||
CustomCodes []string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.RegionCountry_LogUpdateRegionCountryCustom, params.CountryId)
|
||||
|
||||
_, err := this.RPC().RegionCountryRPC().UpdateRegionCountryCustom(this.AdminContext(), &pb.UpdateRegionCountryCustomRequest{
|
||||
RegionCountryId: params.CountryId,
|
||||
CustomName: params.CustomName,
|
||||
CustomCodes: params.CustomCodes,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package creating
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
)
|
||||
|
||||
type AddCityCustomCodeAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *AddCityCustomCodeAction) RunPost(params struct {
|
||||
CityId int64
|
||||
Code string
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.RegionCity_LogAddRegionCityCode, params.CityId, params.Code)
|
||||
|
||||
cityResp, err := this.RPC().RegionCityRPC().FindRegionCity(this.AdminContext(), &pb.FindRegionCityRequest{RegionCityId: params.CityId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var city = cityResp.RegionCity
|
||||
if city == nil {
|
||||
this.NotFound("regionCity", params.CityId)
|
||||
return
|
||||
}
|
||||
|
||||
if len(params.Code) > 0 && !lists.ContainsString(city.CustomCodes, params.Code) {
|
||||
city.CustomCodes = append(city.CustomCodes, params.Code)
|
||||
_, err = this.RPC().RegionCityRPC().UpdateRegionCityCustom(this.AdminContext(), &pb.UpdateRegionCityCustomRequest{
|
||||
RegionCityId: params.CityId,
|
||||
CustomName: city.CustomName,
|
||||
CustomCodes: city.CustomCodes,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package creating
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
)
|
||||
|
||||
type AddCountryCustomCodeAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *AddCountryCustomCodeAction) RunPost(params struct {
|
||||
CountryId int64
|
||||
Code string
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.RegionCountry_LogAddRegionCountryCode, params.CountryId, params.Code)
|
||||
|
||||
countryResp, err := this.RPC().RegionCountryRPC().FindRegionCountry(this.AdminContext(), &pb.FindRegionCountryRequest{RegionCountryId: params.CountryId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var country = countryResp.RegionCountry
|
||||
if country == nil {
|
||||
this.NotFound("regionCountry", params.CountryId)
|
||||
return
|
||||
}
|
||||
|
||||
if len(params.Code) > 0 && !lists.ContainsString(country.CustomCodes, params.Code) {
|
||||
country.CustomCodes = append(country.CustomCodes, params.Code)
|
||||
_, err = this.RPC().RegionCountryRPC().UpdateRegionCountryCustom(this.AdminContext(), &pb.UpdateRegionCountryCustomRequest{
|
||||
RegionCountryId: params.CountryId,
|
||||
CustomName: country.CustomName,
|
||||
CustomCodes: country.CustomCodes,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package creating
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
)
|
||||
|
||||
type AddProviderCustomCodeAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *AddProviderCustomCodeAction) RunPost(params struct {
|
||||
ProviderId int64
|
||||
Code string
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.RegionProvider_LogAddRegionProviderCode, params.ProviderId, params.Code)
|
||||
|
||||
providerResp, err := this.RPC().RegionProviderRPC().FindRegionProvider(this.AdminContext(), &pb.FindRegionProviderRequest{RegionProviderId: params.ProviderId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var provider = providerResp.RegionProvider
|
||||
if provider == nil {
|
||||
this.NotFound("regionProvider", params.ProviderId)
|
||||
return
|
||||
}
|
||||
|
||||
if len(params.Code) > 0 && !lists.ContainsString(provider.CustomCodes, params.Code) {
|
||||
provider.CustomCodes = append(provider.CustomCodes, params.Code)
|
||||
_, err = this.RPC().RegionProviderRPC().UpdateRegionProviderCustom(this.AdminContext(), &pb.UpdateRegionProviderCustomRequest{
|
||||
RegionProviderId: params.ProviderId,
|
||||
CustomName: provider.CustomName,
|
||||
CustomCodes: provider.CustomCodes,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package creating
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
)
|
||||
|
||||
type AddProvinceCustomCodeAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *AddProvinceCustomCodeAction) RunPost(params struct {
|
||||
ProvinceId int64
|
||||
Code string
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.RegionProvince_LogAddRegionProvinceCode, params.ProvinceId, params.Code)
|
||||
|
||||
provinceResp, err := this.RPC().RegionProvinceRPC().FindRegionProvince(this.AdminContext(), &pb.FindRegionProvinceRequest{RegionProvinceId: params.ProvinceId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var province = provinceResp.RegionProvince
|
||||
if province == nil {
|
||||
this.NotFound("regionProvince", params.ProvinceId)
|
||||
return
|
||||
}
|
||||
|
||||
if len(params.Code) > 0 && !lists.ContainsString(province.CustomCodes, params.Code) {
|
||||
province.CustomCodes = append(province.CustomCodes, params.Code)
|
||||
_, err = this.RPC().RegionProvinceRPC().UpdateRegionProvinceCustom(this.AdminContext(), &pb.UpdateRegionProvinceCustomRequest{
|
||||
RegionProvinceId: params.ProvinceId,
|
||||
CustomName: province.CustomName,
|
||||
CustomCodes: province.CustomCodes,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package creating
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
)
|
||||
|
||||
type AddTownCustomCodeAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *AddTownCustomCodeAction) RunPost(params struct {
|
||||
TownId int64
|
||||
Code string
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.RegionTown_LogAddRegionTownCode, params.TownId, params.Code)
|
||||
|
||||
townResp, err := this.RPC().RegionTownRPC().FindRegionTown(this.AdminContext(), &pb.FindRegionTownRequest{RegionTownId: params.TownId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var town = townResp.RegionTown
|
||||
if town == nil {
|
||||
this.NotFound("regionTown", params.TownId)
|
||||
return
|
||||
}
|
||||
|
||||
if len(params.Code) > 0 && !lists.ContainsString(town.CustomCodes, params.Code) {
|
||||
town.CustomCodes = append(town.CustomCodes, params.Code)
|
||||
_, err = this.RPC().RegionTownRPC().UpdateRegionTownCustom(this.AdminContext(), &pb.UpdateRegionTownCustomRequest{
|
||||
RegionTownId: params.TownId,
|
||||
CustomName: town.CustomName,
|
||||
CustomCodes: town.CustomCodes,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package creating
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type CitiesAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CitiesAction) RunPost(params struct {
|
||||
LibraryFileId int64
|
||||
Size int
|
||||
}) {
|
||||
missingCitiesResp, err := this.RPC().IPLibraryFileRPC().CheckCitiesWithIPLibraryFileId(this.AdminContext(), &pb.CheckCitiesWithIPLibraryFileIdRequest{
|
||||
IpLibraryFileId: params.LibraryFileId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var missingCityMaps = []maps.Map{}
|
||||
for _, missingCity := range missingCitiesResp.MissingCities {
|
||||
var similarCityMaps = []maps.Map{}
|
||||
for _, city := range missingCity.SimilarCities {
|
||||
similarCityMaps = append(similarCityMaps, maps.Map{
|
||||
"id": city.Id,
|
||||
"displayName": city.DisplayName,
|
||||
})
|
||||
}
|
||||
|
||||
missingCityMaps = append(missingCityMaps, maps.Map{
|
||||
"countryName": missingCity.CountryName,
|
||||
"provinceName": missingCity.ProvinceName,
|
||||
"cityName": missingCity.CityName,
|
||||
"similarCities": similarCityMaps,
|
||||
})
|
||||
|
||||
if params.Size > 0 && len(missingCityMaps) >= params.Size {
|
||||
break
|
||||
}
|
||||
}
|
||||
this.Data["missingCities"] = missingCityMaps
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package creating
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type CountriesAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CountriesAction) RunPost(params struct {
|
||||
LibraryFileId int64
|
||||
}) {
|
||||
missingCountriesResp, err := this.RPC().IPLibraryFileRPC().CheckCountriesWithIPLibraryFileId(this.AdminContext(), &pb.CheckCountriesWithIPLibraryFileIdRequest{
|
||||
IpLibraryFileId: params.LibraryFileId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var missingCountryMaps = []maps.Map{}
|
||||
for _, missingCountry := range missingCountriesResp.MissingCountries {
|
||||
var similarCountryMaps = []maps.Map{}
|
||||
for _, country := range missingCountry.SimilarCountries {
|
||||
similarCountryMaps = append(similarCountryMaps, maps.Map{
|
||||
"id": country.Id,
|
||||
"displayName": country.DisplayName,
|
||||
})
|
||||
}
|
||||
|
||||
missingCountryMaps = append(missingCountryMaps, maps.Map{
|
||||
"countryName": missingCountry.CountryName,
|
||||
"similarCountries": similarCountryMaps,
|
||||
})
|
||||
}
|
||||
this.Data["missingCountries"] = missingCountryMaps
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package creating
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
type FinishAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *FinishAction) RunPost(params struct {
|
||||
LibraryFileId int64
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.IPLibrary_LogFinishIPLibrary, params.LibraryFileId)
|
||||
|
||||
// set finished
|
||||
_, err := this.RPC().IPLibraryFileRPC().UpdateIPLibraryFileFinished(this.AdminContext(), &pb.UpdateIPLibraryFileFinishedRequest{IpLibraryFileId: params.LibraryFileId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// generate
|
||||
_, err = this.RPC().IPLibraryFileRPC().GenerateIPLibraryFile(this.AdminContext(), &pb.GenerateIPLibraryFileRequest{IpLibraryFileId: params.LibraryFileId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package creating
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
type GenerateAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *GenerateAction) RunPost(params struct {
|
||||
LibraryFileId int64
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.IPLibraryFile_LogGenerateIPLibraryFile, params.LibraryFileId)
|
||||
|
||||
_, err := this.RPC().IPLibraryFileRPC().GenerateIPLibraryFile(this.AdminContext(), &pb.GenerateIPLibraryFileRequest{IpLibraryFileId: params.LibraryFileId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package creating
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type ProvidersAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *ProvidersAction) RunPost(params struct {
|
||||
LibraryFileId int64
|
||||
}) {
|
||||
missingProvidersResp, err := this.RPC().IPLibraryFileRPC().CheckProvidersWithIPLibraryFileId(this.AdminContext(), &pb.CheckProvidersWithIPLibraryFileIdRequest{
|
||||
IpLibraryFileId: params.LibraryFileId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var missingProviderMaps = []maps.Map{}
|
||||
for _, missingProvider := range missingProvidersResp.MissingProviders {
|
||||
var similarProviderMaps = []maps.Map{}
|
||||
for _, provider := range missingProvider.SimilarProviders {
|
||||
similarProviderMaps = append(similarProviderMaps, maps.Map{
|
||||
"id": provider.Id,
|
||||
"displayName": provider.DisplayName,
|
||||
})
|
||||
}
|
||||
|
||||
missingProviderMaps = append(missingProviderMaps, maps.Map{
|
||||
"providerName": missingProvider.ProviderName,
|
||||
"similarProviders": similarProviderMaps,
|
||||
})
|
||||
}
|
||||
this.Data["missingProviders"] = missingProviderMaps
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package creating
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type ProvincesAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *ProvincesAction) RunPost(params struct {
|
||||
LibraryFileId int64
|
||||
}) {
|
||||
missingProvincesResp, err := this.RPC().IPLibraryFileRPC().CheckProvincesWithIPLibraryFileId(this.AdminContext(), &pb.CheckProvincesWithIPLibraryFileIdRequest{
|
||||
IpLibraryFileId: params.LibraryFileId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var missingProvinceMaps = []maps.Map{}
|
||||
for _, missingProvince := range missingProvincesResp.MissingProvinces {
|
||||
var similarProvinceMaps = []maps.Map{}
|
||||
for _, province := range missingProvince.SimilarProvinces {
|
||||
similarProvinceMaps = append(similarProvinceMaps, maps.Map{
|
||||
"id": province.Id,
|
||||
"displayName": province.DisplayName,
|
||||
})
|
||||
}
|
||||
|
||||
missingProvinceMaps = append(missingProvinceMaps, maps.Map{
|
||||
"countryName": missingProvince.CountryName,
|
||||
"provinceName": missingProvince.ProvinceName,
|
||||
"similarProvinces": similarProvinceMaps,
|
||||
})
|
||||
}
|
||||
this.Data["missingProvinces"] = missingProvinceMaps
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package creating
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
)
|
||||
|
||||
type TestFormatAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *TestFormatAction) RunPost(params struct {
|
||||
Template string
|
||||
Text string
|
||||
}) {
|
||||
if len(params.Text) == 0 {
|
||||
this.Fail("请先输入测试数据")
|
||||
}
|
||||
|
||||
template, err := iplibrary.NewTemplate(params.Template)
|
||||
if err != nil {
|
||||
this.Fail("数据格式模板解析错误:" + err.Error())
|
||||
}
|
||||
|
||||
var emptyValues = []string{"0", "无"} // TODO
|
||||
|
||||
values, ok := template.Extract(params.Text, emptyValues)
|
||||
if !ok {
|
||||
this.Fail("无法分析数据,填写的测试数据和模板不符合")
|
||||
}
|
||||
this.Data["values"] = values
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package creating
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type TownsAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *TownsAction) RunPost(params struct {
|
||||
LibraryFileId int64
|
||||
Size int
|
||||
}) {
|
||||
missingTownsResp, err := this.RPC().IPLibraryFileRPC().CheckTownsWithIPLibraryFileId(this.AdminContext(), &pb.CheckTownsWithIPLibraryFileIdRequest{
|
||||
IpLibraryFileId: params.LibraryFileId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var missingTownMaps = []maps.Map{}
|
||||
for _, missingTown := range missingTownsResp.MissingTowns {
|
||||
var similarTownMaps = []maps.Map{}
|
||||
for _, town := range missingTown.SimilarTowns {
|
||||
similarTownMaps = append(similarTownMaps, maps.Map{
|
||||
"id": town.Id,
|
||||
"displayName": town.DisplayName,
|
||||
})
|
||||
}
|
||||
|
||||
missingTownMaps = append(missingTownMaps, maps.Map{
|
||||
"countryName": missingTown.CountryName,
|
||||
"provinceName": missingTown.ProvinceName,
|
||||
"cityName": missingTown.CityName,
|
||||
"townName": missingTown.TownName,
|
||||
"similarTowns": similarTownMaps,
|
||||
})
|
||||
|
||||
if params.Size > 0 && len(missingTownMaps) >= params.Size {
|
||||
break
|
||||
}
|
||||
}
|
||||
this.Data["missingTowns"] = missingTownMaps
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package creating
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"io"
|
||||
)
|
||||
|
||||
type UploadAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UploadAction) RunPost(params struct {
|
||||
Name string
|
||||
Template string
|
||||
EmptyValues []string
|
||||
Password string
|
||||
File *actions.File
|
||||
}) {
|
||||
if len(params.Name) == 0 {
|
||||
this.Fail("请输入IP库名称")
|
||||
return
|
||||
}
|
||||
|
||||
if len(params.Template) == 0 {
|
||||
this.Fail("请输入数据格式模板")
|
||||
return
|
||||
}
|
||||
|
||||
template, err := iplibrary.NewTemplate(params.Template)
|
||||
if err != nil {
|
||||
this.Fail("模板格式错误:" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if params.File == nil {
|
||||
this.Fail("请上传文件")
|
||||
}
|
||||
|
||||
var emptyValues = []string{"0", "无"}
|
||||
|
||||
if len(params.EmptyValues) > 0 {
|
||||
for _, emptyValue := range params.EmptyValues {
|
||||
if !lists.ContainsString(emptyValues, emptyValue) {
|
||||
emptyValues = append(emptyValues, emptyValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileResp, err := this.RPC().FileRPC().CreateFile(this.AdminContext(), &pb.CreateFileRequest{
|
||||
Filename: params.File.Filename,
|
||||
Size: params.File.Size,
|
||||
IsPublic: false,
|
||||
MimeType: params.File.ContentType,
|
||||
Type: "ipLibraryFile",
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var fileId = fileResp.FileId
|
||||
|
||||
fp, err := params.File.OriginFile.Open()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_ = fp.Close()
|
||||
}()
|
||||
|
||||
var buf = make([]byte, 128*1024)
|
||||
var dataBuf = []byte{}
|
||||
|
||||
var countries = []string{}
|
||||
var provinces = [][2]string{}
|
||||
var cities = [][3]string{}
|
||||
var towns = [][4]string{}
|
||||
var providers = []string{}
|
||||
|
||||
var countryMap = map[string]bool{} // countryName => bool
|
||||
var provinceMap = map[string]bool{} // countryName_provinceName => bool
|
||||
var cityMap = map[string]bool{} // countryName_provinceName_cityName => bool
|
||||
var townMap = map[string]bool{} // countryName_provinceName_cityName_townName => bool
|
||||
var providerMap = map[string]bool{} // providerName => bool
|
||||
|
||||
for {
|
||||
n, err := fp.Read(buf)
|
||||
if n > 0 {
|
||||
var data = buf[:n]
|
||||
|
||||
// upload
|
||||
_, uploadErr := this.RPC().FileChunkRPC().CreateFileChunk(this.AdminContext(), &pb.CreateFileChunkRequest{
|
||||
FileId: fileId,
|
||||
Data: data,
|
||||
})
|
||||
if uploadErr != nil {
|
||||
this.Fail("文件上传失败:" + uploadErr.Error())
|
||||
}
|
||||
|
||||
// parse
|
||||
dataBuf = append(dataBuf, data...)
|
||||
left, valuesList, failLine, parseIsOk, err := this.parse(template, dataBuf, emptyValues)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if !parseIsOk {
|
||||
this.Fail("在 '" + failLine + "' 这行分析失败,请对比数据模板,检查数据格式")
|
||||
return
|
||||
}
|
||||
for _, value := range valuesList {
|
||||
var countryName = value["country"]
|
||||
var provinceName = value["province"]
|
||||
var cityName = value["city"]
|
||||
var townName = value["town"]
|
||||
var providerName = value["provider"]
|
||||
|
||||
// country
|
||||
if len(countryName) > 0 {
|
||||
_, ok := countryMap[countryName]
|
||||
if !ok {
|
||||
countries = append(countries, countryName)
|
||||
countryMap[countryName] = true
|
||||
}
|
||||
|
||||
// province
|
||||
if len(provinceName) > 0 {
|
||||
var key = countryName + "_" + provinceName
|
||||
_, ok = provinceMap[key]
|
||||
if !ok {
|
||||
provinceMap[key] = true
|
||||
provinces = append(provinces, [2]string{countryName, provinceName})
|
||||
}
|
||||
|
||||
// city
|
||||
if len(cityName) > 0 {
|
||||
key += "_" + cityName
|
||||
_, ok = cityMap[key]
|
||||
if !ok {
|
||||
cityMap[key] = true
|
||||
cities = append(cities, [3]string{countryName, provinceName, cityName})
|
||||
}
|
||||
|
||||
// town
|
||||
if len(townName) > 0 {
|
||||
key += "_" + townName
|
||||
_, ok = townMap[key]
|
||||
if !ok {
|
||||
townMap[key] = true
|
||||
towns = append(towns, [4]string{countryName, provinceName, cityName, townName})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// provider
|
||||
if len(providerName) > 0 {
|
||||
_, ok := providerMap[providerName]
|
||||
if !ok {
|
||||
providerMap[providerName] = true
|
||||
providers = append(providers, providerName)
|
||||
}
|
||||
}
|
||||
}
|
||||
dataBuf = left
|
||||
}
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
countriesJSON, err := json.Marshal(countries)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
provincesJSON, err := json.Marshal(provinces)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
citiesJSON, err := json.Marshal(cities)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
townsJSON, err := json.Marshal(towns)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
providersJSON, err := json.Marshal(providers)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
createResp, err := this.RPC().IPLibraryFileRPC().CreateIPLibraryFile(this.AdminContext(), &pb.CreateIPLibraryFileRequest{
|
||||
Name: params.Name,
|
||||
Template: params.Template,
|
||||
FileId: fileId,
|
||||
EmptyValues: params.EmptyValues,
|
||||
Password: params.Password,
|
||||
CountriesJSON: countriesJSON,
|
||||
ProvincesJSON: provincesJSON,
|
||||
CitiesJSON: citiesJSON,
|
||||
TownsJSON: townsJSON,
|
||||
ProvidersJSON: providersJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var ipLibraryFileId = createResp.IpLibraryFileId
|
||||
defer this.CreateLogInfo(codes.IPLibraryFile_LogUploadIPLibraryFile, ipLibraryFileId)
|
||||
|
||||
this.Data["libraryFileId"] = ipLibraryFileId
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
func (this *UploadAction) parse(template *iplibrary.Template, data []byte, emptyValues []string) (left []byte, valuesList []map[string]string, failLine string, ok bool, err error) {
|
||||
ok = true
|
||||
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
for {
|
||||
var index = bytes.IndexByte(data, '\n')
|
||||
if index >= 0 {
|
||||
var line = data[:index+1]
|
||||
values, found := template.Extract(string(line), emptyValues)
|
||||
if found {
|
||||
valuesList = append(valuesList, values)
|
||||
} else {
|
||||
// 防止错误信息太长
|
||||
if len(line) > 256 {
|
||||
line = line[:256]
|
||||
}
|
||||
failLine = string(line)
|
||||
ok = false
|
||||
return
|
||||
}
|
||||
|
||||
data = data[index+1:]
|
||||
} else {
|
||||
left = data
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
type DeleteAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DeleteAction) RunPost(params struct {
|
||||
ArtifactId int64
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.IPLibraryArtifact_LogDeleteIPLibraryArtifact, params.ArtifactId)
|
||||
|
||||
// 删除数据库中的记录
|
||||
_, err := this.RPC().IPLibraryArtifactRPC().DeleteIPLibraryArtifact(this.AdminContext(), &pb.DeleteIPLibraryArtifactRequest{IpLibraryArtifactId: params.ArtifactId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 删除 EdgeAPI 目录下的 MaxMind 文件(删除所有,因为无法确定具体是哪个文件)
|
||||
_, err = this.RPC().IPLibraryRPC().DeleteMaxMindFile(this.AdminContext(), &pb.DeleteMaxMindFileRequest{
|
||||
Filename: "", // 空字符串表示删除所有
|
||||
})
|
||||
if err != nil {
|
||||
// 记录错误但不影响删除操作
|
||||
this.Fail("删除IP库文件失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
)
|
||||
|
||||
type DownloadAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DownloadAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *DownloadAction) RunGet(params struct {
|
||||
ArtifactId int64
|
||||
}) {
|
||||
artifactResp, err := this.RPC().IPLibraryArtifactRPC().FindIPLibraryArtifact(this.AdminContext(), &pb.FindIPLibraryArtifactRequest{
|
||||
IpLibraryArtifactId: params.ArtifactId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var artifact = artifactResp.IpLibraryArtifact
|
||||
if artifact == nil {
|
||||
this.NotFound("IPLibraryArtifact", params.ArtifactId)
|
||||
return
|
||||
}
|
||||
|
||||
var fileId = artifact.FileId
|
||||
if fileId <= 0 {
|
||||
this.WriteString("ip artifact file not generated")
|
||||
return
|
||||
}
|
||||
|
||||
fileResp, err := this.RPC().FileRPC().FindEnabledFile(this.AdminContext(), &pb.FindEnabledFileRequest{FileId: fileId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var file = fileResp.File
|
||||
if file == nil {
|
||||
this.WriteString("file not found")
|
||||
return
|
||||
}
|
||||
|
||||
chunkIdsResp, err := this.RPC().FileChunkRPC().FindAllFileChunkIds(this.AdminContext(), &pb.FindAllFileChunkIdsRequest{FileId: file.Id})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.AddHeader("Content-Disposition", "attachment; filename=\"ip-"+artifact.Code+".db\";")
|
||||
this.AddHeader("Content-Length", types.String(file.Size))
|
||||
for _, chunkId := range chunkIdsResp.FileChunkIds {
|
||||
chunkResp, err := this.RPC().FileChunkRPC().DownloadFileChunk(this.AdminContext(), &pb.DownloadFileChunkRequest{FileChunkId: chunkId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var chunk = chunkResp.FileChunk
|
||||
if chunk == nil {
|
||||
break
|
||||
}
|
||||
_, _ = this.Write(chunkResp.FileChunk.Data)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package iplibrary
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Helper struct {
|
||||
}
|
||||
|
||||
func NewHelper() *Helper {
|
||||
return &Helper{}
|
||||
}
|
||||
|
||||
func (this *Helper) BeforeAction(action *actions.ActionObject) {
|
||||
if action.Request.Method != http.MethodGet {
|
||||
return
|
||||
}
|
||||
|
||||
action.Data["mainTab"] = "component"
|
||||
action.Data["secondMenuItem"] = "ip-library"
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/iplibraryutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "ipLibrary", "index")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
this.Data["canAccess"] = iplibraryutils.CanAccess()
|
||||
|
||||
// IP库列表
|
||||
artifactsResp, err := this.RPC().IPLibraryArtifactRPC().FindAllIPLibraryArtifacts(this.AdminContext(), &pb.FindAllIPLibraryArtifactsRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var artifactMaps = []maps.Map{}
|
||||
|
||||
// 按创建时间倒序排序,最新的在前
|
||||
artifacts := artifactsResp.IpLibraryArtifacts
|
||||
for i := 0; i < len(artifacts); i++ {
|
||||
for j := i + 1; j < len(artifacts); j++ {
|
||||
if artifacts[i].CreatedAt < artifacts[j].CreatedAt {
|
||||
artifacts[i], artifacts[j] = artifacts[j], artifacts[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 确保最后一个(最新的)是使用中的,其他都是未使用的
|
||||
// 如果列表不为空,将最新的设为使用中,其他设为未使用
|
||||
if len(artifacts) > 0 {
|
||||
latestId := artifacts[0].Id
|
||||
for _, artifact := range artifacts {
|
||||
var shouldBePublic = (artifact.Id == latestId)
|
||||
if artifact.IsPublic != shouldBePublic {
|
||||
// 需要更新状态(这里只标记,不实际更新数据库,因为上传时已经更新了)
|
||||
// 如果数据库状态不一致,这里会显示不一致,但不会自动修复
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, artifact := range artifacts {
|
||||
var meta = &iplibrary.Meta{}
|
||||
if len(artifact.MetaJSON) > 0 {
|
||||
err = json.Unmarshal(artifact.MetaJSON, meta)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 文件信息
|
||||
var fileMap = maps.Map{
|
||||
"size": 0,
|
||||
}
|
||||
if artifact.File != nil {
|
||||
fileMap = maps.Map{
|
||||
"size": artifact.File.Size,
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是否使用中:如果是列表中的第一个(最新的),则标记为使用中
|
||||
isPublic := artifact.Id == artifacts[0].Id
|
||||
|
||||
// 根据文件名判断IP库类型
|
||||
libraryType := "未知"
|
||||
if artifact.File != nil && len(artifact.File.Filename) > 0 {
|
||||
filename := strings.ToLower(artifact.File.Filename)
|
||||
if strings.Contains(filename, "city") {
|
||||
libraryType = "city库"
|
||||
} else if strings.Contains(filename, "asn") {
|
||||
libraryType = "asn库"
|
||||
}
|
||||
}
|
||||
|
||||
artifactMaps = append(artifactMaps, maps.Map{
|
||||
"id": artifact.Id,
|
||||
"name": artifact.Name,
|
||||
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", artifact.CreatedAt),
|
||||
"isPublic": isPublic,
|
||||
"fileId": artifact.FileId,
|
||||
"code": artifact.Code,
|
||||
"libraryType": libraryType,
|
||||
"countCountries": len(meta.Countries),
|
||||
"countProvinces": len(meta.Provinces),
|
||||
"countCities": len(meta.Cities),
|
||||
"countTowns": len(meta.Towns),
|
||||
"countProviders": len(meta.Providers),
|
||||
"file": fileMap,
|
||||
})
|
||||
}
|
||||
this.Data["artifacts"] = artifactMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
//go:build plus
|
||||
|
||||
package iplibrary
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/cities"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/countries"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/creating"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/libraries"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/library"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/providers"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/provinces"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/towns"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(plus.NewBasicHelper()).
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)).
|
||||
Helper(NewHelper()).
|
||||
Helper(settingutils.NewHelper("ipLibrary")).
|
||||
|
||||
// IP库
|
||||
Prefix("/settings/ip-library").
|
||||
Get("", new(IndexAction)).
|
||||
GetPost("/upload", new(UploadAction)).
|
||||
Get("/download", new(DownloadAction)).
|
||||
Post("/delete", new(DeleteAction)).
|
||||
Post("/updatePublic", new(UpdatePublicAction)).
|
||||
|
||||
// IP库
|
||||
Prefix("/settings/ip-library/libraries").
|
||||
Get("", new(libraries.IndexAction)).
|
||||
GetPost("/create", new(libraries.CreateAction)).
|
||||
Post("/delete", new(libraries.DeleteAction)).
|
||||
|
||||
// 创建
|
||||
Prefix("/settings/ip-library/creating").
|
||||
Post("/testFormat", new(creating.TestFormatAction)).
|
||||
Post("/upload", new(creating.UploadAction)).
|
||||
Post("/countries", new(creating.CountriesAction)).
|
||||
Post("/provinces", new(creating.ProvincesAction)).
|
||||
Post("/cities", new(creating.CitiesAction)).
|
||||
Post("/towns", new(creating.TownsAction)).
|
||||
Post("/providers", new(creating.ProvidersAction)).
|
||||
Post("/addCountryCustomCode", new(creating.AddCountryCustomCodeAction)).
|
||||
Post("/addProvinceCustomCode", new(creating.AddProvinceCustomCodeAction)).
|
||||
Post("/addCityCustomCode", new(creating.AddCityCustomCodeAction)).
|
||||
Post("/addTownCustomCode", new(creating.AddTownCustomCodeAction)).
|
||||
Post("/addProviderCustomCode", new(creating.AddProviderCustomCodeAction)).
|
||||
Post("/finish", new(creating.FinishAction)).
|
||||
Post("/generate", new(creating.GenerateAction)).
|
||||
|
||||
// 单个IP库
|
||||
Prefix("/settings/ip-library/library").
|
||||
Get("", new(library.IndexAction)).
|
||||
Get("/download", new(library.DownloadAction)).
|
||||
GetPost("/test", new(library.TestAction)).
|
||||
|
||||
// 国家/地区
|
||||
Prefix("/settings/ip-library/countries").
|
||||
Get("", new(countries.IndexAction)).
|
||||
GetPost("/updatePopup", new(countries.UpdatePopupAction)).
|
||||
|
||||
// 省份
|
||||
Prefix("/settings/ip-library/provinces").
|
||||
Get("", new(provinces.IndexAction)).
|
||||
GetPost("/updatePopup", new(provinces.UpdatePopupAction)).
|
||||
|
||||
// 城市
|
||||
Prefix("/settings/ip-library/cities").
|
||||
Get("", new(cities.IndexAction)).
|
||||
GetPost("/updatePopup", new(cities.UpdatePopupAction)).
|
||||
Post("/provinceOptions", new(cities.ProvinceOptionsAction)).
|
||||
|
||||
// 区县
|
||||
Prefix("/settings/ip-library/towns").
|
||||
Get("", new(towns.IndexAction)).
|
||||
GetPost("/updatePopup", new(towns.UpdatePopupAction)).
|
||||
Post("/provinceOptions", new(towns.ProvinceOptionsAction)).
|
||||
Post("/cityOptions", new(towns.CityOptionsAction)).
|
||||
|
||||
// ISP
|
||||
Prefix("/settings/ip-library/providers").
|
||||
Get("", new(providers.IndexAction)).
|
||||
GetPost("/updatePopup", new(providers.UpdatePopupAction)).
|
||||
|
||||
//
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibraryutils
|
||||
|
||||
import "github.com/iwind/TeaGo/Tea"
|
||||
|
||||
func CanAccess() bool {
|
||||
return Tea.IsTesting()
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package libraries
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/iplibraryutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type CreateAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CreateAction) Init() {
|
||||
this.Nav("", "", "create")
|
||||
}
|
||||
|
||||
func (this *CreateAction) RunGet(params struct {
|
||||
LibraryFileId int64
|
||||
}) {
|
||||
// 检查权限
|
||||
if !iplibraryutils.CanAccess() {
|
||||
return
|
||||
}
|
||||
this.Data["canAccess"] = true
|
||||
|
||||
// 初始化
|
||||
this.Data["updatingLibraryFile"] = nil
|
||||
|
||||
if params.LibraryFileId > 0 {
|
||||
libraryFileResp, err := this.RPC().IPLibraryFileRPC().FindIPLibraryFile(this.AdminContext(), &pb.FindIPLibraryFileRequest{IpLibraryFileId: params.LibraryFileId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var libraryFile = libraryFileResp.IpLibraryFile
|
||||
if libraryFile != nil {
|
||||
this.Data["updatingLibraryFile"] = maps.Map{
|
||||
"id": libraryFile.Id,
|
||||
"name": libraryFile.Name,
|
||||
"template": libraryFile.Template,
|
||||
"emptyValues": libraryFile.EmptyValues,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package libraries
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
type DeleteAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DeleteAction) RunPost(params struct {
|
||||
LibraryFileId int64
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.IPLibraryFile_LogDeleteIPLibraryFile, params.LibraryFileId)
|
||||
|
||||
_, err := this.RPC().IPLibraryFileRPC().DeleteIPLibraryFile(this.AdminContext(), &pb.DeleteIPLibraryFileRequest{IpLibraryFileId: params.LibraryFileId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package libraries
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/iplibraryutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "library")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
this.Data["canAccess"] = iplibraryutils.CanAccess()
|
||||
|
||||
librariesResp, err := this.RPC().IPLibraryFileRPC().FindAllFinishedIPLibraryFiles(this.AdminContext(), &pb.FindAllFinishedIPLibraryFilesRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var libraryMaps = []maps.Map{}
|
||||
for _, library := range librariesResp.IpLibraryFiles {
|
||||
if library.EmptyValues == nil {
|
||||
library.EmptyValues = []string{}
|
||||
}
|
||||
libraryMaps = append(libraryMaps, maps.Map{
|
||||
"id": library.Id,
|
||||
"name": library.Name,
|
||||
"template": library.Template,
|
||||
"emptyValues": library.EmptyValues,
|
||||
"generatedTime": timeutil.FormatTime("Y-m-d H:i:s", library.GeneratedAt),
|
||||
"generatedFileId": library.GeneratedFileId,
|
||||
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", library.CreatedAt),
|
||||
})
|
||||
}
|
||||
this.Data["libraries"] = libraryMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package library
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
)
|
||||
|
||||
type DownloadAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DownloadAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *DownloadAction) RunGet(params struct {
|
||||
LibraryFileId int64
|
||||
}) {
|
||||
libraryResp, err := this.RPC().IPLibraryFileRPC().FindIPLibraryFile(this.AdminContext(), &pb.FindIPLibraryFileRequest{IpLibraryFileId: params.LibraryFileId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var library = libraryResp.IpLibraryFile
|
||||
if library == nil {
|
||||
this.NotFound("IPLibraryFile", params.LibraryFileId)
|
||||
return
|
||||
}
|
||||
|
||||
var fileId = library.GeneratedFileId
|
||||
if fileId <= 0 {
|
||||
this.WriteString("ip library file not generated")
|
||||
return
|
||||
}
|
||||
|
||||
fileResp, err := this.RPC().FileRPC().FindEnabledFile(this.AdminContext(), &pb.FindEnabledFileRequest{FileId: fileId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var file = fileResp.File
|
||||
if file == nil {
|
||||
this.WriteString("file not found")
|
||||
return
|
||||
}
|
||||
|
||||
chunkIdsResp, err := this.RPC().FileChunkRPC().FindAllFileChunkIds(this.AdminContext(), &pb.FindAllFileChunkIdsRequest{FileId: file.Id})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.AddHeader("Content-Disposition", "attachment; filename=\"ip-library.db\";")
|
||||
this.AddHeader("Content-Length", types.String(file.Size))
|
||||
for _, chunkId := range chunkIdsResp.FileChunkIds {
|
||||
chunkResp, err := this.RPC().FileChunkRPC().DownloadFileChunk(this.AdminContext(), &pb.DownloadFileChunkRequest{FileChunkId: chunkId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var chunk = chunkResp.FileChunk
|
||||
if chunk == nil {
|
||||
break
|
||||
}
|
||||
_, _ = this.Write(chunkResp.FileChunk.Data)
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package library
|
||||
|
||||
import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
// TODO
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package library
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"os"
|
||||
)
|
||||
|
||||
type TestAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *TestAction) Init() {
|
||||
this.Nav("", "ipLibrary", "test")
|
||||
}
|
||||
|
||||
func (this *TestAction) RunGet(params struct{}) {
|
||||
// 通过 RPC 查询 EdgeAPI 的 MaxMind 文件状态
|
||||
statusResp, err := this.RPC().IPLibraryRPC().FindMaxMindFileStatus(this.AdminContext(), &pb.FindMaxMindFileStatusRequest{})
|
||||
if err != nil {
|
||||
// RPC 调用失败,记录错误并使用本地检查作为后备
|
||||
logs.Println("[IP_LIBRARY_TEST]RPC call failed: " + err.Error())
|
||||
// RPC 调用失败,使用本地检查作为后备
|
||||
iplibDir := Tea.Root + "/data/iplibrary"
|
||||
cityDBPath := iplibDir + "/maxmind-city.mmdb"
|
||||
asnDBPath := iplibDir + "/maxmind-asn.mmdb"
|
||||
|
||||
uploadedCityExists := false
|
||||
uploadedASNExists := false
|
||||
|
||||
if _, err := os.Stat(cityDBPath); err == nil {
|
||||
uploadedCityExists = true
|
||||
}
|
||||
if _, err := os.Stat(asnDBPath); err == nil {
|
||||
uploadedASNExists = true
|
||||
}
|
||||
|
||||
// 检查是否使用了MaxMind库(通过测试查询来判断)
|
||||
testIP := "8.8.8.8"
|
||||
testResult := iplibrary.LookupIP(testIP)
|
||||
usingMaxMind := false
|
||||
if testResult != nil && testResult.IsOk() {
|
||||
if testResult.CountryId() == 0 && len(testResult.CountryName()) > 0 {
|
||||
usingMaxMind = true
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["maxMindCityExists"] = uploadedCityExists
|
||||
this.Data["maxMindASNExists"] = uploadedASNExists
|
||||
this.Data["usingMaxMind"] = usingMaxMind
|
||||
this.Data["usingEmbeddedMaxMind"] = usingMaxMind && !uploadedCityExists
|
||||
} else {
|
||||
// 使用 RPC 返回的状态
|
||||
this.Data["maxMindCityExists"] = statusResp.CityExists
|
||||
this.Data["maxMindASNExists"] = statusResp.AsnExists
|
||||
this.Data["usingMaxMind"] = statusResp.UsingMaxMind
|
||||
this.Data["usingEmbeddedMaxMind"] = statusResp.UsingEmbeddedMaxMind
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *TestAction) RunPost(params struct {
|
||||
Ip string
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
if len(params.Ip) == 0 {
|
||||
this.Fail("请输入IP地址")
|
||||
return
|
||||
}
|
||||
|
||||
// 查询IP信息
|
||||
var result = iplibrary.LookupIP(params.Ip)
|
||||
if result == nil || !result.IsOk() {
|
||||
this.Data["result"] = maps.Map{
|
||||
"isOk": false,
|
||||
"ip": params.Ip,
|
||||
"error": "未找到该IP的地理位置信息",
|
||||
}
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
// 判断IP库类型
|
||||
// MaxMind库的特征:CountryId 和 ProvinceId 通常为 0(因为MaxMind不使用ID系统)
|
||||
// 只要查询结果中CountryId为0且有国家名称,就认为是MaxMind(不管文件是否存在,可能是嵌入的)
|
||||
iplibDir := Tea.Root + "/data/iplibrary"
|
||||
cityDBPath := iplibDir + "/maxmind-city.mmdb"
|
||||
|
||||
var libraryType = "默认IP库"
|
||||
var libraryVersion = ""
|
||||
|
||||
// 如果查询结果中CountryId为0(MaxMind特征)且有国家名称,则认为是MaxMind
|
||||
// 不管文件是否存在,因为可能是使用嵌入的 MaxMind 库
|
||||
if result.CountryId() == 0 && len(result.CountryName()) > 0 {
|
||||
libraryType = "MaxMind GeoIP2"
|
||||
libraryVersion = "3"
|
||||
}
|
||||
|
||||
// 通过 RPC 查询 EdgeAPI 的 MaxMind 文件状态
|
||||
statusResp, err := this.RPC().IPLibraryRPC().FindMaxMindFileStatus(this.AdminContext(), &pb.FindMaxMindFileStatusRequest{})
|
||||
uploadedCityExists := false
|
||||
uploadedASNExists := false
|
||||
if err == nil {
|
||||
uploadedCityExists = statusResp.CityExists
|
||||
uploadedASNExists = statusResp.AsnExists
|
||||
} else {
|
||||
// RPC 调用失败,使用本地检查作为后备
|
||||
if _, err := os.Stat(cityDBPath); err == nil {
|
||||
uploadedCityExists = true
|
||||
}
|
||||
asnDBPath := iplibDir + "/maxmind-asn.mmdb"
|
||||
if _, err := os.Stat(asnDBPath); err == nil {
|
||||
uploadedASNExists = true
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是否使用了MaxMind库(即使没有上传文件,也可能使用嵌入的默认MaxMind库)
|
||||
usingMaxMind := false
|
||||
if result.CountryId() == 0 && len(result.CountryName()) > 0 {
|
||||
usingMaxMind = true
|
||||
}
|
||||
|
||||
this.Data["result"] = maps.Map{
|
||||
"isDone": true,
|
||||
"isOk": true,
|
||||
"ip": params.Ip,
|
||||
"libraryType": libraryType,
|
||||
"libraryVersion": libraryVersion,
|
||||
"country": result.CountryName(),
|
||||
"countryId": result.CountryId(),
|
||||
"province": result.ProvinceName(),
|
||||
"provinceId": result.ProvinceId(),
|
||||
"city": result.CityName(),
|
||||
"cityId": result.CityId(),
|
||||
"town": result.TownName(),
|
||||
"townId": result.TownId(),
|
||||
"provider": result.ProviderName(),
|
||||
"providerId": result.ProviderId(),
|
||||
"summary": result.Summary(),
|
||||
"regionSummary": result.RegionSummary(),
|
||||
}
|
||||
this.Data["maxMindCityExists"] = uploadedCityExists
|
||||
this.Data["maxMindASNExists"] = uploadedASNExists
|
||||
this.Data["usingMaxMind"] = usingMaxMind
|
||||
this.Data["usingEmbeddedMaxMind"] = usingMaxMind && !uploadedCityExists
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package providers
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/iplibraryutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "provider")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
// 检查权限
|
||||
if !iplibraryutils.CanAccess() {
|
||||
return
|
||||
}
|
||||
this.Data["canAccess"] = true
|
||||
|
||||
providersResp, err := this.RPC().RegionProviderRPC().FindAllRegionProviders(this.AdminContext(), &pb.FindAllRegionProvidersRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var providerMaps = []maps.Map{}
|
||||
for _, provider := range providersResp.RegionProviders {
|
||||
if provider.Codes == nil {
|
||||
provider.Codes = []string{}
|
||||
}
|
||||
if provider.CustomCodes == nil {
|
||||
provider.CustomCodes = []string{}
|
||||
}
|
||||
providerMaps = append(providerMaps, maps.Map{
|
||||
"id": provider.Id,
|
||||
"name": provider.Name,
|
||||
"codes": provider.Codes,
|
||||
"customName": provider.CustomName,
|
||||
"customCodes": provider.CustomCodes,
|
||||
})
|
||||
}
|
||||
this.Data["providers"] = providerMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package providers
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type UpdatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunGet(params struct {
|
||||
ProviderId int64
|
||||
}) {
|
||||
providerResp, err := this.RPC().RegionProviderRPC().FindRegionProvider(this.AdminContext(), &pb.FindRegionProviderRequest{RegionProviderId: params.ProviderId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var provider = providerResp.RegionProvider
|
||||
if provider == nil {
|
||||
this.NotFound("regionProvider", params.ProviderId)
|
||||
return
|
||||
}
|
||||
|
||||
if provider.Codes == nil {
|
||||
provider.Codes = []string{}
|
||||
}
|
||||
|
||||
if provider.CustomCodes == nil {
|
||||
provider.CustomCodes = []string{}
|
||||
}
|
||||
|
||||
this.Data["provider"] = maps.Map{
|
||||
"id": provider.Id,
|
||||
"name": provider.Name,
|
||||
"codes": provider.Codes,
|
||||
"customName": provider.CustomName,
|
||||
"customCodes": provider.CustomCodes,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunPost(params struct {
|
||||
ProviderId int64
|
||||
CustomName string
|
||||
CustomCodes []string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.RegionProvider_LogUpdateRegionProviderCustom, params.ProviderId)
|
||||
|
||||
_, err := this.RPC().RegionProviderRPC().UpdateRegionProviderCustom(this.AdminContext(), &pb.UpdateRegionProviderCustomRequest{
|
||||
RegionProviderId: params.ProviderId,
|
||||
CustomName: params.CustomName,
|
||||
CustomCodes: params.CustomCodes,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package provinces
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/iplibraryutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "province")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
CountryId int64
|
||||
}) {
|
||||
// 检查权限
|
||||
if !iplibraryutils.CanAccess() {
|
||||
return
|
||||
}
|
||||
this.Data["canAccess"] = true
|
||||
|
||||
if params.CountryId <= 0 {
|
||||
params.CountryId = 1 // china
|
||||
}
|
||||
|
||||
this.Data["countryId"] = params.CountryId
|
||||
|
||||
// 所有国家/地区
|
||||
countriesResp, err := this.RPC().RegionCountryRPC().FindAllRegionCountries(this.AdminContext(), &pb.FindAllRegionCountriesRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var countryMaps = []maps.Map{}
|
||||
for _, country := range countriesResp.RegionCountries {
|
||||
countryMaps = append(countryMaps, maps.Map{
|
||||
"id": country.Id,
|
||||
"name": country.DisplayName,
|
||||
})
|
||||
}
|
||||
this.Data["countries"] = countryMaps
|
||||
|
||||
// 当前国家/地区下的省份
|
||||
provincesResp, err := this.RPC().RegionProvinceRPC().FindAllRegionProvincesWithRegionCountryId(this.AdminContext(), &pb.FindAllRegionProvincesWithRegionCountryIdRequest{
|
||||
RegionCountryId: params.CountryId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var provinceMaps = []maps.Map{}
|
||||
for _, province := range provincesResp.RegionProvinces {
|
||||
if province.Codes == nil {
|
||||
province.Codes = []string{}
|
||||
}
|
||||
if province.CustomCodes == nil {
|
||||
province.CustomCodes = []string{}
|
||||
}
|
||||
provinceMaps = append(provinceMaps, maps.Map{
|
||||
"id": province.Id,
|
||||
"name": province.Name,
|
||||
"codes": province.Codes,
|
||||
"customName": province.CustomName,
|
||||
"customCodes": province.CustomCodes,
|
||||
})
|
||||
}
|
||||
this.Data["provinces"] = provinceMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package provinces
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type UpdatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunGet(params struct {
|
||||
ProvinceId int64
|
||||
}) {
|
||||
provinceResp, err := this.RPC().RegionProvinceRPC().FindRegionProvince(this.AdminContext(), &pb.FindRegionProvinceRequest{RegionProvinceId: params.ProvinceId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var province = provinceResp.RegionProvince
|
||||
if province == nil {
|
||||
this.NotFound("regionProvince", params.ProvinceId)
|
||||
return
|
||||
}
|
||||
if province.Codes == nil {
|
||||
province.Codes = []string{}
|
||||
}
|
||||
if province.CustomCodes == nil {
|
||||
province.CustomCodes = []string{}
|
||||
}
|
||||
this.Data["province"] = maps.Map{
|
||||
"id": province.Id,
|
||||
"name": province.Name,
|
||||
"codes": province.Codes,
|
||||
"customName": province.CustomName,
|
||||
"customCodes": province.CustomCodes,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunPost(params struct {
|
||||
ProvinceId int64
|
||||
CustomName string
|
||||
CustomCodes []string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.RegionProvince_LogUpdateRegionProvinceCustom, params.ProvinceId)
|
||||
|
||||
_, err := this.RPC().RegionProvinceRPC().UpdateRegionProvinceCustom(this.AdminContext(), &pb.UpdateRegionProvinceCustomRequest{
|
||||
RegionProvinceId: params.ProvinceId,
|
||||
CustomName: params.CustomName,
|
||||
CustomCodes: params.CustomCodes,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package towns
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type CityOptionsAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CityOptionsAction) RunPost(params struct {
|
||||
ProvinceId int64
|
||||
}) {
|
||||
citiesResp, err := this.RPC().RegionCityRPC().FindAllRegionCitiesWithRegionProvinceId(this.AdminContext(), &pb.FindAllRegionCitiesWithRegionProvinceIdRequest{
|
||||
RegionProvinceId: params.ProvinceId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var cityMaps = []maps.Map{}
|
||||
for _, city := range citiesResp.RegionCities {
|
||||
cityMaps = append(cityMaps, maps.Map{
|
||||
"id": city.Id,
|
||||
"name": city.DisplayName,
|
||||
})
|
||||
}
|
||||
this.Data["cities"] = cityMaps
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package towns
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/iplibraryutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "town")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
CountryId int64
|
||||
ProvinceId int64
|
||||
CityId int64
|
||||
}) {
|
||||
// 检查权限
|
||||
if !iplibraryutils.CanAccess() {
|
||||
return
|
||||
}
|
||||
this.Data["canAccess"] = true
|
||||
|
||||
if params.CountryId <= 0 || params.ProvinceId <= 0 || params.CityId <= 0 {
|
||||
params.CountryId = 1 // china
|
||||
params.ProvinceId = 1 // beijing
|
||||
params.CityId = 1 // beijing
|
||||
}
|
||||
|
||||
this.Data["countryId"] = params.CountryId
|
||||
this.Data["provinceId"] = params.ProvinceId
|
||||
this.Data["cityId"] = params.CityId
|
||||
|
||||
// 所有国家/地区
|
||||
countriesResp, err := this.RPC().RegionCountryRPC().FindAllRegionCountries(this.AdminContext(), &pb.FindAllRegionCountriesRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var countryMaps = []maps.Map{}
|
||||
for _, country := range countriesResp.RegionCountries {
|
||||
countryMaps = append(countryMaps, maps.Map{
|
||||
"id": country.Id,
|
||||
"name": country.DisplayName,
|
||||
})
|
||||
}
|
||||
this.Data["countries"] = countryMaps
|
||||
|
||||
// 区县列表
|
||||
townsResp, err := this.RPC().RegionTownRPC().FindAllRegionTownsWithRegionCityId(this.AdminContext(), &pb.FindAllRegionTownsWithRegionCityIdRequest{
|
||||
RegionCityId: params.CityId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var townMaps = []maps.Map{}
|
||||
for _, town := range townsResp.RegionTowns {
|
||||
if town.Codes == nil {
|
||||
town.Codes = []string{}
|
||||
}
|
||||
if town.CustomCodes == nil {
|
||||
town.CustomCodes = []string{}
|
||||
}
|
||||
townMaps = append(townMaps, maps.Map{
|
||||
"id": town.Id,
|
||||
"name": town.Name,
|
||||
"codes": town.Codes,
|
||||
"customName": town.CustomName,
|
||||
"customCodes": town.CustomCodes,
|
||||
})
|
||||
}
|
||||
this.Data["towns"] = townMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package towns
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type ProvinceOptionsAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *ProvinceOptionsAction) RunPost(params struct {
|
||||
CountryId int64
|
||||
}) {
|
||||
provincesResp, err := this.RPC().RegionProvinceRPC().FindAllRegionProvincesWithRegionCountryId(this.AdminContext(), &pb.FindAllRegionProvincesWithRegionCountryIdRequest{
|
||||
RegionCountryId: params.CountryId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var provinceMaps = []maps.Map{}
|
||||
for _, province := range provincesResp.RegionProvinces {
|
||||
provinceMaps = append(provinceMaps, maps.Map{
|
||||
"id": province.Id,
|
||||
"name": province.DisplayName,
|
||||
})
|
||||
}
|
||||
this.Data["provinces"] = provinceMaps
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package towns
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type UpdatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunGet(params struct {
|
||||
TownId int64
|
||||
}) {
|
||||
townResp, err := this.RPC().RegionTownRPC().FindRegionTown(this.AdminContext(), &pb.FindRegionTownRequest{RegionTownId: params.TownId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var town = townResp.RegionTown
|
||||
if town == nil {
|
||||
this.NotFound("regionTown", params.TownId)
|
||||
return
|
||||
}
|
||||
if town.Codes == nil {
|
||||
town.Codes = []string{}
|
||||
}
|
||||
if town.CustomCodes == nil {
|
||||
town.CustomCodes = []string{}
|
||||
}
|
||||
this.Data["town"] = maps.Map{
|
||||
"id": town.Id,
|
||||
"name": town.Name,
|
||||
"codes": town.Codes,
|
||||
"customName": town.CustomName,
|
||||
"customCodes": town.CustomCodes,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunPost(params struct {
|
||||
TownId int64
|
||||
CustomName string
|
||||
CustomCodes []string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.RegionTown_LogUpdateRegionTownCustom, params.TownId)
|
||||
|
||||
_, err := this.RPC().RegionTownRPC().UpdateRegionTownCustom(this.AdminContext(), &pb.UpdateRegionTownCustomRequest{
|
||||
RegionTownId: params.TownId,
|
||||
CustomName: params.CustomName,
|
||||
CustomCodes: params.CustomCodes,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
type UpdatePublicAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdatePublicAction) RunPost(params struct {
|
||||
ArtifactId int64
|
||||
IsPublic bool
|
||||
}) {
|
||||
if params.IsPublic {
|
||||
defer this.CreateLogInfo(codes.IPLibraryArtifact_LogUseIPLibraryArtifact, params.ArtifactId)
|
||||
} else {
|
||||
defer this.CreateLogInfo(codes.IPLibraryArtifact_LogCancelIPLibraryArtifact, params.ArtifactId)
|
||||
}
|
||||
|
||||
_, err := this.RPC().IPLibraryArtifactRPC().UpdateIPLibraryArtifactIsPublic(this.AdminContext(), &pb.UpdateIPLibraryArtifactIsPublicRequest{
|
||||
IpLibraryArtifactId: params.ArtifactId,
|
||||
IsPublic: params.IsPublic,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/sizes"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/iplibraryutils"
|
||||
iplib "github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type UploadAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UploadAction) Init() {
|
||||
this.Nav("", "", "upload")
|
||||
}
|
||||
|
||||
func (this *UploadAction) RunGet(params struct{}) {
|
||||
this.Data["canAccess"] = iplibraryutils.CanAccess()
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UploadAction) RunPost(params struct {
|
||||
Name string
|
||||
File *actions.File
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
if len(params.Name) == 0 {
|
||||
this.FailField("name", "请输入IP库名称")
|
||||
return
|
||||
}
|
||||
|
||||
if params.File == nil {
|
||||
this.Fail("请选择要上传的IP库文件")
|
||||
return
|
||||
}
|
||||
|
||||
fp, err := params.File.OriginFile.Open()
|
||||
if err != nil {
|
||||
this.Fail("读取IP库文件失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_ = fp.Close()
|
||||
}()
|
||||
|
||||
data, err := io.ReadAll(io.LimitReader(fp, 64*sizes.M)) // 最大不超过64M
|
||||
if err != nil {
|
||||
this.Fail("读取IP库文件失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 只支持 MaxMind 格式文件(.mmdb)
|
||||
filename := strings.ToLower(params.File.Filename)
|
||||
if !strings.HasSuffix(filename, ".mmdb") {
|
||||
this.Fail("只支持 MaxMind 格式文件(.mmdb),请上传 GeoLite2-City.mmdb 或 GeoLite2-ASN.mmdb 文件")
|
||||
return
|
||||
}
|
||||
|
||||
// MaxMind 格式文件,保存到 data/iplibrary/ 目录
|
||||
iplibDir := Tea.Root + "/data/iplibrary"
|
||||
err = os.MkdirAll(iplibDir, 0755)
|
||||
if err != nil {
|
||||
this.Fail("创建IP库目录失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 根据文件名判断是 City 还是 ASN
|
||||
var targetPath string
|
||||
if strings.Contains(filename, "city") {
|
||||
targetPath = filepath.Join(iplibDir, "maxmind-city.mmdb")
|
||||
} else if strings.Contains(filename, "asn") {
|
||||
targetPath = filepath.Join(iplibDir, "maxmind-asn.mmdb")
|
||||
} else {
|
||||
this.Fail("MaxMind 文件名必须包含 'city' 或 'asn'")
|
||||
return
|
||||
}
|
||||
|
||||
// 保存文件(使用临时文件原子替换)
|
||||
tmpPath := targetPath + ".tmp"
|
||||
err = os.WriteFile(tmpPath, data, 0644)
|
||||
if err != nil {
|
||||
this.Fail("保存IP库文件失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 原子替换
|
||||
err = os.Rename(tmpPath, targetPath)
|
||||
if err != nil {
|
||||
os.Remove(tmpPath)
|
||||
this.Fail("替换IP库文件失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 通过 RPC 将文件上传到 EdgeAPI
|
||||
_, err = this.RPC().IPLibraryRPC().UploadMaxMindFile(this.AdminContext(), &pb.UploadMaxMindFileRequest{
|
||||
Filename: params.File.Filename,
|
||||
Data: data,
|
||||
})
|
||||
if err != nil {
|
||||
logs.Println("[IP_LIBRARY]upload MaxMind file to EdgeAPI failed: " + err.Error())
|
||||
// 继续执行,不影响本地保存
|
||||
}
|
||||
|
||||
// 创建简单的 Meta
|
||||
meta := &iplib.Meta{
|
||||
Version: 3,
|
||||
Code: "maxmind",
|
||||
Author: "MaxMind",
|
||||
}
|
||||
meta.Init()
|
||||
|
||||
// TODO 检查是否要自动创建省市区
|
||||
|
||||
// 上传IP库文件到数据库
|
||||
fileResp, err := this.RPC().FileRPC().CreateFile(this.AdminContext(), &pb.CreateFileRequest{
|
||||
Filename: params.File.Filename,
|
||||
Size: int64(len(data)),
|
||||
IsPublic: false,
|
||||
MimeType: "",
|
||||
Type: "ipLibraryArtifact",
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var fileId = fileResp.FileId
|
||||
|
||||
var buf = make([]byte, 256*1024)
|
||||
var dataReader = bytes.NewReader(data)
|
||||
for {
|
||||
n, err := dataReader.Read(buf)
|
||||
if n > 0 {
|
||||
_, chunkErr := this.RPC().FileChunkRPC().CreateFileChunk(this.AdminContext(), &pb.CreateFileChunkRequest{
|
||||
FileId: fileId,
|
||||
Data: buf[:n],
|
||||
})
|
||||
if chunkErr != nil {
|
||||
this.Fail("上传文件到数据库失败:" + chunkErr.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 创建IP库信息
|
||||
metaJSON, err := json.Marshal(meta)
|
||||
if err != nil {
|
||||
this.Fail("元数据编码失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
createResp, err := this.RPC().IPLibraryArtifactRPC().CreateIPLibraryArtifact(this.AdminContext(), &pb.CreateIPLibraryArtifactRequest{
|
||||
FileId: fileId,
|
||||
MetaJSON: metaJSON,
|
||||
Name: params.Name,
|
||||
})
|
||||
if err != nil {
|
||||
this.Fail("创建IP库失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 获取所有IP库记录,将其他记录的 isPublic 设为 false,新上传的设为 true
|
||||
artifactsResp, err := this.RPC().IPLibraryArtifactRPC().FindAllIPLibraryArtifacts(this.AdminContext(), &pb.FindAllIPLibraryArtifactsRequest{})
|
||||
if err != nil {
|
||||
// 如果获取列表失败,不影响上传成功,只记录日志
|
||||
logs.Println("[IP_LIBRARY]failed to get all artifacts: " + err.Error())
|
||||
} else {
|
||||
// 将所有其他记录设为未使用
|
||||
for _, artifact := range artifactsResp.IpLibraryArtifacts {
|
||||
if artifact.Id != createResp.IpLibraryArtifactId {
|
||||
if artifact.IsPublic {
|
||||
// 将其他使用中的记录设为未使用
|
||||
_, err = this.RPC().IPLibraryArtifactRPC().UpdateIPLibraryArtifactIsPublic(this.AdminContext(), &pb.UpdateIPLibraryArtifactIsPublicRequest{
|
||||
IpLibraryArtifactId: artifact.Id,
|
||||
IsPublic: false,
|
||||
})
|
||||
if err != nil {
|
||||
logs.Println("[IP_LIBRARY]failed to update artifact " + fmt.Sprintf("%d", artifact.Id) + " isPublic: " + err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 将新上传的记录设为使用中
|
||||
_, err = this.RPC().IPLibraryArtifactRPC().UpdateIPLibraryArtifactIsPublic(this.AdminContext(), &pb.UpdateIPLibraryArtifactIsPublicRequest{
|
||||
IpLibraryArtifactId: createResp.IpLibraryArtifactId,
|
||||
IsPublic: true,
|
||||
})
|
||||
if err != nil {
|
||||
logs.Println("[IP_LIBRARY]failed to set new artifact as public: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
17
EdgeAdmin/internal/web/actions/default/settings/lang/init.go
Normal file
17
EdgeAdmin/internal/web/actions/default/settings/lang/init.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package lang
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeServer)).
|
||||
Prefix("/settings/lang").
|
||||
Post("/switch", new(SwitchAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package lang
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
type SwitchAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *SwitchAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *SwitchAction) RunPost(params struct{}) {
|
||||
var langCode = this.LangCode()
|
||||
if len(langCode) == 0 || langCode == "zh-cn" {
|
||||
langCode = "en-us"
|
||||
} else {
|
||||
langCode = "zh-cn"
|
||||
}
|
||||
|
||||
configloaders.UpdateAdminLang(this.AdminId(), langCode)
|
||||
|
||||
_, err := this.RPC().AdminRPC().UpdateAdminLang(this.AdminContext(), &pb.UpdateAdminLangRequest{LangCode: langCode})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package login
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
adminResp, err := this.RPC().AdminRPC().FindEnabledAdmin(this.AdminContext(), &pb.FindEnabledAdminRequest{AdminId: this.AdminId()})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
admin := adminResp.Admin
|
||||
if admin == nil {
|
||||
this.NotFound("admin", this.AdminId())
|
||||
return
|
||||
}
|
||||
this.Data["admin"] = maps.Map{
|
||||
"username": admin.Username,
|
||||
"fullname": admin.Fullname,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunPost(params struct {
|
||||
Username string
|
||||
Password string
|
||||
Password2 string
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.AdminLogin_LogUpdateLogin)
|
||||
|
||||
params.Must.
|
||||
Field("username", params.Username).
|
||||
Require("请输入登录用户名").
|
||||
Match(`^[a-zA-Z0-9_]+$`, "用户名中只能包含英文、数字或下划线")
|
||||
|
||||
existsResp, err := this.RPC().AdminRPC().CheckAdminUsername(this.AdminContext(), &pb.CheckAdminUsernameRequest{
|
||||
AdminId: this.AdminId(),
|
||||
Username: params.Username,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if existsResp.Exists {
|
||||
this.FailField("username", "此用户名已经被别的管理员使用,请换一个")
|
||||
}
|
||||
|
||||
if len(params.Password) > 0 {
|
||||
if params.Password != params.Password2 {
|
||||
this.FailField("password2", "两次输入的密码不一致")
|
||||
}
|
||||
}
|
||||
|
||||
_, err = this.RPC().AdminRPC().UpdateAdminLogin(this.AdminContext(), &pb.UpdateAdminLoginRequest{
|
||||
AdminId: this.AdminId(),
|
||||
Username: params.Username,
|
||||
Password: params.Password,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 通知更新
|
||||
err = configloaders.NotifyAdminModuleMappingChange()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package login
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeCommon)).
|
||||
Helper(settingutils.NewHelper("login")).
|
||||
Prefix("/settings/login").
|
||||
GetPost("", new(IndexAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
adminResp, err := this.RPC().AdminRPC().FindEnabledAdmin(this.AdminContext(), &pb.FindEnabledAdminRequest{AdminId: this.AdminId()})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
admin := adminResp.Admin
|
||||
if admin == nil {
|
||||
this.NotFound("admin", this.AdminId())
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["admin"] = maps.Map{
|
||||
"fullname": admin.Fullname,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunPost(params struct {
|
||||
Fullname string
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.AdminProfile_LogUpdateProfile)
|
||||
|
||||
params.Must.
|
||||
Field("fullname", params.Fullname).
|
||||
Require("请输入你的姓名")
|
||||
|
||||
_, err := this.RPC().AdminRPC().UpdateAdminInfo(this.AdminContext(), &pb.UpdateAdminInfoRequest{
|
||||
AdminId: this.AdminId(),
|
||||
Fullname: params.Fullname,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 通知更新
|
||||
err = configloaders.NotifyAdminModuleMappingChange()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeCommon)).
|
||||
Helper(settingutils.NewHelper("profile")).
|
||||
Prefix("/settings/profile").
|
||||
GetPost("", new(IndexAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package security
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
)
|
||||
|
||||
type DismissXFFPromptAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DismissXFFPromptAction) RunPost(params struct{}) {
|
||||
helpers.DisableXFFPrompt()
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package security
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
ShowAll bool
|
||||
}) {
|
||||
this.Data["showAll"] = params.ShowAll
|
||||
|
||||
config, err := configloaders.LoadSecurityConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if config.AllowIPs == nil {
|
||||
config.AllowIPs = []string{}
|
||||
}
|
||||
|
||||
// 国家和地区
|
||||
var countryMaps = []maps.Map{}
|
||||
for _, countryId := range config.AllowCountryIds {
|
||||
countryResp, err := this.RPC().RegionCountryRPC().FindRegionCountry(this.AdminContext(), &pb.FindRegionCountryRequest{RegionCountryId: countryId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var country = countryResp.RegionCountry
|
||||
if country != nil {
|
||||
countryMaps = append(countryMaps, maps.Map{
|
||||
"id": country.Id,
|
||||
"name": country.DisplayName,
|
||||
})
|
||||
}
|
||||
}
|
||||
this.Data["countries"] = countryMaps
|
||||
|
||||
// 省份
|
||||
var provinceMaps = []maps.Map{}
|
||||
for _, provinceId := range config.AllowProvinceIds {
|
||||
provinceResp, err := this.RPC().RegionProvinceRPC().FindRegionProvince(this.AdminContext(), &pb.FindRegionProvinceRequest{RegionProvinceId: provinceId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var province = provinceResp.RegionProvince
|
||||
if province != nil {
|
||||
provinceMaps = append(provinceMaps, maps.Map{
|
||||
"id": province.Id,
|
||||
"name": province.DisplayName,
|
||||
})
|
||||
}
|
||||
}
|
||||
this.Data["provinces"] = provinceMaps
|
||||
|
||||
this.Data["config"] = config
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunPost(params struct {
|
||||
Frame string
|
||||
CountryIdsJSON []byte
|
||||
ProvinceIdsJSON []byte
|
||||
AllowLocal bool
|
||||
AllowIPs []string
|
||||
AllowRememberLogin bool
|
||||
|
||||
ClientIPHeaderNames string
|
||||
ClientIPHeaderOnly bool
|
||||
|
||||
DenySearchEngines bool
|
||||
DenySpiders bool
|
||||
|
||||
CheckClientFingerprint bool
|
||||
CheckClientRegion bool
|
||||
|
||||
DomainsJSON []byte
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.AdminSecurity_LogUpdateSecuritySettings)
|
||||
|
||||
config, err := configloaders.LoadSecurityConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 框架
|
||||
config.Frame = params.Frame
|
||||
|
||||
// 国家和地区
|
||||
var countryIds = []int64{}
|
||||
if len(params.CountryIdsJSON) > 0 {
|
||||
err = json.Unmarshal(params.CountryIdsJSON, &countryIds)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
config.AllowCountryIds = countryIds
|
||||
|
||||
// 省份
|
||||
var provinceIds = []int64{}
|
||||
if len(params.ProvinceIdsJSON) > 0 {
|
||||
err = json.Unmarshal(params.ProvinceIdsJSON, &provinceIds)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
config.AllowProvinceIds = provinceIds
|
||||
|
||||
// 允许的IP
|
||||
if len(params.AllowIPs) > 0 {
|
||||
for _, ip := range params.AllowIPs {
|
||||
_, err := shared.ParseIPRange(ip)
|
||||
if err != nil {
|
||||
this.Fail("允许访问的IP '" + ip + "' 格式错误:" + err.Error())
|
||||
}
|
||||
}
|
||||
config.AllowIPs = params.AllowIPs
|
||||
} else {
|
||||
config.AllowIPs = []string{}
|
||||
}
|
||||
|
||||
// 允许本地
|
||||
config.AllowLocal = params.AllowLocal
|
||||
|
||||
// 客户端IP获取方式
|
||||
config.ClientIPHeaderNames = params.ClientIPHeaderNames
|
||||
config.ClientIPHeaderOnly = params.ClientIPHeaderOnly
|
||||
|
||||
// 禁止搜索引擎和爬虫
|
||||
config.DenySearchEngines = params.DenySearchEngines
|
||||
config.DenySpiders = params.DenySpiders
|
||||
|
||||
// 允许的域名
|
||||
var domains = []string{}
|
||||
if len(params.DomainsJSON) > 0 {
|
||||
err = json.Unmarshal(params.DomainsJSON, &domains)
|
||||
if err != nil {
|
||||
this.Fail("解析允许访问的域名失败:" + err.Error())
|
||||
}
|
||||
}
|
||||
config.AllowDomains = domains
|
||||
|
||||
// 允许记住登录
|
||||
config.AllowRememberLogin = params.AllowRememberLogin
|
||||
|
||||
// Cookie检查
|
||||
config.CheckClientFingerprint = params.CheckClientFingerprint
|
||||
config.CheckClientRegion = params.CheckClientRegion
|
||||
|
||||
err = configloaders.UpdateSecurityConfig(config)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package security
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)).
|
||||
Helper(settingutils.NewHelper("security")).
|
||||
Prefix("/settings/security").
|
||||
GetPost("", new(IndexAction)).
|
||||
Post("/dismissXFFPrompt", new(DismissXFFPromptAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package adminserverutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/iwind/TeaGo"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"gopkg.in/yaml.v3"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ServerConfigIsChanged = false
|
||||
|
||||
const configFilename = "server.yaml"
|
||||
|
||||
// LoadServerConfig 读取当前服务配置
|
||||
func LoadServerConfig() (*TeaGo.ServerConfig, error) {
|
||||
configFile := Tea.ConfigFile(configFilename)
|
||||
data, err := os.ReadFile(configFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var serverConfig = &TeaGo.ServerConfig{}
|
||||
err = yaml.Unmarshal(data, serverConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return serverConfig, nil
|
||||
}
|
||||
|
||||
// WriteServerConfig 保存当前服务配置
|
||||
func WriteServerConfig(serverConfig *TeaGo.ServerConfig) error {
|
||||
data, err := yaml.Marshal(serverConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile(Tea.ConfigFile(configFilename), data, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ServerConfigIsChanged = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadServerHTTPS 检查HTTPS地址
|
||||
func ReadServerHTTPS() (port int, err error) {
|
||||
config, err := LoadServerConfig()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if config == nil {
|
||||
return 0, errors.New("could not load server config")
|
||||
}
|
||||
|
||||
if config.Https.On && len(config.Https.Listen) > 0 {
|
||||
for _, listen := range config.Https.Listen {
|
||||
_, portString, splitErr := net.SplitHostPort(listen)
|
||||
if splitErr == nil {
|
||||
var portInt = types.Int(portString)
|
||||
if portInt > 0 {
|
||||
// 是否已经启动
|
||||
checkErr := func() error {
|
||||
conn, connErr := net.DialTimeout("tcp", ":"+portString, 1*time.Second)
|
||||
if connErr != nil {
|
||||
return connErr
|
||||
}
|
||||
_ = conn.Close()
|
||||
return nil
|
||||
}()
|
||||
if checkErr != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
port = portInt
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
adminserverutils "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/server/admin-server-utils"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
this.Data["serverIsChanged"] = adminserverutils.ServerConfigIsChanged
|
||||
|
||||
serverConfig, err := adminserverutils.LoadServerConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["serverConfig"] = serverConfig
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeServer)).
|
||||
Helper(settingutils.NewHelper("server")).
|
||||
Prefix("/settings/server").
|
||||
Get("", new(IndexAction)).
|
||||
GetPost("/updateHTTPPopup", new(UpdateHTTPPopupAction)).
|
||||
GetPost("/updateHTTPSPopup", new(UpdateHTTPSPopupAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
adminserverutils "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/server/admin-server-utils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"net"
|
||||
)
|
||||
|
||||
type UpdateHTTPPopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdateHTTPPopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdateHTTPPopupAction) RunGet(params struct{}) {
|
||||
serverConfig, err := adminserverutils.LoadServerConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["serverConfig"] = serverConfig
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdateHTTPPopupAction) RunPost(params struct {
|
||||
IsOn bool
|
||||
Listens []string
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.AdminServer_LogUpdateServerHTTPSettings)
|
||||
|
||||
if len(params.Listens) == 0 {
|
||||
this.Fail("请输入绑定地址")
|
||||
}
|
||||
|
||||
serverConfig, err := adminserverutils.LoadServerConfig()
|
||||
if err != nil {
|
||||
this.Fail("保存失败:" + err.Error())
|
||||
}
|
||||
|
||||
serverConfig.Http.On = params.IsOn
|
||||
|
||||
listen := []string{}
|
||||
for _, addr := range params.Listens {
|
||||
addr = utils.FormatAddress(addr)
|
||||
if len(addr) == 0 {
|
||||
continue
|
||||
}
|
||||
if _, _, err := net.SplitHostPort(addr); err != nil {
|
||||
addr += ":80"
|
||||
}
|
||||
listen = append(listen, addr)
|
||||
}
|
||||
serverConfig.Http.Listen = listen
|
||||
|
||||
err = adminserverutils.WriteServerConfig(serverConfig)
|
||||
if err != nil {
|
||||
this.Fail("保存失败:" + err.Error())
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
adminserverutils "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/server/admin-server-utils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
type UpdateHTTPSPopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdateHTTPSPopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdateHTTPSPopupAction) RunGet(params struct{}) {
|
||||
serverConfig, err := adminserverutils.LoadServerConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["serverConfig"] = serverConfig
|
||||
|
||||
// 证书
|
||||
certConfigs := []*sslconfigs.SSLCertConfig{}
|
||||
if len(serverConfig.Https.Cert) > 0 && len(serverConfig.Https.Key) > 0 {
|
||||
certData, err := os.ReadFile(Tea.Root + "/" + serverConfig.Https.Cert)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
keyData, err := os.ReadFile(Tea.Root + "/" + serverConfig.Https.Key)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
certConfig := &sslconfigs.SSLCertConfig{
|
||||
Id: 0,
|
||||
Name: "-",
|
||||
CertData: certData,
|
||||
KeyData: keyData,
|
||||
}
|
||||
_ = certConfig.Init(context.TODO())
|
||||
certConfig.CertData = nil
|
||||
certConfig.KeyData = nil
|
||||
certConfigs = append(certConfigs, certConfig)
|
||||
}
|
||||
this.Data["certConfigs"] = certConfigs
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdateHTTPSPopupAction) RunPost(params struct {
|
||||
IsOn bool
|
||||
Listens []string
|
||||
CertIdsJSON []byte
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.AdminServer_LogUpdateServerHTTPSSettings)
|
||||
|
||||
if len(params.Listens) == 0 {
|
||||
this.Fail("请输入绑定地址")
|
||||
}
|
||||
|
||||
serverConfig, err := adminserverutils.LoadServerConfig()
|
||||
if err != nil {
|
||||
this.Fail("保存失败:" + err.Error())
|
||||
}
|
||||
|
||||
serverConfig.Https.On = params.IsOn
|
||||
|
||||
listen := []string{}
|
||||
for _, addr := range params.Listens {
|
||||
addr = utils.FormatAddress(addr)
|
||||
if len(addr) == 0 {
|
||||
continue
|
||||
}
|
||||
if _, _, err := net.SplitHostPort(addr); err != nil {
|
||||
addr += ":80"
|
||||
}
|
||||
listen = append(listen, addr)
|
||||
}
|
||||
serverConfig.Https.Listen = listen
|
||||
|
||||
// 证书
|
||||
certIds := []int64{}
|
||||
err = json.Unmarshal(params.CertIdsJSON, &certIds)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if params.IsOn && len(certIds) == 0 {
|
||||
this.Fail("要启用HTTPS,需要先选择或上传一个可用的证书")
|
||||
}
|
||||
|
||||
// 保存证书到本地
|
||||
if len(certIds) > 0 && certIds[0] != 0 {
|
||||
certResp, err := this.RPC().SSLCertRPC().FindEnabledSSLCertConfig(this.AdminContext(), &pb.FindEnabledSSLCertConfigRequest{
|
||||
SslCertId: certIds[0],
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if len(certResp.SslCertJSON) == 0 {
|
||||
this.Fail("选择的证书已失效,请换一个")
|
||||
}
|
||||
|
||||
certConfig := &sslconfigs.SSLCertConfig{}
|
||||
err = json.Unmarshal(certResp.SslCertJSON, certConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
err = os.WriteFile(Tea.ConfigFile("https.key.pem"), certConfig.KeyData, 0666)
|
||||
if err != nil {
|
||||
this.Fail("保存密钥失败:" + err.Error())
|
||||
}
|
||||
err = os.WriteFile(Tea.ConfigFile("https.cert.pem"), certConfig.CertData, 0666)
|
||||
if err != nil {
|
||||
this.Fail("保存证书失败:" + err.Error())
|
||||
}
|
||||
|
||||
serverConfig.Https.Key = "configs/https.key.pem"
|
||||
serverConfig.Https.Cert = "configs/https.cert.pem"
|
||||
}
|
||||
|
||||
err = adminserverutils.WriteServerConfig(serverConfig)
|
||||
if err != nil {
|
||||
this.Fail("保存配置失败:" + err.Error())
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//go:build !plus
|
||||
|
||||
package settingutils
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
type AdvancedHelper struct {
|
||||
helpers.LangHelper
|
||||
|
||||
tab string
|
||||
}
|
||||
|
||||
func NewAdvancedHelper(tab string) *AdvancedHelper {
|
||||
return &AdvancedHelper{
|
||||
tab: tab,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *AdvancedHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext bool) {
|
||||
goNext = true
|
||||
|
||||
var action = actionPtr.Object()
|
||||
|
||||
// 左侧菜单
|
||||
action.Data["teaMenu"] = "settings"
|
||||
action.Data["teaSubMenu"] = "advanced"
|
||||
|
||||
// 标签栏
|
||||
var tabbar = actionutils.NewTabbar()
|
||||
var session = action.Session()
|
||||
var adminId = session.GetInt64(teaconst.SessionAdminId)
|
||||
if configloaders.AllowModule(adminId, configloaders.AdminModuleCodeSetting) {
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabTransfer), "", "/settings/database", "", this.tab == "database")
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabAPINodes), "", "/settings/api", "", this.tab == "apiNodes")
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabAccessLogDatabases), "", "/db", "", this.tab == "dbNodes")
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabTransfer), "", "/settings/transfer", "", this.tab == "transfer")
|
||||
|
||||
//tabbar.Add(codes.AdminSettingsTabBackup, "", "/settings/backup", "", this.tab == "backup")
|
||||
}
|
||||
actionutils.SetTabbar(actionPtr, tabbar)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
//go:build plus
|
||||
|
||||
package settingutils
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
type AdvancedHelper struct {
|
||||
helpers.LangHelper
|
||||
|
||||
tab string
|
||||
}
|
||||
|
||||
func NewAdvancedHelper(tab string) *AdvancedHelper {
|
||||
return &AdvancedHelper{
|
||||
tab: tab,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *AdvancedHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext bool) {
|
||||
goNext = true
|
||||
|
||||
var action = actionPtr.Object()
|
||||
|
||||
// 左侧菜单
|
||||
action.Data["teaMenu"] = "settings"
|
||||
action.Data["teaSubMenu"] = "advanced"
|
||||
|
||||
// 标签栏
|
||||
var tabbar = actionutils.NewTabbar()
|
||||
var session = action.Session()
|
||||
var adminId = session.GetInt64(teaconst.SessionAdminId)
|
||||
if configloaders.AllowModule(adminId, configloaders.AdminModuleCodeSetting) {
|
||||
if this.tab != "authority" {
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabDatabase), "", "/settings/database", "", this.tab == "database")
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabAPINodes), "", "/settings/api", "", this.tab == "apiNodes")
|
||||
if plus.AllowComponent(plus.ComponentCodeUser) {
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabUserNodes), "", "/settings/userNodes", "", this.tab == "userNodes")
|
||||
}
|
||||
// 外层始终显示「日志数据库」与「ClickHouse 配置」两个标签,不随点击变化
|
||||
path := action.Request.URL.Path
|
||||
langCode := strings.ToLower(configloaders.FindAdminLangForAction(actionPtr))
|
||||
mysqlTabName := "Log Databases (MySQL)"
|
||||
clickHouseTabName := "Log Databases (ClickHouse)"
|
||||
if strings.HasPrefix(langCode, "zh") {
|
||||
mysqlTabName = "日志数据库(MySQL)"
|
||||
clickHouseTabName = "日志数据库(ClickHouse)"
|
||||
}
|
||||
tabbar.Add(mysqlTabName, "", "/db", "", (path == "/db" || strings.HasPrefix(path, "/db/")) && path != "/db/clickhouse")
|
||||
tabbar.Add(clickHouseTabName, "", "/db/clickhouse", "", path == "/db/clickhouse")
|
||||
if teaconst.IsPlus {
|
||||
// 目前仅在调试模式下使用
|
||||
if Tea.IsTesting() {
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabIPLibrary), "", "/settings/ip-library", "", this.tab == "ipLibrary")
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabClientOperationSystems), "", "/settings/client-systems", "", this.tab == "clientSystem")
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabClientBrowsers), "", "/settings/client-browsers", "", this.tab == "clientBrowser")
|
||||
}
|
||||
}
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabTransfer), "", "/settings/transfer", "", this.tab == "transfer")
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabAuthority), "", "/settings/authority", "", this.tab == "authority") // 兼容以往版本
|
||||
|
||||
//tabbar.Add(this.Lang(actionPtr, codes.AdminSettings_TabBackup), "", "/settings/backup", "", this.tab == "backup")
|
||||
} else {
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabAuthority), "", "/settings/authority", "", this.tab == "authority")
|
||||
}
|
||||
}
|
||||
actionutils.SetTabbar(actionPtr, tabbar)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//go:build !plus
|
||||
|
||||
package settingutils
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
type Helper struct {
|
||||
helpers.LangHelper
|
||||
|
||||
tab string
|
||||
}
|
||||
|
||||
func NewHelper(tab string) *Helper {
|
||||
return &Helper{
|
||||
tab: tab,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Helper) BeforeAction(actionPtr actions.ActionWrapper) (goNext bool) {
|
||||
goNext = true
|
||||
|
||||
var action = actionPtr.Object()
|
||||
|
||||
// 左侧菜单
|
||||
action.Data["teaMenu"] = "settings"
|
||||
action.Data["teaSubMenu"] = "basic"
|
||||
|
||||
// 标签栏
|
||||
var tabbar = actionutils.NewTabbar()
|
||||
var session = action.Session()
|
||||
var adminId = session.GetInt64(teaconst.SessionAdminId)
|
||||
if configloaders.AllowModule(adminId, configloaders.AdminModuleCodeSetting) {
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabAdminServer), "", "/settings/server", "", this.tab == "server")
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabAdminUI), "", "/settings/ui", "", this.tab == "ui")
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabAdminSecuritySettings), "", "/settings/security", "", this.tab == "security")
|
||||
if teaconst.IsPlus {
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabIPLibrary), "", "/settings/ip-library", "", this.tab == "ipLibrary")
|
||||
}
|
||||
}
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabProfile), "", "/settings/profile", "", this.tab == "profile")
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabLogin), "", "/settings/login", "", this.tab == "login")
|
||||
actionutils.SetTabbar(actionPtr, tabbar)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
//go:build plus
|
||||
|
||||
package settingutils
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
type Helper struct {
|
||||
helpers.LangHelper
|
||||
|
||||
tab string
|
||||
}
|
||||
|
||||
func NewHelper(tab string) *Helper {
|
||||
return &Helper{
|
||||
tab: tab,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Helper) BeforeAction(actionPtr actions.ActionWrapper) (goNext bool) {
|
||||
goNext = true
|
||||
|
||||
var action = actionPtr.Object()
|
||||
|
||||
// 左侧菜单
|
||||
action.Data["teaMenu"] = "settings"
|
||||
action.Data["teaSubMenu"] = "basic"
|
||||
|
||||
// 标签栏
|
||||
var tabbar = actionutils.NewTabbar()
|
||||
var session = action.Session()
|
||||
var adminId = session.GetInt64(teaconst.SessionAdminId)
|
||||
if configloaders.AllowModule(adminId, configloaders.AdminModuleCodeSetting) {
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabAdminServer), "", "/settings/server", "", this.tab == "server")
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabAdminUI), "", "/settings/ui", "", this.tab == "ui")
|
||||
if teaconst.IsPlus {
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabUserUI), "", "/settings/user-ui", "", this.tab == "userUI")
|
||||
}
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabAdminSecuritySettings), "", "/settings/security", "", this.tab == "security")
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabIPLibrary), "", "/settings/ip-library", "", this.tab == "ipLibrary")
|
||||
}
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabProfile), "", "/settings/profile", "", this.tab == "profile")
|
||||
tabbar.Add(this.Lang(actionPtr, codes.AdminSetting_TabLogin), "", "/settings/login", "", this.tab == "login")
|
||||
actionutils.SetTabbar(actionPtr, tabbar)
|
||||
|
||||
return
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user