From e1d9bf31d17fc147099893c2494773581b632f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Wed, 15 Feb 2023 10:17:13 +0100 Subject: [PATCH] remove subdomains --- lnbits/extensions/subdomains/README.md | 54 -- lnbits/extensions/subdomains/__init__.py | 34 -- lnbits/extensions/subdomains/cloudflare.py | 49 -- lnbits/extensions/subdomains/config.json | 6 - lnbits/extensions/subdomains/crud.py | 161 ----- lnbits/extensions/subdomains/migrations.py | 41 -- lnbits/extensions/subdomains/models.py | 52 -- .../subdomains/static/image/subdomains.png | Bin 37360 -> 0 bytes lnbits/extensions/subdomains/tasks.py | 65 --- .../templates/subdomains/_api_docs.html | 31 - .../templates/subdomains/display.html | 221 ------- .../templates/subdomains/index.html | 549 ------------------ lnbits/extensions/subdomains/util.py | 32 - lnbits/extensions/subdomains/views.py | 47 -- lnbits/extensions/subdomains/views_api.py | 199 ------- 15 files changed, 1541 deletions(-) delete mode 100644 lnbits/extensions/subdomains/README.md delete mode 100644 lnbits/extensions/subdomains/__init__.py delete mode 100644 lnbits/extensions/subdomains/cloudflare.py delete mode 100644 lnbits/extensions/subdomains/config.json delete mode 100644 lnbits/extensions/subdomains/crud.py delete mode 100644 lnbits/extensions/subdomains/migrations.py delete mode 100644 lnbits/extensions/subdomains/models.py delete mode 100644 lnbits/extensions/subdomains/static/image/subdomains.png delete mode 100644 lnbits/extensions/subdomains/tasks.py delete mode 100644 lnbits/extensions/subdomains/templates/subdomains/_api_docs.html delete mode 100644 lnbits/extensions/subdomains/templates/subdomains/display.html delete mode 100644 lnbits/extensions/subdomains/templates/subdomains/index.html delete mode 100644 lnbits/extensions/subdomains/util.py delete mode 100644 lnbits/extensions/subdomains/views.py delete mode 100644 lnbits/extensions/subdomains/views_api.py diff --git a/lnbits/extensions/subdomains/README.md b/lnbits/extensions/subdomains/README.md deleted file mode 100644 index 3797c761f..000000000 --- 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 7434555d9..000000000 --- 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 3d3b9bdeb..000000000 --- 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 cec2ec647..000000000 --- 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 b3476ed93..000000000 --- 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 292d1f180..000000000 --- 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 552c37c7c..000000000 --- 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 c552cb7bdfcc754a96288deb9aa68961abb136a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37360 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 sV}Oak!ry^#-i 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 035d67a6d..000000000 --- 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 f11c9ddc6..000000000 --- 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 a39773e71..000000000 --- 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 9265e870a..000000000 --- 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}(?