1.5.0
This commit is contained in:
@@ -128,13 +128,11 @@ function build() {
|
|||||||
|
|
||||||
# copy files
|
# copy files
|
||||||
echo "copying ..."
|
echo "copying ..."
|
||||||
if [ ! -d "$DIST" ]; then
|
rm -rf "$DIST"
|
||||||
mkdir "$DIST"
|
mkdir -p "$DIST"/bin
|
||||||
mkdir "$DIST"/bin
|
mkdir -p "$DIST"/configs
|
||||||
mkdir "$DIST"/configs
|
mkdir -p "$DIST"/logs
|
||||||
mkdir "$DIST"/logs
|
mkdir -p "$DIST"/data
|
||||||
mkdir "$DIST"/data
|
|
||||||
fi
|
|
||||||
cp "$ROOT"/configs/api.template.yaml "$DIST"/configs/
|
cp "$ROOT"/configs/api.template.yaml "$DIST"/configs/
|
||||||
cp "$ROOT"/configs/db.template.yaml "$DIST"/configs/
|
cp "$ROOT"/configs/db.template.yaml "$DIST"/configs/
|
||||||
# 复制 EdgeCommon 的配置文件(如果存在)
|
# 复制 EdgeCommon 的配置文件(如果存在)
|
||||||
@@ -148,42 +146,6 @@ function build() {
|
|||||||
rm -f "$DIST"/deploy/.gitignore
|
rm -f "$DIST"/deploy/.gitignore
|
||||||
cp -R "$ROOT"/installers "$DIST"/
|
cp -R "$ROOT"/installers "$DIST"/
|
||||||
|
|
||||||
# copy fluent-bit templates and local packages from repo root
|
|
||||||
FLUENT_ROOT="$ROOT/../../deploy/fluent-bit"
|
|
||||||
FLUENT_DIST="$DIST/deploy/fluent-bit"
|
|
||||||
if [ -d "$FLUENT_ROOT" ]; then
|
|
||||||
rm -rf "$FLUENT_DIST"
|
|
||||||
mkdir -p "$FLUENT_DIST"
|
|
||||||
|
|
||||||
FLUENT_FILES=(
|
|
||||||
"fluent-bit.conf"
|
|
||||||
"fluent-bit-dns.conf"
|
|
||||||
"fluent-bit-https.conf"
|
|
||||||
"fluent-bit-dns-https.conf"
|
|
||||||
"fluent-bit-windows.conf"
|
|
||||||
"fluent-bit-windows-https.conf"
|
|
||||||
"parsers.conf"
|
|
||||||
"clickhouse-upstream.conf"
|
|
||||||
"clickhouse-upstream-windows.conf"
|
|
||||||
"logrotate.conf"
|
|
||||||
"README.md"
|
|
||||||
)
|
|
||||||
for file in "${FLUENT_FILES[@]}"; do
|
|
||||||
if [ -f "$FLUENT_ROOT/$file" ]; then
|
|
||||||
cp "$FLUENT_ROOT/$file" "$FLUENT_DIST/"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -d "$FLUENT_ROOT/packages" ]; then
|
|
||||||
cp -R "$FLUENT_ROOT/packages" "$FLUENT_DIST/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# remove local runtime artifacts if present
|
|
||||||
rm -f "$FLUENT_DIST/.gitignore"
|
|
||||||
rm -f "$FLUENT_DIST"/logs.db*
|
|
||||||
rm -rf "$FLUENT_DIST/storage"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# building edge installer
|
# building edge installer
|
||||||
echo "building node installer ..."
|
echo "building node installer ..."
|
||||||
architects=("amd64")
|
architects=("amd64")
|
||||||
|
|||||||
@@ -47,6 +47,33 @@ type HTTPDNSAccessLogListFilter struct {
|
|||||||
Size int64
|
Size int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HTTPDNSAccessLogHourlyStat struct {
|
||||||
|
Hour string
|
||||||
|
CountRequests int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPDNSAccessLogDailyStat struct {
|
||||||
|
Day string
|
||||||
|
CountRequests int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPDNSAccessLogTopAppStat struct {
|
||||||
|
AppId string
|
||||||
|
AppName string
|
||||||
|
CountRequests int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPDNSAccessLogTopDomainStat struct {
|
||||||
|
Domain string
|
||||||
|
CountRequests int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPDNSAccessLogTopNodeStat struct {
|
||||||
|
ClusterId uint32
|
||||||
|
NodeId uint32
|
||||||
|
CountRequests int64
|
||||||
|
}
|
||||||
|
|
||||||
type HTTPDNSAccessLogsStore struct {
|
type HTTPDNSAccessLogsStore struct {
|
||||||
client *Client
|
client *Client
|
||||||
}
|
}
|
||||||
@@ -176,6 +203,155 @@ func (s *HTTPDNSAccessLogsStore) List(ctx context.Context, f HTTPDNSAccessLogLis
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *HTTPDNSAccessLogsStore) FindHourlyStats(ctx context.Context, hourFrom string, hourTo string) ([]*HTTPDNSAccessLogHourlyStat, error) {
|
||||||
|
if !s.client.IsConfigured() {
|
||||||
|
return nil, fmt.Errorf("clickhouse: not configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
query := fmt.Sprintf(
|
||||||
|
"SELECT formatDateTime(toDateTime(created_at), '%%Y%%m%%d%%H') AS hour, count() AS count_requests FROM %s WHERE formatDateTime(toDateTime(created_at), '%%Y%%m%%d%%H') BETWEEN '%s' AND '%s' GROUP BY hour ORDER BY hour ASC",
|
||||||
|
s.tableName(),
|
||||||
|
escapeString(hourFrom),
|
||||||
|
escapeString(hourTo),
|
||||||
|
)
|
||||||
|
|
||||||
|
rows := []map[string]interface{}{}
|
||||||
|
if err := s.client.Query(ctx, query, &rows); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]*HTTPDNSAccessLogHourlyStat, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
result = append(result, &HTTPDNSAccessLogHourlyStat{
|
||||||
|
Hour: toString(row["hour"]),
|
||||||
|
CountRequests: toInt64(row["count_requests"]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HTTPDNSAccessLogsStore) FindDailyStats(ctx context.Context, dayFrom string, dayTo string) ([]*HTTPDNSAccessLogDailyStat, error) {
|
||||||
|
if !s.client.IsConfigured() {
|
||||||
|
return nil, fmt.Errorf("clickhouse: not configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
query := fmt.Sprintf(
|
||||||
|
"SELECT day, count() AS count_requests FROM %s WHERE day BETWEEN '%s' AND '%s' GROUP BY day ORDER BY day ASC",
|
||||||
|
s.tableName(),
|
||||||
|
escapeString(dayFrom),
|
||||||
|
escapeString(dayTo),
|
||||||
|
)
|
||||||
|
|
||||||
|
rows := []map[string]interface{}{}
|
||||||
|
if err := s.client.Query(ctx, query, &rows); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]*HTTPDNSAccessLogDailyStat, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
result = append(result, &HTTPDNSAccessLogDailyStat{
|
||||||
|
Day: toString(row["day"]),
|
||||||
|
CountRequests: toInt64(row["count_requests"]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HTTPDNSAccessLogsStore) ListTopApps(ctx context.Context, dayFrom string, dayTo string, limit int64) ([]*HTTPDNSAccessLogTopAppStat, error) {
|
||||||
|
if !s.client.IsConfigured() {
|
||||||
|
return nil, fmt.Errorf("clickhouse: not configured")
|
||||||
|
}
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
query := fmt.Sprintf(
|
||||||
|
"SELECT app_id, max(app_name) AS app_name, count() AS count_requests FROM %s WHERE day BETWEEN '%s' AND '%s' GROUP BY app_id ORDER BY count_requests DESC LIMIT %d",
|
||||||
|
s.tableName(),
|
||||||
|
escapeString(dayFrom),
|
||||||
|
escapeString(dayTo),
|
||||||
|
limit,
|
||||||
|
)
|
||||||
|
|
||||||
|
rows := []map[string]interface{}{}
|
||||||
|
if err := s.client.Query(ctx, query, &rows); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]*HTTPDNSAccessLogTopAppStat, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
result = append(result, &HTTPDNSAccessLogTopAppStat{
|
||||||
|
AppId: toString(row["app_id"]),
|
||||||
|
AppName: toString(row["app_name"]),
|
||||||
|
CountRequests: toInt64(row["count_requests"]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HTTPDNSAccessLogsStore) ListTopDomains(ctx context.Context, dayFrom string, dayTo string, limit int64) ([]*HTTPDNSAccessLogTopDomainStat, error) {
|
||||||
|
if !s.client.IsConfigured() {
|
||||||
|
return nil, fmt.Errorf("clickhouse: not configured")
|
||||||
|
}
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
query := fmt.Sprintf(
|
||||||
|
"SELECT domain, count() AS count_requests FROM %s WHERE day BETWEEN '%s' AND '%s' AND domain != '' GROUP BY domain ORDER BY count_requests DESC LIMIT %d",
|
||||||
|
s.tableName(),
|
||||||
|
escapeString(dayFrom),
|
||||||
|
escapeString(dayTo),
|
||||||
|
limit,
|
||||||
|
)
|
||||||
|
|
||||||
|
rows := []map[string]interface{}{}
|
||||||
|
if err := s.client.Query(ctx, query, &rows); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]*HTTPDNSAccessLogTopDomainStat, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
result = append(result, &HTTPDNSAccessLogTopDomainStat{
|
||||||
|
Domain: toString(row["domain"]),
|
||||||
|
CountRequests: toInt64(row["count_requests"]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HTTPDNSAccessLogsStore) ListTopNodes(ctx context.Context, dayFrom string, dayTo string, limit int64) ([]*HTTPDNSAccessLogTopNodeStat, error) {
|
||||||
|
if !s.client.IsConfigured() {
|
||||||
|
return nil, fmt.Errorf("clickhouse: not configured")
|
||||||
|
}
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
query := fmt.Sprintf(
|
||||||
|
"SELECT min(cluster_id) AS cluster_id, node_id, count() AS count_requests FROM %s WHERE day BETWEEN '%s' AND '%s' GROUP BY node_id ORDER BY count_requests DESC LIMIT %d",
|
||||||
|
s.tableName(),
|
||||||
|
escapeString(dayFrom),
|
||||||
|
escapeString(dayTo),
|
||||||
|
limit,
|
||||||
|
)
|
||||||
|
|
||||||
|
rows := []map[string]interface{}{}
|
||||||
|
if err := s.client.Query(ctx, query, &rows); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]*HTTPDNSAccessLogTopNodeStat, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
result = append(result, &HTTPDNSAccessLogTopNodeStat{
|
||||||
|
ClusterId: uint32(toInt64(row["cluster_id"])),
|
||||||
|
NodeId: uint32(toInt64(row["node_id"])),
|
||||||
|
CountRequests: toInt64(row["count_requests"]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func HTTPDNSRowToPB(row *HTTPDNSAccessLogRow) *pb.HTTPDNSAccessLog {
|
func HTTPDNSRowToPB(row *HTTPDNSAccessLogRow) *pb.HTTPDNSAccessLog {
|
||||||
if row == nil {
|
if row == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package teaconst
|
package teaconst
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "1.4.9" //1.3.9
|
Version = "1.5.0" //1.3.9
|
||||||
|
|
||||||
ProductName = "Edge API"
|
ProductName = "Edge API"
|
||||||
ProcessName = "edge-api"
|
ProcessName = "edge-api"
|
||||||
@@ -17,6 +17,6 @@ const (
|
|||||||
|
|
||||||
// 其他节点版本号,用来检测是否有需要升级的节点
|
// 其他节点版本号,用来检测是否有需要升级的节点
|
||||||
|
|
||||||
NodeVersion = "1.4.9" //1.3.8.2
|
NodeVersion = "1.5.0" //1.3.8.2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
package teaconst
|
package teaconst
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DNSNodeVersion = "1.4.9" //1.3.8.2
|
DNSNodeVersion = "1.5.0" //1.3.8.2
|
||||||
UserNodeVersion = "1.4.9" //1.3.8.2
|
UserNodeVersion = "1.5.0" //1.3.8.2
|
||||||
ReportNodeVersion = "0.1.5"
|
ReportNodeVersion = "1.5.0"
|
||||||
|
|
||||||
DefaultMaxNodes int32 = 50
|
DefaultMaxNodes int32 = 50
|
||||||
)
|
)
|
||||||
|
|||||||
138
EdgeAPI/internal/db/models/httpdns_access_log_dao_stat.go
Normal file
138
EdgeAPI/internal/db/models/httpdns_access_log_dao_stat.go
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/iwind/TeaGo/dbs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HTTPDNSAccessLogHourlyStat struct {
|
||||||
|
Hour string `field:"hour"`
|
||||||
|
CountRequests int64 `field:"countRequests"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPDNSAccessLogDailyStat struct {
|
||||||
|
Day string `field:"day"`
|
||||||
|
CountRequests int64 `field:"countRequests"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPDNSAccessLogTopAppStat struct {
|
||||||
|
AppId string `field:"appId"`
|
||||||
|
AppName string `field:"appName"`
|
||||||
|
CountRequests int64 `field:"countRequests"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPDNSAccessLogTopDomainStat struct {
|
||||||
|
Domain string `field:"domain"`
|
||||||
|
CountRequests int64 `field:"countRequests"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPDNSAccessLogTopNodeStat struct {
|
||||||
|
ClusterId uint32 `field:"clusterId"`
|
||||||
|
NodeId uint32 `field:"nodeId"`
|
||||||
|
CountRequests int64 `field:"countRequests"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSAccessLogDAO) FindHourlyStats(tx *dbs.Tx, hourFrom string, hourTo string) (result []*HTTPDNSAccessLogHourlyStat, err error) {
|
||||||
|
rows, _, err := this.Query(tx).
|
||||||
|
Result("CONCAT(day, LPAD(HOUR(FROM_UNIXTIME(createdAt)),2,'0')) AS hour", "COUNT(*) AS countRequests").
|
||||||
|
Where("CONCAT(day, LPAD(HOUR(FROM_UNIXTIME(createdAt)),2,'0')) BETWEEN :hourFrom AND :hourTo").
|
||||||
|
Param("hourFrom", hourFrom).
|
||||||
|
Param("hourTo", hourTo).
|
||||||
|
Group("hour").
|
||||||
|
FindOnes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, row := range rows {
|
||||||
|
result = append(result, &HTTPDNSAccessLogHourlyStat{
|
||||||
|
Hour: row.GetString("hour"),
|
||||||
|
CountRequests: row.GetInt64("countRequests"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSAccessLogDAO) FindDailyStats(tx *dbs.Tx, dayFrom string, dayTo string) (result []*HTTPDNSAccessLogDailyStat, err error) {
|
||||||
|
rows, _, err := this.Query(tx).
|
||||||
|
Result("day", "COUNT(*) AS countRequests").
|
||||||
|
Between("day", dayFrom, dayTo).
|
||||||
|
Group("day").
|
||||||
|
FindOnes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, row := range rows {
|
||||||
|
result = append(result, &HTTPDNSAccessLogDailyStat{
|
||||||
|
Day: row.GetString("day"),
|
||||||
|
CountRequests: row.GetInt64("countRequests"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSAccessLogDAO) ListTopApps(tx *dbs.Tx, dayFrom string, dayTo string, limit int64) (result []*HTTPDNSAccessLogTopAppStat, err error) {
|
||||||
|
rows, _, err := this.Query(tx).
|
||||||
|
Result("appId", "MAX(appName) AS appName", "COUNT(*) AS countRequests").
|
||||||
|
Between("day", dayFrom, dayTo).
|
||||||
|
Group("appId").
|
||||||
|
Desc("countRequests").
|
||||||
|
Limit(limit).
|
||||||
|
FindOnes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, row := range rows {
|
||||||
|
result = append(result, &HTTPDNSAccessLogTopAppStat{
|
||||||
|
AppId: row.GetString("appId"),
|
||||||
|
AppName: row.GetString("appName"),
|
||||||
|
CountRequests: row.GetInt64("countRequests"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSAccessLogDAO) ListTopDomains(tx *dbs.Tx, dayFrom string, dayTo string, limit int64) (result []*HTTPDNSAccessLogTopDomainStat, err error) {
|
||||||
|
rows, _, err := this.Query(tx).
|
||||||
|
Result("domain", "COUNT(*) AS countRequests").
|
||||||
|
Between("day", dayFrom, dayTo).
|
||||||
|
Where("domain != ''").
|
||||||
|
Group("domain").
|
||||||
|
Desc("countRequests").
|
||||||
|
Limit(limit).
|
||||||
|
FindOnes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, row := range rows {
|
||||||
|
result = append(result, &HTTPDNSAccessLogTopDomainStat{
|
||||||
|
Domain: row.GetString("domain"),
|
||||||
|
CountRequests: row.GetInt64("countRequests"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSAccessLogDAO) ListTopNodes(tx *dbs.Tx, dayFrom string, dayTo string, limit int64) (result []*HTTPDNSAccessLogTopNodeStat, err error) {
|
||||||
|
rows, _, err := this.Query(tx).
|
||||||
|
Result("MIN(clusterId) AS clusterId", "nodeId", "COUNT(*) AS countRequests").
|
||||||
|
Between("day", dayFrom, dayTo).
|
||||||
|
Group("nodeId").
|
||||||
|
Desc("countRequests").
|
||||||
|
Limit(limit).
|
||||||
|
FindOnes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, row := range rows {
|
||||||
|
result = append(result, &HTTPDNSAccessLogTopNodeStat{
|
||||||
|
ClusterId: row.GetUint32("clusterId"),
|
||||||
|
NodeId: row.GetUint32("nodeId"),
|
||||||
|
CountRequests: row.GetInt64("countRequests"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -113,3 +113,11 @@ func (this *HTTPDNSDomainDAO) ListEnabledDomainsWithAppId(tx *dbs.Tx, appDbId in
|
|||||||
_, err = query.Slice(&result).FindAll()
|
_, err = query.Slice(&result).FindAll()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSDomainDAO) CountEnabledDomains(tx *dbs.Tx, appDbId int64) (int64, error) {
|
||||||
|
query := this.Query(tx).State(HTTPDNSDomainStateEnabled)
|
||||||
|
if appDbId > 0 {
|
||||||
|
query = query.Attr("appId", appDbId)
|
||||||
|
}
|
||||||
|
return query.Count()
|
||||||
|
}
|
||||||
|
|||||||
@@ -221,6 +221,41 @@ func (this *NodeValueDAO) ListValuesForNSNodes(tx *dbs.Tx, item string, key stri
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListValuesForHTTPDNSNodes 列出HTTPDNS节点相关的平均数据
|
||||||
|
func (this *NodeValueDAO) ListValuesForHTTPDNSNodes(tx *dbs.Tx, item string, key string, timeRange nodeconfigs.NodeValueRange) (result []*NodeValue, err error) {
|
||||||
|
query := this.Query(tx).
|
||||||
|
Attr("role", "httpdns").
|
||||||
|
Attr("item", item).
|
||||||
|
Result("AVG(JSON_EXTRACT(value, '$." + key + "')) AS value, MIN(createdAt) AS createdAt")
|
||||||
|
|
||||||
|
switch timeRange {
|
||||||
|
// TODO 支持更多的时间范围
|
||||||
|
case nodeconfigs.NodeValueRangeMinute:
|
||||||
|
fromMinute := timeutil.FormatTime("YmdHi", time.Now().Unix()-3600) // 一个小时之前的
|
||||||
|
query.Gte("minute", fromMinute)
|
||||||
|
query.Result("minute")
|
||||||
|
query.Group("minute")
|
||||||
|
default:
|
||||||
|
err = errors.New("invalid 'range' value: '" + timeRange + "'")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = query.Slice(&result).
|
||||||
|
FindAll()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, nodeValue := range result {
|
||||||
|
nodeValue.Value, _ = json.Marshal(maps.Map{
|
||||||
|
key: types.Float32(string(nodeValue.Value)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// SumAllNodeValues 计算所有节点的某项参数值
|
// SumAllNodeValues 计算所有节点的某项参数值
|
||||||
func (this *NodeValueDAO) SumAllNodeValues(tx *dbs.Tx, role string, item nodeconfigs.NodeValueItem, param string, duration int32, durationUnit nodeconfigs.NodeValueDurationUnit) (total float64, avg float64, max float64, err error) {
|
func (this *NodeValueDAO) SumAllNodeValues(tx *dbs.Tx, role string, item nodeconfigs.NodeValueItem, param string, duration int32, durationUnit nodeconfigs.NodeValueDurationUnit) (total float64, avg float64, max float64, err error) {
|
||||||
if duration <= 0 {
|
if duration <= 0 {
|
||||||
|
|||||||
@@ -1,889 +0,0 @@
|
|||||||
package installers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
slashpath "path"
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
|
||||||
"github.com/iwind/TeaGo/Tea"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
fluentBitConfigDir = "/etc/fluent-bit"
|
|
||||||
fluentBitStorageDir = "/var/lib/fluent-bit/storage"
|
|
||||||
fluentBitMainConfigFile = "/etc/fluent-bit/fluent-bit.conf"
|
|
||||||
fluentBitParsersFile = "/etc/fluent-bit/parsers.conf"
|
|
||||||
fluentBitManagedMetaFile = "/etc/fluent-bit/.edge-managed.json"
|
|
||||||
fluentBitManagedEnvFile = "/etc/fluent-bit/.edge-managed.env"
|
|
||||||
fluentBitDropInDir = "/etc/systemd/system/fluent-bit.service.d"
|
|
||||||
fluentBitDropInFile = "/etc/systemd/system/fluent-bit.service.d/edge-managed.conf"
|
|
||||||
fluentBitServiceName = "fluent-bit"
|
|
||||||
fluentBitDefaultBinPath = "/opt/fluent-bit/bin/fluent-bit"
|
|
||||||
fluentBitLocalPackagesRoot = "packages"
|
|
||||||
fluentBitHTTPPathPattern = "/var/log/edge/edge-node/*.log"
|
|
||||||
fluentBitDNSPathPattern = "/var/log/edge/edge-dns/*.log"
|
|
||||||
fluentBitHTTPDNSPathPattern = "/var/log/edge/edge-httpdns/*.log"
|
|
||||||
fluentBitManagedMarker = "managed-by-edgeapi"
|
|
||||||
fluentBitRoleNode = "node"
|
|
||||||
fluentBitRoleDNS = "dns"
|
|
||||||
fluentBitRoleHTTPDNS = "httpdns"
|
|
||||||
)
|
|
||||||
|
|
||||||
var errFluentBitLocalPackageNotFound = errors.New("fluent-bit local package not found")
|
|
||||||
|
|
||||||
var fluentBitPackageFileMapping = map[string]string{
|
|
||||||
"ubuntu22.04-amd64": "fluent-bit_4.2.2_amd64.deb",
|
|
||||||
"ubuntu22.04-arm64": "fluent-bit_4.2.2_arm64.deb",
|
|
||||||
"amzn2023-amd64": "fluent-bit-4.2.2-1.x86_64.rpm",
|
|
||||||
"amzn2023-arm64": "fluent-bit-4.2.2-1.aarch64.rpm",
|
|
||||||
}
|
|
||||||
|
|
||||||
type fluentBitManagedMeta struct {
|
|
||||||
Roles []string `json:"roles"`
|
|
||||||
Hash string `json:"hash"`
|
|
||||||
UpdatedAt int64 `json:"updatedAt"`
|
|
||||||
SourceVersion string `json:"sourceVersion"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type fluentBitDesiredConfig struct {
|
|
||||||
Roles []string
|
|
||||||
ClickHouse *systemconfigs.ClickHouseSetting
|
|
||||||
HTTPPathPattern string
|
|
||||||
DNSPathPattern string
|
|
||||||
HTTPDNSPathPattern string
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetupFluentBit 安装并托管 Fluent Bit 配置(离线包 + 平台渲染配置)。
|
|
||||||
func (this *BaseInstaller) SetupFluentBit(role nodeconfigs.NodeRole) error {
|
|
||||||
if this.client == nil {
|
|
||||||
return errors.New("ssh client is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
uname := this.uname()
|
|
||||||
if !strings.Contains(uname, "Linux") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
tempDir := strings.TrimRight(this.client.UserHome(), "/") + "/.edge-fluent-bit"
|
|
||||||
_, _, _ = this.client.Exec("mkdir -p " + shQuote(tempDir))
|
|
||||||
defer func() {
|
|
||||||
_, _, _ = this.client.Exec("rm -rf " + shQuote(tempDir))
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := this.ensureFluentBitInstalled(tempDir); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, stderr, err := this.client.Exec("mkdir -p " + shQuote(fluentBitConfigDir) + " " + shQuote(fluentBitStorageDir))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("prepare fluent-bit directories failed: %w, stderr: %s", err, stderr)
|
|
||||||
}
|
|
||||||
|
|
||||||
parserContent, err := this.readLocalParsersContent()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
existingMeta, err := this.readManagedMeta()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
mergedRoles, err := mergeManagedRoles(existingMeta, role)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
desiredConfig, err := this.buildDesiredFluentBitConfig(mergedRoles)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
configChanged, err := this.applyManagedConfig(tempDir, desiredConfig, parserContent, existingMeta)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
binPath, err := this.lookupFluentBitBinPath()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := this.ensureFluentBitService(tempDir, binPath, configChanged); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *BaseInstaller) ensureFluentBitInstalled(tempDir string) error {
|
|
||||||
binPath, _ := this.lookupFluentBitBinPath()
|
|
||||||
if binPath != "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
platformKey, packageName, arch, err := this.detectRemotePlatformAndPackage()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("detect fluent-bit platform failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := this.installFluentBitFromLocalPackage(tempDir, arch, packageName); err != nil {
|
|
||||||
if errors.Is(err, errFluentBitLocalPackageNotFound) {
|
|
||||||
expectedPath := filepath.Join("deploy", "fluent-bit", fluentBitLocalPackagesRoot, "linux-"+arch, packageName)
|
|
||||||
return fmt.Errorf("install fluent-bit failed: local package missing for platform '%s', expected '%s'", platformKey, expectedPath)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("install fluent-bit from local package failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
binPath, err = this.lookupFluentBitBinPath()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if binPath == "" {
|
|
||||||
return errors.New("fluent-bit binary not found after local package install")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, stderr, err := this.client.Exec(binPath + " --version")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("verify fluent-bit version failed: %w, stderr: %s", err, stderr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *BaseInstaller) installFluentBitFromLocalPackage(tempDir string, arch string, packageName string) error {
|
|
||||||
packageDir := filepath.Join(Tea.Root, "deploy", "fluent-bit", fluentBitLocalPackagesRoot, "linux-"+arch)
|
|
||||||
localPackagePath := filepath.Join(packageDir, packageName)
|
|
||||||
if _, err := os.Stat(localPackagePath); err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return errFluentBitLocalPackageNotFound
|
|
||||||
}
|
|
||||||
return fmt.Errorf("check local package failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
remotePackagePath := tempDir + "/" + filepath.Base(localPackagePath)
|
|
||||||
if err := this.client.Copy(localPackagePath, remotePackagePath, 0644); err != nil {
|
|
||||||
return fmt.Errorf("upload local package failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var installCmd string
|
|
||||||
lowerName := strings.ToLower(localPackagePath)
|
|
||||||
switch {
|
|
||||||
case strings.HasSuffix(lowerName, ".deb"):
|
|
||||||
installCmd = "dpkg -i " + shQuote(remotePackagePath)
|
|
||||||
case strings.HasSuffix(lowerName, ".rpm"):
|
|
||||||
installCmd = "rpm -Uvh --force " + shQuote(remotePackagePath) + " || rpm -ivh --force " + shQuote(remotePackagePath)
|
|
||||||
case strings.HasSuffix(lowerName, ".tar.gz") || strings.HasSuffix(lowerName, ".tgz"):
|
|
||||||
extractDir := tempDir + "/extract"
|
|
||||||
installCmd = "rm -rf " + shQuote(extractDir) + "; mkdir -p " + shQuote(extractDir) + "; tar -xzf " + shQuote(remotePackagePath) + " -C " + shQuote(extractDir) + "; " +
|
|
||||||
"bin=$(find " + shQuote(extractDir) + " -type f -name fluent-bit | head -n 1); " +
|
|
||||||
"if [ -z \"$bin\" ]; then exit 3; fi; " +
|
|
||||||
"mkdir -p /opt/fluent-bit/bin /usr/local/bin; " +
|
|
||||||
"install -m 0755 \"$bin\" /opt/fluent-bit/bin/fluent-bit; " +
|
|
||||||
"ln -sf /opt/fluent-bit/bin/fluent-bit /usr/local/bin/fluent-bit"
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unsupported local package format: %s", packageName)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, stderr, err := this.client.Exec(installCmd)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("install fluent-bit local package '%s' failed: %w, stderr: %s", filepath.Base(localPackagePath), err, stderr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *BaseInstaller) detectRemotePlatformAndPackage() (platformKey string, packageName string, arch string, err error) {
|
|
||||||
arch, err = this.detectRemoteLinuxArch()
|
|
||||||
if err != nil {
|
|
||||||
return "", "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
releaseData, stderr, err := this.client.Exec("cat /etc/os-release")
|
|
||||||
if err != nil {
|
|
||||||
return "", "", "", fmt.Errorf("read /etc/os-release failed: %w, stderr: %s", err, stderr)
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(releaseData) == "" {
|
|
||||||
return "", "", "", errors.New("/etc/os-release is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
releaseMap := parseOSRelease(releaseData)
|
|
||||||
id := strings.ToLower(strings.TrimSpace(releaseMap["ID"]))
|
|
||||||
versionID := strings.TrimSpace(releaseMap["VERSION_ID"])
|
|
||||||
|
|
||||||
var distro string
|
|
||||||
switch {
|
|
||||||
case id == "ubuntu" && strings.HasPrefix(versionID, "22.04"):
|
|
||||||
distro = "ubuntu22.04"
|
|
||||||
case id == "amzn" && strings.HasPrefix(versionID, "2023"):
|
|
||||||
distro = "amzn2023"
|
|
||||||
default:
|
|
||||||
return "", "", "", fmt.Errorf("unsupported linux platform id='%s' version='%s'", id, versionID)
|
|
||||||
}
|
|
||||||
|
|
||||||
platformKey = distro + "-" + arch
|
|
||||||
packageName, ok := fluentBitPackageFileMapping[platformKey]
|
|
||||||
if !ok {
|
|
||||||
return "", "", "", fmt.Errorf("no local package mapping for platform '%s'", platformKey)
|
|
||||||
}
|
|
||||||
return platformKey, packageName, arch, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseOSRelease(content string) map[string]string {
|
|
||||||
result := map[string]string{}
|
|
||||||
lines := strings.Split(content, "\n")
|
|
||||||
for _, line := range lines {
|
|
||||||
line = strings.TrimSpace(line)
|
|
||||||
if line == "" || strings.HasPrefix(line, "#") || !strings.Contains(line, "=") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
parts := strings.SplitN(line, "=", 2)
|
|
||||||
key := strings.TrimSpace(parts[0])
|
|
||||||
value := strings.TrimSpace(parts[1])
|
|
||||||
value = strings.Trim(value, "\"")
|
|
||||||
result[key] = value
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *BaseInstaller) detectRemoteLinuxArch() (string, error) {
|
|
||||||
stdout, stderr, err := this.client.Exec("uname -m")
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("detect remote arch failed: %w, stderr: %s", err, stderr)
|
|
||||||
}
|
|
||||||
|
|
||||||
arch := strings.ToLower(strings.TrimSpace(stdout))
|
|
||||||
switch arch {
|
|
||||||
case "x86_64", "amd64":
|
|
||||||
return "amd64", nil
|
|
||||||
case "aarch64", "arm64":
|
|
||||||
return "arm64", nil
|
|
||||||
default:
|
|
||||||
return arch, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *BaseInstaller) lookupFluentBitBinPath() (string, error) {
|
|
||||||
stdout, stderr, err := this.client.Exec("if command -v fluent-bit >/dev/null 2>&1; then command -v fluent-bit; elif [ -x " + fluentBitDefaultBinPath + " ]; then echo " + fluentBitDefaultBinPath + "; fi")
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("lookup fluent-bit binary failed: %w, stderr: %s", err, stderr)
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(stdout), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *BaseInstaller) readLocalParsersContent() (string, error) {
|
|
||||||
parsersPath := filepath.Join(Tea.Root, "deploy", "fluent-bit", "parsers.conf")
|
|
||||||
data, err := os.ReadFile(parsersPath)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("read local parsers config failed: %w", err)
|
|
||||||
}
|
|
||||||
return string(data), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *BaseInstaller) readManagedMeta() (*fluentBitManagedMeta, error) {
|
|
||||||
exists, err := this.remoteFileExists(fluentBitManagedMetaFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !exists {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
content, stderr, err := this.client.Exec("cat " + shQuote(fluentBitManagedMetaFile))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("read fluent-bit managed metadata failed: %w, stderr: %s", err, stderr)
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(content) == "" {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
meta := &fluentBitManagedMeta{}
|
|
||||||
if err := json.Unmarshal([]byte(content), meta); err != nil {
|
|
||||||
return nil, fmt.Errorf("decode fluent-bit managed metadata failed: %w", err)
|
|
||||||
}
|
|
||||||
meta.Roles = normalizeRoles(meta.Roles)
|
|
||||||
return meta, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mergeManagedRoles(meta *fluentBitManagedMeta, role nodeconfigs.NodeRole) ([]string, error) {
|
|
||||||
roleName, err := mapNodeRole(role)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
roleSet := map[string]struct{}{}
|
|
||||||
if meta != nil {
|
|
||||||
for _, r := range normalizeRoles(meta.Roles) {
|
|
||||||
roleSet[r] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
roleSet[roleName] = struct{}{}
|
|
||||||
|
|
||||||
roles := make([]string, 0, len(roleSet))
|
|
||||||
for r := range roleSet {
|
|
||||||
roles = append(roles, r)
|
|
||||||
}
|
|
||||||
sort.Strings(roles)
|
|
||||||
return roles, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mapNodeRole(role nodeconfigs.NodeRole) (string, error) {
|
|
||||||
switch role {
|
|
||||||
case nodeconfigs.NodeRoleNode:
|
|
||||||
return fluentBitRoleNode, nil
|
|
||||||
case nodeconfigs.NodeRoleDNS:
|
|
||||||
return fluentBitRoleDNS, nil
|
|
||||||
case nodeconfigs.NodeRoleHTTPDNS:
|
|
||||||
return fluentBitRoleHTTPDNS, nil
|
|
||||||
default:
|
|
||||||
return "", fmt.Errorf("unsupported fluent-bit role '%s'", role)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func normalizeRoles(rawRoles []string) []string {
|
|
||||||
roleSet := map[string]struct{}{}
|
|
||||||
for _, role := range rawRoles {
|
|
||||||
role = strings.ToLower(strings.TrimSpace(role))
|
|
||||||
if role != fluentBitRoleNode && role != fluentBitRoleDNS && role != fluentBitRoleHTTPDNS {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
roleSet[role] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
roles := make([]string, 0, len(roleSet))
|
|
||||||
for role := range roleSet {
|
|
||||||
roles = append(roles, role)
|
|
||||||
}
|
|
||||||
sort.Strings(roles)
|
|
||||||
return roles
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasRole(roles []string, role string) bool {
|
|
||||||
for _, one := range roles {
|
|
||||||
if one == role {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *BaseInstaller) buildDesiredFluentBitConfig(roles []string) (*fluentBitDesiredConfig, error) {
|
|
||||||
if len(roles) == 0 {
|
|
||||||
return nil, errors.New("fluent-bit roles should not be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
ch, err := models.SharedSysSettingDAO.ReadClickHouseConfig(nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("read clickhouse setting failed: %w", err)
|
|
||||||
}
|
|
||||||
if ch == nil {
|
|
||||||
ch = &systemconfigs.ClickHouseSetting{}
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(ch.Host) == "" {
|
|
||||||
ch.Host = "127.0.0.1"
|
|
||||||
}
|
|
||||||
|
|
||||||
ch.Scheme = strings.ToLower(strings.TrimSpace(ch.Scheme))
|
|
||||||
if ch.Scheme == "" {
|
|
||||||
ch.Scheme = "https"
|
|
||||||
}
|
|
||||||
if ch.Scheme != "http" && ch.Scheme != "https" {
|
|
||||||
return nil, fmt.Errorf("unsupported clickhouse scheme '%s'", ch.Scheme)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ch.Port <= 0 {
|
|
||||||
if ch.Scheme == "https" {
|
|
||||||
ch.Port = 8443
|
|
||||||
} else {
|
|
||||||
ch.Port = 8443
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(ch.Database) == "" {
|
|
||||||
ch.Database = "default"
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(ch.User) == "" {
|
|
||||||
ch.User = "default"
|
|
||||||
}
|
|
||||||
// 当前平台策略:后台固定跳过 ClickHouse TLS 证书校验,不暴露 ServerName 配置。
|
|
||||||
ch.TLSSkipVerify = true
|
|
||||||
ch.TLSServerName = ""
|
|
||||||
|
|
||||||
httpPathPattern := fluentBitHTTPPathPattern
|
|
||||||
dnsPathPattern := fluentBitDNSPathPattern
|
|
||||||
httpdnsPathPattern := fluentBitHTTPDNSPathPattern
|
|
||||||
publicPolicyPath, err := this.readPublicAccessLogPolicyPath()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
policyDir := dirFromPolicyPath(publicPolicyPath)
|
|
||||||
if policyDir != "" {
|
|
||||||
pattern := strings.TrimRight(policyDir, "/") + "/*.log"
|
|
||||||
httpPathPattern = pattern
|
|
||||||
dnsPathPattern = pattern
|
|
||||||
httpdnsPathPattern = pattern
|
|
||||||
}
|
|
||||||
|
|
||||||
return &fluentBitDesiredConfig{
|
|
||||||
Roles: normalizeRoles(roles),
|
|
||||||
ClickHouse: ch,
|
|
||||||
HTTPPathPattern: httpPathPattern,
|
|
||||||
DNSPathPattern: dnsPathPattern,
|
|
||||||
HTTPDNSPathPattern: httpdnsPathPattern,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *BaseInstaller) readPublicAccessLogPolicyPath() (string, error) {
|
|
||||||
policyId, err := models.SharedHTTPAccessLogPolicyDAO.FindCurrentPublicPolicyId(nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("find current public access log policy failed: %w", err)
|
|
||||||
}
|
|
||||||
if policyId <= 0 {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
policy, err := models.SharedHTTPAccessLogPolicyDAO.FindEnabledHTTPAccessLogPolicy(nil, policyId)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("read public access log policy failed: %w", err)
|
|
||||||
}
|
|
||||||
if policy == nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.TrimSpace(models.ParseHTTPAccessLogPolicyFilePath(policy)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func dirFromPolicyPath(policyPath string) string {
|
|
||||||
pathValue := strings.TrimSpace(policyPath)
|
|
||||||
if pathValue == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
pathValue = strings.ReplaceAll(pathValue, "\\", "/")
|
|
||||||
dir := slashpath.Dir(pathValue)
|
|
||||||
if dir == "." {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return strings.TrimRight(dir, "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *BaseInstaller) applyManagedConfig(tempDir string, desired *fluentBitDesiredConfig, parserContent string, existingMeta *fluentBitManagedMeta) (bool, error) {
|
|
||||||
mainExists, err := this.remoteFileExists(fluentBitMainConfigFile)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if mainExists && existingMeta == nil {
|
|
||||||
containsMarker, err := this.remoteFileContains(fluentBitMainConfigFile, fluentBitManagedMarker)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if !containsMarker {
|
|
||||||
// Adopt unmanaged config by backing it up and replacing with managed config below.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
configContent, err := renderManagedConfig(desired)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
envContent := renderManagedEnv(desired.ClickHouse)
|
|
||||||
metaContent, newMeta, err := renderManagedMeta(desired, configContent, parserContent, envContent)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
requiredFiles := []string{fluentBitMainConfigFile, fluentBitParsersFile, fluentBitManagedEnvFile, fluentBitManagedMetaFile}
|
|
||||||
if existingMeta != nil && existingMeta.Hash == newMeta.Hash {
|
|
||||||
allExists := true
|
|
||||||
for _, file := range requiredFiles {
|
|
||||||
exists, err := this.remoteFileExists(file)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if !exists {
|
|
||||||
allExists = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if allExists {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if mainExists {
|
|
||||||
backup := fluentBitMainConfigFile + ".bak." + strconv.FormatInt(time.Now().Unix(), 10)
|
|
||||||
_, stderr, err := this.client.Exec("cp -f " + shQuote(fluentBitMainConfigFile) + " " + shQuote(backup))
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("backup existing fluent-bit config failed: %w, stderr: %s", err, stderr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := this.writeRemoteFileByTemp(tempDir, fluentBitMainConfigFile, configContent, 0644); err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if err := this.writeRemoteFileByTemp(tempDir, fluentBitParsersFile, parserContent, 0644); err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if err := this.writeRemoteFileByTemp(tempDir, fluentBitManagedEnvFile, envContent, 0600); err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if err := this.writeRemoteFileByTemp(tempDir, fluentBitManagedMetaFile, metaContent, 0644); err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func renderManagedConfig(desired *fluentBitDesiredConfig) (string, error) {
|
|
||||||
if desired == nil || desired.ClickHouse == nil {
|
|
||||||
return "", errors.New("invalid fluent-bit desired config")
|
|
||||||
}
|
|
||||||
|
|
||||||
scheme := strings.ToLower(strings.TrimSpace(desired.ClickHouse.Scheme))
|
|
||||||
if scheme == "" {
|
|
||||||
scheme = "http"
|
|
||||||
}
|
|
||||||
if scheme != "http" && scheme != "https" {
|
|
||||||
return "", fmt.Errorf("unsupported clickhouse scheme '%s'", desired.ClickHouse.Scheme)
|
|
||||||
}
|
|
||||||
useTLS := scheme == "https"
|
|
||||||
|
|
||||||
insertHTTP := url.QueryEscape(fmt.Sprintf("INSERT INTO %s.logs_ingest FORMAT JSONEachRow", desired.ClickHouse.Database))
|
|
||||||
insertDNS := url.QueryEscape(fmt.Sprintf("INSERT INTO %s.dns_logs_ingest FORMAT JSONEachRow", desired.ClickHouse.Database))
|
|
||||||
insertHTTPDNS := url.QueryEscape(fmt.Sprintf("INSERT INTO %s.httpdns_access_logs_ingest FORMAT JSONEachRow", desired.ClickHouse.Database))
|
|
||||||
|
|
||||||
lines := []string{
|
|
||||||
"# " + fluentBitManagedMarker,
|
|
||||||
"[SERVICE]",
|
|
||||||
" Flush 1",
|
|
||||||
" Log_Level info",
|
|
||||||
" Parsers_File " + fluentBitParsersFile,
|
|
||||||
" storage.path " + fluentBitStorageDir,
|
|
||||||
" storage.sync normal",
|
|
||||||
" storage.checksum off",
|
|
||||||
" storage.backlog.mem_limit 512MB",
|
|
||||||
"",
|
|
||||||
}
|
|
||||||
|
|
||||||
if hasRole(desired.Roles, fluentBitRoleNode) {
|
|
||||||
lines = append(lines,
|
|
||||||
"[INPUT]",
|
|
||||||
" Name tail",
|
|
||||||
" Path "+desired.HTTPPathPattern,
|
|
||||||
" Tag app.http.logs",
|
|
||||||
" Parser json",
|
|
||||||
" Refresh_Interval 2",
|
|
||||||
" Read_from_Head false",
|
|
||||||
" DB /var/lib/fluent-bit/http-logs.db",
|
|
||||||
" storage.type filesystem",
|
|
||||||
" Mem_Buf_Limit 256MB",
|
|
||||||
" Skip_Long_Lines On",
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if hasRole(desired.Roles, fluentBitRoleDNS) {
|
|
||||||
lines = append(lines,
|
|
||||||
"[INPUT]",
|
|
||||||
" Name tail",
|
|
||||||
" Path "+desired.DNSPathPattern,
|
|
||||||
" Tag app.dns.logs",
|
|
||||||
" Parser json",
|
|
||||||
" Refresh_Interval 2",
|
|
||||||
" Read_from_Head false",
|
|
||||||
" DB /var/lib/fluent-bit/dns-logs.db",
|
|
||||||
" storage.type filesystem",
|
|
||||||
" Mem_Buf_Limit 256MB",
|
|
||||||
" Skip_Long_Lines On",
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if hasRole(desired.Roles, fluentBitRoleHTTPDNS) {
|
|
||||||
lines = append(lines,
|
|
||||||
"[INPUT]",
|
|
||||||
" Name tail",
|
|
||||||
" Path "+desired.HTTPDNSPathPattern,
|
|
||||||
" Tag app.httpdns.logs",
|
|
||||||
" Parser json",
|
|
||||||
" Refresh_Interval 2",
|
|
||||||
" Read_from_Head false",
|
|
||||||
" DB /var/lib/fluent-bit/httpdns-logs.db",
|
|
||||||
" storage.type filesystem",
|
|
||||||
" Mem_Buf_Limit 256MB",
|
|
||||||
" Skip_Long_Lines On",
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if hasRole(desired.Roles, fluentBitRoleNode) {
|
|
||||||
lines = append(lines,
|
|
||||||
"[OUTPUT]",
|
|
||||||
" Name http",
|
|
||||||
" Match app.http.logs",
|
|
||||||
" Host "+desired.ClickHouse.Host,
|
|
||||||
" Port "+strconv.Itoa(desired.ClickHouse.Port),
|
|
||||||
" URI /?query="+insertHTTP,
|
|
||||||
" Format json_lines",
|
|
||||||
" http_user ${CH_USER}",
|
|
||||||
" http_passwd ${CH_PASSWORD}",
|
|
||||||
" json_date_key timestamp",
|
|
||||||
" json_date_format epoch",
|
|
||||||
" workers 2",
|
|
||||||
" net.keepalive On",
|
|
||||||
" Retry_Limit False",
|
|
||||||
)
|
|
||||||
if useTLS {
|
|
||||||
lines = append(lines, " tls On")
|
|
||||||
if desired.ClickHouse.TLSSkipVerify {
|
|
||||||
lines = append(lines, " tls.verify Off")
|
|
||||||
} else {
|
|
||||||
lines = append(lines, " tls.verify On")
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(desired.ClickHouse.TLSServerName) != "" {
|
|
||||||
lines = append(lines, " tls.vhost "+strings.TrimSpace(desired.ClickHouse.TLSServerName))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lines = append(lines, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
if hasRole(desired.Roles, fluentBitRoleDNS) {
|
|
||||||
lines = append(lines,
|
|
||||||
"[OUTPUT]",
|
|
||||||
" Name http",
|
|
||||||
" Match app.dns.logs",
|
|
||||||
" Host "+desired.ClickHouse.Host,
|
|
||||||
" Port "+strconv.Itoa(desired.ClickHouse.Port),
|
|
||||||
" URI /?query="+insertDNS,
|
|
||||||
" Format json_lines",
|
|
||||||
" http_user ${CH_USER}",
|
|
||||||
" http_passwd ${CH_PASSWORD}",
|
|
||||||
" json_date_key timestamp",
|
|
||||||
" json_date_format epoch",
|
|
||||||
" workers 2",
|
|
||||||
" net.keepalive On",
|
|
||||||
" Retry_Limit False",
|
|
||||||
)
|
|
||||||
if useTLS {
|
|
||||||
lines = append(lines, " tls On")
|
|
||||||
if desired.ClickHouse.TLSSkipVerify {
|
|
||||||
lines = append(lines, " tls.verify Off")
|
|
||||||
} else {
|
|
||||||
lines = append(lines, " tls.verify On")
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(desired.ClickHouse.TLSServerName) != "" {
|
|
||||||
lines = append(lines, " tls.vhost "+strings.TrimSpace(desired.ClickHouse.TLSServerName))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lines = append(lines, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
if hasRole(desired.Roles, fluentBitRoleHTTPDNS) {
|
|
||||||
lines = append(lines,
|
|
||||||
"[OUTPUT]",
|
|
||||||
" Name http",
|
|
||||||
" Match app.httpdns.logs",
|
|
||||||
" Host "+desired.ClickHouse.Host,
|
|
||||||
" Port "+strconv.Itoa(desired.ClickHouse.Port),
|
|
||||||
" URI /?query="+insertHTTPDNS,
|
|
||||||
" Format json_lines",
|
|
||||||
" http_user ${CH_USER}",
|
|
||||||
" http_passwd ${CH_PASSWORD}",
|
|
||||||
" json_date_key timestamp",
|
|
||||||
" json_date_format epoch",
|
|
||||||
" workers 2",
|
|
||||||
" net.keepalive On",
|
|
||||||
" Retry_Limit False",
|
|
||||||
)
|
|
||||||
if useTLS {
|
|
||||||
lines = append(lines, " tls On")
|
|
||||||
if desired.ClickHouse.TLSSkipVerify {
|
|
||||||
lines = append(lines, " tls.verify Off")
|
|
||||||
} else {
|
|
||||||
lines = append(lines, " tls.verify On")
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(desired.ClickHouse.TLSServerName) != "" {
|
|
||||||
lines = append(lines, " tls.vhost "+strings.TrimSpace(desired.ClickHouse.TLSServerName))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lines = append(lines, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(lines, "\n"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func renderManagedEnv(ch *systemconfigs.ClickHouseSetting) string {
|
|
||||||
user := "default"
|
|
||||||
password := ""
|
|
||||||
if ch != nil {
|
|
||||||
if strings.TrimSpace(ch.User) != "" {
|
|
||||||
user = strings.TrimSpace(ch.User)
|
|
||||||
}
|
|
||||||
password = ch.Password
|
|
||||||
}
|
|
||||||
return "CH_USER=" + strconv.Quote(user) + "\n" +
|
|
||||||
"CH_PASSWORD=" + strconv.Quote(password) + "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
func renderManagedMeta(desired *fluentBitDesiredConfig, configContent string, parserContent string, envContent string) (string, *fluentBitManagedMeta, error) {
|
|
||||||
hashInput := configContent + "\n---\n" + parserContent + "\n---\n" + envContent + "\n---\n" + strings.Join(desired.Roles, ",")
|
|
||||||
hashBytes := sha256.Sum256([]byte(hashInput))
|
|
||||||
hash := fmt.Sprintf("%x", hashBytes[:])
|
|
||||||
|
|
||||||
meta := &fluentBitManagedMeta{
|
|
||||||
Roles: desired.Roles,
|
|
||||||
Hash: hash,
|
|
||||||
UpdatedAt: time.Now().Unix(),
|
|
||||||
SourceVersion: teaconst.Version,
|
|
||||||
}
|
|
||||||
data, err := json.MarshalIndent(meta, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, fmt.Errorf("encode fluent-bit managed metadata failed: %w", err)
|
|
||||||
}
|
|
||||||
return string(data) + "\n", meta, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *BaseInstaller) copyLocalFileToRemote(tempDir string, localPath string, remotePath string, mode os.FileMode) error {
|
|
||||||
tempFile := tempDir + "/" + filepath.Base(remotePath)
|
|
||||||
if err := this.client.Copy(localPath, tempFile, mode); err != nil {
|
|
||||||
return fmt.Errorf("upload fluent-bit file '%s' failed: %w", localPath, err)
|
|
||||||
}
|
|
||||||
_, stderr, err := this.client.Exec("cp -f " + shQuote(tempFile) + " " + shQuote(remotePath) + " && chmod " + fmt.Sprintf("%04o", mode) + " " + shQuote(remotePath))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("install remote fluent-bit file '%s' failed: %w, stderr: %s", remotePath, err, stderr)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *BaseInstaller) writeRemoteFileByTemp(tempDir string, remotePath string, content string, mode os.FileMode) error {
|
|
||||||
tempFile := tempDir + "/" + filepath.Base(remotePath) + ".tmp"
|
|
||||||
if _, err := this.client.WriteFile(tempFile, []byte(content)); err != nil {
|
|
||||||
return fmt.Errorf("write temp fluent-bit file '%s' failed: %w", tempFile, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, stderr, err := this.client.Exec("cp -f " + shQuote(tempFile) + " " + shQuote(remotePath) + " && chmod " + fmt.Sprintf("%04o", mode) + " " + shQuote(remotePath))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("write managed fluent-bit file '%s' failed: %w, stderr: %s", remotePath, err, stderr)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *BaseInstaller) ensureFluentBitService(tempDir string, binPath string, configChanged bool) error {
|
|
||||||
_, _, _ = this.client.Exec("if command -v systemctl >/dev/null 2>&1 && [ ! -f /etc/systemd/system/" + fluentBitServiceName + ".service ] && [ ! -f /lib/systemd/system/" + fluentBitServiceName + ".service ]; then " +
|
|
||||||
"cat > /etc/systemd/system/" + fluentBitServiceName + ".service <<'EOF'\n" +
|
|
||||||
"[Unit]\n" +
|
|
||||||
"Description=Fluent Bit\n" +
|
|
||||||
"After=network.target\n" +
|
|
||||||
"\n" +
|
|
||||||
"[Service]\n" +
|
|
||||||
"ExecStart=" + binPath + " -c " + fluentBitMainConfigFile + "\n" +
|
|
||||||
"Restart=always\n" +
|
|
||||||
"RestartSec=5\n" +
|
|
||||||
"\n" +
|
|
||||||
"[Install]\n" +
|
|
||||||
"WantedBy=multi-user.target\n" +
|
|
||||||
"EOF\n" +
|
|
||||||
"fi")
|
|
||||||
|
|
||||||
stdout, _, err := this.client.Exec("if command -v systemctl >/dev/null 2>&1; then echo 1; else echo 0; fi")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("check systemctl failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.TrimSpace(stdout) == "1" {
|
|
||||||
dropInChanged, err := this.ensureServiceDropIn(tempDir, binPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
restartRequired := configChanged || dropInChanged
|
|
||||||
_, stderr, err := this.client.Exec("systemctl daemon-reload; systemctl enable " + fluentBitServiceName + " >/dev/null 2>&1 || true; " +
|
|
||||||
"if systemctl is-active " + fluentBitServiceName + " >/dev/null 2>&1; then " +
|
|
||||||
"if [ \"" + boolToString(restartRequired) + "\" = \"1\" ]; then systemctl restart " + fluentBitServiceName + "; fi; " +
|
|
||||||
"else systemctl start " + fluentBitServiceName + "; fi")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("ensure fluent-bit service failed: %w, stderr: %s", err, stderr)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if configChanged {
|
|
||||||
_, _, _ = this.client.Exec("pkill -f \"fluent-bit.*fluent-bit.conf\" >/dev/null 2>&1 || true")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _, runningErr := this.client.Exec("pgrep -f \"fluent-bit.*fluent-bit.conf\" >/dev/null 2>&1")
|
|
||||||
if runningErr != nil {
|
|
||||||
startCmd := "set -a; [ -f " + shQuote(fluentBitManagedEnvFile) + " ] && . " + shQuote(fluentBitManagedEnvFile) + "; set +a; " +
|
|
||||||
shQuote(binPath) + " -c " + shQuote(fluentBitMainConfigFile) + " >/dev/null 2>&1 &"
|
|
||||||
_, stderr, err := this.client.Exec(startCmd)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("start fluent-bit without systemd failed: %w, stderr: %s", err, stderr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *BaseInstaller) ensureServiceDropIn(tempDir string, binPath string) (bool, error) {
|
|
||||||
_, stderr, err := this.client.Exec("mkdir -p " + shQuote(fluentBitDropInDir))
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("prepare fluent-bit drop-in dir failed: %w, stderr: %s", err, stderr)
|
|
||||||
}
|
|
||||||
|
|
||||||
content := "[Service]\n" +
|
|
||||||
"EnvironmentFile=-" + fluentBitManagedEnvFile + "\n" +
|
|
||||||
"ExecStart=\n" +
|
|
||||||
"ExecStart=" + binPath + " -c " + fluentBitMainConfigFile + "\n"
|
|
||||||
|
|
||||||
existing, _, _ := this.client.Exec("if [ -f " + shQuote(fluentBitDropInFile) + " ]; then cat " + shQuote(fluentBitDropInFile) + "; fi")
|
|
||||||
if existing == content {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := this.writeRemoteFileByTemp(tempDir, fluentBitDropInFile, content, 0644); err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *BaseInstaller) remoteFileExists(path string) (bool, error) {
|
|
||||||
stdout, stderr, err := this.client.Exec("if [ -f " + shQuote(path) + " ]; then echo 1; else echo 0; fi")
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("check remote file '%s' failed: %w, stderr: %s", path, err, stderr)
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(stdout) == "1", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *BaseInstaller) remoteFileContains(path string, pattern string) (bool, error) {
|
|
||||||
stdout, stderr, err := this.client.Exec("if grep -F " + shQuote(pattern) + " " + shQuote(path) + " >/dev/null 2>&1; then echo 1; else echo 0; fi")
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("check remote file content '%s' failed: %w, stderr: %s", path, err, stderr)
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(stdout) == "1", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func shQuote(value string) string {
|
|
||||||
if value == "" {
|
|
||||||
return "''"
|
|
||||||
}
|
|
||||||
return "'" + strings.ReplaceAll(value, "'", "'\"'\"'") + "'"
|
|
||||||
}
|
|
||||||
|
|
||||||
func boolToString(v bool) string {
|
|
||||||
if v {
|
|
||||||
return "1"
|
|
||||||
}
|
|
||||||
return "0"
|
|
||||||
}
|
|
||||||
@@ -279,3 +279,27 @@ func (this *BaseInstaller) uname() (uname string) {
|
|||||||
|
|
||||||
return "x86_64 GNU/Linux"
|
return "x86_64 GNU/Linux"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseOSRelease(content string) map[string]string {
|
||||||
|
result := map[string]string{}
|
||||||
|
lines := strings.Split(content, "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if line == "" || strings.HasPrefix(line, "#") || !strings.Contains(line, "=") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(line, "=", 2)
|
||||||
|
key := strings.TrimSpace(parts[0])
|
||||||
|
value := strings.TrimSpace(parts[1])
|
||||||
|
value = strings.Trim(value, "\"")
|
||||||
|
result[key] = value
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func shQuote(s string) string {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return "''"
|
||||||
|
}
|
||||||
|
return "'" + strings.ReplaceAll(s, "'", "'\"'\"'") + "'"
|
||||||
|
}
|
||||||
|
|||||||
@@ -131,11 +131,6 @@ https.key: "${keyFile}"`)
|
|||||||
return fmt.Errorf("write '%s': %w", configFile, err)
|
return fmt.Errorf("write '%s': %w", configFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = i.SetupFluentBit(nodeconfigs.NodeRoleHTTPDNS)
|
|
||||||
if err != nil {
|
|
||||||
installStatus.ErrorCode = "SETUP_FLUENT_BIT_FAILED"
|
|
||||||
return fmt.Errorf("setup fluent-bit failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
startCmdPrefix := "cd " + shQuote(appDir+"/configs") + " && ../bin/edge-httpdns "
|
startCmdPrefix := "cd " + shQuote(appDir+"/configs") + " && ../bin/edge-httpdns "
|
||||||
|
|
||||||
|
|||||||
@@ -137,12 +137,6 @@ secret: "${nodeSecret}"`)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 在线安装/更新 Fluent Bit(与边缘节点安装流程联动)
|
|
||||||
err = this.SetupFluentBit(nodeconfigs.NodeRoleNode)
|
|
||||||
if err != nil {
|
|
||||||
installStatus.ErrorCode = "SETUP_FLUENT_BIT_FAILED"
|
|
||||||
return fmt.Errorf("setup fluent-bit failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 测试
|
// 测试
|
||||||
_, stderr, err = this.client.Exec(dir + "/edge-node/bin/edge-node test")
|
_, stderr, err = this.client.Exec(dir + "/edge-node/bin/edge-node test")
|
||||||
|
|||||||
@@ -139,12 +139,6 @@ secret: "${nodeSecret}"`)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 在线安装/更新 Fluent Bit(与 DNS 节点安装流程联动)
|
|
||||||
err = this.SetupFluentBit(nodeconfigs.NodeRoleDNS)
|
|
||||||
if err != nil {
|
|
||||||
installStatus.ErrorCode = "SETUP_FLUENT_BIT_FAILED"
|
|
||||||
return fmt.Errorf("setup fluent-bit failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 测试
|
// 测试
|
||||||
_, stderr, err = this.client.Exec(dir + "/edge-dns/bin/edge-dns test")
|
_, stderr, err = this.client.Exec(dir + "/edge-dns/bin/edge-dns test")
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
|
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/accounts"
|
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/accounts"
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/anti-ddos"
|
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/anti-ddos"
|
||||||
|
httpdnsServices "github.com/TeaOSLab/EdgeAPI/internal/rpc/services/httpdns"
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/nameservers"
|
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/nameservers"
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/posts"
|
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/posts"
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/reporters"
|
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/reporters"
|
||||||
@@ -313,4 +314,9 @@ func APINodeServicesRegister(node *APINode, server *grpc.Server) {
|
|||||||
pb.RegisterPostCategoryServiceServer(server, instance)
|
pb.RegisterPostCategoryServiceServer(server, instance)
|
||||||
node.rest(instance)
|
node.rest(instance)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
var instance = node.serviceInstance(&httpdnsServices.HTTPDNSBoardService{}).(*httpdnsServices.HTTPDNSBoardService)
|
||||||
|
pb.RegisterHTTPDNSBoardServiceServer(server, instance)
|
||||||
|
node.rest(instance)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
324
EdgeAPI/internal/rpc/services/httpdns/service_httpdns_board.go
Normal file
324
EdgeAPI/internal/rpc/services/httpdns/service_httpdns_board.go
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
package httpdns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/clickhouse"
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/dbs"
|
||||||
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTPDNSBoardService HTTPDNS 仪表盘服务
|
||||||
|
type HTTPDNSBoardService struct {
|
||||||
|
services.BaseService
|
||||||
|
}
|
||||||
|
|
||||||
|
// ComposeHTTPDNSBoard 组合看板数据
|
||||||
|
func (this *HTTPDNSBoardService) ComposeHTTPDNSBoard(ctx context.Context, req *pb.ComposeHTTPDNSBoardRequest) (*pb.ComposeHTTPDNSBoardResponse, error) {
|
||||||
|
_, err := this.ValidateAdmin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := this.NullTx()
|
||||||
|
result := &pb.ComposeHTTPDNSBoardResponse{}
|
||||||
|
|
||||||
|
countApps, err := models.SharedHTTPDNSAppDAO.CountEnabledApps(tx, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result.CountApps = countApps
|
||||||
|
|
||||||
|
countDomains, err := models.SharedHTTPDNSDomainDAO.CountEnabledDomains(tx, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result.CountDomains = countDomains
|
||||||
|
|
||||||
|
countClusters, err := models.SharedHTTPDNSClusterDAO.CountEnabledClusters(tx, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result.CountClusters = countClusters
|
||||||
|
|
||||||
|
allNodes, err := models.SharedHTTPDNSNodeDAO.ListEnabledNodes(tx, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result.CountNodes = int64(len(allNodes))
|
||||||
|
|
||||||
|
var countOffline int64
|
||||||
|
for _, node := range allNodes {
|
||||||
|
if !node.IsActive {
|
||||||
|
countOffline++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.CountOfflineNodes = countOffline
|
||||||
|
|
||||||
|
hourFrom := timeutil.Format("YmdH", time.Now().Add(-23*time.Hour))
|
||||||
|
hourTo := timeutil.Format("YmdH")
|
||||||
|
dayFrom := timeutil.Format("Ymd", time.Now().AddDate(0, 0, -14))
|
||||||
|
dayTo := timeutil.Format("Ymd")
|
||||||
|
todayFrom := timeutil.Format("Ymd", time.Now().Add(-24*time.Hour))
|
||||||
|
|
||||||
|
store := clickhouse.NewHTTPDNSAccessLogsStore()
|
||||||
|
if store.Client().IsConfigured() {
|
||||||
|
err = this.composeTrafficAndRanksFromClickHouse(ctx, tx, store, result, hourFrom, hourTo, dayFrom, dayTo, todayFrom)
|
||||||
|
}
|
||||||
|
if err != nil || !store.Client().IsConfigured() {
|
||||||
|
err = this.composeTrafficAndRanksFromMySQL(tx, result, hourFrom, hourTo, dayFrom, dayTo, todayFrom)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = this.fillNodeValues(tx, result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSBoardService) composeTrafficAndRanksFromClickHouse(ctx context.Context, tx *dbs.Tx, store *clickhouse.HTTPDNSAccessLogsStore, result *pb.ComposeHTTPDNSBoardResponse, hourFrom string, hourTo string, dayFrom string, dayTo string, todayFrom string) error {
|
||||||
|
hourlyStats, err := store.FindHourlyStats(ctx, hourFrom, hourTo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hourlyMap := map[string]*clickhouse.HTTPDNSAccessLogHourlyStat{}
|
||||||
|
for _, stat := range hourlyStats {
|
||||||
|
hourlyMap[stat.Hour] = stat
|
||||||
|
}
|
||||||
|
hours, err := utils.RangeHours(hourFrom, hourTo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, hour := range hours {
|
||||||
|
stat, ok := hourlyMap[hour]
|
||||||
|
if ok {
|
||||||
|
result.HourlyTrafficStats = append(result.HourlyTrafficStats, &pb.ComposeHTTPDNSBoardResponse_HourlyTrafficStat{
|
||||||
|
Hour: hour,
|
||||||
|
CountRequests: stat.CountRequests,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
result.HourlyTrafficStats = append(result.HourlyTrafficStats, &pb.ComposeHTTPDNSBoardResponse_HourlyTrafficStat{Hour: hour})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dailyStats, err := store.FindDailyStats(ctx, dayFrom, dayTo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dailyMap := map[string]*clickhouse.HTTPDNSAccessLogDailyStat{}
|
||||||
|
for _, stat := range dailyStats {
|
||||||
|
dailyMap[stat.Day] = stat
|
||||||
|
}
|
||||||
|
days, err := utils.RangeDays(dayFrom, dayTo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, day := range days {
|
||||||
|
stat, ok := dailyMap[day]
|
||||||
|
if ok {
|
||||||
|
result.DailyTrafficStats = append(result.DailyTrafficStats, &pb.ComposeHTTPDNSBoardResponse_DailyTrafficStat{
|
||||||
|
Day: day,
|
||||||
|
CountRequests: stat.CountRequests,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
result.DailyTrafficStats = append(result.DailyTrafficStats, &pb.ComposeHTTPDNSBoardResponse_DailyTrafficStat{Day: day})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
topAppStats, err := store.ListTopApps(ctx, todayFrom, dayTo, 10)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, stat := range topAppStats {
|
||||||
|
appName := stat.AppName
|
||||||
|
if len(appName) == 0 {
|
||||||
|
appName = stat.AppId
|
||||||
|
}
|
||||||
|
result.TopAppStats = append(result.TopAppStats, &pb.ComposeHTTPDNSBoardResponse_TopAppStat{
|
||||||
|
AppId: 0,
|
||||||
|
AppName: appName,
|
||||||
|
CountRequests: stat.CountRequests,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
topDomainStats, err := store.ListTopDomains(ctx, todayFrom, dayTo, 10)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, stat := range topDomainStats {
|
||||||
|
result.TopDomainStats = append(result.TopDomainStats, &pb.ComposeHTTPDNSBoardResponse_TopDomainStat{
|
||||||
|
DomainName: stat.Domain,
|
||||||
|
CountRequests: stat.CountRequests,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
topNodeStats, err := store.ListTopNodes(ctx, todayFrom, dayTo, 10)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, stat := range topNodeStats {
|
||||||
|
nodeName := ""
|
||||||
|
node, nodeErr := models.SharedHTTPDNSNodeDAO.FindEnabledNode(tx, int64(stat.NodeId))
|
||||||
|
if nodeErr == nil && node != nil {
|
||||||
|
nodeName = node.Name
|
||||||
|
}
|
||||||
|
if len(nodeName) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result.TopNodeStats = append(result.TopNodeStats, &pb.ComposeHTTPDNSBoardResponse_TopNodeStat{
|
||||||
|
ClusterId: int64(stat.ClusterId),
|
||||||
|
NodeId: int64(stat.NodeId),
|
||||||
|
NodeName: nodeName,
|
||||||
|
CountRequests: stat.CountRequests,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSBoardService) composeTrafficAndRanksFromMySQL(tx *dbs.Tx, result *pb.ComposeHTTPDNSBoardResponse, hourFrom string, hourTo string, dayFrom string, dayTo string, todayFrom string) error {
|
||||||
|
hourlyStats, err := models.SharedHTTPDNSAccessLogDAO.FindHourlyStats(tx, hourFrom, hourTo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hourlyMap := map[string]*models.HTTPDNSAccessLogHourlyStat{}
|
||||||
|
for _, stat := range hourlyStats {
|
||||||
|
hourlyMap[stat.Hour] = stat
|
||||||
|
}
|
||||||
|
hours, err := utils.RangeHours(hourFrom, hourTo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, hour := range hours {
|
||||||
|
stat, ok := hourlyMap[hour]
|
||||||
|
if ok {
|
||||||
|
result.HourlyTrafficStats = append(result.HourlyTrafficStats, &pb.ComposeHTTPDNSBoardResponse_HourlyTrafficStat{
|
||||||
|
Hour: hour,
|
||||||
|
CountRequests: stat.CountRequests,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
result.HourlyTrafficStats = append(result.HourlyTrafficStats, &pb.ComposeHTTPDNSBoardResponse_HourlyTrafficStat{Hour: hour})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dailyStats, err := models.SharedHTTPDNSAccessLogDAO.FindDailyStats(tx, dayFrom, dayTo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dailyMap := map[string]*models.HTTPDNSAccessLogDailyStat{}
|
||||||
|
for _, stat := range dailyStats {
|
||||||
|
dailyMap[stat.Day] = stat
|
||||||
|
}
|
||||||
|
days, err := utils.RangeDays(dayFrom, dayTo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, day := range days {
|
||||||
|
stat, ok := dailyMap[day]
|
||||||
|
if ok {
|
||||||
|
result.DailyTrafficStats = append(result.DailyTrafficStats, &pb.ComposeHTTPDNSBoardResponse_DailyTrafficStat{
|
||||||
|
Day: day,
|
||||||
|
CountRequests: stat.CountRequests,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
result.DailyTrafficStats = append(result.DailyTrafficStats, &pb.ComposeHTTPDNSBoardResponse_DailyTrafficStat{Day: day})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
topAppStats, err := models.SharedHTTPDNSAccessLogDAO.ListTopApps(tx, todayFrom, dayTo, 10)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, stat := range topAppStats {
|
||||||
|
appName := stat.AppName
|
||||||
|
if len(appName) == 0 {
|
||||||
|
appName = stat.AppId
|
||||||
|
}
|
||||||
|
result.TopAppStats = append(result.TopAppStats, &pb.ComposeHTTPDNSBoardResponse_TopAppStat{
|
||||||
|
AppId: 0,
|
||||||
|
AppName: appName,
|
||||||
|
CountRequests: stat.CountRequests,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
topDomainStats, err := models.SharedHTTPDNSAccessLogDAO.ListTopDomains(tx, todayFrom, dayTo, 10)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, stat := range topDomainStats {
|
||||||
|
result.TopDomainStats = append(result.TopDomainStats, &pb.ComposeHTTPDNSBoardResponse_TopDomainStat{
|
||||||
|
DomainName: stat.Domain,
|
||||||
|
CountRequests: stat.CountRequests,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
topNodeStats, err := models.SharedHTTPDNSAccessLogDAO.ListTopNodes(tx, todayFrom, dayTo, 10)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, stat := range topNodeStats {
|
||||||
|
nodeName := ""
|
||||||
|
node, nodeErr := models.SharedHTTPDNSNodeDAO.FindEnabledNode(tx, int64(stat.NodeId))
|
||||||
|
if nodeErr == nil && node != nil {
|
||||||
|
nodeName = node.Name
|
||||||
|
}
|
||||||
|
if len(nodeName) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result.TopNodeStats = append(result.TopNodeStats, &pb.ComposeHTTPDNSBoardResponse_TopNodeStat{
|
||||||
|
ClusterId: int64(stat.ClusterId),
|
||||||
|
NodeId: int64(stat.NodeId),
|
||||||
|
NodeName: nodeName,
|
||||||
|
CountRequests: stat.CountRequests,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSBoardService) fillNodeValues(tx *dbs.Tx, result *pb.ComposeHTTPDNSBoardResponse) error {
|
||||||
|
cpuValues, err := models.SharedNodeValueDAO.ListValuesForHTTPDNSNodes(tx, nodeconfigs.NodeValueItemCPU, "usage", nodeconfigs.NodeValueRangeMinute)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, v := range cpuValues {
|
||||||
|
result.CpuNodeValues = append(result.CpuNodeValues, &pb.NodeValue{
|
||||||
|
ValueJSON: v.Value,
|
||||||
|
CreatedAt: int64(v.CreatedAt),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryValues, err := models.SharedNodeValueDAO.ListValuesForHTTPDNSNodes(tx, nodeconfigs.NodeValueItemMemory, "usage", nodeconfigs.NodeValueRangeMinute)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, v := range memoryValues {
|
||||||
|
result.MemoryNodeValues = append(result.MemoryNodeValues, &pb.NodeValue{
|
||||||
|
ValueJSON: v.Value,
|
||||||
|
CreatedAt: int64(v.CreatedAt),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
loadValues, err := models.SharedNodeValueDAO.ListValuesForHTTPDNSNodes(tx, nodeconfigs.NodeValueItemLoad, "load1m", nodeconfigs.NodeValueRangeMinute)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, v := range loadValues {
|
||||||
|
result.LoadNodeValues = append(result.LoadNodeValues, &pb.NodeValue{
|
||||||
|
ValueJSON: v.Value,
|
||||||
|
CreatedAt: int64(v.CreatedAt),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ type NodeValueService struct {
|
|||||||
|
|
||||||
// CreateNodeValue 记录数据
|
// CreateNodeValue 记录数据
|
||||||
func (this *NodeValueService) CreateNodeValue(ctx context.Context, req *pb.CreateNodeValueRequest) (*pb.RPCSuccess, error) {
|
func (this *NodeValueService) CreateNodeValue(ctx context.Context, req *pb.CreateNodeValueRequest) (*pb.RPCSuccess, error) {
|
||||||
role, nodeId, _, err := rpcutils.ValidateRequest(ctx, rpcutils.UserTypeNode, rpcutils.UserTypeDNS, rpcutils.UserTypeUser)
|
role, nodeId, _, err := rpcutils.ValidateRequest(ctx, rpcutils.UserTypeNode, rpcutils.UserTypeDNS, rpcutils.UserTypeUser, rpcutils.UserTypeHTTPDNS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -30,6 +30,8 @@ func (this *NodeValueService) CreateNodeValue(ctx context.Context, req *pb.Creat
|
|||||||
clusterId, err = models.SharedNodeDAO.FindNodeClusterId(tx, nodeId)
|
clusterId, err = models.SharedNodeDAO.FindNodeClusterId(tx, nodeId)
|
||||||
case rpcutils.UserTypeDNS:
|
case rpcutils.UserTypeDNS:
|
||||||
clusterId, err = models.SharedNSNodeDAO.FindNodeClusterId(tx, nodeId)
|
clusterId, err = models.SharedNSNodeDAO.FindNodeClusterId(tx, nodeId)
|
||||||
|
case rpcutils.UserTypeHTTPDNS:
|
||||||
|
clusterId, err = models.SharedHTTPDNSNodeDAO.FindNodeClusterId(tx, nodeId)
|
||||||
case rpcutils.UserTypeUser:
|
case rpcutils.UserTypeUser:
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -97,12 +97,10 @@ function build() {
|
|||||||
|
|
||||||
# create dir & copy files
|
# create dir & copy files
|
||||||
echo "copying ..."
|
echo "copying ..."
|
||||||
if [ ! -d "$DIST" ]; then
|
rm -rf "$DIST"
|
||||||
mkdir "$DIST"
|
mkdir -p "$DIST"/bin
|
||||||
mkdir "$DIST"/bin
|
mkdir -p "$DIST"/configs
|
||||||
mkdir "$DIST"/configs
|
mkdir -p "$DIST"/logs
|
||||||
mkdir "$DIST"/logs
|
|
||||||
fi
|
|
||||||
|
|
||||||
cp -R "$ROOT"/../web "$DIST"/
|
cp -R "$ROOT"/../web "$DIST"/
|
||||||
rm -f "$DIST"/web/tmp/*
|
rm -f "$DIST"/web/tmp/*
|
||||||
@@ -135,30 +133,6 @@ function build() {
|
|||||||
rm -f "$(basename "$EDGE_API_ZIP_FILE")"
|
rm -f "$(basename "$EDGE_API_ZIP_FILE")"
|
||||||
|
|
||||||
# ensure edge-api package always contains fluent-bit runtime assets/packages
|
# ensure edge-api package always contains fluent-bit runtime assets/packages
|
||||||
FLUENT_ROOT="$ROOT/../../deploy/fluent-bit"
|
|
||||||
FLUENT_DIST="$DIST/edge-api/deploy/fluent-bit"
|
|
||||||
if [ -d "$FLUENT_ROOT" ]; then
|
|
||||||
verify_fluent_bit_package_matrix "$FLUENT_ROOT" "$ARCH" || exit 1
|
|
||||||
rm -rf "$FLUENT_DIST"
|
|
||||||
mkdir -p "$FLUENT_DIST"
|
|
||||||
|
|
||||||
FLUENT_FILES=(
|
|
||||||
"parsers.conf"
|
|
||||||
)
|
|
||||||
for file in "${FLUENT_FILES[@]}"; do
|
|
||||||
if [ -f "$FLUENT_ROOT/$file" ]; then
|
|
||||||
cp "$FLUENT_ROOT/$file" "$FLUENT_DIST/"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -d "$FLUENT_ROOT/packages" ]; then
|
|
||||||
cp -R "$FLUENT_ROOT/packages" "$FLUENT_DIST/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
rm -f "$FLUENT_DIST/.gitignore"
|
|
||||||
rm -f "$FLUENT_DIST"/logs.db*
|
|
||||||
rm -rf "$FLUENT_DIST/storage"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 鍒犻櫎 MaxMind 鏁版嵁搴撴枃浠讹紙浣跨敤宓屽叆鐨勬暟鎹簱锛屼笉闇€瑕佸閮ㄦ枃浠讹級
|
# 鍒犻櫎 MaxMind 鏁版嵁搴撴枃浠讹紙浣跨敤宓屽叆鐨勬暟鎹簱锛屼笉闇€瑕佸閮ㄦ枃浠讹級
|
||||||
find . -name "*.mmdb" -type f -delete
|
find . -name "*.mmdb" -type f -delete
|
||||||
@@ -220,39 +194,6 @@ function build() {
|
|||||||
echo "[done]"
|
echo "[done]"
|
||||||
}
|
}
|
||||||
|
|
||||||
function verify_fluent_bit_package_matrix() {
|
|
||||||
FLUENT_ROOT=$1
|
|
||||||
ARCH=$2
|
|
||||||
REQUIRED_FILES=()
|
|
||||||
if [ "$ARCH" = "amd64" ]; then
|
|
||||||
REQUIRED_FILES=(
|
|
||||||
"packages/linux-amd64/fluent-bit_4.2.2_amd64.deb"
|
|
||||||
"packages/linux-amd64/fluent-bit-4.2.2-1.x86_64.rpm"
|
|
||||||
)
|
|
||||||
elif [ "$ARCH" = "arm64" ]; then
|
|
||||||
REQUIRED_FILES=(
|
|
||||||
"packages/linux-arm64/fluent-bit_4.2.2_arm64.deb"
|
|
||||||
"packages/linux-arm64/fluent-bit-4.2.2-1.aarch64.rpm"
|
|
||||||
)
|
|
||||||
else
|
|
||||||
echo "[error] unsupported arch for fluent-bit package validation: $ARCH"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
MISSING=0
|
|
||||||
for FILE in "${REQUIRED_FILES[@]}"; do
|
|
||||||
if [ ! -f "$FLUENT_ROOT/$FILE" ]; then
|
|
||||||
echo "[error] fluent-bit matrix package missing: $FLUENT_ROOT/$FILE"
|
|
||||||
MISSING=1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$MISSING" -ne 0 ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
function lookup-version() {
|
function lookup-version() {
|
||||||
FILE=$1
|
FILE=$1
|
||||||
VERSION_DATA=$(cat "$FILE")
|
VERSION_DATA=$(cat "$FILE")
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package teaconst
|
package teaconst
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "1.4.9" //1.3.9
|
Version = "1.5.0" //1.3.9
|
||||||
|
|
||||||
APINodeVersion = "1.4.9" //1.3.9
|
APINodeVersion = "1.5.0" //1.3.9
|
||||||
|
|
||||||
ProductName = "Edge Admin"
|
ProductName = "Edge Admin"
|
||||||
ProcessName = "edge-admin"
|
ProcessName = "edge-admin"
|
||||||
|
|||||||
@@ -248,3 +248,7 @@ func (this *RPCClient) PostCategoryRPC() pb.PostCategoryServiceClient {
|
|||||||
func (this *RPCClient) PostRPC() pb.PostServiceClient {
|
func (this *RPCClient) PostRPC() pb.PostServiceClient {
|
||||||
return pb.NewPostServiceClient(this.pickConn())
|
return pb.NewPostServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *RPCClient) HTTPDNSBoardRPC() pb.HTTPDNSBoardServiceClient {
|
||||||
|
return pb.NewHTTPDNSBoardServiceClient(this.pickConn())
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
nodethresholds "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/settings/thresholds"
|
nodethresholds "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/settings/thresholds"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/cc"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/cc"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/http3"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/http3"
|
||||||
networksecurity "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/network-security"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/pages"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/pages"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/thresholds"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/thresholds"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/uam"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/uam"
|
||||||
@@ -53,7 +52,6 @@ func init() {
|
|||||||
GetPost("/thresholds", new(thresholds.IndexAction)).
|
GetPost("/thresholds", new(thresholds.IndexAction)).
|
||||||
|
|
||||||
//
|
//
|
||||||
GetPost("/network-security", new(networksecurity.IndexAction)).
|
|
||||||
|
|
||||||
// 节点设置相关
|
// 节点设置相关
|
||||||
Prefix("/clusters/cluster/node/settings").
|
Prefix("/clusters/cluster/node/settings").
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
|
||||||
//go:build plus
|
|
||||||
|
|
||||||
package networksecurity
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"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"
|
|
||||||
)
|
|
||||||
|
|
||||||
type IndexAction struct {
|
|
||||||
actionutils.ParentAction
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *IndexAction) Init() {
|
|
||||||
this.Nav("", "setting", "index")
|
|
||||||
this.SecondMenu("networkSecurity")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *IndexAction) RunGet(params struct {
|
|
||||||
ClusterId int64
|
|
||||||
}) {
|
|
||||||
policyResp, err := this.RPC().NodeClusterRPC().FindNodeClusterNetworkSecurityPolicy(this.AdminContext(), &pb.FindNodeClusterNetworkSecurityPolicyRequest{
|
|
||||||
NodeClusterId: params.ClusterId,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var policy = nodeconfigs.NewNetworkSecurityPolicy()
|
|
||||||
if len(policyResp.NetworkSecurityPolicyJSON) > 0 {
|
|
||||||
err = json.Unmarshal(policyResp.NetworkSecurityPolicyJSON, policy)
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.Data["policy"] = policy
|
|
||||||
|
|
||||||
this.Show()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *IndexAction) RunPost(params struct {
|
|
||||||
ClusterId int64
|
|
||||||
Status string
|
|
||||||
|
|
||||||
Must *actions.Must
|
|
||||||
CSRF *actionutils.CSRF
|
|
||||||
}) {
|
|
||||||
policyResp, err := this.RPC().NodeClusterRPC().FindNodeClusterNetworkSecurityPolicy(this.AdminContext(), &pb.FindNodeClusterNetworkSecurityPolicyRequest{
|
|
||||||
NodeClusterId: params.ClusterId,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var policy = nodeconfigs.NewNetworkSecurityPolicy()
|
|
||||||
if len(policyResp.NetworkSecurityPolicyJSON) > 0 {
|
|
||||||
err = json.Unmarshal(policyResp.NetworkSecurityPolicyJSON, policy)
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
policy.Status = params.Status
|
|
||||||
|
|
||||||
err = policy.Init()
|
|
||||||
if err != nil {
|
|
||||||
this.Fail("配置校验失败:" + err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
policyJSON, err := json.Marshal(policy)
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = this.RPC().NodeClusterRPC().UpdateNodeClusterNetworkSecurityPolicy(this.AdminContext(), &pb.UpdateNodeClusterNetworkSecurityPolicyRequest{
|
|
||||||
NodeClusterId: params.ClusterId,
|
|
||||||
NetworkSecurityPolicyJSON: policyJSON,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Success()
|
|
||||||
}
|
|
||||||
@@ -44,17 +44,10 @@ func (this *ClusterHelper) filterMenuItems1(items []maps.Map, info *pb.FindEnabl
|
|||||||
|
|
||||||
func (this *ClusterHelper) filterMenuItems2(items []maps.Map, info *pb.FindEnabledNodeClusterConfigInfoResponse, clusterIdString string, selectedItem string, actionPtr actions.ActionWrapper) []maps.Map {
|
func (this *ClusterHelper) filterMenuItems2(items []maps.Map, info *pb.FindEnabledNodeClusterConfigInfoResponse, clusterIdString string, selectedItem string, actionPtr actions.ActionWrapper) []maps.Map {
|
||||||
if teaconst.IsPlus {
|
if teaconst.IsPlus {
|
||||||
items = append(items, maps.Map{
|
|
||||||
"name": this.Lang(actionPtr, codes.NodeClusterMenu_SettingSecurityPolicy),
|
|
||||||
"url": "/clusters/cluster/settings/network-security?clusterId=" + clusterIdString,
|
|
||||||
"isActive": selectedItem == "networkSecurity",
|
|
||||||
"isOn": info != nil && info.HasNetworkSecurityPolicy, // TODO 将来 加入 info.HasDDoSProtection
|
|
||||||
})
|
|
||||||
|
|
||||||
items = append(items, maps.Map{
|
|
||||||
"name": "-",
|
|
||||||
})
|
|
||||||
if plusutils.CheckComponent(plusutils.ComponentCodeScheduling) {
|
if plusutils.CheckComponent(plusutils.ComponentCodeScheduling) {
|
||||||
|
items = append(items, maps.Map{
|
||||||
|
"name": "-",
|
||||||
|
})
|
||||||
items = append(items, maps.Map{
|
items = append(items, maps.Map{
|
||||||
"name": this.Lang(actionPtr, codes.NodeClusterMenu_SettingSchedule),
|
"name": this.Lang(actionPtr, codes.NodeClusterMenu_SettingSchedule),
|
||||||
"url": "/clusters/cluster/settings/schedule?clusterId=" + clusterIdString,
|
"url": "/clusters/cluster/settings/schedule?clusterId=" + clusterIdString,
|
||||||
@@ -89,14 +82,12 @@ func (this *ClusterHelper) filterMenuItems2(items []maps.Map, info *pb.FindEnabl
|
|||||||
"isOn": info != nil && info.HasSystemServices,
|
"isOn": info != nil && info.HasSystemServices,
|
||||||
})
|
})
|
||||||
|
|
||||||
{
|
items = append(items, maps.Map{
|
||||||
items = append(items, maps.Map{
|
"name": this.Lang(actionPtr, codes.NodeClusterMenu_SettingTOA),
|
||||||
"name": this.Lang(actionPtr, codes.NodeClusterMenu_SettingTOA),
|
"url": "/clusters/cluster/settings/toa?clusterId=" + clusterIdString,
|
||||||
"url": "/clusters/cluster/settings/toa?clusterId=" + clusterIdString,
|
"isActive": selectedItem == "toa",
|
||||||
"isActive": selectedItem == "toa",
|
"isOn": info != nil && info.IsTOAEnabled,
|
||||||
"isOn": info != nil && info.IsTOAEnabled,
|
})
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
//go:build plus
|
||||||
|
|
||||||
|
package boards
|
||||||
|
|
||||||
|
import (
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dashboard/boards/boardutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HTTPDNSAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSAction) Init() {
|
||||||
|
this.Nav("", "", "httpdns")
|
||||||
|
this.ViewDir("@default")
|
||||||
|
this.View("dashboard/boards/httpdns")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSAction) RunGet(params struct{}) {
|
||||||
|
if !teaconst.IsPlus {
|
||||||
|
this.RedirectURL("/dashboard")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := boardutils.InitBoard(this.Parent())
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["board"] = maps.Map{
|
||||||
|
"countApps": 0,
|
||||||
|
"countDomains": 0,
|
||||||
|
"countClusters": 0,
|
||||||
|
"countNodes": 0,
|
||||||
|
"countOfflineNodes": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSAction) RunPost(params struct{}) {
|
||||||
|
resp, err := this.RPC().HTTPDNSBoardRPC().ComposeHTTPDNSBoard(this.AdminContext(), &pb.ComposeHTTPDNSBoardRequest{})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.Data["board"] = maps.Map{
|
||||||
|
"countApps": resp.CountApps,
|
||||||
|
"countDomains": resp.CountDomains,
|
||||||
|
"countClusters": resp.CountClusters,
|
||||||
|
"countNodes": resp.CountNodes,
|
||||||
|
"countOfflineNodes": resp.CountOfflineNodes,
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var statMaps = []maps.Map{}
|
||||||
|
for _, stat := range resp.HourlyTrafficStats {
|
||||||
|
statMaps = append(statMaps, maps.Map{
|
||||||
|
"day": stat.Hour[4:6] + "月" + stat.Hour[6:8] + "日",
|
||||||
|
"hour": stat.Hour[8:],
|
||||||
|
"countRequests": stat.CountRequests,
|
||||||
|
"bytes": stat.Bytes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["hourlyStats"] = statMaps
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var statMaps = []maps.Map{}
|
||||||
|
for _, stat := range resp.DailyTrafficStats {
|
||||||
|
statMaps = append(statMaps, maps.Map{
|
||||||
|
"day": stat.Day[4:6] + "月" + stat.Day[6:] + "日",
|
||||||
|
"countRequests": stat.CountRequests,
|
||||||
|
"bytes": stat.Bytes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["dailyStats"] = statMaps
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var statMaps = []maps.Map{}
|
||||||
|
for _, stat := range resp.TopAppStats {
|
||||||
|
statMaps = append(statMaps, maps.Map{
|
||||||
|
"appId": stat.AppId,
|
||||||
|
"appName": stat.AppName,
|
||||||
|
"countRequests": stat.CountRequests,
|
||||||
|
"bytes": stat.Bytes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["topAppStats"] = statMaps
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var statMaps = []maps.Map{}
|
||||||
|
for _, stat := range resp.TopDomainStats {
|
||||||
|
statMaps = append(statMaps, maps.Map{
|
||||||
|
"domainId": stat.DomainId,
|
||||||
|
"domainName": stat.DomainName,
|
||||||
|
"countRequests": stat.CountRequests,
|
||||||
|
"bytes": stat.Bytes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["topDomainStats"] = statMaps
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var statMaps = []maps.Map{}
|
||||||
|
for _, stat := range resp.TopNodeStats {
|
||||||
|
statMaps = append(statMaps, maps.Map{
|
||||||
|
"clusterId": stat.ClusterId,
|
||||||
|
"nodeId": stat.NodeId,
|
||||||
|
"nodeName": stat.NodeName,
|
||||||
|
"countRequests": stat.CountRequests,
|
||||||
|
"bytes": stat.Bytes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["topNodeStats"] = statMaps
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
|
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
"regexp"
|
"regexp"
|
||||||
"time"
|
"time"
|
||||||
@@ -409,5 +410,55 @@ func (this *IndexAction) RunPost(params struct {
|
|||||||
}
|
}
|
||||||
this.Data["countWeakAdmins"] = countWeakAdminsResp.Count
|
this.Data["countWeakAdmins"] = countWeakAdminsResp.Count
|
||||||
|
|
||||||
|
upgradeConfig, err := configloaders.LoadUpgradeConfig()
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.Data["autoUpgrade"] = upgradeConfig.AutoUpgrade
|
||||||
|
|
||||||
|
httpdnsNodeUpgradeInfo, err := this.composeHTTPDNSNodeUpgradeInfo()
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.Data["httpdnsNodeUpgradeInfo"] = httpdnsNodeUpgradeInfo
|
||||||
|
|
||||||
this.Success()
|
this.Success()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *IndexAction) composeHTTPDNSNodeUpgradeInfo() (maps.Map, error) {
|
||||||
|
clustersResp, err := this.RPC().HTTPDNSClusterRPC().ListHTTPDNSClusters(this.AdminContext(), &pb.ListHTTPDNSClustersRequest{
|
||||||
|
Offset: 0,
|
||||||
|
Size: 10000,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
version := ""
|
||||||
|
for _, cluster := range clustersResp.Clusters {
|
||||||
|
resp, err := this.RPC().HTTPDNSNodeRPC().FindAllUpgradeHTTPDNSNodesWithClusterId(this.AdminContext(), &pb.FindAllUpgradeHTTPDNSNodesWithClusterIdRequest{
|
||||||
|
ClusterId: cluster.Id,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
count += len(resp.Nodes)
|
||||||
|
for _, nodeUpgrade := range resp.Nodes {
|
||||||
|
if len(nodeUpgrade.NewVersion) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(version) == 0 || stringutil.VersionCompare(version, nodeUpgrade.NewVersion) < 0 {
|
||||||
|
version = nodeUpgrade.NewVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maps.Map{
|
||||||
|
"count": count,
|
||||||
|
"version": version,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -245,5 +245,12 @@ func (this *IndexAction) RunPost(params struct{}) {
|
|||||||
}
|
}
|
||||||
this.Data["countWeakAdmins"] = countWeakAdminsResp.Count
|
this.Data["countWeakAdmins"] = countWeakAdminsResp.Count
|
||||||
|
|
||||||
|
upgradeConfig, err := configloaders.LoadUpgradeConfig()
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.Data["autoUpgrade"] = upgradeConfig.AutoUpgrade
|
||||||
|
|
||||||
this.Success()
|
this.Success()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ func init() {
|
|||||||
Get("/waf", new(boards.WafAction)).
|
Get("/waf", new(boards.WafAction)).
|
||||||
Post("/wafLogs", new(boards.WafLogsAction)).
|
Post("/wafLogs", new(boards.WafLogsAction)).
|
||||||
GetPost("/dns", new(boards.DnsAction)).
|
GetPost("/dns", new(boards.DnsAction)).
|
||||||
|
GetPost("/httpdns", new(boards.HTTPDNSAction)).
|
||||||
Get("/user", new(boards.UserAction)).
|
Get("/user", new(boards.UserAction)).
|
||||||
Get("/events", new(boards.EventsAction)).
|
Get("/events", new(boards.EventsAction)).
|
||||||
Post("/readLogs", new(boards.ReadLogsAction)).
|
Post("/readLogs", new(boards.ReadLogsAction)).
|
||||||
|
|||||||
@@ -1,11 +1,110 @@
|
|||||||
package httpdns
|
package httpdns
|
||||||
|
|
||||||
import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
)
|
||||||
|
|
||||||
type IndexAction struct {
|
type IndexAction struct {
|
||||||
actionutils.ParentAction
|
actionutils.ParentAction
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *IndexAction) RunGet(params struct{}) {
|
func (this *IndexAction) Init() {
|
||||||
this.RedirectURL("/httpdns/clusters")
|
this.Nav("", "", "httpdns")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *IndexAction) RunGet(params struct{}) {
|
||||||
|
this.Data["board"] = maps.Map{
|
||||||
|
"countApps": 0,
|
||||||
|
"countDomains": 0,
|
||||||
|
"countClusters": 0,
|
||||||
|
"countNodes": 0,
|
||||||
|
"countOfflineNodes": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *IndexAction) RunPost(params struct{}) {
|
||||||
|
resp, err := this.RPC().HTTPDNSBoardRPC().ComposeHTTPDNSBoard(this.AdminContext(), &pb.ComposeHTTPDNSBoardRequest{})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["board"] = maps.Map{
|
||||||
|
"countApps": resp.CountApps,
|
||||||
|
"countDomains": resp.CountDomains,
|
||||||
|
"countClusters": resp.CountClusters,
|
||||||
|
"countNodes": resp.CountNodes,
|
||||||
|
"countOfflineNodes": resp.CountOfflineNodes,
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
statMaps := []maps.Map{}
|
||||||
|
for _, stat := range resp.HourlyTrafficStats {
|
||||||
|
statMaps = append(statMaps, maps.Map{
|
||||||
|
"day": stat.Hour[4:6] + "月" + stat.Hour[6:8] + "日",
|
||||||
|
"hour": stat.Hour[8:],
|
||||||
|
"countRequests": stat.CountRequests,
|
||||||
|
"bytes": stat.Bytes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["hourlyStats"] = statMaps
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
statMaps := []maps.Map{}
|
||||||
|
for _, stat := range resp.DailyTrafficStats {
|
||||||
|
statMaps = append(statMaps, maps.Map{
|
||||||
|
"day": stat.Day[4:6] + "月" + stat.Day[6:] + "日",
|
||||||
|
"countRequests": stat.CountRequests,
|
||||||
|
"bytes": stat.Bytes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["dailyStats"] = statMaps
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
statMaps := []maps.Map{}
|
||||||
|
for _, stat := range resp.TopAppStats {
|
||||||
|
statMaps = append(statMaps, maps.Map{
|
||||||
|
"appId": stat.AppId,
|
||||||
|
"appName": stat.AppName,
|
||||||
|
"countRequests": stat.CountRequests,
|
||||||
|
"bytes": stat.Bytes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["topAppStats"] = statMaps
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
statMaps := []maps.Map{}
|
||||||
|
for _, stat := range resp.TopDomainStats {
|
||||||
|
statMaps = append(statMaps, maps.Map{
|
||||||
|
"domainId": stat.DomainId,
|
||||||
|
"domainName": stat.DomainName,
|
||||||
|
"countRequests": stat.CountRequests,
|
||||||
|
"bytes": stat.Bytes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["topDomainStats"] = statMaps
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
statMaps := []maps.Map{}
|
||||||
|
for _, stat := range resp.TopNodeStats {
|
||||||
|
statMaps = append(statMaps, maps.Map{
|
||||||
|
"clusterId": stat.ClusterId,
|
||||||
|
"nodeId": stat.NodeId,
|
||||||
|
"nodeName": stat.NodeName,
|
||||||
|
"countRequests": stat.CountRequests,
|
||||||
|
"bytes": stat.Bytes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["topNodeStats"] = statMaps
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Success()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ func init() {
|
|||||||
server.
|
server.
|
||||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeHttpDNS)).
|
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeHttpDNS)).
|
||||||
Data("teaMenu", "httpdns").
|
Data("teaMenu", "httpdns").
|
||||||
Data("teaSubMenu", "cluster").
|
Data("teaSubMenu", "board").
|
||||||
Prefix("/httpdns").
|
Prefix("/httpdns").
|
||||||
Get("", new(IndexAction)).
|
GetPost("", new(IndexAction)).
|
||||||
GetPost("/addPortPopup", new(AddPortPopupAction)).
|
GetPost("/addPortPopup", new(AddPortPopupAction)).
|
||||||
EndAll()
|
EndAll()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ func (this *CreatePopupAction) Init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *CreatePopupAction) RunGet(params struct{}) {
|
func (this *CreatePopupAction) RunGet(params struct{}) {
|
||||||
var authMethods = []*serverconfigs.HTTPAuthTypeDefinition{}
|
authMethods := []*serverconfigs.HTTPAuthTypeDefinition{}
|
||||||
for _, method := range serverconfigs.FindAllHTTPAuthTypes(teaconst.Role) {
|
for _, method := range serverconfigs.FindAllHTTPAuthTypes(teaconst.Role) {
|
||||||
if !method.IsPlus || (method.IsPlus && teaconst.IsPlus) {
|
if !method.IsPlus || (method.IsPlus && teaconst.IsPlus) {
|
||||||
authMethods = append(authMethods, method)
|
authMethods = append(authMethods, method)
|
||||||
@@ -59,6 +59,10 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
TypeDTimestampParamName string
|
TypeDTimestampParamName string
|
||||||
TypeDLife int
|
TypeDLife int
|
||||||
|
|
||||||
|
// TypeE
|
||||||
|
TypeESecret string
|
||||||
|
TypeELife int
|
||||||
|
|
||||||
// BasicAuth
|
// BasicAuth
|
||||||
HttpAuthBasicAuthUsersJSON []byte
|
HttpAuthBasicAuthUsersJSON []byte
|
||||||
BasicAuthRealm string
|
BasicAuthRealm string
|
||||||
@@ -81,29 +85,25 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
Field("type", params.Type).
|
Field("type", params.Type).
|
||||||
Require("请输入鉴权类型")
|
Require("请输入鉴权类型")
|
||||||
|
|
||||||
var ref = &serverconfigs.HTTPAuthPolicyRef{IsOn: true}
|
ref := &serverconfigs.HTTPAuthPolicyRef{IsOn: true}
|
||||||
var method serverconfigs.HTTPAuthMethodInterface
|
var method serverconfigs.HTTPAuthMethodInterface
|
||||||
|
|
||||||
// 扩展名
|
exts := utils.NewStringsStream(params.Exts).
|
||||||
var exts = utils.NewStringsStream(params.Exts).
|
|
||||||
Map(strings.TrimSpace, strings.ToLower).
|
Map(strings.TrimSpace, strings.ToLower).
|
||||||
Filter(utils.FilterNotEmpty).
|
Filter(utils.FilterNotEmpty).
|
||||||
Map(utils.MapAddPrefixFunc(".")).
|
Map(utils.MapAddPrefixFunc(".")).
|
||||||
Unique().
|
Unique().
|
||||||
Result()
|
Result()
|
||||||
|
|
||||||
// 域名
|
domains := []string{}
|
||||||
var domains = []string{}
|
|
||||||
if len(params.DomainsJSON) > 0 {
|
if len(params.DomainsJSON) > 0 {
|
||||||
var rawDomains = []string{}
|
rawDomains := []string{}
|
||||||
err := json.Unmarshal(params.DomainsJSON, &rawDomains)
|
err := json.Unmarshal(params.DomainsJSON, &rawDomains)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO 如果用户填写了一个网址,应该分析域名并填入
|
|
||||||
|
|
||||||
domains = utils.NewStringsStream(rawDomains).
|
domains = utils.NewStringsStream(rawDomains).
|
||||||
Map(strings.TrimSpace, strings.ToLower).
|
Map(strings.TrimSpace, strings.ToLower).
|
||||||
Filter(utils.FilterNotEmpty).
|
Filter(utils.FilterNotEmpty).
|
||||||
@@ -116,11 +116,11 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
params.Must.
|
params.Must.
|
||||||
Field("typeASecret", params.TypeASecret).
|
Field("typeASecret", params.TypeASecret).
|
||||||
Require("请输入鉴权密钥").
|
Require("请输入鉴权密钥").
|
||||||
MaxLength(40, "鉴权密钥不能超过40个字符").
|
MaxLength(40, "鉴权密钥长度不能超过40个字符").
|
||||||
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母、数字").
|
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母和数字").
|
||||||
Field("typeASignParamName", params.TypeASignParamName).
|
Field("typeASignParamName", params.TypeASignParamName).
|
||||||
Require("请输入签名参数").
|
Require("请输入签名参数").
|
||||||
Match(`^[a-zA-Z0-9_]{1,40}$`, "签名参数中只能包含字母、数字、下划线")
|
Match(`^[a-zA-Z0-9_]{1,40}$`, "签名参数中只能包含字母、数字和下划线")
|
||||||
|
|
||||||
if params.TypeALife < 0 {
|
if params.TypeALife < 0 {
|
||||||
params.TypeALife = 0
|
params.TypeALife = 0
|
||||||
@@ -135,8 +135,8 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
params.Must.
|
params.Must.
|
||||||
Field("typeBSecret", params.TypeBSecret).
|
Field("typeBSecret", params.TypeBSecret).
|
||||||
Require("请输入鉴权密钥").
|
Require("请输入鉴权密钥").
|
||||||
MaxLength(40, "鉴权密钥不能超过40个字符").
|
MaxLength(40, "鉴权密钥长度不能超过40个字符").
|
||||||
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母、数字")
|
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母和数字")
|
||||||
|
|
||||||
method = &serverconfigs.HTTPAuthTypeBMethod{
|
method = &serverconfigs.HTTPAuthTypeBMethod{
|
||||||
Secret: params.TypeBSecret,
|
Secret: params.TypeBSecret,
|
||||||
@@ -146,8 +146,8 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
params.Must.
|
params.Must.
|
||||||
Field("typeCSecret", params.TypeCSecret).
|
Field("typeCSecret", params.TypeCSecret).
|
||||||
Require("请输入鉴权密钥").
|
Require("请输入鉴权密钥").
|
||||||
MaxLength(40, "鉴权密钥不能超过40个字符").
|
MaxLength(40, "鉴权密钥长度不能超过40个字符").
|
||||||
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母、数字")
|
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母和数字")
|
||||||
|
|
||||||
method = &serverconfigs.HTTPAuthTypeCMethod{
|
method = &serverconfigs.HTTPAuthTypeCMethod{
|
||||||
Secret: params.TypeCSecret,
|
Secret: params.TypeCSecret,
|
||||||
@@ -157,14 +157,14 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
params.Must.
|
params.Must.
|
||||||
Field("typeDSecret", params.TypeDSecret).
|
Field("typeDSecret", params.TypeDSecret).
|
||||||
Require("请输入鉴权密钥").
|
Require("请输入鉴权密钥").
|
||||||
MaxLength(40, "鉴权密钥不能超过40个字符").
|
MaxLength(40, "鉴权密钥长度不能超过40个字符").
|
||||||
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母、数字").
|
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母和数字").
|
||||||
Field("typeDSignParamName", params.TypeDSignParamName).
|
Field("typeDSignParamName", params.TypeDSignParamName).
|
||||||
Require("请输入签名参数").
|
Require("请输入签名参数").
|
||||||
Match(`^[a-zA-Z0-9_]{1,40}$`, "签名参数中只能包含字母、数字、下划线").
|
Match(`^[a-zA-Z0-9_]{1,40}$`, "签名参数中只能包含字母、数字和下划线").
|
||||||
Field("typeDTimestampParamName", params.TypeDTimestampParamName).
|
Field("typeDTimestampParamName", params.TypeDTimestampParamName).
|
||||||
Require("请输入时间戳参数").
|
Require("请输入时间参数").
|
||||||
Match(`^[a-zA-Z0-9_]{1,40}$`, "时间戳参数中只能包含字母、数字、下划线")
|
Match(`^[a-zA-Z0-9_]{1,40}$`, "时间参数中只能包含字母、数字和下划线")
|
||||||
|
|
||||||
method = &serverconfigs.HTTPAuthTypeDMethod{
|
method = &serverconfigs.HTTPAuthTypeDMethod{
|
||||||
Secret: params.TypeDSecret,
|
Secret: params.TypeDSecret,
|
||||||
@@ -172,15 +172,26 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
TimestampParamName: params.TypeDTimestampParamName,
|
TimestampParamName: params.TypeDTimestampParamName,
|
||||||
Life: params.TypeDLife,
|
Life: params.TypeDLife,
|
||||||
}
|
}
|
||||||
|
case serverconfigs.HTTPAuthTypeTypeE:
|
||||||
|
params.Must.
|
||||||
|
Field("typeESecret", params.TypeESecret).
|
||||||
|
Require("请输入鉴权密钥").
|
||||||
|
MaxLength(40, "鉴权密钥长度不能超过40个字符").
|
||||||
|
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母和数字")
|
||||||
|
|
||||||
|
method = &serverconfigs.HTTPAuthTypeEMethod{
|
||||||
|
Secret: params.TypeESecret,
|
||||||
|
Life: params.TypeELife,
|
||||||
|
}
|
||||||
case serverconfigs.HTTPAuthTypeBasicAuth:
|
case serverconfigs.HTTPAuthTypeBasicAuth:
|
||||||
var users = []*serverconfigs.HTTPAuthBasicMethodUser{}
|
users := []*serverconfigs.HTTPAuthBasicMethodUser{}
|
||||||
err := json.Unmarshal(params.HttpAuthBasicAuthUsersJSON, &users)
|
err := json.Unmarshal(params.HttpAuthBasicAuthUsersJSON, &users)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(users) == 0 {
|
if len(users) == 0 {
|
||||||
this.Fail("请添加至少一个用户")
|
this.Fail("请至少添加一个用户")
|
||||||
}
|
}
|
||||||
method = &serverconfigs.HTTPAuthBasicMethod{
|
method = &serverconfigs.HTTPAuthBasicMethod{
|
||||||
Users: users,
|
Users: users,
|
||||||
@@ -188,8 +199,9 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
Charset: params.BasicAuthCharset,
|
Charset: params.BasicAuthCharset,
|
||||||
}
|
}
|
||||||
case serverconfigs.HTTPAuthTypeSubRequest:
|
case serverconfigs.HTTPAuthTypeSubRequest:
|
||||||
params.Must.Field("subRequestURL", params.SubRequestURL).
|
params.Must.
|
||||||
Require("请输入子请求URL")
|
Field("subRequestURL", params.SubRequestURL).
|
||||||
|
Require("请输入子请求 URL")
|
||||||
if params.SubRequestFollowRequest {
|
if params.SubRequestFollowRequest {
|
||||||
params.SubRequestMethod = ""
|
params.SubRequestMethod = ""
|
||||||
}
|
}
|
||||||
@@ -198,7 +210,7 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
Method: params.SubRequestMethod,
|
Method: params.SubRequestMethod,
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
this.Fail("不支持的鉴权类型'" + params.Type + "'")
|
this.Fail("不支持的鉴权类型 '" + params.Type + "'")
|
||||||
}
|
}
|
||||||
|
|
||||||
if method == nil {
|
if method == nil {
|
||||||
@@ -214,7 +226,7 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var paramsMap = maps.Map{}
|
paramsMap := maps.Map{}
|
||||||
err = json.Unmarshal(methodJSON, ¶msMap)
|
err = json.Unmarshal(methodJSON, ¶msMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
@@ -231,6 +243,7 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer this.CreateLogInfo(codes.HTTPAuthPolicy_LogCreateHTTPAuthPolicy, createResp.HttpAuthPolicyId)
|
defer this.CreateLogInfo(codes.HTTPAuthPolicy_LogCreateHTTPAuthPolicy, createResp.HttpAuthPolicyId)
|
||||||
|
|
||||||
ref.AuthPolicyId = createResp.HttpAuthPolicyId
|
ref.AuthPolicyId = createResp.HttpAuthPolicyId
|
||||||
ref.AuthPolicy = &serverconfigs.HTTPAuthPolicy{
|
ref.AuthPolicy = &serverconfigs.HTTPAuthPolicy{
|
||||||
Id: createResp.HttpAuthPolicyId,
|
Id: createResp.HttpAuthPolicyId,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func (this *UpdatePopupAction) Init() {
|
|||||||
func (this *UpdatePopupAction) RunGet(params struct {
|
func (this *UpdatePopupAction) RunGet(params struct {
|
||||||
PolicyId int64
|
PolicyId int64
|
||||||
}) {
|
}) {
|
||||||
var authMethods = []*serverconfigs.HTTPAuthTypeDefinition{}
|
authMethods := []*serverconfigs.HTTPAuthTypeDefinition{}
|
||||||
for _, method := range serverconfigs.FindAllHTTPAuthTypes(teaconst.Role) {
|
for _, method := range serverconfigs.FindAllHTTPAuthTypes(teaconst.Role) {
|
||||||
if !method.IsPlus || (method.IsPlus && teaconst.IsPlus) {
|
if !method.IsPlus || (method.IsPlus && teaconst.IsPlus) {
|
||||||
authMethods = append(authMethods, method)
|
authMethods = append(authMethods, method)
|
||||||
@@ -47,7 +47,7 @@ func (this *UpdatePopupAction) RunGet(params struct {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var authParams = map[string]interface{}{}
|
authParams := map[string]interface{}{}
|
||||||
if len(policy.ParamsJSON) > 0 {
|
if len(policy.ParamsJSON) > 0 {
|
||||||
err = json.Unmarshal(policy.ParamsJSON, &authParams)
|
err = json.Unmarshal(policy.ParamsJSON, &authParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -91,6 +91,10 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
|||||||
TypeDTimestampParamName string
|
TypeDTimestampParamName string
|
||||||
TypeDLife int
|
TypeDLife int
|
||||||
|
|
||||||
|
// TypeE
|
||||||
|
TypeESecret string
|
||||||
|
TypeELife int
|
||||||
|
|
||||||
// BasicAuth
|
// BasicAuth
|
||||||
HttpAuthBasicAuthUsersJSON []byte
|
HttpAuthBasicAuthUsersJSON []byte
|
||||||
BasicAuthRealm string
|
BasicAuthRealm string
|
||||||
@@ -125,29 +129,25 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
|||||||
Field("name", params.Name).
|
Field("name", params.Name).
|
||||||
Require("请输入名称")
|
Require("请输入名称")
|
||||||
|
|
||||||
var ref = &serverconfigs.HTTPAuthPolicyRef{IsOn: true}
|
ref := &serverconfigs.HTTPAuthPolicyRef{IsOn: true}
|
||||||
var method serverconfigs.HTTPAuthMethodInterface
|
var method serverconfigs.HTTPAuthMethodInterface
|
||||||
|
|
||||||
// 扩展名
|
exts := utils.NewStringsStream(params.Exts).
|
||||||
var exts = utils.NewStringsStream(params.Exts).
|
|
||||||
Map(strings.TrimSpace, strings.ToLower).
|
Map(strings.TrimSpace, strings.ToLower).
|
||||||
Filter(utils.FilterNotEmpty).
|
Filter(utils.FilterNotEmpty).
|
||||||
Map(utils.MapAddPrefixFunc(".")).
|
Map(utils.MapAddPrefixFunc(".")).
|
||||||
Unique().
|
Unique().
|
||||||
Result()
|
Result()
|
||||||
|
|
||||||
// 域名
|
domains := []string{}
|
||||||
var domains = []string{}
|
|
||||||
if len(params.DomainsJSON) > 0 {
|
if len(params.DomainsJSON) > 0 {
|
||||||
var rawDomains = []string{}
|
rawDomains := []string{}
|
||||||
err := json.Unmarshal(params.DomainsJSON, &rawDomains)
|
err := json.Unmarshal(params.DomainsJSON, &rawDomains)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO 如果用户填写了一个网址,应该分析域名并填入
|
|
||||||
|
|
||||||
domains = utils.NewStringsStream(rawDomains).
|
domains = utils.NewStringsStream(rawDomains).
|
||||||
Map(strings.TrimSpace, strings.ToLower).
|
Map(strings.TrimSpace, strings.ToLower).
|
||||||
Filter(utils.FilterNotEmpty).
|
Filter(utils.FilterNotEmpty).
|
||||||
@@ -160,11 +160,11 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
|||||||
params.Must.
|
params.Must.
|
||||||
Field("typeASecret", params.TypeASecret).
|
Field("typeASecret", params.TypeASecret).
|
||||||
Require("请输入鉴权密钥").
|
Require("请输入鉴权密钥").
|
||||||
MaxLength(40, "鉴权密钥不能超过40个字符").
|
MaxLength(40, "鉴权密钥长度不能超过40个字符").
|
||||||
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母、数字").
|
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母和数字").
|
||||||
Field("typeASignParamName", params.TypeASignParamName).
|
Field("typeASignParamName", params.TypeASignParamName).
|
||||||
Require("请输入签名参数").
|
Require("请输入签名参数").
|
||||||
Match(`^[a-zA-Z0-9_]{1,40}$`, "签名参数中只能包含字母、数字、下划线")
|
Match(`^[a-zA-Z0-9_]{1,40}$`, "签名参数中只能包含字母、数字和下划线")
|
||||||
|
|
||||||
if params.TypeALife < 0 {
|
if params.TypeALife < 0 {
|
||||||
params.TypeALife = 0
|
params.TypeALife = 0
|
||||||
@@ -179,8 +179,8 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
|||||||
params.Must.
|
params.Must.
|
||||||
Field("typeBSecret", params.TypeBSecret).
|
Field("typeBSecret", params.TypeBSecret).
|
||||||
Require("请输入鉴权密钥").
|
Require("请输入鉴权密钥").
|
||||||
MaxLength(40, "鉴权密钥不能超过40个字符").
|
MaxLength(40, "鉴权密钥长度不能超过40个字符").
|
||||||
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母、数字")
|
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母和数字")
|
||||||
|
|
||||||
method = &serverconfigs.HTTPAuthTypeBMethod{
|
method = &serverconfigs.HTTPAuthTypeBMethod{
|
||||||
Secret: params.TypeBSecret,
|
Secret: params.TypeBSecret,
|
||||||
@@ -190,8 +190,8 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
|||||||
params.Must.
|
params.Must.
|
||||||
Field("typeCSecret", params.TypeCSecret).
|
Field("typeCSecret", params.TypeCSecret).
|
||||||
Require("请输入鉴权密钥").
|
Require("请输入鉴权密钥").
|
||||||
MaxLength(40, "鉴权密钥不能超过40个字符").
|
MaxLength(40, "鉴权密钥长度不能超过40个字符").
|
||||||
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母、数字")
|
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母和数字")
|
||||||
|
|
||||||
method = &serverconfigs.HTTPAuthTypeCMethod{
|
method = &serverconfigs.HTTPAuthTypeCMethod{
|
||||||
Secret: params.TypeCSecret,
|
Secret: params.TypeCSecret,
|
||||||
@@ -201,14 +201,14 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
|||||||
params.Must.
|
params.Must.
|
||||||
Field("typeDSecret", params.TypeDSecret).
|
Field("typeDSecret", params.TypeDSecret).
|
||||||
Require("请输入鉴权密钥").
|
Require("请输入鉴权密钥").
|
||||||
MaxLength(40, "鉴权密钥不能超过40个字符").
|
MaxLength(40, "鉴权密钥长度不能超过40个字符").
|
||||||
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母、数字").
|
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母和数字").
|
||||||
Field("typeDSignParamName", params.TypeDSignParamName).
|
Field("typeDSignParamName", params.TypeDSignParamName).
|
||||||
Require("请输入签名参数").
|
Require("请输入签名参数").
|
||||||
Match(`^[a-zA-Z0-9_]{1,40}$`, "签名参数中只能包含字母、数字、下划线").
|
Match(`^[a-zA-Z0-9_]{1,40}$`, "签名参数中只能包含字母、数字和下划线").
|
||||||
Field("typeDTimestampParamName", params.TypeDTimestampParamName).
|
Field("typeDTimestampParamName", params.TypeDTimestampParamName).
|
||||||
Require("请输入时间戳参数").
|
Require("请输入时间参数").
|
||||||
Match(`^[a-zA-Z0-9_]{1,40}$`, "时间戳参数中只能包含字母、数字、下划线")
|
Match(`^[a-zA-Z0-9_]{1,40}$`, "时间参数中只能包含字母、数字和下划线")
|
||||||
|
|
||||||
method = &serverconfigs.HTTPAuthTypeDMethod{
|
method = &serverconfigs.HTTPAuthTypeDMethod{
|
||||||
Secret: params.TypeDSecret,
|
Secret: params.TypeDSecret,
|
||||||
@@ -216,6 +216,17 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
|||||||
TimestampParamName: params.TypeDTimestampParamName,
|
TimestampParamName: params.TypeDTimestampParamName,
|
||||||
Life: params.TypeDLife,
|
Life: params.TypeDLife,
|
||||||
}
|
}
|
||||||
|
case serverconfigs.HTTPAuthTypeTypeE:
|
||||||
|
params.Must.
|
||||||
|
Field("typeESecret", params.TypeESecret).
|
||||||
|
Require("请输入鉴权密钥").
|
||||||
|
MaxLength(40, "鉴权密钥长度不能超过40个字符").
|
||||||
|
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母和数字")
|
||||||
|
|
||||||
|
method = &serverconfigs.HTTPAuthTypeEMethod{
|
||||||
|
Secret: params.TypeESecret,
|
||||||
|
Life: params.TypeELife,
|
||||||
|
}
|
||||||
case serverconfigs.HTTPAuthTypeBasicAuth:
|
case serverconfigs.HTTPAuthTypeBasicAuth:
|
||||||
users := []*serverconfigs.HTTPAuthBasicMethodUser{}
|
users := []*serverconfigs.HTTPAuthBasicMethodUser{}
|
||||||
err := json.Unmarshal(params.HttpAuthBasicAuthUsersJSON, &users)
|
err := json.Unmarshal(params.HttpAuthBasicAuthUsersJSON, &users)
|
||||||
@@ -224,7 +235,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(users) == 0 {
|
if len(users) == 0 {
|
||||||
this.Fail("请添加至少一个用户")
|
this.Fail("请至少添加一个用户")
|
||||||
}
|
}
|
||||||
method = &serverconfigs.HTTPAuthBasicMethod{
|
method = &serverconfigs.HTTPAuthBasicMethod{
|
||||||
Users: users,
|
Users: users,
|
||||||
@@ -232,8 +243,9 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
|||||||
Charset: params.BasicAuthCharset,
|
Charset: params.BasicAuthCharset,
|
||||||
}
|
}
|
||||||
case serverconfigs.HTTPAuthTypeSubRequest:
|
case serverconfigs.HTTPAuthTypeSubRequest:
|
||||||
params.Must.Field("subRequestURL", params.SubRequestURL).
|
params.Must.
|
||||||
Require("请输入子请求URL")
|
Field("subRequestURL", params.SubRequestURL).
|
||||||
|
Require("请输入子请求 URL")
|
||||||
if params.SubRequestFollowRequest {
|
if params.SubRequestFollowRequest {
|
||||||
params.SubRequestMethod = ""
|
params.SubRequestMethod = ""
|
||||||
}
|
}
|
||||||
@@ -242,7 +254,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
|||||||
Method: params.SubRequestMethod,
|
Method: params.SubRequestMethod,
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
this.Fail("不支持的鉴权类型'" + policyType + "'")
|
this.Fail("不支持的鉴权类型 '" + policyType + "'")
|
||||||
}
|
}
|
||||||
|
|
||||||
if method == nil {
|
if method == nil {
|
||||||
@@ -275,6 +287,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
|||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ref.AuthPolicy = &serverconfigs.HTTPAuthPolicy{
|
ref.AuthPolicy = &serverconfigs.HTTPAuthPolicy{
|
||||||
Id: params.PolicyId,
|
Id: params.PolicyId,
|
||||||
Name: params.Name,
|
Name: params.Name,
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import (
|
|||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ComponentsAction actions.Action
|
type ComponentsAction actions.Action
|
||||||
@@ -41,12 +43,7 @@ func (this *ComponentsAction) RunGet(params struct{}) {
|
|||||||
|
|
||||||
var buffer = bytes.NewBuffer([]byte{})
|
var buffer = bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
var webRoot string
|
webRoot := findComponentsRoot()
|
||||||
if Tea.IsTesting() {
|
|
||||||
webRoot = Tea.Root + "/../web/public/js/components/"
|
|
||||||
} else {
|
|
||||||
webRoot = Tea.Root + "/web/public/js/components/"
|
|
||||||
}
|
|
||||||
f := files.NewFile(webRoot)
|
f := files.NewFile(webRoot)
|
||||||
|
|
||||||
f.Range(func(file *files.File) {
|
f.Range(func(file *files.File) {
|
||||||
@@ -173,3 +170,27 @@ func (this *ComponentsAction) RunGet(params struct{}) {
|
|||||||
|
|
||||||
_, _ = this.Write(componentsData)
|
_, _ = this.Write(componentsData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func findComponentsRoot() string {
|
||||||
|
candidates := []string{
|
||||||
|
filepath.Join(Tea.Root, "web", "public", "js", "components"),
|
||||||
|
filepath.Join(Tea.Root, "..", "web", "public", "js", "components"),
|
||||||
|
filepath.Join(Tea.Root, "..", "..", "web", "public", "js", "components"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if cwd, err := os.Getwd(); err == nil && len(cwd) > 0 {
|
||||||
|
candidates = append(candidates,
|
||||||
|
filepath.Join(cwd, "web", "public", "js", "components"),
|
||||||
|
filepath.Join(cwd, "EdgeAdmin", "web", "public", "js", "components"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, candidate := range candidates {
|
||||||
|
info, err := os.Stat(candidate)
|
||||||
|
if err == nil && info.IsDir() {
|
||||||
|
return candidate + string(filepath.Separator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(Tea.Root, "web", "public", "js", "components") + string(filepath.Separator)
|
||||||
|
}
|
||||||
|
|||||||
@@ -141,7 +141,6 @@ import (
|
|||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/apps"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/apps"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/clusters"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/clusters"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/clusters"
|
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/resolveLogs"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/resolveLogs"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/runtimeLogs"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/runtimeLogs"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/sandbox"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/sandbox"
|
||||||
|
|||||||
@@ -2272,6 +2272,57 @@ Vue.component("health-check-config-box", {
|
|||||||
</div>`
|
</div>`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Vue.component("httpdns-clusters-selector", {
|
||||||
|
props: ["vClusters", "vName"],
|
||||||
|
data: function () {
|
||||||
|
let inputClusters = this.vClusters
|
||||||
|
let clusters = []
|
||||||
|
|
||||||
|
if (inputClusters != null && inputClusters.length > 0) {
|
||||||
|
if (inputClusters[0].isChecked !== undefined) {
|
||||||
|
// 带 isChecked 标志的完整集群列表
|
||||||
|
clusters = inputClusters.map(function (c) {
|
||||||
|
return {id: c.id, name: c.name, isChecked: c.isChecked}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 仅包含已选集群,全部标记为选中
|
||||||
|
clusters = inputClusters.map(function (c) {
|
||||||
|
return {id: c.id, name: c.name, isChecked: true}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无 prop 时从根实例读取所有集群(如创建应用页面)
|
||||||
|
if (clusters.length === 0) {
|
||||||
|
let rootClusters = this.$root.clusters
|
||||||
|
if (rootClusters != null && rootClusters.length > 0) {
|
||||||
|
clusters = rootClusters.map(function (c) {
|
||||||
|
return {id: c.id, name: c.name, isChecked: false}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
clusters: clusters,
|
||||||
|
fieldName: this.vName || "clusterIds"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
changeCluster: function (cluster) {
|
||||||
|
cluster.isChecked = !cluster.isChecked
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `<div>
|
||||||
|
<div v-if="clusters.length > 0">
|
||||||
|
<checkbox v-for="cluster in clusters" :key="cluster.id" :v-value="cluster.id" :value="cluster.isChecked ? cluster.id : 0" style="margin-right: 1em" @input="changeCluster(cluster)" :name="fieldName">
|
||||||
|
{{cluster.name}}
|
||||||
|
</checkbox>
|
||||||
|
</div>
|
||||||
|
<span class="grey" v-else>暂无可用集群</span>
|
||||||
|
</div>`
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 菜单项
|
* 菜单项
|
||||||
*/
|
*/
|
||||||
@@ -11294,10 +11345,18 @@ Vue.component("http-auth-config-box", {
|
|||||||
if (authConfig.policyRefs == null) {
|
if (authConfig.policyRefs == null) {
|
||||||
authConfig.policyRefs = []
|
authConfig.policyRefs = []
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
authConfig: authConfig
|
authConfig: authConfig
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
"authConfig.isOn": function () {
|
||||||
|
this.change()
|
||||||
|
},
|
||||||
|
"authConfig.isPrior": function () {
|
||||||
|
this.change()
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
isOn: function () {
|
isOn: function () {
|
||||||
return (!this.vIsLocation || this.authConfig.isPrior) && this.authConfig.isOn
|
return (!this.vIsLocation || this.authConfig.isPrior) && this.authConfig.isOn
|
||||||
@@ -11309,18 +11368,17 @@ Vue.component("http-auth-config-box", {
|
|||||||
that.authConfig.policyRefs.push(resp.data.policyRef)
|
that.authConfig.policyRefs.push(resp.data.policyRef)
|
||||||
that.change()
|
that.change()
|
||||||
},
|
},
|
||||||
height: "28em"
|
height: "32em"
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
update: function (index, policyId) {
|
update: function (index, policyId) {
|
||||||
let that = this
|
|
||||||
teaweb.popup("/servers/server/settings/access/updatePopup?policyId=" + policyId, {
|
teaweb.popup("/servers/server/settings/access/updatePopup?policyId=" + policyId, {
|
||||||
callback: function (resp) {
|
callback: function () {
|
||||||
teaweb.success("保存成功", function () {
|
teaweb.success("保存成功", function () {
|
||||||
teaweb.reload()
|
teaweb.reload()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
height: "28em"
|
height: "32em"
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
remove: function (index) {
|
remove: function (index) {
|
||||||
@@ -11341,14 +11399,15 @@ Vue.component("http-auth-config-box", {
|
|||||||
return "URL鉴权C"
|
return "URL鉴权C"
|
||||||
case "typeD":
|
case "typeD":
|
||||||
return "URL鉴权D"
|
return "URL鉴权D"
|
||||||
|
case "typeE":
|
||||||
|
return "URL鉴权E"
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
},
|
},
|
||||||
change: function () {
|
change: function () {
|
||||||
let that = this
|
let that = this
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
// 延时通知,是为了让表单有机会变更数据
|
that.$emit("change", that.authConfig)
|
||||||
that.$emit("change", this.authConfig)
|
|
||||||
}, 100)
|
}, 100)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -11369,7 +11428,6 @@ Vue.component("http-auth-config-box", {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="margin"></div>
|
<div class="margin"></div>
|
||||||
<!-- 鉴权方式 -->
|
|
||||||
<div v-show="isOn()">
|
<div v-show="isOn()">
|
||||||
<h4>鉴权方式</h4>
|
<h4>鉴权方式</h4>
|
||||||
<table class="ui table selectable celled" v-show="authConfig.policyRefs.length > 0">
|
<table class="ui table selectable celled" v-show="authConfig.policyRefs.length > 0">
|
||||||
@@ -11385,9 +11443,7 @@ Vue.component("http-auth-config-box", {
|
|||||||
<tbody v-for="(ref, index) in authConfig.policyRefs" :key="ref.authPolicyId">
|
<tbody v-for="(ref, index) in authConfig.policyRefs" :key="ref.authPolicyId">
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ref.authPolicy.name}}</td>
|
<td>{{ref.authPolicy.name}}</td>
|
||||||
<td>
|
<td>{{methodName(ref.authPolicy.type)}}</td>
|
||||||
{{methodName(ref.authPolicy.type)}}
|
|
||||||
</td>
|
|
||||||
<td>
|
<td>
|
||||||
<span v-if="ref.authPolicy.type == 'basicAuth'">{{ref.authPolicy.params.users.length}}个用户</span>
|
<span v-if="ref.authPolicy.type == 'basicAuth'">{{ref.authPolicy.params.users.length}}个用户</span>
|
||||||
<span v-if="ref.authPolicy.type == 'subRequest'">
|
<span v-if="ref.authPolicy.type == 'subRequest'">
|
||||||
@@ -11398,7 +11454,7 @@ Vue.component("http-auth-config-box", {
|
|||||||
<span v-if="ref.authPolicy.type == 'typeB'">有效期{{ref.authPolicy.params.life}}秒</span>
|
<span v-if="ref.authPolicy.type == 'typeB'">有效期{{ref.authPolicy.params.life}}秒</span>
|
||||||
<span v-if="ref.authPolicy.type == 'typeC'">有效期{{ref.authPolicy.params.life}}秒</span>
|
<span v-if="ref.authPolicy.type == 'typeC'">有效期{{ref.authPolicy.params.life}}秒</span>
|
||||||
<span v-if="ref.authPolicy.type == 'typeD'">{{ref.authPolicy.params.signParamName}}/{{ref.authPolicy.params.timestampParamName}}/有效期{{ref.authPolicy.params.life}}秒</span>
|
<span v-if="ref.authPolicy.type == 'typeD'">{{ref.authPolicy.params.signParamName}}/{{ref.authPolicy.params.timestampParamName}}/有效期{{ref.authPolicy.params.life}}秒</span>
|
||||||
|
<span v-if="ref.authPolicy.type == 'typeE'">路径模式/有效期{{ref.authPolicy.params.life}}秒</span>
|
||||||
<div v-if="(ref.authPolicy.params.exts != null && ref.authPolicy.params.exts.length > 0) || (ref.authPolicy.params.domains != null && ref.authPolicy.params.domains.length > 0)">
|
<div v-if="(ref.authPolicy.params.exts != null && ref.authPolicy.params.exts.length > 0) || (ref.authPolicy.params.domains != null && ref.authPolicy.params.domains.length > 0)">
|
||||||
<grey-label v-if="ref.authPolicy.params.exts != null" v-for="ext in ref.authPolicy.params.exts">扩展名:{{ext}}</grey-label>
|
<grey-label v-if="ref.authPolicy.params.exts != null" v-for="ext in ref.authPolicy.params.exts">扩展名:{{ext}}</grey-label>
|
||||||
<grey-label v-if="ref.authPolicy.params.domains != null" v-for="domain in ref.authPolicy.params.domains">域名:{{domain}}</grey-label>
|
<grey-label v-if="ref.authPolicy.params.domains != null" v-for="domain in ref.authPolicy.params.domains">域名:{{domain}}</grey-label>
|
||||||
@@ -11420,6 +11476,7 @@ Vue.component("http-auth-config-box", {
|
|||||||
</div>`
|
</div>`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
Vue.component("http-cache-config-box", {
|
Vue.component("http-cache-config-box", {
|
||||||
props: ["v-cache-config", "v-is-location", "v-is-group", "v-cache-policy", "v-web-id"],
|
props: ["v-cache-config", "v-is-location", "v-is-group", "v-cache-policy", "v-web-id"],
|
||||||
data: function () {
|
data: function () {
|
||||||
|
|||||||
@@ -2272,6 +2272,57 @@ Vue.component("health-check-config-box", {
|
|||||||
</div>`
|
</div>`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Vue.component("httpdns-clusters-selector", {
|
||||||
|
props: ["vClusters", "vName"],
|
||||||
|
data: function () {
|
||||||
|
let inputClusters = this.vClusters
|
||||||
|
let clusters = []
|
||||||
|
|
||||||
|
if (inputClusters != null && inputClusters.length > 0) {
|
||||||
|
if (inputClusters[0].isChecked !== undefined) {
|
||||||
|
// 带 isChecked 标志的完整集群列表
|
||||||
|
clusters = inputClusters.map(function (c) {
|
||||||
|
return {id: c.id, name: c.name, isChecked: c.isChecked}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 仅包含已选集群,全部标记为选中
|
||||||
|
clusters = inputClusters.map(function (c) {
|
||||||
|
return {id: c.id, name: c.name, isChecked: true}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无 prop 时从根实例读取所有集群(如创建应用页面)
|
||||||
|
if (clusters.length === 0) {
|
||||||
|
let rootClusters = this.$root.clusters
|
||||||
|
if (rootClusters != null && rootClusters.length > 0) {
|
||||||
|
clusters = rootClusters.map(function (c) {
|
||||||
|
return {id: c.id, name: c.name, isChecked: false}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
clusters: clusters,
|
||||||
|
fieldName: this.vName || "clusterIds"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
changeCluster: function (cluster) {
|
||||||
|
cluster.isChecked = !cluster.isChecked
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `<div>
|
||||||
|
<div v-if="clusters.length > 0">
|
||||||
|
<checkbox v-for="cluster in clusters" :key="cluster.id" :v-value="cluster.id" :value="cluster.isChecked ? cluster.id : 0" style="margin-right: 1em" @input="changeCluster(cluster)" :name="fieldName">
|
||||||
|
{{cluster.name}}
|
||||||
|
</checkbox>
|
||||||
|
</div>
|
||||||
|
<span class="grey" v-else>暂无可用集群</span>
|
||||||
|
</div>`
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 菜单项
|
* 菜单项
|
||||||
*/
|
*/
|
||||||
@@ -11294,10 +11345,18 @@ Vue.component("http-auth-config-box", {
|
|||||||
if (authConfig.policyRefs == null) {
|
if (authConfig.policyRefs == null) {
|
||||||
authConfig.policyRefs = []
|
authConfig.policyRefs = []
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
authConfig: authConfig
|
authConfig: authConfig
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
"authConfig.isOn": function () {
|
||||||
|
this.change()
|
||||||
|
},
|
||||||
|
"authConfig.isPrior": function () {
|
||||||
|
this.change()
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
isOn: function () {
|
isOn: function () {
|
||||||
return (!this.vIsLocation || this.authConfig.isPrior) && this.authConfig.isOn
|
return (!this.vIsLocation || this.authConfig.isPrior) && this.authConfig.isOn
|
||||||
@@ -11309,18 +11368,17 @@ Vue.component("http-auth-config-box", {
|
|||||||
that.authConfig.policyRefs.push(resp.data.policyRef)
|
that.authConfig.policyRefs.push(resp.data.policyRef)
|
||||||
that.change()
|
that.change()
|
||||||
},
|
},
|
||||||
height: "28em"
|
height: "32em"
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
update: function (index, policyId) {
|
update: function (index, policyId) {
|
||||||
let that = this
|
|
||||||
teaweb.popup("/servers/server/settings/access/updatePopup?policyId=" + policyId, {
|
teaweb.popup("/servers/server/settings/access/updatePopup?policyId=" + policyId, {
|
||||||
callback: function (resp) {
|
callback: function () {
|
||||||
teaweb.success("保存成功", function () {
|
teaweb.success("保存成功", function () {
|
||||||
teaweb.reload()
|
teaweb.reload()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
height: "28em"
|
height: "32em"
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
remove: function (index) {
|
remove: function (index) {
|
||||||
@@ -11341,14 +11399,15 @@ Vue.component("http-auth-config-box", {
|
|||||||
return "URL鉴权C"
|
return "URL鉴权C"
|
||||||
case "typeD":
|
case "typeD":
|
||||||
return "URL鉴权D"
|
return "URL鉴权D"
|
||||||
|
case "typeE":
|
||||||
|
return "URL鉴权E"
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
},
|
},
|
||||||
change: function () {
|
change: function () {
|
||||||
let that = this
|
let that = this
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
// 延时通知,是为了让表单有机会变更数据
|
that.$emit("change", that.authConfig)
|
||||||
that.$emit("change", this.authConfig)
|
|
||||||
}, 100)
|
}, 100)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -11369,7 +11428,6 @@ Vue.component("http-auth-config-box", {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="margin"></div>
|
<div class="margin"></div>
|
||||||
<!-- 鉴权方式 -->
|
|
||||||
<div v-show="isOn()">
|
<div v-show="isOn()">
|
||||||
<h4>鉴权方式</h4>
|
<h4>鉴权方式</h4>
|
||||||
<table class="ui table selectable celled" v-show="authConfig.policyRefs.length > 0">
|
<table class="ui table selectable celled" v-show="authConfig.policyRefs.length > 0">
|
||||||
@@ -11385,9 +11443,7 @@ Vue.component("http-auth-config-box", {
|
|||||||
<tbody v-for="(ref, index) in authConfig.policyRefs" :key="ref.authPolicyId">
|
<tbody v-for="(ref, index) in authConfig.policyRefs" :key="ref.authPolicyId">
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ref.authPolicy.name}}</td>
|
<td>{{ref.authPolicy.name}}</td>
|
||||||
<td>
|
<td>{{methodName(ref.authPolicy.type)}}</td>
|
||||||
{{methodName(ref.authPolicy.type)}}
|
|
||||||
</td>
|
|
||||||
<td>
|
<td>
|
||||||
<span v-if="ref.authPolicy.type == 'basicAuth'">{{ref.authPolicy.params.users.length}}个用户</span>
|
<span v-if="ref.authPolicy.type == 'basicAuth'">{{ref.authPolicy.params.users.length}}个用户</span>
|
||||||
<span v-if="ref.authPolicy.type == 'subRequest'">
|
<span v-if="ref.authPolicy.type == 'subRequest'">
|
||||||
@@ -11398,7 +11454,7 @@ Vue.component("http-auth-config-box", {
|
|||||||
<span v-if="ref.authPolicy.type == 'typeB'">有效期{{ref.authPolicy.params.life}}秒</span>
|
<span v-if="ref.authPolicy.type == 'typeB'">有效期{{ref.authPolicy.params.life}}秒</span>
|
||||||
<span v-if="ref.authPolicy.type == 'typeC'">有效期{{ref.authPolicy.params.life}}秒</span>
|
<span v-if="ref.authPolicy.type == 'typeC'">有效期{{ref.authPolicy.params.life}}秒</span>
|
||||||
<span v-if="ref.authPolicy.type == 'typeD'">{{ref.authPolicy.params.signParamName}}/{{ref.authPolicy.params.timestampParamName}}/有效期{{ref.authPolicy.params.life}}秒</span>
|
<span v-if="ref.authPolicy.type == 'typeD'">{{ref.authPolicy.params.signParamName}}/{{ref.authPolicy.params.timestampParamName}}/有效期{{ref.authPolicy.params.life}}秒</span>
|
||||||
|
<span v-if="ref.authPolicy.type == 'typeE'">路径模式/有效期{{ref.authPolicy.params.life}}秒</span>
|
||||||
<div v-if="(ref.authPolicy.params.exts != null && ref.authPolicy.params.exts.length > 0) || (ref.authPolicy.params.domains != null && ref.authPolicy.params.domains.length > 0)">
|
<div v-if="(ref.authPolicy.params.exts != null && ref.authPolicy.params.exts.length > 0) || (ref.authPolicy.params.domains != null && ref.authPolicy.params.domains.length > 0)">
|
||||||
<grey-label v-if="ref.authPolicy.params.exts != null" v-for="ext in ref.authPolicy.params.exts">扩展名:{{ext}}</grey-label>
|
<grey-label v-if="ref.authPolicy.params.exts != null" v-for="ext in ref.authPolicy.params.exts">扩展名:{{ext}}</grey-label>
|
||||||
<grey-label v-if="ref.authPolicy.params.domains != null" v-for="domain in ref.authPolicy.params.domains">域名:{{domain}}</grey-label>
|
<grey-label v-if="ref.authPolicy.params.domains != null" v-for="domain in ref.authPolicy.params.domains">域名:{{domain}}</grey-label>
|
||||||
@@ -11420,6 +11476,7 @@ Vue.component("http-auth-config-box", {
|
|||||||
</div>`
|
</div>`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
Vue.component("http-cache-config-box", {
|
Vue.component("http-cache-config-box", {
|
||||||
props: ["v-cache-config", "v-is-location", "v-is-group", "v-cache-policy", "v-web-id"],
|
props: ["v-cache-config", "v-is-location", "v-is-group", "v-cache-policy", "v-web-id"],
|
||||||
data: function () {
|
data: function () {
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
Vue.component("httpdns-clusters-selector", {
|
||||||
|
props: ["vClusters", "vName"],
|
||||||
|
data: function () {
|
||||||
|
let inputClusters = this.vClusters
|
||||||
|
let clusters = []
|
||||||
|
|
||||||
|
if (inputClusters != null && inputClusters.length > 0) {
|
||||||
|
if (inputClusters[0].isChecked !== undefined) {
|
||||||
|
// 带 isChecked 标志的完整集群列表
|
||||||
|
clusters = inputClusters.map(function (c) {
|
||||||
|
return {id: c.id, name: c.name, isChecked: c.isChecked}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 仅包含已选集群,全部标记为选中
|
||||||
|
clusters = inputClusters.map(function (c) {
|
||||||
|
return {id: c.id, name: c.name, isChecked: true}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无 prop 时从根实例读取所有集群(如创建应用页面)
|
||||||
|
if (clusters.length === 0) {
|
||||||
|
let rootClusters = this.$root.clusters
|
||||||
|
if (rootClusters != null && rootClusters.length > 0) {
|
||||||
|
clusters = rootClusters.map(function (c) {
|
||||||
|
return {id: c.id, name: c.name, isChecked: false}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
clusters: clusters,
|
||||||
|
fieldName: this.vName || "clusterIds"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
changeCluster: function (cluster) {
|
||||||
|
cluster.isChecked = !cluster.isChecked
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `<div>
|
||||||
|
<div v-if="clusters.length > 0">
|
||||||
|
<checkbox v-for="cluster in clusters" :key="cluster.id" :v-value="cluster.id" :value="cluster.isChecked ? cluster.id : 0" style="margin-right: 1em" @input="changeCluster(cluster)" :name="fieldName">
|
||||||
|
{{cluster.name}}
|
||||||
|
</checkbox>
|
||||||
|
</div>
|
||||||
|
<span class="grey" v-else>暂无可用集群</span>
|
||||||
|
</div>`
|
||||||
|
})
|
||||||
@@ -12,10 +12,18 @@ Vue.component("http-auth-config-box", {
|
|||||||
if (authConfig.policyRefs == null) {
|
if (authConfig.policyRefs == null) {
|
||||||
authConfig.policyRefs = []
|
authConfig.policyRefs = []
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
authConfig: authConfig
|
authConfig: authConfig
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
"authConfig.isOn": function () {
|
||||||
|
this.change()
|
||||||
|
},
|
||||||
|
"authConfig.isPrior": function () {
|
||||||
|
this.change()
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
isOn: function () {
|
isOn: function () {
|
||||||
return (!this.vIsLocation || this.authConfig.isPrior) && this.authConfig.isOn
|
return (!this.vIsLocation || this.authConfig.isPrior) && this.authConfig.isOn
|
||||||
@@ -27,18 +35,17 @@ Vue.component("http-auth-config-box", {
|
|||||||
that.authConfig.policyRefs.push(resp.data.policyRef)
|
that.authConfig.policyRefs.push(resp.data.policyRef)
|
||||||
that.change()
|
that.change()
|
||||||
},
|
},
|
||||||
height: "28em"
|
height: "32em"
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
update: function (index, policyId) {
|
update: function (index, policyId) {
|
||||||
let that = this
|
|
||||||
teaweb.popup("/servers/server/settings/access/updatePopup?policyId=" + policyId, {
|
teaweb.popup("/servers/server/settings/access/updatePopup?policyId=" + policyId, {
|
||||||
callback: function (resp) {
|
callback: function () {
|
||||||
teaweb.success("保存成功", function () {
|
teaweb.success("保存成功", function () {
|
||||||
teaweb.reload()
|
teaweb.reload()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
height: "28em"
|
height: "32em"
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
remove: function (index) {
|
remove: function (index) {
|
||||||
@@ -59,14 +66,15 @@ Vue.component("http-auth-config-box", {
|
|||||||
return "URL鉴权C"
|
return "URL鉴权C"
|
||||||
case "typeD":
|
case "typeD":
|
||||||
return "URL鉴权D"
|
return "URL鉴权D"
|
||||||
|
case "typeE":
|
||||||
|
return "URL鉴权E"
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
},
|
},
|
||||||
change: function () {
|
change: function () {
|
||||||
let that = this
|
let that = this
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
// 延时通知,是为了让表单有机会变更数据
|
that.$emit("change", that.authConfig)
|
||||||
that.$emit("change", this.authConfig)
|
|
||||||
}, 100)
|
}, 100)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -87,7 +95,6 @@ Vue.component("http-auth-config-box", {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="margin"></div>
|
<div class="margin"></div>
|
||||||
<!-- 鉴权方式 -->
|
|
||||||
<div v-show="isOn()">
|
<div v-show="isOn()">
|
||||||
<h4>鉴权方式</h4>
|
<h4>鉴权方式</h4>
|
||||||
<table class="ui table selectable celled" v-show="authConfig.policyRefs.length > 0">
|
<table class="ui table selectable celled" v-show="authConfig.policyRefs.length > 0">
|
||||||
@@ -103,9 +110,7 @@ Vue.component("http-auth-config-box", {
|
|||||||
<tbody v-for="(ref, index) in authConfig.policyRefs" :key="ref.authPolicyId">
|
<tbody v-for="(ref, index) in authConfig.policyRefs" :key="ref.authPolicyId">
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ref.authPolicy.name}}</td>
|
<td>{{ref.authPolicy.name}}</td>
|
||||||
<td>
|
<td>{{methodName(ref.authPolicy.type)}}</td>
|
||||||
{{methodName(ref.authPolicy.type)}}
|
|
||||||
</td>
|
|
||||||
<td>
|
<td>
|
||||||
<span v-if="ref.authPolicy.type == 'basicAuth'">{{ref.authPolicy.params.users.length}}个用户</span>
|
<span v-if="ref.authPolicy.type == 'basicAuth'">{{ref.authPolicy.params.users.length}}个用户</span>
|
||||||
<span v-if="ref.authPolicy.type == 'subRequest'">
|
<span v-if="ref.authPolicy.type == 'subRequest'">
|
||||||
@@ -116,7 +121,7 @@ Vue.component("http-auth-config-box", {
|
|||||||
<span v-if="ref.authPolicy.type == 'typeB'">有效期{{ref.authPolicy.params.life}}秒</span>
|
<span v-if="ref.authPolicy.type == 'typeB'">有效期{{ref.authPolicy.params.life}}秒</span>
|
||||||
<span v-if="ref.authPolicy.type == 'typeC'">有效期{{ref.authPolicy.params.life}}秒</span>
|
<span v-if="ref.authPolicy.type == 'typeC'">有效期{{ref.authPolicy.params.life}}秒</span>
|
||||||
<span v-if="ref.authPolicy.type == 'typeD'">{{ref.authPolicy.params.signParamName}}/{{ref.authPolicy.params.timestampParamName}}/有效期{{ref.authPolicy.params.life}}秒</span>
|
<span v-if="ref.authPolicy.type == 'typeD'">{{ref.authPolicy.params.signParamName}}/{{ref.authPolicy.params.timestampParamName}}/有效期{{ref.authPolicy.params.life}}秒</span>
|
||||||
|
<span v-if="ref.authPolicy.type == 'typeE'">路径模式/有效期{{ref.authPolicy.params.life}}秒</span>
|
||||||
<div v-if="(ref.authPolicy.params.exts != null && ref.authPolicy.params.exts.length > 0) || (ref.authPolicy.params.domains != null && ref.authPolicy.params.domains.length > 0)">
|
<div v-if="(ref.authPolicy.params.exts != null && ref.authPolicy.params.exts.length > 0) || (ref.authPolicy.params.domains != null && ref.authPolicy.params.domains.length > 0)">
|
||||||
<grey-label v-if="ref.authPolicy.params.exts != null" v-for="ext in ref.authPolicy.params.exts">扩展名:{{ext}}</grey-label>
|
<grey-label v-if="ref.authPolicy.params.exts != null" v-for="ext in ref.authPolicy.params.exts">扩展名:{{ext}}</grey-label>
|
||||||
<grey-label v-if="ref.authPolicy.params.domains != null" v-for="domain in ref.authPolicy.params.domains">域名:{{domain}}</grey-label>
|
<grey-label v-if="ref.authPolicy.params.domains != null" v-for="domain in ref.authPolicy.params.domains">域名:{{domain}}</grey-label>
|
||||||
@@ -136,4 +141,4 @@ Vue.component("http-auth-config-box", {
|
|||||||
</div>
|
</div>
|
||||||
<div class="margin"></div>
|
<div class="margin"></div>
|
||||||
</div>`
|
</div>`
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<script type="text/javascript" src="/js/config/brand.js"></script>
|
<script type="text/javascript" src="/js/config/brand.js"></script>
|
||||||
<script type="text/javascript" src="/_/@default/@layout.js"></script>
|
<script type="text/javascript" src="/_/@default/@layout.js"></script>
|
||||||
<script type="text/javascript" src="/js/components.js"></script>
|
<script type="text/javascript" src="/js/components.js?v={$ .teaVersion}"></script>
|
||||||
<script type="text/javascript" src="/js/utils.min.js"></script>
|
<script type="text/javascript" src="/js/utils.min.js"></script>
|
||||||
<script type="text/javascript" src="/js/sweetalert2/dist/sweetalert2.all.min.js" async></script>
|
<script type="text/javascript" src="/js/sweetalert2/dist/sweetalert2.all.min.js" async></script>
|
||||||
<script type="text/javascript" src="/js/date.tea.js"></script>
|
<script type="text/javascript" src="/js/date.tea.js"></script>
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
{$layout}
|
|
||||||
|
|
||||||
{$template "../menu"}
|
|
||||||
{$template "/left_menu_with_menu"}
|
|
||||||
|
|
||||||
<div class="right-box with-menu">
|
|
||||||
<div class="margin"></div>
|
|
||||||
|
|
||||||
<warning-message>此功能为试验功能,目前只能做一些简单的网络数据包统计。</warning-message>
|
|
||||||
|
|
||||||
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
|
||||||
<csrf-token></csrf-token>
|
|
||||||
|
|
||||||
<input type="hidden" name="clusterId" :value="clusterId"/>
|
|
||||||
<table class="ui table definition selectable">
|
|
||||||
<tr>
|
|
||||||
<td class="title">启用数据包统计</td>
|
|
||||||
<td>
|
|
||||||
<select class="ui dropdown auto-width" name="status" v-model="policy.status">
|
|
||||||
<option value="auto">自动</option>
|
|
||||||
<option value="on">启用</option>
|
|
||||||
<option value="off">停用</option>
|
|
||||||
</select>
|
|
||||||
<p class="comment" v-if="policy.status == 'auto'">自动根据服务器硬件配置决定是否启用,避免影响性能。</p>
|
|
||||||
<p class="comment" v-if="policy.status == 'on'">强制启用;如果节点服务器配置较低,可能会严重影响性能,建议仅在8线程以上CPU节点服务器上选择强制启用。</p>
|
|
||||||
<p class="comment" v-if="policy.status == 'off'">完全关闭此功能。</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<submit-btn></submit-btn>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
Tea.context(function () {
|
|
||||||
this.success = NotifyReloadSuccess("保存成功")
|
|
||||||
})
|
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
<span v-if="countTodayAttacks > 0" :class="{red: countTodayAttacks != countTodayAttacksRead}">({{countTodayAttacksFormat}})</span>
|
<span v-if="countTodayAttacks > 0" :class="{red: countTodayAttacks != countTodayAttacksRead}">({{countTodayAttacksFormat}})</span>
|
||||||
</menu-item>
|
</menu-item>
|
||||||
<menu-item href="/dashboard/boards/dns" code="dns">{{LANG('admin_dashboard@ui_dns')}}</menu-item>
|
<menu-item href="/dashboard/boards/dns" code="dns">{{LANG('admin_dashboard@ui_dns')}}</menu-item>
|
||||||
|
<menu-item href="/dashboard/boards/httpdns" code="httpdns">HTTPDNS</menu-item>
|
||||||
<menu-item href="/dashboard/boards/user" code="user">{{LANG('admin_dashboard@ui_user')}}</menu-item>
|
<menu-item href="/dashboard/boards/user" code="user">{{LANG('admin_dashboard@ui_user')}}</menu-item>
|
||||||
<menu-item href="/dashboard/boards/events" code="event">{{LANG('admin_dashboard@ui_events')}}<span :class="{red: countEvents > 0}">({{countEvents}})</span></menu-item>
|
<menu-item href="/dashboard/boards/events" code="event">{{LANG('admin_dashboard@ui_events')}}<span :class="{red: countEvents > 0}">({{countEvents}})</span></menu-item>
|
||||||
</first-menu>
|
</first-menu>
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
.ui.message .icon {
|
||||||
|
position: absolute;
|
||||||
|
right: 1em;
|
||||||
|
top: 1.8em;
|
||||||
|
}
|
||||||
|
.chart-box {
|
||||||
|
height: 14em;
|
||||||
|
}
|
||||||
67
EdgeAdmin/web/views/@default/dashboard/boards/httpdns.html
Normal file
67
EdgeAdmin/web/views/@default/dashboard/boards/httpdns.html
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
{$layout}
|
||||||
|
{$template "menu"}
|
||||||
|
{$template "/echarts"}
|
||||||
|
|
||||||
|
<div style="margin-top: 0.8em" v-if="isLoading">
|
||||||
|
<div class="ui message loading">
|
||||||
|
<div class="ui active inline loader small"></div> 数据加载中...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-show="!isLoading">
|
||||||
|
<columns-grid>
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>应用<link-icon href="/httpdns/apps"></link-icon></h4>
|
||||||
|
<div class="value"><span>{{board.countApps}}</span>个</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>域名<link-icon href="/httpdns/apps"></link-icon></h4>
|
||||||
|
<div class="value"><span>{{board.countDomains}}</span>个</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>集群<link-icon href="/httpdns/clusters"></link-icon></h4>
|
||||||
|
<div class="value"><span>{{board.countClusters}}</span>个</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>节点<link-icon href="/httpdns/clusters"></link-icon></h4>
|
||||||
|
<div class="value"><span>{{board.countNodes}}</span>
|
||||||
|
<span v-if="board.countOfflineNodes > 0" style="font-size: 1em">
|
||||||
|
/ <a href="/httpdns/clusters"><span class="red" style="font-size: 1em">{{board.countOfflineNodes}}离线</span></a>
|
||||||
|
</span>
|
||||||
|
<span v-else style="font-size: 1em">个</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</columns-grid>
|
||||||
|
|
||||||
|
<chart-columns-grid>
|
||||||
|
<div class="ui column">
|
||||||
|
<div class="ui menu text blue">
|
||||||
|
<a href="" class="item" :class="{active: trafficTab == 'hourly'}" @click.prevent="selectTrafficTab('hourly')">24小时请求趋势</a>
|
||||||
|
<a href="" class="item" :class="{active: trafficTab == 'daily'}" @click.prevent="selectTrafficTab('daily')">15天请求趋势</a>
|
||||||
|
</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
|
||||||
|
<div class="chart-box" id="hourly-traffic-chart" v-show="trafficTab == 'hourly'"></div>
|
||||||
|
<div class="chart-box" id="daily-traffic-chart" v-show="trafficTab == 'daily'"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>应用请求排行 <span>(24小时)</span></h4>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="chart-box" id="top-apps-chart"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>域名请求排行 <span>(24小时)</span></h4>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="chart-box" id="top-domains-chart"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>节点访问排行 <span>(24小时)</span></h4>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="chart-box" id="top-nodes-chart"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</chart-columns-grid>
|
||||||
|
</div>
|
||||||
192
EdgeAdmin/web/views/@default/dashboard/boards/httpdns.js
Normal file
192
EdgeAdmin/web/views/@default/dashboard/boards/httpdns.js
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.isLoading = true
|
||||||
|
|
||||||
|
this.$delay(function () {
|
||||||
|
this.$post("$")
|
||||||
|
.success(function (resp) {
|
||||||
|
for (let k in resp.data) {
|
||||||
|
this[k] = resp.data[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$delay(function () {
|
||||||
|
this.reloadHourlyTrafficChart()
|
||||||
|
this.reloadTopAppsChart()
|
||||||
|
this.reloadTopDomainsChart()
|
||||||
|
this.reloadTopNodesChart()
|
||||||
|
})
|
||||||
|
|
||||||
|
this.isLoading = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
this.trafficTab = "hourly"
|
||||||
|
|
||||||
|
this.selectTrafficTab = function (tab) {
|
||||||
|
this.trafficTab = tab
|
||||||
|
if (tab == "hourly") {
|
||||||
|
this.$delay(function () {
|
||||||
|
this.reloadHourlyTrafficChart()
|
||||||
|
})
|
||||||
|
} else if (tab == "daily") {
|
||||||
|
this.$delay(function () {
|
||||||
|
this.reloadDailyTrafficChart()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reloadHourlyTrafficChart = function () {
|
||||||
|
let stats = this.hourlyStats
|
||||||
|
if (stats == null || stats.length == 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.reloadTrafficChart("hourly-traffic-chart", stats, function (args) {
|
||||||
|
return stats[args.dataIndex].day + " " + stats[args.dataIndex].hour + "时<br/>请求数:" + teaweb.formatNumber(stats[args.dataIndex].countRequests)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reloadDailyTrafficChart = function () {
|
||||||
|
let stats = this.dailyStats
|
||||||
|
if (stats == null || stats.length == 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.reloadTrafficChart("daily-traffic-chart", stats, function (args) {
|
||||||
|
return stats[args.dataIndex].day + "<br/>请求数:" + teaweb.formatNumber(stats[args.dataIndex].countRequests)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reloadTrafficChart = function (chartId, stats, tooltipFunc) {
|
||||||
|
let chartBox = document.getElementById(chartId)
|
||||||
|
if (chartBox == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let axis = teaweb.countAxis(stats, function (v) {
|
||||||
|
return v.countRequests
|
||||||
|
})
|
||||||
|
|
||||||
|
let chart = teaweb.initChart(chartBox)
|
||||||
|
let option = {
|
||||||
|
xAxis: {
|
||||||
|
data: stats.map(function (v) {
|
||||||
|
if (v.hour != null) {
|
||||||
|
return v.hour
|
||||||
|
}
|
||||||
|
return v.day
|
||||||
|
})
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
axisLabel: {
|
||||||
|
formatter: function (value) {
|
||||||
|
return value + axis.unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
show: true,
|
||||||
|
trigger: "item",
|
||||||
|
formatter: tooltipFunc
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: 50,
|
||||||
|
top: 10,
|
||||||
|
right: 20,
|
||||||
|
bottom: 20
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: "请求数",
|
||||||
|
type: "line",
|
||||||
|
data: stats.map(function (v) {
|
||||||
|
return v.countRequests / axis.divider
|
||||||
|
}),
|
||||||
|
itemStyle: {
|
||||||
|
color: teaweb.DefaultChartColor
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: teaweb.DefaultChartColor
|
||||||
|
},
|
||||||
|
smooth: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
animation: true
|
||||||
|
}
|
||||||
|
chart.setOption(option)
|
||||||
|
chart.resize()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reloadTopAppsChart = function () {
|
||||||
|
if (this.topAppStats == null || this.topAppStats.length == 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let axis = teaweb.countAxis(this.topAppStats, function (v) {
|
||||||
|
return v.countRequests
|
||||||
|
})
|
||||||
|
teaweb.renderBarChart({
|
||||||
|
id: "top-apps-chart",
|
||||||
|
name: "应用",
|
||||||
|
values: this.topAppStats,
|
||||||
|
x: function (v) {
|
||||||
|
return v.appName
|
||||||
|
},
|
||||||
|
tooltip: function (args, stats) {
|
||||||
|
return stats[args.dataIndex].appName + "<br/>请求数:" + teaweb.formatNumber(stats[args.dataIndex].countRequests)
|
||||||
|
},
|
||||||
|
value: function (v) {
|
||||||
|
return v.countRequests / axis.divider
|
||||||
|
},
|
||||||
|
axis: axis
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reloadTopDomainsChart = function () {
|
||||||
|
if (this.topDomainStats == null || this.topDomainStats.length == 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let axis = teaweb.countAxis(this.topDomainStats, function (v) {
|
||||||
|
return v.countRequests
|
||||||
|
})
|
||||||
|
teaweb.renderBarChart({
|
||||||
|
id: "top-domains-chart",
|
||||||
|
name: "域名",
|
||||||
|
values: this.topDomainStats,
|
||||||
|
x: function (v) {
|
||||||
|
return v.domainName
|
||||||
|
},
|
||||||
|
tooltip: function (args, stats) {
|
||||||
|
return stats[args.dataIndex].domainName + "<br/>请求数:" + teaweb.formatNumber(stats[args.dataIndex].countRequests)
|
||||||
|
},
|
||||||
|
value: function (v) {
|
||||||
|
return v.countRequests / axis.divider
|
||||||
|
},
|
||||||
|
axis: axis
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reloadTopNodesChart = function () {
|
||||||
|
if (this.topNodeStats == null || this.topNodeStats.length == 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let axis = teaweb.countAxis(this.topNodeStats, function (v) {
|
||||||
|
return v.countRequests
|
||||||
|
})
|
||||||
|
teaweb.renderBarChart({
|
||||||
|
id: "top-nodes-chart",
|
||||||
|
name: "节点",
|
||||||
|
values: this.topNodeStats,
|
||||||
|
x: function (v) {
|
||||||
|
return v.nodeName
|
||||||
|
},
|
||||||
|
tooltip: function (args, stats) {
|
||||||
|
return stats[args.dataIndex].nodeName + "<br/>请求数:" + teaweb.formatNumber(stats[args.dataIndex].countRequests)
|
||||||
|
},
|
||||||
|
value: function (v) {
|
||||||
|
return v.countRequests / axis.divider
|
||||||
|
},
|
||||||
|
axis: axis,
|
||||||
|
click: function (args, stats) {
|
||||||
|
window.location = "/httpdns/clusters/cluster/node?nodeId=" + stats[args.dataIndex].nodeId + "&clusterId=" + stats[args.dataIndex].clusterId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
@@ -53,8 +53,9 @@
|
|||||||
<!-- 升级提醒 -->
|
<!-- 升级提醒 -->
|
||||||
<div class="ui icon message error" v-if="!isLoading && nodeUpgradeInfo.count > 0">
|
<div class="ui icon message error" v-if="!isLoading && nodeUpgradeInfo.count > 0">
|
||||||
<i class="icon warning circle"></i>
|
<i class="icon warning circle"></i>
|
||||||
<a href="/clusters">
|
<a href="/clusters" v-if="autoUpgrade">升级提醒:有 {{nodeUpgradeInfo.count}} 个边缘节点需要升级到 v{{nodeUpgradeInfo.version}} 版本,系统正在尝试自动升级,请耐心等待...</a>
|
||||||
升级提醒:有 {{nodeUpgradeInfo.count}} 个边缘节点需要升级到 v{{nodeUpgradeInfo.version}} 版本,系统正在尝试自动升级,请耐心等待...</a><a href="" title="关闭" @click.prevent="closeMessage"><i class="ui icon remove small"></i></a>
|
<a href="/settings/upgrade" v-else>升级提醒:有 {{nodeUpgradeInfo.count}} 个边缘节点需要升级到 v{{nodeUpgradeInfo.version}} 版本,请到系统设置-升级设置页面自行升级...</a>
|
||||||
|
<a href="" title="关闭" @click.prevent="closeMessage"><i class="ui icon remove small"></i></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui icon message error" v-if="!isLoading && userNodeUpgradeInfo.count > 0 && teaIsPlus">
|
<div class="ui icon message error" v-if="!isLoading && userNodeUpgradeInfo.count > 0 && teaIsPlus">
|
||||||
<i class="icon warning circle"></i>
|
<i class="icon warning circle"></i>
|
||||||
@@ -73,7 +74,15 @@
|
|||||||
|
|
||||||
<div class="ui icon message error" v-if="!isLoading && nsNodeUpgradeInfo.count > 0 && teaIsPlus">
|
<div class="ui icon message error" v-if="!isLoading && nsNodeUpgradeInfo.count > 0 && teaIsPlus">
|
||||||
<i class="icon warning circle"></i>
|
<i class="icon warning circle"></i>
|
||||||
<a href="/ns/clusters">升级提醒:有 {{nsNodeUpgradeInfo.count}} 个DNS节点需要升级到 v{{nsNodeUpgradeInfo.version}} 版本,系统正在尝试自动升级,请耐心等待...</a><a href="" title="关闭" @click.prevent="closeMessage"><i class="ui icon remove small"></i></a>
|
<a href="/ns/clusters" v-if="autoUpgrade">升级提醒:有 {{nsNodeUpgradeInfo.count}} 个DNS节点需要升级到 v{{nsNodeUpgradeInfo.version}} 版本,系统正在尝试自动升级,请耐心等待...</a>
|
||||||
|
<a href="/settings/upgrade" v-else>升级提醒:有 {{nsNodeUpgradeInfo.count}} 个DNS节点需要升级到 v{{nsNodeUpgradeInfo.version}} 版本,请到系统设置-升级设置页面自行升级...</a>
|
||||||
|
<a href="" title="关闭" @click.prevent="closeMessage"><i class="ui icon remove small"></i></a>
|
||||||
|
</div>
|
||||||
|
<div class="ui icon message error" v-if="!isLoading && httpdnsNodeUpgradeInfo.count > 0 && teaIsPlus">
|
||||||
|
<i class="icon warning circle"></i>
|
||||||
|
<a href="/httpdns/clusters" v-if="autoUpgrade">升级提醒:有 {{httpdnsNodeUpgradeInfo.count}} 个HTTPDNS节点需要升级到 v{{httpdnsNodeUpgradeInfo.version}} 版本,系统正在尝试自动升级,请耐心等待...</a>
|
||||||
|
<a href="/settings/upgrade" v-else>升级提醒:有 {{httpdnsNodeUpgradeInfo.count}} 个HTTPDNS节点需要升级到 v{{httpdnsNodeUpgradeInfo.version}} 版本,请到系统设置-升级设置页面自行升级...</a>
|
||||||
|
<a href="" title="关闭" @click.prevent="closeMessage"><i class="ui icon remove small"></i></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui icon message error" v-if="!isLoading && reportNodeUpgradeInfo.count > 0 && teaIsPlus">
|
<div class="ui icon message error" v-if="!isLoading && reportNodeUpgradeInfo.count > 0 && teaIsPlus">
|
||||||
<i class="icon warning circle"></i>
|
<i class="icon warning circle"></i>
|
||||||
|
|||||||
@@ -33,7 +33,9 @@
|
|||||||
<!-- 边缘节点升级提醒 -->
|
<!-- 边缘节点升级提醒 -->
|
||||||
<div class="ui icon message error" v-if="!isLoading && nodeUpgradeInfo.count > 0">
|
<div class="ui icon message error" v-if="!isLoading && nodeUpgradeInfo.count > 0">
|
||||||
<i class="icon warning circle"></i>
|
<i class="icon warning circle"></i>
|
||||||
<a href="/clusters">升级提醒:有 {{nodeUpgradeInfo.count}} 个边缘节点需要升级到 v{{nodeUpgradeInfo.version}} 版本,系统正在尝试自动升级,请耐心等待...</a><a href="" title="关闭" @click.prevent="closeMessage"><i class="ui icon remove small"></i></a>
|
<a href="/clusters" v-if="autoUpgrade">升级提醒:有 {{nodeUpgradeInfo.count}} 个边缘节点需要升级到 v{{nodeUpgradeInfo.version}} 版本,系统正在尝试自动升级,请耐心等待...</a>
|
||||||
|
<a href="/settings/upgrade" v-else>升级提醒:有 {{nodeUpgradeInfo.count}} 个边缘节点需要升级到 v{{nodeUpgradeInfo.version}} 版本,请到系统设置-升级设置页面自行升级...</a>
|
||||||
|
<a href="" title="关闭" @click.prevent="closeMessage"><i class="ui icon remove small"></i></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- API节点升级提醒 -->
|
<!-- API节点升级提醒 -->
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
Tea.context(function () {
|
Tea.context(function () {
|
||||||
this.isOn = true
|
this.isOn = true
|
||||||
|
|
||||||
|
this.syncDefaultClusterEnabled = function () {
|
||||||
|
}
|
||||||
|
|
||||||
this.success = function (resp) {
|
this.success = function (resp) {
|
||||||
let clusterId = 0
|
let clusterId = 0
|
||||||
if (resp != null && resp.data != null && typeof resp.data.clusterId != "undefined") {
|
if (resp != null && resp.data != null && typeof resp.data.clusterId != "undefined") {
|
||||||
|
|||||||
8
EdgeAdmin/web/views/@default/httpdns/index.css
Normal file
8
EdgeAdmin/web/views/@default/httpdns/index.css
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
.ui.message .icon {
|
||||||
|
position: absolute;
|
||||||
|
right: 1em;
|
||||||
|
top: 1.8em;
|
||||||
|
}
|
||||||
|
.chart-box {
|
||||||
|
height: 14em;
|
||||||
|
}
|
||||||
62
EdgeAdmin/web/views/@default/httpdns/index.html
Normal file
62
EdgeAdmin/web/views/@default/httpdns/index.html
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
{$layout}
|
||||||
|
{$template "/echarts"}
|
||||||
|
|
||||||
|
<div style="margin-top: 0.8em" v-if="isLoading">
|
||||||
|
<div class="ui message loading">
|
||||||
|
<div class="ui active inline loader small"></div> 数据加载中...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-show="!isLoading">
|
||||||
|
<div class="ui four columns grid counter-chart">
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>应用<link-icon href="/httpdns/apps"></link-icon></h4>
|
||||||
|
<div class="value"><span>{{board.countApps}}</span>个</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>域名<link-icon href="/httpdns/apps"></link-icon></h4>
|
||||||
|
<div class="value"><span>{{board.countDomains}}</span>个</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>集群<link-icon href="/httpdns/clusters"></link-icon></h4>
|
||||||
|
<div class="value"><span>{{board.countClusters}}</span>个</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui column with-border">
|
||||||
|
<h4>节点<link-icon href="/httpdns/clusters"></link-icon></h4>
|
||||||
|
<div class="value"><span>{{board.countNodes}}</span>
|
||||||
|
<span v-if="board.countOfflineNodes > 0" class="red" style="font-size: 1em">{{board.countOfflineNodes}}离线</span>
|
||||||
|
<span v-else style="font-size: 1em">个</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ui four columns grid chart-grid" style="margin-top: 1em">
|
||||||
|
<div class="ui column">
|
||||||
|
<div class="ui menu text blue small">
|
||||||
|
<a href="" class="item" :class="{active: trafficTab == 'hourly'}" @click.prevent="selectTrafficTab('hourly')">24小时请求趋势</a>
|
||||||
|
<a href="" class="item" :class="{active: trafficTab == 'daily'}" @click.prevent="selectTrafficTab('daily')">15天请求趋势</a>
|
||||||
|
</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="chart-box" id="hourly-traffic-chart" v-show="trafficTab == 'hourly'"></div>
|
||||||
|
<div class="chart-box" id="daily-traffic-chart" v-show="trafficTab == 'daily'"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>应用请求排行 <span>(24小时)</span></h4>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="chart-box" id="top-apps-chart"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>域名请求排行 <span>(24小时)</span></h4>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="chart-box" id="top-domains-chart"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>节点访问排行 <span>(24小时)</span></h4>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="chart-box" id="top-nodes-chart"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
191
EdgeAdmin/web/views/@default/httpdns/index.js
Normal file
191
EdgeAdmin/web/views/@default/httpdns/index.js
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.isLoading = true
|
||||||
|
|
||||||
|
this.$delay(function () {
|
||||||
|
this.$post("$")
|
||||||
|
.success(function (resp) {
|
||||||
|
for (let k in resp.data) {
|
||||||
|
this[k] = resp.data[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$delay(function () {
|
||||||
|
this.reloadHourlyTrafficChart()
|
||||||
|
this.reloadTopAppsChart()
|
||||||
|
this.reloadTopDomainsChart()
|
||||||
|
this.reloadTopNodesChart()
|
||||||
|
})
|
||||||
|
|
||||||
|
this.isLoading = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
this.trafficTab = "hourly"
|
||||||
|
|
||||||
|
this.selectTrafficTab = function (tab) {
|
||||||
|
this.trafficTab = tab
|
||||||
|
if (tab == "hourly") {
|
||||||
|
this.$delay(function () {
|
||||||
|
this.reloadHourlyTrafficChart()
|
||||||
|
})
|
||||||
|
} else if (tab == "daily") {
|
||||||
|
this.$delay(function () {
|
||||||
|
this.reloadDailyTrafficChart()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reloadHourlyTrafficChart = function () {
|
||||||
|
let stats = this.hourlyStats
|
||||||
|
if (stats == null || stats.length == 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.reloadTrafficChart("hourly-traffic-chart", stats, function (args) {
|
||||||
|
return stats[args.dataIndex].day + " " + stats[args.dataIndex].hour + "时<br/>请求数:" + teaweb.formatNumber(stats[args.dataIndex].countRequests)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reloadDailyTrafficChart = function () {
|
||||||
|
let stats = this.dailyStats
|
||||||
|
if (stats == null || stats.length == 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.reloadTrafficChart("daily-traffic-chart", stats, function (args) {
|
||||||
|
return stats[args.dataIndex].day + "<br/>请求数:" + teaweb.formatNumber(stats[args.dataIndex].countRequests)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reloadTrafficChart = function (chartId, stats, tooltipFunc) {
|
||||||
|
let chartBox = document.getElementById(chartId)
|
||||||
|
if (chartBox == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let axis = teaweb.countAxis(stats, function (v) {
|
||||||
|
return v.countRequests
|
||||||
|
})
|
||||||
|
|
||||||
|
let chart = teaweb.initChart(chartBox)
|
||||||
|
let option = {
|
||||||
|
xAxis: {
|
||||||
|
data: stats.map(function (v) {
|
||||||
|
if (v.hour != null) {
|
||||||
|
return v.hour
|
||||||
|
}
|
||||||
|
return v.day
|
||||||
|
})
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
axisLabel: {
|
||||||
|
formatter: function (value) {
|
||||||
|
return value + axis.unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
show: true,
|
||||||
|
trigger: "item",
|
||||||
|
formatter: tooltipFunc
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: 50,
|
||||||
|
top: 10,
|
||||||
|
right: 20,
|
||||||
|
bottom: 20
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: "请求数",
|
||||||
|
type: "line",
|
||||||
|
data: stats.map(function (v) {
|
||||||
|
return v.countRequests / axis.divider
|
||||||
|
}),
|
||||||
|
itemStyle: {
|
||||||
|
color: teaweb.DefaultChartColor
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: teaweb.DefaultChartColor
|
||||||
|
},
|
||||||
|
smooth: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
animation: true
|
||||||
|
}
|
||||||
|
chart.setOption(option)
|
||||||
|
chart.resize()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reloadTopAppsChart = function () {
|
||||||
|
if (this.topAppStats == null || this.topAppStats.length == 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let axis = teaweb.countAxis(this.topAppStats, function (v) {
|
||||||
|
return v.countRequests
|
||||||
|
})
|
||||||
|
teaweb.renderBarChart({
|
||||||
|
id: "top-apps-chart",
|
||||||
|
name: "应用",
|
||||||
|
values: this.topAppStats,
|
||||||
|
x: function (v) {
|
||||||
|
return v.appName
|
||||||
|
},
|
||||||
|
tooltip: function (args, stats) {
|
||||||
|
return stats[args.dataIndex].appName + "<br/>请求数:" + teaweb.formatNumber(stats[args.dataIndex].countRequests)
|
||||||
|
},
|
||||||
|
value: function (v) {
|
||||||
|
return v.countRequests / axis.divider
|
||||||
|
},
|
||||||
|
axis: axis
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reloadTopDomainsChart = function () {
|
||||||
|
if (this.topDomainStats == null || this.topDomainStats.length == 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let axis = teaweb.countAxis(this.topDomainStats, function (v) {
|
||||||
|
return v.countRequests
|
||||||
|
})
|
||||||
|
teaweb.renderBarChart({
|
||||||
|
id: "top-domains-chart",
|
||||||
|
name: "域名",
|
||||||
|
values: this.topDomainStats,
|
||||||
|
x: function (v) {
|
||||||
|
return v.domainName
|
||||||
|
},
|
||||||
|
tooltip: function (args, stats) {
|
||||||
|
return stats[args.dataIndex].domainName + "<br/>请求数:" + teaweb.formatNumber(stats[args.dataIndex].countRequests)
|
||||||
|
},
|
||||||
|
value: function (v) {
|
||||||
|
return v.countRequests / axis.divider
|
||||||
|
},
|
||||||
|
axis: axis
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reloadTopNodesChart = function () {
|
||||||
|
if (this.topNodeStats == null || this.topNodeStats.length == 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let axis = teaweb.countAxis(this.topNodeStats, function (v) {
|
||||||
|
return v.countRequests
|
||||||
|
})
|
||||||
|
teaweb.renderBarChart({
|
||||||
|
id: "top-nodes-chart",
|
||||||
|
name: "节点",
|
||||||
|
values: this.topNodeStats,
|
||||||
|
x: function (v) {
|
||||||
|
return v.nodeName
|
||||||
|
},
|
||||||
|
tooltip: function (args, stats) {
|
||||||
|
return stats[args.dataIndex].nodeName + "<br/>请求数:" + teaweb.formatNumber(stats[args.dataIndex].countRequests)
|
||||||
|
},
|
||||||
|
value: function (v) {
|
||||||
|
return v.countRequests / axis.divider
|
||||||
|
},
|
||||||
|
axis: axis,
|
||||||
|
click: function (args, stats) {
|
||||||
|
window.location = "/httpdns/clusters/cluster/node?nodeId=" + stats[args.dataIndex].nodeId + "&clusterId=" + stats[args.dataIndex].clusterId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -124,6 +124,27 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
||||||
|
<!-- TypeE -->
|
||||||
|
<tbody v-show="type == 'typeE'">
|
||||||
|
<tr>
|
||||||
|
<td>鉴权密钥 *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" maxlength="40" name="typeESecret" v-model="typeESecret" autocomplete="off"/>
|
||||||
|
<p class="comment">只能包含字母、数字,长度不超过40。<a href="" @click.prevent="generateTypeESecret()">[随机生成]</a></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>有效时间</td>
|
||||||
|
<td>
|
||||||
|
<div class="ui input right labeled">
|
||||||
|
<input type="text" maxlength="8" name="typeELife" value="30" style="width: 7em"/>
|
||||||
|
<span class="ui label">秒</span>
|
||||||
|
</div>
|
||||||
|
<p class="comment">URL 格式:<code-label>/hash/timestamp/path</code-label>;签名算法:<code-label>md5(secret + uri + timestamp)</code-label>;timestamp 为十六进制 Unix 秒数。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
|
||||||
<!-- BasicAuth -->
|
<!-- BasicAuth -->
|
||||||
<tbody v-show="type == 'basicAuth'">
|
<tbody v-show="type == 'basicAuth'">
|
||||||
<tr>
|
<tr>
|
||||||
@@ -138,7 +159,7 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-show="moreBasicAuthOptionsVisible">
|
<tr v-show="moreBasicAuthOptionsVisible">
|
||||||
<td>认证领域名<em>(Realm)</em></td>
|
<td>认证领域名 <em>(Realm)</em></td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="basicAuthRealm" value="" maxlength="100"/>
|
<input type="text" name="basicAuthRealm" value="" maxlength="100"/>
|
||||||
</td>
|
</td>
|
||||||
@@ -147,7 +168,7 @@
|
|||||||
<td>字符集</td>
|
<td>字符集</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="basicAuthCharset" style="width: 6em" maxlength="50"/>
|
<input type="text" name="basicAuthCharset" style="width: 6em" maxlength="50"/>
|
||||||
<p class="comment">类似于<code-label>UTF-8</code-label>。</p>
|
<p class="comment">类似于 <code-label>UTF-8</code-label>。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -186,7 +207,7 @@
|
|||||||
<td>限定文件扩展名</td>
|
<td>限定文件扩展名</td>
|
||||||
<td>
|
<td>
|
||||||
<values-box name="exts"></values-box>
|
<values-box name="exts"></values-box>
|
||||||
<p class="comment">如果不为空,则表示只有这些扩展名的文件才需要鉴权;扩展名需要包含点符号(.),比如<code-label>.png</code-label></p>
|
<p class="comment">如果不为空,则表示只有这些扩展名的文件才需要鉴权;扩展名需要包含点符号,比如 <code-label>.png</code-label></p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -200,4 +221,4 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<submit-btn></submit-btn>
|
<submit-btn></submit-btn>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -84,6 +84,18 @@ Tea.context(function () {
|
|||||||
this.authDescription = this.authDescription.replace("t=", this.typeDTimestampParamName + "=")
|
this.authDescription = this.authDescription.replace("t=", this.typeDTimestampParamName + "=")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TypeE
|
||||||
|
*/
|
||||||
|
this.typeESecret = ""
|
||||||
|
|
||||||
|
this.generateTypeESecret = function () {
|
||||||
|
this.$post(".random")
|
||||||
|
.success(function (resp) {
|
||||||
|
this.typeESecret = resp.data.random
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基本认证
|
* 基本认证
|
||||||
*/
|
*/
|
||||||
@@ -97,4 +109,4 @@ Tea.context(function () {
|
|||||||
* 子请求
|
* 子请求
|
||||||
*/
|
*/
|
||||||
this.subRequestFollowRequest = 1
|
this.subRequestFollowRequest = 1
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -122,6 +122,27 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
||||||
|
<!-- TypeE -->
|
||||||
|
<tbody v-show="type == 'typeE'">
|
||||||
|
<tr>
|
||||||
|
<td>鉴权密钥 *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" maxlength="40" name="typeESecret" v-model="typeESecret" autocomplete="off"/>
|
||||||
|
<p class="comment">只能包含字母、数字,长度不超过40。<a href="" @click.prevent="generateTypeESecret()">[随机生成]</a></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>有效时间</td>
|
||||||
|
<td>
|
||||||
|
<div class="ui input right labeled">
|
||||||
|
<input type="text" maxlength="8" name="typeELife" value="30" style="width: 7em" v-model="policy.params.life"/>
|
||||||
|
<span class="ui label">秒</span>
|
||||||
|
</div>
|
||||||
|
<p class="comment">URL 格式:<code-label>/hash/timestamp/path</code-label>;签名算法:<code-label>md5(secret + uri + timestamp)</code-label>;timestamp 为十六进制 Unix 秒数。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
|
||||||
<!-- BasicAuth -->
|
<!-- BasicAuth -->
|
||||||
<tbody v-show="type == 'basicAuth'">
|
<tbody v-show="type == 'basicAuth'">
|
||||||
<tr>
|
<tr>
|
||||||
@@ -136,7 +157,7 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-show="moreBasicAuthOptionsVisible">
|
<tr v-show="moreBasicAuthOptionsVisible">
|
||||||
<td>认证领域名<em>(Realm)</em></td>
|
<td>认证领域名 <em>(Realm)</em></td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="basicAuthRealm" value="" maxlength="100" v-model="policy.params.realm"/>
|
<input type="text" name="basicAuthRealm" value="" maxlength="100" v-model="policy.params.realm"/>
|
||||||
</td>
|
</td>
|
||||||
@@ -145,7 +166,7 @@
|
|||||||
<td>字符集</td>
|
<td>字符集</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="basicAuthCharset" style="width: 6em" v-model="policy.params.charset" maxlength="50"/>
|
<input type="text" name="basicAuthCharset" style="width: 6em" v-model="policy.params.charset" maxlength="50"/>
|
||||||
<p class="comment">类似于<code-label>utf-8</code-label>。</p>
|
<p class="comment">类似于 <code-label>utf-8</code-label>。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -185,7 +206,7 @@
|
|||||||
<td>限定文件扩展名</td>
|
<td>限定文件扩展名</td>
|
||||||
<td>
|
<td>
|
||||||
<values-box name="exts" :v-values="policy.params.exts"></values-box>
|
<values-box name="exts" :v-values="policy.params.exts"></values-box>
|
||||||
<p class="comment">如果不为空,则表示只有这些扩展名的文件才需要鉴权;扩展名需要包含点符号(.),比如<code-label>.png</code-label></p>
|
<p class="comment">如果不为空,则表示只有这些扩展名的文件才需要鉴权;扩展名需要包含点符号,比如 <code-label>.png</code-label></p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -203,4 +224,4 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<submit-btn></submit-btn>
|
<submit-btn></submit-btn>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -115,6 +115,22 @@ Tea.context(function () {
|
|||||||
this.authDescription = this.authDescription.replace("t=", this.typeDTimestampParamName + "=")
|
this.authDescription = this.authDescription.replace("t=", this.typeDTimestampParamName + "=")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TypeE
|
||||||
|
*/
|
||||||
|
this.typeESecret = ""
|
||||||
|
|
||||||
|
if (this.policy.type == "typeE") {
|
||||||
|
this.typeESecret = this.policy.params.secret
|
||||||
|
}
|
||||||
|
|
||||||
|
this.generateTypeESecret = function () {
|
||||||
|
this.$post(".random")
|
||||||
|
.success(function (resp) {
|
||||||
|
this.typeESecret = resp.data.random
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基本鉴权
|
* 基本鉴权
|
||||||
*/
|
*/
|
||||||
@@ -128,4 +144,4 @@ Tea.context(function () {
|
|||||||
* 子请求
|
* 子请求
|
||||||
*/
|
*/
|
||||||
this.subRequestFollowRequest = (this.policy.params.method != null && this.policy.params.method.length > 0) ? 0 : 1
|
this.subRequestFollowRequest = (this.policy.params.method != null && this.policy.params.method.length > 0) ? 0 : 1
|
||||||
})
|
})
|
||||||
|
|||||||
1
EdgeCommon/.gitignore
vendored
1
EdgeCommon/.gitignore
vendored
@@ -0,0 +1 @@
|
|||||||
|
build/temp_build.sh
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
650
EdgeCommon/pkg/rpc/pb/service_httpdns_board.pb.go
Normal file
650
EdgeCommon/pkg/rpc/pb/service_httpdns_board.pb.go
Normal file
@@ -0,0 +1,650 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.36.11
|
||||||
|
// protoc v3.12.4
|
||||||
|
// source: service_httpdns_board.proto
|
||||||
|
|
||||||
|
package pb
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
unsafe "unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 组合看板数据请求
|
||||||
|
type ComposeHTTPDNSBoardRequest struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardRequest) Reset() {
|
||||||
|
*x = ComposeHTTPDNSBoardRequest{}
|
||||||
|
mi := &file_service_httpdns_board_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ComposeHTTPDNSBoardRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_service_httpdns_board_proto_msgTypes[0]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ComposeHTTPDNSBoardRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ComposeHTTPDNSBoardRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_service_httpdns_board_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组合看板数据响应
|
||||||
|
type ComposeHTTPDNSBoardResponse struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
CountApps int64 `protobuf:"varint,1,opt,name=countApps,proto3" json:"countApps,omitempty"`
|
||||||
|
CountDomains int64 `protobuf:"varint,2,opt,name=countDomains,proto3" json:"countDomains,omitempty"`
|
||||||
|
CountClusters int64 `protobuf:"varint,3,opt,name=countClusters,proto3" json:"countClusters,omitempty"`
|
||||||
|
CountNodes int64 `protobuf:"varint,4,opt,name=countNodes,proto3" json:"countNodes,omitempty"`
|
||||||
|
CountOfflineNodes int64 `protobuf:"varint,5,opt,name=countOfflineNodes,proto3" json:"countOfflineNodes,omitempty"`
|
||||||
|
DailyTrafficStats []*ComposeHTTPDNSBoardResponse_DailyTrafficStat `protobuf:"bytes,30,rep,name=dailyTrafficStats,proto3" json:"dailyTrafficStats,omitempty"`
|
||||||
|
HourlyTrafficStats []*ComposeHTTPDNSBoardResponse_HourlyTrafficStat `protobuf:"bytes,31,rep,name=hourlyTrafficStats,proto3" json:"hourlyTrafficStats,omitempty"`
|
||||||
|
TopAppStats []*ComposeHTTPDNSBoardResponse_TopAppStat `protobuf:"bytes,32,rep,name=topAppStats,proto3" json:"topAppStats,omitempty"`
|
||||||
|
TopDomainStats []*ComposeHTTPDNSBoardResponse_TopDomainStat `protobuf:"bytes,33,rep,name=topDomainStats,proto3" json:"topDomainStats,omitempty"`
|
||||||
|
TopNodeStats []*ComposeHTTPDNSBoardResponse_TopNodeStat `protobuf:"bytes,34,rep,name=topNodeStats,proto3" json:"topNodeStats,omitempty"`
|
||||||
|
CpuNodeValues []*NodeValue `protobuf:"bytes,35,rep,name=cpuNodeValues,proto3" json:"cpuNodeValues,omitempty"`
|
||||||
|
MemoryNodeValues []*NodeValue `protobuf:"bytes,36,rep,name=memoryNodeValues,proto3" json:"memoryNodeValues,omitempty"`
|
||||||
|
LoadNodeValues []*NodeValue `protobuf:"bytes,37,rep,name=loadNodeValues,proto3" json:"loadNodeValues,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse) Reset() {
|
||||||
|
*x = ComposeHTTPDNSBoardResponse{}
|
||||||
|
mi := &file_service_httpdns_board_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ComposeHTTPDNSBoardResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_service_httpdns_board_proto_msgTypes[1]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ComposeHTTPDNSBoardResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ComposeHTTPDNSBoardResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_service_httpdns_board_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse) GetCountApps() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.CountApps
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse) GetCountDomains() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.CountDomains
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse) GetCountClusters() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.CountClusters
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse) GetCountNodes() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.CountNodes
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse) GetCountOfflineNodes() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.CountOfflineNodes
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse) GetDailyTrafficStats() []*ComposeHTTPDNSBoardResponse_DailyTrafficStat {
|
||||||
|
if x != nil {
|
||||||
|
return x.DailyTrafficStats
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse) GetHourlyTrafficStats() []*ComposeHTTPDNSBoardResponse_HourlyTrafficStat {
|
||||||
|
if x != nil {
|
||||||
|
return x.HourlyTrafficStats
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse) GetTopAppStats() []*ComposeHTTPDNSBoardResponse_TopAppStat {
|
||||||
|
if x != nil {
|
||||||
|
return x.TopAppStats
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse) GetTopDomainStats() []*ComposeHTTPDNSBoardResponse_TopDomainStat {
|
||||||
|
if x != nil {
|
||||||
|
return x.TopDomainStats
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse) GetTopNodeStats() []*ComposeHTTPDNSBoardResponse_TopNodeStat {
|
||||||
|
if x != nil {
|
||||||
|
return x.TopNodeStats
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse) GetCpuNodeValues() []*NodeValue {
|
||||||
|
if x != nil {
|
||||||
|
return x.CpuNodeValues
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse) GetMemoryNodeValues() []*NodeValue {
|
||||||
|
if x != nil {
|
||||||
|
return x.MemoryNodeValues
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse) GetLoadNodeValues() []*NodeValue {
|
||||||
|
if x != nil {
|
||||||
|
return x.LoadNodeValues
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ComposeHTTPDNSBoardResponse_DailyTrafficStat struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Day string `protobuf:"bytes,1,opt,name=day,proto3" json:"day,omitempty"`
|
||||||
|
Bytes int64 `protobuf:"varint,2,opt,name=bytes,proto3" json:"bytes,omitempty"`
|
||||||
|
CountRequests int64 `protobuf:"varint,3,opt,name=countRequests,proto3" json:"countRequests,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_DailyTrafficStat) Reset() {
|
||||||
|
*x = ComposeHTTPDNSBoardResponse_DailyTrafficStat{}
|
||||||
|
mi := &file_service_httpdns_board_proto_msgTypes[2]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_DailyTrafficStat) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ComposeHTTPDNSBoardResponse_DailyTrafficStat) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_DailyTrafficStat) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_service_httpdns_board_proto_msgTypes[2]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ComposeHTTPDNSBoardResponse_DailyTrafficStat.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ComposeHTTPDNSBoardResponse_DailyTrafficStat) Descriptor() ([]byte, []int) {
|
||||||
|
return file_service_httpdns_board_proto_rawDescGZIP(), []int{1, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_DailyTrafficStat) GetDay() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Day
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_DailyTrafficStat) GetBytes() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Bytes
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_DailyTrafficStat) GetCountRequests() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.CountRequests
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type ComposeHTTPDNSBoardResponse_HourlyTrafficStat struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Hour string `protobuf:"bytes,1,opt,name=hour,proto3" json:"hour,omitempty"`
|
||||||
|
Bytes int64 `protobuf:"varint,2,opt,name=bytes,proto3" json:"bytes,omitempty"`
|
||||||
|
CountRequests int64 `protobuf:"varint,3,opt,name=countRequests,proto3" json:"countRequests,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_HourlyTrafficStat) Reset() {
|
||||||
|
*x = ComposeHTTPDNSBoardResponse_HourlyTrafficStat{}
|
||||||
|
mi := &file_service_httpdns_board_proto_msgTypes[3]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_HourlyTrafficStat) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ComposeHTTPDNSBoardResponse_HourlyTrafficStat) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_HourlyTrafficStat) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_service_httpdns_board_proto_msgTypes[3]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ComposeHTTPDNSBoardResponse_HourlyTrafficStat.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ComposeHTTPDNSBoardResponse_HourlyTrafficStat) Descriptor() ([]byte, []int) {
|
||||||
|
return file_service_httpdns_board_proto_rawDescGZIP(), []int{1, 1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_HourlyTrafficStat) GetHour() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Hour
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_HourlyTrafficStat) GetBytes() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Bytes
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_HourlyTrafficStat) GetCountRequests() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.CountRequests
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type ComposeHTTPDNSBoardResponse_TopAppStat struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
AppId int64 `protobuf:"varint,1,opt,name=appId,proto3" json:"appId,omitempty"`
|
||||||
|
AppName string `protobuf:"bytes,2,opt,name=appName,proto3" json:"appName,omitempty"`
|
||||||
|
CountRequests int64 `protobuf:"varint,3,opt,name=countRequests,proto3" json:"countRequests,omitempty"`
|
||||||
|
Bytes int64 `protobuf:"varint,4,opt,name=bytes,proto3" json:"bytes,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopAppStat) Reset() {
|
||||||
|
*x = ComposeHTTPDNSBoardResponse_TopAppStat{}
|
||||||
|
mi := &file_service_httpdns_board_proto_msgTypes[4]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopAppStat) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ComposeHTTPDNSBoardResponse_TopAppStat) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopAppStat) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_service_httpdns_board_proto_msgTypes[4]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ComposeHTTPDNSBoardResponse_TopAppStat.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ComposeHTTPDNSBoardResponse_TopAppStat) Descriptor() ([]byte, []int) {
|
||||||
|
return file_service_httpdns_board_proto_rawDescGZIP(), []int{1, 2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopAppStat) GetAppId() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.AppId
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopAppStat) GetAppName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.AppName
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopAppStat) GetCountRequests() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.CountRequests
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopAppStat) GetBytes() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Bytes
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type ComposeHTTPDNSBoardResponse_TopDomainStat struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
DomainId int64 `protobuf:"varint,1,opt,name=domainId,proto3" json:"domainId,omitempty"`
|
||||||
|
DomainName string `protobuf:"bytes,2,opt,name=domainName,proto3" json:"domainName,omitempty"`
|
||||||
|
CountRequests int64 `protobuf:"varint,3,opt,name=countRequests,proto3" json:"countRequests,omitempty"`
|
||||||
|
Bytes int64 `protobuf:"varint,4,opt,name=bytes,proto3" json:"bytes,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopDomainStat) Reset() {
|
||||||
|
*x = ComposeHTTPDNSBoardResponse_TopDomainStat{}
|
||||||
|
mi := &file_service_httpdns_board_proto_msgTypes[5]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopDomainStat) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ComposeHTTPDNSBoardResponse_TopDomainStat) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopDomainStat) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_service_httpdns_board_proto_msgTypes[5]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ComposeHTTPDNSBoardResponse_TopDomainStat.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ComposeHTTPDNSBoardResponse_TopDomainStat) Descriptor() ([]byte, []int) {
|
||||||
|
return file_service_httpdns_board_proto_rawDescGZIP(), []int{1, 3}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopDomainStat) GetDomainId() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.DomainId
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopDomainStat) GetDomainName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.DomainName
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopDomainStat) GetCountRequests() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.CountRequests
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopDomainStat) GetBytes() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Bytes
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type ComposeHTTPDNSBoardResponse_TopNodeStat struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
ClusterId int64 `protobuf:"varint,1,opt,name=clusterId,proto3" json:"clusterId,omitempty"`
|
||||||
|
NodeId int64 `protobuf:"varint,2,opt,name=nodeId,proto3" json:"nodeId,omitempty"`
|
||||||
|
NodeName string `protobuf:"bytes,3,opt,name=nodeName,proto3" json:"nodeName,omitempty"`
|
||||||
|
CountRequests int64 `protobuf:"varint,4,opt,name=countRequests,proto3" json:"countRequests,omitempty"`
|
||||||
|
Bytes int64 `protobuf:"varint,5,opt,name=bytes,proto3" json:"bytes,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopNodeStat) Reset() {
|
||||||
|
*x = ComposeHTTPDNSBoardResponse_TopNodeStat{}
|
||||||
|
mi := &file_service_httpdns_board_proto_msgTypes[6]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopNodeStat) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ComposeHTTPDNSBoardResponse_TopNodeStat) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopNodeStat) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_service_httpdns_board_proto_msgTypes[6]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ComposeHTTPDNSBoardResponse_TopNodeStat.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ComposeHTTPDNSBoardResponse_TopNodeStat) Descriptor() ([]byte, []int) {
|
||||||
|
return file_service_httpdns_board_proto_rawDescGZIP(), []int{1, 4}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopNodeStat) GetClusterId() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.ClusterId
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopNodeStat) GetNodeId() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.NodeId
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopNodeStat) GetNodeName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.NodeName
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopNodeStat) GetCountRequests() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.CountRequests
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ComposeHTTPDNSBoardResponse_TopNodeStat) GetBytes() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Bytes
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_service_httpdns_board_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
const file_service_httpdns_board_proto_rawDesc = "" +
|
||||||
|
"\n" +
|
||||||
|
"\x1bservice_httpdns_board.proto\x12\x02pb\x1a\x1dmodels/model_node_value.proto\"\x1c\n" +
|
||||||
|
"\x1aComposeHTTPDNSBoardRequest\"\x9c\v\n" +
|
||||||
|
"\x1bComposeHTTPDNSBoardResponse\x12\x1c\n" +
|
||||||
|
"\tcountApps\x18\x01 \x01(\x03R\tcountApps\x12\"\n" +
|
||||||
|
"\fcountDomains\x18\x02 \x01(\x03R\fcountDomains\x12$\n" +
|
||||||
|
"\rcountClusters\x18\x03 \x01(\x03R\rcountClusters\x12\x1e\n" +
|
||||||
|
"\n" +
|
||||||
|
"countNodes\x18\x04 \x01(\x03R\n" +
|
||||||
|
"countNodes\x12,\n" +
|
||||||
|
"\x11countOfflineNodes\x18\x05 \x01(\x03R\x11countOfflineNodes\x12^\n" +
|
||||||
|
"\x11dailyTrafficStats\x18\x1e \x03(\v20.pb.ComposeHTTPDNSBoardResponse.DailyTrafficStatR\x11dailyTrafficStats\x12a\n" +
|
||||||
|
"\x12hourlyTrafficStats\x18\x1f \x03(\v21.pb.ComposeHTTPDNSBoardResponse.HourlyTrafficStatR\x12hourlyTrafficStats\x12L\n" +
|
||||||
|
"\vtopAppStats\x18 \x03(\v2*.pb.ComposeHTTPDNSBoardResponse.TopAppStatR\vtopAppStats\x12U\n" +
|
||||||
|
"\x0etopDomainStats\x18! \x03(\v2-.pb.ComposeHTTPDNSBoardResponse.TopDomainStatR\x0etopDomainStats\x12O\n" +
|
||||||
|
"\ftopNodeStats\x18\" \x03(\v2+.pb.ComposeHTTPDNSBoardResponse.TopNodeStatR\ftopNodeStats\x123\n" +
|
||||||
|
"\rcpuNodeValues\x18# \x03(\v2\r.pb.NodeValueR\rcpuNodeValues\x129\n" +
|
||||||
|
"\x10memoryNodeValues\x18$ \x03(\v2\r.pb.NodeValueR\x10memoryNodeValues\x125\n" +
|
||||||
|
"\x0eloadNodeValues\x18% \x03(\v2\r.pb.NodeValueR\x0eloadNodeValues\x1a`\n" +
|
||||||
|
"\x10DailyTrafficStat\x12\x10\n" +
|
||||||
|
"\x03day\x18\x01 \x01(\tR\x03day\x12\x14\n" +
|
||||||
|
"\x05bytes\x18\x02 \x01(\x03R\x05bytes\x12$\n" +
|
||||||
|
"\rcountRequests\x18\x03 \x01(\x03R\rcountRequests\x1ac\n" +
|
||||||
|
"\x11HourlyTrafficStat\x12\x12\n" +
|
||||||
|
"\x04hour\x18\x01 \x01(\tR\x04hour\x12\x14\n" +
|
||||||
|
"\x05bytes\x18\x02 \x01(\x03R\x05bytes\x12$\n" +
|
||||||
|
"\rcountRequests\x18\x03 \x01(\x03R\rcountRequests\x1ax\n" +
|
||||||
|
"\n" +
|
||||||
|
"TopAppStat\x12\x14\n" +
|
||||||
|
"\x05appId\x18\x01 \x01(\x03R\x05appId\x12\x18\n" +
|
||||||
|
"\aappName\x18\x02 \x01(\tR\aappName\x12$\n" +
|
||||||
|
"\rcountRequests\x18\x03 \x01(\x03R\rcountRequests\x12\x14\n" +
|
||||||
|
"\x05bytes\x18\x04 \x01(\x03R\x05bytes\x1a\x87\x01\n" +
|
||||||
|
"\rTopDomainStat\x12\x1a\n" +
|
||||||
|
"\bdomainId\x18\x01 \x01(\x03R\bdomainId\x12\x1e\n" +
|
||||||
|
"\n" +
|
||||||
|
"domainName\x18\x02 \x01(\tR\n" +
|
||||||
|
"domainName\x12$\n" +
|
||||||
|
"\rcountRequests\x18\x03 \x01(\x03R\rcountRequests\x12\x14\n" +
|
||||||
|
"\x05bytes\x18\x04 \x01(\x03R\x05bytes\x1a\x9b\x01\n" +
|
||||||
|
"\vTopNodeStat\x12\x1c\n" +
|
||||||
|
"\tclusterId\x18\x01 \x01(\x03R\tclusterId\x12\x16\n" +
|
||||||
|
"\x06nodeId\x18\x02 \x01(\x03R\x06nodeId\x12\x1a\n" +
|
||||||
|
"\bnodeName\x18\x03 \x01(\tR\bnodeName\x12$\n" +
|
||||||
|
"\rcountRequests\x18\x04 \x01(\x03R\rcountRequests\x12\x14\n" +
|
||||||
|
"\x05bytes\x18\x05 \x01(\x03R\x05bytes2m\n" +
|
||||||
|
"\x13HTTPDNSBoardService\x12V\n" +
|
||||||
|
"\x13composeHTTPDNSBoard\x12\x1e.pb.ComposeHTTPDNSBoardRequest\x1a\x1f.pb.ComposeHTTPDNSBoardResponseB\x06Z\x04./pbb\x06proto3"
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_service_httpdns_board_proto_rawDescOnce sync.Once
|
||||||
|
file_service_httpdns_board_proto_rawDescData []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_service_httpdns_board_proto_rawDescGZIP() []byte {
|
||||||
|
file_service_httpdns_board_proto_rawDescOnce.Do(func() {
|
||||||
|
file_service_httpdns_board_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_service_httpdns_board_proto_rawDesc), len(file_service_httpdns_board_proto_rawDesc)))
|
||||||
|
})
|
||||||
|
return file_service_httpdns_board_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_service_httpdns_board_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||||
|
var file_service_httpdns_board_proto_goTypes = []any{
|
||||||
|
(*ComposeHTTPDNSBoardRequest)(nil), // 0: pb.ComposeHTTPDNSBoardRequest
|
||||||
|
(*ComposeHTTPDNSBoardResponse)(nil), // 1: pb.ComposeHTTPDNSBoardResponse
|
||||||
|
(*ComposeHTTPDNSBoardResponse_DailyTrafficStat)(nil), // 2: pb.ComposeHTTPDNSBoardResponse.DailyTrafficStat
|
||||||
|
(*ComposeHTTPDNSBoardResponse_HourlyTrafficStat)(nil), // 3: pb.ComposeHTTPDNSBoardResponse.HourlyTrafficStat
|
||||||
|
(*ComposeHTTPDNSBoardResponse_TopAppStat)(nil), // 4: pb.ComposeHTTPDNSBoardResponse.TopAppStat
|
||||||
|
(*ComposeHTTPDNSBoardResponse_TopDomainStat)(nil), // 5: pb.ComposeHTTPDNSBoardResponse.TopDomainStat
|
||||||
|
(*ComposeHTTPDNSBoardResponse_TopNodeStat)(nil), // 6: pb.ComposeHTTPDNSBoardResponse.TopNodeStat
|
||||||
|
(*NodeValue)(nil), // 7: pb.NodeValue
|
||||||
|
}
|
||||||
|
var file_service_httpdns_board_proto_depIdxs = []int32{
|
||||||
|
2, // 0: pb.ComposeHTTPDNSBoardResponse.dailyTrafficStats:type_name -> pb.ComposeHTTPDNSBoardResponse.DailyTrafficStat
|
||||||
|
3, // 1: pb.ComposeHTTPDNSBoardResponse.hourlyTrafficStats:type_name -> pb.ComposeHTTPDNSBoardResponse.HourlyTrafficStat
|
||||||
|
4, // 2: pb.ComposeHTTPDNSBoardResponse.topAppStats:type_name -> pb.ComposeHTTPDNSBoardResponse.TopAppStat
|
||||||
|
5, // 3: pb.ComposeHTTPDNSBoardResponse.topDomainStats:type_name -> pb.ComposeHTTPDNSBoardResponse.TopDomainStat
|
||||||
|
6, // 4: pb.ComposeHTTPDNSBoardResponse.topNodeStats:type_name -> pb.ComposeHTTPDNSBoardResponse.TopNodeStat
|
||||||
|
7, // 5: pb.ComposeHTTPDNSBoardResponse.cpuNodeValues:type_name -> pb.NodeValue
|
||||||
|
7, // 6: pb.ComposeHTTPDNSBoardResponse.memoryNodeValues:type_name -> pb.NodeValue
|
||||||
|
7, // 7: pb.ComposeHTTPDNSBoardResponse.loadNodeValues:type_name -> pb.NodeValue
|
||||||
|
0, // 8: pb.HTTPDNSBoardService.composeHTTPDNSBoard:input_type -> pb.ComposeHTTPDNSBoardRequest
|
||||||
|
1, // 9: pb.HTTPDNSBoardService.composeHTTPDNSBoard:output_type -> pb.ComposeHTTPDNSBoardResponse
|
||||||
|
9, // [9:10] is the sub-list for method output_type
|
||||||
|
8, // [8:9] is the sub-list for method input_type
|
||||||
|
8, // [8:8] is the sub-list for extension type_name
|
||||||
|
8, // [8:8] is the sub-list for extension extendee
|
||||||
|
0, // [0:8] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_service_httpdns_board_proto_init() }
|
||||||
|
func file_service_httpdns_board_proto_init() {
|
||||||
|
if File_service_httpdns_board_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file_models_model_node_value_proto_init()
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_service_httpdns_board_proto_rawDesc), len(file_service_httpdns_board_proto_rawDesc)),
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 7,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 1,
|
||||||
|
},
|
||||||
|
GoTypes: file_service_httpdns_board_proto_goTypes,
|
||||||
|
DependencyIndexes: file_service_httpdns_board_proto_depIdxs,
|
||||||
|
MessageInfos: file_service_httpdns_board_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_service_httpdns_board_proto = out.File
|
||||||
|
file_service_httpdns_board_proto_goTypes = nil
|
||||||
|
file_service_httpdns_board_proto_depIdxs = nil
|
||||||
|
}
|
||||||
125
EdgeCommon/pkg/rpc/pb/service_httpdns_board_grpc.pb.go
Normal file
125
EdgeCommon/pkg/rpc/pb/service_httpdns_board_grpc.pb.go
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// - protoc-gen-go-grpc v1.6.1
|
||||||
|
// - protoc v3.12.4
|
||||||
|
// source: service_httpdns_board.proto
|
||||||
|
|
||||||
|
package pb
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
// Requires gRPC-Go v1.64.0 or later.
|
||||||
|
const _ = grpc.SupportPackageIsVersion9
|
||||||
|
|
||||||
|
const (
|
||||||
|
HTTPDNSBoardService_ComposeHTTPDNSBoard_FullMethodName = "/pb.HTTPDNSBoardService/composeHTTPDNSBoard"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTPDNSBoardServiceClient is the client API for HTTPDNSBoardService service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
//
|
||||||
|
// HTTPDNS 看板服务
|
||||||
|
type HTTPDNSBoardServiceClient interface {
|
||||||
|
// 组合看板数据
|
||||||
|
ComposeHTTPDNSBoard(ctx context.Context, in *ComposeHTTPDNSBoardRequest, opts ...grpc.CallOption) (*ComposeHTTPDNSBoardResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type hTTPDNSBoardServiceClient struct {
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHTTPDNSBoardServiceClient(cc grpc.ClientConnInterface) HTTPDNSBoardServiceClient {
|
||||||
|
return &hTTPDNSBoardServiceClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *hTTPDNSBoardServiceClient) ComposeHTTPDNSBoard(ctx context.Context, in *ComposeHTTPDNSBoardRequest, opts ...grpc.CallOption) (*ComposeHTTPDNSBoardResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(ComposeHTTPDNSBoardResponse)
|
||||||
|
err := c.cc.Invoke(ctx, HTTPDNSBoardService_ComposeHTTPDNSBoard_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPDNSBoardServiceServer is the server API for HTTPDNSBoardService service.
|
||||||
|
// All implementations should embed UnimplementedHTTPDNSBoardServiceServer
|
||||||
|
// for forward compatibility.
|
||||||
|
//
|
||||||
|
// HTTPDNS 看板服务
|
||||||
|
type HTTPDNSBoardServiceServer interface {
|
||||||
|
// 组合看板数据
|
||||||
|
ComposeHTTPDNSBoard(context.Context, *ComposeHTTPDNSBoardRequest) (*ComposeHTTPDNSBoardResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedHTTPDNSBoardServiceServer should be embedded to have
|
||||||
|
// forward compatible implementations.
|
||||||
|
//
|
||||||
|
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||||
|
// pointer dereference when methods are called.
|
||||||
|
type UnimplementedHTTPDNSBoardServiceServer struct{}
|
||||||
|
|
||||||
|
func (UnimplementedHTTPDNSBoardServiceServer) ComposeHTTPDNSBoard(context.Context, *ComposeHTTPDNSBoardRequest) (*ComposeHTTPDNSBoardResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "method ComposeHTTPDNSBoard not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedHTTPDNSBoardServiceServer) testEmbeddedByValue() {}
|
||||||
|
|
||||||
|
// UnsafeHTTPDNSBoardServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
// Use of this interface is not recommended, as added methods to HTTPDNSBoardServiceServer will
|
||||||
|
// result in compilation errors.
|
||||||
|
type UnsafeHTTPDNSBoardServiceServer interface {
|
||||||
|
mustEmbedUnimplementedHTTPDNSBoardServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterHTTPDNSBoardServiceServer(s grpc.ServiceRegistrar, srv HTTPDNSBoardServiceServer) {
|
||||||
|
// If the following call panics, it indicates UnimplementedHTTPDNSBoardServiceServer was
|
||||||
|
// embedded by pointer and is nil. This will cause panics if an
|
||||||
|
// unimplemented method is ever invoked, so we test this at initialization
|
||||||
|
// time to prevent it from happening at runtime later due to I/O.
|
||||||
|
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||||
|
t.testEmbeddedByValue()
|
||||||
|
}
|
||||||
|
s.RegisterService(&HTTPDNSBoardService_ServiceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _HTTPDNSBoardService_ComposeHTTPDNSBoard_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(ComposeHTTPDNSBoardRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(HTTPDNSBoardServiceServer).ComposeHTTPDNSBoard(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: HTTPDNSBoardService_ComposeHTTPDNSBoard_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(HTTPDNSBoardServiceServer).ComposeHTTPDNSBoard(ctx, req.(*ComposeHTTPDNSBoardRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPDNSBoardService_ServiceDesc is the grpc.ServiceDesc for HTTPDNSBoardService service.
|
||||||
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
var HTTPDNSBoardService_ServiceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "pb.HTTPDNSBoardService",
|
||||||
|
HandlerType: (*HTTPDNSBoardServiceServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "composeHTTPDNSBoard",
|
||||||
|
Handler: _HTTPDNSBoardService_ComposeHTTPDNSBoard_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{},
|
||||||
|
Metadata: "service_httpdns_board.proto",
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var httpAuthTimestampRegexp = regexp.MustCompile(`^\d{10}$`)
|
var httpAuthTimestampRegexp = regexp.MustCompile(`^\d{10}$`)
|
||||||
|
var httpAuthHexTimestampRegexp = regexp.MustCompile(`^[0-9a-fA-F]{1,16}$`)
|
||||||
|
|
||||||
type HTTPAuthBaseMethod struct {
|
type HTTPAuthBaseMethod struct {
|
||||||
Exts []string `json:"exts"`
|
Exts []string `json:"exts"`
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ type HTTPAuthType = string
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
HTTPAuthTypeBasicAuth HTTPAuthType = "basicAuth" // BasicAuth
|
HTTPAuthTypeBasicAuth HTTPAuthType = "basicAuth" // BasicAuth
|
||||||
HTTPAuthTypeSubRequest HTTPAuthType = "subRequest" // 子请求
|
HTTPAuthTypeSubRequest HTTPAuthType = "subRequest" // SubRequest
|
||||||
|
HTTPAuthTypeTypeA HTTPAuthType = "typeA"
|
||||||
HTTPAuthTypeTypeA HTTPAuthType = "typeA"
|
HTTPAuthTypeTypeB HTTPAuthType = "typeB"
|
||||||
HTTPAuthTypeTypeB HTTPAuthType = "typeB"
|
HTTPAuthTypeTypeC HTTPAuthType = "typeC"
|
||||||
HTTPAuthTypeTypeC HTTPAuthType = "typeC"
|
HTTPAuthTypeTypeD HTTPAuthType = "typeD"
|
||||||
HTTPAuthTypeTypeD HTTPAuthType = "typeD"
|
HTTPAuthTypeTypeE HTTPAuthType = "typeE"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HTTPAuthTypeDefinition struct {
|
type HTTPAuthTypeDefinition struct {
|
||||||
@@ -23,10 +23,11 @@ type HTTPAuthTypeDefinition struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func FindAllHTTPAuthTypes(role string) []*HTTPAuthTypeDefinition {
|
func FindAllHTTPAuthTypes(role string) []*HTTPAuthTypeDefinition {
|
||||||
var urlDocA = ""
|
urlDocA := ""
|
||||||
var urlDocB = ""
|
urlDocB := ""
|
||||||
var urlDocC = ""
|
urlDocC := ""
|
||||||
var urlDocD = ""
|
urlDocD := ""
|
||||||
|
urlDocE := "https://help.aliyun.com/zh/cdn/user-guide/type-c-signing"
|
||||||
|
|
||||||
switch role {
|
switch role {
|
||||||
case "admin":
|
case "admin":
|
||||||
@@ -66,15 +67,21 @@ func FindAllHTTPAuthTypes(role string) []*HTTPAuthTypeDefinition {
|
|||||||
Description: "示例URL:https://example.com/images/test.jpg?sign=f66af42f87cf63a64f4b86ec11c7797a&t=1661753717<br/><a href=\"" + urlDocD + "\" target=\"_blank\">[算法详解]</a>",
|
Description: "示例URL:https://example.com/images/test.jpg?sign=f66af42f87cf63a64f4b86ec11c7797a&t=1661753717<br/><a href=\"" + urlDocD + "\" target=\"_blank\">[算法详解]</a>",
|
||||||
IsPlus: true,
|
IsPlus: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "URL鉴权E",
|
||||||
|
Code: HTTPAuthTypeTypeE,
|
||||||
|
Description: "严格兼容阿里云 Type-C 路径鉴权,示例URL:https://example.com/3a2c79f2d2f0df2f8f9e05ec9f482e5d/67cfdb9e/images/test.jpg<br/><a href=\"" + urlDocE + "\" target=\"_blank\">[阿里云文档]</a>",
|
||||||
|
IsPlus: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "基本认证",
|
Name: "基本认证",
|
||||||
Code: HTTPAuthTypeBasicAuth,
|
Code: HTTPAuthTypeBasicAuth,
|
||||||
Description: "BasicAuth,最简单的HTTP请求认证方式,通过传递<span class=\"ui label tiny basic text\">Authorization: Basic xxx</span> Header认证。",
|
Description: "BasicAuth,最简单的 HTTP 请求认证方式,通过传递 <span class=\"ui label tiny basic text\">Authorization: Basic xxx</span> Header 认证。",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "子请求",
|
Name: "子请求",
|
||||||
Code: HTTPAuthTypeSubRequest,
|
Code: HTTPAuthTypeSubRequest,
|
||||||
Description: "通过自定义的URL子请求来认证请求。",
|
Description: "通过自定义的 URL 子请求来认证请求。",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ func (this *HTTPAuthPolicy) Init() error {
|
|||||||
this.method = NewHTTPAuthTypeCMethod()
|
this.method = NewHTTPAuthTypeCMethod()
|
||||||
case HTTPAuthTypeTypeD:
|
case HTTPAuthTypeTypeD:
|
||||||
this.method = NewHTTPAuthTypeDMethod()
|
this.method = NewHTTPAuthTypeDMethod()
|
||||||
|
case HTTPAuthTypeTypeE:
|
||||||
|
this.method = NewHTTPAuthTypeEMethod()
|
||||||
}
|
}
|
||||||
|
|
||||||
if this.method == nil {
|
if this.method == nil {
|
||||||
|
|||||||
@@ -1,5 +1,138 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
function find-binary() {
|
||||||
|
local candidate
|
||||||
|
for candidate in "$@"; do
|
||||||
|
if [ -z "$candidate" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [ -x "$candidate" ]; then
|
||||||
|
echo "$candidate"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if command -v "$candidate" >/dev/null 2>&1; then
|
||||||
|
command -v "$candidate"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function host-goarch() {
|
||||||
|
case "$(uname -m)" in
|
||||||
|
x86_64|amd64)
|
||||||
|
echo "amd64"
|
||||||
|
;;
|
||||||
|
aarch64|arm64)
|
||||||
|
echo "arm64"
|
||||||
|
;;
|
||||||
|
i386|i486|i586|i686)
|
||||||
|
echo "386"
|
||||||
|
;;
|
||||||
|
mips64)
|
||||||
|
echo "mips64"
|
||||||
|
;;
|
||||||
|
mips64el)
|
||||||
|
echo "mips64le"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo ""
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
function find-linux-static-toolchain() {
|
||||||
|
local arch=$1
|
||||||
|
local cc_bin=""
|
||||||
|
local cxx_bin=""
|
||||||
|
local host_arch
|
||||||
|
|
||||||
|
host_arch=$(host-goarch)
|
||||||
|
|
||||||
|
case "$arch" in
|
||||||
|
amd64)
|
||||||
|
cc_bin=$(find-binary \
|
||||||
|
"/usr/local/gcc/x86_64-unknown-linux-gnu/bin/x86_64-unknown-linux-gnu-gcc" \
|
||||||
|
"/usr/local/opt/musl-cross/bin/x86_64-linux-musl-gcc" \
|
||||||
|
"x86_64-unknown-linux-gnu-gcc" \
|
||||||
|
"x86_64-linux-musl-gcc" \
|
||||||
|
"musl-gcc")
|
||||||
|
cxx_bin=$(find-binary \
|
||||||
|
"/usr/local/gcc/x86_64-unknown-linux-gnu/bin/x86_64-unknown-linux-gnu-g++" \
|
||||||
|
"/usr/local/opt/musl-cross/bin/x86_64-linux-musl-g++" \
|
||||||
|
"x86_64-unknown-linux-gnu-g++" \
|
||||||
|
"x86_64-linux-musl-g++")
|
||||||
|
if [ -z "$cc_bin" ] && [ "$host_arch" = "amd64" ]; then
|
||||||
|
cc_bin=$(find-binary "gcc" "cc" "clang")
|
||||||
|
fi
|
||||||
|
if [ -z "$cxx_bin" ] && [ "$host_arch" = "amd64" ]; then
|
||||||
|
cxx_bin=$(find-binary "g++" "c++" "clang++")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
386)
|
||||||
|
cc_bin=$(find-binary "/usr/local/opt/musl-cross/bin/i486-linux-musl-gcc" "i486-linux-musl-gcc")
|
||||||
|
cxx_bin=$(find-binary "/usr/local/opt/musl-cross/bin/i486-linux-musl-g++" "i486-linux-musl-g++")
|
||||||
|
if [ -z "$cc_bin" ] && [ "$host_arch" = "386" ]; then
|
||||||
|
cc_bin=$(find-binary "gcc" "cc" "clang")
|
||||||
|
fi
|
||||||
|
if [ -z "$cxx_bin" ] && [ "$host_arch" = "386" ]; then
|
||||||
|
cxx_bin=$(find-binary "g++" "c++" "clang++")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
arm64)
|
||||||
|
cc_bin=$(find-binary \
|
||||||
|
"/usr/local/gcc/aarch64-unknown-linux-gnu/bin/aarch64-unknown-linux-gnu-gcc" \
|
||||||
|
"/usr/local/opt/musl-cross/bin/aarch64-linux-musl-gcc" \
|
||||||
|
"aarch64-unknown-linux-gnu-gcc" \
|
||||||
|
"aarch64-linux-musl-gcc")
|
||||||
|
cxx_bin=$(find-binary \
|
||||||
|
"/usr/local/gcc/aarch64-unknown-linux-gnu/bin/aarch64-unknown-linux-gnu-g++" \
|
||||||
|
"/usr/local/opt/musl-cross/bin/aarch64-linux-musl-g++" \
|
||||||
|
"aarch64-unknown-linux-gnu-g++" \
|
||||||
|
"aarch64-linux-musl-g++")
|
||||||
|
if [ -z "$cc_bin" ] && [ "$host_arch" = "arm64" ]; then
|
||||||
|
cc_bin=$(find-binary "gcc" "cc" "clang")
|
||||||
|
fi
|
||||||
|
if [ -z "$cxx_bin" ] && [ "$host_arch" = "arm64" ]; then
|
||||||
|
cxx_bin=$(find-binary "g++" "c++" "clang++")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
mips64)
|
||||||
|
cc_bin=$(find-binary "/usr/local/opt/musl-cross/bin/mips64-linux-musl-gcc" "mips64-linux-musl-gcc")
|
||||||
|
cxx_bin=$(find-binary "/usr/local/opt/musl-cross/bin/mips64-linux-musl-g++" "mips64-linux-musl-g++")
|
||||||
|
if [ -z "$cc_bin" ] && [ "$host_arch" = "mips64" ]; then
|
||||||
|
cc_bin=$(find-binary "gcc" "cc" "clang")
|
||||||
|
fi
|
||||||
|
if [ -z "$cxx_bin" ] && [ "$host_arch" = "mips64" ]; then
|
||||||
|
cxx_bin=$(find-binary "g++" "c++" "clang++")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
mips64le)
|
||||||
|
cc_bin=$(find-binary "/usr/local/opt/musl-cross/bin/mips64el-linux-musl-gcc" "mips64el-linux-musl-gcc")
|
||||||
|
cxx_bin=$(find-binary "/usr/local/opt/musl-cross/bin/mips64el-linux-musl-g++" "mips64el-linux-musl-g++")
|
||||||
|
if [ -z "$cc_bin" ] && [ "$host_arch" = "mips64le" ]; then
|
||||||
|
cc_bin=$(find-binary "gcc" "cc" "clang")
|
||||||
|
fi
|
||||||
|
if [ -z "$cxx_bin" ] && [ "$host_arch" = "mips64le" ]; then
|
||||||
|
cxx_bin=$(find-binary "g++" "c++" "clang++")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$cc_bin" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [ -z "$cxx_bin" ]; then
|
||||||
|
cxx_bin="$cc_bin"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$cc_bin|$cxx_bin"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
function build() {
|
function build() {
|
||||||
ROOT=$(dirname "$0")
|
ROOT=$(dirname "$0")
|
||||||
NAME="edge-dns"
|
NAME="edge-dns"
|
||||||
@@ -18,10 +151,9 @@ function build() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "checking ..."
|
echo "checking ..."
|
||||||
ZIP_PATH=$(which zip)
|
if ! command -v zip >/dev/null 2>&1; then
|
||||||
if [ -z "$ZIP_PATH" ]; then
|
|
||||||
echo "we need 'zip' command to compress files"
|
echo "we need 'zip' command to compress files"
|
||||||
exit
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "building v${VERSION}/${OS}/${ARCH} ..."
|
echo "building v${VERSION}/${OS}/${ARCH} ..."
|
||||||
@@ -37,38 +169,20 @@ function build() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
cp "$ROOT"/configs/api_dns.template.yaml "$DIST"/configs
|
cp "$ROOT"/configs/api_dns.template.yaml "$DIST"/configs
|
||||||
copy_fluent_bit_assets "$ROOT" "$DIST" "$OS" "$ARCH" || exit 1
|
|
||||||
|
|
||||||
echo "building ..."
|
echo "building ..."
|
||||||
|
|
||||||
MUSL_DIR="/usr/local/opt/musl-cross/bin"
|
if [ "$OS" == "linux" ]; then
|
||||||
CC_PATH=""
|
TOOLCHAIN=$(find-linux-static-toolchain "$ARCH")
|
||||||
CXX_PATH=""
|
if [ -z "$TOOLCHAIN" ]; then
|
||||||
if [[ $(uname -a) == *"Darwin"* && "${OS}" == "linux" ]]; then
|
echo "could not find a static Linux toolchain for ${ARCH}"
|
||||||
# /usr/local/opt/musl-cross/bin/
|
echo "install a musl toolchain before building edge-dns"
|
||||||
if [ "${ARCH}" == "amd64" ]; then
|
exit 1
|
||||||
CC_PATH="x86_64-linux-musl-gcc"
|
|
||||||
CXX_PATH="x86_64-linux-musl-g++"
|
|
||||||
fi
|
fi
|
||||||
if [ "${ARCH}" == "386" ]; then
|
|
||||||
CC_PATH="i486-linux-musl-gcc"
|
CC_BIN=${TOOLCHAIN%|*}
|
||||||
CXX_PATH="i486-linux-musl-g++"
|
CXX_BIN=${TOOLCHAIN#*|}
|
||||||
fi
|
env CC="$CC_BIN" CXX="$CXX_BIN" GOOS="${OS}" GOARCH="${ARCH}" CGO_ENABLED=1 go build -trimpath -tags="plus" -o "$DIST"/bin/${NAME} -ldflags "-linkmode external -extldflags -static -s -w" "$ROOT"/../cmd/edge-dns/main.go
|
||||||
if [ "${ARCH}" == "arm64" ]; then
|
|
||||||
CC_PATH="aarch64-linux-musl-gcc"
|
|
||||||
CXX_PATH="aarch64-linux-musl-g++"
|
|
||||||
fi
|
|
||||||
if [ "${ARCH}" == "mips64" ]; then
|
|
||||||
CC_PATH="mips64-linux-musl-gcc"
|
|
||||||
CXX_PATH="mips64-linux-musl-g++"
|
|
||||||
fi
|
|
||||||
if [ "${ARCH}" == "mips64le" ]; then
|
|
||||||
CC_PATH="mips64el-linux-musl-gcc"
|
|
||||||
CXX_PATH="mips64el-linux-musl-g++"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if [ ! -z $CC_PATH ]; then
|
|
||||||
env CC=$MUSL_DIR/$CC_PATH CXX=$MUSL_DIR/$CXX_PATH GOOS="${OS}" GOARCH="${ARCH}" CGO_ENABLED=1 go build -trimpath -tags="plus" -o "$DIST"/bin/${NAME} -ldflags "-linkmode external -extldflags -static -s -w" "$ROOT"/../cmd/edge-dns/main.go
|
|
||||||
else
|
else
|
||||||
env GOOS="${OS}" GOARCH="${ARCH}" CGO_ENABLED=1 go build -trimpath -tags="plus" -o "$DIST"/bin/${NAME} -ldflags="-s -w" "$ROOT"/../cmd/edge-dns/main.go
|
env GOOS="${OS}" GOARCH="${ARCH}" CGO_ENABLED=1 go build -trimpath -tags="plus" -o "$DIST"/bin/${NAME} -ldflags="-s -w" "$ROOT"/../cmd/edge-dns/main.go
|
||||||
fi
|
fi
|
||||||
@@ -76,7 +190,7 @@ function build() {
|
|||||||
# check build result
|
# check build result
|
||||||
RESULT=$?
|
RESULT=$?
|
||||||
if [ "${RESULT}" != "0" ]; then
|
if [ "${RESULT}" != "0" ]; then
|
||||||
exit
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# delete hidden files
|
# delete hidden files
|
||||||
@@ -95,81 +209,6 @@ function build() {
|
|||||||
echo "OK"
|
echo "OK"
|
||||||
}
|
}
|
||||||
|
|
||||||
function copy_fluent_bit_assets() {
|
|
||||||
ROOT=$1
|
|
||||||
DIST=$2
|
|
||||||
OS=$3
|
|
||||||
ARCH=$4
|
|
||||||
FLUENT_ROOT="$ROOT/../../deploy/fluent-bit"
|
|
||||||
FLUENT_DIST="$DIST/deploy/fluent-bit"
|
|
||||||
|
|
||||||
if [ ! -d "$FLUENT_ROOT" ]; then
|
|
||||||
echo "[error] fluent-bit source directory not found: $FLUENT_ROOT"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
verify_fluent_bit_package_matrix "$FLUENT_ROOT" "$ARCH" || return 1
|
|
||||||
|
|
||||||
rm -rf "$FLUENT_DIST"
|
|
||||||
mkdir -p "$FLUENT_DIST"
|
|
||||||
|
|
||||||
for file in fluent-bit.conf fluent-bit-dns.conf fluent-bit-https.conf fluent-bit-dns-https.conf fluent-bit-windows.conf fluent-bit-windows-https.conf parsers.conf clickhouse-upstream.conf clickhouse-upstream-windows.conf README.md; do
|
|
||||||
if [ -f "$FLUENT_ROOT/$file" ]; then
|
|
||||||
cp "$FLUENT_ROOT/$file" "$FLUENT_DIST/"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$OS" = "linux" ]; then
|
|
||||||
PACKAGE_SRC="$FLUENT_ROOT/packages/linux-$ARCH"
|
|
||||||
PACKAGE_DST="$FLUENT_DIST/packages/linux-$ARCH"
|
|
||||||
if [ -d "$PACKAGE_SRC" ]; then
|
|
||||||
mkdir -p "$PACKAGE_DST"
|
|
||||||
cp -R "$PACKAGE_SRC/." "$PACKAGE_DST/"
|
|
||||||
else
|
|
||||||
echo "[error] fluent-bit package directory not found: $PACKAGE_SRC"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
rm -f "$FLUENT_DIST/.gitignore"
|
|
||||||
rm -f "$FLUENT_DIST"/logs.db*
|
|
||||||
rm -rf "$FLUENT_DIST/storage"
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
function verify_fluent_bit_package_matrix() {
|
|
||||||
FLUENT_ROOT=$1
|
|
||||||
ARCH=$2
|
|
||||||
REQUIRED_FILES=()
|
|
||||||
if [ "$ARCH" = "amd64" ]; then
|
|
||||||
REQUIRED_FILES=(
|
|
||||||
"packages/linux-amd64/fluent-bit_4.2.2_amd64.deb"
|
|
||||||
"packages/linux-amd64/fluent-bit-4.2.2-1.x86_64.rpm"
|
|
||||||
)
|
|
||||||
elif [ "$ARCH" = "arm64" ]; then
|
|
||||||
REQUIRED_FILES=(
|
|
||||||
"packages/linux-arm64/fluent-bit_4.2.2_arm64.deb"
|
|
||||||
"packages/linux-arm64/fluent-bit-4.2.2-1.aarch64.rpm"
|
|
||||||
)
|
|
||||||
else
|
|
||||||
echo "[error] unsupported arch for fluent-bit package validation: $ARCH"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
MISSING=0
|
|
||||||
for FILE in "${REQUIRED_FILES[@]}"; do
|
|
||||||
if [ ! -f "$FLUENT_ROOT/$FILE" ]; then
|
|
||||||
echo "[error] fluent-bit matrix package missing: $FLUENT_ROOT/$FILE"
|
|
||||||
MISSING=1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$MISSING" -ne 0 ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
function lookup-version() {
|
function lookup-version() {
|
||||||
FILE=$1
|
FILE=$1
|
||||||
VERSION_DATA=$(cat "$FILE")
|
VERSION_DATA=$(cat "$FILE")
|
||||||
@@ -179,7 +218,7 @@ function lookup-version() {
|
|||||||
echo "$VERSION"
|
echo "$VERSION"
|
||||||
else
|
else
|
||||||
echo "could not match version"
|
echo "could not match version"
|
||||||
exit
|
exit 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package teaconst
|
package teaconst
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "1.4.9" //1.3.8.2
|
Version = "1.5.0" //1.3.8.2
|
||||||
|
|
||||||
ProductName = "Edge DNS"
|
ProductName = "Edge DNS"
|
||||||
ProcessName = "edge-dns"
|
ProcessName = "edge-dns"
|
||||||
|
|||||||
@@ -34,10 +34,14 @@ function build() {
|
|||||||
mkdir -p "$DIST"/data
|
mkdir -p "$DIST"/data
|
||||||
|
|
||||||
cp "$ROOT"/configs/api_httpdns.template.yaml "$DIST"/configs
|
cp "$ROOT"/configs/api_httpdns.template.yaml "$DIST"/configs
|
||||||
copy_fluent_bit_assets "$ROOT" "$DIST" "$OS" "$ARCH" || exit 1
|
|
||||||
|
|
||||||
env GOOS="${OS}" GOARCH="${ARCH}" CGO_ENABLED=1 \
|
if [ "${OS}" = "linux" ]; then
|
||||||
go build -trimpath -o "$DIST"/bin/${NAME} -ldflags="-s -w" "$ROOT"/../cmd/edge-httpdns/main.go
|
env GOOS="${OS}" GOARCH="${ARCH}" CGO_ENABLED=0 \
|
||||||
|
go build -trimpath -o "$DIST"/bin/${NAME} -ldflags="-s -w" "$ROOT"/../cmd/edge-httpdns/main.go
|
||||||
|
else
|
||||||
|
env GOOS="${OS}" GOARCH="${ARCH}" CGO_ENABLED=1 \
|
||||||
|
go build -trimpath -o "$DIST"/bin/${NAME} -ldflags="-s -w" "$ROOT"/../cmd/edge-httpdns/main.go
|
||||||
|
fi
|
||||||
|
|
||||||
if [ ! -f "$DIST"/bin/${NAME} ]; then
|
if [ ! -f "$DIST"/bin/${NAME} ]; then
|
||||||
echo "build failed!"
|
echo "build failed!"
|
||||||
@@ -60,81 +64,6 @@ function build() {
|
|||||||
echo "OK"
|
echo "OK"
|
||||||
}
|
}
|
||||||
|
|
||||||
function copy_fluent_bit_assets() {
|
|
||||||
ROOT=$1
|
|
||||||
DIST=$2
|
|
||||||
OS=$3
|
|
||||||
ARCH=$4
|
|
||||||
FLUENT_ROOT="$ROOT/../../deploy/fluent-bit"
|
|
||||||
FLUENT_DIST="$DIST/deploy/fluent-bit"
|
|
||||||
|
|
||||||
if [ ! -d "$FLUENT_ROOT" ]; then
|
|
||||||
echo "[error] fluent-bit source directory not found: $FLUENT_ROOT"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
verify_fluent_bit_package_matrix "$FLUENT_ROOT" "$ARCH" || return 1
|
|
||||||
|
|
||||||
rm -rf "$FLUENT_DIST"
|
|
||||||
mkdir -p "$FLUENT_DIST"
|
|
||||||
|
|
||||||
for file in fluent-bit.conf fluent-bit-dns.conf fluent-bit-https.conf fluent-bit-dns-https.conf fluent-bit-windows.conf fluent-bit-windows-https.conf parsers.conf clickhouse-upstream.conf clickhouse-upstream-windows.conf README.md; do
|
|
||||||
if [ -f "$FLUENT_ROOT/$file" ]; then
|
|
||||||
cp "$FLUENT_ROOT/$file" "$FLUENT_DIST/"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$OS" = "linux" ]; then
|
|
||||||
PACKAGE_SRC="$FLUENT_ROOT/packages/linux-$ARCH"
|
|
||||||
PACKAGE_DST="$FLUENT_DIST/packages/linux-$ARCH"
|
|
||||||
if [ -d "$PACKAGE_SRC" ]; then
|
|
||||||
mkdir -p "$PACKAGE_DST"
|
|
||||||
cp -R "$PACKAGE_SRC/." "$PACKAGE_DST/"
|
|
||||||
else
|
|
||||||
echo "[error] fluent-bit package directory not found: $PACKAGE_SRC"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
rm -f "$FLUENT_DIST/.gitignore"
|
|
||||||
rm -f "$FLUENT_DIST"/logs.db*
|
|
||||||
rm -rf "$FLUENT_DIST/storage"
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
function verify_fluent_bit_package_matrix() {
|
|
||||||
FLUENT_ROOT=$1
|
|
||||||
ARCH=$2
|
|
||||||
REQUIRED_FILES=()
|
|
||||||
if [ "$ARCH" = "amd64" ]; then
|
|
||||||
REQUIRED_FILES=(
|
|
||||||
"packages/linux-amd64/fluent-bit_4.2.2_amd64.deb"
|
|
||||||
"packages/linux-amd64/fluent-bit-4.2.2-1.x86_64.rpm"
|
|
||||||
)
|
|
||||||
elif [ "$ARCH" = "arm64" ]; then
|
|
||||||
REQUIRED_FILES=(
|
|
||||||
"packages/linux-arm64/fluent-bit_4.2.2_arm64.deb"
|
|
||||||
"packages/linux-arm64/fluent-bit-4.2.2-1.aarch64.rpm"
|
|
||||||
)
|
|
||||||
else
|
|
||||||
echo "[error] unsupported arch for fluent-bit package validation: $ARCH"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
MISSING=0
|
|
||||||
for FILE in "${REQUIRED_FILES[@]}"; do
|
|
||||||
if [ ! -f "$FLUENT_ROOT/$FILE" ]; then
|
|
||||||
echo "[error] fluent-bit matrix package missing: $FLUENT_ROOT/$FILE"
|
|
||||||
MISSING=1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$MISSING" -ne 0 ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
function lookup-version() {
|
function lookup-version() {
|
||||||
FILE=$1
|
FILE=$1
|
||||||
VERSION_DATA=$(cat "$FILE")
|
VERSION_DATA=$(cat "$FILE")
|
||||||
|
|||||||
@@ -8,21 +8,29 @@ require (
|
|||||||
github.com/TeaOSLab/EdgeCommon v0.0.0-00010101000000-000000000000
|
github.com/TeaOSLab/EdgeCommon v0.0.0-00010101000000-000000000000
|
||||||
github.com/iwind/TeaGo v0.0.0-20240411075713-6c1fc9aca7b6
|
github.com/iwind/TeaGo v0.0.0-20240411075713-6c1fc9aca7b6
|
||||||
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4
|
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4
|
||||||
|
github.com/miekg/dns v1.1.72
|
||||||
|
github.com/shirou/gopsutil/v3 v3.24.5
|
||||||
|
golang.org/x/sys v0.39.0
|
||||||
google.golang.org/grpc v1.78.0
|
google.golang.org/grpc v1.78.0
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/miekg/dns v1.1.72 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||||
github.com/oschwald/geoip2-golang v1.13.0 // indirect
|
github.com/oschwald/geoip2-golang v1.13.0 // indirect
|
||||||
github.com/oschwald/maxminddb-golang v1.13.0 // indirect
|
github.com/oschwald/maxminddb-golang v1.13.0 // indirect
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||||
|
github.com/tklauser/go-sysconf v0.3.13 // indirect
|
||||||
|
github.com/tklauser/numcpus v0.7.0 // indirect
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
golang.org/x/mod v0.31.0 // indirect
|
golang.org/x/mod v0.31.0 // indirect
|
||||||
golang.org/x/net v0.48.0 // indirect
|
golang.org/x/net v0.48.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.39.0 // indirect
|
|
||||||
golang.org/x/text v0.32.0 // indirect
|
golang.org/x/text v0.32.0 // indirect
|
||||||
golang.org/x/tools v0.40.0 // indirect
|
golang.org/x/tools v0.40.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda // indirect
|
||||||
google.golang.org/protobuf v1.36.10 // indirect
|
google.golang.org/protobuf v1.36.10 // indirect
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||||
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
@@ -16,12 +21,32 @@ github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
|||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||||
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
|
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
|
||||||
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
||||||
github.com/oschwald/geoip2-golang v1.13.0 h1:Q44/Ldc703pasJeP5V9+aFSZFmBN7DKHbNsSFzQATJI=
|
github.com/oschwald/geoip2-golang v1.13.0 h1:Q44/Ldc703pasJeP5V9+aFSZFmBN7DKHbNsSFzQATJI=
|
||||||
github.com/oschwald/geoip2-golang v1.13.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
|
github.com/oschwald/geoip2-golang v1.13.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
|
||||||
github.com/oschwald/maxminddb-golang v1.13.0 h1:R8xBorY71s84yO06NgTmQvqvTvlS/bnYZrrWX1MElnU=
|
github.com/oschwald/maxminddb-golang v1.13.0 h1:R8xBorY71s84yO06NgTmQvqvTvlS/bnYZrrWX1MElnU=
|
||||||
github.com/oschwald/maxminddb-golang v1.13.0/go.mod h1:BU0z8BfFVhi1LQaonTwwGQlsHUEu9pWNdMfmq4ztm0o=
|
github.com/oschwald/maxminddb-golang v1.13.0/go.mod h1:BU0z8BfFVhi1LQaonTwwGQlsHUEu9pWNdMfmq4ztm0o=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
|
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||||
|
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||||
|
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||||
|
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0=
|
||||||
|
github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4=
|
||||||
|
github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||||
@@ -36,22 +61,19 @@ go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJr
|
|||||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||||
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
||||||
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
||||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
|
||||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
|
||||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
|
||||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
|
||||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||||
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
||||||
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
|
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda h1:i/Q+bfisr7gq6feoJnS/DlpdwEL4ihp41fvRiM3Ork0=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda h1:i/Q+bfisr7gq6feoJnS/DlpdwEL4ihp41fvRiM3Ork0=
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package teaconst
|
package teaconst
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "1.4.9"
|
Version = "1.5.0"
|
||||||
|
|
||||||
ProductName = "Edge HTTPDNS"
|
ProductName = "Edge HTTPDNS"
|
||||||
ProcessName = "edge-httpdns"
|
ProcessName = "edge-httpdns"
|
||||||
|
|||||||
@@ -15,18 +15,28 @@ import (
|
|||||||
teaconst "github.com/TeaOSLab/EdgeHttpDNS/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeHttpDNS/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeHttpDNS/internal/rpc"
|
"github.com/TeaOSLab/EdgeHttpDNS/internal/rpc"
|
||||||
"github.com/TeaOSLab/EdgeHttpDNS/internal/utils"
|
"github.com/TeaOSLab/EdgeHttpDNS/internal/utils"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"github.com/shirou/gopsutil/v3/cpu"
|
||||||
|
"github.com/shirou/gopsutil/v3/mem"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
type StatusManager struct {
|
type StatusManager struct {
|
||||||
quitCh <-chan struct{}
|
quitCh <-chan struct{}
|
||||||
ticker *time.Ticker
|
ticker *time.Ticker
|
||||||
|
|
||||||
|
isFirstTime bool
|
||||||
|
cpuUpdatedTime time.Time
|
||||||
|
cpuLogicalCount int
|
||||||
|
cpuPhysicalCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStatusManager(quitCh <-chan struct{}) *StatusManager {
|
func NewStatusManager(quitCh <-chan struct{}) *StatusManager {
|
||||||
return &StatusManager{
|
return &StatusManager{
|
||||||
quitCh: quitCh,
|
quitCh: quitCh,
|
||||||
ticker: time.NewTicker(30 * time.Second),
|
ticker: time.NewTicker(30 * time.Second),
|
||||||
|
isFirstTime: true,
|
||||||
|
cpuUpdatedTime: time.Now(),
|
||||||
|
cpuLogicalCount: runtime.NumCPU(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,10 +82,12 @@ func (m *StatusManager) update() {
|
|||||||
IsActive: true,
|
IsActive: true,
|
||||||
StatusJSON: statusJSON,
|
StatusJSON: statusJSON,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("[HTTPDNS_NODE][status]update status failed:", err.Error())
|
log.Println("[HTTPDNS_NODE][status]update status failed:", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.reportNodeValues(rpcClient, status)
|
||||||
|
m.isFirstTime = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *StatusManager) collectStatus() *nodeconfigs.NodeStatus {
|
func (m *StatusManager) collectStatus() *nodeconfigs.NodeStatus {
|
||||||
@@ -87,8 +99,8 @@ func (m *StatusManager) collectStatus() *nodeconfigs.NodeStatus {
|
|||||||
ConfigVersion: 0,
|
ConfigVersion: 0,
|
||||||
OS: runtime.GOOS,
|
OS: runtime.GOOS,
|
||||||
Arch: runtime.GOARCH,
|
Arch: runtime.GOARCH,
|
||||||
CPULogicalCount: runtime.NumCPU(),
|
CPULogicalCount: m.cpuLogicalCount,
|
||||||
CPUPhysicalCount: runtime.NumCPU(),
|
CPUPhysicalCount: m.cpuPhysicalCount,
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
ConnectionCount: 0,
|
ConnectionCount: 0,
|
||||||
UpdatedAt: now,
|
UpdatedAt: now,
|
||||||
@@ -104,26 +116,104 @@ func (m *StatusManager) collectStatus() *nodeconfigs.NodeStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
hostname, _ := os.Hostname()
|
hostname, _ := os.Hostname()
|
||||||
status.Hostname = hostname
|
status.Hostname = hostname
|
||||||
|
|
||||||
exePath, _ := os.Executable()
|
exePath, _ := os.Executable()
|
||||||
status.ExePath = exePath
|
status.ExePath = exePath
|
||||||
|
|
||||||
var memStats runtime.MemStats
|
m.updateCPU(status)
|
||||||
runtime.ReadMemStats(&memStats)
|
m.updateMemory(status)
|
||||||
status.MemoryTotal = memStats.Sys
|
m.updateLoad(status)
|
||||||
if status.MemoryTotal > 0 {
|
|
||||||
status.MemoryUsage = float64(memStats.Alloc) / float64(status.MemoryTotal)
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StatusManager) updateCPU(status *nodeconfigs.NodeStatus) {
|
||||||
|
duration := time.Duration(0)
|
||||||
|
if m.isFirstTime {
|
||||||
|
duration = 100 * time.Millisecond
|
||||||
}
|
}
|
||||||
|
|
||||||
|
percents, err := cpu.Percent(duration, false)
|
||||||
|
if err == nil && len(percents) > 0 {
|
||||||
|
status.CPUUsage = percents[0] / 100
|
||||||
|
}
|
||||||
|
|
||||||
|
if time.Since(m.cpuUpdatedTime) >= 5*time.Minute || m.cpuLogicalCount <= 0 {
|
||||||
|
m.cpuUpdatedTime = time.Now()
|
||||||
|
|
||||||
|
if logicalCount, countErr := cpu.Counts(true); countErr == nil && logicalCount > 0 {
|
||||||
|
m.cpuLogicalCount = logicalCount
|
||||||
|
}
|
||||||
|
if physicalCount, countErr := cpu.Counts(false); countErr == nil && physicalCount > 0 {
|
||||||
|
m.cpuPhysicalCount = physicalCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status.CPULogicalCount = m.cpuLogicalCount
|
||||||
|
if m.cpuPhysicalCount > 0 {
|
||||||
|
status.CPUPhysicalCount = m.cpuPhysicalCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StatusManager) updateMemory(status *nodeconfigs.NodeStatus) {
|
||||||
|
stat, err := mem.VirtualMemory()
|
||||||
|
if err != nil || stat == nil || stat.Total == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
usedBytes := stat.Used
|
||||||
|
if stat.Total > stat.Free+stat.Buffers+stat.Cached {
|
||||||
|
usedBytes = stat.Total - stat.Free - stat.Buffers - stat.Cached
|
||||||
|
}
|
||||||
|
|
||||||
|
status.MemoryTotal = stat.Total
|
||||||
|
status.MemoryUsage = float64(usedBytes) / float64(stat.Total)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StatusManager) updateLoad(status *nodeconfigs.NodeStatus) {
|
||||||
load1m, load5m, load15m := readLoadAvg()
|
load1m, load5m, load15m := readLoadAvg()
|
||||||
status.Load1m = load1m
|
status.Load1m = load1m
|
||||||
status.Load5m = load5m
|
status.Load5m = load5m
|
||||||
status.Load15m = load15m
|
status.Load15m = load15m
|
||||||
|
}
|
||||||
|
|
||||||
return status
|
func (m *StatusManager) reportNodeValues(rpcClient *rpc.RPCClient, status *nodeconfigs.NodeStatus) {
|
||||||
|
if rpcClient == nil || status == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m.createNodeValue(rpcClient, nodeconfigs.NodeValueItemCPU, maps.Map{
|
||||||
|
"usage": status.CPUUsage,
|
||||||
|
"cores": status.CPULogicalCount,
|
||||||
|
})
|
||||||
|
m.createNodeValue(rpcClient, nodeconfigs.NodeValueItemMemory, maps.Map{
|
||||||
|
"usage": status.MemoryUsage,
|
||||||
|
"total": status.MemoryTotal,
|
||||||
|
})
|
||||||
|
m.createNodeValue(rpcClient, nodeconfigs.NodeValueItemLoad, maps.Map{
|
||||||
|
"load1m": status.Load1m,
|
||||||
|
"load5m": status.Load5m,
|
||||||
|
"load15m": status.Load15m,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StatusManager) createNodeValue(rpcClient *rpc.RPCClient, item string, value maps.Map) {
|
||||||
|
valueJSON, err := json.Marshal(value)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("[HTTPDNS_NODE][status]marshal node value failed:", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = rpcClient.NodeValueRPC.CreateNodeValue(rpcClient.Context(), &pb.CreateNodeValueRequest{
|
||||||
|
Item: item,
|
||||||
|
ValueJSON: valueJSON,
|
||||||
|
CreatedAt: time.Now().Unix(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Println("[HTTPDNS_NODE][status]create node value failed:", item, err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func readLoadAvg() (float64, float64, float64) {
|
func readLoadAvg() (float64, float64, float64) {
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
type RPCClient struct {
|
type RPCClient struct {
|
||||||
apiConfig *configs.APIConfig
|
apiConfig *configs.APIConfig
|
||||||
conns []*grpc.ClientConn
|
conns []*grpc.ClientConn
|
||||||
locker sync.RWMutex
|
locker sync.RWMutex
|
||||||
|
|
||||||
NodeTaskRPC pb.NodeTaskServiceClient
|
NodeTaskRPC pb.NodeTaskServiceClient
|
||||||
|
NodeValueRPC pb.NodeValueServiceClient
|
||||||
HTTPDNSNodeRPC pb.HTTPDNSNodeServiceClient
|
HTTPDNSNodeRPC pb.HTTPDNSNodeServiceClient
|
||||||
HTTPDNSClusterRPC pb.HTTPDNSClusterServiceClient
|
HTTPDNSClusterRPC pb.HTTPDNSClusterServiceClient
|
||||||
HTTPDNSAppRPC pb.HTTPDNSAppServiceClient
|
HTTPDNSAppRPC pb.HTTPDNSAppServiceClient
|
||||||
@@ -47,7 +47,6 @@ type RPCClient struct {
|
|||||||
totalCostMs int64
|
totalCostMs int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func NewRPCClient(apiConfig *configs.APIConfig) (*RPCClient, error) {
|
func NewRPCClient(apiConfig *configs.APIConfig) (*RPCClient, error) {
|
||||||
if apiConfig == nil {
|
if apiConfig == nil {
|
||||||
return nil, errors.New("api config should not be nil")
|
return nil, errors.New("api config should not be nil")
|
||||||
@@ -55,6 +54,7 @@ func NewRPCClient(apiConfig *configs.APIConfig) (*RPCClient, error) {
|
|||||||
|
|
||||||
client := &RPCClient{apiConfig: apiConfig}
|
client := &RPCClient{apiConfig: apiConfig}
|
||||||
client.NodeTaskRPC = pb.NewNodeTaskServiceClient(client)
|
client.NodeTaskRPC = pb.NewNodeTaskServiceClient(client)
|
||||||
|
client.NodeValueRPC = pb.NewNodeValueServiceClient(client)
|
||||||
client.HTTPDNSNodeRPC = pb.NewHTTPDNSNodeServiceClient(client)
|
client.HTTPDNSNodeRPC = pb.NewHTTPDNSNodeServiceClient(client)
|
||||||
client.HTTPDNSClusterRPC = pb.NewHTTPDNSClusterServiceClient(client)
|
client.HTTPDNSClusterRPC = pb.NewHTTPDNSClusterServiceClient(client)
|
||||||
client.HTTPDNSAppRPC = pb.NewHTTPDNSAppServiceClient(client)
|
client.HTTPDNSAppRPC = pb.NewHTTPDNSAppServiceClient(client)
|
||||||
@@ -212,7 +212,6 @@ func (c *RPCClient) GetAndResetMetrics() (total int64, failed int64, avgCostSeco
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (c *RPCClient) NewStream(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
func (c *RPCClient) NewStream(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||||
conn := c.pickConn()
|
conn := c.pickConn()
|
||||||
if conn == nil {
|
if conn == nil {
|
||||||
|
|||||||
2
EdgeNode/.gitignore
vendored
2
EdgeNode/.gitignore
vendored
@@ -8,6 +8,8 @@ configs/api_node.yaml
|
|||||||
|
|
||||||
# Build binaries
|
# Build binaries
|
||||||
bin/
|
bin/
|
||||||
|
libs/
|
||||||
|
.third_party_build/
|
||||||
|
|
||||||
# Runtime Data
|
# Runtime Data
|
||||||
data/
|
data/
|
||||||
|
|||||||
178
EdgeNode/build/build-libs-ubuntu2204.sh
Executable file
178
EdgeNode/build/build-libs-ubuntu2204.sh
Executable file
@@ -0,0 +1,178 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
function host-goarch() {
|
||||||
|
case "$(uname -m)" in
|
||||||
|
x86_64|amd64)
|
||||||
|
echo "amd64"
|
||||||
|
;;
|
||||||
|
aarch64|arm64)
|
||||||
|
echo "arm64"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo ""
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
function require-command() {
|
||||||
|
local command_name=$1
|
||||||
|
if ! command -v "$command_name" >/dev/null 2>&1; then
|
||||||
|
echo "missing required command: $command_name"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function cpu_count() {
|
||||||
|
if command -v nproc >/dev/null 2>&1; then
|
||||||
|
nproc
|
||||||
|
else
|
||||||
|
getconf _NPROCESSORS_ONLN
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function download() {
|
||||||
|
local url=$1
|
||||||
|
local output=$2
|
||||||
|
if [ ! -f "$output" ]; then
|
||||||
|
curl -fsSL "$url" -o "$output"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepare-dir() {
|
||||||
|
local dir=$1
|
||||||
|
rm -rf "$dir"
|
||||||
|
mkdir -p "$dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
function copy-tree() {
|
||||||
|
local from_dir=$1
|
||||||
|
local to_dir=$2
|
||||||
|
rm -rf "$to_dir"
|
||||||
|
mkdir -p "$to_dir"
|
||||||
|
cp -a "$from_dir"/. "$to_dir"/
|
||||||
|
}
|
||||||
|
|
||||||
|
function copy-static-lib() {
|
||||||
|
local pattern=$1
|
||||||
|
local destination=$2
|
||||||
|
local source_path=""
|
||||||
|
|
||||||
|
source_path=$(find "$(dirname "$pattern")" -type f -name "$(basename "$pattern")" | head -n 1)
|
||||||
|
if [ -z "$source_path" ]; then
|
||||||
|
echo "missing static library: $pattern"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp "$source_path" "$destination"
|
||||||
|
}
|
||||||
|
|
||||||
|
ROOT=$(cd "$(dirname "$0")/.." && pwd)
|
||||||
|
ARCH=${1:-$(host-goarch)}
|
||||||
|
HOST_ARCH=$(host-goarch)
|
||||||
|
LIBPCAP_VERSION=${LIBPCAP_VERSION:-1.10.5}
|
||||||
|
BROTLI_VERSION=${BROTLI_VERSION:-1.1.0}
|
||||||
|
WORKDIR=${WORKDIR:-"$ROOT/.third_party_build"}
|
||||||
|
DOWNLOAD_DIR="$WORKDIR/downloads"
|
||||||
|
SOURCE_DIR="$WORKDIR/sources"
|
||||||
|
BUILD_DIR="$WORKDIR/build"
|
||||||
|
LIBPCAP_URL="https://www.tcpdump.org/release/libpcap-${LIBPCAP_VERSION}.tar.gz"
|
||||||
|
BROTLI_URL="https://github.com/google/brotli/archive/refs/tags/v${BROTLI_VERSION}.tar.gz"
|
||||||
|
|
||||||
|
if [ -z "$ARCH" ]; then
|
||||||
|
echo "unsupported host architecture: $(uname -m)"
|
||||||
|
echo "usage: $0 [amd64|arm64]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$ARCH" != "amd64" ] && [ "$ARCH" != "arm64" ]; then
|
||||||
|
echo "unsupported architecture: $ARCH"
|
||||||
|
echo "supported architectures: amd64 arm64"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$HOST_ARCH" ] && [ "$ARCH" != "$HOST_ARCH" ]; then
|
||||||
|
echo "this helper only builds native Ubuntu 22.04 static libraries"
|
||||||
|
echo "host arch: ${HOST_ARCH}, requested arch: ${ARCH}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for tool in curl tar make cmake gcc g++ ar ranlib; do
|
||||||
|
require-command "$tool"
|
||||||
|
done
|
||||||
|
|
||||||
|
mkdir -p "$DOWNLOAD_DIR" "$SOURCE_DIR" "$BUILD_DIR"
|
||||||
|
|
||||||
|
echo "building third-party static libraries for ${ARCH} ..."
|
||||||
|
echo "workspace: $WORKDIR"
|
||||||
|
|
||||||
|
LIBPCAP_ARCHIVE="$DOWNLOAD_DIR/libpcap-${LIBPCAP_VERSION}.tar.gz"
|
||||||
|
BROTLI_ARCHIVE="$DOWNLOAD_DIR/brotli-${BROTLI_VERSION}.tar.gz"
|
||||||
|
|
||||||
|
download "$LIBPCAP_URL" "$LIBPCAP_ARCHIVE"
|
||||||
|
download "$BROTLI_URL" "$BROTLI_ARCHIVE"
|
||||||
|
|
||||||
|
LIBPCAP_SOURCE="$SOURCE_DIR/libpcap-${LIBPCAP_VERSION}"
|
||||||
|
BROTLI_SOURCE="$SOURCE_DIR/brotli-${BROTLI_VERSION}"
|
||||||
|
prepare-dir "$LIBPCAP_SOURCE"
|
||||||
|
prepare-dir "$BROTLI_SOURCE"
|
||||||
|
|
||||||
|
tar -xzf "$LIBPCAP_ARCHIVE" -C "$LIBPCAP_SOURCE" --strip-components=1
|
||||||
|
tar -xzf "$BROTLI_ARCHIVE" -C "$BROTLI_SOURCE" --strip-components=1
|
||||||
|
|
||||||
|
LIBPCAP_BUILD="$BUILD_DIR/libpcap-${ARCH}"
|
||||||
|
BROTLI_BUILD_SOURCE="$BUILD_DIR/brotli-source-${ARCH}"
|
||||||
|
BROTLI_BUILD_DIR="$BUILD_DIR/brotli-build-${ARCH}"
|
||||||
|
prepare-dir "$LIBPCAP_BUILD"
|
||||||
|
prepare-dir "$BROTLI_BUILD_SOURCE"
|
||||||
|
prepare-dir "$BROTLI_BUILD_DIR"
|
||||||
|
|
||||||
|
cp -a "$LIBPCAP_SOURCE"/. "$LIBPCAP_BUILD"/
|
||||||
|
cp -a "$BROTLI_SOURCE"/. "$BROTLI_BUILD_SOURCE"/
|
||||||
|
|
||||||
|
pushd "$LIBPCAP_BUILD" >/dev/null
|
||||||
|
./configure \
|
||||||
|
--disable-shared \
|
||||||
|
--enable-static \
|
||||||
|
--without-dbus \
|
||||||
|
--without-libnl \
|
||||||
|
--without-bluetooth \
|
||||||
|
--without-dag \
|
||||||
|
--without-septel \
|
||||||
|
--without-snf \
|
||||||
|
--without-turbocap \
|
||||||
|
--without-rdma \
|
||||||
|
--without-netmap \
|
||||||
|
CFLAGS="-O2 -fPIC"
|
||||||
|
make -j"$(cpu_count)"
|
||||||
|
popd >/dev/null
|
||||||
|
|
||||||
|
cmake -S "$BROTLI_BUILD_SOURCE" -B "$BROTLI_BUILD_DIR" \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DBUILD_SHARED_LIBS=OFF \
|
||||||
|
-DBROTLI_DISABLE_TESTS=ON \
|
||||||
|
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||||
|
cmake --build "$BROTLI_BUILD_DIR" --parallel "$(cpu_count)"
|
||||||
|
|
||||||
|
mkdir -p "$ROOT/libs/libpcap/${ARCH}"
|
||||||
|
mkdir -p "$ROOT/libs/libbrotli/${ARCH}"
|
||||||
|
mkdir -p "$ROOT/libs/libpcap/src"
|
||||||
|
mkdir -p "$ROOT/libs/libbrotli/src"
|
||||||
|
|
||||||
|
cp "$LIBPCAP_BUILD/libpcap.a" "$ROOT/libs/libpcap/${ARCH}/libpcap.a"
|
||||||
|
copy-tree "$LIBPCAP_BUILD" "$ROOT/libs/libpcap/src/libpcap"
|
||||||
|
|
||||||
|
copy-static-lib "$BROTLI_BUILD_DIR/libbrotlicommon*.a" "$ROOT/libs/libbrotli/${ARCH}/libbrotlicommon.a"
|
||||||
|
copy-static-lib "$BROTLI_BUILD_DIR/libbrotlidec*.a" "$ROOT/libs/libbrotli/${ARCH}/libbrotlidec.a"
|
||||||
|
copy-static-lib "$BROTLI_BUILD_DIR/libbrotlienc*.a" "$ROOT/libs/libbrotli/${ARCH}/libbrotlienc.a"
|
||||||
|
copy-tree "$BROTLI_SOURCE" "$ROOT/libs/libbrotli/src/brotli"
|
||||||
|
|
||||||
|
echo "done"
|
||||||
|
echo "generated:"
|
||||||
|
echo " $ROOT/libs/libpcap/${ARCH}/libpcap.a"
|
||||||
|
echo " $ROOT/libs/libbrotli/${ARCH}/libbrotlicommon.a"
|
||||||
|
echo " $ROOT/libs/libbrotli/${ARCH}/libbrotlidec.a"
|
||||||
|
echo " $ROOT/libs/libbrotli/${ARCH}/libbrotlienc.a"
|
||||||
|
echo ""
|
||||||
|
echo "next:"
|
||||||
|
echo " cd \"$ROOT/build\" && ./build.sh linux ${ARCH} plus"
|
||||||
@@ -1,42 +1,217 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
function find-binary() {
|
||||||
|
local candidate
|
||||||
|
for candidate in "$@"; do
|
||||||
|
if [ -z "$candidate" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [ -x "$candidate" ]; then
|
||||||
|
echo "$candidate"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if command -v "$candidate" >/dev/null 2>&1; then
|
||||||
|
command -v "$candidate"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function host-goarch() {
|
||||||
|
case "$(uname -m)" in
|
||||||
|
x86_64|amd64)
|
||||||
|
echo "amd64"
|
||||||
|
;;
|
||||||
|
aarch64|arm64)
|
||||||
|
echo "arm64"
|
||||||
|
;;
|
||||||
|
i386|i486|i586|i686)
|
||||||
|
echo "386"
|
||||||
|
;;
|
||||||
|
mips64)
|
||||||
|
echo "mips64"
|
||||||
|
;;
|
||||||
|
mips64el)
|
||||||
|
echo "mips64le"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo ""
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
function find-linux-static-toolchain() {
|
||||||
|
local arch=$1
|
||||||
|
local cc_bin=""
|
||||||
|
local cxx_bin=""
|
||||||
|
local host_arch
|
||||||
|
|
||||||
|
host_arch=$(host-goarch)
|
||||||
|
|
||||||
|
case "$arch" in
|
||||||
|
amd64)
|
||||||
|
cc_bin=$(find-binary \
|
||||||
|
"/usr/local/gcc/x86_64-unknown-linux-gnu/bin/x86_64-unknown-linux-gnu-gcc" \
|
||||||
|
"/usr/local/opt/musl-cross/bin/x86_64-linux-musl-gcc" \
|
||||||
|
"x86_64-unknown-linux-gnu-gcc" \
|
||||||
|
"x86_64-linux-musl-gcc" \
|
||||||
|
"musl-gcc")
|
||||||
|
cxx_bin=$(find-binary \
|
||||||
|
"/usr/local/gcc/x86_64-unknown-linux-gnu/bin/x86_64-unknown-linux-gnu-g++" \
|
||||||
|
"/usr/local/opt/musl-cross/bin/x86_64-linux-musl-g++" \
|
||||||
|
"x86_64-unknown-linux-gnu-g++" \
|
||||||
|
"x86_64-linux-musl-g++")
|
||||||
|
if [ -z "$cc_bin" ] && [ "$host_arch" = "amd64" ]; then
|
||||||
|
cc_bin=$(find-binary "gcc" "cc" "clang")
|
||||||
|
fi
|
||||||
|
if [ -z "$cxx_bin" ] && [ "$host_arch" = "amd64" ]; then
|
||||||
|
cxx_bin=$(find-binary "g++" "c++" "clang++")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
386)
|
||||||
|
cc_bin=$(find-binary \
|
||||||
|
"/usr/local/opt/musl-cross/bin/i486-linux-musl-gcc" \
|
||||||
|
"i486-linux-musl-gcc")
|
||||||
|
cxx_bin=$(find-binary \
|
||||||
|
"/usr/local/opt/musl-cross/bin/i486-linux-musl-g++" \
|
||||||
|
"i486-linux-musl-g++")
|
||||||
|
if [ -z "$cc_bin" ] && [ "$host_arch" = "386" ]; then
|
||||||
|
cc_bin=$(find-binary "gcc" "cc" "clang")
|
||||||
|
fi
|
||||||
|
if [ -z "$cxx_bin" ] && [ "$host_arch" = "386" ]; then
|
||||||
|
cxx_bin=$(find-binary "g++" "c++" "clang++")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
arm64)
|
||||||
|
cc_bin=$(find-binary \
|
||||||
|
"/usr/local/gcc/aarch64-unknown-linux-gnu/bin/aarch64-unknown-linux-gnu-gcc" \
|
||||||
|
"/usr/local/opt/musl-cross/bin/aarch64-linux-musl-gcc" \
|
||||||
|
"aarch64-unknown-linux-gnu-gcc" \
|
||||||
|
"aarch64-linux-musl-gcc")
|
||||||
|
cxx_bin=$(find-binary \
|
||||||
|
"/usr/local/gcc/aarch64-unknown-linux-gnu/bin/aarch64-unknown-linux-gnu-g++" \
|
||||||
|
"/usr/local/opt/musl-cross/bin/aarch64-linux-musl-g++" \
|
||||||
|
"aarch64-unknown-linux-gnu-g++" \
|
||||||
|
"aarch64-linux-musl-g++")
|
||||||
|
if [ -z "$cc_bin" ] && [ "$host_arch" = "arm64" ]; then
|
||||||
|
cc_bin=$(find-binary "gcc" "cc" "clang")
|
||||||
|
fi
|
||||||
|
if [ -z "$cxx_bin" ] && [ "$host_arch" = "arm64" ]; then
|
||||||
|
cxx_bin=$(find-binary "g++" "c++" "clang++")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
arm)
|
||||||
|
cc_bin=$(find-binary \
|
||||||
|
"/usr/local/opt/musl-cross/bin/arm-linux-musleabi-gcc" \
|
||||||
|
"arm-linux-musleabi-gcc")
|
||||||
|
cxx_bin=$(find-binary \
|
||||||
|
"/usr/local/opt/musl-cross/bin/arm-linux-musleabi-g++" \
|
||||||
|
"arm-linux-musleabi-g++")
|
||||||
|
if [ -z "$cc_bin" ] && [ "$host_arch" = "arm" ]; then
|
||||||
|
cc_bin=$(find-binary "gcc" "cc" "clang")
|
||||||
|
fi
|
||||||
|
if [ -z "$cxx_bin" ] && [ "$host_arch" = "arm" ]; then
|
||||||
|
cxx_bin=$(find-binary "g++" "c++" "clang++")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
mips64)
|
||||||
|
cc_bin=$(find-binary \
|
||||||
|
"/usr/local/opt/musl-cross/bin/mips64-linux-musl-gcc" \
|
||||||
|
"mips64-linux-musl-gcc")
|
||||||
|
cxx_bin=$(find-binary \
|
||||||
|
"/usr/local/opt/musl-cross/bin/mips64-linux-musl-g++" \
|
||||||
|
"mips64-linux-musl-g++")
|
||||||
|
if [ -z "$cc_bin" ] && [ "$host_arch" = "mips64" ]; then
|
||||||
|
cc_bin=$(find-binary "gcc" "cc" "clang")
|
||||||
|
fi
|
||||||
|
if [ -z "$cxx_bin" ] && [ "$host_arch" = "mips64" ]; then
|
||||||
|
cxx_bin=$(find-binary "g++" "c++" "clang++")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
mips64le)
|
||||||
|
cc_bin=$(find-binary \
|
||||||
|
"/usr/local/opt/musl-cross/bin/mips64el-linux-musl-gcc" \
|
||||||
|
"mips64el-linux-musl-gcc")
|
||||||
|
cxx_bin=$(find-binary \
|
||||||
|
"/usr/local/opt/musl-cross/bin/mips64el-linux-musl-g++" \
|
||||||
|
"mips64el-linux-musl-g++")
|
||||||
|
if [ -z "$cc_bin" ] && [ "$host_arch" = "mips64le" ]; then
|
||||||
|
cc_bin=$(find-binary "gcc" "cc" "clang")
|
||||||
|
fi
|
||||||
|
if [ -z "$cxx_bin" ] && [ "$host_arch" = "mips64le" ]; then
|
||||||
|
cxx_bin=$(find-binary "g++" "c++" "clang++")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$cc_bin" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [ -z "$cxx_bin" ]; then
|
||||||
|
cxx_bin="$cc_bin"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$cc_bin|$cxx_bin"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function require-packet-static-libs() {
|
||||||
|
local srcdir=$1
|
||||||
|
local arch=$2
|
||||||
|
local missing=0
|
||||||
|
local file
|
||||||
|
|
||||||
|
for file in \
|
||||||
|
"${srcdir}/libs/libpcap/${arch}/libpcap.a" \
|
||||||
|
"${srcdir}/libs/libbrotli/${arch}/libbrotlienc.a" \
|
||||||
|
"${srcdir}/libs/libbrotli/${arch}/libbrotlidec.a" \
|
||||||
|
"${srcdir}/libs/libbrotli/${arch}/libbrotlicommon.a"; do
|
||||||
|
if [ ! -f "$file" ]; then
|
||||||
|
echo "missing required plus packet library: $file"
|
||||||
|
missing=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$missing" -ne 0 ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
function build() {
|
function build() {
|
||||||
ROOT=$(dirname $0)
|
ROOT=$(dirname "$0")
|
||||||
NAME="edge-node"
|
NAME="edge-node"
|
||||||
VERSION=$(lookup-version "$ROOT"/../internal/const/const.go)
|
VERSION=$(lookup-version "$ROOT"/../internal/const/const.go)
|
||||||
DIST=$ROOT/"../dist/${NAME}"
|
DIST=$ROOT/"../dist/${NAME}"
|
||||||
MUSL_DIR="/usr/local/opt/musl-cross/bin"
|
|
||||||
SRCDIR=$(realpath "$ROOT/..")
|
SRCDIR=$(realpath "$ROOT/..")
|
||||||
|
|
||||||
# for macOS users: precompiled gcc can be downloaded from https://github.com/messense/homebrew-macos-cross-toolchains
|
|
||||||
GCC_X86_64_DIR="/usr/local/gcc/x86_64-unknown-linux-gnu/bin"
|
|
||||||
GCC_ARM64_DIR="/usr/local/gcc/aarch64-unknown-linux-gnu/bin"
|
|
||||||
|
|
||||||
OS=${1}
|
OS=${1}
|
||||||
ARCH=${2}
|
ARCH=${2}
|
||||||
TAG=${3}
|
TAG=${3}
|
||||||
|
|
||||||
if [ -z "$OS" ]; then
|
if [ -z "$OS" ]; then
|
||||||
echo "usage: build.sh OS ARCH"
|
echo "usage: build.sh OS ARCH"
|
||||||
exit
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ -z "$ARCH" ]; then
|
if [ -z "$ARCH" ]; then
|
||||||
echo "usage: build.sh OS ARCH"
|
echo "usage: build.sh OS ARCH"
|
||||||
exit
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ -z "$TAG" ]; then
|
if [ -z "$TAG" ]; then
|
||||||
TAG="community"
|
TAG="community"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "checking ..."
|
echo "checking ..."
|
||||||
ZIP_PATH=$(which zip)
|
if ! command -v zip >/dev/null 2>&1; then
|
||||||
if [ -z "$ZIP_PATH" ]; then
|
|
||||||
echo "we need 'zip' command to compress files"
|
echo "we need 'zip' command to compress files"
|
||||||
exit
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "building v${VERSION}/${OS}/${ARCH}/${TAG} ..."
|
echo "building v${VERSION}/${OS}/${ARCH}/${TAG} ..."
|
||||||
# 生成 zip 文件名时不包含 plus 标记
|
|
||||||
if [ "${TAG}" = "plus" ]; then
|
if [ "${TAG}" = "plus" ]; then
|
||||||
ZIP="${NAME}-${OS}-${ARCH}-v${VERSION}.zip"
|
ZIP="${NAME}-${OS}-${ARCH}-v${VERSION}.zip"
|
||||||
else
|
else
|
||||||
@@ -63,11 +238,8 @@ function build() {
|
|||||||
cp -R "$ROOT"/pages "$DIST"/
|
cp -R "$ROOT"/pages "$DIST"/
|
||||||
copy_fluent_bit_assets "$ROOT" "$DIST" "$OS" "$ARCH" || exit 1
|
copy_fluent_bit_assets "$ROOT" "$DIST" "$OS" "$ARCH" || exit 1
|
||||||
|
|
||||||
# we support TOA on linux only
|
if [ "$OS" == "linux" ] && [ -f "${ROOT}/edge-toa/edge-toa-${ARCH}" ]; then
|
||||||
if [ "$OS" == "linux" ] && [ -f "${ROOT}/edge-toa/edge-toa-${ARCH}" ]
|
if [ ! -d "$DIST/edge-toa" ]; then
|
||||||
then
|
|
||||||
if [ ! -d "$DIST/edge-toa" ]
|
|
||||||
then
|
|
||||||
mkdir "$DIST/edge-toa"
|
mkdir "$DIST/edge-toa"
|
||||||
fi
|
fi
|
||||||
cp "${ROOT}/edge-toa/edge-toa-${ARCH}" "$DIST/edge-toa/edge-toa"
|
cp "${ROOT}/edge-toa/edge-toa-${ARCH}" "$DIST/edge-toa/edge-toa"
|
||||||
@@ -75,96 +247,59 @@ function build() {
|
|||||||
|
|
||||||
echo "building ..."
|
echo "building ..."
|
||||||
|
|
||||||
CC_PATH=""
|
CC_BIN=""
|
||||||
CXX_PATH=""
|
CXX_BIN=""
|
||||||
CGO_LDFLAGS=""
|
CGO_LDFLAGS=""
|
||||||
CGO_CFLAGS=""
|
CGO_CFLAGS=""
|
||||||
BUILD_TAG=$TAG
|
BUILD_TAG=$TAG
|
||||||
if [[ `uname -a` == *"Darwin"* && "${OS}" == "linux" ]]; then
|
|
||||||
if [ "${ARCH}" == "amd64" ]; then
|
|
||||||
# build with script support
|
|
||||||
if [ -d $GCC_X86_64_DIR ]; then
|
|
||||||
MUSL_DIR=$GCC_X86_64_DIR
|
|
||||||
CC_PATH="x86_64-unknown-linux-gnu-gcc"
|
|
||||||
CXX_PATH="x86_64-unknown-linux-gnu-g++"
|
|
||||||
if [ "$TAG" = "plus" ]; then
|
|
||||||
BUILD_TAG="plus,script,packet"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
CC_PATH="x86_64-linux-musl-gcc"
|
|
||||||
CXX_PATH="x86_64-linux-musl-g++"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if [ "${ARCH}" == "386" ]; then
|
|
||||||
CC_PATH="i486-linux-musl-gcc"
|
|
||||||
CXX_PATH="i486-linux-musl-g++"
|
|
||||||
fi
|
|
||||||
if [ "${ARCH}" == "arm64" ]; then
|
|
||||||
# build with script support
|
|
||||||
if [ -d $GCC_ARM64_DIR ]; then
|
|
||||||
MUSL_DIR=$GCC_ARM64_DIR
|
|
||||||
CC_PATH="aarch64-unknown-linux-gnu-gcc"
|
|
||||||
CXX_PATH="aarch64-unknown-linux-gnu-g++"
|
|
||||||
if [ "$TAG" = "plus" ]; then
|
|
||||||
BUILD_TAG="plus,script,packet"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
CC_PATH="aarch64-linux-musl-gcc"
|
|
||||||
CXX_PATH="aarch64-linux-musl-g++"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if [ "${ARCH}" == "arm" ]; then
|
|
||||||
CC_PATH="arm-linux-musleabi-gcc"
|
|
||||||
CXX_PATH="arm-linux-musleabi-g++"
|
|
||||||
fi
|
|
||||||
if [ "${ARCH}" == "mips64" ]; then
|
|
||||||
CC_PATH="mips64-linux-musl-gcc"
|
|
||||||
CXX_PATH="mips64-linux-musl-g++"
|
|
||||||
fi
|
|
||||||
if [ "${ARCH}" == "mips64le" ]; then
|
|
||||||
CC_PATH="mips64el-linux-musl-gcc"
|
|
||||||
CXX_PATH="mips64el-linux-musl-g++"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# libpcap
|
if [ "$OS" == "linux" ] && [[ "$ARCH" == "amd64" || "$ARCH" == "arm64" ]] && [ "$TAG" == "plus" ]; then
|
||||||
if [ "$OS" == "linux" ] && [[ "$ARCH" == "amd64" || "$ARCH" == "arm64" ]] && [ "$TAG" == "plus" ]; then
|
require-packet-static-libs "$SRCDIR" "$ARCH" || exit 1
|
||||||
|
BUILD_TAG="plus,script,packet"
|
||||||
CGO_LDFLAGS="-L${SRCDIR}/libs/libpcap/${ARCH} -lpcap -L${SRCDIR}/libs/libbrotli/${ARCH} -lbrotlienc -lbrotlidec -lbrotlicommon"
|
CGO_LDFLAGS="-L${SRCDIR}/libs/libpcap/${ARCH} -lpcap -L${SRCDIR}/libs/libbrotli/${ARCH} -lbrotlienc -lbrotlidec -lbrotlicommon"
|
||||||
CGO_CFLAGS="-I${SRCDIR}/libs/libpcap/src/libpcap -I${SRCDIR}/libs/libpcap/src/libpcap/pcap -I${SRCDIR}/libs/libbrotli/src/brotli/c/include -I${SRCDIR}/libs/libbrotli/src/brotli/c/include/brotli"
|
CGO_CFLAGS="-I${SRCDIR}/libs/libpcap/src/libpcap -I${SRCDIR}/libs/libpcap/src/libpcap/pcap -I${SRCDIR}/libs/libbrotli/src/brotli/c/include -I${SRCDIR}/libs/libbrotli/src/brotli/c/include/brotli"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -z $CC_PATH ]; then
|
if [ "$OS" == "linux" ]; then
|
||||||
env CC=$MUSL_DIR/$CC_PATH \
|
TOOLCHAIN=$(find-linux-static-toolchain "$ARCH")
|
||||||
CXX=$MUSL_DIR/$CXX_PATH GOOS="${OS}" \
|
if [ -z "$TOOLCHAIN" ]; then
|
||||||
GOARCH="${ARCH}" CGO_ENABLED=1 \
|
echo "could not find a static Linux toolchain for ${ARCH}"
|
||||||
CGO_LDFLAGS="${CGO_LDFLAGS}" \
|
echo "install a musl cross compiler or provide /usr/local/gcc toolchains before building"
|
||||||
CGO_CFLAGS="${CGO_CFLAGS}" \
|
exit 1
|
||||||
go build -trimpath -tags $BUILD_TAG -o "$DIST"/bin/${NAME} -ldflags "-linkmode external -extldflags -static -s -w" "$ROOT"/../cmd/edge-node/main.go
|
|
||||||
else
|
|
||||||
if [[ `uname` == *"Linux"* ]] && [ "$OS" == "linux" ] && [[ "$ARCH" == "amd64" || "$ARCH" == "arm64" ]] && [ "$TAG" == "plus" ]; then
|
|
||||||
BUILD_TAG="plus,script,packet"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
env GOOS="${OS}" GOARCH="${ARCH}" CGO_ENABLED=1 CGO_LDFLAGS="${CGO_LDFLAGS}" CGO_CFLAGS="${CGO_CFLAGS}" go build -trimpath -tags $BUILD_TAG -o "$DIST"/bin/${NAME} -ldflags="-s -w" "$ROOT"/../cmd/edge-node/main.go
|
CC_BIN=${TOOLCHAIN%|*}
|
||||||
|
CXX_BIN=${TOOLCHAIN#*|}
|
||||||
|
|
||||||
|
env CC="$CC_BIN" \
|
||||||
|
CXX="$CXX_BIN" \
|
||||||
|
GOOS="${OS}" \
|
||||||
|
GOARCH="${ARCH}" \
|
||||||
|
CGO_ENABLED=1 \
|
||||||
|
CGO_LDFLAGS="${CGO_LDFLAGS}" \
|
||||||
|
CGO_CFLAGS="${CGO_CFLAGS}" \
|
||||||
|
go build -trimpath -tags "$BUILD_TAG" -o "$DIST"/bin/${NAME} -ldflags "-linkmode external -extldflags -static -s -w" "$ROOT"/../cmd/edge-node/main.go
|
||||||
|
else
|
||||||
|
env GOOS="${OS}" GOARCH="${ARCH}" CGO_ENABLED=1 CGO_LDFLAGS="${CGO_LDFLAGS}" CGO_CFLAGS="${CGO_CFLAGS}" \
|
||||||
|
go build -trimpath -tags "$BUILD_TAG" -o "$DIST"/bin/${NAME} -ldflags="-s -w" "$ROOT"/../cmd/edge-node/main.go
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -f "${DIST}/bin/${NAME}" ]; then
|
if [ ! -f "${DIST}/bin/${NAME}" ]; then
|
||||||
echo "build failed!"
|
echo "build failed!"
|
||||||
exit
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# delete hidden files
|
|
||||||
find "$DIST" -name ".DS_Store" -delete
|
find "$DIST" -name ".DS_Store" -delete
|
||||||
find "$DIST" -name ".gitignore" -delete
|
find "$DIST" -name ".gitignore" -delete
|
||||||
|
|
||||||
echo "zip files"
|
echo "zip files"
|
||||||
cd "${DIST}/../" || exit
|
cd "${DIST}/../" || exit 1
|
||||||
if [ -f "${ZIP}" ]; then
|
if [ -f "${ZIP}" ]; then
|
||||||
rm -f "${ZIP}"
|
rm -f "${ZIP}"
|
||||||
fi
|
fi
|
||||||
zip -r -X -q "${ZIP}" ${NAME}/
|
zip -r -X -q "${ZIP}" ${NAME}/
|
||||||
rm -rf ${NAME}
|
rm -rf ${NAME}
|
||||||
cd - || exit
|
cd - || exit 1
|
||||||
|
|
||||||
echo "OK"
|
echo "OK"
|
||||||
}
|
}
|
||||||
@@ -253,7 +388,7 @@ function lookup-version() {
|
|||||||
echo "$VERSION"
|
echo "$VERSION"
|
||||||
else
|
else
|
||||||
echo "could not match version"
|
echo "could not match version"
|
||||||
exit
|
exit 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"speed":1,"speedMB":1400,"countTests":3}
|
{"speed":1,"speedMB":1510,"countTests":10}
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
sudo go run -tags="plus script packet" ../cmd/edge-node/main.go
|
sudo go run -tags="plus script" ../cmd/edge-node/main.go
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ require (
|
|||||||
github.com/dchest/captcha v0.0.0-00010101000000-000000000000
|
github.com/dchest/captcha v0.0.0-00010101000000-000000000000
|
||||||
github.com/fsnotify/fsnotify v1.7.0
|
github.com/fsnotify/fsnotify v1.7.0
|
||||||
github.com/go-redis/redis/v8 v8.11.5
|
github.com/go-redis/redis/v8 v8.11.5
|
||||||
github.com/google/gopacket v1.1.19
|
|
||||||
github.com/google/nftables v0.2.0
|
github.com/google/nftables v0.2.0
|
||||||
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.24.9+incompatible
|
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.24.9+incompatible
|
||||||
github.com/iwind/TeaGo v0.0.0-20240411075713-6c1fc9aca7b6
|
github.com/iwind/TeaGo v0.0.0-20240411075713-6c1fc9aca7b6
|
||||||
|
|||||||
@@ -67,8 +67,6 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
|||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
|
||||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
|
||||||
github.com/google/nftables v0.2.0 h1:PbJwaBmbVLzpeldoeUKGkE2RjstrjPKMl6oLrfEJ6/8=
|
github.com/google/nftables v0.2.0 h1:PbJwaBmbVLzpeldoeUKGkE2RjstrjPKMl6oLrfEJ6/8=
|
||||||
github.com/google/nftables v0.2.0/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4=
|
github.com/google/nftables v0.2.0/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4=
|
||||||
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 h1:y3N7Bm7Y9/CtpiVkw/ZWj6lSlDF3F74SfKwfTCer72Q=
|
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 h1:y3N7Bm7Y9/CtpiVkw/ZWj6lSlDF3F74SfKwfTCer72Q=
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package teaconst
|
package teaconst
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "1.4.9" //1.3.8.2
|
Version = "1.5.0" //1.3.8.2
|
||||||
|
|
||||||
ProductName = "Edge Node"
|
ProductName = "Edge Node"
|
||||||
ProcessName = "edge-node"
|
ProcessName = "edge-node"
|
||||||
|
|||||||
@@ -1,135 +0,0 @@
|
|||||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
|
||||||
//go:build plus && packet
|
|
||||||
|
|
||||||
package networksecurity
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/monitor"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/netpackets"
|
|
||||||
"github.com/iwind/TeaGo/Tea"
|
|
||||||
"github.com/iwind/TeaGo/maps"
|
|
||||||
"runtime"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var SharedManager = NewManager()
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
if !teaconst.IsMain {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
events.On(events.EventLoaded, func() {
|
|
||||||
nodeConfig, _ := nodeconfigs.SharedNodeConfig()
|
|
||||||
if nodeConfig != nil {
|
|
||||||
go SharedManager.Apply(nodeConfig.NetworkSecurityPolicy)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
events.On(events.EventQuit, func() {
|
|
||||||
go SharedManager.Apply(nil)
|
|
||||||
})
|
|
||||||
|
|
||||||
goman.New(func() {
|
|
||||||
var ticker = time.NewTicker(1 * time.Minute)
|
|
||||||
for range ticker.C {
|
|
||||||
SharedManager.Upload()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type Manager struct {
|
|
||||||
listener *netpackets.Listener
|
|
||||||
isRunning bool
|
|
||||||
|
|
||||||
policy *nodeconfigs.NetworkSecurityPolicy
|
|
||||||
|
|
||||||
totalTCPPacketsMinutely uint64
|
|
||||||
totalUDPPacketsMinutely uint64
|
|
||||||
totalICMPPacketsMinutely uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewManager() *Manager {
|
|
||||||
return &Manager{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply 应用配置
|
|
||||||
// 非线程安全
|
|
||||||
func (this *Manager) Apply(policy *nodeconfigs.NetworkSecurityPolicy) {
|
|
||||||
if this.policy != nil && this.policy.IsSame(policy) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.policy = policy
|
|
||||||
|
|
||||||
if policy == nil ||
|
|
||||||
policy.Status == nodeconfigs.NetworkSecurityStatusOff ||
|
|
||||||
(policy.Status == nodeconfigs.NetworkSecurityStatusAuto && runtime.NumCPU() < 8) {
|
|
||||||
if this.listener != nil {
|
|
||||||
remotelogs.Println("NETWORK_SECURITY_MANAGER", "stop")
|
|
||||||
this.listener.Stop()
|
|
||||||
}
|
|
||||||
this.isRunning = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if this.listener == nil {
|
|
||||||
this.listener = netpackets.NewListener()
|
|
||||||
|
|
||||||
// References:
|
|
||||||
// - https://biot.com/capstats/bpf.html
|
|
||||||
// - https://www.ibm.com/docs/en/qsip/7.4?topic=queries-berkeley-packet-filters
|
|
||||||
// - https://www.tcpdump.org/manpages/tcpdump.1.html
|
|
||||||
|
|
||||||
if Tea.IsTesting() || utils.IsDebugEnv() { // dev environment
|
|
||||||
this.listener.SetBPF("(tcp or udp or icmp) and not net 127 and not net ::1")
|
|
||||||
} else {
|
|
||||||
this.listener.SetBPF("(tcp or udp or icmp) and not src net 127 and not src net 192.168 and not src net 172.16 and not src net ::1 and not src net 10")
|
|
||||||
}
|
|
||||||
this.listener.AddFilter(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !this.isRunning {
|
|
||||||
this.isRunning = true
|
|
||||||
remotelogs.Println("NETWORK_SECURITY_MANAGER", "start")
|
|
||||||
err := this.listener.Start() // long run function
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.Error("NETWORK_SECURITY_MANAGER", "start listener failed: "+err.Error())
|
|
||||||
}
|
|
||||||
this.isRunning = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Manager) FilterMeta(meta *netpackets.PacketMeta) {
|
|
||||||
switch meta.LayerType {
|
|
||||||
case netpackets.LayerTypeTCP:
|
|
||||||
// 这里不需要试用atomic,因为数据不需要那么精确
|
|
||||||
this.totalTCPPacketsMinutely++
|
|
||||||
case netpackets.LayerTypeUDP:
|
|
||||||
this.totalUDPPacketsMinutely++
|
|
||||||
case netpackets.LayerTypeICMPv4, netpackets.LayerTypeICMPv6:
|
|
||||||
this.totalICMPPacketsMinutely++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Manager) Upload() {
|
|
||||||
if !this.isRunning {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
monitor.SharedValueQueue.Add(nodeconfigs.NodeValueItemNetworkPackets, maps.Map{
|
|
||||||
"tcpInPPS": this.totalTCPPacketsMinutely / 60,
|
|
||||||
"udpInPPS": this.totalUDPPacketsMinutely / 60,
|
|
||||||
"icmpInPPS": this.totalICMPPacketsMinutely / 60,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.totalTCPPacketsMinutely = 0
|
|
||||||
this.totalUDPPacketsMinutely = 0
|
|
||||||
this.totalICMPPacketsMinutely = 0
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
|
||||||
//go:build plus && packet
|
|
||||||
|
|
||||||
package networksecurity_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
|
||||||
networksecurity "github.com/TeaOSLab/EdgeNode/internal/network-security"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestManager_Apply(t *testing.T) {
|
|
||||||
if !testutils.IsSingleTesting() {
|
|
||||||
if os.Getgid() > 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var manager = networksecurity.NewManager()
|
|
||||||
var policy = nodeconfigs.NewNetworkSecurityPolicy()
|
|
||||||
manager.Apply(policy)
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
sudo go test -v -tags="plus packet" -run '^TestManager_Apply'
|
|
||||||
@@ -9,14 +9,20 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 执行认证
|
// 鎵ц璁よ瘉
|
||||||
func (this *HTTPRequest) doAuth() (shouldStop bool) {
|
func (this *HTTPRequest) doAuth() (shouldStop bool) {
|
||||||
if this.web.Auth == nil || !this.web.Auth.IsOn {
|
if this.web.Auth == nil || !this.web.Auth.IsOn {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ref := range this.web.Auth.PolicyRefs {
|
for _, ref := range this.web.Auth.PolicyRefs {
|
||||||
if !ref.IsOn || ref.AuthPolicy == nil || !ref.AuthPolicy.IsOn {
|
if !ref.IsOn {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ref.AuthPolicy == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !ref.AuthPolicy.IsOn {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !ref.AuthPolicy.MatchRequest(this.RawReq) {
|
if !ref.AuthPolicy.MatchRequest(this.RawReq) {
|
||||||
@@ -36,7 +42,7 @@ func (this *HTTPRequest) doAuth() (shouldStop bool) {
|
|||||||
return writer.StatusCode(), nil
|
return writer.StatusCode(), nil
|
||||||
}, this.Format)
|
}, this.Format)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.write50x(err, http.StatusInternalServerError, "Failed to execute the AuthPolicy", "认证策略执行失败", false)
|
this.write50x(err, http.StatusInternalServerError, "Failed to execute the AuthPolicy", "璁よ瘉绛栫暐鎵ц澶辫触", false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ok {
|
if ok {
|
||||||
@@ -45,28 +51,28 @@ func (this *HTTPRequest) doAuth() (shouldStop bool) {
|
|||||||
}
|
}
|
||||||
this.tags = append(this.tags, "auth:"+ref.AuthPolicy.Type)
|
this.tags = append(this.tags, "auth:"+ref.AuthPolicy.Type)
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
// Basic Auth比较特殊
|
|
||||||
if ref.AuthPolicy.Type == serverconfigs.HTTPAuthTypeBasicAuth {
|
|
||||||
method, ok := ref.AuthPolicy.Method().(*serverconfigs.HTTPAuthBasicMethod)
|
|
||||||
if ok {
|
|
||||||
var headerValue = "Basic realm=\""
|
|
||||||
if len(method.Realm) > 0 {
|
|
||||||
headerValue += method.Realm
|
|
||||||
} else {
|
|
||||||
headerValue += this.ReqHost
|
|
||||||
}
|
|
||||||
headerValue += "\""
|
|
||||||
if len(method.Charset) > 0 {
|
|
||||||
headerValue += ", charset=\"" + method.Charset + "\""
|
|
||||||
}
|
|
||||||
this.writer.Header()["WWW-Authenticate"] = []string{headerValue}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.writer.WriteHeader(http.StatusUnauthorized)
|
|
||||||
this.tags = append(this.tags, "auth:"+ref.AuthPolicy.Type)
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Basic Auth 姣旇緝鐗规畩
|
||||||
|
if ref.AuthPolicy.Type == serverconfigs.HTTPAuthTypeBasicAuth {
|
||||||
|
method, ok := ref.AuthPolicy.Method().(*serverconfigs.HTTPAuthBasicMethod)
|
||||||
|
if ok {
|
||||||
|
var headerValue = "Basic realm=\""
|
||||||
|
if len(method.Realm) > 0 {
|
||||||
|
headerValue += method.Realm
|
||||||
|
} else {
|
||||||
|
headerValue += this.ReqHost
|
||||||
|
}
|
||||||
|
headerValue += "\""
|
||||||
|
if len(method.Charset) > 0 {
|
||||||
|
headerValue += ", charset=\"" + method.Charset + "\""
|
||||||
|
}
|
||||||
|
this.writer.Header()["WWW-Authenticate"] = []string{headerValue}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.writer.WriteHeader(http.StatusUnauthorized)
|
||||||
|
this.tags = append(this.tags, "auth:"+ref.AuthPolicy.Type)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
|
||||||
//go:build plus && packet
|
|
||||||
|
|
||||||
package nodes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
networksecurity "github.com/TeaOSLab/EdgeNode/internal/network-security"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (this *Node) execNetworkSecurityPolicyChangedTask(rpcClient *rpc.RPCClient) error {
|
|
||||||
remotelogs.Println("NODE", "updating network security policy ...")
|
|
||||||
resp, err := rpcClient.NodeRPC.FindNodeNetworkSecurityPolicy(rpcClient.Context(), &pb.FindNodeNetworkSecurityPolicyRequest{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var policy = nodeconfigs.NewNetworkSecurityPolicy()
|
|
||||||
if len(resp.NetworkSecurityPolicyJSON) > 0 {
|
|
||||||
err = json.Unmarshal(resp.NetworkSecurityPolicyJSON, policy)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sharedNodeConfig.NetworkSecurityPolicy = policy
|
|
||||||
|
|
||||||
go networksecurity.SharedManager.Apply(policy)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
//go:build plus && !packet
|
//go:build plus
|
||||||
|
|
||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
|
||||||
//go:build plus && packet
|
|
||||||
|
|
||||||
package netpackets
|
|
||||||
|
|
||||||
type FilterInterface interface {
|
|
||||||
FilterMeta(meta *PacketMeta)
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
|
||||||
//go:build plus
|
|
||||||
|
|
||||||
package netpackets
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/linkedlist"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type IgnoredIPList struct {
|
|
||||||
mu sync.RWMutex
|
|
||||||
|
|
||||||
itemMap map[string]*linkedlist.Item[string] // linked => item
|
|
||||||
list *linkedlist.List[string]
|
|
||||||
|
|
||||||
capacity int
|
|
||||||
lastIP string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewIgnoredIPList(capacity int) *IgnoredIPList {
|
|
||||||
return &IgnoredIPList{
|
|
||||||
itemMap: map[string]*linkedlist.Item[string]{},
|
|
||||||
list: linkedlist.NewList[string](),
|
|
||||||
capacity: capacity,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *IgnoredIPList) Add(ip string) {
|
|
||||||
this.mu.Lock()
|
|
||||||
defer this.mu.Unlock()
|
|
||||||
|
|
||||||
if this.lastIP == ip {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.lastIP = ip
|
|
||||||
|
|
||||||
item, ok := this.itemMap[ip]
|
|
||||||
if !ok {
|
|
||||||
if this.capacity > 0 && len(this.itemMap) == this.capacity {
|
|
||||||
var firstItem = this.list.Shift()
|
|
||||||
if firstItem != nil {
|
|
||||||
delete(this.itemMap, firstItem.Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
item = linkedlist.NewItem[string](ip)
|
|
||||||
this.itemMap[ip] = item
|
|
||||||
}
|
|
||||||
this.list.Push(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *IgnoredIPList) Remove(ip string) {
|
|
||||||
this.mu.Lock()
|
|
||||||
defer this.mu.Unlock()
|
|
||||||
|
|
||||||
item, ok := this.itemMap[ip]
|
|
||||||
if ok {
|
|
||||||
delete(this.itemMap, ip)
|
|
||||||
this.list.Remove(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *IgnoredIPList) Contains(ip string) bool {
|
|
||||||
this.mu.RLock()
|
|
||||||
defer this.mu.RUnlock()
|
|
||||||
_, ok := this.itemMap[ip]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *IgnoredIPList) List(size int) (ipList []string) {
|
|
||||||
if size <= 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mu.RLock()
|
|
||||||
defer this.mu.RUnlock()
|
|
||||||
|
|
||||||
this.list.RangeReverse(func(item *linkedlist.Item[string]) (goNext bool) {
|
|
||||||
ipList = append(ipList, item.Value)
|
|
||||||
size--
|
|
||||||
return size > 0
|
|
||||||
})
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *IgnoredIPList) Len() int {
|
|
||||||
this.mu.RLock()
|
|
||||||
defer this.mu.RUnlock()
|
|
||||||
return len(this.itemMap)
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
|
||||||
//go:build plus
|
|
||||||
|
|
||||||
package netpackets_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/netpackets"
|
|
||||||
"github.com/iwind/TeaGo/assert"
|
|
||||||
"github.com/iwind/TeaGo/rands"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestIgnoredIPList_Add(t *testing.T) {
|
|
||||||
var a = assert.NewAssertion(t)
|
|
||||||
|
|
||||||
var list = netpackets.NewIgnoredIPList(10)
|
|
||||||
list.Add("192.168.2.1")
|
|
||||||
list.Add("192.168.2.2")
|
|
||||||
list.Add("192.168.2.3")
|
|
||||||
|
|
||||||
a.IsTrue(list.Contains("192.168.2.1"))
|
|
||||||
a.IsFalse(list.Contains("192.168.2.0"))
|
|
||||||
|
|
||||||
t.Log(list.List(0))
|
|
||||||
t.Log(list.List(2))
|
|
||||||
t.Log(list.List(4))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIgnoredIPList_Add_Capacity(t *testing.T) {
|
|
||||||
var list = netpackets.NewIgnoredIPList(4)
|
|
||||||
list.Add("192.168.2.1")
|
|
||||||
list.Add("192.168.2.2")
|
|
||||||
list.Add("192.168.2.3")
|
|
||||||
list.Add("192.168.2.4")
|
|
||||||
list.Add("192.168.2.5")
|
|
||||||
list.Add("192.168.2.6")
|
|
||||||
list.Add("192.168.2.7")
|
|
||||||
t.Log(list.List(10))
|
|
||||||
t.Log(list.Len(), "items")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIgnoredIPList_Remove(t *testing.T) {
|
|
||||||
var list = netpackets.NewIgnoredIPList(10)
|
|
||||||
list.Add("192.168.2.1")
|
|
||||||
list.Add("192.168.2.2")
|
|
||||||
list.Add("192.168.2.3")
|
|
||||||
list.Remove("192.168.2.2")
|
|
||||||
t.Log(list.List(4))
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkIgnoredIPList_Add(b *testing.B) {
|
|
||||||
runtime.GOMAXPROCS(1)
|
|
||||||
|
|
||||||
var genIPFunc = func() string {
|
|
||||||
return fmt.Sprintf("%d.%d.%d.%d", rands.Int(0, 255), rands.Int(0, 255), rands.Int(0, 255), rands.Int(0, 255))
|
|
||||||
}
|
|
||||||
|
|
||||||
var list = netpackets.NewIgnoredIPList(65535)
|
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
|
||||||
for pb.Next() {
|
|
||||||
list.Add(genIPFunc())
|
|
||||||
list.Remove(genIPFunc())
|
|
||||||
list.Contains(genIPFunc())
|
|
||||||
if rands.Int(0, 100) == 0 {
|
|
||||||
list.List(1000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
|
||||||
//go:build plus && packet
|
|
||||||
|
|
||||||
package netpackets
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/google/gopacket"
|
|
||||||
"github.com/google/gopacket/layers"
|
|
||||||
)
|
|
||||||
|
|
||||||
type LayerType = gopacket.LayerType
|
|
||||||
|
|
||||||
var (
|
|
||||||
LayerTypeTCP = layers.LayerTypeTCP
|
|
||||||
LayerTypeUDP = layers.LayerTypeUDP
|
|
||||||
LayerTypeICMPv4 = layers.LayerTypeICMPv4
|
|
||||||
LayerTypeICMPv6 = layers.LayerTypeICMPv6
|
|
||||||
)
|
|
||||||
@@ -1,293 +0,0 @@
|
|||||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
|
||||||
//go:build plus && packet
|
|
||||||
|
|
||||||
package netpackets
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
|
||||||
"github.com/google/gopacket"
|
|
||||||
"github.com/google/gopacket/layers"
|
|
||||||
"github.com/google/gopacket/pcap"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const defaultBPFFilter = "(tcp or udp or icmp) and not net 127.0.0.1"
|
|
||||||
|
|
||||||
type Listener struct {
|
|
||||||
filters []FilterInterface
|
|
||||||
incomingHandle *pcap.Handle
|
|
||||||
bpfFilter string
|
|
||||||
|
|
||||||
decodeDstIP bool
|
|
||||||
|
|
||||||
isClosed bool
|
|
||||||
|
|
||||||
outgoingIPList *IgnoredIPList
|
|
||||||
outgoingHandle *pcap.Handle
|
|
||||||
|
|
||||||
filterTicker *time.Ticker
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewListener() *Listener {
|
|
||||||
return &Listener{
|
|
||||||
isClosed: true,
|
|
||||||
outgoingIPList: NewIgnoredIPList(65535),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Listener) Start() error {
|
|
||||||
if !this.isClosed {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isClosed = false
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
startErr := this.loopOutgoing()
|
|
||||||
if startErr != nil {
|
|
||||||
remotelogs.Error("NET_PACKET", "start outgoing packet listener failed: "+startErr.Error())
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
this.loopUpdateFilter()
|
|
||||||
}()
|
|
||||||
|
|
||||||
for { // 无限 for 是为了防止意外退出
|
|
||||||
err := this.loopIncoming()
|
|
||||||
if err != nil {
|
|
||||||
if this.isClosed {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("start packet listener failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if this.isClosed {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Listener) AddFilter(filter FilterInterface) {
|
|
||||||
this.filters = append(this.filters, filter)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Listener) SetBPF(bpfFilter string) {
|
|
||||||
this.bpfFilter = bpfFilter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Listener) DecodeDstIP() {
|
|
||||||
this.decodeDstIP = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Listener) Stop() {
|
|
||||||
this.isClosed = true
|
|
||||||
this.incomingHandle.Close()
|
|
||||||
this.outgoingHandle.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Listener) IsRunning() bool {
|
|
||||||
return this.incomingHandle != nil && !this.isClosed
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Listener) loopIncoming() error {
|
|
||||||
const device = "any"
|
|
||||||
var err error
|
|
||||||
this.incomingHandle, err = pcap.OpenLive(device, 128, false /** ignore collision domain **/, pcap.BlockForever)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
this.incomingHandle.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
err = this.incomingHandle.SetDirection(pcap.DirectionIn)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(this.bpfFilter) > 0 {
|
|
||||||
err = this.incomingHandle.SetBPFFilter(this.bpfFilter)
|
|
||||||
} else {
|
|
||||||
this.bpfFilter = defaultBPFFilter
|
|
||||||
err = this.incomingHandle.SetBPFFilter(defaultBPFFilter)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var meta = &PacketMeta{}
|
|
||||||
|
|
||||||
var packetSource = gopacket.NewPacketSource(this.incomingHandle, this.incomingHandle.LinkType())
|
|
||||||
packetSource.NoCopy = true
|
|
||||||
packetSource.Lazy = true
|
|
||||||
|
|
||||||
var filters = this.filters
|
|
||||||
var countFilters = len(filters)
|
|
||||||
|
|
||||||
for packet := range packetSource.Packets() {
|
|
||||||
var networkLayer = packet.NetworkLayer()
|
|
||||||
if networkLayer == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var networkFlow = networkLayer.NetworkFlow()
|
|
||||||
|
|
||||||
var src = networkFlow.Src()
|
|
||||||
|
|
||||||
meta.SrcIP = src.String()
|
|
||||||
|
|
||||||
// ignore outgoing ip
|
|
||||||
if this.outgoingIPList.Contains(meta.SrcIP) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if this.decodeDstIP {
|
|
||||||
meta.DstIP = networkFlow.Dst().String()
|
|
||||||
}
|
|
||||||
meta.Length = packet.Metadata().Length
|
|
||||||
|
|
||||||
var transportLayer = packet.TransportLayer()
|
|
||||||
if transportLayer == nil {
|
|
||||||
meta.SrcPort = 0
|
|
||||||
meta.DstPort = 0
|
|
||||||
|
|
||||||
switch x := networkLayer.(type) {
|
|
||||||
case *layers.IPv4:
|
|
||||||
meta.LayerType = x.NextLayerType()
|
|
||||||
case *layers.IPv6:
|
|
||||||
meta.LayerType = x.NextLayerType()
|
|
||||||
}
|
|
||||||
|
|
||||||
// call filters
|
|
||||||
if countFilters == 1 {
|
|
||||||
filters[0].FilterMeta(meta)
|
|
||||||
} else {
|
|
||||||
for _, filter := range filters {
|
|
||||||
filter.FilterMeta(meta)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var transportFlow = transportLayer.TransportFlow()
|
|
||||||
meta.SrcPort = int(binary.BigEndian.Uint16(transportFlow.Src().Raw()))
|
|
||||||
meta.LayerType = transportLayer.LayerType()
|
|
||||||
meta.DstPort = int(binary.BigEndian.Uint16(transportFlow.Dst().Raw()))
|
|
||||||
|
|
||||||
// call filters
|
|
||||||
if countFilters == 1 {
|
|
||||||
filters[0].FilterMeta(meta)
|
|
||||||
} else {
|
|
||||||
for _, filter := range filters {
|
|
||||||
filter.FilterMeta(meta)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Listener) loopOutgoing() error {
|
|
||||||
const device = "any"
|
|
||||||
var err error
|
|
||||||
this.outgoingHandle, err = pcap.OpenLive(device, 128, true /** ignore collision domain **/, pcap.BlockForever)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
this.outgoingHandle.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
err = this.outgoingHandle.SetDirection(pcap.DirectionOut)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = this.outgoingHandle.SetBPFFilter("tcp and tcp[tcpflags] & (tcp-syn) != 0 and tcp[tcpflags] & (tcp-ack) = 0")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var packetSource = gopacket.NewPacketSource(this.outgoingHandle, this.outgoingHandle.LinkType())
|
|
||||||
packetSource.NoCopy = true
|
|
||||||
packetSource.Lazy = true
|
|
||||||
|
|
||||||
for packet := range packetSource.Packets() {
|
|
||||||
var networkLayer = packet.NetworkLayer()
|
|
||||||
if networkLayer == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var networkFlow = networkLayer.NetworkFlow()
|
|
||||||
var dstIP = networkFlow.Dst().String()
|
|
||||||
this.outgoingIPList.Add(dstIP)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Listener) loopUpdateFilter() {
|
|
||||||
if this.filterTicker != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.filterTicker = time.NewTicker(1 * time.Second)
|
|
||||||
var lastIPList []string
|
|
||||||
for range this.filterTicker.C {
|
|
||||||
if this.isClosed {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var ipList = this.outgoingIPList.List(512) // 基于bfp长度的限制,这里数量不能太多
|
|
||||||
sort.Strings(ipList)
|
|
||||||
if this.equalStrings(lastIPList, ipList) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
lastIPList = ipList
|
|
||||||
|
|
||||||
// apply
|
|
||||||
var incomingHandle = this.incomingHandle
|
|
||||||
if incomingHandle != nil {
|
|
||||||
var rules = []string{}
|
|
||||||
for _, ip := range ipList {
|
|
||||||
rules = append(rules, "not src host "+ip)
|
|
||||||
}
|
|
||||||
var newBPFFilter = this.bpfFilter + " and " + strings.Join(rules, " and ")
|
|
||||||
if utils.IsDebugEnv() {
|
|
||||||
remotelogs.Debug("NET_PACKET", "set new BPF filter: "+newBPFFilter)
|
|
||||||
}
|
|
||||||
err := incomingHandle.SetBPFFilter(newBPFFilter)
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.Error("NET_PACKET", "set new BPF filter failed: "+err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Listener) equalStrings(s1 []string, s2 []string) bool {
|
|
||||||
var l = len(s1)
|
|
||||||
if len(s2) != l {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if l == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for i := 0; i < l; i++ {
|
|
||||||
if s1[i] != s2[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
|
||||||
//go:build plus && packet
|
|
||||||
|
|
||||||
package netpackets_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/netpackets"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
|
|
||||||
"github.com/google/gopacket"
|
|
||||||
"github.com/google/gopacket/layers"
|
|
||||||
"github.com/iwind/TeaGo/types"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type testFilter struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *testFilter) FilterMeta(meta *netpackets.PacketMeta) {
|
|
||||||
log.Println(meta.LayerType.String() + " " + meta.SrcIP + ":" + types.String(meta.SrcPort) + " => " + meta.DstIP + ":" + types.String(meta.DstPort) + " " + types.String(meta.Length) + "bytes")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestListener_Start(t *testing.T) {
|
|
||||||
if !testutils.IsSingleTesting() {
|
|
||||||
if os.Getgid() > 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var listener = netpackets.NewListener()
|
|
||||||
listener.AddFilter(&testFilter{})
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
time.Sleep(10 * time.Second)
|
|
||||||
t.Log("stopping ...")
|
|
||||||
listener.Stop()
|
|
||||||
}()
|
|
||||||
|
|
||||||
t.Log("starting ...")
|
|
||||||
err := listener.Start()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestListener_DecodePacket_UDP(t *testing.T) {
|
|
||||||
var packetData = []byte{69, 0, 0, 134, 140, 133, 0, 0, 118, 17, 16, 79, 223, 5, 5, 5, 192, 168, 2, 224, 0, 53, 232, 163, 0, 114, 0, 0, 69, 42, 129, 128, 0, 1, 0, 3, 0, 0, 0, 0, 6, 115, 116, 97, 116, 105, 99, 7, 111, 115, 99, 104, 105, 110, 97, 3, 110, 101, 116, 0, 0, 1, 0, 1, 192, 12, 0, 5, 0, 1, 0, 0, 0, 1, 0, 25, 10, 115, 116, 97, 116, 105, 99, 45, 111, 115, 99, 2, 98, 48, 5, 97, 105, 99, 100, 110, 3, 99, 111, 109, 0, 192, 48, 0, 5, 0, 1, 0, 0, 0, 1, 0, 5, 2, 118, 109, 192, 62, 192, 85, 0, 1, 0, 1, 0, 0, 0, 1, 0, 4, 218, 28, 104, 157}
|
|
||||||
|
|
||||||
var packet = gopacket.NewPacket(packetData, layers.LayerTypeIPv4, gopacket.DecodeOptions{})
|
|
||||||
var networkFlow = packet.NetworkLayer().NetworkFlow()
|
|
||||||
|
|
||||||
t.Log(networkFlow)
|
|
||||||
|
|
||||||
t.Log(packet.Metadata().Length)
|
|
||||||
t.Log(packet.TransportLayer().TransportFlow())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestListener_DecodePacket_TCP(t *testing.T) {
|
|
||||||
var packetData = []byte{69, 8, 0, 64, 6, 51, 64, 0, 52, 6, 188, 222, 74, 91, 117, 187, 192, 168, 2, 224, 1, 187, 225, 226, 137, 198, 251, 25, 221, 137, 133, 93, 176, 16, 1, 245, 224, 6, 0, 0, 1, 1, 8, 10, 30, 187, 162, 175, 35, 215, 100, 174, 1, 1, 5, 10, 221, 137, 133, 68, 221, 137, 133, 93}
|
|
||||||
|
|
||||||
var packet = gopacket.NewPacket(packetData, layers.LayerTypeIPv4, gopacket.DecodeOptions{})
|
|
||||||
var networkFlow = packet.NetworkLayer().NetworkFlow()
|
|
||||||
t.Log(networkFlow.Src().Raw(), len(networkFlow.Src().Raw()))
|
|
||||||
|
|
||||||
t.Log(networkFlow)
|
|
||||||
|
|
||||||
t.Log(packet.Metadata().Length)
|
|
||||||
t.Log(packet.TransportLayer().TransportFlow())
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkListener_DecodePacket(b *testing.B) {
|
|
||||||
var packetData = []byte{69, 0, 0, 134, 140, 133, 0, 0, 118, 17, 16, 79, 223, 5, 5, 5, 192, 168, 2, 224, 0, 53, 232, 163, 0, 114, 0, 0, 69, 42, 129, 128, 0, 1, 0, 3, 0, 0, 0, 0, 6, 115, 116, 97, 116, 105, 99, 7, 111, 115, 99, 104, 105, 110, 97, 3, 110, 101, 116, 0, 0, 1, 0, 1, 192, 12, 0, 5, 0, 1, 0, 0, 0, 1, 0, 25, 10, 115, 116, 97, 116, 105, 99, 45, 111, 115, 99, 2, 98, 48, 5, 97, 105, 99, 100, 110, 3, 99, 111, 109, 0, 192, 48, 0, 5, 0, 1, 0, 0, 0, 1, 0, 5, 2, 118, 109, 192, 62, 192, 85, 0, 1, 0, 1, 0, 0, 0, 1, 0, 4, 218, 28, 104, 157}
|
|
||||||
|
|
||||||
var decodeOptions = gopacket.DecodeOptions{
|
|
||||||
Lazy: true,
|
|
||||||
NoCopy: true,
|
|
||||||
//SkipDecodeRecovery: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
var packet = gopacket.NewPacket(packetData, layers.LayerTypeIPv4, decodeOptions)
|
|
||||||
|
|
||||||
var networkFlow = packet.NetworkLayer().NetworkFlow()
|
|
||||||
var src = networkFlow.Src()
|
|
||||||
var dest = networkFlow.Dst()
|
|
||||||
|
|
||||||
_ = netpackets.IsLocalRawIPv4(src.Raw())
|
|
||||||
_ = netpackets.IsLocalRawIPv4(dest.Raw())
|
|
||||||
|
|
||||||
_ = src.String()
|
|
||||||
_ = dest.String()
|
|
||||||
|
|
||||||
_ = packet.Metadata().Length
|
|
||||||
|
|
||||||
var transportFlow = packet.TransportLayer().TransportFlow()
|
|
||||||
//_ = transportFlow.Src().String()
|
|
||||||
//_ = transportFlow.Dst().String()
|
|
||||||
|
|
||||||
_ = int(binary.BigEndian.Uint16(transportFlow.Src().Raw()))
|
|
||||||
_ = int(binary.BigEndian.Uint16(transportFlow.Dst().Raw()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
|
||||||
//go:build plus && packet
|
|
||||||
|
|
||||||
package netpackets
|
|
||||||
|
|
||||||
type PacketMeta struct {
|
|
||||||
LayerType LayerType
|
|
||||||
SrcIP string
|
|
||||||
SrcPort int
|
|
||||||
DstIP string
|
|
||||||
DstPort int
|
|
||||||
Length int
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
|
||||||
|
|
||||||
package netpackets
|
|
||||||
|
|
||||||
// IsLocalRawIPv4 使用原始IP数据判断是否为本地IPv4
|
|
||||||
func IsLocalRawIPv4(ip []byte) bool {
|
|
||||||
if len(ip) != 4 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if ip[0] == 127 ||
|
|
||||||
ip[0] == 10 ||
|
|
||||||
(ip[0] == 172 && ip[1]&0xf0 == 16) ||
|
|
||||||
(ip[0] == 192 && ip[1] == 168) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
|
||||||
|
|
||||||
package netpackets_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/netpackets"
|
|
||||||
"github.com/iwind/TeaGo/assert"
|
|
||||||
"net"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestIsLocalRawIPv4(t *testing.T) {
|
|
||||||
var a = assert.NewAssertion(t)
|
|
||||||
|
|
||||||
a.IsTrue(netpackets.IsLocalRawIPv4(net.ParseIP("192.168.2.100").To4()))
|
|
||||||
a.IsTrue(netpackets.IsLocalRawIPv4(net.ParseIP("127.0.0.1").To4()))
|
|
||||||
a.IsTrue(netpackets.IsLocalRawIPv4(net.ParseIP("172.16.0.1").To4()))
|
|
||||||
a.IsTrue(netpackets.IsLocalRawIPv4(net.ParseIP("10.0.0.1").To4()))
|
|
||||||
|
|
||||||
a.IsFalse(netpackets.IsLocalRawIPv4(net.ParseIP("1.2.3.4").To4()))
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package teaconst
|
package teaconst
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "0.1.5"
|
Version = "1.5.0"
|
||||||
|
|
||||||
ProductName = "Edge Reporter"
|
ProductName = "Edge Reporter"
|
||||||
ProcessName = "edge-reporter"
|
ProcessName = "edge-reporter"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package teaconst
|
package teaconst
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "1.4.9" //1.3.8.2
|
Version = "1.5.0" //1.3.8.2
|
||||||
|
|
||||||
ProductName = "Edge User"
|
ProductName = "Edge User"
|
||||||
ProcessName = "edge-user"
|
ProcessName = "edge-user"
|
||||||
|
|||||||
@@ -174,6 +174,8 @@ replace_files() {
|
|||||||
# 替换 EdgeAdmin bin
|
# 替换 EdgeAdmin bin
|
||||||
if [ -d "$TEMP_DIR/edge-admin/bin" ]; then
|
if [ -d "$TEMP_DIR/edge-admin/bin" ]; then
|
||||||
log_info "替换 EdgeAdmin 可执行文件..."
|
log_info "替换 EdgeAdmin 可执行文件..."
|
||||||
|
rm -rf "$TARGET_DIR/bin"
|
||||||
|
mkdir -p "$TARGET_DIR/bin"
|
||||||
cp -r "$TEMP_DIR/edge-admin/bin"/* "$TARGET_DIR/bin/"
|
cp -r "$TEMP_DIR/edge-admin/bin"/* "$TARGET_DIR/bin/"
|
||||||
log_info "✅ EdgeAdmin bin 已更新"
|
log_info "✅ EdgeAdmin bin 已更新"
|
||||||
fi
|
fi
|
||||||
@@ -181,15 +183,9 @@ replace_files() {
|
|||||||
# 替换 EdgeAdmin web(排除 tmp)
|
# 替换 EdgeAdmin web(排除 tmp)
|
||||||
if [ -d "$TEMP_DIR/edge-admin/web" ]; then
|
if [ -d "$TEMP_DIR/edge-admin/web" ]; then
|
||||||
log_info "替换 EdgeAdmin 前端文件..."
|
log_info "替换 EdgeAdmin 前端文件..."
|
||||||
if command -v rsync > /dev/null; then
|
rm -rf "$TARGET_DIR/web"
|
||||||
rsync -av --exclude='tmp' \
|
cp -r "$TEMP_DIR/edge-admin/web" "$TARGET_DIR/"
|
||||||
"$TEMP_DIR/edge-admin/web/" "$TARGET_DIR/web/"
|
mkdir -p "$TARGET_DIR/web/tmp"
|
||||||
else
|
|
||||||
# 如果没有 rsync,使用 cp
|
|
||||||
cp -r "$TEMP_DIR/edge-admin/web"/* "$TARGET_DIR/web/" 2>/dev/null || true
|
|
||||||
rm -rf "$TARGET_DIR/web/tmp"/* 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
# 清空 tmp 目录
|
|
||||||
rm -rf "$TARGET_DIR/web/tmp"/* 2>/dev/null || true
|
rm -rf "$TARGET_DIR/web/tmp"/* 2>/dev/null || true
|
||||||
log_info "✅ EdgeAdmin web 已更新"
|
log_info "✅ EdgeAdmin web 已更新"
|
||||||
fi
|
fi
|
||||||
@@ -203,6 +199,7 @@ replace_files() {
|
|||||||
|
|
||||||
# 替换 bin
|
# 替换 bin
|
||||||
if [ -d "$TEMP_DIR/edge-admin/edge-api/bin" ]; then
|
if [ -d "$TEMP_DIR/edge-admin/edge-api/bin" ]; then
|
||||||
|
rm -rf "$TARGET_DIR/edge-api/bin"
|
||||||
mkdir -p "$TARGET_DIR/edge-api/bin"
|
mkdir -p "$TARGET_DIR/edge-api/bin"
|
||||||
cp -r "$TEMP_DIR/edge-admin/edge-api/bin"/* \
|
cp -r "$TEMP_DIR/edge-admin/edge-api/bin"/* \
|
||||||
"$TARGET_DIR/edge-api/bin/" 2>/dev/null || true
|
"$TARGET_DIR/edge-api/bin/" 2>/dev/null || true
|
||||||
@@ -211,6 +208,7 @@ replace_files() {
|
|||||||
|
|
||||||
# 替换 deploy(节点安装包)
|
# 替换 deploy(节点安装包)
|
||||||
if [ -d "$TEMP_DIR/edge-admin/edge-api/deploy" ]; then
|
if [ -d "$TEMP_DIR/edge-admin/edge-api/deploy" ]; then
|
||||||
|
rm -rf "$TARGET_DIR/edge-api/deploy"
|
||||||
mkdir -p "$TARGET_DIR/edge-api/deploy"
|
mkdir -p "$TARGET_DIR/edge-api/deploy"
|
||||||
cp -r "$TEMP_DIR/edge-admin/edge-api/deploy"/* \
|
cp -r "$TEMP_DIR/edge-admin/edge-api/deploy"/* \
|
||||||
"$TARGET_DIR/edge-api/deploy/" 2>/dev/null || true
|
"$TARGET_DIR/edge-api/deploy/" 2>/dev/null || true
|
||||||
@@ -219,6 +217,7 @@ replace_files() {
|
|||||||
|
|
||||||
# 替换 installers(安装工具)
|
# 替换 installers(安装工具)
|
||||||
if [ -d "$TEMP_DIR/edge-admin/edge-api/installers" ]; then
|
if [ -d "$TEMP_DIR/edge-admin/edge-api/installers" ]; then
|
||||||
|
rm -rf "$TARGET_DIR/edge-api/installers"
|
||||||
mkdir -p "$TARGET_DIR/edge-api/installers"
|
mkdir -p "$TARGET_DIR/edge-api/installers"
|
||||||
cp -r "$TEMP_DIR/edge-admin/edge-api/installers"/* \
|
cp -r "$TEMP_DIR/edge-admin/edge-api/installers"/* \
|
||||||
"$TARGET_DIR/edge-api/installers/" 2>/dev/null || true
|
"$TARGET_DIR/edge-api/installers/" 2>/dev/null || true
|
||||||
@@ -345,4 +344,3 @@ main() {
|
|||||||
|
|
||||||
# 执行主函数
|
# 执行主函数
|
||||||
main
|
main
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user