Új DDNS szolgáltatások integrálása
A Hálózati elérés szekcióban látható beépített DDNS szolgáltatások (pl. DuckDNS) mellett lehetőség van saját vagy más nyilvános DDNS szolgáltatók beépítésére is.
Ebben a dokumentumban bemutatjuk, hogy milyen interfészt kell megvalósítani egy új DDNS szolgáltató integrálásához a StremHU Source backendjében.
Működési elv
A StremHU Source automatikusan felfedezi a DDNS szolgáltatókat az app/modules/network/ddns/providers/ könyvtárban található Python fájlok vizsgálatával. Bármely osztály, amely a BaseDDNSProvider absztrakt osztályból származik, automatikusan regisztrálásra kerül. Nincs szükség manuális regisztrációra központi helyen.
Új szolgáltató beépítése
- Hozz létre egy új Python fájlt az
app/modules/network/ddns/providers/könyvtárban (pl.custom_ddns.py). - Készíts egy osztályt, amely a
BaseDDNSProvider-ből öröklődik. - Implementáld a kötelező tulajdonságokat (property) és az
updatemetódust.
A BaseDDNSProvider interfész
Az új szolgáltatónak az alábbiakat kell megvalósítania:
from app.modules.network.ddns.base import BaseDDNSProvider
from app.modules.network.ddns.schemas.internal import DDNSIpUpdate, DDNSTxtUpdate
class MyCustomProvider(BaseDDNSProvider):
@property
def id(self) -> str:
"""A szolgáltató egyedi string azonosítója (pl. 'duckdns', 'myaddr')"""
return "custom_id"
@property
def name(self) -> str:
"""A szolgáltató neve, ahogy a UI-on megjelenik (pl. 'Custom DNS')"""
return "Custom DNS"
@property
def website_url(self) -> str:
"""A szolgáltató weboldala, ez megjelenik kliens oldalon is"""
return "https://www.example.com"
@property
def domain_regex(self) -> str:
"""A szolgáltatóhoz tartozó érvényes domain validálására szolgáló reguláris kifejezés"""
return r"^[a-zA-Z0-9-]+\.example\.com$"
async def update(self, payload: DDNSIpUpdate | DDNSTxtUpdate) -> None:
"""IP cím vagy TXT rekord (ACME challenge) frissítése a szolgáltatónál"""
# API hívás implementálása...
passAz update metódus és a payloadok
Az update metódus kétféle esemény kezeléséért felelős: az IP cím frissítéséért (alap DDNS funkció), illetve a Let’s Encrypt SSL tanúsítványok kiállításához szükséges TXT rekordok beállításáért (DNS-01 ACME challenge folyamat során).
A paraméterként kapott payload típusa alapján el kell dönteni, hogy milyen akciót kell végrehajtani:
1. DDNSIpUpdate (IP frissítés)
payload.provider_token: A felhasználó tokenje / kulcsa az API-hoz.payload.host: A frissítendő domain név.payload.ip: Az új IP cím, amire a domaint be kell állítani.
2. DDNSTxtUpdate (TXT rekord / ACME Challenge)
payload.provider_token: A felhasználó tokenje / kulcsa az API-hoz.payload.host: A domain név.payload.txt: A beállítandó TXT rekord értéke.payload.clear_txt: Boolean érték. HaTrue, akkor törölni kell a korábbi TXT rekordot (takarítás a sikeres challenge validáció után). Ha a szolgáltatód ezt nem igényli/támogatja, elég egyreturnutasítással kilépni a metódusból.
Példa API híváshoz
A hálózati kérésekhez a projektben az httpx könyvtár ajánlott aszinkron (httpx.AsyncClient) módban:
import httpx
from app.common.logger import logger
# ... az update metódus implementációja a fenti osztályban:
async def update(self, payload: DDNSIpUpdate | DDNSTxtUpdate) -> None:
params = {"token": payload.provider_token, "domain": payload.host}
if isinstance(payload, DDNSIpUpdate):
params["ip"] = payload.ip
if isinstance(payload, DDNSTxtUpdate):
if payload.clear_txt:
# TXT rekord törlésének logikája, ha szükséges
params["clear"] = "true"
else:
params["txt"] = payload.txt
try:
async with httpx.AsyncClient(timeout=10.0) as client:
# Példa kérés elküldése (szolgáltató API-jától függően POST is lehet json payload-al)
response = await client.get(f"{self.website_url}/api/update", params=params)
response.raise_for_status()
except httpx.HTTPError as e:
logger.error("MyCustomProvider hálózati hiba: %s", e)
raise RuntimeError(f"MyCustomProvider hálózati hiba: {e}") from e
if "ERROR" in response.text:
logger.error("MyCustomProvider API hiba: %s", response.text)
raise RuntimeError(f"Frissítés sikertelen: {response.text}")A szolgáltatónak az update folyamat sikerességét Exception (célszerűen RuntimeError) dobásával (hibás esetben) vagy anélkül (sikeres esetben) kell jeleznie. Javasolt a hálózati szintű hibákat (httpx.HTTPError) és az API logikai hibáit külön kezelni. Ha a metódus lefut anélkül, hogy hibát dobna, a frissítést a szerver sikeresnek tekinti.