diff --git a/lnbits/extensions/subdomains/cloudflare.py b/lnbits/extensions/subdomains/cloudflare.py new file mode 100644 index 000000000..7af85f40d --- /dev/null +++ b/lnbits/extensions/subdomains/cloudflare.py @@ -0,0 +1,44 @@ +from lnbits.extensions.subdomains.models import Domains +import httpx, json + + +async def cloudflare_create_subdomain(domain: Domains, subdomain: str, record_type: str, ip: str): + # Call to cloudflare sort of a dry-run - if success delete the domain and wait for payment + ### SEND REQUEST TO CLOUDFLARE + url = "https://api.cloudflare.com/client/v4/zones/" + domain.cf_zone_id + "/dns_records" + header = {"Authorization": "Bearer " + domain.cf_token, "Content-Type": "application/json"} + aRecord = subdomain + "." + domain.domain + cf_response = "" + async with httpx.AsyncClient() as client: + try: + r = await client.post( + url, + headers=header, + json={ + "type": record_type, + "name": aRecord, + "content": ip, + "ttl": 0, + "proxed": False, + }, + timeout=40, + ) + cf_response = json.loads(r.text) + except AssertionError: + cf_response = "Error occured" + return cf_response + + +async def cloudflare_deletesubdomain(domain: Domains, domain_id: str): + url = "https://api.cloudflare.com/client/v4/zones/" + domain.cf_zone_id + "/dns_records" + header = {"Authorization": "Bearer " + domain.cf_token, "Content-Type": "application/json"} + async with httpx.AsyncClient() as client: + try: + r = await client.delete( + url + "/" + domain_id, + headers=header, + timeout=40, + ) + cf_response = r.text + except AssertionError: + cf_response = "Error occured" diff --git a/lnbits/extensions/subdomains/tasks.py b/lnbits/extensions/subdomains/tasks.py index 322543329..f5f193a62 100644 --- a/lnbits/extensions/subdomains/tasks.py +++ b/lnbits/extensions/subdomains/tasks.py @@ -1,7 +1,6 @@ from http import HTTPStatus from quart.json import jsonify import trio # type: ignore -import json import httpx from .crud import get_domain, set_subdomain_paid @@ -9,6 +8,7 @@ from lnbits.core.crud import get_user, get_wallet from lnbits.core import db as core_db from lnbits.core.models import Payment from lnbits.tasks import register_invoice_listener +from .cloudflare import cloudflare_create_subdomain async def register_listeners(): @@ -31,28 +31,10 @@ async def on_invoice_paid(payment: Payment) -> None: subdomain = await set_subdomain_paid(payment_hash=payment.payment_hash) domain = await get_domain(subdomain.domain) - ### SEND REQUEST TO CLOUDFLARE - url = "https://api.cloudflare.com/client/v4/zones/" + domain.cf_zone_id + "/dns_records" - header = {"Authorization": "Bearer " + domain.cf_token, "Content-Type": "application/json"} - aRecord = subdomain.subdomain + "." + subdomain.domain_name - cf_response = "" - async with httpx.AsyncClient() as client: - try: - r = await client.post( - url, - headers=header, - json={ - "type": subdomain.record_type, - "name": aRecord, - "content": subdomain.ip, - "ttl": 0, - "proxed": False, - }, - timeout=40, - ) - cf_response = r.text - except AssertionError: - cf_response = "Error occured" + ### Create subdomain + cf_response = cloudflare_create_subdomain( + domain=domain, subdomain=subdomain.subdomain, record_type=subdomain.record_type, ip=subdomain.ip + ) ### Use webhook to notify about cloudflare registration if domain.webhook: diff --git a/lnbits/extensions/subdomains/views_api.py b/lnbits/extensions/subdomains/views_api.py index e4ed980a3..92375b561 100644 --- a/lnbits/extensions/subdomains/views_api.py +++ b/lnbits/extensions/subdomains/views_api.py @@ -2,7 +2,9 @@ import re from quart import g, jsonify, request from http import HTTPStatus from lnbits.core import crud +import json +import httpx from lnbits.core.crud import get_user, get_wallet from lnbits.core.services import create_invoice, check_invoice_status from lnbits.decorators import api_check_wallet_key, api_validate_post_request @@ -21,6 +23,7 @@ from .crud import ( delete_domain, get_subdomainBySubdomain, ) +from .cloudflare import cloudflare_create_subdomain, cloudflare_deletesubdomain # domainS @@ -113,32 +116,45 @@ async def api_subdomains(): async def api_subdomain_make_subdomain(domain_id): domain = await get_domain(domain_id) + # If the request is coming for the non-existant domain if not domain: return jsonify({"message": "LNsubdomain does not exist."}), HTTPStatus.NOT_FOUND + # regex if IP is address if not isvalidIPAddress(g.data["ip"]): return jsonify({"message": g.data["ip"] + " Not a valid IP address"}), HTTPStatus.BAD_REQUEST + # regex for checking if domain is valid if not isValidDomain(g.data["subdomain"] + "." + domain.domain): return ( jsonify({"message": g.data["subdomain"] + "." + domain.domain + " bad domain name"}), HTTPStatus.BAD_REQUEST, ) - + ## If domain already exist in our database reject it if await get_subdomainBySubdomain(g.data["subdomain"]) is not None: return ( jsonify({"message": g.data["subdomain"] + "." + domain.domain + " domain already taken"}), HTTPStatus.BAD_REQUEST, ) - + ## If record_type is not one of the allowed ones reject the request if g.data["record_type"] not in domain.allowed_record_types: return jsonify({"message": g.data["record_type"] + "Not a valid record"}), HTTPStatus.BAD_REQUEST + ## Dry run cloudflare... (create and if create is sucessful delete it) + cf_response = await cloudflare_create_subdomain( + domain=domain, subdomain=g.data["subdomain"], record_type=g.data["record_type"], ip=g.data["ip"] + ) + if cf_response["success"] == True: + cloudflare_deletesubdomain(domain=domain, domain_id=cf_response["result"]["id"]) + else: + return ( + jsonify({"message": "Problem with cloudflare: " + cf_response["errors"][0]["message"]}), + HTTPStatus.BAD_REQUEST, + ) - subdomain = g.data["subdomain"] - duration = g.data["duration"] + ## ALL OK - create an invoice and return it to the user sats = g.data["sats"] payment_hash, payment_request = await create_invoice( wallet_id=domain.wallet, amount=sats, - memo=f"subdomain {subdomain}.{domain.domain} for {sats} sats for {duration} days", + memo=f"subdomain {g.data['subdomain']}.{domain.domain} for {sats} sats for {g.data['duration']} days", extra={"tag": "lnsubdomain"}, )