feat(rpc): 设置默认RPC地址为本地回环

修复RPC客户端连接问题,将默认服务器地址设置为127.0.0.1以确保本地连接正常

refactor(qqwry): 优化IP地址查询功能

移除不必要的正则表达式依赖,重构IP地址查询逻辑,提高代码性能和可维护性

fix(server): 保存确定的端口到配置中

确保服务器端口在确定后正确保存到配置中,避免端口配置丢失
This commit is contained in:
2026-01-08 05:15:10 +08:00
parent 56fe334045
commit 4d0464c76b
8 changed files with 116 additions and 164 deletions

View File

@@ -86,7 +86,7 @@ func StartClient(id, port uint16, callback any) *struct {
} {
var rpcaddr, _ = service.NewBaseSysParamService().DataByKey(context.Background(), "server_ip")
//rpcaddr = "127.0.0.1"
rpcaddr = "127.0.0.1"
closer1, err := jsonrpc.NewMergeClient(context.Background(),
"ws://"+rpcaddr+":"+rpcport, "", []interface{}{
&RPCClient,

View File

@@ -12,12 +12,9 @@ import (
"net/http"
"net/url"
"os"
"regexp"
"strings"
"sync"
_ "embed"
"github.com/ipipdotnet/ipdb-go"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
@@ -88,143 +85,6 @@ func QueryIP(ip string) (location *Location, err error) {
}
}
// GetIpAddress 获取 IP 归属地和运营商
func GetIpAddress(ip string) (string, error) {
html, err := doGet("https://www.ipshudi.com/" + ip + "/")
if err != nil {
return "", err
}
div := getAddress(html, `<td>\n<span>.*</span>\n`)
div1 := getAddress(html, `<td class="th">运营商</td><td><span>.*</span>`)
location := "未知归属地"
if div != "" {
location = strings.Split(div, "<span>")[1]
location = strings.Split(location, "</span>")[0]
}
isp := ""
if div1 != "" {
isp = strings.Split(div1, "<span>")[1]
isp = strings.Split(isp, "</span>")[0]
}
return location + " " + isp, nil
}
// 定义响应数据结构(与 API 返回字段对应)
type IPLocationResponse struct {
IP string `json:"ip"`
IPNumber string `json:"ip_number"`
IPVersion int `json:"ip_version"`
CountryName string `json:"country_name"`
CountryCode2 string `json:"country_code2"`
ISP string `json:"isp"`
ResponseCode string `json:"response_code"`
ResponseMessage string `json:"response_message"`
}
// 调用 IP 定位 API
func LookupIP(ip string, format string) (*IPLocationResponse, error) {
// 构建请求 URL
baseURL := "https://api.iplocation.net/"
params := url.Values{}
params.Add("ip", ip)
if format != "" {
params.Add("format", format)
}
fullURL := baseURL + "?" + params.Encode()
// 发送 GET 请求
resp, err := http.Get(fullURL)
if err != nil {
return nil, fmt.Errorf("请求失败: %v", err)
}
defer resp.Body.Close()
// 读取响应体
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %v", err)
}
// 解析 JSON 响应(默认格式为 json
var result IPLocationResponse
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("解析响应失败: %v (响应内容: %s)", err, string(body))
}
// 检查 API 响应状态
if result.ResponseCode != "200" {
return nil, fmt.Errorf("API 调用失败: %s (%s)", result.ResponseMessage, result.ResponseCode)
}
return &result, nil
}
// doGet 发送 HTTP GET 请求,返回响应内容
func doGet(url string) ([]byte, error) {
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, nil
}
// getAddress 使用正则表达式从 HTML 中提取内容
func getAddress(html []byte, pattern string) string {
re := regexp.MustCompile(pattern)
return re.FindString(string(html))
}
// 判断IP是否为私有地址包括IPv4和IPv6
func isPrivateIP(ipStr string) (bool, error) {
ip := net.ParseIP(ipStr)
if ip == nil {
return false, fmt.Errorf("无效的IP地址: %s", ipStr)
}
// 定义私有网段
privateNets := []string{
// IPv4私有网段
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
// IPv6私有网段ULA和链路本地
"fc00::/7",
"fe80::/10",
}
// 检查IP是否属于任一私有网段
for _, cidr := range privateNets {
_, netIP, err := net.ParseCIDR(cidr)
if err != nil {
return false, err
}
if netIP.Contains(ip) {
return true, nil
}
}
return false, nil
}
// 判断IP是否为环回地址
func isLoopbackIP(ipStr string) (bool, error) {
ip := net.ParseIP(ipStr)
if ip == nil {
return false, fmt.Errorf("无效的IP地址: %s", ipStr)
}
return ip.IsLoopback(), nil
}
// QueryIPDat 从dat查询IP仅加载dat格式数据库时使用
func QueryIPDat(ipv4 string) (location *Location, err error) {
ip := net.ParseIP(ipv4).To4()
@@ -232,11 +92,24 @@ func QueryIPDat(ipv4 string) (location *Location, err error) {
return nil, errors.New("ip is not ipv4")
}
if isLoopback, err := isLoopbackIP(ipv4); isLoopback || err != nil {
if ip.IsLoopback() {
return &Location{ISP: "局域网", IP: ipv4}, nil
}
if isPrivateIP, err := isPrivateIP(ipv4); isPrivateIP || err != nil {
return &Location{ISP: "私有地址", IP: ipv4}, nil
privateNets := []string{
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
}
for _, cidr := range privateNets {
_, ipnet, err := net.ParseCIDR(cidr)
if err != nil {
continue
}
if ipnet.Contains(ip) {
return &Location{ISP: "私有地址", IP: ipv4}, nil
}
}
ip32 := binary.BigEndian.Uint32(ip)
posA := binary.LittleEndian.Uint32(data[:4])
@@ -245,16 +118,19 @@ func QueryIPDat(ipv4 string) (location *Location, err error) {
for {
mid := posA + (((posZ-posA)/indexLen)>>1)*indexLen
if int(mid+indexLen) > len(data) {
// 当数据中找不到IP时尝试使用网络查询
r, _ := LookupIP(ipv4, "json")
if r != nil {
location := &Location{ISP: r.ISP, IP: r.IP, Country: r.CountryName, District: r.CountryCode2}
location := &Location{
IP: r.IP,
Country: r.CountryName,
ISP: r.ISP,
}
locationCache.Store(ipv4, location)
return location, nil
} else {
return nil, errors.New("ip not found")
}
}
buf := data[mid : mid+indexLen]
_ip := binary.LittleEndian.Uint32(buf[:4])
@@ -360,6 +236,56 @@ func QueryIPIpdb(ip string) (location *Location, err error) {
locationCache.Store(ip, location)
return location, nil
}
// IPLocationResponse 定义IP定位API的响应结构
type IPLocationResponse struct {
IP string `json:"ip"`
IPNumber string `json:"ip_number"`
IPVersion int `json:"ip_version"`
CountryName string `json:"country_name"`
CountryCode2 string `json:"country_code2"`
ISP string `json:"isp"`
ResponseCode string `json:"response_code"`
ResponseMessage string `json:"response_message"`
}
// LookupIP 调用外部API查询IP地理位置
func LookupIP(ip string, format string) (*IPLocationResponse, error) {
// 构建请求 URL
baseURL := "https://api.iplocation.net/"
params := url.Values{}
params.Add("ip", ip)
if format != "" {
params.Add("format", format)
}
fullURL := baseURL + "?" + params.Encode()
// 发送 GET 请求
resp, err := http.Get(fullURL)
if err != nil {
return nil, fmt.Errorf("请求失败: %v", err)
}
defer resp.Body.Close()
// 读取响应体
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %v", err)
}
// 解析 JSON 响应
var result IPLocationResponse
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("解析响应失败: %v (响应内容: %s)", err, string(body))
}
// 检查 API 响应状态
if result.ResponseCode != "200" {
return nil, fmt.Errorf("API 调用失败: %s (%s)", result.ResponseMessage, result.ResponseCode)
}
return &result, nil
}
// LoadData 从内存加载IP数据库
func LoadData(database []byte) {
@@ -407,3 +333,4 @@ func SplitResult(addr string, isp string, ipv4 string) (location *Location) {
}
return
}

View File

@@ -63,6 +63,7 @@ func Start() {
}
port, err := determinePort(cool.Config.ServerInfo.CanPort)
cool.Config.ServerInfo.Port = uint16(port)
if err != nil {
log.Fatalf("Failed to determine port: %v", err)
}

View File

@@ -2,6 +2,7 @@ package service
import (
"blazing/cool"
"fmt"
"sync"
"blazing/modules/base/model"
@@ -33,22 +34,38 @@ func NewBaseSysLogService() *BaseSysLogService {
},
},
ModifyResult: func(ctx g.Ctx, data interface{}) interface{} {
// baseSysLog.IPAddr = fmt.Sprintf("国家:%s省份%s城市%s区县%s运营商%s\n",
// location.Country,
// location.Province,
// location.City,
// location.District,
// location.ISP,
// )
var rr []g.MapStrAny
r, _ := gconv.Map(data)["list"].(gdb.Result)
for _, v := range r {
ipaddr, err := qqwry.QueryIP(v.Map()["ip"].(string))
for i := 0; i < len(r); i++ {
subm := r[i].GMap()
ipaddr, err := qqwry.QueryIP(r[i].Map()["ip"].(string))
if err == nil {
v.GMap().Set("ipAddr", ipaddr)
// baseSysLog.IPAddr = fmt.Sprintf("国家:%s省份%s城市%s区县%s运营商%s\n",
// location.Country,
// location.Province,
// location.City,
// location.District,
// location.ISP,
// )
subm.Set("ipAddr", fmt.Sprintf("国家:%s省份%s城市%s区县%s运营商%s\n",
ipaddr.Country,
ipaddr.Province,
ipaddr.City,
ipaddr.District,
ipaddr.ISP,
))
}
rr = append(rr, subm.MapStrAny())
}
data.(map[string]interface{})["list"] = rr
return data
},
},
},

View File

@@ -38,7 +38,7 @@ func (s *ItemService) UPDATE(id uint32, count int) {
"item_id": id,
"item_cnt": count,
}
data = cool.SetTest(data)
//data = cool.SetTest(data)
m.Data(data).Insert()
}

View File

@@ -12,6 +12,8 @@ const TableNameServerList = "server_list"
type ServerList struct {
*cool.Model
coolconfig.ServerList
//isonline
IsOnline uint8 `gorm:"column:isonline;default:0;comment:'是否在线'" json:"isonline"`
}
// TableName ServerList's table name

View File

@@ -29,24 +29,29 @@ func NewServerService() *ServerService {
Model: model.NewServerList(),
PageQueryOp: &cool.QueryOp{
ModifyResult: func(ctx g.Ctx, data interface{}) interface{} {
var rr []g.MapStrAny
r, _ := gconv.Map(data)["list"].(gdb.Result)
for _, v := range r {
t, ok := cool.GetClient(gconv.Uint16(v.Map()["port"]))
for i := 0; i < len(r); i++ {
t, ok := cool.GetClient(gconv.Uint16(r[i].Map()["port"]))
// tt.Friends = v.Friends
subm := r[i].GMap()
if ok {
cool.Logger.Info(context.TODO(), "服务器假踢人")
err := t.KickPerson(0) //实现指定服务器踢人
if err == nil {
// tt.Friends = v.Friends
v.GMap().Set("isonline", 1)
subm.Set("isonline", 1)
}
}
rr = append(rr, subm.MapStrAny())
}
data.(map[string]interface{})["list"] = rr
return data
},
},

Binary file not shown.