Skip to main content

小雫DNS Python SDK 和 CLI - 零依赖的 DNSHE 子域名管理工具

Project description

小雫DNS Python SDK + CLI (xndns)

零依赖的 DNSHE 子域名管理 Python 包,含同步/异步 SDK 和命令行工具

特性

  • 零依赖:仅用 Python 3.9+ 标准库(urllib / asyncio / json),无需 pip install requests
  • 同步 + 异步:提供 Client(同步)和 AsyncClient(异步)两种客户端
  • 三传输层:支持 rest / rpc / graphql,可通过参数或配置文件切换
  • 完整功能:子域名 CRUD / DNS 记录 CRUD / API 密钥管理 / 配额查询 / WHOIS / 安装检测
  • 多种配置方式:环境变量 / 配置文件(~/.xndnsrc)/ 构造参数
  • JSON 输出:CLI 所有命令支持 --json 切换为机器可读格式
  • 跨平台:Linux / macOS / Windows

安装

方式 1:从源码安装(推荐)

# 解压后进入目录
cd xndns-py

# 用户级安装(推荐,无需 sudo)
python install.py

# 或系统级安装(需要 sudo)
sudo python install.py --system

安装脚本会:

  1. 通过 pip install . 安装 xndns 包到当前 Python 环境
  2. ~/.local/bin(或 /usr/local/bin)创建 xndns 包装脚本
  3. 验证安装并提示如何配置 PATH

方式 2:开发模式安装

cd xndns-py
pip install -e .

适合开发调试,修改源码立即生效。

方式 3:直接运行(不安装)

# 直接通过 Python 模块运行
python -m xndns quota

# 或直接调用 cli.py
python xndns/cli.py help

需要在脚本所在目录或设置 PYTHONPATH

方式 4:发布到 PyPI 后

pip install xndns

卸载

python uninstall.py

会移除 pip 包、包装脚本和 ~/.xndnsrc 配置文件。

配置凭证

方式 1:使用 login 命令(推荐)

xndns login cfsd_xxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyy --host=http://dnshe.xiaona.cc.cd

凭证保存在 ~/.xndnsrc(权限 600)。

方式 2:环境变量

export XNDNS_API_KEY=cfsd_xxxxxxxxxxxxxxxx
export XNDNS_API_SECRET=yyyyyyyyyyyyyyyyyyyy
export XNDNS_HOST=http://dnshe.xiaona.cc.cd
# 可选:传输层(默认 rest)
export XNDNS_TRANSPORT=rpc

适合 CI/CD 或临时使用。

方式 3:配置文件

直接编辑 ~/.xndnsrc

{
  "apiKey": "cfsd_xxx",
  "apiSecret": "yyyy",
  "host": "http://dnshe.xiaona.cc.cd",
  "transport": "rest"
}

或用 config 命令:

xndns config show
xndns config set transport rpc
xndns config set host https://other-host

方式 4:在代码中直接传参

from xndns import Client

client = Client(
    api_key="cfsd_xxx",
    api_secret="yyyy",
    host="http://dnshe.xiaona.cc.cd",
    transport="rest",
)

SDK 使用

同步客户端

from xndns import Client

# 初始化(也可以用 Client.from_config() 从 ~/.xndnsrc 加载)
client = Client(
    api_key="cfsd_xxxxxxxxxxxxxxxx",
    api_secret="yyyyyyyyyyyyyyyyyyyy",
    host="http://dnshe.xiaona.cc.cd",
    transport="rest",  # "rest" | "rpc" | "graphql"
)

# 验证凭证
verify = client.auth.verify()
print(verify["account"]["quota"])
# {'used': 3, 'total': 7, 'available': 4}

# 列出子域名
subs = client.subdomains.list(per_page=10)
for s in subs["subdomains"]:
    print(s["full_domain"])

# 注册新子域名
created = client.subdomains.create(subdomain="myapp", rootdomain="us.kg")
print(created["full_domain"])

# 创建 DNS 记录
client.dns_records.create(
    subdomain_id=created["subdomain_id"],
    type="A",
    content="192.0.2.1",
    ttl=600,
)

# 查询 WHOIS
whois = client.whois.query("foo.example.com")

# 检测某域名是否安装了小雫DNS
detect = client.system.detect("example.com")
if detect["detected"]:
    print(f"检测到小雫DNS v{detect['version']}")

异步客户端

import asyncio
from xndns import AsyncClient

async def main():
    client = AsyncClient(
        api_key="cfsd_xxx",
        api_secret="yyyy",
        host="http://dnshe.xiaona.cc.cd",
    )

    # 并发查询配额和子域名列表
    quota, subs = await asyncio.gather(
        client.quota.get(),
        client.subdomains.list(per_page=10),
    )
    print(quota)
    print(subs)

    # 注册新子域名
    created = await client.subdomains.create("myapp", "us.kg")
    print(created["full_domain"])

asyncio.run(main())

错误处理

from xndns import Client, ApiError

client = Client(api_key="...", api_secret="...")

try:
    client.subdomains.create(subdomain="x", rootdomain="us.kg")
except ApiError as e:
    print(e.code)         # "quota_exceeded"
    print(e.message)      # "Quota exceeded"
    print(e.http_status)  # 429
    print(e.details)      # {"limit": 60, "reset_at": "..."}

SDK API 总览

命名空间 方法 对应 RPC method 说明
auth verify() auth.verify 验证凭证并返回配额摘要
subdomains list(...) subdomains.list 分页列出子域名
subdomains get(id) subdomains.get 获取子域名详情
subdomains create(subdomain, rootdomain) subdomains.create 注册子域名
subdomains delete(id) subdomains.delete 删除子域名
subdomains renew(id) subdomains.renew 续费子域名
dns_records list(subdomain_id) dns_records.list 列出 DNS 记录
dns_records create(subdomain_id, type, content, ...) dns_records.create 创建 DNS 记录
dns_records update(id, ...) dns_records.update 更新 DNS 记录
dns_records delete(id) dns_records.delete 删除 DNS 记录
api_keys list() api_keys.list 列出 API 密钥
api_keys create(name, ip_whitelist?) api_keys.create 创建 API 密钥
api_keys delete(key_id) api_keys.delete 删除 API 密钥
api_keys regenerate(key_id) api_keys.regenerate 重置 API Secret
quota get() quota.get 查询配额
whois query(domain) whois.query WHOIS 查询
system detect(domain) system.detect 检测目标域名
system selftest() - 自检

注:dnsRecordsapiKeysdns_recordsapi_keys 的 camelCase 别名,方便 JS 习惯的用户。

CLI 命令一览

基础

命令 说明
xndns login <key> <secret> [--host=URL] 保存凭证
xndns logout 清除凭证
xndns whoami 显示当前凭证
xndns config [show|set <key> <value>] 查看/修改配置
xndns help 显示帮助
xndns version 显示版本

子域名

命令 说明
xndns subdomains list [opts] 列出子域名
xndns subdomains get <id> 查看详情
xndns subdomains create <prefix> <root> 注册
xndns subdomains delete <id> 删除
xndns subdomains renew <id> 续费

list 选项:--page=N --per-page=N --search=KEY --status=STATUS --rootdomain=ROOT

DNS 记录

命令 说明
xndns dns list <subdomain_id> 列出记录
xndns dns create <subdomain_id> <type> <content> [--ttl=N] [--priority=N] [--name=NAME] 创建
xndns dns update <id> [--type=T] [--content=V] [--ttl=N] 更新
xndns dns delete <id> 删除

类型支持:A / AAAA / CNAME / MX / TXT / NS / SRV / CAA

密钥 / 配额 / WHOIS

命令 说明
xndns quota 查询配额
xndns whois <domain> WHOIS 查询
xndns keys list 列出 API 密钥
xndns keys create <name> [--ip-whitelist=IPS] 创建密钥
xndns keys delete <key_id> 删除密钥
xndns keys regenerate <key_id> 重置 Secret

检测

命令 说明
xndns detect <domain> 检测目标域名是否安装小雫DNS
xndns selftest 检测当前配置的主机

全局选项

选项 说明
--json 输出原始 JSON
--host=URL 临时覆盖主机
--transport=rest|rpc|graphql 临时覆盖传输层
--debug 显示完整错误堆栈

使用示例

# 登录
xndns login cfsd_0adc0ce77bc62a2903caf044ddb877a5 b190ba15... --host=http://dnshe.xiaona.cc.cd

# 查看配额
xndns quota
# 配额: 1/3 已用
#   基础: 3
#   邀请奖励: 0
#   可用: 2

# 列出子域名
xndns subdomains list --per-page=5
# 共 1 条,本页 1 条
#
#   #7477265796  mynmail.bbroot.com  [Registered]  2036-07-07 01:08:40

# 注册新子域名
xndns subdomains create myapp us.kg
# ✓ 已注册 myapp.us.kg (#7477265797)

# 创建 DNS 记录
xndns dns create 7477265797 A 192.0.2.1 --ttl=600
# ✓ 已创建 [A] 记录 (id=100)

# WHOIS 查询
xndns whois mynmail.bbroot.com
# 域名:   mynmail.bbroot.com
# 状态:   active
# 注册:   2026-07-07 01:08
# 到期:   2036-07-07 01:08
# 邮箱:   abuse@dnshe.com
# NS:     ns1.dnshe.com, ns2.dnshe.com

# 切换到 RPC 传输层
xndns config set transport rpc
xndns quota
# (相同输出,但底层走 JSON-RPC)

# JSON 输出(适合脚本)
xndns quota --json | python -c "import sys, json; print(json.load(sys.stdin)['quota']['available'])"
# 2

# 检测某域名是否安装小雫DNS
xndns detect example.com
# ✗ example.com 未安装小雫DNS

xndns selftest
# ✓ http://dnshe.xiaona.cc.cd 是小雫DNS v1.0.0
#   功能: rest, rpc, graphql, cli, sdk, docs

Python 脚本示例

# 找出所有即将到期的子域名(30 天内)
import time
from datetime import datetime
from xndns import Client

client = Client.from_config()
subs = client.subdomains.list(per_page=100)["subdomains"]
now = time.time()
for s in subs:
    if not s.get("expires_at"):
        continue
    try:
        dt = datetime.strptime(s["expires_at"], "%Y-%m-%d %H:%M:%S").timestamp()
        if dt - now < 30 * 86400:
            print(f"即将到期: {s['full_domain']} ({s['expires_at']})")
    except ValueError:
        pass
# 批量备份所有子域名的 DNS 记录
import json
from xndns import Client

client = Client.from_config()
subs = client.subdomains.list(per_page=500)["subdomains"]
for s in subs:
    recs = client.dns_records.list(s["id"])
    with open(f"dns-backup-{s['id']}.json", "w", encoding="utf-8") as f:
        json.dump({"subdomain": s, "dns_records": recs["records"]}, f, indent=2, ensure_ascii=False)
    print(f"已备份 {s['full_domain']}: {recs['count']} 条记录")
# 异步并发查询多个域名的 WHOIS
import asyncio
from xndns import AsyncClient

async def main():
    client = AsyncClient.from_config()
    domains = ["foo.example.com", "bar.example.com", "baz.example.com"]
    results = await asyncio.gather(*[client.whois.query(d) for d in domains])
    for d, r in zip(domains, results):
        status = "已注册" if r.get("registered") else "未注册"
        print(f"{d}: {status}")

asyncio.run(main())

传输层对比

传输层 优势 何时使用
rest (默认) 资源化 URL,缓存友好 日常使用
rpc 单端点,支持批量调用 需要批量操作时
graphql 按需取字段,减少传输量 只需要部分字段时

三种传输层在功能上完全等价,可以随时通过 xndns config set transport <value> 或构造参数 transport=... 切换。

错误处理

CLI 在出错时会返回非零退出码,错误信息格式:

[error_code] error_message

Python SDK 抛出 ApiError 异常,包含 code / message / http_status / details 字段。

常见错误码:

error_code HTTP 说明
auth_invalid_credentials 401 凭证无效
quota_exceeded 429 配额超限
rate_limit_exceeded 429 频率超限
subdomain_not_found 404 子域名不存在
provider_operation_failed 502 上游失败

完整错误码列表见 API 文档

配置文件

~/.xndnsrc 是 JSON 格式,权限 600:

{
  "apiKey": "cfsd_xxx",
  "apiSecret": "yyyy",
  "host": "http://dnshe.xiaona.cc.cd",
  "transport": "rest"
}

优先级:命令行参数 > 环境变量 > 配置文件 > 构造参数默认值。

系统要求

  • Python 3.9+
  • Linux / macOS / Windows
  • 网络可访问小雫DNS 主机

项目结构

xndns-py/
├── xndns/
│   ├── __init__.py      # 包入口,暴露 Client / AsyncClient / ApiError
│   ├── __main__.py      # python -m xndns 入口
│   ├── cli.py           # CLI 实现(与 Node 版功能对齐)
│   ├── client.py        # Client + AsyncClient + 命名空间
│   ├── config.py        # Config + ApiError
│   └── transport.py     # REST / RPC / GraphQL 传输层
├── pyproject.toml       # 包定义(PEP 621)
├── install.py           # 安装脚本
├── uninstall.py         # 卸载脚本
├── README.md
├── LICENSE
└── CHANGELOG.md

开发

# 开发模式安装
pip install -e .

# 运行测试(如果安装了 dev 依赖)
pytest

# 代码检查
ruff check xndns/

License

MIT

相关

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

xndns-1.0.0.tar.gz (21.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

xndns-1.0.0-py3-none-any.whl (21.1 kB view details)

Uploaded Python 3

File details

Details for the file xndns-1.0.0.tar.gz.

File metadata

  • Download URL: xndns-1.0.0.tar.gz
  • Upload date:
  • Size: 21.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for xndns-1.0.0.tar.gz
Algorithm Hash digest
SHA256 7ee0d939983c1acc94b0e3d0e74f32914be5a967f153067db557b16ac862cdfd
MD5 9f7fa87ce5b1d8c737204813946ba7fa
BLAKE2b-256 be4905f34df87c7d2808c382c254accd33416fb7a0fd315b3980b4be7914a849

See more details on using hashes here.

File details

Details for the file xndns-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: xndns-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 21.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for xndns-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ddfc6466738d6e8d49372bd5ccf5d1e99b2a88125e901c4777e149284cc34970
MD5 7b0af94e3fbf195d24e5ba58cc8b42ff
BLAKE2b-256 253878ecb1aef77dcbbb63d23b1913e1b46c8e15e5bddb54697bb2e2c6252743

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page