Initial commit (code only without large binaries)

This commit is contained in:
robin
2026-02-15 18:58:44 +08:00
commit 35df75498f
9442 changed files with 1495866 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package cities
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/iplibraryutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "city")
}
func (this *IndexAction) RunGet(params struct {
CountryId int64
ProvinceId int64
}) {
// 检查权限
if !iplibraryutils.CanAccess() {
return
}
this.Data["canAccess"] = true
if params.CountryId <= 0 || params.ProvinceId <= 0 {
params.CountryId = 1 // china
params.ProvinceId = 1 // beijing
}
this.Data["countryId"] = params.CountryId
this.Data["provinceId"] = params.ProvinceId
// 所有国家/地区
countriesResp, err := this.RPC().RegionCountryRPC().FindAllRegionCountries(this.AdminContext(), &pb.FindAllRegionCountriesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var countryMaps = []maps.Map{}
for _, country := range countriesResp.RegionCountries {
countryMaps = append(countryMaps, maps.Map{
"id": country.Id,
"name": country.DisplayName,
})
}
this.Data["countries"] = countryMaps
// 城市列表
citiesResp, err := this.RPC().RegionCityRPC().FindAllRegionCitiesWithRegionProvinceId(this.AdminContext(), &pb.FindAllRegionCitiesWithRegionProvinceIdRequest{
RegionProvinceId: params.ProvinceId,
})
if err != nil {
this.ErrorPage(err)
return
}
var cityMaps = []maps.Map{}
for _, city := range citiesResp.RegionCities {
if city.Codes == nil {
city.Codes = []string{}
}
if city.CustomCodes == nil {
city.CustomCodes = []string{}
}
cityMaps = append(cityMaps, maps.Map{
"id": city.Id,
"name": city.Name,
"codes": city.Codes,
"customName": city.CustomName,
"customCodes": city.CustomCodes,
})
}
this.Data["cities"] = cityMaps
this.Show()
}

View File

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

View File

@@ -0,0 +1,72 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package cities
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
CityId int64
}) {
cityResp, err := this.RPC().RegionCityRPC().FindRegionCity(this.AdminContext(), &pb.FindRegionCityRequest{RegionCityId: params.CityId})
if err != nil {
this.ErrorPage(err)
return
}
var city = cityResp.RegionCity
if city == nil {
this.NotFound("regionCity", params.CityId)
return
}
if city.Codes == nil {
city.Codes = []string{}
}
if city.CustomCodes == nil {
city.CustomCodes = []string{}
}
this.Data["city"] = maps.Map{
"id": city.Id,
"name": city.Name,
"codes": city.Codes,
"customName": city.CustomName,
"customCodes": city.CustomCodes,
}
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
CityId int64
CustomName string
CustomCodes []string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.RegionCity_LogUpdateRegionCityCustom, params.CityId)
_, err := this.RPC().RegionCityRPC().UpdateRegionCityCustom(this.AdminContext(), &pb.UpdateRegionCityCustomRequest{
RegionCityId: params.CityId,
CustomName: params.CustomName,
CustomCodes: params.CustomCodes,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,52 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package countries
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/iplibraryutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "country")
}
func (this *IndexAction) RunGet(params struct{}) {
// 检查权限
if !iplibraryutils.CanAccess() {
return
}
this.Data["canAccess"] = true
countriesResp, err := this.RPC().RegionCountryRPC().FindAllRegionCountries(this.AdminContext(), &pb.FindAllRegionCountriesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var countryMaps = []maps.Map{}
for _, country := range countriesResp.RegionCountries {
if country.Codes == nil {
country.Codes = []string{}
}
if country.CustomCodes == nil {
country.CustomCodes = []string{}
}
countryMaps = append(countryMaps, maps.Map{
"id": country.Id,
"name": country.Name,
"codes": country.Codes,
"customName": country.CustomName,
"customCodes": country.CustomCodes,
})
}
this.Data["countries"] = countryMaps
this.Show()
}

View File

@@ -0,0 +1,74 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package countries
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
CountryId int64
}) {
countryResp, err := this.RPC().RegionCountryRPC().FindRegionCountry(this.AdminContext(), &pb.FindRegionCountryRequest{
RegionCountryId: params.CountryId,
})
if err != nil {
this.ErrorPage(err)
return
}
var country = countryResp.RegionCountry
if country == nil {
this.NotFound("regionCountry", params.CountryId)
return
}
if country.Codes == nil {
country.Codes = []string{}
}
if country.CustomCodes == nil {
country.CustomCodes = []string{}
}
this.Data["country"] = maps.Map{
"id": country.Id,
"name": country.Name,
"codes": country.Codes,
"customName": country.CustomName,
"customCodes": country.CustomCodes,
}
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
CountryId int64
CustomName string
CustomCodes []string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.RegionCountry_LogUpdateRegionCountryCustom, params.CountryId)
_, err := this.RPC().RegionCountryRPC().UpdateRegionCountryCustom(this.AdminContext(), &pb.UpdateRegionCountryCustomRequest{
RegionCountryId: params.CountryId,
CustomName: params.CustomName,
CustomCodes: params.CustomCodes,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,48 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package creating
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/lists"
)
type AddCityCustomCodeAction struct {
actionutils.ParentAction
}
func (this *AddCityCustomCodeAction) RunPost(params struct {
CityId int64
Code string
}) {
defer this.CreateLogInfo(codes.RegionCity_LogAddRegionCityCode, params.CityId, params.Code)
cityResp, err := this.RPC().RegionCityRPC().FindRegionCity(this.AdminContext(), &pb.FindRegionCityRequest{RegionCityId: params.CityId})
if err != nil {
this.ErrorPage(err)
return
}
var city = cityResp.RegionCity
if city == nil {
this.NotFound("regionCity", params.CityId)
return
}
if len(params.Code) > 0 && !lists.ContainsString(city.CustomCodes, params.Code) {
city.CustomCodes = append(city.CustomCodes, params.Code)
_, err = this.RPC().RegionCityRPC().UpdateRegionCityCustom(this.AdminContext(), &pb.UpdateRegionCityCustomRequest{
RegionCityId: params.CityId,
CustomName: city.CustomName,
CustomCodes: city.CustomCodes,
})
if err != nil {
this.ErrorPage(err)
return
}
}
this.Success()
}

View File

@@ -0,0 +1,48 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package creating
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/lists"
)
type AddCountryCustomCodeAction struct {
actionutils.ParentAction
}
func (this *AddCountryCustomCodeAction) RunPost(params struct {
CountryId int64
Code string
}) {
defer this.CreateLogInfo(codes.RegionCountry_LogAddRegionCountryCode, params.CountryId, params.Code)
countryResp, err := this.RPC().RegionCountryRPC().FindRegionCountry(this.AdminContext(), &pb.FindRegionCountryRequest{RegionCountryId: params.CountryId})
if err != nil {
this.ErrorPage(err)
return
}
var country = countryResp.RegionCountry
if country == nil {
this.NotFound("regionCountry", params.CountryId)
return
}
if len(params.Code) > 0 && !lists.ContainsString(country.CustomCodes, params.Code) {
country.CustomCodes = append(country.CustomCodes, params.Code)
_, err = this.RPC().RegionCountryRPC().UpdateRegionCountryCustom(this.AdminContext(), &pb.UpdateRegionCountryCustomRequest{
RegionCountryId: params.CountryId,
CustomName: country.CustomName,
CustomCodes: country.CustomCodes,
})
if err != nil {
this.ErrorPage(err)
return
}
}
this.Success()
}

View File

@@ -0,0 +1,48 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package creating
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/lists"
)
type AddProviderCustomCodeAction struct {
actionutils.ParentAction
}
func (this *AddProviderCustomCodeAction) RunPost(params struct {
ProviderId int64
Code string
}) {
defer this.CreateLogInfo(codes.RegionProvider_LogAddRegionProviderCode, params.ProviderId, params.Code)
providerResp, err := this.RPC().RegionProviderRPC().FindRegionProvider(this.AdminContext(), &pb.FindRegionProviderRequest{RegionProviderId: params.ProviderId})
if err != nil {
this.ErrorPage(err)
return
}
var provider = providerResp.RegionProvider
if provider == nil {
this.NotFound("regionProvider", params.ProviderId)
return
}
if len(params.Code) > 0 && !lists.ContainsString(provider.CustomCodes, params.Code) {
provider.CustomCodes = append(provider.CustomCodes, params.Code)
_, err = this.RPC().RegionProviderRPC().UpdateRegionProviderCustom(this.AdminContext(), &pb.UpdateRegionProviderCustomRequest{
RegionProviderId: params.ProviderId,
CustomName: provider.CustomName,
CustomCodes: provider.CustomCodes,
})
if err != nil {
this.ErrorPage(err)
return
}
}
this.Success()
}

View File

@@ -0,0 +1,48 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package creating
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/lists"
)
type AddProvinceCustomCodeAction struct {
actionutils.ParentAction
}
func (this *AddProvinceCustomCodeAction) RunPost(params struct {
ProvinceId int64
Code string
}) {
defer this.CreateLogInfo(codes.RegionProvince_LogAddRegionProvinceCode, params.ProvinceId, params.Code)
provinceResp, err := this.RPC().RegionProvinceRPC().FindRegionProvince(this.AdminContext(), &pb.FindRegionProvinceRequest{RegionProvinceId: params.ProvinceId})
if err != nil {
this.ErrorPage(err)
return
}
var province = provinceResp.RegionProvince
if province == nil {
this.NotFound("regionProvince", params.ProvinceId)
return
}
if len(params.Code) > 0 && !lists.ContainsString(province.CustomCodes, params.Code) {
province.CustomCodes = append(province.CustomCodes, params.Code)
_, err = this.RPC().RegionProvinceRPC().UpdateRegionProvinceCustom(this.AdminContext(), &pb.UpdateRegionProvinceCustomRequest{
RegionProvinceId: params.ProvinceId,
CustomName: province.CustomName,
CustomCodes: province.CustomCodes,
})
if err != nil {
this.ErrorPage(err)
return
}
}
this.Success()
}

View File

@@ -0,0 +1,48 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package creating
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/lists"
)
type AddTownCustomCodeAction struct {
actionutils.ParentAction
}
func (this *AddTownCustomCodeAction) RunPost(params struct {
TownId int64
Code string
}) {
defer this.CreateLogInfo(codes.RegionTown_LogAddRegionTownCode, params.TownId, params.Code)
townResp, err := this.RPC().RegionTownRPC().FindRegionTown(this.AdminContext(), &pb.FindRegionTownRequest{RegionTownId: params.TownId})
if err != nil {
this.ErrorPage(err)
return
}
var town = townResp.RegionTown
if town == nil {
this.NotFound("regionTown", params.TownId)
return
}
if len(params.Code) > 0 && !lists.ContainsString(town.CustomCodes, params.Code) {
town.CustomCodes = append(town.CustomCodes, params.Code)
_, err = this.RPC().RegionTownRPC().UpdateRegionTownCustom(this.AdminContext(), &pb.UpdateRegionTownCustomRequest{
RegionTownId: params.TownId,
CustomName: town.CustomName,
CustomCodes: town.CustomCodes,
})
if err != nil {
this.ErrorPage(err)
return
}
}
this.Success()
}

View File

@@ -0,0 +1,50 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package creating
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type CitiesAction struct {
actionutils.ParentAction
}
func (this *CitiesAction) RunPost(params struct {
LibraryFileId int64
Size int
}) {
missingCitiesResp, err := this.RPC().IPLibraryFileRPC().CheckCitiesWithIPLibraryFileId(this.AdminContext(), &pb.CheckCitiesWithIPLibraryFileIdRequest{
IpLibraryFileId: params.LibraryFileId,
})
if err != nil {
this.ErrorPage(err)
return
}
var missingCityMaps = []maps.Map{}
for _, missingCity := range missingCitiesResp.MissingCities {
var similarCityMaps = []maps.Map{}
for _, city := range missingCity.SimilarCities {
similarCityMaps = append(similarCityMaps, maps.Map{
"id": city.Id,
"displayName": city.DisplayName,
})
}
missingCityMaps = append(missingCityMaps, maps.Map{
"countryName": missingCity.CountryName,
"provinceName": missingCity.ProvinceName,
"cityName": missingCity.CityName,
"similarCities": similarCityMaps,
})
if params.Size > 0 && len(missingCityMaps) >= params.Size {
break
}
}
this.Data["missingCities"] = missingCityMaps
this.Success()
}

View File

@@ -0,0 +1,43 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package creating
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type CountriesAction struct {
actionutils.ParentAction
}
func (this *CountriesAction) RunPost(params struct {
LibraryFileId int64
}) {
missingCountriesResp, err := this.RPC().IPLibraryFileRPC().CheckCountriesWithIPLibraryFileId(this.AdminContext(), &pb.CheckCountriesWithIPLibraryFileIdRequest{
IpLibraryFileId: params.LibraryFileId,
})
if err != nil {
this.ErrorPage(err)
return
}
var missingCountryMaps = []maps.Map{}
for _, missingCountry := range missingCountriesResp.MissingCountries {
var similarCountryMaps = []maps.Map{}
for _, country := range missingCountry.SimilarCountries {
similarCountryMaps = append(similarCountryMaps, maps.Map{
"id": country.Id,
"displayName": country.DisplayName,
})
}
missingCountryMaps = append(missingCountryMaps, maps.Map{
"countryName": missingCountry.CountryName,
"similarCountries": similarCountryMaps,
})
}
this.Data["missingCountries"] = missingCountryMaps
this.Success()
}

View File

@@ -0,0 +1,35 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package creating
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type FinishAction struct {
actionutils.ParentAction
}
func (this *FinishAction) RunPost(params struct {
LibraryFileId int64
}) {
defer this.CreateLogInfo(codes.IPLibrary_LogFinishIPLibrary, params.LibraryFileId)
// set finished
_, err := this.RPC().IPLibraryFileRPC().UpdateIPLibraryFileFinished(this.AdminContext(), &pb.UpdateIPLibraryFileFinishedRequest{IpLibraryFileId: params.LibraryFileId})
if err != nil {
this.ErrorPage(err)
return
}
// generate
_, err = this.RPC().IPLibraryFileRPC().GenerateIPLibraryFile(this.AdminContext(), &pb.GenerateIPLibraryFileRequest{IpLibraryFileId: params.LibraryFileId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,27 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package creating
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type GenerateAction struct {
actionutils.ParentAction
}
func (this *GenerateAction) RunPost(params struct {
LibraryFileId int64
}) {
defer this.CreateLogInfo(codes.IPLibraryFile_LogGenerateIPLibraryFile, params.LibraryFileId)
_, err := this.RPC().IPLibraryFileRPC().GenerateIPLibraryFile(this.AdminContext(), &pb.GenerateIPLibraryFileRequest{IpLibraryFileId: params.LibraryFileId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,43 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package creating
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type ProvidersAction struct {
actionutils.ParentAction
}
func (this *ProvidersAction) RunPost(params struct {
LibraryFileId int64
}) {
missingProvidersResp, err := this.RPC().IPLibraryFileRPC().CheckProvidersWithIPLibraryFileId(this.AdminContext(), &pb.CheckProvidersWithIPLibraryFileIdRequest{
IpLibraryFileId: params.LibraryFileId,
})
if err != nil {
this.ErrorPage(err)
return
}
var missingProviderMaps = []maps.Map{}
for _, missingProvider := range missingProvidersResp.MissingProviders {
var similarProviderMaps = []maps.Map{}
for _, provider := range missingProvider.SimilarProviders {
similarProviderMaps = append(similarProviderMaps, maps.Map{
"id": provider.Id,
"displayName": provider.DisplayName,
})
}
missingProviderMaps = append(missingProviderMaps, maps.Map{
"providerName": missingProvider.ProviderName,
"similarProviders": similarProviderMaps,
})
}
this.Data["missingProviders"] = missingProviderMaps
this.Success()
}

View File

@@ -0,0 +1,44 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package creating
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type ProvincesAction struct {
actionutils.ParentAction
}
func (this *ProvincesAction) RunPost(params struct {
LibraryFileId int64
}) {
missingProvincesResp, err := this.RPC().IPLibraryFileRPC().CheckProvincesWithIPLibraryFileId(this.AdminContext(), &pb.CheckProvincesWithIPLibraryFileIdRequest{
IpLibraryFileId: params.LibraryFileId,
})
if err != nil {
this.ErrorPage(err)
return
}
var missingProvinceMaps = []maps.Map{}
for _, missingProvince := range missingProvincesResp.MissingProvinces {
var similarProvinceMaps = []maps.Map{}
for _, province := range missingProvince.SimilarProvinces {
similarProvinceMaps = append(similarProvinceMaps, maps.Map{
"id": province.Id,
"displayName": province.DisplayName,
})
}
missingProvinceMaps = append(missingProvinceMaps, maps.Map{
"countryName": missingProvince.CountryName,
"provinceName": missingProvince.ProvinceName,
"similarProvinces": similarProvinceMaps,
})
}
this.Data["missingProvinces"] = missingProvinceMaps
this.Success()
}

View File

@@ -0,0 +1,36 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package creating
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
)
type TestFormatAction struct {
actionutils.ParentAction
}
func (this *TestFormatAction) RunPost(params struct {
Template string
Text string
}) {
if len(params.Text) == 0 {
this.Fail("请先输入测试数据")
}
template, err := iplibrary.NewTemplate(params.Template)
if err != nil {
this.Fail("数据格式模板解析错误:" + err.Error())
}
var emptyValues = []string{"0", "无"} // TODO
values, ok := template.Extract(params.Text, emptyValues)
if !ok {
this.Fail("无法分析数据,填写的测试数据和模板不符合")
}
this.Data["values"] = values
this.Success()
}

View File

@@ -0,0 +1,51 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package creating
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type TownsAction struct {
actionutils.ParentAction
}
func (this *TownsAction) RunPost(params struct {
LibraryFileId int64
Size int
}) {
missingTownsResp, err := this.RPC().IPLibraryFileRPC().CheckTownsWithIPLibraryFileId(this.AdminContext(), &pb.CheckTownsWithIPLibraryFileIdRequest{
IpLibraryFileId: params.LibraryFileId,
})
if err != nil {
this.ErrorPage(err)
return
}
var missingTownMaps = []maps.Map{}
for _, missingTown := range missingTownsResp.MissingTowns {
var similarTownMaps = []maps.Map{}
for _, town := range missingTown.SimilarTowns {
similarTownMaps = append(similarTownMaps, maps.Map{
"id": town.Id,
"displayName": town.DisplayName,
})
}
missingTownMaps = append(missingTownMaps, maps.Map{
"countryName": missingTown.CountryName,
"provinceName": missingTown.ProvinceName,
"cityName": missingTown.CityName,
"townName": missingTown.TownName,
"similarTowns": similarTownMaps,
})
if params.Size > 0 && len(missingTownMaps) >= params.Size {
break
}
}
this.Data["missingTowns"] = missingTownMaps
this.Success()
}

View File

@@ -0,0 +1,271 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package creating
import (
"bytes"
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"io"
)
type UploadAction struct {
actionutils.ParentAction
}
func (this *UploadAction) RunPost(params struct {
Name string
Template string
EmptyValues []string
Password string
File *actions.File
}) {
if len(params.Name) == 0 {
this.Fail("请输入IP库名称")
return
}
if len(params.Template) == 0 {
this.Fail("请输入数据格式模板")
return
}
template, err := iplibrary.NewTemplate(params.Template)
if err != nil {
this.Fail("模板格式错误:" + err.Error())
return
}
if params.File == nil {
this.Fail("请上传文件")
}
var emptyValues = []string{"0", "无"}
if len(params.EmptyValues) > 0 {
for _, emptyValue := range params.EmptyValues {
if !lists.ContainsString(emptyValues, emptyValue) {
emptyValues = append(emptyValues, emptyValue)
}
}
}
fileResp, err := this.RPC().FileRPC().CreateFile(this.AdminContext(), &pb.CreateFileRequest{
Filename: params.File.Filename,
Size: params.File.Size,
IsPublic: false,
MimeType: params.File.ContentType,
Type: "ipLibraryFile",
})
if err != nil {
this.ErrorPage(err)
return
}
var fileId = fileResp.FileId
fp, err := params.File.OriginFile.Open()
if err != nil {
this.ErrorPage(err)
return
}
defer func() {
_ = fp.Close()
}()
var buf = make([]byte, 128*1024)
var dataBuf = []byte{}
var countries = []string{}
var provinces = [][2]string{}
var cities = [][3]string{}
var towns = [][4]string{}
var providers = []string{}
var countryMap = map[string]bool{} // countryName => bool
var provinceMap = map[string]bool{} // countryName_provinceName => bool
var cityMap = map[string]bool{} // countryName_provinceName_cityName => bool
var townMap = map[string]bool{} // countryName_provinceName_cityName_townName => bool
var providerMap = map[string]bool{} // providerName => bool
for {
n, err := fp.Read(buf)
if n > 0 {
var data = buf[:n]
// upload
_, uploadErr := this.RPC().FileChunkRPC().CreateFileChunk(this.AdminContext(), &pb.CreateFileChunkRequest{
FileId: fileId,
Data: data,
})
if uploadErr != nil {
this.Fail("文件上传失败:" + uploadErr.Error())
}
// parse
dataBuf = append(dataBuf, data...)
left, valuesList, failLine, parseIsOk, err := this.parse(template, dataBuf, emptyValues)
if err != nil {
this.ErrorPage(err)
return
}
if !parseIsOk {
this.Fail("在 '" + failLine + "' 这行分析失败,请对比数据模板,检查数据格式")
return
}
for _, value := range valuesList {
var countryName = value["country"]
var provinceName = value["province"]
var cityName = value["city"]
var townName = value["town"]
var providerName = value["provider"]
// country
if len(countryName) > 0 {
_, ok := countryMap[countryName]
if !ok {
countries = append(countries, countryName)
countryMap[countryName] = true
}
// province
if len(provinceName) > 0 {
var key = countryName + "_" + provinceName
_, ok = provinceMap[key]
if !ok {
provinceMap[key] = true
provinces = append(provinces, [2]string{countryName, provinceName})
}
// city
if len(cityName) > 0 {
key += "_" + cityName
_, ok = cityMap[key]
if !ok {
cityMap[key] = true
cities = append(cities, [3]string{countryName, provinceName, cityName})
}
// town
if len(townName) > 0 {
key += "_" + townName
_, ok = townMap[key]
if !ok {
townMap[key] = true
towns = append(towns, [4]string{countryName, provinceName, cityName, townName})
}
}
}
}
}
// provider
if len(providerName) > 0 {
_, ok := providerMap[providerName]
if !ok {
providerMap[providerName] = true
providers = append(providers, providerName)
}
}
}
dataBuf = left
}
if err != nil {
if err != io.EOF {
this.ErrorPage(err)
return
}
break
}
}
countriesJSON, err := json.Marshal(countries)
if err != nil {
this.ErrorPage(err)
return
}
provincesJSON, err := json.Marshal(provinces)
if err != nil {
this.ErrorPage(err)
return
}
citiesJSON, err := json.Marshal(cities)
if err != nil {
this.ErrorPage(err)
return
}
townsJSON, err := json.Marshal(towns)
if err != nil {
this.ErrorPage(err)
return
}
providersJSON, err := json.Marshal(providers)
if err != nil {
this.ErrorPage(err)
return
}
createResp, err := this.RPC().IPLibraryFileRPC().CreateIPLibraryFile(this.AdminContext(), &pb.CreateIPLibraryFileRequest{
Name: params.Name,
Template: params.Template,
FileId: fileId,
EmptyValues: params.EmptyValues,
Password: params.Password,
CountriesJSON: countriesJSON,
ProvincesJSON: provincesJSON,
CitiesJSON: citiesJSON,
TownsJSON: townsJSON,
ProvidersJSON: providersJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
var ipLibraryFileId = createResp.IpLibraryFileId
defer this.CreateLogInfo(codes.IPLibraryFile_LogUploadIPLibraryFile, ipLibraryFileId)
this.Data["libraryFileId"] = ipLibraryFileId
this.Success()
}
func (this *UploadAction) parse(template *iplibrary.Template, data []byte, emptyValues []string) (left []byte, valuesList []map[string]string, failLine string, ok bool, err error) {
ok = true
if len(data) == 0 {
return
}
for {
var index = bytes.IndexByte(data, '\n')
if index >= 0 {
var line = data[:index+1]
values, found := template.Extract(string(line), emptyValues)
if found {
valuesList = append(valuesList, values)
} else {
// 防止错误信息太长
if len(line) > 256 {
line = line[:256]
}
failLine = string(line)
ok = false
return
}
data = data[index+1:]
} else {
left = data
break
}
}
return
}

View File

@@ -0,0 +1,38 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
ArtifactId int64
}) {
defer this.CreateLogInfo(codes.IPLibraryArtifact_LogDeleteIPLibraryArtifact, params.ArtifactId)
// 删除数据库中的记录
_, err := this.RPC().IPLibraryArtifactRPC().DeleteIPLibraryArtifact(this.AdminContext(), &pb.DeleteIPLibraryArtifactRequest{IpLibraryArtifactId: params.ArtifactId})
if err != nil {
this.ErrorPage(err)
return
}
// 删除 EdgeAPI 目录下的 MaxMind 文件(删除所有,因为无法确定具体是哪个文件)
_, err = this.RPC().IPLibraryRPC().DeleteMaxMindFile(this.AdminContext(), &pb.DeleteMaxMindFileRequest{
Filename: "", // 空字符串表示删除所有
})
if err != nil {
// 记录错误但不影响删除操作
this.Fail("删除IP库文件失败" + err.Error())
return
}
this.Success()
}

View File

@@ -0,0 +1,73 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/types"
)
type DownloadAction struct {
actionutils.ParentAction
}
func (this *DownloadAction) Init() {
this.Nav("", "", "")
}
func (this *DownloadAction) RunGet(params struct {
ArtifactId int64
}) {
artifactResp, err := this.RPC().IPLibraryArtifactRPC().FindIPLibraryArtifact(this.AdminContext(), &pb.FindIPLibraryArtifactRequest{
IpLibraryArtifactId: params.ArtifactId,
})
if err != nil {
this.ErrorPage(err)
return
}
var artifact = artifactResp.IpLibraryArtifact
if artifact == nil {
this.NotFound("IPLibraryArtifact", params.ArtifactId)
return
}
var fileId = artifact.FileId
if fileId <= 0 {
this.WriteString("ip artifact file not generated")
return
}
fileResp, err := this.RPC().FileRPC().FindEnabledFile(this.AdminContext(), &pb.FindEnabledFileRequest{FileId: fileId})
if err != nil {
this.ErrorPage(err)
return
}
var file = fileResp.File
if file == nil {
this.WriteString("file not found")
return
}
chunkIdsResp, err := this.RPC().FileChunkRPC().FindAllFileChunkIds(this.AdminContext(), &pb.FindAllFileChunkIdsRequest{FileId: file.Id})
if err != nil {
this.ErrorPage(err)
return
}
this.AddHeader("Content-Disposition", "attachment; filename=\"ip-"+artifact.Code+".db\";")
this.AddHeader("Content-Length", types.String(file.Size))
for _, chunkId := range chunkIdsResp.FileChunkIds {
chunkResp, err := this.RPC().FileChunkRPC().DownloadFileChunk(this.AdminContext(), &pb.DownloadFileChunkRequest{FileChunkId: chunkId})
if err != nil {
this.ErrorPage(err)
return
}
var chunk = chunkResp.FileChunk
if chunk == nil {
break
}
_, _ = this.Write(chunkResp.FileChunk.Data)
}
}

View File

@@ -0,0 +1,22 @@
package iplibrary
import (
"github.com/iwind/TeaGo/actions"
"net/http"
)
type Helper struct {
}
func NewHelper() *Helper {
return &Helper{}
}
func (this *Helper) BeforeAction(action *actions.ActionObject) {
if action.Request.Method != http.MethodGet {
return
}
action.Data["mainTab"] = "component"
action.Data["secondMenuItem"] = "ip-library"
}

View File

@@ -0,0 +1,112 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/iplibraryutils"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"strings"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "ipLibrary", "index")
}
func (this *IndexAction) RunGet(params struct{}) {
this.Data["canAccess"] = iplibraryutils.CanAccess()
// IP库列表
artifactsResp, err := this.RPC().IPLibraryArtifactRPC().FindAllIPLibraryArtifacts(this.AdminContext(), &pb.FindAllIPLibraryArtifactsRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var artifactMaps = []maps.Map{}
// 按创建时间倒序排序,最新的在前
artifacts := artifactsResp.IpLibraryArtifacts
for i := 0; i < len(artifacts); i++ {
for j := i + 1; j < len(artifacts); j++ {
if artifacts[i].CreatedAt < artifacts[j].CreatedAt {
artifacts[i], artifacts[j] = artifacts[j], artifacts[i]
}
}
}
// 确保最后一个(最新的)是使用中的,其他都是未使用的
// 如果列表不为空,将最新的设为使用中,其他设为未使用
if len(artifacts) > 0 {
latestId := artifacts[0].Id
for _, artifact := range artifacts {
var shouldBePublic = (artifact.Id == latestId)
if artifact.IsPublic != shouldBePublic {
// 需要更新状态(这里只标记,不实际更新数据库,因为上传时已经更新了)
// 如果数据库状态不一致,这里会显示不一致,但不会自动修复
}
}
}
for _, artifact := range artifacts {
var meta = &iplibrary.Meta{}
if len(artifact.MetaJSON) > 0 {
err = json.Unmarshal(artifact.MetaJSON, meta)
if err != nil {
this.ErrorPage(err)
return
}
}
// 文件信息
var fileMap = maps.Map{
"size": 0,
}
if artifact.File != nil {
fileMap = maps.Map{
"size": artifact.File.Size,
}
}
// 判断是否使用中:如果是列表中的第一个(最新的),则标记为使用中
isPublic := artifact.Id == artifacts[0].Id
// 根据文件名判断IP库类型
libraryType := "未知"
if artifact.File != nil && len(artifact.File.Filename) > 0 {
filename := strings.ToLower(artifact.File.Filename)
if strings.Contains(filename, "city") {
libraryType = "city库"
} else if strings.Contains(filename, "asn") {
libraryType = "asn库"
}
}
artifactMaps = append(artifactMaps, maps.Map{
"id": artifact.Id,
"name": artifact.Name,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", artifact.CreatedAt),
"isPublic": isPublic,
"fileId": artifact.FileId,
"code": artifact.Code,
"libraryType": libraryType,
"countCountries": len(meta.Countries),
"countProvinces": len(meta.Provinces),
"countCities": len(meta.Cities),
"countTowns": len(meta.Towns),
"countProviders": len(meta.Providers),
"file": fileMap,
})
}
this.Data["artifacts"] = artifactMaps
this.Show()
}

View File

@@ -0,0 +1,97 @@
//go:build plus
package iplibrary
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/cities"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/countries"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/creating"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/libraries"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/library"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/providers"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/provinces"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/towns"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(plus.NewBasicHelper()).
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)).
Helper(NewHelper()).
Helper(settingutils.NewHelper("ipLibrary")).
// IP库
Prefix("/settings/ip-library").
Get("", new(IndexAction)).
GetPost("/upload", new(UploadAction)).
Get("/download", new(DownloadAction)).
Post("/delete", new(DeleteAction)).
Post("/updatePublic", new(UpdatePublicAction)).
// IP库
Prefix("/settings/ip-library/libraries").
Get("", new(libraries.IndexAction)).
GetPost("/create", new(libraries.CreateAction)).
Post("/delete", new(libraries.DeleteAction)).
// 创建
Prefix("/settings/ip-library/creating").
Post("/testFormat", new(creating.TestFormatAction)).
Post("/upload", new(creating.UploadAction)).
Post("/countries", new(creating.CountriesAction)).
Post("/provinces", new(creating.ProvincesAction)).
Post("/cities", new(creating.CitiesAction)).
Post("/towns", new(creating.TownsAction)).
Post("/providers", new(creating.ProvidersAction)).
Post("/addCountryCustomCode", new(creating.AddCountryCustomCodeAction)).
Post("/addProvinceCustomCode", new(creating.AddProvinceCustomCodeAction)).
Post("/addCityCustomCode", new(creating.AddCityCustomCodeAction)).
Post("/addTownCustomCode", new(creating.AddTownCustomCodeAction)).
Post("/addProviderCustomCode", new(creating.AddProviderCustomCodeAction)).
Post("/finish", new(creating.FinishAction)).
Post("/generate", new(creating.GenerateAction)).
// 单个IP库
Prefix("/settings/ip-library/library").
Get("", new(library.IndexAction)).
Get("/download", new(library.DownloadAction)).
GetPost("/test", new(library.TestAction)).
// 国家/地区
Prefix("/settings/ip-library/countries").
Get("", new(countries.IndexAction)).
GetPost("/updatePopup", new(countries.UpdatePopupAction)).
// 省份
Prefix("/settings/ip-library/provinces").
Get("", new(provinces.IndexAction)).
GetPost("/updatePopup", new(provinces.UpdatePopupAction)).
// 城市
Prefix("/settings/ip-library/cities").
Get("", new(cities.IndexAction)).
GetPost("/updatePopup", new(cities.UpdatePopupAction)).
Post("/provinceOptions", new(cities.ProvinceOptionsAction)).
// 区县
Prefix("/settings/ip-library/towns").
Get("", new(towns.IndexAction)).
GetPost("/updatePopup", new(towns.UpdatePopupAction)).
Post("/provinceOptions", new(towns.ProvinceOptionsAction)).
Post("/cityOptions", new(towns.CityOptionsAction)).
// ISP
Prefix("/settings/ip-library/providers").
Get("", new(providers.IndexAction)).
GetPost("/updatePopup", new(providers.UpdatePopupAction)).
//
EndAll()
})
}

View File

@@ -0,0 +1,9 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibraryutils
import "github.com/iwind/TeaGo/Tea"
func CanAccess() bool {
return Tea.IsTesting()
}

View File

@@ -0,0 +1,50 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package libraries
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/iplibraryutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type CreateAction struct {
actionutils.ParentAction
}
func (this *CreateAction) Init() {
this.Nav("", "", "create")
}
func (this *CreateAction) RunGet(params struct {
LibraryFileId int64
}) {
// 检查权限
if !iplibraryutils.CanAccess() {
return
}
this.Data["canAccess"] = true
// 初始化
this.Data["updatingLibraryFile"] = nil
if params.LibraryFileId > 0 {
libraryFileResp, err := this.RPC().IPLibraryFileRPC().FindIPLibraryFile(this.AdminContext(), &pb.FindIPLibraryFileRequest{IpLibraryFileId: params.LibraryFileId})
if err != nil {
this.ErrorPage(err)
return
}
var libraryFile = libraryFileResp.IpLibraryFile
if libraryFile != nil {
this.Data["updatingLibraryFile"] = maps.Map{
"id": libraryFile.Id,
"name": libraryFile.Name,
"template": libraryFile.Template,
"emptyValues": libraryFile.EmptyValues,
}
}
}
this.Show()
}

View File

@@ -0,0 +1,27 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package libraries
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
LibraryFileId int64
}) {
defer this.CreateLogInfo(codes.IPLibraryFile_LogDeleteIPLibraryFile, params.LibraryFileId)
_, err := this.RPC().IPLibraryFileRPC().DeleteIPLibraryFile(this.AdminContext(), &pb.DeleteIPLibraryFileRequest{IpLibraryFileId: params.LibraryFileId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,48 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package libraries
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/iplibraryutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "library")
}
func (this *IndexAction) RunGet(params struct{}) {
this.Data["canAccess"] = iplibraryutils.CanAccess()
librariesResp, err := this.RPC().IPLibraryFileRPC().FindAllFinishedIPLibraryFiles(this.AdminContext(), &pb.FindAllFinishedIPLibraryFilesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var libraryMaps = []maps.Map{}
for _, library := range librariesResp.IpLibraryFiles {
if library.EmptyValues == nil {
library.EmptyValues = []string{}
}
libraryMaps = append(libraryMaps, maps.Map{
"id": library.Id,
"name": library.Name,
"template": library.Template,
"emptyValues": library.EmptyValues,
"generatedTime": timeutil.FormatTime("Y-m-d H:i:s", library.GeneratedAt),
"generatedFileId": library.GeneratedFileId,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", library.CreatedAt),
})
}
this.Data["libraries"] = libraryMaps
this.Show()
}

View File

@@ -0,0 +1,73 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package library
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/types"
)
type DownloadAction struct {
actionutils.ParentAction
}
func (this *DownloadAction) Init() {
this.Nav("", "", "")
}
func (this *DownloadAction) RunGet(params struct {
LibraryFileId int64
}) {
libraryResp, err := this.RPC().IPLibraryFileRPC().FindIPLibraryFile(this.AdminContext(), &pb.FindIPLibraryFileRequest{IpLibraryFileId: params.LibraryFileId})
if err != nil {
this.ErrorPage(err)
return
}
var library = libraryResp.IpLibraryFile
if library == nil {
this.NotFound("IPLibraryFile", params.LibraryFileId)
return
}
var fileId = library.GeneratedFileId
if fileId <= 0 {
this.WriteString("ip library file not generated")
return
}
fileResp, err := this.RPC().FileRPC().FindEnabledFile(this.AdminContext(), &pb.FindEnabledFileRequest{FileId: fileId})
if err != nil {
this.ErrorPage(err)
return
}
var file = fileResp.File
if file == nil {
this.WriteString("file not found")
return
}
chunkIdsResp, err := this.RPC().FileChunkRPC().FindAllFileChunkIds(this.AdminContext(), &pb.FindAllFileChunkIdsRequest{FileId: file.Id})
if err != nil {
this.ErrorPage(err)
return
}
this.AddHeader("Content-Disposition", "attachment; filename=\"ip-library.db\";")
this.AddHeader("Content-Length", types.String(file.Size))
for _, chunkId := range chunkIdsResp.FileChunkIds {
chunkResp, err := this.RPC().FileChunkRPC().DownloadFileChunk(this.AdminContext(), &pb.DownloadFileChunkRequest{FileChunkId: chunkId})
if err != nil {
this.ErrorPage(err)
return
}
var chunk = chunkResp.FileChunk
if chunk == nil {
break
}
_, _ = this.Write(chunkResp.FileChunk.Data)
}
this.Show()
}

View File

@@ -0,0 +1,18 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package library
import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "")
}
func (this *IndexAction) RunGet(params struct{}) {
// TODO
this.Show()
}

View File

@@ -0,0 +1,157 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package library
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
"os"
)
type TestAction struct {
actionutils.ParentAction
}
func (this *TestAction) Init() {
this.Nav("", "ipLibrary", "test")
}
func (this *TestAction) RunGet(params struct{}) {
// 通过 RPC 查询 EdgeAPI 的 MaxMind 文件状态
statusResp, err := this.RPC().IPLibraryRPC().FindMaxMindFileStatus(this.AdminContext(), &pb.FindMaxMindFileStatusRequest{})
if err != nil {
// RPC 调用失败,记录错误并使用本地检查作为后备
logs.Println("[IP_LIBRARY_TEST]RPC call failed: " + err.Error())
// RPC 调用失败,使用本地检查作为后备
iplibDir := Tea.Root + "/data/iplibrary"
cityDBPath := iplibDir + "/maxmind-city.mmdb"
asnDBPath := iplibDir + "/maxmind-asn.mmdb"
uploadedCityExists := false
uploadedASNExists := false
if _, err := os.Stat(cityDBPath); err == nil {
uploadedCityExists = true
}
if _, err := os.Stat(asnDBPath); err == nil {
uploadedASNExists = true
}
// 检查是否使用了MaxMind库通过测试查询来判断
testIP := "8.8.8.8"
testResult := iplibrary.LookupIP(testIP)
usingMaxMind := false
if testResult != nil && testResult.IsOk() {
if testResult.CountryId() == 0 && len(testResult.CountryName()) > 0 {
usingMaxMind = true
}
}
this.Data["maxMindCityExists"] = uploadedCityExists
this.Data["maxMindASNExists"] = uploadedASNExists
this.Data["usingMaxMind"] = usingMaxMind
this.Data["usingEmbeddedMaxMind"] = usingMaxMind && !uploadedCityExists
} else {
// 使用 RPC 返回的状态
this.Data["maxMindCityExists"] = statusResp.CityExists
this.Data["maxMindASNExists"] = statusResp.AsnExists
this.Data["usingMaxMind"] = statusResp.UsingMaxMind
this.Data["usingEmbeddedMaxMind"] = statusResp.UsingEmbeddedMaxMind
}
this.Show()
}
func (this *TestAction) RunPost(params struct {
Ip string
Must *actions.Must
}) {
if len(params.Ip) == 0 {
this.Fail("请输入IP地址")
return
}
// 查询IP信息
var result = iplibrary.LookupIP(params.Ip)
if result == nil || !result.IsOk() {
this.Data["result"] = maps.Map{
"isOk": false,
"ip": params.Ip,
"error": "未找到该IP的地理位置信息",
}
this.Success()
return
}
// 判断IP库类型
// MaxMind库的特征CountryId 和 ProvinceId 通常为 0因为MaxMind不使用ID系统
// 只要查询结果中CountryId为0且有国家名称就认为是MaxMind不管文件是否存在可能是嵌入的
iplibDir := Tea.Root + "/data/iplibrary"
cityDBPath := iplibDir + "/maxmind-city.mmdb"
var libraryType = "默认IP库"
var libraryVersion = ""
// 如果查询结果中CountryId为0MaxMind特征且有国家名称则认为是MaxMind
// 不管文件是否存在,因为可能是使用嵌入的 MaxMind 库
if result.CountryId() == 0 && len(result.CountryName()) > 0 {
libraryType = "MaxMind GeoIP2"
libraryVersion = "3"
}
// 通过 RPC 查询 EdgeAPI 的 MaxMind 文件状态
statusResp, err := this.RPC().IPLibraryRPC().FindMaxMindFileStatus(this.AdminContext(), &pb.FindMaxMindFileStatusRequest{})
uploadedCityExists := false
uploadedASNExists := false
if err == nil {
uploadedCityExists = statusResp.CityExists
uploadedASNExists = statusResp.AsnExists
} else {
// RPC 调用失败,使用本地检查作为后备
if _, err := os.Stat(cityDBPath); err == nil {
uploadedCityExists = true
}
asnDBPath := iplibDir + "/maxmind-asn.mmdb"
if _, err := os.Stat(asnDBPath); err == nil {
uploadedASNExists = true
}
}
// 判断是否使用了MaxMind库即使没有上传文件也可能使用嵌入的默认MaxMind库
usingMaxMind := false
if result.CountryId() == 0 && len(result.CountryName()) > 0 {
usingMaxMind = true
}
this.Data["result"] = maps.Map{
"isDone": true,
"isOk": true,
"ip": params.Ip,
"libraryType": libraryType,
"libraryVersion": libraryVersion,
"country": result.CountryName(),
"countryId": result.CountryId(),
"province": result.ProvinceName(),
"provinceId": result.ProvinceId(),
"city": result.CityName(),
"cityId": result.CityId(),
"town": result.TownName(),
"townId": result.TownId(),
"provider": result.ProviderName(),
"providerId": result.ProviderId(),
"summary": result.Summary(),
"regionSummary": result.RegionSummary(),
}
this.Data["maxMindCityExists"] = uploadedCityExists
this.Data["maxMindASNExists"] = uploadedASNExists
this.Data["usingMaxMind"] = usingMaxMind
this.Data["usingEmbeddedMaxMind"] = usingMaxMind && !uploadedCityExists
this.Success()
}

View File

@@ -0,0 +1,52 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package providers
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/iplibraryutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "provider")
}
func (this *IndexAction) RunGet(params struct{}) {
// 检查权限
if !iplibraryutils.CanAccess() {
return
}
this.Data["canAccess"] = true
providersResp, err := this.RPC().RegionProviderRPC().FindAllRegionProviders(this.AdminContext(), &pb.FindAllRegionProvidersRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var providerMaps = []maps.Map{}
for _, provider := range providersResp.RegionProviders {
if provider.Codes == nil {
provider.Codes = []string{}
}
if provider.CustomCodes == nil {
provider.CustomCodes = []string{}
}
providerMaps = append(providerMaps, maps.Map{
"id": provider.Id,
"name": provider.Name,
"codes": provider.Codes,
"customName": provider.CustomName,
"customCodes": provider.CustomCodes,
})
}
this.Data["providers"] = providerMaps
this.Show()
}

View File

@@ -0,0 +1,75 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package providers
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
ProviderId int64
}) {
providerResp, err := this.RPC().RegionProviderRPC().FindRegionProvider(this.AdminContext(), &pb.FindRegionProviderRequest{RegionProviderId: params.ProviderId})
if err != nil {
this.ErrorPage(err)
return
}
var provider = providerResp.RegionProvider
if provider == nil {
this.NotFound("regionProvider", params.ProviderId)
return
}
if provider.Codes == nil {
provider.Codes = []string{}
}
if provider.CustomCodes == nil {
provider.CustomCodes = []string{}
}
this.Data["provider"] = maps.Map{
"id": provider.Id,
"name": provider.Name,
"codes": provider.Codes,
"customName": provider.CustomName,
"customCodes": provider.CustomCodes,
}
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
ProviderId int64
CustomName string
CustomCodes []string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.RegionProvider_LogUpdateRegionProviderCustom, params.ProviderId)
_, err := this.RPC().RegionProviderRPC().UpdateRegionProviderCustom(this.AdminContext(), &pb.UpdateRegionProviderCustomRequest{
RegionProviderId: params.ProviderId,
CustomName: params.CustomName,
CustomCodes: params.CustomCodes,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,78 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package provinces
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/iplibraryutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "province")
}
func (this *IndexAction) RunGet(params struct {
CountryId int64
}) {
// 检查权限
if !iplibraryutils.CanAccess() {
return
}
this.Data["canAccess"] = true
if params.CountryId <= 0 {
params.CountryId = 1 // china
}
this.Data["countryId"] = params.CountryId
// 所有国家/地区
countriesResp, err := this.RPC().RegionCountryRPC().FindAllRegionCountries(this.AdminContext(), &pb.FindAllRegionCountriesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var countryMaps = []maps.Map{}
for _, country := range countriesResp.RegionCountries {
countryMaps = append(countryMaps, maps.Map{
"id": country.Id,
"name": country.DisplayName,
})
}
this.Data["countries"] = countryMaps
// 当前国家/地区下的省份
provincesResp, err := this.RPC().RegionProvinceRPC().FindAllRegionProvincesWithRegionCountryId(this.AdminContext(), &pb.FindAllRegionProvincesWithRegionCountryIdRequest{
RegionCountryId: params.CountryId,
})
if err != nil {
this.ErrorPage(err)
return
}
var provinceMaps = []maps.Map{}
for _, province := range provincesResp.RegionProvinces {
if province.Codes == nil {
province.Codes = []string{}
}
if province.CustomCodes == nil {
province.CustomCodes = []string{}
}
provinceMaps = append(provinceMaps, maps.Map{
"id": province.Id,
"name": province.Name,
"codes": province.Codes,
"customName": province.CustomName,
"customCodes": province.CustomCodes,
})
}
this.Data["provinces"] = provinceMaps
this.Show()
}

View File

@@ -0,0 +1,72 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package provinces
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
ProvinceId int64
}) {
provinceResp, err := this.RPC().RegionProvinceRPC().FindRegionProvince(this.AdminContext(), &pb.FindRegionProvinceRequest{RegionProvinceId: params.ProvinceId})
if err != nil {
this.ErrorPage(err)
return
}
var province = provinceResp.RegionProvince
if province == nil {
this.NotFound("regionProvince", params.ProvinceId)
return
}
if province.Codes == nil {
province.Codes = []string{}
}
if province.CustomCodes == nil {
province.CustomCodes = []string{}
}
this.Data["province"] = maps.Map{
"id": province.Id,
"name": province.Name,
"codes": province.Codes,
"customName": province.CustomName,
"customCodes": province.CustomCodes,
}
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
ProvinceId int64
CustomName string
CustomCodes []string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.RegionProvince_LogUpdateRegionProvinceCustom, params.ProvinceId)
_, err := this.RPC().RegionProvinceRPC().UpdateRegionProvinceCustom(this.AdminContext(), &pb.UpdateRegionProvinceCustomRequest{
RegionProvinceId: params.ProvinceId,
CustomName: params.CustomName,
CustomCodes: params.CustomCodes,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,36 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package towns
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type CityOptionsAction struct {
actionutils.ParentAction
}
func (this *CityOptionsAction) RunPost(params struct {
ProvinceId int64
}) {
citiesResp, err := this.RPC().RegionCityRPC().FindAllRegionCitiesWithRegionProvinceId(this.AdminContext(), &pb.FindAllRegionCitiesWithRegionProvinceIdRequest{
RegionProvinceId: params.ProvinceId,
})
if err != nil {
this.ErrorPage(err)
return
}
var cityMaps = []maps.Map{}
for _, city := range citiesResp.RegionCities {
cityMaps = append(cityMaps, maps.Map{
"id": city.Id,
"name": city.DisplayName,
})
}
this.Data["cities"] = cityMaps
this.Success()
}

View File

@@ -0,0 +1,84 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package towns
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/iplibraryutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "town")
}
func (this *IndexAction) RunGet(params struct {
CountryId int64
ProvinceId int64
CityId int64
}) {
// 检查权限
if !iplibraryutils.CanAccess() {
return
}
this.Data["canAccess"] = true
if params.CountryId <= 0 || params.ProvinceId <= 0 || params.CityId <= 0 {
params.CountryId = 1 // china
params.ProvinceId = 1 // beijing
params.CityId = 1 // beijing
}
this.Data["countryId"] = params.CountryId
this.Data["provinceId"] = params.ProvinceId
this.Data["cityId"] = params.CityId
// 所有国家/地区
countriesResp, err := this.RPC().RegionCountryRPC().FindAllRegionCountries(this.AdminContext(), &pb.FindAllRegionCountriesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var countryMaps = []maps.Map{}
for _, country := range countriesResp.RegionCountries {
countryMaps = append(countryMaps, maps.Map{
"id": country.Id,
"name": country.DisplayName,
})
}
this.Data["countries"] = countryMaps
// 区县列表
townsResp, err := this.RPC().RegionTownRPC().FindAllRegionTownsWithRegionCityId(this.AdminContext(), &pb.FindAllRegionTownsWithRegionCityIdRequest{
RegionCityId: params.CityId,
})
if err != nil {
this.ErrorPage(err)
return
}
var townMaps = []maps.Map{}
for _, town := range townsResp.RegionTowns {
if town.Codes == nil {
town.Codes = []string{}
}
if town.CustomCodes == nil {
town.CustomCodes = []string{}
}
townMaps = append(townMaps, maps.Map{
"id": town.Id,
"name": town.Name,
"codes": town.Codes,
"customName": town.CustomName,
"customCodes": town.CustomCodes,
})
}
this.Data["towns"] = townMaps
this.Show()
}

View File

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

View File

@@ -0,0 +1,72 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package towns
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
TownId int64
}) {
townResp, err := this.RPC().RegionTownRPC().FindRegionTown(this.AdminContext(), &pb.FindRegionTownRequest{RegionTownId: params.TownId})
if err != nil {
this.ErrorPage(err)
return
}
var town = townResp.RegionTown
if town == nil {
this.NotFound("regionTown", params.TownId)
return
}
if town.Codes == nil {
town.Codes = []string{}
}
if town.CustomCodes == nil {
town.CustomCodes = []string{}
}
this.Data["town"] = maps.Map{
"id": town.Id,
"name": town.Name,
"codes": town.Codes,
"customName": town.CustomName,
"customCodes": town.CustomCodes,
}
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
TownId int64
CustomName string
CustomCodes []string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.RegionTown_LogUpdateRegionTownCustom, params.TownId)
_, err := this.RPC().RegionTownRPC().UpdateRegionTownCustom(this.AdminContext(), &pb.UpdateRegionTownCustomRequest{
RegionTownId: params.TownId,
CustomName: params.CustomName,
CustomCodes: params.CustomCodes,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,35 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type UpdatePublicAction struct {
actionutils.ParentAction
}
func (this *UpdatePublicAction) RunPost(params struct {
ArtifactId int64
IsPublic bool
}) {
if params.IsPublic {
defer this.CreateLogInfo(codes.IPLibraryArtifact_LogUseIPLibraryArtifact, params.ArtifactId)
} else {
defer this.CreateLogInfo(codes.IPLibraryArtifact_LogCancelIPLibraryArtifact, params.ArtifactId)
}
_, err := this.RPC().IPLibraryArtifactRPC().UpdateIPLibraryArtifactIsPublic(this.AdminContext(), &pb.UpdateIPLibraryArtifactIsPublicRequest{
IpLibraryArtifactId: params.ArtifactId,
IsPublic: params.IsPublic,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,213 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
import (
"bytes"
"encoding/json"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/sizes"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library/iplibraryutils"
iplib "github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/logs"
"io"
"os"
"path/filepath"
"strings"
)
type UploadAction struct {
actionutils.ParentAction
}
func (this *UploadAction) Init() {
this.Nav("", "", "upload")
}
func (this *UploadAction) RunGet(params struct{}) {
this.Data["canAccess"] = iplibraryutils.CanAccess()
this.Show()
}
func (this *UploadAction) RunPost(params struct {
Name string
File *actions.File
Must *actions.Must
CSRF *actionutils.CSRF
}) {
if len(params.Name) == 0 {
this.FailField("name", "请输入IP库名称")
return
}
if params.File == nil {
this.Fail("请选择要上传的IP库文件")
return
}
fp, err := params.File.OriginFile.Open()
if err != nil {
this.Fail("读取IP库文件失败" + err.Error())
return
}
defer func() {
_ = fp.Close()
}()
data, err := io.ReadAll(io.LimitReader(fp, 64*sizes.M)) // 最大不超过64M
if err != nil {
this.Fail("读取IP库文件失败" + err.Error())
return
}
// 只支持 MaxMind 格式文件(.mmdb
filename := strings.ToLower(params.File.Filename)
if !strings.HasSuffix(filename, ".mmdb") {
this.Fail("只支持 MaxMind 格式文件(.mmdb请上传 GeoLite2-City.mmdb 或 GeoLite2-ASN.mmdb 文件")
return
}
// MaxMind 格式文件,保存到 data/iplibrary/ 目录
iplibDir := Tea.Root + "/data/iplibrary"
err = os.MkdirAll(iplibDir, 0755)
if err != nil {
this.Fail("创建IP库目录失败" + err.Error())
return
}
// 根据文件名判断是 City 还是 ASN
var targetPath string
if strings.Contains(filename, "city") {
targetPath = filepath.Join(iplibDir, "maxmind-city.mmdb")
} else if strings.Contains(filename, "asn") {
targetPath = filepath.Join(iplibDir, "maxmind-asn.mmdb")
} else {
this.Fail("MaxMind 文件名必须包含 'city' 或 'asn'")
return
}
// 保存文件(使用临时文件原子替换)
tmpPath := targetPath + ".tmp"
err = os.WriteFile(tmpPath, data, 0644)
if err != nil {
this.Fail("保存IP库文件失败" + err.Error())
return
}
// 原子替换
err = os.Rename(tmpPath, targetPath)
if err != nil {
os.Remove(tmpPath)
this.Fail("替换IP库文件失败" + err.Error())
return
}
// 通过 RPC 将文件上传到 EdgeAPI
_, err = this.RPC().IPLibraryRPC().UploadMaxMindFile(this.AdminContext(), &pb.UploadMaxMindFileRequest{
Filename: params.File.Filename,
Data: data,
})
if err != nil {
logs.Println("[IP_LIBRARY]upload MaxMind file to EdgeAPI failed: " + err.Error())
// 继续执行,不影响本地保存
}
// 创建简单的 Meta
meta := &iplib.Meta{
Version: 3,
Code: "maxmind",
Author: "MaxMind",
}
meta.Init()
// TODO 检查是否要自动创建省市区
// 上传IP库文件到数据库
fileResp, err := this.RPC().FileRPC().CreateFile(this.AdminContext(), &pb.CreateFileRequest{
Filename: params.File.Filename,
Size: int64(len(data)),
IsPublic: false,
MimeType: "",
Type: "ipLibraryArtifact",
})
if err != nil {
this.ErrorPage(err)
return
}
var fileId = fileResp.FileId
var buf = make([]byte, 256*1024)
var dataReader = bytes.NewReader(data)
for {
n, err := dataReader.Read(buf)
if n > 0 {
_, chunkErr := this.RPC().FileChunkRPC().CreateFileChunk(this.AdminContext(), &pb.CreateFileChunkRequest{
FileId: fileId,
Data: buf[:n],
})
if chunkErr != nil {
this.Fail("上传文件到数据库失败:" + chunkErr.Error())
return
}
}
if err != nil {
break
}
}
// 创建IP库信息
metaJSON, err := json.Marshal(meta)
if err != nil {
this.Fail("元数据编码失败:" + err.Error())
return
}
createResp, err := this.RPC().IPLibraryArtifactRPC().CreateIPLibraryArtifact(this.AdminContext(), &pb.CreateIPLibraryArtifactRequest{
FileId: fileId,
MetaJSON: metaJSON,
Name: params.Name,
})
if err != nil {
this.Fail("创建IP库失败" + err.Error())
return
}
// 获取所有IP库记录将其他记录的 isPublic 设为 false新上传的设为 true
artifactsResp, err := this.RPC().IPLibraryArtifactRPC().FindAllIPLibraryArtifacts(this.AdminContext(), &pb.FindAllIPLibraryArtifactsRequest{})
if err != nil {
// 如果获取列表失败,不影响上传成功,只记录日志
logs.Println("[IP_LIBRARY]failed to get all artifacts: " + err.Error())
} else {
// 将所有其他记录设为未使用
for _, artifact := range artifactsResp.IpLibraryArtifacts {
if artifact.Id != createResp.IpLibraryArtifactId {
if artifact.IsPublic {
// 将其他使用中的记录设为未使用
_, err = this.RPC().IPLibraryArtifactRPC().UpdateIPLibraryArtifactIsPublic(this.AdminContext(), &pb.UpdateIPLibraryArtifactIsPublicRequest{
IpLibraryArtifactId: artifact.Id,
IsPublic: false,
})
if err != nil {
logs.Println("[IP_LIBRARY]failed to update artifact " + fmt.Sprintf("%d", artifact.Id) + " isPublic: " + err.Error())
}
}
}
}
// 将新上传的记录设为使用中
_, err = this.RPC().IPLibraryArtifactRPC().UpdateIPLibraryArtifactIsPublic(this.AdminContext(), &pb.UpdateIPLibraryArtifactIsPublicRequest{
IpLibraryArtifactId: createResp.IpLibraryArtifactId,
IsPublic: true,
})
if err != nil {
logs.Println("[IP_LIBRARY]failed to set new artifact as public: " + err.Error())
}
}
this.Success()
}