feat: update to lnbits 1.0.0 (#27)

* feat: update to lnbits 1.0.0
* fix select wallet
* fix splits
* fix: types, postgres errors with cache
---------

Co-authored-by: Tiago Vasconcelos <talvasconcelos@gmail.com>
This commit is contained in:
dni ⚡ 2024-11-28 12:28:00 +01:00 committed by GitHub
commit 5042d40af6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 1300 additions and 1218 deletions

View file

@ -2,7 +2,7 @@
"name": "Split Payments",
"short_description": "Split incoming payments across wallets",
"tile": "/splitpayments/static/image/split-payments.png",
"min_lnbits_version": "0.11.0",
"min_lnbits_version": "1.0.0",
"contributors": [
{
"name": "cryptograffiti",

32
crud.py
View file

@ -1,37 +1,23 @@
from typing import List
from lnbits.db import Database
from lnbits.helpers import urlsafe_short_hash
from .models import Target
db = Database("ext_splitpayments")
async def get_targets(source_wallet: str) -> List[Target]:
rows = await db.fetchall(
"SELECT * FROM splitpayments.targets WHERE source = ?", (source_wallet,)
async def get_targets(source_wallet: str) -> list[Target]:
return await db.fetchall(
"SELECT * FROM splitpayments.targets WHERE source = :source_wallet",
{"source_wallet": source_wallet},
Target,
)
return [Target(**row) for row in rows]
async def set_targets(source_wallet: str, targets: List[Target]):
async def set_targets(source_wallet: str, targets: list[Target]):
async with db.connect() as conn:
await conn.execute(
"DELETE FROM splitpayments.targets WHERE source = ?", (source_wallet,)
"DELETE FROM splitpayments.targets WHERE source = :source_wallet",
{"source_wallet": source_wallet},
)
for target in targets:
await conn.execute(
"""
INSERT INTO splitpayments.targets
(id, source, wallet, percent, alias)
VALUES (?, ?, ?, ?, ?)
""",
(
urlsafe_short_hash(),
source_wallet,
target.wallet,
target.percent,
target.alias,
),
)
await conn.insert("splitpayments.targets", target)

View file

@ -1,7 +1,8 @@
from lnbits.db import Connection
from lnbits.helpers import urlsafe_short_hash
async def m001_initial(db):
async def m001_initial(db: Connection):
"""
Initial split payment table.
"""
@ -19,49 +20,24 @@ async def m001_initial(db):
)
async def m002_float_percent(db):
async def m002_float_percent(db: Connection):
"""
Add float percent and migrates the existing data.
"""
await db.execute("ALTER TABLE splitpayments.targets RENAME TO splitpayments_old")
await db.execute(
"""
CREATE TABLE splitpayments.targets (
wallet TEXT NOT NULL,
source TEXT NOT NULL,
percent REAL NOT NULL CHECK (percent >= 0 AND percent <= 100),
alias TEXT,
UNIQUE (source, wallet)
);
ALTER TABLE splitpayments.targets
ADD COLUMN percent REAL NOT NULL CHECK (percent >= 0 AND percent <= 100)
"""
)
for row in [
list(row)
for row in await db.fetchall("SELECT * FROM splitpayments.splitpayments_old")
]:
await db.execute(
"""
INSERT INTO splitpayments.targets (
wallet,
source,
percent,
alias
)
VALUES (?, ?, ?, ?)
""",
(row[0], row[1], row[2], row[3]),
)
await db.execute("DROP TABLE splitpayments.splitpayments_old")
async def m003_add_id_and_tag(db):
async def m003_add_id_and_tag(db: Connection):
"""
Add float percent and migrates the existing data.
Add id, tag and migrates the existing data.
"""
await db.execute("ALTER TABLE splitpayments.targets RENAME TO splitpayments_old")
await db.execute("ALTER TABLE splitpayments.targets RENAME TO splitpayments_m002")
await db.execute(
"""
CREATE TABLE splitpayments.targets (
@ -76,11 +52,9 @@ async def m003_add_id_and_tag(db):
);
"""
)
for row in [
list(row)
for row in await db.fetchall("SELECT * FROM splitpayments.splitpayments_old")
]:
result = await db.execute("SELECT * FROM splitpayments.splitpayments_m002")
rows = result.mappings().all()
for row in rows:
await db.execute(
"""
INSERT INTO splitpayments.targets (
@ -91,23 +65,31 @@ async def m003_add_id_and_tag(db):
tag,
alias
)
VALUES (?, ?, ?, ?, ?, ?)
VALUES (:id, :wallet, :source, :percent, :tag, :alias)
""",
(urlsafe_short_hash(), row[0], row[1], row[2], "", row[3]),
{
"id": urlsafe_short_hash(),
"wallet": row["wallet"],
"source": row["source"],
"percent": row["percent"],
"tag": row["tag"],
"alias": row["alias"],
},
)
await db.execute("DROP TABLE splitpayments.splitpayments_old")
await db.execute("DROP TABLE splitpayments.splitpayments_m002")
async def m004_remove_tag(db):
async def m004_remove_tag(db: Connection):
"""
This removes tag
"""
keys = "id,wallet,source,percent,alias"
new_db = "splitpayments.targets"
old_db = "splitpayments.targets_old"
old_db = "splitpayments.targets_m003"
await db.execute(f"ALTER TABLE {new_db} RENAME TO targets_m003")
await db.execute(f"ALTER TABLE {new_db} RENAME TO targets_old")
await db.execute(
f"""
CREATE TABLE {new_db} (

View file

@ -1,19 +1,15 @@
from sqlite3 import Row
from typing import List, Optional
from typing import Optional
from fastapi import Query
from pydantic import BaseModel
class Target(BaseModel):
id: str
wallet: str
source: str
percent: float
alias: Optional[str]
@classmethod
def from_row(cls, row: Row):
return cls(**dict(row))
alias: Optional[str] = None
class TargetPut(BaseModel):
@ -23,4 +19,4 @@ class TargetPut(BaseModel):
class TargetPutList(BaseModel):
targets: List[TargetPut]
targets: list[TargetPut]

2360
poetry.lock generated

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,7 @@ authors = ["Alan Bits <alan@lnbits.com>"]
[tool.poetry.dependencies]
python = "^3.10 | ^3.9"
lnbits = "*"
lnbits = {version = "*", allow-prereleases = true}
[tool.poetry.group.dev.dependencies]
black = "^24.3.0"

View file

@ -1,7 +1,3 @@
/* globals Quasar, Vue, _, VueQrcode, windowMixin, LNbits, LOCALE */
Vue.component(VueQrcode.name, VueQrcode)
function hashTargets(targets) {
return targets
.filter(isTargetComplete)
@ -17,9 +13,14 @@ function isTargetComplete(target) {
)
}
new Vue({
window.app = Vue.createApp({
el: '#vue',
mixins: [windowMixin],
watch: {
selectedWallet() {
this.getTargets()
}
},
data() {
return {
selectedWallet: null,
@ -38,7 +39,7 @@ new Vue({
return this.deleteTargets()
}
this.targets.splice(index, 1)
this.$q.notify({
Quasar.Notify.create({
message: 'Removed item. You must click to save manually.',
timeout: 500
})
@ -75,7 +76,7 @@ new Vue({
}
)
.then(response => {
this.$q.notify({
Quasar.Notify.create({
message: 'Split payments targets set.',
timeout: 700
})
@ -96,7 +97,7 @@ new Vue({
this.selectedWallet.adminkey
)
.then(response => {
this.$q.notify({
Quasar.Notify.create({
message: 'Split payments targets deleted.',
timeout: 700
})
@ -109,6 +110,5 @@ new Vue({
},
created() {
this.selectedWallet = this.g.user.wallets[0]
this.getTargets()
}
})

View file

@ -8,7 +8,6 @@ import httpx
from lnbits.core.crud import get_standalone_payment
from lnbits.core.models import Payment
from lnbits.core.services import create_invoice, fee_reserve, pay_invoice
from lnbits.helpers import get_current_extension_name
from lnbits.tasks import register_invoice_listener
from loguru import logger
@ -17,8 +16,7 @@ from .crud import get_targets
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_splitpayments_invoice_listener")
while True:
payment = await invoice_queue.get()
await on_invoice_paid(payment)
@ -58,14 +56,15 @@ async def on_invoice_paid(payment: Payment) -> None:
target.wallet, payment.wallet_id, safe_amount_msat, memo
)
else:
_, payment_request = await create_invoice(
new_payment = await create_invoice(
wallet_id=target.wallet,
amount=int(amount_msat / 1000),
internal=True,
memo=memo,
)
payment_request = new_payment.bolt11
extra = {**payment.extra, "tag": "splitpayments", "splitted": True}
extra = {**payment.extra, "splitted": True}
if payment_request:
await pay_invoice(
@ -90,7 +89,7 @@ async def get_lnurl_invoice(
r = await client.get(
data["callback"],
params={"amount": rounded_amount, "comment": memo},
timeout=40,
timeout=5,
)
if r.is_error:
raise httpx.ConnectError("issue with scrub callback")

View file

@ -9,10 +9,9 @@
filled
dense
:options="g.user.wallets"
:value="selectedWallet"
v-model="selectedWallet"
label="Source Wallet:"
option-label="name"
@input="changedWallet"
>
</q-select>
</q-form>

View file

@ -1,11 +1,9 @@
from fastapi import APIRouter, Depends, Request
from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse
from lnbits.core.models import User
from lnbits.decorators import check_user_exists
from lnbits.helpers import template_renderer
from starlette.responses import HTMLResponse
templates = Jinja2Templates(directory="templates")
splitpayments_generic_router = APIRouter()
@ -16,5 +14,5 @@ def splitpayments_renderer():
@splitpayments_generic_router.get("/", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_user_exists)):
return splitpayments_renderer().TemplateResponse(
"splitpayments/index.html", {"request": request, "user": user.dict()}
"splitpayments/index.html", {"request": request, "user": user.json()}
)

View file

@ -4,6 +4,7 @@ from fastapi import APIRouter, Depends, HTTPException
from lnbits.core.crud import get_wallet, get_wallet_for_key
from lnbits.core.models import WalletTypeInfo
from lnbits.decorators import require_admin_key
from lnbits.helpers import urlsafe_short_hash
from loguru import logger
from .crud import get_targets, set_targets
@ -53,6 +54,7 @@ async def api_targets_set(
targets.append(
Target(
id=urlsafe_short_hash(),
wallet=entry.wallet,
source=source_wallet.wallet.id,
percent=entry.percent,