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 2bcbf0548..000000000 --- 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 a9cb526d5..000000000 --- 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 8621b17ce..000000000 --- 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 f7ec929c7..000000000 --- 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 7c5a49dd4..000000000 --- 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 7e7bf2546..000000000 --- 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 e69de29bb..000000000 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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 W 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 a3f91201f..000000000 --- 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 8ebaa5027..000000000 --- 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 c9011507b..000000000 --- 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 152948171..000000000 --- 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 40f498c1d..000000000 --- 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 aa5dc887b..000000000 --- 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}