diff --git a/lnbits/extensions/subdomains/README.md b/lnbits/extensions/subdomains/README.md deleted file mode 100644 index 3797c761..00000000 --- a/lnbits/extensions/subdomains/README.md +++ /dev/null @@ -1,54 +0,0 @@ -

Subdomains Extension

- -So the goal of the extension is to allow the owner of a domain to sell subdomains to anyone who is willing to pay some money for it. - -[![video tutorial livestream](http://img.youtube.com/vi/O1X0fy3uNpw/0.jpg)](https://youtu.be/O1X0fy3uNpw 'video tutorial subdomains') - -## Requirements - -- Free Cloudflare account -- Cloudflare as a DNS server provider -- Cloudflare TOKEN and Cloudflare zone-ID where the domain is parked - -## Usage - -1. Register at Cloudflare and setup your domain with them. (Just follow instructions they provide...) -2. Change DNS server at your domain registrar to point to Cloudflare's -3. Get Cloudflare zone-ID for your domain - -4. Get Cloudflare API TOKEN - - -5. Open the LNbits subdomains extension and register your domain -6. Click on the button in the table to open the public form that was generated for your domain - - - Extension also supports webhooks so you can get notified when someone buys a new subdomain\ - - -## API Endpoints - -- **Domains** - - GET /api/v1/domains - - POST /api/v1/domains - - PUT /api/v1/domains/ - - DELETE /api/v1/domains/ -- **Subdomains** - - GET /api/v1/subdomains - - POST /api/v1/subdomains/ - - GET /api/v1/subdomains/ - - DELETE /api/v1/subdomains/ - -### Cloudflare - -- Cloudflare offers programmatic subdomain registration... (create new A record) -- you can keep your existing domain's registrar, you just have to transfer dns records to the cloudflare (free service) -- more information: - - https://api.cloudflare.com/#getting-started-requests - - API endpoints needed for our project: - - https://api.cloudflare.com/#dns-records-for-a-zone-list-dns-records - - https://api.cloudflare.com/#dns-records-for-a-zone-create-dns-record - - https://api.cloudflare.com/#dns-records-for-a-zone-delete-dns-record - - https://api.cloudflare.com/#dns-records-for-a-zone-update-dns-record -- api can be used by providing authorization token OR authorization key - - check API Tokens and API Keys : https://api.cloudflare.com/#getting-started-requests -- Cloudflare API postman collection: https://support.cloudflare.com/hc/en-us/articles/115002323852-Using-Cloudflare-API-with-Postman-Collections diff --git a/lnbits/extensions/subdomains/__init__.py b/lnbits/extensions/subdomains/__init__.py deleted file mode 100644 index 7434555d..00000000 --- a/lnbits/extensions/subdomains/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -import asyncio - -from fastapi import APIRouter -from fastapi.staticfiles import StaticFiles - -from lnbits.db import Database -from lnbits.helpers import template_renderer -from lnbits.tasks import catch_everything_and_restart - -db = Database("ext_subdomains") - -subdomains_ext: APIRouter = APIRouter(prefix="/subdomains", tags=["subdomains"]) - -subdomains_static_files = [ - { - "path": "/subdomains/static", - "app": StaticFiles(directory="lnbits/extensions/subdomains/static"), - "name": "subdomains_static", - } -] - - -def subdomains_renderer(): - return template_renderer(["lnbits/extensions/subdomains/templates"]) - - -from .tasks import wait_for_paid_invoices -from .views import * # noqa: F401,F403 -from .views_api import * # noqa: F401,F403 - - -def subdomains_start(): - loop = asyncio.get_event_loop() - loop.create_task(catch_everything_and_restart(wait_for_paid_invoices)) diff --git a/lnbits/extensions/subdomains/cloudflare.py b/lnbits/extensions/subdomains/cloudflare.py deleted file mode 100644 index 3d3b9bde..00000000 --- a/lnbits/extensions/subdomains/cloudflare.py +++ /dev/null @@ -1,49 +0,0 @@ -import httpx - -from .models import Domains - - -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 - async with httpx.AsyncClient() as client: - r = await client.post( - url, - headers=header, - json={ - "type": record_type, - "name": aRecord, - "content": ip, - "ttl": 0, - "proxied": False, - }, - timeout=40, - ) - r.raise_for_status() - return r.json() - - -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: - await client.delete(url + "/" + domain_id, headers=header, timeout=40) diff --git a/lnbits/extensions/subdomains/config.json b/lnbits/extensions/subdomains/config.json deleted file mode 100644 index cec2ec64..00000000 --- a/lnbits/extensions/subdomains/config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "Subdomains", - "short_description": "Sell subdomains of your domain", - "tile": "/subdomains/static/image/subdomains.png", - "contributors": ["grmkris"] -} diff --git a/lnbits/extensions/subdomains/crud.py b/lnbits/extensions/subdomains/crud.py deleted file mode 100644 index b3476ed9..00000000 --- a/lnbits/extensions/subdomains/crud.py +++ /dev/null @@ -1,161 +0,0 @@ -from typing import List, Optional, Union - -from lnbits.helpers import urlsafe_short_hash - -from . import db -from .models import CreateDomain, CreateSubdomain, Domains, Subdomains - - -async def create_subdomain(payment_hash, wallet, data: CreateSubdomain) -> Subdomains: - await db.execute( - """ - INSERT INTO subdomains.subdomain (id, domain, email, subdomain, ip, wallet, sats, duration, paid, record_type) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, - ( - payment_hash, - data.domain, - data.email, - data.subdomain, - data.ip, - wallet, - data.sats, - data.duration, - False, - data.record_type, - ), - ) - - new_subdomain = await get_subdomain(payment_hash) - assert new_subdomain, "Newly created subdomain couldn't be retrieved" - return new_subdomain - - -async def set_subdomain_paid(payment_hash: str) -> Subdomains: - row = await db.fetchone( - "SELECT s.*, d.domain as domain_name FROM subdomains.subdomain s INNER JOIN subdomains.domain d ON (s.domain = d.id) WHERE s.id = ?", - (payment_hash,), - ) - if row[8] is False: - await db.execute( - """ - UPDATE subdomains.subdomain - SET paid = true - WHERE id = ? - """, - (payment_hash,), - ) - - domaindata = await get_domain(row[1]) - assert domaindata, "Couldn't get domain from paid subdomain" - - amount = domaindata.amountmade + row[8] - await db.execute( - """ - UPDATE subdomains.domain - SET amountmade = ? - WHERE id = ? - """, - (amount, row[1]), - ) - - new_subdomain = await get_subdomain(payment_hash) - assert new_subdomain, "Newly paid subdomain couldn't be retrieved" - return new_subdomain - - -async def get_subdomain(subdomain_id: str) -> Optional[Subdomains]: - row = await db.fetchone( - "SELECT s.*, d.domain as domain_name FROM subdomains.subdomain s INNER JOIN subdomains.domain d ON (s.domain = d.id) WHERE s.id = ?", - (subdomain_id,), - ) - return Subdomains(**row) if row else None - - -async def get_subdomainBySubdomain(subdomain: str) -> Optional[Subdomains]: - row = await db.fetchone( - "SELECT s.*, d.domain as domain_name FROM subdomains.subdomain s INNER JOIN subdomains.domain d ON (s.domain = d.id) WHERE s.subdomain = ?", - (subdomain,), - ) - return Subdomains(**row) if row else None - - -async def get_subdomains(wallet_ids: Union[str, List[str]]) -> List[Subdomains]: - if isinstance(wallet_ids, str): - wallet_ids = [wallet_ids] - - q = ",".join(["?"] * len(wallet_ids)) - rows = await db.fetchall( - f"SELECT s.*, d.domain as domain_name FROM subdomains.subdomain s INNER JOIN subdomains.domain d ON (s.domain = d.id) WHERE s.wallet IN ({q})", - (*wallet_ids,), - ) - - return [Subdomains(**row) for row in rows] - - -async def delete_subdomain(subdomain_id: str) -> None: - await db.execute("DELETE FROM subdomains.subdomain WHERE id = ?", (subdomain_id,)) - - -# Domains - - -async def create_domain(data: CreateDomain) -> Domains: - domain_id = urlsafe_short_hash() - await db.execute( - """ - INSERT INTO subdomains.domain (id, wallet, domain, webhook, cf_token, cf_zone_id, description, cost, amountmade, allowed_record_types) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, - ( - domain_id, - data.wallet, - data.domain, - data.webhook, - data.cf_token, - data.cf_zone_id, - data.description, - data.cost, - 0, - data.allowed_record_types, - ), - ) - - new_domain = await get_domain(domain_id) - assert new_domain, "Newly created domain couldn't be retrieved" - return new_domain - - -async def update_domain(domain_id: str, **kwargs) -> Domains: - q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) - await db.execute( - f"UPDATE subdomains.domain SET {q} WHERE id = ?", (*kwargs.values(), domain_id) - ) - row = await db.fetchone( - "SELECT * FROM subdomains.domain WHERE id = ?", (domain_id,) - ) - assert row, "Newly updated domain couldn't be retrieved" - return Domains(**row) - - -async def get_domain(domain_id: str) -> Optional[Domains]: - row = await db.fetchone( - "SELECT * FROM subdomains.domain WHERE id = ?", (domain_id,) - ) - return Domains(**row) if row else None - - -async def get_domains(wallet_ids: Union[str, List[str]]) -> List[Domains]: - if isinstance(wallet_ids, str): - wallet_ids = [wallet_ids] - - q = ",".join(["?"] * len(wallet_ids)) - rows = await db.fetchall( - f"SELECT * FROM subdomains.domain WHERE wallet IN ({q})", (*wallet_ids,) - ) - - return [Domains(**row) for row in rows] - - -async def delete_domain(domain_id: str) -> None: - await db.execute("DELETE FROM subdomains.domain WHERE id = ?", (domain_id,)) diff --git a/lnbits/extensions/subdomains/migrations.py b/lnbits/extensions/subdomains/migrations.py deleted file mode 100644 index 292d1f18..00000000 --- a/lnbits/extensions/subdomains/migrations.py +++ /dev/null @@ -1,41 +0,0 @@ -async def m001_initial(db): - - await db.execute( - """ - CREATE TABLE subdomains.domain ( - id TEXT PRIMARY KEY, - wallet TEXT NOT NULL, - domain TEXT NOT NULL, - webhook TEXT, - cf_token TEXT NOT NULL, - cf_zone_id TEXT NOT NULL, - description TEXT NOT NULL, - cost INTEGER NOT NULL, - amountmade INTEGER NOT NULL, - allowed_record_types TEXT NOT NULL, - time TIMESTAMP NOT NULL DEFAULT """ - + db.timestamp_now - + """ - ); - """ - ) - - await db.execute( - """ - CREATE TABLE subdomains.subdomain ( - id TEXT PRIMARY KEY, - domain TEXT NOT NULL, - email TEXT NOT NULL, - subdomain TEXT NOT NULL, - ip TEXT NOT NULL, - wallet TEXT NOT NULL, - sats INTEGER NOT NULL, - duration INTEGER NOT NULL, - paid BOOLEAN NOT NULL, - record_type TEXT NOT NULL, - time TIMESTAMP NOT NULL DEFAULT """ - + db.timestamp_now - + """ - ); - """ - ) diff --git a/lnbits/extensions/subdomains/models.py b/lnbits/extensions/subdomains/models.py deleted file mode 100644 index 552c37c7..00000000 --- a/lnbits/extensions/subdomains/models.py +++ /dev/null @@ -1,52 +0,0 @@ -from fastapi import Query -from pydantic import BaseModel - - -class CreateDomain(BaseModel): - wallet: str = Query(...) - domain: str = Query(...) - cf_token: str = Query(...) - cf_zone_id: str = Query(...) - webhook: str = Query("") - description: str = Query(..., min_length=0) - cost: int = Query(..., ge=0) - allowed_record_types: str = Query(...) - - -class CreateSubdomain(BaseModel): - domain: str = Query(...) - subdomain: str = Query(...) - email: str = Query(...) - ip: str = Query(...) - sats: int = Query(..., ge=0) - duration: int = Query(...) - record_type: str = Query(...) - - -class Domains(BaseModel): - id: str - wallet: str - domain: str - cf_token: str - cf_zone_id: str - webhook: str - description: str - cost: int - amountmade: int - time: int - allowed_record_types: str - - -class Subdomains(BaseModel): - id: str - wallet: str - domain: str - domain_name: str - subdomain: str - email: str - ip: str - sats: int - duration: int - paid: bool - time: int - record_type: str diff --git a/lnbits/extensions/subdomains/static/image/subdomains.png b/lnbits/extensions/subdomains/static/image/subdomains.png deleted file mode 100644 index c552cb7b..00000000 Binary files a/lnbits/extensions/subdomains/static/image/subdomains.png and /dev/null differ diff --git a/lnbits/extensions/subdomains/tasks.py b/lnbits/extensions/subdomains/tasks.py deleted file mode 100644 index 42b7885a..00000000 --- a/lnbits/extensions/subdomains/tasks.py +++ /dev/null @@ -1,65 +0,0 @@ -import asyncio - -import httpx -from loguru import logger - -from lnbits.core.models import Payment -from lnbits.helpers import get_current_extension_name -from lnbits.tasks import register_invoice_listener - -from .cloudflare import cloudflare_create_subdomain -from .crud import get_domain, set_subdomain_paid - - -async def wait_for_paid_invoices(): - invoice_queue = asyncio.Queue() - register_invoice_listener(invoice_queue, get_current_extension_name()) - - while True: - payment = await invoice_queue.get() - await on_invoice_paid(payment) - - -async def on_invoice_paid(payment: Payment) -> None: - if payment.extra.get("tag") != "lnsubdomain": - # not an lnsubdomain invoice - return - - await payment.set_pending(False) - subdomain = await set_subdomain_paid(payment_hash=payment.payment_hash) - domain = await get_domain(subdomain.domain) - - ### Create subdomain - try: - cf_response = await cloudflare_create_subdomain( - domain=domain, # type: ignore - subdomain=subdomain.subdomain, - record_type=subdomain.record_type, - ip=subdomain.ip, - ) - except Exception as exc: - logger.error(exc) - logger.error("could not create subdomain on cloudflare") - return - - ### Use webhook to notify about cloudflare registration - if domain and domain.webhook: - async with httpx.AsyncClient() as client: - try: - r = await client.post( - domain.webhook, - json={ - "domain": subdomain.domain_name, - "subdomain": subdomain.subdomain, - "record_type": subdomain.record_type, - "email": subdomain.email, - "ip": subdomain.ip, - "cost:": str(subdomain.sats) + " sats", - "duration": str(subdomain.duration) + " days", - "cf_response": cf_response, - }, - timeout=40, - ) - assert r - except AssertionError: - pass diff --git a/lnbits/extensions/subdomains/templates/subdomains/_api_docs.html b/lnbits/extensions/subdomains/templates/subdomains/_api_docs.html deleted file mode 100644 index 035d67a6..00000000 --- a/lnbits/extensions/subdomains/templates/subdomains/_api_docs.html +++ /dev/null @@ -1,31 +0,0 @@ - - - -
- lnSubdomains: Get paid sats to sell your subdomains -
-

- Charge people for using your subdomain name...
- - More details -
- - Created by, - Kris -

-
- -
-
diff --git a/lnbits/extensions/subdomains/templates/subdomains/display.html b/lnbits/extensions/subdomains/templates/subdomains/display.html deleted file mode 100644 index f11c9ddc..00000000 --- a/lnbits/extensions/subdomains/templates/subdomains/display.html +++ /dev/null @@ -1,221 +0,0 @@ -{% extends "public.html" %} {% block page %} -
-
- - -

{{ domain_domain }}

-
-
{{ domain_desc }}
-
- - - - - - - - - -

- Cost per day: {{ domain_cost }} sats
- {% raw %} Total cost: {{amountSats}} sats {% endraw %} -

-
- Submit - Cancel -
-
-
-
-
- - - - - - -
- Copy invoice - Close -
-
-
-
- -{% endblock %} {% block scripts %} - -{% endblock %} diff --git a/lnbits/extensions/subdomains/templates/subdomains/index.html b/lnbits/extensions/subdomains/templates/subdomains/index.html deleted file mode 100644 index a39773e7..00000000 --- a/lnbits/extensions/subdomains/templates/subdomains/index.html +++ /dev/null @@ -1,549 +0,0 @@ -{% extends "base.html" %} {% from "macros.jinja" import window_vars with context -%} {% block page %} - -
-
- - - New Domain - - - - - -
-
-
Domains
-
-
- Export to CSV -
-
- - {% raw %} - - - {% endraw %} - -
-
- - - -
-
-
Subdomains
-
-
- Export to CSV -
-
- - {% raw %} - - - {% endraw %} - -
-
-
-
- - -
- {{SITE_TITLE}} Subdomain extension -
-
- - - {% include "subdomains/_api_docs.html" %} - -
-
- - - - - - - - - - - - - - - - -
- Update Form - Create Domain - Cancel -
-
-
-
-
- -{% endblock %} {% block scripts %} {{ window_vars(user) }} - -{% endblock %} diff --git a/lnbits/extensions/subdomains/util.py b/lnbits/extensions/subdomains/util.py deleted file mode 100644 index 9265e870..00000000 --- a/lnbits/extensions/subdomains/util.py +++ /dev/null @@ -1,32 +0,0 @@ -import re -import socket - - -# Function to validate domain name. -def isValidDomain(str): - # Regex to check valid - # domain name. - regex = "^((?!-)[A-Za-z0-9-]{1,63}(?