Code refactor, adding GoLand workspace with Bazel plugin.
This commit is contained in:
parent
6ac03c18cc
commit
2dbb47f519
|
@ -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
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.system.id="Blaze" type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$/../..">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.." />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../.idea" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.system.id="Blaze" type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$/../../..">
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../.." isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../.." />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../../bazel-bin" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../../bazel-genfiles" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../../bazel-navigator" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../../bazel-out" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../../bazel-testlogs" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="NodePackageJsonFileManager">
|
||||
<packageJsonPaths />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,6 @@
|
|||
# Default ignored files
|
||||
/workspace.xml
|
||||
# Project exclude paths
|
||||
/.
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
navigator
|
|
@ -0,0 +1,3 @@
|
|||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="eve" />
|
||||
</component>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalDependencies">
|
||||
<plugin id="com.google.idea.bazel.ijwb" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,72 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MarkdownProjectSettings">
|
||||
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="false" showSelectionInPreview="true" openRemoteLinks="true">
|
||||
<PanelProvider>
|
||||
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
|
||||
</PanelProvider>
|
||||
</PreviewSettings>
|
||||
<ParserSettings gitHubSyntaxChange="false">
|
||||
<PegdownExtensions>
|
||||
<option name="ABBREVIATIONS" value="false" />
|
||||
<option name="ANCHORLINKS" value="true" />
|
||||
<option name="ASIDE" value="false" />
|
||||
<option name="ATXHEADERSPACE" value="true" />
|
||||
<option name="AUTOLINKS" value="true" />
|
||||
<option name="DEFINITIONS" value="false" />
|
||||
<option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
|
||||
<option name="FENCED_CODE_BLOCKS" value="true" />
|
||||
<option name="FOOTNOTES" value="false" />
|
||||
<option name="HARDWRAPS" value="false" />
|
||||
<option name="HTML_DEEP_PARSER" value="false" />
|
||||
<option name="INSERTED" value="false" />
|
||||
<option name="QUOTES" value="false" />
|
||||
<option name="RELAXEDHRULES" value="true" />
|
||||
<option name="SMARTS" value="false" />
|
||||
<option name="STRIKETHROUGH" value="true" />
|
||||
<option name="SUBSCRIPT" value="false" />
|
||||
<option name="SUPERSCRIPT" value="false" />
|
||||
<option name="SUPPRESS_HTML_BLOCKS" value="false" />
|
||||
<option name="SUPPRESS_INLINE_HTML" value="false" />
|
||||
<option name="TABLES" value="true" />
|
||||
<option name="TASKLISTITEMS" value="true" />
|
||||
<option name="TOC" value="false" />
|
||||
<option name="WIKILINKS" value="true" />
|
||||
</PegdownExtensions>
|
||||
<ParserOptions>
|
||||
<option name="COMMONMARK_LISTS" value="true" />
|
||||
<option name="DUMMY" value="false" />
|
||||
<option name="EMOJI_SHORTCUTS" value="true" />
|
||||
<option name="FLEXMARK_FRONT_MATTER" value="false" />
|
||||
<option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="false" />
|
||||
<option name="GFM_TABLE_RENDERING" value="true" />
|
||||
<option name="GITBOOK_URL_ENCODING" value="false" />
|
||||
<option name="GITHUB_EMOJI_URL" value="false" />
|
||||
<option name="GITHUB_LISTS" value="false" />
|
||||
<option name="GITHUB_WIKI_LINKS" value="true" />
|
||||
<option name="JEKYLL_FRONT_MATTER" value="false" />
|
||||
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
|
||||
</ParserOptions>
|
||||
</ParserSettings>
|
||||
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true" embedImages="false" embedHttpImages="false">
|
||||
<GeneratorProvider>
|
||||
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
|
||||
</GeneratorProvider>
|
||||
<headerTop />
|
||||
<headerBottom />
|
||||
<bodyTop />
|
||||
<bodyBottom />
|
||||
</HtmlSettings>
|
||||
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssTextEnabled="false" isDynamicPageWidth="true">
|
||||
<StylesheetProvider>
|
||||
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
|
||||
</StylesheetProvider>
|
||||
<ScriptProviders />
|
||||
<cssText />
|
||||
</CssSettings>
|
||||
<HtmlExportSettings updateOnSave="false" parentDir="$ProjectFileDir$" targetDir="$ProjectFileDir$" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetExt="" useTargetExt="false" noCssNoScripts="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
|
||||
<LinkMapSettings>
|
||||
<textMaps />
|
||||
</LinkMapSettings>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.blaze/modules/.project-data-dir.iml" filepath="$PROJECT_DIR$/.blaze/modules/.project-data-dir.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.blaze/modules/.workspace.iml" filepath="$PROJECT_DIR$/.blaze/modules/.workspace.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.goide.execution.application.GoApplicationRunConfigurationProducer" />
|
||||
<option value="com.goide.execution.testing.frameworks.gobench.GobenchRunConfigurationProducer" />
|
||||
<option value="com.goide.execution.testing.frameworks.gocheck.GocheckRunConfigurationProducer" />
|
||||
<option value="com.goide.execution.testing.frameworks.gotest.GotestRunConfigurationProducer" />
|
||||
<option value="com.intellij.javascript.jest.JestRunConfigurationProducer" />
|
||||
<option value="com.intellij.javascript.protractor.ProtractorRunConfigurationProducer" />
|
||||
<option value="com.intellij.lang.javascript.buildTools.grunt.rc.GruntRunConfigurationProducer" />
|
||||
<option value="com.intellij.lang.javascript.buildTools.gulp.rc.GulpRunConfigurationProducer" />
|
||||
<option value="com.intellij.lang.javascript.buildTools.npm.rc.NpmRunConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectTasksOptions">
|
||||
<TaskOptions isEnabled="true">
|
||||
<option name="arguments" value="fmt $FilePath$" />
|
||||
<option name="checkSyntaxErrors" value="true" />
|
||||
<option name="description" />
|
||||
<option name="exitCodeBehavior" value="ERROR" />
|
||||
<option name="fileExtension" value="go" />
|
||||
<option name="immediateSync" value="true" />
|
||||
<option name="name" value="go fmt" />
|
||||
<option name="output" value="$FilePath$" />
|
||||
<option name="outputFilters">
|
||||
<array />
|
||||
</option>
|
||||
<option name="outputFromStdout" value="false" />
|
||||
<option name="program" value="$GoExecPath$" />
|
||||
<option name="runOnExternalChanges" value="false" />
|
||||
<option name="scopeName" value="Project Files" />
|
||||
<option name="trackOnlyRoot" value="true" />
|
||||
<option name="workingDir" value="$ProjectFileDir$" />
|
||||
<envs>
|
||||
<env name="GOROOT" value="$GOROOT$" />
|
||||
<env name="GOPATH" value="$GOPATH$" />
|
||||
<env name="PATH" value="$GoBinDirs$" />
|
||||
</envs>
|
||||
</TaskOptions>
|
||||
<TaskOptions isEnabled="true">
|
||||
<option name="arguments" value="run //:gazelle -- -go_prefix git.eve.moe/jackyyf/navigator" />
|
||||
<option name="checkSyntaxErrors" value="true" />
|
||||
<option name="description" />
|
||||
<option name="exitCodeBehavior" value="ERROR" />
|
||||
<option name="fileExtension" value="go" />
|
||||
<option name="immediateSync" value="true" />
|
||||
<option name="name" value="gazelle" />
|
||||
<option name="output" value="" />
|
||||
<option name="outputFilters">
|
||||
<array />
|
||||
</option>
|
||||
<option name="outputFromStdout" value="false" />
|
||||
<option name="program" value="bazel" />
|
||||
<option name="runOnExternalChanges" value="true" />
|
||||
<option name="scopeName" value="All Places" />
|
||||
<option name="trackOnlyRoot" value="false" />
|
||||
<option name="workingDir" value="$Projectpath$" />
|
||||
<envs />
|
||||
</TaskOptions>
|
||||
</component>
|
||||
</project>
|
|
@ -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"],
|
||||
)
|
||||
|
|
|
@ -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"],
|
||||
)
|
|
@ -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))
|
||||
}
|
|
@ -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",
|
||||
],
|
||||
)
|
|
@ -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)
|
||||
}
|
|
@ -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",
|
||||
],
|
||||
)
|
|
@ -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)
|
||||
})
|
||||
}
|
183
main.go
183
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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"],
|
||||
)
|
|
@ -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)
|
||||
}
|
Loading…
Reference in New Issue