* Initial plan * feat(docs): add static documentation website builder script Add build_docs.py script to convert markdown docs to static HTML website with: - Responsive navigation bar with collapsible directory tree - In-page table of contents (right sidebar, hidden on small screens) - Language switcher (Chinese/English) - Pure static HTML output (no server-side processing) - Footer with GitHub links and edit page link - Mobile-friendly responsive design Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * style: format build_docs.py with ruff Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * refactor(docs): use MkDocs with Material theme instead of custom parser Replace custom markdown parser with industry-standard tools: - MkDocs: Popular static site generator for documentation - Material for MkDocs: Modern, feature-rich theme Benefits: - Mature, well-tested codebase used by thousands of projects - Built-in features: search, navigation, TOC, code highlighting - Active development with regular updates - Extensive documentation and community support - Beautiful, responsive design out of the box - Easy to maintain with configuration-based approach Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * refactor(docs): use VitePress instead of MkDocs for documentation Replace MkDocs with VitePress - a Vue-powered static site generator: - VitePress: Modern, fast, designed for documentation - Vue 3 + Vite stack with instant HMR - Built-in i18n, search, dark mode, responsive design - Chinese-friendly (created by Evan You) Features: - Responsive navigation with collapsible sidebar - In-page TOC automatically generated - Bilingual support (Chinese/English) - Local search with keyboard shortcuts - Light/Dark mode toggle - Edit page links to GitHub - Pure static HTML output Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * feat(docs): add automated setup and build with npm run build Add automated directory setup for VitePress build: - Created scripts/setup-docs.mjs to auto-setup docs structure - Updated package.json with 'build' command that runs setup + build - Updated docs:dev to include setup automatically - Simplified workflow to one command: npm run build Changes: - npm run build now handles everything automatically - No need for separate bash script - Directory structure created on-demand - Updated BUILD_DOCS.md with simplified instructions Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(docs): improve VitePress configuration per feedback Addressed all feedback items: 1. Fixed edit link mapping: index.html now correctly maps to README.md/README.en.md in GitHub 2. Added schema directory to build output via public/ directory 3. Removed /doc/ prefix from all URLs - doc/ is now the web root 4. Fixed path references in README files during setup Changes: - Updated editLink to use function for proper README mapping - Modified setup script to copy doc/ contents to docs root (not docs/doc/) - Added schema/ to public/ directory for static asset serving - Fixed /doc/ path references in copied README files - Updated all navigation and sidebar paths to remove /doc/ prefix URLs now work as: - / (homepage from README.md) - /docker, /install (no /doc/ prefix) - /config/cli, /providers/dnspod (clean paths) - /schema/v4.0.json (accessible in build) Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(docs): preserve original /doc/ paths in documentation Per user feedback, reverted path modifications: - No longer replace /doc/ paths in README files - Restored doc/ directory structure (docs/doc/) - Updated VitePress navigation to use /doc/ prefix - Edit links correctly map to source files in doc/ directory Changes: - setup-docs.mjs: removed path replacement, copy doc/ to docs/doc/ - config.mts: restored /doc/ prefix in all navigation and sidebar paths - URLs now match original documentation: /doc/docker, /doc/providers/dnspod This preserves the original documentation structure and paths as they appear in the source repository. Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(docs): remove /doc/ prefix from URLs and ignore intermediate files Per user feedback, changed URLs from /doc/xxx.html to /xxx.html: Changes: 1. Updated setup-docs.mjs to copy doc/ contents to docs/ root (not docs/doc/) 2. Modified path replacement to handle both links and image src attributes 3. Updated VitePress config.mts to remove /doc/ prefix from all nav/sidebar paths 4. Updated .gitignore to exclude intermediate copied files from commits 5. Removed docs/index.md and docs/en/index.md from git tracking URLs now: - /docker.html instead of /doc/docker.html - /providers/dnspod.html instead of /doc/providers/dnspod.html - /config/cli.html instead of /doc/config/cli.html - Images at /img/ddns.svg instead of /doc/img/ddns.svg - Schema files remain at /schema/v4.0.json Intermediate files generated during build are now gitignored and won't be committed. Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * refactor(docs): move build scripts to doc/ directory Moved documentation build scripts from root and scripts/ to doc/ directory: - Moved scripts/setup-docs.mjs → doc/setup-docs.mjs - Moved build_docs.sh → doc/build_docs.sh - Removed empty scripts/ directory - Updated package.json to reference new script locations - Updated BUILD_DOCS.md with new directory structure This organizes all documentation-related files under doc/ directory, keeping the repository root cleaner. Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * refactor(docs): consolidate into single doc/ directory Eliminated docs/ directory, consolidated everything under doc/: - Moved docs/.vitepress/ → doc/.vitepress/ - Removed docs/ directory entirely - Updated package.json to use doc/ instead of docs/ - Updated .gitignore to reflect new structure (doc/.vitepress/dist/, doc/index.md, doc/en/, doc/public/) - Updated setup-docs.mjs to generate files within doc/ - Updated BUILD_DOCS.md documentation Structure now: - doc/ contains source files, VitePress config, and generated intermediate files - All build artifacts stay within doc/.vitepress/dist/ - Simpler single-directory structure without doc/docs nesting Commands remain the same: - npm run build - npm run docs:dev - npm run docs:preview Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * refactor(docs): move package.json to doc/ directory Moved package.json to doc/ directory to consolidate all documentation-related files: - Moved package.json from root → doc/package.json - Updated package.json scripts to use relative paths (. instead of doc) - Updated BUILD_DOCS.md with new usage instructions (cd doc first) - Updated build_docs.sh to work from within doc/ directory - .gitignore already handles node_modules/ globally All documentation dependencies and configuration now in single doc/ directory. Usage: ```bash cd doc npm install npm run build ``` Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * feat(docs): add install.sh to public, auto-redirects, and ensure README→index.html mapping Implemented three enhancements: 1. Copy install.sh to public/ directory - install.sh now accessible at /install.sh in build - Users can download directly from documentation site 2. Add automatic redirects for old URLs - Created custom VitePress theme with redirect logic - /doc/xxx URLs redirect to /xxx - /doc/xxx.en.html URLs redirect to /en/xxx.html - Maintains backward compatibility with old links 3. README.md renders as index.html with correct edit links - Already configured: index.md maps to README.md for editing - en/index.md maps to README.en.md for editing - Edit links work correctly for both Chinese and English homepages Updated BUILD_DOCS.md with new features documentation. Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * feat(docs): upgrade to VitePress 2.0 (next) Upgraded VitePress from v1.x to v2.0 (next tag): - Updated package.json to use "vitepress": "next" - Currently using v2.0.0-alpha.15 - Tested build successfully - all features working - Updated BUILD_DOCS.md to reflect VitePress 2.0 VitePress 2.0 benefits: - Better performance and faster build times - Improved developer experience - Enhanced features and bug fixes - Future-ready with latest Vue 3 + Vite improvements Build tested and confirmed working with no breaking changes. Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(docs): remove redundant build script and fix copyright years Changes per user feedback: 1. Removed doc/build_docs.sh (redundant - standard npm commands sufficient) 2. Updated copyright in config.mts line 133: "Copyright © 2016 ~ 2024 NewFuture" 3. Updated copyright in config.mts line 305: "Copyright © 2016 ~ 2024 NewFuture" Usage now simplified to standard npm commands: - cd doc && npm install - npm run build - npm run docs:dev - npm run docs:preview Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(docs): dynamic copyright year, move redirects to esa.js, update providers link Per user feedback: 1. Made copyright year dynamic: `${new Date().getFullYear()}` (line 305) 2. Moved redirect logic from theme/index.ts to esa.js for server-side handling 3. Changed providers link from "概述 /providers/README" to "所有服务商 /providers/index" (line 70) Changes: - config.mts: Dynamic copyright calculation, updated providers navigation - esa.js: Added redirect logic for /doc/ URLs (301 permanent redirects) - theme/index.ts: Removed client-side redirect logic (now handled server-side) Redirects now handled at edge function level for better SEO and performance. Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(docs): improve redirects, provider link, and add code-to-GitHub link conversion Per user feedback (comment #3736692270): 1. Moved .en.html redirect logic before general /doc/ redirect in esa.js 2. Added /index.en.html → /en/ redirect 3. Changed providers/README URL to providers/ (line 39) 4. Added markdown transformer to convert code links (e.g., /ddns/provider/dnspod.py) to GitHub links Changes: - esa.js: Reordered redirect logic (en first), added index.en.html redirect - config.mts: Changed providers link from /providers/README to /providers/ - config.mts: Added markdown.config to transform inline code paths to GitHub links Code paths like `/ddns/provider/dnspod.py` in documentation will now automatically link to GitHub repository. Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * feat(docs): enable dead link check, add sitemap, llms.txt, and GitHub Actions build Per user feedback (comment #3736806631): 1. Changed redirects to 301 permanent redirects in esa.js (line 183) 2. Enabled dead link checking in config.mts (ignoreDeadLinks: false) 3. Added sitemap generation (sitemap.hostname: https://ddns.newfuture.cc) 4. Added llms.txt generation via buildEnd hook for AI/LLM context 5. Created GitHub Actions workflow (.github/workflows/build-docs.yml) to build docs on push Changes: - esa.js: Clarified 301 redirect comment - config.mts: Enabled dead link check, added sitemap config, added buildEnd hook for llms.txt - .github/workflows/build-docs.yml: New workflow to build and optionally deploy docs GitHub Action triggers on: - Push to master/main (doc/, README*, schema/ paths) - Pull requests - Manual workflow dispatch Build output includes sitemap.xml and llms.txt for better SEO and AI integration. Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(docs): update Node.js to v24, retention to 7 days, simplify BUILD_DOCS Per user feedback (comment #3736833266): 1. Updated Node.js version from v20 to v24 in build-docs.yml (line 33) 2. Changed artifact retention from 30 to 7 days (line 50) 3. Simplified BUILD_DOCS.md - removed verbose explanations, kept essentials Changes: - .github/workflows/build-docs.yml: Node 24, 7-day retention - BUILD_DOCS.md: Condensed from 327 to 40 lines, focused on quick start and essential commands BUILD_DOCS.md now provides concise reference instead of lengthy tutorial. 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(docs): simplify link transformer, extract llms.txt template, add docs agent Per user feedback (comment #3737275691): 1. Simplified markdown transformer to only process link (a tag) hrefs instead of code_inline tokens 2. Extracted llms.txt content to separate template file (doc/llms.txt) with: - Complete documentation structure with all page URLs - All schema file URLs (v4.0, v2.8, v2) - Template variable {{DATE}} for build-time replacement 3. Deleted BUILD_DOCS.md and created .github/agents/docs.agent.md: - Comprehensive documentation maintenance agent - Full guide for updating docs, navigation, providers - Build/test procedures and common tasks - Guidelines for writing and link handling Changes: - config.mts: Transform link hrefs (not inline code), read llms.txt from template - doc/llms.txt: New template with full doc/schema URLs - .github/agents/docs.agent.md: New documentation maintenance agent - BUILD_DOCS.md: Deleted (replaced by agent instructions) Link transformer now only modifies existing <a> tags, preserving markdown styling. Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(docs): add metadata to docs agent, fix markdown syntax, add v4.1.json schema Per user feedback (comment #3737334654): 1. Added YAML metadata to docs.agent.md (name, description) following new-provider.agent.md format 2. Fixed markdown syntax to be GitHub-compatible: - Changed inline code blocks in list items to properly formatted code blocks - Ensured all code examples are in fenced code blocks 3. Added missing v4.1.json schema to llms.txt (was only showing v4.0, v2.8, v2) Changes: - .github/agents/docs.agent.md: Added YAML frontmatter, fixed markdown formatting - doc/llms.txt: Added v4.1 schema URL (first in list as newest version) All schema files now documented: v4.1, v4.0, v2.8, v2 Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * refactor(docs): restructure to docs/ directory with cleaner organization Per user feedback (comment #3737399407): 1. Renamed doc/ → docs/ 2. Moved all .en.md files → docs/en/*.md (removed .en suffix) 3. Moved install.sh → docs/install.sh (source file, not copied) 4. Moved favicon.ico → docs/favicon.ico (source file) 5. Simplified setup-docs.mjs - no longer copies English files or install.sh 6. Updated all path references: - GitHub Actions workflow: doc/ → docs/ - .gitignore: doc/ → docs/ - VitePress config: edit links now map to docs/ - Documentation agent: updated structure docs New Structure: - docs/ (was doc/) - config/, providers/, dev/ (Chinese markdown) - en/ (English markdown, no .en suffix) - config/, providers/, dev/ - install.sh (source) - favicon.ico (source) - llms.txt (template) Build command unchanged: cd docs && npm run build All English documentation now sourced directly from docs/en/ instead of being generated. Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(docs): update documentation links from /doc/ to docs/ in README files Per user feedback (comment #3737497400): 1. Updated setup-docs.mjs to properly replace /doc/ → docs/ → / in README processing 2. Updated all /doc/ links to docs/ in README.md: - Image src: /doc/img/ddns.svg → docs/img/ddns.svg - Docker docs: /doc/docker.md → docs/docker.md - Config docs: /doc/config/* → docs/config/* - Provider docs: /doc/providers/* → docs/providers/* 3. Updated all /doc/ links to docs/en/ in README.en.md: - Image src: /doc/img/ddns.svg → docs/img/ddns.svg - Docker docs: /doc/docker.en.md → docs/en/docker.md - Config docs: /doc/config/*.en.md → docs/en/config/*.md - Provider docs: /doc/providers/*.en.md → docs/en/providers/*.md Changes: - docs/setup-docs.mjs: Two-step replacement (/doc/ → docs/ → /) for GitHub links - README.md: All documentation links now use docs/ prefix - README.en.md: All documentation links now use docs/en/ prefix Verified: No /doc/ links remain in README files or .github markdown files. Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * refactor(docs): simplify setup script and move to .vitepress directory Per user feedback (comment #3737562776): 1. Moved setup-docs.mjs to docs/.vitepress/ directory 2. Simplified link replacement logic: - Removed unnecessary /doc/ processing code - Now only converts docs/ → / (Chinese version) - And docs/en/ → / (English version) - Since README files already use correct docs/ prefix 3. Updated package.json script paths to reference .vitepress/setup-docs.mjs 4. Updated path variables in script to work from .vitepress/ location Changes: - docs/.vitepress/setup-docs.mjs: New location, simplified logic - docs/package.json: Updated setup script path - Removed obsolete /doc/ → docs/ replacement steps Build process now simpler and cleaner. Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(docs): fix all broken documentation links Per user feedback (comment #3737640946): Fixed all broken links throughout the documentation: 1. Fixed providers/README links → providers/ (clean URL) 2. Removed all .en.md suffixes from links (files now in docs/en/ with .md extension) 3. Fixed /doc/ prefix references → proper docs/ paths 4. Fixed cross-references between Chinese and English docs: - Chinese providers/README.md → ../en/providers/*.md for English docs - English docs use relative paths within en/ directory 5. Fixed redirect_to URLs in dnscom.md files 6. Fixed image URL: /doc/img/ → /img/ Changes affect 28 files: - All config documentation (cli.md, env.md, json.md) in both languages - All provider documentation in both languages - Developer documentation (dev/config.md) - Docker documentation - Provider README files All dead links now resolved. Documentation navigation works correctly. Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(docs): fix remaining dead links in generated index.md and provider files Per user feedback (comment #3737692822): Fixed all remaining dead links: 1. Enhanced setup-docs.mjs to handle ./doc/ prefix links: - Added replacement for (./doc/ → (/ - Added replacement for /providers/index → /providers/ - Now correctly converts README links to clean web URLs 2. Fixed provider documentation links: - callback.md and debug.md: Added "Return to All Providers" links - English versions: Added /en/providers/ links - Removed broken ./../../README links All dead links now resolved: - ./doc/install → /install - ./doc/providers/* → /providers/* - ./doc/config/* → /config/* - /providers/index → /providers/ - Provider files now link to /providers/ and /en/providers/ Dead link checking should now pass successfully. Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(docs): fix all remaining dead links in documentation Per user feedback (comment #3737728554): Fixed all remaining dead links throughout documentation: 1. Enhanced setup-docs.mjs: - Added replacement for ](doc/ pattern - Handles more link format variations 2. Fixed config files (6 files): - config/cli.md: providers/ → ../providers/, providers/callback → ../providers/callback - config/json.md: docker.md → ../docker.md, providers/ → ../providers/ - en/config/env.md: providers/callback → ../providers/callback, docker → ../docker, providers/ → ../providers/ - en/config/json.md: docker.md → ../docker.md, providers/ → ../providers/ 3. Fixed provider files (6 files): - providers/callback.md, debug.md, README.md: ../../README.md → / - en/providers/callback.md, debug.md: ../../README.md → /en/ - en/providers/he.md: ../json.md → ../config/json.md All dead links resolved: ✓ ./providers/index → ../providers/ ✓ ./providers/callback → ../providers/callback ✓ ./docker → ../docker ✓ ./../../README → / (or /en/) ✓ ../json → ../config/json ✓ ](doc/ → ](/ Dead link checking should now pass successfully with zero errors. Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(docs): add VitePress rewrites to map providers/README.md to /providers/ Per user feedback (comment #3737780259): Fixed `/providers/index` dead links by adding VitePress rewrites configuration. Changes to docs/.vitepress/config.mts: 1. Added rewrites mapping: - providers/README.md → providers/index.md - en/providers/README.md → en/providers/index.md 2. Updated sidebar links: - /providers/index → /providers/ - /en/providers/index → /en/providers/ This tells VitePress that the README.md files in providers directories should be accessible via the /providers/ URL (with cleanUrls enabled). All /providers/index links now correctly resolve to /providers/ Dead link checking should now pass with zero errors. Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(docs): fix providers/index links and simplify setup script Per user feedback (comment #3737836475): 1. Fixed dead links in config/env.md files: - ./providers/ → ../providers/ (both Chinese and English) 2. Simplified setup-docs.mjs: - Removed unnecessary doc/ processing (README no longer contains doc/) - Merged logic for both README files into single processReadme function - Cleaner, more maintainable code with less duplication Changes: - docs/config/env.md: Fixed providers/ link - docs/en/config/env.md: Fixed providers/ link - docs/.vitepress/setup-docs.mjs: Refactored with shared function Dead link checking should now pass with zero errors. Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * refactor(docs): remove providers/index replacement from setup script Per user feedback (comment #3737887893): Removed `/providers/index` → `/providers/` replacement from setup-docs.mjs. This should be fixed in markdown files instead, not in the setup script. Simplified setup script by removing unnecessary transformation logic. Changes: - docs/.vitepress/setup-docs.mjs: Removed line 29 replacement Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(docs): update remaining /doc/ references to docs/ in README files Per user feedback (comment #3737970179): Updated all remaining /doc/ references to docs/ or docs/en/ in both README files. Changes in README.md: - doc/install.md → docs/install.md - All doc/providers/*.md → docs/providers/*.md (10 provider links) Changes in README.en.md: - doc/install.en.md → docs/en/install.md - doc/providers/dnspod.en.md → docs/en/providers/dnspod.md - doc/providers/alidns.en.md → docs/en/providers/alidns.md All documentation links now correctly use docs/ prefix for GitHub navigation. Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * fix(docs): update all remaining doc/ references to docs/ in README files Per user feedback (comment #3738298603): Updated ALL remaining doc/ references to docs/ or docs/en/ in both README files. Changes in README.md (13 links): - doc/providers/edgeone.md → docs/providers/edgeone.md - doc/providers/noip.md → docs/providers/noip.md - doc/config/cli.md → docs/config/cli.md (3 occurrences) - doc/config/json.md → docs/config/json.md (2 occurrences) - doc/config/env.md → docs/config/env.md (2 occurrences) - doc/providers/ → docs/providers/ (3 occurrences) Changes in README.en.md (13 links): - doc/providers/*.en.md → docs/en/providers/*.md (9 provider links) - doc/config/cli.en.md → docs/en/config/cli.md - doc/config/json.en.md → docs/en/config/json.md - doc/config/env.en.md → docs/en/config/env.md - doc/providers/ → docs/en/providers/ All documentation links now use correct docs/ prefix with NO /doc/ references remaining. Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * refactor(docs): integrate setup logic into config.mts Per user feedback (comment #3738600577): Integrated setup-docs.mjs functionality into config.mts. Changes: 1. Removed standalone setup-docs.mjs file 2. Added setupDocs() function in config.mts that runs before config export 3. Simplified package.json scripts - removed setup step 4. Setup now runs automatically when config loads (works for both dev and build) Benefits: - Single configuration file - easier to maintain - No separate setup script needed - Automatic execution on both dev and build - Cleaner package.json scripts - Same functionality, simpler implementation Build command: `npm run build` or `npm run docs:dev` Co-authored-by: NewFuture <6290356+NewFuture@users.noreply.github.com> * update link * /doc/img/ddns.svg * fix docs paths * fix docs * fix docs url --------- 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>
16 KiB
Development Guide: How to Implement a New DNS Provider
This guide explains how to quickly implement a custom DNS provider adapter class based on different abstract base classes to support dynamic DNS record creation and updates.
📦 Directory Structure
ddns/
├── provider/
│ ├── _base.py # Abstract base classes SimpleProvider and BaseProvider, signature auth functions
│ └── myprovider.py # Your new provider implementation
tests/
├── base_test.py # Shared test utilities and base classes
├── test_provider_*.py # Provider-specific test files
├── test_module_*.py # Other tests
└── README.md # Testing guide
doc/dev/
└── provider.md # Provider development guide (this document)
🚀 Quick Start
DDNS provides two abstract base classes. Choose the appropriate base class based on your DNS provider's API characteristics:
1. SimpleProvider - Simple DNS Provider
Suitable for DNS providers that only offer simple update interfaces without support for querying existing records.
Required Methods:
| Method | Description | Required |
|---|---|---|
set_record(domain, value, record_type="A", ttl=None, line=None, **extra) |
Update or create DNS record | ✅ Required |
_validate() |
Validate authentication info | ❌ Optional (has default implementation) |
Use Cases:
- DNS providers that only offer update interfaces (like HE.net)
- Simple scenarios that don't need to query existing records
- Debugging and testing purposes
- Callback/Webhook type DNS updates
2. BaseProvider - Full DNS Provider ⭐️ Recommended
Suitable for standard DNS provider APIs that offer complete CRUD operations.
Required Methods:
| Method | Description | Required |
|---|---|---|
_query_zone_id(domain) |
Query zone ID for main domain | ✅ Required |
_query_record(zone_id, subdomain, main_domain, record_type, line=None, extra=None) |
Query current DNS record | ✅ Required |
_create_record(zone_id, subdomain, main_domain, value, record_type, ttl=None, line=None, extra=None) |
Create new record | ✅ Required |
_update_record(zone_id, old_record, value, record_type, ttl=None, line=None, extra=None) |
Update existing record | ✅ Required |
_validate() |
Validate authentication info | ❌ Optional (default requires id and token) |
Built-in Features:
- ✅ All SimpleProvider functionality
- 🎯 Automatic record management (complete query→create/update workflow)
- 💾 Caching mechanism
- 📝 Detailed operation logging and error handling
Use Cases:
- DNS providers with complete REST APIs (like Cloudflare, Alibaba Cloud DNS)
- Scenarios requiring query of existing record status
- Support for precise record management and status tracking
🔧 Implementation Examples
SimpleProvider Example
Suitable for simple DNS providers, refer to existing implementations:
provider/he.py: Hurricane Electric DNS updatesprovider/debug.py: Debugging purposes, prints IP addressesprovider/callback.py: Callback/Webhook type DNS updates
provider/mysimpleprovider.py
# coding=utf-8
"""
Custom simple DNS provider example
@author: YourGithubUsername
"""
from ._base import SimpleProvider, TYPE_FORM
class MySimpleProvider(SimpleProvider):
"""
Example SimpleProvider implementation
Supports simple DNS record updates, suitable for most simple DNS APIs
"""
API = 'https://api.simpledns.com'
content_type = TYPE_FORM # or TYPE_JSON
decode_response = False # Set to False if returns plain text instead of JSON
def _validate(self):
"""Validate authentication info (optional override)"""
super(MySimpleProvider, self)._validate()
# Add specific validation logic, like checking API key format
if not self.token or len(self.token) < 16:
raise ValueError("Invalid API token format")
def set_record(self, domain, value, record_type="A", ttl=None, line=None, **extra):
"""Update DNS record - must implement"""
# logic to update DNS record
BaseProvider Example
Suitable for standard DNS providers, refer to existing implementations:
provider/dnspod.py: POST form data, no signatureprovider/cloudflare.py: RESTful JSON, no signatureprovider/alidns.py: POST form + sha256 parameter signatureprovider/huaweidns.py: RESTful JSON, parameter header signature
provider/myprovider.py
# coding=utf-8
"""
Custom standard DNS provider example
@author: YourGithubUsername
"""
from ._base import BaseProvider, TYPE_JSON, hmac_sha256_authorization, sha256_hash
class MyProvider(BaseProvider):
"""
Example BaseProvider implementation
Suitable for DNS providers offering complete CRUD APIs
"""
API = 'https://api.exampledns.com'
content_type = TYPE_JSON # or TYPE_FORM
def _query_zone_id(self, domain):
# type: (str) -> str | None
"""Query zone ID for main domain"""
# Exact lookup or list matching
def _query_record(self, zone_id, subdomain, main_domain, record_type, line=None, extra=None):
# type: (str, str, str, str, str | None, dict | None) -> Any
"""Query existing DNS record"""
def _create_record(self, zone_id, subdomain, main_domain, value, record_type, ttl=None, line=None, extra=None):
# type: (str, str, str, str, str, int | str | None, str | None, dict | None) -> bool
"""Create new DNS record"""
def _update_record(self, zone_id, old_record, value, record_type, ttl=None, line=None, extra=None):
# type: (str, dict, str, str, int | str | None, str | None, dict | None) -> bool
"""Update existing DNS record"""
def _request(self, action, **params):
# type: (str, **(str | int | bytes | bool | None)) -> dict
"""[Recommended] Encapsulate common request logic, handle auth and common parameters"""
# Build request parameters
request_params = {
"Action": action,
"Version": "2023-01-01",
"AccessKeyId": self.id,
**{k: v for k, v in params.items() if v is not None}
}
res = self._http("POST", "/", params=request_params, headers=headers)
return res.get("data", {})
✅ Development Best Practices
Choosing the Right Base Class
-
SimpleProvider - For DNS providers with incomplete functionality
- ✅ DNS provider only offers update API
- ✅ No need to query existing records
-
BaseProvider - Suitable for standard and complex scenarios
- ✅ DNS provider offers complete query, create, modify APIs
- ✅ Need precise record state management
- ✅ Support complex domain resolution logic
General Development Recommendations
🌐 HTTP Request Handling
# Use built-in _http method, automatically handles proxy, encoding, logging
response = self._http("POST", path, params=params, headers=headers)
🔒 Format Validation
def _validate(self):
"""Authentication info validation example"""
super(MyProvider, self)._validate()
# Check API key format
if not self.token or len(self.token) < 16:
raise ValueError("API token must be at least 16 characters")
📝 Logging
if result:
self.logger.info("DNS record got: %s", result.get("id"))
return True
else:
self.logger.warning("DNS record update returned false")
🧪 Testing and Debugging
Unit Testing
Each Provider should have comprehensive unit tests. The project provides unified test base classes and tools:
# tests/test_provider_myprovider.py
from base_test import BaseProviderTestCase, unittest, patch, MagicMock
from ddns.provider.myprovider import MyProvider
class TestMyProvider(BaseProviderTestCase):
def setUp(self):
super(TestMyProvider, self).setUp()
# Provider-specific setup
def test_init_with_basic_config(self):
"""Test basic initialization"""
Running Tests
# Run all tests
python -m unittest discover tests -v
# Run specific Provider tests
python -m unittest tests.test_provider_myprovider -v
# Run specific test method
python tests/test_provider_myprovider.py
📚 More Resources and Best Practices
🏗️ Project Structure Recommendations
ddns/
├── provider/
│ ├── _base.py # Base class definitions
│ ├── myprovider.py # Your Provider implementation
│ └── __init__.py # Import and registration
tests/
├── base_test.py # Shared test base class
├── test_provider_myprovider.py # Your Provider tests
└── README.md # Testing guide
📖 Reference Implementations
SimpleProvider References:
provider/he.py- Hurricane Electric (simple form submission)provider/debug.py- Debug tool (prints info only)provider/callback.py- Callback/Webhook mode
BaseProvider References:
provider/cloudflare.py- RESTful JSON APIprovider/alidns.py- POST + signature authenticationprovider/dnspod.py- POST form data submission
🔐 Cloud Provider Authentication Signature Algorithms
For cloud providers requiring signature authentication (like Alibaba Cloud, Huawei Cloud, Tencent Cloud), DDNS provides generic HMAC-SHA256 signature authentication functions.
Signature Authentication Utility Functions
hmac_sha256_authorization() - Generic Signature Generator
Generic cloud provider API authentication signature generation function, supporting Alibaba Cloud, Huawei Cloud, Tencent Cloud and other cloud providers. Uses HMAC-SHA256 algorithm to generate Authorization headers compliant with various cloud provider specifications. All cloud provider differences are passed through template parameters, achieving complete provider independence.
from ddns.provider._base import hmac_sha256_authorization, sha256_hash
# Generic signature function call example
authorization = hmac_sha256_authorization(
secret_key=secret_key, # Signature key (already derived)
method="POST", # HTTP method
path="/v1/domains/records", # API path
query="limit=20&offset=0", # Query string
headers=request_headers, # Request headers dictionary
body_hash=sha256_hash(request_body), # Request body hash
signing_string_format=signing_template, # Signing string template
authorization_format=auth_template # Authorization header template
)
Function Parameters:
| Parameter | Type | Description |
|---|---|---|
secret_key |
str | bytes |
Signature key, already processed through key derivation |
method |
str |
HTTP request method (GET, POST, etc.) |
path |
str |
API request path |
query |
str |
URL query string |
headers |
dict[str, str] |
HTTP request headers |
body_hash |
str |
SHA256 hash of request body |
signing_string_format |
str |
Signing string template with {HashedCanonicalRequest} placeholder |
authorization_format |
str |
Authorization header template with {SignedHeaders}, {Signature} placeholders |
Template Variables:
{HashedCanonicalRequest}- SHA256 hash of canonical request{SignedHeaders}- Alphabetically sorted list of signed headers{Signature}- Final HMAC-SHA256 signature value
Cloud Provider Signature Implementation Examples
Alibaba Cloud (ACS3-HMAC-SHA256)
def _request(self, action, **params):
# Build request headers
headers = {
"host": "alidns.aliyuncs.com",
"x-acs-action": action,
"x-acs-content-sha256": sha256_hash(body),
"x-acs-date": timestamp,
"x-acs-signature-nonce": nonce,
"x-acs-version": "2015-01-09"
}
# Alibaba Cloud signature template
auth_template = (
"ACS3-HMAC-SHA256 Credential={access_key},"
"SignedHeaders={{SignedHeaders}},Signature={{Signature}}"
)
signing_template = "ACS3-HMAC-SHA256\n{timestamp}\n{{HashedCanonicalRequest}}"
# Generate signature
authorization = hmac_sha256_authorization(
secret_key=self.token,
method="POST",
path="/",
query=query_string,
headers=headers,
body_hash=sha256_hash(body),
signing_string_format=signing_template,
authorization_format=auth_template
)
headers["authorization"] = authorization
return self._http("POST", "/", body=body, headers=headers)
Tencent Cloud (TC3-HMAC-SHA256)
def _request(self, action, **params):
# Tencent Cloud requires derived key
derived_key = self._derive_signing_key(date, service, self.token)
# Build request headers
headers = {
"content-type": "application/json",
"host": "dnspod.tencentcloudapi.com",
"x-tc-action": action,
"x-tc-timestamp": timestamp,
"x-tc-version": "2021-03-23"
}
# Tencent Cloud signature template
auth_template = (
"TC3-HMAC-SHA256 Credential={secret_id}/{date}/{service}/tc3_request, "
"SignedHeaders={{SignedHeaders}}, Signature={{Signature}}"
)
signing_template = "TC3-HMAC-SHA256\n{timestamp}\n{date}/{service}/tc3_request\n{{HashedCanonicalRequest}}"
# Generate signature
authorization = hmac_sha256_authorization(
secret_key=derived_key, # Note: use derived key
method="POST",
path="/",
query="",
headers=headers,
body_hash=sha256_hash(body),
signing_string_format=signing_template,
authorization_format=auth_template
)
headers["authorization"] = authorization
return self._http("POST", "/", body=body, headers=headers)
Helper Utility Functions
sha256_hash() - SHA256 Hash Calculation
from ddns.provider._base import sha256_hash
# Calculate SHA256 hash of string
hash_value = sha256_hash("request body content")
# Calculate SHA256 hash of binary data
hash_value = sha256_hash(b"binary data")
hmac_sha256() - HMAC-SHA256 Signature Object
from ddns.provider._base import hmac_sha256
# Generate HMAC-SHA256 byte signature
# Get HMAC object, can call .digest() for bytes or .hexdigest() for hex string
hmac_obj = hmac_sha256("secret_key", "message_to_sign")
signature_bytes = hmac_obj.digest() # Byte format
signature_hex = hmac_obj.hexdigest() # Hex string format
🛠️ Recommended Development Tools
- Local development environment: VSCode
- Online code editor: GitHub Codespaces or github.dev
🎯 Common Issue Solutions
- Q: Why choose SimpleProvider over BaseProvider?
- A: If the DNS provider only offers update API without query API, SimpleProvider is simpler and more efficient
🎉 Summary
Quick Checklist
- Choose appropriate base class (
SimpleProvidervsBaseProvider) - Implement all required methods (with GPT or Copilot assistance)
- Add appropriate error handling and logging
- Write comprehensive unit tests (using GPT or Copilot generation)
- Test various edge cases and error scenarios
- Update relevant documentation