This commit is contained in:
unknown
2026-02-04 20:27:13 +08:00
commit 3b042d1dad
9410 changed files with 1488147 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package ui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type CityOptionsAction struct {
actionutils.ParentAction
}
func (this *CityOptionsAction) RunPost(params struct{}) {
citiesResp, err := this.RPC().RegionCityRPC().FindAllRegionCities(this.AdminContext(), &pb.FindAllRegionCitiesRequest{
IncludeRegionProvince: true,
})
if err != nil {
this.ErrorPage(err)
return
}
var cityMaps = []maps.Map{}
for _, city := range citiesResp.RegionCities {
if city.Codes == nil {
city.Codes = []string{}
}
var fullname = city.Name
if city.RegionProvince != nil && len(city.RegionProvince.Name) > 0 && city.RegionProvince.Name != city.Name {
fullname = city.RegionProvince.Name + " " + fullname
}
cityMaps = append(cityMaps, maps.Map{
"id": city.Id,
"name": city.Name,
"fullname": fullname,
"codes": city.Codes,
})
}
this.Data["cities"] = cityMaps
this.Success()
}

View File

@@ -0,0 +1,175 @@
package ui
import (
"bytes"
"crypto/md5"
"encoding/json"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/conds/condutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/files"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
"net/http"
)
type ComponentsAction actions.Action
var componentsData = []byte{}
var componentsDataSum string
func (this *ComponentsAction) RunGet(params struct{}) {
this.AddHeader("Content-Type", "text/javascript; charset=utf-8")
// etag
var requestETag = this.Header("If-None-Match")
if len(requestETag) > 0 && requestETag == "\""+componentsDataSum+"\"" {
this.ResponseWriter.WriteHeader(http.StatusNotModified)
return
}
if !Tea.IsTesting() && len(componentsData) > 0 {
this.AddHeader("Last-Modified", "Fri, 06 Sep 2019 08:29:50 GMT")
_, _ = this.Write(componentsData)
return
}
var buffer = bytes.NewBuffer([]byte{})
var webRoot string
if Tea.IsTesting() {
webRoot = Tea.Root + "/../web/public/js/components/"
} else {
webRoot = Tea.Root + "/web/public/js/components/"
}
f := files.NewFile(webRoot)
f.Range(func(file *files.File) {
if !file.IsFile() {
return
}
if file.Ext() != ".js" {
return
}
data, err := file.ReadAll()
if err != nil {
logs.Error(err)
return
}
buffer.Write(data)
buffer.Write([]byte{'\n', '\n'})
})
// 条件组件
typesJSON, err := json.Marshal(condutils.ReadAllAvailableCondTypes())
if err != nil {
logs.Println("ComponentsAction marshal request cond types failed: " + err.Error())
} else {
buffer.WriteString("window.REQUEST_COND_COMPONENTS = ")
buffer.Write(typesJSON)
buffer.Write([]byte{';', '\n', '\n'})
}
// 条件操作符
requestOperatorsJSON, err := json.Marshal(shared.AllRequestOperators())
if err != nil {
logs.Println("ComponentsAction marshal request operators failed: " + err.Error())
} else {
buffer.WriteString("window.REQUEST_COND_OPERATORS = ")
buffer.Write(requestOperatorsJSON)
buffer.Write([]byte{';', '\n', '\n'})
}
// 请求变量
requestVariablesJSON, err := json.Marshal(shared.DefaultRequestVariables())
if err != nil {
logs.Println("ComponentsAction marshal request variables failed: " + err.Error())
} else {
buffer.WriteString("window.REQUEST_VARIABLES = ")
buffer.Write(requestVariablesJSON)
buffer.Write([]byte{';', '\n', '\n'})
}
// 指标
metricHTTPKeysJSON, err := json.Marshal(serverconfigs.FindAllMetricKeyDefinitions(serverconfigs.MetricItemCategoryHTTP))
if err != nil {
logs.Println("ComponentsAction marshal metric http keys failed: " + err.Error())
} else {
buffer.WriteString("window.METRIC_HTTP_KEYS = ")
buffer.Write(metricHTTPKeysJSON)
buffer.Write([]byte{';', '\n', '\n'})
}
// IP地址阈值项目
ipAddrThresholdItemsJSON, err := json.Marshal(nodeconfigs.FindAllIPAddressThresholdItems())
if err != nil {
logs.Println("ComponentsAction marshal ip addr threshold items failed: " + err.Error())
} else {
buffer.WriteString("window.IP_ADDR_THRESHOLD_ITEMS = ")
buffer.Write(ipAddrThresholdItemsJSON)
buffer.Write([]byte{';', '\n', '\n'})
}
// IP地址阈值动作
ipAddrThresholdActionsJSON, err := json.Marshal(nodeconfigs.FindAllIPAddressThresholdActions())
if err != nil {
logs.Println("ComponentsAction marshal ip addr threshold actions failed: " + err.Error())
} else {
buffer.WriteString("window.IP_ADDR_THRESHOLD_ACTIONS = ")
buffer.Write(ipAddrThresholdActionsJSON)
buffer.Write([]byte{';', '\n', '\n'})
}
// WAF checkpoints
var wafCheckpointsMaps = []maps.Map{}
for _, checkpoint := range firewallconfigs.AllCheckpoints {
wafCheckpointsMaps = append(wafCheckpointsMaps, maps.Map{
"name": checkpoint.Name,
"prefix": checkpoint.Prefix,
"description": checkpoint.Description,
})
}
wafCheckpointsJSON, err := json.Marshal(wafCheckpointsMaps)
if err != nil {
logs.Println("ComponentsAction marshal waf rule checkpoints failed: " + err.Error())
} else {
buffer.WriteString("window.WAF_RULE_CHECKPOINTS = ")
buffer.Write(wafCheckpointsJSON)
buffer.Write([]byte{';', '\n', '\n'})
}
// WAF操作符
wafOperatorsJSON, err := json.Marshal(firewallconfigs.AllRuleOperators)
if err != nil {
logs.Println("ComponentsAction marshal waf rule operators failed: " + err.Error())
} else {
buffer.WriteString("window.WAF_RULE_OPERATORS = ")
buffer.Write(wafOperatorsJSON)
buffer.Write([]byte{';', '\n', '\n'})
}
// WAF验证码类型
captchaTypesJSON, err := json.Marshal(firewallconfigs.FindAllCaptchaTypes())
if err != nil {
logs.Println("ComponentsAction marshal captcha types failed: " + err.Error())
} else {
buffer.WriteString("window.WAF_CAPTCHA_TYPES = ")
buffer.Write(captchaTypesJSON)
buffer.Write([]byte{';', '\n', '\n'})
}
componentsData = buffer.Bytes()
// ETag
var h = md5.New()
h.Write(buffer.Bytes())
componentsDataSum = fmt.Sprintf("%x", h.Sum(nil))
this.AddHeader("ETag", "\""+componentsDataSum+"\"")
_, _ = this.Write(componentsData)
}

View File

@@ -0,0 +1,49 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package ui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/regionconfigs"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"strings"
)
type CountryOptionsAction struct {
actionutils.ParentAction
}
func (this *CountryOptionsAction) RunPost(params struct{}) {
countriesResp, err := this.RPC().RegionCountryRPC().FindAllRegionCountries(this.AdminContext(), &pb.FindAllRegionCountriesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var countryMaps = []maps.Map{}
for _, country := range countriesResp.RegionCountries {
if lists.ContainsInt64(regionconfigs.FindAllGreaterChinaSubRegionIds(), country.Id) {
continue
}
if country.Codes == nil {
country.Codes = []string{}
}
var letter = ""
if len(country.Pinyin) > 0 && len(country.Pinyin[0]) > 0 {
letter = strings.ToUpper(country.Pinyin[0][:1])
}
countryMaps = append(countryMaps, maps.Map{
"id": country.Id,
"name": country.Name,
"fullname": letter + " " + country.Name,
"codes": country.Codes,
})
}
this.Data["countries"] = countryMaps
this.Success()
}

View File

@@ -0,0 +1,22 @@
package ui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
)
// DownloadAction 下载指定的文本内容
type DownloadAction struct {
actionutils.ParentAction
}
func (this *DownloadAction) Init() {
this.Nav("", "", "")
}
func (this *DownloadAction) RunGet(params struct {
File string
Text string
}) {
this.AddHeader("Content-Disposition", "attachment; filename=\""+params.File+"\";")
this.WriteString(params.Text)
}

View File

@@ -0,0 +1,16 @@
package ui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
)
type EventLevelOptionsAction struct {
actionutils.ParentAction
}
func (this *EventLevelOptionsAction) RunPost(params struct{}) {
this.Data["eventLevels"] = firewallconfigs.FindAllFirewallEventLevels()
this.Success()
}

View File

@@ -0,0 +1,30 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package ui
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/Tea"
"os"
)
type HideTipAction struct {
actionutils.ParentAction
}
func (this *HideTipAction) RunPost(params struct {
Code string
}) {
tipKeyLocker.Lock()
tipKeyMap[params.Code] = true
tipKeyLocker.Unlock()
// 保存到文件
tipJSON, err := json.Marshal(tipKeyMap)
if err == nil {
_ = os.WriteFile(Tea.ConfigFile(tipConfigFile), tipJSON, 0666)
}
this.Success()
}

View File

@@ -0,0 +1,68 @@
package ui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"mime"
"path/filepath"
"strconv"
)
// 公开的图片,不需要检查用户权限
type ImageAction struct {
actionutils.ParentAction
}
func (this *ImageAction) Init() {
this.Nav("", "", "")
}
func (this *ImageAction) RunGet(params struct {
FileId int64
}) {
fileResp, err := this.RPC().FileRPC().FindEnabledFile(this.AdminContext(), &pb.FindEnabledFileRequest{FileId: params.FileId})
if err != nil {
this.ErrorPage(err)
return
}
file := fileResp.File
if file == nil {
this.NotFound("file", params.FileId)
return
}
if !file.IsPublic {
this.NotFound("file", params.FileId)
return
}
chunkIdsResp, err := this.RPC().FileChunkRPC().FindAllFileChunkIds(this.AdminContext(), &pb.FindAllFileChunkIdsRequest{FileId: file.Id})
if err != nil {
this.ErrorPage(err)
return
}
mimeType := ""
if len(file.Filename) > 0 {
ext := filepath.Ext(file.Filename)
mimeType = mime.TypeByExtension(ext)
}
if len(mimeType) == 0 {
mimeType = "image/png"
}
this.AddHeader("Last-Modified", "Fri, 06 Sep 2019 08:29:50 GMT")
this.AddHeader("Content-Type", mimeType)
this.AddHeader("Content-Length", strconv.FormatInt(file.Size, 10))
for _, chunkId := range chunkIdsResp.FileChunkIds {
chunkResp, err := this.RPC().FileChunkRPC().DownloadFileChunk(this.AdminContext(), &pb.DownloadFileChunkRequest{FileChunkId: chunkId})
if err != nil {
this.ErrorPage(err)
return
}
if chunkResp.FileChunk == nil {
continue
}
_, _ = this.Write(chunkResp.FileChunk.Data)
}
}

View File

@@ -0,0 +1,41 @@
package ui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
"github.com/iwind/TeaGo/Tea"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Prefix("/ui").
// 公共可以访问的链接
Get("/image/:fileId", new(ImageAction)).
// 以下需要登录
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeCommon)).
Get("/download", new(DownloadAction)).
GetPost("/selectProvincesPopup", new(SelectProvincesPopupAction)).
GetPost("/selectCountriesPopup", new(SelectCountriesPopupAction)).
Post("/eventLevelOptions", new(EventLevelOptionsAction)).
Post("/showTip", new(ShowTipAction)).
Post("/hideTip", new(HideTipAction)).
Post("/theme", new(ThemeAction)).
Post("/validateIPs", new(ValidateIPsAction)).
Post("/providerOptions", new(ProviderOptionsAction)).
Post("/countryOptions", new(CountryOptionsAction)).
Post("/provinceOptions", new(ProvinceOptionsAction)).
Post("/cityOptions", new(CityOptionsAction)).
EndAll()
// 开发环境下总是动态加载,以便于调试
if Tea.IsTesting() {
server.
Get("/js/components.js", new(ComponentsAction)).
EndAll()
}
})
}

View File

@@ -0,0 +1,36 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package ui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type ProviderOptionsAction struct {
actionutils.ParentAction
}
func (this *ProviderOptionsAction) RunPost(params struct{}) {
providersResp, err := this.RPC().RegionProviderRPC().FindAllRegionProviders(this.AdminContext(), &pb.FindAllRegionProvidersRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var providerMaps = []maps.Map{}
for _, provider := range providersResp.RegionProviders {
if provider.Codes == nil {
provider.Codes = []string{}
}
providerMaps = append(providerMaps, maps.Map{
"id": provider.Id,
"name": provider.Name,
"codes": provider.Codes,
})
}
this.Data["providers"] = providerMaps
this.Success()
}

View File

@@ -0,0 +1,36 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package ui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/regionconfigs"
"github.com/iwind/TeaGo/maps"
)
type ProvinceOptionsAction struct {
actionutils.ParentAction
}
func (this *ProvinceOptionsAction) RunPost(params struct{}) {
provincesResp, err := this.RPC().RegionProvinceRPC().FindAllRegionProvincesWithRegionCountryId(this.AdminContext(), &pb.FindAllRegionProvincesWithRegionCountryIdRequest{RegionCountryId: regionconfigs.RegionChinaId})
if err != nil {
this.ErrorPage(err)
return
}
var provinceMaps = []maps.Map{}
for _, province := range provincesResp.RegionProvinces {
if province.Codes == nil {
province.Codes = []string{}
}
provinceMaps = append(provinceMaps, maps.Map{
"id": province.Id,
"name": province.DisplayName,
"codes": province.Codes,
})
}
this.Data["provinces"] = provinceMaps
this.Success()
}

View File

@@ -0,0 +1,70 @@
package ui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"strings"
)
type SelectCountriesPopupAction struct {
actionutils.ParentAction
}
func (this *SelectCountriesPopupAction) Init() {
this.Nav("", "", "")
}
func (this *SelectCountriesPopupAction) RunGet(params struct {
CountryIds string
}) {
var selectedCountryIds = utils.SplitNumbers(params.CountryIds)
countriesResp, err := this.RPC().RegionCountryRPC().FindAllRegionCountries(this.AdminContext(), &pb.FindAllRegionCountriesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var countryMaps = []maps.Map{}
for _, country := range countriesResp.RegionCountries {
countryMaps = append(countryMaps, maps.Map{
"id": country.Id,
"name": country.DisplayName,
"letter": strings.ToUpper(string(country.Pinyin[0][0])),
"isChecked": lists.ContainsInt64(selectedCountryIds, country.Id),
})
}
this.Data["countries"] = countryMaps
this.Show()
}
func (this *SelectCountriesPopupAction) RunPost(params struct {
CountryIds []int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var countryMaps = []maps.Map{}
for _, countryId := range params.CountryIds {
countryResp, err := this.RPC().RegionCountryRPC().FindRegionCountry(this.AdminContext(), &pb.FindRegionCountryRequest{RegionCountryId: countryId})
if err != nil {
this.ErrorPage(err)
return
}
country := countryResp.RegionCountry
if country == nil {
continue
}
countryMaps = append(countryMaps, maps.Map{
"id": country.Id,
"name": country.DisplayName,
})
}
this.Data["countries"] = countryMaps
this.Success()
}

View File

@@ -0,0 +1,69 @@
package ui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/regionconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
)
type SelectProvincesPopupAction struct {
actionutils.ParentAction
}
func (this *SelectProvincesPopupAction) Init() {
this.Nav("", "", "")
}
func (this *SelectProvincesPopupAction) RunGet(params struct {
ProvinceIds string
}) {
var selectedProvinceIds = utils.SplitNumbers(params.ProvinceIds)
provincesResp, err := this.RPC().RegionProvinceRPC().FindAllRegionProvincesWithRegionCountryId(this.AdminContext(), &pb.FindAllRegionProvincesWithRegionCountryIdRequest{RegionCountryId: regionconfigs.RegionChinaId})
if err != nil {
this.ErrorPage(err)
return
}
var provinceMaps = []maps.Map{}
for _, province := range provincesResp.RegionProvinces {
provinceMaps = append(provinceMaps, maps.Map{
"id": province.Id,
"name": province.DisplayName,
"isChecked": lists.ContainsInt64(selectedProvinceIds, province.Id),
})
}
this.Data["provinces"] = provinceMaps
this.Show()
}
func (this *SelectProvincesPopupAction) RunPost(params struct {
ProvinceIds []int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var provinceMaps = []maps.Map{}
for _, provinceId := range params.ProvinceIds {
provinceResp, err := this.RPC().RegionProvinceRPC().FindRegionProvince(this.AdminContext(), &pb.FindRegionProvinceRequest{RegionProvinceId: provinceId})
if err != nil {
this.ErrorPage(err)
return
}
province := provinceResp.RegionProvince
if province == nil {
continue
}
provinceMaps = append(provinceMaps, maps.Map{
"id": province.Id,
"name": province.DisplayName,
})
}
this.Data["provinces"] = provinceMaps
this.Success()
}

View File

@@ -0,0 +1,48 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package ui
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/iwind/TeaGo"
"github.com/iwind/TeaGo/Tea"
"os"
"sync"
)
var tipKeyMap = map[string]bool{}
var tipKeyLocker = sync.Mutex{}
var tipConfigFile = "tip.cache.json"
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
// 从配置文件中加载已关闭的tips
data, err := os.ReadFile(Tea.ConfigFile(tipConfigFile))
if err == nil {
var m = map[string]bool{}
err = json.Unmarshal(data, &m)
if err == nil {
tipKeyLocker.Lock()
tipKeyMap = m
tipKeyLocker.Unlock()
}
}
})
}
type ShowTipAction struct {
actionutils.ParentAction
}
func (this *ShowTipAction) RunPost(params struct {
Code string
}) {
tipKeyLocker.Lock()
_, ok := tipKeyMap[params.Code]
tipKeyLocker.Unlock()
this.Data["visible"] = !ok
this.Success()
}

View File

@@ -0,0 +1,47 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package ui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type ThemeAction struct {
actionutils.ParentAction
}
func (this *ThemeAction) RunPost(params struct{}) {
theme := configloaders.FindAdminTheme(this.AdminId())
var themes = []string{"theme1", "theme2", "theme3", "theme4", "theme5", "theme6", "theme7"}
var nextTheme = "theme1"
if len(theme) == 0 {
nextTheme = "theme2"
} else {
for index, t := range themes {
if t == theme {
if index < len(themes)-1 {
nextTheme = themes[index+1]
break
}
}
}
}
_, err := this.RPC().AdminRPC().UpdateAdminTheme(this.AdminContext(), &pb.UpdateAdminThemeRequest{
AdminId: this.AdminId(),
Theme: nextTheme,
})
if err != nil {
this.ErrorPage(err)
return
}
configloaders.UpdateAdminTheme(this.AdminId(), nextTheme)
this.Data["theme"] = nextTheme
this.Success()
}

View File

@@ -0,0 +1,41 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package ui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"net"
"strings"
)
type ValidateIPsAction struct {
actionutils.ParentAction
}
func (this *ValidateIPsAction) RunPost(params struct {
Ips string
}) {
var ips = params.Ips
if len(ips) == 0 {
this.Data["ips"] = []string{}
this.Success()
}
var ipSlice = strings.Split(ips, "\n")
var result = []string{}
for _, ip := range ipSlice {
ip = strings.TrimSpace(ip)
if len(ip) == 0 {
continue
}
data := net.ParseIP(ip)
if len(data) == 0 {
this.Data["failIP"] = ip
this.Fail()
}
result = append(result, ip)
}
this.Data["ips"] = result
this.Success()
}