diff --git a/.ijwb/.bazelproject b/.ijwb/.bazelproject
new file mode 100644
index 0000000..0ff1046
--- /dev/null
+++ b/.ijwb/.bazelproject
@@ -0,0 +1,18 @@
+directories:
+ .
+
+# Automatically includes all relevant targets under the 'directories' above
+derive_targets_from_directories: true
+
+targets:
+ # If source code isn't resolving, add additional targets that compile it here
+
+additional_languages:
+ # Uncomment any additional languages you want supported
+ # dart
+ # javascript
+ # python
+ # typescript
+
+build_flags:
+ --features=pure
diff --git a/.ijwb/.blaze/modules/.project-data-dir.iml b/.ijwb/.blaze/modules/.project-data-dir.iml
new file mode 100644
index 0000000..cf80c62
--- /dev/null
+++ b/.ijwb/.blaze/modules/.project-data-dir.iml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.ijwb/.blaze/modules/.workspace.iml b/.ijwb/.blaze/modules/.workspace.iml
new file mode 100644
index 0000000..70dd7e2
--- /dev/null
+++ b/.ijwb/.blaze/modules/.workspace.iml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.ijwb/.idea/$CACHE_FILE$ b/.ijwb/.idea/$CACHE_FILE$
new file mode 100644
index 0000000..6cb8985
--- /dev/null
+++ b/.ijwb/.idea/$CACHE_FILE$
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.ijwb/.idea/.gitignore b/.ijwb/.idea/.gitignore
new file mode 100644
index 0000000..f9a0b04
--- /dev/null
+++ b/.ijwb/.idea/.gitignore
@@ -0,0 +1,6 @@
+# Default ignored files
+/workspace.xml
+# Project exclude paths
+/.
+
+
diff --git a/.ijwb/.idea/.name b/.ijwb/.idea/.name
new file mode 100644
index 0000000..7b66269
--- /dev/null
+++ b/.ijwb/.idea/.name
@@ -0,0 +1 @@
+navigator
\ No newline at end of file
diff --git a/.ijwb/.idea/dictionaries/eve.xml b/.ijwb/.idea/dictionaries/eve.xml
new file mode 100644
index 0000000..85f8d5c
--- /dev/null
+++ b/.ijwb/.idea/dictionaries/eve.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.ijwb/.idea/externalDependencies.xml b/.ijwb/.idea/externalDependencies.xml
new file mode 100644
index 0000000..133bf3d
--- /dev/null
+++ b/.ijwb/.idea/externalDependencies.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.ijwb/.idea/misc.xml b/.ijwb/.idea/misc.xml
new file mode 100644
index 0000000..dbe0e46
--- /dev/null
+++ b/.ijwb/.idea/misc.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.ijwb/.idea/modules.xml b/.ijwb/.idea/modules.xml
new file mode 100644
index 0000000..6e46a2e
--- /dev/null
+++ b/.ijwb/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.ijwb/.idea/runConfigurations.xml b/.ijwb/.idea/runConfigurations.xml
new file mode 100644
index 0000000..26bfd41
--- /dev/null
+++ b/.ijwb/.idea/runConfigurations.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.ijwb/.idea/vcs.xml b/.ijwb/.idea/vcs.xml
new file mode 100644
index 0000000..6c0b863
--- /dev/null
+++ b/.ijwb/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.ijwb/.idea/watcherTasks.xml b/.ijwb/.idea/watcherTasks.xml
new file mode 100644
index 0000000..eedcd2f
--- /dev/null
+++ b/.ijwb/.idea/watcherTasks.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BUILD.bazel b/BUILD.bazel
index e777585..eb96ae4 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -10,15 +10,16 @@ go_library(
importpath = "git.eve.moe/jackyyf/navigator",
visibility = ["//visibility:private"],
deps = [
+ "//api/beacon/v1:go_default_library",
+ "//api/navigator:go_default_library",
"//ipgeo:go_default_library",
"//mapping:go_default_library",
- "@com_github_ipipdotnet_ipdb_go//:go_default_library",
],
)
go_binary(
name = "navigator",
- pure = "on",
+ data = glob(["rules/**"]),
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)
diff --git a/api/beacon/BUILD.bazel b/api/beacon/BUILD.bazel
new file mode 100644
index 0000000..a6f0a81
--- /dev/null
+++ b/api/beacon/BUILD.bazel
@@ -0,0 +1,8 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+ name = "go_default_library",
+ srcs = ["api.go"],
+ importpath = "git.eve.moe/jackyyf/navigator/api/beacon",
+ visibility = ["//visibility:public"],
+)
diff --git a/api/beacon/api.go b/api/beacon/api.go
new file mode 100644
index 0000000..5a53e06
--- /dev/null
+++ b/api/beacon/api.go
@@ -0,0 +1,18 @@
+package beacon
+
+import (
+ "fmt"
+ "net/http"
+)
+
+var (
+ apiServeMux = http.NewServeMux()
+)
+
+func init() {
+ http.Handle("/beacon/", http.StripPrefix("/beacon", apiServeMux))
+}
+
+func RegisterApi(version string, handler http.Handler) {
+ apiServeMux.Handle(fmt.Sprintf("/%s/", version), http.StripPrefix(fmt.Sprint("/", version), handler))
+}
diff --git a/api/beacon/v1/BUILD.bazel b/api/beacon/v1/BUILD.bazel
new file mode 100644
index 0000000..5acba9f
--- /dev/null
+++ b/api/beacon/v1/BUILD.bazel
@@ -0,0 +1,13 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+ name = "go_default_library",
+ srcs = ["api.go"],
+ importpath = "git.eve.moe/jackyyf/navigator/api/beacon/v1",
+ visibility = ["//visibility:public"],
+ deps = [
+ "//api/beacon:go_default_library",
+ "//mapping:go_default_library",
+ "//utils:go_default_library",
+ ],
+)
diff --git a/api/beacon/v1/api.go b/api/beacon/v1/api.go
new file mode 100644
index 0000000..9d914aa
--- /dev/null
+++ b/api/beacon/v1/api.go
@@ -0,0 +1,33 @@
+package v1
+
+import (
+ "encoding/json"
+ "git.eve.moe/jackyyf/navigator/api/beacon"
+ "git.eve.moe/jackyyf/navigator/mapping"
+ "git.eve.moe/jackyyf/navigator/utils"
+ "net/http"
+)
+
+var (
+ serveMux = http.NewServeMux()
+)
+
+func init() {
+ serveMux.HandleFunc("/getNodes", func(resp http.ResponseWriter, req *http.Request) {
+ host := utils.GetRemoteIP(req)
+ nodes := mapping.GetNodes()
+ if nodes == nil {
+ utils.ResponseWithJsonError(resp, http.StatusInternalServerError, "Unable to get nodes")
+ return
+ }
+ suffix := mapping.GetSuffix(host)
+ resp.Header().Set("Content-Type", "application/json")
+ resp.WriteHeader(http.StatusOK)
+ jsonEncoder := json.NewEncoder(resp)
+ jsonEncoder.Encode(map[string]interface{}{
+ "nodes": nodes,
+ "suffix": suffix,
+ })
+ })
+ beacon.RegisterApi("v1", serveMux)
+}
diff --git a/api/navigator/BUILD.bazel b/api/navigator/BUILD.bazel
new file mode 100644
index 0000000..0267039
--- /dev/null
+++ b/api/navigator/BUILD.bazel
@@ -0,0 +1,13 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+ name = "go_default_library",
+ srcs = ["api.go"],
+ importpath = "git.eve.moe/jackyyf/navigator/api/navigator",
+ visibility = ["//visibility:public"],
+ deps = [
+ "//ipgeo:go_default_library",
+ "//mapping:go_default_library",
+ "//utils:go_default_library",
+ ],
+)
diff --git a/api/navigator/api.go b/api/navigator/api.go
new file mode 100644
index 0000000..70b565b
--- /dev/null
+++ b/api/navigator/api.go
@@ -0,0 +1,109 @@
+package navigator
+
+import (
+ "encoding/json"
+ "fmt"
+ "git.eve.moe/jackyyf/navigator/ipgeo"
+ "git.eve.moe/jackyyf/navigator/mapping"
+ "git.eve.moe/jackyyf/navigator/utils"
+ "log"
+ "net"
+ "net/http"
+ "strings"
+)
+
+const (
+ errIPv4Only = "Navigator works for valid IPv4 only :)"
+)
+
+func init() {
+ http.HandleFunc("/healthz", func(resp http.ResponseWriter, req *http.Request) {
+ resp.WriteHeader(200)
+ resp.Write([]byte("ok"))
+ })
+
+ http.HandleFunc("/info", func(resp http.ResponseWriter, req *http.Request) {
+ var host string
+ if argIp := req.FormValue("ip"); argIp != "" {
+ host = argIp
+ if net.ParseIP(host).To4() == nil {
+ utils.ResponseWithError(resp, http.StatusPreconditionFailed, errIPv4Only)
+ return
+ }
+ } else {
+ host = utils.GetRemoteIP(req)
+ }
+ db := ipgeo.Get()
+
+ info_cn, err := db.FindInfo(host, "CN")
+ if err != nil {
+ fmt.Fprintf(resp, "IP %s not found in the database.", host)
+ return
+ }
+ info_en, err := db.FindInfo(host, "EN")
+ if err != nil {
+ fmt.Fprintf(resp, "IP %s not found in the database.", host)
+ return
+ }
+ resp.Header().Set("Content-Type", "text/plain")
+ resp.WriteHeader(http.StatusOK)
+ server := mapping.Get(host)
+ fmt.Fprintln(resp, "您的IP:", host)
+ fmt.Fprintln(resp, "数据库中IP所属位置:", utils.BuildLocation(info_cn))
+ if info_cn.IspDomain != "" {
+ fmt.Fprintln(resp, "数据库中IP所属运营商:", info_cn.IspDomain)
+ }
+ fmt.Fprintln(resp, "您被分配的CDN节点为:", server)
+ fmt.Fprintln(resp)
+ fmt.Fprintln(resp, strings.Repeat("=", 72))
+
+ fmt.Fprintln(resp, "Your IP:", host)
+ fmt.Fprintln(resp, "Location for your IP according our database:", utils.BuildLocation(info_en))
+ if info_en.IspDomain != "" {
+ fmt.Fprintln(resp, "ISP for your IP according our database:", info_en.IspDomain)
+ }
+ fmt.Fprintln(resp, "Allocated CDN node for you:", server)
+ fmt.Fprintln(resp)
+ })
+
+ http.HandleFunc("/mapping", func(resp http.ResponseWriter, req *http.Request) {
+ host := utils.GetRemoteIP(req)
+ resp.Header().Set("Content-Type", "text/plain")
+ resp.WriteHeader(http.StatusOK)
+ server := mapping.Get(host)
+ fmt.Fprint(resp, server)
+ })
+ http.HandleFunc("/getMapping", func(resp http.ResponseWriter, req *http.Request) {
+ ip := req.FormValue("ip")
+ if net.ParseIP(ip).To4() == nil {
+ utils.ResponseWithError(resp, http.StatusBadRequest, errIPv4Only)
+ return
+ }
+ resp.Header().Set("Content-Type", "text/plain")
+ resp.WriteHeader(http.StatusOK)
+ server := mapping.Get(ip)
+ log.Printf("%s => %s\n", ip, server)
+ fmt.Fprint(resp, server)
+ })
+ http.HandleFunc("/getNodes", func(resp http.ResponseWriter, req *http.Request) {
+ ip := req.FormValue("ip")
+ if net.ParseIP(ip).To4() == nil {
+ utils.ResponseWithError(resp, http.StatusBadRequest, errIPv4Only)
+ return
+ }
+ nodes := mapping.GetNodes()
+ if nodes == nil {
+ utils.ResponseWithJsonError(resp, http.StatusInternalServerError, "Unable to get nodes")
+ return
+ }
+ suffix := mapping.GetSuffix(ip)
+ resp.Header().Set("Content-Type", "application/json")
+ resp.WriteHeader(http.StatusOK)
+ jsonEncoder := json.NewEncoder(resp)
+ ret := make([]string, 0, len(nodes))
+ for _, node := range nodes {
+ ret = append(ret, node+suffix)
+ }
+ jsonEncoder.Encode(ret)
+ })
+}
diff --git a/main.go b/main.go
index 1bf14ff..dce42c9 100644
--- a/main.go
+++ b/main.go
@@ -1,197 +1,24 @@
package main
import (
- "encoding/json"
"flag"
- "fmt"
- "log"
- "net"
- "net/http"
- _ "net/http/pprof"
- "strings"
-
+ _ "git.eve.moe/jackyyf/navigator/api/beacon/v1"
+ _ "git.eve.moe/jackyyf/navigator/api/navigator"
"git.eve.moe/jackyyf/navigator/ipgeo"
"git.eve.moe/jackyyf/navigator/mapping"
- "github.com/ipipdotnet/ipdb-go"
+ "log"
+ "net/http"
+ _ "net/http/pprof"
)
-const (
- errIPv4Only = "Navigator works for valid IPv4 only :)"
- remoteAddrHeader = "X-NAV-REMOTE-IP"
-)
-
-type errorMessage struct {
- Error string `json:error`
-}
-
var (
listen_spec = flag.String("bind", "127.0.0.1:8086", "http server bind spec")
)
-func responseWithError(resp http.ResponseWriter, statusCode int, message string) {
- resp.Header().Set("Content-Type", "text/plain")
- resp.WriteHeader(statusCode)
- resp.Write([]byte("error: " + message))
-}
-
-func responseWithJsonError(resp http.ResponseWriter, statusCode int, message string) {
- resp.Header().Set("Content-Type", "application/json")
- resp.WriteHeader(statusCode)
- encoder := json.NewEncoder(resp)
- err := encoder.Encode(&errorMessage{
- Error: message,
- })
- if err != nil {
- // This should never happen
- panic("json marshal failed, check code")
- }
-}
-
-func getRemoteIP(req *http.Request) string {
- if addr := req.Header.Get(remoteAddrHeader); addr != "" {
- if net.ParseIP(addr).To4() == nil {
- return ""
- }
- return addr
- }
- host, _, err := net.SplitHostPort(req.RemoteAddr)
- if err != nil {
- return ""
- }
- if net.ParseIP(host).To4() == nil {
- return ""
- }
- return host
-}
-
-func buildLocation(info *ipdb.CityInfo) string {
- ret := ""
- if info.CountryName != "" {
- ret += info.CountryName + " "
- }
- if info.RegionName != "" {
- ret += info.RegionName + " "
- }
- if info.CityName != "" {
- ret += info.CityName + " "
- }
- return strings.TrimSpace(ret)
-}
-
func main() {
flag.Parse()
ipgeo.Initialize()
mapping.Initialize()
-
- http.HandleFunc("/healthz", func(resp http.ResponseWriter, req *http.Request) {
- resp.WriteHeader(200)
- resp.Write([]byte("ok"))
- })
-
- http.HandleFunc("/info", func(resp http.ResponseWriter, req *http.Request) {
- var host string
- if argIp := req.FormValue("ip"); argIp != "" {
- host = argIp
- if net.ParseIP(host).To4() == nil {
- responseWithError(resp, http.StatusPreconditionFailed, errIPv4Only)
- return
- }
- } else {
- host = getRemoteIP(req)
- }
- db := ipgeo.Get()
-
- info_cn, err := db.FindInfo(host, "CN")
- if err != nil {
- fmt.Fprintf(resp, "IP %s not found in the database.", host)
- return
- }
- info_en, err := db.FindInfo(host, "EN")
- if err != nil {
- fmt.Fprintf(resp, "IP %s not found in the database.", host)
- return
- }
- resp.Header().Set("Content-Type", "text/plain")
- resp.WriteHeader(http.StatusOK)
- server := mapping.Get(host)
- fmt.Fprintln(resp, "您的IP:", host)
- fmt.Fprintln(resp, "数据库中IP所属位置:", buildLocation(info_cn))
- if info_cn.IspDomain != "" {
- fmt.Fprintln(resp, "数据库中IP所属运营商:", info_cn.IspDomain)
- }
- fmt.Fprintln(resp, "您被分配的CDN节点为:", server)
- fmt.Fprintln(resp)
- fmt.Fprintln(resp, strings.Repeat("=", 72))
-
- fmt.Fprintln(resp, "Your IP:", host)
- fmt.Fprintln(resp, "Location for your IP according our database:", buildLocation(info_en))
- if info_en.IspDomain != "" {
- fmt.Fprintln(resp, "ISP for your IP according our database:", info_en.IspDomain)
- }
- fmt.Fprintln(resp, "Allocated CDN node for you:", server)
- fmt.Fprintln(resp)
- })
-
- http.HandleFunc("/mapping", func(resp http.ResponseWriter, req *http.Request) {
- host := getRemoteIP(req)
- resp.Header().Set("Content-Type", "text/plain")
- resp.WriteHeader(http.StatusOK)
- server := mapping.Get(host)
- fmt.Fprint(resp, server)
- })
- http.HandleFunc("/getMapping", func(resp http.ResponseWriter, req *http.Request) {
- ip := req.FormValue("ip")
- if net.ParseIP(ip).To4() == nil {
- responseWithError(resp, http.StatusBadRequest, errIPv4Only)
- return
- }
- resp.Header().Set("Content-Type", "text/plain")
- resp.WriteHeader(http.StatusOK)
- server := mapping.Get(ip)
- log.Printf("%s => %s\n", ip, server)
- fmt.Fprint(resp, server)
- })
- http.HandleFunc("/getNodes", func(resp http.ResponseWriter, req *http.Request) {
- ip := req.FormValue("ip")
- if net.ParseIP(ip).To4() == nil {
- responseWithError(resp, http.StatusBadRequest, errIPv4Only)
- return
- }
- nodes := mapping.GetNodes()
- if nodes == nil {
- responseWithJsonError(resp, http.StatusInternalServerError, "Unable to get nodes")
- return
- }
- suffix := mapping.GetSuffix(ip)
- resp.Header().Set("Content-Type", "application/json")
- resp.WriteHeader(http.StatusOK)
- jsonEncoder := json.NewEncoder(resp)
- ret := make([]string, 0, len(nodes))
- for _, node := range nodes {
- ret = append(ret, node+suffix)
- }
- jsonEncoder.Encode(ret)
- })
- clientApi := http.NewServeMux()
- http.Handle("/client/", http.StripPrefix("/client", clientApi))
- clientV1Api := http.NewServeMux()
- clientApi.Handle("/v1/", http.StripPrefix("/v1", clientV1Api))
- clientV1Api.HandleFunc("/getNodes", func(resp http.ResponseWriter, req *http.Request) {
- host := getRemoteIP(req)
- nodes := mapping.GetNodes()
- if nodes == nil {
- responseWithJsonError(resp, http.StatusInternalServerError, "Unable to get nodes")
- return
- }
- suffix := mapping.GetSuffix(host)
- resp.Header().Set("Content-Type", "application/json")
- resp.WriteHeader(http.StatusOK)
- jsonEncoder := json.NewEncoder(resp)
- jsonEncoder.Encode(map[string]interface{}{
- "nodes": nodes,
- "suffix": suffix,
- })
- })
log.Println("HTTP server is running on", *listen_spec)
http.ListenAndServe(*listen_spec, nil)
}
diff --git a/mapping/elf/core.go b/mapping/elf/core.go
index 1387d88..6c26e24 100644
--- a/mapping/elf/core.go
+++ b/mapping/elf/core.go
@@ -143,14 +143,7 @@ func reload() {
parsedFunc = &globals
}
-func GetMapping(ip string) string {
- thread := pool.Get()
- ret, err := starlark.Call(thread, (*parsedFunc)["getMapping"],
- starlark.Tuple{starlark.String(ip)}, nil)
- if err != nil {
- log.Println("Starlark execute error:", err.Error())
- return ""
- }
+func getTarget(ret starlark.Value) string {
switch r := ret.(type) {
case starlark.String:
return string(r)
@@ -205,6 +198,17 @@ scriptError:
return ""
}
+func GetMapping(ip string) string {
+ thread := pool.Get()
+ ret, err := starlark.Call(thread, (*parsedFunc)["getMapping"],
+ starlark.Tuple{starlark.String(ip)}, nil)
+ if err != nil {
+ log.Println("Starlark execute error:", err.Error())
+ return ""
+ }
+ return getTarget(ret)
+}
+
func GetNodes() (res []string) {
thread := pool.Get()
ret, err := starlark.Call(thread, (*parsedFunc)["getNodes"], nil, nil)
@@ -215,12 +219,11 @@ func GetNodes() (res []string) {
if r, ok := ret.(*starlark.List); ok {
res = make([]string, r.Len())
for i := 0; i < r.Len(); i++ {
- v := r.Index(i)
- if s, ok := v.(starlark.String); ok {
- res[i] = string(s)
- } else {
+ res[i] = getTarget(r.Index(i))
+ if res[i] == "" {
log.Println("Script returned unexpected result:",
ret.String())
+ return nil
}
}
} else {
diff --git a/rules/map.starlark b/rules/map.starlark
index bd444c0..9e09059 100644
--- a/rules/map.starlark
+++ b/rules/map.starlark
@@ -3,7 +3,9 @@ HETZNER_FSN_1GE = "ge-fsn1-de"
HETZNER_HEL_1GE = "ge-hel1-fi"
CLOUDCONE_LAX1_1GE = "ge-lax1-us"
CLOUDCONE_LAX2_1GE = "ge-lax2-us"
-CLOUDCONE_LAX_LB = (CLOUDCONE_LAX1_1GE, CLOUDCONE_LAX2_1GE)
+CLOUDCONE_LAX3_1GE = "ge-lax3-us"
+HOSTSOLUTIONS_OMR1_1GE = "ge-omr1-ro"
+CLOUDCONE_LAX_LB = (CLOUDCONE_LAX1_1GE, CLOUDCONE_LAX2_1GE, CLOUDCONE_LAX3_1GE)
default_server = WHOLESALE_INTERNET_10GE
CHINA_MAINLAND_SUFFIX = ".eveedge.link"
@@ -30,7 +32,7 @@ def getMapping(ip):
return default_server
def getNodes():
- return ["xe-mci1-us", "ge-fsn1-de", "ge-lax1-us"]
+ return [WHOLESALE_INTERNET_10GE, HETZNER_FSN_1GE, CLOUDCONE_LAX_LB, HOSTSOLUTIONS_OMR1_1GE]
def getSuffix(ip):
info = geoLookup(ip)
diff --git a/utils/BUILD.bazel b/utils/BUILD.bazel
new file mode 100644
index 0000000..8034a1e
--- /dev/null
+++ b/utils/BUILD.bazel
@@ -0,0 +1,9 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+ name = "go_default_library",
+ srcs = ["utils.go"],
+ importpath = "git.eve.moe/jackyyf/navigator/utils",
+ visibility = ["//visibility:public"],
+ deps = ["@com_github_ipipdotnet_ipdb_go//:go_default_library"],
+)
diff --git a/utils/utils.go b/utils/utils.go
new file mode 100644
index 0000000..2a042dd
--- /dev/null
+++ b/utils/utils.go
@@ -0,0 +1,67 @@
+package utils
+
+import (
+ "encoding/json"
+ "github.com/ipipdotnet/ipdb-go"
+ "net"
+ "net/http"
+ "strings"
+)
+
+const (
+ remoteAddrHeader = "X-NAV-REMOTE-IP"
+)
+
+type errorMessage struct {
+ Error string `json:"error"`
+}
+
+func ResponseWithError(resp http.ResponseWriter, statusCode int, message string) {
+ resp.Header().Set("Content-Type", "text/plain")
+ resp.WriteHeader(statusCode)
+ resp.Write([]byte("error: " + message))
+}
+
+func ResponseWithJsonError(resp http.ResponseWriter, statusCode int, message string) {
+ resp.Header().Set("Content-Type", "application/json")
+ resp.WriteHeader(statusCode)
+ encoder := json.NewEncoder(resp)
+ err := encoder.Encode(&errorMessage{
+ Error: message,
+ })
+ if err != nil {
+ // This should never happen
+ panic("json marshal failed, check code")
+ }
+}
+
+func GetRemoteIP(req *http.Request) string {
+ if addr := req.Header.Get(remoteAddrHeader); addr != "" {
+ if net.ParseIP(addr).To4() == nil {
+ return ""
+ }
+ return addr
+ }
+ host, _, err := net.SplitHostPort(req.RemoteAddr)
+ if err != nil {
+ return ""
+ }
+ if net.ParseIP(host).To4() == nil {
+ return ""
+ }
+ return host
+}
+
+func BuildLocation(info *ipdb.CityInfo) string {
+ ret := ""
+ if info.CountryName != "" {
+ ret += info.CountryName + " "
+ }
+ if info.RegionName != "" {
+ ret += info.RegionName + " "
+ }
+ if info.CityName != "" {
+ ret += info.CityName + " "
+ }
+ return strings.TrimSpace(ret)
+}