From be9295310c7dce638ddf3d858ac982616f178522 Mon Sep 17 00:00:00 2001 From: laowang Date: Mon, 10 Nov 2025 10:27:07 +0800 Subject: [PATCH] initial --- README.md | 96 ++++++++++ ddns.sh | 514 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 610 insertions(+) create mode 100644 README.md create mode 100644 ddns.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..13ab2dc --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +# Debian/Ubuntu 全自动 Cloudflare DDNS 一键脚本 + +这是一个功能强大且高度优化的 Shell 脚本,旨在帮助用户在 Debian 或 Ubuntu 系统上一键配置 Cloudflare DDNS (动态域名解析服务)。脚本会自动检测并更新您的公网 IPv4 和 IPv6 地址,确保您的域名始终指向服务器的最新 IP。 + +## 脚本特点 + +- **一键式安装**:自动检测并安装 `curl` 和 `jq` 依赖,无需手动干预。 +- **双栈支持**:同时支持 IPv4 (A 记录) 和 IPv6 (AAAA 记录) 的动态更新。 +- **智能定时**:通过 systemd 定时器实现,每 5 分钟自动执行一次 IP 检测与更新,稳定可靠。 +- **Telegram 通知**:当 IP 地址成功更新或发生错误时,可通过 Telegram Bot 发送实时通知。 +- **安全加固**:配置文件和 IP 缓存文件权限被严格设置为 `600`,防止未授权访问。 +- **高效稳定**:缓存 Zone ID 以减少不必要的 API 请求;从多个源获取公网 IP,提高成功率。 +- **管理便捷**:安装后可通过 `ddns` 命令随时调出管理菜单,进行启动、停止、修改配置、查看日志等操作。 +- **彻底卸载**:提供一键卸载功能,干净地移除所有相关文件和服务。 + +## 准备工作 + +在运行脚本之前,请确保您已准备好以下信息: + +1. 一台运行 **Debian 或 Ubuntu** 的服务器。 +2. 拥有服务器的 **root** 权限。 +3. 一个在 **Cloudflare** 解析的域名。 +4. 您的 **Cloudflare 账户邮箱**。 +5. 您的 **Cloudflare Global API Key**。 + > **获取方式**:登录 Cloudflare -> 右上角头像 -> 我的个人资料 -> API 令牌 -> Global API Key -> 查看。 +6. **(重要)** 在 Cloudflare DNS 管理页面,**提前创建**好您想用于 DDNS 的 A 记录和 AAAA 记录。例如,如果您想用 `v4.yourdomain.com` 指向 IPv4,`v6.yourdomain.com` 指向 IPv6,您必须先手动创建这两个记录(IP 地址可以随便填,如 `1.1.1.1`)。 +7. **(可选)** 如果需要 Telegram 通知,请准备好您的 **Telegram Bot Token** 和 **Chat ID**。 + +## 使用方法 + +### 1. 下载并运行脚本 + +使用 SSH 连接到您的服务器,并以 root 用户身份执行以下命令: + +```bash +wget -O ddns.sh https://raw.githubusercontent.com/chinggirltube/my_script/refs/heads/main/ddns.sh && bash ddns.sh +``` + +### 2. 首次配置 + +脚本第一次运行时,会引导您完成安装和配置流程: + +1. **自动安装依赖**:脚本会检查并安装 `curl` 和 `jq`。 +2. **输入 Cloudflare API 信息**:根据提示,输入您的 Cloudflare 账户邮箱和 Global API Key。 +3. **输入域名**: + - 输入要用于 IPv4 DDNS 的完整域名(如 `v4.yourdomain.com`)。如果不需要,直接回车跳过。 + - 输入要用于 IPv6 DDNS 的完整域名(如 `v6.yourdomain.com`)。如果不需要,直接回车跳过。 +4. **配置 Telegram 通知 (可选)**:根据提示输入您的 Bot Token 和 Chat ID。如果不需要,直接回车跳过。 + +配置完成后,脚本会自动设置并启动定时任务。安装成功! + +## 后续管理 + +您可以随时在终端输入 `ddns` 命令,调出管理菜单进行后续操作。 + +```bash +ddns +``` + +**管理菜单选项说明:** + +- **1. 启动 / 重启 DDNS**:启动或重启后台的定时任务。 +- **2. 停止 DDNS**:停止后台的定时任务。 +- **3. 修改要解析的域名**:重新设置用于 IPv4 和 IPv6 的域名。 +- **4. 修改 Cloudflare API**:更新您的 Cloudflare 邮箱和 API Key。 +- **5. 配置 Telegram 通知**:修改或添加 Telegram Bot 配置。 +- **6. 彻底卸载 DDNS**:从系统中移除脚本、配置文件和定时任务。 +- **7. 查看 DDNS 实时日志**:实时监控 DDNS 脚本的运行情况,排查问题。 +- **8. 测试 Telegram 通知**:发送一条测试消息,以验证 Telegram 配置是否正确。 +- **9. 立即手动执行一次DDNS检查**:不等定时器触发,立即手动运行一次 IP 更新。 + +## 文件结构 + +脚本安装后,相关文件位于以下路径: + +- **管理脚本**: `/usr/bin/ddns` +- **核心执行脚本**: `/etc/DDNS/DDNS` +- **配置文件**: `/etc/DDNS/.config` (包含 API Key, 域名等敏感信息) +- **日志文件**: `/var/log/ddns.log` +- **Systemd 服务单元**: `/etc/systemd/system/ddns.service` +- **Systemd 定时器单元**: `/etc/systemd/system/ddns.timer` + +## 常见问题 (FAQ) + +> **Q: 日志提示 `无法获取 Zone ID` 是为什么?** +> **A:** 这通常意味着您的 **Cloudflare 邮箱** 或 **Global API Key** 不正确,或者您填写的域名(例如 `yourdomain.com`)并不在该 Cloudflare 账户下。请通过 `ddns` 命令选择 `4` 重新配置。 + +> **Q: 日志提示 `无法获取 DNS Record ID` 是为什么?** +> **A:** 这是最常见的问题。原因是您**没有在 Cloudflare 上预先创建**该 DNS 记录。例如,如果您在脚本中配置了 `v4.mydomain.com`,您必须先登录 Cloudflare 手动为 `mydomain.com` 添加一条名为 `v4` 的 A 记录。 + +> **Q: 脚本多久检查一次 IP 地址?** +> **A:** 默认每 5 分钟检查一次。 + +> **Q: 如何修改检查频率?** +> **A:** 编辑文件 `/etc/systemd/system/ddns.timer`,修改 `OnUnitActiveSec=5min` 这一行,例如改为 `10min` 就是 10 分钟一次。修改后执行 `systemctl daemon-reload` 和 `ddns` 菜单中的 `1` 重启服务。 + diff --git a/ddns.sh b/ddns.sh new file mode 100644 index 0000000..9ab8e19 --- /dev/null +++ b/ddns.sh @@ -0,0 +1,514 @@ +#!/bin/bash + +# --- 脚本元信息与颜色定义 --- +GREEN="\033[32m" +RED="\033[31m" +YELLOW="\033[0;33m" +NC="\033[0m" +GREEN_ground="\033[42;37m" # 全局绿色 +RED_ground="\033[41;37m" # 全局红色 +Info="${GREEN}[信息]${NC}" +Error="${RED}[错误]${NC}" +Tip="${YELLOW}[提示]${NC}" + +# --- 脚本欢迎界面 --- +cop_info(){ +clear +echo -e "${GREEN}####################################################### +# ${RED}Debian DDNS 一键脚本 (最终修正版)${GREEN} # +# 作者: ${YELLOW}LaoWang & AI ${GREEN}# +# https://blog.wlens.top ${GREEN}# +# ${YELLOW}优化: 缓存ZoneID, 重构更新逻辑, 加固文件权限${GREEN} # +#######################################################${NC}" +echo -e "${Info}此版本已修复所有已知BUG,感谢您的耐心。 " +echo +} + +# --- 环境检查 --- + +# 检查系统 +if ! grep -qiE "debian|ubuntu" /etc/os-release; then + echo -e "${Error}本脚本仅支持 Debian 或 Ubuntu 系统。" + exit 1 +fi + +# 检查root权限 +check_root(){ + if [[ $(whoami) != "root" ]]; then + echo -e "${Error}请以root身份执行该脚本!" + exit 1 + fi +} + +# 检查并安装curl和jq +check_curl() { + if ! command -v curl &>/dev/null || ! command -v jq &>/dev/null; then + echo -e "${YELLOW}未检测到 curl 或 jq,正在安装...${NC}" + apt-get update && apt-get install -y curl jq + if [ $? -ne 0 ]; then + echo -e "${RED}安装 curl/jq 失败,请手动安装后重试。${NC}" + exit 1 + fi + fi +} + +# --- 核心安装与文件生成 --- + +# 安装DDNS相关文件 +install_ddns(){ + # 备份旧版本,避免覆盖 + if [ -d "/etc/DDNS" ]; then + echo -e "${Tip}检测到已存在的DDNS目录,将备份为 /etc/DDNS.bak_$(date +%s)" + mv /etc/DDNS "/etc/DDNS.bak_$(date +%s)" 2>/dev/null + fi + + # 创建工作目录 + mkdir -p /etc/DDNS + + # 将此脚本自身复制为系统命令 + cp "$0" /usr/bin/ddns && chmod +x /usr/bin/ddns + + # 创建纯净的配置文件 (仅包含用户可配置项) + cat <<'EOF' > /etc/DDNS/.config +Domain="your_domain.com" +Domainv6="your_domainv6.com" +Email="your_email@gmail.com" +Api_key="your_api_key" + +Telegram_Bot_Token="" +Telegram_Chat_ID="" +EOF + chmod 600 /etc/DDNS/.config # 安全加固:设置配置文件权限 + + # 创建独立的IP状态文件 + touch /etc/DDNS/.old_ipv4 && chmod 600 /etc/DDNS/.old_ipv4 + touch /etc/DDNS/.old_ipv6 && chmod 600 /etc/DDNS/.old_ipv6 + + # ================================================================= # + # 创建全新的、语法完全正确的DDNS执行脚本 + # ================================================================= # + cat <<'EOF' > /etc/DDNS/DDNS +#!/bin/bash + +# DDNS工作目录 +WORK_DIR="/etc/DDNS" +# 日志文件 +LOG_FILE="/var/log/ddns.log" + +# --- 全局变量 --- +declare -A ZONE_ID_CACHE + +# --- 基础函数 --- + +log() { + echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE" +} + +send_telegram_notification(){ + local message="$1" + if [[ -n "$Telegram_Bot_Token" && -n "$Telegram_Chat_ID" ]]; then + log "正在发送Telegram通知..." + curl -s --connect-timeout 10 -X POST "https://api.telegram.org/bot$Telegram_Bot_Token/sendMessage" \ + -d chat_id="$Telegram_Chat_ID" -d text="$message" >> "$LOG_FILE" 2>&1 + else + log "Telegram通知未配置或配置不完整,跳过发送。" + fi +} + +# --- Cloudflare API 函数 --- + +get_root_domain() { + echo "$1" | awk -F. '{print $(NF-1)"."$NF}' +} + +get_zone_id() { + local full_domain=$1 + local root_domain + root_domain=$(get_root_domain "$full_domain") + + if [[ -n "${ZONE_ID_CACHE[$root_domain]}" ]]; then + log "从缓存命中 Zone ID for $root_domain: ${ZONE_ID_CACHE[$root_domain]}" + echo "${ZONE_ID_CACHE[$root_domain]}" + return + fi + + log "通过API获取 Zone ID for $root_domain..." + local zone_id_val + ZONE_ID_RESPONSE=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$root_domain" \ + -H "X-Auth-Email: $Email" \ + -H "X-Auth-Key: $Api_key" \ + -H "Content-Type: application/json") + zone_id_val=$(echo "$ZONE_ID_RESPONSE" | jq -r '.result[] | select(.name=="'"$root_domain"'") | .id' 2>/dev/null) + + if [ -z "$zone_id_val" ]; then + log "错误: 无法获取 Zone ID for '$root_domain'. 检查邮箱、API Key或根域名。API响应: $ZONE_ID_RESPONSE" + send_telegram_notification "DDNS 错误: 无法获取 ${root_domain} 的 Cloudflare Zone ID。" + echo "" + else + log "获取成功! Zone ID for $root_domain: $zone_id_val. 已缓存。" + ZONE_ID_CACHE["$root_domain"]="$zone_id_val" + echo "$zone_id_val" + fi +} + +get_dns_record_id() { + local zone_id=$1 + local record_type=$2 + local domain_name=$3 + + log "尝试获取 DNS Record ID for $domain_name (Type $record_type)..." + DNS_ID_RESPONSE=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records?type=$record_type&name=$domain_name" \ + -H "X-Auth-Email: $Email" \ + -H "X-Auth-Key: $Api_key" \ + -H "Content-Type: application/json") + local dns_id_val + dns_id_val=$(echo "$DNS_ID_RESPONSE" | jq -r '.result[0].id' 2>/dev/null) + + if [ -z "$dns_id_val" ]; then + log "警告: 无法获取 '$domain_name' 的 $record_type 记录 ID. 请确保该记录已存在。API响应: $DNS_ID_RESPONSE" + echo "" + else + log "DNS Record ID for $domain_name: $dns_id_val" + echo "$dns_id_val" + fi +} + +update_dns_record() { + local record_type=$1 + local domain=$2 + local public_ip=$3 + local old_ip=$4 + local old_ip_file=$5 + + if [[ "$public_ip" == "$old_ip" ]]; then + log "$record_type 地址 ($public_ip) 未变化。" + return 0 + fi + + log "检测到 $record_type 地址变化: 旧[$old_ip] -> 新[$public_ip]" + + local zone_id + zone_id=$(get_zone_id "$domain") + if [ -z "$zone_id" ]; then + log "错误: 因无法获取Zone ID,跳过 $domain 的更新。" + return 1 + fi + + local dns_id + dns_id=$(get_dns_record_id "$zone_id" "$record_type" "$domain") + if [ -z "$dns_id" ]; then + log "错误: 因无法获取DNS Record ID,跳过 $domain 的更新。" + send_telegram_notification "DDNS 错误: 更新 ${domain} ($record_type) 失败,无法获取 DNS Record ID。" + return 1 + fi + + log "正在更新 $domain ($record_type) -> $public_ip..." + response=$(curl -s -w "%{http_code}" -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records/$dns_id" \ + -H "X-Auth-Email: $Email" \ + -H "X-Auth-Key: $Api_key" \ + -H "Content-Type: application/json" \ + --data "{\"type\":\"$record_type\",\"name\":\"$domain\",\"content\":\"$public_ip\",\"ttl\":60,\"proxied\":false}") + + http_code=${response: -3} + body=${response::-3} + + if [ "$http_code" -eq 200 ] && [[ "$body" == *"\"success\":true"* ]]; then + log "成功: $domain 的 $record_type 记录已更新为 $public_ip" + echo "$public_ip" > "$old_ip_file" + echo "${domain} 的 ${record_type} 地址已更新为 ${public_ip}。旧IP为 ${old_ip}。" + return 0 + else + log "失败: 更新 $domain 失败。HTTP Code: $http_code, Response: $body" + send_telegram_notification "DDNS 错误: 更新 ${domain} ($record_type) 失败. HTTP Code: ${http_code}." + return 1 + fi +} + +# ==================== 主逻辑开始 ==================== +log "====== DDNS 任务开始 ======" +cd "$WORK_DIR" || { log "错误: 无法进入DDNS工作目录 $WORK_DIR"; exit 1; } + +source .config + +# --- 获取并验证公网IP地址 (已整合老版本验证逻辑) --- +ipv4Regex="^([0-9]{1,3}\.){3}[0-9]{1,3}$" +ipv6Regex="^([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])$" + +log "正在从多个源获取公网IP地址..." +raw_ipv4=$(curl -s4 --max-time 10 https://api.ipify.org || curl -s4 --max-time 10 https://ip.sb || curl -s4 --max-time 10 https://ipv4.icanhazip.com) +raw_ipv6=$(curl -s6 --max-time 10 https://api6.ipify.org || curl -s6 --max-time 10 https://ip.sb || curl -s6 --max-time 10 https://ipv6.icanhazip.com) + +Public_IPv4="" +if [[ -n "$raw_ipv4" && "$raw_ipv4" =~ $ipv4Regex ]]; then + Public_IPv4="$raw_ipv4" + log "成功获取并验证公网 IPv4: $Public_IPv4" +else + log "警告: 获取到的IPv4内容不是有效地址: '$raw_ipv4'。将忽略IPv4更新。" +fi + +Public_IPv6="" +if [[ -n "$raw_ipv6" && "$raw_ipv6" =~ $ipv6Regex ]]; then + Public_IPv6="$raw_ipv6" + log "成功获取并验证公网 IPv6: $Public_IPv6" +else + log "警告: 获取到的IPv6内容不是有效地址: '$raw_ipv6'。将忽略IPv6更新。" +fi + +# --- 读取旧的 IP 地址 --- +Old_Public_IPv4=$(cat "$WORK_DIR/.old_ipv4" 2>/dev/null) +Old_Public_IPv6=$(cat "$WORK_DIR/.old_ipv6" 2>/dev/null) +log "旧IPv4: [$Old_Public_IPv4], 旧IPv6: [$Old_Public_IPv6]" + +# --- 依次处理IPv4和IPv6的更新 --- +notification_message="" +update_result="" + +if [[ -n "$Domain" && "$Domain" != "your_domain.com" && -n "$Public_IPv4" ]]; then + update_result=$(update_dns_record "A" "$Domain" "$Public_IPv4" "$Old_Public_IPv4" "$WORK_DIR/.old_ipv4") + if [[ $? -eq 0 && -n "$update_result" ]]; then + notification_message+="$update_result " + fi +else + log "跳过 IPv4 更新:未配置域名或未获取到有效的公网IP。" +fi + +if [[ -n "$Domainv6" && "$Domainv6" != "your_domainv6.com" && -n "$Public_IPv6" ]]; then + update_result=$(update_dns_record "AAAA" "$Domainv6" "$Public_IPv6" "$Old_Public_IPv6" "$WORK_DIR/.old_ipv6") + if [[ $? -eq 0 && -n "$update_result" ]]; then + notification_message+="$update_result " + fi +else + log "跳过 IPv6 更新:未配置域名或未获取到有效的公网IP。" +fi + +# --- 任务结束,发送汇总通知 --- +if [ -n "$notification_message" ]; then + log "IP发生变化,准备发送最终通知。" + send_telegram_notification "$notification_message" +else + log "所有IP均未变化或无需更新,不发送通知。" +fi + +log "====== DDNS 任务结束 ======" +EOF + + chmod 700 /etc/DDNS/DDNS + + touch /var/log/ddns.log && chmod 644 /var/log/ddns.log + + echo -e "${Info}DDNS 文件安装完成!" + echo -e "${Tip}核心逻辑脚本位于: ${YELLOW}/etc/DDNS/DDNS${NC}" + echo -e "${Tip}配置文件位于: ${YELLOW}/etc/DDNS/.config (权限600)${NC}" + echo -e "${Tip}IP状态文件权限已设置为 600。" + echo -e "${Tip}日志文件位于: ${YELLOW}/var/log/ddns.log${NC}" + echo +} + +# --- 管理菜单与功能 --- + +check_ddns_status(){ + if systemctl is-active --quiet ddns.timer; then + ddns_status="running" + else + ddns_status="dead" + fi +} + +test_telegram_notification(){ + echo -e "${Tip}正在测试 Telegram 通知...${NC}" + source /etc/DDNS/.config + + if [[ -z "$Telegram_Bot_Token" || -z "$Telegram_Chat_ID" ]]; then + echo -e "${Error}Telegram Bot Token 或 Chat ID 未配置。请先通过选项 ${GREEN}5${NC} 进行配置。${NC}" + return 1 + fi + + current_ipv4=$(curl -s4 --max-time 5 https://api.ipify.org || echo "N/A") + local test_message="DDNS (最终修正版) 测试通知。服务器IP: ${current_ipv4}。如果能收到此消息,说明Telegram通知功能正常。" + + echo -e "${Info}尝试发送测试消息...详情请查看 /var/log/ddns.log" + + ( + Telegram_Bot_Token="$Telegram_Bot_Token" + Telegram_Chat_ID="$Telegram_Chat_ID" + LOG_FILE="/var/log/ddns.log" + source /etc/DDNS/DDNS + send_telegram_notification "$test_message" + ) + + echo -e "${Info}测试消息已发送(或尝试发送)。请查看 Telegram 和 ${YELLOW}/var/log/ddns.log${NC} 确认结果。" +} + +go_ahead(){ + echo -e "${Tip}请选择一个操作: + ${GREEN}1${NC}:启动 / 重启 DDNS + ${GREEN}2${NC}:停止 DDNS + ${GREEN}3${NC}:修改要解析的域名 + ${GREEN}4${NC}:修改 Cloudflare API + ${GREEN}5${NC}:配置 Telegram 通知 + ${GREEN}6${NC}:${RED}彻底卸载 DDNS${NC} + ${GREEN}7${NC}:查看 DDNS 实时日志 + ${GREEN}8${NC}:测试 Telegram 通知 + ${GREEN}9${NC}:立即手动执行一次DDNS检查 + ${GREEN}0${NC}:退出脚本" + echo + read -p "请输入选项 [0-9]: " option + case "$option" in + 0) exit 0 ;; + 1) restart_ddns; main ;; + 2) stop_ddns; main ;; + 3) set_domain; restart_ddns; sleep 2; main ;; + 4) set_cloudflare_api; restart_ddns; sleep 2; main ;; + 5) set_telegram_settings; restart_ddns; sleep 2; main ;; + 6) uninstall_ddns ;; + 7) echo -e "${Info}正在显示实时日志... 按 ${RED}Ctrl+C${NC} 退出。"; tail -f /var/log/ddns.log; main ;; + 8) test_telegram_notification; sleep 2; main ;; + 9) echo -e "${Info}正在手动触发一次DDNS检查..."; systemctl start ddns.service; echo -e "${Info}已触发,请通过日志查看结果。"; sleep 2; main ;; + *) echo -e "${Error}无效的输入!"; sleep 2; main ;; + esac +} + +uninstall_ddns(){ + read -p "你确定要彻底卸载 DDNS 吗? 这会删除所有配置和脚本。[y/N]: " confirm + if [[ ! "${confirm,,}" =~ ^y$ ]]; then + echo -e "${Info}操作已取消。" + main + return + fi + echo -e "${Tip}正在停止并禁用 systemd 服务..." + systemctl disable --now ddns.service ddns.timer >/dev/null 2>&1 + echo -e "${Tip}正在删除相关文件..." + rm -f /etc/systemd/system/ddns.service /etc/systemd/system/ddns.timer + rm -rf /etc/DDNS /usr/bin/ddns + rm -f /var/log/ddns.log + systemctl daemon-reload + echo -e "${Info}DDNS 已成功卸载!" + echo +} + +set_cloudflare_api(){ + echo -e "${Tip}开始配置 Cloudflare API..." && echo + read -p "请输入您的 Cloudflare 邮箱: " email + read -p "请输入您的 Cloudflare Global API Key: " api_key + + if [ -z "$email" ] || [ -z "$api_key" ]; then + echo -e "${Error}邮箱和 API Key 不能为空!" + return 1 + fi + + sed -i "s/^Email=.*/Email=\"$email\"/" /etc/DDNS/.config + sed -i "s/^Api_key=.*/Api_key=\"$api_key\"/" /etc/DDNS/.config + + echo -e "${Info}Cloudflare API 设置已更新!" +} + +set_domain(){ + echo && echo -e "${Tip}开始配置要解析的域名..." + + echo -e "${Tip}请输入要解析的 IPv4 域名 (如: v4.yourdomain.com, 回车跳过):" + read -p "IPv4 域名: " domain_v4 + sed -i "s/^Domain=.*/Domain=\"$domain_v4\"/" /etc/DDNS/.config + + echo -e "${Tip}请输入要解析的 IPv6 域名 (如: v6.yourdomain.com, 回车跳过):" + read -p "IPv6 域名: " domain_v6 + sed -i "s/^Domainv6=.*/Domainv6=\"$domain_v6\"/" /etc/DDNS/.config + + echo -e "${Info}域名设置已更新!" +} + +set_telegram_settings(){ + echo && echo -e "${Tip}开始配置 Telegram 通知 (全部留空则禁用)..." + read -p "请输入您的 Telegram Bot Token: " token + read -p "请输入您的 Telegram Chat ID: " chat_id + + sed -i "s/^Telegram_Bot_Token=.*/Telegram_Bot_Token=\"$token\"/" /etc/DDNS/.config + sed -i "s/^Telegram_Chat_ID=.*/Telegram_Chat_ID=\"$chat_id\"/" /etc/DDNS/.config + + echo -e "${Info}Telegram 设置已更新!" +} + +run_ddns(){ + if [ ! -f "/etc/systemd/system/ddns.service" ]; then + cat > /etc/systemd/system/ddns.service < /etc/systemd/system/ddns.timer </dev/null 2>&1 + echo -e "${Info}DDNS 服务已启动。将每5分钟检查一次IP变更。" +} + +stop_ddns(){ + echo -e "${Info}正在停止 DDNS 定时任务..." + systemctl disable --now ddns.timer >/dev/null 2>&1 + systemctl stop ddns.service >/dev/null 2>&1 + echo -e "${Info}DDNS 已停止。" +} + +main(){ + if [[ -z "$IS_RECURSIVE" ]]; then + cop_info + fi + + if [ ! -f "/etc/DDNS/.config" ] || [ ! -f "/usr/bin/ddns" ]; then + echo -e "${Tip}首次运行,开始安装流程..." + install_ddns + set_cloudflare_api + set_domain + set_telegram_settings + run_ddns + echo + echo -e "${GREEN_ground} DDNS (最终修正版) 安装并配置成功! ${NC}" + echo -e "${Info}你可以随时再次运行此脚本或直接输入 ${GREEN}ddns${NC} 进行管理。" + echo -e "${Info}重要:请通过选项 ${GREEN}7${NC} 查看日志以确认首次运行是否成功。" + echo + else + check_ddns_status + if [[ "$ddns_status" == "running" ]]; then + echo -e "${Info}DDNS 状态: ${GREEN}已安装${NC} | ${GREEN}运行中${NC}" + else + echo -e "${Info}DDNS 状态: ${GREEN}已安装${NC} | ${RED}已停止${NC}" + fi + echo + fi + + export IS_RECURSIVE=true + go_ahead +} + +# --- 脚本执行起点 --- +check_root +check_curl +main \ No newline at end of file