Files
6c24557505 feat(cli): add task subcommand for automated scheduled task management (#541)
* 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>
2025-08-07 17:58:43 +00:00

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]