commit ac4f568b46eca20e8dc185c75a6594349fc0b123 Author: Karim Abdul-Samad Date: Sat Feb 14 20:15:24 2026 -0500 upinit diff --git a/porkbun-refresher.py b/porkbun-refresher.py new file mode 100644 index 0000000..c32aa91 --- /dev/null +++ b/porkbun-refresher.py @@ -0,0 +1,96 @@ +import http.client +from types import MappingProxyType +import typing as t +import json +import os + +# Need to define the PRIMARY_DOMAIN, otherwise it will fail +PRIMARY_DOMAIN: t.Final = os.environ["PRIMARY_DOMAIN"] +PORKBUN_DOMAIN: t.Final = os.environ.get("PORKBUN_DOMAIN" , "api.porkbun.com") +REFERENTIAL_DOMAIN: t.Final = os.environ.get("REFERENTIAL_DOMAIN", "ifconfig.me") +PORKBUN_DNS_PREFIX: t.Final = os.environ.get("PORKBUN_DNS_PREFIX", "/api/json/v3/dns") +SECRET_FILE: t.Final = os.environ.get("SECRET_FILE", "/run/agenix/porkbun") + +with open(SECRET_FILE) as f: + KEYS: t.Final = MappingProxyType(dict(x.strip().split("=") for x in f.readlines())) + + +def get_current_ip(domain: str): + conn = http.client.HTTPSConnection(domain) + conn.request(method="GET", url="/ip") + res = conn.getresponse() + data = res.read().decode() + if res.status != 200: + raise RuntimeError(f"couldn't get ip addr: {res.status}: {res.reason}") + conn.close() + return data + + +class PorkbunRecord(t.TypedDict): + id: str + name: str + type: str + content: str + ttl: str + prio: str + notes: str + + +class PorkbunResponse(t.TypedDict): + status: str + cloudflare: t.Literal["enabled", "disabled"] + records: list[PorkbunRecord] + + +def get_porkbun_record(domain: str) -> PorkbunResponse: + body = KEYS.copy() + conn = http.client.HTTPSConnection(PORKBUN_DOMAIN) + conn.request( + method="POST", + url=f"{PORKBUN_DNS_PREFIX}/retrieveByNameType/{domain}/A/", + body=json.dumps(body), + ) + res = conn.getresponse() + data = res.read().decode() + if res.status != 200: + raise RuntimeError(f"couldn't get ip addr: {res.status}: {res.reason}") + conn.close() + return json.loads(data) + + +def set_porkbun_record(domain: str, ip_addr: str, id: str): + body = KEYS.copy() + body.update(type="A", content=ip_addr) + conn = http.client.HTTPSConnection(PORKBUN_DOMAIN) + conn.request( + method="POST", + url=f"{PORKBUN_DNS_PREFIX}/edit/{domain}/{id}", + body=json.dumps(body), + ) + res = conn.getresponse() + data = res.read().decode() + if res.status != 200: + raise RuntimeError(f"couldn't set ip addr: {res.status}: {res.reason}") + conn.close() + return json.loads(data) + + +def main(domain: str): + current_ip = get_current_ip(REFERENTIAL_DOMAIN) + + if (porkbun_record := get_porkbun_record(domain=domain))["status"] != "SUCCESS" or len( + porkbun_record["records"] + ) != 1: + raise RuntimeError("Something went very wrong") + + if (porkbun_ip := porkbun_record["records"][0]["content"]) == current_ip: + print("the ip hasn't changed. Nothing left to be done...") + else: + print(f"Current IP: {current_ip} Porkbun IP: {porkbun_ip}. Updating record...") + id = porkbun_record["records"][0]["id"] + set_porkbun_record(domain=domain, ip_addr=current_ip, id=id) + + +if __name__ == "__main__": + main(PRIMARY_DOMAIN) +