带阿里标识的版本
This commit is contained in:
@@ -39,6 +39,7 @@ type HTTPDNSAccessLogListFilter struct {
|
|||||||
ClusterId int64
|
ClusterId int64
|
||||||
NodeId int64
|
NodeId int64
|
||||||
AppId string
|
AppId string
|
||||||
|
AppIds []string
|
||||||
Domain string
|
Domain string
|
||||||
Status string
|
Status string
|
||||||
Keyword string
|
Keyword string
|
||||||
@@ -215,6 +216,20 @@ func (s *HTTPDNSAccessLogsStore) buildConditions(f HTTPDNSAccessLogListFilter) [
|
|||||||
}
|
}
|
||||||
if appID := strings.TrimSpace(f.AppId); appID != "" {
|
if appID := strings.TrimSpace(f.AppId); appID != "" {
|
||||||
conditions = append(conditions, "app_id = '"+escapeString(appID)+"'")
|
conditions = append(conditions, "app_id = '"+escapeString(appID)+"'")
|
||||||
|
} else if len(f.AppIds) > 0 {
|
||||||
|
validAppIds := make([]string, 0, len(f.AppIds))
|
||||||
|
for _, appID := range f.AppIds {
|
||||||
|
appID = strings.TrimSpace(appID)
|
||||||
|
if len(appID) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
validAppIds = append(validAppIds, "'"+escapeString(appID)+"'")
|
||||||
|
}
|
||||||
|
if len(validAppIds) == 0 {
|
||||||
|
conditions = append(conditions, "1 = 0")
|
||||||
|
} else {
|
||||||
|
conditions = append(conditions, "app_id IN ("+strings.Join(validAppIds, ",")+")")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if domain := strings.TrimSpace(f.Domain); domain != "" {
|
if domain := strings.TrimSpace(f.Domain); domain != "" {
|
||||||
conditions = append(conditions, "domain = '"+escapeString(domain)+"'")
|
conditions = append(conditions, "domain = '"+escapeString(domain)+"'")
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/dbs"
|
"github.com/iwind/TeaGo/dbs"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HTTPDNSAccessLogDAO dbs.DAO
|
type HTTPDNSAccessLogDAO dbs.DAO
|
||||||
@@ -52,6 +53,10 @@ func (this *HTTPDNSAccessLogDAO) CreateLog(tx *dbs.Tx, log *HTTPDNSAccessLog) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSAccessLogDAO) BuildListQuery(tx *dbs.Tx, day string, clusterId int64, nodeId int64, appId string, domain string, status string, keyword string) *dbs.Query {
|
func (this *HTTPDNSAccessLogDAO) BuildListQuery(tx *dbs.Tx, day string, clusterId int64, nodeId int64, appId string, domain string, status string, keyword string) *dbs.Query {
|
||||||
|
return this.BuildListQueryWithAppIds(tx, day, clusterId, nodeId, appId, nil, domain, status, keyword)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSAccessLogDAO) BuildListQueryWithAppIds(tx *dbs.Tx, day string, clusterId int64, nodeId int64, appId string, appIds []string, domain string, status string, keyword string) *dbs.Query {
|
||||||
query := this.Query(tx).DescPk()
|
query := this.Query(tx).DescPk()
|
||||||
if len(day) > 0 {
|
if len(day) > 0 {
|
||||||
query = query.Attr("day", day)
|
query = query.Attr("day", day)
|
||||||
@@ -62,6 +67,21 @@ func (this *HTTPDNSAccessLogDAO) BuildListQuery(tx *dbs.Tx, day string, clusterI
|
|||||||
if nodeId > 0 {
|
if nodeId > 0 {
|
||||||
query = query.Attr("nodeId", nodeId)
|
query = query.Attr("nodeId", nodeId)
|
||||||
}
|
}
|
||||||
|
if len(appIds) > 0 {
|
||||||
|
validAppIds := make([]string, 0, len(appIds))
|
||||||
|
for _, value := range appIds {
|
||||||
|
value = strings.TrimSpace(value)
|
||||||
|
if len(value) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
validAppIds = append(validAppIds, value)
|
||||||
|
}
|
||||||
|
if len(validAppIds) == 0 {
|
||||||
|
query = query.Where("1 = 0")
|
||||||
|
} else {
|
||||||
|
query = query.Attr("appId", validAppIds)
|
||||||
|
}
|
||||||
|
}
|
||||||
if len(appId) > 0 {
|
if len(appId) > 0 {
|
||||||
query = query.Attr("appId", appId)
|
query = query.Attr("appId", appId)
|
||||||
}
|
}
|
||||||
@@ -78,11 +98,24 @@ func (this *HTTPDNSAccessLogDAO) BuildListQuery(tx *dbs.Tx, day string, clusterI
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSAccessLogDAO) CountLogs(tx *dbs.Tx, day string, clusterId int64, nodeId int64, appId string, domain string, status string, keyword string) (int64, error) {
|
func (this *HTTPDNSAccessLogDAO) CountLogs(tx *dbs.Tx, day string, clusterId int64, nodeId int64, appId string, domain string, status string, keyword string) (int64, error) {
|
||||||
return this.BuildListQuery(tx, day, clusterId, nodeId, appId, domain, status, keyword).Count()
|
return this.BuildListQueryWithAppIds(tx, day, clusterId, nodeId, appId, nil, domain, status, keyword).Count()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSAccessLogDAO) ListLogs(tx *dbs.Tx, day string, clusterId int64, nodeId int64, appId string, domain string, status string, keyword string, offset int64, size int64) (result []*HTTPDNSAccessLog, err error) {
|
func (this *HTTPDNSAccessLogDAO) ListLogs(tx *dbs.Tx, day string, clusterId int64, nodeId int64, appId string, domain string, status string, keyword string, offset int64, size int64) (result []*HTTPDNSAccessLog, err error) {
|
||||||
_, err = this.BuildListQuery(tx, day, clusterId, nodeId, appId, domain, status, keyword).
|
_, err = this.BuildListQueryWithAppIds(tx, day, clusterId, nodeId, appId, nil, domain, status, keyword).
|
||||||
|
Offset(offset).
|
||||||
|
Limit(size).
|
||||||
|
Slice(&result).
|
||||||
|
FindAll()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSAccessLogDAO) CountLogsWithAppIds(tx *dbs.Tx, day string, clusterId int64, nodeId int64, appId string, appIds []string, domain string, status string, keyword string) (int64, error) {
|
||||||
|
return this.BuildListQueryWithAppIds(tx, day, clusterId, nodeId, appId, appIds, domain, status, keyword).Count()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSAccessLogDAO) ListLogsWithAppIds(tx *dbs.Tx, day string, clusterId int64, nodeId int64, appId string, appIds []string, domain string, status string, keyword string, offset int64, size int64) (result []*HTTPDNSAccessLog, err error) {
|
||||||
|
_, err = this.BuildListQueryWithAppIds(tx, day, clusterId, nodeId, appId, appIds, domain, status, keyword).
|
||||||
Offset(offset).
|
Offset(offset).
|
||||||
Limit(size).
|
Limit(size).
|
||||||
Slice(&result).
|
Slice(&result).
|
||||||
|
|||||||
@@ -85,6 +85,18 @@ func (this *HTTPDNSAppDAO) FindEnabledApp(tx *dbs.Tx, appDbId int64) (*HTTPDNSAp
|
|||||||
return one.(*HTTPDNSApp), nil
|
return one.(*HTTPDNSApp), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSAppDAO) FindEnabledAppWithUser(tx *dbs.Tx, appDbId int64, userId int64) (*HTTPDNSApp, error) {
|
||||||
|
one, err := this.Query(tx).
|
||||||
|
Pk(appDbId).
|
||||||
|
State(HTTPDNSAppStateEnabled).
|
||||||
|
Attr("userId", userId).
|
||||||
|
Find()
|
||||||
|
if one == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return one.(*HTTPDNSApp), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSAppDAO) FindEnabledAppWithAppId(tx *dbs.Tx, appId string) (*HTTPDNSApp, error) {
|
func (this *HTTPDNSAppDAO) FindEnabledAppWithAppId(tx *dbs.Tx, appId string) (*HTTPDNSApp, error) {
|
||||||
one, err := this.Query(tx).
|
one, err := this.Query(tx).
|
||||||
State(HTTPDNSAppStateEnabled).
|
State(HTTPDNSAppStateEnabled).
|
||||||
@@ -96,6 +108,31 @@ func (this *HTTPDNSAppDAO) FindEnabledAppWithAppId(tx *dbs.Tx, appId string) (*H
|
|||||||
return one.(*HTTPDNSApp), nil
|
return one.(*HTTPDNSApp), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSAppDAO) FindEnabledAppWithAppIdAndUser(tx *dbs.Tx, appId string, userId int64) (*HTTPDNSApp, error) {
|
||||||
|
one, err := this.Query(tx).
|
||||||
|
State(HTTPDNSAppStateEnabled).
|
||||||
|
Attr("appId", appId).
|
||||||
|
Attr("userId", userId).
|
||||||
|
Find()
|
||||||
|
if one == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return one.(*HTTPDNSApp), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSAppDAO) FindLatestEnabledAppWithNameAndUser(tx *dbs.Tx, name string, userId int64) (*HTTPDNSApp, error) {
|
||||||
|
one, err := this.Query(tx).
|
||||||
|
State(HTTPDNSAppStateEnabled).
|
||||||
|
Attr("name", name).
|
||||||
|
Attr("userId", userId).
|
||||||
|
DescPk().
|
||||||
|
Find()
|
||||||
|
if one == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return one.(*HTTPDNSApp), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSAppDAO) ListEnabledApps(tx *dbs.Tx, offset int64, size int64, keyword string) (result []*HTTPDNSApp, err error) {
|
func (this *HTTPDNSAppDAO) ListEnabledApps(tx *dbs.Tx, offset int64, size int64, keyword string) (result []*HTTPDNSApp, err error) {
|
||||||
query := this.Query(tx).
|
query := this.Query(tx).
|
||||||
State(HTTPDNSAppStateEnabled).
|
State(HTTPDNSAppStateEnabled).
|
||||||
@@ -110,6 +147,21 @@ func (this *HTTPDNSAppDAO) ListEnabledApps(tx *dbs.Tx, offset int64, size int64,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSAppDAO) ListEnabledAppsWithUser(tx *dbs.Tx, userId int64, offset int64, size int64, keyword string) (result []*HTTPDNSApp, err error) {
|
||||||
|
query := this.Query(tx).
|
||||||
|
State(HTTPDNSAppStateEnabled).
|
||||||
|
Attr("userId", userId).
|
||||||
|
AscPk()
|
||||||
|
if len(keyword) > 0 {
|
||||||
|
query = query.Where("(name LIKE :kw OR appId LIKE :kw)").Param("kw", "%"+keyword+"%")
|
||||||
|
}
|
||||||
|
if size > 0 {
|
||||||
|
query = query.Offset(offset).Limit(size)
|
||||||
|
}
|
||||||
|
_, err = query.Slice(&result).FindAll()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSAppDAO) CountEnabledApps(tx *dbs.Tx, keyword string) (int64, error) {
|
func (this *HTTPDNSAppDAO) CountEnabledApps(tx *dbs.Tx, keyword string) (int64, error) {
|
||||||
query := this.Query(tx).State(HTTPDNSAppStateEnabled)
|
query := this.Query(tx).State(HTTPDNSAppStateEnabled)
|
||||||
if len(keyword) > 0 {
|
if len(keyword) > 0 {
|
||||||
@@ -118,6 +170,14 @@ func (this *HTTPDNSAppDAO) CountEnabledApps(tx *dbs.Tx, keyword string) (int64,
|
|||||||
return query.Count()
|
return query.Count()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSAppDAO) CountEnabledAppsWithUser(tx *dbs.Tx, userId int64, keyword string) (int64, error) {
|
||||||
|
query := this.Query(tx).State(HTTPDNSAppStateEnabled).Attr("userId", userId)
|
||||||
|
if len(keyword) > 0 {
|
||||||
|
query = query.Where("(name LIKE :kw OR appId LIKE :kw)").Param("kw", "%"+keyword+"%")
|
||||||
|
}
|
||||||
|
return query.Count()
|
||||||
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSAppDAO) FindAllEnabledApps(tx *dbs.Tx) (result []*HTTPDNSApp, err error) {
|
func (this *HTTPDNSAppDAO) FindAllEnabledApps(tx *dbs.Tx) (result []*HTTPDNSApp, err error) {
|
||||||
_, err = this.Query(tx).
|
_, err = this.Query(tx).
|
||||||
State(HTTPDNSAppStateEnabled).
|
State(HTTPDNSAppStateEnabled).
|
||||||
@@ -126,3 +186,28 @@ func (this *HTTPDNSAppDAO) FindAllEnabledApps(tx *dbs.Tx) (result []*HTTPDNSApp,
|
|||||||
FindAll()
|
FindAll()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSAppDAO) FindAllEnabledAppsWithUser(tx *dbs.Tx, userId int64) (result []*HTTPDNSApp, err error) {
|
||||||
|
_, err = this.Query(tx).
|
||||||
|
State(HTTPDNSAppStateEnabled).
|
||||||
|
Attr("userId", userId).
|
||||||
|
AscPk().
|
||||||
|
Slice(&result).
|
||||||
|
FindAll()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSAppDAO) ListEnabledAppIdsWithUser(tx *dbs.Tx, userId int64) (result []string, err error) {
|
||||||
|
apps, err := this.FindAllEnabledAppsWithUser(tx, userId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = make([]string, 0, len(apps))
|
||||||
|
for _, app := range apps {
|
||||||
|
if app == nil || len(app.AppId) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, app.AppId)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -38,6 +38,27 @@ func init() {
|
|||||||
func (this *HTTPDNSAppSecretDAO) InitAppSecret(tx *dbs.Tx, appDbId int64, signEnabled bool) (string, uint64, error) {
|
func (this *HTTPDNSAppSecretDAO) InitAppSecret(tx *dbs.Tx, appDbId int64, signEnabled bool) (string, uint64, error) {
|
||||||
signSecret := "ss_" + rands.HexString(12)
|
signSecret := "ss_" + rands.HexString(12)
|
||||||
now := uint64(time.Now().Unix())
|
now := uint64(time.Now().Unix())
|
||||||
|
|
||||||
|
// 兼容历史数据:如果已存在(可能是停用状态)则直接恢复并更新,避免 UNIQUE(appId) 冲突
|
||||||
|
old, err := this.Query(tx).
|
||||||
|
Attr("appId", appDbId).
|
||||||
|
Find()
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
if old != nil {
|
||||||
|
oldSecret := old.(*HTTPDNSAppSecret)
|
||||||
|
_, err = this.Query(tx).
|
||||||
|
Pk(oldSecret.Id).
|
||||||
|
Set("signEnabled", signEnabled).
|
||||||
|
Set("signSecret", signSecret).
|
||||||
|
Set("signUpdatedAt", now).
|
||||||
|
Set("updatedAt", now).
|
||||||
|
Set("state", HTTPDNSAppSecretStateEnabled).
|
||||||
|
Update()
|
||||||
|
return signSecret, now, err
|
||||||
|
}
|
||||||
|
|
||||||
var op = NewHTTPDNSAppSecretOperator()
|
var op = NewHTTPDNSAppSecretOperator()
|
||||||
op.AppId = appDbId
|
op.AppId = appDbId
|
||||||
op.SignEnabled = signEnabled
|
op.SignEnabled = signEnabled
|
||||||
@@ -45,7 +66,7 @@ func (this *HTTPDNSAppSecretDAO) InitAppSecret(tx *dbs.Tx, appDbId int64, signEn
|
|||||||
op.SignUpdatedAt = now
|
op.SignUpdatedAt = now
|
||||||
op.UpdatedAt = now
|
op.UpdatedAt = now
|
||||||
op.State = HTTPDNSAppSecretStateEnabled
|
op.State = HTTPDNSAppSecretStateEnabled
|
||||||
err := this.Save(tx, op)
|
err = this.Save(tx, op)
|
||||||
return signSecret, now, err
|
return signSecret, now, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -152,6 +152,22 @@ func (this *HTTPDNSClusterDAO) FindAllEnabledClusters(tx *dbs.Tx) (result []*HTT
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *HTTPDNSClusterDAO) FindDefaultPrimaryClusterId(tx *dbs.Tx) (int64, error) {
|
||||||
|
col, err := this.Query(tx).
|
||||||
|
State(HTTPDNSClusterStateEnabled).
|
||||||
|
Attr("isDefault", true).
|
||||||
|
Result("id").
|
||||||
|
AscPk().
|
||||||
|
FindCol(nil)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if col == nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return types.Int64(col), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSClusterDAO) UpdateDefaultCluster(tx *dbs.Tx, clusterId int64) error {
|
func (this *HTTPDNSClusterDAO) UpdateDefaultCluster(tx *dbs.Tx, clusterId int64) error {
|
||||||
err := this.Query(tx).
|
err := this.Query(tx).
|
||||||
State(HTTPDNSClusterStateEnabled).
|
State(HTTPDNSClusterStateEnabled).
|
||||||
|
|||||||
@@ -104,11 +104,16 @@ func (i *HTTPDNSNodeInstaller) Install(dir string, params interface{}, installSt
|
|||||||
_, _, _ = i.client.Exec("chown " + i.client.User() + " " + filepath.Dir(configFile))
|
_, _, _ = i.client.Exec("chown " + i.client.User() + " " + filepath.Dir(configFile))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listenAddr := strings.TrimSpace(nodeParams.HTTPDNSListenAddr)
|
||||||
|
if len(listenAddr) == 0 {
|
||||||
|
listenAddr = ":443"
|
||||||
|
}
|
||||||
|
|
||||||
configData := []byte(`rpc.endpoints: [ ${endpoints} ]
|
configData := []byte(`rpc.endpoints: [ ${endpoints} ]
|
||||||
nodeId: "${nodeId}"
|
nodeId: "${nodeId}"
|
||||||
secret: "${nodeSecret}"
|
secret: "${nodeSecret}"
|
||||||
|
|
||||||
https.listenAddr: ":443"
|
https.listenAddr: "${listenAddr}"
|
||||||
https.cert: "${certFile}"
|
https.cert: "${certFile}"
|
||||||
https.key: "${keyFile}"`)
|
https.key: "${keyFile}"`)
|
||||||
certFileClean := strings.ReplaceAll(certFile, "\\", "/")
|
certFileClean := strings.ReplaceAll(certFile, "\\", "/")
|
||||||
@@ -117,6 +122,7 @@ https.key: "${keyFile}"`)
|
|||||||
configData = bytes.ReplaceAll(configData, []byte("${endpoints}"), []byte(nodeParams.QuoteEndpoints()))
|
configData = bytes.ReplaceAll(configData, []byte("${endpoints}"), []byte(nodeParams.QuoteEndpoints()))
|
||||||
configData = bytes.ReplaceAll(configData, []byte("${nodeId}"), []byte(nodeParams.NodeId))
|
configData = bytes.ReplaceAll(configData, []byte("${nodeId}"), []byte(nodeParams.NodeId))
|
||||||
configData = bytes.ReplaceAll(configData, []byte("${nodeSecret}"), []byte(nodeParams.Secret))
|
configData = bytes.ReplaceAll(configData, []byte("${nodeSecret}"), []byte(nodeParams.Secret))
|
||||||
|
configData = bytes.ReplaceAll(configData, []byte("${listenAddr}"), []byte(listenAddr))
|
||||||
configData = bytes.ReplaceAll(configData, []byte("${certFile}"), []byte(certFileClean))
|
configData = bytes.ReplaceAll(configData, []byte("${certFile}"), []byte(certFileClean))
|
||||||
configData = bytes.ReplaceAll(configData, []byte("${keyFile}"), []byte(keyFileClean))
|
configData = bytes.ReplaceAll(configData, []byte("${keyFile}"), []byte(keyFileClean))
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type NodeParams struct {
|
type NodeParams struct {
|
||||||
Endpoints []string
|
Endpoints []string
|
||||||
NodeId string
|
NodeId string
|
||||||
Secret string
|
Secret string
|
||||||
TLSCertData []byte
|
TLSCertData []byte
|
||||||
TLSKeyData []byte
|
TLSKeyData []byte
|
||||||
IsUpgrading bool // 是否为升级
|
HTTPDNSListenAddr string
|
||||||
|
IsUpgrading bool // 是否为升级
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *NodeParams) Validate() error {
|
func (this *NodeParams) Validate() error {
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
package installers
|
package installers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
|
"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
@@ -136,14 +140,20 @@ func (q *HTTPDNSNodeQueue) InstallNode(nodeId int64, installStatus *models.NodeI
|
|||||||
installStatus.ErrorCode = "EMPTY_TLS_CERT"
|
installStatus.ErrorCode = "EMPTY_TLS_CERT"
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
httpdnsListenAddr, err := q.resolveClusterTLSListenAddr(cluster)
|
||||||
|
if err != nil {
|
||||||
|
installStatus.ErrorCode = "INVALID_TLS_LISTEN"
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
params := &NodeParams{
|
params := &NodeParams{
|
||||||
Endpoints: apiEndpoints,
|
Endpoints: apiEndpoints,
|
||||||
NodeId: node.UniqueId,
|
NodeId: node.UniqueId,
|
||||||
Secret: node.Secret,
|
Secret: node.Secret,
|
||||||
TLSCertData: tlsCertData,
|
TLSCertData: tlsCertData,
|
||||||
TLSKeyData: tlsKeyData,
|
TLSKeyData: tlsKeyData,
|
||||||
IsUpgrading: isUpgrading,
|
HTTPDNSListenAddr: httpdnsListenAddr,
|
||||||
|
IsUpgrading: isUpgrading,
|
||||||
}
|
}
|
||||||
|
|
||||||
installer := &HTTPDNSNodeInstaller{}
|
installer := &HTTPDNSNodeInstaller{}
|
||||||
@@ -246,6 +256,37 @@ func (q *HTTPDNSNodeQueue) resolveClusterTLSCertPair(cluster *models.HTTPDNSClus
|
|||||||
return nil, nil, errors.New("cluster tls certificate is not configured")
|
return nil, nil, errors.New("cluster tls certificate is not configured")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *HTTPDNSNodeQueue) resolveClusterTLSListenAddr(cluster *models.HTTPDNSCluster) (string, error) {
|
||||||
|
const defaultListenAddr = ":443"
|
||||||
|
|
||||||
|
if cluster == nil || len(cluster.TLSPolicy) == 0 {
|
||||||
|
return defaultListenAddr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConfig, err := serverconfigs.NewTLSProtocolConfigFromJSON(cluster.TLSPolicy)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("decode cluster tls listen failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, listen := range tlsConfig.Listen {
|
||||||
|
if listen == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := listen.Init(); err != nil {
|
||||||
|
return "", fmt.Errorf("invalid cluster tls listen address '%s': %w", listen.PortRange, err)
|
||||||
|
}
|
||||||
|
if listen.MinPort <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
host := strings.TrimSpace(listen.Host)
|
||||||
|
return net.JoinHostPort(host, strconv.Itoa(listen.MinPort)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultListenAddr, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (q *HTTPDNSNodeQueue) parseSSHInfo(node *models.HTTPDNSNode) (string, int, int64, error) {
|
func (q *HTTPDNSNodeQueue) parseSSHInfo(node *models.HTTPDNSNode) (string, int, int64, error) {
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return "", 0, 0, errors.New("node should not be nil")
|
return "", 0, 0, errors.New("node should not be nil")
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/clickhouse"
|
"github.com/TeaOSLab/EdgeAPI/internal/clickhouse"
|
||||||
@@ -132,16 +133,43 @@ func (s *HTTPDNSAccessLogService) CreateHTTPDNSAccessLogs(ctx context.Context, r
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *HTTPDNSAccessLogService) ListHTTPDNSAccessLogs(ctx context.Context, req *pb.ListHTTPDNSAccessLogsRequest) (*pb.ListHTTPDNSAccessLogsResponse, error) {
|
func (s *HTTPDNSAccessLogService) ListHTTPDNSAccessLogs(ctx context.Context, req *pb.ListHTTPDNSAccessLogsRequest) (*pb.ListHTTPDNSAccessLogsResponse, error) {
|
||||||
_, _, err := s.ValidateAdminAndUser(ctx, true)
|
_, userId, err := s.ValidateAdminAndUser(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allowedAppIds := []string(nil)
|
||||||
|
if userId > 0 {
|
||||||
|
if len(strings.TrimSpace(req.GetAppId())) > 0 {
|
||||||
|
app, err := ensureAppAccessByAppId(s.NullTx(), req.GetAppId(), userId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if app == nil {
|
||||||
|
return &pb.ListHTTPDNSAccessLogsResponse{
|
||||||
|
Logs: []*pb.HTTPDNSAccessLog{},
|
||||||
|
Total: 0,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
allowedAppIds, err = models.SharedHTTPDNSAppDAO.ListEnabledAppIdsWithUser(s.NullTx(), userId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(allowedAppIds) == 0 {
|
||||||
|
return &pb.ListHTTPDNSAccessLogsResponse{
|
||||||
|
Logs: []*pb.HTTPDNSAccessLog{},
|
||||||
|
Total: 0,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
store := clickhouse.NewHTTPDNSAccessLogsStore()
|
store := clickhouse.NewHTTPDNSAccessLogsStore()
|
||||||
canReadFromClickHouse := s.shouldReadHTTPDNSAccessLogsFromClickHouse() && store.Client().IsConfigured()
|
canReadFromClickHouse := s.shouldReadHTTPDNSAccessLogsFromClickHouse() && store.Client().IsConfigured()
|
||||||
canReadFromMySQL := s.shouldReadHTTPDNSAccessLogsFromMySQL()
|
canReadFromMySQL := s.shouldReadHTTPDNSAccessLogsFromMySQL()
|
||||||
if canReadFromClickHouse {
|
if canReadFromClickHouse {
|
||||||
resp, listErr := s.listFromClickHouse(ctx, store, req)
|
resp, listErr := s.listFromClickHouse(ctx, store, req, allowedAppIds)
|
||||||
if listErr == nil {
|
if listErr == nil {
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
@@ -158,11 +186,11 @@ func (s *HTTPDNSAccessLogService) ListHTTPDNSAccessLogs(ctx context.Context, req
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
total, err := models.SharedHTTPDNSAccessLogDAO.CountLogs(s.NullTx(), req.GetDay(), req.GetClusterId(), req.GetNodeId(), req.GetAppId(), req.GetDomain(), req.GetStatus(), req.GetKeyword())
|
total, err := models.SharedHTTPDNSAccessLogDAO.CountLogsWithAppIds(s.NullTx(), req.GetDay(), req.GetClusterId(), req.GetNodeId(), req.GetAppId(), allowedAppIds, req.GetDomain(), req.GetStatus(), req.GetKeyword())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
logs, err := models.SharedHTTPDNSAccessLogDAO.ListLogs(s.NullTx(), req.GetDay(), req.GetClusterId(), req.GetNodeId(), req.GetAppId(), req.GetDomain(), req.GetStatus(), req.GetKeyword(), req.GetOffset(), req.GetSize())
|
logs, err := models.SharedHTTPDNSAccessLogDAO.ListLogsWithAppIds(s.NullTx(), req.GetDay(), req.GetClusterId(), req.GetNodeId(), req.GetAppId(), allowedAppIds, req.GetDomain(), req.GetStatus(), req.GetKeyword(), req.GetOffset(), req.GetSize())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -212,12 +240,13 @@ func (s *HTTPDNSAccessLogService) ListHTTPDNSAccessLogs(ctx context.Context, req
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HTTPDNSAccessLogService) listFromClickHouse(ctx context.Context, store *clickhouse.HTTPDNSAccessLogsStore, req *pb.ListHTTPDNSAccessLogsRequest) (*pb.ListHTTPDNSAccessLogsResponse, error) {
|
func (s *HTTPDNSAccessLogService) listFromClickHouse(ctx context.Context, store *clickhouse.HTTPDNSAccessLogsStore, req *pb.ListHTTPDNSAccessLogsRequest, allowedAppIds []string) (*pb.ListHTTPDNSAccessLogsResponse, error) {
|
||||||
filter := clickhouse.HTTPDNSAccessLogListFilter{
|
filter := clickhouse.HTTPDNSAccessLogListFilter{
|
||||||
Day: req.GetDay(),
|
Day: req.GetDay(),
|
||||||
ClusterId: req.GetClusterId(),
|
ClusterId: req.GetClusterId(),
|
||||||
NodeId: req.GetNodeId(),
|
NodeId: req.GetNodeId(),
|
||||||
AppId: req.GetAppId(),
|
AppId: req.GetAppId(),
|
||||||
|
AppIds: allowedAppIds,
|
||||||
Domain: req.GetDomain(),
|
Domain: req.GetDomain(),
|
||||||
Status: req.GetStatus(),
|
Status: req.GetStatus(),
|
||||||
Keyword: req.GetKeyword(),
|
Keyword: req.GetKeyword(),
|
||||||
|
|||||||
@@ -5,8 +5,11 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
|
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||||
"github.com/iwind/TeaGo/dbs"
|
"github.com/iwind/TeaGo/dbs"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||||
)
|
)
|
||||||
@@ -18,19 +21,44 @@ type HTTPDNSAppService struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSAppService) CreateHTTPDNSApp(ctx context.Context, req *pb.CreateHTTPDNSAppRequest) (*pb.CreateHTTPDNSAppResponse, error) {
|
func (this *HTTPDNSAppService) CreateHTTPDNSApp(ctx context.Context, req *pb.CreateHTTPDNSAppRequest) (*pb.CreateHTTPDNSAppResponse, error) {
|
||||||
_, err := this.ValidateAdmin(ctx)
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(req.Name) == 0 || len(req.AppId) == 0 {
|
if userId > 0 {
|
||||||
|
req.UserId = userId
|
||||||
|
}
|
||||||
|
appName := strings.TrimSpace(req.Name)
|
||||||
|
appId := strings.TrimSpace(req.AppId)
|
||||||
|
if len(appName) == 0 || len(appId) == 0 {
|
||||||
return nil, errors.New("required 'name' and 'appId'")
|
return nil, errors.New("required 'name' and 'appId'")
|
||||||
}
|
}
|
||||||
if req.PrimaryClusterId <= 0 {
|
|
||||||
return nil, errors.New("required 'primaryClusterId'")
|
|
||||||
}
|
|
||||||
var appDbId int64
|
var appDbId int64
|
||||||
|
now := time.Now().Unix()
|
||||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||||
exists, err := models.SharedHTTPDNSAppDAO.FindEnabledAppWithAppId(tx, strings.TrimSpace(req.AppId))
|
// 用户端防重复提交:短时间内同用户同应用名仅创建一次。
|
||||||
|
if req.UserId > 0 {
|
||||||
|
latest, err := models.SharedHTTPDNSAppDAO.FindLatestEnabledAppWithNameAndUser(tx, appName, req.UserId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if latest != nil && int64(latest.CreatedAt) >= now-5 {
|
||||||
|
appDbId = int64(latest.Id)
|
||||||
|
secret, err := models.SharedHTTPDNSAppSecretDAO.FindEnabledAppSecret(tx, appDbId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if secret == nil {
|
||||||
|
_, _, err = models.SharedHTTPDNSAppSecretDAO.InitAppSecret(tx, appDbId, req.SignEnabled)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exists, err := models.SharedHTTPDNSAppDAO.FindEnabledAppWithAppId(tx, appId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -38,7 +66,25 @@ func (this *HTTPDNSAppService) CreateHTTPDNSApp(ctx context.Context, req *pb.Cre
|
|||||||
return errors.New("appId already exists")
|
return errors.New("appId already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
appDbId, err = models.SharedHTTPDNSAppDAO.CreateApp(tx, req.Name, strings.TrimSpace(req.AppId), req.PrimaryClusterId, req.BackupClusterId, req.IsOn, req.UserId)
|
primaryClusterId := req.PrimaryClusterId
|
||||||
|
backupClusterId := req.BackupClusterId
|
||||||
|
if primaryClusterId <= 0 || backupClusterId <= 0 {
|
||||||
|
defaultPrimaryClusterId, defaultBackupClusterId, err := readHTTPDNSDefaultClusterIds(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if primaryClusterId <= 0 {
|
||||||
|
primaryClusterId = defaultPrimaryClusterId
|
||||||
|
}
|
||||||
|
if backupClusterId <= 0 {
|
||||||
|
backupClusterId = defaultBackupClusterId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if primaryClusterId > 0 && backupClusterId == primaryClusterId {
|
||||||
|
backupClusterId = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
appDbId, err = models.SharedHTTPDNSAppDAO.CreateApp(tx, appName, appId, primaryClusterId, backupClusterId, req.IsOn, req.UserId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -54,13 +100,53 @@ func (this *HTTPDNSAppService) CreateHTTPDNSApp(ctx context.Context, req *pb.Cre
|
|||||||
return &pb.CreateHTTPDNSAppResponse{AppDbId: appDbId}, nil
|
return &pb.CreateHTTPDNSAppResponse{AppDbId: appDbId}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readHTTPDNSDefaultClusterIds(tx *dbs.Tx) (primaryClusterId int64, backupClusterId int64, err error) {
|
||||||
|
primaryClusterId, err = models.SharedHTTPDNSClusterDAO.FindDefaultPrimaryClusterId(tx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
backupClusterId = 0
|
||||||
|
backupValueJSON, err := models.SharedSysSettingDAO.ReadSetting(tx, systemconfigs.SettingCodeHTTPDNSDefaultBackupClusterId)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
if len(backupValueJSON) > 0 {
|
||||||
|
backupClusterId = types.Int64(string(backupValueJSON))
|
||||||
|
}
|
||||||
|
if backupClusterId > 0 {
|
||||||
|
backupCluster, err := models.SharedHTTPDNSClusterDAO.FindEnabledCluster(tx, backupClusterId)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
if backupCluster == nil || !backupCluster.IsOn {
|
||||||
|
backupClusterId = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if primaryClusterId > 0 {
|
||||||
|
primaryCluster, err := models.SharedHTTPDNSClusterDAO.FindEnabledCluster(tx, primaryClusterId)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
if primaryCluster == nil || !primaryCluster.IsOn {
|
||||||
|
primaryClusterId = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if primaryClusterId > 0 && backupClusterId == primaryClusterId {
|
||||||
|
backupClusterId = 0
|
||||||
|
}
|
||||||
|
return primaryClusterId, backupClusterId, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSAppService) UpdateHTTPDNSApp(ctx context.Context, req *pb.UpdateHTTPDNSAppRequest) (*pb.RPCSuccess, error) {
|
func (this *HTTPDNSAppService) UpdateHTTPDNSApp(ctx context.Context, req *pb.UpdateHTTPDNSAppRequest) (*pb.RPCSuccess, error) {
|
||||||
_, err := this.ValidateAdmin(ctx)
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||||
oldApp, err := models.SharedHTTPDNSAppDAO.FindEnabledApp(tx, req.AppDbId)
|
oldApp, err := ensureAppAccess(tx, req.AppDbId, userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -68,7 +154,20 @@ func (this *HTTPDNSAppService) UpdateHTTPDNSApp(ctx context.Context, req *pb.Upd
|
|||||||
return errors.New("app not found")
|
return errors.New("app not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = models.SharedHTTPDNSAppDAO.UpdateApp(tx, req.AppDbId, req.Name, req.PrimaryClusterId, req.BackupClusterId, req.IsOn, req.UserId)
|
targetUserId := req.UserId
|
||||||
|
if targetUserId <= 0 {
|
||||||
|
targetUserId = oldApp.UserId
|
||||||
|
}
|
||||||
|
if userId > 0 {
|
||||||
|
targetUserId = userId
|
||||||
|
}
|
||||||
|
primaryClusterId := req.PrimaryClusterId
|
||||||
|
backupClusterId := req.BackupClusterId
|
||||||
|
if primaryClusterId > 0 && backupClusterId == primaryClusterId {
|
||||||
|
backupClusterId = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
err = models.SharedHTTPDNSAppDAO.UpdateApp(tx, req.AppDbId, req.Name, primaryClusterId, backupClusterId, req.IsOn, targetUserId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -86,12 +185,12 @@ func (this *HTTPDNSAppService) UpdateHTTPDNSApp(ctx context.Context, req *pb.Upd
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSAppService) DeleteHTTPDNSApp(ctx context.Context, req *pb.DeleteHTTPDNSAppRequest) (*pb.RPCSuccess, error) {
|
func (this *HTTPDNSAppService) DeleteHTTPDNSApp(ctx context.Context, req *pb.DeleteHTTPDNSAppRequest) (*pb.RPCSuccess, error) {
|
||||||
_, err := this.ValidateAdmin(ctx)
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||||
app, err := models.SharedHTTPDNSAppDAO.FindEnabledApp(tx, req.AppDbId)
|
app, err := ensureAppAccess(tx, req.AppDbId, userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -146,14 +245,17 @@ func (this *HTTPDNSAppService) DeleteHTTPDNSApp(ctx context.Context, req *pb.Del
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSAppService) FindHTTPDNSApp(ctx context.Context, req *pb.FindHTTPDNSAppRequest) (*pb.FindHTTPDNSAppResponse, error) {
|
func (this *HTTPDNSAppService) FindHTTPDNSApp(ctx context.Context, req *pb.FindHTTPDNSAppRequest) (*pb.FindHTTPDNSAppResponse, error) {
|
||||||
_, _, err := this.ValidateAdminAndUser(ctx, true)
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
app, err := models.SharedHTTPDNSAppDAO.FindEnabledApp(this.NullTx(), req.AppDbId)
|
app, err := ensureAppAccess(this.NullTx(), req.AppDbId, userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if app == nil {
|
||||||
|
return &pb.FindHTTPDNSAppResponse{}, nil
|
||||||
|
}
|
||||||
secret, err := models.SharedHTTPDNSAppSecretDAO.FindEnabledAppSecret(this.NullTx(), req.AppDbId)
|
secret, err := models.SharedHTTPDNSAppSecretDAO.FindEnabledAppSecret(this.NullTx(), req.AppDbId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -162,11 +264,16 @@ func (this *HTTPDNSAppService) FindHTTPDNSApp(ctx context.Context, req *pb.FindH
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSAppService) ListHTTPDNSApps(ctx context.Context, req *pb.ListHTTPDNSAppsRequest) (*pb.ListHTTPDNSAppsResponse, error) {
|
func (this *HTTPDNSAppService) ListHTTPDNSApps(ctx context.Context, req *pb.ListHTTPDNSAppsRequest) (*pb.ListHTTPDNSAppsResponse, error) {
|
||||||
_, _, err := this.ValidateAdminAndUser(ctx, true)
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
apps, err := models.SharedHTTPDNSAppDAO.ListEnabledApps(this.NullTx(), req.Offset, req.Size, req.Keyword)
|
var apps []*models.HTTPDNSApp
|
||||||
|
if userId > 0 {
|
||||||
|
apps, err = models.SharedHTTPDNSAppDAO.ListEnabledAppsWithUser(this.NullTx(), userId, req.Offset, req.Size, req.Keyword)
|
||||||
|
} else {
|
||||||
|
apps, err = models.SharedHTTPDNSAppDAO.ListEnabledApps(this.NullTx(), req.Offset, req.Size, req.Keyword)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -182,13 +289,19 @@ func (this *HTTPDNSAppService) ListHTTPDNSApps(ctx context.Context, req *pb.List
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSAppService) FindAllHTTPDNSApps(ctx context.Context, req *pb.FindAllHTTPDNSAppsRequest) (*pb.FindAllHTTPDNSAppsResponse, error) {
|
func (this *HTTPDNSAppService) FindAllHTTPDNSApps(ctx context.Context, req *pb.FindAllHTTPDNSAppsRequest) (*pb.FindAllHTTPDNSAppsResponse, error) {
|
||||||
_, _, validateErr := this.ValidateAdminAndUser(ctx, true)
|
_, userId, validateErr := this.ValidateAdminAndUser(ctx, true)
|
||||||
if validateErr != nil {
|
if validateErr != nil {
|
||||||
if _, nodeErr := this.ValidateHTTPDNSNode(ctx); nodeErr != nil {
|
if _, nodeErr := this.ValidateHTTPDNSNode(ctx); nodeErr != nil {
|
||||||
return nil, validateErr
|
return nil, validateErr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
apps, err := models.SharedHTTPDNSAppDAO.FindAllEnabledApps(this.NullTx())
|
var apps []*models.HTTPDNSApp
|
||||||
|
var err error
|
||||||
|
if validateErr == nil && userId > 0 {
|
||||||
|
apps, err = models.SharedHTTPDNSAppDAO.FindAllEnabledAppsWithUser(this.NullTx(), userId)
|
||||||
|
} else {
|
||||||
|
apps, err = models.SharedHTTPDNSAppDAO.FindAllEnabledApps(this.NullTx())
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -204,12 +317,20 @@ func (this *HTTPDNSAppService) FindAllHTTPDNSApps(ctx context.Context, req *pb.F
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSAppService) UpdateHTTPDNSAppSignEnabled(ctx context.Context, req *pb.UpdateHTTPDNSAppSignEnabledRequest) (*pb.RPCSuccess, error) {
|
func (this *HTTPDNSAppService) UpdateHTTPDNSAppSignEnabled(ctx context.Context, req *pb.UpdateHTTPDNSAppSignEnabledRequest) (*pb.RPCSuccess, error) {
|
||||||
_, err := this.ValidateAdmin(ctx)
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||||
err := models.SharedHTTPDNSAppSecretDAO.UpdateSignEnabled(tx, req.AppDbId, req.SignEnabled)
|
app, err := ensureAppAccess(tx, req.AppDbId, userId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if app == nil {
|
||||||
|
return errors.New("app not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = models.SharedHTTPDNSAppSecretDAO.UpdateSignEnabled(tx, req.AppDbId, req.SignEnabled)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -222,14 +343,21 @@ func (this *HTTPDNSAppService) UpdateHTTPDNSAppSignEnabled(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSAppService) ResetHTTPDNSAppSignSecret(ctx context.Context, req *pb.ResetHTTPDNSAppSignSecretRequest) (*pb.ResetHTTPDNSAppSignSecretResponse, error) {
|
func (this *HTTPDNSAppService) ResetHTTPDNSAppSignSecret(ctx context.Context, req *pb.ResetHTTPDNSAppSignSecretRequest) (*pb.ResetHTTPDNSAppSignSecretResponse, error) {
|
||||||
_, err := this.ValidateAdmin(ctx)
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var signSecret string
|
var signSecret string
|
||||||
var updatedAt int64
|
var updatedAt int64
|
||||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||||
var err error
|
app, err := ensureAppAccess(tx, req.AppDbId, userId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if app == nil {
|
||||||
|
return errors.New("app not found")
|
||||||
|
}
|
||||||
|
|
||||||
signSecret, updatedAt, err = models.SharedHTTPDNSAppSecretDAO.ResetSignSecret(tx, req.AppDbId)
|
signSecret, updatedAt, err = models.SharedHTTPDNSAppSecretDAO.ResetSignSecret(tx, req.AppDbId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ type HTTPDNSDomainService struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSDomainService) CreateHTTPDNSDomain(ctx context.Context, req *pb.CreateHTTPDNSDomainRequest) (*pb.CreateHTTPDNSDomainResponse, error) {
|
func (this *HTTPDNSDomainService) CreateHTTPDNSDomain(ctx context.Context, req *pb.CreateHTTPDNSDomainRequest) (*pb.CreateHTTPDNSDomainResponse, error) {
|
||||||
_, err := this.ValidateAdmin(ctx)
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,14 @@ func (this *HTTPDNSDomainService) CreateHTTPDNSDomain(ctx context.Context, req *
|
|||||||
}
|
}
|
||||||
var domainId int64
|
var domainId int64
|
||||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||||
|
app, err := ensureAppAccess(tx, req.AppDbId, userId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if app == nil {
|
||||||
|
return errors.New("app not found")
|
||||||
|
}
|
||||||
|
|
||||||
domainId, err = models.SharedHTTPDNSDomainDAO.CreateDomain(tx, req.AppDbId, req.Domain, req.IsOn)
|
domainId, err = models.SharedHTTPDNSDomainDAO.CreateDomain(tx, req.AppDbId, req.Domain, req.IsOn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -38,12 +46,12 @@ func (this *HTTPDNSDomainService) CreateHTTPDNSDomain(ctx context.Context, req *
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSDomainService) DeleteHTTPDNSDomain(ctx context.Context, req *pb.DeleteHTTPDNSDomainRequest) (*pb.RPCSuccess, error) {
|
func (this *HTTPDNSDomainService) DeleteHTTPDNSDomain(ctx context.Context, req *pb.DeleteHTTPDNSDomainRequest) (*pb.RPCSuccess, error) {
|
||||||
_, err := this.ValidateAdmin(ctx)
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||||
domain, err := models.SharedHTTPDNSDomainDAO.FindEnabledDomain(tx, req.DomainId)
|
domain, app, err := ensureDomainAccess(tx, req.DomainId, userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -55,7 +63,7 @@ func (this *HTTPDNSDomainService) DeleteHTTPDNSDomain(ctx context.Context, req *
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return notifyHTTPDNSAppTasksByAppDbId(tx, int64(domain.AppId), models.HTTPDNSNodeTaskTypeDomainChanged)
|
return notifyHTTPDNSAppTasksByAppDbId(tx, int64(app.Id), models.HTTPDNSNodeTaskTypeDomainChanged)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -64,12 +72,12 @@ func (this *HTTPDNSDomainService) DeleteHTTPDNSDomain(ctx context.Context, req *
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSDomainService) UpdateHTTPDNSDomainStatus(ctx context.Context, req *pb.UpdateHTTPDNSDomainStatusRequest) (*pb.RPCSuccess, error) {
|
func (this *HTTPDNSDomainService) UpdateHTTPDNSDomainStatus(ctx context.Context, req *pb.UpdateHTTPDNSDomainStatusRequest) (*pb.RPCSuccess, error) {
|
||||||
_, err := this.ValidateAdmin(ctx)
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||||
domain, err := models.SharedHTTPDNSDomainDAO.FindEnabledDomain(tx, req.DomainId)
|
domain, app, err := ensureDomainAccess(tx, req.DomainId, userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -81,7 +89,7 @@ func (this *HTTPDNSDomainService) UpdateHTTPDNSDomainStatus(ctx context.Context,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return notifyHTTPDNSAppTasksByAppDbId(tx, int64(domain.AppId), models.HTTPDNSNodeTaskTypeDomainChanged)
|
return notifyHTTPDNSAppTasksByAppDbId(tx, int64(app.Id), models.HTTPDNSNodeTaskTypeDomainChanged)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -90,11 +98,19 @@ func (this *HTTPDNSDomainService) UpdateHTTPDNSDomainStatus(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSDomainService) ListHTTPDNSDomainsWithAppId(ctx context.Context, req *pb.ListHTTPDNSDomainsWithAppIdRequest) (*pb.ListHTTPDNSDomainsWithAppIdResponse, error) {
|
func (this *HTTPDNSDomainService) ListHTTPDNSDomainsWithAppId(ctx context.Context, req *pb.ListHTTPDNSDomainsWithAppIdRequest) (*pb.ListHTTPDNSDomainsWithAppIdResponse, error) {
|
||||||
_, _, validateErr := this.ValidateAdminAndUser(ctx, true)
|
_, userId, validateErr := this.ValidateAdminAndUser(ctx, true)
|
||||||
if validateErr != nil {
|
if validateErr != nil {
|
||||||
if _, nodeErr := this.ValidateHTTPDNSNode(ctx); nodeErr != nil {
|
if _, nodeErr := this.ValidateHTTPDNSNode(ctx); nodeErr != nil {
|
||||||
return nil, validateErr
|
return nil, validateErr
|
||||||
}
|
}
|
||||||
|
} else if userId > 0 {
|
||||||
|
app, err := ensureAppAccess(this.NullTx(), req.AppDbId, userId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if app == nil {
|
||||||
|
return &pb.ListHTTPDNSDomainsWithAppIdResponse{}, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
domains, err := models.SharedHTTPDNSDomainDAO.ListEnabledDomainsWithAppId(this.NullTx(), req.AppDbId, req.Keyword)
|
domains, err := models.SharedHTTPDNSDomainDAO.ListEnabledDomainsWithAppId(this.NullTx(), req.AppDbId, req.Keyword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ type HTTPDNSRuleService struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSRuleService) CreateHTTPDNSCustomRule(ctx context.Context, req *pb.CreateHTTPDNSCustomRuleRequest) (*pb.CreateHTTPDNSCustomRuleResponse, error) {
|
func (this *HTTPDNSRuleService) CreateHTTPDNSCustomRule(ctx context.Context, req *pb.CreateHTTPDNSCustomRuleRequest) (*pb.CreateHTTPDNSCustomRuleResponse, error) {
|
||||||
_, err := this.ValidateAdmin(ctx)
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -25,8 +25,16 @@ func (this *HTTPDNSRuleService) CreateHTTPDNSCustomRule(ctx context.Context, req
|
|||||||
}
|
}
|
||||||
var ruleId int64
|
var ruleId int64
|
||||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||||
|
domain, app, err := ensureDomainAccess(tx, req.Rule.DomainId, userId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if domain == nil || app == nil {
|
||||||
|
return errors.New("domain not found")
|
||||||
|
}
|
||||||
|
|
||||||
rule := &models.HTTPDNSCustomRule{
|
rule := &models.HTTPDNSCustomRule{
|
||||||
AppId: uint32(req.Rule.AppId),
|
AppId: domain.AppId,
|
||||||
DomainId: uint32(req.Rule.DomainId),
|
DomainId: uint32(req.Rule.DomainId),
|
||||||
RuleName: req.Rule.RuleName,
|
RuleName: req.Rule.RuleName,
|
||||||
LineScope: req.Rule.LineScope,
|
LineScope: req.Rule.LineScope,
|
||||||
@@ -49,7 +57,7 @@ func (this *HTTPDNSRuleService) CreateHTTPDNSCustomRule(ctx context.Context, req
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return notifyHTTPDNSAppTasksByAppDbId(tx, req.Rule.AppId, models.HTTPDNSNodeTaskTypeRuleChanged)
|
return notifyHTTPDNSAppTasksByAppDbId(tx, int64(app.Id), models.HTTPDNSNodeTaskTypeRuleChanged)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -58,7 +66,7 @@ func (this *HTTPDNSRuleService) CreateHTTPDNSCustomRule(ctx context.Context, req
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSRuleService) UpdateHTTPDNSCustomRule(ctx context.Context, req *pb.UpdateHTTPDNSCustomRuleRequest) (*pb.RPCSuccess, error) {
|
func (this *HTTPDNSRuleService) UpdateHTTPDNSCustomRule(ctx context.Context, req *pb.UpdateHTTPDNSCustomRuleRequest) (*pb.RPCSuccess, error) {
|
||||||
_, err := this.ValidateAdmin(ctx)
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -66,7 +74,7 @@ func (this *HTTPDNSRuleService) UpdateHTTPDNSCustomRule(ctx context.Context, req
|
|||||||
return nil, errors.New("invalid 'rule.id'")
|
return nil, errors.New("invalid 'rule.id'")
|
||||||
}
|
}
|
||||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||||
oldRule, err := models.SharedHTTPDNSCustomRuleDAO.FindEnabledRule(tx, req.Rule.Id)
|
oldRule, app, err := ensureRuleAccess(tx, req.Rule.Id, userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -101,15 +109,12 @@ func (this *HTTPDNSRuleService) UpdateHTTPDNSCustomRule(ctx context.Context, req
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = notifyHTTPDNSAppTasksByAppDbId(tx, int64(oldRule.AppId), models.HTTPDNSNodeTaskTypeRuleChanged)
|
err = notifyHTTPDNSAppTasksByAppDbId(tx, int64(app.Id), models.HTTPDNSNodeTaskTypeRuleChanged)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
targetAppDbId := req.Rule.AppId
|
targetAppDbId := int64(app.Id)
|
||||||
if targetAppDbId <= 0 {
|
|
||||||
targetAppDbId = int64(oldRule.AppId)
|
|
||||||
}
|
|
||||||
return notifyHTTPDNSAppTasksByAppDbId(tx, targetAppDbId, models.HTTPDNSNodeTaskTypeRuleChanged)
|
return notifyHTTPDNSAppTasksByAppDbId(tx, targetAppDbId, models.HTTPDNSNodeTaskTypeRuleChanged)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -119,12 +124,12 @@ func (this *HTTPDNSRuleService) UpdateHTTPDNSCustomRule(ctx context.Context, req
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSRuleService) DeleteHTTPDNSCustomRule(ctx context.Context, req *pb.DeleteHTTPDNSCustomRuleRequest) (*pb.RPCSuccess, error) {
|
func (this *HTTPDNSRuleService) DeleteHTTPDNSCustomRule(ctx context.Context, req *pb.DeleteHTTPDNSCustomRuleRequest) (*pb.RPCSuccess, error) {
|
||||||
_, err := this.ValidateAdmin(ctx)
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||||
rule, err := models.SharedHTTPDNSCustomRuleDAO.FindEnabledRule(tx, req.RuleId)
|
rule, app, err := ensureRuleAccess(tx, req.RuleId, userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -136,7 +141,7 @@ func (this *HTTPDNSRuleService) DeleteHTTPDNSCustomRule(ctx context.Context, req
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return notifyHTTPDNSAppTasksByAppDbId(tx, int64(rule.AppId), models.HTTPDNSNodeTaskTypeRuleChanged)
|
return notifyHTTPDNSAppTasksByAppDbId(tx, int64(app.Id), models.HTTPDNSNodeTaskTypeRuleChanged)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -145,12 +150,12 @@ func (this *HTTPDNSRuleService) DeleteHTTPDNSCustomRule(ctx context.Context, req
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSRuleService) UpdateHTTPDNSCustomRuleStatus(ctx context.Context, req *pb.UpdateHTTPDNSCustomRuleStatusRequest) (*pb.RPCSuccess, error) {
|
func (this *HTTPDNSRuleService) UpdateHTTPDNSCustomRuleStatus(ctx context.Context, req *pb.UpdateHTTPDNSCustomRuleStatusRequest) (*pb.RPCSuccess, error) {
|
||||||
_, err := this.ValidateAdmin(ctx)
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = this.RunTx(func(tx *dbs.Tx) error {
|
err = this.RunTx(func(tx *dbs.Tx) error {
|
||||||
rule, err := models.SharedHTTPDNSCustomRuleDAO.FindEnabledRule(tx, req.RuleId)
|
rule, app, err := ensureRuleAccess(tx, req.RuleId, userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -162,7 +167,7 @@ func (this *HTTPDNSRuleService) UpdateHTTPDNSCustomRuleStatus(ctx context.Contex
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return notifyHTTPDNSAppTasksByAppDbId(tx, int64(rule.AppId), models.HTTPDNSNodeTaskTypeRuleChanged)
|
return notifyHTTPDNSAppTasksByAppDbId(tx, int64(app.Id), models.HTTPDNSNodeTaskTypeRuleChanged)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -171,11 +176,19 @@ func (this *HTTPDNSRuleService) UpdateHTTPDNSCustomRuleStatus(ctx context.Contex
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSRuleService) ListHTTPDNSCustomRulesWithDomainId(ctx context.Context, req *pb.ListHTTPDNSCustomRulesWithDomainIdRequest) (*pb.ListHTTPDNSCustomRulesWithDomainIdResponse, error) {
|
func (this *HTTPDNSRuleService) ListHTTPDNSCustomRulesWithDomainId(ctx context.Context, req *pb.ListHTTPDNSCustomRulesWithDomainIdRequest) (*pb.ListHTTPDNSCustomRulesWithDomainIdResponse, error) {
|
||||||
_, _, validateErr := this.ValidateAdminAndUser(ctx, true)
|
_, userId, validateErr := this.ValidateAdminAndUser(ctx, true)
|
||||||
if validateErr != nil {
|
if validateErr != nil {
|
||||||
if _, nodeErr := this.ValidateHTTPDNSNode(ctx); nodeErr != nil {
|
if _, nodeErr := this.ValidateHTTPDNSNode(ctx); nodeErr != nil {
|
||||||
return nil, validateErr
|
return nil, validateErr
|
||||||
}
|
}
|
||||||
|
} else if userId > 0 {
|
||||||
|
domain, _, err := ensureDomainAccess(this.NullTx(), req.DomainId, userId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if domain == nil {
|
||||||
|
return &pb.ListHTTPDNSCustomRulesWithDomainIdResponse{}, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rules, err := models.SharedHTTPDNSCustomRuleDAO.ListEnabledRulesWithDomainId(this.NullTx(), req.DomainId)
|
rules, err := models.SharedHTTPDNSCustomRuleDAO.ListEnabledRulesWithDomainId(this.NullTx(), req.DomainId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ type nodeClientInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPDNSSandboxService) TestHTTPDNSResolve(ctx context.Context, req *pb.TestHTTPDNSResolveRequest) (*pb.TestHTTPDNSResolveResponse, error) {
|
func (this *HTTPDNSSandboxService) TestHTTPDNSResolve(ctx context.Context, req *pb.TestHTTPDNSResolveRequest) (*pb.TestHTTPDNSResolveResponse, error) {
|
||||||
_, _, err := this.ValidateAdminAndUser(ctx, true)
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -73,6 +73,9 @@ func (this *HTTPDNSSandboxService) TestHTTPDNSResolve(ctx context.Context, req *
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if userId > 0 && app != nil && app.UserId != userId {
|
||||||
|
return nil, errors.New("access denied")
|
||||||
|
}
|
||||||
if app == nil || !app.IsOn {
|
if app == nil || !app.IsOn {
|
||||||
return &pb.TestHTTPDNSResolveResponse{
|
return &pb.TestHTTPDNSResolveResponse{
|
||||||
Code: "APP_NOT_FOUND_OR_DISABLED",
|
Code: "APP_NOT_FOUND_OR_DISABLED",
|
||||||
|
|||||||
81
EdgeAPI/internal/rpc/services/httpdns/user_auth_helpers.go
Normal file
81
EdgeAPI/internal/rpc/services/httpdns/user_auth_helpers.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package httpdns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||||
|
"github.com/iwind/TeaGo/dbs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ensureAppAccess(tx *dbs.Tx, appDbId int64, userId int64) (*models.HTTPDNSApp, error) {
|
||||||
|
app, err := models.SharedHTTPDNSAppDAO.FindEnabledApp(tx, appDbId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if app == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if userId > 0 && app.UserId != userId {
|
||||||
|
return nil, errors.New("access denied")
|
||||||
|
}
|
||||||
|
return app, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureAppAccessByAppId(tx *dbs.Tx, appId string, userId int64) (*models.HTTPDNSApp, error) {
|
||||||
|
appId = strings.TrimSpace(appId)
|
||||||
|
if len(appId) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
app, err := models.SharedHTTPDNSAppDAO.FindEnabledAppWithAppId(tx, appId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if app == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if userId > 0 && app.UserId != userId {
|
||||||
|
return nil, errors.New("access denied")
|
||||||
|
}
|
||||||
|
return app, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureDomainAccess(tx *dbs.Tx, domainId int64, userId int64) (*models.HTTPDNSDomain, *models.HTTPDNSApp, error) {
|
||||||
|
domain, err := models.SharedHTTPDNSDomainDAO.FindEnabledDomain(tx, domainId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if domain == nil {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
app, err := ensureAppAccess(tx, int64(domain.AppId), userId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if app == nil {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return domain, app, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureRuleAccess(tx *dbs.Tx, ruleId int64, userId int64) (*models.HTTPDNSCustomRule, *models.HTTPDNSApp, error) {
|
||||||
|
rule, err := models.SharedHTTPDNSCustomRuleDAO.FindEnabledRule(tx, ruleId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if rule == nil {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
app, err := ensureAppAccess(tx, int64(rule.AppId), userId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if app == nil {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return rule, app, nil
|
||||||
|
}
|
||||||
@@ -42,10 +42,10 @@ func EnsureClickHouseTables() error {
|
|||||||
firewall_rule_group_id UInt64 DEFAULT 0,
|
firewall_rule_group_id UInt64 DEFAULT 0,
|
||||||
firewall_rule_set_id UInt64 DEFAULT 0,
|
firewall_rule_set_id UInt64 DEFAULT 0,
|
||||||
firewall_rule_id UInt64 DEFAULT 0,
|
firewall_rule_id UInt64 DEFAULT 0,
|
||||||
request_headers String CODEC(ZSTD(3)) DEFAULT '',
|
request_headers String DEFAULT '' CODEC(ZSTD(3)),
|
||||||
request_body String CODEC(ZSTD(3)) DEFAULT '',
|
request_body String DEFAULT '' CODEC(ZSTD(3)),
|
||||||
response_headers String CODEC(ZSTD(3)) DEFAULT '',
|
response_headers String DEFAULT '' CODEC(ZSTD(3)),
|
||||||
response_body String CODEC(ZSTD(3)) DEFAULT '',
|
response_body String DEFAULT '' CODEC(ZSTD(3)),
|
||||||
INDEX idx_trace_id trace_id TYPE bloom_filter(0.01) GRANULARITY 4,
|
INDEX idx_trace_id trace_id TYPE bloom_filter(0.01) GRANULARITY 4,
|
||||||
INDEX idx_ip ip TYPE bloom_filter(0.01) GRANULARITY 4,
|
INDEX idx_ip ip TYPE bloom_filter(0.01) GRANULARITY 4,
|
||||||
INDEX idx_host host TYPE tokenbf_v1(10240, 3, 0) GRANULARITY 4,
|
INDEX idx_host host TYPE tokenbf_v1(10240, 3, 0) GRANULARITY 4,
|
||||||
@@ -74,7 +74,7 @@ SETTINGS index_granularity = 8192`,
|
|||||||
is_recursive UInt8,
|
is_recursive UInt8,
|
||||||
error String CODEC(ZSTD(1)),
|
error String CODEC(ZSTD(1)),
|
||||||
ns_route_codes Array(String),
|
ns_route_codes Array(String),
|
||||||
content_json String CODEC(ZSTD(3)) DEFAULT '',
|
content_json String DEFAULT '' CODEC(ZSTD(3)),
|
||||||
INDEX idx_request_id request_id TYPE bloom_filter(0.01) GRANULARITY 4,
|
INDEX idx_request_id request_id TYPE bloom_filter(0.01) GRANULARITY 4,
|
||||||
INDEX idx_remote_addr remote_addr TYPE bloom_filter(0.01) GRANULARITY 4,
|
INDEX idx_remote_addr remote_addr TYPE bloom_filter(0.01) GRANULARITY 4,
|
||||||
INDEX idx_question_name question_name TYPE tokenbf_v1(10240, 3, 0) GRANULARITY 4,
|
INDEX idx_question_name question_name TYPE tokenbf_v1(10240, 3, 0) GRANULARITY 4,
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/httpdnsutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/httpdnsutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||||
"github.com/iwind/TeaGo/actions"
|
"github.com/iwind/TeaGo/actions"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AppSettingsAction struct {
|
type AppSettingsAction struct {
|
||||||
@@ -52,30 +54,83 @@ func (this *AppSettingsAction) RunGet(params struct {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clusterResp, err := this.RPC().HTTPDNSClusterRPC().FindAllHTTPDNSClusters(this.AdminContext(), &pb.FindAllHTTPDNSClustersRequest{})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
clusters := make([]maps.Map, 0, len(clusterResp.GetClusters()))
|
||||||
|
clusterDomainMap := map[int64]string{}
|
||||||
|
clusterNameMap := map[int64]string{}
|
||||||
|
defaultPrimaryClusterId := int64(0)
|
||||||
|
for _, cluster := range clusterResp.GetClusters() {
|
||||||
|
clusterId := cluster.GetId()
|
||||||
|
clusterName := cluster.GetName()
|
||||||
|
clusters = append(clusters, maps.Map{
|
||||||
|
"id": clusterId,
|
||||||
|
"name": clusterName,
|
||||||
|
"serviceDomain": cluster.GetServiceDomain(),
|
||||||
|
"isDefault": cluster.GetIsDefault(),
|
||||||
|
})
|
||||||
|
clusterDomainMap[clusterId] = cluster.GetServiceDomain()
|
||||||
|
clusterNameMap[clusterId] = clusterName
|
||||||
|
if defaultPrimaryClusterId <= 0 && cluster.GetIsDefault() {
|
||||||
|
defaultPrimaryClusterId = clusterId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultBackupClusterId := int64(0)
|
||||||
|
defaultBackupResp, err := this.RPC().SysSettingRPC().ReadSysSetting(this.AdminContext(), &pb.ReadSysSettingRequest{
|
||||||
|
Code: string(systemconfigs.SettingCodeHTTPDNSDefaultBackupClusterId),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if defaultBackupResp != nil && len(defaultBackupResp.GetValueJSON()) > 0 {
|
||||||
|
defaultBackupClusterId = types.Int64(string(defaultBackupResp.GetValueJSON()))
|
||||||
|
}
|
||||||
|
|
||||||
|
primaryClusterId := app.GetInt64("primaryClusterId")
|
||||||
|
backupClusterId := app.GetInt64("backupClusterId")
|
||||||
|
|
||||||
settings := maps.Map{
|
settings := maps.Map{
|
||||||
"appId": app.GetString("appId"),
|
"appId": app.GetString("appId"),
|
||||||
"appStatus": app.GetBool("isOn"),
|
"appStatus": app.GetBool("isOn"),
|
||||||
"primaryClusterId": app.GetInt64("primaryClusterId"),
|
"primaryClusterId": primaryClusterId,
|
||||||
"backupClusterId": app.GetInt64("backupClusterId"),
|
"backupClusterId": backupClusterId,
|
||||||
"signEnabled": app.GetBool("signEnabled"),
|
"defaultPrimaryClusterId": defaultPrimaryClusterId,
|
||||||
"signSecretPlain": app.GetString("signSecretPlain"),
|
"defaultPrimaryClusterName": clusterNameMap[defaultPrimaryClusterId],
|
||||||
"signSecretMasked": app.GetString("signSecretMasked"),
|
"defaultBackupClusterId": defaultBackupClusterId,
|
||||||
"signSecretUpdatedAt": app.GetString("signSecretUpdated"),
|
"defaultBackupClusterName": clusterNameMap[defaultBackupClusterId],
|
||||||
|
"primaryServiceDomain": clusterDomainMap[primaryClusterId],
|
||||||
|
"backupServiceDomain": clusterDomainMap[backupClusterId],
|
||||||
|
"signEnabled": app.GetBool("signEnabled"),
|
||||||
|
"signSecretPlain": app.GetString("signSecretPlain"),
|
||||||
|
"signSecretMasked": app.GetString("signSecretMasked"),
|
||||||
|
"signSecretUpdatedAt": app.GetString("signSecretUpdated"),
|
||||||
}
|
}
|
||||||
this.Data["app"] = app
|
this.Data["app"] = app
|
||||||
this.Data["settings"] = settings
|
this.Data["settings"] = settings
|
||||||
|
this.Data["clusters"] = clusters
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *AppSettingsAction) RunPost(params struct {
|
func (this *AppSettingsAction) RunPost(params struct {
|
||||||
AppId int64
|
AppId int64
|
||||||
|
|
||||||
AppStatus bool
|
AppStatus bool
|
||||||
|
PrimaryClusterId int64
|
||||||
|
BackupClusterId int64
|
||||||
|
|
||||||
Must *actions.Must
|
Must *actions.Must
|
||||||
CSRF *actionutils.CSRF
|
CSRF *actionutils.CSRF
|
||||||
}) {
|
}) {
|
||||||
params.Must.Field("appId", params.AppId).Gt(0, "请选择应用")
|
params.Must.Field("appId", params.AppId).Gt(0, "请选择应用")
|
||||||
|
if params.PrimaryClusterId > 0 && params.BackupClusterId > 0 && params.PrimaryClusterId == params.BackupClusterId {
|
||||||
|
this.FailField("backupClusterId", "备用集群不能与主集群相同")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
appResp, err := this.RPC().HTTPDNSAppRPC().FindHTTPDNSApp(this.AdminContext(), &pb.FindHTTPDNSAppRequest{
|
appResp, err := this.RPC().HTTPDNSAppRPC().FindHTTPDNSApp(this.AdminContext(), &pb.FindHTTPDNSAppRequest{
|
||||||
AppDbId: params.AppId,
|
AppDbId: params.AppId,
|
||||||
@@ -92,8 +147,8 @@ func (this *AppSettingsAction) RunPost(params struct {
|
|||||||
_, err = this.RPC().HTTPDNSAppRPC().UpdateHTTPDNSApp(this.AdminContext(), &pb.UpdateHTTPDNSAppRequest{
|
_, err = this.RPC().HTTPDNSAppRPC().UpdateHTTPDNSApp(this.AdminContext(), &pb.UpdateHTTPDNSAppRequest{
|
||||||
AppDbId: params.AppId,
|
AppDbId: params.AppId,
|
||||||
Name: appResp.GetApp().GetName(),
|
Name: appResp.GetApp().GetName(),
|
||||||
PrimaryClusterId: appResp.GetApp().GetPrimaryClusterId(),
|
PrimaryClusterId: params.PrimaryClusterId,
|
||||||
BackupClusterId: appResp.GetApp().GetBackupClusterId(),
|
BackupClusterId: params.BackupClusterId,
|
||||||
IsOn: params.AppStatus,
|
IsOn: params.AppStatus,
|
||||||
UserId: appResp.GetApp().GetUserId(),
|
UserId: appResp.GetApp().GetUserId(),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -55,6 +55,24 @@ func (this *CreateAction) RunGet(params struct{}) {
|
|||||||
}
|
}
|
||||||
this.Data["defaultBackupClusterId"] = defaultBackupClusterId
|
this.Data["defaultBackupClusterId"] = defaultBackupClusterId
|
||||||
|
|
||||||
|
usersResp, err := this.RPC().UserRPC().ListEnabledUsers(this.AdminContext(), &pb.ListEnabledUsersRequest{
|
||||||
|
Offset: 0,
|
||||||
|
Size: 10_000,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
users := make([]maps.Map, 0, len(usersResp.GetUsers()))
|
||||||
|
for _, user := range usersResp.GetUsers() {
|
||||||
|
users = append(users, maps.Map{
|
||||||
|
"id": user.GetId(),
|
||||||
|
"fullname": user.GetFullname(),
|
||||||
|
"username": user.GetUsername(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["users"] = users
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ func init() {
|
|||||||
Get("/sdk", new(SdkAction)).
|
Get("/sdk", new(SdkAction)).
|
||||||
GetPost("/sdk/upload", new(SdkUploadAction)).
|
GetPost("/sdk/upload", new(SdkUploadAction)).
|
||||||
Post("/sdk/upload/delete", new(SdkUploadDeleteAction)).
|
Post("/sdk/upload/delete", new(SdkUploadDeleteAction)).
|
||||||
|
Get("/sdk/check", new(SdkCheckAction)).
|
||||||
Get("/sdk/download", new(SdkDownloadAction)).
|
Get("/sdk/download", new(SdkDownloadAction)).
|
||||||
Get("/sdk/doc", new(SdkDocAction)).
|
Get("/sdk/doc", new(SdkDocAction)).
|
||||||
GetPost("/app/settings", new(AppSettingsAction)).
|
GetPost("/app/settings", new(AppSettingsAction)).
|
||||||
|
|||||||
@@ -7,11 +7,20 @@ import (
|
|||||||
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func listAppMaps(parent *actionutils.ParentAction, keyword string) ([]maps.Map, error) {
|
func listAppMaps(parent *actionutils.ParentAction, keyword string) ([]maps.Map, error) {
|
||||||
|
clusterNameMap, err := loadHTTPDNSClusterNameMap(parent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
userMapByID, err := loadHTTPDNSUserMap(parent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := parent.RPC().HTTPDNSAppRPC().ListHTTPDNSApps(parent.AdminContext(), &pb.ListHTTPDNSAppsRequest{
|
resp, err := parent.RPC().HTTPDNSAppRPC().ListHTTPDNSApps(parent.AdminContext(), &pb.ListHTTPDNSAppsRequest{
|
||||||
Offset: 0,
|
Offset: 0,
|
||||||
Size: 10_000,
|
Size: 10_000,
|
||||||
@@ -30,13 +39,22 @@ func listAppMaps(parent *actionutils.ParentAction, keyword string) ([]maps.Map,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
result = append(result, appPBToMap(app, int64(len(domainResp.GetDomains()))))
|
result = append(result, appPBToMap(app, int64(len(domainResp.GetDomains())), clusterNameMap, userMapByID))
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func findAppMap(parent *actionutils.ParentAction, appDbId int64) (maps.Map, error) {
|
func findAppMap(parent *actionutils.ParentAction, appDbId int64) (maps.Map, error) {
|
||||||
|
clusterNameMap, err := loadHTTPDNSClusterNameMap(parent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
userMapByID, err := loadHTTPDNSUserMap(parent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if appDbId > 0 {
|
if appDbId > 0 {
|
||||||
resp, err := parent.RPC().HTTPDNSAppRPC().FindHTTPDNSApp(parent.AdminContext(), &pb.FindHTTPDNSAppRequest{
|
resp, err := parent.RPC().HTTPDNSAppRPC().FindHTTPDNSApp(parent.AdminContext(), &pb.FindHTTPDNSAppRequest{
|
||||||
AppDbId: appDbId,
|
AppDbId: appDbId,
|
||||||
@@ -51,7 +69,7 @@ func findAppMap(parent *actionutils.ParentAction, appDbId int64) (maps.Map, erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return appPBToMap(resp.GetApp(), int64(len(domainResp.GetDomains()))), nil
|
return appPBToMap(resp.GetApp(), int64(len(domainResp.GetDomains())), clusterNameMap, userMapByID), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,16 +277,37 @@ func toggleCustomRule(parent *actionutils.ParentAction, ruleId int64, isOn bool)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func appPBToMap(app *pb.HTTPDNSApp, domainCount int64) maps.Map {
|
func appPBToMap(app *pb.HTTPDNSApp, domainCount int64, clusterNameMap map[int64]string, userMapByID map[int64]maps.Map) maps.Map {
|
||||||
signSecret := app.GetSignSecret()
|
signSecret := app.GetSignSecret()
|
||||||
|
|
||||||
|
primaryClusterID := app.GetPrimaryClusterId()
|
||||||
|
backupClusterID := app.GetBackupClusterId()
|
||||||
|
primaryClusterMap := maps.Map{"id": primaryClusterID, "name": clusterNameMap[primaryClusterID]}
|
||||||
|
backupClusterMap := maps.Map{"id": backupClusterID, "name": clusterNameMap[backupClusterID]}
|
||||||
|
|
||||||
|
var userMap maps.Map
|
||||||
|
if app.GetUserId() > 0 {
|
||||||
|
userMap = userMapByID[app.GetUserId()]
|
||||||
|
if userMap == nil {
|
||||||
|
userMap = maps.Map{
|
||||||
|
"id": app.GetUserId(),
|
||||||
|
"fullname": "用户#" + strconv.FormatInt(app.GetUserId(), 10),
|
||||||
|
"username": "-",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return maps.Map{
|
return maps.Map{
|
||||||
"id": app.GetId(),
|
"id": app.GetId(),
|
||||||
"name": app.GetName(),
|
"name": app.GetName(),
|
||||||
"appId": app.GetAppId(),
|
"appId": app.GetAppId(),
|
||||||
"clusterId": app.GetPrimaryClusterId(),
|
"clusterId": primaryClusterID,
|
||||||
"primaryClusterId": app.GetPrimaryClusterId(),
|
"primaryClusterId": primaryClusterID,
|
||||||
"backupClusterId": app.GetBackupClusterId(),
|
"backupClusterId": backupClusterID,
|
||||||
|
"primaryCluster": primaryClusterMap,
|
||||||
|
"backupCluster": backupClusterMap,
|
||||||
"userId": app.GetUserId(),
|
"userId": app.GetUserId(),
|
||||||
|
"user": userMap,
|
||||||
"isOn": app.GetIsOn(),
|
"isOn": app.GetIsOn(),
|
||||||
"domainCount": domainCount,
|
"domainCount": domainCount,
|
||||||
"sniPolicyText": "隐匿 SNI",
|
"sniPolicyText": "隐匿 SNI",
|
||||||
@@ -279,6 +318,39 @@ func appPBToMap(app *pb.HTTPDNSApp, domainCount int64) maps.Map {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadHTTPDNSClusterNameMap(parent *actionutils.ParentAction) (map[int64]string, error) {
|
||||||
|
resp, err := parent.RPC().HTTPDNSClusterRPC().FindAllHTTPDNSClusters(parent.AdminContext(), &pb.FindAllHTTPDNSClustersRequest{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := map[int64]string{}
|
||||||
|
for _, cluster := range resp.GetClusters() {
|
||||||
|
result[cluster.GetId()] = cluster.GetName()
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadHTTPDNSUserMap(parent *actionutils.ParentAction) (map[int64]maps.Map, error) {
|
||||||
|
resp, err := parent.RPC().UserRPC().ListEnabledUsers(parent.AdminContext(), &pb.ListEnabledUsersRequest{
|
||||||
|
Offset: 0,
|
||||||
|
Size: 10_000,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := map[int64]maps.Map{}
|
||||||
|
for _, user := range resp.GetUsers() {
|
||||||
|
result[user.GetId()] = maps.Map{
|
||||||
|
"id": user.GetId(),
|
||||||
|
"fullname": user.GetFullname(),
|
||||||
|
"username": user.GetUsername(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func defaultLineField(value string) string {
|
func defaultLineField(value string) string {
|
||||||
value = strings.TrimSpace(value)
|
value = strings.TrimSpace(value)
|
||||||
if len(value) == 0 {
|
if len(value) == 0 {
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package apps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SdkCheckAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *SdkCheckAction) Init() {
|
||||||
|
this.Nav("", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *SdkCheckAction) RunGet(params struct {
|
||||||
|
Platform string
|
||||||
|
Version string
|
||||||
|
Type string
|
||||||
|
}) {
|
||||||
|
platform, _, _, filename, err := resolveSDKPlatform(params.Platform)
|
||||||
|
if err != nil {
|
||||||
|
this.Data["exists"] = false
|
||||||
|
this.Data["message"] = err.Error()
|
||||||
|
this.Success()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t := strings.ToLower(strings.TrimSpace(params.Type))
|
||||||
|
if t == "doc" {
|
||||||
|
docPath := findUploadedSDKDocPath(platform, params.Version)
|
||||||
|
if len(docPath) == 0 {
|
||||||
|
this.Data["exists"] = false
|
||||||
|
this.Data["message"] = "Documentation is unavailable, please upload first"
|
||||||
|
this.Success()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadURL := "/httpdns/apps/sdk/doc?platform=" + url.QueryEscape(platform)
|
||||||
|
if len(strings.TrimSpace(params.Version)) > 0 {
|
||||||
|
downloadURL += "&version=" + url.QueryEscape(strings.TrimSpace(params.Version))
|
||||||
|
}
|
||||||
|
this.Data["exists"] = true
|
||||||
|
this.Data["url"] = downloadURL
|
||||||
|
this.Success()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
archivePath := findSDKArchivePath(filename, params.Version)
|
||||||
|
if len(archivePath) == 0 {
|
||||||
|
this.Data["exists"] = false
|
||||||
|
this.Data["message"] = "SDK package is unavailable, please upload first"
|
||||||
|
this.Success()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadURL := "/httpdns/apps/sdk/download?platform=" + url.QueryEscape(platform)
|
||||||
|
if len(strings.TrimSpace(params.Version)) > 0 {
|
||||||
|
downloadURL += "&version=" + url.QueryEscape(strings.TrimSpace(params.Version))
|
||||||
|
}
|
||||||
|
downloadURL += "&raw=1"
|
||||||
|
this.Data["exists"] = true
|
||||||
|
this.Data["url"] = downloadURL
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
package apps
|
package apps
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SdkDocAction struct {
|
type SdkDocAction struct {
|
||||||
@@ -17,15 +18,18 @@ func (this *SdkDocAction) Init() {
|
|||||||
|
|
||||||
func (this *SdkDocAction) RunGet(params struct {
|
func (this *SdkDocAction) RunGet(params struct {
|
||||||
Platform string
|
Platform string
|
||||||
|
Version string
|
||||||
}) {
|
}) {
|
||||||
platform, _, readmeRelativePath, _, err := resolveSDKPlatform(params.Platform)
|
platform, _, readmeRelativePath, _, err := resolveSDKPlatform(params.Platform)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.Fail(err.Error())
|
this.Data["exists"] = false
|
||||||
|
this.Data["message"] = err.Error()
|
||||||
|
this.Success()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var data []byte
|
var data []byte
|
||||||
uploadedDocPath := findUploadedSDKDocPath(platform)
|
uploadedDocPath := findUploadedSDKDocPath(platform, params.Version)
|
||||||
if len(uploadedDocPath) > 0 {
|
if len(uploadedDocPath) > 0 {
|
||||||
data, err = os.ReadFile(uploadedDocPath)
|
data, err = os.ReadFile(uploadedDocPath)
|
||||||
}
|
}
|
||||||
@@ -44,11 +48,18 @@ func (this *SdkDocAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(data) == 0 || err != nil {
|
if len(data) == 0 || err != nil {
|
||||||
this.Fail("当前服务器未找到 SDK 集成文档,请先在“SDK 集成”页面上传对应平台文档")
|
this.Data["exists"] = false
|
||||||
|
this.Data["message"] = "SDK documentation is not found on server, please upload first"
|
||||||
|
this.Success()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
downloadName := filepath.Base(uploadedDocPath)
|
||||||
|
if len(downloadName) == 0 || downloadName == "." || downloadName == string(filepath.Separator) {
|
||||||
|
downloadName = "httpdns-sdk-" + strings.ToLower(platform) + ".md"
|
||||||
|
}
|
||||||
|
|
||||||
this.AddHeader("Content-Type", "text/markdown; charset=utf-8")
|
this.AddHeader("Content-Type", "text/markdown; charset=utf-8")
|
||||||
this.AddHeader("Content-Disposition", "attachment; filename=\"httpdns-sdk-"+strings.ToLower(platform)+"-README.md\";")
|
this.AddHeader("Content-Disposition", "attachment; filename=\""+downloadName+"\";")
|
||||||
_, _ = this.ResponseWriter.Write(data)
|
_, _ = this.ResponseWriter.Write(data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package apps
|
package apps
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SdkDownloadAction struct {
|
type SdkDownloadAction struct {
|
||||||
@@ -16,30 +18,48 @@ func (this *SdkDownloadAction) Init() {
|
|||||||
|
|
||||||
func (this *SdkDownloadAction) RunGet(params struct {
|
func (this *SdkDownloadAction) RunGet(params struct {
|
||||||
Platform string
|
Platform string
|
||||||
|
Version string
|
||||||
|
Raw int
|
||||||
}) {
|
}) {
|
||||||
_, _, _, filename, err := resolveSDKPlatform(params.Platform)
|
_, _, _, filename, err := resolveSDKPlatform(params.Platform)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.Fail(err.Error())
|
this.Data["exists"] = false
|
||||||
|
this.Data["message"] = err.Error()
|
||||||
|
this.Success()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
archivePath := findSDKArchivePath(filename)
|
archivePath := findSDKArchivePath(filename, params.Version)
|
||||||
if len(archivePath) == 0 {
|
if len(archivePath) == 0 {
|
||||||
this.Fail("当前服务器未找到 SDK 包,请先在“SDK 集成”页面上传对应平台包: " + filename)
|
this.Data["exists"] = false
|
||||||
|
this.Data["message"] = "SDK archive not found on server, please upload first: " + filename
|
||||||
|
this.Success()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fp, err := os.Open(archivePath)
|
fp, err := os.Open(archivePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.Fail("打开 SDK 包失败: " + err.Error())
|
this.Data["exists"] = false
|
||||||
|
this.Data["message"] = "failed to open SDK archive: " + err.Error()
|
||||||
|
this.Success()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = fp.Close()
|
_ = fp.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
this.AddHeader("Content-Type", "application/zip")
|
downloadName := filepath.Base(archivePath)
|
||||||
this.AddHeader("Content-Disposition", "attachment; filename=\""+filename+"\";")
|
if len(downloadName) == 0 || downloadName == "." || downloadName == string(filepath.Separator) {
|
||||||
|
downloadName = filename
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.Raw == 1 {
|
||||||
|
this.AddHeader("Content-Type", "application/octet-stream")
|
||||||
|
this.AddHeader("X-SDK-Filename", downloadName)
|
||||||
|
} else {
|
||||||
|
this.AddHeader("Content-Type", "application/zip")
|
||||||
|
this.AddHeader("Content-Disposition", "attachment; filename=\""+downloadName+"\";")
|
||||||
|
}
|
||||||
this.AddHeader("X-Accel-Buffering", "no")
|
this.AddHeader("X-Accel-Buffering", "no")
|
||||||
_, _ = io.Copy(this.ResponseWriter, fp)
|
_, _ = io.Copy(this.ResponseWriter, fp)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,18 +2,58 @@ package apps
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/iwind/TeaGo/Tea"
|
||||||
)
|
)
|
||||||
|
|
||||||
func sdkUploadDir() string {
|
func sdkUploadDir() string {
|
||||||
|
dirs := sdkUploadDirs()
|
||||||
|
if len(dirs) > 0 {
|
||||||
|
return dirs[0]
|
||||||
|
}
|
||||||
return filepath.Clean(Tea.Root + "/data/httpdns/sdk")
|
return filepath.Clean(Tea.Root + "/data/httpdns/sdk")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sdkUploadDirs() []string {
|
||||||
|
candidates := []string{
|
||||||
|
filepath.Clean(Tea.Root + "/../data/httpdns/sdk"),
|
||||||
|
filepath.Clean(Tea.Root + "/data/httpdns/sdk"),
|
||||||
|
filepath.Clean(Tea.Root + "/../edge-admin/data/httpdns/sdk"),
|
||||||
|
filepath.Clean(Tea.Root + "/../edge-user/data/httpdns/sdk"),
|
||||||
|
filepath.Clean(Tea.Root + "/../../data/httpdns/sdk"),
|
||||||
|
}
|
||||||
|
results := make([]string, 0, len(candidates))
|
||||||
|
seen := map[string]bool{}
|
||||||
|
for _, dir := range candidates {
|
||||||
|
if len(dir) == 0 || seen[dir] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[dir] = true
|
||||||
|
results = append(results, dir)
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
func sdkUploadSearchDirs() []string {
|
||||||
|
dirs := sdkUploadDirs()
|
||||||
|
results := make([]string, 0, len(dirs))
|
||||||
|
for _, dir := range dirs {
|
||||||
|
stat, err := os.Stat(dir)
|
||||||
|
if err == nil && stat.IsDir() {
|
||||||
|
results = append(results, dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(results) == 0 {
|
||||||
|
results = append(results, sdkUploadDir())
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
func findFirstExistingDir(paths []string) string {
|
func findFirstExistingDir(paths []string) string {
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
stat, err := os.Stat(path)
|
stat, err := os.Stat(path)
|
||||||
@@ -27,7 +67,7 @@ func findFirstExistingDir(paths []string) string {
|
|||||||
func findFirstExistingFile(paths []string) string {
|
func findFirstExistingFile(paths []string) string {
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
stat, err := os.Stat(path)
|
stat, err := os.Stat(path)
|
||||||
if err == nil && !stat.IsDir() {
|
if err == nil && !stat.IsDir() && stat.Size() > 0 {
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,6 +85,9 @@ func findNewestExistingFile(paths []string) string {
|
|||||||
if err != nil || stat.IsDir() {
|
if err != nil || stat.IsDir() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if stat.Size() <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if len(result.path) == 0 || stat.ModTime().After(result.modTime) || (stat.ModTime().Equal(result.modTime) && path > result.path) {
|
if len(result.path) == 0 || stat.ModTime().After(result.modTime) || (stat.ModTime().Equal(result.modTime) && path > result.path) {
|
||||||
result.path = path
|
result.path = path
|
||||||
result.modTime = stat.ModTime()
|
result.modTime = stat.ModTime()
|
||||||
@@ -85,22 +128,23 @@ func resolveSDKPlatform(platform string) (key string, relativeDir string, readme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func findSDKArchivePath(downloadFilename string) string {
|
func findSDKArchivePath(downloadFilename string, version string) string {
|
||||||
searchDirs := []string{sdkUploadDir()}
|
searchDirs := sdkUploadSearchDirs()
|
||||||
|
|
||||||
// 1) Exact filename first.
|
normalizedVersion := strings.TrimSpace(version)
|
||||||
exactFiles := []string{}
|
|
||||||
for _, dir := range searchDirs {
|
|
||||||
exactFiles = append(exactFiles, filepath.Join(dir, downloadFilename))
|
|
||||||
}
|
|
||||||
path := findFirstExistingFile(exactFiles)
|
|
||||||
if len(path) > 0 {
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2) Version-suffixed archives, e.g. httpdns-sdk-android-v1.4.8.zip
|
|
||||||
base := strings.TrimSuffix(downloadFilename, ".zip")
|
base := strings.TrimSuffix(downloadFilename, ".zip")
|
||||||
patternName := base + "-*.zip"
|
if len(normalizedVersion) > 0 {
|
||||||
|
versionFiles := []string{}
|
||||||
|
for _, dir := range searchDirs {
|
||||||
|
versionFiles = append(versionFiles, filepath.Join(dir, base+"-v"+normalizedVersion+".zip"))
|
||||||
|
}
|
||||||
|
if path := findFirstExistingFile(versionFiles); len(path) > 0 {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
patternName := base + "-v*.zip"
|
||||||
matches := []string{}
|
matches := []string{}
|
||||||
for _, dir := range searchDirs {
|
for _, dir := range searchDirs {
|
||||||
found, _ := filepath.Glob(filepath.Join(dir, patternName))
|
found, _ := filepath.Glob(filepath.Join(dir, patternName))
|
||||||
@@ -115,28 +159,52 @@ func findSDKArchivePath(downloadFilename string) string {
|
|||||||
return findNewestExistingFile(matches)
|
return findNewestExistingFile(matches)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exactFiles := []string{}
|
||||||
|
for _, dir := range searchDirs {
|
||||||
|
exactFiles = append(exactFiles, filepath.Join(dir, downloadFilename))
|
||||||
|
}
|
||||||
|
if path := findFirstExistingFile(exactFiles); len(path) > 0 {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func findUploadedSDKDocPath(platform string) string {
|
func findUploadedSDKDocPath(platform string, version string) string {
|
||||||
platform = strings.ToLower(strings.TrimSpace(platform))
|
platform = strings.ToLower(strings.TrimSpace(platform))
|
||||||
if len(platform) == 0 {
|
if len(platform) == 0 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
searchDir := sdkUploadDir()
|
searchDirs := sdkUploadSearchDirs()
|
||||||
exact := filepath.Join(searchDir, "httpdns-sdk-"+platform+".md")
|
normalizedVersion := strings.TrimSpace(version)
|
||||||
if file := findFirstExistingFile([]string{exact}); len(file) > 0 {
|
if len(normalizedVersion) > 0 {
|
||||||
return file
|
exactVersion := []string{}
|
||||||
}
|
for _, dir := range searchDirs {
|
||||||
|
exactVersion = append(exactVersion, filepath.Join(dir, "httpdns-sdk-"+platform+"-v"+normalizedVersion+".md"))
|
||||||
pattern := filepath.Join(searchDir, "httpdns-sdk-"+platform+"-*.md")
|
}
|
||||||
matches, _ := filepath.Glob(pattern)
|
if file := findFirstExistingFile(exactVersion); len(file) > 0 {
|
||||||
if len(matches) == 0 {
|
return file
|
||||||
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
sort.Strings(matches)
|
|
||||||
return findNewestExistingFile(matches)
|
matches := []string{}
|
||||||
|
for _, dir := range searchDirs {
|
||||||
|
pattern := filepath.Join(dir, "httpdns-sdk-"+platform+"-v*.md")
|
||||||
|
found, _ := filepath.Glob(pattern)
|
||||||
|
matches = append(matches, found...)
|
||||||
|
}
|
||||||
|
if len(matches) > 0 {
|
||||||
|
sort.Strings(matches)
|
||||||
|
return findNewestExistingFile(matches)
|
||||||
|
}
|
||||||
|
|
||||||
|
exact := []string{}
|
||||||
|
for _, dir := range searchDirs {
|
||||||
|
exact = append(exact, filepath.Join(dir, "httpdns-sdk-"+platform+".md"))
|
||||||
|
}
|
||||||
|
return findFirstExistingFile(exact)
|
||||||
}
|
}
|
||||||
|
|
||||||
func findLocalSDKDocPath(platform string) string {
|
func findLocalSDKDocPath(platform string) string {
|
||||||
|
|||||||
@@ -2,15 +2,16 @@ package apps
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/httpdnsutils"
|
|
||||||
"github.com/iwind/TeaGo/actions"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/httpdnsutils"
|
||||||
|
"github.com/iwind/TeaGo/actions"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SdkUploadAction struct {
|
type SdkUploadAction struct {
|
||||||
@@ -44,7 +45,7 @@ func (this *SdkUploadAction) RunPost(params struct {
|
|||||||
AppId int64
|
AppId int64
|
||||||
Platform string
|
Platform string
|
||||||
Version string
|
Version string
|
||||||
SDKFile *actions.File
|
SdkFile *actions.File
|
||||||
DocFile *actions.File
|
DocFile *actions.File
|
||||||
|
|
||||||
Must *actions.Must
|
Must *actions.Must
|
||||||
@@ -63,7 +64,7 @@ func (this *SdkUploadAction) RunPost(params struct {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if params.SDKFile == nil && params.DocFile == nil {
|
if params.SdkFile == nil && params.DocFile == nil {
|
||||||
this.Fail("请至少上传一个文件")
|
this.Fail("请至少上传一个文件")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -75,24 +76,25 @@ func (this *SdkUploadAction) RunPost(params struct {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if params.SDKFile != nil {
|
if params.SdkFile != nil {
|
||||||
filename := strings.ToLower(strings.TrimSpace(params.SDKFile.Filename))
|
filename := strings.ToLower(strings.TrimSpace(params.SdkFile.Filename))
|
||||||
if !strings.HasSuffix(filename, ".zip") {
|
if !strings.HasSuffix(filename, ".zip") {
|
||||||
this.Fail("SDK 包仅支持 .zip 文件")
|
this.Fail("SDK 包仅支持 .zip 文件")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sdkData, readErr := params.SDKFile.Read()
|
sdkData, readErr := params.SdkFile.Read()
|
||||||
if readErr != nil {
|
if readErr != nil {
|
||||||
this.Fail("读取 SDK 包失败: " + readErr.Error())
|
this.Fail("读取 SDK 包失败: " + readErr.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if len(sdkData) == 0 {
|
||||||
|
this.Fail("SDK 包文件为空,请重新上传")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
baseName := strings.TrimSuffix(downloadFilename, ".zip")
|
baseName := strings.TrimSuffix(downloadFilename, ".zip")
|
||||||
err = saveSDKUploadFile(uploadDir, downloadFilename, sdkData)
|
err = saveSDKUploadFile(uploadDir, baseName+"-v"+version+".zip", sdkData)
|
||||||
if err == nil {
|
|
||||||
err = saveSDKUploadFile(uploadDir, baseName+"-v"+version+".zip", sdkData)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.Fail("保存 SDK 包失败: " + err.Error())
|
this.Fail("保存 SDK 包失败: " + err.Error())
|
||||||
return
|
return
|
||||||
@@ -111,12 +113,12 @@ func (this *SdkUploadAction) RunPost(params struct {
|
|||||||
this.Fail("读取集成文档失败: " + readErr.Error())
|
this.Fail("读取集成文档失败: " + readErr.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if len(docData) == 0 {
|
||||||
filename := "httpdns-sdk-" + platform + ".md"
|
this.Fail("集成文档文件为空,请重新上传")
|
||||||
err = saveSDKUploadFile(uploadDir, filename, docData)
|
return
|
||||||
if err == nil {
|
|
||||||
err = saveSDKUploadFile(uploadDir, "httpdns-sdk-"+platform+"-v"+version+".md", docData)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = saveSDKUploadFile(uploadDir, "httpdns-sdk-"+platform+"-v"+version+".md", docData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.Fail("保存集成文档失败: " + err.Error())
|
this.Fail("保存集成文档失败: " + err.Error())
|
||||||
return
|
return
|
||||||
@@ -151,12 +153,6 @@ func saveSDKUploadFile(baseDir string, filename string, data []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func listUploadedSDKFiles() []map[string]interface{} {
|
func listUploadedSDKFiles() []map[string]interface{} {
|
||||||
dir := sdkUploadDir()
|
|
||||||
entries, err := os.ReadDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
return []map[string]interface{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type item struct {
|
type item struct {
|
||||||
Name string
|
Name string
|
||||||
Platform string
|
Platform string
|
||||||
@@ -166,30 +162,45 @@ func listUploadedSDKFiles() []map[string]interface{} {
|
|||||||
UpdatedAt int64
|
UpdatedAt int64
|
||||||
}
|
}
|
||||||
|
|
||||||
items := make([]item, 0)
|
byName := map[string]item{}
|
||||||
for _, entry := range entries {
|
for _, dir := range sdkUploadSearchDirs() {
|
||||||
if entry.IsDir() {
|
entries, err := os.ReadDir(dir)
|
||||||
continue
|
if err != nil {
|
||||||
}
|
|
||||||
name := entry.Name()
|
|
||||||
platform, version, fileType, ok := parseSDKUploadFilename(name)
|
|
||||||
if !ok {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := entry.Name()
|
||||||
|
platform, version, fileType, ok := parseSDKUploadFilename(name)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
info, statErr := entry.Info()
|
info, statErr := entry.Info()
|
||||||
if statErr != nil {
|
if statErr != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
items = append(items, item{
|
current := item{
|
||||||
Name: name,
|
Name: name,
|
||||||
Platform: platform,
|
Platform: platform,
|
||||||
FileType: fileType,
|
FileType: fileType,
|
||||||
Version: version,
|
Version: version,
|
||||||
SizeBytes: info.Size(),
|
SizeBytes: info.Size(),
|
||||||
UpdatedAt: info.ModTime().Unix(),
|
UpdatedAt: info.ModTime().Unix(),
|
||||||
})
|
}
|
||||||
|
old, exists := byName[name]
|
||||||
|
if !exists || current.UpdatedAt >= old.UpdatedAt {
|
||||||
|
byName[name] = current
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
items := make([]item, 0, len(byName))
|
||||||
|
for _, it := range byName {
|
||||||
|
items = append(items, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Slice(items, func(i, j int) bool {
|
sort.Slice(items, func(i, j int) bool {
|
||||||
@@ -200,14 +211,14 @@ func listUploadedSDKFiles() []map[string]interface{} {
|
|||||||
})
|
})
|
||||||
|
|
||||||
result := make([]map[string]interface{}, 0, len(items))
|
result := make([]map[string]interface{}, 0, len(items))
|
||||||
for _, item := range items {
|
for _, it := range items {
|
||||||
result = append(result, map[string]interface{}{
|
result = append(result, map[string]interface{}{
|
||||||
"name": item.Name,
|
"name": it.Name,
|
||||||
"platform": item.Platform,
|
"platform": it.Platform,
|
||||||
"fileType": item.FileType,
|
"fileType": it.FileType,
|
||||||
"version": item.Version,
|
"version": it.Version,
|
||||||
"sizeText": formatSDKFileSize(item.SizeBytes),
|
"sizeText": formatSDKFileSize(it.SizeBytes),
|
||||||
"updatedAt": time.Unix(item.UpdatedAt, 0).Format("2006-01-02 15:04:05"),
|
"updatedAt": time.Unix(it.UpdatedAt, 0).Format("2006-01-02 15:04:05"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
@@ -231,7 +242,7 @@ func parseSDKUploadFilename(filename string) (platform string, version string, f
|
|||||||
}
|
}
|
||||||
|
|
||||||
main := strings.TrimSuffix(strings.TrimPrefix(filename, "httpdns-sdk-"), ext)
|
main := strings.TrimSuffix(strings.TrimPrefix(filename, "httpdns-sdk-"), ext)
|
||||||
version = "latest"
|
version = ""
|
||||||
if idx := strings.Index(main, "-v"); idx > 0 && idx+2 < len(main) {
|
if idx := strings.Index(main, "-v"); idx > 0 && idx+2 < len(main) {
|
||||||
version = main[idx+2:]
|
version = main[idx+2:]
|
||||||
main = main[:idx]
|
main = main[:idx]
|
||||||
@@ -241,6 +252,9 @@ func parseSDKUploadFilename(filename string) (platform string, version string, f
|
|||||||
switch main {
|
switch main {
|
||||||
case "android", "ios", "flutter":
|
case "android", "ios", "flutter":
|
||||||
platform = main
|
platform = main
|
||||||
|
if len(version) == 0 {
|
||||||
|
version = "-"
|
||||||
|
}
|
||||||
return platform, version, fileType, true
|
return platform, version, fileType, true
|
||||||
default:
|
default:
|
||||||
return "", "", "", false
|
return "", "", "", false
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package apps
|
package apps
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SdkUploadDeleteAction struct {
|
type SdkUploadDeleteAction struct {
|
||||||
@@ -42,15 +43,16 @@ func (this *SdkUploadDeleteAction) RunPost(params struct {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fullPath := filepath.Join(sdkUploadDir(), filename)
|
for _, dir := range sdkUploadDirs() {
|
||||||
_, err := os.Stat(fullPath)
|
fullPath := filepath.Join(dir, filename)
|
||||||
if err != nil {
|
_, err := os.Stat(fullPath)
|
||||||
this.Success()
|
if err != nil {
|
||||||
return
|
continue
|
||||||
}
|
}
|
||||||
if err = os.Remove(fullPath); err != nil {
|
if err = os.Remove(fullPath); err != nil {
|
||||||
this.Fail("删除失败: " + err.Error())
|
this.Fail("删除失败: " + err.Error())
|
||||||
return
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Success()
|
this.Success()
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||||
"github.com/iwind/TeaGo/actions"
|
"github.com/iwind/TeaGo/actions"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ClusterSettingsAction struct {
|
type ClusterSettingsAction struct {
|
||||||
@@ -41,13 +43,14 @@ func (this *ClusterSettingsAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
settings := maps.Map{
|
settings := maps.Map{
|
||||||
"name": cluster.GetString("name"),
|
"name": cluster.GetString("name"),
|
||||||
"gatewayDomain": cluster.GetString("gatewayDomain"),
|
"gatewayDomain": cluster.GetString("gatewayDomain"),
|
||||||
"cacheTtl": cluster.GetInt("defaultTTL"),
|
"cacheTtl": cluster.GetInt("defaultTTL"),
|
||||||
"fallbackTimeout": cluster.GetInt("fallbackTimeout"),
|
"fallbackTimeout": cluster.GetInt("fallbackTimeout"),
|
||||||
"installDir": cluster.GetString("installDir"),
|
"installDir": cluster.GetString("installDir"),
|
||||||
"isOn": cluster.GetBool("isOn"),
|
"isOn": cluster.GetBool("isOn"),
|
||||||
"isDefaultCluster": cluster.GetBool("isDefault"),
|
"isDefaultCluster": cluster.GetBool("isDefault"),
|
||||||
|
"isDefaultBackupCluster": false,
|
||||||
}
|
}
|
||||||
if settings.GetInt("cacheTtl") <= 0 {
|
if settings.GetInt("cacheTtl") <= 0 {
|
||||||
settings["cacheTtl"] = 30
|
settings["cacheTtl"] = 30
|
||||||
@@ -59,6 +62,19 @@ func (this *ClusterSettingsAction) RunGet(params struct {
|
|||||||
settings["installDir"] = "/opt/edge-httpdns"
|
settings["installDir"] = "/opt/edge-httpdns"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defaultBackupResp, err := this.RPC().SysSettingRPC().ReadSysSetting(this.AdminContext(), &pb.ReadSysSettingRequest{
|
||||||
|
Code: string(systemconfigs.SettingCodeHTTPDNSDefaultBackupClusterId),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defaultBackupClusterId := int64(0)
|
||||||
|
if defaultBackupResp != nil && len(defaultBackupResp.GetValueJSON()) > 0 {
|
||||||
|
defaultBackupClusterId = types.Int64(string(defaultBackupResp.GetValueJSON()))
|
||||||
|
}
|
||||||
|
settings["isDefaultBackupCluster"] = defaultBackupClusterId == params.ClusterId
|
||||||
|
|
||||||
listenAddresses := []*serverconfigs.NetworkAddressConfig{
|
listenAddresses := []*serverconfigs.NetworkAddressConfig{
|
||||||
{
|
{
|
||||||
Protocol: serverconfigs.ProtocolHTTPS,
|
Protocol: serverconfigs.ProtocolHTTPS,
|
||||||
@@ -104,14 +120,15 @@ func (this *ClusterSettingsAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *ClusterSettingsAction) RunPost(params struct {
|
func (this *ClusterSettingsAction) RunPost(params struct {
|
||||||
ClusterId int64
|
ClusterId int64
|
||||||
Name string
|
Name string
|
||||||
GatewayDomain string
|
GatewayDomain string
|
||||||
CacheTtl int32
|
CacheTtl int32
|
||||||
FallbackTimeout int32
|
FallbackTimeout int32
|
||||||
InstallDir string
|
InstallDir string
|
||||||
IsOn bool
|
IsOn bool
|
||||||
IsDefaultCluster bool
|
IsDefaultCluster bool
|
||||||
|
IsDefaultBackupCluster bool
|
||||||
|
|
||||||
Addresses []byte
|
Addresses []byte
|
||||||
SslPolicyJSON []byte
|
SslPolicyJSON []byte
|
||||||
@@ -137,7 +154,15 @@ func (this *ClusterSettingsAction) RunPost(params struct {
|
|||||||
params.InstallDir = "/opt/edge-httpdns"
|
params.InstallDir = "/opt/edge-httpdns"
|
||||||
}
|
}
|
||||||
if params.IsDefaultCluster && !params.IsOn {
|
if params.IsDefaultCluster && !params.IsOn {
|
||||||
this.Fail("默认集群必须保持启用状态")
|
this.Fail("默认主集群必须保持启用状态")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if params.IsDefaultBackupCluster && !params.IsOn {
|
||||||
|
this.Fail("默认备用集群必须保持启用状态")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if params.IsDefaultCluster && params.IsDefaultBackupCluster {
|
||||||
|
this.Fail("默认主集群和默认备用集群不能是同一个集群")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,5 +220,33 @@ func (this *ClusterSettingsAction) RunPost(params struct {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backupClusterValue := int64(0)
|
||||||
|
if params.IsDefaultBackupCluster {
|
||||||
|
backupClusterValue = params.ClusterId
|
||||||
|
} else {
|
||||||
|
readResp, err := this.RPC().SysSettingRPC().ReadSysSetting(this.AdminContext(), &pb.ReadSysSettingRequest{
|
||||||
|
Code: string(systemconfigs.SettingCodeHTTPDNSDefaultBackupClusterId),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if readResp != nil && len(readResp.GetValueJSON()) > 0 {
|
||||||
|
oldBackupClusterId := types.Int64(string(readResp.GetValueJSON()))
|
||||||
|
if oldBackupClusterId != params.ClusterId {
|
||||||
|
backupClusterValue = oldBackupClusterId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = this.RPC().SysSettingRPC().UpdateSysSetting(this.AdminContext(), &pb.UpdateSysSettingRequest{
|
||||||
|
Code: string(systemconfigs.SettingCodeHTTPDNSDefaultBackupClusterId),
|
||||||
|
ValueJSON: []byte(strconv.FormatInt(backupClusterValue, 10)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.Success()
|
this.Success()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package clusters
|
package clusters
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/httpdnsutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/httpdnsutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||||
"github.com/iwind/TeaGo/actions"
|
"github.com/iwind/TeaGo/actions"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,13 +25,14 @@ func (this *CreateAction) RunGet(params struct{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *CreateAction) RunPost(params struct {
|
func (this *CreateAction) RunPost(params struct {
|
||||||
Name string
|
Name string
|
||||||
GatewayDomain string
|
GatewayDomain string
|
||||||
CacheTtl int32
|
CacheTtl int32
|
||||||
FallbackTimeout int32
|
FallbackTimeout int32
|
||||||
InstallDir string
|
InstallDir string
|
||||||
IsOn bool
|
IsOn bool
|
||||||
IsDefault bool
|
IsDefaultPrimary bool
|
||||||
|
IsDefaultBackup bool
|
||||||
|
|
||||||
Must *actions.Must
|
Must *actions.Must
|
||||||
}) {
|
}) {
|
||||||
@@ -49,6 +52,19 @@ func (this *CreateAction) RunPost(params struct {
|
|||||||
params.Must.Field("name", params.Name).Require("请输入集群名称")
|
params.Must.Field("name", params.Name).Require("请输入集群名称")
|
||||||
params.Must.Field("gatewayDomain", params.GatewayDomain).Require("请输入服务域名")
|
params.Must.Field("gatewayDomain", params.GatewayDomain).Require("请输入服务域名")
|
||||||
|
|
||||||
|
if params.IsDefaultPrimary && !params.IsOn {
|
||||||
|
this.Fail("默认主集群必须保持启用状态")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if params.IsDefaultBackup && !params.IsOn {
|
||||||
|
this.Fail("默认备用集群必须保持启用状态")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if params.IsDefaultPrimary && params.IsDefaultBackup {
|
||||||
|
this.Fail("默认主集群和默认备用集群不能是同一个集群")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := this.RPC().HTTPDNSClusterRPC().CreateHTTPDNSCluster(this.AdminContext(), &pb.CreateHTTPDNSClusterRequest{
|
resp, err := this.RPC().HTTPDNSClusterRPC().CreateHTTPDNSCluster(this.AdminContext(), &pb.CreateHTTPDNSClusterRequest{
|
||||||
Name: params.Name,
|
Name: params.Name,
|
||||||
ServiceDomain: params.GatewayDomain,
|
ServiceDomain: params.GatewayDomain,
|
||||||
@@ -56,13 +72,24 @@ func (this *CreateAction) RunPost(params struct {
|
|||||||
FallbackTimeoutMs: params.FallbackTimeout,
|
FallbackTimeoutMs: params.FallbackTimeout,
|
||||||
InstallDir: params.InstallDir,
|
InstallDir: params.InstallDir,
|
||||||
IsOn: params.IsOn,
|
IsOn: params.IsOn,
|
||||||
IsDefault: params.IsDefault,
|
IsDefault: params.IsDefaultPrimary,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if params.IsDefaultBackup {
|
||||||
|
_, err = this.RPC().SysSettingRPC().UpdateSysSetting(this.AdminContext(), &pb.UpdateSysSettingRequest{
|
||||||
|
Code: string(systemconfigs.SettingCodeHTTPDNSDefaultBackupClusterId),
|
||||||
|
ValueJSON: []byte(strconv.FormatInt(resp.GetClusterId(), 10)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.Data["clusterId"] = resp.GetClusterId()
|
this.Data["clusterId"] = resp.GetClusterId()
|
||||||
this.Success()
|
this.Success()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,8 @@ func (this *IndexAction) RunPost(params struct {
|
|||||||
|
|
||||||
NsIsOn bool
|
NsIsOn bool
|
||||||
|
|
||||||
|
HttpdnsIsOn bool
|
||||||
|
|
||||||
Must *actions.Must
|
Must *actions.Must
|
||||||
CSRF *actionutils.CSRF
|
CSRF *actionutils.CSRF
|
||||||
}) {
|
}) {
|
||||||
@@ -112,6 +114,15 @@ func (this *IndexAction) RunPost(params struct {
|
|||||||
Gt(0, "请选择一个集群")
|
Gt(0, "请选择一个集群")
|
||||||
|
|
||||||
var config = userconfigs.DefaultUserRegisterConfig()
|
var config = userconfigs.DefaultUserRegisterConfig()
|
||||||
|
{
|
||||||
|
// 先读取现有配置,避免保存时把未出现在当前表单里的字段重置为默认值
|
||||||
|
resp, err := this.RPC().SysSettingRPC().ReadSysSetting(this.AdminContext(), &pb.ReadSysSettingRequest{
|
||||||
|
Code: systemconfigs.SettingCodeUserRegisterConfig,
|
||||||
|
})
|
||||||
|
if err == nil && len(resp.ValueJSON) > 0 {
|
||||||
|
_ = json.Unmarshal(resp.ValueJSON, config)
|
||||||
|
}
|
||||||
|
}
|
||||||
config.IsOn = params.IsOn
|
config.IsOn = params.IsOn
|
||||||
config.ComplexPassword = params.ComplexPassword
|
config.ComplexPassword = params.ComplexPassword
|
||||||
config.RequireVerification = params.RequireVerification
|
config.RequireVerification = params.RequireVerification
|
||||||
@@ -142,6 +153,7 @@ func (this *IndexAction) RunPost(params struct {
|
|||||||
config.ADIsOn = params.AdIsOn
|
config.ADIsOn = params.AdIsOn
|
||||||
|
|
||||||
config.NSIsOn = params.NsIsOn
|
config.NSIsOn = params.NsIsOn
|
||||||
|
config.HTTPDNSIsOn = params.HttpdnsIsOn
|
||||||
|
|
||||||
configJSON, err := json.Marshal(config)
|
configJSON, err := json.Marshal(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -157,10 +169,15 @@ func (this *IndexAction) RunPost(params struct {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if params.FeatureOp != "keep" {
|
featureOp := params.FeatureOp
|
||||||
|
if featureOp != "overwrite" && featureOp != "append" && featureOp != "keep" {
|
||||||
|
featureOp = "keep"
|
||||||
|
}
|
||||||
|
|
||||||
|
if featureOp != "keep" {
|
||||||
_, err = this.RPC().UserRPC().UpdateAllUsersFeatures(this.AdminContext(), &pb.UpdateAllUsersFeaturesRequest{
|
_, err = this.RPC().UserRPC().UpdateAllUsersFeatures(this.AdminContext(), &pb.UpdateAllUsersFeaturesRequest{
|
||||||
FeatureCodes: params.Features,
|
FeatureCodes: params.Features,
|
||||||
Overwrite: params.FeatureOp == "overwrite",
|
Overwrite: featureOp == "overwrite",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
|
|||||||
28
EdgeAdmin/web/views/@default/httpdns/addPortPopup.html
Normal file
28
EdgeAdmin/web/views/@default/httpdns/addPortPopup.html
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{$layout "layout_popup"}
|
||||||
|
|
||||||
|
<h3 v-if="!isUpdating">添加端口绑定</h3>
|
||||||
|
<h3 v-if="isUpdating">修改端口绑定</h3>
|
||||||
|
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||||
|
<input type="hidden" name="supportRange" :value="supportRange ? 1 : 0"/>
|
||||||
|
<table class="ui table definition selectable">
|
||||||
|
<tr>
|
||||||
|
<td>网络协议</td>
|
||||||
|
<td>
|
||||||
|
<select class="ui dropdown auto-width" name="protocol" v-model="protocol" @change="changeProtocol">
|
||||||
|
<option v-for="p in protocols" :value="p.code">{{p.name}}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="title">端口 *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="address" ref="focus" v-model="address"/>
|
||||||
|
<p class="comment">可以是一个数字端口(通常不超过65535),也可以是"地址:端口"的方式。<span v-if="supportRange">支持端口范围,形式为<code-label>port1-port2</code-label>。</span>
|
||||||
|
<span v-if="from.length == 0 && protocol == 'http'">HTTP常用端口为<a href="" title="点击添加" @click.prevent="addPort('80')">80</a>。</span>
|
||||||
|
<span v-if="from.length == 0 && protocol == 'https'">HTTPS常用端口为<a href="" title="点击添加" @click.prevent="addPort('443')">443</a>。</span>
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<submit-btn></submit-btn>
|
||||||
|
</form>
|
||||||
47
EdgeAdmin/web/views/@default/httpdns/addPortPopup.js
Normal file
47
EdgeAdmin/web/views/@default/httpdns/addPortPopup.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.success = NotifyPopup;
|
||||||
|
|
||||||
|
this.isUpdating = false
|
||||||
|
|
||||||
|
this.address = ""
|
||||||
|
this.protocol = this.protocols[0].code
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
// from 用来标记是否为特殊的节点
|
||||||
|
if (this.from.length == 0) {
|
||||||
|
if (this.protocol == "http") {
|
||||||
|
this.address = "80"
|
||||||
|
} else if (this.protocol == "https") {
|
||||||
|
this.address = "443"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.parent.UPDATING_ADDR != null) {
|
||||||
|
this.isUpdating = true
|
||||||
|
let addr = window.parent.UPDATING_ADDR
|
||||||
|
this.protocol = addr.protocol
|
||||||
|
if (addr.host.length == 0) {
|
||||||
|
this.address = addr.portRange
|
||||||
|
} else {
|
||||||
|
this.address = addr.host.quoteIP() + ":" + addr.portRange
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.changeProtocol = function () {
|
||||||
|
if (this.from.length > 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch (this.protocol) {
|
||||||
|
case "http":
|
||||||
|
this.address = "80"
|
||||||
|
break
|
||||||
|
case "https":
|
||||||
|
this.address = "443"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addPort = function (port) {
|
||||||
|
this.address = port
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -67,8 +67,24 @@
|
|||||||
|
|
||||||
<table class="ui table selectable definition" v-show="activeSection == 'basic'">
|
<table class="ui table selectable definition" v-show="activeSection == 'basic'">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">App ID</td>
|
<td class="title">主集群</td>
|
||||||
<td><code>{{settings.appId}}</code></td>
|
<td>
|
||||||
|
<select class="ui dropdown auto-width" name="primaryClusterId" v-model="settings.primaryClusterId">
|
||||||
|
<option :value="0">[不设置]</option>
|
||||||
|
<option v-for="cluster in clusters" :value="cluster.id">{{cluster.name}}</option>
|
||||||
|
</select>
|
||||||
|
<p class="comment httpdns-note">未设置时,按默认主集群处理(当前默认主集群:{{settings.defaultPrimaryClusterName || '-' }})。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="title">备集群</td>
|
||||||
|
<td>
|
||||||
|
<select class="ui dropdown auto-width" name="backupClusterId" v-model="settings.backupClusterId">
|
||||||
|
<option :value="0">[不设置]</option>
|
||||||
|
<option v-for="cluster in clusters" :value="cluster.id">{{cluster.name}}</option>
|
||||||
|
</select>
|
||||||
|
<p class="comment httpdns-note">未设置时,按默认备用集群处理(当前默认备用集群:{{settings.defaultBackupClusterName || '-' }})。</p>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">应用启用</td>
|
<td class="title">应用启用</td>
|
||||||
@@ -86,14 +102,34 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<table class="ui table selectable definition httpdns-auth-table" v-show="activeSection == 'auth'">
|
<table class="ui table selectable definition httpdns-auth-table" v-show="activeSection == 'auth'">
|
||||||
|
<tr>
|
||||||
|
<td class="title">App ID</td>
|
||||||
|
<td>
|
||||||
|
<code>{{settings.appId}}</code>
|
||||||
|
<a href="" class="httpdns-mini-icon" title="复制 App ID" @click.prevent="copySecret(settings.appId, 'App ID')"><i class="copy outline icon"></i></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="title">主服务域名</td>
|
||||||
|
<td>
|
||||||
|
<code v-if="settings.primaryServiceDomain && settings.primaryServiceDomain.length > 0">{{settings.primaryServiceDomain}}</code>
|
||||||
|
<span class="grey" v-else>未配置</span>
|
||||||
|
<a v-if="settings.primaryServiceDomain && settings.primaryServiceDomain.length > 0" href="" class="httpdns-mini-icon" title="复制主服务域名" @click.prevent="copySecret(settings.primaryServiceDomain, '主服务域名')"><i class="copy outline icon"></i></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="title">备用服务域名</td>
|
||||||
|
<td>
|
||||||
|
<code v-if="settings.backupServiceDomain && settings.backupServiceDomain.length > 0">{{settings.backupServiceDomain}}</code>
|
||||||
|
<span class="grey" v-else>未配置</span>
|
||||||
|
<a v-if="settings.backupServiceDomain && settings.backupServiceDomain.length > 0" href="" class="httpdns-mini-icon" title="复制备用服务域名" @click.prevent="copySecret(settings.backupServiceDomain, '备用服务域名')"><i class="copy outline icon"></i></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">请求验签</td>
|
<td class="title">请求验签</td>
|
||||||
<td>
|
<td>
|
||||||
<span
|
<span :class="settings.signEnabled ? 'httpdns-state-on' : 'httpdns-state-off'">{{settings.signEnabled ? "已开启" : "已关闭"}}</span>
|
||||||
:class="settings.signEnabled ? 'httpdns-state-on' : 'httpdns-state-off'">{{settings.signEnabled
|
<a href="" class="ui mini button basic" style="margin-left: .8em;" @click.prevent="toggleSignEnabled">{{settings.signEnabled ? "关闭请求验签" : "开启请求验签"}}</a>
|
||||||
? "已开启" : "已关闭"}}</span>
|
|
||||||
<a href="" class="ui mini button basic" style="margin-left: .8em;"
|
|
||||||
@click.prevent="toggleSignEnabled">{{settings.signEnabled ? "关闭请求验签" : "开启请求验签"}}</a>
|
|
||||||
<p class="comment httpdns-note">打开后,服务端会对请求进行签名校验。</p>
|
<p class="comment httpdns-note">打开后,服务端会对请求进行签名校验。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -102,14 +138,9 @@
|
|||||||
<td>
|
<td>
|
||||||
<div class="httpdns-secret-line">
|
<div class="httpdns-secret-line">
|
||||||
<code>{{signSecretVisible ? settings.signSecretPlain : settings.signSecretMasked}}</code>
|
<code>{{signSecretVisible ? settings.signSecretPlain : settings.signSecretMasked}}</code>
|
||||||
<a href="" class="httpdns-mini-icon" @click.prevent="signSecretVisible = !signSecretVisible"
|
<a href="" class="httpdns-mini-icon" @click.prevent="signSecretVisible = !signSecretVisible" :title="signSecretVisible ? '隐藏明文' : '查看明文'"><i class="icon" :class="signSecretVisible ? 'eye slash' : 'eye'"></i></a>
|
||||||
:title="signSecretVisible ? '隐藏明文' : '查看明文'"><i class="icon"
|
<a href="" class="httpdns-mini-icon" title="复制加签 Secret" @click.prevent="copySecret(settings.signSecretPlain, '加签 Secret')"><i class="copy outline icon"></i></a>
|
||||||
:class="signSecretVisible ? 'eye slash' : 'eye'"></i></a>
|
<a href="" class="httpdns-mini-icon" title="重置加签 Secret" @click.prevent="resetSignSecret"><i class="redo icon"></i></a>
|
||||||
<a href="" class="httpdns-mini-icon" title="复制加签 Secret"
|
|
||||||
@click.prevent="copySecret(settings.signSecretPlain, '加签 Secret')"><i
|
|
||||||
class="copy outline icon"></i></a>
|
|
||||||
<a href="" class="httpdns-mini-icon" title="重置加签 Secret" @click.prevent="resetSignSecret"><i
|
|
||||||
class="redo icon"></i></a>
|
|
||||||
</div>
|
</div>
|
||||||
<p class="comment httpdns-note">最近更新:{{settings.signSecretUpdatedAt}}</p>
|
<p class="comment httpdns-note">最近更新:{{settings.signSecretUpdatedAt}}</p>
|
||||||
<p class="comment httpdns-note" v-if="!settings.signEnabled">请求验签已关闭,当前不使用加签 Secret。</p>
|
<p class="comment httpdns-note" v-if="!settings.signEnabled">请求验签已关闭,当前不使用加签 Secret。</p>
|
||||||
|
|||||||
@@ -34,10 +34,13 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>所属用户</td>
|
<td>所属用户</td>
|
||||||
<td>
|
<td>
|
||||||
<user-selector></user-selector>
|
<select class="ui dropdown auto-width" name="userId">
|
||||||
|
<option value="0">[不设置]</option>
|
||||||
|
<option v-for="user in users" :value="user.id">{{user.fullname}} ({{user.username}})</option>
|
||||||
|
</select>
|
||||||
<p class="comment">可以选择当前应用所属的平台用户。</p>
|
<p class="comment">可以选择当前应用所属的平台用户。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<submit-btn></submit-btn>
|
<submit-btn></submit-btn>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{$layout}
|
{$layout}
|
||||||
{$template "menu"}
|
{$template "menu"}
|
||||||
|
|
||||||
<div class="ui margin"></div>
|
<div class="ui margin"></div>
|
||||||
@@ -26,16 +26,22 @@
|
|||||||
|
|
||||||
<table class="ui table selectable celled httpdns-apps-table" v-if="apps.length > 0">
|
<table class="ui table selectable celled httpdns-apps-table" v-if="apps.length > 0">
|
||||||
<colgroup>
|
<colgroup>
|
||||||
<col style="width:32%;" />
|
<col style="width:20%;" />
|
||||||
<col style="width:26%;" />
|
|
||||||
<col style="width:12%;" />
|
<col style="width:12%;" />
|
||||||
<col style="width:10%;" />
|
<col style="width:11%;" />
|
||||||
|
<col style="width:11%;" />
|
||||||
|
<col style="width:12%;" />
|
||||||
|
<col style="width:8%;" />
|
||||||
|
<col style="width:6%;" />
|
||||||
<col style="width:20%;" />
|
<col style="width:20%;" />
|
||||||
</colgroup>
|
</colgroup>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>应用名称</th>
|
<th>应用名称</th>
|
||||||
<th>AppID</th>
|
<th>AppID</th>
|
||||||
|
<th>主集群</th>
|
||||||
|
<th>备用集群</th>
|
||||||
|
<th>用户</th>
|
||||||
<th class="center">绑定域名数</th>
|
<th class="center">绑定域名数</th>
|
||||||
<th class="center">状态</th>
|
<th class="center">状态</th>
|
||||||
<th>操作</th>
|
<th>操作</th>
|
||||||
@@ -54,6 +60,26 @@
|
|||||||
<code>{{app.appId}}</code>
|
<code>{{app.appId}}</code>
|
||||||
<copy-icon :text="app.appId"></copy-icon>
|
<copy-icon :text="app.appId"></copy-icon>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="app.primaryCluster != null && app.primaryCluster.id > 0">
|
||||||
|
{{app.primaryCluster.name || ('#' + app.primaryCluster.id)}}
|
||||||
|
<link-icon :href="'/httpdns/clusters/cluster?clusterId=' + app.primaryCluster.id"></link-icon>
|
||||||
|
</span>
|
||||||
|
<span v-else class="disabled">-</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="app.backupCluster != null && app.backupCluster.id > 0">
|
||||||
|
{{app.backupCluster.name || ('#' + app.backupCluster.id)}}
|
||||||
|
<link-icon :href="'/httpdns/clusters/cluster?clusterId=' + app.backupCluster.id"></link-icon>
|
||||||
|
</span>
|
||||||
|
<span v-else class="disabled">-</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="app.user != null && app.user.id > 0">
|
||||||
|
{{app.user.fullname}} ({{app.user.username}})
|
||||||
|
</span>
|
||||||
|
<span v-else class="disabled">-</span>
|
||||||
|
</td>
|
||||||
<td class="center"><a :href="'/httpdns/apps/domains?appId=' + app.id">{{app.domainCount}}</a></td>
|
<td class="center"><a :href="'/httpdns/apps/domains?appId=' + app.id">{{app.domainCount}}</a></td>
|
||||||
<td class="center">
|
<td class="center">
|
||||||
<label-on :v-is-on="app.isOn"></label-on>
|
<label-on :v-is-on="app.isOn"></label-on>
|
||||||
@@ -68,4 +94,4 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div class="page" v-html="page"></div>
|
<div class="page" v-html="page"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -49,8 +49,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="extra content">
|
<div class="extra content">
|
||||||
<div class="httpdns-sdk-actions">
|
<div class="httpdns-sdk-actions">
|
||||||
<a class="ui button compact mini basic" href="/httpdns/apps/sdk/download?platform=android"><i class="icon download"></i> 下载 SDK</a>
|
<a class="ui button compact mini basic" href="/httpdns/apps/sdk/download?platform=android" @click="downloadSDK('android', $event)"><i class="icon download"></i> 下载 SDK</a>
|
||||||
<a class="ui button compact mini basic" href="/httpdns/apps/sdk/doc?platform=android"><i class="icon book"></i> 下载文档</a>
|
<a class="ui button compact mini basic" href="/httpdns/apps/sdk/doc?platform=android" @click="downloadDoc('android', $event)"><i class="icon book"></i> 下载文档</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -63,8 +63,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="extra content">
|
<div class="extra content">
|
||||||
<div class="httpdns-sdk-actions">
|
<div class="httpdns-sdk-actions">
|
||||||
<a class="ui button compact mini basic" href="/httpdns/apps/sdk/download?platform=ios"><i class="icon download"></i> 下载 SDK</a>
|
<a class="ui button compact mini basic" href="/httpdns/apps/sdk/download?platform=ios" @click="downloadSDK('ios', $event)"><i class="icon download"></i> 下载 SDK</a>
|
||||||
<a class="ui button compact mini basic" href="/httpdns/apps/sdk/doc?platform=ios"><i class="icon book"></i> 下载文档</a>
|
<a class="ui button compact mini basic" href="/httpdns/apps/sdk/doc?platform=ios" @click="downloadDoc('ios', $event)"><i class="icon book"></i> 下载文档</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -77,8 +77,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="extra content">
|
<div class="extra content">
|
||||||
<div class="httpdns-sdk-actions">
|
<div class="httpdns-sdk-actions">
|
||||||
<a class="ui button compact mini basic" href="/httpdns/apps/sdk/download?platform=flutter"><i class="icon download"></i> 下载 SDK</a>
|
<a class="ui button compact mini basic" href="/httpdns/apps/sdk/download?platform=flutter" @click="downloadSDK('flutter', $event)"><i class="icon download"></i> 下载 SDK</a>
|
||||||
<a class="ui button compact mini basic" href="/httpdns/apps/sdk/doc?platform=flutter"><i class="icon book"></i> 下载文档</a>
|
<a class="ui button compact mini basic" href="/httpdns/apps/sdk/doc?platform=flutter" @click="downloadDoc('flutter', $event)"><i class="icon book"></i> 下载文档</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
123
EdgeAdmin/web/views/@default/httpdns/apps/sdk.js
Normal file
123
EdgeAdmin/web/views/@default/httpdns/apps/sdk.js
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.downloadSDK = function (platform, event) {
|
||||||
|
this.checkAndDownload(platform, "sdk", event)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.downloadDoc = function (platform, event) {
|
||||||
|
this.checkAndDownload(platform, "doc", event)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.checkAndDownload = function (platform, type, event) {
|
||||||
|
if (event != null && typeof event.preventDefault == "function") {
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$get("/httpdns/apps/sdk/check")
|
||||||
|
.params({
|
||||||
|
platform: platform,
|
||||||
|
type: type
|
||||||
|
})
|
||||||
|
.success(function (resp) {
|
||||||
|
let data = (resp != null && resp.data != null) ? resp.data : {}
|
||||||
|
if (!data.exists) {
|
||||||
|
teaweb.warn(data.message || "当前暂无可下载文件")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (typeof data.url == "string" && data.url.length > 0) {
|
||||||
|
this.downloadByBlob(data.url, platform, type)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
teaweb.warn("下载地址生成失败,请稍后重试")
|
||||||
|
}.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.downloadByBlob = function (url, platform, type) {
|
||||||
|
let defaultFileName = "httpdns-sdk-" + platform + (type == "doc" ? ".md" : ".zip")
|
||||||
|
|
||||||
|
let xhr = new XMLHttpRequest()
|
||||||
|
xhr.open("GET", url, true)
|
||||||
|
xhr.responseType = "blob"
|
||||||
|
|
||||||
|
xhr.onload = function () {
|
||||||
|
if (xhr.status < 200 || xhr.status >= 300) {
|
||||||
|
teaweb.warn("下载失败(HTTP " + xhr.status + ")")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (xhr.status == 204) {
|
||||||
|
teaweb.warn("下载被浏览器扩展或下载工具拦截,请暂时关闭相关扩展后重试")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let contentType = (xhr.getResponseHeader("Content-Type") || "").toLowerCase()
|
||||||
|
if (contentType.indexOf("application/json") >= 0) {
|
||||||
|
let reader = new FileReader()
|
||||||
|
reader.onload = function () {
|
||||||
|
try {
|
||||||
|
let json = JSON.parse(reader.result)
|
||||||
|
teaweb.warn((json && json.message) ? json.message : "下载失败,请稍后重试")
|
||||||
|
} catch (_e) {
|
||||||
|
teaweb.warn("下载失败,请稍后重试")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reader.readAsText(xhr.response)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let disposition = xhr.getResponseHeader("Content-Disposition") || ""
|
||||||
|
let fileName = xhr.getResponseHeader("X-SDK-Filename") || this.parseFileName(disposition) || defaultFileName
|
||||||
|
if (xhr.response == null || xhr.response.size <= 0) {
|
||||||
|
teaweb.warn("下载文件为空,请检查已上传 SDK 包是否完整")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.saveBlob(xhr.response, fileName)
|
||||||
|
}.bind(this)
|
||||||
|
|
||||||
|
xhr.onerror = function () {
|
||||||
|
teaweb.warn("下载失败,请检查网络后重试")
|
||||||
|
}
|
||||||
|
|
||||||
|
xhr.send()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parseFileName = function (disposition) {
|
||||||
|
if (typeof disposition != "string" || disposition.length == 0) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
let utf8Match = disposition.match(/filename\*=UTF-8''([^;]+)/i)
|
||||||
|
if (utf8Match != null && utf8Match.length > 1) {
|
||||||
|
try {
|
||||||
|
return decodeURIComponent(utf8Match[1])
|
||||||
|
} catch (_e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let plainMatch = disposition.match(/filename="?([^";]+)"?/i)
|
||||||
|
if (plainMatch != null && plainMatch.length > 1) {
|
||||||
|
return plainMatch[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveBlob = function (blob, fileName) {
|
||||||
|
if (window.navigator != null && typeof window.navigator.msSaveOrOpenBlob == "function") {
|
||||||
|
window.navigator.msSaveOrOpenBlob(blob, fileName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let objectURL = window.URL.createObjectURL(blob)
|
||||||
|
let a = document.createElement("a")
|
||||||
|
a.style.display = "none"
|
||||||
|
a.href = objectURL
|
||||||
|
a.download = fileName
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
setTimeout(function () {
|
||||||
|
window.URL.revokeObjectURL(objectURL)
|
||||||
|
if (a.parentNode != null) {
|
||||||
|
a.parentNode.removeChild(a)
|
||||||
|
}
|
||||||
|
}, 30000)
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{$layout}
|
{$layout}
|
||||||
|
|
||||||
<second-menu>
|
<second-menu>
|
||||||
<a class="item" :href="'/httpdns/apps/domains?appId=' + app.id">{{app.name}}</a>
|
<a class="item" :href="'/httpdns/apps/domains?appId=' + app.id">{{app.name}}</a>
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
<td class="title">版本号 *</td>
|
<td class="title">版本号 *</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="version" v-model="version" maxlength="32"/>
|
<input type="text" name="version" v-model="version" maxlength="32"/>
|
||||||
<p class="comment">默认 `1.0.0`。同平台上传会覆盖“最新版本”下载内容。</p>
|
<p class="comment">默认 `1.0.0`。同平台+同版本再次上传会覆盖该版本文件。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -90,4 +90,3 @@
|
|||||||
<div class="ui message" v-else>
|
<div class="ui message" v-else>
|
||||||
暂无上传记录。
|
暂无上传记录。
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Tea.context(function () {
|
Tea.context(function () {
|
||||||
this.platform = "android";
|
this.platform = "android";
|
||||||
this.version = this.defaultVersion || "1.0.0";
|
this.version = this.defaultVersion || "1.0.0";
|
||||||
this.isUploading = false;
|
this.isUploading = false;
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
<div class="item"><strong>集群设置</strong></div>
|
<div class="item"><strong>集群设置</strong></div>
|
||||||
</second-menu>
|
</second-menu>
|
||||||
|
|
||||||
|
|
||||||
{$template "/left_menu_with_menu"}
|
{$template "/left_menu_with_menu"}
|
||||||
|
|
||||||
<div class="right-box with-menu">
|
<div class="right-box with-menu">
|
||||||
@@ -39,50 +38,65 @@
|
|||||||
<td>节点安装根目录</td>
|
<td>节点安装根目录</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="installDir" maxlength="100" v-model="settings.installDir" />
|
<input type="text" name="installDir" maxlength="100" v-model="settings.installDir" />
|
||||||
<p class="comment">边缘节点安装 HTTPDNS 服务的默认所在目录,此目录将被用于下发配置。通常保持默认即可。</p>
|
<p class="comment">边缘节点安装 HTTPDNS 服务的默认目录,通常保持默认即可。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>默认解析 TTL</td>
|
<td>默认解析 TTL</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="ui input right labeled">
|
<div class="ui input right labeled">
|
||||||
<input type="text" name="cacheTtl" maxlength="5" v-model="settings.cacheTtl"
|
<input type="text" name="cacheTtl" maxlength="5" v-model="settings.cacheTtl" style="width: 6em" />
|
||||||
style="width: 6em" />
|
|
||||||
<span class="ui label">秒</span>
|
<span class="ui label">秒</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="comment">SDK 向 HTTPDNS 请求域名解析时,返回的默认缓存有效期 (TTL)。SDK 超时后将重新发起请求。</p>
|
<p class="comment">SDK 通过 HTTPDNS 解析域名时返回的默认 TTL。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>降级超时容忍度</td>
|
<td>降级超时容忍度</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="ui input right labeled">
|
<div class="ui input right labeled">
|
||||||
<input type="text" name="fallbackTimeout" maxlength="5" v-model="settings.fallbackTimeout"
|
<input type="text" name="fallbackTimeout" maxlength="5" v-model="settings.fallbackTimeout" style="width: 6em" />
|
||||||
style="width: 6em" />
|
|
||||||
<span class="ui label">毫秒</span>
|
<span class="ui label">毫秒</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="comment">HTTPDNS 节点在回源查询其它 DNS 时的最大等待时间。超出此时间将触发服务降级逻辑(返回上一有效缓存或错误)。</p>
|
<p class="comment">节点回源查询上游 DNS 时的最大等待时间。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>启用当前集群</td>
|
<td>启用当前集群</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" name="isOn" value="1" v-model="settings.isOn"
|
<input type="checkbox" name="isOn" value="1" v-model="settings.isOn" @change="syncDefaultCluster" />
|
||||||
@change="syncDefaultCluster" />
|
|
||||||
<label></label>
|
<label></label>
|
||||||
</div>
|
</div>
|
||||||
<p class="comment">如果取消启用,整个集群的 HTTPDNS 解析服务将停止。</p>
|
<p class="comment">取消启用后,该集群不会参与 HTTPDNS 服务。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>默认集群</td>
|
<td>默认集群</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" name="isDefaultCluster" value="1" v-model="settings.isDefaultCluster" />
|
<input type="checkbox" value="1" v-model="settings.defaultClusterEnabled" @change="syncDefaultClusterSelection" />
|
||||||
<label>设置为默认部署集群</label>
|
<label>设为默认集群</label>
|
||||||
</div>
|
</div>
|
||||||
<p class="comment">全局设置。如果应用未单独指定集群,将默认分配和部署到该集群中。</p>
|
<div class="ui form" style="margin-top: .8em;" v-if="settings.defaultClusterEnabled">
|
||||||
|
<div class="grouped fields" style="margin:0;">
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui radio checkbox">
|
||||||
|
<input type="radio" v-model="settings.defaultClusterRole" value="primary" @change="syncDefaultClusterSelection" />
|
||||||
|
<label>主集群</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui radio checkbox">
|
||||||
|
<input type="radio" v-model="settings.defaultClusterRole" value="backup" @change="syncDefaultClusterSelection" />
|
||||||
|
<label>备用集群</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="isDefaultCluster" value="1" v-if="settings.defaultClusterEnabled && settings.defaultClusterRole == 'primary'" />
|
||||||
|
<input type="hidden" name="isDefaultBackupCluster" value="1" v-if="settings.defaultClusterEnabled && settings.defaultClusterRole == 'backup'" />
|
||||||
|
<p class="comment">同一时刻最多 1 个默认集群角色,新设置会自动取消旧设置。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@@ -91,15 +105,12 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td class="title">绑定端口 *</td>
|
<td class="title">绑定端口 *</td>
|
||||||
<td>
|
<td>
|
||||||
<network-addresses-box :v-url="'/httpdns/addPortPopup'" :v-addresses="tlsConfig.listen"
|
<network-addresses-box :v-url="'/httpdns/addPortPopup'" :v-addresses="tlsConfig.listen" :v-protocol="'tls'" :v-support-range="true"></network-addresses-box>
|
||||||
:v-protocol="'tls'" :v-support-range="true"></network-addresses-box>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- SSL配置 -->
|
<ssl-config-box v-show="activeSection == 'tls'" :v-ssl-policy="tlsConfig.sslPolicy" :v-protocol="'tls'"></ssl-config-box>
|
||||||
<ssl-config-box v-show="activeSection == 'tls'" :v-ssl-policy="tlsConfig.sslPolicy"
|
|
||||||
:v-protocol="'tls'"></ssl-config-box>
|
|
||||||
|
|
||||||
<submit-btn></submit-btn>
|
<submit-btn></submit-btn>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Tea.context(function () {
|
Tea.context(function () {
|
||||||
this.success = NotifyReloadSuccess("保存成功")
|
this.success = NotifyReloadSuccess("保存成功")
|
||||||
|
|
||||||
this.activeSection = this.activeSection || "basic"
|
this.activeSection = this.activeSection || "basic"
|
||||||
@@ -8,9 +8,38 @@ Tea.context(function () {
|
|||||||
this.settings = {}
|
this.settings = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 兼容旧字段,转换成统一“默认集群 + 角色”表现
|
||||||
|
let isDefaultPrimary = !!this.settings.isDefaultCluster
|
||||||
|
let isDefaultBackup = !!this.settings.isDefaultBackupCluster
|
||||||
|
this.settings.defaultClusterEnabled = isDefaultPrimary || isDefaultBackup
|
||||||
|
this.settings.defaultClusterRole = isDefaultBackup ? "backup" : "primary"
|
||||||
|
|
||||||
this.syncDefaultCluster = function () {
|
this.syncDefaultCluster = function () {
|
||||||
if (!this.settings.isOn) {
|
if (!this.settings.isOn) {
|
||||||
|
this.settings.defaultClusterEnabled = false
|
||||||
|
this.settings.defaultClusterRole = "primary"
|
||||||
this.settings.isDefaultCluster = false
|
this.settings.isDefaultCluster = false
|
||||||
|
this.settings.isDefaultBackupCluster = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.syncDefaultClusterSelection()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.syncDefaultClusterSelection = function () {
|
||||||
|
if (!this.settings.defaultClusterEnabled) {
|
||||||
|
this.settings.isDefaultCluster = false
|
||||||
|
this.settings.isDefaultBackupCluster = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.settings.defaultClusterRole === "backup") {
|
||||||
|
this.settings.isDefaultCluster = false
|
||||||
|
this.settings.isDefaultBackupCluster = true
|
||||||
|
} else {
|
||||||
|
this.settings.defaultClusterRole = "primary"
|
||||||
|
this.settings.isDefaultCluster = true
|
||||||
|
this.settings.isDefaultBackupCluster = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{$layout}
|
{$layout}
|
||||||
{$template "menu"}
|
{$template "menu"}
|
||||||
|
|
||||||
<div class="ui margin"></div>
|
<div class="ui margin"></div>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
<input type="text" name="cacheTtl" maxlength="5" value="30" style="width: 6em" />
|
<input type="text" name="cacheTtl" maxlength="5" value="30" style="width: 6em" />
|
||||||
<span class="ui label">秒</span>
|
<span class="ui label">秒</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="comment">SDK 向 HTTPDNS 请求域名解析时,返回的默认缓存有效期 (TTL)。SDK 超时后将重新发起请求。</p>
|
<p class="comment">SDK 通过 HTTPDNS 解析域名时返回的默认 TTL。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -37,36 +37,52 @@
|
|||||||
<input type="text" name="fallbackTimeout" maxlength="5" value="300" style="width: 6em" />
|
<input type="text" name="fallbackTimeout" maxlength="5" value="300" style="width: 6em" />
|
||||||
<span class="ui label">毫秒</span>
|
<span class="ui label">毫秒</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="comment">HTTPDNS 节点在回源查询其它 DNS 时的最大等待时间。超出此时间将触发服务降级逻辑(返回上一有效缓存或错误)。</p>
|
<p class="comment">节点回源查询上游 DNS 时的最大等待时间。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>节点安装根目录</td>
|
<td>节点安装根目录</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="installDir" maxlength="255" value="/opt/edge-httpdns" />
|
<input type="text" name="installDir" maxlength="255" value="/opt/edge-httpdns" />
|
||||||
<p class="comment">边缘节点安装 HTTPDNS 服务的默认所在目录,此目录将被用于下发配置。通常保持默认即可。</p>
|
<p class="comment">边缘节点安装 HTTPDNS 服务的默认目录。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>启用当前集群</td>
|
<td>启用当前集群</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" name="isOn" value="1" checked />
|
<input type="checkbox" name="isOn" value="1" v-model="isOn" @change="syncDefaultClusterEnabled" checked />
|
||||||
<label></label>
|
<label></label>
|
||||||
</div>
|
</div>
|
||||||
<p class="comment">如果取消启用,整个集群的 HTTPDNS 解析服务将不被系统分配。</p>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>默认集群</td>
|
<td>默认集群</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" name="isDefault" value="1" />
|
<input type="checkbox" value="1" v-model="defaultClusterEnabled" @change="syncDefaultClusterEnabled" />
|
||||||
<label>设置为默认部署集群</label>
|
<label>设为默认集群</label>
|
||||||
</div>
|
</div>
|
||||||
<p class="comment">全局设置。如果应用未单独指定集群,将默认分配和部署到该集群中。</p>
|
<div class="ui form" style="margin-top: .8em;" v-if="defaultClusterEnabled">
|
||||||
|
<div class="grouped fields" style="margin:0;">
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui radio checkbox">
|
||||||
|
<input type="radio" v-model="defaultClusterRole" value="primary" />
|
||||||
|
<label>主集群</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui radio checkbox">
|
||||||
|
<input type="radio" v-model="defaultClusterRole" value="backup" />
|
||||||
|
<label>备用集群</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="isDefaultPrimary" value="1" v-if="defaultClusterEnabled && defaultClusterRole == 'primary'" />
|
||||||
|
<input type="hidden" name="isDefaultBackup" value="1" v-if="defaultClusterEnabled && defaultClusterRole == 'backup'" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<submit-btn></submit-btn>
|
<submit-btn></submit-btn>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,4 +1,15 @@
|
|||||||
Tea.context(function () {
|
Tea.context(function () {
|
||||||
|
this.isOn = true
|
||||||
|
this.defaultClusterEnabled = false
|
||||||
|
this.defaultClusterRole = "primary"
|
||||||
|
|
||||||
|
this.syncDefaultClusterEnabled = function () {
|
||||||
|
if (!this.isOn) {
|
||||||
|
this.defaultClusterEnabled = false
|
||||||
|
this.defaultClusterRole = "primary"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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") {
|
||||||
|
|||||||
@@ -256,5 +256,17 @@
|
|||||||
<div class="margin"></div>
|
<div class="margin"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="margin"></div>
|
||||||
|
<h4>HTTPDNS服务</h4>
|
||||||
|
<table class="ui table definition selectable">
|
||||||
|
<tr>
|
||||||
|
<td class="title">开通HTTPDNS服务</td>
|
||||||
|
<td><checkbox name="httpdnsIsOn" v-model="config.httpdnsIsOn"></checkbox>
|
||||||
|
<p class="comment">选中表示自动为用户开通HTTPDNS服务。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<div class="margin"></div>
|
||||||
|
|
||||||
<submit-btn></submit-btn>
|
<submit-btn></submit-btn>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ Tea.context(function () {
|
|||||||
this.mobileVerificationMoreOptions = false
|
this.mobileVerificationMoreOptions = false
|
||||||
this.mobileResetPasswordMoreOptions = false
|
this.mobileResetPasswordMoreOptions = false
|
||||||
|
|
||||||
this.featureOp = "overwrite"
|
// 默认不影响已有用户功能,避免保存注册设置时误改用户功能绑定
|
||||||
|
this.featureOp = "keep"
|
||||||
this.featuresVisible = false
|
this.featuresVisible = false
|
||||||
|
|
||||||
this.showFeatures = function () {
|
this.showFeatures = function () {
|
||||||
@@ -27,4 +28,4 @@ Tea.context(function () {
|
|||||||
})
|
})
|
||||||
return names.join(" / ")
|
return names.join(" / ")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ var File_models_model_httpdns_app_proto protoreflect.FileDescriptor
|
|||||||
var file_models_model_httpdns_app_proto_rawDesc = []byte{
|
var file_models_model_httpdns_app_proto_rawDesc = []byte{
|
||||||
0x0a, 0x1e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x68,
|
0x0a, 0x1e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x68,
|
||||||
0x74, 0x74, 0x70, 0x64, 0x6e, 0x73, 0x5f, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
0x74, 0x74, 0x70, 0x64, 0x6e, 0x73, 0x5f, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
0x12, 0x02, 0x70, 0x62, 0x22, 0xee, 0x02, 0x0a, 0x0a, 0x48, 0x54, 0x54, 0x50, 0x44, 0x4e, 0x53,
|
0x12, 0x02, 0x70, 0x62, 0x22, 0x86, 0x03, 0x0a, 0x0a, 0x48, 0x54, 0x54, 0x50, 0x44, 0x4e, 0x53,
|
||||||
0x41, 0x70, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52,
|
0x41, 0x70, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52,
|
||||||
0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||||
0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64,
|
0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64,
|
||||||
@@ -189,7 +189,9 @@ var file_models_model_httpdns_app_proto_rawDesc = []byte{
|
|||||||
0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72,
|
0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72,
|
||||||
0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74,
|
0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74,
|
||||||
0x65, 0x64, 0x41, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61,
|
0x65, 0x64, 0x41, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61,
|
||||||
0x74, 0x65, 0x64, 0x41, 0x74, 0x42, 0x06, 0x5a, 0x04, 0x2e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70,
|
0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x18,
|
||||||
|
0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x42, 0x06, 0x5a,
|
||||||
|
0x04, 0x2e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70,
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -724,7 +724,7 @@ var file_service_httpdns_app_proto_rawDesc = []byte{
|
|||||||
0x1e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x68, 0x74,
|
0x1e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x68, 0x74,
|
||||||
0x74, 0x70, 0x64, 0x6e, 0x73, 0x5f, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
|
0x74, 0x70, 0x64, 0x6e, 0x73, 0x5f, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
|
||||||
0x19, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x2f, 0x72, 0x70, 0x63, 0x5f, 0x6d, 0x65, 0x73, 0x73,
|
0x19, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x2f, 0x72, 0x70, 0x63, 0x5f, 0x6d, 0x65, 0x73, 0x73,
|
||||||
0x61, 0x67, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xcf, 0x01, 0x0a, 0x17, 0x43,
|
0x61, 0x67, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe7, 0x01, 0x0a, 0x17, 0x43,
|
||||||
0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x54, 0x54, 0x50, 0x44, 0x4e, 0x53, 0x41, 0x70, 0x70, 0x52,
|
0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x54, 0x54, 0x50, 0x44, 0x4e, 0x53, 0x41, 0x70, 0x70, 0x52,
|
||||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
|
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x70,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x70,
|
||||||
@@ -737,11 +737,13 @@ var file_service_httpdns_app_proto_rawDesc = []byte{
|
|||||||
0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x73, 0x4f, 0x6e, 0x18, 0x05,
|
0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x73, 0x4f, 0x6e, 0x18, 0x05,
|
||||||
0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x69, 0x73, 0x4f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x69,
|
0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x69, 0x73, 0x4f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x69,
|
||||||
0x67, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52,
|
0x67, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||||
0x0b, 0x73, 0x69, 0x67, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x34, 0x0a, 0x18,
|
0x0b, 0x73, 0x69, 0x67, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06,
|
||||||
|
0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73,
|
||||||
|
0x65, 0x72, 0x49, 0x64, 0x22, 0x34, 0x0a, 0x18,
|
||||||
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x54, 0x54, 0x50, 0x44, 0x4e, 0x53, 0x41, 0x70, 0x70,
|
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x54, 0x54, 0x50, 0x44, 0x4e, 0x53, 0x41, 0x70, 0x70,
|
||||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x70, 0x70, 0x44,
|
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x70, 0x70, 0x44,
|
||||||
0x62, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, 0x70, 0x70, 0x44, 0x62,
|
0x62, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, 0x70, 0x70, 0x44, 0x62,
|
||||||
0x49, 0x64, 0x22, 0xb1, 0x01, 0x0a, 0x17, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x54, 0x54,
|
0x49, 0x64, 0x22, 0xc9, 0x01, 0x0a, 0x17, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x54, 0x54,
|
||||||
0x50, 0x44, 0x4e, 0x53, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18,
|
0x50, 0x44, 0x4e, 0x53, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18,
|
||||||
0x0a, 0x07, 0x61, 0x70, 0x70, 0x44, 0x62, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52,
|
0x0a, 0x07, 0x61, 0x70, 0x70, 0x44, 0x62, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52,
|
||||||
0x07, 0x61, 0x70, 0x70, 0x44, 0x62, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
|
0x07, 0x61, 0x70, 0x70, 0x44, 0x62, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
|
||||||
@@ -752,7 +754,9 @@ var file_service_httpdns_app_proto_rawDesc = []byte{
|
|||||||
0x75, 0x70, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28,
|
0x75, 0x70, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28,
|
||||||
0x03, 0x52, 0x0f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72,
|
0x03, 0x52, 0x0f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72,
|
||||||
0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x73, 0x4f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08,
|
0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x73, 0x4f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08,
|
||||||
0x52, 0x04, 0x69, 0x73, 0x4f, 0x6e, 0x22, 0x33, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
|
0x52, 0x04, 0x69, 0x73, 0x4f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64,
|
||||||
|
0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x33,
|
||||||
|
0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
|
||||||
0x48, 0x54, 0x54, 0x50, 0x44, 0x4e, 0x53, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
0x48, 0x54, 0x54, 0x50, 0x44, 0x4e, 0x53, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||||
0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x70, 0x70, 0x44, 0x62, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01,
|
0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x70, 0x70, 0x44, 0x62, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x28, 0x03, 0x52, 0x07, 0x61, 0x70, 0x70, 0x44, 0x62, 0x49, 0x64, 0x22, 0x31, 0x0a, 0x15, 0x46,
|
0x28, 0x03, 0x52, 0x07, 0x61, 0x70, 0x70, 0x44, 0x62, 0x49, 0x64, 0x22, 0x31, 0x0a, 0x15, 0x46,
|
||||||
|
|||||||
@@ -3,18 +3,18 @@ package systemconfigs
|
|||||||
type SettingCode = string
|
type SettingCode = string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SettingCodeNodeMonitor SettingCode = "nodeMonitor" // 监控节点状态
|
SettingCodeNodeMonitor SettingCode = "nodeMonitor" // 监控节点状态
|
||||||
SettingCodeClusterHealthCheck SettingCode = "clusterHealthCheck" // 集群健康检查
|
SettingCodeClusterHealthCheck SettingCode = "clusterHealthCheck" // 集群健康检查
|
||||||
SettingCodeIPListVersion SettingCode = "ipListVersion" // IP名单的版本号
|
SettingCodeIPListVersion SettingCode = "ipListVersion" // IP名单版本号
|
||||||
SettingCodeAdminSecurityConfig SettingCode = "adminSecurityConfig" // 管理员安全设置
|
SettingCodeAdminSecurityConfig SettingCode = "adminSecurityConfig" // 管理端安全设置
|
||||||
SettingCodeAdminUIConfig SettingCode = "adminUIConfig" // 管理员界面设置
|
SettingCodeAdminUIConfig SettingCode = "adminUIConfig" // 管理端界面设置
|
||||||
SettingCodeDatabaseConfigSetting SettingCode = "databaseConfig" // 数据库相关配置
|
SettingCodeDatabaseConfigSetting SettingCode = "databaseConfig" // 数据库配置
|
||||||
SettingCodeAccessLogQueue SettingCode = "accessLogQueue" // 访问日志队列
|
SettingCodeAccessLogQueue SettingCode = "accessLogQueue" // 访问日志队列
|
||||||
SettingCodeCheckUpdates SettingCode = "checkUpdates" // 检查自动更新配置
|
SettingCodeCheckUpdates SettingCode = "checkUpdates" // 自动更新配置
|
||||||
|
SettingCodeUserServerConfig SettingCode = "userServerConfig" // 用户服务设置
|
||||||
|
SettingCodeUserRegisterConfig SettingCode = "userRegisterConfig" // 用户注册配置
|
||||||
|
SettingCodeUserUIConfig SettingCode = "userUIConfig" // 用户界面配置
|
||||||
|
SettingCodeStandaloneInstanceInitialized SettingCode = "standaloneInstanceInitialized" // 单体实例初始化状态
|
||||||
|
|
||||||
SettingCodeUserServerConfig SettingCode = "userServerConfig" // 用户服务设置
|
SettingCodeHTTPDNSDefaultBackupClusterId SettingCode = "httpdnsDefaultBackupClusterId" // HTTPDNS默认备用集群ID
|
||||||
SettingCodeUserRegisterConfig SettingCode = "userRegisterConfig" // 用户注册配置
|
|
||||||
SettingCodeUserUIConfig SettingCode = "userUIConfig" // 用户界面配置
|
|
||||||
|
|
||||||
SettingCodeStandaloneInstanceInitialized SettingCode = "standaloneInstanceInitialized" // 单体实例是否已经被初始化:0 未被初始化, 1 已经成功初始化
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ type UserFeatureCode = string
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
UserFeatureCodePlan UserFeatureCode = "plan"
|
UserFeatureCodePlan UserFeatureCode = "plan"
|
||||||
|
UserFeatureCodeHTTPDNS UserFeatureCode = "httpdns"
|
||||||
|
|
||||||
UserFeatureCodeServerTCP UserFeatureCode = "server.tcp"
|
UserFeatureCodeServerTCP UserFeatureCode = "server.tcp"
|
||||||
UserFeatureCodeServerTCPPort UserFeatureCode = "server.tcp.port"
|
UserFeatureCodeServerTCPPort UserFeatureCode = "server.tcp.port"
|
||||||
@@ -212,6 +213,12 @@ func FindAllUserFeatures() []*UserFeature {
|
|||||||
Description: "用户可以购买和管理套餐。",
|
Description: "用户可以购买和管理套餐。",
|
||||||
SupportPlan: false,
|
SupportPlan: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "HTTPDNS",
|
||||||
|
Code: UserFeatureCodeHTTPDNS,
|
||||||
|
Description: "用户可以使用 HTTPDNS 应用管理、访问日志和解析测试。",
|
||||||
|
SupportPlan: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ type UserRegisterConfig struct {
|
|||||||
|
|
||||||
// 开通DNS服务
|
// 开通DNS服务
|
||||||
NSIsOn bool `json:"nsIsOn"` // 是否开启智能DNS服务
|
NSIsOn bool `json:"nsIsOn"` // 是否开启智能DNS服务
|
||||||
|
// 开通HTTPDNS服务
|
||||||
|
HTTPDNSIsOn bool `json:"httpdnsIsOn"` // 是否开启用户端HTTPDNS服务
|
||||||
|
|
||||||
// 开通高防服务
|
// 开通高防服务
|
||||||
ADIsOn bool `json:"adIsOn"` // 是否开启高防服务
|
ADIsOn bool `json:"adIsOn"` // 是否开启高防服务
|
||||||
@@ -63,6 +65,7 @@ func DefaultUserRegisterConfig() *UserRegisterConfig {
|
|||||||
ComplexPassword: true,
|
ComplexPassword: true,
|
||||||
CDNIsOn: true,
|
CDNIsOn: true,
|
||||||
NSIsOn: false,
|
NSIsOn: false,
|
||||||
|
HTTPDNSIsOn: false,
|
||||||
Features: []string{
|
Features: []string{
|
||||||
UserFeatureCodeServerAccessLog,
|
UserFeatureCodeServerAccessLog,
|
||||||
UserFeatureCodeServerViewAccessLog,
|
UserFeatureCodeServerViewAccessLog,
|
||||||
|
|||||||
165
EdgeHttpDNS/Android SDK集成文档.md
Normal file
165
EdgeHttpDNS/Android SDK集成文档.md
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
# Android SDK 集成文档(Edge HTTPDNS)
|
||||||
|
|
||||||
|
## 1. 版本与依赖
|
||||||
|
|
||||||
|
- SDK 模块:`EdgeHttpDNS/sdk/android/httpdns-sdk`
|
||||||
|
- `minSdkVersion`:19
|
||||||
|
- `targetSdkVersion`:33
|
||||||
|
- `compileSdk`:33
|
||||||
|
|
||||||
|
将发布包中的 `jar/aar` 放到应用模块 `libs/`,在 `app/build.gradle` 中添加:
|
||||||
|
|
||||||
|
```gradle
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. SNI 行为说明(关键)
|
||||||
|
|
||||||
|
当前 SDK 行为与代码一致:
|
||||||
|
|
||||||
|
1. `/resolve` 请求链路(SDK -> 你的 HTTPDNS 服务域名)
|
||||||
|
- 走域名 HTTPS
|
||||||
|
- 默认 TLS 行为(会带 SNI)
|
||||||
|
|
||||||
|
2. 业务请求链路(拿到 CDN IP 后发起业务 HTTPS)
|
||||||
|
- 使用 `HttpDnsHttpAdapter`:按 IP 建连,`Host` 保留原域名,并清空 SNI(No-SNI)
|
||||||
|
|
||||||
|
## 3. 初始化 SDK(推荐用 V1 客户端)
|
||||||
|
|
||||||
|
### Kotlin
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
import com.new.sdk.android.httpdns.HttpDnsV1Client
|
||||||
|
import com.new.sdk.android.httpdns.HttpDnsService
|
||||||
|
|
||||||
|
val service: HttpDnsService = HttpDnsV1Client.init(
|
||||||
|
context = applicationContext,
|
||||||
|
appId = "your-app-id",
|
||||||
|
primaryServiceHost = "httpdns.example.com",
|
||||||
|
backupServiceHost = "httpdns-backup.example.com", // 可空字符串
|
||||||
|
servicePort = 443,
|
||||||
|
signSecret = "your-sign-secret" // 可空字符串
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Java
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.new.sdk.android.httpdns.HttpDnsService;
|
||||||
|
import com.new.sdk.android.httpdns.HttpDnsV1Client;
|
||||||
|
|
||||||
|
HttpDnsService service = HttpDnsV1Client.init(
|
||||||
|
getApplicationContext(),
|
||||||
|
"your-app-id",
|
||||||
|
"httpdns.example.com",
|
||||||
|
"httpdns-backup.example.com", // 可传 ""
|
||||||
|
443,
|
||||||
|
"your-sign-secret" // 可传 ""
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 解析域名获取 CDN IP
|
||||||
|
|
||||||
|
### Kotlin
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
import com.new.sdk.android.httpdns.HTTPDNSResult
|
||||||
|
|
||||||
|
val result: HTTPDNSResult = HttpDnsV1Client.resolveHost(
|
||||||
|
service = service,
|
||||||
|
host = "api.example.com",
|
||||||
|
qtype = "A", // "A" 或 "AAAA"
|
||||||
|
cip = null // 可选,客户端 IP 透传
|
||||||
|
)
|
||||||
|
|
||||||
|
val ips = result.ips ?: emptyArray()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Java
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.new.sdk.android.httpdns.HTTPDNSResult;
|
||||||
|
|
||||||
|
HTTPDNSResult result = HttpDnsV1Client.resolveHost(
|
||||||
|
service,
|
||||||
|
"api.example.com",
|
||||||
|
"A",
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
String[] ips = result.getIps();
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 业务请求接入方式
|
||||||
|
|
||||||
|
#使用 `HttpDnsHttpAdapter`(IP 直连 + No-SNI)
|
||||||
|
|
||||||
|
业务请求侧做“隐匿 SNI”。该适配器仅支持 HTTPS URL。
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
import com.new.sdk.android.httpdns.HttpDnsV1Client
|
||||||
|
import com.new.sdk.android.httpdns.network.HttpDnsAdapterOptions
|
||||||
|
import com.new.sdk.android.httpdns.network.HttpDnsAdapterRequest
|
||||||
|
|
||||||
|
val adapter = HttpDnsV1Client.buildHttpClientAdapter(
|
||||||
|
service,
|
||||||
|
HttpDnsAdapterOptions.Builder()
|
||||||
|
.setConnectTimeoutMillis(3000)
|
||||||
|
.setReadTimeoutMillis(5000)
|
||||||
|
.setRequestIpType(com.new.sdk.android.httpdns.RequestIpType.auto)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
|
||||||
|
val req = HttpDnsAdapterRequest(
|
||||||
|
"GET",
|
||||||
|
"https://api.example.com/path?x=1"
|
||||||
|
)
|
||||||
|
|
||||||
|
val resp = adapter.execute(req)
|
||||||
|
val code = resp.statusCode
|
||||||
|
val bodyBytes = resp.body
|
||||||
|
val usedIp = resp.usedIp
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. 预解析与常用接口
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
service.setPreResolveHosts(listOf("api.example.com", "img.example.com"))
|
||||||
|
|
||||||
|
val r1 = service.getHttpDnsResultForHostSync("api.example.com", com.new.sdk.android.httpdns.RequestIpType.auto)
|
||||||
|
val r2 = service.getHttpDnsResultForHostSyncNonBlocking("api.example.com", com.new.sdk.android.httpdns.RequestIpType.auto)
|
||||||
|
```
|
||||||
|
|
||||||
|
- `Sync`:允许阻塞等待刷新结果(上限受 timeout 等配置影响)
|
||||||
|
- `NonBlocking`:快速返回当前可用缓存/结果,不阻塞等待
|
||||||
|
|
||||||
|
## 7. 验证建议
|
||||||
|
|
||||||
|
1. 验证 `/resolve`
|
||||||
|
- 抓包看目标应为 `https://<serviceHost>:<servicePort>/resolve...`
|
||||||
|
|
||||||
|
2. 验证业务请求(若使用 `HttpDnsHttpAdapter`)
|
||||||
|
- 目标地址应是 CDN IP
|
||||||
|
- HTTP `Host` 应为原域名
|
||||||
|
- TLS ClientHello 不应携带 SNI(No-SNI)
|
||||||
|
|
||||||
|
## 8. 混淆配置
|
||||||
|
|
||||||
|
```proguard
|
||||||
|
-keep class com.new.sdk.android.** { *; }
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9. 常见问题
|
||||||
|
|
||||||
|
1. HTTPDNS 没生效
|
||||||
|
- 检查是否真正使用了 SDK 返回 IP(或用了 `HttpDnsHttpAdapter`)
|
||||||
|
- 检查失败回退逻辑是否总是直接走了系统 DNS
|
||||||
|
|
||||||
|
2. 使用 `HttpDnsHttpAdapter` 仍失败
|
||||||
|
- 只支持 HTTPS URL
|
||||||
|
|
||||||
|
3. 线上不要开启不安全证书
|
||||||
|
- `HttpDnsAdapterOptions.Builder#setAllowInsecureCertificatesForDebugOnly(true)` 仅限调试环境
|
||||||
64
EdgeHttpDNS/Changelog及升级指南_v1.4.8.txt
Normal file
64
EdgeHttpDNS/Changelog及升级指南_v1.4.8.txt
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
1.4.8版本Changelog(HTTPDNS功能全量发布)
|
||||||
|
|
||||||
|
1、通过智能解析把用户就近调度到最优节点,显著降低首包时延与连接抖动。
|
||||||
|
2、支持按地域、运营商、国内/海外精细分流,提升弱网与跨网访问稳定性。
|
||||||
|
3、解析链路内置签名鉴权与请求追踪,增强安全性与可观测性。
|
||||||
|
4、无命中规则时自动回源兜底,保障解析连续可用。
|
||||||
|
5、支持 A/AAAA 双栈与多记录返回,兼容不同终端网络环境。
|
||||||
|
6、提供Android、iOS、Flutter 多端SDK 开箱可用,支持预解析、缓存与同步/非阻塞解析。
|
||||||
|
7、提供 IP 直连适配能力(含 Host 保留与 No-SNI 模式),适配复杂 HTTPS 场景。
|
||||||
|
8、控制台支持应用/域名/规则全流程配置与在线验证,缩短问题定位和发布周期。
|
||||||
|
9、节点支持在线安装升级与日志上报,降低运维复杂度并提升可维护性。
|
||||||
|
|
||||||
|
1.4.8版本升级步骤
|
||||||
|
|
||||||
|
1、备份现有配置与数据:
|
||||||
|
将 edge-api、edge-admin、edge-user 等组件目录下的 configs 文件夹,以及平台的 MySQL 数据库进行全量备份;
|
||||||
|
|
||||||
|
2、停止旧版本进程(管理端各组件):
|
||||||
|
killall -9 edge-api
|
||||||
|
killall -9 edge-admin
|
||||||
|
killall -9 edge-user
|
||||||
|
|
||||||
|
3、上传并解压新版本包(以 Linux x64 环境为例):
|
||||||
|
unzip -o edge-admin-linux-amd64-v1.4.8.zip -d /data/
|
||||||
|
unzip -o edge-user-linux-amd64-v1.4.8.zip -d /data/
|
||||||
|
|
||||||
|
4、依次运行edge-api、edge-admin、edge-user
|
||||||
|
|
||||||
|
# 启动 API 服务
|
||||||
|
cd /data/edge-api/bin
|
||||||
|
chmod +x edge-api
|
||||||
|
nohup ./edge-api 2>&1 &
|
||||||
|
|
||||||
|
# 启动管理后台
|
||||||
|
cd /data/edge-admin/bin
|
||||||
|
chmod +x edge-admin
|
||||||
|
nohup ./edge-admin 2>&1 &
|
||||||
|
|
||||||
|
# 启动租户控制台
|
||||||
|
cd /data/edge-user/bin
|
||||||
|
chmod +x edge-user
|
||||||
|
nohup ./edge-user 2>&1 &
|
||||||
|
|
||||||
|
5、检查版本状态:
|
||||||
|
登录管理后台,确认系统版本显示为 1.4.8;
|
||||||
|
|
||||||
|
6、配置主备集群:
|
||||||
|
进入“HTTPDNS -> 集群列表 -> 集群设置”,按需勾选“默认主集群”或“默认备用集群”角色,以便后续应用自动关联;
|
||||||
|
|
||||||
|
7、在线升级 HTTPDNS 节点:
|
||||||
|
进入“HTTPDNS -> 节点列表”,点击对应节点的“详情”,在“安装信息”页面点击 **[在线安装]** 或 **[升级]**。系统会自动下发最新的 edge-httpdns 二进制文件并完成重启。
|
||||||
|
|
||||||
|
8、验证节点在线状态:
|
||||||
|
等待 30 秒左右,确认节点状态恢复为“在线”,并验证硬件负载监控数据是否正常上报。
|
||||||
|
|
||||||
|
9、业务解析验证:
|
||||||
|
使用控制台“解析测试”工具,验证域名在当前环境下是否能正确返回调度的 IP 地址。
|
||||||
|
|
||||||
|
10、完成升级。
|
||||||
|
|
||||||
|
特别说明:
|
||||||
|
1、在线升级模式:Edge HTTPDNS 节点支持通过管理平台一键在线升级,无需手动上传文件和重启进程。
|
||||||
|
2、离线安装模式:如节点服务器无法连接控制台,可手动上传 edge-httpdns 压缩包并解压,更新 bin 目录下的程序文件后手动执行 `./edge-httpdns restart` 即可。
|
||||||
|
3、SNI 隐匿功能:请确保关联的 CDN 边缘节点也已同步更新至配套版本(会自动升级)。
|
||||||
133
EdgeHttpDNS/Flutter SDK集成文档.md
Normal file
133
EdgeHttpDNS/Flutter SDK集成文档.md
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
# Flutter SDK 集成文档(Edge HTTPDNS)
|
||||||
|
|
||||||
|
## 1. 版本与依赖
|
||||||
|
|
||||||
|
- SDK 插件:`EdgeHttpDNS/sdk/flutter/new_httpdns`
|
||||||
|
- 环境要求:Flutter 2.15+ / Dart 2.15+
|
||||||
|
|
||||||
|
在 `pubspec.yaml` 中引用本地插件:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dependencies:
|
||||||
|
new_httpdns:
|
||||||
|
path: path/to/sdk/flutter/new_httpdns
|
||||||
|
```
|
||||||
|
|
||||||
|
执行 `flutter pub get` 完成安装。
|
||||||
|
|
||||||
|
## 2. SNI 行为说明(关键)
|
||||||
|
|
||||||
|
1. **/resolve 请求链路**(SDK -> 你的 HTTPDNS 服务域名)
|
||||||
|
- 走标准 HTTPS,默认携带 SNI(用于路由到边缘控制节点)。
|
||||||
|
|
||||||
|
2. **业务请求链路**(拿到 CDN IP 后发起业务 HTTPS)
|
||||||
|
- **IP 直连 + No-SNI**:使用 `TrustAPPHttpdnsHttpAdapter` 进行请求。
|
||||||
|
- 逻辑:解析域名 -> 拿到 IP 列表 -> `uri.replace(host: ip)` -> `req.headers.host = uri.host` -> **清空 SNI**。
|
||||||
|
- 仅支持 HTTPS URL。
|
||||||
|
|
||||||
|
## 3. 初始化 SDK(推荐用 TrustAPP 封装)
|
||||||
|
|
||||||
|
### Dart
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:new_httpdns/new_httpdns.dart';
|
||||||
|
|
||||||
|
bool ok = await TrustAPPHttpdns.init(
|
||||||
|
appId: "your-app-id",
|
||||||
|
primaryServiceHost: "httpdns.example.com",
|
||||||
|
backupServiceHost: "httpdns-backup.example.com",
|
||||||
|
servicePort: 443,
|
||||||
|
secretKey: "your-sign-secret" // 可选,开启签名校验需传入
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
print("Edge HTTPDNS 初始化成功");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 解析域名获取 CDN IP
|
||||||
|
|
||||||
|
### Dart
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// V1 风格解析接口
|
||||||
|
Map<String, dynamic> result = await TrustAPPHttpdns.resolveHost(
|
||||||
|
"api.example.com",
|
||||||
|
qtype: 'A', // 可选 'A' 或 'AAAA'
|
||||||
|
cip: '1.2.3.4' // 可选,模拟客户端 IP
|
||||||
|
);
|
||||||
|
|
||||||
|
List<String> ipv4s = result['ipv4'];
|
||||||
|
int ttl = result['ttl'];
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 业务请求接入方式
|
||||||
|
|
||||||
|
使用 `TrustAPPHttpdnsHttpAdapter` 实现“SNI 隐匿”业务请求。
|
||||||
|
|
||||||
|
### Dart
|
||||||
|
|
||||||
|
```dart
|
||||||
|
final adapter = TrustAPPHttpdns.createHttpAdapter(
|
||||||
|
options: const TrustAPPHttpdnsAdapterOptions(
|
||||||
|
connectTimeoutMs: 3000,
|
||||||
|
readTimeoutMs: 5000,
|
||||||
|
ipType: 'auto', // auto/ipv4/ipv6
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final res = await adapter.request(
|
||||||
|
Uri.parse("https://api.example.com/path?x=1"),
|
||||||
|
method: 'GET',
|
||||||
|
headers: {'Custom-Header': 'Value'},
|
||||||
|
body: null
|
||||||
|
);
|
||||||
|
|
||||||
|
print("Status Code: ${res.statusCode}");
|
||||||
|
print("Body Length: ${res.body.length}");
|
||||||
|
print("Used IP: ${res.usedIp}");
|
||||||
|
} catch (e) {
|
||||||
|
print("请求失败: $e");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. 其他常用接口
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// 1. 设置预解析域名
|
||||||
|
await TrustAPPHttpdns.setPreResolveHosts(["api.example.com", "img.example.com"]);
|
||||||
|
|
||||||
|
// 2. 只有开启缓存时可用
|
||||||
|
Map<String, List<String>> cacheRes = await TrustAPPHttpdns.resolveHostSyncNonBlocking("api.example.com");
|
||||||
|
|
||||||
|
// 3. 开启持久化缓存(重启 App 后任然可用)
|
||||||
|
await TrustAPPHttpdns.setPersistentCacheIPEnabled(true);
|
||||||
|
|
||||||
|
// 4. 控制台日志(建议仅调试开启)
|
||||||
|
await TrustAPPHttpdns.setLogEnabled(true);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. 验证建议
|
||||||
|
|
||||||
|
1. **验证 /resolve**
|
||||||
|
- 观察控制台日志或抓包工具,解析请求应指向 `https://<serviceHost>:<servicePort>/resolve...`。
|
||||||
|
|
||||||
|
2. **验证业务请求**
|
||||||
|
- 如果使用 `TrustAPPHttpdnsHttpAdapter`,观察抓包:
|
||||||
|
- TCP 连接 IP 为 CDN 私有/边缘 IP。
|
||||||
|
- HTTP `Host` 为原始域名。
|
||||||
|
- TLS ClientHello 中 **无 SNI** 扩展。
|
||||||
|
|
||||||
|
## 8. 平台配置事项
|
||||||
|
|
||||||
|
- **Android**: 参照 Android SDK 文档配置混淆。
|
||||||
|
- **iOS**: 如果是手动集成 Flutter 插件,请确保 iOS 模块已包含依赖的静态库,并设置 `Allow Arbitrary Loads` (如果启用 HTTP)。
|
||||||
|
|
||||||
|
## 9. 常见问题
|
||||||
|
|
||||||
|
1. **Flutter 端报错:NO_IP_AVAILABLE**
|
||||||
|
- SDK 尚未解析出有效结果,请确认域名是否已在控制台添加并配置规则。
|
||||||
|
|
||||||
|
2. **请求报错:TLS_EMPTY_SNI_FAILED**
|
||||||
|
- 仅支持 HTTPS 网站。如果所有 IP 尝试均失败,请检查网络权限及服务端防火墙。
|
||||||
98
EdgeHttpDNS/HTTPDNS用户使用手册.md
Normal file
98
EdgeHttpDNS/HTTPDNS用户使用手册.md
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# Edge HTTPDNS 用户使用手册
|
||||||
|
|
||||||
|
欢迎使用 Edge HTTPDNS 服务。本文档旨在帮助您快速完成应用创建、域名配置及解析测试,实现精准、安全的业务调度。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 快速入门流程
|
||||||
|
1. **创建应用**:获取接入凭证(AppId 和 SecretKey)。
|
||||||
|
2. **添加域名**:登记需要通过 HTTPDNS 解析的业务域名。
|
||||||
|
3. **自定义解析规则**:设置域名对应的 IP 地址及智能分流规则。
|
||||||
|
4. **解析测试**:通过沙箱工具验证解析是否生效。
|
||||||
|
5. **集成 SDK**:将解析功能集成至您的 App 中。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 应用管理
|
||||||
|
|
||||||
|
应用是您接入 HTTPDNS 的基础单元。
|
||||||
|
|
||||||
|
### 2.1 创建应用
|
||||||
|
1. 登录用户控制台,点击 **HTTPDNS -> 应用列表**。
|
||||||
|
2. 点击 **创建应用**。
|
||||||
|
3. 填写应用名称(如“我的安卓App”)。
|
||||||
|
4. 系统会自动关联默认的服务域名,无需手动选择。
|
||||||
|
|
||||||
|
### 2.2 获取接入凭证
|
||||||
|
在应用详情页面,您可以找到:
|
||||||
|
* **AppId**:应用的唯一识别 ID。
|
||||||
|
* **SecretKey**:签名密钥。**请务必妥善保管,切勿泄露。** 在 SDK 初始化时使用此密钥可开启“签名鉴权”,防止解析接口被他人盗刷。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 域名与记录配置
|
||||||
|
|
||||||
|
### 3.1 添加域名
|
||||||
|
1. 进入应用详情页,切换至 **域名管理** 标签。
|
||||||
|
2. 点击 **添加域名**,输入您的业务域名(如 `api.example.com`)。
|
||||||
|
|
||||||
|
### 3.2 自定义解析规则
|
||||||
|
**作用**:自定义解析规则允许您根据终端用户的网络环境(如运营商)或物理位置,为其分配最优的访问地址。通过精细化的线路调度,可以有效降低跨境或跨网访问带来的延迟,提升 App 的响应速度。
|
||||||
|
|
||||||
|
点击域名后的 **解析规则**,进入详细设置:
|
||||||
|
* **解析类型**:支持 **A 记录 (IPv4)** 和 **AAAA 记录 (IPv6)**。
|
||||||
|
* **线路选择**:可选择针对特定运营商(如:移动、电信、联通)或特定地域(如:浙江省、海外)进行精准匹配。若不选择则代表全局默认配置。
|
||||||
|
* **解析结果**:填写您的服务器目标 IP 地址。
|
||||||
|
* **TTL**:解析结果在客户端缓存的时间。默认为 30 秒,建议保持默认以兼顾调度灵活性。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 配合 CDN 实现网络加速与安全
|
||||||
|
|
||||||
|
Edge HTTPDNS 不仅仅提供域名解析功能,更通过与 CDN 节点的深度集成,解决了移动端常见的 **HTTPS SNI 隐匿访问**及 **跨运营商加速**问题。
|
||||||
|
|
||||||
|
### 4.1 自动获取 CDN 边缘节点
|
||||||
|
如果您在系统内开通了 **CDN 加速服务**,只需将业务域名配置在 CDN 平台中,并将其 CNAME 解析指向 CDN 提供的地址:
|
||||||
|
* **智能调度**:HTTPDNS 会自动识别该域名已接入 CDN,并针对终端用户的地理位置和运营商,智能返回最优的 **CDN 边缘节点 IP**。
|
||||||
|
* **无感知兜底**:如果域名未接入 CDN 或未配置解析规则,HTTPDNS 将自动回源查询 **权威 DNS**,并返回业务真实的源站 IP,确保解析永不中断。
|
||||||
|
|
||||||
|
### 4.2 解决 HTTPS SNI 隐匿问题
|
||||||
|
在使用 IP 直连(如 `https://1.2.3.4/path`)访问 HTTPS 业务时,传统的网络库会因为无法获取正确的 Host 导致 SSL 握手失败。
|
||||||
|
|
||||||
|
**我们的方案:**
|
||||||
|
* **配合 CDN 节点**:我们的 CDN 节点已针对 HTTPDNS 进行了特殊适配。
|
||||||
|
* **SDK 自动适配**:SDK 内部集成了标准适配器。在您的代码中,只需配合解析出的 IP 设置 HTTP 请求头的 `Host` 字段,即可透明地完成 SNI 握手,无需复杂的 SSL 改写逻辑。
|
||||||
|
* **稳定性保障**:通过 CDN 节点的全局负载均衡,即使某个节点异常,HTTPDNS 也会实时踢除并将流量导向其他可用节点,确保业务高可用。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 调试与验证
|
||||||
|
|
||||||
|
### 4.1 在线解析测试
|
||||||
|
在左侧菜单进入 **解析测试**:
|
||||||
|
1. 选择您创建的 **应用**、**HTTPDNS 服务域名** 和 **待解析域名**。
|
||||||
|
2. **模拟客户端 IP**(可选):输入特定地区的 IP,验证该地区的解析结果是否符合预期(地域调度验证)。
|
||||||
|
3. 点击 **在线解析**,查看返回的具体 IP 列表。
|
||||||
|
|
||||||
|
### 4.2 访问日志查询
|
||||||
|
在 **访问日志** 中,您可以实时监控解析请求:
|
||||||
|
* 查看各个 AppId 下域名的解析成功率。
|
||||||
|
* 查看请求的来源 IP、耗时以及命中的路由规则。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 获取 SDK
|
||||||
|
在 **应用详情 -> SDK下载** 中:
|
||||||
|
* 您可以下载最新版本的 Android、iOS 或 Flutter SDK 压缩包。
|
||||||
|
* 查看配套的 **SDK 集成文档**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 常见问题 (FAQ)
|
||||||
|
|
||||||
|
* **Q:为什么我设置了记录,解析测试却返回为空?**
|
||||||
|
* A:请检查记录是否已启用;或者检查该域名是否已被添加到对应的 AppId 允许列表下。
|
||||||
|
* **Q:如何应对冷启动时的解析延迟?**
|
||||||
|
* A:建议在 SDK 初始化后调用“解析预热”接口,提前将热点域名加载至缓存。
|
||||||
|
* **Q:SecretKey 泄露了怎么办?**
|
||||||
|
* A:请在应用设置中重置 SecretKey,并在 App 代码中同步更新。
|
||||||
82
EdgeHttpDNS/HTTPDNS管理员配置手册.md
Normal file
82
EdgeHttpDNS/HTTPDNS管理员配置手册.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# Edge HTTPDNS 管理员配置手册
|
||||||
|
|
||||||
|
本文档汇总了 Edge HTTPDNS 的核心配置流程,重点介绍了集群管理、节点在线安装以及多集群调度的详细操作。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 集群管理
|
||||||
|
|
||||||
|
集群是 HTTPDNS 服务的基本组织单元。通过设置“默认”角色,可以实现应用配置的自动关联与 SDK 侧的高可用容灾。
|
||||||
|
|
||||||
|
### 1.1 默认集群角色定义
|
||||||
|
在“集群设置”中,您可以将集群开启为 **“设为默认集群”**,并指派以下角色:
|
||||||
|
|
||||||
|
* **默认主集群**:
|
||||||
|
* **自动关联**:当在控制台“添加应用”时,系统会自动将该应用关联到此默认主集群。
|
||||||
|
* **服务首选**:SDK 初始化后,会优先使用该集群的服务域名进行解析请求。
|
||||||
|
* **默认备用集群**:
|
||||||
|
* **自动容灾**:当默认主集群的节点全部宕机或网络不可达时,SDK 会自动切换至默认备用集群进行解析,确保业务不中断。
|
||||||
|
* **自动关联**:与主集群一样,新应用创建时也会自动关联此备用集群信息。
|
||||||
|
|
||||||
|
> **注意**:同一时刻,系统内仅允许存在一个“默认主集群”和一个“默认备用集群”。新设置的默认集群会自动取消之前的旧设置。
|
||||||
|
|
||||||
|
### 1.2 核心参数配置
|
||||||
|
* **服务域名**:该集群对外提供 HTTPDNS 服务的接入地址。
|
||||||
|
* **降级超时容忍度**:指节点回源查询上游 DNS 时的最大等待时间(单位:毫秒)。若超过此阈值未获得结果,将视作解析失败。该选项可在“集群设置”中统一调整。
|
||||||
|
* **默认 TTL**:解析结果在客户端缓存的缺省时长(默认 30s)。
|
||||||
|
* **TLS 设置**:
|
||||||
|
* **端口绑定**:通常绑定 `443`。
|
||||||
|
* **SSL 证书**:必须配置合法证书,否则 SDK 的 HTTPS 请求将失败。
|
||||||
|
* **TLS 版本**:默认支持 TLS 1.1 及以上版本。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 节点安装与维护
|
||||||
|
|
||||||
|
节点是处理解析请求的实体。Edge HTTPDNS 支持“在线安装”。
|
||||||
|
|
||||||
|
### 2.1 在线安装
|
||||||
|
1. **创建节点**:在集群下点击“创建节点”,填写名称及公网 IP。
|
||||||
|
2. **配置 SSH**:在节点详情中点击“设置 SSH”,输入服务器的 Host、Port 及登录授权。
|
||||||
|
3. **启动安装**:在“安装信息”页面点击 **[开始安装]**。
|
||||||
|
* 自动下发二进制文件及服务脚本。
|
||||||
|
* 自动生成 `configs/api_httpdns.yaml`(包含节点识别需要的 `nodeId` 和 `secret`)。
|
||||||
|
4. **实时状态**:安装成功后,节点状态变为 **[在线]**,控制台每 30 秒更新一次节点的 CPU、内存及负载数据。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 应用与解析规则
|
||||||
|
|
||||||
|
### 3.1 接入应用管理
|
||||||
|
* **AppId/SecretKey**:创建应用后生成的凭证。应用在创建时已根据上述“默认集群”设定自动关联了服务入口。
|
||||||
|
* **鉴权配置**:开启“签名鉴权”可配合 SDK 的 `setSecretKey` 接口,杜绝接口被盗刷风险。
|
||||||
|
|
||||||
|
### 3.2 智能解析策略
|
||||||
|
* **线路/地域匹配**:根据来源运营商和地理位置返回最优 IP。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 调试与监控
|
||||||
|
|
||||||
|
### 4.1 解析测试
|
||||||
|
在 **调试工具 -> 解析测试** 中,您可以模拟客户端请求:
|
||||||
|
* 支持指定 **目标应用** 与 **所属集群**。
|
||||||
|
* 支持模拟 **客户端 IP** 以验证 ECS 掩码及地域调度效果。
|
||||||
|
* 实时展示请求 URL、客户端归属地、命中线路以及解析结果(IP 列表与 TTL)。
|
||||||
|
|
||||||
|
### 4.2 访问日志
|
||||||
|
在 **访问日志** 菜单中,可实时查阅所有终端发起的解析请求:
|
||||||
|
* 记录包括:请求时间、客户端 IP/操作系统、SDK 版本、解析域名、耗时以及最终返回的 IP 结果。
|
||||||
|
* 支持按 AppID、域名、状态(成功/失败)或关键字搜索排查。
|
||||||
|
|
||||||
|
### 4.3 运行日志
|
||||||
|
记录服务端及节点的底层运行事件:
|
||||||
|
* 包括节点心跳、SSL 证书加载、API 连接状态等系统级信息。
|
||||||
|
* 分为 Error、Warning、Info 等级别,是排查节点离线或连接故障的首要工具。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 常见问题
|
||||||
|
|
||||||
|
* **节点与 API 时间偏离**:若节点时间与 API Server 相差超过 30s,会导致鉴权失败(SIGN_INVALID)。请务必开启 NTP 时间同步。
|
||||||
|
* **SDK 无法连接备用集群**:请检查默认备用集群的 SSL 证书是否有效,以及防火墙端口是否开放。
|
||||||
BIN
EdgeHttpDNS/bin/edge-httpdns.exe
Normal file
BIN
EdgeHttpDNS/bin/edge-httpdns.exe
Normal file
Binary file not shown.
124
EdgeHttpDNS/iOS SDK集成文档.md
Normal file
124
EdgeHttpDNS/iOS SDK集成文档.md
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
# iOS SDK 集成文档(Edge HTTPDNS)
|
||||||
|
|
||||||
|
## 1. 版本与依赖
|
||||||
|
|
||||||
|
- SDK 模块:`EdgeHttpDNS/sdk/ios/NewHttpDNS`
|
||||||
|
- 支持系统:iOS 11.0+
|
||||||
|
- 集成方式:
|
||||||
|
- **CocoaPods**:在 `Podfile` 中添加 `pod 'NewHTTPDNS', :path => 'path/to/sdk/ios'`
|
||||||
|
- **手动集成**:将 `NewHttpDNS` 源码或编译后的静态库导入项目,并添加依赖的系统库(Foundation, CFNetwork, SystemConfiguration)。
|
||||||
|
|
||||||
|
## 2. SNI 行为说明(关键)
|
||||||
|
|
||||||
|
1. **/resolve 请求链路**(SDK -> 你的 HTTPDNS 服务域名)
|
||||||
|
- 使用标准 HTTPS 请求。
|
||||||
|
- 默认携带 SNI(用于通过 WAF/CDN 识别服务域名)。
|
||||||
|
|
||||||
|
2. **业务请求链路**(拿到 CDN IP 后通过 `HttpdnsEdgeService` 发起业务 HTTPS)
|
||||||
|
- **IP 直连 + No-SNI**:SDK 会建立与 IP 的连接,并将 `NSURLRequest` 的 `URL` 替换为 IP,同时保留 `Host` 头部为原域名。
|
||||||
|
- **证书校验**:由于清空了 SNI,常规 SNI 校验会跳过,需确保后端节点支持 Host 匹配证书。
|
||||||
|
|
||||||
|
## 3. 初始化 SDK(推荐用 EdgeService 封装)
|
||||||
|
|
||||||
|
### Objective-C
|
||||||
|
|
||||||
|
```objective-c
|
||||||
|
#import <NewHttpDNS/HttpdnsEdgeService.h>
|
||||||
|
|
||||||
|
HttpdnsEdgeService *service = [[HttpdnsEdgeService alloc] initWithAppId:@"your-app-id"
|
||||||
|
primaryServiceHost:@"httpdns.example.com"
|
||||||
|
backupServiceHost:@"httpdns-backup.example.com"
|
||||||
|
servicePort:443
|
||||||
|
signSecret:@"your-sign-secret"];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Swift
|
||||||
|
|
||||||
|
```swift
|
||||||
|
import NewHttpDNS
|
||||||
|
|
||||||
|
let service = HttpdnsEdgeService(appId: "your-app-id",
|
||||||
|
primaryServiceHost: "httpdns.example.com",
|
||||||
|
backupServiceHost: "httpdns-backup.example.com",
|
||||||
|
servicePort: 443,
|
||||||
|
signSecret: "your-sign-secret")
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 解析域名获取 CDN IP
|
||||||
|
|
||||||
|
### Objective-C
|
||||||
|
|
||||||
|
```objective-c
|
||||||
|
[service resolveHost:@"api.example.com"
|
||||||
|
queryType:@"A"
|
||||||
|
completion:^(HttpdnsEdgeResolveResult * _Nullable result, NSError * _Nullable error) {
|
||||||
|
if (result) {
|
||||||
|
NSLog(@"IPv4s: %@", result.ipv4s);
|
||||||
|
NSLog(@"TTL: %ld", (long)result.ttl);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Swift
|
||||||
|
|
||||||
|
```swift
|
||||||
|
service.resolveHost("api.example.com", queryType: "A") { result, error in
|
||||||
|
if let ips = result?.ipv4s {
|
||||||
|
print("Resolved IPs: \(ips)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 业务请求接入方式
|
||||||
|
|
||||||
|
使用 `HttpdnsEdgeService` 提供的 `requestURL` 方法,自动处理 IP 直连与 SNI 隐藏。
|
||||||
|
|
||||||
|
### Objective-C
|
||||||
|
|
||||||
|
```objective-c
|
||||||
|
NSURL *url = [NSURL URLWithString:@"https://api.example.com/path?x=1"];
|
||||||
|
[service requestURL:url
|
||||||
|
method:@"GET"
|
||||||
|
headers:@{@"Custom-Header": @"Value"}
|
||||||
|
body:nil
|
||||||
|
completion:^(NSData * _Nullable data, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error) {
|
||||||
|
if (!error) {
|
||||||
|
NSLog(@"Status Code: %ld", (long)response.statusCode);
|
||||||
|
// 处理 data
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Swift
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let url = URL(string: "https://api.example.com/path?x=1")!
|
||||||
|
service.requestURL(url, method: "GET", headers: ["Custom-Header": "Value"], body: nil) { data, response, error in
|
||||||
|
if let resp = response {
|
||||||
|
print("Status Code: \(resp.statusCode)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. 验证建议
|
||||||
|
|
||||||
|
1. **验证 /resolve**
|
||||||
|
- 观察网络请求,应指向 `https://httpdns.example.com/resolve?appId=...&dn=...`。
|
||||||
|
- 确认返回 JSON 包含 `code: "SUCCESS"`。
|
||||||
|
|
||||||
|
2. **验证业务请求**
|
||||||
|
- 确认请求握手阶段不携带 SNI 扩展。
|
||||||
|
- 确认请求的 TCP 连接目标为解析出的私有 IP/CDN IP。
|
||||||
|
|
||||||
|
## 7. 常见问题
|
||||||
|
|
||||||
|
1. **编译报错:找不到头文件**
|
||||||
|
- 请确认 `Header Search Paths` 包含 SDK 路径。
|
||||||
|
- 如果使用 CocoaPods,请确保执行 `pod install` 并打开 `.xcworkspace`。
|
||||||
|
|
||||||
|
2. **请求返回 403 (Sign Invalid)**
|
||||||
|
- 确认控制台已开启“签名校验”,且本地传入的 `signSecret` 与控制台一致。
|
||||||
|
- 确认系统时间正常(差值超过 30s 可能导致签名失效)。
|
||||||
|
|
||||||
|
3. **HTTPS 证书验证失败**
|
||||||
|
- 检查 `HttpdnsEdgeService` 是否能正确匹配证书,通常是在 No-SNI 模式下通过 `Host` 字段匹配。
|
||||||
@@ -130,8 +130,14 @@ func NewResolveServer(quitCh <-chan struct{}, snapshotManager *SnapshotManager)
|
|||||||
IdleTimeout: 75 * time.Second,
|
IdleTimeout: 75 * time.Second,
|
||||||
MaxHeaderBytes: 8 * 1024,
|
MaxHeaderBytes: 8 * 1024,
|
||||||
TLSConfig: &tls.Config{
|
TLSConfig: &tls.Config{
|
||||||
MinVersion: tls.VersionTLS12,
|
MinVersion: tls.VersionTLS11,
|
||||||
|
// /resolve is a small JSON API; pin to HTTP/1.1 to avoid ALPN/h2 handshake variance
|
||||||
|
// across some clients and middleboxes.
|
||||||
|
NextProtos: []string{"http/1.1"},
|
||||||
},
|
},
|
||||||
|
// Disable automatic HTTP/2 upgrade on TLS listeners. This keeps handshake behavior
|
||||||
|
// deterministic for SDK resolve calls.
|
||||||
|
TLSNextProto: map[string]func(*http.Server, *tls.Conn, http.Handler){},
|
||||||
}
|
}
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|||||||
77
EdgeHttpDNS/sdk/.tmp_android_bundle/README.md
Normal file
77
EdgeHttpDNS/sdk/.tmp_android_bundle/README.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# HTTPDNS Android SDK (SNI Hidden v1.0.0)
|
||||||
|
|
||||||
|
## 1. Init
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.Trust.sdk.android.httpdns.HttpDns;
|
||||||
|
import com.Trust.sdk.android.httpdns.HttpDnsService;
|
||||||
|
import com.Trust.sdk.android.httpdns.InitConfig;
|
||||||
|
|
||||||
|
String appId = "app1f1ndpo9";
|
||||||
|
|
||||||
|
new InitConfig.Builder()
|
||||||
|
.setContext(context)
|
||||||
|
.setPrimaryServiceHost("httpdns-a.example.com")
|
||||||
|
.setBackupServiceHost("httpdns-b.example.com")
|
||||||
|
.setServicePort(443)
|
||||||
|
.setSecretKey("your-sign-secret") // optional if sign is enabled
|
||||||
|
.setEnableHttps(true)
|
||||||
|
.buildFor(appId);
|
||||||
|
|
||||||
|
HttpDnsService httpDnsService = HttpDns.getService(appId);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Resolve
|
||||||
|
|
||||||
|
```java
|
||||||
|
HTTPDNSResult result = httpDnsService.getHttpDnsResultForHostSyncNonBlocking(
|
||||||
|
"api.business.com",
|
||||||
|
RequestIpType.auto,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Official HTTP Adapter (IP + Empty-SNI + Host)
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.Trust.sdk.android.httpdns.network.HttpDnsAdapterOptions;
|
||||||
|
import com.Trust.sdk.android.httpdns.network.HttpDnsAdapterRequest;
|
||||||
|
import com.Trust.sdk.android.httpdns.network.HttpDnsAdapterResponse;
|
||||||
|
import com.Trust.sdk.android.httpdns.network.HttpDnsHttpAdapter;
|
||||||
|
|
||||||
|
HttpDnsHttpAdapter adapter = HttpDns.buildHttpClientAdapter(
|
||||||
|
httpDnsService,
|
||||||
|
new HttpDnsAdapterOptions.Builder()
|
||||||
|
.setConnectTimeoutMillis(3000)
|
||||||
|
.setReadTimeoutMillis(5000)
|
||||||
|
.setRequestIpType(RequestIpType.auto)
|
||||||
|
.setAllowInsecureCertificatesForDebugOnly(false)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
HttpDnsAdapterResponse response = adapter.execute(
|
||||||
|
new HttpDnsAdapterRequest("GET", "https://api.business.com/v1/ping")
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Behavior is fixed:
|
||||||
|
- Resolve by `/resolve`.
|
||||||
|
- Connect to resolved IP over HTTPS.
|
||||||
|
- Keep `Host` header as business domain.
|
||||||
|
- No fallback to domain direct request.
|
||||||
|
|
||||||
|
## 4. Public Errors
|
||||||
|
|
||||||
|
- `NO_IP_AVAILABLE`
|
||||||
|
- `TLS_EMPTY_SNI_FAILED`
|
||||||
|
- `HOST_ROUTE_REJECTED`
|
||||||
|
- `RESOLVE_SIGN_INVALID`
|
||||||
|
|
||||||
|
## 5. Removed Public Params
|
||||||
|
|
||||||
|
Do not use legacy public parameters:
|
||||||
|
- `accountId`
|
||||||
|
- `serviceDomain`
|
||||||
|
- `endpoint`
|
||||||
|
- `aesSecretKey`
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
68
EdgeHttpDNS/sdk/.tmp_android_bundle/proguard-rules.pro
vendored
Normal file
68
EdgeHttpDNS/sdk/.tmp_android_bundle/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# By default, the flags in this file are appended to flags specified
|
||||||
|
# in /Users/ryan/Downloads/adt-bundle-mac-x86_64-20131030/sdk/tools/proguard/proguard-android.txt
|
||||||
|
# You can edit the include path and order by changing the proguardFiles
|
||||||
|
# directive in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# Add any project specific keep options here:
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
-optimizationpasses 3
|
||||||
|
-dontoptimize
|
||||||
|
-dontusemixedcaseclassnames
|
||||||
|
-dontskipnonpubliclibraryclasses
|
||||||
|
-verbose
|
||||||
|
-overloadaggressively
|
||||||
|
#-allowaccessmodification
|
||||||
|
-useuniqueclassmembernames
|
||||||
|
|
||||||
|
-dontwarn com.alibaba.sdk.android.httpdns.net.HttpDnsNetworkDetector
|
||||||
|
|
||||||
|
-keeppackagenames com.alibaba.sdk.android.httpdns
|
||||||
|
-flattenpackagehierarchy com.alibaba.sdk.android.httpdns
|
||||||
|
-keep class com.alibaba.sdk.android.httpdns.HttpDns{*;}
|
||||||
|
-keep interface com.alibaba.sdk.android.httpdns.HttpDnsService{*;}
|
||||||
|
-keep class com.alibaba.sdk.android.httpdns.impl.ErrorImpl{*;}
|
||||||
|
-keep interface com.alibaba.sdk.android.httpdns.SyncService{*;}
|
||||||
|
-keep class com.alibaba.sdk.android.httpdns.InitConfig{*;}
|
||||||
|
-keep class com.alibaba.sdk.android.httpdns.InitConfig$Builder{*;}
|
||||||
|
-keep class com.alibaba.sdk.android.httpdns.RequestIpType{*;}
|
||||||
|
-keep interface com.alibaba.sdk.android.httpdns.DegradationFilter{*;}
|
||||||
|
-keep interface com.alibaba.sdk.android.httpdns.NotUseHttpDnsFilter{*;}
|
||||||
|
-keep interface com.alibaba.sdk.android.httpdns.HttpDnsCallback{*;}
|
||||||
|
-keep class com.alibaba.sdk.android.httpdns.ranking.IPRankingBean{*;}
|
||||||
|
-keep interface com.alibaba.sdk.android.httpdns.ILogger{*;}
|
||||||
|
-keep interface com.alibaba.sdk.android.httpdns.CacheTtlChanger{*;}
|
||||||
|
-keep class com.alibaba.sdk.android.httpdns.NetType{*;}
|
||||||
|
-keepclasseswithmembers class com.alibaba.sdk.android.httpdns.log.HttpDnsLog {
|
||||||
|
public static *** setLogger(***);
|
||||||
|
public static *** removeLogger(***);
|
||||||
|
public static *** enable(***);
|
||||||
|
}
|
||||||
|
-keep class com.alibaba.sdk.android.httpdns.HTTPDNSResult{*;}
|
||||||
|
-keepclasseswithmembers class com.alibaba.sdk.android.httpdns.HttpDnsSettings {
|
||||||
|
public static *** setDailyReport(***);
|
||||||
|
public static *** setNetworkChecker(***);
|
||||||
|
}
|
||||||
|
|
||||||
|
-keep class com.alibaba.sdk.android.httpdns.net.HttpDnsNetworkDetector {
|
||||||
|
public <methods>;
|
||||||
|
public <fields>;
|
||||||
|
}
|
||||||
|
|
||||||
|
-keep interface com.alibaba.sdk.android.httpdns.HttpDnsSettings$NetworkChecker{*;}
|
||||||
|
-keep interface com.alibaba.sdk.android.httpdns.HttpDnsSettings$NetworkDetector{*;}
|
||||||
|
-keep class com.alibaba.sdk.android.httpdns.utils.CommonUtil{
|
||||||
|
public <methods>;
|
||||||
|
public <fields>;
|
||||||
|
}
|
||||||
|
-keep enum com.alibaba.sdk.android.httpdns.Region {*;}
|
||||||
|
-keep class com.alibaba.sdk.android.httpdns.exception.InitException{*;}
|
||||||
@@ -6,20 +6,20 @@ Fetched at (UTC): `2026-02-18T09:31:28Z`
|
|||||||
|
|
||||||
## Android SDK
|
## Android SDK
|
||||||
|
|
||||||
- Upstream repository: `https://github.com/aliyun/alibabacloud-httpdns-android-sdk`
|
- Upstream repository: `https://github.com/TrustAPP/Trustcloud-httpdns-android-sdk`
|
||||||
- Locked commit: `eeb17d677161ec94b5f41a9d6437501ddc24e6d2`
|
- Locked commit: `eeb17d677161ec94b5f41a9d6437501ddc24e6d2`
|
||||||
- Local path: `EdgeHttpDNS/sdk/android`
|
- Local path: `EdgeHttpDNS/sdk/android`
|
||||||
|
|
||||||
## iOS SDK
|
## iOS SDK
|
||||||
|
|
||||||
- Upstream repository: `https://github.com/aliyun/alibabacloud-httpdns-ios-sdk`
|
- Upstream repository: `https://github.com/TrustAPP/Trustcloud-httpdns-ios-sdk`
|
||||||
- Locked commit: `19f5bacd1d1399a00ba654bb72ababb3e91d0a3a`
|
- Locked commit: `19f5bacd1d1399a00ba654bb72ababb3e91d0a3a`
|
||||||
- Local path: `EdgeHttpDNS/sdk/ios`
|
- Local path: `EdgeHttpDNS/sdk/ios`
|
||||||
|
|
||||||
## Flutter Plugin SDK
|
## Flutter Plugin SDK
|
||||||
|
|
||||||
- Upstream repository: `https://github.com/aliyun/alicloud-flutter-demo`
|
- Upstream repository: `https://github.com/TrustAPP/Trust-flutter-demo`
|
||||||
- Locked commit: `588b807e5480d8592c57d439a6b1c52e8c313569`
|
- Locked commit: `588b807e5480d8592c57d439a6b1c52e8c313569`
|
||||||
- Imported subtree: `httpdns_flutter_demo/packages/aliyun_httpdns`
|
- Imported subtree: `httpdns_flutter_demo/packages/TrustAPP_httpdns`
|
||||||
- Local path: `EdgeHttpDNS/sdk/flutter/aliyun_httpdns`
|
- Local path: `EdgeHttpDNS/sdk/flutter/TrustAPP_httpdns`
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
# Third-Party Notices for EdgeHttpDNS SDK Sources
|
# Third-Party Notices for EdgeHttpDNS SDK Sources
|
||||||
|
|
||||||
This directory includes third-party source snapshots imported from Alibaba Cloud open-source repositories.
|
This directory includes third-party source snapshots imported from Trust Cloud open-source repositories.
|
||||||
|
|
||||||
## 1) Android SDK (`EdgeHttpDNS/sdk/android`)
|
## 1) Android SDK (`EdgeHttpDNS/sdk/android`)
|
||||||
|
|
||||||
- Source: `https://github.com/aliyun/alibabacloud-httpdns-android-sdk`
|
- Source: `https://github.com/TrustAPP/Trustcloud-httpdns-android-sdk`
|
||||||
- Commit: `eeb17d677161ec94b5f41a9d6437501ddc24e6d2`
|
- Commit: `eeb17d677161ec94b5f41a9d6437501ddc24e6d2`
|
||||||
- License file in imported source:
|
- License file in imported source:
|
||||||
- `EdgeHttpDNS/sdk/android/LICENSE`
|
- `EdgeHttpDNS/sdk/android/LICENSE`
|
||||||
- Observed license: MIT License
|
- Observed license: MIT License
|
||||||
|
|
||||||
## 2) Flutter plugin (`EdgeHttpDNS/sdk/flutter/aliyun_httpdns`)
|
## 2) Flutter plugin (`EdgeHttpDNS/sdk/flutter/TrustAPP_httpdns`)
|
||||||
|
|
||||||
- Source: `https://github.com/aliyun/alicloud-flutter-demo`
|
- Source: `https://github.com/TrustAPP/Trust-flutter-demo`
|
||||||
- Commit: `588b807e5480d8592c57d439a6b1c52e8c313569`
|
- Commit: `588b807e5480d8592c57d439a6b1c52e8c313569`
|
||||||
- Imported subtree: `httpdns_flutter_demo/packages/aliyun_httpdns`
|
- Imported subtree: `httpdns_flutter_demo/packages/TrustAPP_httpdns`
|
||||||
- License file in imported source:
|
- License file in imported source:
|
||||||
- `EdgeHttpDNS/sdk/flutter/aliyun_httpdns/LICENSE`
|
- `EdgeHttpDNS/sdk/flutter/TrustAPP_httpdns/LICENSE`
|
||||||
- Observed license: MIT License
|
- Observed license: MIT License
|
||||||
|
|
||||||
## 3) iOS SDK (`EdgeHttpDNS/sdk/ios`)
|
## 3) iOS SDK (`EdgeHttpDNS/sdk/ios`)
|
||||||
|
|
||||||
- Source: `https://github.com/aliyun/alibabacloud-httpdns-ios-sdk`
|
- Source: `https://github.com/TrustAPP/Trustcloud-httpdns-ios-sdk`
|
||||||
- Commit: `19f5bacd1d1399a00ba654bb72ababb3e91d0a3a`
|
- Commit: `19f5bacd1d1399a00ba654bb72ababb3e91d0a3a`
|
||||||
- Current status:
|
- Current status:
|
||||||
- No standalone top-level `LICENSE` file was found in the upstream repository snapshot imported here.
|
- No standalone top-level `LICENSE` file was found in the upstream repository snapshot imported here.
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# HTTPDNS Android SDK (SNI Hidden v1.0.0)
|
# HTTPDNS Android SDK (SNI Hidden v1.0.0)
|
||||||
|
|
||||||
## 1. Init
|
## 1. Init
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import com.alibaba.sdk.android.httpdns.HttpDns;
|
import com.Trust.sdk.android.httpdns.HttpDns;
|
||||||
import com.alibaba.sdk.android.httpdns.HttpDnsService;
|
import com.Trust.sdk.android.httpdns.HttpDnsService;
|
||||||
import com.alibaba.sdk.android.httpdns.InitConfig;
|
import com.Trust.sdk.android.httpdns.InitConfig;
|
||||||
|
|
||||||
String appId = "app1f1ndpo9";
|
String appId = "app1f1ndpo9";
|
||||||
|
|
||||||
@@ -35,10 +35,10 @@ HTTPDNSResult result = httpDnsService.getHttpDnsResultForHostSyncNonBlocking(
|
|||||||
## 3. Official HTTP Adapter (IP + Empty-SNI + Host)
|
## 3. Official HTTP Adapter (IP + Empty-SNI + Host)
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import com.alibaba.sdk.android.httpdns.network.HttpDnsAdapterOptions;
|
import com.Trust.sdk.android.httpdns.network.HttpDnsAdapterOptions;
|
||||||
import com.alibaba.sdk.android.httpdns.network.HttpDnsAdapterRequest;
|
import com.Trust.sdk.android.httpdns.network.HttpDnsAdapterRequest;
|
||||||
import com.alibaba.sdk.android.httpdns.network.HttpDnsAdapterResponse;
|
import com.Trust.sdk.android.httpdns.network.HttpDnsAdapterResponse;
|
||||||
import com.alibaba.sdk.android.httpdns.network.HttpDnsHttpAdapter;
|
import com.Trust.sdk.android.httpdns.network.HttpDnsHttpAdapter;
|
||||||
|
|
||||||
HttpDnsHttpAdapter adapter = HttpDns.buildHttpClientAdapter(
|
HttpDnsHttpAdapter adapter = HttpDns.buildHttpClientAdapter(
|
||||||
httpDnsService,
|
httpDnsService,
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ android {
|
|||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
forTest {
|
forTest {
|
||||||
// 注意这里的配置,并不是需要编译forTest的app,而是避免httpdns-sdk在AndroidStudio改为end2end运行测试时 BuildVariants报错
|
// 娉ㄦ剰杩欓噷鐨勯厤缃紝骞朵笉鏄渶瑕佺紪璇慺orTest鐨刟pp锛岃€屾槸閬垮厤httpdns-sdk鍦ˋndroidStudio鏀逛负end2end杩愯娴嬭瘯鏃?BuildVariants鎶ラ敊
|
||||||
initWith release
|
initWith release
|
||||||
debuggable true
|
debuggable true
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
end2end {
|
end2end {
|
||||||
// 注意这里的配置,并不是需要编译end2end的app,而是避免httpdns-sdk在AndroidStudio改为end2end运行测试时 BuildVariants报错
|
// 娉ㄦ剰杩欓噷鐨勯厤缃紝骞朵笉鏄渶瑕佺紪璇慹nd2end鐨刟pp锛岃€屾槸閬垮厤httpdns-sdk鍦ˋndroidStudio鏀逛负end2end杩愯娴嬭瘯鏃?BuildVariants鎶ラ敊
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,10 +33,10 @@ public class HttpDnsActivity extends BaseActivity {
|
|||||||
public static final String SCHEMA_HTTP = "http://";
|
public static final String SCHEMA_HTTP = "http://";
|
||||||
|
|
||||||
public static final String TAOBAO_URL = "www.taobao.com";
|
public static final String TAOBAO_URL = "www.taobao.com";
|
||||||
public static final String ALIYUN_URL = "www.aliyun.com";
|
public static final String Aliyun_URL = "www.Aliyun.com";
|
||||||
|
|
||||||
public static final String[] hosts = new String[]{
|
public static final String[] hosts = new String[]{
|
||||||
TAOBAO_URL, ALIYUN_URL
|
TAOBAO_URL, Aliyun_URL
|
||||||
};
|
};
|
||||||
|
|
||||||
public static String getUrl(String schema, String host) {
|
public static String getUrl(String schema, String host) {
|
||||||
@@ -44,15 +44,15 @@ public class HttpDnsActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 要请求的schema
|
* 瑕佽姹傜殑schema
|
||||||
*/
|
*/
|
||||||
private String schema = SCHEMA_HTTPS;
|
private String schema = SCHEMA_HTTPS;
|
||||||
/**
|
/**
|
||||||
* 要请求的域名
|
* 瑕佽姹傜殑鍩熷悕
|
||||||
*/
|
*/
|
||||||
private String host = ALIYUN_URL;
|
private String host = Aliyun_URL;
|
||||||
/**
|
/**
|
||||||
* 要解析的ip类型
|
* 瑕佽В鏋愮殑ip绫诲瀷
|
||||||
*/
|
*/
|
||||||
private RequestIpType requestIpType = RequestIpType.v4;
|
private RequestIpType requestIpType = RequestIpType.v4;
|
||||||
|
|
||||||
@@ -69,77 +69,77 @@ public class HttpDnsActivity extends BaseActivity {
|
|||||||
okHttpRequest = new OkHttpRequest(this);
|
okHttpRequest = new OkHttpRequest(this);
|
||||||
networkRequest = httpUrlConnectionRequest;
|
networkRequest = httpUrlConnectionRequest;
|
||||||
|
|
||||||
addFourButton("切换实例", v -> {
|
addFourButton("鍒囨崲瀹炰緥", v -> {
|
||||||
MyApp.getInstance().changeHolder();
|
MyApp.getInstance().changeHolder();
|
||||||
sendLog("httpdns实例已切换");
|
sendLog("httpdns瀹炰緥宸插垏鎹?);
|
||||||
}, "获取配置", v -> {
|
}, "鑾峰彇閰嶇疆", v -> {
|
||||||
sendLog(MyApp.getInstance().getCurrentHolder().getCurrentConfig());
|
sendLog(MyApp.getInstance().getCurrentHolder().getCurrentConfig());
|
||||||
sendLog("要解析的域名是" + host);
|
sendLog("瑕佽В鏋愮殑鍩熷悕鏄? + host);
|
||||||
sendLog("要解析的ip类型是" + requestIpType.name());
|
sendLog("瑕佽В鏋愮殑ip绫诲瀷鏄? + requestIpType.name());
|
||||||
sendLog("要模拟请求的url是" + getUrl(schema, host));
|
sendLog("瑕佹ā鎷熻姹傜殑url鏄? + getUrl(schema, host));
|
||||||
sendLog("模拟请求的网络框架是" + (networkRequest == httpUrlConnectionRequest ? " HttpUrlConnection" : "OkHttp"));
|
sendLog("妯℃嫙璇锋眰鐨勭綉缁滄鏋舵槸" + (networkRequest == httpUrlConnectionRequest ? " HttpUrlConnection" : "OkHttp"));
|
||||||
}, "清除配置缓存", v -> {
|
}, "娓呴櫎閰嶇疆缂撳瓨", v -> {
|
||||||
MyApp.getInstance().getCurrentHolder().cleanSp();
|
MyApp.getInstance().getCurrentHolder().cleanSp();
|
||||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "配置缓存清除, 重启生效");
|
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "閰嶇疆缂撳瓨娓呴櫎, 閲嶅惎鐢熸晥");
|
||||||
}, "清除日志", v -> cleanLog());
|
}, "娓呴櫎鏃ュ織", v -> cleanLog());
|
||||||
|
|
||||||
addTwoButton("开启https", v -> {
|
addTwoButton("寮€鍚痟ttps", v -> {
|
||||||
MyApp.getInstance().getCurrentHolder().setEnableHttps(true);
|
MyApp.getInstance().getCurrentHolder().setEnableHttps(true);
|
||||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "开启https");
|
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "寮€鍚痟ttps");
|
||||||
}, "关闭https", v -> {
|
}, "鍏抽棴https", v -> {
|
||||||
MyApp.getInstance().getCurrentHolder().setEnableHttps(false);
|
MyApp.getInstance().getCurrentHolder().setEnableHttps(false);
|
||||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "关闭https");
|
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "鍏抽棴https");
|
||||||
});
|
});
|
||||||
|
|
||||||
addTwoButton("允许过期IP", v -> {
|
addTwoButton("鍏佽杩囨湡IP", v -> {
|
||||||
MyApp.getInstance().getCurrentHolder().setEnableExpiredIp(true);
|
MyApp.getInstance().getCurrentHolder().setEnableExpiredIp(true);
|
||||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "允许过期IP");
|
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "鍏佽杩囨湡IP");
|
||||||
}, "不允许过期IP", v -> {
|
}, "涓嶅厑璁歌繃鏈烮P", v -> {
|
||||||
MyApp.getInstance().getCurrentHolder().setEnableExpiredIp(false);
|
MyApp.getInstance().getCurrentHolder().setEnableExpiredIp(false);
|
||||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "不允许过期IP");
|
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "涓嶅厑璁歌繃鏈烮P");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
addTwoButton("允许持久化缓存", v -> {
|
addTwoButton("鍏佽鎸佷箙鍖栫紦瀛?, v -> {
|
||||||
MyApp.getInstance().getCurrentHolder().setEnableCacheIp(true);
|
MyApp.getInstance().getCurrentHolder().setEnableCacheIp(true);
|
||||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "允许持久化缓存");
|
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "鍏佽鎸佷箙鍖栫紦瀛?);
|
||||||
}, "不允许持久化缓存", v -> {
|
}, "涓嶅厑璁告寔涔呭寲缂撳瓨", v -> {
|
||||||
MyApp.getInstance().getCurrentHolder().setEnableCacheIp(false);
|
MyApp.getInstance().getCurrentHolder().setEnableCacheIp(false);
|
||||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "不允许持久化缓存");
|
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "涓嶅厑璁告寔涔呭寲缂撳瓨");
|
||||||
});
|
});
|
||||||
|
|
||||||
addThreeButton("设置中国大陆", v -> {
|
addThreeButton("璁剧疆涓浗澶ч檰", v -> {
|
||||||
MyApp.getInstance().getCurrentHolder().setRegion(null);
|
MyApp.getInstance().getCurrentHolder().setRegion(null);
|
||||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "切换到中国大陆");
|
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "鍒囨崲鍒颁腑鍥藉ぇ闄?);
|
||||||
}, "设置中国香港", v -> {
|
}, "璁剧疆涓浗棣欐腐", v -> {
|
||||||
MyApp.getInstance().getCurrentHolder().setRegion("hk");
|
MyApp.getInstance().getCurrentHolder().setRegion("hk");
|
||||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "切换到中国香港");
|
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "鍒囨崲鍒颁腑鍥介娓?);
|
||||||
}, "设置新加坡", v -> {
|
}, "璁剧疆鏂板姞鍧?, v -> {
|
||||||
MyApp.getInstance().getCurrentHolder().setRegion("sg");
|
MyApp.getInstance().getCurrentHolder().setRegion("sg");
|
||||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "切换到新加坡");
|
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "鍒囨崲鍒版柊鍔犲潯");
|
||||||
});
|
});
|
||||||
|
|
||||||
addEditTextButton("超时时长 ms", "设置超时ms", view -> {
|
addEditTextButton("瓒呮椂鏃堕暱 ms", "璁剧疆瓒呮椂ms", view -> {
|
||||||
EditText et = (EditText) view;
|
EditText et = (EditText) view;
|
||||||
int timeout = Integer.parseInt(et.getEditableText().toString());
|
int timeout = Integer.parseInt(et.getEditableText().toString());
|
||||||
MyApp.getInstance().getCurrentHolder().setTimeout(timeout);
|
MyApp.getInstance().getCurrentHolder().setTimeout(timeout);
|
||||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "设置超时 " + timeout);
|
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "璁剧疆瓒呮椂 " + timeout);
|
||||||
});
|
});
|
||||||
|
|
||||||
addTwoButton("开启降级", v -> {
|
addTwoButton("寮€鍚檷绾?, v -> {
|
||||||
// 注意:降级过滤器现在需要通过InitConfig设置,重启应用生效
|
// 娉ㄦ剰锛氶檷绾ц繃婊ゅ櫒鐜板湪闇€瑕侀€氳繃InitConfig璁剧疆锛岄噸鍚簲鐢ㄧ敓鏁?
|
||||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "降级功能需要通过InitConfig配置");
|
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "闄嶇骇鍔熻兘闇€瑕侀€氳繃InitConfig閰嶇疆");
|
||||||
}, "关闭降级", v -> {
|
}, "鍏抽棴闄嶇骇", v -> {
|
||||||
// 注意:降级过滤器现在需要通过InitConfig设置,重启应用生效
|
// 娉ㄦ剰锛氶檷绾ц繃婊ゅ櫒鐜板湪闇€瑕侀€氳繃InitConfig璁剧疆锛岄噸鍚簲鐢ㄧ敓鏁?
|
||||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "降级功能需要通过InitConfig配置");
|
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "闄嶇骇鍔熻兘闇€瑕侀€氳繃InitConfig閰嶇疆");
|
||||||
});
|
});
|
||||||
|
|
||||||
addTwoButton("开启网络变化后预解析", v -> {
|
addTwoButton("寮€鍚綉缁滃彉鍖栧悗棰勮В鏋?, v -> {
|
||||||
// 注意:此功能现在需要通过InitConfig设置,重启应用生效
|
// 娉ㄦ剰锛氭鍔熻兘鐜板湪闇€瑕侀€氳繃InitConfig璁剧疆锛岄噸鍚簲鐢ㄧ敓鏁?
|
||||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "网络变化后预解析需要通过InitConfig配置");
|
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "缃戠粶鍙樺寲鍚庨瑙f瀽闇€瑕侀€氳繃InitConfig閰嶇疆");
|
||||||
}, "关闭网络变化后预解析", v -> {
|
}, "鍏抽棴缃戠粶鍙樺寲鍚庨瑙f瀽", v -> {
|
||||||
// 注意:此功能现在需要通过InitConfig设置,重启应用生效
|
// 娉ㄦ剰锛氭鍔熻兘鐜板湪闇€瑕侀€氳繃InitConfig璁剧疆锛岄噸鍚簲鐢ㄧ敓鏁?
|
||||||
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "网络变化后预解析需要通过InitConfig配置");
|
sendLog(MyApp.getInstance().getCurrentHolder().getAccountId() + "缃戠粶鍙樺寲鍚庨瑙f瀽闇€瑕侀€氳繃InitConfig閰嶇疆");
|
||||||
});
|
});
|
||||||
|
|
||||||
addView(R.layout.item_autocomplete_edittext_button, view -> {
|
addView(R.layout.item_autocomplete_edittext_button, view -> {
|
||||||
@@ -147,21 +147,21 @@ public class HttpDnsActivity extends BaseActivity {
|
|||||||
final EditText etOne = view.findViewById(R.id.etOne);
|
final EditText etOne = view.findViewById(R.id.etOne);
|
||||||
Button btnOne = view.findViewById(R.id.btnOne);
|
Button btnOne = view.findViewById(R.id.btnOne);
|
||||||
|
|
||||||
actvOne.setHint("请输入域名");
|
actvOne.setHint("璇疯緭鍏ュ煙鍚?);
|
||||||
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getApplicationContext(),
|
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getApplicationContext(),
|
||||||
android.R.layout.simple_dropdown_item_1line, hosts);
|
android.R.layout.simple_dropdown_item_1line, hosts);
|
||||||
actvOne.setAdapter(adapter);
|
actvOne.setAdapter(adapter);
|
||||||
|
|
||||||
etOne.setHint("请输入自定义ttl");
|
etOne.setHint("璇疯緭鍏ヨ嚜瀹氫箟ttl");
|
||||||
|
|
||||||
btnOne.setText("指定域名ttl s");
|
btnOne.setText("鎸囧畾鍩熷悕ttl s");
|
||||||
btnOne.setOnClickListener(new View.OnClickListener() {
|
btnOne.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
String host = actvOne.getEditableText().toString();
|
String host = actvOne.getEditableText().toString();
|
||||||
int ttl = Integer.parseInt(etOne.getEditableText().toString());
|
int ttl = Integer.parseInt(etOne.getEditableText().toString());
|
||||||
MyApp.getInstance().getCurrentHolder().setHostTtl(host, ttl);
|
MyApp.getInstance().getCurrentHolder().setHostTtl(host, ttl);
|
||||||
sendLog("指定域名" + host + "的ttl为" + ttl + "秒");
|
sendLog("鎸囧畾鍩熷悕" + host + "鐨則tl涓? + ttl + "绉?);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -171,124 +171,124 @@ public class HttpDnsActivity extends BaseActivity {
|
|||||||
final EditText etOne = view.findViewById(R.id.etOne);
|
final EditText etOne = view.findViewById(R.id.etOne);
|
||||||
Button btnOne = view.findViewById(R.id.btnOne);
|
Button btnOne = view.findViewById(R.id.btnOne);
|
||||||
|
|
||||||
actvOne.setHint("域名");
|
actvOne.setHint("鍩熷悕");
|
||||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(getApplicationContext(),
|
ArrayAdapter<String> adapter = new ArrayAdapter<>(getApplicationContext(),
|
||||||
android.R.layout.simple_dropdown_item_1line, hosts);
|
android.R.layout.simple_dropdown_item_1line, hosts);
|
||||||
actvOne.setAdapter(adapter);
|
actvOne.setAdapter(adapter);
|
||||||
|
|
||||||
etOne.setHint("请输入端口");
|
etOne.setHint("璇疯緭鍏ョ鍙?);
|
||||||
|
|
||||||
btnOne.setText("添加ipRanking配置");
|
btnOne.setText("娣诲姞ipRanking閰嶇疆");
|
||||||
btnOne.setOnClickListener(new View.OnClickListener() {
|
btnOne.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
String host = actvOne.getEditableText().toString();
|
String host = actvOne.getEditableText().toString();
|
||||||
int port = Integer.parseInt(etOne.getEditableText().toString());
|
int port = Integer.parseInt(etOne.getEditableText().toString());
|
||||||
MyApp.getInstance().getCurrentHolder().addIpProbeItem(new IPRankingBean(host, port));
|
MyApp.getInstance().getCurrentHolder().addIpProbeItem(new IPRankingBean(host, port));
|
||||||
sendLog("添加域名" + host + " 探测");
|
sendLog("娣诲姞鍩熷悕" + host + " 鎺㈡祴");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
addAutoCompleteTextViewButton(hosts, "主站域名", "添加主站域名", view -> {
|
addAutoCompleteTextViewButton(hosts, "涓荤珯鍩熷悕", "娣诲姞涓荤珯鍩熷悕", view -> {
|
||||||
AutoCompleteTextView actvOne = (AutoCompleteTextView) view;
|
AutoCompleteTextView actvOne = (AutoCompleteTextView) view;
|
||||||
String host = actvOne.getEditableText().toString();
|
String host = actvOne.getEditableText().toString();
|
||||||
MyApp.getInstance().getCurrentHolder().addHostWithFixedIp(host);
|
MyApp.getInstance().getCurrentHolder().addHostWithFixedIp(host);
|
||||||
sendLog("添加主站域名" + host);
|
sendLog("娣诲姞涓荤珯鍩熷悕" + host);
|
||||||
});
|
});
|
||||||
|
|
||||||
addAutoCompleteTextViewButton(hosts, "域名", "删除指定域名的缓存", view -> {
|
addAutoCompleteTextViewButton(hosts, "鍩熷悕", "鍒犻櫎鎸囧畾鍩熷悕鐨勭紦瀛?, view -> {
|
||||||
AutoCompleteTextView actvOne = (AutoCompleteTextView) view;
|
AutoCompleteTextView actvOne = (AutoCompleteTextView) view;
|
||||||
String host = actvOne.getEditableText().toString();
|
String host = actvOne.getEditableText().toString();
|
||||||
ArrayList<String> list = new ArrayList<>();
|
ArrayList<String> list = new ArrayList<>();
|
||||||
list.add(host);
|
list.add(host);
|
||||||
MyApp.getInstance().getService().cleanHostCache(list);
|
MyApp.getInstance().getService().cleanHostCache(list);
|
||||||
sendLog("清除指定host的缓存" + host);
|
sendLog("娓呴櫎鎸囧畾host鐨勭紦瀛? + host);
|
||||||
});
|
});
|
||||||
|
|
||||||
addOneButton("清除所有缓存", v -> {
|
addOneButton("娓呴櫎鎵€鏈夌紦瀛?, v -> {
|
||||||
MyApp.getInstance().getService().cleanHostCache(null);
|
MyApp.getInstance().getService().cleanHostCache(null);
|
||||||
sendLog("清除所有缓存");
|
sendLog("娓呴櫎鎵€鏈夌紦瀛?);
|
||||||
});
|
});
|
||||||
|
|
||||||
addFourButton("获取当前网络状态", v -> {
|
addFourButton("鑾峰彇褰撳墠缃戠粶鐘舵€?, v -> {
|
||||||
NetType type = HttpDnsNetworkDetector.getInstance().getNetType(getApplicationContext());
|
NetType type = HttpDnsNetworkDetector.getInstance().getNetType(getApplicationContext());
|
||||||
sendLog("获取网络状态 " + type.name());
|
sendLog("鑾峰彇缃戠粶鐘舵€?" + type.name());
|
||||||
}, "禁用网络状态缓存", v -> {
|
}, "绂佺敤缃戠粶鐘舵€佺紦瀛?, v -> {
|
||||||
HttpDnsNetworkDetector.getInstance().disableCache(true);
|
HttpDnsNetworkDetector.getInstance().disableCache(true);
|
||||||
sendLog("网络状态 禁用缓存 ");
|
sendLog("缃戠粶鐘舵€?绂佺敤缂撳瓨 ");
|
||||||
}, "开启网络状态缓存", v -> {
|
}, "寮€鍚綉缁滅姸鎬佺紦瀛?, v -> {
|
||||||
HttpDnsNetworkDetector.getInstance().disableCache(false);
|
HttpDnsNetworkDetector.getInstance().disableCache(false);
|
||||||
sendLog("网络状态 开启缓存 ");
|
sendLog("缃戠粶鐘舵€?寮€鍚紦瀛?");
|
||||||
}, "清除网络状态缓存", v -> {
|
}, "娓呴櫎缃戠粶鐘舵€佺紦瀛?, v -> {
|
||||||
HttpDnsNetworkDetector.getInstance().cleanCache(false);
|
HttpDnsNetworkDetector.getInstance().cleanCache(false);
|
||||||
sendLog("网络状态清除缓存 ");
|
sendLog("缃戠粶鐘舵€佹竻闄ょ紦瀛?");
|
||||||
});
|
});
|
||||||
|
|
||||||
addTwoButton("禁止读取IP", v -> {
|
addTwoButton("绂佹璇诲彇IP", v -> {
|
||||||
HttpDnsNetworkDetector.getInstance().setCheckInterface(false);
|
HttpDnsNetworkDetector.getInstance().setCheckInterface(false);
|
||||||
sendLog("查询网络状态时 禁止读取IP");
|
sendLog("鏌ヨ缃戠粶鐘舵€佹椂 绂佹璇诲彇IP");
|
||||||
}, "允许读取IP", v -> {
|
}, "鍏佽璇诲彇IP", v -> {
|
||||||
HttpDnsNetworkDetector.getInstance().setCheckInterface(true);
|
HttpDnsNetworkDetector.getInstance().setCheckInterface(true);
|
||||||
sendLog("查询网络状态时 允许读取IP");
|
sendLog("鏌ヨ缃戠粶鐘舵€佹椂 鍏佽璇诲彇IP");
|
||||||
});
|
});
|
||||||
|
|
||||||
addAutoCompleteTextViewButton(hosts, "域名", "设置检测网络使用的域名", view -> {
|
addAutoCompleteTextViewButton(hosts, "鍩熷悕", "璁剧疆妫€娴嬬綉缁滀娇鐢ㄧ殑鍩熷悕", view -> {
|
||||||
AutoCompleteTextView actvOne = (AutoCompleteTextView) view;
|
AutoCompleteTextView actvOne = (AutoCompleteTextView) view;
|
||||||
String host = actvOne.getEditableText().toString();
|
String host = actvOne.getEditableText().toString();
|
||||||
HttpDnsNetworkDetector.getInstance().setHostToCheckNetType(host);
|
HttpDnsNetworkDetector.getInstance().setHostToCheckNetType(host);
|
||||||
sendLog("设置检测网络状态使用的域名为" + host);
|
sendLog("璁剧疆妫€娴嬬綉缁滅姸鎬佷娇鐢ㄧ殑鍩熷悕涓? + host);
|
||||||
});
|
});
|
||||||
|
|
||||||
addTwoButton("模拟请求使用https请求", v -> {
|
addTwoButton("妯℃嫙璇锋眰浣跨敤https璇锋眰", v -> {
|
||||||
schema = SCHEMA_HTTPS;
|
schema = SCHEMA_HTTPS;
|
||||||
sendLog("测试url使用https");
|
sendLog("娴嬭瘯url浣跨敤https");
|
||||||
}, "模拟请求使用http请求", v -> {
|
}, "妯℃嫙璇锋眰浣跨敤http璇锋眰", v -> {
|
||||||
schema = SCHEMA_HTTP;
|
schema = SCHEMA_HTTP;
|
||||||
sendLog("测试url使用http");
|
sendLog("娴嬭瘯url浣跨敤http");
|
||||||
});
|
});
|
||||||
|
|
||||||
addTwoButton("HttpUrlConnection", v -> {
|
addTwoButton("HttpUrlConnection", v -> {
|
||||||
networkRequest = httpUrlConnectionRequest;
|
networkRequest = httpUrlConnectionRequest;
|
||||||
sendLog("指定网络实现方式为HttpUrlConnection");
|
sendLog("鎸囧畾缃戠粶瀹炵幇鏂瑰紡涓篐ttpUrlConnection");
|
||||||
}, "Okhttp", v -> {
|
}, "Okhttp", v -> {
|
||||||
networkRequest = okHttpRequest;
|
networkRequest = okHttpRequest;
|
||||||
sendLog("指定网络实现方式为okhttp");
|
sendLog("鎸囧畾缃戠粶瀹炵幇鏂瑰紡涓簅khttp");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
addFourButton("指定v4", v -> {
|
addFourButton("鎸囧畾v4", v -> {
|
||||||
requestIpType = RequestIpType.v4;
|
requestIpType = RequestIpType.v4;
|
||||||
sendLog("要解析的IP类型指定为ipv4");
|
sendLog("瑕佽В鏋愮殑IP绫诲瀷鎸囧畾涓篿pv4");
|
||||||
}, "指定v6", v -> {
|
}, "鎸囧畾v6", v -> {
|
||||||
requestIpType = RequestIpType.v6;
|
requestIpType = RequestIpType.v6;
|
||||||
sendLog("要解析的IP类型指定为ipv6");
|
sendLog("瑕佽В鏋愮殑IP绫诲瀷鎸囧畾涓篿pv6");
|
||||||
}, "都解析", v -> {
|
}, "閮借В鏋?, v -> {
|
||||||
requestIpType = RequestIpType.both;
|
requestIpType = RequestIpType.both;
|
||||||
sendLog("要解析的IP类型指定为ipv4和ipv6");
|
sendLog("瑕佽В鏋愮殑IP绫诲瀷鎸囧畾涓篿pv4鍜宨pv6");
|
||||||
}, "自动判断", v -> {
|
}, "鑷姩鍒ゆ柇", v -> {
|
||||||
requestIpType = RequestIpType.auto;
|
requestIpType = RequestIpType.auto;
|
||||||
sendLog("要解析的IP类型根据网络情况自动判断");
|
sendLog("瑕佽В鏋愮殑IP绫诲瀷鏍规嵁缃戠粶鎯呭喌鑷姩鍒ゆ柇");
|
||||||
});
|
});
|
||||||
|
|
||||||
addAutoCompleteTextViewButton(hosts, "域名", "指定要解析的域名", new OnButtonClick() {
|
addAutoCompleteTextViewButton(hosts, "鍩熷悕", "鎸囧畾瑕佽В鏋愮殑鍩熷悕", new OnButtonClick() {
|
||||||
@Override
|
@Override
|
||||||
public void onBtnClick(View view) {
|
public void onBtnClick(View view) {
|
||||||
AutoCompleteTextView actvOne = (AutoCompleteTextView) view;
|
AutoCompleteTextView actvOne = (AutoCompleteTextView) view;
|
||||||
host = actvOne.getEditableText().toString();
|
host = actvOne.getEditableText().toString();
|
||||||
sendLog("要解析的域名" + host);
|
sendLog("瑕佽В鏋愮殑鍩熷悕" + host);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
addTwoButton("异步解析", v -> worker.execute(new Runnable() {
|
addTwoButton("寮傛瑙f瀽", v -> worker.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
sendLog("开始发起网络请求");
|
sendLog("寮€濮嬪彂璧风綉缁滆姹?);
|
||||||
sendLog("网络实现方式为" + (networkRequest == httpUrlConnectionRequest ? "HttpUrlConnection" : "okhttp"));
|
sendLog("缃戠粶瀹炵幇鏂瑰紡涓? + (networkRequest == httpUrlConnectionRequest ? "HttpUrlConnection" : "okhttp"));
|
||||||
String url = getUrl(schema, host);
|
String url = getUrl(schema, host);
|
||||||
sendLog("url is " + url);
|
sendLog("url is " + url);
|
||||||
sendLog("httpdns 使用 异步解析api");
|
sendLog("httpdns 浣跨敤 寮傛瑙f瀽api");
|
||||||
sendLog("指定解析ip类型为" + requestIpType.name());
|
sendLog("鎸囧畾瑙f瀽ip绫诲瀷涓? + requestIpType.name());
|
||||||
networkRequest.updateHttpDnsConfig(true, requestIpType);
|
networkRequest.updateHttpDnsConfig(true, requestIpType);
|
||||||
try {
|
try {
|
||||||
String response = networkRequest.httpGet(url);
|
String response = networkRequest.httpGet(url);
|
||||||
@@ -298,40 +298,40 @@ public class HttpDnsActivity extends BaseActivity {
|
|||||||
response = response.substring(0, 30) + "...";
|
response = response.substring(0, 30) + "...";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sendLog("请求结束 response is " + response + " 完整记录请看logcat日志");
|
sendLog("璇锋眰缁撴潫 response is " + response + " 瀹屾暣璁板綍璇风湅logcat鏃ュ織");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
sendLog("请求结束 发生异常 " + e.getClass().getName() + e.getMessage() + " 完整记录请看logcat日志");
|
sendLog("璇锋眰缁撴潫 鍙戠敓寮傚父 " + e.getClass().getName() + e.getMessage() + " 瀹屾暣璁板綍璇风湅logcat鏃ュ織");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}), "同步解析", v -> worker.execute(() -> {
|
}), "鍚屾瑙f瀽", v -> worker.execute(() -> {
|
||||||
sendLog("开始发起网络请求");
|
sendLog("寮€濮嬪彂璧风綉缁滆姹?);
|
||||||
sendLog("网络实现方式为" + (networkRequest == httpUrlConnectionRequest ? "HttpUrlConnection" : "okhttp"));
|
sendLog("缃戠粶瀹炵幇鏂瑰紡涓? + (networkRequest == httpUrlConnectionRequest ? "HttpUrlConnection" : "okhttp"));
|
||||||
String url = getUrl(schema, host);
|
String url = getUrl(schema, host);
|
||||||
sendLog("url is " + url);
|
sendLog("url is " + url);
|
||||||
sendLog("httpdns 使用 同步解析api");
|
sendLog("httpdns 浣跨敤 鍚屾瑙f瀽api");
|
||||||
sendLog("指定解析ip类型为" + requestIpType.name());
|
sendLog("鎸囧畾瑙f瀽ip绫诲瀷涓? + requestIpType.name());
|
||||||
networkRequest.updateHttpDnsConfig(false, requestIpType);
|
networkRequest.updateHttpDnsConfig(false, requestIpType);
|
||||||
try {
|
try {
|
||||||
String response = networkRequest.httpGet(url);
|
String response = networkRequest.httpGet(url);
|
||||||
if (response != null && response.length() > 30) {
|
if (response != null && response.length() > 30) {
|
||||||
response = response.substring(0, 30) + "...";
|
response = response.substring(0, 30) + "...";
|
||||||
}
|
}
|
||||||
sendLog("请求结束 response is " + response + " 完整记录请看logcat日志");
|
sendLog("璇锋眰缁撴潫 response is " + response + " 瀹屾暣璁板綍璇风湅logcat鏃ュ織");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
sendLog("请求结束 发生异常 " + e.getClass().getName() + e.getMessage() + " 完整记录请看logcat日志");
|
sendLog("璇锋眰缁撴潫 鍙戠敓寮傚父 " + e.getClass().getName() + e.getMessage() + " 瀹屾暣璁板綍璇风湅logcat鏃ュ織");
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
addThreeButton("发起预解析", v -> worker.execute(() -> {
|
addThreeButton("鍙戣捣棰勮В鏋?, v -> worker.execute(() -> {
|
||||||
ArrayList<String> tmp = new ArrayList<>();
|
ArrayList<String> tmp = new ArrayList<>();
|
||||||
Collections.addAll(tmp, hosts);
|
Collections.addAll(tmp, hosts);
|
||||||
MyApp.getInstance().getService().setPreResolveHosts(tmp, requestIpType);
|
MyApp.getInstance().getService().setPreResolveHosts(tmp, requestIpType);
|
||||||
sendLog("已发起预解析请求");
|
sendLog("宸插彂璧烽瑙f瀽璇锋眰");
|
||||||
}), "跳转SDNS测试界面",
|
}), "璺宠浆SDNS娴嬭瘯鐣岄潰",
|
||||||
v -> startActivity(new Intent(HttpDnsActivity.this, SDNSActivity.class)), "跳转Webview测试界面", new View.OnClickListener() {
|
v -> startActivity(new Intent(HttpDnsActivity.this, SDNSActivity.class)), "璺宠浆Webview娴嬭瘯鐣岄潰", new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
startActivity(new Intent(HttpDnsActivity.this, WebViewActivity.class));
|
startActivity(new Intent(HttpDnsActivity.this, WebViewActivity.class));
|
||||||
@@ -340,17 +340,18 @@ public class HttpDnsActivity extends BaseActivity {
|
|||||||
|
|
||||||
|
|
||||||
final String[] validHosts = new String[]{
|
final String[] validHosts = new String[]{
|
||||||
"www.aliyun.com",
|
"www.Aliyun.com",
|
||||||
"www.taobao.com"
|
"www.taobao.com"
|
||||||
};
|
};
|
||||||
|
|
||||||
addTwoButton("并发异步请求", v -> {
|
addTwoButton("骞跺彂寮傛璇锋眰", v -> {
|
||||||
ThreadUtil.multiThreadTest(validHosts, 100, 20, 10 * 60 * 1000, true, requestIpType);
|
ThreadUtil.multiThreadTest(validHosts, 100, 20, 10 * 60 * 1000, true, requestIpType);
|
||||||
sendLog("异步api并发测试开始,大约耗时10分钟,请最后查看logcat日志,确认结果,建议关闭httpdns日志,避免日志量过大");
|
sendLog("寮傛api骞跺彂娴嬭瘯寮€濮嬶紝澶х害鑰楁椂10鍒嗛挓锛岃鏈€鍚庢煡鐪媗ogcat鏃ュ織锛岀‘璁ょ粨鏋滐紝寤鸿鍏抽棴httpdns鏃ュ織锛岄伩鍏嶆棩蹇楅噺杩囧ぇ");
|
||||||
}, "并发同步请求", v -> {
|
}, "骞跺彂鍚屾璇锋眰", v -> {
|
||||||
ThreadUtil.multiThreadTest(validHosts, 100, 20, 10 * 60 * 1000, false, requestIpType);
|
ThreadUtil.multiThreadTest(validHosts, 100, 20, 10 * 60 * 1000, false, requestIpType);
|
||||||
sendLog("同步api并发测试开始,大约耗时10分钟,请最后查看logcat日志,确认结果,建议关闭httpdns日志,避免日志量过大");
|
sendLog("鍚屾api骞跺彂娴嬭瘯寮€濮嬶紝澶х害鑰楁椂10鍒嗛挓锛岃鏈€鍚庢煡鐪媗ogcat鏃ュ織锛岀‘璁ょ粨鏋滐紝寤鸿鍏抽棴httpdns鏃ュ織锛岄伩鍏嶆棩蹇楅噺杩囧ぇ");
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import java.util.Iterator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存Httpdns 及 相关配置,
|
* 淇濆瓨Httpdns 鍙?鐩稿叧閰嶇疆锛?
|
||||||
* 方便修改
|
* 鏂逛究淇敼
|
||||||
*/
|
*/
|
||||||
public class HttpDnsHolder {
|
public class HttpDnsHolder {
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ public class HttpDnsHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化httpdns的配置
|
* 鍒濆鍖杊ttpdns鐨勯厤缃?
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
*/
|
*/
|
||||||
@@ -94,7 +94,7 @@ public class HttpDnsHolder {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 初始化httpdns 的配置,此步骤需要在第一次获取HttpDnsService实例之前
|
// 鍒濆鍖杊ttpdns 鐨勯厤缃紝姝ゆ楠ら渶瑕佸湪绗竴娆¤幏鍙朒ttpDnsService瀹炰緥涔嬪墠
|
||||||
new InitConfig.Builder()
|
new InitConfig.Builder()
|
||||||
.setEnableExpiredIp(enableExpiredIp)
|
.setEnableExpiredIp(enableExpiredIp)
|
||||||
.setEnableCacheIp(enableCacheIp)
|
.setEnableCacheIp(enableCacheIp)
|
||||||
@@ -126,7 +126,7 @@ public class HttpDnsHolder {
|
|||||||
|
|
||||||
public void setEnableExpiredIp(final boolean enableExpiredIp) {
|
public void setEnableExpiredIp(final boolean enableExpiredIp) {
|
||||||
this.enableExpiredIp = enableExpiredIp;
|
this.enableExpiredIp = enableExpiredIp;
|
||||||
// 注意:此配置需要重启应用生效,因为现在通过InitConfig设置
|
// 娉ㄦ剰锛氭閰嶇疆闇€瑕侀噸鍚簲鐢ㄧ敓鏁堬紝鍥犱负鐜板湪閫氳繃InitConfig璁剧疆
|
||||||
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
||||||
@Override
|
@Override
|
||||||
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
||||||
@@ -137,7 +137,7 @@ public class HttpDnsHolder {
|
|||||||
|
|
||||||
public void setEnableCacheIp(final boolean enableCacheIp) {
|
public void setEnableCacheIp(final boolean enableCacheIp) {
|
||||||
this.enableCacheIp = enableCacheIp;
|
this.enableCacheIp = enableCacheIp;
|
||||||
// 注意:此配置需要重启应用生效,因为现在通过InitConfig设置
|
// 娉ㄦ剰锛氭閰嶇疆闇€瑕侀噸鍚簲鐢ㄧ敓鏁堬紝鍥犱负鐜板湪閫氳繃InitConfig璁剧疆
|
||||||
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
||||||
@Override
|
@Override
|
||||||
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
||||||
@@ -148,7 +148,7 @@ public class HttpDnsHolder {
|
|||||||
|
|
||||||
public void setTimeout(final int timeout) {
|
public void setTimeout(final int timeout) {
|
||||||
this.timeout = timeout;
|
this.timeout = timeout;
|
||||||
// 注意:此配置需要重启应用生效,因为现在通过InitConfig设置
|
// 娉ㄦ剰锛氭閰嶇疆闇€瑕侀噸鍚簲鐢ㄧ敓鏁堬紝鍥犱负鐜板湪閫氳繃InitConfig璁剧疆
|
||||||
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
||||||
@Override
|
@Override
|
||||||
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
||||||
@@ -159,7 +159,7 @@ public class HttpDnsHolder {
|
|||||||
|
|
||||||
public void setEnableHttps(final boolean enableHttps) {
|
public void setEnableHttps(final boolean enableHttps) {
|
||||||
this.enableHttps = enableHttps;
|
this.enableHttps = enableHttps;
|
||||||
// 注意:此配置需要重启应用生效,因为现在通过InitConfig设置
|
// 娉ㄦ剰锛氭閰嶇疆闇€瑕侀噸鍚簲鐢ㄧ敓鏁堬紝鍥犱负鐜板湪閫氳繃InitConfig璁剧疆
|
||||||
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
||||||
@Override
|
@Override
|
||||||
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
||||||
@@ -184,7 +184,7 @@ public class HttpDnsHolder {
|
|||||||
this.hostListWithFixedIp = new ArrayList<>();
|
this.hostListWithFixedIp = new ArrayList<>();
|
||||||
}
|
}
|
||||||
this.hostListWithFixedIp.add(host);
|
this.hostListWithFixedIp.add(host);
|
||||||
// 重启生效
|
// 閲嶅惎鐢熸晥
|
||||||
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
||||||
@Override
|
@Override
|
||||||
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
||||||
@@ -198,7 +198,7 @@ public class HttpDnsHolder {
|
|||||||
this.ipRankingList = new ArrayList<>();
|
this.ipRankingList = new ArrayList<>();
|
||||||
}
|
}
|
||||||
this.ipRankingList.add(ipProbeItem);
|
this.ipRankingList.add(ipProbeItem);
|
||||||
// 注意:此配置需要重启应用生效,因为现在通过InitConfig设置
|
// 娉ㄦ剰锛氭閰嶇疆闇€瑕侀噸鍚簲鐢ㄧ敓鏁堬紝鍥犱负鐜板湪閫氳繃InitConfig璁剧疆
|
||||||
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
SpUtil.writeSp(context, getSpName(accountId), new SpUtil.OnGetSpEditor() {
|
||||||
@Override
|
@Override
|
||||||
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
public void onGetSpEditor(SharedPreferences.Editor editor) {
|
||||||
@@ -232,15 +232,15 @@ public class HttpDnsHolder {
|
|||||||
|
|
||||||
public String getCurrentConfig() {
|
public String getCurrentConfig() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("当前配置 accountId : ").append(accountId).append("\n")
|
sb.append("褰撳墠閰嶇疆 accountId : ").append(accountId).append("\n")
|
||||||
.append("是否允许过期IP : ").append(enableExpiredIp).append("\n")
|
.append("鏄惁鍏佽杩囨湡IP : ").append(enableExpiredIp).append("\n")
|
||||||
.append("是否开启本地缓存 : ").append(enableCacheIp).append("\n")
|
.append("鏄惁寮€鍚湰鍦扮紦瀛?: ").append(enableCacheIp).append("\n")
|
||||||
.append("是否开启HTTPS : ").append(enableHttps).append("\n")
|
.append("鏄惁寮€鍚疕TTPS : ").append(enableHttps).append("\n")
|
||||||
.append("当前region设置 : ").append(region).append("\n")
|
.append("褰撳墠region璁剧疆 : ").append(region).append("\n")
|
||||||
.append("当前超时设置 : ").append(timeout).append("\n")
|
.append("褰撳墠瓒呮椂璁剧疆 : ").append(timeout).append("\n")
|
||||||
.append("当前探测配置 : ").append(convertProbeList(ipRankingList)).append("\n")
|
.append("褰撳墠鎺㈡祴閰嶇疆 : ").append(convertProbeList(ipRankingList)).append("\n")
|
||||||
.append("当前缓存配置 : ").append(convertTtlCache(ttlCache)).append("\n")
|
.append("褰撳墠缂撳瓨閰嶇疆 : ").append(convertTtlCache(ttlCache)).append("\n")
|
||||||
.append("当前主站域名配置 : ").append(convertHostList(hostListWithFixedIp)).append("\n")
|
.append("褰撳墠涓荤珯鍩熷悕閰嶇疆 : ").append(convertHostList(hostListWithFixedIp)).append("\n")
|
||||||
;
|
;
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
@@ -340,3 +340,4 @@ public class HttpDnsHolder {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ public class MyApp extends Application {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final HttpDnsHolder holderA = new HttpDnsHolder("请替换为测试用A实例的accountId", "请替换为测试用A实例的secret");
|
private final HttpDnsHolder holderA = new HttpDnsHolder("璇锋浛鎹负娴嬭瘯鐢ˋ瀹炰緥鐨刟ccountId", "璇锋浛鎹负娴嬭瘯鐢ˋ瀹炰緥鐨剆ecret");
|
||||||
private final HttpDnsHolder holderB = new HttpDnsHolder("请替换为测试用B实例的accountId", null);
|
private final HttpDnsHolder holderB = new HttpDnsHolder("璇锋浛鎹负娴嬭瘯鐢˙瀹炰緥鐨刟ccountId", null);
|
||||||
|
|
||||||
private HttpDnsHolder current = holderA;
|
private HttpDnsHolder current = holderA;
|
||||||
|
|
||||||
@@ -33,9 +33,9 @@ public class MyApp extends Application {
|
|||||||
super.onCreate();
|
super.onCreate();
|
||||||
instance = this;
|
instance = this;
|
||||||
|
|
||||||
// 开启logcat 日志 默认关闭, 开发测试过程中可以开启
|
// 寮€鍚痩ogcat 鏃ュ織 榛樿鍏抽棴, 寮€鍙戞祴璇曡繃绋嬩腑鍙互寮€鍚?
|
||||||
HttpDnsLog.enable(true);
|
HttpDnsLog.enable(true);
|
||||||
// 注入日志接口,接受httpdns的日志,开发测试过程中可以开启, 基础日志需要先enable才生效,一些错误日志不需要
|
// 娉ㄥ叆鏃ュ織鎺ュ彛锛屾帴鍙梙ttpdns鐨勬棩蹇楋紝寮€鍙戞祴璇曡繃绋嬩腑鍙互寮€鍚? 鍩虹鏃ュ織闇€瑕佸厛enable鎵嶇敓鏁堬紝涓€浜涢敊璇棩蹇椾笉闇€瑕?
|
||||||
HttpDnsLog.setLogger(new ILogger() {
|
HttpDnsLog.setLogger(new ILogger() {
|
||||||
@Override
|
@Override
|
||||||
public void log(String msg) {
|
public void log(String msg) {
|
||||||
@@ -43,7 +43,7 @@ public class MyApp extends Application {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 初始化httpdns的配置
|
// 鍒濆鍖杊ttpdns鐨勯厤缃?
|
||||||
holderA.init(this);
|
holderA.init(this);
|
||||||
holderB.init(this);
|
holderB.init(this);
|
||||||
|
|
||||||
@@ -90,3 +90,4 @@ public class MyApp extends Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import com.alibaba.sdk.android.httpdns.RequestIpType;
|
|||||||
public interface NetworkRequest {
|
public interface NetworkRequest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置httpdns的配置
|
* 璁剧疆httpdns鐨勯厤缃?
|
||||||
*/
|
*/
|
||||||
void updateHttpDnsConfig(boolean async, RequestIpType requestIpType);
|
void updateHttpDnsConfig(boolean async, RequestIpType requestIpType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get请求
|
* get璇锋眰
|
||||||
*
|
*
|
||||||
* @param url
|
* @param url
|
||||||
* @return
|
* @return
|
||||||
@@ -18,3 +18,4 @@ public interface NetworkRequest {
|
|||||||
String httpGet(String url) throws Exception;
|
String httpGet(String url) throws Exception;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,17 +11,17 @@ import com.aliyun.ams.httpdns.demo.base.BaseActivity;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import static com.aliyun.ams.httpdns.demo.HttpDnsActivity.ALIYUN_URL;
|
import static com.aliyun.ams.httpdns.demo.HttpDnsActivity.Aliyun_URL;
|
||||||
|
|
||||||
public class SDNSActivity extends BaseActivity {
|
public class SDNSActivity extends BaseActivity {
|
||||||
|
|
||||||
private final HashMap<String, String> globalParams = new HashMap<>();
|
private final HashMap<String, String> globalParams = new HashMap<>();
|
||||||
/**
|
/**
|
||||||
* 要请求的域名
|
* 瑕佽姹傜殑鍩熷悕
|
||||||
*/
|
*/
|
||||||
private String host = HttpDnsActivity.ALIYUN_URL;
|
private String host = HttpDnsActivity.Aliyun_URL;
|
||||||
/**
|
/**
|
||||||
* 要解析的ip类型
|
* 瑕佽В鏋愮殑ip绫诲瀷
|
||||||
*/
|
*/
|
||||||
private RequestIpType requestIpType = RequestIpType.v4;
|
private RequestIpType requestIpType = RequestIpType.v4;
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ public class SDNSActivity extends BaseActivity {
|
|||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
addEditTextEditTextButton("key", "value", "添加全局配置", new OnButtonClickMoreView() {
|
addEditTextEditTextButton("key", "value", "娣诲姞鍏ㄥ眬閰嶇疆", new OnButtonClickMoreView() {
|
||||||
@Override
|
@Override
|
||||||
public void onBtnClick(View[] views) {
|
public void onBtnClick(View[] views) {
|
||||||
EditText etOne = (EditText) views[0];
|
EditText etOne = (EditText) views[0];
|
||||||
@@ -40,56 +40,56 @@ public class SDNSActivity extends BaseActivity {
|
|||||||
|
|
||||||
globalParams.put(key, value);
|
globalParams.put(key, value);
|
||||||
|
|
||||||
// 注意:SDNS全局参数现在需要通过InitConfig设置,重启应用生效
|
// 娉ㄦ剰锛歋DNS鍏ㄥ眬鍙傛暟鐜板湪闇€瑕侀€氳繃InitConfig璁剧疆锛岄噸鍚簲鐢ㄧ敓鏁?
|
||||||
sendLog("添加全局参数 " + key + " : " + value);
|
sendLog("娣诲姞鍏ㄥ眬鍙傛暟 " + key + " : " + value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
addOneButton("清除全局配置", new View.OnClickListener() {
|
addOneButton("娓呴櫎鍏ㄥ眬閰嶇疆", new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
globalParams.clear();
|
globalParams.clear();
|
||||||
// 注意:SDNS全局参数现在需要通过InitConfig设置,重启应用生效
|
// 娉ㄦ剰锛歋DNS鍏ㄥ眬鍙傛暟鐜板湪闇€瑕侀€氳繃InitConfig璁剧疆锛岄噸鍚簲鐢ㄧ敓鏁?
|
||||||
sendLog("清除全局参数");
|
sendLog("娓呴櫎鍏ㄥ眬鍙傛暟");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
addFourButton("指定v4", new View.OnClickListener() {
|
addFourButton("鎸囧畾v4", new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
requestIpType = RequestIpType.v4;
|
requestIpType = RequestIpType.v4;
|
||||||
sendLog("要解析的IP类型指定为ipv4");
|
sendLog("瑕佽В鏋愮殑IP绫诲瀷鎸囧畾涓篿pv4");
|
||||||
}
|
}
|
||||||
}, "指定v6", new View.OnClickListener() {
|
}, "鎸囧畾v6", new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
requestIpType = RequestIpType.v6;
|
requestIpType = RequestIpType.v6;
|
||||||
sendLog("要解析的IP类型指定为ipv6");
|
sendLog("瑕佽В鏋愮殑IP绫诲瀷鎸囧畾涓篿pv6");
|
||||||
}
|
}
|
||||||
}, "都解析", new View.OnClickListener() {
|
}, "閮借В鏋?, new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
requestIpType = RequestIpType.both;
|
requestIpType = RequestIpType.both;
|
||||||
sendLog("要解析的IP类型指定为ipv4和ipv6");
|
sendLog("瑕佽В鏋愮殑IP绫诲瀷鎸囧畾涓篿pv4鍜宨pv6");
|
||||||
}
|
}
|
||||||
}, "自动判断", new View.OnClickListener() {
|
}, "鑷姩鍒ゆ柇", new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
requestIpType = RequestIpType.auto;
|
requestIpType = RequestIpType.auto;
|
||||||
sendLog("要解析的IP类型根据网络情况自动判断");
|
sendLog("瑕佽В鏋愮殑IP绫诲瀷鏍规嵁缃戠粶鎯呭喌鑷姩鍒ゆ柇");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
addAutoCompleteTextViewButton(HttpDnsActivity.hosts, "域名", "指定要解析的域名", new OnButtonClick() {
|
addAutoCompleteTextViewButton(HttpDnsActivity.hosts, "鍩熷悕", "鎸囧畾瑕佽В鏋愮殑鍩熷悕", new OnButtonClick() {
|
||||||
@Override
|
@Override
|
||||||
public void onBtnClick(View view) {
|
public void onBtnClick(View view) {
|
||||||
AutoCompleteTextView actvOne = (AutoCompleteTextView) view;
|
AutoCompleteTextView actvOne = (AutoCompleteTextView) view;
|
||||||
host = actvOne.getEditableText().toString();
|
host = actvOne.getEditableText().toString();
|
||||||
sendLog("要解析的域名" + host);
|
sendLog("瑕佽В鏋愮殑鍩熷悕" + host);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
addEditTextEditTextButton("key", "value", "发起请求", new OnButtonClickMoreView() {
|
addEditTextEditTextButton("key", "value", "鍙戣捣璇锋眰", new OnButtonClickMoreView() {
|
||||||
@Override
|
@Override
|
||||||
public void onBtnClick(View[] views) {
|
public void onBtnClick(View[] views) {
|
||||||
EditText etOne = (EditText) views[0];
|
EditText etOne = (EditText) views[0];
|
||||||
@@ -102,31 +102,32 @@ public class SDNSActivity extends BaseActivity {
|
|||||||
|
|
||||||
map.put(key, value);
|
map.put(key, value);
|
||||||
|
|
||||||
sendLog("发起SDNS请求 requestIpType is " + requestIpType.name());
|
sendLog("鍙戣捣SDNS璇锋眰 requestIpType is " + requestIpType.name());
|
||||||
HTTPDNSResult result = MyApp.getInstance().getService().getIpsByHostAsync(host, requestIpType, map, "测试SDNS");
|
HTTPDNSResult result = MyApp.getInstance().getService().getIpsByHostAsync(host, requestIpType, map, "娴嬭瘯SDNS");
|
||||||
sendLog("结果 " + result);
|
sendLog("缁撴灉 " + result);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
addTwoButton("scale1参数请求", new View.OnClickListener() {
|
addTwoButton("scale1鍙傛暟璇锋眰", new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
HashMap<String, String> map = new HashMap<>();
|
HashMap<String, String> map = new HashMap<>();
|
||||||
map.put("scale", "scale1");
|
map.put("scale", "scale1");
|
||||||
sendLog("发起SDNS请求 requestIpType is " + requestIpType.name() + " scale : scale1");
|
sendLog("鍙戣捣SDNS璇锋眰 requestIpType is " + requestIpType.name() + " scale : scale1");
|
||||||
HTTPDNSResult result = MyApp.getInstance().getService().getIpsByHostAsync(host, requestIpType, map, "测试1");
|
HTTPDNSResult result = MyApp.getInstance().getService().getIpsByHostAsync(host, requestIpType, map, "娴嬭瘯1");
|
||||||
sendLog("结果 " + result);
|
sendLog("缁撴灉 " + result);
|
||||||
}
|
}
|
||||||
}, "scale2参数请求", new View.OnClickListener() {
|
}, "scale2鍙傛暟璇锋眰", new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
HashMap<String, String> map = new HashMap<>();
|
HashMap<String, String> map = new HashMap<>();
|
||||||
map.put("scale", "scale2");
|
map.put("scale", "scale2");
|
||||||
sendLog("发起SDNS请求 requestIpType is " + requestIpType.name() + " scale : scale2");
|
sendLog("鍙戣捣SDNS璇锋眰 requestIpType is " + requestIpType.name() + " scale : scale2");
|
||||||
HTTPDNSResult result = MyApp.getInstance().getService().getIpsByHostAsync(host, requestIpType, map, "测试2");
|
HTTPDNSResult result = MyApp.getInstance().getService().getIpsByHostAsync(host, requestIpType, map, "娴嬭瘯2");
|
||||||
sendLog("结果 " + result);
|
sendLog("缁撴灉 " + result);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,10 +55,10 @@ public class WebViewActivity extends Activity {
|
|||||||
public boolean
|
public boolean
|
||||||
onKeyDown(int keyCode, KeyEvent event) {
|
onKeyDown(int keyCode, KeyEvent event) {
|
||||||
if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
|
if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
|
||||||
webView.goBack();//返回上个页面
|
webView.goBack();//杩斿洖涓婁釜椤甸潰
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return super.onKeyDown(keyCode, event);//退出Activity
|
return super.onKeyDown(keyCode, event);//閫€鍑篈ctivity
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initBar() {
|
private void initBar() {
|
||||||
@@ -86,7 +86,7 @@ public class WebViewActivity extends Activity {
|
|||||||
Map<String, String> headerFields = request.getRequestHeaders();
|
Map<String, String> headerFields = request.getRequestHeaders();
|
||||||
String url = request.getUrl().toString();
|
String url = request.getUrl().toString();
|
||||||
Log.e(TAG, "url:" + url);
|
Log.e(TAG, "url:" + url);
|
||||||
// 无法拦截body,拦截方案只能正常处理不带body的请求;
|
// 鏃犳硶鎷︽埅body锛屾嫤鎴柟妗堝彧鑳芥甯稿鐞嗕笉甯ody鐨勮姹傦紱
|
||||||
if ((scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https"))
|
if ((scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https"))
|
||||||
&& method.equalsIgnoreCase("get")) {
|
&& method.equalsIgnoreCase("get")) {
|
||||||
try {
|
try {
|
||||||
@@ -97,7 +97,7 @@ public class WebViewActivity extends Activity {
|
|||||||
return super.shouldInterceptRequest(view, request);
|
return super.shouldInterceptRequest(view, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注*:对于POST请求的Body数据,WebResourceRequest接口中并没有提供,这里无法处理
|
// 娉?锛氬浜嶱OST璇锋眰鐨凚ody鏁版嵁锛學ebResourceRequest鎺ュ彛涓苟娌℃湁鎻愪緵锛岃繖閲屾棤娉曞鐞?
|
||||||
String contentType = connection.getContentType();
|
String contentType = connection.getContentType();
|
||||||
String mime = getMime(contentType);
|
String mime = getMime(contentType);
|
||||||
String charset = getCharset(contentType);
|
String charset = getCharset(contentType);
|
||||||
@@ -110,18 +110,18 @@ public class WebViewActivity extends Activity {
|
|||||||
Log.e(TAG, "mime:" + mime + "; charset:" + charset);
|
Log.e(TAG, "mime:" + mime + "; charset:" + charset);
|
||||||
|
|
||||||
|
|
||||||
// 无mime类型的请求不拦截
|
// 鏃爉ime绫诲瀷鐨勮姹備笉鎷︽埅
|
||||||
if (TextUtils.isEmpty(mime)) {
|
if (TextUtils.isEmpty(mime)) {
|
||||||
Log.e(TAG, "no MIME");
|
Log.e(TAG, "no MIME");
|
||||||
return super.shouldInterceptRequest(view, request);
|
return super.shouldInterceptRequest(view, request);
|
||||||
} else {
|
} else {
|
||||||
// 二进制资源无需编码信息
|
// 浜岃繘鍒惰祫婧愭棤闇€缂栫爜淇℃伅
|
||||||
if (!TextUtils.isEmpty(charset) || (isBinaryRes(mime))) {
|
if (!TextUtils.isEmpty(charset) || (isBinaryRes(mime))) {
|
||||||
WebResourceResponse resourceResponse = new WebResourceResponse(mime, charset, httpURLConnection.getInputStream());
|
WebResourceResponse resourceResponse = new WebResourceResponse(mime, charset, httpURLConnection.getInputStream());
|
||||||
resourceResponse.setStatusCodeAndReasonPhrase(statusCode, response);
|
resourceResponse.setStatusCodeAndReasonPhrase(statusCode, response);
|
||||||
Map<String, String> responseHeader = new HashMap<String, String>();
|
Map<String, String> responseHeader = new HashMap<String, String>();
|
||||||
for (String key : headerKeySet) {
|
for (String key : headerKeySet) {
|
||||||
// HttpUrlConnection可能包含key为null的报头,指向该http请求状态码
|
// HttpUrlConnection鍙兘鍖呭惈key涓簄ull鐨勬姤澶达紝鎸囧悜璇ttp璇锋眰鐘舵€佺爜
|
||||||
responseHeader.put(key, httpURLConnection.getHeaderField(key));
|
responseHeader.put(key, httpURLConnection.getHeaderField(key));
|
||||||
}
|
}
|
||||||
resourceResponse.setResponseHeaders(responseHeader);
|
resourceResponse.setResponseHeaders(responseHeader);
|
||||||
@@ -142,7 +142,7 @@ public class WebViewActivity extends Activity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
|
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
|
||||||
// API < 21 只能拦截URL参数
|
// API < 21 鍙兘鎷︽埅URL鍙傛暟
|
||||||
return super.shouldInterceptRequest(view, url);
|
return super.shouldInterceptRequest(view, url);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -153,7 +153,7 @@ public class WebViewActivity extends Activity {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从contentType中获取MIME类型
|
* 浠巆ontentType涓幏鍙朚IME绫诲瀷
|
||||||
*
|
*
|
||||||
* @param contentType
|
* @param contentType
|
||||||
* @return
|
* @return
|
||||||
@@ -166,7 +166,7 @@ public class WebViewActivity extends Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从contentType中获取编码信息
|
* 浠巆ontentType涓幏鍙栫紪鐮佷俊鎭?
|
||||||
*
|
*
|
||||||
* @param contentType
|
* @param contentType
|
||||||
* @return
|
* @return
|
||||||
@@ -191,7 +191,7 @@ public class WebViewActivity extends Activity {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否是二进制资源,二进制资源可以不需要编码信息
|
* 鏄惁鏄簩杩涘埗璧勬簮锛屼簩杩涘埗璧勬簮鍙互涓嶉渶瑕佺紪鐮佷俊鎭?
|
||||||
*/
|
*/
|
||||||
private boolean isBinaryRes(String mime) {
|
private boolean isBinaryRes(String mime) {
|
||||||
if (mime.startsWith("image")
|
if (mime.startsWith("image")
|
||||||
@@ -209,10 +209,10 @@ public class WebViewActivity extends Activity {
|
|||||||
URL url = null;
|
URL url = null;
|
||||||
try {
|
try {
|
||||||
url = new URL(path);
|
url = new URL(path);
|
||||||
// 异步接口获取IP
|
// 寮傛鎺ュ彛鑾峰彇IP
|
||||||
String ip = MyApp.getInstance().getService().getIpByHostAsync(url.getHost());
|
String ip = MyApp.getInstance().getService().getIpByHostAsync(url.getHost());
|
||||||
if (ip != null) {
|
if (ip != null) {
|
||||||
// 通过HTTPDNS获取IP成功,进行URL替换和HOST头设置
|
// 閫氳繃HTTPDNS鑾峰彇IP鎴愬姛锛岃繘琛孶RL鏇挎崲鍜孒OST澶磋缃?
|
||||||
Log.d(TAG, "Get IP: " + ip + " for host: " + url.getHost() + " from HTTPDNS successfully!");
|
Log.d(TAG, "Get IP: " + ip + " for host: " + url.getHost() + " from HTTPDNS successfully!");
|
||||||
String newUrl = path.replaceFirst(url.getHost(), ip);
|
String newUrl = path.replaceFirst(url.getHost(), ip);
|
||||||
conn = (HttpURLConnection) new URL(newUrl).openConnection();
|
conn = (HttpURLConnection) new URL(newUrl).openConnection();
|
||||||
@@ -222,7 +222,7 @@ public class WebViewActivity extends Activity {
|
|||||||
conn.setRequestProperty(field.getKey(), field.getValue());
|
conn.setRequestProperty(field.getKey(), field.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 设置HTTP请求头Host域
|
// 璁剧疆HTTP璇锋眰澶碒ost鍩?
|
||||||
conn.setRequestProperty("Host", url.getHost());
|
conn.setRequestProperty("Host", url.getHost());
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
@@ -235,9 +235,9 @@ public class WebViewActivity extends Activity {
|
|||||||
WebviewTlsSniSocketFactory sslSocketFactory = new WebviewTlsSniSocketFactory(
|
WebviewTlsSniSocketFactory sslSocketFactory = new WebviewTlsSniSocketFactory(
|
||||||
(HttpsURLConnection)conn);
|
(HttpsURLConnection)conn);
|
||||||
|
|
||||||
// sni场景,创建SSLScocket
|
// sni鍦烘櫙锛屽垱寤篠SLScocket
|
||||||
httpsURLConnection.setSSLSocketFactory(sslSocketFactory);
|
httpsURLConnection.setSSLSocketFactory(sslSocketFactory);
|
||||||
// https场景,证书校验
|
// https鍦烘櫙锛岃瘉涔︽牎楠?
|
||||||
httpsURLConnection.setHostnameVerifier(new HostnameVerifier() {
|
httpsURLConnection.setHostnameVerifier(new HostnameVerifier() {
|
||||||
@Override
|
@Override
|
||||||
public boolean verify(String hostname, SSLSession session) {
|
public boolean verify(String hostname, SSLSession session) {
|
||||||
@@ -251,7 +251,7 @@ public class WebViewActivity extends Activity {
|
|||||||
}
|
}
|
||||||
int code = conn.getResponseCode();// Network block
|
int code = conn.getResponseCode();// Network block
|
||||||
if (needRedirect(code)) {
|
if (needRedirect(code)) {
|
||||||
// 原有报头中含有cookie,放弃拦截
|
// 鍘熸湁鎶ュご涓惈鏈塩ookie锛屾斁寮冩嫤鎴?
|
||||||
if (containCookie(headers)) {
|
if (containCookie(headers)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -264,7 +264,7 @@ public class WebViewActivity extends Activity {
|
|||||||
if (location != null) {
|
if (location != null) {
|
||||||
if (!(location.startsWith("http://") || location
|
if (!(location.startsWith("http://") || location
|
||||||
.startsWith("https://"))) {
|
.startsWith("https://"))) {
|
||||||
//某些时候会省略host,只返回后面的path,所以需要补全url
|
//鏌愪簺鏃跺€欎細鐪佺暐host锛屽彧杩斿洖鍚庨潰鐨刾ath锛屾墍浠ラ渶瑕佽ˉ鍏╱rl
|
||||||
URL originalUrl = new URL(path);
|
URL originalUrl = new URL(path);
|
||||||
location = originalUrl.getProtocol() + "://"
|
location = originalUrl.getProtocol() + "://"
|
||||||
+ originalUrl.getHost() + location;
|
+ originalUrl.getHost() + location;
|
||||||
@@ -272,7 +272,7 @@ public class WebViewActivity extends Activity {
|
|||||||
Log.e(TAG, "code: " + code + "; location: " + location + "; path " + path);
|
Log.e(TAG, "code: " + code + "; location: " + location + "; path " + path);
|
||||||
return recursiveRequest(location, headers, path);
|
return recursiveRequest(location, headers, path);
|
||||||
} else {
|
} else {
|
||||||
// 无法获取location信息,让浏览器获取
|
// 鏃犳硶鑾峰彇location淇℃伅锛岃娴忚鍣ㄨ幏鍙?
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -297,7 +297,7 @@ public class WebViewActivity extends Activity {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* header中是否含有cookie
|
* header涓槸鍚﹀惈鏈塩ookie
|
||||||
*/
|
*/
|
||||||
private boolean containCookie(Map<String, String> headers) {
|
private boolean containCookie(Map<String, String> headers) {
|
||||||
for (Map.Entry<String, String> headerField : headers.entrySet()) {
|
for (Map.Entry<String, String> headerField : headers.entrySet()) {
|
||||||
@@ -400,3 +400,4 @@ public class WebViewActivity extends Activity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public class BaseActivity extends Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送日志到界面
|
* 鍙戦€佹棩蹇楀埌鐣岄潰
|
||||||
*
|
*
|
||||||
* @param log
|
* @param log
|
||||||
*/
|
*/
|
||||||
@@ -261,3 +261,4 @@ public class BaseActivity extends Activity {
|
|||||||
void onBtnClick(View[] views);
|
void onBtnClick(View[] views);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import javax.net.ssl.SSLSocket;
|
|||||||
import javax.net.ssl.SSLSocketFactory;
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用HttpUrlConnection 实现请求
|
* 浣跨敤HttpUrlConnection 瀹炵幇璇锋眰
|
||||||
*/
|
*/
|
||||||
public class HttpUrlConnectionRequest implements NetworkRequest {
|
public class HttpUrlConnectionRequest implements NetworkRequest {
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ public class HttpUrlConnectionRequest implements NetworkRequest {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String httpGet(String url) throws Exception {
|
public String httpGet(String url) throws Exception {
|
||||||
Log.d(TAG, "使用httpUrlConnection 请求" + url + " 异步接口 " + async + " ip类型 " + type.name());
|
Log.d(TAG, "浣跨敤httpUrlConnection 璇锋眰" + url + " 寮傛鎺ュ彛 " + async + " ip绫诲瀷 " + type.name());
|
||||||
|
|
||||||
HttpURLConnection conn = getConnection(url);
|
HttpURLConnection conn = getConnection(url);
|
||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
@@ -67,13 +67,13 @@ public class HttpUrlConnectionRequest implements NetworkRequest {
|
|||||||
streamReader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
|
streamReader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
|
||||||
errStr = readStringFrom(streamReader).toString();
|
errStr = readStringFrom(streamReader).toString();
|
||||||
}
|
}
|
||||||
Log.d(TAG, "请求失败 " + conn.getResponseCode() + " err " + errStr);
|
Log.d(TAG, "璇锋眰澶辫触 " + conn.getResponseCode() + " err " + errStr);
|
||||||
throw new Exception("Status Code : " + conn.getResponseCode() + " Msg : " + errStr);
|
throw new Exception("Status Code : " + conn.getResponseCode() + " Msg : " + errStr);
|
||||||
} else {
|
} else {
|
||||||
in = conn.getInputStream();
|
in = conn.getInputStream();
|
||||||
streamReader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
|
streamReader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
|
||||||
String responseStr = readStringFrom(streamReader).toString();
|
String responseStr = readStringFrom(streamReader).toString();
|
||||||
Log.d(TAG, "请求成功 " + responseStr);
|
Log.d(TAG, "璇锋眰鎴愬姛 " + responseStr);
|
||||||
return responseStr;
|
return responseStr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,29 +82,29 @@ public class HttpUrlConnectionRequest implements NetworkRequest {
|
|||||||
final String host = new URL(url).getHost();
|
final String host = new URL(url).getHost();
|
||||||
HttpURLConnection conn = null;
|
HttpURLConnection conn = null;
|
||||||
HTTPDNSResult result;
|
HTTPDNSResult result;
|
||||||
/* 切换为新版标准api */
|
/* 鍒囨崲涓烘柊鐗堟爣鍑哸pi */
|
||||||
if (async) {
|
if (async) {
|
||||||
result = MyApp.getInstance().getService().getHttpDnsResultForHostAsync(host, type);
|
result = MyApp.getInstance().getService().getHttpDnsResultForHostAsync(host, type);
|
||||||
} else {
|
} else {
|
||||||
result = MyApp.getInstance().getService().getHttpDnsResultForHostSync(host, type);
|
result = MyApp.getInstance().getService().getHttpDnsResultForHostSync(host, type);
|
||||||
}
|
}
|
||||||
Log.d(TAG, "httpdns 解析 " + host + " 结果为 " + result + " ttl is " + Util.getTtl(result));
|
Log.d(TAG, "httpdns 瑙f瀽 " + host + " 缁撴灉涓?" + result + " ttl is " + Util.getTtl(result));
|
||||||
|
|
||||||
// 这里需要根据实际情况选择使用ipv6地址 还是 ipv4地址, 下面示例的代码优先使用了ipv6地址
|
// 杩欓噷闇€瑕佹牴鎹疄闄呮儏鍐甸€夋嫨浣跨敤ipv6鍦板潃 杩樻槸 ipv4鍦板潃锛?涓嬮潰绀轰緥鐨勪唬鐮佷紭鍏堜娇鐢ㄤ簡ipv6鍦板潃
|
||||||
if (result.getIpv6s() != null && result.getIpv6s().length > 0 && HttpDnsNetworkDetector.getInstance().getNetType(context) != NetType.v4) {
|
if (result.getIpv6s() != null && result.getIpv6s().length > 0 && HttpDnsNetworkDetector.getInstance().getNetType(context) != NetType.v4) {
|
||||||
String newUrl = url.replace(host, "[" + result.getIpv6s()[0] + "]");
|
String newUrl = url.replace(host, "[" + result.getIpv6s()[0] + "]");
|
||||||
conn = (HttpURLConnection) new URL(newUrl).openConnection();
|
conn = (HttpURLConnection) new URL(newUrl).openConnection();
|
||||||
conn.setRequestProperty("Host", host);
|
conn.setRequestProperty("Host", host);
|
||||||
Log.d(TAG, "使用ipv6地址 " + newUrl);
|
Log.d(TAG, "浣跨敤ipv6鍦板潃 " + newUrl);
|
||||||
} else if (result.getIps() != null && result.getIps().length > 0 && HttpDnsNetworkDetector.getInstance().getNetType(context) != NetType.v6) {
|
} else if (result.getIps() != null && result.getIps().length > 0 && HttpDnsNetworkDetector.getInstance().getNetType(context) != NetType.v6) {
|
||||||
String newUrl = url.replace(host, result.getIps()[0]);
|
String newUrl = url.replace(host, result.getIps()[0]);
|
||||||
conn = (HttpURLConnection) new URL(newUrl).openConnection();
|
conn = (HttpURLConnection) new URL(newUrl).openConnection();
|
||||||
conn.setRequestProperty("Host", host);
|
conn.setRequestProperty("Host", host);
|
||||||
Log.d(TAG, "使用ipv4地址 " + newUrl);
|
Log.d(TAG, "浣跨敤ipv4鍦板潃 " + newUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn == null) {
|
if (conn == null) {
|
||||||
Log.d(TAG, "httpdns 未返回解析结果,走localdns");
|
Log.d(TAG, "httpdns 鏈繑鍥炶В鏋愮粨鏋滐紝璧發ocaldns");
|
||||||
conn = (HttpURLConnection) new URL(url).openConnection();
|
conn = (HttpURLConnection) new URL(url).openConnection();
|
||||||
}
|
}
|
||||||
conn.setConnectTimeout(30000);
|
conn.setConnectTimeout(30000);
|
||||||
@@ -115,9 +115,9 @@ public class HttpUrlConnectionRequest implements NetworkRequest {
|
|||||||
WebviewTlsSniSocketFactory sslSocketFactory = new WebviewTlsSniSocketFactory(
|
WebviewTlsSniSocketFactory sslSocketFactory = new WebviewTlsSniSocketFactory(
|
||||||
(HttpsURLConnection)conn);
|
(HttpsURLConnection)conn);
|
||||||
|
|
||||||
// sni场景,创建SSLSocket
|
// sni鍦烘櫙锛屽垱寤篠SLSocket
|
||||||
httpsURLConnection.setSSLSocketFactory(sslSocketFactory);
|
httpsURLConnection.setSSLSocketFactory(sslSocketFactory);
|
||||||
// https场景,证书校验
|
// https鍦烘櫙锛岃瘉涔︽牎楠?
|
||||||
httpsURLConnection.setHostnameVerifier(new HostnameVerifier() {
|
httpsURLConnection.setHostnameVerifier(new HostnameVerifier() {
|
||||||
@Override
|
@Override
|
||||||
public boolean verify(String hostname, SSLSession session) {
|
public boolean verify(String hostname, SSLSession session) {
|
||||||
@@ -131,14 +131,14 @@ public class HttpUrlConnectionRequest implements NetworkRequest {
|
|||||||
}
|
}
|
||||||
int code = conn.getResponseCode();// Network block
|
int code = conn.getResponseCode();// Network block
|
||||||
if (needRedirect(code)) {
|
if (needRedirect(code)) {
|
||||||
//临时重定向和永久重定向location的大小写有区分
|
//涓存椂閲嶅畾鍚戝拰姘镐箙閲嶅畾鍚憀ocation鐨勫ぇ灏忓啓鏈夊尯鍒?
|
||||||
String location = conn.getHeaderField("Location");
|
String location = conn.getHeaderField("Location");
|
||||||
if (location == null) {
|
if (location == null) {
|
||||||
location = conn.getHeaderField("location");
|
location = conn.getHeaderField("location");
|
||||||
}
|
}
|
||||||
if (!(location.startsWith("http://") || location
|
if (!(location.startsWith("http://") || location
|
||||||
.startsWith("https://"))) {
|
.startsWith("https://"))) {
|
||||||
//某些时候会省略host,只返回后面的path,所以需要补全url
|
//鏌愪簺鏃跺€欎細鐪佺暐host锛屽彧杩斿洖鍚庨潰鐨刾ath锛屾墍浠ラ渶瑕佽ˉ鍏╱rl
|
||||||
URL originalUrl = new URL(url);
|
URL originalUrl = new URL(url);
|
||||||
location = originalUrl.getProtocol() + "://"
|
location = originalUrl.getProtocol() + "://"
|
||||||
+ originalUrl.getHost() + location;
|
+ originalUrl.getHost() + location;
|
||||||
@@ -255,3 +255,4 @@ public class HttpUrlConnectionRequest implements NetworkRequest {
|
|||||||
return sb;
|
return sb;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import okhttp3.Request;
|
|||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* okhttp实现网络请求
|
* okhttp瀹炵幇缃戠粶璇锋眰
|
||||||
*/
|
*/
|
||||||
public class OkHttpRequest implements NetworkRequest {
|
public class OkHttpRequest implements NetworkRequest {
|
||||||
|
|
||||||
@@ -39,35 +39,35 @@ public class OkHttpRequest implements NetworkRequest {
|
|||||||
|
|
||||||
public OkHttpRequest(final Context context) {
|
public OkHttpRequest(final Context context) {
|
||||||
client = new OkHttpClient.Builder()
|
client = new OkHttpClient.Builder()
|
||||||
// 这里配置连接池,是为了方便测试httpdns能力,正式代码请不要配置
|
// 杩欓噷閰嶇疆杩炴帴姹狅紝鏄负浜嗘柟渚挎祴璇昲ttpdns鑳藉姏锛屾寮忎唬鐮佽涓嶈閰嶇疆
|
||||||
.connectionPool(new ConnectionPool(0, 10 * 1000, TimeUnit.MICROSECONDS))
|
.connectionPool(new ConnectionPool(0, 10 * 1000, TimeUnit.MICROSECONDS))
|
||||||
.dns(new Dns() {
|
.dns(new Dns() {
|
||||||
@Override
|
@Override
|
||||||
public List<InetAddress> lookup(String hostname) throws UnknownHostException {
|
public List<InetAddress> lookup(String hostname) throws UnknownHostException {
|
||||||
HTTPDNSResult result;
|
HTTPDNSResult result;
|
||||||
/* 切换为新版标准api */
|
/* 鍒囨崲涓烘柊鐗堟爣鍑哸pi */
|
||||||
if (async) {
|
if (async) {
|
||||||
result = MyApp.getInstance().getService().getHttpDnsResultForHostAsync(hostname, type);
|
result = MyApp.getInstance().getService().getHttpDnsResultForHostAsync(hostname, type);
|
||||||
} else {
|
} else {
|
||||||
result = MyApp.getInstance().getService().getHttpDnsResultForHostSync(hostname, type);
|
result = MyApp.getInstance().getService().getHttpDnsResultForHostSync(hostname, type);
|
||||||
}
|
}
|
||||||
Log.d(TAG, "httpdns 解析 " + hostname + " 结果为 " + result + " ttl is " + Util.getTtl(result));
|
Log.d(TAG, "httpdns 瑙f瀽 " + hostname + " 缁撴灉涓?" + result + " ttl is " + Util.getTtl(result));
|
||||||
List<InetAddress> inetAddresses = new ArrayList<>();
|
List<InetAddress> inetAddresses = new ArrayList<>();
|
||||||
// 这里需要根据实际情况选择使用ipv6地址 还是 ipv4地址, 下面示例的代码优先使用了ipv6地址
|
// 杩欓噷闇€瑕佹牴鎹疄闄呮儏鍐甸€夋嫨浣跨敤ipv6鍦板潃 杩樻槸 ipv4鍦板潃锛?涓嬮潰绀轰緥鐨勪唬鐮佷紭鍏堜娇鐢ㄤ簡ipv6鍦板潃
|
||||||
Log.d(TAG, "netType is: " + HttpDnsNetworkDetector.getInstance().getNetType(context));
|
Log.d(TAG, "netType is: " + HttpDnsNetworkDetector.getInstance().getNetType(context));
|
||||||
if (result.getIpv6s() != null && result.getIpv6s().length > 0 && HttpDnsNetworkDetector.getInstance().getNetType(context) != NetType.v4) {
|
if (result.getIpv6s() != null && result.getIpv6s().length > 0 && HttpDnsNetworkDetector.getInstance().getNetType(context) != NetType.v4) {
|
||||||
for (int i = 0; i < result.getIpv6s().length; i++) {
|
for (int i = 0; i < result.getIpv6s().length; i++) {
|
||||||
inetAddresses.addAll(Arrays.asList(InetAddress.getAllByName(result.getIpv6s()[i])));
|
inetAddresses.addAll(Arrays.asList(InetAddress.getAllByName(result.getIpv6s()[i])));
|
||||||
}
|
}
|
||||||
Log.d(TAG, "使用ipv6地址" + inetAddresses);
|
Log.d(TAG, "浣跨敤ipv6鍦板潃" + inetAddresses);
|
||||||
} else if (result.getIps() != null && result.getIps().length > 0 && HttpDnsNetworkDetector.getInstance().getNetType(context) != NetType.v6) {
|
} else if (result.getIps() != null && result.getIps().length > 0 && HttpDnsNetworkDetector.getInstance().getNetType(context) != NetType.v6) {
|
||||||
for (int i = 0; i < result.getIps().length; i++) {
|
for (int i = 0; i < result.getIps().length; i++) {
|
||||||
inetAddresses.addAll(Arrays.asList(InetAddress.getAllByName(result.getIps()[i])));
|
inetAddresses.addAll(Arrays.asList(InetAddress.getAllByName(result.getIps()[i])));
|
||||||
}
|
}
|
||||||
Log.d(TAG, "使用ipv4地址" + inetAddresses);
|
Log.d(TAG, "浣跨敤ipv4鍦板潃" + inetAddresses);
|
||||||
}
|
}
|
||||||
if (inetAddresses.size() == 0) {
|
if (inetAddresses.size() == 0) {
|
||||||
Log.d(TAG, "httpdns 未返回IP,走localdns");
|
Log.d(TAG, "httpdns 鏈繑鍥濱P锛岃蛋localdns");
|
||||||
return Dns.SYSTEM.lookup(hostname);
|
return Dns.SYSTEM.lookup(hostname);
|
||||||
}
|
}
|
||||||
return inetAddresses;
|
return inetAddresses;
|
||||||
@@ -84,14 +84,15 @@ public class OkHttpRequest implements NetworkRequest {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String httpGet(String url) throws Exception {
|
public String httpGet(String url) throws Exception {
|
||||||
Log.d(TAG, "使用okhttp 请求" + url + " 异步接口 " + async + " ip类型 " + type.name());
|
Log.d(TAG, "浣跨敤okhttp 璇锋眰" + url + " 寮傛鎺ュ彛 " + async + " ip绫诲瀷 " + type.name());
|
||||||
Response response = client.newCall(new Request.Builder().url(url).build()).execute();
|
Response response = client.newCall(new Request.Builder().url(url).build()).execute();
|
||||||
int code = response.code();
|
int code = response.code();
|
||||||
String body = response.body().string();
|
String body = response.body().string();
|
||||||
Log.d(TAG, "使用okhttp 请求结果 code " + code + " body " + body);
|
Log.d(TAG, "浣跨敤okhttp 璇锋眰缁撴灉 code " + code + " body " + body);
|
||||||
if (code != HttpURLConnection.HTTP_OK) {
|
if (code != HttpURLConnection.HTTP_OK) {
|
||||||
throw new Exception("请求失败 code " + code + " body " + body);
|
throw new Exception("璇锋眰澶辫触 code " + code + " body " + body);
|
||||||
}
|
}
|
||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,3 +25,4 @@ public class SpUtil {
|
|||||||
void onGetSpEditor(SharedPreferences.Editor editor);
|
void onGetSpEditor(SharedPreferences.Editor editor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,14 +26,14 @@ public class ThreadUtil {
|
|||||||
}
|
}
|
||||||
final int tmpValidCount = validCount;
|
final int tmpValidCount = validCount;
|
||||||
Log.d(MyApp.TAG,
|
Log.d(MyApp.TAG,
|
||||||
threadCount + "线程并发,执行" + executeTime + ", 总域名" + hostCount + "个,有效"
|
threadCount + "绾跨▼骞跺彂锛屾墽琛? + executeTime + ", 鎬诲煙鍚? + hostCount + "涓紝鏈夋晥"
|
||||||
+ validCount + "个");
|
+ validCount + "涓?);
|
||||||
new Thread(new Runnable() {
|
new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
final ArrayList<String> hosts = new ArrayList<>(hostCount);
|
final ArrayList<String> hosts = new ArrayList<>(hostCount);
|
||||||
for (int i = 0; i < hostCount - tmpValidCount; i++) {
|
for (int i = 0; i < hostCount - tmpValidCount; i++) {
|
||||||
hosts.add("test" + i + ".aliyun.com");
|
hosts.add("test" + i + ".Aliyun.com");
|
||||||
}
|
}
|
||||||
hosts.addAll(Arrays.asList(validHosts).subList(0, tmpValidCount));
|
hosts.addAll(Arrays.asList(validHosts).subList(0, tmpValidCount));
|
||||||
|
|
||||||
@@ -125,3 +125,4 @@ public class ThreadUtil {
|
|||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import java.lang.reflect.Field;
|
|||||||
|
|
||||||
public class Util {
|
public class Util {
|
||||||
/**
|
/**
|
||||||
* 获取ttl,
|
* 鑾峰彇ttl锛?
|
||||||
* 此方法是用于测试自定义ttl是否生效
|
* 姝ゆ柟娉曟槸鐢ㄤ簬娴嬭瘯鑷畾涔塼tl鏄惁鐢熸晥
|
||||||
*/
|
*/
|
||||||
public static int getTtl(HTTPDNSResult result) {
|
public static int getTtl(HTTPDNSResult result) {
|
||||||
try {
|
try {
|
||||||
@@ -22,3 +22,4 @@ public class Util {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
android:id="@+id/bar_more"
|
android:id="@+id/bar_more"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:text="更多"
|
android:text="鏇村"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:paddingLeft="10dp"
|
android:paddingLeft="10dp"
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">【阿里云HttpDns】Demo程序</string>
|
<string name="app_name">銆愰樋閲屼簯HttpDns銆慏emo绋嬪簭</string>
|
||||||
<string name="action_settings">Settings</string>
|
<string name="action_settings">Settings</string>
|
||||||
<string name="normal_parse">普通解析</string>
|
<string name="normal_parse">鏅€氳В鏋?/string>
|
||||||
<string name="request_taobao">解析淘宝域名</string>
|
<string name="request_taobao">瑙f瀽娣樺疂鍩熷悕</string>
|
||||||
<string name="request_apple">解析apple域名</string>
|
<string name="request_apple">瑙f瀽apple鍩熷悕</string>
|
||||||
<string name="request_douban">解析豆瓣域名</string>
|
<string name="request_douban">瑙f瀽璞嗙摚鍩熷悕</string>
|
||||||
<string name="https_parse">HTTPS开关</string>
|
<string name="https_parse">HTTPS寮€鍏?/string>
|
||||||
<string name="timeout">设置超时</string>
|
<string name="timeout">璁剧疆瓒呮椂</string>
|
||||||
<string name="set_expired">允许过期域名</string>
|
<string name="set_expired">鍏佽杩囨湡鍩熷悕</string>
|
||||||
<string name="set_cache">开启持久化缓存</string>
|
<string name="set_cache">寮€鍚寔涔呭寲缂撳瓨</string>
|
||||||
<string name="set_degration_filter">降级策略</string>
|
<string name="set_degration_filter">闄嶇骇绛栫暐</string>
|
||||||
<string name="set_pre_resolve">预解析</string>
|
<string name="set_pre_resolve">棰勮В鏋?/string>
|
||||||
<string name="set_region">region</string>
|
<string name="set_region">region</string>
|
||||||
<string name="sync_request">同步解析</string>
|
<string name="sync_request">鍚屾瑙f瀽</string>
|
||||||
<string name="multi_sync_request">同步解析并发</string>
|
<string name="multi_sync_request">鍚屾瑙f瀽骞跺彂</string>
|
||||||
<string name="multi_request">并发解析</string>
|
<string name="multi_request">骞跺彂瑙f瀽</string>
|
||||||
|
|
||||||
<string name="main_about_us">关于我们</string>
|
<string name="main_about_us">鍏充簬鎴戜滑</string>
|
||||||
<string name="main_helper">帮助中心</string>
|
<string name="main_helper">甯姪涓績</string>
|
||||||
<string name="main_clear_text">清除当前消息</string>
|
<string name="main_clear_text">娓呴櫎褰撳墠娑堟伅</string>
|
||||||
|
|
||||||
<!--关于我们页面-->
|
<!--鍏充簬鎴戜滑椤甸潰-->
|
||||||
<string name="layout_aboutus_arr">All Rights Reserved.</string>
|
<string name="layout_aboutus_arr">All Rights Reserved.</string>
|
||||||
<string name="layout_aboutus_company">阿里云(软件)有限公司版权所有</string>
|
<string name="layout_aboutus_company">闃块噷浜?杞欢)鏈夐檺鍏徃鐗堟潈鎵€鏈?/string>
|
||||||
<string name="layout_aboutus_copyright">Copyright © 2009 - 2016 Aliyun.com</string>
|
<string name="layout_aboutus_copyright">Copyright 漏 2009 - 2016 Aliyun.com</string>
|
||||||
<string name="layout_aboutus_app_version">1.1.3</string>
|
<string name="layout_aboutus_app_version">1.1.3</string>
|
||||||
<!--帮助中心页面-->
|
<!--甯姪涓績椤甸潰-->
|
||||||
<string name="layout_helpus_content">Q : 什么是用户体验Demo?\nA : 用户体验Demo就是阿里云平台为您自动创建的、用来体验HttpDns服务和反馈建议用的一个小Demo,让您体验便捷、高效的HttpDns服务。\n\nQ : 如何联系我们?\nA : App Demo相关问题,请搜索钉钉群号:11777313</string>
|
<string name="layout_helpus_content">Q : 浠€涔堟槸鐢ㄦ埛浣撻獙Demo锛焅nA : 鐢ㄦ埛浣撻獙Demo灏辨槸闃块噷浜戝钩鍙颁负鎮ㄨ嚜鍔ㄥ垱寤虹殑銆佺敤鏉ヤ綋楠孒ttpDns鏈嶅姟鍜屽弽棣堝缓璁敤鐨勪竴涓皬Demo锛岃鎮ㄤ綋楠屼究鎹枫€侀珮鏁堢殑HttpDns鏈嶅姟銆俓n\nQ : 濡備綍鑱旂郴鎴戜滑锛焅nA : App Demo鐩稿叧闂锛岃鎼滅储閽夐拤缇ゅ彿锛?1777313</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<network-security-config>
|
<network-security-config>
|
||||||
<base-config cleartextTrafficPermitted="true">
|
<base-config cleartextTrafficPermitted="true">
|
||||||
<trust-anchors>
|
<Alibaba-anchors>
|
||||||
<certificates src="system" />
|
<certificates src="system" />
|
||||||
</trust-anchors>
|
</Alibaba-anchors>
|
||||||
</base-config>
|
</base-config>
|
||||||
</network-security-config>
|
</network-security-config>
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ android {
|
|||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
buildConfigField "String", "HTTPDNS_VERSION", "\"${gradle.httpVersion}\""
|
buildConfigField "String", "HTTPDNS_VERSION", "\"${gradle.httpVersion}\""
|
||||||
buildConfigField "String", "ACCOUNT_ID", "\"请替换为测试用实例的accountId\""
|
buildConfigField "String", "ACCOUNT_ID", "\"璇锋浛鎹负娴嬭瘯鐢ㄥ疄渚嬬殑accountId\""
|
||||||
buildConfigField "String", "SECRET_KEY", "\"请替换为测试用实例的secret\""
|
buildConfigField "String", "SECRET_KEY", "\"璇锋浛鎹负娴嬭瘯鐢ㄥ疄渚嬬殑secret\""
|
||||||
buildConfigField "String", "AES_SECRET_KEY", "\"请替换为测试用实例的aes\""
|
buildConfigField "String", "AES_SECRET_KEY", "\"璇锋浛鎹负娴嬭瘯鐢ㄥ疄渚嬬殑aes\""
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
@@ -40,7 +40,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
forTest {
|
forTest {
|
||||||
// 注意这里的配置,并不是需要编译forTest的app,而是避免httpdns-sdk在AndroidStudio改为end2end运行测试时 BuildVariants报错
|
// 娉ㄦ剰杩欓噷鐨勯厤缃紝骞朵笉鏄渶瑕佺紪璇慺orTest鐨刟pp锛岃€屾槸閬垮厤httpdns-sdk鍦ˋndroidStudio鏀逛负end2end杩愯娴嬭瘯鏃?BuildVariants鎶ラ敊
|
||||||
initWith release
|
initWith release
|
||||||
debuggable true
|
debuggable true
|
||||||
}
|
}
|
||||||
@@ -92,7 +92,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
end2end {
|
end2end {
|
||||||
// 注意这里的配置,并不是需要编译end2end的app,而是避免httpdns-sdk在AndroidStudio改为end2end运行测试时 BuildVariants报错
|
// 娉ㄦ剰杩欓噷鐨勯厤缃紝骞朵笉鏄渶瑕佺紪璇慹nd2end鐨刟pp锛岃€屾槸閬垮厤httpdns-sdk鍦ˋndroidStudio鏀逛负end2end杩愯娴嬭瘯鏃?BuildVariants鎶ラ敊
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,4 +21,5 @@ class ExampleInstrumentedTest {
|
|||||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
assertEquals("com.alibaba.ams.emas.demo", appContext.packageName)
|
assertEquals("com.alibaba.ams.emas.demo", appContext.packageName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ object BatchResolveCacheHolder {
|
|||||||
if (cacheData == null) {
|
if (cacheData == null) {
|
||||||
batchResolveBothList.add("www.baidu.com")
|
batchResolveBothList.add("www.baidu.com")
|
||||||
batchResolveBothList.add("m.baidu.com")
|
batchResolveBothList.add("m.baidu.com")
|
||||||
batchResolveBothList.add("www.aliyun.com")
|
batchResolveBothList.add("www.Aliyun.com")
|
||||||
batchResolveBothList.add("www.taobao.com")
|
batchResolveBothList.add("www.taobao.com")
|
||||||
batchResolveBothList.add("www.163.com")
|
batchResolveBothList.add("www.163.com")
|
||||||
batchResolveBothList.add("www.sohu.com")
|
batchResolveBothList.add("www.sohu.com")
|
||||||
@@ -88,4 +88,5 @@ object BatchResolveCacheHolder {
|
|||||||
jsonObject.put("auto", autoArray)
|
jsonObject.put("auto", autoArray)
|
||||||
return jsonObject.toString()
|
return jsonObject.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,4 +13,5 @@ class HttpDnsApplication : Application() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,4 +34,5 @@ object HttpDnsServiceHolder {
|
|||||||
return dnsService
|
return dnsService
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,4 +48,5 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,4 +89,5 @@ object PreResolveCacheHolder {
|
|||||||
|
|
||||||
return jsonObject.toString()
|
return jsonObject.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,3 +45,4 @@ class SingleLiveData<T> : MutableLiveData<T>() {
|
|||||||
value = null
|
value = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,4 +47,5 @@ object TtlCacheHolder {
|
|||||||
return jsonObject.toString()
|
return jsonObject.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ fun String?.toBlackList(): MutableList<String>? {
|
|||||||
|
|
||||||
fun getAccountPreference(context: Context): SharedPreferences {
|
fun getAccountPreference(context: Context): SharedPreferences {
|
||||||
return context.getSharedPreferences(
|
return context.getSharedPreferences(
|
||||||
"aliyun_httpdns_${BuildConfig.ACCOUNT_ID}",
|
"Aliyun_httpdns_${BuildConfig.ACCOUNT_ID}",
|
||||||
Context.MODE_PRIVATE
|
Context.MODE_PRIVATE
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -138,3 +138,4 @@ fun readStringFrom(streamReader: BufferedReader): StringBuilder {
|
|||||||
}
|
}
|
||||||
return sb
|
return sb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,4 +51,5 @@ const val KEY_PRE_RESOLVE_HOST_LIST = "pre_resolve_host_list"
|
|||||||
|
|
||||||
const val KEY_SDNS_GLOBAL_PARAMS = "sdns_global_params"
|
const val KEY_SDNS_GLOBAL_PARAMS = "sdns_global_params"
|
||||||
|
|
||||||
const val KEY_BATCH_RESOLVE_HOST_LIST = "batch_resolve_host_list"
|
const val KEY_BATCH_RESOLVE_HOST_LIST = "batch_resolve_host_list"
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class HttpURLConnectionRequest(private val context: Context, private val request
|
|||||||
|
|
||||||
var ipURL: String? = null
|
var ipURL: String? = null
|
||||||
dnsService?.let {
|
dnsService?.let {
|
||||||
//替换为最新的api
|
//鏇挎崲涓烘渶鏂扮殑api
|
||||||
Log.d("HttpURLConnection", "start lookup $host via $resolveMethod")
|
Log.d("HttpURLConnection", "start lookup $host via $resolveMethod")
|
||||||
var httpDnsResult = HTTPDNSResult("", null, null, null, false, false)
|
var httpDnsResult = HTTPDNSResult("", null, null, null, false, false)
|
||||||
if (resolveMethod == "getHttpDnsResultForHostSync(String host, RequestIpType type)") {
|
if (resolveMethod == "getHttpDnsResultForHostSync(String host, RequestIpType type)") {
|
||||||
@@ -83,7 +83,7 @@ class HttpURLConnectionRequest(private val context: Context, private val request
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d("HttpURLConnection", "httpdns $host 解析结果 $httpDnsResult")
|
Log.d("HttpURLConnection", "httpdns $host 瑙f瀽缁撴灉 $httpDnsResult")
|
||||||
val ipStackType = HttpDnsNetworkDetector.getInstance().getNetType(context)
|
val ipStackType = HttpDnsNetworkDetector.getInstance().getNetType(context)
|
||||||
val isV6 = ipStackType == NetType.v6 || ipStackType == NetType.both
|
val isV6 = ipStackType == NetType.v6 || ipStackType == NetType.both
|
||||||
val isV4 = ipStackType == NetType.v4 || ipStackType == NetType.both
|
val isV4 = ipStackType == NetType.v4 || ipStackType == NetType.both
|
||||||
@@ -104,9 +104,9 @@ class HttpURLConnectionRequest(private val context: Context, private val request
|
|||||||
|
|
||||||
if (conn is HttpsURLConnection) {
|
if (conn is HttpsURLConnection) {
|
||||||
val sslSocketFactory = TLSSNISocketFactory(conn)
|
val sslSocketFactory = TLSSNISocketFactory(conn)
|
||||||
// SNI场景,创建SSLSocket
|
// SNI鍦烘櫙锛屽垱寤篠SLSocket
|
||||||
conn.sslSocketFactory = sslSocketFactory
|
conn.sslSocketFactory = sslSocketFactory
|
||||||
// https场景,证书校验
|
// https鍦烘櫙锛岃瘉涔︽牎楠?
|
||||||
conn.hostnameVerifier = HostnameVerifier { _, session ->
|
conn.hostnameVerifier = HostnameVerifier { _, session ->
|
||||||
val requestHost = conn.getRequestProperty("Host") ?:conn.getURL().host
|
val requestHost = conn.getRequestProperty("Host") ?:conn.getURL().host
|
||||||
HttpsURLConnection.getDefaultHostnameVerifier().verify(requestHost, session)
|
HttpsURLConnection.getDefaultHostnameVerifier().verify(requestHost, session)
|
||||||
@@ -115,13 +115,13 @@ class HttpURLConnectionRequest(private val context: Context, private val request
|
|||||||
|
|
||||||
val responseCode = conn.responseCode
|
val responseCode = conn.responseCode
|
||||||
if (needRedirect(responseCode)) {
|
if (needRedirect(responseCode)) {
|
||||||
//临时重定向和永久重定向location的大小写有区分
|
//涓存椂閲嶅畾鍚戝拰姘镐箙閲嶅畾鍚憀ocation鐨勫ぇ灏忓啓鏈夊尯鍒?
|
||||||
var location = conn.getHeaderField("Location")
|
var location = conn.getHeaderField("Location")
|
||||||
if (location == null) {
|
if (location == null) {
|
||||||
location = conn.getHeaderField("location")
|
location = conn.getHeaderField("location")
|
||||||
}
|
}
|
||||||
if (!(location!!.startsWith("http://") || location.startsWith("https://"))) {
|
if (!(location!!.startsWith("http://") || location.startsWith("https://"))) {
|
||||||
//某些时候会省略host,只返回后面的path,所以需要补全url
|
//鏌愪簺鏃跺€欎細鐪佺暐host锛屽彧杩斿洖鍚庨潰鐨刾ath锛屾墍浠ラ渶瑕佽ˉ鍏╱rl
|
||||||
val originalUrl = URL(url)
|
val originalUrl = URL(url)
|
||||||
location = (originalUrl.protocol + "://"
|
location = (originalUrl.protocol + "://"
|
||||||
+ originalUrl.host + location)
|
+ originalUrl.host + location)
|
||||||
@@ -135,4 +135,5 @@ class HttpURLConnectionRequest(private val context: Context, private val request
|
|||||||
return code in 300..399
|
return code in 300..399
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ import com.alibaba.ams.emas.demo.ui.resolve.Response
|
|||||||
*/
|
*/
|
||||||
interface IRequest {
|
interface IRequest {
|
||||||
fun get(url: String): Response
|
fun get(url: String): Response
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ import java.util.concurrent.TimeUnit
|
|||||||
override fun lookup(hostname: String): List<InetAddress> {
|
override fun lookup(hostname: String): List<InetAddress> {
|
||||||
Log.d(tag, "start lookup $hostname via $mResolveMethod")
|
Log.d(tag, "start lookup $hostname via $mResolveMethod")
|
||||||
val dnsService = HttpDnsServiceHolder.getHttpDnsService(mContext.get()!!)
|
val dnsService = HttpDnsServiceHolder.getHttpDnsService(mContext.get()!!)
|
||||||
//修改为最新的通俗易懂的api
|
//淇敼涓烘渶鏂扮殑閫氫織鏄撴噦鐨刟pi
|
||||||
var httpDnsResult: HTTPDNSResult? = null
|
var httpDnsResult: HTTPDNSResult? = null
|
||||||
val inetAddresses = mutableListOf<InetAddress>()
|
val inetAddresses = mutableListOf<InetAddress>()
|
||||||
if (mResolveMethod == "getHttpDnsResultForHostSync(String host, RequestIpType type)") {
|
if (mResolveMethod == "getHttpDnsResultForHostSync(String host, RequestIpType type)") {
|
||||||
@@ -113,11 +113,11 @@ import java.util.concurrent.TimeUnit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(tag, "httpdns $hostname 解析结果 $httpDnsResult")
|
Log.d(tag, "httpdns $hostname 瑙f瀽缁撴灉 $httpDnsResult")
|
||||||
httpDnsResult?.let { processDnsResult(it, inetAddresses) }
|
httpDnsResult?.let { processDnsResult(it, inetAddresses) }
|
||||||
|
|
||||||
if (inetAddresses.isEmpty()) {
|
if (inetAddresses.isEmpty()) {
|
||||||
Log.d(tag, "httpdns 未返回IP,走local dns")
|
Log.d(tag, "httpdns 鏈繑鍥濱P锛岃蛋local dns")
|
||||||
return Dns.SYSTEM.lookup(hostname)
|
return Dns.SYSTEM.lookup(hostname)
|
||||||
}
|
}
|
||||||
return inetAddresses
|
return inetAddresses
|
||||||
@@ -147,3 +147,4 @@ import java.util.concurrent.TimeUnit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,4 +7,5 @@ class OkHttpLog: HttpLoggingInterceptor.Logger {
|
|||||||
override fun log(message: String) {
|
override fun log(message: String) {
|
||||||
Log.d("Okhttp", message)
|
Log.d("Okhttp", message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,4 +23,5 @@ class OkHttpRequest constructor(
|
|||||||
.use { response -> return Response(response.code, response.body?.string()) }
|
.use { response -> return Response(response.code, response.body?.string()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,4 +80,5 @@ class TLSSNISocketFactory(connection: HttpsURLConnection): SSLSocketFactory() {
|
|||||||
override fun getSupportedCipherSuites(): Array<String?> {
|
override fun getSupportedCipherSuites(): Array<String?> {
|
||||||
return arrayOfNulls(0)
|
return arrayOfNulls(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -175,4 +175,5 @@ class BasicSettingFragment : Fragment(), IBasicShowDialog {
|
|||||||
_binding?.initHttpdns?.isClickable = false
|
_binding?.initHttpdns?.isClickable = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,54 +37,54 @@ class BasicSettingViewModel(application: Application) : AndroidViewModel(applica
|
|||||||
var secretKeySetByConfig = true
|
var secretKeySetByConfig = true
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否开启鉴权模式
|
* 鏄惁寮€鍚壌鏉冩ā寮?
|
||||||
*/
|
*/
|
||||||
var enableAuthMode = true
|
var enableAuthMode = true
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否开启加密模式
|
* 鏄惁寮€鍚姞瀵嗘ā寮?
|
||||||
*/
|
*/
|
||||||
var enableEncryptMode = true
|
var enableEncryptMode = true
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否允许过期IP
|
* 鏄惁鍏佽杩囨湡IP
|
||||||
*/
|
*/
|
||||||
var enableExpiredIP = false
|
var enableExpiredIP = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否开启本地缓存
|
* 鏄惁寮€鍚湰鍦扮紦瀛?
|
||||||
*/
|
*/
|
||||||
var enableCacheIP = false
|
var enableCacheIP = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否允许HTTPS
|
* 鏄惁鍏佽HTTPS
|
||||||
*/
|
*/
|
||||||
var enableHttps = false
|
var enableHttps = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否开启降级
|
* 鏄惁寮€鍚檷绾?
|
||||||
*/
|
*/
|
||||||
var enableDegrade = false
|
var enableDegrade = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否允许网络切换自动刷新
|
* 鏄惁鍏佽缃戠粶鍒囨崲鑷姩鍒锋柊
|
||||||
*/
|
*/
|
||||||
var enableAutoRefresh = false
|
var enableAutoRefresh = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否允许打印日志
|
* 鏄惁鍏佽鎵撳嵃鏃ュ織
|
||||||
*/
|
*/
|
||||||
var enableLog = false
|
var enableLog = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当前Region
|
* 褰撳墠Region
|
||||||
*/
|
*/
|
||||||
var currentRegion = SingleLiveData<String>().apply {
|
var currentRegion = SingleLiveData<String>().apply {
|
||||||
value = ""
|
value = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当前超时
|
* 褰撳墠瓒呮椂
|
||||||
*/
|
*/
|
||||||
var currentTimeout = SingleLiveData<String>().apply {
|
var currentTimeout = SingleLiveData<String>().apply {
|
||||||
value = "2000ms"
|
value = "2000ms"
|
||||||
@@ -188,7 +188,7 @@ class BasicSettingViewModel(application: Application) : AndroidViewModel(applica
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setRegion() {
|
fun setRegion() {
|
||||||
//弹窗选择region
|
//寮圭獥閫夋嫨region
|
||||||
showDialog?.showSelectRegionDialog()
|
showDialog?.showSelectRegionDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,21 +253,21 @@ class BasicSettingViewModel(application: Application) : AndroidViewModel(applica
|
|||||||
val timeout = preferences.getInt(KEY_TIMEOUT, 2000)
|
val timeout = preferences.getInt(KEY_TIMEOUT, 2000)
|
||||||
val region = preferences.getString(KEY_REGION, "cn")
|
val region = preferences.getString(KEY_REGION, "cn")
|
||||||
val enableDegradationLocalDns = preferences.getBoolean(KEY_ENABLE_DEGRADE, false);
|
val enableDegradationLocalDns = preferences.getBoolean(KEY_ENABLE_DEGRADE, false);
|
||||||
//自定义ttl
|
//鑷畾涔塼tl
|
||||||
val ttlCacheStr = preferences.getString(KEY_TTL_CHANGER, null)
|
val ttlCacheStr = preferences.getString(KEY_TTL_CHANGER, null)
|
||||||
TtlCacheHolder.convertTtlCacheData(ttlCacheStr)
|
TtlCacheHolder.convertTtlCacheData(ttlCacheStr)
|
||||||
//IP探测
|
//IP鎺㈡祴
|
||||||
val ipRankingItemJson = preferences.getString(KEY_IP_RANKING_ITEMS, null)
|
val ipRankingItemJson = preferences.getString(KEY_IP_RANKING_ITEMS, null)
|
||||||
//主站域名
|
//涓荤珯鍩熷悕
|
||||||
val hostListWithFixedIpJson =
|
val hostListWithFixedIpJson =
|
||||||
preferences.getString(KEY_HOST_WITH_FIXED_IP, null)
|
preferences.getString(KEY_HOST_WITH_FIXED_IP, null)
|
||||||
val tagsJson = preferences.getString(KEY_TAGS, null)
|
val tagsJson = preferences.getString(KEY_TAGS, null)
|
||||||
//预解析
|
//棰勮В鏋?
|
||||||
val preResolveHostList = preferences.getString(KEY_PRE_RESOLVE_HOST_LIST, null)
|
val preResolveHostList = preferences.getString(KEY_PRE_RESOLVE_HOST_LIST, null)
|
||||||
preResolveHostList?.let { Log.d("httpdns:HttpDnsApplication", "pre resolve list: $it") }
|
preResolveHostList?.let { Log.d("httpdns:HttpDnsApplication", "pre resolve list: $it") }
|
||||||
PreResolveCacheHolder.convertPreResolveCacheData(preResolveHostList)
|
PreResolveCacheHolder.convertPreResolveCacheData(preResolveHostList)
|
||||||
|
|
||||||
//批量解析
|
//鎵归噺瑙f瀽
|
||||||
val batchResolveHostList = preferences.getString(KEY_BATCH_RESOLVE_HOST_LIST, null)
|
val batchResolveHostList = preferences.getString(KEY_BATCH_RESOLVE_HOST_LIST, null)
|
||||||
BatchResolveCacheHolder.convertBatchResolveCacheData(batchResolveHostList)
|
BatchResolveCacheHolder.convertBatchResolveCacheData(batchResolveHostList)
|
||||||
|
|
||||||
@@ -358,4 +358,5 @@ class BasicSettingViewModel(application: Application) : AndroidViewModel(applica
|
|||||||
private fun getString(resId: Int, vararg args: String): String {
|
private fun getString(resId: Int, vararg args: String): String {
|
||||||
return getApplication<HttpDnsApplication>().getString(resId, *args)
|
return getApplication<HttpDnsApplication>().getString(resId, *args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user