Compare commits

...

5 Commits

Author SHA1 Message Date
c0d1d4b38d
use generic provider for ipfy + wtfismyip 2024-02-21 10:02:08 +03:00
38923fec9e
fix cli 2024-02-21 10:00:54 +03:00
0844e4935f
add generic http source providers 2024-02-21 09:42:38 +03:00
31564c22d5
add ip addrs validation 2024-02-21 09:41:51 +03:00
0509ba9d63
fix vscale response check 2024-02-21 09:12:08 +03:00
7 changed files with 103 additions and 39 deletions

View File

@ -1,7 +1,8 @@
import httpx import httpx
import asyncio import asyncio
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import NamedTuple from typing import NamedTuple, Optional
from netaddr import valid_ipv4, valid_ipv6
class IPAddreses(NamedTuple): class IPAddreses(NamedTuple):
@ -154,3 +155,11 @@ class BaseFilterProvider(ABC):
@abstractmethod @abstractmethod
async def check_imp(self, source_provider, addr_v4, addr_v6): ... async def check_imp(self, source_provider, addr_v4, addr_v6): ...
def filter_ipv4(value: str) -> Optional[str]:
return value and valid_ipv4(value) and value or None
def filter_ipv6(value: str) -> Optional[str]:
return value and valid_ipv6(value) and value or None

View File

@ -1,6 +1,5 @@
import httpx import httpx
import asyncio import asyncio
from abc import ABC, abstractmethod
import toml import toml
from .base import BaseFilterProvider, BaseSourceProvider, BaseOutputProvider, IPAddreses from .base import BaseFilterProvider, BaseSourceProvider, BaseOutputProvider, IPAddreses
from .plugins import use_plugins from .plugins import use_plugins

View File

@ -22,7 +22,7 @@ class VscaleDomains(BaseOutputProvider):
async def find_domain_id(self, client) -> Optional[int]: async def find_domain_id(self, client) -> Optional[int]:
response = await client.get("/domains/") response = await client.get("/domains/")
if httpx.codes.is_success(response.status_code): if response.is_success:
data = response.json() data = response.json()
if isinstance(data, list): if isinstance(data, list):
for entry in data: for entry in data:
@ -37,7 +37,7 @@ class VscaleDomains(BaseOutputProvider):
response = await client.get( response = await client.get(
f"/domains/{domain_id}/records/", f"/domains/{domain_id}/records/",
) )
if httpx.codes.is_success(response.status_code): if response.is_success:
data = response.json() data = response.json()
if isinstance(data, list): if isinstance(data, list):
for entry in data: for entry in data:
@ -50,7 +50,7 @@ class VscaleDomains(BaseOutputProvider):
async def get_record_value(self, client, domain_id, record_id) -> str: async def get_record_value(self, client, domain_id, record_id) -> str:
response = await client.get(f"/domains/{domain_id}/records/{record_id}") response = await client.get(f"/domains/{domain_id}/records/{record_id}")
if httpx.codes.is_success(response.status_code): if response.is_success:
data = response.json() data = response.json()
if isinstance(data, dict): if isinstance(data, dict):
return data["content"] return data["content"]
@ -68,7 +68,7 @@ class VscaleDomains(BaseOutputProvider):
f"/domains/{domain_id}/records/{record_id}", f"/domains/{domain_id}/records/{record_id}",
json=data, json=data,
) )
if not httpx.codes.is_success(response.status_code): if not response.is_success:
raise RuntimeError( raise RuntimeError(
f"failed to change record: {self.target=},{domain_id=}, {record_id=}, {record_type=}, {value=}" f"failed to change record: {self.target=},{domain_id=}, {record_id=}, {record_type=}, {value=}"
) )
@ -84,7 +84,7 @@ class VscaleDomains(BaseOutputProvider):
f"/domains/{domain_id}/records/", f"/domains/{domain_id}/records/",
json=data, json=data,
) )
if not httpx.codes.is_success(response.status_code): if not response.is_success:
raise RuntimeError( raise RuntimeError(
f"failed to create record: {self.target=},{domain_id=}, {record_type=}, {value=}, {response.status_code=}" f"failed to create record: {self.target=},{domain_id=}, {record_type=}, {value=}, {response.status_code=}"
) )

75
pddnsc/sources/http.py Normal file
View File

@ -0,0 +1,75 @@
import httpx
from pddnsc.base import BaseSourceProvider, filter_ipv4, filter_ipv6
class GenericHttpSource(BaseSourceProvider):
def post_init(self):
self.url_v4 = self.config.get("url_v4")
self.url_v6 = self.config.get("url_v6")
self.headers = self.config.get("headers", {})
async def fetch_v4(self) -> str:
if not self.url_v4:
return None
result = ""
async with httpx.AsyncClient(
transport=self.ipv4t, headers=self.headers
) as client:
response = await client.get(self.url_v4)
if response.is_success:
result = response.text.strip()
return filter_ipv4(result)
async def fetch_v6(self) -> str:
if not self.url_v6:
return None
result = ""
async with httpx.AsyncClient(
transport=self.ipv6t, headers=self.headers
) as client:
response = await client.get(self.url_v6)
if response.is_success:
result = response.text.strip()
return filter_ipv6(result)
class GenericHttpJsonSource(BaseSourceProvider):
def post_init(self):
self.url_v4 = self.config.get("url_v4")
self.url_v6 = self.config.get("url_v6")
self.key_v4 = self.config.get("key_v4")
self.key_v6 = self.config.get("key_v6")
if self.config.get("use_accept", True):
headers = {"Accept": self.config.get("acept_type", "application/json")}
else:
headers = {}
self.headers = headers.update(self.config.get("headers", {}))
async def fetch_v4(self) -> str:
if not self.url_v4 or self.key_v4 is None:
return None
result = ""
async with httpx.AsyncClient(
transport=self.ipv4t, headers=self.headers
) as client:
response = await client.get(self.url_v4)
if response.is_success:
result = response.json()[self.key_v4].strip()
return filter_ipv4(result)
async def fetch_v6(self) -> str:
if not self.url_v6 or self.key_v6 is None:
return None
result = ""
async with httpx.AsyncClient(
transport=self.ipv6t, headers=self.headers
) as client:
response = await client.get(self.url_v6)
if response.is_success:
result = response.json()[self.key_v6].strip()
return filter_ipv6(result)

View File

@ -1,19 +1,9 @@
import httpx from pddnsc.sources.http import GenericHttpSource
from pddnsc.base import BaseSourceProvider
# https://www.ipify.org/ # https://www.ipify.org/
class IPIFYSource(BaseSourceProvider): class IPIFYSource(GenericHttpSource):
async def fetch_v4(self) -> str: def post_init(self):
async with httpx.AsyncClient(transport=self.ipv4t) as client: super().post_init()
response = await client.get("https://api4.ipify.org") self.url_v4 = "https://api4.ipify.org"
if response.is_success: self.url_v6 = "https://api6.ipify.org"
result = response.text.strip() or None
return result
async def fetch_v6(self) -> str:
async with httpx.AsyncClient(transport=self.ipv6t) as client:
response = await client.get("https://api6.ipify.org")
if response.is_success:
result = response.text.strip() or None
return result

View File

@ -1,20 +1,10 @@
import httpx from pddnsc.sources.http import GenericHttpSource
from pddnsc.base import BaseSourceProvider
# https://wtfismyip.com/ # https://wtfismyip.com/
# https://gitlab.com/wtfismyip/wtfismyip # https://gitlab.com/wtfismyip/wtfismyip
class WTFIsMyIP(BaseSourceProvider): class WTFIsMyIP(GenericHttpSource):
async def fetch_v4(self) -> str: def post_init(self):
async with httpx.AsyncClient(transport=self.ipv4t) as client: super().post_init()
response = await client.get("https://text.ipv4.myip.wtf") self.url_v4 = "https://text.ipv4.myip.wtf"
if response.is_success: self.url_v6 = "https://text.ipv6.myip.wtf"
result = response.text.strip() or None
return result
async def fetch_v6(self) -> str:
async with httpx.AsyncClient(transport=self.ipv6t) as client:
response = await client.get("https://text.ipv6.myip.wtf")
if response.is_success:
result = response.text.strip() or None
return result

View File

@ -2,3 +2,4 @@ httpx[http2]>=0.26,<1.0
asyncio>=3.4.3,<4 asyncio>=3.4.3,<4
aiofiles>=23,<24 aiofiles>=23,<24
toml>=0.10,<1 toml>=0.10,<1
netaddr>=1,<2