diff --git a/.github/workflows/sync.py b/.github/sync.py similarity index 100% rename from .github/workflows/sync.py rename to .github/sync.py diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6dc11cafff..09ca5923f0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,6 +12,13 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Fetch IPInfo GeoIP Database + env: + IPINFO_TOKEN: ${{ secrets.IPINFO_TOKEN }} + run: | + rm pkg/geoip/geoip.db + wget -O pkg/geoip/geoip.db https://ipinfo.io/data/free/country.mmdb?token=${IPINFO_TOKEN} + - name: Extract branch name run: | export TAG_NAME=$(echo ${GITHUB_REF#refs/tags/}) diff --git a/.github/workflows/sync-release.yml b/.github/workflows/sync-release.yml index a6482ed8c6..9b6208c4ca 100644 --- a/.github/workflows/sync-release.yml +++ b/.github/workflows/sync-release.yml @@ -13,4 +13,4 @@ jobs: - name: Sync to Gitee run: | pip3 install PyGitHub - python3 .github/workflows/sync.py \ No newline at end of file + python3 .github/sync.py \ No newline at end of file diff --git a/README.md b/README.md index b429ea2724..ff8d6d4211 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@
LOGO designed by 熊大 .

-GitHub release (with filter)    +GitHub release (with filter)   

:trollface: Nezha Monitoring: Self-hostable, lightweight, servers and websites monitoring and O&M tool.

@@ -92,6 +92,9 @@ You can change the dashboard language in the settings page (`/setting`) after th Crazy Coming +## Special Thanks +- [IPInfo](https://ipinfo.io/) for providing an accurate GeoIP Database. + ## Star History [![Star History Chart](https://api.star-history.com/svg?repos=naiba/nezha&type=Timeline)](https://star-history.com/#naiba/nezha&Timeline) diff --git a/go.mod b/go.mod index 679494b3b7..cef8cc5313 100644 --- a/go.mod +++ b/go.mod @@ -61,6 +61,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/oschwald/maxminddb-golang v1.13.1 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect diff --git a/go.sum b/go.sum index 933d897c83..cdb105e1ca 100644 --- a/go.sum +++ b/go.sum @@ -131,6 +131,8 @@ github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE= github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= github.com/ory/graceful v0.1.3 h1:FaeXcHZh168WzS+bqruqWEw/HgXWLdNv2nJ+fbhxbhc= github.com/ory/graceful v0.1.3/go.mod h1:4zFz687IAF7oNHHiB586U4iL+/4aV09o/PYLE34t2bA= +github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE= +github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= diff --git a/pkg/geoip/geoip.db b/pkg/geoip/geoip.db new file mode 100644 index 0000000000..7bd069c80f --- /dev/null +++ b/pkg/geoip/geoip.db @@ -0,0 +1 @@ +stub \ No newline at end of file diff --git a/pkg/geoip/geoip.go b/pkg/geoip/geoip.go new file mode 100644 index 0000000000..6d913b960c --- /dev/null +++ b/pkg/geoip/geoip.go @@ -0,0 +1,54 @@ +package geoip + +import ( + "embed" + "fmt" + "log" + "net" + "strings" + + maxminddb "github.com/oschwald/maxminddb-golang" +) + +//go:embed geoip.db +var geoDBFS embed.FS + +var ( + dbData []byte + err error +) + +type IPInfo struct { + Country string `maxminddb:"country"` + CountryName string `maxminddb:"country_name"` + Continent string `maxminddb:"continent"` + ContinentName string `maxminddb:"continent_name"` +} + +func init() { + dbData, err = geoDBFS.ReadFile("geoip.db") + if err != nil { + log.Printf("NEZHA>> Failed to open geoip database: %v", err) + } +} + +func Lookup(ip net.IP, record *IPInfo) (string, error) { + db, err := maxminddb.FromBytes(dbData) + if err != nil { + return "", err + } + defer db.Close() + + err = db.Lookup(ip, record) + if err != nil { + return "", err + } + + if record.Country != "" { + return strings.ToLower(record.Country), nil + } else if record.Continent != "" { + return strings.ToLower(record.Continent), nil + } + + return "", fmt.Errorf("IP not found") +} diff --git a/proto/nezha.pb.go b/proto/nezha.pb.go index 0d7720e5ac..729deb0420 100644 --- a/proto/nezha.pb.go +++ b/proto/nezha.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.34.2 -// protoc v5.27.1 +// protoc-gen-go v1.34.1 +// protoc v5.26.1 // source: proto/nezha.proto package proto @@ -35,7 +35,7 @@ type Host struct { Virtualization string `protobuf:"bytes,8,opt,name=virtualization,proto3" json:"virtualization,omitempty"` BootTime uint64 `protobuf:"varint,9,opt,name=boot_time,json=bootTime,proto3" json:"boot_time,omitempty"` Ip string `protobuf:"bytes,10,opt,name=ip,proto3" json:"ip,omitempty"` - CountryCode string `protobuf:"bytes,11,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"` + CountryCode string `protobuf:"bytes,11,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"` // deprecated Version string `protobuf:"bytes,12,opt,name=version,proto3" json:"version,omitempty"` Gpu []string `protobuf:"bytes,13,rep,name=gpu,proto3" json:"gpu,omitempty"` } @@ -629,6 +629,61 @@ func (x *IOStreamData) GetData() []byte { return nil } +type GeoIP struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Ip string `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"` + CountryCode string `protobuf:"bytes,2,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"` +} + +func (x *GeoIP) Reset() { + *x = GeoIP{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_nezha_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GeoIP) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GeoIP) ProtoMessage() {} + +func (x *GeoIP) ProtoReflect() protoreflect.Message { + mi := &file_proto_nezha_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GeoIP.ProtoReflect.Descriptor instead. +func (*GeoIP) Descriptor() ([]byte, []int) { + return file_proto_nezha_proto_rawDescGZIP(), []int{7} +} + +func (x *GeoIP) GetIp() string { + if x != nil { + return x.Ip + } + return "" +} + +func (x *GeoIP) GetCountryCode() string { + if x != nil { + return x.CountryCode + } + return "" +} + var File_proto_nezha_proto protoreflect.FileDescriptor var file_proto_nezha_proto_rawDesc = []byte{ @@ -712,26 +767,32 @@ var file_proto_nezha_proto_rawDesc = []byte{ 0x69, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x64, 0x22, 0x22, 0x0a, 0x0c, 0x49, 0x4f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x32, - 0x92, 0x02, 0x0a, 0x0c, 0x4e, 0x65, 0x7a, 0x68, 0x61, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x12, 0x33, 0x0a, 0x11, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x63, 0x65, - 0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, - 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, - 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x0a, 0x52, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, - 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x2b, 0x0a, 0x0b, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0b, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x54, 0x61, 0x73, 0x6b, 0x22, 0x00, 0x30, 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x49, 0x4f, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x49, 0x4f, 0x53, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x49, 0x4f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x22, 0x00, - 0x28, 0x01, 0x30, 0x01, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, + 0x3a, 0x0a, 0x05, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x32, 0xbf, 0x02, 0x0a, 0x0c, + 0x4e, 0x65, 0x7a, 0x68, 0x61, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x11, + 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, + 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, + 0x00, 0x12, 0x31, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x6f, + 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, + 0x70, 0x74, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x0a, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x61, + 0x73, 0x6b, 0x12, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, + 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x2b, 0x0a, 0x0b, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, + 0x6f, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x61, 0x73, 0x6b, + 0x22, 0x00, 0x30, 0x01, 0x12, 0x3a, 0x0a, 0x08, 0x49, 0x4f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x49, 0x4f, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x49, 0x4f, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, + 0x12, 0x2b, 0x0a, 0x0b, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x12, + 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x1a, 0x0c, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x22, 0x00, 0x42, 0x09, 0x5a, + 0x07, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -746,8 +807,8 @@ func file_proto_nezha_proto_rawDescGZIP() []byte { return file_proto_nezha_proto_rawDescData } -var file_proto_nezha_proto_msgTypes = make([]protoimpl.MessageInfo, 7) -var file_proto_nezha_proto_goTypes = []any{ +var file_proto_nezha_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_proto_nezha_proto_goTypes = []interface{}{ (*Host)(nil), // 0: proto.Host (*State)(nil), // 1: proto.State (*State_SensorTemperature)(nil), // 2: proto.State_SensorTemperature @@ -755,6 +816,7 @@ var file_proto_nezha_proto_goTypes = []any{ (*TaskResult)(nil), // 4: proto.TaskResult (*Receipt)(nil), // 5: proto.Receipt (*IOStreamData)(nil), // 6: proto.IOStreamData + (*GeoIP)(nil), // 7: proto.GeoIP } var file_proto_nezha_proto_depIdxs = []int32{ 2, // 0: proto.State.temperatures:type_name -> proto.State_SensorTemperature @@ -763,13 +825,15 @@ var file_proto_nezha_proto_depIdxs = []int32{ 4, // 3: proto.NezhaService.ReportTask:input_type -> proto.TaskResult 0, // 4: proto.NezhaService.RequestTask:input_type -> proto.Host 6, // 5: proto.NezhaService.IOStream:input_type -> proto.IOStreamData - 5, // 6: proto.NezhaService.ReportSystemState:output_type -> proto.Receipt - 5, // 7: proto.NezhaService.ReportSystemInfo:output_type -> proto.Receipt - 5, // 8: proto.NezhaService.ReportTask:output_type -> proto.Receipt - 3, // 9: proto.NezhaService.RequestTask:output_type -> proto.Task - 6, // 10: proto.NezhaService.IOStream:output_type -> proto.IOStreamData - 6, // [6:11] is the sub-list for method output_type - 1, // [1:6] is the sub-list for method input_type + 7, // 6: proto.NezhaService.LookupGeoIP:input_type -> proto.GeoIP + 5, // 7: proto.NezhaService.ReportSystemState:output_type -> proto.Receipt + 5, // 8: proto.NezhaService.ReportSystemInfo:output_type -> proto.Receipt + 5, // 9: proto.NezhaService.ReportTask:output_type -> proto.Receipt + 3, // 10: proto.NezhaService.RequestTask:output_type -> proto.Task + 6, // 11: proto.NezhaService.IOStream:output_type -> proto.IOStreamData + 7, // 12: proto.NezhaService.LookupGeoIP:output_type -> proto.GeoIP + 7, // [7:13] is the sub-list for method output_type + 1, // [1:7] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name @@ -781,7 +845,7 @@ func file_proto_nezha_proto_init() { return } if !protoimpl.UnsafeEnabled { - file_proto_nezha_proto_msgTypes[0].Exporter = func(v any, i int) any { + file_proto_nezha_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Host); i { case 0: return &v.state @@ -793,7 +857,7 @@ func file_proto_nezha_proto_init() { return nil } } - file_proto_nezha_proto_msgTypes[1].Exporter = func(v any, i int) any { + file_proto_nezha_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*State); i { case 0: return &v.state @@ -805,7 +869,7 @@ func file_proto_nezha_proto_init() { return nil } } - file_proto_nezha_proto_msgTypes[2].Exporter = func(v any, i int) any { + file_proto_nezha_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*State_SensorTemperature); i { case 0: return &v.state @@ -817,7 +881,7 @@ func file_proto_nezha_proto_init() { return nil } } - file_proto_nezha_proto_msgTypes[3].Exporter = func(v any, i int) any { + file_proto_nezha_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Task); i { case 0: return &v.state @@ -829,7 +893,7 @@ func file_proto_nezha_proto_init() { return nil } } - file_proto_nezha_proto_msgTypes[4].Exporter = func(v any, i int) any { + file_proto_nezha_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TaskResult); i { case 0: return &v.state @@ -841,7 +905,7 @@ func file_proto_nezha_proto_init() { return nil } } - file_proto_nezha_proto_msgTypes[5].Exporter = func(v any, i int) any { + file_proto_nezha_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Receipt); i { case 0: return &v.state @@ -853,7 +917,7 @@ func file_proto_nezha_proto_init() { return nil } } - file_proto_nezha_proto_msgTypes[6].Exporter = func(v any, i int) any { + file_proto_nezha_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*IOStreamData); i { case 0: return &v.state @@ -865,6 +929,18 @@ func file_proto_nezha_proto_init() { return nil } } + file_proto_nezha_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GeoIP); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -872,7 +948,7 @@ func file_proto_nezha_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_nezha_proto_rawDesc, NumEnums: 0, - NumMessages: 7, + NumMessages: 8, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/nezha.proto b/proto/nezha.proto index da62c9accd..b2a3600c9a 100644 --- a/proto/nezha.proto +++ b/proto/nezha.proto @@ -9,6 +9,7 @@ service NezhaService { rpc ReportTask(TaskResult)returns(Receipt){} rpc RequestTask(Host)returns(stream Task){} rpc IOStream(stream IOStreamData)returns(stream IOStreamData){} + rpc LookupGeoIP(GeoIP)returns(GeoIP){} } message Host { @@ -22,7 +23,7 @@ message Host { string virtualization = 8; uint64 boot_time = 9; string ip = 10; - string country_code = 11; + string country_code = 11; // deprecated string version = 12; repeated string gpu = 13; } @@ -73,3 +74,8 @@ message Receipt{ message IOStreamData { bytes data = 1; } + +message GeoIP { + string ip = 1; + string country_code = 2; +} \ No newline at end of file diff --git a/proto/nezha_grpc.pb.go b/proto/nezha_grpc.pb.go index 2c59636a3b..07543ce1db 100644 --- a/proto/nezha_grpc.pb.go +++ b/proto/nezha_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v5.27.1 +// - protoc v5.26.1 // source: proto/nezha.proto package proto @@ -24,6 +24,7 @@ const ( NezhaService_ReportTask_FullMethodName = "/proto.NezhaService/ReportTask" NezhaService_RequestTask_FullMethodName = "/proto.NezhaService/RequestTask" NezhaService_IOStream_FullMethodName = "/proto.NezhaService/IOStream" + NezhaService_LookupGeoIP_FullMethodName = "/proto.NezhaService/LookupGeoIP" ) // NezhaServiceClient is the client API for NezhaService service. @@ -35,6 +36,7 @@ type NezhaServiceClient interface { ReportTask(ctx context.Context, in *TaskResult, opts ...grpc.CallOption) (*Receipt, error) RequestTask(ctx context.Context, in *Host, opts ...grpc.CallOption) (NezhaService_RequestTaskClient, error) IOStream(ctx context.Context, opts ...grpc.CallOption) (NezhaService_IOStreamClient, error) + LookupGeoIP(ctx context.Context, in *GeoIP, opts ...grpc.CallOption) (*GeoIP, error) } type nezhaServiceClient struct { @@ -135,6 +137,15 @@ func (x *nezhaServiceIOStreamClient) Recv() (*IOStreamData, error) { return m, nil } +func (c *nezhaServiceClient) LookupGeoIP(ctx context.Context, in *GeoIP, opts ...grpc.CallOption) (*GeoIP, error) { + out := new(GeoIP) + err := c.cc.Invoke(ctx, NezhaService_LookupGeoIP_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // NezhaServiceServer is the server API for NezhaService service. // All implementations should embed UnimplementedNezhaServiceServer // for forward compatibility @@ -144,6 +155,7 @@ type NezhaServiceServer interface { ReportTask(context.Context, *TaskResult) (*Receipt, error) RequestTask(*Host, NezhaService_RequestTaskServer) error IOStream(NezhaService_IOStreamServer) error + LookupGeoIP(context.Context, *GeoIP) (*GeoIP, error) } // UnimplementedNezhaServiceServer should be embedded to have forward compatible implementations. @@ -165,6 +177,9 @@ func (UnimplementedNezhaServiceServer) RequestTask(*Host, NezhaService_RequestTa func (UnimplementedNezhaServiceServer) IOStream(NezhaService_IOStreamServer) error { return status.Errorf(codes.Unimplemented, "method IOStream not implemented") } +func (UnimplementedNezhaServiceServer) LookupGeoIP(context.Context, *GeoIP) (*GeoIP, error) { + return nil, status.Errorf(codes.Unimplemented, "method LookupGeoIP not implemented") +} // UnsafeNezhaServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to NezhaServiceServer will @@ -278,6 +293,24 @@ func (x *nezhaServiceIOStreamServer) Recv() (*IOStreamData, error) { return m, nil } +func _NezhaService_LookupGeoIP_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GeoIP) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NezhaServiceServer).LookupGeoIP(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NezhaService_LookupGeoIP_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NezhaServiceServer).LookupGeoIP(ctx, req.(*GeoIP)) + } + return interceptor(ctx, in, info, handler) +} + // NezhaService_ServiceDesc is the grpc.ServiceDesc for NezhaService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -297,6 +330,10 @@ var NezhaService_ServiceDesc = grpc.ServiceDesc{ MethodName: "ReportTask", Handler: _NezhaService_ReportTask_Handler, }, + { + MethodName: "LookupGeoIP", + Handler: _NezhaService_LookupGeoIP_Handler, + }, }, Streams: []grpc.StreamDesc{ { diff --git a/script/install.sh b/script/install.sh index 95dbf631e7..c1f3042641 100755 --- a/script/install.sh +++ b/script/install.sh @@ -12,7 +12,7 @@ NZ_DASHBOARD_PATH="${NZ_BASE_PATH}/dashboard" NZ_AGENT_PATH="${NZ_BASE_PATH}/agent" NZ_DASHBOARD_SERVICE="/etc/systemd/system/nezha-dashboard.service" NZ_DASHBOARD_SERVICERC="/etc/init.d/nezha-dashboard" -NZ_VERSION="v0.18.1" +NZ_VERSION="v0.18.2" red='\033[0;31m' green='\033[0;32m' @@ -49,7 +49,7 @@ err() { } geo_check() { - api_list="http://api.myip.la/en?json https://api.ip.sb/geoip https://ipapi.co/json http://ip-api.com/json/" + api_list="https://blog.cloudflare.com/cdn-cgi/trace https://dash.cloudflare.com/cdn-cgi/trace https://cf-ns.com/cdn-cgi/trace" ua="Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/81.0" set -- $api_list for url in $api_list; do diff --git a/script/install_en.sh b/script/install_en.sh index 85456e4b1e..23c20e77d1 100755 --- a/script/install_en.sh +++ b/script/install_en.sh @@ -12,7 +12,7 @@ NZ_DASHBOARD_PATH="${NZ_BASE_PATH}/dashboard" NZ_AGENT_PATH="${NZ_BASE_PATH}/agent" NZ_DASHBOARD_SERVICE="/etc/systemd/system/nezha-dashboard.service" NZ_DASHBOARD_SERVICERC="/etc/init.d/nezha-dashboard" -NZ_VERSION="v0.18.1" +NZ_VERSION="v0.18.2" red='\033[0;31m' green='\033[0;32m' @@ -49,7 +49,7 @@ err() { } geo_check() { - api_list="http://api.myip.la/en?json https://api.ip.sb/geoip https://ipapi.co/json http://ip-api.com/json/" + api_list="https://blog.cloudflare.com/cdn-cgi/trace https://dash.cloudflare.com/cdn-cgi/trace https://cf-ns.com/cdn-cgi/trace" ua="Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/81.0" set -- $api_list for url in $api_list; do diff --git a/service/rpc/nezha.go b/service/rpc/nezha.go index 852107d293..661ccb858a 100644 --- a/service/rpc/nezha.go +++ b/service/rpc/nezha.go @@ -4,10 +4,12 @@ import ( "context" "fmt" "log" + "net" "sync" "time" "github.com/naiba/nezha/pkg/ddns" + "github.com/naiba/nezha/pkg/geoip" "github.com/naiba/nezha/pkg/grpcx" "github.com/naiba/nezha/pkg/utils" @@ -217,3 +219,27 @@ func (s *NezhaHandler) IOStream(stream pb.NezhaService_IOStreamServer) error { iw.Wait() return nil } + +func (s *NezhaHandler) LookupGeoIP(c context.Context, r *pb.GeoIP) (*pb.GeoIP, error) { + var clientID uint64 + var err error + if clientID, err = s.Auth.Check(c); err != nil { + return nil, err + } + + // 根据内置数据库查询 IP 地理位置 + record := &geoip.IPInfo{} + ip := r.GetIp() + netIP := net.ParseIP(ip) + location, err := geoip.Lookup(netIP, record) + if err != nil { + return nil, err + } + + // 将地区码写入到 Host + singleton.ServerLock.RLock() + defer singleton.ServerLock.RUnlock() + singleton.ServerList[clientID].Host.CountryCode = location + + return &pb.GeoIP{Ip: ip, CountryCode: location}, nil +}