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",
|
importpath = "git.eve.moe/jackyyf/navigator",
|
||||||
visibility = ["//visibility:private"],
|
visibility = ["//visibility:private"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//api/beacon/v1:go_default_library",
|
||||||
|
"//api/navigator:go_default_library",
|
||||||
"//ipgeo:go_default_library",
|
"//ipgeo:go_default_library",
|
||||||
"//mapping:go_default_library",
|
"//mapping:go_default_library",
|
||||||
"@com_github_ipipdotnet_ipdb_go//:go_default_library",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
go_binary(
|
go_binary(
|
||||||
name = "navigator",
|
name = "navigator",
|
||||||
pure = "on",
|
data = glob(["rules/**"]),
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
visibility = ["//visibility:public"],
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
_ "git.eve.moe/jackyyf/navigator/api/beacon/v1"
|
||||||
"log"
|
_ "git.eve.moe/jackyyf/navigator/api/navigator"
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
_ "net/http/pprof"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.eve.moe/jackyyf/navigator/ipgeo"
|
"git.eve.moe/jackyyf/navigator/ipgeo"
|
||||||
"git.eve.moe/jackyyf/navigator/mapping"
|
"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 (
|
var (
|
||||||
listen_spec = flag.String("bind", "127.0.0.1:8086", "http server bind spec")
|
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() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
ipgeo.Initialize()
|
ipgeo.Initialize()
|
||||||
mapping.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)
|
log.Println("HTTP server is running on", *listen_spec)
|
||||||
http.ListenAndServe(*listen_spec, nil)
|
http.ListenAndServe(*listen_spec, nil)
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,14 +143,7 @@ func reload() {
|
||||||
parsedFunc = &globals
|
parsedFunc = &globals
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMapping(ip string) string {
|
func getTarget(ret starlark.Value) 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 ""
|
|
||||||
}
|
|
||||||
switch r := ret.(type) {
|
switch r := ret.(type) {
|
||||||
case starlark.String:
|
case starlark.String:
|
||||||
return string(r)
|
return string(r)
|
||||||
|
@ -205,6 +198,17 @@ scriptError:
|
||||||
return ""
|
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) {
|
func GetNodes() (res []string) {
|
||||||
thread := pool.Get()
|
thread := pool.Get()
|
||||||
ret, err := starlark.Call(thread, (*parsedFunc)["getNodes"], nil, nil)
|
ret, err := starlark.Call(thread, (*parsedFunc)["getNodes"], nil, nil)
|
||||||
|
@ -215,12 +219,11 @@ func GetNodes() (res []string) {
|
||||||
if r, ok := ret.(*starlark.List); ok {
|
if r, ok := ret.(*starlark.List); ok {
|
||||||
res = make([]string, r.Len())
|
res = make([]string, r.Len())
|
||||||
for i := 0; i < r.Len(); i++ {
|
for i := 0; i < r.Len(); i++ {
|
||||||
v := r.Index(i)
|
res[i] = getTarget(r.Index(i))
|
||||||
if s, ok := v.(starlark.String); ok {
|
if res[i] == "" {
|
||||||
res[i] = string(s)
|
|
||||||
} else {
|
|
||||||
log.Println("Script returned unexpected result:",
|
log.Println("Script returned unexpected result:",
|
||||||
ret.String())
|
ret.String())
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -3,7 +3,9 @@ HETZNER_FSN_1GE = "ge-fsn1-de"
|
||||||
HETZNER_HEL_1GE = "ge-hel1-fi"
|
HETZNER_HEL_1GE = "ge-hel1-fi"
|
||||||
CLOUDCONE_LAX1_1GE = "ge-lax1-us"
|
CLOUDCONE_LAX1_1GE = "ge-lax1-us"
|
||||||
CLOUDCONE_LAX2_1GE = "ge-lax2-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
|
default_server = WHOLESALE_INTERNET_10GE
|
||||||
|
|
||||||
CHINA_MAINLAND_SUFFIX = ".eveedge.link"
|
CHINA_MAINLAND_SUFFIX = ".eveedge.link"
|
||||||
|
@ -30,7 +32,7 @@ def getMapping(ip):
|
||||||
return default_server
|
return default_server
|
||||||
|
|
||||||
def getNodes():
|
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):
|
def getSuffix(ip):
|
||||||
info = geoLookup(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