Merge branch 'lnbits:main' into ext-boltcards_keys

This commit is contained in:
Gene Takavic 2022-09-21 10:24:53 +02:00 committed by GitHub
commit 01fb80d739
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 112 additions and 106 deletions

View file

@ -1,6 +1,7 @@
FROM python:3.9-slim FROM python:3.9-slim
RUN apt-get clean
RUN apt-get update RUN apt-get update
RUN apt-get install -y curl RUN apt-get install -y curl pkg-config build-essential
RUN curl -sSL https://install.python-poetry.org | python3 - RUN curl -sSL https://install.python-poetry.org | python3 -
ENV PATH="/root/.local/bin:$PATH" ENV PATH="/root/.local/bin:$PATH"
WORKDIR /app WORKDIR /app

View file

@ -12,6 +12,8 @@ By default, LNbits will use SQLite as its database. You can also use PostgreSQL
## Option 1 (recommended): poetry ## Option 1 (recommended): poetry
If you have problems installing LNbits using these instructions, please have a look at the [Troubleshooting](#troubleshooting) section.
```sh ```sh
git clone https://github.com/lnbits/lnbits-legend.git git clone https://github.com/lnbits/lnbits-legend.git
cd lnbits-legend/ cd lnbits-legend/
@ -26,12 +28,11 @@ curl -sSL https://install.python-poetry.org | python3 -
export PATH="/home/ubuntu/.local/bin:$PATH" # or whatever is suggested in the poetry install notes printed to terminal export PATH="/home/ubuntu/.local/bin:$PATH" # or whatever is suggested in the poetry install notes printed to terminal
poetry env use python3.9 poetry env use python3.9
poetry install --no-dev poetry install --no-dev
poetry run python build.py
mkdir data mkdir data
cp .env.example .env cp .env.example .env
sudo nano .env # set funding source nano .env # set funding source
``` ```
#### Running the server #### Running the server
@ -176,13 +177,15 @@ Problems installing? These commands have helped us install LNbits.
```sh ```sh
sudo apt install pkg-config libffi-dev libpq-dev sudo apt install pkg-config libffi-dev libpq-dev
# build essentials for debian/ubuntu
sudo apt install python3.9-dev gcc build-essential
# if the secp256k1 build fails: # if the secp256k1 build fails:
# if you used venv
./venv/bin/pip install setuptools wheel
# if you used poetry # if you used poetry
poetry add setuptools wheel poetry add setuptools wheel
# build essentials for debian/ubuntu
sudo apt install python3-dev gcc build-essential # if you used venv
./venv/bin/pip install setuptools wheel
``` ```
### Optional: PostgreSQL database ### Optional: PostgreSQL database

View file

@ -452,6 +452,15 @@ async def delete_payment(checking_id: str, conn: Optional[Connection] = None) ->
) )
async def delete_wallet_payment(
checking_id: str, wallet_id: str, conn: Optional[Connection] = None
) -> None:
await (conn or db).execute(
"DELETE FROM apipayments WHERE checking_id = ? AND wallet = ?",
(checking_id, wallet_id),
)
async def check_internal( async def check_internal(
payment_hash: str, conn: Optional[Connection] = None payment_hash: str, conn: Optional[Connection] = None
) -> Optional[str]: ) -> Optional[str]:

View file

@ -9,6 +9,7 @@ from lnurl import encode as lnurl_encode # type: ignore
from loguru import logger from loguru import logger
from pydantic import BaseModel from pydantic import BaseModel
from lnbits.db import Connection
from lnbits.helpers import url_for from lnbits.helpers import url_for
from lnbits.settings import WALLET from lnbits.settings import WALLET
from lnbits.wallets.base import PaymentStatus from lnbits.wallets.base import PaymentStatus
@ -131,7 +132,11 @@ class Payment(BaseModel):
def is_uncheckable(self) -> bool: def is_uncheckable(self) -> bool:
return self.checking_id.startswith("internal_") return self.checking_id.startswith("internal_")
async def update_status(self, status: PaymentStatus) -> None: async def update_status(
self,
status: PaymentStatus,
conn: Optional[Connection] = None,
) -> None:
from .crud import update_payment_details from .crud import update_payment_details
await update_payment_details( await update_payment_details(
@ -139,6 +144,7 @@ class Payment(BaseModel):
pending=status.pending, pending=status.pending,
fee=status.fee_msat, fee=status.fee_msat,
preimage=status.preimage, preimage=status.preimage,
conn=conn,
) )
async def set_pending(self, pending: bool) -> None: async def set_pending(self, pending: bool) -> None:
@ -146,7 +152,10 @@ class Payment(BaseModel):
await update_payment_status(self.checking_id, pending) await update_payment_status(self.checking_id, pending)
async def check_status(self) -> PaymentStatus: async def check_status(
self,
conn: Optional[Connection] = None,
) -> PaymentStatus:
if self.is_uncheckable: if self.is_uncheckable:
return PaymentStatus(None) return PaymentStatus(None)
@ -165,18 +174,18 @@ class Payment(BaseModel):
logger.warning( logger.warning(
f"Deleting outgoing failed payment {self.checking_id}: {status}" f"Deleting outgoing failed payment {self.checking_id}: {status}"
) )
await self.delete() await self.delete(conn)
elif not status.pending: elif not status.pending:
logger.info( logger.info(
f"Marking '{'in' if self.is_in else 'out'}' {self.checking_id} as not pending anymore: {status}" f"Marking '{'in' if self.is_in else 'out'}' {self.checking_id} as not pending anymore: {status}"
) )
await self.update_status(status) await self.update_status(status, conn=conn)
return status return status
async def delete(self) -> None: async def delete(self, conn: Optional[Connection] = None) -> None:
from .crud import delete_payment from .crud import delete_payment
await delete_payment(self.checking_id) await delete_payment(self.checking_id, conn=conn)
class BalanceCheck(BaseModel): class BalanceCheck(BaseModel):

View file

@ -28,7 +28,7 @@ from . import db
from .crud import ( from .crud import (
check_internal, check_internal,
create_payment, create_payment,
delete_payment, delete_wallet_payment,
get_wallet, get_wallet,
get_wallet_payment, get_wallet_payment,
update_payment_details, update_payment_details,
@ -221,7 +221,7 @@ async def pay_invoice(
logger.warning(f"backend sent payment failure") logger.warning(f"backend sent payment failure")
async with db.connect() as conn: async with db.connect() as conn:
logger.debug(f"deleting temporary payment {temp_id}") logger.debug(f"deleting temporary payment {temp_id}")
await delete_payment(temp_id, conn=conn) await delete_wallet_payment(temp_id, wallet_id, conn=conn)
raise PaymentFailure( raise PaymentFailure(
f"payment failed: {payment.error_message}" f"payment failed: {payment.error_message}"
or "payment failed, but backend didn't give us an error message" or "payment failed, but backend didn't give us an error message"

View file

@ -369,9 +369,9 @@ new Vue({
decodeRequest: function () { decodeRequest: function () {
this.parse.show = true this.parse.show = true
let req = this.parse.data.request.toLowerCase() let req = this.parse.data.request.toLowerCase()
if (this.parse.data.request.startsWith('lightning:')) { if (this.parse.data.request.toLowerCase().startsWith('lightning:')) {
this.parse.data.request = this.parse.data.request.slice(10) this.parse.data.request = this.parse.data.request.slice(10)
} else if (this.parse.data.request.startsWith('lnurl:')) { } else if (this.parse.data.request.toLowerCase().startsWith('lnurl:')) {
this.parse.data.request = this.parse.data.request.slice(6) this.parse.data.request = this.parse.data.request.slice(6)
} else if (req.indexOf('lightning=lnurl1') !== -1) { } else if (req.indexOf('lightning=lnurl1') !== -1) {
this.parse.data.request = this.parse.data.request this.parse.data.request = this.parse.data.request

View file

@ -711,7 +711,7 @@
<q-card class="q-pa-lg"> <q-card class="q-pa-lg">
<h6 class="q-my-md text-primary">Warning</h6> <h6 class="q-my-md text-primary">Warning</h6>
<p> <p>
Login functionality to be released in v0.2, for now, Login functionality to be released in a future update, for now,
<strong <strong
>make sure you bookmark this page for future access to your >make sure you bookmark this page for future access to your
wallet</strong wallet</strong

View file

@ -402,10 +402,6 @@ async def subscribe(request: Request, wallet: Wallet):
async def api_payments_sse( async def api_payments_sse(
request: Request, wallet: WalletTypeInfo = Depends(get_key_type) request: Request, wallet: WalletTypeInfo = Depends(get_key_type)
): ):
if wallet is None or wallet.wallet is None:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Wallet does not exist."
)
return EventSourceResponse( return EventSourceResponse(
subscribe(request, wallet.wallet), ping=20, media_type="text/event-stream" subscribe(request, wallet.wallet), ping=20, media_type="text/event-stream"
) )

View file

@ -138,44 +138,38 @@ async def get_key_type(
detail="Invoice (or Admin) key required.", detail="Invoice (or Admin) key required.",
) )
try: for typenr, WalletChecker in zip(
admin_checker = WalletAdminKeyChecker(api_key=token) [0, 1], [WalletAdminKeyChecker, WalletInvoiceKeyChecker]
await admin_checker.__call__(r) ):
wallet = WalletTypeInfo(0, admin_checker.wallet) # type: ignore try:
if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and ( checker = WalletChecker(api_key=token)
LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS await checker.__call__(r)
): wallet = WalletTypeInfo(typenr, checker.wallet) # type: ignore
raise HTTPException( if wallet is None or wallet.wallet is None:
status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized." raise HTTPException(
) status_code=HTTPStatus.NOT_FOUND, detail="Wallet does not exist."
return wallet )
except HTTPException as e: if (
if e.status_code == HTTPStatus.BAD_REQUEST: LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS
) and (LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS):
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="User not authorized for this extension.",
)
return wallet
except HTTPException as e:
if e.status_code == HTTPStatus.BAD_REQUEST:
raise
elif e.status_code == HTTPStatus.UNAUTHORIZED:
# we pass this in case it is not an invoice key, nor an admin key, and then return NOT_FOUND at the end of this block
pass
else:
raise
except:
raise raise
if e.status_code == HTTPStatus.UNAUTHORIZED: raise HTTPException(
pass status_code=HTTPStatus.NOT_FOUND, detail="Wallet does not exist."
except: )
raise
try:
invoice_checker = WalletInvoiceKeyChecker(api_key=token)
await invoice_checker.__call__(r)
wallet = WalletTypeInfo(1, invoice_checker.wallet) # type: ignore
if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and (
LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS
):
raise HTTPException(
status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
)
return wallet
except HTTPException as e:
if e.status_code == HTTPStatus.BAD_REQUEST:
raise
if e.status_code == HTTPStatus.UNAUTHORIZED:
return WalletTypeInfo(2, None) # type: ignore
except:
raise
return wallet
async def require_admin_key( async def require_admin_key(

View file

@ -19,42 +19,25 @@ async def create_ticket(
(payment_hash, wallet, event, name, email, False, True), (payment_hash, wallet, event, name, email, False, True),
) )
# UPDATE EVENT DATA ON SOLD TICKET
eventdata = await get_event(event)
assert eventdata, "Couldn't get event from ticket being paid"
sold = eventdata.sold + 1
amount_tickets = eventdata.amount_tickets - 1
await db.execute(
"""
UPDATE events.events
SET sold = ?, amount_tickets = ?
WHERE id = ?
""",
(sold, amount_tickets, event),
)
ticket = await get_ticket(payment_hash) ticket = await get_ticket(payment_hash)
assert ticket, "Newly created ticket couldn't be retrieved" assert ticket, "Newly created ticket couldn't be retrieved"
return ticket return ticket
async def set_ticket_paid(payment_hash: str) -> Tickets:
row = await db.fetchone("SELECT * FROM events.ticket WHERE id = ?", (payment_hash,))
if row[6] != True:
await db.execute(
"""
UPDATE events.ticket
SET paid = true
WHERE id = ?
""",
(payment_hash,),
)
eventdata = await get_event(row[2])
assert eventdata, "Couldn't get event from ticket being paid"
sold = eventdata.sold + 1
amount_tickets = eventdata.amount_tickets - 1
await db.execute(
"""
UPDATE events.events
SET sold = ?, amount_tickets = ?
WHERE id = ?
""",
(sold, amount_tickets, row[2]),
)
ticket = await get_ticket(payment_hash)
assert ticket, "Newly updated ticket couldn't be retrieved"
return ticket
async def get_ticket(payment_hash: str) -> Optional[Tickets]: async def get_ticket(payment_hash: str) -> Optional[Tickets]:
row = await db.fetchone("SELECT * FROM events.ticket WHERE id = ?", (payment_hash,)) row = await db.fetchone("SELECT * FROM events.ticket WHERE id = ?", (payment_hash,))
return Tickets(**row) if row else None return Tickets(**row) if row else None

View file

@ -24,7 +24,6 @@ from .crud import (
get_ticket, get_ticket,
get_tickets, get_tickets,
reg_ticket, reg_ticket,
set_ticket_paid,
update_event, update_event,
) )

View file

@ -5,13 +5,13 @@ animals = [
"duck", "duck",
"eagle", "eagle",
"flamingo", "flamingo",
"gorila", "gorilla",
"hamster", "hamster",
"iguana", "iguana",
"jaguar", "jaguar",
"koala", "koala",
"llama", "llama",
"macaroni penguim", "macaroni penguin",
"numbat", "numbat",
"octopus", "octopus",
"platypus", "platypus",

View file

@ -138,8 +138,9 @@
hide-dropdown-icon hide-dropdown-icon
input-debounce="0" input-debounce="0"
new-value-mode="add-unique" new-value-mode="add-unique"
label="Tip % Options" label="Tip % Options (hit enter to add values)"
></q-select> ><q-tooltip>Hit enter to add values</q-tooltip></q-select
>
<div class="row q-mt-lg"> <div class="row q-mt-lg">
<q-btn <q-btn
unelevated unelevated

View file

@ -253,7 +253,7 @@
name="check" name="check"
transition-show="fade" transition-show="fade"
class="text-light-green" class="text-light-green"
style="font-size: 40em" style="font-size: min(90vw, 40em)"
></q-icon> ></q-icon>
</q-dialog> </q-dialog>
</q-page> </q-page>
@ -294,6 +294,7 @@
exchangeRate: null, exchangeRate: null,
stack: [], stack: [],
tipAmount: 0.0, tipAmount: 0.0,
hasNFC: false,
nfcTagReading: false, nfcTagReading: false,
invoiceDialog: { invoiceDialog: {
show: false, show: false,
@ -370,7 +371,7 @@
this.showInvoice() this.showInvoice()
}, },
submitForm: function () { submitForm: function () {
if (this.tip_options.length) { if (this.tip_options && this.tip_options.length) {
this.showTipModal() this.showTipModal()
} else { } else {
this.showInvoice() this.showInvoice()
@ -413,9 +414,6 @@
dialog.show = false dialog.show = false
self.complete.show = true self.complete.show = true
setTimeout(function () {
self.complete.show = false
}, 5000)
} }
}) })
}, 3000) }, 3000)

View file

@ -3,23 +3,36 @@
<p> <p>
Onchain Wallet (watch-only) extension uses mempool.space<br /> Onchain Wallet (watch-only) extension uses mempool.space<br />
For use with "account Extended Public Key" For use with "account Extended Public Key"
<a href="https://iancoleman.io/bip39/">https://iancoleman.io/bip39/</a> <a
href="https://iancoleman.io/bip39/"
target="_blank"
style="color: unset"
>https://iancoleman.io/bip39/</a
>
<br />
Flash binaries
<a
href="https://lnbits.github.io/hardware-wallet"
target="_blank"
style="color: unset"
>directly from browser</a
>
<small> <small>
<br />Created by, <br />Created by,
<a target="_blank" class="text-white" href="https://github.com/arcbtc" <a target="_blank" style="color: unset" href="https://github.com/arcbtc"
>Ben Arc</a >Ben Arc</a
> >
(using, (using,
<a <a
target="_blank" target="_blank"
class="text-white" style="color: unset"
href="https://github.com/diybitcoinhardware/embit" href="https://github.com/diybitcoinhardware/embit"
>Embit</a >Embit</a
></small ></small
>) >)
<br /> <br />
<br /> <br />
<a target="_blank" href="/docs#/watchonly" class="text-white" <a target="_blank" href="/docs#/watchonly" style="color: unset"
>Swagger REST API Documentation</a >Swagger REST API Documentation</a
> >
</p> </p>

View file

@ -103,7 +103,7 @@ async def check_pending_payments():
conn=conn, conn=conn,
) )
for payment in pending_payments: for payment in pending_payments:
await payment.check_status() await payment.check_status(conn=conn)
logger.debug( logger.debug(
f"Task: pending check finished for {len(pending_payments)} payments (took {time.time() - start_time:0.3f} s)" f"Task: pending check finished for {len(pending_payments)} payments (took {time.time() - start_time:0.3f} s)"