views and tasks

This commit is contained in:
Arc 2024-11-19 13:47:45 +00:00
commit 7265199139
5 changed files with 90 additions and 71 deletions

View file

@ -2,7 +2,7 @@
"name": "NWC Service Provider", "name": "NWC Service Provider",
"short_description": "A NWC service provider for LNbits", "short_description": "A NWC service provider for LNbits",
"tile": "/nwcprovider/static/image/nwcprovider.png", "tile": "/nwcprovider/static/image/nwcprovider.png",
"min_lnbits_version": "0.12.5", "min_lnbits_version": "1.0.0",
"contributors": [ "contributors": [
{ {
"name": "Riccardo Balbo", "name": "Riccardo Balbo",

View file

@ -9,7 +9,7 @@ from .models import (
NWCKey, NWCKey,
CreateNWCKey, CreateNWCKey,
GetNWCKey, GetNWCKey,
GetWalletNWCKey, GetWalletNWC,
GetBudgetsNWC, GetBudgetsNWC,
TrackedSpendNWC, TrackedSpendNWC,
DeleteNWC DeleteNWC
@ -44,7 +44,7 @@ async def delete_nwc(data:DeleteNWC) -> None:
"DELETE FROM nwcprovider.keys WHERE pubkey = :pubkey AND wallet = :wallet", {"pubkey": data.pubkey, "wallet": data.wallet_id} "DELETE FROM nwcprovider.keys WHERE pubkey = :pubkey AND wallet = :wallet", {"pubkey": data.pubkey, "wallet": data.wallet_id}
) )
async def get_wallet_nwcs(data: GetWalletNWCKey) -> List[NWCKey]: async def get_wallet_nwcs(data: GetWalletNWC) -> List[NWCKey]:
return await db.fetchall( return await db.fetchall(
""" """
SELECT * FROM nwcprovider.keys SELECT * FROM nwcprovider.keys

View file

@ -5,7 +5,7 @@ from sqlite3 import Row
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
from pydantic import BaseModel from pydantic import BaseModel
from .nwcp import NWCServiceProvider
class NWCKey(BaseModel): class NWCKey(BaseModel):
pubkey: str pubkey: str
@ -32,7 +32,11 @@ class GetNWCKey(BaseModel):
include_expired: Optional[bool] = False include_expired: Optional[bool] = False
refresh_last_used: Optional[bool] = False refresh_last_used: Optional[bool] = False
class GetWalletNWCKey(BaseModel): class GetNWCs(BaseModel):
include_expired: bool = False
calculate_spent_budget: bool = False
class GetWalletNWC(BaseModel):
wallet_id: Optional[str] = None wallet_id: Optional[str] = None
include_expired: Optional[bool] = False include_expired: Optional[bool] = False
@ -46,7 +50,12 @@ class TrackedSpendNWC(BaseModel):
class DeleteNWC(BaseModel): class DeleteNWC(BaseModel):
pubkey: str pubkey: str
wallet_id: str wallet_id: Optional[str] = None
class OnInvoicePaid(BaseModel):
sp: NWCServiceProvider
pubkey: str
payload: Dict
class NWCBudget(BaseModel): class NWCBudget(BaseModel):
id: int id: int
@ -96,6 +105,9 @@ class NWCRegistrationRequest(BaseModel):
expires_at: int expires_at: int
budgets: List[NWCNewBudget] budgets: List[NWCNewBudget]
class RegisterNWC(BaseModel):
pubkey: str
registration_data: NWCRegistrationRequest
class NWCGetResponse(BaseModel): class NWCGetResponse(BaseModel):
data: NWCKey data: NWCKey

View file

@ -19,12 +19,12 @@ from loguru import logger
from .crud import get_config_nwc, get_nwc, tracked_spend_nwc from .crud import get_config_nwc, get_nwc, tracked_spend_nwc
from .execution_queue import execution_queue from .execution_queue import execution_queue
from .models import NWCKey from .models import NWCKey, OnInvoicePaid
from .nwcp import NWCServiceProvider from .nwcp import NWCServiceProvider
from .permission import nwc_permissions from .permission import nwc_permissions
async def _check(nwc: Optional[NWCKey], method: str, payload: Dict) -> Optional[Dict]: async def _check(nwc: Optional[NWCKey], method: str) -> Optional[Dict]:
# check # check
if not nwc: if not nwc:
return { return {
@ -106,15 +106,15 @@ async def _process_invoice(
async def _on_pay_invoice( async def _on_pay_invoice(
sp: NWCServiceProvider, pubkey: str, payload: Dict data:OnInvoicePaid
) -> List[Tuple[Optional[Dict], Optional[Dict], List]]: ) -> List[Tuple[Optional[Dict], Optional[Dict], List]]:
nwc = await get_nwc(pubkey, None, False, True) nwc = await get_nwc(data.pubkey, None, False, True)
error = await _check(nwc, "pay_invoice", payload) error = await _check(nwc, "pay_invoice", data.payload)
if error: if error:
return [(None, error, [])] return [(None, error, [])]
if not nwc: if not nwc:
raise Exception("Pubkey has no associated wallet") raise Exception("Pubkey has no associated wallet")
params = payload.get("params", {}) params = data.payload.get("params", {})
invoice = params.get("invoice", None) invoice = params.get("invoice", None)
# Ensures invoice is provided # Ensures invoice is provided
if not invoice: if not invoice:
@ -122,7 +122,7 @@ async def _on_pay_invoice(
invoice_data = bolt11_decode(invoice) invoice_data = bolt11_decode(invoice)
amount_msats = int(invoice_data.amount_msat or 0) amount_msats = int(invoice_data.amount_msat or 0)
res = await _process_invoice( res = await _process_invoice(
nwc.wallet, pubkey, invoice, amount_msats, invoice_data.description nwc.wallet, data.pubkey, invoice, amount_msats, invoice_data.description
) )
error = res.get("error") error = res.get("error")
if error: if error:
@ -136,15 +136,15 @@ async def _on_pay_invoice(
async def _on_multi_pay_invoice( async def _on_multi_pay_invoice(
sp: NWCServiceProvider, pubkey: str, payload: Dict data:OnInvoicePaid
) -> List[Tuple[Optional[Dict], Optional[Dict], List]]: ) -> List[Tuple[Optional[Dict], Optional[Dict], List]]:
nwc = await get_nwc(pubkey, None, False, True) nwc = await get_nwc(data.pubkey, None, False, True)
error = await _check(nwc, "multi_pay_invoice", payload) error = await _check(nwc, "multi_pay_invoice", data.payload)
if error: if error:
return [(None, error, [])] return [(None, error, [])]
if not nwc: if not nwc:
raise Exception("Pubkey has no associated wallet") raise Exception("Pubkey has no associated wallet")
params = payload.get("params", {}) params = data.payload.get("params", {})
invoices = params.get("invoices", []) invoices = params.get("invoices", [])
results: List[Tuple[Optional[Dict], Optional[Dict], List]] = [] results: List[Tuple[Optional[Dict], Optional[Dict], List]] = []
@ -182,15 +182,15 @@ async def _on_multi_pay_invoice(
async def _on_make_invoice( async def _on_make_invoice(
sp: NWCServiceProvider, pubkey: str, payload: Dict data:OnInvoicePaid
) -> List[Tuple[Optional[Dict], Optional[Dict], List]]: ) -> List[Tuple[Optional[Dict], Optional[Dict], List]]:
nwc = await get_nwc(pubkey, None, False, True) nwc = await get_nwc(data.pubkey, None, False, True)
error = await _check(nwc, "make_invoice", payload) error = await _check(nwc, "make_invoice", data.payload)
if error: if error:
return [(None, error, [])] return [(None, error, [])]
if not nwc: if not nwc:
raise Exception("Pubkey has no associated wallet") raise Exception("Pubkey has no associated wallet")
params = payload.get("params", {}) params = data.payload.get("params", {})
amount_msats = params.get("amount", None) amount_msats = params.get("amount", None)
# Ensures amount is provided # Ensures amount is provided
if not amount_msats: if not amount_msats:
@ -234,15 +234,15 @@ async def _on_make_invoice(
async def _on_lookup_invoice( async def _on_lookup_invoice(
sp: NWCServiceProvider, pubkey: str, payload: Dict data:OnInvoicePaid
) -> List[Tuple[Optional[Dict], Optional[Dict], List]]: ) -> List[Tuple[Optional[Dict], Optional[Dict], List]]:
nwc = await get_nwc(pubkey, None, False, True) nwc = await get_nwc(data.pubkey, None, False, True)
error = await _check(nwc, "lookup_invoice", payload) error = await _check(nwc, "lookup_invoice", data.payload)
if error: if error:
return [(None, error, [])] return [(None, error, [])]
if not nwc: if not nwc:
raise Exception("Pubkey has no associated wallet") raise Exception("Pubkey has no associated wallet")
params = payload.get("params", {}) params = data.payload.get("params", {})
payment_hash = params.get("payment_hash", None) payment_hash = params.get("payment_hash", None)
invoice = params.get("invoice", None) invoice = params.get("invoice", None)
# Ensure payment_hash or invoice are provided # Ensure payment_hash or invoice are provided
@ -281,20 +281,20 @@ async def _on_lookup_invoice(
async def _on_list_transactions( async def _on_list_transactions(
sp: NWCServiceProvider, pubkey: str, payload: Dict data:OnInvoicePaid
) -> List[Tuple[Optional[Dict], Optional[Dict], List]]: ) -> List[Tuple[Optional[Dict], Optional[Dict], List]]:
nwc = await get_nwc(pubkey, None, False, True) nwc = await get_nwc(data.pubkey, None, False, True)
error = await _check(nwc, "list_transactions", payload) error = await _check(nwc, "list_transactions", data.payload)
if error: if error:
return [(None, error, [])] return [(None, error, [])]
if not nwc: if not nwc:
raise Exception("Pubkey has no associated wallet") raise Exception("Pubkey has no associated wallet")
tfrom = payload.get("from", 0) tfrom = data.payload.get("from", 0)
tto = payload.get("to", int(time.time())) tto = data.payload.get("to", int(time.time()))
limit = payload.get("limit", 10) limit = data.payload.get("limit", 10)
offset = payload.get("offset", 0) offset = data.payload.get("offset", 0)
unpaid = payload.get("unpaid", False) unpaid = data.payload.get("unpaid", False)
tx_type = payload.get("type", None) tx_type = data.payload.get("type", None)
values = [] values = []
filters: Filters = Filters() filters: Filters = Filters()
filters.where(["time <= ?"]) filters.where(["time <= ?"])
@ -337,10 +337,10 @@ async def _on_list_transactions(
async def _on_get_balance( async def _on_get_balance(
sp: NWCServiceProvider, pubkey: str, payload: Dict data:OnInvoicePaid
) -> List[Tuple[Optional[Dict], Optional[Dict], List]]: ) -> List[Tuple[Optional[Dict], Optional[Dict], List]]:
nwc = await get_nwc(pubkey, None, False, True) nwc = await get_nwc(data.pubkey, None, False, True)
error = await _check(nwc, "get_balance", payload) error = await _check(nwc, "get_balance", data.payload)
if error: if error:
return [(None, error, [])] return [(None, error, [])]
if not nwc: if not nwc:
@ -355,10 +355,10 @@ async def _on_get_balance(
async def _on_get_info( async def _on_get_info(
sp: NWCServiceProvider, pubkey: str, payload: Dict data:OnInvoicePaid
) -> List[Tuple[Optional[Dict], Optional[Dict], List]]: ) -> List[Tuple[Optional[Dict], Optional[Dict], List]]:
nwc = await get_nwc(pubkey, None, False, True) nwc = await get_nwc(data.pubkey, None, False, True)
error = await _check(nwc, "get_info", payload) error = await _check(nwc, "get_info", data.payload)
if error: if error:
return [(None, error, [])] return [(None, error, [])]
if not nwc: if not nwc:

View file

@ -17,7 +17,15 @@ from .crud import (
get_wallet_nwcs, get_wallet_nwcs,
set_config_nwc, set_config_nwc,
) )
from .models import NWCGetResponse, NWCRegistrationRequest from .models import (
NWCGetResponse,
RegisterNWC,
GetWalletNWC,
GetBudgetsNWC,
GetNWCKey,
GetNWCs,
DeleteNWC
)
from .permission import nwc_permissions from .permission import nwc_permissions
nwcprovider_api_router = APIRouter() nwcprovider_api_router = APIRouter()
@ -26,7 +34,6 @@ nwcprovider_api_router = APIRouter()
# Get supported permissions # Get supported permissions
@nwcprovider_api_router.get("/api/v1/permissions", status_code=HTTPStatus.OK) @nwcprovider_api_router.get("/api/v1/permissions", status_code=HTTPStatus.OK)
async def api_get_permissions( async def api_get_permissions(
req: Request,
wallet: WalletTypeInfo = Depends(require_admin_key), wallet: WalletTypeInfo = Depends(require_admin_key),
) -> Dict: ) -> Dict:
return nwc_permissions return nwc_permissions
@ -37,17 +44,23 @@ async def api_get_permissions(
"/api/v1/nwc", status_code=HTTPStatus.OK, response_model=List[NWCGetResponse] "/api/v1/nwc", status_code=HTTPStatus.OK, response_model=List[NWCGetResponse]
) )
async def api_get_nwcs( async def api_get_nwcs(
req: Request, data: GetNWCs,
include_expired: bool = False,
calculate_spent_budget: bool = False,
wallet: WalletTypeInfo = Depends(require_admin_key), wallet: WalletTypeInfo = Depends(require_admin_key),
): ):
wallet_id = wallet.wallet.id wallet_id = wallet.wallet.id
nwcs = await get_wallet_nwcs(wallet_id, include_expired) wallet_nwcs = GetWalletNWC(
wallet_id=wallet_id,
include_expired=data.include_expired
)
nwcs = await get_wallet_nwcs(wallet_nwcs)
out = [] out = []
for nwc in nwcs: for nwc in nwcs:
budgets = await get_budgets_nwc(nwc.pubkey, calculate_spent_budget) budgets_nwc = GetBudgetsNWC(
pubkey=nwc.pubkey,
calculate_spent=data.calculate_spent_budget
)
budgets = await get_budgets_nwc(budgets_nwc)
res = NWCGetResponse(data=nwc, budgets=budgets) res = NWCGetResponse(data=nwc, budgets=budgets)
out.append(res) out.append(res)
return out return out
@ -58,16 +71,14 @@ async def api_get_nwcs(
"/api/v1/nwc/{pubkey}", status_code=HTTPStatus.OK, response_model=NWCGetResponse "/api/v1/nwc/{pubkey}", status_code=HTTPStatus.OK, response_model=NWCGetResponse
) )
async def api_get_nwc( async def api_get_nwc(
req: Request, data:GetNWCKey,
pubkey: str, wallet: WalletTypeInfo = Depends(require_admin_key)
include_expired: Optional[bool] = False, ) -> NWCGetResponse:
wallet: WalletTypeInfo = Depends(require_admin_key),
) -> NWCGetResponse:
wallet_id = wallet.wallet.id wallet_id = wallet.wallet.id
nwc = await get_nwc(pubkey, wallet_id, include_expired) nwc = await get_nwc(data.pubkey, wallet_id, data.include_expired)
if not nwc: if not nwc:
raise Exception("Pubkey has no associated wallet") raise Exception("Pubkey has no associated wallet")
res = NWCGetResponse(data=nwc, budgets=await get_budgets_nwc(pubkey)) res = NWCGetResponse(data=nwc, budgets=await get_budgets_nwc(data.pubkey))
return res return res
@ -115,21 +126,19 @@ async def api_get_pairing_url(req: Request, secret: str) -> str:
response_model=NWCGetResponse, response_model=NWCGetResponse,
) )
async def api_register_nwc( async def api_register_nwc(
req: Request, data:RegisterNWC, # Use the Pydantic model here
pubkey: str,
registration_data: NWCRegistrationRequest, # Use the Pydantic model here
wallet: WalletTypeInfo = Depends(require_admin_key), wallet: WalletTypeInfo = Depends(require_admin_key),
): ):
wallet_id = wallet.wallet.id wallet_id = wallet.wallet.id
nwc = await create_nwc( nwc = await create_nwc(
pubkey, data.pubkey,
wallet_id, wallet_id,
registration_data.description, data.registration_data.description,
registration_data.expires_at, data.registration_data.expires_at,
registration_data.permissions, data.registration_data.permissions,
registration_data.budgets, data.registration_data.budgets,
) )
budgets = await get_budgets_nwc(pubkey) budgets = await get_budgets_nwc(data.pubkey)
res = NWCGetResponse(data=nwc, budgets=budgets) res = NWCGetResponse(data=nwc, budgets=budgets)
return res return res
@ -137,20 +146,18 @@ async def api_register_nwc(
# Delete a nwc key # Delete a nwc key
@nwcprovider_api_router.delete("/api/v1/nwc/{pubkey}", status_code=HTTPStatus.OK) @nwcprovider_api_router.delete("/api/v1/nwc/{pubkey}", status_code=HTTPStatus.OK)
async def api_delete_nwc( async def api_delete_nwc(
req: Request, pubkey: str, wallet: WalletTypeInfo = Depends(require_admin_key) data:DeleteNWC, wallet: WalletTypeInfo = Depends(require_admin_key)
): ):
wallet_id = wallet.wallet.id wallet_id = wallet.wallet.id
await delete_nwc(pubkey, wallet_id) await delete_nwc(data.pubkey, wallet_id)
return JSONResponse(content={"message": f"NWC key {pubkey} deleted successfully."}) return JSONResponse(content={"message": f"NWC key {data.pubkey} deleted successfully."})
# Get config # Get config
@nwcprovider_api_router.get( @nwcprovider_api_router.get(
"/api/v1/config", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)] "/api/v1/config", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)]
) )
async def api_get_all_config_nwc( async def api_get_all_config_nwc():
req: Request,
):
config = await get_all_config_nwc() config = await get_all_config_nwc()
return config return config
@ -161,7 +168,7 @@ async def api_get_all_config_nwc(
status_code=HTTPStatus.OK, status_code=HTTPStatus.OK,
dependencies=[Depends(check_admin)], dependencies=[Depends(check_admin)],
) )
async def api_get_config_nwc(req: Request, key: str): async def api_get_config_nwc(key: str):
config = await get_config_nwc(key) config = await get_config_nwc(key)
out = {} out = {}
out[key] = config out[key] = config