fix: lnurl_encoding error was not handled (#56)
This commit is contained in:
parent
6b11dec0cc
commit
b42fee99e5
6 changed files with 1047 additions and 750 deletions
28
helpers.py
Normal file
28
helpers.py
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
from fastapi import Request
|
||||||
|
from lnurl import Lnurl
|
||||||
|
from lnurl import encode as lnurl_encode
|
||||||
|
from shortuuid import uuid
|
||||||
|
|
||||||
|
from .models import WithdrawLink
|
||||||
|
|
||||||
|
|
||||||
|
def create_lnurl(link: WithdrawLink, req: Request) -> Lnurl:
|
||||||
|
if link.is_unique:
|
||||||
|
usescssv = link.usescsv.split(",")
|
||||||
|
tohash = link.id + link.unique_hash + usescssv[link.number]
|
||||||
|
multihash = uuid(name=tohash)
|
||||||
|
url = req.url_for(
|
||||||
|
"withdraw.api_lnurl_multi_response",
|
||||||
|
unique_hash=link.unique_hash,
|
||||||
|
id_unique_hash=multihash,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
url = req.url_for("withdraw.api_lnurl_response", unique_hash=link.unique_hash)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return lnurl_encode(str(url))
|
||||||
|
except Exception as e:
|
||||||
|
raise ValueError(
|
||||||
|
f"Error creating LNURL with url: `{url!s}`, "
|
||||||
|
"check your webserver proxy configuration."
|
||||||
|
) from e
|
||||||
39
models.py
39
models.py
|
|
@ -1,14 +1,6 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import shortuuid
|
from fastapi import Query
|
||||||
from fastapi import Query, Request
|
|
||||||
from lnurl import (
|
|
||||||
ClearnetUrl,
|
|
||||||
Lnurl,
|
|
||||||
LnurlWithdrawResponse,
|
|
||||||
MilliSatoshi,
|
|
||||||
)
|
|
||||||
from lnurl import encode as lnurl_encode
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -50,35 +42,6 @@ class WithdrawLink(BaseModel):
|
||||||
def is_spent(self) -> bool:
|
def is_spent(self) -> bool:
|
||||||
return self.used >= self.uses
|
return self.used >= self.uses
|
||||||
|
|
||||||
def lnurl(self, req: Request) -> Lnurl:
|
|
||||||
if self.is_unique:
|
|
||||||
usescssv = self.usescsv.split(",")
|
|
||||||
tohash = self.id + self.unique_hash + usescssv[self.number]
|
|
||||||
multihash = shortuuid.uuid(name=tohash)
|
|
||||||
url = str(
|
|
||||||
req.url_for(
|
|
||||||
"withdraw.api_lnurl_multi_response",
|
|
||||||
unique_hash=self.unique_hash,
|
|
||||||
id_unique_hash=multihash,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
url = str(
|
|
||||||
req.url_for("withdraw.api_lnurl_response", unique_hash=self.unique_hash)
|
|
||||||
)
|
|
||||||
|
|
||||||
return lnurl_encode(url)
|
|
||||||
|
|
||||||
def lnurl_response(self, req: Request) -> LnurlWithdrawResponse:
|
|
||||||
url = req.url_for("withdraw.api_lnurl_callback", unique_hash=self.unique_hash)
|
|
||||||
return LnurlWithdrawResponse(
|
|
||||||
callback=ClearnetUrl(url, scheme="https"), # type: ignore
|
|
||||||
k1=self.k1,
|
|
||||||
minWithdrawable=MilliSatoshi(self.min_withdrawable * 1000),
|
|
||||||
maxWithdrawable=MilliSatoshi(self.max_withdrawable * 1000),
|
|
||||||
defaultDescription=self.title,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class HashCheck(BaseModel):
|
class HashCheck(BaseModel):
|
||||||
hash: bool
|
hash: bool
|
||||||
|
|
|
||||||
1650
poetry.lock
generated
1650
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -5,7 +5,7 @@ description = "LNbits, free and open-source Lightning wallet and accounts system
|
||||||
authors = ["Alan Bits <alan@lnbits.com>"]
|
authors = ["Alan Bits <alan@lnbits.com>"]
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.10 | ^3.9"
|
python = "~3.12 | ~3.11 | ~3.10"
|
||||||
lnbits = {version = "*", allow-prereleases = true}
|
lnbits = {version = "*", allow-prereleases = true}
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
|
|
||||||
40
views.py
40
views.py
|
|
@ -9,6 +9,7 @@ from lnbits.decorators import check_user_exists
|
||||||
from lnbits.helpers import template_renderer
|
from lnbits.helpers import template_renderer
|
||||||
|
|
||||||
from .crud import chunks, get_withdraw_link
|
from .crud import chunks, get_withdraw_link
|
||||||
|
from .helpers import create_lnurl
|
||||||
|
|
||||||
withdraw_ext_generic = APIRouter()
|
withdraw_ext_generic = APIRouter()
|
||||||
|
|
||||||
|
|
@ -32,12 +33,21 @@ async def display(request: Request, link_id):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="Withdraw link does not exist."
|
status_code=HTTPStatus.NOT_FOUND, detail="Withdraw link does not exist."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
lnurl = create_lnurl(link, request)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
detail=str(exc),
|
||||||
|
) from exc
|
||||||
|
|
||||||
return withdraw_renderer().TemplateResponse(
|
return withdraw_renderer().TemplateResponse(
|
||||||
"withdraw/display.html",
|
"withdraw/display.html",
|
||||||
{
|
{
|
||||||
"request": request,
|
"request": request,
|
||||||
"link": link.json(),
|
"link": link.json(),
|
||||||
"lnurl": link.lnurl(req=request),
|
"lnurl": lnurl,
|
||||||
"unique": True,
|
"unique": True,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -50,7 +60,15 @@ async def img(request: Request, link_id):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="Withdraw link does not exist."
|
status_code=HTTPStatus.NOT_FOUND, detail="Withdraw link does not exist."
|
||||||
)
|
)
|
||||||
qr = pyqrcode.create(link.lnurl(request))
|
try:
|
||||||
|
lnurl = create_lnurl(link, request)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
detail=str(exc),
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
qr = pyqrcode.create(lnurl)
|
||||||
stream = BytesIO()
|
stream = BytesIO()
|
||||||
qr.svg(stream, scale=3)
|
qr.svg(stream, scale=3)
|
||||||
stream.seek(0)
|
stream.seek(0)
|
||||||
|
|
@ -94,7 +112,14 @@ async def print_qr(request: Request, link_id):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="Withdraw link does not exist."
|
status_code=HTTPStatus.NOT_FOUND, detail="Withdraw link does not exist."
|
||||||
)
|
)
|
||||||
links.append(str(linkk.lnurl(request)))
|
try:
|
||||||
|
lnurl = create_lnurl(linkk, request)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
detail=str(exc),
|
||||||
|
) from exc
|
||||||
|
links.append(str(lnurl))
|
||||||
count = count + 1
|
count = count + 1
|
||||||
page_link = list(chunks(links, 2))
|
page_link = list(chunks(links, 2))
|
||||||
linked = list(chunks(page_link, 5))
|
linked = list(chunks(page_link, 5))
|
||||||
|
|
@ -141,7 +166,14 @@ async def csv(request: Request, link_id):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="Withdraw link does not exist."
|
status_code=HTTPStatus.NOT_FOUND, detail="Withdraw link does not exist."
|
||||||
)
|
)
|
||||||
links.append(str(linkk.lnurl(request)))
|
try:
|
||||||
|
lnurl = create_lnurl(linkk, request)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
detail=str(exc),
|
||||||
|
) from exc
|
||||||
|
links.append(str(lnurl))
|
||||||
count = count + 1
|
count = count + 1
|
||||||
page_link = list(chunks(links, 2))
|
page_link = list(chunks(links, 2))
|
||||||
linked = list(chunks(page_link, 5))
|
linked = list(chunks(page_link, 5))
|
||||||
|
|
|
||||||
38
views_api.py
38
views_api.py
|
|
@ -15,6 +15,7 @@ from .crud import (
|
||||||
get_withdraw_links,
|
get_withdraw_links,
|
||||||
update_withdraw_link,
|
update_withdraw_link,
|
||||||
)
|
)
|
||||||
|
from .helpers import create_lnurl
|
||||||
from .models import CreateWithdrawData, HashCheck
|
from .models import CreateWithdrawData, HashCheck
|
||||||
|
|
||||||
withdraw_ext_api = APIRouter(prefix="/api/v1")
|
withdraw_ext_api = APIRouter(prefix="/api/v1")
|
||||||
|
|
@ -22,7 +23,7 @@ withdraw_ext_api = APIRouter(prefix="/api/v1")
|
||||||
|
|
||||||
@withdraw_ext_api.get("/links", status_code=HTTPStatus.OK)
|
@withdraw_ext_api.get("/links", status_code=HTTPStatus.OK)
|
||||||
async def api_links(
|
async def api_links(
|
||||||
req: Request,
|
request: Request,
|
||||||
key_info: WalletTypeInfo = Depends(require_invoice_key),
|
key_info: WalletTypeInfo = Depends(require_invoice_key),
|
||||||
all_wallets: bool = Query(False),
|
all_wallets: bool = Query(False),
|
||||||
offset: int = Query(0),
|
offset: int = Query(0),
|
||||||
|
|
@ -35,8 +36,20 @@ async def api_links(
|
||||||
wallet_ids = user.wallet_ids if user else []
|
wallet_ids = user.wallet_ids if user else []
|
||||||
|
|
||||||
links, total = await get_withdraw_links(wallet_ids, limit, offset)
|
links, total = await get_withdraw_links(wallet_ids, limit, offset)
|
||||||
|
|
||||||
|
data = []
|
||||||
|
for link in links:
|
||||||
|
try:
|
||||||
|
lnurl = create_lnurl(link, request)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
detail=str(exc),
|
||||||
|
) from exc
|
||||||
|
data.append({**link.dict(), **{"lnurl": lnurl}})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"data": [{**link.dict(), **{"lnurl": link.lnurl(req)}} for link in links],
|
"data": data,
|
||||||
"total": total,
|
"total": total,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,13 +71,20 @@ async def api_link_retrieve(
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
detail="Not your withdraw link.", status_code=HTTPStatus.FORBIDDEN
|
detail="Not your withdraw link.", status_code=HTTPStatus.FORBIDDEN
|
||||||
)
|
)
|
||||||
return {**link.dict(), **{"lnurl": link.lnurl(request)}}
|
try:
|
||||||
|
lnurl = create_lnurl(link, request)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
detail=str(exc),
|
||||||
|
) from exc
|
||||||
|
return {**link.dict(), **{"lnurl": lnurl}}
|
||||||
|
|
||||||
|
|
||||||
@withdraw_ext_api.post("/links", status_code=HTTPStatus.CREATED)
|
@withdraw_ext_api.post("/links", status_code=HTTPStatus.CREATED)
|
||||||
@withdraw_ext_api.put("/links/{link_id}", status_code=HTTPStatus.OK)
|
@withdraw_ext_api.put("/links/{link_id}", status_code=HTTPStatus.OK)
|
||||||
async def api_link_create_or_update(
|
async def api_link_create_or_update(
|
||||||
req: Request,
|
request: Request,
|
||||||
data: CreateWithdrawData,
|
data: CreateWithdrawData,
|
||||||
link_id: Optional[str] = None,
|
link_id: Optional[str] = None,
|
||||||
key_info: WalletTypeInfo = Depends(require_admin_key),
|
key_info: WalletTypeInfo = Depends(require_admin_key),
|
||||||
|
|
@ -141,7 +161,15 @@ async def api_link_create_or_update(
|
||||||
else:
|
else:
|
||||||
link = await create_withdraw_link(wallet_id=key_info.wallet.id, data=data)
|
link = await create_withdraw_link(wallet_id=key_info.wallet.id, data=data)
|
||||||
|
|
||||||
return {**link.dict(), **{"lnurl": link.lnurl(req)}}
|
try:
|
||||||
|
lnurl = create_lnurl(link, request)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
detail=str(exc),
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
return {**link.dict(), **{"lnurl": lnurl}}
|
||||||
|
|
||||||
|
|
||||||
@withdraw_ext_api.delete("/links/{link_id}", status_code=HTTPStatus.OK)
|
@withdraw_ext_api.delete("/links/{link_id}", status_code=HTTPStatus.OK)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue