added Cloudflare API support
All checks were successful
CI / build (push) Successful in 45s

This commit is contained in:
2025-03-31 03:59:26 +08:00
parent 7f0ecf04e1
commit 3ae5a865fe
7 changed files with 225 additions and 39 deletions

View File

@@ -4,7 +4,7 @@ on:
branches: [ master ] branches: [ master ]
jobs: jobs:
build: build:
runs-on: woryzen runs-on: hostinger
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v4 uses: actions/checkout@v4

View File

@@ -1,10 +1,8 @@
#!/usr/bin/env python3
import json import json
from urllib.request import urlopen, Request
import argparse import argparse
from datetime import datetime from datetime import datetime
from subprocess import check_call from subprocess import check_call
from .utils import get_public_ip
def run(): def run():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
@@ -19,11 +17,8 @@ def run():
args = parser.parse_args() args = parser.parse_args()
url = "http://v4.ipv6-test.com/api/myip.php"
request = Request(url)
with urlopen(request) as request:
public_ip = request.read().decode()
date = datetime.now().strftime('%Y-%m-%d %H:%M:%S') date = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
public_ip = get_public_ip()
params = { params = {
'Comment': f'Auto update by awsdyndns on {date}', 'Comment': f'Auto update by awsdyndns on {date}',
'Changes': [ 'Changes': [

80
dyndns/cloudflare.py Normal file
View File

@@ -0,0 +1,80 @@
import json
import argparse
from os import environ
from .utils import get_public_ip
from urllib.request import urlopen, Request
# Define your variables
cloudflare_api_key = environ['CLOUDFLARE_API_KEY']
def update_dns_record(
zone_id: str,
dns_record_id: str,
ip_address: str,
domain_name: str,
ttl: int,
record_type : str,
proxied : bool = False,
comment : str = None
):
# Set the URL
url = f'https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records/{dns_record_id}'
# Set the headers
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {cloudflare_api_key}'
}
# Define the data to be sent in the PATCH request
data = {
"comment": comment,
"content": ip_address,
"name": domain_name,
"proxied": proxied,
"ttl": ttl,
"type": record_type
}
# Make the PATCH request
request = Request(url, headers=headers, data=json.dumps(data).encode('UTF-8'), method='PATCH')
with urlopen(request) as request:
if request.status != 200:
response = request.read().decode()
msg = f'Failed to update DNS record: {request.status} {response}'
raise RuntimeError(msg)
def run():
parser = argparse.ArgumentParser()
parser.add_argument('-z', "--hosted-zone", metavar='HOSTED_ZONE_ID', required=True,
help="Specify the hosted zone id")
parser.add_argument('-d', "--domain", metavar='DOMAIN_NAME', required=True,
help="Specify domain name")
parser.add_argument('-t', "--type", metavar='DNS_TYPE', default='A',
help="Specify DNS type")
parser.add_argument('-l', "--ttl", metavar='TTL_S', type=int, default=300,
help="Specify time-to-live in seconds")
parser.add_argument('-r', "--record-id", metavar='RECORD_ID',
help="Cloudflare DNS record id")
parser.add_argument('-p', "--proxied", metavar='PROXIED', type=bool, default=False,
help="Whether or not the traffic will be proxied through Cloudflare network")
parser.add_argument('-c', "--comment", metavar='COMMENT', help="Specify a comment for this entry")
args = parser.parse_args()
public_ip = get_public_ip()
update_dns_record(
zone_id=args.hosted_zone,
domain_name=args.domain,
record_type=args.type,
ip_address=public_ip,
ttl=args.ttl,
dns_record_id=args.record_id,
proxied=args.proxied,
comment=args.comment
)

7
dyndns/utils.py Normal file
View File

@@ -0,0 +1,7 @@
from urllib.request import urlopen, Request
def get_public_ip() -> str:
url = "http://v4.ipv6-test.com/api/myip.php"
request = Request(url)
with urlopen(request) as request:
return request.read().decode()

View File

@@ -3,8 +3,8 @@ requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
[project] [project]
name = "awsdyndns" name = "dyndns"
version = "0.2.0" version = "0.3.0"
authors = [ authors = [
{ name="Walter Oggioni", email="oggioni.walter@gmail.com" }, { name="Walter Oggioni", email="oggioni.walter@gmail.com" },
] ]
@@ -16,14 +16,25 @@ classifiers = [
"License :: OSI Approved :: MIT License", "License :: OSI Approved :: MIT License",
"Operating System :: OS Independent", "Operating System :: OS Independent",
] ]
dependencies = [] dependencies = [
]
[project.optional-dependencies]
dev = [
"build", "mypy", "ipdb", "twine"
]
[tool.setuptools.package-data]
dyndns = ['conf/*']
[project.urls] [project.urls]
"Homepage" = "https://woggioni.net/cgit/awsdyndns.git/" "Homepage" = "https://gitea.woggioni.net/woggioni/dyndns/"
"Bug Tracker" = "https://woggioni.net/cgit/awsdyndns.git/" "Bug Tracker" = "https://gitea.woggioni.net/woggioni/dyndns/"
[project.scripts] [project.scripts]
awsdyndns = "awsdyndns.wrapper:run" awsdyndns = "dyndns.aws:run"
cloudflaredyndns = "dyndns.cloudflare:run"
[tool.mypy] [tool.mypy]
python_version = "3.11" python_version = "3.11"
@@ -33,4 +44,4 @@ no_implicit_optional = true
warn_return_any = true warn_return_any = true
warn_unused_ignores = true warn_unused_ignores = true
exclude = ["scripts", "docs", "test"] exclude = ["scripts", "docs", "test"]
strict = true strict = true

View File

@@ -1,31 +1,124 @@
build==1.2.1 #
certifi==2024.7.4 # This file is autogenerated by pip-compile with Python 3.13
cffi==1.16.0 # by the following command:
charset-normalizer==3.3.2 #
cryptography==43.0.0 # pip-compile --allow-unsafe --extra=dev --output-file=requirements.txt pyproject.toml
#
--index-url https://gitea.woggioni.net/api/packages/woggioni/pypi/simple
--extra-index-url https://pypi.org/simple
asttokens==3.0.0
# via stack-data
build==1.2.2.post1
# via dyndns (pyproject.toml)
certifi==2025.1.31
# via requests
cffi==1.17.1
# via cryptography
charset-normalizer==3.4.1
# via requests
cryptography==44.0.2
# via secretstorage
decorator==5.2.1
# via
# ipdb
# ipython
docutils==0.21.2 docutils==0.21.2
idna==3.7 # via readme-renderer
importlib_metadata==8.2.0 executing==2.2.0
jaraco.classes==3.4.0 # via stack-data
jaraco.context==5.3.0 id==1.5.0
jaraco.functools==4.0.1 # via twine
jeepney==0.8.0 idna==3.10
keyring==25.2.1 # via requests
ipdb==0.13.13
# via dyndns (pyproject.toml)
ipython==9.0.2
# via ipdb
ipython-pygments-lexers==1.1.1
# via ipython
jaraco-classes==3.4.0
# via keyring
jaraco-context==6.0.1
# via keyring
jaraco-functools==4.1.0
# via keyring
jedi==0.19.2
# via ipython
jeepney==0.9.0
# via
# keyring
# secretstorage
keyring==25.6.0
# via twine
markdown-it-py==3.0.0 markdown-it-py==3.0.0
# via rich
matplotlib-inline==0.1.7
# via ipython
mdurl==0.1.2 mdurl==0.1.2
more-itertools==10.3.0 # via markdown-it-py
nh3==0.2.18 more-itertools==10.6.0
packaging==24.1 # via
pkginfo==1.10.0 # jaraco-classes
# jaraco-functools
mypy==1.15.0
# via dyndns (pyproject.toml)
mypy-extensions==1.0.0
# via mypy
nh3==0.2.21
# via readme-renderer
packaging==24.2
# via
# build
# twine
parso==0.8.4
# via jedi
pexpect==4.9.0
# via ipython
prompt-toolkit==3.0.50
# via ipython
ptyprocess==0.7.0
# via pexpect
pure-eval==0.2.3
# via stack-data
pycparser==2.22 pycparser==2.22
Pygments==2.18.0 # via cffi
pyproject_hooks==1.1.0 pygments==2.19.1
readme_renderer==44.0 # via
# ipython
# ipython-pygments-lexers
# readme-renderer
# rich
pyproject-hooks==1.2.0
# via build
readme-renderer==44.0
# via twine
requests==2.32.3 requests==2.32.3
# via
# id
# requests-toolbelt
# twine
requests-toolbelt==1.0.0 requests-toolbelt==1.0.0
# via twine
rfc3986==2.0.0 rfc3986==2.0.0
rich==13.7.1 # via twine
SecretStorage==3.3.3 rich==14.0.0
twine==5.1.1 # via twine
urllib3==2.2.2 secretstorage==3.3.3
zipp==3.19.2 # via keyring
stack-data==0.6.3
# via ipython
traitlets==5.14.3
# via
# ipython
# matplotlib-inline
twine==6.1.0
# via dyndns (pyproject.toml)
typing-extensions==4.13.0
# via mypy
urllib3==2.3.0
# via
# requests
# twine
wcwidth==0.2.13
# via prompt-toolkit