mirror of
https://github.com/NewFuture/DDNS.git
synced 2026-10-19 16:15:45 +00:00
* Initial plan * feat(cli): implement task subcommand with cross-platform scheduler support Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * docs(cli): add task subcommand documentation Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(cli,task): address review feedback - move task handler to cli, remove defaults, improve fallback status checking Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * refactor(task): convert TaskManager class to functions and handle task commands entirely in CLI Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(cli,tests): remove handle_task_command from exports and rename test file Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * feat(task): add enable and disable commands for scheduled tasks * add task tests * refactor(task): simplify task command handling and update status retrieval * update task * test(task): update subcommand tests to assert exit codes and rename delete to uninstall * Add unit tests for DDNS task subcommand functionality and enhance existing tests - Introduced comprehensive unit tests for the DDNS task subcommand, covering help, status, install, enable, disable, delete, and argument handling. - Added tests for file I/O operations in the ddns.util.fileio module, ensuring proper directory creation, file reading/writing, and error handling. - Enhanced test coverage for task scheduling functions, including systemd and cron installations, with additional checks for argument handling. - Updated existing tests to improve compatibility and reliability across different operating systems, particularly for path handling on Windows. - Refactored task command building tests to ensure correct command generation with various argument types, including lists and boolean flags. * refactor(task): improve command execution and path handling for DDNS tasks * remove DEVNULL * fix(task): ensure proper task interval handling and add sudo for task commands in test script * add and fix tests * fix lint * refactor(cli): organize DDNS arguments into groups and improve task subcommand handling * fix tests * +x * fix py2 and types * rm sudo * fix(tests): use sudo for task installation and uninstallation in test script * update test command * always force * fix format * docs: add task management parameters and usage examples to CLI documentation * update log * docs: update CLI task management instructions and examples * fix py2 * fix task in python2 * fix lint * refactor tasks * update tests * add schtasks-based task scheduler implementation and unit tests * Enhance scheduler functionality and improve error handling - Added a 60-second timeout to subprocess calls in BaseScheduler to prevent hanging. - Improved error logging in BaseScheduler to capture command failures. - Refactored CronScheduler to remove unnecessary variable initialization. - Updated SystemdScheduler to handle PermissionError and provide user feedback for permission issues. - Enhanced file I/O functions in fileio.py to return default values on exceptions. - Improved unit tests for scheduler classes to ensure robust testing of functionality and error handling. - Added lifecycle operation tests for SystemdScheduler to validate install, enable, disable, and uninstall processes. - Ensured consistent status reporting across multiple calls in SystemdScheduler. - Cleaned up test code for better readability and maintainability. * fix lint * Refactor scheduler command execution and enhance error handling in BaseScheduler and LaunchdScheduler tests * fix tests * Add unittest configuration to settings and improve SchtasksScheduler error handling * Refactor code for improved readability and maintainability in various test files and the DnspodProvider class * Refactor LaunchdScheduler tests for improved readability and error handling * fix py2 * fix mac launchd test * fix lint * fix macos tests * fix windows test * patch readme * update doc * Update tests/test_scheduler_cron.py Co-authored-by: Copilot <175728472+Copilot@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>
115 lines
4.3 KiB
Python
115 lines
4.3 KiB
Python
# coding=utf-8
|
|
"""
|
|
DNSPOD API
|
|
@doc: https://docs.dnspod.cn/api/
|
|
@author: NewFuture
|
|
"""
|
|
|
|
from ._base import BaseProvider, TYPE_FORM
|
|
|
|
|
|
class DnspodProvider(BaseProvider):
|
|
"""
|
|
DNSPOD API
|
|
DNSPOD 接口解析操作库
|
|
"""
|
|
|
|
endpoint = "https://dnsapi.cn"
|
|
content_type = TYPE_FORM
|
|
|
|
DefaultLine = "默认"
|
|
|
|
def _request(self, action, extra=None, **params):
|
|
# type: (str, dict | None, **(str | int | bytes | bool | None)) -> dict
|
|
"""
|
|
发送请求数据
|
|
|
|
Send request to DNSPod API.
|
|
Args:
|
|
action (str): API 动作/Action
|
|
extra (dict|None): 额外参数/Extra params
|
|
params (dict): 其它参数/Other params
|
|
Returns:
|
|
dict: 响应数据/Response data
|
|
"""
|
|
# 过滤掉None参数
|
|
if extra:
|
|
params.update(extra)
|
|
params = {k: v for k, v in params.items() if v is not None}
|
|
params.update({"login_token": "{0},{1}".format(self.id, self.token), "format": "json"})
|
|
data = self._http("POST", "/" + action, body=params)
|
|
if data and data.get("status", {}).get("code") == "1": # 请求成功
|
|
return data
|
|
else: # 请求失败
|
|
error_msg = "Unknown error"
|
|
if data and isinstance(data, dict):
|
|
error_msg = data.get("status", {}).get("message", "Unknown error")
|
|
self.logger.warning("DNSPod API error: %s", error_msg)
|
|
return data
|
|
|
|
def _create_record(self, zone_id, subdomain, main_domain, value, record_type, ttl, line, extra):
|
|
# type: (str, str, str, str, str, int | str | None, str | None, dict) -> bool
|
|
"""https://docs.dnspod.cn/api/add-record/"""
|
|
res = self._request(
|
|
"Record.Create",
|
|
extra=extra,
|
|
domain_id=zone_id,
|
|
sub_domain=subdomain,
|
|
value=value,
|
|
record_type=record_type,
|
|
record_line=line or self.DefaultLine,
|
|
ttl=ttl,
|
|
)
|
|
record = res and res.get("record")
|
|
if record: # 记录创建成功
|
|
self.logger.info("Record created: %s", record)
|
|
return True
|
|
else: # 记录创建失败
|
|
self.logger.error("Failed to create record: %s", res)
|
|
return False
|
|
|
|
def _update_record(self, zone_id, old_record, value, record_type, ttl, line, extra):
|
|
# type: (str, dict, str, str, int | str | None, str | None, dict) -> bool
|
|
"""https://docs.dnspod.cn/api/modify-records/"""
|
|
record_line = (line or old_record.get("line") or self.DefaultLine).replace("Default", "default")
|
|
res = self._request(
|
|
"Record.Modify",
|
|
domain_id=zone_id,
|
|
record_id=old_record.get("id"),
|
|
sub_domain=old_record.get("name"),
|
|
record_type=record_type,
|
|
value=value,
|
|
record_line=record_line,
|
|
extra=extra,
|
|
)
|
|
record = res and res.get("record")
|
|
if record: # 记录更新成功
|
|
self.logger.debug("Record updated: %s", record)
|
|
return True
|
|
else: # 记录更新失败
|
|
self.logger.error("Failed to update record: %s", res)
|
|
return False
|
|
|
|
def _query_zone_id(self, domain):
|
|
# type: (str) -> str | None
|
|
"""查询域名信息 https://docs.dnspod.cn/api/domain-info/"""
|
|
res = self._request("Domain.Info", domain=domain)
|
|
if res and isinstance(res, dict):
|
|
return res.get("domain", {}).get("id")
|
|
return None
|
|
|
|
def _query_record(self, zone_id, subdomain, main_domain, record_type, line, extra):
|
|
# type: (str, str, str, str, str | None, dict) -> dict | None
|
|
"""查询记录 list 然后逐个查找 https://docs.dnspod.cn/api/record-list/"""
|
|
res = self._request("Record.List", domain_id=zone_id, sub_domain=subdomain, record_type=record_type, line=line)
|
|
# length="3000"
|
|
records = res.get("records", [])
|
|
n = len(records)
|
|
if not n:
|
|
self.logger.warning("No record found for [%s] %s<%s>(line: %s)", zone_id, subdomain, record_type, line)
|
|
return None
|
|
if n > 1:
|
|
self.logger.warning("%d records found for %s<%s>(%s):\n %s", n, subdomain, record_type, line, records)
|
|
return next((r for r in records if r.get("name") == subdomain), None)
|
|
return records[0]
|