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 6dc11ca..09ca592 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 a6482ed..9b6208c 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 b429ea2..ff8d6d4 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
LOGO designed by 熊大 .
-
+
: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
+## Special Thanks
+- [IPInfo](https://ipinfo.io/) for providing an accurate GeoIP Database.
+
## Star History
[](https://star-history.com/#naiba/nezha&Timeline)
diff --git a/go.mod b/go.mod
index 679494b..cef8cc5 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 933d897..cdb105e 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 0000000..7bd069c
--- /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 0000000..6d913b9
--- /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 0d7720e..729deb0 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 da62c9a..b2a3600 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 2c59636..07543ce 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 95dbf63..c1f3042 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 85456e4..23c20e7 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 852107d..661ccb8 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
+}