- Implement modular Textual TUI (main, app, widgets, config, api_client) - Integrate IPv64.net REST API client using httpx - Support record listing, creation, in-place edit, and deletion - Add flexible token resolution (.env, env export, interactive prompt) - Add bilingual README (DE/EN) and project configuration
107 lines
3.3 KiB
Python
Executable File
107 lines
3.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""IPv64 DNS TUI Client
|
|
Author: Pascal Bouquet <pascal@ptbos.de>
|
|
License: GNU General Public License v3.0 (GPL-3.0)
|
|
Year: 2026
|
|
"""
|
|
|
|
import httpx
|
|
|
|
BASE_URL = "https://ipv64.net/api.php"
|
|
|
|
|
|
class IPv64Client:
|
|
def __init__(self, api_key: str):
|
|
self.headers = {"Authorization": f"Bearer {api_key.strip()}"}
|
|
|
|
async def get_account_info(self) -> dict:
|
|
"""[GET] get_account_info: Ruft Kontoinformationen und Limits ab."""
|
|
async with httpx.AsyncClient() as client:
|
|
resp = await client.get(
|
|
f"{BASE_URL}?get_account_info",
|
|
headers=self.headers,
|
|
timeout=10.0,
|
|
)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
async def get_domains(self) -> dict:
|
|
"""[GET] get_domains: Liefert alle Domains und Records."""
|
|
async with httpx.AsyncClient() as client:
|
|
resp = await client.get(
|
|
f"{BASE_URL}?get_domains",
|
|
headers=self.headers,
|
|
timeout=10.0,
|
|
)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
async def add_domain(self, domain_name: str) -> dict:
|
|
"""[POST] add_domain: Legt eine neue Domain an."""
|
|
data = {"add_domain": domain_name}
|
|
async with httpx.AsyncClient() as client:
|
|
resp = await client.post(
|
|
BASE_URL,
|
|
headers=self.headers,
|
|
data=data,
|
|
timeout=10.0,
|
|
)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
async def delete_domain(self, domain_name: str) -> dict:
|
|
"""[DELETE] del_domain: Löscht Domain mit allen Einträgen."""
|
|
data = {"del_domain": domain_name}
|
|
async with httpx.AsyncClient() as client:
|
|
resp = await client.request(
|
|
"DELETE",
|
|
BASE_URL,
|
|
headers=self.headers,
|
|
data=data,
|
|
timeout=10.0,
|
|
)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
async def add_record(
|
|
self,
|
|
domain: str,
|
|
subdomain: str,
|
|
typ: str,
|
|
content: str,
|
|
prio: str = "",
|
|
) -> dict:
|
|
"""[POST] add_record: DNS-Record anlegen."""
|
|
data = {
|
|
"add_record": domain,
|
|
"praefix": subdomain,
|
|
"type": typ.upper(),
|
|
"content": content,
|
|
}
|
|
if prio and prio.strip() and prio.strip().lower() != "none":
|
|
data["prio"] = prio.strip()
|
|
|
|
async with httpx.AsyncClient() as client:
|
|
resp = await client.post(
|
|
BASE_URL,
|
|
headers=self.headers,
|
|
data=data,
|
|
timeout=10.0,
|
|
)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
async def delete_record_by_id(self, record_id: int | str) -> dict:
|
|
"""[DELETE] del_record per ID."""
|
|
data = {"del_record": str(record_id)}
|
|
async with httpx.AsyncClient() as client:
|
|
resp = await client.request(
|
|
"DELETE",
|
|
BASE_URL,
|
|
headers=self.headers,
|
|
data=data,
|
|
timeout=10.0,
|
|
)
|
|
resp.raise_for_status()
|
|
return resp.json()
|