CI / backend (push) Canceled after 0s
- 新增 app_configs 表与 appconfig 注册表:读取优先级 页面 → .env → 默认,删除即回退 - 页面可配 7 项:支付回调基址、成功回跳、Webhook 密钥(加密入库)、未验签放行开关、CORS、可信代理、GeoIP 路径 - 引导密钥(JWT_SECRET / OPSLIC_MASTER_KEY / OFFLINE_LICENSE_SEED)与部署参数只读展示掩码+指纹,永不入库 - 校验:URL 格式、密钥长度≥16、CIDR、release 下禁止开启未验签回调;变更写审计日志 - 消费点改造:支付回调地址、Waffo 回跳、webhook 验签、可信代理、CORS、GeoIP 统一走配置中心 - 前端新增「审计与设置 → 运行配置」页(分组表格 + 来源标记 + 编辑弹窗 + 恢复默认) - .env.example 补注释:哪些必须留在 .env、哪些已收敛到页面
72 lines
1.9 KiB
Go
72 lines
1.9 KiB
Go
package utils
|
|
|
|
// IP 地理位置识别:复用 opsx-waf 的 GeoLite2-City 方案(中文优先,内网标记)。
|
|
// 数据文件默认 /data/geoip/GeoLite2-City.mmdb(容器卷内),缺失时静默返回空串,不影响业务。 // Logan.Li
|
|
|
|
import (
|
|
"backend/internal/service/appconfig"
|
|
"net/netip"
|
|
"strings"
|
|
"sync"
|
|
|
|
geoip2 "github.com/oschwald/geoip2-golang/v2"
|
|
)
|
|
|
|
var geoIPReader struct {
|
|
once sync.Once
|
|
db *geoip2.Reader
|
|
}
|
|
|
|
// LookupIPLocation 返回 "国家 / 省份 / 城市"(中文优先);内网返回"内网",无法识别返回空串 // Logan.Li
|
|
func LookupIPLocation(value string) string {
|
|
ip, err := netip.ParseAddr(strings.TrimSpace(value))
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
if ip.IsPrivate() || ip.IsLoopback() || ip.IsLinkLocalUnicast() {
|
|
return "内网"
|
|
}
|
|
geoIPReader.once.Do(func() {
|
|
path := appconfig.String("GEOIP_DB_PATH")
|
|
if path == "" {
|
|
path = "/data/geoip/GeoLite2-City.mmdb"
|
|
}
|
|
geoIPReader.db, _ = geoip2.Open(path)
|
|
})
|
|
if geoIPReader.db == nil {
|
|
return ""
|
|
}
|
|
record, err := geoIPReader.db.City(ip)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
parts := localizedNames(record.Country.Names, record.Country.ISOCode)
|
|
if len(record.Subdivisions) > 0 {
|
|
if sub := localizedNames(record.Subdivisions[0].Names, record.Subdivisions[0].ISOCode); sub != "" && sub != parts {
|
|
parts = joinLocation(parts, sub)
|
|
}
|
|
}
|
|
if city := localizedNames(record.City.Names, ""); city != "" {
|
|
parts = joinLocation(parts, city)
|
|
}
|
|
return parts
|
|
}
|
|
|
|
// localizedNames 中文优先;GeoLite2 少数中国城市只有英文名,走兜底映射 // Logan.Li
|
|
func localizedNames(names geoip2.Names, fallback string) string {
|
|
if names.SimplifiedChinese != "" {
|
|
return names.SimplifiedChinese
|
|
}
|
|
if names.English != "" {
|
|
return names.English
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func joinLocation(left, right string) string {
|
|
if left == "" {
|
|
return right
|
|
}
|
|
return left + " / " + right
|
|
}
|