From 82761b876e279499336e7ec3af081cf436c0cea3 Mon Sep 17 00:00:00 2001 From: Licoy Date: Fri, 17 Jun 2022 19:02:47 +0800 Subject: [PATCH] feat: release v1.0 --- .github/workflows/release.yml | 26 +++++ .goreleaser.yml | 13 +++ README.md | 124 +++++++++++++++++--- args.go | 2 + fetch_hosts.go | 42 +++++-- go.mod | 4 +- go.sum | 4 - index.template | 210 ++++++++++++++++++++++++++-------- main.go | 106 ++++++++++++----- util.go | 9 +- 10 files changed, 433 insertions(+), 107 deletions(-) create mode 100755 .github/workflows/release.yml create mode 100755 .goreleaser.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100755 index 0000000..1ef99f9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,26 @@ +name: Release + +on: + push: + tags: + - v* + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: 1.17.4 + - name: Create release on GitHub + uses: goreleaser/goreleaser-action@v2 + with: + version: latest + args: release --rm-dist + env: + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100755 index 0000000..3c556a4 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,13 @@ +builds: + - + goos: + - darwin + - linux + - windows + goarch: + - 386 + - amd64 + - arm64 + - arm + env: + - CGO_ENABLED=0 \ No newline at end of file diff --git a/README.md b/README.md index 5b41a89..588a806 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,141 @@ ## 介绍 -`fetch-github-hosts` 是主要为解决研究及学习人员访问 `Github` 过慢或其他问题而提供的免费的 `Github Hosts` 同步服务。 + +`fetch-github-hosts` 是主要为解决研究及学习人员访问 `Github` 过慢或其他问题而提供的 `Github Hosts` 同步服务。 ## 原理 -此项目是通过部署此项目本身的服务器来获取 `github.com` 的 `hosts`,而不是通过第三方ip地址接口来进行获取,例如 `ipaddress.com` 等 + +此项目是通过部署此项目本身的服务器来获取 `github.com` 的 `hosts`,而不是通过第三方ip地址接口来进行获取,例如 `ipaddress.com` 等。 ## 使用方法 + +### 自动(推荐) + +到 [Releases](https://github.com/Licoy/fetch-github-hosts/releases) +或 [FastGit镜像](https://hub.fastgit.xyz/Licoy/fetch-github-hosts/releases) 中下载您的系统版本(目前支持`Windows`/`Linux`/`MacOS` +),包括`ARM` +架构的系统。 + +#### 参数 + +| 参数名 | 缩写 | 默认值 | 必填 | 描述 | +|------------|-----|--------------------------------------|-----|------------------------------------| +| `mode` | `m` | client | 否 | 启动模式 `server(服务端)` / `client(客户端)` | +| `interval` | `i` | 60 | 否 | 获取记录值间隔(分钟) | +| `port` | `p` | 9898 | 否 | 服务模式监听端口以访问HTTP服务 | +| `url` | `u` | `https://hosts.gitcdn.top/hosts.txt` | 否 | 客户端模式远程hosts获取链接 | + +#### 启动客户端: + +> 注意: +> +> Windows下需要右键【以管理员身份运行】; +> +> Linux/Macos下需要使用root或具有hosts写入权限的账户执行,或直接使用`sudo`运行; + +- 直接运行 + +```bash +# Linux/Macos +fetch-github-hosts + +# Windows +fetch-github-hosts.exe +``` + +- 自定义获取时间间隔 + +```bash +# Linux/Macos(10分钟获取一次) +fetch-github-hosts -i=10 + +# Windows(10分钟获取一次) +fetch-github-hosts.exe -i=10 +``` + +- 自定义获取链接 + +```bash +# Linux/Macos +fetch-github-hosts -u=http://127.0.0.1:9898/hosts.json + +# Windows +fetch-github-hosts.exe -u=http://127.0.0.1:9898/hosts.json +``` + +#### 启动服务端: + +- 直接运行 + +```bash +# Linux/Macos +fetch-github-hosts -m=server + +# Windows +fetch-github-hosts.exe -m=server +``` + +- 自定义监听端口 + +```bash +# Linux/Macos +fetch-github-hosts -m=server -p=6666 + +# Windows +fetch-github-hosts.exe -m=server -p=6666 +``` + ### 手动 + #### 添加hosts + 访问 [https://hosts.gitcdn.top/hosts.txt](https://hosts.gitcdn.top/hosts.txt) , 将其全部内容粘贴到你的hosts文件中,即可。 + - `Linux / MacOS` hosts路径:`/etc/hosts` - `Windows` hosts路径:`C:\Windows\System32\drivers\etc\hosts` + #### 刷新生效 + - `Linux`: `/etc/init.d/network restart` - `Windows`: `ipconfig /flushdns` - `Macos`: `sudo killall -HUP mDNSResponder` -### Unix/Linux 一键使用 + +#### Unix/Linux 一键使用 + ```shell sed -i "/# fetch-github-hosts begin/Q" /etc/hosts && curl https://hosts.gitcdn.top/hosts.txt >> /etc/hosts ``` + > 提示:可以设置crontab定时任务定时获取更新即可,解放双手! -### Chrome -使用 [FasterHosts](https://github.com/gauseen/faster-hosts) 插件,若访问速度过慢可以直接使用 -[点击此处](https://gitcdn.top/https://github.com/gauseen/faster-hosts/archive/refs/heads/master.zip) 来进行下载。 +### 其他自动方式 + +#### Chrome浏览器下 + +使用 [FasterHosts](https://github.com/gauseen/faster-hosts) 插件,点击进入下载。 下载完成之后解压压缩包,Chrome地址栏输入`chrome://extensions/`回车进入,勾选`开发者模式`,选择`加载已解压的扩展程序`, 选择刚才的解压目录即可。 -### Windows /MacOS 及其他桌面端 + +#### Windows / MacOS 及其他桌面端 + 使用 [SwitchHosts](https://swh.app/) 桌面端应用,安装添加新规则: + - `Title`: 任意 - `Type`: `Remote` - `Url`: `https://hosts.gitcdn.top/hosts.txt` - `Auto refresh`: `1 hour` ## 私有部署 -下载本仓库的代码:[fetch-github-hosts.zip](https://gitcdn.top/https://github.com/Licoy/fetch-github-hosts/archive/refs/heads/main.zip) , -部署到任意一个含有PHP环境的服务器即可,部署完成之后可以计划任务脚本定时更新hosts: -```shell -cd /wwwroot/fetch-github-hosts #此处更换为你部署的项目路径 -php fetch_hosts.php -``` -> 注意:必须部署到非大陆的服务器节点! + +下载最新的发行版(到 [Releases](https://github.com/Licoy/fetch-github-hosts/releases) +或 [FastGit镜像](https://hub.fastgit.xyz/Licoy/fetch-github-hosts/releases) 进行下载) +,并选择您的系统对应版本,直接以服务模式运行即可:`fetch-github-hosts -m=server -p=9898`,会自动监听`0.0.0.0:9898`,您可以直接浏览器访问 `http://127.0.0.1:9898` +以访问您自定义服务。 +(具体方法可参见【启动服务端】小节详细说明) + +> 注意:因网络影响,尽量部署到海外服务器节点! ### 开源协议 -[GPL 3.0](./LICENSE) + +[GPL 3.0](https://github.com/Licoy/fetch-github-hosts/blob/main/LICENSE) diff --git a/args.go b/args.go index 1dd1404..9d6c35f 100644 --- a/args.go +++ b/args.go @@ -10,6 +10,8 @@ import ( type CmdArgs struct { Mode string `default:"client" short:"m" long:"mode" description:"启动模式(client或server)"` FetchInterval int `default:"60" short:"i" long:"interval" description:"获取hosts的间隔时间,单位为分钟"` + Port int `default:"9898" short:"p" long:"port" description:"服务模式监听端口"` + Url string `default:"https://hosts.gitcdn.top/hosts.txt" short:"u" long:"url" description:"客户端模式远程hosts获取链接"` Version bool `short:"v" long:"version" description:"查看当前版本"` } diff --git a/fetch_hosts.go b/fetch_hosts.go index 8b026aa..8ea72b0 100644 --- a/fetch_hosts.go +++ b/fetch_hosts.go @@ -2,7 +2,9 @@ package main import ( "bytes" + "embed" "encoding/json" + "errors" "fmt" "io/ioutil" "net" @@ -17,11 +19,16 @@ const ( Windows = "windows" Linux = "linux" Darwin = "darwin" - HostUrl = "https://hosts.gitcdn.top/hosts.txt" ) +//go:embed index.template +var indexTemplate embed.FS + +//go:embed domains.json +var domainsJson embed.FS + // ClientFetchHosts 获取最新的host并写入hosts文件 -func ClientFetchHosts() (err error) { +func ClientFetchHosts(url string) (err error) { hostsPath := GetSystemHostsPath() hostsBytes, err := ioutil.ReadFile(hostsPath) if err != nil { @@ -29,7 +36,7 @@ func ClientFetchHosts() (err error) { return } - resp, err := http.Get(HostUrl) + resp, err := http.Get(url) if err != nil || resp.StatusCode != http.StatusOK { err = ComposeError("获取最新的hosts失败", err) return @@ -41,18 +48,26 @@ func ClientFetchHosts() (err error) { return } - hosts := string(hostsBytes) + fetchHostsStr := strings.Trim(string(fetchHosts), "\n") mth, err := regexp.Compile(`# fetch-github-hosts begin(([\s\S])*.?)# fetch-github-hosts end`) if err != nil { err = ComposeError("创建内容正则匹配失败", err) return } + + if len(mth.FindStringSubmatch(fetchHostsStr)) == 0 { + err = errors.New("无效的远程hosts链接,未通过格式校验") + return + } + + hosts := string(hostsBytes) + findStr := mth.FindStringSubmatch(hosts) if len(findStr) > 0 { - hosts = strings.Replace(hosts, findStr[0], string(fetchHosts), 1) + hosts = strings.Replace(hosts, findStr[0], fetchHostsStr, 1) } else { - hosts += "\n\n" + string(fetchHosts) + hosts += "\n\n" + fetchHostsStr + "\n" } if err = ioutil.WriteFile(hostsPath, []byte(hosts), os.ModeType); err != nil { @@ -66,7 +81,7 @@ func ClientFetchHosts() (err error) { // ServerFetchHosts 服务端获取github最新的hosts并写入到对应文件及更新首页 func ServerFetchHosts() (err error) { execDir := AppExecDir() - fileData, err := ioutil.ReadFile(execDir + "/domains.json") + fileData, err := getExecOrEmbedFile(&domainsJson, "domains.json") if err != nil { err = ComposeError("读取文件domains.json错误", err) return @@ -95,7 +110,7 @@ func ServerFetchHosts() (err error) { } var templateFile []byte - templateFile, err = ioutil.ReadFile(execDir + "/index.template") + templateFile, err = getExecOrEmbedFile(&indexTemplate, "index.template") if err != nil { err = ComposeError("读取首页模板文件失败", err) return @@ -131,3 +146,14 @@ func FetchHosts(domains []string) (hostsJson, hostsFile []byte, now string, err hostsJson, err = json.Marshal(hosts) return } + +func getExecOrEmbedFile(fs *embed.FS, filename string) (template []byte, err error) { + exeDirFile := AppExecDir() + "/" + filename + _, err = os.Stat(exeDirFile) + if err == nil { + template, err = ioutil.ReadFile(exeDirFile) + return + } + template, err = fs.ReadFile(filename) + return +} diff --git a/go.mod b/go.mod index 159c4bc..7f140d8 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,4 @@ go 1.18 require github.com/jessevdk/go-flags v1.5.0 -require ( - golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 // indirect -) +require golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 // indirect diff --git a/go.sum b/go.sum index fa42e0d..df31363 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,4 @@ -github.com/akavel/rsrc v0.10.2 h1:Zxm8V5eI1hW4gGaYsJQUhxpjkENuG91ki8B4zCrvEsw= -github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= -github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef h1:UD99BBEz19F21KhOFHLNAI6KodDWUvXaPr4Oqu8yMV8= -github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 h1:EZ2mChiOa8udjfp6rRmswTbtZN/QzUQp4ptM4rnjHvc= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/index.template b/index.template index 48f39b2..99fb4ac 100644 --- a/index.template +++ b/index.template @@ -8,62 +8,182 @@ -

介绍

-

fetch-github-hosts 是主要为解决研究及学习人员访问 Github 过慢或其他问题而提供的免费的 Github Hosts 同步服务。

-

最近获取时间: - +

介绍

+

fetch-github-hosts 是主要为解决研究及学习人员访问 Github 过慢或其他问题而提供的 Github Hosts 同步服务。

+

最近获取时间: + -   - [hosts.txt] - [hosts.json] -

-

原理

-

此项目是通过部署此项目本身的服务器来获取 github.comhosts,而不是通过第三方ip地址接口来进行获取,例如 ipaddress.com

-

使用方法

-

手动

-

添加hosts

-

访问 https://hosts.gitcdn.top/hosts.txt , +   + [hosts.txt] + [hosts.json] +

+

原理

+

此项目是通过部署此项目本身的服务器来获取 github.comhosts,而不是通过第三方ip地址接口来进行获取,例如 ipaddress.com 等。

+

使用方法

+

自动(推荐)

+

Releases + 或 FastGit镜像 中下载您的系统版本(目前支持Windows/Linux/MacOS + ),包括ARM + 架构的系统。

+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
参数名缩写默认值必填描述
+ mode + + m + client启动模式 + server(服务端) / + client(客户端) +
+ interval + + i + 60获取记录值间隔(分钟)
+ port + + p + 9898服务模式监听端口以访问HTTP服务
+ url + + u + + https://hosts.gitcdn.top/hosts.txt + 客户端模式远程hosts获取链接
+

启动客户端:

+
+

注意:

+

Windows下需要右键【以管理员身份运行】;

+

Linux/Macos下需要使用root或具有hosts写入权限的账户执行,或直接使用sudo运行;

+
+ +
# Linux/Macos
+fetch-github-hosts
+
+# Windows
+fetch-github-hosts.exe
+
+ +
# Linux/Macos(10分钟获取一次)
+fetch-github-hosts -i=10
+
+# Windows(10分钟获取一次)
+fetch-github-hosts.exe -i=10
+
+ +
# Linux/Macos
+fetch-github-hosts -u=http://127.0.0.1:9898/hosts.json
+
+# Windows
+fetch-github-hosts.exe -u=http://127.0.0.1:9898/hosts.json
+
+

启动服务端:

+ +
# Linux/Macos
+fetch-github-hosts -m=server
+
+# Windows
+fetch-github-hosts.exe -m=server
+
+ +
# Linux/Macos
+fetch-github-hosts -m=server -p=6666
+
+# Windows
+fetch-github-hosts.exe -m=server -p=6666
+
+

手动

+

添加hosts

+

访问 https://hosts.gitcdn.top/hosts.txt , 将其全部内容粘贴到你的hosts文件中,即可。

- +

Unix/Linux 一键使用

+
sed -i "/# fetch-github-hosts begin/Q" /etc/hosts && curl https://hosts.gitcdn.top/hosts.txt >> /etc/hosts
+
+
+

提示:可以设置crontab定时任务定时获取更新即可,解放双手!

+
+

其他自动方式

+

Chrome浏览器下

+

使用 FasterHosts 插件,点击进入下载。

+

下载完成之后解压压缩包,Chrome地址栏输入chrome://extensions/回车进入,勾选开发者模式,选择加载已解压的扩展程序, 选择刚才的解压目录即可。

-

Windows /MacOS 及其他桌面端

-

使用 SwitchHosts 桌面端应用,安装添加新规则:

- +

私有部署

+

下载最新的发行版(到 Releases + 或 FastGit镜像 进行下载) + ,并选择您的系统对应版本,直接以服务模式运行即可:fetch-github-hosts -m=server -p=9898,会自动监听0.0.0.0:9898,您可以直接浏览器访问 http://127.0.0.1:9898 + 以访问您自定义服务。 + (具体方法可参见【启动服务端】小节详细说明)

+
+

注意:因网络影响,尽量部署到海外服务器节点!

+
+

开源协议

+

GPL 3.0

- + \ No newline at end of file diff --git a/main.go b/main.go index c7f05b5..ca1bcc8 100644 --- a/main.go +++ b/main.go @@ -2,58 +2,90 @@ package main import ( "fmt" + "io/ioutil" + "net" + "net/http" + "os" "runtime" "time" ) func main() { - permission, err := PreCheckHasHostsRWPermission() - if err != nil { - fmt.Println("检查hosts读写权限失败", err.Error()) - return - } - if !permission { - if runtime.GOOS == Windows { - fmt.Println("请鼠标右键选择【以管理员的身份运行】来执行本程序!") - } else { - fmt.Println("请以root账户或sudo来执行本程序!", err.Error()) + if !IsDebug() { + permission, err := PreCheckHasHostsRWPermission() + if err != nil { + fmt.Println("检查hosts读写权限失败", err.Error()) + return + } + if !permission { + if runtime.GOOS == Windows { + fmt.Println("请鼠标右键选择【以管理员的身份运行】来执行本程序!") + } else { + fmt.Println("请以root账户或sudo来执行本程序!", err.Error()) + } + return } - return } + args := ParseBootArgs() - ticker := time.NewTicker(time.Minute * time.Duration(args.FetchInterval)) logPrint(fmt.Sprintf("开始程序监听,当前以%d分钟更新一次Github-Hosts!", args.FetchInterval)) logPrint("请不要关闭此窗口以保持再前台运行") logPrint("可以将此程序注册为服务,具体请参考项目说明:https://github.com/Licoy/fetch-github-hosts") + + ticker := time.NewTicker(time.Minute * time.Duration(args.FetchInterval)) if args.Mode == "server" { - startServer(ticker) + startServer(ticker, args.Port) } else { - startClient(ticker) + startClient(ticker, args.Url) } } -func startServer(ticker *time.Ticker) { +func startClient(ticker *time.Ticker, url string) { + logPrint("远程hosts获取链接:" + url) + fn := func() { + if err := ClientFetchHosts(url); err != nil { + logPrint("更新Github-Hosts失败:" + err.Error()) + } else { + logPrint("更新Github-Hosts成功!") + } + } + fn() for { select { case <-ticker.C: - if err := ServerFetchHosts(); err != nil { - logPrint("执行更新Github-Hosts失败:" + err.Error()) - } else { - logPrint("执行更新Github-Hosts成功!") - } + fn() } } } -func startClient(ticker *time.Ticker) { +func startServer(ticker *time.Ticker, port int) { + listen, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) + if err != nil { + fmt.Println("服务启动失败(可能是目标端口已被占用):", err.Error()) + return + } + logPrint(fmt.Sprintf("已监听HTTP服务成功:http://127.0.0.1:%d", port)) + logPrint(fmt.Sprintf("hosts文件链接:http://127.0.0.1:%d/hosts.txt", port)) + logPrint(fmt.Sprintf("hosts的JSON格式链接:http://127.0.0.1:%d/hosts.json", port)) + go func() { + if err = http.Serve(listen, &serverHandle{}); err != nil { + fmt.Println("HTTP服务启动失败:", err.Error()) + os.Exit(1) + return + } + }() + fn := func() { + if err := ServerFetchHosts(); err != nil { + logPrint("执行更新Github-Hosts失败:" + err.Error()) + } else { + logPrint("执行更新Github-Hosts成功!") + } + } + fn() for { select { case <-ticker.C: - if err := ClientFetchHosts(); err != nil { - logPrint("更新Github-Hosts失败:" + err.Error()) - } else { - logPrint("更新Github-Hosts成功!") - } + fn() } } } @@ -62,3 +94,25 @@ func logPrint(msg string) { now := time.Now().Format("2006-01-02 15:04:05") fmt.Printf("[%s] %s\n", now, msg) } + +type serverHandle struct { +} + +func (s serverHandle) ServeHTTP(resp http.ResponseWriter, request *http.Request) { + p := request.URL.Path + if p == "/" || p == "/hosts.txt" || p == "/hosts.json" { + if p == "/" { + p = "/index.html" + } + file, err := ioutil.ReadFile(AppExecDir() + p) + if err != nil { + resp.WriteHeader(http.StatusInternalServerError) + resp.Write([]byte("server error")) + logPrint("获取首页文件失败: " + err.Error()) + return + } + resp.Write(file) + return + } + http.Redirect(resp, request, "/", http.StatusMovedPermanently) +} diff --git a/util.go b/util.go index 954a80b..32d9c0c 100644 --- a/util.go +++ b/util.go @@ -3,6 +3,7 @@ package main import ( "errors" "os" + "path/filepath" "runtime" "strings" "syscall" @@ -26,7 +27,8 @@ func initAppExecDir() { if _debug { _execDir, _ = os.Getwd() } else { - _execDir, _ = os.Executable() + _exec, _ := os.Executable() + _execDir = filepath.Dir(_exec) } } @@ -51,11 +53,6 @@ func GetSystemHostsPath() string { func PreCheckHasHostsRWPermission() (yes bool, err error) { _, err = syscall.Open(GetSystemHostsPath(), syscall.O_RDWR, 0655) if err != nil { - if runtime.GOOS == Windows { - if errors.Is(err, syscall.ERROR_ACCESS_DENIED) { - err = nil - } - } if strings.Contains(err.Error(), "Access is denied") { err = nil }