mirror of
https://github.com/NewFuture/DDNS.git
synced 2026-10-21 04:56:00 +00:00
* Initial plan * feat(provider): add EdgeOne DNS provider for non-accelerated domains Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * docs(provider): add EdgeOne DNS documentation for non-accelerated domains Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(provider): update EdgeOne provider aliases per feedback - Acceleration domains: edgeone, edgeone_acc, teo_acc - DNS records: edgeone_dns, teo_dns, edgeone_noacc - Removed: teo, tencentedgeone, neo_acc, neo - Updated documentation to reflect new aliases Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * feat(provider): add extra parameter support and update documentation index - Add edgeone_dns to provider index documentation (README.md) - Add "teo" alias for backward compatibility (not documented) - Enable EdgeOneProvider to support both acceleration and DNS records via extra['dns_record'] parameter - Update provider aliases and documentation Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * docs: create comprehensive new-provider agent guide - Add detailed step-by-step guide for creating new DNS providers - Include API analysis, implementation, testing, and documentation steps - Provide code templates and examples for both BaseProvider and SimpleProvider - Add completion checklist and common pitfalls - Document all requirements including compatibility checks and index updates Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * refactor(provider): change extra parameter from dns_record to teoDomainType - Change EdgeOneProvider to use teoDomainType parameter instead of dns_record - Support values: "dns" or "acceleration" (default) - Parameter is case-insensitive - EdgeOneDnsProvider now uses extra parameter to control behavior - All 55 tests passing Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * refactor(provider): simplify EdgeOneDnsProvider with helper method - Extract common extra parameter preparation into _prepare_extra() method - Reduces code duplication across _query_record, _create_record, _update_record - All 55 tests passing Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * refactor(provider): make EdgeOneDnsProvider more elegant and concise - Use class attribute _default_domain_type = "dns" for clarity - Replace _prepare_extra() with inline setdefault() for more Pythonic code - setdefault() allows users to override teoDomainType if needed - All 55 tests passing Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * refactor(provider): use options instead of extra for teoDomainType - Move teoDomainType from per-call extra to instance-level options - More appropriate since provider type is fixed per instance - EdgeOneProvider reads teoDomainType from self.options at initialization - EdgeOneDnsProvider sets teoDomainType="dns" in __init__ - Simplified implementation: 28 lines vs 43 lines for EdgeOneDnsProvider - All 55 tests passing Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * refactor(provider): support both extra and options for teoDomainType with priority - Remove __init__ from EdgeOneProvider, read directly from self.options - Support teoDomainType in both extra (per-call) and options (initialization) - extra parameter has higher priority than options - Priority: extra > options > default ("acceleration") - All 55 tests passing Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * docs(provider): add teoDomainType parameter documentation for EdgeOne - Document how to switch between acceleration domains and DNS records - Explain extra.teoDomainType parameter usage - Add configuration examples for both modes - Update parameter tables with teoDomainType and extra - Document priority: extra > initialization parameter - Add bilingual documentation (Chinese and English) Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * docs(provider): remove initialization parameter config example from EdgeOne - Remove the example showing teoDomainType via initialization parameters - Only document usage via extra parameter - Remove priority note since only one method is documented - Keep recommendation to use EdgeOneDnsProvider for cleaner code - Update both Chinese and English documentation Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * docs(agent): update new-provider guide with relative paths and schema notes - Update CLI compatibility note to mention checking --dns parameter choices - Change schema note to only require v4.0 (latest format) updates - Replace "multi-provider format" with "latest schema format" - Convert all absolute paths to relative paths from project root - Remove /home/runner/work/DDNS/DDNS/ prefix from all file paths Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * docs(agent): correct latest schema version to v4.1 - Update schema compatibility section to reference v4.1 instead of v4.0 - v4.1 is the latest schema format that requires updates Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * feat(config): add edgeone_dns to CLI and schema v4.1 - Add edgeone_dns to CLI --dns parameter choices - Add edgeone_dns to schema v4.1 provider enum (both legacy and multi-provider sections) - Update provider descriptions to include EdgeOne DNS Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Refactor labels_to_prompts_mapping format * fix(provider): filter teoDomainType from extra params before API call - Remove teoDomainType from extra dict before passing to Tencent Cloud API - Fixes "UnknownParameter - The parameter `extra_teoDomainType` is not recognized" error - teoDomainType is internal parameter for routing, not for API requests - All 55 tests passing Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * docs(provider): update schema references to v4.1.json in EdgeOne docs - Update all $schema references from v4.0.json to v4.1.json - Applied to edgeone.md, edgeone.en.md, edgeone_dns.md, edgeone_dns.en.md - Ensures documentation references the latest schema format Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * Changes before error encountered Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(config): preserve nested extra dict from JSON/ENV config - Process "extra" dict before extra_ prefixed keys to avoid flattening - Fixes issue where {"extra":{"teoDomainType":"dns"}} was incorrectly transformed to {"extra_teoDomainType":"dns"} - Maintains backward compatibility with extra_ prefixed keys - Priority: explicit extra dict > extra_ prefixed keys > unknown keys Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update tests/test_provider_edgeone_dns.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update tests/test_provider_edgeone_dns.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * style: apply ruff formatting fixes - Format 9 files with ruff - Fix 2 linting issues automatically - One complexity warning remains (C901) but doesn't affect functionality - All code now follows consistent formatting standards Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * docs(agent): add ruff installation instructions to new-provider guide - Add pip3 install ruff command before formatting/linting steps - Ensures developers have required tools before running verification - Prevents common setup errors Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * docs(config): clarify extra dict and extra_ prefix priority within config sources - Add comments explaining that extra_ prefixed keys override nested extra dict values within the same source - Improves code documentation clarity for config flattening logic - Addresses feedback about intra-source priority ordering Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * refactor(config): reduce complexity of _collect_extra method - Extract _process_extra_from_source helper method to reduce cyclomatic complexity - Fixes ruff C901 error (complexity was 14, now under threshold) - Maintains all existing functionality and test coverage - All 60 extra-related tests passing Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> Co-authored-by: New Future <NewFuture@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
154 lines
5.3 KiB
Python
Executable File
154 lines
5.3 KiB
Python
Executable File
# -*- coding:utf-8 -*-
|
|
"""
|
|
DDNS
|
|
@author: NewFuture, rufengsuixing
|
|
"""
|
|
|
|
import sys
|
|
from io import TextIOWrapper
|
|
from logging import getLogger
|
|
from subprocess import check_output
|
|
|
|
from . import ip
|
|
from .__init__ import __description__, __version__, build_date
|
|
from .cache import Cache
|
|
from .config import Config, load_configs # noqa: F401
|
|
from .provider import SimpleProvider, get_provider_class # noqa: F401
|
|
|
|
logger = getLogger()
|
|
|
|
|
|
def get_ip(ip_type, rules):
|
|
"""
|
|
get IP address
|
|
"""
|
|
if rules is False: # disabled
|
|
return False
|
|
for i in rules:
|
|
try:
|
|
logger.debug("get_ip:(%s, %s)", ip_type, i)
|
|
if str(i).isdigit(): # 数字 local eth
|
|
return getattr(ip, "local_v" + ip_type)(i)
|
|
elif i.startswith("cmd:"): # cmd
|
|
return str(check_output(i[4:]).strip().decode("utf-8"))
|
|
elif i.startswith("shell:"): # shell
|
|
return str(check_output(i[6:], shell=True).strip().decode("utf-8"))
|
|
elif i.startswith("url:"): # 自定义 url
|
|
return getattr(ip, "public_v" + ip_type)(i[4:])
|
|
elif i.startswith("regex:"): # 正则 regex
|
|
return getattr(ip, "regex_v" + ip_type)(i[6:])
|
|
else:
|
|
return getattr(ip, i + "_v" + ip_type)()
|
|
except Exception as e:
|
|
logger.error("Failed to get %s address: %s", ip_type, e)
|
|
return None
|
|
|
|
|
|
def update_ip(dns, cache, index_rule, domains, record_type, config):
|
|
# type: (SimpleProvider, Cache | None, list[str]|bool, list[str], str, Config) -> bool | None
|
|
"""
|
|
更新IP并变更DNS记录
|
|
"""
|
|
if not domains:
|
|
return None
|
|
|
|
ip_type = "4" if record_type == "A" else "6"
|
|
address = get_ip(ip_type, index_rule)
|
|
if not address:
|
|
logger.error("Fail to get %s address!", ip_type)
|
|
return False
|
|
|
|
update_success = False
|
|
|
|
for domain in domains:
|
|
domain = domain.lower()
|
|
cache_key = "{}:{}".format(domain, record_type)
|
|
if cache and cache.get(cache_key) == address:
|
|
logger.info("%s[%s] address not changed, using cache: %s", domain, record_type, address)
|
|
update_success = True
|
|
else:
|
|
try:
|
|
result = dns.set_record(
|
|
domain, address, record_type=record_type, ttl=config.ttl, line=config.line, **config.extra
|
|
)
|
|
if result:
|
|
logger.warning("set %s[IPv%s]: %s successfully.", domain, ip_type, address)
|
|
update_success = True
|
|
if isinstance(cache, dict):
|
|
cache[cache_key] = address
|
|
else:
|
|
logger.error("Failed to update %s record for %s", record_type, domain)
|
|
except Exception as e:
|
|
logger.exception("Failed to update %s record for %s: %s", record_type, domain, e)
|
|
return update_success
|
|
|
|
|
|
def run(config):
|
|
# type: (Config) -> bool
|
|
"""
|
|
Run the DDNS update process
|
|
"""
|
|
# 设置IP模块的SSL验证配置
|
|
ip.ssl_verify = config.ssl
|
|
|
|
# dns provider class
|
|
provider_class = get_provider_class(config.dns)
|
|
dns = provider_class(
|
|
config.id, config.token, endpoint=config.endpoint, logger=logger, proxy=config.proxy, ssl=config.ssl
|
|
)
|
|
cache = Cache.new(config.cache, config.md5(), logger)
|
|
return (
|
|
update_ip(dns, cache, config.index4, config.ipv4, "A", config) is not False
|
|
and update_ip(dns, cache, config.index6, config.ipv6, "AAAA", config) is not False
|
|
)
|
|
|
|
|
|
def main():
|
|
stdout = sys.stdout # pythonw 模式无 stdout
|
|
if stdout and stdout.encoding and stdout.encoding.lower() != "utf-8" and hasattr(stdout, "buffer"):
|
|
# 兼容windows 和部分ASCII编码的老旧系统
|
|
sys.stdout = TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
|
|
sys.stderr = TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
|
|
|
|
# Windows 下输出一个空行
|
|
if stdout and sys.platform.startswith("win"):
|
|
stdout.write("\r\n")
|
|
|
|
logger.name = "ddns"
|
|
|
|
# 使用多配置加载器,它会自动处理单个和多个配置
|
|
configs = load_configs(__description__, __version__, build_date)
|
|
|
|
if len(configs) == 1:
|
|
# 单个配置,使用原有逻辑(向后兼容)
|
|
config = configs[0]
|
|
success = run(config)
|
|
if not success:
|
|
sys.exit(1)
|
|
else:
|
|
# 多个配置,使用新的批处理逻辑
|
|
overall_success = True
|
|
for i, config in enumerate(configs):
|
|
# 如果log_level有值则设置setLevel
|
|
if hasattr(config, "log_level") and config.log_level:
|
|
logger.setLevel(config.log_level)
|
|
logger.info("Running configuration %d/%d", i + 1, len(configs))
|
|
# 记录当前provider
|
|
logger.info("Using DNS provider: %s", config.dns)
|
|
success = run(config)
|
|
if not success:
|
|
overall_success = False
|
|
logger.error("Configuration %d failed", i + 1)
|
|
else:
|
|
logger.info("Configuration %d completed successfully", i + 1)
|
|
|
|
if not overall_success:
|
|
logger.error("Some configurations failed")
|
|
sys.exit(1)
|
|
else:
|
|
logger.info("All configurations completed successfully")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|