another v1 fixup + moved lnurl stuff to models
This commit is contained in:
parent
42b5edaf5d
commit
8b2b36a4f9
11 changed files with 82 additions and 104 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import asyncio
|
||||
|
||||
from fastapi import APIRouter
|
||||
from lnbits.tasks import create_permanent_unique_task
|
||||
from loguru import logger
|
||||
|
||||
from .crud import db
|
||||
|
|
@ -39,8 +40,6 @@ def myextension_stop():
|
|||
|
||||
|
||||
def myextension_start():
|
||||
from lnbits.tasks import create_permanent_unique_task
|
||||
|
||||
task = create_permanent_unique_task("ext_myextension", wait_for_paid_invoices)
|
||||
scheduled_tasks.append(task)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "MyExtension",
|
||||
"short_description": "Minimal extension to build on",
|
||||
"tile": "/myextension/static/image/myextension.png",
|
||||
"min_lnbits_version": "0.12.5",
|
||||
"min_lnbits_version": "1.0.0",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Alan Bits",
|
||||
|
|
|
|||
66
crud.py
66
crud.py
|
|
@ -1,77 +1,41 @@
|
|||
from typing import Optional, Union
|
||||
from typing import List, Optional, Union
|
||||
|
||||
from lnbits.db import Database
|
||||
from lnbits.helpers import insert_query, update_query
|
||||
|
||||
from .models import MyExtension
|
||||
|
||||
db = Database("ext_myextension")
|
||||
table_name = "myextension.maintable"
|
||||
|
||||
|
||||
async def create_myextension(data: MyExtension) -> MyExtension:
|
||||
await db.execute(
|
||||
insert_query(table_name, data),
|
||||
(*data.dict().values(),),
|
||||
)
|
||||
await db.insert("myextension.maintable", data)
|
||||
return data
|
||||
|
||||
# this is how we used to do it
|
||||
|
||||
# myextension_id = urlsafe_short_hash()
|
||||
# await db.execute(
|
||||
# """
|
||||
# INSERT INTO myextension.maintable
|
||||
# (id, wallet, name, lnurlpayamount, lnurlwithdrawamount)
|
||||
# VALUES (?, ?, ?, ?, ?)
|
||||
# """,
|
||||
# (
|
||||
# myextension_id,
|
||||
# wallet_id,
|
||||
# data.name,
|
||||
# data.lnurlpayamount,
|
||||
# data.lnurlwithdrawamount,
|
||||
# ),
|
||||
# )
|
||||
# myextension = await get_myextension(myextension_id)
|
||||
# assert myextension, "Newly created table couldn't be retrieved"
|
||||
|
||||
|
||||
async def get_myextension(myextension_id: str) -> Optional[MyExtension]:
|
||||
row = await db.fetchone(
|
||||
f"SELECT * FROM {table_name} WHERE id = ?", (myextension_id,)
|
||||
return await db.fetchone(
|
||||
"SELECT * FROM myextension.maintable WHERE id = :id",
|
||||
{"id": myextension_id},
|
||||
MyExtension,
|
||||
)
|
||||
return MyExtension(**row) if row else None
|
||||
|
||||
|
||||
async def get_myextensions(wallet_ids: Union[str, list[str]]) -> list[MyExtension]:
|
||||
async def get_myextensions(wallet_ids: Union[str, List[str]]) -> List[MyExtension]:
|
||||
if isinstance(wallet_ids, str):
|
||||
wallet_ids = [wallet_ids]
|
||||
|
||||
q = ",".join(["?"] * len(wallet_ids))
|
||||
rows = await db.fetchall(
|
||||
f"SELECT * FROM {table_name} WHERE wallet IN ({q})", (*wallet_ids,)
|
||||
q = ",".join([f"'{w}'" for w in wallet_ids])
|
||||
return await db.fetchall(
|
||||
f"SELECT * FROM myextension.maintable WHERE wallet IN ({q}) ORDER BY id",
|
||||
model=MyExtension,
|
||||
)
|
||||
return [MyExtension(**row) for row in rows]
|
||||
|
||||
|
||||
async def update_myextension(data: MyExtension) -> MyExtension:
|
||||
await db.execute(
|
||||
update_query(table_name, data),
|
||||
(
|
||||
*data.dict().values(),
|
||||
data.id,
|
||||
),
|
||||
)
|
||||
await db.update("myextension.maintable", data)
|
||||
return data
|
||||
# this is how we used to do it
|
||||
|
||||
# q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
||||
# await db.execute(
|
||||
# f"UPDATE myextension.maintable SET {q} WHERE id = ?",
|
||||
# (*kwargs.values(), myextension_id),
|
||||
# )
|
||||
|
||||
|
||||
async def delete_myextension(myextension_id: str) -> None:
|
||||
await db.execute(f"DELETE FROM {table_name} WHERE id = ?", (myextension_id,))
|
||||
await db.execute(
|
||||
"DELETE FROM myextension.maintable WHERE id = :id", {"id": myextension_id}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,9 +15,7 @@ async def m001_initial(db):
|
|||
name TEXT NOT NULL,
|
||||
total INTEGER DEFAULT 0,
|
||||
lnurlpayamount INTEGER DEFAULT 0,
|
||||
lnurlwithdrawamount INTEGER DEFAULT 0,
|
||||
lnurlwithdraw TEXT,
|
||||
lnurlpay TEXT
|
||||
lnurlwithdrawamount INTEGER DEFAULT 0
|
||||
);
|
||||
"""
|
||||
)
|
||||
|
|
|
|||
20
models.py
20
models.py
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import Request
|
||||
from lnurl.core import encode as lnurl_encode
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
|
|
@ -20,5 +22,19 @@ class MyExtension(BaseModel):
|
|||
name: str
|
||||
lnurlwithdrawamount: int
|
||||
total: int
|
||||
lnurlpay: Optional[str]
|
||||
lnurlwithdraw: Optional[str]
|
||||
|
||||
def lnurlpay(self, req: Request) -> str:
|
||||
url = req.url_for("myextension.api_lnurl_pay", myextension_id=self.id)
|
||||
url_str = str(url)
|
||||
if url.netloc.endswith(".onion"):
|
||||
url_str = url_str.replace("https://", "http://")
|
||||
|
||||
return lnurl_encode(url_str)
|
||||
|
||||
def lnurlwithdraw(self, req: Request) -> str:
|
||||
url = req.url_for("myextension.api_lnurl_withdraw", myextension_id=self.id)
|
||||
url_str = str(url)
|
||||
if url.netloc.endswith(".onion"):
|
||||
url_str = url_str.replace("https://", "http://")
|
||||
|
||||
return lnurl_encode(url_str)
|
||||
|
|
|
|||
3
tasks.py
3
tasks.py
|
|
@ -2,7 +2,6 @@ import asyncio
|
|||
|
||||
from lnbits.core.models import Payment
|
||||
from lnbits.core.services import websocket_updater
|
||||
from lnbits.helpers import get_current_extension_name
|
||||
from lnbits.tasks import register_invoice_listener
|
||||
|
||||
from .crud import get_myextension, update_myextension
|
||||
|
|
@ -16,7 +15,7 @@ from .crud import get_myextension, update_myextension
|
|||
|
||||
async def wait_for_paid_invoices():
|
||||
invoice_queue = asyncio.Queue()
|
||||
register_invoice_listener(invoice_queue, get_current_extension_name())
|
||||
register_invoice_listener(invoice_queue, "ext_myextension")
|
||||
while True:
|
||||
payment = await invoice_queue.get()
|
||||
await on_invoice_paid(payment)
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@
|
|||
<q-table
|
||||
dense
|
||||
flat
|
||||
:data="myex"
|
||||
:rows="myex"
|
||||
row-key="id"
|
||||
:columns="myexTable.columns"
|
||||
:pagination.sync="myexTable.pagination"
|
||||
v-model:pagination="myexTable.pagination"
|
||||
>
|
||||
<myextension v-slot:header="props">
|
||||
<q-tr :props="props">
|
||||
|
|
@ -250,7 +250,7 @@
|
|||
obj.myextension = ['/myextension/', obj.id].join('')
|
||||
return obj
|
||||
}
|
||||
new Vue({
|
||||
window.app = Vue.createApp({
|
||||
el: '#vue',
|
||||
mixins: [windowMixin],
|
||||
delimiters: ['${', '}'],
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@
|
|||
<div class="text-center">
|
||||
<a class="text-secondary" href="lightning:{{ lnurl }}">
|
||||
<q-responsive :ratio="1" class="q-mx-md">
|
||||
<qrcode
|
||||
<lnbits-qrcode
|
||||
:value="qrValue"
|
||||
:options="{width: 800}"
|
||||
class="rounded-borders"
|
||||
></qrcode>
|
||||
></lnbits-qrcode>
|
||||
</q-responsive>
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -49,9 +49,7 @@
|
|||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script>
|
||||
Vue.component(VueQrcode.name, VueQrcode)
|
||||
|
||||
new Vue({
|
||||
window.app = Vue.createApp({
|
||||
el: '#vue',
|
||||
mixins: [windowMixin],
|
||||
data: function () {
|
||||
|
|
|
|||
4
views.py
4
views.py
|
|
@ -28,7 +28,7 @@ def myextension_renderer():
|
|||
@myextension_generic_router.get("/", response_class=HTMLResponse)
|
||||
async def index(request: Request, user: User = Depends(check_user_exists)):
|
||||
return myextension_renderer().TemplateResponse(
|
||||
"myextension/index.html", {"request": request, "user": user.dict()}
|
||||
"myextension/index.html", {"request": request, "user": user.json()}
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ async def myextension(request: Request, myextension_id):
|
|||
{
|
||||
"request": request,
|
||||
"myextension_id": myextension_id,
|
||||
"lnurlpay": myextension.lnurlpay,
|
||||
"lnurlpay": myextension.lnurlpay(request),
|
||||
"web_manifest": f"/myextension/manifest/{myextension_id}.webmanifest",
|
||||
},
|
||||
)
|
||||
|
|
|
|||
66
views_api.py
66
views_api.py
|
|
@ -9,8 +9,6 @@ from lnbits.decorators import (
|
|||
require_admin_key,
|
||||
require_invoice_key,
|
||||
)
|
||||
from lnbits.helpers import urlsafe_short_hash
|
||||
from lnurl import encode as lnurl_encode
|
||||
from starlette.exceptions import HTTPException
|
||||
|
||||
from .crud import (
|
||||
|
|
@ -29,11 +27,15 @@ myextension_api_router = APIRouter()
|
|||
##### ADD YOUR API ENDPOINTS HERE #####
|
||||
#######################################
|
||||
|
||||
# Note: we add the lnurl params to returns so the links
|
||||
# are generated in the MyExtension model in models.py
|
||||
|
||||
## Get all the records belonging to the user
|
||||
|
||||
|
||||
@myextension_api_router.get("/api/v1/myex", status_code=HTTPStatus.OK)
|
||||
async def api_myextensions(
|
||||
req: Request,
|
||||
all_wallets: bool = Query(False),
|
||||
wallet: WalletTypeInfo = Depends(get_key_type),
|
||||
):
|
||||
|
|
@ -41,7 +43,14 @@ async def api_myextensions(
|
|||
if all_wallets:
|
||||
user = await get_user(wallet.wallet.user)
|
||||
wallet_ids = user.wallet_ids if user else []
|
||||
return [myextension.dict() for myextension in await get_myextensions(wallet_ids)]
|
||||
return [
|
||||
{
|
||||
**myextension.dict(),
|
||||
"lnurlpay": myextension.lnurlpay(req),
|
||||
"lnurlwithdraw": myextension.lnurlwithdraw(req),
|
||||
}
|
||||
for myextension in await get_myextensions(wallet_ids)
|
||||
]
|
||||
|
||||
|
||||
## Get a single record
|
||||
|
|
@ -52,13 +61,17 @@ async def api_myextensions(
|
|||
status_code=HTTPStatus.OK,
|
||||
dependencies=[Depends(require_invoice_key)],
|
||||
)
|
||||
async def api_myextension(myextension_id: str):
|
||||
async def api_myextension(myextension_id: str, req: Request):
|
||||
myextension = await get_myextension(myextension_id)
|
||||
if not myextension:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.NOT_FOUND, detail="MyExtension does not exist."
|
||||
)
|
||||
return myextension.dict()
|
||||
return {
|
||||
**myextension.dict(),
|
||||
"lnurlpay": myextension.lnurlpay(req),
|
||||
"lnurlwithdraw": myextension.lnurlwithdraw(req),
|
||||
}
|
||||
|
||||
|
||||
## update a record
|
||||
|
|
@ -67,13 +80,11 @@ async def api_myextension(myextension_id: str):
|
|||
@myextension_api_router.put("/api/v1/myex/{myextension_id}")
|
||||
async def api_myextension_update(
|
||||
data: CreateMyExtensionData,
|
||||
req: Request,
|
||||
myextension_id: str,
|
||||
wallet: WalletTypeInfo = Depends(get_key_type),
|
||||
) -> MyExtension:
|
||||
if not myextension_id:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.NOT_FOUND, detail="MyExtension does not exist."
|
||||
)
|
||||
|
||||
myextension = await get_myextension(myextension_id)
|
||||
assert myextension, "MyExtension couldn't be retrieved"
|
||||
|
||||
|
|
@ -85,7 +96,12 @@ async def api_myextension_update(
|
|||
for key, value in data.dict().items():
|
||||
setattr(myextension, key, value)
|
||||
|
||||
return await update_myextension(myextension)
|
||||
myextension = await update_myextension(data)
|
||||
return {
|
||||
**myextension.dict(),
|
||||
"lnurlpay": myextension.lnurlpay(req),
|
||||
"lnurlwithdraw": myextension.lnurlwithdraw(req),
|
||||
}
|
||||
|
||||
|
||||
## Create a new record
|
||||
|
|
@ -93,29 +109,17 @@ async def api_myextension_update(
|
|||
|
||||
@myextension_api_router.post("/api/v1/myex", status_code=HTTPStatus.CREATED)
|
||||
async def api_myextension_create(
|
||||
request: Request,
|
||||
data: CreateMyExtensionData,
|
||||
req: Request,
|
||||
key_type: WalletTypeInfo = Depends(require_admin_key),
|
||||
) -> MyExtension:
|
||||
myextension_id = urlsafe_short_hash()
|
||||
lnurlpay = lnurl_encode(
|
||||
str(request.url_for("myextension.api_lnurl_pay", myextension_id=myextension_id))
|
||||
)
|
||||
lnurlwithdraw = lnurl_encode(
|
||||
str(
|
||||
request.url_for(
|
||||
"myextension.api_lnurl_withdraw", myextension_id=myextension_id
|
||||
)
|
||||
)
|
||||
)
|
||||
data.wallet = data.wallet or key_type.wallet.id
|
||||
myext = MyExtension(
|
||||
id=myextension_id,
|
||||
lnurlpay=lnurlpay,
|
||||
lnurlwithdraw=lnurlwithdraw,
|
||||
**data.dict(),
|
||||
)
|
||||
return await create_myextension(myext)
|
||||
myextension = create_myextension(data)
|
||||
return {
|
||||
**myextension.dict(),
|
||||
"lnurlpay": myextension.lnurlpay(req),
|
||||
"lnurlwithdraw": myextension.lnurlwithdraw(req),
|
||||
}
|
||||
|
||||
|
||||
## Delete a record
|
||||
|
|
@ -163,7 +167,7 @@ async def api_myextension_create_invoice(
|
|||
# so tasks.py can grab the payment once its paid
|
||||
|
||||
try:
|
||||
payment_hash, payment_request = await create_invoice(
|
||||
payment = await create_invoice(
|
||||
wallet_id=myextension.wallet,
|
||||
amount=amount,
|
||||
memo=f"{memo} to {myextension.name}" if memo else f"{myextension.name}",
|
||||
|
|
@ -177,4 +181,4 @@ async def api_myextension_create_invoice(
|
|||
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(exc)
|
||||
) from exc
|
||||
|
||||
return {"payment_hash": payment_hash, "payment_request": payment_request}
|
||||
return {"payment_hash": payment.payment_hash, "payment_request": payment.bolt11}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ async def api_lnurl_pay_cb(
|
|||
if not myextension:
|
||||
return {"status": "ERROR", "reason": "No myextension found"}
|
||||
|
||||
_, payment_request = await create_invoice(
|
||||
payment = await create_invoice(
|
||||
wallet_id=myextension.wallet,
|
||||
amount=int(amount / 1000),
|
||||
memo=myextension.name,
|
||||
|
|
@ -73,7 +73,7 @@ async def api_lnurl_pay_cb(
|
|||
},
|
||||
)
|
||||
return {
|
||||
"pr": payment_request,
|
||||
"pr": payment.bolt11,
|
||||
"routes": [],
|
||||
"successAction": {"tag": "message", "message": f"Paid {myextension.name}"},
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue