Skip to content

Commit

Permalink
Add DDNS support (#324)
Browse files Browse the repository at this point in the history
* feat: add ddns updater framework

Note: no functionality implemented yet

* feat: add webhook ddns provider

* feat: update dashboard template

* fix: check nil and cron task string

* fix: webhook string formated with unexcepted param

* fix: webhook header split error

* feat: cloudflare ddns provider

* refract: move ddns update trigger into ReportSystemInfo

* lang: update other languages text

* fix: clear codes and logics

* fix: move update ddns to goroutine to avoid blocking

* fix: clear unused codes

* fix: update timestamp to prevent cache
  • Loading branch information
DarcJC authored Feb 24, 2024
1 parent c4b2c47 commit 3b5ee46
Show file tree
Hide file tree
Showing 16 changed files with 439 additions and 6 deletions.
4 changes: 4 additions & 0 deletions cmd/dashboard/controller/member_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,8 @@ type serverForm struct {
Tag string
Note string
HideForGuest string
EnableDDNS string
DDNSDomain string
}

func (ma *memberAPI) addOrEditServer(c *gin.Context) {
Expand All @@ -315,6 +317,8 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) {
s.Tag = sf.Tag
s.Note = sf.Note
s.HideForGuest = sf.HideForGuest == "on"
s.EnableDDNS = sf.EnableDDNS == "on"
s.DDNSDomain = sf.DDNSDomain
if s.ID == 0 {
s.Secret, err = utils.GenerateRandomString(18)
if err == nil {
Expand Down
22 changes: 22 additions & 0 deletions model/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,19 @@ type Config struct {
IgnoredIPNotificationServerIDs map[uint64]bool // [ServerID] -> bool(值为true代表当前ServerID在特定服务器列表内)
MaxTCPPingValue int32
AvgPingCount int

// 动态域名解析更新
DDNS struct {
Enable bool
Provider string
AccessID string
AccessSecret string
WebhookURL string
WebhookMethod string
WebhookRequestBody string
WebhookHeaders string
MaxRetries uint32
}
}

// Read 读取配置文件并应用
Expand Down Expand Up @@ -152,6 +165,15 @@ func (c *Config) Read(path string) error {
if c.AvgPingCount == 0 {
c.AvgPingCount = 2
}
if c.DDNS.Provider == "" {
c.DDNS.Provider = "webhook"
}
if c.DDNS.WebhookMethod == "" {
c.DDNS.WebhookMethod = "POST"
}
if c.DDNS.MaxRetries == 0 {
c.DDNS.MaxRetries = 3
}

c.updateIgnoredIPNotificationID()
return nil
Expand Down
5 changes: 4 additions & 1 deletion model/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ type Server struct {
Note string `json:"-"` // 管理员可见备注
DisplayIndex int // 展示排序,越大越靠前
HideForGuest bool // 对游客隐藏
EnableDDNS bool // 是否启用DDNS 未在配置文件中启用DDNS 或 DDNS检查时间为0时此项无效
DDNSDomain string // DDNS中的前缀 如基础域名为abc.oracle DDNSName为mjj 就会把mjj.abc.oracle解析服务器IP 为空则停用

Host *Host `gorm:"-"`
State *HostState `gorm:"-"`
Expand Down Expand Up @@ -51,5 +53,6 @@ func (s Server) Marshal() template.JS {
tag, _ := utils.Json.Marshal(s.Tag)
note, _ := utils.Json.Marshal(s.Note)
secret, _ := utils.Json.Marshal(s.Secret)
return template.JS(fmt.Sprintf(`{"ID":%d,"Name":%s,"Secret":%s,"DisplayIndex":%d,"Tag":%s,"Note":%s,"HideForGuest": %s}`, s.ID, name, secret, s.DisplayIndex, tag, note, boolToString(s.HideForGuest))) // #nosec
ddnsDomain, _ := utils.Json.Marshal(s.DDNSDomain)
return template.JS(fmt.Sprintf(`{"ID":%d,"Name":%s,"Secret":%s,"DisplayIndex":%d,"Tag":%s,"Note":%s,"HideForGuest": %s,"EnableDDNS": %s,"DDNSDomain": %s}`, s.ID, name, secret, s.DisplayIndex, tag, note, boolToString(s.HideForGuest), boolToString(s.EnableDDNS), ddnsDomain)) // #nosec
}
8 changes: 7 additions & 1 deletion resource/l10n/en-US.toml
Original file line number Diff line number Diff line change
Expand Up @@ -614,4 +614,10 @@ other = "Menu"
other = "Network"

[EnableShowInService]
other = "Enable Show in Service"
other = "Enable Show in Service"

[EnableDDNS]
other = "Enable DDNS"

[DDNSDomain]
other = "DDNS Domain"
8 changes: 7 additions & 1 deletion resource/l10n/es-ES.toml
Original file line number Diff line number Diff line change
Expand Up @@ -614,4 +614,10 @@ other = "Menú"
other = "Red"

[EnableShowInService]
other = "Mostrar en servicio"
other = "Mostrar en servicio"

[EnableDDNS]
other = "Habilitar DDNS"

[DDNSDomain]
other = "Dominio DDNS"
6 changes: 6 additions & 0 deletions resource/l10n/zh-CN.toml
Original file line number Diff line number Diff line change
Expand Up @@ -615,3 +615,9 @@ other = "网络"

[EnableShowInService]
other = "在服务中显示"

[EnableDDNS]
other = "启用DDNS"

[DDNSDomain]
other = "DDNS域名"
8 changes: 7 additions & 1 deletion resource/l10n/zh-TW.toml
Original file line number Diff line number Diff line change
Expand Up @@ -614,4 +614,10 @@ other = "菜單"
other = "網絡"

[EnableShowInService]
other = "在服務中顯示"
other = "在服務中顯示"

[EnableDDNS]
other = "啟用DDNS"

[DDNSDomain]
other = "DDNS網域"
6 changes: 6 additions & 0 deletions resource/static/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ function addOrEditServer(server, conf) {
modal.find("input[name=id]").val(server ? server.ID : null);
modal.find("input[name=name]").val(server ? server.Name : null);
modal.find("input[name=Tag]").val(server ? server.Tag : null);
modal.find("input[name=DDNSDomain]").val(server ? server.DDNSDomain : null);
modal
.find("input[name=DisplayIndex]")
.val(server ? server.DisplayIndex : null);
Expand All @@ -321,6 +322,11 @@ function addOrEditServer(server, conf) {
} else {
modal.find(".ui.hideforguest.checkbox").checkbox("set unchecked");
}
if (server && server.EnableDDNS) {
modal.find(".ui.enableddns.checkbox").checkbox("set checked");
} else {
modal.find(".ui.enableddns.checkbox").checkbox("set unchecked");
}
showFormModal(".server.modal", "#serverForm", "/api/server");
}

Expand Down
2 changes: 1 addition & 1 deletion resource/template/common/footer.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/semantic-ui/2.4.1/semantic.min.js"></script>
<script src="/static/semantic-ui-alerts.min.js"></script>
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/vue/2.6.14/vue.min.js"></script>
<script src="/static/main.js?v20240213"></script>
<script src="/static/main.js?v20240224"></script>
<script>
(function () {
updateLang({{.LANG }});
Expand Down
10 changes: 10 additions & 0 deletions resource/template/component/server.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@
<label>{{tr "HideForGuest"}}</label>
</div>
</div>
<div class="field">
<div class="ui enableddns checkbox">
<input name="EnableDDNS" type="checkbox" tabindex="0" />
<label>{{tr "EnableDDNS"}}</label>
</div>
</div>
<div class="field">
<label>{{tr "DDNSDomain"}}</label>
<input type="text" name="DDNSDomain" placeholder="{{tr "DDNSDomain"}}">
</div>
<div class="field">
<label>{{tr "Note"}}</label>
<textarea name="Note"></textarea>
Expand Down
4 changes: 4 additions & 0 deletions resource/template/dashboard-default/server.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
<th>IP</th>
<th>{{tr "VersionNumber"}}</th>
<th>{{tr "HideForGuest"}}</th>
<th>{{tr "EnableDDNS"}}</th>
<th>{{tr "DDNSDomain"}}</th>
<th>{{tr "Secret"}}</th>
<th>{{tr "OneKeyInstall"}}</th>
<th>{{tr "Note"}}</th>
Expand All @@ -45,6 +47,8 @@
<td>{{$server.Host.IP}}</td>
<td>{{$server.Host.Version}}</td>
<td>{{$server.HideForGuest}}</td>
<td>{{$server.EnableDDNS}}</td>
<td>{{$server.DDNSDomain}}</td>
<td>
<button class="ui icon green mini button" data-clipboard-text="{{$server.Secret}}" data-tooltip="{{tr "ClickToCopy"}}">
<i class="copy icon"></i>
Expand Down
2 changes: 1 addition & 1 deletion resource/template/theme-angel-kanade/footer.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/semantic-ui/2.4.1/semantic.min.js"></script>
<script src="/static/semantic-ui-alerts.min.js"></script>
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/vue/2.6.14/vue.min.js"></script>
<script src="/static/main.js?v20240213"></script>
<script src="/static/main.js?v20240224"></script>
<script>
(function () {
updateLang({{.LANG }});
Expand Down
10 changes: 10 additions & 0 deletions script/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,13 @@ site:
brand: "nz_site_title"
cookiename: "nezha-dashboard" #浏览器 Cookie 字段名,可不改
theme: "default"
ddns:
enable: false
provider: "webhook"
accessid: ""
accesssecret: ""
webhookmethod: ""
webhookurl: ""
webhookrequestbody: ""
webhookheaders: ""
maxretries: 3
32 changes: 32 additions & 0 deletions service/rpc/nezha.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package rpc
import (
"context"
"fmt"
"github.com/naiba/nezha/pkg/utils"
"log"
"time"

"github.com/jinzhu/copier"
Expand Down Expand Up @@ -110,6 +112,36 @@ func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Rece
host := model.PB2Host(r)
singleton.ServerLock.RLock()
defer singleton.ServerLock.RUnlock()

// 检查并更新DDNS
if singleton.Conf.DDNS.Enable &&
singleton.ServerList[clientID].EnableDDNS &&
singleton.ServerList[clientID].Host != nil &&
host.IP != "" &&
singleton.ServerList[clientID].Host.IP != host.IP {

serverDomain := singleton.ServerList[clientID].DDNSDomain
provider, err := singleton.GetDDNSProviderFromString(singleton.Conf.DDNS.Provider)
if err == nil && serverDomain != "" {
ipv4, ipv6, _ := utils.SplitIPAddr(host.IP)
maxRetries := int(singleton.Conf.DDNS.MaxRetries)
config := &singleton.DDNSDomainConfig{
EnableIPv4: true,
EnableIpv6: true,
FullDomain: serverDomain,
Ipv4Addr: ipv4,
Ipv6Addr: ipv6,
}
go singleton.RetryableUpdateDomain(provider, config, maxRetries)

} else {
// 虽然会在启动时panic, 可以断言不会走这个分支, 但是考虑到动态加载配置或者其它情况, 这里输出一下方便检查奇奇怪怪的BUG
log.Printf("NEZHA>> 未找到对应的DDNS提供者(%s), 请前往config.yml检查你的设置\n", singleton.Conf.DDNS.Provider)
}

}

// 发送IP变动通知
if singleton.Conf.EnableIPChangeNotification &&
((singleton.Conf.Cover == model.ConfigCoverAll && !singleton.Conf.IgnoredIPNotificationServerIDs[clientID]) ||
(singleton.Conf.Cover == model.ConfigCoverIgnoreAll && singleton.Conf.IgnoredIPNotificationServerIDs[clientID])) &&
Expand Down
Loading

0 comments on commit 3b5ee46

Please sign in to comment.