Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DDNS support #324

Merged
merged 13 commits into from
Feb 24, 2024
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
15 changes: 15 additions & 0 deletions cmd/dashboard/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"
"errors"
"fmt"
"log"

Expand Down Expand Up @@ -38,6 +39,20 @@ func init() {
initSystem()
}

func secondsToCronString(seconds uint32) (string, error) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

好像没用到了

if seconds > 86400 {
return "", errors.New("时间不能超过24小时")
}

//hours := seconds / 3600
//minutes := (seconds % 3600) / 60
//secondsRemainder := seconds % 60 // 保留剩余的秒数

cronExpr := fmt.Sprintf("%d * * * * *", seconds)

return cronExpr, nil
}

func initSystem() {
// 启动 singleton 包下的所有服务
singleton.LoadSingleton()
Expand Down
18 changes: 18 additions & 0 deletions model/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,18 @@ 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
}
}

// Read 读取配置文件并应用
Expand Down Expand Up @@ -152,6 +164,12 @@ 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"
}

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
}
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]
DarcJC marked this conversation as resolved.
Show resolved Hide resolved
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);
DarcJC marked this conversation as resolved.
Show resolved Hide resolved
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
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
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
checkperiod: 60
DarcJC marked this conversation as resolved.
Show resolved Hide resolved
provider: "webhook"
accessid: ""
accesssecret: ""
webhookmethod: ""
webhookurl: ""
webhookrequestbody: ""
webhookheaders: ""
29 changes: 29 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,33 @@ func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Rece
host := model.PB2Host(r)
singleton.ServerLock.RLock()
defer singleton.ServerLock.RUnlock()
cacheKey := fmt.Sprintf("ddns-created-%d", clientID)
DarcJC marked this conversation as resolved.
Show resolved Hide resolved
_, found := singleton.Cache.Get(cacheKey)
if singleton.Conf.DDNS.Enable &&
singleton.ServerList[clientID].EnableDDNS &&
singleton.ServerList[clientID].Host != nil &&
host.IP != "" &&
(singleton.ServerList[clientID].Host.IP != host.IP || !found) {
//go func() {
naiba marked this conversation as resolved.
Show resolved Hide resolved
serverDomain := singleton.ServerList[clientID].DDNSDomain
provider, err := singleton.GetDDNSProviderFromString(singleton.Conf.DDNS.Provider)
if err == nil {
ipv4, ipv6, _ := utils.SplitIPAddr(host.IP)
if provider.UpdateDomain(&singleton.DDNSDomainConfig{
EnableIPv4: true,
EnableIpv6: true,
FullDomain: serverDomain,
Ipv4Addr: ipv4,
Ipv6Addr: ipv6,
}) {
log.Printf("NEZHA>> 更新域名(%s)DDNS成功", serverDomain)
singleton.Cache.Set(cacheKey, true, 300*time.Second)
} else {
log.Printf("NEZHA>> 更新域名(%s)DDNS失败", serverDomain)
}
}
//}()
}
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