mirror of
https://github.com/woodchen-ink/dnspod-yxip.git
synced 2025-07-18 05:42:08 +08:00
first commit
This commit is contained in:
commit
bb4d8473db
42
.env.example
Normal file
42
.env.example
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# DNSPOD API 配置
|
||||||
|
DNSPOD_ID=your_id_here
|
||||||
|
DNSPOD_TOKEN=your_token_here
|
||||||
|
|
||||||
|
# 域名配置 - 域名1
|
||||||
|
DOMAIN_1=example1.com
|
||||||
|
SUB_DOMAIN_1=@ # 子域名,@ 表示根域名
|
||||||
|
REMARK_1=优选IP # 记录备注
|
||||||
|
TTL_1=600 # TTL值(秒)
|
||||||
|
UPDATE_INTERVAL_1=15 # 更新间隔(分钟)
|
||||||
|
IPV4_ENABLED_1=true # 是否启用IPv4记录
|
||||||
|
IPV6_ENABLED_1=true # 是否启用IPv6记录
|
||||||
|
ENABLED_1=true # 是否启用此域名配置
|
||||||
|
|
||||||
|
# 域名配置 - 域名2(更频繁的更新)
|
||||||
|
DOMAIN_2=example2.com
|
||||||
|
SUB_DOMAIN_2=www
|
||||||
|
REMARK_2=CF优选 # 记录备注
|
||||||
|
TTL_2=300 # 更短的TTL
|
||||||
|
UPDATE_INTERVAL_2=5 # 更频繁的更新
|
||||||
|
IPV4_ENABLED_2=true # 只启用IPv4
|
||||||
|
IPV6_ENABLED_2=false # 不启用IPv6
|
||||||
|
ENABLED_2=true
|
||||||
|
|
||||||
|
# 域名配置 - 域名3(更长的缓存)
|
||||||
|
DOMAIN_3=example3.com
|
||||||
|
SUB_DOMAIN_3=*
|
||||||
|
REMARK_3=CloudFlare优选 # 记录备注
|
||||||
|
TTL_3=1800 # 更长的TTL
|
||||||
|
UPDATE_INTERVAL_3=30 # 更长的更新间隔
|
||||||
|
IPV4_ENABLED_3=true # 启用IPv4
|
||||||
|
IPV6_ENABLED_3=true # 启用IPv6
|
||||||
|
ENABLED_3=true
|
||||||
|
|
||||||
|
# 可以继续添加更多域名配置...
|
||||||
|
# 每个域名可以独立控制:
|
||||||
|
# - 是否启用IPv4 (IPV4_ENABLED_n)
|
||||||
|
# - 是否启用IPv6 (IPV6_ENABLED_n)
|
||||||
|
# - TTL值 (TTL_n)
|
||||||
|
# - 更新间隔 (UPDATE_INTERVAL_n)
|
||||||
|
# - 是否启用 (ENABLED_n)
|
||||||
|
# - 记录备注 (REMARK_n)
|
40
.github/workflows/docker-publish.yml
vendored
Normal file
40
.github/workflows/docker-publish.yml
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
name: Docker
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
|
||||||
|
env:
|
||||||
|
IMAGE_NAME: dnspod-yxip
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: woodchen
|
||||||
|
password: ${{ secrets.ACCESS_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: woodchen/${{ env.IMAGE_NAME }}:latest
|
||||||
|
platforms: linux/amd64,linux/arm64
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/__pycache__
|
17
Dockerfile
Normal file
17
Dockerfile
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 复制项目文件
|
||||||
|
COPY requirements.txt .
|
||||||
|
COPY config.py .
|
||||||
|
COPY main.py .
|
||||||
|
|
||||||
|
# 安装依赖
|
||||||
|
RUN pip install -r requirements.txt
|
||||||
|
|
||||||
|
# 创建配置文件和日志目录
|
||||||
|
RUN mkdir -p logs && touch .env
|
||||||
|
|
||||||
|
# 运行程序
|
||||||
|
CMD ["python", "main.py"]
|
93
config.py
Normal file
93
config.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
# 加载环境变量
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
# DNSPOD API配置
|
||||||
|
DNSPOD_ID = os.getenv("DNSPOD_ID")
|
||||||
|
DNSPOD_TOKEN = os.getenv("DNSPOD_TOKEN")
|
||||||
|
|
||||||
|
# API接口配置
|
||||||
|
API_URL = "https://api.vvhan.com/tool/cf_ip"
|
||||||
|
|
||||||
|
# 默认配置
|
||||||
|
DEFAULT_TTL = 600
|
||||||
|
DEFAULT_UPDATE_INTERVAL = 15
|
||||||
|
|
||||||
|
# 支持的线路类型
|
||||||
|
LINE_TYPES = ["默认", "移动", "联通", "电信"]
|
||||||
|
|
||||||
|
# 支持的记录类型
|
||||||
|
RECORD_TYPES = ["A", "AAAA"]
|
||||||
|
|
||||||
|
# 域名配置
|
||||||
|
DOMAINS = []
|
||||||
|
|
||||||
|
# 从环境变量加载域名配置
|
||||||
|
i = 1
|
||||||
|
while True:
|
||||||
|
domain_key = f"DOMAIN_{i}"
|
||||||
|
if not os.getenv(domain_key):
|
||||||
|
break
|
||||||
|
|
||||||
|
# 获取基本配置
|
||||||
|
base_config = {
|
||||||
|
"domain": os.getenv(domain_key),
|
||||||
|
"sub_domain": os.getenv(f"SUB_DOMAIN_{i}", "@"),
|
||||||
|
"line": LINE_TYPES,
|
||||||
|
"ttl": int(os.getenv(f"TTL_{i}", str(DEFAULT_TTL))),
|
||||||
|
"update_interval": int(
|
||||||
|
os.getenv(f"UPDATE_INTERVAL_{i}", str(DEFAULT_UPDATE_INTERVAL))
|
||||||
|
),
|
||||||
|
"enabled": os.getenv(f"ENABLED_{i}", "true").lower() == "true",
|
||||||
|
"remark": os.getenv(f"REMARK_{i}", "YXIP"),
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查是否启用IPv4
|
||||||
|
ipv4_enabled = os.getenv(f"IPV4_ENABLED_{i}", "true").lower() == "true"
|
||||||
|
# 检查是否启用IPv6
|
||||||
|
ipv6_enabled = os.getenv(f"IPV6_ENABLED_{i}", "true").lower() == "true"
|
||||||
|
|
||||||
|
# 为每个启用的记录类型创建配置
|
||||||
|
if ipv4_enabled:
|
||||||
|
ipv4_config = base_config.copy()
|
||||||
|
ipv4_config["record_type"] = "A"
|
||||||
|
DOMAINS.append(ipv4_config)
|
||||||
|
|
||||||
|
if ipv6_enabled:
|
||||||
|
ipv6_config = base_config.copy()
|
||||||
|
ipv6_config["record_type"] = "AAAA"
|
||||||
|
DOMAINS.append(ipv6_config)
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
# 如果没有配置任何域名,使用默认配置
|
||||||
|
if not DOMAINS:
|
||||||
|
base_config = {
|
||||||
|
"domain": os.getenv("DOMAIN", "example.com"),
|
||||||
|
"sub_domain": os.getenv("SUB_DOMAIN", "@"),
|
||||||
|
"line": LINE_TYPES,
|
||||||
|
"ttl": int(os.getenv("TTL", str(DEFAULT_TTL))),
|
||||||
|
"update_interval": int(
|
||||||
|
os.getenv("UPDATE_INTERVAL", str(DEFAULT_UPDATE_INTERVAL))
|
||||||
|
),
|
||||||
|
"enabled": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查默认的IPv4和IPv6设置
|
||||||
|
ipv4_enabled = os.getenv("IPV4_ENABLED", "true").lower() == "true"
|
||||||
|
ipv6_enabled = os.getenv("IPV6_ENABLED", "true").lower() == "true"
|
||||||
|
|
||||||
|
if ipv4_enabled:
|
||||||
|
ipv4_config = base_config.copy()
|
||||||
|
ipv4_config["record_type"] = "A"
|
||||||
|
DOMAINS.append(ipv4_config)
|
||||||
|
|
||||||
|
if ipv6_enabled:
|
||||||
|
ipv6_config = base_config.copy()
|
||||||
|
ipv6_config["record_type"] = "AAAA"
|
||||||
|
DOMAINS.append(ipv6_config)
|
||||||
|
|
||||||
|
# 日志配置
|
||||||
|
LOG_LEVEL = "INFO"
|
8
docker-compose.yml
Normal file
8
docker-compose.yml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
services:
|
||||||
|
dnspod-yxip:
|
||||||
|
image: woodchen/dnspod-yxip:latest
|
||||||
|
container_name: dnspod-yxip
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- ./.env:/app/.env:ro # 映射环境变量文件
|
||||||
|
- ./logs:/app/logs # 映射日志目录
|
282
main.py
Normal file
282
main.py
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
import json
|
||||||
|
import time
|
||||||
|
import requests
|
||||||
|
import schedule
|
||||||
|
from loguru import logger
|
||||||
|
from typing import Dict, List, Optional, Tuple
|
||||||
|
import config
|
||||||
|
|
||||||
|
# 配置日志
|
||||||
|
logger.add("dnspod.log", rotation="10 MB", level=config.LOG_LEVEL)
|
||||||
|
|
||||||
|
|
||||||
|
class DNSPodManager:
|
||||||
|
def __init__(self):
|
||||||
|
self.api_url = "https://dnsapi.cn"
|
||||||
|
self.common_params = {
|
||||||
|
"login_token": f"{config.DNSPOD_ID},{config.DNSPOD_TOKEN}",
|
||||||
|
"format": "json",
|
||||||
|
"lang": "cn",
|
||||||
|
"error_on_empty": "no",
|
||||||
|
}
|
||||||
|
# 记录每个域名每种记录类型的最后更新时间
|
||||||
|
self.last_update = {} # 格式: {domain: {'A': timestamp, 'AAAA': timestamp}}
|
||||||
|
|
||||||
|
def _api_request(self, path: str, data: Dict) -> Dict:
|
||||||
|
"""发送API请求"""
|
||||||
|
try:
|
||||||
|
url = f"{self.api_url}/{path}"
|
||||||
|
response = requests.post(url, data={**self.common_params, **data})
|
||||||
|
result = response.json()
|
||||||
|
|
||||||
|
if int(result.get("status", {}).get("code", -1)) != 1:
|
||||||
|
raise Exception(result.get("status", {}).get("message", "未知错误"))
|
||||||
|
|
||||||
|
return result
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"API请求失败: {str(e)}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
def get_optimal_ips(self) -> Dict:
|
||||||
|
"""获取优选IP"""
|
||||||
|
try:
|
||||||
|
response = requests.get(config.API_URL)
|
||||||
|
data = response.json()
|
||||||
|
if data.get("success"):
|
||||||
|
return data["data"]
|
||||||
|
raise Exception("API返回数据格式错误")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取优选IP失败: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def find_best_ip(self, ip_data: Dict, ip_version: str) -> Optional[Tuple[str, int]]:
|
||||||
|
"""查找延迟最低的IP,返回(IP, 延迟)"""
|
||||||
|
best_ip = None
|
||||||
|
min_latency = float("inf")
|
||||||
|
|
||||||
|
# 遍历所有线路
|
||||||
|
for line_key in ["CM", "CU", "CT"]:
|
||||||
|
if line_key in ip_data[ip_version]:
|
||||||
|
ips = ip_data[ip_version][line_key]
|
||||||
|
for ip_info in ips:
|
||||||
|
if ip_info["latency"] < min_latency:
|
||||||
|
min_latency = ip_info["latency"]
|
||||||
|
best_ip = (ip_info["ip"], ip_info["latency"])
|
||||||
|
|
||||||
|
return best_ip
|
||||||
|
|
||||||
|
def find_line_best_ip(
|
||||||
|
self, ip_data: Dict, ip_version: str, line_key: str
|
||||||
|
) -> Optional[Tuple[str, int]]:
|
||||||
|
"""查找指定线路延迟最低的IP"""
|
||||||
|
if line_key not in ip_data[ip_version]:
|
||||||
|
return None
|
||||||
|
|
||||||
|
ips = ip_data[ip_version][line_key]
|
||||||
|
if not ips:
|
||||||
|
return None
|
||||||
|
|
||||||
|
best_ip = min(ips, key=lambda x: x["latency"])
|
||||||
|
return (best_ip["ip"], best_ip["latency"])
|
||||||
|
|
||||||
|
def get_record_list(self, domain: str) -> List:
|
||||||
|
"""获取域名记录列表"""
|
||||||
|
try:
|
||||||
|
result = self._api_request("Record.List", {"domain": domain})
|
||||||
|
return result.get("records", [])
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取记录列表失败: {str(e)}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def delete_record(self, domain: str, record_id: str) -> bool:
|
||||||
|
"""删除DNS记录"""
|
||||||
|
try:
|
||||||
|
self._api_request(
|
||||||
|
"Record.Remove", {"domain": domain, "record_id": record_id}
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"删除记录失败: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def handle_record_conflicts(self, domain: str, sub_domain: str, record_type: str):
|
||||||
|
"""处理记录冲突"""
|
||||||
|
records = self.get_record_list(domain)
|
||||||
|
for record in records:
|
||||||
|
# 如果是要添加A记录,需要删除CNAME记录
|
||||||
|
if (
|
||||||
|
record_type == "A"
|
||||||
|
and record["type"] == "CNAME"
|
||||||
|
and record["name"] == sub_domain
|
||||||
|
):
|
||||||
|
logger.info(f"删除冲突的CNAME记录: {domain} - {sub_domain}")
|
||||||
|
self.delete_record(domain, record["id"])
|
||||||
|
# 如果是要添加CNAME记录,需要删除A记录
|
||||||
|
elif (
|
||||||
|
record_type == "CNAME"
|
||||||
|
and record["type"] == "A"
|
||||||
|
and record["name"] == sub_domain
|
||||||
|
):
|
||||||
|
logger.info(f"删除冲突的A记录: {domain} - {sub_domain}")
|
||||||
|
self.delete_record(domain, record["id"])
|
||||||
|
|
||||||
|
def update_record(
|
||||||
|
self,
|
||||||
|
domain: str,
|
||||||
|
sub_domain: str,
|
||||||
|
record_type: str,
|
||||||
|
line: str,
|
||||||
|
value: str,
|
||||||
|
ttl: int,
|
||||||
|
remark: str = "YXIP",
|
||||||
|
) -> bool:
|
||||||
|
"""更新DNS记录"""
|
||||||
|
try:
|
||||||
|
# 处理记录冲突
|
||||||
|
self.handle_record_conflicts(domain, sub_domain, record_type)
|
||||||
|
|
||||||
|
# 获取域名记录列表
|
||||||
|
records = self.get_record_list(domain)
|
||||||
|
|
||||||
|
# 查找匹配的记录
|
||||||
|
record_id = None
|
||||||
|
for record in records:
|
||||||
|
if (
|
||||||
|
record["name"] == sub_domain
|
||||||
|
and record["line"] == line
|
||||||
|
and record["type"] == record_type
|
||||||
|
):
|
||||||
|
record_id = record["id"]
|
||||||
|
break
|
||||||
|
|
||||||
|
# 更新或创建记录
|
||||||
|
data = {
|
||||||
|
"domain": domain,
|
||||||
|
"sub_domain": sub_domain,
|
||||||
|
"record_type": record_type,
|
||||||
|
"record_line": line,
|
||||||
|
"value": value,
|
||||||
|
"ttl": ttl,
|
||||||
|
"remark": remark,
|
||||||
|
}
|
||||||
|
|
||||||
|
if record_id:
|
||||||
|
data["record_id"] = record_id
|
||||||
|
self._api_request("Record.Modify", data)
|
||||||
|
else:
|
||||||
|
self._api_request("Record.Create", data)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"更新DNS记录失败: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def update_domain_records(self, domain_config: Dict) -> None:
|
||||||
|
"""更新单个域名的记录"""
|
||||||
|
domain = domain_config["domain"]
|
||||||
|
sub_domain = domain_config["sub_domain"]
|
||||||
|
record_type = domain_config["record_type"]
|
||||||
|
ttl = domain_config["ttl"]
|
||||||
|
remark = domain_config["remark"]
|
||||||
|
|
||||||
|
# 获取优选IP数据
|
||||||
|
ip_data = self.get_optimal_ips()
|
||||||
|
if not ip_data:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 获取对应版本的IP数据
|
||||||
|
ip_version = "v6" if record_type == "AAAA" else "v4"
|
||||||
|
if ip_version not in ip_data:
|
||||||
|
logger.warning(
|
||||||
|
f"未找到{ip_version}版本的IP数据,跳过更新 {domain} 的 {record_type} 记录"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 检查是否有可用的IP数据
|
||||||
|
has_ip_data = False
|
||||||
|
for line_key in ["CM", "CU", "CT"]:
|
||||||
|
if line_key in ip_data[ip_version] and ip_data[ip_version][line_key]:
|
||||||
|
has_ip_data = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not has_ip_data:
|
||||||
|
logger.warning(
|
||||||
|
f"没有可用的{ip_version}版本IP数据,跳过更新 {domain} 的 {record_type} 记录"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 先处理默认线路
|
||||||
|
best_ip = self.find_best_ip(ip_data, ip_version)
|
||||||
|
if best_ip:
|
||||||
|
ip, latency = best_ip
|
||||||
|
logger.info(
|
||||||
|
f"更新{record_type}记录: {domain} - {sub_domain} - 默认 - {ip} (延迟: {latency}ms)"
|
||||||
|
)
|
||||||
|
self.update_record(domain, sub_domain, record_type, "默认", ip, ttl, remark)
|
||||||
|
|
||||||
|
# 更新其他线路的记录
|
||||||
|
for line in domain_config["line"]:
|
||||||
|
if line == "默认":
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line == "移动":
|
||||||
|
line_key = "CM"
|
||||||
|
elif line == "联通":
|
||||||
|
line_key = "CU"
|
||||||
|
elif line == "电信":
|
||||||
|
line_key = "CT"
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line_key in ip_data[ip_version]:
|
||||||
|
best_ip = self.find_line_best_ip(ip_data, ip_version, line_key)
|
||||||
|
if best_ip:
|
||||||
|
ip, latency = best_ip
|
||||||
|
logger.info(
|
||||||
|
f"更新{record_type}记录: {domain} - {sub_domain} - {line} - {ip} (延迟: {latency}ms)"
|
||||||
|
)
|
||||||
|
self.update_record(
|
||||||
|
domain, sub_domain, record_type, line, ip, ttl, remark
|
||||||
|
)
|
||||||
|
|
||||||
|
def check_and_update(self):
|
||||||
|
"""检查并更新所有域名"""
|
||||||
|
current_time = time.time()
|
||||||
|
|
||||||
|
for domain_config in config.DOMAINS:
|
||||||
|
if not domain_config["enabled"]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
domain = domain_config["domain"]
|
||||||
|
record_type = domain_config["record_type"]
|
||||||
|
update_interval = domain_config["update_interval"] * 60 # 转换为秒
|
||||||
|
|
||||||
|
# 初始化域名的更新时间记录
|
||||||
|
if domain not in self.last_update:
|
||||||
|
self.last_update[domain] = {}
|
||||||
|
|
||||||
|
# 获取该记录类型的最后更新时间
|
||||||
|
last_update = self.last_update[domain].get(record_type, 0)
|
||||||
|
|
||||||
|
# 检查是否需要更新
|
||||||
|
if current_time - last_update >= update_interval:
|
||||||
|
logger.info(f"开始更新域名: {domain} 的 {record_type} 记录")
|
||||||
|
self.update_domain_records(domain_config)
|
||||||
|
self.last_update[domain][record_type] = current_time
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
manager = DNSPodManager()
|
||||||
|
|
||||||
|
# 首次运行,更新所有域名
|
||||||
|
manager.check_and_update()
|
||||||
|
|
||||||
|
# 每分钟检查一次是否需要更新
|
||||||
|
schedule.every(1).minutes.do(manager.check_and_update)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
schedule.run_pending()
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
118
readme.md
Normal file
118
readme.md
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
# DNSPOD 优选域名
|
||||||
|
|
||||||
|
根据API接口返回的IP地址, 在DNSPOD中创建或者更新域名的解析记录, 支持分线路和多权重。
|
||||||
|
|
||||||
|
支持docker运行, 需要配置DNSPOD的ID和TOKEN, 可以配置多个域名。默认每15分钟自动获取IP地址并更新DNSPOD。
|
||||||
|
|
||||||
|
## 特性
|
||||||
|
|
||||||
|
- 自动获取优选IP地址
|
||||||
|
- 支持IPv4和IPv6
|
||||||
|
- 支持移动、联通、电信三个线路
|
||||||
|
- 自动选择延迟最低的IP
|
||||||
|
- 支持多域名配置
|
||||||
|
- 可配置的更新间隔和TTL
|
||||||
|
- 完整的日志记录
|
||||||
|
- Docker支持
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 使用 Docker Compose
|
||||||
|
|
||||||
|
1. 创建配置文件:
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 编辑 `.env` 文件,填写您的配置:
|
||||||
|
```ini
|
||||||
|
# DNSPOD API 配置
|
||||||
|
DNSPOD_ID=your_id_here
|
||||||
|
DNSPOD_TOKEN=your_token_here
|
||||||
|
|
||||||
|
# 域名配置 - 域名1
|
||||||
|
DOMAIN_1=example1.com
|
||||||
|
SUB_DOMAIN_1=@ # 子域名,@ 表示根域名
|
||||||
|
REMARK_1=优选IP # 记录备注
|
||||||
|
TTL_1=600 # TTL值(秒)
|
||||||
|
UPDATE_INTERVAL_1=15 # 更新间隔(分钟)
|
||||||
|
IPV4_ENABLED_1=true # 是否启用IPv4记录
|
||||||
|
IPV6_ENABLED_1=true # 是否启用IPv6记录
|
||||||
|
ENABLED_1=true # 是否启用此域名配置
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 拉取并运行容器:
|
||||||
|
```bash
|
||||||
|
docker compose pull
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### 手动构建运行
|
||||||
|
|
||||||
|
1. 克隆仓库:
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/your-username/dnspod-yxip.git
|
||||||
|
cd dnspod-yxip
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 创建并编辑配置文件:
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
# 编辑 .env 文件
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 构建镜像:
|
||||||
|
```bash
|
||||||
|
docker compose build
|
||||||
|
```
|
||||||
|
|
||||||
|
4. 运行容器:
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## 配置说明
|
||||||
|
|
||||||
|
每个域名配置包含以下参数:
|
||||||
|
- `DOMAIN_n`: 域名
|
||||||
|
- `SUB_DOMAIN_n`: 子域名,@ 表示根域名,* 表示泛解析
|
||||||
|
- `REMARK_n`: 记录备注
|
||||||
|
- `TTL_n`: TTL值(秒)
|
||||||
|
- `UPDATE_INTERVAL_n`: 更新间隔(分钟)
|
||||||
|
- `IPV4_ENABLED_n`: 是否启用IPv4记录
|
||||||
|
- `IPV6_ENABLED_n`: 是否启用IPv6记录
|
||||||
|
- `ENABLED_n`: 是否启用此域名配置
|
||||||
|
|
||||||
|
其中 n 是域名编号(1, 2, 3...),可以配置任意数量的域名。
|
||||||
|
|
||||||
|
## 日志查看
|
||||||
|
|
||||||
|
日志文件保存在 `logs` 目录下:
|
||||||
|
```bash
|
||||||
|
# 查看实时日志
|
||||||
|
docker compose logs -f
|
||||||
|
|
||||||
|
# 查看日志文件
|
||||||
|
cat logs/dnspod.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## 更新
|
||||||
|
|
||||||
|
1. 拉取最新镜像:
|
||||||
|
```bash
|
||||||
|
docker compose pull
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 重新启动容器:
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## 贡献
|
||||||
|
|
||||||
|
欢迎提交 Issue 和 Pull Request!
|
||||||
|
|
||||||
|
## 许可证
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
requests>=2.31.0
|
||||||
|
python-dotenv>=1.0.0
|
||||||
|
schedule>=1.2.1
|
||||||
|
loguru>=0.7.2
|
Loading…
x
Reference in New Issue
Block a user