diff --git a/README.md b/README.md index f8fa21f..5fd2618 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ [![GitHub release](https://img.shields.io/github/release/jeessy2/ddns-go.svg?logo=github&style=flat-square) ![GitHub release downloads](https://img.shields.io/github/downloads/jeessy2/ddns-go/total?logo=github)](https://github.com/jeessy2/ddns-go/releases/latest) [![Go version](https://img.shields.io/github/go-mod/go-version/jeessy2/ddns-go)](https://github.com/jeessy2/ddns-go/blob/master/go.mod) [![](https://goreportcard.com/badge/github.com/jeessy2/ddns-go/v5)](https://goreportcard.com/report/github.com/jeessy2/ddns-go/v5) [![](https://img.shields.io/docker/image-size/jeessy/ddns-go)](https://registry.hub.docker.com/r/jeessy/ddns-go) [![](https://img.shields.io/docker/pulls/jeessy/ddns-go)](https://registry.hub.docker.com/r/jeessy/ddns-go) +中文 | [English](https://github.com/jeessy2/ddns-go/blob/master/README_EN.md) + 自动获得你的公网 IPv4 或 IPv6 地址,并解析到对应的域名服务。 - [特性](#特性) @@ -16,7 +18,7 @@ ## 特性 - 支持Mac、Windows、Linux系统,支持ARM、x86架构 -- 支持的域名服务商 `Alidns(阿里云)` `Dnspod(腾讯云)` `Cloudflare` `华为云` `Callback` `百度云` `Porkbun` `GoDaddy` `Google Domain` +- 支持的域名服务商 `阿里云` `腾讯云` `Dnspod` `Cloudflare` `华为云` `Callback` `百度云` `Porkbun` `GoDaddy` `Google Domain` - 支持接口/网卡/[命令](https://github.com/jeessy2/ddns-go/wiki/通过命令获取IP参考)获取IP - 支持以服务的方式运行 - 默认间隔5分钟同步一次 @@ -35,20 +37,18 @@ ## 系统中使用 - 从 [Releases](https://github.com/jeessy2/ddns-go/releases) 下载并解压 ddns-go -- [可选] 使用 [Homebrew](https://brew.sh) 安装 [ddns-go](https://formulae.brew.sh/formula/ddns-go): - - ```bash - brew install ddns-go - ``` - -- 双击运行, 如没有找到配置, 程序将自动打开 http://127.0.0.1:9876 -- [可选] 安装服务 +- 安装服务 - Mac/Linux: `sudo ./ddns-go -s install` - Win(以管理员打开cmd): `.\ddns-go.exe -s install` - [可选] 服务卸载 - Mac/Linux: `sudo ./ddns-go -s uninstall` - Win(以管理员打开cmd): `.\ddns-go.exe -s uninstall` -- [可选] 支持安装或启动时带参数 `-l`监听地址 `-f`同步间隔时间(秒) `-cacheTimes`间隔N次与服务商比对 `-c`自定义配置文件路径 `-noweb`不启动web服务 `-skipVerify`跳过证书验证 `-dns` 自定义 DNS 服务器。如:`./ddns-go -s install -l :9877 -f 600 -c /Users/name/ddns-go.yaml` +- [可选] 支持安装带参数 `-l`监听地址 `-f`同步间隔时间(秒) `-cacheTimes`间隔N次与服务商比对 `-c`自定义配置文件路径 `-noweb`不启动web服务 `-skipVerify`跳过证书验证 `-dns` 自定义 DNS 服务器。如:`./ddns-go -s install -l :9876 -f 600 -c /Users/name/ddns-go.yaml` +- [可选] 使用 [Homebrew](https://brew.sh) 安装 [ddns-go](https://formulae.brew.sh/formula/ddns-go): + + ```bash + brew install ddns-go + ``` > [!NOTE] > 通过合理的配置 `-f` 和 `-cacheTimes` 可以实现 IP 变化即时触发更新且不会被 DDNS 服务商限流, 例如 `-f 10 -cacheTimes 360` 效果为每 10 秒检查一次本地 IP 变化, 每小时去公网对比一下 IP 变化 @@ -61,7 +61,7 @@ docker run -d --name ddns-go --restart=always --net=host -v /opt/ddns-go:/root jeessy/ddns-go ``` -- 在浏览器中打开`http://主机IP:9876`,修改你的配置,成功 +- 在浏览器中打开`http://主机IP:9876`,并修改你的配置 - [可选] 使用 `ghcr.io` 镜像 diff --git a/README_EN.md b/README_EN.md new file mode 100644 index 0000000..718b310 --- /dev/null +++ b/README_EN.md @@ -0,0 +1,140 @@ +# ddns-go + +[![GitHub release](https://img.shields.io/github/release/jeessy2/ddns-go.svg?logo=github&style=flat-square) ![GitHub release downloads](https://img.shields.io/github/downloads/jeessy2/ddns-go/total?logo=github)](https://github.com/jeessy2/ddns-go/releases/latest) [![Go version](https://img.shields.io/github/go-mod/go-version/jeessy2/ddns-go)](https://github.com/jeessy2/ddns-go/blob/master/go.mod) [![](https://goreportcard.com/badge/github.com/jeessy2/ddns-go/v5)](https://goreportcard.com/report/github.com/jeessy2/ddns-go/v5) [![](https://img.shields.io/docker/image-size/jeessy/ddns-go)](https://registry.hub.docker.com/r/jeessy/ddns-go) [![](https://img.shields.io/docker/pulls/jeessy/ddns-go)](https://registry.hub.docker.com/r/jeessy/ddns-go) + +[中文](https://github.com/jeessy2/ddns-go/blob/master/README.md) | English + +Automatically obtain your public IPv4 or IPv6 address and resolve it to the corresponding domain name service. + +- [Features](#Features) +- [Use in system](#Use-in-system) +- [Use in docker](#Use-in-docker) +- [Webhook](#webhook) +- [Callback](#callback) +- [Web interfaces](#Web-interfaces) + +## Features + +- Support Mac, Windows, Linux system, support ARM, x86 architecture +- Support domain service providers `Aliyun` `Tencent` `Dnspod` `Cloudflare` `Huawei` `Callback` `Baidu` `Porkbun` `GoDaddy` `Google Domain` `Namecheap` `NameSilo` +- Support interface / netcard / command to get IP +- Support running as a service +- Default interval is 5 minutes +- Support configuring multiple DNS service providers at the same time +- Support multiple domain name resolution at the same time +- Support multi-level domain name +- Configured on the web page, simple and convenient +- In the web page, you can quickly view the latest 50 logs +- Support Webhook notification +- Support TTL + +> [!NOTE] +> If you enable public network access, it is recommended to use Nginx and other reverse proxy software to enable HTTPS access to ensure security. + +## Use in system + +- Download and unzip ddns-go from [Releases](https://github.com/jeessy2/ddns-go/releases) +- Run in service mode + - Mac/Linux: `sudo ./ddns-go -s install` + - Win(Run as administrator): `.\ddns-go.exe -s install` +- [Optional] Uninstall service + - Mac/Linux: `sudo ./ddns-go -s uninstall` + - Win(Run as administrator): `.\ddns-go.exe -s uninstall` +- [Optional] Support installation with parameters `-l` listen address `-f` Sync frequency(seconds) `-cacheTimes` interval N times compared with service providers `-c` custom configuration file path `-noweb` does not start web service `-skipVerify` skip certificate verification `-dns` custom DNS server. example:`./ddns-go -s install -l :9876 -f 600 -c /Users/name/ddns-go.yaml` +- [Optional] You can use [Homebrew](https://brew.sh) to install [ddns-go](https://formulae.brew.sh/formula/ddns-go) + + ```bash + brew install ddns-go + ``` + +## Use in docker + +- Mount the host directory, use the docker host mode. You can replace `/opt/ddns-go` with any directory on your host, the configuration file is a hidden file + + ```bash + docker run -d --name ddns-go --restart=always --net=host -v /opt/ddns-go:/root jeessy/ddns-go + ``` + +- Open `http://DOCKER_IP:9876` in the browser, modify your configuration + +- [Optional] Use `ghcr.io` mirror + + ```bash + docker run -d --name ddns-go --restart=always --net=host -v /opt/ddns-go:/root ghcr.io/jeessy2/ddns-go + ``` + +- [Optional] Support startup with parameters `-l`listen address `-f`Sync frequency(seconds) + + ```bash + docker run -d --name ddns-go --restart=always --net=host -v /opt/ddns-go:/root jeessy/ddns-go -l :9877 -f 600 + ``` + +- [Optional] Without using docker host mode + + ```bash + docker run -d --name ddns-go --restart=always -p 9876:9876 -v /opt/ddns-go:/root jeessy/ddns-go + ``` + +## Webhook + +- Support webhook, when the domain name is updated successfully or not, the URL filled in will be called back +- Support variables + + | Variable name | Comments | + | ---- | ---- | + | #{ipv4Addr} | The new IPv4 | + | #{ipv4Result} | IPv4 update result: `no changed` `success` `failed`| + | #{ipv4Domains} | IPv4 domains,Split by `,` | + | #{ipv6Addr} | The new IPv6 | + | #{ipv6Result} | IPv6 update result: `no changed` `success` `failed`| + | #{ipv6Domains} | IPv6 domains,Split by `,` | + +- If RequestBody is empty, it is a `GET` request, otherwise it is a `POST` request + +-
Telegram + + [ddns-telegram-bot](https://github.com/WingLim/ddns-telegram-bot) +
+-
Discord + + - Discord client -> Server -> Channel Settings -> Integration -> View Webhook -> New Webhook -> Copy Webhook URL + - Input the `Webhook URL` copied from Discord in the URL + - Input in RequestBody + ```json + { + "content": "The domain name #{ipv4Domains} dynamically resolves to #{ipv4Result}.", + "embeds": [ + { + "description": "Domains: #{ipv4Domains}, Result: #{ipv4Result}, IP: #{ipv4Addr}", + "color": 15258703, + "author": { + "name": "DDNS" + }, + "footer": { + "text": "DDNS #{ipv4Result}" + } + } + ] + } + ``` +
+ +- [More webhook configuration reference](https://github.com/jeessy2/ddns-go/issues/327) + +## Callback + +- Support more third-party DNS service providers through custom callback +- Callback will be called as many times as there are lines in the configured domain name +- Support variables + + | Variable name | Comments | + | ---- | ---- | + | #{ip} | The new IPv4/IPv6 address| + | #{domain} | Current domain | + | #{recordType} | Record type `A` or `AAAA` | + | #{ttl} | TTL | +- If RequestBody is empty, it is a `GET` request, otherwise it is a `POST` request + +## Web interfaces + +![screenshots](https://raw.githubusercontent.com/jeessy2/ddns-go/master/ddns-web.png) diff --git a/config/config.go b/config/config.go index c37db0e..0ef1133 100755 --- a/config/config.go +++ b/config/config.go @@ -60,6 +60,8 @@ type Config struct { Webhook // 禁止公网访问 NotAllowWanAccess bool + // 语言 + Lang string } // ConfigCache ConfigCache @@ -92,14 +94,14 @@ func GetConfigCached() (conf Config, err error) { byt, err := os.ReadFile(configFilePath) if err != nil { - log.Println(configFilePath + " 读取失败") + util.Log("异常信息: %s", err) cache.Err = err return *cache.ConfigSingle, err } err = yaml.Unmarshal(byt, cache.ConfigSingle) if err != nil { - log.Println("反序列化配置文件失败", err) + util.Log("异常信息: %s", err) cache.Err = err return *cache.ConfigSingle, err } @@ -109,6 +111,9 @@ func GetConfigCached() (conf Config, err error) { cache.ConfigSingle.NotAllowWanAccess = true } + // 初始化语言 + util.InitLogLang(cache.ConfigSingle.Lang) + // remove err cache.Err = nil return *cache.ConfigSingle, err @@ -161,7 +166,7 @@ func (conf *Config) SaveConfig() (err error) { return } - log.Printf("配置文件已保存在: %s\n", configFilePath) + util.Log("配置文件已保存在: %s", configFilePath) // 清空配置缓存 cache.ConfigSingle = nil @@ -172,7 +177,7 @@ func (conf *Config) SaveConfig() (err error) { func (conf *DnsConfig) getIpv4AddrFromInterface() string { ipv4, _, err := GetNetInterface() if err != nil { - log.Println("从网卡获得IPv4失败!") + util.Log("从网卡获得IPv4失败") return "" } @@ -182,7 +187,7 @@ func (conf *DnsConfig) getIpv4AddrFromInterface() string { } } - log.Println("从网卡中获得IPv4失败! 网卡名: ", conf.Ipv4.NetInterface) + util.Log("从网卡中获得IPv4失败! 网卡名: %s", conf.Ipv4.NetInterface) return "" } @@ -193,20 +198,20 @@ func (conf *DnsConfig) getIpv4AddrFromUrl() string { url = strings.TrimSpace(url) resp, err := client.Get(url) if err != nil { - log.Printf("连接失败! 点击查看接口能否返回IPv4地址\n", url) - log.Printf("错误信息: %s\n", err) + util.Log("通过接口获取IPv4失败! 接口地址: %s", url) + util.Log("异常信息: %s", err) continue } defer resp.Body.Close() lr := io.LimitReader(resp.Body, 1024000) body, err := io.ReadAll(lr) if err != nil { - log.Println("读取IPv4结果失败! 接口: ", url) + util.Log("异常信息: %s", err) continue } result := Ipv4Reg.FindString(string(body)) if result == "" { - log.Printf("获取IPv4结果失败! 接口: %s ,返回值: %s\n", url, result) + util.Log("获取IPv4结果失败! 接口: %s ,返回值: %s", url, string(body)) } return result } @@ -243,14 +248,14 @@ func (conf *DnsConfig) getAddrFromCmd(addrType string) string { // run cmd out, err := execCmd.CombinedOutput() if err != nil { - log.Printf("获取%s结果失败! 未能成功执行命令:%s,错误:%q,退出状态码:%s\n", addrType, execCmd.String(), out, err) + util.Log("获取%s结果失败! 未能成功执行命令:%s, 错误:%q, 退出状态码:%s", addrType, execCmd.String(), out, err) return "" } str := string(out) // get result result := comp.FindString(str) if result == "" { - log.Printf("获取%s结果失败! 命令:%s,标准输出:%q\n", addrType, execCmd.String(), str) + util.Log("获取%s结果失败! 命令: %s, 标准输出: %q", addrType, execCmd.String(), str) } return result } @@ -269,7 +274,7 @@ func (conf *DnsConfig) GetIpv4Addr() string { // 从命令行获取 IP return conf.getAddrFromCmd("IPv4") default: - log.Println("IPv4 的 获取 IP 方式 未知!") + log.Println("IPv4's get IP method is unknown") return "" // unknown type } } @@ -277,7 +282,7 @@ func (conf *DnsConfig) GetIpv4Addr() string { func (conf *DnsConfig) getIpv6AddrFromInterface() string { _, ipv6, err := GetNetInterface() if err != nil { - log.Println("从网卡获得IPv6失败!") + util.Log("从网卡获得IPv6失败") return "" } @@ -289,34 +294,32 @@ func (conf *DnsConfig) getIpv6AddrFromInterface() string { num, err := strconv.Atoi(conf.Ipv6.IPv6Reg[1:]) if err == nil { if num > 0 { - log.Printf("IPv6将使用第 %d 个IPv6地址\n", num) if num <= len(netInterface.Address) { return netInterface.Address[num-1] } - log.Printf("未找到第 %d 个IPv6地址! 将使用第一个IPv6地址\n", num) + util.Log("未找到第 %d 个IPv6地址! 将使用第一个IPv6地址", num) return netInterface.Address[0] } - log.Printf("IPv6匹配表达式 %s 不正确! 最小从1开始\n", conf.Ipv6.IPv6Reg) + util.Log("IPv6匹配表达式 %s 不正确! 最小从1开始", conf.Ipv6.IPv6Reg) return "" } } // 正则表达式匹配 - log.Printf("IPv6将使用正则表达式 %s 进行匹配\n", conf.Ipv6.IPv6Reg) + util.Log("IPv6将使用正则表达式 %s 进行匹配", conf.Ipv6.IPv6Reg) for i := 0; i < len(netInterface.Address); i++ { matched, err := regexp.MatchString(conf.Ipv6.IPv6Reg, netInterface.Address[i]) if matched && err == nil { - log.Println("匹配成功! 匹配到地址: ", netInterface.Address[i]) + util.Log("匹配成功! 匹配到地址: ", netInterface.Address[i]) return netInterface.Address[i] } - log.Printf("第 %d 个地址 %s 不匹配, 将匹配下一个地址\n", i+1, netInterface.Address[i]) } - log.Println("没有匹配到任何一个IPv6地址, 将使用第一个地址") + util.Log("没有匹配到任何一个IPv6地址, 将使用第一个地址") } return netInterface.Address[0] } } - log.Println("从网卡中获得IPv6失败! 网卡名: ", conf.Ipv6.NetInterface) + util.Log("从网卡中获得IPv6失败! 网卡名: %s", conf.Ipv6.NetInterface) return "" } @@ -327,8 +330,8 @@ func (conf *DnsConfig) getIpv6AddrFromUrl() string { url = strings.TrimSpace(url) resp, err := client.Get(url) if err != nil { - log.Printf("连接失败! 点击查看接口能否返回IPv6地址, 参考说明:点击访问\n", url, "https://github.com/jeessy2/ddns-go#使用ipv6") - log.Printf("错误信息: %s\n", err) + util.Log("通过接口获取IPv6失败! 接口地址: %s", url) + util.Log("异常信息: %s", err) continue } @@ -336,12 +339,12 @@ func (conf *DnsConfig) getIpv6AddrFromUrl() string { lr := io.LimitReader(resp.Body, 1024000) body, err := io.ReadAll(lr) if err != nil { - log.Println("读取IPv6结果失败! 接口: ", url) + util.Log("异常信息: %s", err) continue } result := Ipv6Reg.FindString(string(body)) if result == "" { - log.Printf("获取IPv6结果失败! 接口: %s ,返回值: %s\n", url, result) + util.Log("获取IPv6结果失败! 接口: %s ,返回值: %s", url, result) } return result } @@ -362,7 +365,7 @@ func (conf *DnsConfig) GetIpv6Addr() (result string) { // 从命令行获取 IP return conf.getAddrFromCmd("IPv6") default: - log.Println("IPv6 的 获取 IP 方式 未知!") + log.Println("IPv6's get IP method is unknown") return "" // unknown type } } diff --git a/config/domains.go b/config/domains.go index f48a6bb..47cbd9a 100644 --- a/config/domains.go +++ b/config/domains.go @@ -1,7 +1,6 @@ package config import ( - "log" "net/url" "strings" @@ -81,7 +80,7 @@ func (domains *Domains) GetNewIp(dnsConf *DnsConfig) { if domains.Ipv4Cache.TimesFailedIP == 3 { domains.Ipv4Domains[0].UpdateStatus = UpdatedFailed } - log.Println("未能获取IPv4地址, 将不会更新") + util.Log("未能获取IPv4地址, 将不会更新") } } @@ -97,7 +96,7 @@ func (domains *Domains) GetNewIp(dnsConf *DnsConfig) { if domains.Ipv6Cache.TimesFailedIP == 3 { domains.Ipv6Domains[0].UpdateStatus = UpdatedFailed } - log.Println("未能获取IPv6地址, 将不会更新") + util.Log("未能获取IPv6地址, 将不会更新") } } @@ -124,7 +123,8 @@ func checkParseDomains(domainArr []string) (domains []*Domain) { case 1: // 不使用冒号分割,自动识别域名 domainName, err := publicsuffix.EffectiveTLDPlusOne(domainStr) if err != nil { - log.Println(domainStr, "域名不正确:", err) + util.Log("域名: %s 不正确", domainStr) + util.Log("异常信息: %s", err) continue } domain.DomainName = domainName @@ -136,21 +136,21 @@ func checkParseDomains(domainArr []string) (domains []*Domain) { case 2: // 使用冒号分隔,为 子域名:根域名 格式 sp := strings.Split(dp[1], ".") if len(sp) <= 1 { - log.Println(domainStr, "域名不正确") + util.Log("域名: %s 不正确", domainStr) continue } domain.DomainName = dp[1] domain.SubDomain = dp[0] default: - log.Println(domainStr, "域名不正确") + util.Log("域名: %s 不正确", domainStr) continue } // 参数条件 if len(qp) == 2 { - u, err := url.Parse("http://baidu.com?" + qp[1]) + u, err := url.Parse("https://baidu.com?" + qp[1]) if err != nil { - log.Println(domainStr, "域名解析失败") + util.Log("域名: %s 解析失败", domainStr) continue } domain.CustomParams = u.Query().Encode() @@ -166,7 +166,7 @@ func (domains *Domains) GetNewIpResult(recordType string) (ipAddr string, retDom if domains.Ipv6Cache.Check(domains.Ipv6Addr) { return domains.Ipv6Addr, domains.Ipv6Domains } else { - log.Printf("IPv6未改变,将等待 %d 次后与DNS服务商进行比对\n", domains.Ipv6Cache.Times) + util.Log("IPv6未改变, 将等待 %d 次后与DNS服务商进行比对", domains.Ipv6Cache.Times) return "", domains.Ipv6Domains } } @@ -174,7 +174,7 @@ func (domains *Domains) GetNewIpResult(recordType string) (ipAddr string, retDom if domains.Ipv4Cache.Check(domains.Ipv4Addr) { return domains.Ipv4Addr, domains.Ipv4Domains } else { - log.Printf("IPv4未改变,将等待 %d 次后与DNS服务商进行比对\n", domains.Ipv4Cache.Times) + util.Log("IPv4未改变, 将等待 %d 次后与DNS服务商进行比对", domains.Ipv4Cache.Times) return "", domains.Ipv4Domains } } diff --git a/config/webhook.go b/config/webhook.go index 5283038..1b7f1f6 100644 --- a/config/webhook.go +++ b/config/webhook.go @@ -3,7 +3,6 @@ package config import ( "encoding/json" "fmt" - "log" "net/http" "net/url" "strings" @@ -48,7 +47,7 @@ func ExecWebhook(domains *Domains, conf *Config) (v4Status updateStatusType, v6S if v4Status == UpdatedFailed || v6Status == UpdatedFailed { updatedFailedTimes++ if updatedFailedTimes != 3 { - log.Println("将不会触发Webhook,仅在第 3 次失败时触发一次Webhook,当前失败次数:", updatedFailedTimes) + util.Log("将不会触发Webhook, 仅在第 3 次失败时触发一次Webhook, 当前失败次数:%d", updatedFailedTimes) return } } else { @@ -66,18 +65,18 @@ func ExecWebhook(domains *Domains, conf *Config) (v4Status updateStatusType, v6S contentType = "application/json" } else if hasJSONPrefix(postPara) { // 如果 RequestBody 的 JSON 无效但前缀为 JSON,提示无效 - log.Println("RequestBody 的 JSON 无效!") + util.Log("Webhook中的 RequestBody JSON 无效") } } requestURL := replacePara(domains, conf.WebhookURL, v4Status, v6Status) u, err := url.Parse(requestURL) if err != nil { - log.Println("Webhook配置中的URL不正确") + util.Log("Webhook配置中的URL不正确") return } req, err := http.NewRequest(method, fmt.Sprintf("%s://%s%s?%s", u.Scheme, u.Host, u.Path, u.Query().Encode()), strings.NewReader(postPara)) if err != nil { - log.Println("创建Webhook请求异常, Err:", err) + util.Log("Webhook调用失败! 异常信息:%s", err) return } @@ -89,11 +88,11 @@ func ExecWebhook(domains *Domains, conf *Config) (v4Status updateStatusType, v6S clt := util.CreateHTTPClient() resp, err := clt.Do(req) - body, err := util.GetHTTPResponseOrg(resp, requestURL, err) + body, err := util.GetHTTPResponseOrg(resp, err) if err == nil { - log.Printf("Webhook调用成功, 返回数据: %q\n", string(body)) + util.Log("Webhook调用成功! 返回数据:%s", string(body)) } else { - log.Printf("Webhook调用失败,Err:%s\n", err) + util.Log("Webhook调用失败! 异常信息:%s", err) } } return @@ -122,11 +121,11 @@ func getDomainsStatus(domains []*Domain) updateStatusType { // replacePara 替换参数 func replacePara(domains *Domains, orgPara string, ipv4Result updateStatusType, ipv6Result updateStatusType) (newPara string) { orgPara = strings.ReplaceAll(orgPara, "#{ipv4Addr}", domains.Ipv4Addr) - orgPara = strings.ReplaceAll(orgPara, "#{ipv4Result}", string(ipv4Result)) + orgPara = strings.ReplaceAll(orgPara, "#{ipv4Result}", util.LogStr(string(ipv4Result))) // i18n orgPara = strings.ReplaceAll(orgPara, "#{ipv4Domains}", getDomainsStr(domains.Ipv4Domains)) orgPara = strings.ReplaceAll(orgPara, "#{ipv6Addr}", domains.Ipv6Addr) - orgPara = strings.ReplaceAll(orgPara, "#{ipv6Result}", string(ipv6Result)) + orgPara = strings.ReplaceAll(orgPara, "#{ipv6Result}", util.LogStr(string(ipv6Result))) // i18n orgPara = strings.ReplaceAll(orgPara, "#{ipv6Domains}", getDomainsStr(domains.Ipv6Domains)) return orgPara @@ -153,7 +152,7 @@ func checkParseHeaders(headerStr string) (headers map[string]string) { if headerStr != "" { parts := strings.Split(headerStr, ":") if len(parts) != 2 { - log.Println(headerStr, "Header不正确") + util.Log("Webhook Header不正确: %s", headerStr) continue } headers[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1]) diff --git a/ddns-web.png b/ddns-web.png index 86ee5af..8446b9e 100644 Binary files a/ddns-web.png and b/ddns-web.png differ diff --git a/dns/alidns.go b/dns/alidns.go index 9e2ebe1..4c5e4e7 100644 --- a/dns/alidns.go +++ b/dns/alidns.go @@ -2,7 +2,6 @@ package dns import ( "bytes" - "log" "net/http" "net/url" @@ -82,6 +81,7 @@ func (ali *Alidns) addUpdateDomainRecords(recordType string) { err := ali.request(params, &records) if err != nil { + util.Log("查询域名信息发生异常! %s", err) domain.UpdateStatus = config.UpdatedFailed return } @@ -120,10 +120,10 @@ func (ali *Alidns) create(domain *config.Domain, recordType string, ipAddr strin err := ali.request(params, &result) if err == nil && result.RecordID != "" { - log.Printf("新增域名解析 %s 成功!IP: %s", domain, ipAddr) + util.Log("新增域名解析 %s 成功! IP: %s", domain, ipAddr) domain.UpdateStatus = config.UpdatedSuccess } else { - log.Printf("新增域名解析 %s 失败!", domain) + util.Log("新增域名解析 %s 失败! 异常信息: %s", domain, err) domain.UpdateStatus = config.UpdatedFailed } } @@ -133,7 +133,7 @@ func (ali *Alidns) modify(recordSelected AlidnsRecord, domain *config.Domain, re // 相同不修改 if recordSelected.Value == ipAddr { - log.Printf("你的IP %s 没有变化, 域名 %s", ipAddr, domain) + util.Log("你的IP %s 没有变化, 域名 %s", ipAddr, domain) return } @@ -149,10 +149,10 @@ func (ali *Alidns) modify(recordSelected AlidnsRecord, domain *config.Domain, re err := ali.request(params, &result) if err == nil && result.RecordID != "" { - log.Printf("更新域名解析 %s 成功!IP: %s", domain, ipAddr) + util.Log("更新域名解析 %s 成功! IP: %s", domain, ipAddr) domain.UpdateStatus = config.UpdatedSuccess } else { - log.Printf("更新域名解析 %s 失败!", domain) + util.Log("更新域名解析 %s 失败! 异常信息: %s", domain, err) domain.UpdateStatus = config.UpdatedFailed } } @@ -170,13 +170,13 @@ func (ali *Alidns) request(params url.Values, result interface{}) (err error) { req.URL.RawQuery = params.Encode() if err != nil { - log.Println("http.NewRequest失败. Error: ", err) + util.Log("异常信息: %s", err) return } client := util.CreateHTTPClient() resp, err := client.Do(req) - err = util.GetHTTPResponse(resp, alidnsEndpoint, err, result) + err = util.GetHTTPResponse(resp, err, result) return } diff --git a/dns/baidu.go b/dns/baidu.go index 8cc5b59..f47de5a 100644 --- a/dns/baidu.go +++ b/dns/baidu.go @@ -3,7 +3,6 @@ package dns import ( "bytes" "encoding/json" - "log" "net/http" "strconv" @@ -110,6 +109,7 @@ func (baidu *BaiduCloud) addUpdateDomainRecords(recordType string) { err := baidu.request("POST", baiduEndpoint+"/v1/domain/resolve/list", requestBody, &records) if err != nil { + util.Log("查询域名信息发生异常! %s", err) domain.UpdateStatus = config.UpdatedFailed return } @@ -143,10 +143,10 @@ func (baidu *BaiduCloud) create(domain *config.Domain, recordType string, ipAddr err := baidu.request("POST", baiduEndpoint+"/v1/domain/resolve/add", baiduCreateRequest, &result) if err == nil { - log.Printf("新增域名解析 %s 成功!IP: %s", domain, ipAddr) + util.Log("新增域名解析 %s 成功! IP: %s", domain, ipAddr) domain.UpdateStatus = config.UpdatedSuccess } else { - log.Printf("新增域名解析 %s 失败!", domain) + util.Log("新增域名解析 %s 失败! 异常信息: %s", domain, err) domain.UpdateStatus = config.UpdatedFailed } } @@ -155,7 +155,7 @@ func (baidu *BaiduCloud) create(domain *config.Domain, recordType string, ipAddr func (baidu *BaiduCloud) modify(record BaiduRecord, domain *config.Domain, rdType string, ipAddr string) { //没有变化直接跳过 if record.Rdata == ipAddr { - log.Printf("你的IP %s 没有变化, 域名 %s", ipAddr, domain) + util.Log("你的IP %s 没有变化, 域名 %s", ipAddr, domain) return } var baiduModifyRequest = BaiduModifyRequest{ @@ -171,10 +171,10 @@ func (baidu *BaiduCloud) modify(record BaiduRecord, domain *config.Domain, rdTyp err := baidu.request("POST", baiduEndpoint+"/v1/domain/resolve/edit", baiduModifyRequest, &result) if err == nil { - log.Printf("更新域名解析 %s 成功!IP: %s", domain, ipAddr) + util.Log("更新域名解析 %s 成功! IP: %s", domain, ipAddr) domain.UpdateStatus = config.UpdatedSuccess } else { - log.Printf("更新域名解析 %s 失败!", domain) + util.Log("更新域名解析 %s 失败! 异常信息: %s", domain, err) domain.UpdateStatus = config.UpdatedFailed } } @@ -193,7 +193,7 @@ func (baidu *BaiduCloud) request(method string, url string, data interface{}, re ) if err != nil { - log.Println("http.NewRequest失败. Error: ", err) + util.Log("异常信息: %s", err) return } @@ -201,7 +201,7 @@ func (baidu *BaiduCloud) request(method string, url string, data interface{}, re client := util.CreateHTTPClient() resp, err := client.Do(req) - err = util.GetHTTPResponse(resp, url, err, result) + err = util.GetHTTPResponse(resp, err, result) return } diff --git a/dns/callback.go b/dns/callback.go index ec91d9f..9fb4f7e 100644 --- a/dns/callback.go +++ b/dns/callback.go @@ -2,7 +2,6 @@ package dns import ( "encoding/json" - "log" "net/http" "net/url" "strings" @@ -53,12 +52,12 @@ func (cb *Callback) addUpdateDomainRecords(recordType string) { // 防止多次发送Webhook通知 if recordType == "A" { if cb.lastIpv4 == ipAddr { - log.Println("你的IPv4未变化, 未触发Callback") + util.Log("你的IPv4未变化, 未触发 %s 请求", "Callback") return } } else { if cb.lastIpv6 == ipAddr { - log.Println("你的IPv6未变化, 未触发Callback") + util.Log("你的IPv6未变化, 未触发 %s 请求", "Callback") return } } @@ -77,24 +76,24 @@ func (cb *Callback) addUpdateDomainRecords(recordType string) { requestURL := replacePara(cb.DNS.ID, ipAddr, domain, recordType, cb.TTL) u, err := url.Parse(requestURL) if err != nil { - log.Println("Callback的URL不正确") + util.Log("Callback的URL不正确") return } req, err := http.NewRequest(method, u.String(), strings.NewReader(postPara)) if err != nil { - log.Println("创建Callback请求异常, Err:", err) + util.Log("异常信息: %s", err) return } req.Header.Add("content-type", contentType) clt := util.CreateHTTPClient() resp, err := clt.Do(req) - body, err := util.GetHTTPResponseOrg(resp, requestURL, err) + body, err := util.GetHTTPResponseOrg(resp, err) if err == nil { - log.Printf("Callback调用成功, 域名: %s, IP: %s, 返回数据: %s, \n", domain, ipAddr, string(body)) + util.Log("Callback调用成功, 域名: %s, IP: %s, 返回数据: %s", domain, ipAddr, string(body)) domain.UpdateStatus = config.UpdatedSuccess } else { - log.Printf("Callback调用失败,Err:%s\n", err) + util.Log("Callback调用失败, 异常信息: %s", err) domain.UpdateStatus = config.UpdatedFailed } } diff --git a/dns/cloudflare.go b/dns/cloudflare.go index c7d7977..6324c8f 100644 --- a/dns/cloudflare.go +++ b/dns/cloudflare.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/json" "fmt" - "log" "net/http" "strconv" @@ -93,6 +92,7 @@ func (cf *Cloudflare) addUpdateDomainRecords(recordType string) { // get zone result, err := cf.getZones(domain) if err != nil || len(result.Result) != 1 { + util.Log("查询域名信息发生异常! %s", err) domain.UpdateStatus = config.UpdatedFailed return } @@ -108,6 +108,7 @@ func (cf *Cloudflare) addUpdateDomainRecords(recordType string) { ) if err != nil || !records.Success { + util.Log("查询域名信息发生异常! %s", err) return } @@ -139,10 +140,10 @@ func (cf *Cloudflare) create(zoneID string, domain *config.Domain, recordType st &status, ) if err == nil && status.Success { - log.Printf("新增域名解析 %s 成功!IP: %s", domain, ipAddr) + util.Log("新增域名解析 %s 成功! IP: %s", domain, ipAddr) domain.UpdateStatus = config.UpdatedSuccess } else { - log.Printf("新增域名解析 %s 失败!Messages: %s", domain, status.Messages) + util.Log("新增域名解析 %s 失败! 异常信息: %s", domain, err) domain.UpdateStatus = config.UpdatedFailed } } @@ -152,7 +153,7 @@ func (cf *Cloudflare) modify(result CloudflareRecordsResp, zoneID string, domain for _, record := range result.Result { // 相同不修改 if record.Content == ipAddr { - log.Printf("你的IP %s 没有变化, 域名 %s", ipAddr, domain) + util.Log("你的IP %s 没有变化, 域名 %s", ipAddr, domain) continue } var status CloudflareStatus @@ -169,10 +170,10 @@ func (cf *Cloudflare) modify(result CloudflareRecordsResp, zoneID string, domain &status, ) if err == nil && status.Success { - log.Printf("更新域名解析 %s 成功!IP: %s", domain, ipAddr) + util.Log("更新域名解析 %s 成功! IP: %s", domain, ipAddr) domain.UpdateStatus = config.UpdatedSuccess } else { - log.Printf("更新域名解析 %s 失败!Messages: %s", domain, status.Messages) + util.Log("更新域名解析 %s 失败! 异常信息: %s", domain, err) domain.UpdateStatus = config.UpdatedFailed } } @@ -202,7 +203,7 @@ func (cf *Cloudflare) request(method string, url string, data interface{}, resul bytes.NewBuffer(jsonStr), ) if err != nil { - log.Println("http.NewRequest失败. Error: ", err) + util.Log("异常信息: %s", err) return } req.Header.Set("Authorization", "Bearer "+cf.DNS.Secret) @@ -210,7 +211,7 @@ func (cf *Cloudflare) request(method string, url string, data interface{}, resul client := util.CreateHTTPClient() resp, err := client.Do(req) - err = util.GetHTTPResponse(resp, url, err, result) + err = util.GetHTTPResponse(resp, err, result) return } diff --git a/dns/dnspod.go b/dns/dnspod.go index b164bec..01f6e6c 100644 --- a/dns/dnspod.go +++ b/dns/dnspod.go @@ -1,7 +1,6 @@ package dns import ( - "log" "net/url" "github.com/jeessy2/ddns-go/v5/config" @@ -76,6 +75,7 @@ func (dnspod *Dnspod) addUpdateDomainRecords(recordType string) { for _, domain := range domains { result, err := dnspod.getRecordList(domain, recordType) if err != nil { + util.Log("查询域名信息发生异常! %s", err) domain.UpdateStatus = config.UpdatedFailed return } @@ -117,10 +117,10 @@ func (dnspod *Dnspod) create(domain *config.Domain, recordType string, ipAddr st status, err := dnspod.commonRequest(recordCreateAPI, params, domain) if err == nil && status.Status.Code == "1" { - log.Printf("新增域名解析 %s 成功!IP: %s", domain, ipAddr) + util.Log("新增域名解析 %s 成功! IP: %s", domain, ipAddr) domain.UpdateStatus = config.UpdatedSuccess } else { - log.Printf("新增域名解析 %s 失败!Code: %s, Message: %s", domain, status.Status.Code, status.Status.Message) + util.Log("新增域名解析 %s 失败! 异常信息: %s", domain, status.Status.Message) domain.UpdateStatus = config.UpdatedFailed } } @@ -130,7 +130,7 @@ func (dnspod *Dnspod) modify(record DnspodRecord, domain *config.Domain, recordT // 相同不修改 if record.Value == ipAddr { - log.Printf("你的IP %s 没有变化, 域名 %s", ipAddr, domain) + util.Log("你的IP %s 没有变化, 域名 %s", ipAddr, domain) return } @@ -149,10 +149,10 @@ func (dnspod *Dnspod) modify(record DnspodRecord, domain *config.Domain, recordT } status, err := dnspod.commonRequest(recordModifyURL, params, domain) if err == nil && status.Status.Code == "1" { - log.Printf("更新域名解析 %s 成功!IP: %s", domain, ipAddr) + util.Log("更新域名解析 %s 成功! IP: %s", domain, ipAddr) domain.UpdateStatus = config.UpdatedSuccess } else { - log.Printf("更新域名解析 %s 失败!Code: %s, Message: %s", domain, status.Status.Code, status.Status.Message) + util.Log("更新域名解析 %s 失败! 异常信息: %s", domain, status.Status.Message) domain.UpdateStatus = config.UpdatedFailed } } @@ -165,7 +165,7 @@ func (dnspod *Dnspod) commonRequest(apiAddr string, values url.Values, domain *c values, ) - err = util.GetHTTPResponse(resp, apiAddr, err, &status) + err = util.GetHTTPResponse(resp, err, &status) return } @@ -186,7 +186,7 @@ func (dnspod *Dnspod) getRecordList(domain *config.Domain, typ string) (result D params, ) - err = util.GetHTTPResponse(resp, recordListAPI, err, &result) + err = util.GetHTTPResponse(resp, err, &result) return } diff --git a/dns/godaddy.go b/dns/godaddy.go index b7516cd..e4958c3 100644 --- a/dns/godaddy.go +++ b/dns/godaddy.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/json" "fmt" - "log" "net/http" "strconv" @@ -59,12 +58,12 @@ func (g *GoDaddyDNS) updateDomainRecord(recordType string, ipAddr string, domain // 防止多次发送Webhook通知 if recordType == "A" { if g.lastIpv4 == ipAddr { - log.Println("你的IPv4未变化, 未触发Godaddy请求") + util.Log("你的IPv4未变化, 未触发 %s 请求", "godaddy") return } } else { if g.lastIpv6 == ipAddr { - log.Println("你的IPv6未变化, 未触发Godaddy请求") + util.Log("你的IPv6未变化, 未触发 %s 请求", "godaddy") return } } @@ -77,10 +76,10 @@ func (g *GoDaddyDNS) updateDomainRecord(recordType string, ipAddr string, domain Type: recordType, }}) if err == nil { - log.Printf("更新域名解析 %s 成功! IP: %s", domain, ipAddr) + util.Log("更新域名解析 %s 成功! IP: %s", domain, ipAddr) domain.UpdateStatus = config.UpdatedSuccess } else { - log.Printf("更新域名解析 %s 失败!", domain) + util.Log("更新域名解析 %s 失败! 异常信息: %s", domain, err) domain.UpdateStatus = config.UpdatedFailed } } @@ -115,6 +114,6 @@ func (g *GoDaddyDNS) sendReq(method string, rType string, domain *config.Domain, } req.Header = g.header resp, err := g.client.Do(req) - _, err = util.GetHTTPResponseOrg(resp, path, err) + _, err = util.GetHTTPResponseOrg(resp, err) return err } diff --git a/dns/google_domain.go b/dns/google_domain.go index 813017f..44fc81a 100644 --- a/dns/google_domain.go +++ b/dns/google_domain.go @@ -2,7 +2,6 @@ package dns import ( "io" - "log" "net/http" "net/url" "strings" @@ -55,12 +54,12 @@ func (gd *GoogleDomain) addUpdateDomainRecords(recordType string) { // 防止多次发送Webhook通知 if recordType == "A" { if gd.lastIpv4 == ipAddr { - log.Println("你的IPv4未变化, 未触发Google请求") + util.Log("你的IPv4未变化, 未触发 %s 请求", "GoogleDomain") return } } else { if gd.lastIpv6 == ipAddr { - log.Println("你的IPv6未变化, 未触发Google请求") + util.Log("你的IPv6未变化, 未触发 %s 请求", "GoogleDomain") return } } @@ -80,19 +79,19 @@ func (gd *GoogleDomain) modify(domain *config.Domain, recordType string, ipAddr err := gd.request(params, &result) if err != nil { - log.Printf("修改域名解析 %s 失败!", domain) + util.Log("更新域名解析 %s 失败! 异常信息: %s", domain, err) domain.UpdateStatus = config.UpdatedFailed return } switch result.Status { case "nochg": - log.Printf("你的IP %s 没有变化, 域名 %s", ipAddr, domain) + util.Log("你的IP %s 没有变化, 域名 %s", ipAddr, domain) case "good": - log.Printf("修改域名解析 %s 成功!IP: %s", domain, ipAddr) + util.Log("更新域名解析 %s 成功! IP: %s", domain, ipAddr) domain.UpdateStatus = config.UpdatedSuccess default: - log.Printf("修改域名解析 %s 失败!Status: %s", domain, result.Status) + util.Log("更新域名解析 %s 失败! 异常信息: %s", domain, result) domain.UpdateStatus = config.UpdatedFailed } } @@ -107,7 +106,7 @@ func (gd *GoogleDomain) request(params url.Values, result *GoogleDomainResp) (er ) if err != nil { - log.Println("http.NewRequest失败. Error: ", err) + util.Log("异常信息: %s", err) return } @@ -117,7 +116,7 @@ func (gd *GoogleDomain) request(params url.Values, result *GoogleDomainResp) (er client := util.CreateHTTPClient() resp, err := client.Do(req) if err != nil { - log.Println("client.Do失败. Error: ", err) + util.Log("异常信息: %s", err) return } diff --git a/dns/huawei.go b/dns/huawei.go index f60d1ea..789c650 100644 --- a/dns/huawei.go +++ b/dns/huawei.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/json" "fmt" - "log" "net/http" "strconv" @@ -94,6 +93,7 @@ func (hw *Huaweicloud) addUpdateDomainRecords(recordType string) { ) if err != nil { + util.Log("查询域名信息发生异常! %s", err) domain.UpdateStatus = config.UpdatedFailed return } @@ -124,7 +124,7 @@ func (hw *Huaweicloud) create(domain *config.Domain, recordType string, ipAddr s return } if len(zone.Zones) == 0 { - log.Println("未能找到公网域名, 请检查域名是否添加") + util.Log("在DNS服务商中未找到域名: %s", domain.String()) return } @@ -150,10 +150,10 @@ func (hw *Huaweicloud) create(domain *config.Domain, recordType string, ipAddr s &result, ) if err == nil && (len(result.Records) > 0 && result.Records[0] == ipAddr) { - log.Printf("新增域名解析 %s 成功!IP: %s", domain, ipAddr) + util.Log("新增域名解析 %s 成功! IP: %s", domain, ipAddr) domain.UpdateStatus = config.UpdatedSuccess } else { - log.Printf("新增域名解析 %s 失败!Status: %s", domain, result.Status) + util.Log("新增域名解析 %s 失败! 异常信息: %s", domain, err) domain.UpdateStatus = config.UpdatedFailed } } @@ -163,7 +163,7 @@ func (hw *Huaweicloud) modify(record HuaweicloudRecordsets, domain *config.Domai // 相同不修改 if len(record.Records) > 0 && record.Records[0] == ipAddr { - log.Printf("你的IP %s 没有变化, 域名 %s", ipAddr, domain) + util.Log("你的IP %s 没有变化, 域名 %s", ipAddr, domain) return } @@ -181,10 +181,10 @@ func (hw *Huaweicloud) modify(record HuaweicloudRecordsets, domain *config.Domai ) if err == nil && (len(result.Records) > 0 && result.Records[0] == ipAddr) { - log.Printf("更新域名解析 %s 成功!IP: %s, 状态: %s", domain, ipAddr, result.Status) + util.Log("更新域名解析 %s 成功! IP: %s", domain, ipAddr) domain.UpdateStatus = config.UpdatedSuccess } else { - log.Printf("更新域名解析 %s 失败!Status: %s", domain, result.Status) + util.Log("更新域名解析 %s 失败! 异常信息: %s", domain, err) domain.UpdateStatus = config.UpdatedFailed } } @@ -215,7 +215,7 @@ func (hw *Huaweicloud) request(method string, url string, data interface{}, resu ) if err != nil { - log.Println("http.NewRequest失败. Error: ", err) + util.Log("异常信息: %s", err) return } @@ -229,7 +229,7 @@ func (hw *Huaweicloud) request(method string, url string, data interface{}, resu client := util.CreateHTTPClient() resp, err := client.Do(req) - err = util.GetHTTPResponse(resp, url, err, result) + err = util.GetHTTPResponse(resp, err, result) return } diff --git a/dns/internal/wait_net.go b/dns/internal/wait_net.go index 204a7d5..8dcfef4 100644 --- a/dns/internal/wait_net.go +++ b/dns/internal/wait_net.go @@ -1,7 +1,6 @@ package internal import ( - "log" "strings" "time" @@ -28,15 +27,14 @@ func WaitForNetworkConnected(addresses []string) { // 如果 err 包含回环地址([::1]:53)则表示没有 DNS 服务器,设置 DNS 服务器 if strings.Contains(err.Error(), loopbackServer) && !find { server := "1.1.1.1:53" - log.Printf("解析回环地址 %s 失败!将默认使用 %s,可参考文档通过 -dns 自定义 DNS 服务器", - loopbackServer, server) - + util.Log("本机DNS异常! 将默认使用 %s, 可参考文档通过 -dns 自定义 DNS 服务器", loopbackServer, server) util.NewDialerResolver(server) find = true continue } - log.Printf("等待网络连接:%s。%s 后重试...", err, timeout) + util.Log("等待网络连接: %s", err) + util.Log("%s 后重试...", timeout) // 等待 5 秒后重试 time.Sleep(timeout) continue diff --git a/dns/namecheap.go b/dns/namecheap.go index d82a229..e54c5e4 100644 --- a/dns/namecheap.go +++ b/dns/namecheap.go @@ -2,7 +2,6 @@ package dns import ( "io" - "log" "net/http" "strings" @@ -56,17 +55,13 @@ func (nc *NameCheap) addUpdateDomainRecords(recordType string) { // 防止多次发送Webhook通知 if recordType == "A" { if nc.lastIpv4 == ipAddr { - log.Println("你的IPv4未变化, 未触发Namecheap请求") + util.Log("你的IPv4未变化, 未触发 %s 请求", "NameCheap") return } } else { // https://www.namecheap.com/support/knowledgebase/article.aspx/29/11/how-to-dynamically-update-the-hosts-ip-with-an-http-request/ - log.Println("Namecheap DDNS 不支持更新 IPv6!") + util.Log("Namecheap 不支持更新 IPv6") return - // if nc.lastIpv6 == ipAddr { - // log.Println("你的IPv6未变化, 未触发Namecheap请求") - // return - // } } for _, domain := range domains { @@ -80,17 +75,17 @@ func (nc *NameCheap) modify(domain *config.Domain, recordType string, ipAddr str err := nc.request(&result, ipAddr, domain) if err != nil { - log.Printf("修改域名解析 %s 失败!", domain) + util.Log("更新域名解析 %s 失败! 异常信息: %s", domain, result) domain.UpdateStatus = config.UpdatedFailed return } switch result.Status { case "Success": - log.Printf("修改域名解析 %s 成功!IP: %s\n", domain, ipAddr) + util.Log("更新域名解析 %s 成功! IP: %s", domain, ipAddr) domain.UpdateStatus = config.UpdatedSuccess default: - log.Printf("修改域名解析 %s 失败!Status: %s\n", domain, result.Status) + util.Log("更新域名解析 %s 失败! 异常信息: %s", domain, result) domain.UpdateStatus = config.UpdatedFailed } } @@ -110,21 +105,21 @@ func (nc *NameCheap) request(result *NameCheapResp, ipAddr string, domain *confi ) if err != nil { - log.Println("http.NewRequest失败. Error: ", err) + util.Log("异常信息: %s", err) return } client := util.CreateHTTPClient() resp, err := client.Do(req) if err != nil { - log.Println("client.Do失败. Error: ", err) + util.Log("异常信息: %s", err) return } defer resp.Body.Close() data, err := io.ReadAll(resp.Body) if err != nil { - log.Println("请求namecheap失败") + util.Log("异常信息: %s", err) return err } diff --git a/dns/namesilo.go b/dns/namesilo.go index 80a5607..6b762d1 100644 --- a/dns/namesilo.go +++ b/dns/namesilo.go @@ -3,7 +3,6 @@ package dns import ( "encoding/xml" "io" - "log" "net/http" "strings" @@ -97,7 +96,7 @@ func (ns *NameSilo) addUpdateDomainRecords(recordType string) { // 拿到DNS记录列表,从列表中去取对应域名的id,有id进行修改,没ID进行新增 records, err := ns.listRecords(domain) if err != nil { - log.Printf("获取域名列表 %s 失败!", domain) + util.Log("查询域名信息发生异常! %s", err) domain.UpdateStatus = config.UpdatedFailed return } @@ -110,7 +109,7 @@ func (ns *NameSilo) addUpdateDomainRecords(recordType string) { } else { recordID = record.RecordID if record.Value == ipAddr { - log.Printf("你的IP %s 没有变化, 域名 %s", ipAddr, domain) + util.Log("你的IP %s 没有变化, 域名 %s", ipAddr, domain) return } } @@ -127,21 +126,21 @@ func (ns *NameSilo) modify(domain *config.Domain, recordID, recordType, ipAddr s requestType = "新增" result, err = ns.request(ipAddr, domain, "", recordType, nameSiloAddRecordEndpoint) } else { - requestType = "修改" + requestType = "更新" result, err = ns.request(ipAddr, domain, recordID, "", nameSiloUpdateRecordEndpoint) } if err != nil { - log.Printf("修改域名解析 %s 失败!", domain) + util.Log("异常信息: %s", err) domain.UpdateStatus = config.UpdatedFailed return } var resp NameSiloResp xml.Unmarshal([]byte(result), &resp) if resp.Reply.Code == 300 { - log.Printf("%s 域名解析 %s 成功!IP: %s\n", requestType, domain, ipAddr) + util.Log(requestType+"域名解析 %s 成功! IP: %s\n", domain, ipAddr) domain.UpdateStatus = config.UpdatedSuccess } else { - log.Printf("%s 域名解析 %s 失败!Deatil: %s\n", requestType, domain, resp.Reply.Detail) + util.Log(requestType+"域名解析 %s 失败! 异常信息: %s", domain, resp.Reply.Detail) domain.UpdateStatus = config.UpdatedFailed } } @@ -172,14 +171,14 @@ func (ns *NameSilo) request(ipAddr string, domain *config.Domain, recordID, reco ) if err != nil { - log.Println("http.NewRequest失败. Error: ", err) + util.Log("异常信息: %s", err) return } client := util.CreateHTTPClient() resp, err := client.Do(req) if err != nil { - log.Println("client.Do失败. Error: ", err) + util.Log("异常信息: %s", err) return } diff --git a/dns/porkbun.go b/dns/porkbun.go index 057ff37..56cb45e 100644 --- a/dns/porkbun.go +++ b/dns/porkbun.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/json" "fmt" - "log" "net/http" "github.com/jeessy2/ddns-go/v5/config" @@ -87,26 +86,27 @@ func (pb *Porkbun) addUpdateDomainRecords(recordType string) { ) if err != nil { + util.Log("查询域名信息发生异常! %s", err) domain.UpdateStatus = config.UpdatedFailed return } if record.Status == "SUCCESS" { if len(record.Records) > 0 { // 存在,更新 - pb.modify(&record, domain, &recordType, &ipAddr) + pb.modify(&record, domain, recordType, ipAddr) } else { // 不存在,创建 - pb.create(domain, &recordType, &ipAddr) + pb.create(domain, recordType, ipAddr) } } else { - log.Printf("查询现有域名记录失败") + util.Log("在DNS服务商中未找到域名: %s", domain.String()) domain.UpdateStatus = config.UpdatedFailed } } } // 创建 -func (pb *Porkbun) create(domain *config.Domain, recordType *string, ipAddr *string) { +func (pb *Porkbun) create(domain *config.Domain, recordType string, ipAddr string) { var response PorkbunResponse err := pb.request( @@ -118,8 +118,8 @@ func (pb *Porkbun) create(domain *config.Domain, recordType *string, ipAddr *str }, PorkbunDomainRecord: &PorkbunDomainRecord{ Name: &domain.SubDomain, - Type: recordType, - Content: ipAddr, + Type: &recordType, + Content: &ipAddr, Ttl: &pb.TTL, }, }, @@ -127,34 +127,34 @@ func (pb *Porkbun) create(domain *config.Domain, recordType *string, ipAddr *str ) if err == nil && response.Status == "SUCCESS" { - log.Printf("新增域名解析 %s 成功!IP: %s", domain, *ipAddr) + util.Log("新增域名解析 %s 成功! IP: %s", domain, ipAddr) domain.UpdateStatus = config.UpdatedSuccess } else { - log.Printf("新增域名解析 %s 失败!", domain) + util.Log("新增域名解析 %s 失败! 异常信息: %s", domain, err) domain.UpdateStatus = config.UpdatedFailed } } // 修改 -func (pb *Porkbun) modify(record *PorkbunDomainQueryResponse, domain *config.Domain, recordType *string, ipAddr *string) { +func (pb *Porkbun) modify(record *PorkbunDomainQueryResponse, domain *config.Domain, recordType string, ipAddr string) { // 相同不修改 - if len(record.Records) > 0 && *record.Records[0].Content == *ipAddr { - log.Printf("你的IP %s 没有变化, 域名 %s", *ipAddr, domain) + if len(record.Records) > 0 && *record.Records[0].Content == ipAddr { + util.Log("你的IP %s 没有变化, 域名 %s", ipAddr, domain) return } var response PorkbunResponse err := pb.request( - porkbunEndpoint+fmt.Sprintf("/editByNameType/%s/%s/%s", domain.DomainName, *recordType, domain.SubDomain), + porkbunEndpoint+fmt.Sprintf("/editByNameType/%s/%s/%s", domain.DomainName, recordType, domain.SubDomain), &PorkbunDomainCreateOrUpdateVO{ PorkbunApiKey: &PorkbunApiKey{ AccessKey: pb.DNSConfig.ID, SecretKey: pb.DNSConfig.Secret, }, PorkbunDomainRecord: &PorkbunDomainRecord{ - Content: ipAddr, + Content: &ipAddr, Ttl: &pb.TTL, }, }, @@ -162,10 +162,10 @@ func (pb *Porkbun) modify(record *PorkbunDomainQueryResponse, domain *config.Dom ) if err == nil && response.Status == "SUCCESS" { - log.Printf("更新域名解析 %s 成功!IP: %s", domain, *ipAddr) + util.Log("更新域名解析 %s 成功! IP: %s", domain, ipAddr) domain.UpdateStatus = config.UpdatedSuccess } else { - log.Printf("更新域名解析 %s 失败!", domain) + util.Log("更新域名解析 %s 失败! 异常信息: %s", domain, err) domain.UpdateStatus = config.UpdatedFailed } } @@ -182,14 +182,14 @@ func (pb *Porkbun) request(url string, data interface{}, result interface{}) (er bytes.NewBuffer(jsonStr), ) if err != nil { - log.Println("http.NewRequest失败. Error: ", err) + util.Log("异常信息: %s", err) return } req.Header.Set("Content-Type", "application/json") client := util.CreateHTTPClient() resp, err := client.Do(req) - err = util.GetHTTPResponse(resp, url, err, result) + err = util.GetHTTPResponse(resp, err, result) return } diff --git a/dns/tencent_cloud.go b/dns/tencent_cloud.go index 7c5687c..f778172 100644 --- a/dns/tencent_cloud.go +++ b/dns/tencent_cloud.go @@ -3,7 +3,6 @@ package dns import ( "bytes" "encoding/json" - "log" "net/http" "strconv" @@ -99,6 +98,7 @@ func (tc *TencentCloud) addUpdateDomainRecords(recordType string) { for _, domain := range domains { result, err := tc.getRecordList(domain, recordType) if err != nil { + util.Log("查询域名信息发生异常! %s", err) domain.UpdateStatus = config.UpdatedFailed return } @@ -143,10 +143,10 @@ func (tc *TencentCloud) create(domain *config.Domain, recordType string, ipAddr &status, ) if err == nil && status.Response.Error.Code == "" { - log.Printf("新增域名解析 %s 成功!IP: %s", domain, ipAddr) + util.Log("新增域名解析 %s 成功! IP: %s", domain, ipAddr) domain.UpdateStatus = config.UpdatedSuccess } else { - log.Printf("新增域名解析 %s 失败!Code: %s, Message: %s", domain, status.Response.Error.Code, status.Response.Error.Message) + util.Log("新增域名解析 %s 失败! 异常信息: %s", domain, status.Response.Error.Message) domain.UpdateStatus = config.UpdatedFailed } } @@ -156,7 +156,7 @@ func (tc *TencentCloud) create(domain *config.Domain, recordType string, ipAddr func (tc *TencentCloud) modify(record TencentCloudRecord, domain *config.Domain, recordType string, ipAddr string) { // 相同不修改 if record.Value == ipAddr { - log.Printf("你的IP %s 没有变化, 域名 %s", ipAddr, domain) + util.Log("你的IP %s 没有变化, 域名 %s", ipAddr, domain) return } var status TencentCloudStatus @@ -172,10 +172,10 @@ func (tc *TencentCloud) modify(record TencentCloudRecord, domain *config.Domain, &status, ) if err == nil && status.Response.Error.Code == "" { - log.Printf("更新域名解析 %s 成功!IP: %s", domain, ipAddr) + util.Log("更新域名解析 %s 成功! IP: %s", domain, ipAddr) domain.UpdateStatus = config.UpdatedSuccess } else { - log.Printf("更新域名解析 %s 失败!Code: %s, Message: %s", domain, status.Response.Error.Code, status.Response.Error.Message) + util.Log("更新域名解析 %s 失败! 异常信息: %s", domain, status.Response.Error.Message) domain.UpdateStatus = config.UpdatedFailed } } @@ -218,7 +218,7 @@ func (tc *TencentCloud) request(action string, data interface{}, result interfac bytes.NewBuffer(jsonStr), ) if err != nil { - log.Println("http.NewRequest 失败. Error: ", err) + util.Log("异常信息: %s", err) return } @@ -229,7 +229,7 @@ func (tc *TencentCloud) request(action string, data interface{}, result interfac client := util.CreateHTTPClient() resp, err := client.Do(req) - err = util.GetHTTPResponse(resp, tencentCloudEndPoint, err, result) + err = util.GetHTTPResponse(resp, err, result) return } diff --git a/go.mod b/go.mod index 4c2fd79..ddae7fd 100644 --- a/go.mod +++ b/go.mod @@ -9,4 +9,7 @@ require ( gopkg.in/yaml.v3 v3.0.1 ) -require golang.org/x/sys v0.16.0 // indirect +require ( + golang.org/x/sys v0.16.0 // indirect + golang.org/x/text v0.14.0 +) diff --git a/go.sum b/go.sum index 14ae5a7..062bd88 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/main.go b/main.go index a8f02c7..fb54695 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "embed" + "errors" "flag" "fmt" "log" @@ -24,34 +25,34 @@ import ( // ddns-go 版本 // ddns-go version -var versionFlag = flag.Bool("v", false, "ddns-go 版本") +var versionFlag = flag.Bool("v", false, "ddns-go version") // 更新 ddns-go -var updateFlag = flag.Bool("u", false, "更新 ddns-go") +var updateFlag = flag.Bool("u", false, "Upgrade ddns-go to the latest version") // 监听地址 -var listen = flag.String("l", ":9876", "监听地址") +var listen = flag.String("l", ":9876", "Listen address") // 更新频率(秒) -var every = flag.Int("f", 300, "同步间隔时间(秒)") +var every = flag.Int("f", 300, "Sync frequency(seconds)") // 缓存次数 -var ipCacheTimes = flag.Int("cacheTimes", 5, "间隔N次与服务商比对") +var ipCacheTimes = flag.Int("cacheTimes", 5, "Interval N times compared with service providers") // 服务管理 -var serviceType = flag.String("s", "", "服务管理, 支持install, uninstall, restart") +var serviceType = flag.String("s", "", "Service management (install|uninstall|restart)") // 配置文件路径 -var configFilePath = flag.String("c", util.GetConfigFilePathDefault(), "自定义配置文件路径") +var configFilePath = flag.String("c", util.GetConfigFilePathDefault(), "config file path") // Web 服务 -var noWebService = flag.Bool("noweb", false, "不启动 web 服务") +var noWebService = flag.Bool("noweb", false, "No web service") // 跳过验证证书 -var skipVerify = flag.Bool("skipVerify", false, "跳过验证证书, 适合不能升级的老系统") +var skipVerify = flag.Bool("skipVerify", false, "Skip certificate verification") // 自定义 DNS 服务器 -var customDNSServer = flag.String("dns", "", "自定义 DNS 服务器(例如 1.1.1.1)") +var customDNSServer = flag.String("dns", "", "Custom DNS server, example: 8.8.8.8") //go:embed static var staticEmbeddedFiles embed.FS @@ -73,7 +74,7 @@ func main() { return } if _, err := net.ResolveTCPAddr("tcp", *listen); err != nil { - log.Fatalf("解析监听地址异常,%s", err) + log.Fatalf("Parse listen address failed! Exception: %s", err) } os.Setenv(web.VersionEnv, version) if *configFilePath != "" { @@ -111,9 +112,9 @@ func main() { // 非服务方式运行 switch s.Platform() { case "windows-service": - log.Println("可使用 .\\ddns-go.exe -s install 安装服务运行") + util.Log("可使用 .\\ddns-go.exe -s install 安装服务运行") default: - log.Println("可使用 sudo ./ddns-go -s install 安装服务运行") + util.Log("可使用 sudo ./ddns-go -s install 安装服务运行") } run() } @@ -163,11 +164,11 @@ func runWebServer() error { http.HandleFunc("/ipv6NetInterface", web.BasicAuth(web.Ipv6NetInterfaces)) http.HandleFunc("/webhookTest", web.BasicAuth(web.WebhookTest)) - log.Println("监听", *listen, "...") + util.Log("监听 %s", *listen) l, err := net.Listen("tcp", *listen) if err != nil { - return fmt.Errorf("监听端口发生异常, 请检查端口是否被占用: %w", err) + return errors.New(util.LogStr("监听端口发生异常, 请检查端口是否被占用! %s", err)) } // 没有配置, 自动打开浏览器 @@ -211,7 +212,7 @@ func getService() service.Service { svcConfig := &service.Config{ Name: "ddns-go", DisplayName: "ddns-go", - Description: "简单好用的DDNS。自动更新域名解析到公网IP(支持阿里云、腾讯云dnspod、Cloudflare、Callback、华为云、百度云、Porkbun、GoDaddy、Google Domain)", + Description: "Simple and easy to use DDNS. Automatically update domain name resolution to public IP (Support Aliyun, Tencent Cloud, Dnspod, Cloudflare, Callback, Huawei Cloud, Baidu Cloud, Porkbun, GoDaddy...)", Arguments: []string{"-l", *listen, "-f", strconv.Itoa(*every), "-cacheTimes", strconv.Itoa(*ipCacheTimes), "-c", *configFilePath}, Dependencies: depends, Option: options, @@ -247,9 +248,9 @@ func uninstallService() { } } if err := s.Uninstall(); err == nil { - log.Println("ddns-go 服务卸载成功!") + util.Log("ddns-go 服务卸载成功") } else { - log.Printf("ddns-go 服务卸载失败, ERR: %s\n", err) + util.Log("ddns-go 服务卸载失败, 异常信息: %s", err) } } @@ -262,7 +263,7 @@ func installService() { // 服务未知,创建服务 if err = s.Install(); err == nil { s.Start() - log.Println("安装 ddns-go 服务成功! 请打开浏览器并进行配置。") + util.Log("安装 ddns-go 服务成功! 请打开浏览器并进行配置") if service.ChosenSystem().String() == "unix-systemv" { if _, err := exec.Command("/etc/init.d/ddns-go", "enable").Output(); err != nil { log.Println(err) @@ -273,12 +274,11 @@ func installService() { } return } - - log.Printf("安装 ddns-go 服务失败, ERR: %s\n", err) + util.Log("安装 ddns-go 服务失败, 异常信息: %s", err) } if status != service.StatusUnknown { - log.Println("ddns-go 服务已安装, 无需再次安装") + util.Log("ddns-go 服务已安装, 无需再次安装") } } @@ -289,15 +289,15 @@ func restartService() { if err == nil { if status == service.StatusRunning { if err = s.Restart(); err == nil { - log.Println("重启 ddns-go 服务成功!") + util.Log("重启 ddns-go 服务成功") } } else if status == service.StatusStopped { if err = s.Start(); err == nil { - log.Println("启动 ddns-go 服务成功!") + util.Log("启动 ddns-go 服务成功") } } } else { - log.Println("ddns-go 服务未安装, 请先安装服务") + util.Log("ddns-go 服务未安装, 请先安装服务") } } @@ -308,7 +308,7 @@ func autoOpenExplorer() { if err != nil { if util.IsRunInDocker() { // docker中运行, 提示 - fmt.Println("Docker中运行, 请在浏览器中打开 http://docker主机IP:端口 进行配置") + util.Log("Docker中运行, 请在浏览器中打开 http://docker主机IP:9876 进行配置") } else { // 主机运行, 打开浏览器 addr, err := net.ResolveTCPAddr("tcp", *listen) diff --git a/static/common.css b/static/common.css index d07015e..9988113 100644 --- a/static/common.css +++ b/static/common.css @@ -226,6 +226,5 @@ main { margin-right: 8px; line-height: 0; text-align: center; - vertical-align: text-bottom; font-size: 16px; } \ No newline at end of file diff --git a/util/http_util.go b/util/http_util.go index 40d1e43..3622c88 100644 --- a/util/http_util.go +++ b/util/http_util.go @@ -4,21 +4,17 @@ import ( "encoding/json" "fmt" "io" - "log" "net/http" ) // GetHTTPResponse 处理HTTP结果,返回序列化的json -func GetHTTPResponse(resp *http.Response, url string, err error, result interface{}) error { - body, err := GetHTTPResponseOrg(resp, url, err) +func GetHTTPResponse(resp *http.Response, err error, result interface{}) error { + body, err := GetHTTPResponseOrg(resp, err) if err == nil { // log.Println(string(body)) if len(body) != 0 { err = json.Unmarshal(body, &result) - if err != nil { - log.Printf("请求接口%s解析json结果失败! ERROR: %s\n", url, err) - } } } @@ -27,9 +23,8 @@ func GetHTTPResponse(resp *http.Response, url string, err error, result interfac } // GetHTTPResponseOrg 处理HTTP结果,返回byte -func GetHTTPResponseOrg(resp *http.Response, url string, err error) ([]byte, error) { +func GetHTTPResponseOrg(resp *http.Response, err error) ([]byte, error) { if err != nil { - log.Printf("请求接口%s失败! ERROR: %s\n", url, err) return nil, err } @@ -38,14 +33,12 @@ func GetHTTPResponseOrg(resp *http.Response, url string, err error) ([]byte, err body, err := io.ReadAll(lr) if err != nil { - log.Printf("请求接口%s失败! ERROR: %s\n", url, err) + return nil, err } // 300及以上状态码都算异常 if resp.StatusCode >= 300 { - errMsg := fmt.Sprintf("请求接口 %s 失败! 返回内容: %s ,返回状态码: %d\n", url, string(body), resp.StatusCode) - log.Println(errMsg) - err = fmt.Errorf(errMsg) + err = fmt.Errorf(LogStr("返回内容: %s ,返回状态码: %d", string(body), resp.StatusCode)) } return body, err diff --git a/util/messages.go b/util/messages.go new file mode 100644 index 0000000..7b053e8 --- /dev/null +++ b/util/messages.go @@ -0,0 +1,128 @@ +package util + +import ( + "log" + "strings" + + "golang.org/x/text/language" + "golang.org/x/text/message" +) + +var logPrinter = message.NewPrinter(language.English) + +func init() { + + message.SetString(language.English, "可使用 .\\ddns-go.exe -s install 安装服务运行", "You can use 'sudo .\\ddns-go -s install' to install service") + message.SetString(language.English, "可使用 sudo ./ddns-go -s install 安装服务运行", "You can use 'sudo ./ddns-go -s install' to install service") + message.SetString(language.English, "监听 %s", "Listen on %s") + message.SetString(language.English, "配置文件已保存在: %s", "Config file has been saved to: %s") + + message.SetString(language.English, "你的IP %s 没有变化, 域名 %s", "Your's IP %s has not changed! Domain: %s") + message.SetString(language.English, "新增域名解析 %s 成功! IP: %s", "Added domain %s successfully! IP: %s") + message.SetString(language.English, "新增域名解析 %s 失败! 异常信息: %s", "Added domain %s failed! Result: %s") + + message.SetString(language.English, "更新域名解析 %s 成功! IP: %s", "Updated domain %s successfully! IP: %s") + message.SetString(language.English, "更新域名解析 %s 失败! 异常信息: %s", "Updated domain %s failed! Result: %s") + + message.SetString(language.English, "你的IPv4未变化, 未触发 %s 请求", "Your's IPv4 has not changed, %s request has not been triggered") + message.SetString(language.English, "你的IPv6未变化, 未触发 %s 请求", "Your's IPv6 has not changed, %s request has not been triggered") + message.SetString(language.English, "Namecheap 不支持更新 IPv6", "Namecheap don't supports IPv6") + + // http_util + message.SetString(language.English, "异常信息: %s", "Exception: %s") + message.SetString(language.English, "查询域名信息发生异常! %s", "Query domain info failed! %s") + message.SetString(language.English, "返回内容: %s ,返回状态码: %d", "Response body: %s ,Response status code: %d") + message.SetString(language.English, "通过接口获取IPv4失败! 接口地址: %s", "Get IPv4 from %s failed") + message.SetString(language.English, "通过接口获取IPv6失败! 接口地址: %s", "Get IPv6 from %s failed") + message.SetString(language.English, "将不会触发Webhook, 仅在第 3 次失败时触发一次Webhook, 当前失败次数:%d", "Webhook will not be triggered, only trigger once when the third failure, current failure times: %d") + message.SetString(language.English, "在DNS服务商中未找到域名: %s", "Domain %s not found in DNS provider") + + // webhook + message.SetString(language.English, "Webhook配置中的URL不正确", "Webhook url is incorrect") + message.SetString(language.English, "Webhook中的 RequestBody JSON 无效", "Webhook RequestBody JSON is invalid") + message.SetString(language.English, "Webhook调用成功! 返回数据:%s", "Webhook called successfully! Response body: %s") + message.SetString(language.English, "Webhook调用失败! 异常信息:%s", "Webhook called failed! Exception: %s") + message.SetString(language.English, "Webhook Header不正确: %s", "Webhook header is invalid: %s") + message.SetString(language.English, "请输入Webhook的URL", "Please enter the Webhook url") + + // callback + message.SetString(language.English, "Callback的URL不正确", "Callback url is incorrect") + message.SetString(language.English, "Callback调用成功, 域名: %s, IP: %s, 返回数据: %s", "Webhook called successfully! Domain: %s, IP: %s, Response body: %s") + message.SetString(language.English, "Callback调用失败, 异常信息: %s", "Webhook called failed! Exception: %s") + + // save + message.SetString(language.English, "若通过公网访问, 仅允许在ddns-go启动后 5 分钟内完成首次配置", "If accessed via the public network, only allow the first configuration to be completed within 5 minutes after ddns-go starts") + message.SetString(language.English, "若从未设置过帐号密码, 仅允许在ddns-go启动后 5 分钟内设置, 请重启ddns-go", "If you have never set an account password, you can only set it within 5 minutes after ddns-go starts, please restart ddns-go") + message.SetString(language.English, "启用外网访问, 必须输入登录用户名/密码", "Enable external network access, you must enter the login username/password") + message.SetString(language.English, "修改 '通过命令获取' 必须设置帐号密码,请先设置帐号密码", "Modify 'Get by command' must set username/password, please set username/password first") + message.SetString(language.English, "密码不安全!尝试使用更长的密码", "insecure password, try using a longer password") + + // config + message.SetString(language.English, "从网卡获得IPv4失败", "Get IPv4 from network card failed") + message.SetString(language.English, "从网卡中获得IPv4失败! 网卡名: %s", "Get IPv4 from network card failed! Network card name: %s") + message.SetString(language.English, "获取IPv4结果失败! 接口: %s ,返回值: %s", "Get IPv4 result failed! Interface: %s ,Result: %s") + message.SetString(language.English, "获取%s结果失败! 未能成功执行命令:%s, 错误:%q, 退出状态码:%s", "Get %s result failed! Command: %s, Error: %q, Exit status code: %s") + message.SetString(language.English, "获取%s结果失败! 命令: %s, 标准输出: %q", "Get %s result failed! Command: %s, Stdout: %q") + message.SetString(language.English, "从网卡获得IPv6失败", "Get IPv6 from network card failed") + message.SetString(language.English, "从网卡中获得IPv6失败! 网卡名: %s", "Get IPv6 from network card failed! Network card name: %s") + message.SetString(language.English, "获取IPv6结果失败! 接口: %s ,返回值: %s", "Get IPv6 result failed! Interface: %s ,Result: %s") + message.SetString(language.English, "未找到第 %d 个IPv6地址! 将使用第一个IPv6地址", "%dth IPv6 address not found! Will use the first IPv6 address") + message.SetString(language.English, "IPv6匹配表达式 %s 不正确! 最小从1开始", "IPv6 match expression %s is incorrect! Minimum start from 1") + message.SetString(language.English, "IPv6将使用正则表达式 %s 进行匹配", "IPv6 will use regular expression %s for matching") + message.SetString(language.English, "匹配成功! 匹配到地址: %s", "Match successfully! Matched address: %s") + message.SetString(language.English, "没有匹配到任何一个IPv6地址, 将使用第一个地址", "No IPv6 address matched, will use the first address") + message.SetString(language.English, "未能获取IPv4地址, 将不会更新", "Failed to get IPv4 address, will not update") + message.SetString(language.English, "未能获取IPv6地址, 将不会更新", "Failed to get IPv6 address, will not update") + + // domains + message.SetString(language.English, "域名: %s 不正确", "The domain %s is incorrect") + message.SetString(language.English, "域名: %s 解析失败", "The domain %s resolution failed") + message.SetString(language.English, "IPv6未改变, 将等待 %d 次后与DNS服务商进行比对", "IPv6 has not changed, will wait %d times to compare with DNS provider") + message.SetString(language.English, "IPv4未改变, 将等待 %d 次后与DNS服务商进行比对", "IPv4 has not changed, will wait %d times to compare with DNS provider") + + message.SetString(language.English, "本机DNS异常! 将默认使用 %s, 可参考文档通过 -dns 自定义 DNS 服务器", "Local DNS exception! Will use %s by default, you can use -dns to customize DNS server") + message.SetString(language.English, "等待网络连接: %s", "Waiting for network connection: %s") + message.SetString(language.English, "%s 后重试...", "Retry after %s") + + // main + message.SetString(language.English, "监听端口发生异常, 请检查端口是否被占用! %s", "Listen port failed, please check if the port is occupied! %s") + message.SetString(language.English, "Docker中运行, 请在浏览器中打开 http://docker主机IP:9876 进行配置", "Running in Docker, please open http://docker-host-ip:9876 in the browser for configuration") + message.SetString(language.English, "ddns-go 服务卸载成功", "ddns-go service uninstalled successfully") + message.SetString(language.English, "ddns-go 服务卸载失败, 异常信息: %s", "ddns-go service uninstalled failed, Exception: %s") + message.SetString(language.English, "安装 ddns-go 服务成功! 请打开浏览器并进行配置", "Install ddns-go service successfully! Please open the browser and configure it") + message.SetString(language.English, "安装 ddns-go 服务失败, 异常信息: %s", "Install ddns-go service failed, Exception: %s") + message.SetString(language.English, "ddns-go 服务已安装, 无需再次安装", "ddns-go service has been installed, no need to install again") + message.SetString(language.English, "重启 ddns-go 服务成功", "restart ddns-go service successfully") + message.SetString(language.English, "启动 ddns-go 服务成功", "start ddns-go service successfully") + message.SetString(language.English, "ddns-go 服务未安装, 请先安装服务", "ddns-go service is not installed, please install the service first") + + // login + message.SetString(language.English, "%q 配置文件为空, 超过3小时禁止从公网访问", "%q configuration file is empty, public network access is prohibited for more than 3 hours") + message.SetString(language.English, "%q 被禁止从公网访问", "%q is prohibited from accessing the public network") + message.SetString(language.English, "%q 登陆失败超过5次! 并延时5分钟响应", "%q login failed more than 5 times! And delay 5 minutes to respond") + message.SetString(language.English, "%q 帐号密码不正确", "%q username or password is incorrect") + message.SetString(language.English, "%q 请求登陆", "%q request login") + + // webhook通知 + message.SetString(language.English, "未改变", "no changed") + message.SetString(language.English, "失败", "failed") + message.SetString(language.English, "成功", "success") + +} + +func Log(key string, args ...interface{}) { + log.Println(LogStr(key, args...)) +} + +func LogStr(key string, args ...interface{}) string { + return logPrinter.Sprintf(key, args...) +} + +func InitLogLang(lang string) string { + logLang := language.English + if strings.HasPrefix(lang, "zh") { + logLang = language.Chinese + } + logPrinter = message.NewPrinter(logLang) + return logLang.String() +} diff --git a/util/update/release.go b/util/update/release.go index 7085f9e..4e1f0ca 100644 --- a/util/update/release.go +++ b/util/update/release.go @@ -41,8 +41,9 @@ func getLatest(repo string) (*Release, error) { } var result ReleaseResp - err = util.GetHTTPResponse(resp, u, err, &result) + err = util.GetHTTPResponse(resp, err, &result) if err != nil { + util.Log("异常信息: %s", err) return nil, err } diff --git a/web/basic_auth.go b/web/basic_auth.go index cc41896..fb6afa1 100644 --- a/web/basic_auth.go +++ b/web/basic_auth.go @@ -3,7 +3,6 @@ package web import ( "bytes" "encoding/base64" - "log" "net/http" "strings" "time" @@ -30,7 +29,7 @@ func BasicAuth(f ViewFunc) ViewFunc { if err != nil && time.Now().Unix()-startTime > 3*60*60 && (!util.IsPrivateNetwork(r.RemoteAddr) || !util.IsPrivateNetwork(r.Host)) { w.WriteHeader(http.StatusForbidden) - log.Printf("%q 配置文件为空, 超过3小时禁止从公网访问。\n", util.GetRequestIPStr(r)) + util.Log("%q 配置文件为空, 超过3小时禁止从公网访问", util.GetRequestIPStr(r)) return } @@ -38,7 +37,7 @@ func BasicAuth(f ViewFunc) ViewFunc { if conf.NotAllowWanAccess { if !util.IsPrivateNetwork(r.RemoteAddr) || !util.IsPrivateNetwork(r.Host) { w.WriteHeader(http.StatusForbidden) - log.Printf("%q 被禁止从公网访问!\n", util.GetRequestIPStr(r)) + util.Log("%q 被禁止从公网访问", util.GetRequestIPStr(r)) return } } @@ -51,7 +50,7 @@ func BasicAuth(f ViewFunc) ViewFunc { } if ld.FailTimes >= 5 { - log.Printf("%q 登陆失败超过5次! 并延时5分钟响应!\n", util.GetRequestIPStr(r)) + util.Log("%q 登陆失败超过5次! 并延时5分钟响应", util.GetRequestIPStr(r)) time.Sleep(5 * time.Minute) if ld.FailTimes >= 5 { ld.FailTimes = 0 @@ -84,7 +83,7 @@ func BasicAuth(f ViewFunc) ViewFunc { } ld.FailTimes = ld.FailTimes + 1 - log.Printf("%q 帐号密码不正确!\n", util.GetRequestIPStr(r)) + util.Log("%q 帐号密码不正确", util.GetRequestIPStr(r)) } // 认证失败,提示 401 Unauthorized @@ -92,6 +91,6 @@ func BasicAuth(f ViewFunc) ViewFunc { w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) // 401 状态码 w.WriteHeader(http.StatusUnauthorized) - log.Printf("%q 请求登陆!\n", util.GetRequestIPStr(r)) + util.Log("%q 请求登陆", util.GetRequestIPStr(r)) } } diff --git a/web/password.go b/web/password.go deleted file mode 100644 index 3418255..0000000 --- a/web/password.go +++ /dev/null @@ -1,86 +0,0 @@ -package web - -import ( - "errors" - "fmt" - "strings" - - passwordvalidator "github.com/wagslane/go-password-validator" -) - -const ( - replaceChars = `!@$&*` - sepChars = `_-., ` - otherSpecialChars = `"#%'()+/:;<=>?[\]^{|}~` - lowerChars = `abcdefghijklmnopqrstuvwxyz` - upperChars = `ABCDEFGHIJKLMNOPQRSTUVWXYZ` - digitsChars = `0123456789` -) - -// validatePassword 在密码大于或等于 minEntropy 时返回 nil。如果不是则返回错误。 -// 这解释了如何加强密码。向客户端显示此错误是安全的。 -// -// https://github.com/wagslane/go-password-validator/blob/v0.3.0/validate.go#L13 -func validatePassword(password string, minEntropy float64) error { - entropy := passwordvalidator.GetEntropy(password) - if entropy >= minEntropy { - return nil - } - - hasReplace := false - hasSep := false - hasOtherSpecial := false - hasLower := false - hasUpper := false - hasDigits := false - for _, c := range password { - if strings.ContainsRune(replaceChars, c) { - hasReplace = true - continue - } - if strings.ContainsRune(sepChars, c) { - hasSep = true - continue - } - if strings.ContainsRune(otherSpecialChars, c) { - hasOtherSpecial = true - continue - } - if strings.ContainsRune(lowerChars, c) { - hasLower = true - continue - } - if strings.ContainsRune(upperChars, c) { - hasUpper = true - continue - } - if strings.ContainsRune(digitsChars, c) { - hasDigits = true - continue - } - } - - allMessages := []string{} - - if !hasOtherSpecial || !hasSep || !hasReplace { - allMessages = append(allMessages, "包含更多特殊字符") - } - if !hasLower { - allMessages = append(allMessages, "使用小写字母") - } - if !hasUpper { - allMessages = append(allMessages, "使用大写字母") - } - if !hasDigits { - allMessages = append(allMessages, "使用数字") - } - - if len(allMessages) > 0 { - return fmt.Errorf( - "密码不安全!尝试%v或使用更长的密码", - strings.Join(allMessages, ","), - ) - } - - return errors.New("密码不安全!尝试使用更长的密码") -} diff --git a/web/save.go b/web/save.go index d05dc12..5070049 100755 --- a/web/save.go +++ b/web/save.go @@ -9,6 +9,7 @@ import ( "github.com/jeessy2/ddns-go/v5/config" "github.com/jeessy2/ddns-go/v5/dns" "github.com/jeessy2/ddns-go/v5/util" + passwordvalidator "github.com/wagslane/go-password-validator" ) var startTime = time.Now().Unix() @@ -31,6 +32,10 @@ func checkAndSave(request *http.Request) string { usernameNew := strings.TrimSpace(request.FormValue("Username")) passwordNew := request.FormValue("Password") + // 国际化 + accept := request.Header.Get("Accept-Language") + conf.Lang = util.InitLogLang(accept) + // 验证安全性后才允许设置保存配置文件: if time.Now().Unix()-startTime > 5*60 { firstTime := err != nil @@ -38,14 +43,14 @@ func checkAndSave(request *http.Request) string { // 首次设置 && 通过外网访问 必需在服务启动的 5 分钟内 if firstTime && (!util.IsPrivateNetwork(request.RemoteAddr) || !util.IsPrivateNetwork(request.Host)) { - return "若通过公网访问,仅允许在ddns-go启动后 5 分钟内完成首次配置" + return util.LogStr("若通过公网访问, 仅允许在ddns-go启动后 5 分钟内完成首次配置") } // 非首次设置 && 从未设置过帐号密码 && 本次设置了帐号或密码 必须在5分钟内 if !firstTime && (conf.Username == "" && conf.Password == "") && (usernameNew != "" || passwordNew != "") { - return "若从未设置过帐号密码,仅允许在ddns-go启动后 5 分钟内设置,请重启ddns-go" + return util.LogStr("若从未设置过帐号密码, 仅允许在ddns-go启动后 5 分钟内设置, 请重启ddns-go") } } @@ -59,7 +64,7 @@ func checkAndSave(request *http.Request) string { // 如启用公网访问,帐号密码不能为空 if !conf.NotAllowWanAccess && (conf.Username == "" || conf.Password == "") { - return "启用外网访问, 必须输入登录用户名/密码" + return util.LogStr("启用外网访问, 必须输入登录用户名/密码") } // 如果密码不为空则检查是否够强, 内/外网要求强度不同 @@ -68,16 +73,16 @@ func checkAndSave(request *http.Request) string { if conf.NotAllowWanAccess { minEntropyBits = 25 } - err = validatePassword(passwordNew, minEntropyBits) + err = passwordvalidator.Validate(passwordNew, minEntropyBits) if err != nil { - return err.Error() + return util.LogStr("密码不安全!尝试使用更长的密码") } } dnsConfFromJS := []dnsConf4JS{} err = json.Unmarshal([]byte(request.FormValue("DnsConf")), &dnsConfFromJS) if err != nil { - return "解析配置失败,请重试" + return "Please refresh the browser and try again" } dnsConfArray := []config.DnsConfig{} empty := dnsConf4JS{} @@ -125,7 +130,7 @@ func checkAndSave(request *http.Request) string { // 修改cmd需要验证:必须设置帐号密码 if (conf.Username == "" && conf.Password == "") && (c.Ipv4.Cmd != dnsConf.Ipv4.Cmd || c.Ipv6.Cmd != dnsConf.Ipv6.Cmd) { - return "修改 \"通过命令获取\" 必须设置帐号密码,请先设置帐号密码" + return util.LogStr("修改 '通过命令获取' 必须设置帐号密码,请先设置帐号密码") } } diff --git a/web/webhookTest.go b/web/webhookTest.go index d7c9524..b1399fc 100755 --- a/web/webhookTest.go +++ b/web/webhookTest.go @@ -1,7 +1,7 @@ package web import ( - "log" + "github.com/jeessy2/ddns-go/v5/util" "net/http" "strings" @@ -38,6 +38,6 @@ func WebhookTest(writer http.ResponseWriter, request *http.Request) { if url != "" { config.ExecWebhook(fakeDomains, fakeConfig) } else { - log.Println("请输入Webhook的URL") + util.Log("请输入Webhook的URL") } } diff --git a/web/writing.html b/web/writing.html index 843a925..58d8ba4 100755 --- a/web/writing.html +++ b/web/writing.html @@ -1,4 +1,4 @@ - + @@ -32,7 +32,7 @@ id="logsBtn" onclick="showHideLogs()" > - 日志 + Logs