├── doc ├── ddnsgo1.png ├── ddnsgo2.png ├── ddnsgo3.png └── ddnsgo4.png ├── luci-app-ddns-go ├── root │ ├── etc │ │ ├── ddns-go │ │ │ ├── localtime │ │ │ └── ddns-go-config.yaml │ │ └── config │ │ │ └── ddns-go │ └── usr │ │ └── share │ │ ├── ddns-go │ │ └── ddns-go-default.yaml │ │ ├── luci │ │ └── menu.d │ │ │ └── luci-app-ddns-go.json │ │ └── rpcd │ │ ├── acl.d │ │ └── luci-app-ddns-go.json │ │ └── ucode │ │ └── luci.ddns-go ├── Makefile ├── po │ ├── templates │ │ └── ddns-go.pot │ └── zh_Hans │ │ └── ddns-go.po └── htdocs │ └── luci-static │ └── resources │ └── view │ └── ddns-go │ ├── log.js │ ├── ddns-go.js │ └── config.js ├── ddns-go ├── file │ ├── luci-ddns-go.uci-default │ ├── ddns-go-call.libexec │ └── ddns-go.init └── Makefile ├── LICENSE ├── .github └── workflows │ └── release.yml ├── README_CN.md └── README.md /doc/ddnsgo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirpdboy/luci-app-ddns-go/HEAD/doc/ddnsgo1.png -------------------------------------------------------------------------------- /doc/ddnsgo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirpdboy/luci-app-ddns-go/HEAD/doc/ddnsgo2.png -------------------------------------------------------------------------------- /doc/ddnsgo3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirpdboy/luci-app-ddns-go/HEAD/doc/ddnsgo3.png -------------------------------------------------------------------------------- /doc/ddnsgo4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirpdboy/luci-app-ddns-go/HEAD/doc/ddnsgo4.png -------------------------------------------------------------------------------- /luci-app-ddns-go/root/etc/ddns-go/localtime: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirpdboy/luci-app-ddns-go/HEAD/luci-app-ddns-go/root/etc/ddns-go/localtime -------------------------------------------------------------------------------- /ddns-go/file/luci-ddns-go.uci-default: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | [ -s "/etc/ddns-go/localtime" ] && mv -f /etc/ddns-go/localtime /etc/localtime 4 | /etc/init.d/ddns-go enable 5 | /etc/init.d/ddns-go start 6 | rm -f /tmp/luci* 7 | exit 0 8 | -------------------------------------------------------------------------------- /luci-app-ddns-go/root/etc/config/ddns-go: -------------------------------------------------------------------------------- 1 | config basic 'config' 2 | option enabled '0' 3 | option logger '1' 4 | option port '9876' 5 | option time '300' 6 | option ctimes '5' 7 | option skipverify '0' 8 | option delay '0' 9 | option dns '223.5.5.5' 10 | -------------------------------------------------------------------------------- /luci-app-ddns-go/root/etc/ddns-go/ddns-go-config.yaml: -------------------------------------------------------------------------------- 1 | ipv4: 2 | enable: true 3 | gettype: url 4 | url: https://myip4.ipip.net, https://ddns.oray.com/checkip, https://ip.3322.net 5 | netinterface: br-lan 6 | domains: 7 | - "" 8 | ipv6: 9 | enable: false 10 | gettype: url 11 | url: https://myip6.ipip.net, https://speed.neu6.edu.cn/getIP.php, https://v6.ident.me 12 | netinterface: "" 13 | ipv6reg: "" 14 | domains: 15 | - "" 16 | dns: 17 | name: alidns 18 | id: "" 19 | secret: "" 20 | user: 21 | username: "" 22 | password: "" 23 | webhook: 24 | webhookurl: "" 25 | webhookrequestbody: "" 26 | notallowwanaccess: true 27 | ttl: "" 28 | -------------------------------------------------------------------------------- /luci-app-ddns-go/root/usr/share/ddns-go/ddns-go-default.yaml: -------------------------------------------------------------------------------- 1 | ipv4: 2 | enable: true 3 | gettype: url 4 | url: https://4.ipw.cn,https://myip4.ipip.net,https://ddns.oray.com/checkip,https://ip.3322.net 5 | netinterface: br-lan 6 | domains: 7 | - "" 8 | ipv6: 9 | enable: false 10 | gettype: url 11 | url: https://6.ipw.cn, https://speed.neu6.edu.cn/getIP.php, https://v6.ident.me 12 | netinterface: "" 13 | ipv6reg: "" 14 | domains: 15 | - "" 16 | dns: 17 | name: alidns 18 | id: "" 19 | secret: "" 20 | user: 21 | username: "" 22 | password: "" 23 | webhook: 24 | webhookurl: "" 25 | webhookrequestbody: "" 26 | notallowwanaccess: true 27 | ttl: "" 28 | -------------------------------------------------------------------------------- /luci-app-ddns-go/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-3.0-only 2 | # 3 | # Copyright (C) 2021-2025 sirpdboy 4 | # https://github.com/sirpdboy/luci-app-ddns-go 5 | # This is free software, licensed under the Apache License, Version 2.0 . 6 | # 7 | include $(TOPDIR)/rules.mk 8 | 9 | PKG_NAME:=luci-app-ddns-go 10 | PKG_VERSION:=1.6.4 11 | PKG_RELEASE:=20251106 12 | 13 | PKG_MAINTAINER:=sirpdboy 14 | PKG_CONFIG_DEPENDS:= 15 | 16 | LUCI_TITLE:=LuCI Support for Dynamic ddns-go Client 17 | LUCI_DEPENDS:=+ddns-go 18 | LUCI_PKGARCH:=all 19 | 20 | define Package/$(PKG_NAME)/conffiles 21 | /etc/config/ddns-go 22 | /etc/ddns-go/ddns-go-config.yaml 23 | endef 24 | 25 | include $(TOPDIR)/feeds/luci/luci.mk 26 | 27 | # call BuildPackage - OpenWrt buildroot signature 28 | -------------------------------------------------------------------------------- /ddns-go/file/ddns-go-call.libexec: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (C) 2023-2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-ddns-go 4 | # 5 | 6 | logfile="/var/log/ddns-go.log" 7 | lang=$(uci get luci.main.lang 2>/dev/null) 8 | if [ -z "$lang" ] || [[ "$lang" == "auto" ]]; then 9 | lang=$(echo "${LANG:-${LANGUAGE:-${LC_ALL:-${LC_MESSAGES:-zh_cn}}}}" | awk -F'[ .@]' '{print tolower($1)}' | sed 's/-/_/' 2>/dev/null) 10 | fi 11 | 12 | translate() { 13 | # 处理特殊字符 14 | local lua_script=$(cat <"${logfile}" 26 | 27 | elif [ "$1" == "child" ]; then 28 | shift 29 | command_name=$1 30 | shift 31 | "$command_name" "$@" 32 | fi 33 | -------------------------------------------------------------------------------- /luci-app-ddns-go/root/usr/share/luci/menu.d/luci-app-ddns-go.json: -------------------------------------------------------------------------------- 1 | { 2 | "admin/services/ddns-go": { 3 | "title": "DDNS-GO", 4 | "order": 58, 5 | "action": { 6 | "type": "firstchild" 7 | }, 8 | "depends": { 9 | "acl": [ "luci-app-ddns-go" ], 10 | "uci": { "ddns-go": true } 11 | } 12 | }, 13 | 14 | "admin/services/ddns-go/config": { 15 | "title": "Base Setting", 16 | "order": 10, 17 | "action": { 18 | "type": "view", 19 | "path": "ddns-go/config" 20 | } 21 | }, 22 | 23 | "admin/services/ddns-go/ddns-go": { 24 | "title": "DDNS-GO Control panel", 25 | "order": 20, 26 | "action": { 27 | "type": "view", 28 | "path": "ddns-go/ddns-go" 29 | } 30 | }, 31 | 32 | "admin/services/ddns-go/log": { 33 | "title": "Log", 34 | "order": 30, 35 | "action": { 36 | "type": "view", 37 | "path": "ddns-go/log" 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /luci-app-ddns-go/root/usr/share/rpcd/acl.d/luci-app-ddns-go.json: -------------------------------------------------------------------------------- 1 | { 2 | "luci-app-ddns-go": { 3 | "description": "Grant UCI access for luci-app-ddns-go", 4 | "read": { 5 | "uci": [ "ddns-go" ], 6 | "file": { 7 | "/etc/init.d/ddns-go": [ "exec" ], 8 | "/usr/libexec/ddns-go-call": [ "exec" ], 9 | "/usr/share/rpcd/ucode/luci.ddns-go": [ "exec" ], 10 | "/bin/pidof": [ "exec" ], 11 | "/bin/ps": [ "exec" ], 12 | "/bin/ash": [ "exec" ], 13 | "/etc/ddns-go/ddns-go-config.yaml": [ "read" ], 14 | "/var/log/ddns-go.log": [ "read" ] 15 | }, 16 | "ubus": { 17 | "rc": [ "*" ], 18 | "service": ["list"], 19 | "luci.ddns-go": [ "*" ] 20 | } 21 | }, 22 | "write": { 23 | "ubus": { 24 | "luci.ddns-go": [ "*" ] 25 | }, 26 | "file": { 27 | "/etc/ddns-go/ddns-go-config.yaml": ["write"] 28 | }, 29 | "uci": ["ddns-go"] 30 | } 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 sirpdboy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ddns-go/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-3.0-only 2 | # 3 | # Copyright (C) 2021-2025 sirpdboy 4 | # 5 | # This is free software, licensed under the Apache License, Version 2.0 . 6 | # 7 | include $(TOPDIR)/rules.mk 8 | 9 | PKG_NAME:=ddns-go 10 | PKG_VERSION:=6.12.5 11 | PKG_RELEASE:=31 12 | 13 | PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz 14 | PKG_SOURCE_URL:=https://codeload.github.com/jeessy2/ddns-go/tar.gz/v$(PKG_VERSION)? 15 | PKG_HASH:=skip 16 | 17 | 18 | PKG_LICENSE:=AGPL-3.0-only 19 | PKG_LICENSE_FILES:=LICENSE 20 | PKG_MAINTAINER:=herboy2008 21 | 22 | PKG_BUILD_DEPENDS:=golang/host 23 | PKG_BUILD_PARALLEL:=1 24 | PKG_BUILD_FLAGS:=no-mips16 25 | 26 | GO_PKG:=github.com/jeessy2/ddns-go/v6 27 | GO_PKG_LDFLAGS_X:=main.version=$(PKG_VERSION) 28 | 29 | include $(INCLUDE_DIR)/package.mk 30 | include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk 31 | 32 | define Package/ddns-go 33 | SECTION:=net 34 | CATEGORY:=Network 35 | SUBMENU:=Web Servers/Proxies 36 | TITLE:=A Linux web GUI client of ddns-go 37 | URL:=https://github.com/jeessy2/ddns-go 38 | DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle 39 | USERID:=ddns-go:ddns-go 40 | endef 41 | 42 | define Package/ddns-go/description 43 | ddns-go is a automatically obtain your public IPv4 or IPv6 address and resolve it to the corresponding domain name service, 44 | support Alidns Dnspod Cloudflare Hicloud Callback Baiducloud porkbun GoDaddy Google Domains. 45 | endef 46 | 47 | define Package/ddns-go/install 48 | $(call GoPackage/Package/Install/Bin,$(1)) 49 | 50 | $(INSTALL_DIR) $(1)/etc/init.d 51 | $(INSTALL_BIN) $(CURDIR)/file/ddns-go.init $(1)/etc/init.d/ddns-go 52 | $(INSTALL_DIR) $(1)/usr/libexec 53 | $(INSTALL_BIN) $(CURDIR)/file/ddns-go-call.libexec $(1)/usr/libexec/ddns-go-call 54 | $(INSTALL_DIR) $(1)/etc/uci-defaults 55 | $(INSTALL_BIN) $(CURDIR)/file/luci-ddns-go.uci-default $(1)/etc/uci-defaults/luci-ddns-go 56 | endef 57 | 58 | $(eval $(call GoBinPackage,ddns-go)) 59 | $(eval $(call BuildPackage,ddns-go)) 60 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build packages 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | build: 10 | name: Build ${{ matrix.arch }} 11 | runs-on: ubuntu-latest 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | arch: 16 | - aarch64_cortex-a53 17 | - aarch64_cortex-a72 18 | - aarch64_generic 19 | - arm_arm1176jzf-s_vfp 20 | - arm_arm926ej-s 21 | - arm_cortex-a15_neon-vfpv4 22 | - arm_cortex-a5_vfpv4 23 | - arm_cortex-a7 24 | - arm_cortex-a7_neon-vfpv4 25 | - arm_cortex-a8_vfpv3 26 | - arm_cortex-a9 27 | - arm_cortex-a9_neon 28 | - arm_cortex-a9_vfpv3-d16 29 | - arm_fa526 30 | - arm_mpcore 31 | - arm_xscale 32 | - i386_pentium-mmx 33 | - i386_pentium4 34 | - mips64_octeonplus 35 | - mips_24kc 36 | - mips_4kec 37 | - mips_mips32 38 | - mipsel_24kc 39 | - mipsel_24kc_24kf 40 | - mipsel_74kc 41 | - mipsel_mips32 42 | - x86_64 43 | 44 | steps: 45 | - uses: actions/checkout@v4 46 | with: 47 | fetch-depth: 0 48 | 49 | - name: Building packages 50 | uses: sbwml/openwrt-gh-action-sdk@main 51 | env: 52 | ARCH: ${{ matrix.arch }}-openwrt-24.10 53 | FEEDNAME: packages_ci 54 | PACKAGES: luci-app-ddns-go 55 | NO_REFRESH_CHECK: true 56 | 57 | - name: Upload artifacts 58 | uses: actions/upload-artifact@v4 59 | with: 60 | name: ${{ matrix.arch }} 61 | path: bin/packages/${{ matrix.arch }}/packages_ci/*.ipk 62 | 63 | - name: Upload packages 64 | uses: ncipollo/release-action@v1 65 | with: 66 | name: ${{ github.ref_name }} 67 | token: ${{ secrets.GITHUB_TOKEN }} 68 | allowUpdates: true 69 | replacesArtifacts: true 70 | artifacts: "bin/packages/${{ matrix.arch }}/packages_ci/*.ipk" 71 | -------------------------------------------------------------------------------- /luci-app-ddns-go/po/templates/ddns-go.pot: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | 4 | msgid "DDNS-GO" 5 | msgstr "" 6 | 7 | msgid "DDNS-GO automatically obtains your public IPv4 or IPv6 address and resolves it to the corresponding domain name service." 8 | msgstr "" 9 | 10 | msgid "Base Setting" 11 | msgstr "" 12 | 13 | msgid "Status check error:" 14 | msgstr "" 15 | 16 | msgid "Checking status..." 17 | msgstr "" 18 | 19 | msgid "DDNS-GO Control panel" 20 | msgstr "" 21 | 22 | msgid "Open Web Interface" 23 | msgstr "" 24 | 25 | msgid "Log" 26 | msgstr "" 27 | 28 | msgid "Clear Logs..." 29 | msgstr "" 30 | 31 | msgid "Logs cleared successfully!" 32 | msgstr "" 33 | 34 | msgid "Clear Logs" 35 | msgstr "" 36 | 37 | msgid "NOT RUNNING" 38 | msgstr "" 39 | 40 | msgid "RUNNING" 41 | msgstr "" 42 | 43 | msgid "DDNS-GO Service Not Running" 44 | msgstr "" 45 | 46 | msgid "DDNS-GO Web Interface Disabled" 47 | msgstr "" 48 | 49 | msgid "Open Web Interface" 50 | msgstr "" 51 | 52 | msgid "Set the DDNS-TO access port" 53 | msgstr "" 54 | 55 | msgid "Collecting data..." 56 | msgstr "" 57 | 58 | msgid "update interval" 59 | msgstr "" 60 | 61 | msgid "Skip verifying certificates" 62 | msgstr "" 63 | 64 | msgid "Compare with service provider N times intervals" 65 | msgstr "" 66 | 67 | msgid "Specify DNS resolution server" 68 | msgstr "" 69 | 70 | msgid "Do not start web services" 71 | msgstr "" 72 | 73 | msgid "Delayed Start (seconds)" 74 | msgstr "" 75 | 76 | msgid "Update kernel" 77 | msgstr "" 78 | 79 | msgid "Check and update DDNS-Go to the latest version" 80 | msgstr "" 81 | 82 | msgid "Check Update" 83 | msgstr "" 84 | 85 | msgid "Updating, please wait..." 86 | msgstr "" 87 | 88 | msgid "Update failed" 89 | msgstr "" 90 | 91 | msgid "Update status unknown" 92 | msgstr "" 93 | 94 | msgid "Reset account password" 95 | msgstr "" 96 | 97 | msgid "ResetPassword" 98 | msgstr "" 99 | 100 | msgid "SUCCESS:" 101 | msgstr "" 102 | 103 | msgid "Password reset successfully to admin12345" 104 | msgstr "" 105 | 106 | msgid "Password Reset Successful" 107 | msgstr "" 108 | 109 | msgid "User password has been reset to: admin12345" 110 | msgstr "" 111 | 112 | msgid "You need to restart DDNS-Go service for the changes to take effect." 113 | msgstr "" 114 | 115 | msgid "Restart Service Now" 116 | msgstr "" 117 | 118 | msgid "Restart Later" 119 | msgstr "" 120 | 121 | msgid "" 122 | msgstr "" 123 | -------------------------------------------------------------------------------- /luci-app-ddns-go/po/zh_Hans/ddns-go.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "Content-Type: text/plain; charset=UTF-8" 3 | 4 | msgid "DDNS-GO" 5 | msgstr "DDNS-GO" 6 | 7 | msgid "DDNS-GO automatically obtains your public IPv4 or IPv6 address and resolves it to the corresponding domain name service." 8 | msgstr "DDNS-GO自动获得你的公网 IPv4 或 IPv6 地址,并解析到对应的域名服务." 9 | 10 | msgid "Base Setting" 11 | msgstr "基本设置" 12 | 13 | msgid "Status check error:" 14 | msgstr "状态检查错误:" 15 | 16 | msgid "Checking status..." 17 | msgstr "检查状态..." 18 | 19 | msgid "DDNS-GO Control panel" 20 | msgstr "DDNS-GO操作台" 21 | 22 | msgid "Open Web Interface" 23 | msgstr "打开Web界面" 24 | 25 | msgid "Log" 26 | msgstr "日志" 27 | 28 | msgid "Clear Logs..." 29 | msgstr "清除日志..." 30 | 31 | msgid "Logs cleared successfully!" 32 | msgstr "日志清除成功!" 33 | 34 | msgid "Clear Logs" 35 | msgstr "清除日志" 36 | 37 | msgid "NOT RUNNING" 38 | msgstr "未运行" 39 | 40 | msgid "RUNNING" 41 | msgstr "运行中" 42 | 43 | msgid "DDNS-GO Service Not Running" 44 | msgstr "DDNS-GO服务未启用" 45 | 46 | msgid "DDNS-GO Web Interface Disabled" 47 | msgstr "DDNS-GO WEB服务禁用" 48 | 49 | msgid "Open Web Interface" 50 | msgstr "打开Web界面" 51 | 52 | msgid "Set the DDNS-TO access port" 53 | msgstr "设置访问端口" 54 | 55 | msgid "Collecting data..." 56 | msgstr "收集数据..." 57 | 58 | msgid "update interval" 59 | msgstr "更新间隔" 60 | 61 | msgid "Skip verifying certificates" 62 | msgstr "跳过验证证书" 63 | 64 | msgid "Compare with service provider N times intervals" 65 | msgstr "间隔N次与服务商比对" 66 | 67 | msgid "Specify DNS resolution server" 68 | msgstr "指定DNS解析服务器" 69 | 70 | msgid "Do not start web services" 71 | msgstr "不启动web服务" 72 | 73 | msgid "Delayed Start (seconds)" 74 | msgstr "开机延时启动(秒)" 75 | 76 | msgid "Update kernel" 77 | msgstr "更新内核" 78 | 79 | msgid "Check and update DDNS-Go to the latest version" 80 | msgstr "更新DDNS-Go到最新版本" 81 | 82 | msgid "Check Update" 83 | msgstr "检查更新" 84 | 85 | msgid "Updating, please wait..." 86 | msgstr "更新中,请稍等..." 87 | 88 | msgid "Update failed" 89 | msgstr "更新失败" 90 | 91 | msgid "Update status unknown" 92 | msgstr "更新状态未知" 93 | 94 | msgid "Reset account password" 95 | msgstr "重置登录密码" 96 | 97 | msgid "ResetPassword" 98 | msgstr "重置密码" 99 | 100 | msgid "SUCCESS:" 101 | msgstr "完成执行:" 102 | 103 | msgid "Password reset successfully to admin12345" 104 | msgstr "成功重置密码admin12345" 105 | 106 | msgid "Password Reset Successful" 107 | msgstr "重置密码成功" 108 | 109 | msgid "User password has been reset to: admin12345" 110 | msgstr "用户密码重置为admin12345" 111 | 112 | msgid "You need to restart DDNS-Go service for the changes to take effect." 113 | msgstr "需要重启DDNS-GO服务更改才生效" 114 | 115 | msgid "Restart Service Now" 116 | msgstr "立刻重启服务" 117 | 118 | msgid "Restart Later" 119 | msgstr "稍后重启" 120 | 121 | msgid "Due to browser security policies, the DDNS-GO interface https cannot be embedded directly." 122 | msgstr "由于浏览器安全策略,DDNS-GO接口https不能直接嵌入。" 123 | -------------------------------------------------------------------------------- /ddns-go/file/ddns-go.init: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | # 3 | # Copyright (C) 2021-2023 sirpdboy https://github.com/sirpdboy/luci-app-ddns-go 4 | # 5 | # This file is part of ddns-go . 6 | # 7 | # This is free software, licensed under the Apache License, Version 2.0 . 8 | # 9 | 10 | 11 | START=99 12 | USE_PROCD=1 13 | NAME=ddns-go 14 | PROG=/usr/bin/ddns-go 15 | CONFDIR=/etc/ddns-go 16 | CONF=$CONFDIR/ddns-go-config.yaml 17 | 18 | get_tz() 19 | { 20 | SET_TZ="" 21 | 22 | [ -e "/etc/localtime" ] && return 23 | 24 | for tzfile in /etc/TZ /var/etc/TZ 25 | do 26 | [ -e "$tzfile" ] || continue 27 | tz="$(cat $tzfile 2>/dev/null)" 28 | done 29 | 30 | [ -z "$tz" ] && return 31 | 32 | SET_TZ=$tz 33 | } 34 | 35 | init_yaml(){ 36 | [ -d $CONFDIR ] || mkdir -p $CONFDIR 2>/dev/null 37 | cat /usr/share/ddns-go/ddns-go-default.yaml > $CONF 38 | } 39 | 40 | xappend() { param="$param $1"; } 41 | 42 | append_parm() { 43 | local section="$1" 44 | local option="$2" 45 | local switch="$3" 46 | local default="$4" 47 | local _tmp 48 | config_get _tmp "$section" "$option" "$default" 49 | [[ -z "$_tmp" ]] && return 50 | [[ x$default = x && x$_tmp = x0 ]] && return 51 | [[ x$default = x && x$_tmp = x1 ]] && xappend "$switch" || xappend "$switch $_tmp" 52 | } 53 | 54 | start_instance() { 55 | local cfg="$1" param 56 | config_get_bool enabled $cfg enabled 1 57 | config_get delay $cfg delay 0 58 | [[ x$enabled = x1 ]] || return 1 59 | [[ -s ${CONF} ]] || init_yaml 60 | [ $(awk -F. '{print $1}' /proc/uptime) -lt "120" ] && sleep $delay 61 | 62 | config_get_bool logger $cfg logger 1 63 | config_get port $cfg port '9876' 64 | append_parm "$cfg" 'time' '-f' '300' 65 | append_parm "$cfg" 'ctimes' '-cacheTimes' '5' 66 | append_parm "$cfg" 'dns' '-dns' '223.5.5.5' 67 | append_parm "$cfg" 'noweb' '-noweb' 68 | append_parm "$cfg" 'skipverify' '-skipVerify' 69 | procd_open_instance 70 | get_tz 71 | [[ -z "$SET_TZ" ]] || procd_set_param env TZ="$SET_TZ" 72 | procd_set_param command /bin/sh -c " 73 | ${PROG} -l :$port -c \"$CONF\" ${param} >> /var/log/ddns-go.log 2>&1 & 74 | DDNS_GO_PID=\$! 75 | { 76 | while true; do 77 | LOG_SIZE=\$(ls -l /var/log/ddns-go.log | awk '{print int(\$5/1024)}') 78 | if [ \$LOG_SIZE -gt 100 ]; then 79 | tail -n 100 /var/log/ddns-go.log > /var/log/ddns-go.log.tmp 80 | mv /var/log/ddns-go.log.tmp /var/log/ddns-go.log 81 | fi 82 | sleep 3600 83 | done 84 | } & 85 | LOG_MANAGEMENT_PID=\$! 86 | trap \"kill -TERM \$DDNS_GO_PID; kill -TERM \$LOG_MANAGEMENT_PID; exit\" SIGINT SIGTERM EXIT 87 | wait \$DDNS_GO_PID 88 | " 89 | [[ "x$logger" == x1 ]] && procd_set_param stderr 1 90 | procd_set_param respawn 91 | procd_close_instance 92 | } 93 | 94 | start_service() { 95 | config_load 'ddns-go' 96 | config_foreach start_instance 'basic' 97 | } 98 | 99 | service_triggers() { 100 | procd_add_reload_trigger "ddns-go" 101 | } 102 | -------------------------------------------------------------------------------- /luci-app-ddns-go/htdocs/luci-static/resources/view/ddns-go/log.js: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021-2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-ddns-go */ 2 | 'use strict'; 3 | 'require dom'; 4 | 'require fs'; 5 | 'require poll'; 6 | 'require uci'; 7 | 'require view'; 8 | 'require form'; 9 | 10 | return view.extend({ 11 | render: function () { 12 | var css = ` 13 | /* 日志框文本区域 */ 14 | #log_textarea pre { 15 | padding: 10px; /* 内边距 */ 16 | border-bottom: 1px solid #ddd; /* 边框颜色 */ 17 | font-size: small; 18 | line-height: 1.3; /* 行高 */ 19 | white-space: pre-wrap; 20 | word-wrap: break-word; 21 | overflow-y: auto; 22 | } 23 | /* 5s 自动刷新文字 */ 24 | .cbi-section small { 25 | margin-left: 1rem; 26 | font-size: small; 27 | color: #666; /* 深灰色文字 */ 28 | } 29 | `; 30 | 31 | var log_textarea = E('div', { 'id': 'log_textarea' }, 32 | E('img', { 33 | 'src': L.resource(['icons/loading.gif']), 34 | 'alt': _('Loading...'), 35 | 'style': 'vertical-align:middle' 36 | }, _('Collecting data ...')) 37 | ); 38 | 39 | var log_path = '/var/log/ddns-go.log'; 40 | var lastLogContent = ''; 41 | 42 | var clear_log_button = E('div', {}, [ 43 | E('button', { 44 | 'class': 'cbi-button cbi-button-remove', 45 | 'click': function (ev) { 46 | ev.preventDefault(); 47 | var button = ev.target; 48 | button.disabled = true; 49 | button.textContent = _('Clear Logs...'); 50 | fs.exec_direct('/usr/libexec/ddns-go-call', ['clear_log']) 51 | .then(function () { 52 | button.textContent = _('Logs cleared successfully!'); 53 | button.disabled = false; 54 | button.textContent = _('Clear Logs'); 55 | // 立即刷新日志显示框 56 | var log = E('pre', { 'wrap': 'pre' }, [_('Log is clean.')]); 57 | dom.content(log_textarea, log); 58 | lastLogContent = ''; 59 | }) 60 | .catch(function () { 61 | button.textContent = _('Failed to clear log.'); 62 | button.disabled = false; 63 | button.textContent = _('Clear Logs'); 64 | }); 65 | } 66 | }, _('Clear Logs')) 67 | ]); 68 | 69 | poll.add(L.bind(function () { 70 | return fs.read_direct(log_path, 'text') 71 | .then(function (res) { 72 | var newContent = res.trim() || _('Log is clean.'); 73 | 74 | if (newContent !== lastLogContent) { 75 | var log = E('pre', { 'wrap': 'pre' }, [newContent]); 76 | dom.content(log_textarea, log); 77 | log.scrollTop = log.scrollHeight; 78 | lastLogContent = newContent; 79 | } 80 | }).catch(function (err) { 81 | var log; 82 | if (err.toString().includes('NotFoundError')) { 83 | log = E('pre', { 'wrap': 'pre' }, [_('Log file does not exist.')]); 84 | } else { 85 | log = E('pre', { 'wrap': 'pre' }, [_('Unknown error: %s').format(err)]); 86 | } 87 | dom.content(log_textarea, log); 88 | }); 89 | })); 90 | 91 | return E('div', { 'class': 'cbi-map' }, [ 92 | E('style', [css]), 93 | E('div', { 'class': 'cbi-section' }, [ 94 | clear_log_button, 95 | log_textarea, 96 | E('small', {}, _('Refresh every 5 seconds.').format(L.env.pollinterval)), 97 | E('div', { 'class': 'cbi-section-actions cbi-section-actions-right' }) 98 | ]), 99 | E('div', { 'style': 'text-align: right; font-style: italic;' }, [ 100 | E('span', {}, [ 101 | _('© github '), 102 | E('a', { 103 | 'href': 'https://github.com/sirpdboy', 104 | 'target': '_blank', 105 | 'style': 'text-decoration: none;' 106 | }, 'by sirpdboy') 107 | ]) 108 | ]) 109 | 110 | 111 | ]); 112 | } 113 | 114 | //handleSaveApply: null, 115 | //handleSave: null, 116 | //handleReset: null 117 | }); 118 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | ![hello](https://views.whatilearened.today/views/github/sirpdboy/deplives.svg) [![](https://img.shields.io/badge/TG群-点击加入-FFFFFF.svg)](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ) 2 | 3 |

4 |
DDNS GO
5 |

6 | 7 |

8 | 9 | 10 | 11 | 12 |

13 | 14 | [中文] | [English](README.md) 15 | 16 | ![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明1.jpg) 17 | 18 | 请 **认真阅读完毕** 本页面,本页面包含注意事项和如何使用。 19 | 20 | ## 功能说明: 21 | 22 | ### ddns-go动态域名插件 23 | #### 自动获得你的公网 IPv4 或 IPv6 地址,并解析到对应的域名服务。 24 | 25 | 26 | 27 | - [ddns-go](#ddns-go) 28 | - [版本](#版本说明) 29 | - [特性](#特性) 30 | - [使用方法](#使用方法) 31 | - [说明](#说明) 32 | - [界面](#界面) 33 | - [捐助](#捐助) 34 | 35 | 36 | 37 | ## 版本说明 38 | 39 | ## v2025.10.25 luci-app-ddns-go 1.6.3 40 | - 1.增加版本显示 41 | - 2.增加在线更新 42 | - 3.增加重置用户密码功能 43 | 44 | ## 2025.5.14 luci-app-ddns-go 1.5.2 45 | ## 2025.5.12 luci-app-ddns-go 1.5.1 46 | - 更新日志: 47 | - 适配最新OPENWRT24.10,JS版本。老版18.06用LUA分支。 48 | 49 | 50 | ## 特性 51 | 52 | - 支持Mac、Windows、Linux系统,支持ARM、x86架构 53 | - 支持的域名服务商 `Alidns(阿里云)` `Dnspod(腾讯云)` `Cloudflare` `华为云` `Callback` `百度云` `porkbun` `GoDaddy` 54 | - 支持接口/网卡获取IP 55 | - 支持以服务的方式运行 56 | - 默认间隔5分钟同步一次 57 | - 支持多个域名同时解析,公司必备 58 | - 支持多级域名 59 | - 网页中配置,简单又方便,可设置 `登录用户名和密码` / `禁止从公网访问` 60 | - 网页中方便快速查看最近50条日志,不需要跑docker中查看 61 | - 支持webhook通知 62 | - 支持TTL 63 | - 支持部分dns服务商传递自定义参数,实现地域解析等功能 64 | 65 | ## 使用方法 66 | 67 | - 将luci-app-ddns-go添加至 LEDE/OpenWRT 源码的方法。 68 | 69 | ### 下载源码方法: 70 | 71 | ```Brach 72 | 73 | # 下载源码 74 | 75 | git clone https://github.com/sirpdboy/luci-app-ddns-go.git package/ddns-go 76 | make menuconfig 77 | 78 | ``` 79 | ### 配置菜单 80 | 81 | ```Brach 82 | make menuconfig 83 | # 找到 LuCI -> Applications, 选择 luci-app-ddns-go, 保存后退出。 84 | ``` 85 | 86 | ### 编译 87 | 88 | ```Brach 89 | # 编译固件 90 | make package/ddns-go/luci-app-ddns-go/compile V=s 91 | ``` 92 | 93 | ## 说明 94 | 95 | - 源码来源:https://github.com/sirpdboy/luci-app-ddns-go 96 | - 源码来源:https://github.com/jeessy2/ddns-go.git 97 | - 你可以随意使用其中的源码,但请注明出处。 98 | 99 | 100 | ## 使用与授权相关说明 101 | 102 | - 本人开源的所有源码,任何引用需注明本处出处,如需修改二次发布必告之本人,未经许可不得做于任何商用用途。 103 | 104 | 105 | # My other project 106 | 107 | - 路由安全看门狗 :https://github.com/sirpdboy/luci-app-watchdog 108 | - 网络速度测试 :https://github.com/sirpdboy/luci-app-netspeedtest 109 | - 计划任务插件(原定时设置) : https://github.com/sirpdboy/luci-app-taskplan 110 | - 关机功能插件 : https://github.com/sirpdboy/luci-app-poweroffdevice 111 | - opentopd主题 : https://github.com/sirpdboy/luci-theme-opentopd 112 | - kucat酷猫主题: https://github.com/sirpdboy/luci-theme-kucat 113 | - kucat酷猫主题设置工具: https://github.com/sirpdboy/luci-app-kucat-config 114 | - NFT版上网时间控制插件: https://github.com/sirpdboy/luci-app-timecontrol 115 | - 家长控制: https://github.com/sirpdboy/luci-theme-parentcontrol 116 | - 定时限速: https://github.com/sirpdboy/luci-app-eqosplus 117 | - 系统高级设置 : https://github.com/sirpdboy/luci-app-advanced 118 | - ddns-go动态域名: https://github.com/sirpdboy/luci-app-ddns-go 119 | - 进阶设置(系统高级设置+主题设置kucat/agron/opentopd): https://github.com/sirpdboy/luci-app-advancedplus 120 | - 网络设置向导: https://github.com/sirpdboy/luci-app-netwizard 121 | - 一键分区扩容: https://github.com/sirpdboy/luci-app-partexp 122 | - lukcy大吉: https://github.com/sirpdboy/luci-app-lukcy 123 | 124 | ## 捐助 125 | 126 | ![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明3.jpg) 127 | 128 | | 图飞了 | 图飞了 | 129 | | :-----------------: | :-------------: | 130 | |![xm1](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/支付宝.png) | ![xm1](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/微信.png) | 131 | 132 | 133 | 图飞了 134 | 135 | 136 | ![](https://visitor-badge-deno.deno.dev/sirpdboy.sirpdboy.svg) [![](https://img.shields.io/badge/TG群-点击加入-FFFFFF.svg)](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ) 137 | -------------------------------------------------------------------------------- /luci-app-ddns-go/root/usr/share/rpcd/ucode/luci.ddns-go: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ucode 2 | /* 3 | * SPDX-License-Identifier: GPL-2.0-only 4 | * 5 | * Copyright (C) 2021-2025 sirpdboy https://github.com/sirpdboy/luci-app-ddns-go 6 | */ 7 | 8 | 'use strict'; 9 | 10 | import { access, error, lstat, popen, readfile, writefile } from 'fs'; 11 | 12 | /* Kanged from ucode/luci */ 13 | function shellquote(s) { 14 | return `'${replace(s, "'", "'\\''")}'`; 15 | } 16 | function get_current_version() { 17 | if (!access('/usr/bin/ddns-go')) 18 | return null; 19 | 20 | const fd = popen('/usr/bin/ddns-go -v'); 21 | if (fd) { 22 | let version_output = fd.read('all'); 23 | fd.close(); 24 | 25 | if (!version_output || length(version_output) === 0) 26 | return null; 27 | 28 | try { 29 | // 去除开头的 'v' 字符 30 | version_output = replace(trim(version_output), /^v/, ''); 31 | return version_output; 32 | } catch(e) { 33 | return null; 34 | } 35 | } 36 | return null; 37 | } 38 | 39 | const methods = { 40 | get_ver: { 41 | call: function() { 42 | let current_version = get_current_version(); 43 | if (!current_version) 44 | return { ver: {}, error: 'ddns-go not found or version check failed' }; 45 | 46 | return { ver: { version: current_version } }; 47 | } 48 | }, 49 | 50 | last_update: { 51 | call: function() { 52 | if (!access('/usr/bin/ddns-go')) 53 | return { update: {}, error: 'ddns-go not found' }; 54 | let version_before = get_current_version(); 55 | 56 | const fd = popen('/usr/bin/ddns-go -u'); 57 | if (fd) { 58 | let output = fd.read('all'); 59 | fd.close(); 60 | 61 | if (!output || length(output) === 0) 62 | return { update: {}, error: 'empty response' }; 63 | 64 | try { 65 | output = trim(output); 66 | let update_info = { 67 | raw_output: output, 68 | version_before: version_before, 69 | version_after: null, 70 | has_update: false, 71 | update_successful: false, 72 | current_version: '', 73 | latest_version: '', 74 | status: 'unknown', 75 | message: output 76 | }; 77 | 78 | update_info.version_after = get_current_version(); 79 | 80 | if (version_before && update_info.version_after && version_before !== update_info.version_after) { 81 | update_info.update_successful = true; 82 | update_info.has_update = false; // 已经更新完成,所以没有待更新了 83 | update_info.status = 'updated'; 84 | update_info.message = `更新成功: ${version_before} → ${update_info.version_after}`; 85 | } 86 | else if (match(output, /Current version.*is the latest/)) { 87 | update_info.status = 'latest'; 88 | update_info.has_update = false; 89 | let version_match = match(output, /v[\d.]+/); 90 | if (version_match) { 91 | update_info.current_version = replace(version_match[0], /^v/, ''); 92 | update_info.latest_version = update_info.current_version; 93 | } 94 | update_info.message = '已是最新版本 ' + (update_info.current_version || ''); 95 | 96 | } else if (match(output, /new version.*available/)) { 97 | update_info.status = 'update_available'; 98 | update_info.has_update = true; 99 | 100 | let versions = match(output, /v[\d.]+/, 'g'); 101 | if (versions && length(versions) >= 2) { 102 | update_info.current_version = replace(versions[0], /^v/, ''); 103 | update_info.latest_version = replace(versions[1], /^v/, ''); 104 | } else if (version_before) { 105 | update_info.current_version = version_before; 106 | } 107 | update_info.message = '有新版本可用: ' + (update_info.latest_version || ''); 108 | 109 | } else if (match(output, /download.*failed/)) { 110 | update_info.status = 'download_failed'; 111 | update_info.has_update = false; 112 | update_info.message = '下载更新失败'; 113 | 114 | } else if (match(output, /check.*failed/)) { 115 | update_info.status = 'check_failed'; 116 | update_info.has_update = false; 117 | update_info.message = '检查更新失败'; 118 | 119 | } else { 120 | update_info.status = 'unknown'; 121 | update_info.message = output; 122 | } 123 | 124 | return { update: update_info }; 125 | } catch(e) { 126 | return { update: {}, error: 'Parse error: ' + e }; 127 | } 128 | } else { 129 | return { update: {}, error: 'failed to execute ddns-go command' }; 130 | } 131 | } 132 | } 133 | }; 134 | 135 | return { 'luci.ddns-go': methods }; -------------------------------------------------------------------------------- /luci-app-ddns-go/htdocs/luci-static/resources/view/ddns-go/ddns-go.js: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021-2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-ddns-go */ 2 | 3 | 'use strict'; 4 | 'require view'; 5 | 'require fs'; 6 | 'require ui'; 7 | 'require uci'; 8 | 'require form'; 9 | 'require poll'; 10 | 11 | return view.extend({ 12 | load: function() { 13 | return uci.load('ddns-go'); 14 | }, 15 | 16 | checkRunning: function() { 17 | return fs.exec('/bin/pidof', ['ddns-go']).then(function(pidRes) { 18 | if (pidRes.code === 0) return { isRunning: true }; 19 | return fs.exec('/bin/ash', ['-c', 'ps | grep -q "[d]dns-go"']).then(function(grepRes) { 20 | return { isRunning: grepRes.code === 0 }; 21 | }); 22 | }); 23 | }, 24 | render: function() { 25 | var self = this; 26 | 27 | return this.checkRunning().then(function(checkResult) { 28 | var isRunning = checkResult.isRunning; 29 | var port = uci.get('ddns-go', 'config', 'port') || '[::]:9876'; 30 | var noweb = uci.get('ddns-go', 'config', 'noweb'); 31 | port = port.split(':').pop(); 32 | 33 | var container = E('div'); 34 | if (!isRunning || noweb === '1') { 35 | if (!isRunning) { 36 | var message = _('DDNS-GO Service Not Running'); 37 | } 38 | if (noweb === '1') { 39 | var message = _('DDNS-GO Web Interface Disabled'); 40 | } 41 | 42 | container.appendChild(E('div', { 43 | style: 'text-align: center; padding: 2em;' 44 | }, [ 45 | E('img', { 46 | src: 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMjQiIGhlaWdodD0iMTAyNCIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCI+PHBhdGggZmlsbD0iI2RmMDAwMCIgZD0iTTk0Mi40MjEgMjM0LjYyNGw4MC44MTEtODAuODExLTE1My4wNDUtMTUzLjA0NS04MC44MTEgODAuODExYy03OS45NTctNTEuNjI3LTE3NS4xNDctODEuNTc5LTI3Ny4zNzYtODEuNTc5LTI4Mi43NTIgMC01MTIgMjI5LjI0OC01MTIgNTEyIDAgMTAyLjIyOSAyOS45NTIgMTk3LjQxOSA4MS41NzkgMjc3LjM3NmwtODAuODExIDgwLjgxMSAxNTMuMDQ1IDE1My4wNDUgODAuODExLTgwLjgxMWM3OS45NTcgNTEuNjI3IDE3NS4xNDcgODEuNTc5IDI3Ny4zNzYgODEuNTc5IDI4Mi43NTIgMCA1MTItMjI5LjI0OCA1MTItNTEyIDAtMTAyLjIyOS0yOS45NTItMTk3LjQxOS04MS41NzktMjc3LjM3NnpNMTk0Ljk0NCA1MTJjMC0xNzUuMTA0IDE0MS45NTItMzE3LjA1NiAzMTcuMDU2LTMxNy4wNTYgNDggMCA5My40ODMgMTAuNjY3IDEzNC4yMjkgMjkuNzgxbC00MjEuNTQ3IDQyMS41NDdjLTE5LjA3Mi00MC43ODktMjkuNzM5LTg2LjI3Mi0yOS43MzktMTM0LjI3MnpNNTEyIDgyOS4wNTZjLTQ4IDAtOTMuNDgzLTEwLjY2Ny0xMzQuMjI5LTI5Ljc4MWw0MjEuNTQ3LTQyMS41NDdjMTkuMDcyIDQwLjc4OSAyOS43ODEgODYuMjcyIDI5Ljc4MSAxMzQuMjI5LTAuMDQzIDE3NS4xNDctMTQxLjk5NSAzMTcuMDk5LTMxNy4wOTkgMzE3LjA5OXoiLz48L3N2Zz4=', 47 | style: 'width: 100px; height: 100px; margin-bottom: 1em;' 48 | }), 49 | E('h2', {}, message) 50 | ])); 51 | } else { 52 | var isHttps = window.location.protocol === 'https:'; 53 | 54 | if (isHttps) { 55 | var buttonContainer = E('div', { 56 | style: 'text-align: center; padding: 2em;' 57 | }, [ 58 | E('h2', {}, _('DDNS-GO Control panel')), 59 | E('p', {}, _('Due to browser security policies, the DDNS-GO interface https cannot be embedded directly.')), 60 | E('a', { 61 | href: 'http://' + window.location.hostname + ':' + port, 62 | target: '_blank', 63 | class: 'cbi-button cbi-button-apply', 64 | style: 'display: inline-block; margin-top: 1em; padding: 10px 20px; font-size: 16px; text-decoration: none; color: white;' 65 | }, _('Open Web Interface')), 66 | E('div', { 'style': 'text-align: right; font-style: italic;' }, [ 67 | E('span', {}, [ 68 | _('© github '), 69 | E('a', { 70 | 'href': 'https://github.com/sirpdboy', 71 | 'target': '_blank', 72 | 'style': 'text-decoration: none;' 73 | }, 'by sirpdboy') 74 | ]) 75 | ]) 76 | ]); 77 | container.appendChild(buttonContainer); 78 | } else { 79 | var iframe = E('iframe', { 80 | src: 'http://' + window.location.hostname + ':' + port, 81 | style: 'width: 100%; min-height: 100vh; border: none;' 82 | }); 83 | container.appendChild(iframe); 84 | } 85 | } 86 | 87 | poll.add(function() { 88 | return self.checkRunning().then(function(checkResult) { 89 | var newStatus = checkResult.isRunning; 90 | if (newStatus !== isRunning) { 91 | window.location.reload(); 92 | } 93 | }); 94 | }, 5); 95 | 96 | poll.start(); 97 | 98 | return container; 99 | }); 100 | }, 101 | 102 | 103 | handleSaveApply: null, 104 | handleSave: null, 105 | handleReset: null 106 | }); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![hello](https://views.whatilearened.today/views/github/sirpdboy/deplives.svg) [![](https://img.shields.io/badge/TGGroup-ClickJoin-FFFFFF.svg)](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ) 2 | 3 |

4 |
DDNS GO
5 |

6 | 7 |

8 | 9 | 10 | 11 | 12 |

13 | 14 | [中文](README_CN.md) | English 15 | 16 | ![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明1.jpg) 17 | 18 | Please read this page carefully, which includes precautions and instructions on how to use it. 19 | 20 | ## Function Description: 21 | 22 | ### Ddns go dynamic domain plugin 23 | #### Automatically obtain your public IPv4 or IPv6 address and resolve it to the corresponding domain name service. 24 | 25 | 26 | ## release note 27 | 28 | ## 2025.10.25 luci-app-ddns-go 1.6.3 29 | - 1.Add version number check function 30 | - 2.Add upgraded kernel functionality 31 | - 3.Add the function of resetting user passwords 32 | - 4.Kernel updated to 6.13.0 33 | 34 | ## 2025.5.14 luci-app-ddns-go 1.5.2 35 | 36 | ## 2025.5.12 luci-app-ddns-go 1.5.1 37 | - Update log: 38 | - Compatible with the latest OPENWRT24.10, JS version. The old version 18.06 uses LUA branch. 39 | 40 | 41 | ## characteristic 42 | 43 | - Supports Mac, Windows, Linux systems, ARM, x86 architecture 44 | - Supported Domain Name Service Providers: AliDNS, Dnpod, Cloudflare, Huawei Cloud, callbacks, Baidu Cloud, porkbun, GoDaddy` 45 | - Support interface/network card to obtain IP 46 | - Support running as a service 47 | - Default synchronization interval of 5 minutes 48 | - Supporting simultaneous resolution of multiple domain names, a must-have for companies 49 | - Support multi-level domain names 50 | - Configure on the webpage, simple and convenient, can set 'login username and password'/'prohibit access from the public network'` 51 | - Convenient and quick viewing of the last 50 logs on the webpage, without the need to run Docker to view them 52 | - Support webhook notifications 53 | - Supports TTL 54 | - Support some DNS service providers to pass custom parameters and achieve functions such as region resolution 55 | 56 | ## Usage 57 | 58 | Method for adding luci-app-ddns-go to LEDE/OpenWRT source code. 59 | 60 | ### Method for downloading source code one: 61 | Edit the root directory of the source code folder 'feeds.comnf.defect' and add the following content: 62 | 63 | ```Brach 64 | # feeds Get source code: 65 | src-git taskplan https://github.com/sirpdboy/luci-app-ddns-go 66 | ``` 67 | ```Brach 68 | # Update feeds and install themes: 69 | scripts/feeds update ddns-go 70 | scripts/feeds install luci-app-ddns-go 71 | ``` 72 | 73 | ### Method for downloading source code two: 74 | ```Brach 75 | # downloading 76 | git clone https://github.com/sirpdboy/luci-app-ddns-go package/ddns-go 77 | make menuconfig 78 | ``` 79 | ### Configuration Menu 80 | ```Brach 81 | make menuconfig 82 | # find LuCI -> Applications, select luci-app-ddns-go, save and exit 83 | ``` 84 | ### compile 85 | ```Brach 86 | # compile 87 | make package/ddns-go/luci-app-ddns-go/compile V=s 88 | ``` 89 | 90 | 91 | 92 | ## describe 93 | 94 | - Source code source:https://github.com/sirpdboy/luci-app-ddns-go 95 | - Source code source:https://github.com/jeessy2/ddns-go.git 96 | 97 | ![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明2.jpg) 98 | 99 | 100 | ## interface 101 | 102 | ![screenshots](./doc/ddnsgo1.png) 103 | 104 | ![screenshots](./doc/ddnsgo2.png) 105 | 106 | ![screenshots](./doc/ddnsgo3.png) 107 | 108 | ![screenshots](./doc/ddnsgo4.png) 109 | 110 | 111 | # My other project 112 | 113 | - Watch Dog : https://github.com/sirpdboy/luci-app-watchdog 114 | - Net Speedtest : https://github.com/sirpdboy/luci-app-netspeedtest 115 | - Task Plan : https://github.com/sirpdboy/luci-app-taskplan 116 | - Power Off Device : https://github.com/sirpdboy/luci-app-poweroffdevice 117 | - OpentoPD Theme : https://github.com/sirpdboy/luci-theme-opentopd 118 | - Ku Cat Theme : https://github.com/sirpdboy/luci-theme-kucat 119 | - Ku Cat Theme Config : https://github.com/sirpdboy/luci-app-kucat-config 120 | - NFT Time Control : https://github.com/sirpdboy/luci-app-timecontrol 121 | - Parent Control: https://github.com/sirpdboy/luci-theme-parentcontrol 122 | - Eqos Plus: https://github.com/sirpdboy/luci-app-eqosplus 123 | - Advanced : https://github.com/sirpdboy/luci-app-advanced 124 | - ddns-go : https://github.com/sirpdboy/luci-app-ddns-go 125 | - Advanced Plus): https://github.com/sirpdboy/luci-app-advancedplus 126 | - Net Wizard: https://github.com/sirpdboy/luci-app-netwizard 127 | - Part Exp: https://github.com/sirpdboy/luci-app-partexp 128 | - Lukcy: https://github.com/sirpdboy/luci-app-lukcy 129 | 130 | ## HELP 131 | 132 | | 图飞了 | 图飞了 | 133 | | :-----------------: | :-------------: | 134 | |![xm1](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/支付宝.png) | ![xm1](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/微信.png) | 135 | 136 | 137 | no 138 | 139 | 140 | ![hello](https://visitor-badge-deno.deno.dev/sirpdboy.sirpdboy.svg) [![](https://img.shields.io/badge/TGGroup-ClickJoin-FFFFFF.svg)](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ) 141 | -------------------------------------------------------------------------------- /luci-app-ddns-go/htdocs/luci-static/resources/view/ddns-go/config.js: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021-2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-ddns-go */ 2 | 'use strict'; 3 | 'require view'; 4 | 'require fs'; 5 | 'require ui'; 6 | 'require uci'; 7 | 'require form'; 8 | 'require poll'; 9 | 'require rpc'; 10 | 11 | const getDDNSGoInfo = rpc.declare({ 12 | object: 'luci.ddns-go', 13 | method: 'get_ver', 14 | expect: { 'ver': {} } 15 | }); 16 | 17 | const getUpdateInfo = rpc.declare({ 18 | object: 'luci.ddns-go', 19 | method: 'last_update', 20 | expect: { 'update': {} } 21 | }); 22 | 23 | async function checkProcess() { 24 | // 先尝试用 pidof 25 | try { 26 | const pidofRes = await fs.exec('/bin/pidof', ['ddns-go']); 27 | if (pidofRes.code === 0) { 28 | return { 29 | running: true, 30 | pid: pidofRes.stdout.trim() 31 | }; 32 | } 33 | } catch (err) { 34 | // pidof 失败,继续尝试 ps 35 | } 36 | try { 37 | const psRes = await fs.exec('/bin/ps', ['-C', 'ddns-go', '-o', 'pid=']); 38 | const pid = psRes.stdout.trim(); 39 | return { 40 | running: pid !== '', 41 | pid: pid || null 42 | }; 43 | } catch (err) { 44 | return { running: false, pid: null }; 45 | } 46 | } 47 | 48 | function getVersionInfo() { 49 | return L.resolveDefault(getDDNSGoInfo(), {}).then(function(result) { 50 | //console.log('getVersionInfo result:', result); 51 | return result || {}; 52 | }).catch(function(error) { 53 | console.error('Failed to get version:', error); 54 | return {}; 55 | }); 56 | } 57 | 58 | function checkUpdateStatus() { 59 | return L.resolveDefault(getUpdateInfo(), {}).then(function(result) { 60 | //console.log('checkUpdateStatus result:', result); 61 | return result || {}; 62 | }).catch(function(error) { 63 | console.error('Failed to get update info:', error); 64 | return {}; 65 | }); 66 | } 67 | 68 | function renderStatus(isRunning, listen_port, noweb, version) { 69 | var statusText = isRunning ? _('RUNNING') : _('NOT RUNNING'); 70 | var color = isRunning ? 'green' : 'red'; 71 | var icon = isRunning ? '✓' : '✗'; 72 | var versionText = version ? `v${version}` : ''; 73 | 74 | var html = String.format( 75 | '%s %s %s - %s', 76 | color, icon, _('DDNS-Go'), versionText, statusText 77 | ); 78 | 79 | if (isRunning) { 80 | html += String.format(' %s', 81 | window.location.hostname, listen_port, _('Open Web Interface')); 82 | } 83 | 84 | return html; 85 | } 86 | 87 | function renderUpdateStatus(updateInfo) { 88 | if (!updateInfo || !updateInfo.status) { 89 | return ' ⚠ ' + _('Update status unknown') + ''; 90 | } 91 | 92 | var status = updateInfo.status; 93 | var message = updateInfo.message || ''; 94 | 95 | switch(status) { 96 | case 'updated': 97 | return String.format('✓ %s', message); 98 | case 'update_available': 99 | return String.format('↻ %s', message); 100 | case 'latest': 101 | return String.format('✓ %s', message); 102 | case 'download_failed': 103 | case 'check_failed': 104 | return String.format('✗ %s', message); 105 | default: 106 | return String.format('? %s', message); 107 | } 108 | } 109 | 110 | return view.extend({ 111 | load: function() { 112 | return Promise.all([ 113 | uci.load('ddns-go') 114 | ]); 115 | }, 116 | 117 | handleResetPassword: async function () { 118 | try { 119 | ui.showModal(_('Resetting Password'), [ 120 | E('p', { 'class': 'spinning' }, _('Resetting admin password, please wait...')) 121 | ]); 122 | 123 | const result = await fs.exec('/usr/bin/ddns-go', ['-resetPassword', 'admin12345', '-c', '/etc/ddns-go/ddns-go-config.yaml']); 124 | 125 | ui.hideModal(); 126 | 127 | const output = (result.stdout + result.stderr).trim(); 128 | 129 | let success = false; 130 | let message = ''; 131 | 132 | if (result.code === 0) { 133 | 134 | 135 | message = _('Password reset successfully to admin12345'); 136 | 137 | ui.showModal(_('Password Reset Successful'), [ 138 | E('p', _('Admin password has been reset to: admin12345')), 139 | E('p', _('You need to restart DDNS-Go service for the changes to take effect.')), 140 | E('div', { 'class': 'right' }, [ 141 | E('button', { 142 | 'class': 'btn cbi-button cbi-button-positive', 143 | 'click': ui.createHandlerFn(this, function() { 144 | ui.hideModal(); 145 | this.handleRestartService(); 146 | }) 147 | }, _('Restart Service Now')), 148 | ' ', 149 | E('button', { 150 | 'class': 'btn cbi-button cbi-button-neutral', 151 | 'click': ui.hideModal 152 | }, _('Restart Later')) 153 | ]) 154 | ]); 155 | } else { 156 | alert(_('Reset may have failed:') + '\n' + output); 157 | } 158 | 159 | } catch (error) { 160 | ui.hideModal(); 161 | console.error('Reset password failed:', error); 162 | alert(_('ERROR:') + '\n' + _('Reset password failed:') + '\n' + error.message); 163 | } 164 | }, 165 | 166 | handleRestartService: async function() { 167 | try { 168 | await fs.exec('/etc/init.d/ddns-go', ['stop']); 169 | await new Promise(resolve => setTimeout(resolve, 1000)); 170 | await fs.exec('/etc/init.d/ddns-go', ['start']); 171 | 172 | alert(_('SUCCESS:') + '\n' + _('DDNS-Go service restarted successfully')); 173 | if (window.statusPoll) { 174 | window.statusPoll(); 175 | } 176 | } catch (error) { 177 | alert(_('ERROR:') + '\n' + _('Failed to restart service:') + '\n' + error.message); 178 | } 179 | }, 180 | 181 | 182 | handleUpdate: async function () { 183 | try { 184 | var updateView = document.getElementById('update_status'); 185 | if (updateView) { 186 | updateView.innerHTML = ' ' + _('Updating, please wait...'); 187 | } 188 | const updateInfo = await checkUpdateStatus(); 189 | if (updateView) { 190 | updateView.innerHTML = renderUpdateStatus(updateInfo); 191 | } 192 | 193 | if (updateInfo.update_successful || updateInfo.status === 'updated') { 194 | if (window.statusPoll) { 195 | window.statusPoll(); 196 | } 197 | 198 | // 3秒后恢复显示版本信息 199 | setTimeout(() => { 200 | var updateView = document.getElementById('update_status'); 201 | if (updateView) { 202 | getVersionInfo().then(function(versionInfo) { 203 | var version = versionInfo.version || ''; 204 | updateView.innerHTML = String.format('✓ %s v%s', 205 | _('Current Version:'), version); 206 | }); 207 | } 208 | }, 3000); 209 | } 210 | 211 | } catch (error) { 212 | console.error('Update failed:', error); 213 | var updateView = document.getElementById('update_status'); 214 | if (updateView) { 215 | updateView.innerHTML = '✗ ' + _('Update failed') + ''; 216 | 217 | // 5秒后恢复显示版本信息 218 | setTimeout(() => { 219 | getVersionInfo().then(function(versionInfo) { 220 | var version = versionInfo.version || ''; 221 | updateView.innerHTML = String.format('%s v%s', 222 | _('Current Version:'), version); 223 | }); 224 | }, 5000); 225 | } 226 | } 227 | }, 228 | 229 | render: function(data) { 230 | var m, s, o; 231 | var listen_port = (uci.get('ddns-go', 'config', 'port') || '[::]:9876').split(':').slice(-1)[0]; 232 | var noweb = uci.get('ddns-go', 'config', 'noweb') || '0'; 233 | 234 | m = new form.Map('ddns-go', _('DDNS-GO'), 235 | _('DDNS-GO automatically obtains your public IPv4 or IPv6 address and resolves it to the corresponding domain name service.')); 236 | 237 | // 状态显示部分 238 | s = m.section(form.TypedSection); 239 | s.anonymous = true; 240 | 241 | s.render = function() { 242 | var statusView = E('p', { id: 'control_status' }, 243 | ' ' + _('Checking status...')); 244 | 245 | 246 | window.statusPoll = function() { 247 | return Promise.all([ 248 | checkProcess(), 249 | getVersionInfo() 250 | ]).then(function(results) { 251 | var [processInfo, versionInfo] = results; 252 | var version = versionInfo.version || ''; 253 | statusView.innerHTML = renderStatus(processInfo.running, listen_port, noweb, version); 254 | }).catch(function(err) { 255 | console.error('Status check failed:', err); 256 | statusView.innerHTML = '⚠ ' + _('Status check error') + ''; 257 | }); 258 | }; 259 | 260 | var pollInterval = poll.add(window.statusPoll, 5); // 每5秒检查一次 261 | 262 | return E('div', { class: 'cbi-section', id: 'status_bar' }, [ 263 | statusView, 264 | E('div', { 'style': 'text-align: right; font-style: italic;' }, [ 265 | E('span', {}, [ 266 | _('© github '), 267 | E('a', { 268 | 'href': 'https://github.com/sirpdboy', 269 | 'target': '_blank', 270 | 'style': 'text-decoration: none;' 271 | }, 'by sirpdboy') 272 | ]) 273 | ]) 274 | ]); 275 | }; 276 | 277 | s = m.section(form.NamedSection, 'config', 'basic'); 278 | 279 | o = s.option(form.Flag, 'enabled', _('Enable')); 280 | o.default = o.disabled; 281 | o.rmempty = false; 282 | 283 | o = s.option(form.Value, 'port', _('Listen port')); 284 | o.default = '[::]:9876'; 285 | o.rmempty = false; 286 | 287 | o = s.option(form.Value, 'time', _('Update interval')); 288 | o.default = '300'; 289 | 290 | o = s.option(form.Value, 'ctimes', _('Compare with service provider N times intervals')); 291 | o.default = '5'; 292 | 293 | o = s.option(form.Value, 'skipverify', _('Skip verifying certificates')); 294 | o.default = '0'; 295 | 296 | o = s.option(form.Value, 'dns', _('Specify DNS resolution server')); 297 | o.value('223.5.5.5', _('Ali DNS 223.5.5.5')); 298 | o.value('223.6.6.6', _('Ali DNS 223.6.6.6')); 299 | o.value('119.29.29.29', _('Tencent DNS 119.29.29.29')); 300 | o.value('1.1.1.1', _('CloudFlare DNS 1.1.1.1')); 301 | o.value('8.8.8.8', _('Google DNS 8.8.8.8')); 302 | o.value('8.8.4.4', _('Google DNS 8.8.4.4')); 303 | o.datatype = 'ipaddr'; 304 | 305 | o = s.option(form.Flag, 'noweb', _('Do not start web services')); 306 | o.default = '0'; 307 | o.rmempty = false; 308 | 309 | o = s.option(form.Value, 'delay', _('Delayed Start (seconds)')); 310 | o.default = '60'; 311 | 312 | o = s.option(form.Button, '_newpassword', _('Reset account password')); 313 | o.inputtitle = _('ResetPassword'); 314 | o.inputstyle = 'apply'; 315 | o.onclick = L.bind(this.handleResetPassword, this, data); 316 | 317 | o = s.option(form.DummyValue, '_update_status', _('Current Version')); 318 | o.rawhtml = true; 319 | var currentVersion = ''; 320 | 321 | getVersionInfo().then(function(versionInfo) { 322 | currentVersion = versionInfo.version || ''; 323 | var updateView = document.getElementById('update_status'); 324 | if (updateView) { 325 | updateView.innerHTML = String.format('v%s', currentVersion); 326 | } 327 | }); 328 | 329 | o.cfgvalue = function() { 330 | return E('div', { style: 'margin: 5px 0;' }, [ 331 | E('span', { id: 'update_status' }, 332 | currentVersion ? String.format('v%s', currentVersion) : _('Loading...')) 333 | ]); 334 | }; 335 | 336 | o = s.option(form.Button, '_update', _('Update kernel'), 337 | _('Check and update DDNS-Go to the latest version')); 338 | o.inputtitle = _('Check Update'); 339 | o.inputstyle = 'apply'; 340 | o.onclick = L.bind(this.handleUpdate, this, data); 341 | 342 | return m.render(); 343 | } 344 | }); --------------------------------------------------------------------------------