From f23efaf75012691ba41ca4e2fe29402780852b78 Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 14 Feb 2023 13:09:19 +0000 Subject: [PATCH] Removed nostrnip5 --- lnbits/extensions/nostrnip5/README.md | 59 -- lnbits/extensions/nostrnip5/__init__.py | 36 - lnbits/extensions/nostrnip5/config.json | 6 - lnbits/extensions/nostrnip5/crud.py | 211 ----- lnbits/extensions/nostrnip5/migrations.py | 35 - lnbits/extensions/nostrnip5/models.py | 58 -- .../nostrnip5/static/css/signup.css | 0 .../nostrnip5/static/image/nostrnip5.png | Bin 18041 -> 0 bytes lnbits/extensions/nostrnip5/tasks.py | 33 - .../templates/nostrnip5/_api_docs.html | 238 ------ .../nostrnip5/templates/nostrnip5/index.html | 785 ------------------ .../nostrnip5/templates/nostrnip5/rotate.html | 88 -- .../nostrnip5/templates/nostrnip5/signup.html | 209 ----- lnbits/extensions/nostrnip5/views.py | 67 -- lnbits/extensions/nostrnip5/views_api.py | 284 ------- 15 files changed, 2109 deletions(-) delete mode 100644 lnbits/extensions/nostrnip5/README.md delete mode 100644 lnbits/extensions/nostrnip5/__init__.py delete mode 100644 lnbits/extensions/nostrnip5/config.json delete mode 100644 lnbits/extensions/nostrnip5/crud.py delete mode 100644 lnbits/extensions/nostrnip5/migrations.py delete mode 100644 lnbits/extensions/nostrnip5/models.py delete mode 100644 lnbits/extensions/nostrnip5/static/css/signup.css delete mode 100644 lnbits/extensions/nostrnip5/static/image/nostrnip5.png delete mode 100644 lnbits/extensions/nostrnip5/tasks.py delete mode 100644 lnbits/extensions/nostrnip5/templates/nostrnip5/_api_docs.html delete mode 100644 lnbits/extensions/nostrnip5/templates/nostrnip5/index.html delete mode 100644 lnbits/extensions/nostrnip5/templates/nostrnip5/rotate.html delete mode 100644 lnbits/extensions/nostrnip5/templates/nostrnip5/signup.html delete mode 100644 lnbits/extensions/nostrnip5/views.py delete mode 100644 lnbits/extensions/nostrnip5/views_api.py diff --git a/lnbits/extensions/nostrnip5/README.md b/lnbits/extensions/nostrnip5/README.md deleted file mode 100644 index 2bcbf054..00000000 --- a/lnbits/extensions/nostrnip5/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# Nostr NIP-05 - -## Allow users to NIP-05 verify themselves at a domain you control - -This extension allows users to sell NIP-05 verification to other nostr users on a domain they control. - -## Usage - -1. Create a Domain by clicking "NEW DOMAIN"\ -2. Fill the options for your DOMAIN - - select the wallet - - select the fiat currency the invoice will be denominated in - - select an amount in fiat to charge users for verification - - enter the domain (or subdomain) you want to provide verification for - - Note, you must own this domain and have access to a web server -3. You can then use share your signup link with your users to allow them to sign up - - -## Installation - -In order for this to work, you need to have ownership of a domain name, and access to a web server that this domain is pointed to. - -Then, you'll need to set up a proxy that points `https://{your_domain}/.well-known/nostr.json` to `https://{your_lnbits}/nostrnip5/api/v1/domain/{domain_id}/nostr.json` - -Example nginx configuration - -``` -## Proxy Server Caching -proxy_cache_path /tmp/nginx_cache keys_zone=nip5_cache:5m levels=1:2 inactive=300s max_size=100m use_temp_path=off; - -location /.well-known/nostr.json { - proxy_pass https://{your_lnbits}/nostrnip5/api/v1/domain/{domain_id}/nostr.json; - proxy_set_header Host {your_lnbits}; - proxy_ssl_server_name on; - - expires 5m; - add_header Cache-Control "public, no-transform"; - - proxy_cache nip5_cache; - proxy_cache_lock on; - proxy_cache_valid 200 300s; - proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504; -} -``` - -Example Caddy configuration - -``` -my.lnbits.instance { - reverse_proxy {your_lnbits} -} - -nip.5.domain { - route /.well-known/nostr.json { - rewrite * /nostrnip5/api/v1/domain/{domain_id}/nostr.json - reverse_proxy {your_lnbits} - } -} -``` \ No newline at end of file diff --git a/lnbits/extensions/nostrnip5/__init__.py b/lnbits/extensions/nostrnip5/__init__.py deleted file mode 100644 index a9cb526d..00000000 --- a/lnbits/extensions/nostrnip5/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -import asyncio - -from fastapi import APIRouter -from starlette.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_nostrnip5") - -nostrnip5_static_files = [ - { - "path": "/nostrnip5/static", - "app": StaticFiles(directory="lnbits/extensions/nostrnip5/static"), - "name": "nostrnip5_static", - } -] - -nostrnip5_ext: APIRouter = APIRouter(prefix="/nostrnip5", tags=["nostrnip5"]) - - -def nostrnip5_renderer(): - return template_renderer(["lnbits/extensions/nostrnip5/templates"]) - - -from .tasks import wait_for_paid_invoices - - -def nostrnip5_start(): - loop = asyncio.get_event_loop() - loop.create_task(catch_everything_and_restart(wait_for_paid_invoices)) - - -from .views import * # noqa: F401,F403 -from .views_api import * # noqa: F401,F403 diff --git a/lnbits/extensions/nostrnip5/config.json b/lnbits/extensions/nostrnip5/config.json deleted file mode 100644 index 8621b17c..00000000 --- a/lnbits/extensions/nostrnip5/config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "Nostr NIP-5", - "short_description": "Verify addresses for Nostr NIP-5", - "tile": "/nostrnip5/static/image/nostrnip5.png", - "contributors": ["leesalminen"] -} diff --git a/lnbits/extensions/nostrnip5/crud.py b/lnbits/extensions/nostrnip5/crud.py deleted file mode 100644 index f7ec929c..00000000 --- a/lnbits/extensions/nostrnip5/crud.py +++ /dev/null @@ -1,211 +0,0 @@ -from typing import List, Optional, Union - -from lnbits.helpers import urlsafe_short_hash - -from . import db -from .models import Address, CreateAddressData, CreateDomainData, Domain, EditDomainData - - -async def get_domain(domain_id: str) -> Optional[Domain]: - row = await db.fetchone( - "SELECT * FROM nostrnip5.domains WHERE id = ?", (domain_id,) - ) - return Domain.from_row(row) if row else None - - -async def get_domain_by_name(domain: str) -> Optional[Domain]: - row = await db.fetchone( - "SELECT * FROM nostrnip5.domains WHERE domain = ?", (domain,) - ) - return Domain.from_row(row) if row else None - - -async def get_domains(wallet_ids: Union[str, List[str]]) -> List[Domain]: - if isinstance(wallet_ids, str): - wallet_ids = [wallet_ids] - - q = ",".join(["?"] * len(wallet_ids)) - rows = await db.fetchall( - f"SELECT * FROM nostrnip5.domains WHERE wallet IN ({q})", (*wallet_ids,) - ) - - return [Domain.from_row(row) for row in rows] - - -async def get_address(domain_id: str, address_id: str) -> Optional[Address]: - row = await db.fetchone( - "SELECT * FROM nostrnip5.addresses WHERE domain_id = ? AND id = ?", - ( - domain_id, - address_id, - ), - ) - return Address.from_row(row) if row else None - - -async def get_address_by_local_part( - domain_id: str, local_part: str -) -> Optional[Address]: - row = await db.fetchone( - "SELECT * FROM nostrnip5.addresses WHERE domain_id = ? AND local_part = ?", - ( - domain_id, - local_part.lower(), - ), - ) - return Address.from_row(row) if row else None - - -async def get_addresses(domain_id: str) -> List[Address]: - rows = await db.fetchall( - "SELECT * FROM nostrnip5.addresses WHERE domain_id = ?", (domain_id,) - ) - - return [Address.from_row(row) for row in rows] - - -async def get_all_addresses(wallet_ids: Union[str, List[str]]) -> List[Address]: - if isinstance(wallet_ids, str): - wallet_ids = [wallet_ids] - - q = ",".join(["?"] * len(wallet_ids)) - rows = await db.fetchall( - f""" - SELECT a.* - FROM nostrnip5.addresses a - JOIN nostrnip5.domains d ON d.id = a.domain_id - WHERE d.wallet IN ({q}) - """, - (*wallet_ids,), - ) - - return [Address.from_row(row) for row in rows] - - -async def activate_address(domain_id: str, address_id: str) -> Address: - await db.execute( - """ - UPDATE nostrnip5.addresses - SET active = true - WHERE domain_id = ? - AND id = ? - """, - ( - domain_id, - address_id, - ), - ) - - address = await get_address(domain_id, address_id) - assert address, "Newly updated address couldn't be retrieved" - return address - - -async def rotate_address(domain_id: str, address_id: str, pubkey: str) -> Address: - await db.execute( - """ - UPDATE nostrnip5.addresses - SET pubkey = ? - WHERE domain_id = ? - AND id = ? - """, - ( - pubkey, - domain_id, - address_id, - ), - ) - - address = await get_address(domain_id, address_id) - assert address, "Newly updated address couldn't be retrieved" - return address - - -async def delete_domain(domain_id) -> bool: - await db.execute( - """ - DELETE FROM nostrnip5.addresses WHERE domain_id = ? - """, - (domain_id,), - ) - - await db.execute( - """ - DELETE FROM nostrnip5.domains WHERE id = ? - """, - (domain_id,), - ) - - return True - - -async def delete_address(address_id): - await db.execute( - """ - DELETE FROM nostrnip5.addresses WHERE id = ? - """, - (address_id,), - ) - - -async def create_address_internal(domain_id: str, data: CreateAddressData) -> Address: - address_id = urlsafe_short_hash() - - await db.execute( - """ - INSERT INTO nostrnip5.addresses (id, domain_id, local_part, pubkey, active) - VALUES (?, ?, ?, ?, ?) - """, - ( - address_id, - domain_id, - data.local_part.lower(), - data.pubkey, - False, - ), - ) - - address = await get_address(domain_id, address_id) - assert address, "Newly created address couldn't be retrieved" - return address - - -async def update_domain_internal(wallet_id: str, data: EditDomainData) -> Domain: - if data.currency != "Satoshis": - amount = data.amount * 100 - else: - amount = data.amount - print(data) - await db.execute( - """ - UPDATE nostrnip5.domains - SET amount = ?, currency = ? - WHERE id = ? - """, - (int(amount), data.currency, data.id), - ) - - domain = await get_domain(data.id) - assert domain, "Domain couldn't be updated" - return domain - - -async def create_domain_internal(wallet_id: str, data: CreateDomainData) -> Domain: - domain_id = urlsafe_short_hash() - - if data.currency != "Satoshis": - amount = data.amount * 100 - else: - amount = data.amount - - await db.execute( - """ - INSERT INTO nostrnip5.domains (id, wallet, currency, amount, domain) - VALUES (?, ?, ?, ?, ?) - """, - (domain_id, wallet_id, data.currency, int(amount), data.domain), - ) - - domain = await get_domain(domain_id) - assert domain, "Newly created domain couldn't be retrieved" - return domain diff --git a/lnbits/extensions/nostrnip5/migrations.py b/lnbits/extensions/nostrnip5/migrations.py deleted file mode 100644 index 7c5a49dd..00000000 --- a/lnbits/extensions/nostrnip5/migrations.py +++ /dev/null @@ -1,35 +0,0 @@ -async def m001_initial_invoices(db): - - await db.execute( - f""" - CREATE TABLE nostrnip5.domains ( - id TEXT PRIMARY KEY, - wallet TEXT NOT NULL, - - currency TEXT NOT NULL, - amount INTEGER NOT NULL, - - domain TEXT NOT NULL, - - time TIMESTAMP NOT NULL DEFAULT {db.timestamp_now} - ); - """ - ) - - await db.execute( - f""" - CREATE TABLE nostrnip5.addresses ( - id TEXT PRIMARY KEY, - domain_id TEXT NOT NULL, - - local_part TEXT NOT NULL, - pubkey TEXT NOT NULL, - - active BOOLEAN NOT NULL DEFAULT false, - - time TIMESTAMP NOT NULL DEFAULT {db.timestamp_now}, - - FOREIGN KEY(domain_id) REFERENCES {db.references_schema}domains(id) - ); - """ - ) diff --git a/lnbits/extensions/nostrnip5/models.py b/lnbits/extensions/nostrnip5/models.py deleted file mode 100644 index 7e7bf254..00000000 --- a/lnbits/extensions/nostrnip5/models.py +++ /dev/null @@ -1,58 +0,0 @@ -from sqlite3 import Row - -from fastapi.param_functions import Query -from pydantic import BaseModel - - -class RotateAddressData(BaseModel): - pubkey: str - - -class CreateAddressData(BaseModel): - domain_id: str - local_part: str - pubkey: str - active: bool = False - - -class CreateDomainData(BaseModel): - wallet: str - currency: str - amount: float = Query(..., ge=0.01) - domain: str - - -class EditDomainData(BaseModel): - id: str - currency: str - amount: float = Query(..., ge=0.01) - - @classmethod - def from_row(cls, row: Row) -> "EditDomainData": - return cls(**dict(row)) - - -class Domain(BaseModel): - id: str - wallet: str - currency: str - amount: int - domain: str - time: int - - @classmethod - def from_row(cls, row: Row) -> "Domain": - return cls(**dict(row)) - - -class Address(BaseModel): - id: str - domain_id: str - local_part: str - pubkey: str - active: bool - time: int - - @classmethod - def from_row(cls, row: Row) -> "Address": - return cls(**dict(row)) diff --git a/lnbits/extensions/nostrnip5/static/css/signup.css b/lnbits/extensions/nostrnip5/static/css/signup.css deleted file mode 100644 index e69de29b..00000000 diff --git a/lnbits/extensions/nostrnip5/static/image/nostrnip5.png b/lnbits/extensions/nostrnip5/static/image/nostrnip5.png deleted file mode 100644 index 91dc47f0c1584285e86e6eb3ff09d21fb6c588c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18041 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 diff --git a/lnbits/extensions/nostrnip5/tasks.py b/lnbits/extensions/nostrnip5/tasks.py deleted file mode 100644 index f0d0c965..00000000 --- a/lnbits/extensions/nostrnip5/tasks.py +++ /dev/null @@ -1,33 +0,0 @@ -import asyncio - -from loguru import logger - -from lnbits.core.models import Payment -from lnbits.tasks import register_invoice_listener - -from .crud import activate_address - - -async def wait_for_paid_invoices(): - invoice_queue = asyncio.Queue() - register_invoice_listener(invoice_queue) - - 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") != "nostrnip5": - return - - domain_id = payment.extra.get("domain_id") - address_id = payment.extra.get("address_id") - - if domain_id and address_id: - logger.info("Activating NOSTR NIP-05") - logger.info(domain_id) - logger.info(address_id) - await activate_address(domain_id, address_id) - - return diff --git a/lnbits/extensions/nostrnip5/templates/nostrnip5/_api_docs.html b/lnbits/extensions/nostrnip5/templates/nostrnip5/_api_docs.html deleted file mode 100644 index a3f91201..00000000 --- a/lnbits/extensions/nostrnip5/templates/nostrnip5/_api_docs.html +++ /dev/null @@ -1,238 +0,0 @@ - - -

- - Usage
- - 1. Create a Domain by clicking "NEW DOMAIN"\
- 2. Fill the options for your DOMAIN
- - select the wallet
- - select the fiat currency the invoice will be denominated in
- - select an amount in fiat to charge users for verification
- - enter the domain (or subdomain) you want to provide verification - for
- 3. You can then use share your signup link with your users to allow them - to sign up *Note, you must own this domain and have access to a web - server* -

- Installation
- - In order for this to work, you need to have ownership of a domain name, - and access to a web server that this domain is pointed to. Then, you'll - need to set up a proxy that points - `https://{your_domain}/.well-known/nostr.json` to - `https://{your_lnbits}/nostrnip5/api/v1/domain/{domain_id}/nostr.json` -

- Example nginx configuration -
- - - - - proxy_cache_path /tmp/nginx_cache keys_zone=nip5_cache:5m - levels=1:2 inactive=300s max_size=100m use_temp_path=off;
- - location /.well-known/nostr.json {
-     proxy_pass - https://{your_lnbits}/nostrnip5/api/v1/domain/{domain_id}/nostr.json;
-     proxy_set_header Host {your_lnbits};
-     proxy_ssl_server_name on;

- -     expires 5m;
-     add_header Cache-Control "public, - no-transform";

- -     proxy_cache nip5_cache;
-     proxy_cache_lock on;
-     proxy_cache_valid 200 300s;
-     proxy_cache_use_stale error timeout - invalid_header updating http_500 http_502 http_503 http_504;
- } -
-
-
-
-

-
-
- - - - - - - GET /nostrnip5/api/v1/domains -
Headers
- {"X-Api-Key": <invoice_key>}
-
Body (application/json)
-
- Returns 200 OK (application/json) -
- [<domain_object>, ...] -
Curl example
- curl -X GET {{ request.base_url }}nostrnip5/api/v1/domains -H - "X-Api-Key: <invoice_key>" - -
-
-
- - - - - GET /nostrnip5/api/v1/addresses -
Headers
- {"X-Api-Key": <invoice_key>}
-
Body (application/json)
-
- Returns 200 OK (application/json) -
- [<address_object>, ...] -
Curl example
- curl -X GET {{ request.base_url }}nostrnip5/api/v1/addresses -H - "X-Api-Key: <invoice_key>" - -
-
-
- - - - - GET - /nostrnip5/api/v1/domain/{domain_id} -
Headers
- {"X-Api-Key": <invoice_key>}
-
Body (application/json)
-
- Returns 200 OK (application/json) -
- {domain_object} -
Curl example
- curl -X GET {{ request.base_url }}nostrnip5/api/v1/domain/{domain_id} - -H "X-Api-Key: <invoice_key>" - -
-
-
- - - - - POST /nostrnip5/api/v1/domain -
Headers
- {"X-Api-Key": <invoice_key>}
-
Body (application/json)
-
- Returns 200 OK (application/json) -
- {domain_object} -
Curl example
- curl -X POST {{ request.base_url }}nostrnip5/api/v1/domain -H - "X-Api-Key: <invoice_key>" - -
-
-
- - - - - POST - /nostrnip5/api/v1/domain/{domain_id}/address -
Headers
- {"X-Api-Key": <invoice_key>}
-
Body (application/json)
-
- Returns 200 OK (application/json) -
- {address_object} -
Curl example
- curl -X POST {{ request.base_url - }}nostrnip5/api/v1/domain/{domain_id}/address -H "X-Api-Key: - <invoice_key>" - -
-
-
- - - - - POST - /invoices/api/v1/invoice/{invoice_id}/payments -
Headers
-
Body (application/json)
-
- Returns 200 OK (application/json) -
- {payment_object} -
Curl example
- curl -X POST {{ request.base_url - }}invoices/api/v1/invoice/{invoice_id}/payments -H "X-Api-Key: - <invoice_key>" - -
-
-
- - - - - GET - /nostrnip5/api/v1/domain/{domain_id}/payments/{payment_hash} -
Headers
-
Body (application/json)
-
- Returns 200 OK (application/json) -
-
Curl example
- curl -X GET {{ request.base_url - }}nostrnip5/api/v1/domain/{domain_id}/payments/{payment_hash} -H - "X-Api-Key: <invoice_key>" - -
-
-
-
diff --git a/lnbits/extensions/nostrnip5/templates/nostrnip5/index.html b/lnbits/extensions/nostrnip5/templates/nostrnip5/index.html deleted file mode 100644 index 8ebaa502..00000000 --- a/lnbits/extensions/nostrnip5/templates/nostrnip5/index.html +++ /dev/null @@ -1,785 +0,0 @@ -{% extends "base.html" %} {% from "macros.jinja" import window_vars with context -%} {% block page %} -
-
- - - New Domain - New Address - - - - - -
-
-
Domains
-
-
- Export to CSV -
-
- - {% raw %} - - - - {% endraw %} - -
-
- - - -
-
-
Addresses
-
-
- Export to CSV -
-
- - {% raw %} - - - - {% endraw %} - -
-
-
- -
- - -
- {{SITE_TITLE}} Nostr NIP-5 extension -
-

- Allow users to NIP-05 verify themselves at a domain you - control -

-
- - - {% include "nostrnip5/_api_docs.html" %} - -
-
- - - - - - - - - -
- Create Domain - Cancel -
-
-
-
- - - - - - -
- Update Amount - Cancel -
-
-
-
- - - - - - - - -
- Create Address - Cancel -
-
-
-
-
-{% endblock %} {% block scripts %} {{ window_vars(user) }} - -{% endblock %} diff --git a/lnbits/extensions/nostrnip5/templates/nostrnip5/rotate.html b/lnbits/extensions/nostrnip5/templates/nostrnip5/rotate.html deleted file mode 100644 index c9011507..00000000 --- a/lnbits/extensions/nostrnip5/templates/nostrnip5/rotate.html +++ /dev/null @@ -1,88 +0,0 @@ -{% extends "public.html" %} {% block toolbar_title %} Rotate Keys For {{ -domain.domain }} {% endblock %} {% from "macros.jinja" import window_vars with -context %} {% block page %} - -
- - -

- You can use this page to change the public key associated with your - NIP-5 identity. -

-

- Your current NIP-5 identity is {{ address.local_part }}@{{ domain.domain - }} with nostr public key {{ address.pubkey }}. -

- -

Input your new pubkey below to update it.

- - - - -
- Rotate Keys -
-
-
-
-{% endblock %} {% block scripts %} - -{% endblock %} diff --git a/lnbits/extensions/nostrnip5/templates/nostrnip5/signup.html b/lnbits/extensions/nostrnip5/templates/nostrnip5/signup.html deleted file mode 100644 index 15294817..00000000 --- a/lnbits/extensions/nostrnip5/templates/nostrnip5/signup.html +++ /dev/null @@ -1,209 +0,0 @@ -{% extends "public.html" %} {% block toolbar_title %} Verify NIP-5 For {{ -domain.domain }} {% endblock %} {% from "macros.jinja" import window_vars with -context %} {% block page %} - -
- - {% raw %} -

- Success! Your username is now active at {{ successData.local_part }}@{{ - domain }}. Please add this to your nostr profile accordingly. If you ever - need to rotate your keys, you can still keep your identity! -

- -

Important!

-

- Bookmark this link: - {{ base_url }}nostrnip5/rotate/{{ domain_id }}/{{ - successData.address_id }} -

-

- In case you ever need to change your pubkey, you can still keep this NIP-5 - identity. Just come back to the above linked page to change the pubkey - associated to your identity. -

- {% endraw %} -
- - -

- You can use this page to get NIP-5 verified on the nostr protocol under - the {{ domain.domain }} domain. -

-

- The current price is {% if domain.currency != "Satoshis" %} - {{ "{:0,.2f}".format(domain.amount / 100) }} {{ domain.currency }} - {% else %} - {{ "{}".format(domain.amount) }} {{ domain.currency }} - {% endif %} for an account (if you do not own the domain, the service - provider can disable at any time). -

- -

After submitting payment, your address will be

- - - - - -

and will be tied to this nostr pubkey

- - - - -
- Create Address -
-
-
- - - - - - - - -
- Copy Invoice -
-
-
-{% endblock %} {% block scripts %} - -{% endblock %} diff --git a/lnbits/extensions/nostrnip5/views.py b/lnbits/extensions/nostrnip5/views.py deleted file mode 100644 index 40f498c1..00000000 --- a/lnbits/extensions/nostrnip5/views.py +++ /dev/null @@ -1,67 +0,0 @@ -from http import HTTPStatus - -from fastapi import Depends, Request -from fastapi.templating import Jinja2Templates -from starlette.exceptions import HTTPException -from starlette.responses import HTMLResponse - -from lnbits.core.models import User -from lnbits.decorators import check_user_exists - -from . import nostrnip5_ext, nostrnip5_renderer -from .crud import get_address, get_domain - -templates = Jinja2Templates(directory="templates") - - -@nostrnip5_ext.get("/", response_class=HTMLResponse) -async def index(request: Request, user: User = Depends(check_user_exists)): - return nostrnip5_renderer().TemplateResponse( - "nostrnip5/index.html", {"request": request, "user": user.dict()} - ) - - -@nostrnip5_ext.get("/signup/{domain_id}", response_class=HTMLResponse) -async def signup(request: Request, domain_id: str): - domain = await get_domain(domain_id) - - if not domain: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Domain does not exist." - ) - - return nostrnip5_renderer().TemplateResponse( - "nostrnip5/signup.html", - { - "request": request, - "domain_id": domain_id, - "domain": domain, - }, - ) - - -@nostrnip5_ext.get("/rotate/{domain_id}/{address_id}", response_class=HTMLResponse) -async def rotate(request: Request, domain_id: str, address_id: str): - domain = await get_domain(domain_id) - address = await get_address(domain_id, address_id) - - if not domain: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Domain does not exist." - ) - - if not address: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Address does not exist." - ) - - return nostrnip5_renderer().TemplateResponse( - "nostrnip5/rotate.html", - { - "request": request, - "domain_id": domain_id, - "domain": domain, - "address_id": address_id, - "address": address, - }, - ) diff --git a/lnbits/extensions/nostrnip5/views_api.py b/lnbits/extensions/nostrnip5/views_api.py deleted file mode 100644 index aa5dc887..00000000 --- a/lnbits/extensions/nostrnip5/views_api.py +++ /dev/null @@ -1,284 +0,0 @@ -import re -from http import HTTPStatus - -from bech32 import bech32_decode, convertbits -from fastapi import Depends, Query, Response -from loguru import logger -from starlette.exceptions import HTTPException - -from lnbits.core.crud import get_user -from lnbits.core.services import create_invoice -from lnbits.core.views.api import api_payment -from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key -from lnbits.utils.exchange_rates import fiat_amount_as_satoshis - -from . import nostrnip5_ext -from .crud import ( - activate_address, - create_address_internal, - create_domain_internal, - delete_address, - delete_domain, - get_address_by_local_part, - get_addresses, - get_all_addresses, - get_domain, - get_domain_by_name, - get_domains, - rotate_address, - update_domain_internal, -) -from .models import ( - CreateAddressData, - CreateDomainData, - EditDomainData, - RotateAddressData, -) - - -@nostrnip5_ext.get("/api/v1/domains", status_code=HTTPStatus.OK) -async def api_domains( - all_wallets: bool = Query(None), wallet: WalletTypeInfo = Depends(get_key_type) -): - wallet_ids = [wallet.wallet.id] - if all_wallets: - user = await get_user(wallet.wallet.user) - if not user: - return [] - wallet_ids = user.wallet_ids - - return [domain.dict() for domain in await get_domains(wallet_ids)] - - -@nostrnip5_ext.get("/api/v1/addresses", status_code=HTTPStatus.OK) -async def api_addresses( - all_wallets: bool = Query(None), wallet: WalletTypeInfo = Depends(get_key_type) -): - wallet_ids = [wallet.wallet.id] - if all_wallets: - user = await get_user(wallet.wallet.user) - if not user: - return [] - wallet_ids = user.wallet_ids - - return [address.dict() for address in await get_all_addresses(wallet_ids)] - - -@nostrnip5_ext.get( - "/api/v1/domain/{domain_id}", - status_code=HTTPStatus.OK, - dependencies=[Depends(get_key_type)], -) -async def api_invoice(domain_id: str): - domain = await get_domain(domain_id) - if not domain: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Domain does not exist." - ) - - return domain - - -@nostrnip5_ext.post("/api/v1/domain", status_code=HTTPStatus.CREATED) -async def api_domain_create( - data: CreateDomainData, wallet: WalletTypeInfo = Depends(get_key_type) -): - exists = await get_domain_by_name(data.domain) - logger.error(exists) - if exists: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Domain already exists." - ) - - domain = await create_domain_internal(wallet_id=wallet.wallet.id, data=data) - - return domain - - -@nostrnip5_ext.put("/api/v1/domain", status_code=HTTPStatus.OK) -async def api_domain_update( - data: EditDomainData, wallet: WalletTypeInfo = Depends(get_key_type) -): - - domain = await update_domain_internal(wallet_id=wallet.wallet.id, data=data) - - return domain - - -@nostrnip5_ext.delete("/api/v1/domain/{domain_id}", status_code=HTTPStatus.CREATED) -async def api_domain_delete( - domain_id: str, - wallet: WalletTypeInfo = Depends(require_admin_key), -): - await delete_domain(domain_id) - - return True - - -@nostrnip5_ext.delete("/api/v1/address/{address_id}", status_code=HTTPStatus.CREATED) -async def api_address_delete( - address_id: str, - wallet: WalletTypeInfo = Depends(require_admin_key), -): - await delete_address(address_id) - - return True - - -@nostrnip5_ext.post( - "/api/v1/domain/{domain_id}/address/{address_id}/activate", - status_code=HTTPStatus.OK, - dependencies=[Depends(require_admin_key)], -) -async def api_address_activate( - domain_id: str, - address_id: str, -): - await activate_address(domain_id, address_id) - - return True - - -@nostrnip5_ext.post( - "/api/v1/domain/{domain_id}/address/{address_id}/rotate", - status_code=HTTPStatus.OK, -) -async def api_address_rotate( - domain_id: str, - address_id: str, - post_data: RotateAddressData, -): - - if post_data.pubkey.startswith("npub"): - _, data = bech32_decode(post_data.pubkey) - if data: - decoded_data = convertbits(data, 5, 8, False) - if decoded_data: - post_data.pubkey = bytes(decoded_data).hex() - - if len(bytes.fromhex(post_data.pubkey)) != 32: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Pubkey must be in hex format." - ) - - await rotate_address(domain_id, address_id, post_data.pubkey) - - return True - - -@nostrnip5_ext.post( - "/api/v1/domain/{domain_id}/address", status_code=HTTPStatus.CREATED -) -async def api_address_create( - post_data: CreateAddressData, - domain_id: str, -): - domain = await get_domain(domain_id) - - if not domain: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Domain does not exist." - ) - - if post_data.local_part == "_": - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="You're sneaky, nice try." - ) - - regex = re.compile(r"^[a-z0-9_.]+$") - if not re.fullmatch(regex, post_data.local_part.lower()): - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, - detail="Only a-z, 0-9 and .-_ are allowed characters, case insensitive.", - ) - - exists = await get_address_by_local_part(domain_id, post_data.local_part) - - if exists: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Local part already exists." - ) - - if post_data and post_data.pubkey.startswith("npub"): - _, data = bech32_decode(post_data.pubkey) - if data: - decoded_data = convertbits(data, 5, 8, False) - if decoded_data: - post_data.pubkey = bytes(decoded_data).hex() - - if len(bytes.fromhex(post_data.pubkey)) != 32: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Pubkey must be in hex format." - ) - - address = await create_address_internal(domain_id=domain_id, data=post_data) - if domain.currency == "Satoshis": - price_in_sats = domain.amount - else: - price_in_sats = await fiat_amount_as_satoshis( - domain.amount / 100, domain.currency - ) - - try: - payment_hash, payment_request = await create_invoice( - wallet_id=domain.wallet, - amount=price_in_sats, - memo=f"Payment for NIP-05 for {address.local_part}@{domain.domain}", - extra={ - "tag": "nostrnip5", - "domain_id": domain_id, - "address_id": address.id, - }, - ) - except Exception as e: - raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e)) - - return { - "payment_hash": payment_hash, - "payment_request": payment_request, - "address_id": address.id, - } - - -@nostrnip5_ext.get( - "/api/v1/domain/{domain_id}/payments/{payment_hash}", status_code=HTTPStatus.OK -) -async def api_nostrnip5_check_payment(domain_id: str, payment_hash: str): - domain = await get_domain(domain_id) - if not domain: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Domain does not exist." - ) - try: - status = await api_payment(payment_hash) - - except Exception as exc: - logger.error(exc) - return {"paid": False} - return status - - -@nostrnip5_ext.get("/api/v1/domain/{domain_id}/nostr.json", status_code=HTTPStatus.OK) -async def api_get_nostr_json( - response: Response, domain_id: str, name: str = Query(None) -): - addresses = [address.dict() for address in await get_addresses(domain_id)] - output = {} - - for address in addresses: - local_part = address.get("local_part") - if not local_part: - continue - - if address.get("active") is False: - continue - - if name and name.lower() != local_part.lower(): - continue - - output[local_part.lower()] = address.get("pubkey") - - response.headers["Access-Control-Allow-Origin"] = "*" - response.headers["Access-Control-Allow-Methods"] = "GET,OPTIONS" - - return {"names": output}