Merge branch 'main' into matthewcroughan/nixify

This commit is contained in:
ben
2022-07-23 20:09:24 +01:00
114 changed files with 1720 additions and 988 deletions
+3 -5
View File
@@ -1,21 +1,19 @@
import asyncio
import uvloop
from starlette.requests import Request
from loguru import logger
from starlette.requests import Request
from .commands import migrate_databases
from .settings import (
DEBUG,
HOST,
LNBITS_COMMIT,
LNBITS_DATA_FOLDER,
LNBITS_DATABASE_URL,
LNBITS_SITE_TITLE,
HOST,
PORT,
WALLET,
LNBITS_DATABASE_URL,
LNBITS_DATA_FOLDER,
)
uvloop.install()
+32 -8
View File
@@ -1,11 +1,9 @@
import asyncio
import importlib
import logging
import sys
import traceback
import warnings
from loguru import logger
from http import HTTPStatus
from fastapi import FastAPI, Request
@@ -14,6 +12,7 @@ from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware
from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles
from loguru import logger
import lnbits.settings
from lnbits.core.tasks import register_task_listeners
@@ -199,8 +198,33 @@ def register_exception_handlers(app: FastAPI):
def configure_logger() -> None:
logger.remove()
log_level: str = "DEBUG" if lnbits.settings.DEBUG else "INFO"
if lnbits.settings.DEBUG:
fmt: str = "<green>{time:YYYY-MM-DD HH:mm:ss.SS}</green> | <level>{level: <6}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> | <level>{message}</level>"
else:
fmt: str = "<green>{time:YYYY-MM-DD HH:mm:ss.SS}</green> | <level>{level}</level> | <level>{message}</level>"
logger.add(sys.stderr, level=log_level, format=fmt)
formatter = Formatter()
logger.add(sys.stderr, level=log_level, format=formatter.format)
logging.getLogger("uvicorn").handlers = [InterceptHandler()]
logging.getLogger("uvicorn.access").handlers = [InterceptHandler()]
class Formatter:
def __init__(self):
self.padding = 0
self.minimal_fmt: str = "<green>{time:YYYY-MM-DD HH:mm:ss.SS}</green> | <level>{level}</level> | <level>{message}</level>\n"
if lnbits.settings.DEBUG:
self.fmt: str = "<green>{time:YYYY-MM-DD HH:mm:ss.SS}</green> | <level>{level: <4}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> | <level>{message}</level>\n"
else:
self.fmt: str = self.minimal_fmt
def format(self, record):
function = "{function}".format(**record)
if function == "emit": # uvicorn logs
return self.minimal_fmt
return self.fmt
class InterceptHandler(logging.Handler):
def emit(self, record):
try:
level = logger.level(record.levelname).name
except ValueError:
level = record.levelno
logger.log(level, record.getMessage())
+8 -7
View File
@@ -1,15 +1,16 @@
import bitstring # type: ignore
import re
import hashlib
from typing import List, NamedTuple, Optional
from bech32 import bech32_encode, bech32_decode, CHARSET
from ecdsa import SECP256k1, VerifyingKey # type: ignore
from ecdsa.util import sigdecode_string # type: ignore
from binascii import unhexlify
import re
import time
from binascii import unhexlify
from decimal import Decimal
from typing import List, NamedTuple, Optional
import bitstring # type: ignore
import embit
import secp256k1
from bech32 import CHARSET, bech32_decode, bech32_encode
from ecdsa import SECP256k1, VerifyingKey # type: ignore
from ecdsa.util import sigdecode_string # type: ignore
class Route(NamedTuple):
+7 -6
View File
@@ -1,18 +1,19 @@
import asyncio
import warnings
import click
import importlib
import re
import os
import re
import warnings
import click
from loguru import logger
from .db import SQLITE, POSTGRES, COCKROACH
from .core import db as core_db, migrations as core_migrations
from .core import db as core_db
from .core import migrations as core_migrations
from .db import COCKROACH, POSTGRES, SQLITE
from .helpers import (
get_valid_extensions,
get_css_vendored,
get_js_vendored,
get_valid_extensions,
url_for_vendored,
)
from .settings import LNBITS_PATH
+5 -5
View File
@@ -1,15 +1,15 @@
import json
import datetime
from uuid import uuid4
from typing import List, Optional, Dict, Any
import json
from typing import Any, Dict, List, Optional
from urllib.parse import urlparse
from uuid import uuid4
from lnbits import bolt11
from lnbits.db import Connection, POSTGRES, COCKROACH
from lnbits.db import COCKROACH, POSTGRES, Connection
from lnbits.settings import DEFAULT_WALLET_NAME, LNBITS_ADMIN_USERS
from . import db
from .models import User, Wallet, Payment, BalanceCheck
from .models import BalanceCheck, Payment, User, Wallet
# accounts
# --------
+7 -7
View File
@@ -1,15 +1,15 @@
import json
import hmac
import hashlib
from lnbits.helpers import url_for
import hmac
import json
from sqlite3 import Row
from typing import Dict, List, NamedTuple, Optional
from ecdsa import SECP256k1, SigningKey # type: ignore
from lnurl import encode as lnurl_encode # type: ignore
from typing import List, NamedTuple, Optional, Dict
from sqlite3 import Row
from loguru import logger
from pydantic import BaseModel
from loguru import logger
from lnbits.helpers import url_for
from lnbits.settings import WALLET
+20 -9
View File
@@ -3,20 +3,25 @@ import json
from binascii import unhexlify
from io import BytesIO
from typing import Dict, Optional, Tuple
from loguru import logger
from urllib.parse import parse_qs, urlparse
import httpx
from fastapi import Depends
from lnurl import LnurlErrorResponse
from lnurl import decode as decode_lnurl # type: ignore
from loguru import logger
from lnbits import bolt11
from lnbits.db import Connection
from lnbits.decorators import (
WalletTypeInfo,
get_key_type,
require_admin_key,
require_invoice_key,
)
from lnbits.helpers import url_for, urlsafe_short_hash
from lnbits.requestvars import g
from lnbits.settings import WALLET
from lnbits.settings import FAKE_WALLET, WALLET
from lnbits.wallets.base import PaymentResponse, PaymentStatus
from . import db
@@ -51,15 +56,19 @@ async def create_invoice(
description_hash: Optional[bytes] = None,
extra: Optional[Dict] = None,
webhook: Optional[str] = None,
internal: Optional[bool] = False,
conn: Optional[Connection] = None,
) -> Tuple[str, str]:
invoice_memo = None if description_hash else memo
ok, checking_id, payment_request, error_message = await WALLET.create_invoice(
# use the fake wallet if the invoice is for internal use only
wallet = FAKE_WALLET if internal else WALLET
ok, checking_id, payment_request, error_message = await wallet.create_invoice(
amount=amount, memo=invoice_memo, description_hash=description_hash
)
if not ok:
raise InvoiceFailure(error_message or "Unexpected backend error.")
raise InvoiceFailure(error_message or "unexpected backend error.")
invoice = bolt11.decode(payment_request)
@@ -229,7 +238,7 @@ async def redeem_lnurl_withdraw(
conn=conn,
)
except:
logger.warn(
logger.warning(
f"failed to create invoice on redeem_lnurl_withdraw from {lnurl}. params: {res}"
)
return None
@@ -256,12 +265,14 @@ async def redeem_lnurl_withdraw(
async def perform_lnurlauth(
callback: str, conn: Optional[Connection] = None
callback: str,
wallet: WalletTypeInfo = Depends(require_admin_key),
conn: Optional[Connection] = None,
) -> Optional[LnurlErrorResponse]:
cb = urlparse(callback)
k1 = unhexlify(parse_qs(cb.query)["k1"][0])
key = g().wallet.lnurlauth_key(cb.netloc)
key = wallet.wallet.lnurlauth_key(cb.netloc)
def int_to_bytes_suitable_der(x: int) -> bytes:
"""for strict DER we need to encode the integer with some quirks"""
+32
View File
@@ -1,4 +1,36 @@
new Vue({
el: '#vue',
data: function () {
return {
searchTerm: '',
filteredExtensions: null
}
},
mounted() {
this.filteredExtensions = this.g.extensions
},
watch: {
searchTerm(term) {
// Reset the filter
this.filteredExtensions = this.g.extensions
if (term !== '') {
// Filter the extensions list
function extensionNameContains(searchTerm) {
return function (extension) {
return (
extension.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
extension.shortDescription
.toLowerCase()
.includes(searchTerm.toLowerCase())
)
}
}
this.filteredExtensions = this.filteredExtensions.filter(
extensionNameContains(term)
)
}
}
},
mixins: [windowMixin]
})
+1 -1
View File
@@ -1,7 +1,7 @@
import asyncio
import httpx
from typing import List
import httpx
from loguru import logger
from lnbits.tasks import register_invoice_listener
+2 -1
View File
@@ -49,7 +49,8 @@
<h5 class="text-caption q-mt-sm q-mb-none">Body (application/json)</h5>
<code
>{"out": false, "amount": &lt;int&gt;, "memo": &lt;string&gt;, "unit":
&lt;string&gt;, "webhook": &lt;url:string&gt;}</code
&lt;string&gt;, "webhook": &lt;url:string&gt;, "internal":
&lt;bool&gt;}</code
>
<h5 class="text-caption q-mt-sm q-mb-none">
Returns 201 CREATED (application/json)
+14 -1
View File
@@ -2,10 +2,23 @@
%} {% block scripts %} {{ window_vars(user) }}
<script src="/core/static/js/extensions.js"></script>
{% endblock %} {% block page %}
<div class="row q-col-gutter-md q-mb-md">
<div class="col-sm-3 col-xs-8 q-ml-auto">
<q-input v-model="searchTerm" label="Search extensions">
<q-icon
v-if="searchTerm !== ''"
name="close"
@click="searchTerm = ''"
class="cursor-pointer q-mt-lg"
/>
</q-input>
</div>
</div>
<div class="row q-col-gutter-md">
<div
class="col-6 col-md-4 col-lg-3"
v-for="extension in g.extensions"
v-for="extension in filteredExtensions"
:key="extension.code"
>
<q-card>
+17 -19
View File
@@ -7,30 +7,23 @@ from typing import Dict, List, Optional, Union
from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse
import httpx
from loguru import logger
from fastapi import Header, Query, Request
from fastapi import Depends, Header, Query, Request
from fastapi.exceptions import HTTPException
from fastapi.param_functions import Depends
from fastapi.params import Body
from loguru import logger
from pydantic import BaseModel
from pydantic.fields import Field
from sse_starlette.sse import EventSourceResponse
from lnbits import bolt11, lnurl
from lnbits.bolt11 import Invoice
from lnbits.core.models import Payment, Wallet
from lnbits.decorators import (
WalletAdminKeyChecker,
WalletInvoiceKeyChecker,
WalletTypeInfo,
get_key_type,
require_admin_key,
require_invoice_key,
)
from lnbits.helpers import url_for, urlsafe_short_hash
from lnbits.requestvars import g
from lnbits.settings import LNBITS_ADMIN_USERS, LNBITS_SITE_TITLE
from lnbits.utils.exchange_rates import (
currencies,
@@ -149,6 +142,7 @@ class CreateInvoiceData(BaseModel):
lnurl_balance_check: Optional[str] = None
extra: Optional[dict] = None
webhook: Optional[str] = None
internal: Optional[bool] = False
bolt11: Optional[str] = None
@@ -175,6 +169,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
description_hash=description_hash,
extra=data.extra,
webhook=data.webhook,
internal=data.internal,
conn=conn,
)
except InvoiceFailure as e:
@@ -395,7 +390,7 @@ async def api_payment(payment_hash, X_Api_Key: Optional[str] = Header(None)):
wallet = None
try:
if X_Api_Key.extra:
logger.warn("No key")
logger.warning("No key")
except:
wallet = await get_wallet_for_key(X_Api_Key)
payment = await get_standalone_payment(
@@ -435,10 +430,8 @@ async def api_payment(payment_hash, X_Api_Key: Optional[str] = Header(None)):
return {"paid": not payment.pending, "preimage": payment.preimage}
@core_app.get(
"/api/v1/lnurlscan/{code}", dependencies=[Depends(WalletInvoiceKeyChecker())]
)
async def api_lnurlscan(code: str):
@core_app.get("/api/v1/lnurlscan/{code}")
async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type)):
try:
url = lnurl.decode(code)
domain = urlparse(url).netloc
@@ -466,7 +459,7 @@ async def api_lnurlscan(code: str):
params.update(kind="auth")
params.update(callback=url) # with k1 already in it
lnurlauth_key = g().wallet.lnurlauth_key(domain)
lnurlauth_key = wallet.wallet.lnurlauth_key(domain)
params.update(pubkey=lnurlauth_key.verifying_key.to_string("compressed").hex())
else:
async with httpx.AsyncClient() as client:
@@ -582,14 +575,19 @@ async def api_payments_decode(data: DecodePayment):
return {"message": "Failed to decode"}
@core_app.post("/api/v1/lnurlauth", dependencies=[Depends(WalletAdminKeyChecker())])
async def api_perform_lnurlauth(callback: str):
err = await perform_lnurlauth(callback)
class Callback(BaseModel):
callback: str = Query(...)
@core_app.post("/api/v1/lnurlauth")
async def api_perform_lnurlauth(
callback: Callback, wallet: WalletTypeInfo = Depends(require_admin_key)
):
err = await perform_lnurlauth(callback.callback, wallet=wallet)
if err:
raise HTTPException(
status_code=HTTPStatus.SERVICE_UNAVAILABLE, detail=err.reason
)
return ""
+3 -4
View File
@@ -7,11 +7,10 @@ from fastapi.exceptions import HTTPException
from fastapi.params import Depends, Query
from fastapi.responses import FileResponse, RedirectResponse
from fastapi.routing import APIRouter
from loguru import logger
from pydantic.types import UUID4
from starlette.responses import HTMLResponse, JSONResponse
from loguru import logger
from lnbits.core import db
from lnbits.core.models import User
from lnbits.decorators import check_user_exists
@@ -113,7 +112,7 @@ async def wallet(
if not user_id:
user = await get_user((await create_account()).id)
logger.info(f"Created new account for user {user.id}")
logger.info(f"Create user {user.id}")
else:
user = await get_user(user_id)
if not user:
@@ -140,7 +139,7 @@ async def wallet(
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
)
logger.info(f"Access wallet {wallet_name} of user {user.id}")
logger.debug(f"Access wallet {wallet_name}{'of user '+ user.id if user else ''}")
wallet = user.get_wallet(wallet_id)
if not wallet:
return template_renderer().TemplateResponse(
+1 -2
View File
@@ -4,11 +4,10 @@ from http import HTTPStatus
from urllib.parse import urlparse
from fastapi import HTTPException
from loguru import logger
from starlette.requests import Request
from starlette.responses import HTMLResponse
from loguru import logger
from lnbits import bolt11
from .. import core_app
-1
View File
@@ -6,7 +6,6 @@ from contextlib import asynccontextmanager
from typing import Optional
from loguru import logger
from sqlalchemy import create_engine
from sqlalchemy_aio.base import AsyncConnection
from sqlalchemy_aio.strategy import ASYNCIO_STRATEGY # type: ignore
+2 -2
View File
@@ -14,9 +14,9 @@ from lnbits.core.crud import get_user, get_wallet_for_key
from lnbits.core.models import User, Wallet
from lnbits.requestvars import g
from lnbits.settings import (
LNBITS_ALLOWED_USERS,
LNBITS_ADMIN_USERS,
LNBITS_ADMIN_EXTENSIONS,
LNBITS_ADMIN_USERS,
LNBITS_ALLOWED_USERS,
)
@@ -1,7 +1,8 @@
import httpx
import json
import os
import httpx
fiat_currencies = json.load(
open(
os.path.join(
+1 -2
View File
@@ -3,9 +3,8 @@ import math
import traceback
from http import HTTPStatus
from starlette.requests import Request
from loguru import logger
from starlette.requests import Request
from . import bleskomat_ext
from .crud import (
+2 -3
View File
@@ -3,13 +3,12 @@ import time
from typing import Dict
from fastapi.params import Query
from loguru import logger
from pydantic import BaseModel, validator
from starlette.requests import Request
from loguru import logger
from lnbits import bolt11
from lnbits.core.services import pay_invoice, PaymentFailure
from lnbits.core.services import PaymentFailure, pay_invoice
from . import db
from .exchange_rates import exchange_rate_providers, fiat_currencies
+1 -2
View File
@@ -1,9 +1,8 @@
from http import HTTPStatus
from fastapi import Depends, Query
from starlette.exceptions import HTTPException
from loguru import logger
from starlette.exceptions import HTTPException
from lnbits.core.crud import get_user
from lnbits.decorators import WalletTypeInfo, require_admin_key
+9 -7
View File
@@ -1,12 +1,14 @@
from urllib.parse import urlparse, urlunparse, parse_qs, urlencode, ParseResult
from starlette.requests import Request
from fastapi.param_functions import Query
from typing import Optional, Dict
from lnbits.lnurl import encode as lnurl_encode # type: ignore
from lnurl.types import LnurlPayMetadata # type: ignore
from pydantic import BaseModel
import json
from sqlite3 import Row
from typing import Dict, Optional
from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse
from fastapi.param_functions import Query
from lnurl.types import LnurlPayMetadata # type: ignore
from pydantic import BaseModel
from starlette.requests import Request
from lnbits.lnurl import encode as lnurl_encode # type: ignore
class CreateCopilotData(BaseModel):
+1 -1
View File
@@ -25,7 +25,7 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None:
webhook = None
data = None
if "copilot" != payment.extra.get("tag"):
if payment.extra.get("tag") != "copilot":
# not an copilot invoice
return
+1 -1
View File
@@ -1,8 +1,8 @@
from sqlite3 import Row
from typing import Optional
from fastapi.param_functions import Query
from pydantic import BaseModel
from typing import Optional
class CreateUserData(BaseModel):
+3 -3
View File
@@ -1,9 +1,9 @@
from typing import NamedTuple
from sqlite3 import Row
from typing import NamedTuple, Optional
from fastapi.param_functions import Query
from pydantic.main import BaseModel
from pydantic import BaseModel
from typing import Optional
from pydantic.main import BaseModel
class CreateJukeLinkData(BaseModel):
+1 -1
View File
@@ -16,7 +16,7 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None:
if "jukebox" != payment.extra.get("tag"):
if payment.extra.get("tag") != "jukebox":
# not a jukebox invoice
return
await update_jukebox_payment(payment.payment_hash, paid=True)
@@ -117,7 +117,7 @@
>
<q-step
:name="1"
title="Pick wallet, price"
title="1. Pick Wallet and Price"
icon="account_balance_wallet"
:done="step > 1"
>
@@ -170,16 +170,25 @@
<br />
</q-step>
<q-step :name="2" title="Add api keys" icon="vpn_key" :done="step > 2">
<q-step
:name="2"
title="2. Add API keys"
icon="vpn_key"
:done="step > 2"
>
<img src="/jukebox/static/spotapi.gif" />
To use this extension you need a Spotify client ID and client secret.
You get these by creating an app in the Spotify developers dashboard
<a
You get these by creating an app in the Spotify Developer Dashboard
<br />
<br />
<q-btn
type="a"
target="_blank"
style="color: #43a047"
color="primary"
href="https://developer.spotify.com/dashboard/applications"
>here</a
>.
>Open the Spotify Developer Dashboard</q-btn
>
<q-input
filled
class="q-pb-md q-pt-md"
@@ -231,28 +240,39 @@
<br />
</q-step>
<q-step :name="3" title="Add Redirect URI" icon="link" :done="step > 3">
<q-step
:name="3"
title="3. Add Redirect URI"
icon="link"
:done="step > 3"
>
<img src="/jukebox/static/spotapi1.gif" />
In the app go to edit-settings, set the redirect URI to this link
<p>
In the app go to edit-settings, set the redirect URI to this link
</p>
<q-card
class="cursor-pointer word-break"
@click="copyText(locationcb + jukeboxDialog.data.sp_id, 'Link copied to clipboard!')"
>
<q-card-section style="word-break: break-all">
{% raw %}{{ locationcb }}{{ jukeboxDialog.data.sp_id }}{% endraw
%}
</q-card-section>
<q-tooltip> Click to copy URL </q-tooltip>
</q-card>
<br />
<q-btn
dense
outline
unelevated
color="primary"
size="xs"
@click="copyText(locationcb + jukeboxDialog.data.sp_id, 'Link copied to clipboard!')"
>{% raw %}{{ locationcb }}{{ jukeboxDialog.data.sp_id }}{% endraw
%}<q-tooltip> Click to copy URL </q-tooltip>
</q-btn>
<br />
Settings can be found
<a
type="a"
target="_blank"
style="color: #43a047"
color="primary"
href="https://developer.spotify.com/dashboard/applications"
>here</a
>.
>Open the Spotify Application Settings</q-btn
>
<br /><br />
<p>
After adding the redirect URI, click the "Authorise access" button
below.
</p>
<div class="row q-mt-md">
<div class="col-4">
@@ -281,7 +301,7 @@
<q-step
:name="4"
title="Select playlists"
title="4. Select Device and Playlists"
icon="queue_music"
active-color="primary"
:done="step > 4"
+1 -1
View File
@@ -22,7 +22,7 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None:
if "livestream" != payment.extra.get("tag"):
if payment.extra.get("tag") != "livestream":
# not a livestream invoice
return
+2 -2
View File
@@ -1,7 +1,5 @@
from http import HTTPStatus
# from mmap import MAP_DENYWRITE
from fastapi.param_functions import Depends
from fastapi.params import Query
from starlette.exceptions import HTTPException
@@ -15,6 +13,8 @@ from lnbits.decorators import check_user_exists
from . import livestream_ext, livestream_renderer
from .crud import get_livestream_by_track, get_track
# from mmap import MAP_DENYWRITE
@livestream_ext.get("/", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_user_exists)):
+1 -3
View File
@@ -3,15 +3,13 @@ import json
from datetime import datetime, timedelta
import httpx
from loguru import logger
from fastapi.params import Query
from lnurl import ( # type: ignore
LnurlErrorResponse,
LnurlPayActionResponse,
LnurlPayResponse,
)
from loguru import logger
from starlette.requests import Request
from starlette.responses import HTMLResponse
+2 -2
View File
@@ -43,13 +43,13 @@ async def call_webhook_on_paid(payment_hash):
async def on_invoice_paid(payment: Payment) -> None:
if "lnaddress" == payment.extra.get("tag"):
if payment.extra.get("tag") == "lnaddress":
await payment.set_pending(False)
await set_address_paid(payment_hash=payment.payment_hash)
await call_webhook_on_paid(payment_hash=payment.payment_hash)
elif "renew lnaddress" == payment.extra.get("tag"):
elif payment.extra.get("tag") == "renew lnaddress":
await payment.set_pending(False)
await set_address_renewed(
+2 -4
View File
@@ -1,14 +1,12 @@
from base64 import b64decode
from fastapi.param_functions import Security
from fastapi.security.api_key import APIKeyHeader
from fastapi import Request, status
from fastapi.param_functions import Security
from fastapi.security.api_key import APIKeyHeader
from starlette.exceptions import HTTPException
from lnbits.decorators import WalletTypeInfo, get_key_type # type: ignore
api_key_header_auth = APIKeyHeader(
name="AUTHORIZATION",
auto_error=False,
+4 -2
View File
@@ -1,8 +1,10 @@
from lnbits.decorators import check_user_exists
from . import lndhub_ext, lndhub_renderer
from fastapi import Request
from fastapi.params import Depends
from lnbits.core.models import User
from lnbits.decorators import check_user_exists
from . import lndhub_ext, lndhub_renderer
@lndhub_ext.get("/")
+2 -3
View File
@@ -1,6 +1,5 @@
import time
import asyncio
import time
from base64 import urlsafe_b64encode
from http import HTTPStatus
@@ -13,7 +12,7 @@ from lnbits import bolt11
from lnbits.core.crud import delete_expired_invoices, get_payments
from lnbits.core.services import create_invoice, pay_invoice
from lnbits.decorators import WalletTypeInfo
from lnbits.settings import WALLET, LNBITS_SITE_TITLE
from lnbits.settings import LNBITS_SITE_TITLE, WALLET
from . import lndhub_ext
from .decorators import check_wallet, require_admin_key
+4 -3
View File
@@ -1,11 +1,12 @@
from lnbits.core.models import Wallet
from typing import List, Optional, Union
import httpx
from lnbits.core.models import Wallet
from lnbits.helpers import urlsafe_short_hash
from . import db
from .models import CreateFormData, CreateTicketData, Tickets, Forms
import httpx
from .models import CreateFormData, CreateTicketData, Forms, Tickets
async def create_ticket(
+1
View File
@@ -1,4 +1,5 @@
from typing import Optional
from fastapi.param_functions import Query
from pydantic import BaseModel
+1 -1
View File
@@ -18,7 +18,7 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None:
if "lnticket" != payment.extra.get("tag"):
if payment.extra.get("tag") != "lnticket":
# not a lnticket invoice
return
+5 -9
View File
@@ -1,30 +1,26 @@
import base64
import hashlib
import hmac
from http import HTTPStatus
from io import BytesIO
from typing import Optional
from embit import bech32
from embit import compact
import base64
from io import BytesIO
import hmac
from embit import bech32, compact
from fastapi import Request
from fastapi.param_functions import Query
from starlette.exceptions import HTTPException
from lnbits.core.services import create_invoice
from lnbits.utils.exchange_rates import fiat_amount_as_satoshis
from lnbits.core.views.api import pay_invoice
from lnbits.utils.exchange_rates import fiat_amount_as_satoshis
from . import lnurldevice_ext
from .crud import (
create_lnurldevicepayment,
get_lnurldevice,
get_lnurldevicepayment,
update_lnurldevicepayment,
get_lnurlpayload,
update_lnurldevicepayment,
)
+1 -1
View File
@@ -38,7 +38,7 @@ async def m001_initial(db):
async def m002_redux(db):
"""
Moves everything from lnurlpos to lnurldevices
Moves everything from lnurlpos to lnurldevice
"""
try:
for row in [
@@ -120,7 +120,7 @@
<q-card-section>
<code
><span class="text-blue">GET</span>
/lnurldevice/api/v1/lnurlposs</code
/lnurldevice/api/v1/lnurlpos</code
>
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
<code>{"X-Api-Key": &lt;invoice_key&gt;}</code><br />
+2 -1
View File
@@ -1,8 +1,9 @@
from typing import List, Optional, Union
from lnbits.db import SQLITE
from . import db
from .models import PayLink, CreatePayLinkData
from .models import CreatePayLinkData, PayLink
async def create_pay_link(data: CreatePayLinkData, wallet_id: str) -> PayLink:
+8 -6
View File
@@ -1,12 +1,14 @@
import json
from urllib.parse import urlparse, urlunparse, parse_qs, urlencode, ParseResult
from starlette.requests import Request
from fastapi.param_functions import Query
from typing import Optional, Dict
from lnbits.lnurl import encode as lnurl_encode # type: ignore
from lnurl.types import LnurlPayMetadata # type: ignore
from sqlite3 import Row
from typing import Dict, Optional
from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse
from fastapi.param_functions import Query
from lnurl.types import LnurlPayMetadata # type: ignore
from pydantic import BaseModel
from starlette.requests import Request
from lnbits.lnurl import encode as lnurl_encode # type: ignore
class CreatePayLinkData(BaseModel):
@@ -35,6 +35,7 @@ new Vue({
rowsPerPage: 10
}
},
nfcTagWriting: false,
formDialog: {
show: false,
fixedAmount: true,
@@ -205,6 +206,42 @@ new Vue({
.catch(err => {
LNbits.utils.notifyApiError(err)
})
},
writeNfcTag: async function (lnurl) {
try {
if (typeof NDEFReader == 'undefined') {
throw {
toString: function () {
return 'NFC not supported on this device or browser.'
}
}
}
const ndef = new NDEFReader()
this.nfcTagWriting = true
this.$q.notify({
message: 'Tap your NFC tag to write the LNURL-pay link to it.'
})
await ndef.write({
records: [{recordType: 'url', data: 'lightning:' + lnurl, lang: 'en'}]
})
this.nfcTagWriting = false
this.$q.notify({
type: 'positive',
message: 'NFC tag written successfully.'
})
} catch (error) {
this.nfcTagWriting = false
this.$q.notify({
type: 'negative',
message: error
? error.toString()
: 'An unexpected error has occurred.'
})
}
}
},
created() {
+2 -1
View File
@@ -1,5 +1,6 @@
import asyncio
import json
import httpx
from lnbits.core import db as core_db
@@ -19,7 +20,7 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None:
if "lnurlp" != payment.extra.get("tag"):
if payment.extra.get("tag") != "lnurlp":
# not an lnurlp invoice
return
@@ -14,10 +14,17 @@
</q-responsive>
</a>
</div>
<div class="row q-mt-lg">
<div class="row q-mt-lg q-gutter-sm">
<q-btn outline color="grey" @click="copyText('{{ lnurl }}')"
>Copy LNURL</q-btn
>
<q-btn
outline
color="grey"
icon="nfc"
@click="writeNfcTag(' {{ lnurl }} ')"
:disable="nfcTagWriting"
></q-btn>
</div>
</q-card-section>
</q-card>
@@ -99,7 +99,8 @@
@click="openUpdateDialog(props.row.id)"
icon="edit"
color="light-blue"
></q-btn>
>
</q-btn>
<q-btn
flat
dense
@@ -153,7 +154,8 @@
v-model.trim="formDialog.data.description"
type="text"
label="Item description *"
></q-input>
>
</q-input>
<div class="row q-col-gutter-sm">
<q-input
filled
@@ -171,7 +173,8 @@
type="number"
:step="formDialog.data.currency && formDialog.data.currency !== 'satoshis' ? '0.01' : '1'"
label="Max *"
></q-input>
>
</q-input>
</div>
<div class="row q-col-gutter-sm">
<div class="col">
@@ -200,7 +203,8 @@
type="number"
label="Comment maximum characters"
hint="Tell wallets to prompt users for a comment that will be sent along with the payment. LNURLp will store the comment and send it in the webhook."
></q-input>
>
</q-input>
<q-input
filled
dense
@@ -224,7 +228,8 @@
type="text"
label="Success URL (optional)"
hint="Will be shown as a clickable link to the user in his wallet after a successful payment, appended by the payment_hash as a query string."
></q-input>
>
</q-input>
<div class="row q-mt-lg">
<q-btn
v-if="formDialog.data.id"
@@ -294,6 +299,14 @@
@click="copyText(qrCodeDialog.data.pay_url, 'Link copied to clipboard!')"
>Shareable link</q-btn
>
<q-btn
outline
color="grey"
icon="nfc"
@click="writeNfcTag(qrCodeDialog.data.lnurl)"
:disable="nfcTagWriting"
>
</q-btn>
<q-btn
outline
color="grey"
+2 -1
View File
@@ -73,6 +73,7 @@ async def api_link_retrieve(
@lnurlp_ext.put("/api/v1/links/{link_id}", status_code=HTTPStatus.OK)
async def api_link_create_or_update(
data: CreatePayLinkData,
request: Request,
link_id=None,
wallet: WalletTypeInfo = Depends(get_key_type),
):
@@ -117,7 +118,7 @@ async def api_link_create_or_update(
link = await update_pay_link(**data.dict(), link_id=link_id)
else:
link = await create_pay_link(data, wallet_id=wallet.wallet.id)
return {**link.dict(), "lnurl": link.lnurl}
return {**link.dict(), "lnurl": link.lnurl(request)}
@lnurlp_ext.delete("/api/v1/links/{link_id}")
@@ -1,4 +1,5 @@
import asyncio
from fastapi import APIRouter
from lnbits.db import Database
+1 -1
View File
@@ -3,7 +3,7 @@ from typing import List, Optional, Union
from lnbits.helpers import urlsafe_short_hash
from . import db
from .models import lnurlpayout, CreateLnurlPayoutData
from .models import CreateLnurlPayoutData, lnurlpayout
async def create_lnurlpayout(
-2
View File
@@ -2,9 +2,7 @@ import asyncio
from http import HTTPStatus
import httpx
from loguru import logger
from starlette.exceptions import HTTPException
from lnbits.core import db as core_db
+2 -1
View File
@@ -1,9 +1,10 @@
from typing import List, Optional
from lnbits.db import SQLITE
from . import db
from .models import Item, Shop
from .wordlists import animals
from .models import Shop, Item
async def create_shop(*, wallet_id: str) -> int:
+1 -1
View File
@@ -1,6 +1,6 @@
import base64
import struct
import hmac
import struct
import time
+4 -3
View File
@@ -1,14 +1,15 @@
import json
import base64
import hashlib
import json
from collections import OrderedDict
from typing import Dict, List, Optional
from typing import Optional, List, Dict
from lnurl import encode as lnurl_encode # type: ignore
from lnurl.types import LnurlPayMetadata # type: ignore
from lnurl.models import LnurlPaySuccessAction, UrlAction # type: ignore
from lnurl.types import LnurlPayMetadata # type: ignore
from pydantic import BaseModel
from starlette.requests import Request
from .helpers import totp
shop_counters: Dict = {}
+4 -4
View File
@@ -3,18 +3,18 @@ from datetime import datetime
from http import HTTPStatus
from typing import List
from fastapi import HTTPException, Request
from fastapi.params import Depends, Query
from starlette.responses import HTMLResponse
from lnbits.decorators import check_user_exists
from lnbits.core.models import Payment, User
from lnbits.core.crud import get_standalone_payment
from lnbits.core.models import Payment, User
from lnbits.core.views.api import api_payment
from lnbits.decorators import check_user_exists
from . import offlineshop_ext, offlineshop_renderer
from .models import Item
from .crud import get_item, get_shop
from fastapi import Request, HTTPException
from .models import Item
@offlineshop_ext.get("/", response_class=HTMLResponse)
+1 -1
View File
@@ -19,7 +19,7 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None:
if "charge" != payment.extra.get("tag"):
if payment.extra.get("tag") != "charge":
# not a charge invoice
return
@@ -14,3 +14,41 @@ async def m001_initial(db):
);
"""
)
async def m002_float_percent(db):
"""
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)
);
"""
)
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")
+2 -2
View File
@@ -7,14 +7,14 @@ from pydantic import BaseModel
class Target(BaseModel):
wallet: str
source: str
percent: int
percent: float
alias: Optional[str]
class TargetPutList(BaseModel):
wallet: str = Query(...)
alias: str = Query("")
percent: int = Query(..., ge=1)
percent: float = Query(..., ge=0.01)
class TargetPut(BaseModel):
@@ -105,7 +105,7 @@ new Vue({
if (currentTotal > 100 && isPercent) {
let diff = (currentTotal - 100) / (100 - this.targets[index].percent)
this.targets.forEach((target, t) => {
if (t !== index) target.percent -= Math.round(diff * target.percent)
if (t !== index) target.percent -= +(diff * target.percent).toFixed(2)
})
}
+1 -1
View File
@@ -22,7 +22,7 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None:
if "splitpayments" == payment.extra.get("tag") or payment.extra.get("splitted"):
if payment.extra.get("tag") == "splitpayments" or payment.extra.get("splitted"):
# already splitted, ignore
return
@@ -58,14 +58,14 @@
></q-input>
</div>
<q-row class="row justify-evenly q-pa-lg">
<q-col>
<div class="row justify-evenly q-pa-lg">
<div>
<q-btn unelevated outline color="secondary" @click="clearTargets">
Clear
</q-btn>
</q-col>
</div>
<q-col>
<div>
<q-btn
unelevated
color="primary"
@@ -74,8 +74,8 @@
>
Save Targets
</q-btn>
</q-col>
</q-row>
</div>
</div>
</q-form>
</q-card-section>
</q-card>
+4 -1
View File
@@ -1,5 +1,8 @@
import json
import httpx
from lnbits.extensions.subdomains.models import Domains
import httpx, json
async def cloudflare_create_subdomain(
+1 -1
View File
@@ -19,7 +19,7 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None:
if "lnsubdomain" != payment.extra.get("tag"):
if payment.extra.get("tag") != "lnsubdomain":
# not an lnurlp invoice
return
+1 -1
View File
@@ -16,8 +16,8 @@ def tpos_renderer():
from .tasks import wait_for_paid_invoices
from .views_api import * # noqa
from .views import * # noqa
from .views_api import * # noqa
def tpos_start():
+1 -1
View File
@@ -20,7 +20,7 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None:
if "tpos" == payment.extra.get("tag") and payment.extra.get("tipSplitted"):
if payment.extra.get("tag") == "tpos" and payment.extra.get("tipSplitted"):
# already splitted, ignore
return
+1 -4
View File
@@ -8,10 +8,7 @@ from starlette.responses import HTMLResponse
from lnbits.core.models import User
from lnbits.decorators import check_user_exists
from lnbits.settings import (
LNBITS_CUSTOM_LOGO,
LNBITS_SITE_TITLE,
)
from lnbits.settings import LNBITS_CUSTOM_LOGO, LNBITS_SITE_TITLE
from . import tpos_ext, tpos_renderer
from .crud import get_tpos
+1 -2
View File
@@ -2,9 +2,8 @@ from http import HTTPStatus
from fastapi import Query
from fastapi.params import Depends
from starlette.exceptions import HTTPException
from loguru import logger
from starlette.exceptions import HTTPException
from lnbits.core.crud import get_user
from lnbits.core.services import create_invoice
+1 -1
View File
@@ -1,8 +1,8 @@
from sqlite3 import Row
from typing import Optional
from fastapi.param_functions import Query
from pydantic import BaseModel
from typing import Optional
class CreateUserData(BaseModel):
+1 -1
View File
@@ -75,7 +75,7 @@ async def api_usermanager_activate_extension(
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="User does not exist."
)
update_user_extension(user_id=userid, extension=extension, active=active)
await update_user_extension(user_id=userid, extension=extension, active=active)
return {"extension": "updated"}
+2 -4
View File
@@ -1,15 +1,13 @@
import json
import traceback
import httpx
from datetime import datetime
from http import HTTPStatus
from loguru import logger
import httpx
import shortuuid # type: ignore
from fastapi import HTTPException
from fastapi.param_functions import Query
from loguru import logger
from starlette.requests import Request
from starlette.responses import HTMLResponse # type: ignore
@@ -53,6 +53,7 @@ new Vue({
rowsPerPage: 10
}
},
nfcTagWriting: false,
formDialog: {
show: false,
secondMultiplier: 'seconds',
@@ -231,6 +232,42 @@ new Vue({
})
})
},
writeNfcTag: async function (lnurl) {
try {
if (typeof NDEFReader == 'undefined') {
throw {
toString: function () {
return 'NFC not supported on this device or browser.'
}
}
}
const ndef = new NDEFReader()
this.nfcTagWriting = true
this.$q.notify({
message: 'Tap your NFC tag to write the LNURL-withdraw link to it.'
})
await ndef.write({
records: [{recordType: 'url', data: 'lightning:' + lnurl, lang: 'en'}]
})
this.nfcTagWriting = false
this.$q.notify({
type: 'positive',
message: 'NFC tag written successfully.'
})
} catch (error) {
this.nfcTagWriting = false
this.$q.notify({
type: 'negative',
message: error
? error.toString()
: 'An unexpected error has occurred.'
})
}
},
exportCSV: function () {
LNbits.utils.exportCSV(this.paywallsTable.columns, this.paywalls)
}
@@ -13,14 +13,22 @@
:value="this.here + '/?lightning={{lnurl }}'"
:options="{width: 800}"
class="rounded-borders"
></qrcode>
>
</qrcode>
</q-responsive>
</a>
</div>
<div class="row q-mt-lg">
<div class="row q-mt-lg q-gutter-sm">
<q-btn outline color="grey" @click="copyText('{{ lnurl }}')"
>Copy LNURL</q-btn
>
<q-btn
outline
color="grey"
icon="nfc"
@click="writeNfcTag(' {{ lnurl }} ')"
:disable="nfcTagWriting"
></q-btn>
</div>
</q-card-section>
</q-card>
@@ -51,7 +59,8 @@
mixins: [windowMixin],
data: function () {
return {
here: location.protocol + '//' + location.host
here: location.protocol + '//' + location.host,
nfcTagWriting: false
}
}
})
@@ -369,6 +369,13 @@
@click="copyText(qrCodeDialog.data.withdraw_url, 'Link copied to clipboard!')"
>Shareable link</q-btn
>
<q-btn
outline
color="grey"
icon="nfc"
@click="writeNfcTag(qrCodeDialog.data.lnurl)"
:disable="nfcTagWriting"
></q-btn>
<q-btn
outline
color="grey"
+3 -3
View File
@@ -1,11 +1,11 @@
from typing import Optional, List, Callable
from functools import partial
from urllib.request import parse_http_list as _parse_list_header
from typing import Callable, List, Optional
from urllib.parse import urlparse
from werkzeug.datastructures import Headers
from urllib.request import parse_http_list as _parse_list_header
from quart import Request
from quart_trio.asgi import TrioASGIHTTPConnection
from werkzeug.datastructures import Headers
class ASGIProxyFix(TrioASGIHTTPConnection):
+1
View File
@@ -51,6 +51,7 @@ LNBITS_THEME_OPTIONS: List[str] = env.list(
LNBITS_CUSTOM_LOGO = env.str("LNBITS_CUSTOM_LOGO", default="")
WALLET = wallet_class()
FAKE_WALLET = getattr(wallets_module, "FakeWallet")()
DEFAULT_WALLET_NAME = env.str("LNBITS_DEFAULT_WALLET_NAME", default="LNbits wallet")
PREFER_SECURE_URLS = env.bool("LNBITS_FORCE_HTTPS", default=True)
+6 -2
View File
@@ -392,7 +392,7 @@ window.windowMixin = {
}
if (window.extensions) {
var user = this.g.user
this.g.extensions = Object.freeze(
const extensions = Object.freeze(
window.extensions
.map(function (data) {
return window.LNbits.map.extension(data)
@@ -413,9 +413,13 @@ window.windowMixin = {
return obj
})
.sort(function (a, b) {
return a.name > b.name
const nameA = a.name.toUpperCase()
const nameB = b.name.toUpperCase()
return nameA < nameB ? -1 : nameA > nameB ? 1 : 0
})
)
this.g.extensions = extensions
}
}
}
+6 -8
View File
@@ -1,22 +1,20 @@
import time
import asyncio
import time
import traceback
from http import HTTPStatus
from typing import List, Callable
from loguru import logger
from typing import Callable, List
from fastapi.exceptions import HTTPException
from loguru import logger
from lnbits.settings import WALLET
from lnbits.core.crud import (
get_payments,
get_standalone_payment,
delete_expired_invoices,
get_balance_checks,
get_payments,
get_standalone_payment,
)
from lnbits.core.services import redeem_lnurl_withdraw
from lnbits.settings import WALLET
deferred_async: List[Callable] = []
-1
View File
@@ -228,7 +228,6 @@
<script type="text/javascript">
const themes = {{ LNBITS_THEME_OPTIONS | tojson }}
const LNBITS_DENOMINATION = {{ LNBITS_DENOMINATION | tojson}}
console.log(LNBITS_DENOMINATION)
if(themes && themes.length) {
window.allowedThemes = themes.map(str => str.trim())
}
+2 -3
View File
@@ -1,9 +1,8 @@
import asyncio
from typing import Callable, NamedTuple
from loguru import logger
import httpx
from loguru import logger
currencies = {
"AED": "United Arab Emirates Dirham",
@@ -282,7 +281,7 @@ async def btc_price(currency: str) -> float:
if not rates:
return 9999999999
elif len(rates) == 1:
logger.warn("Could only fetch one Bitcoin price.")
logger.warning("Could only fetch one Bitcoin price.")
return sum([rate for rate in rates]) / len(rates)
+1 -1
View File
@@ -1,5 +1,5 @@
from abc import ABC, abstractmethod
from typing import NamedTuple, Optional, AsyncGenerator, Coroutine
from typing import AsyncGenerator, Coroutine, NamedTuple, Optional
class StatusResponse(NamedTuple):
+3 -2
View File
@@ -5,10 +5,12 @@ except ImportError: # pragma: nocover
import asyncio
import random
import time
from functools import partial, wraps
from os import getenv
from typing import AsyncGenerator, Optional
import time
from lnbits import bolt11 as lnbits_bolt11
from .base import (
InvoiceResponse,
@@ -18,7 +20,6 @@ from .base import (
Unsupported,
Wallet,
)
from lnbits import bolt11 as lnbits_bolt11
def async_wrap(func):
+1 -2
View File
@@ -5,9 +5,8 @@ import urllib.parse
from os import getenv
from typing import AsyncGenerator, Dict, Optional
from loguru import logger
import httpx
from loguru import logger
from websockets import connect
from websockets.exceptions import (
ConnectionClosed,
+16 -10
View File
@@ -1,23 +1,27 @@
import asyncio
from os import getenv
from datetime import datetime
from typing import Optional, Dict, AsyncGenerator
import hashlib
import random
from datetime import datetime
from os import getenv
from typing import AsyncGenerator, Dict, Optional
from environs import Env # type: ignore
from loguru import logger
from lnbits.helpers import urlsafe_short_hash
import hashlib
from ..bolt11 import encode, decode
from ..bolt11 import decode, encode
from .base import (
StatusResponse,
InvoiceResponse,
PaymentResponse,
PaymentStatus,
StatusResponse,
Wallet,
)
env = Env()
env.read_env()
class FakeWallet(Wallet):
async def status(self) -> StatusResponse:
@@ -32,7 +36,9 @@ class FakeWallet(Wallet):
memo: Optional[str] = None,
description_hash: Optional[bytes] = None,
) -> InvoiceResponse:
secret = getenv("FAKE_WALLET_SECRET")
# we set a default secret since FakeWallet is used for internal=True invoices
# and the user might not have configured a secret yet
secret = env.str("FAKE_WALLET_SECTRET", default="ToTheMoon1")
data: Dict = {
"out": False,
"amount": amount,
@@ -85,10 +91,10 @@ class FakeWallet(Wallet):
)
async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
return PaymentStatus(False)
return PaymentStatus(None)
async def get_payment_status(self, checking_id: str) -> PaymentStatus:
return PaymentStatus(False)
return PaymentStatus(None)
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
self.queue = asyncio.Queue(0)
+3 -3
View File
@@ -1,16 +1,16 @@
import asyncio
import json
import httpx
from os import getenv
from typing import Optional, Dict, AsyncGenerator
from typing import AsyncGenerator, Dict, Optional
import httpx
from loguru import logger
from .base import (
StatusResponse,
InvoiceResponse,
PaymentResponse,
PaymentStatus,
StatusResponse,
Wallet,
)
@@ -2,11 +2,11 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: lightning.proto
"""Generated protocol buffer code."""
from google.protobuf.internal import enum_type_wrapper
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import enum_type_wrapper
# @@protoc_insertion_point(imports)
+5 -5
View File
@@ -1,30 +1,30 @@
imports_ok = True
try:
from google import protobuf
import grpc
from google import protobuf
except ImportError: # pragma: nocover
imports_ok = False
import binascii
import base64
import binascii
import hashlib
from os import environ, error, getenv
from typing import Optional, Dict, AsyncGenerator
from typing import AsyncGenerator, Dict, Optional
from loguru import logger
from .macaroon import load_macaroon, AESCipher
from .macaroon import AESCipher, load_macaroon
if imports_ok:
import lnbits.wallets.lnd_grpc_files.lightning_pb2 as ln
import lnbits.wallets.lnd_grpc_files.lightning_pb2_grpc as lnrpc
from .base import (
StatusResponse,
InvoiceResponse,
PaymentResponse,
PaymentStatus,
StatusResponse,
Wallet,
)
+6 -6
View File
@@ -1,23 +1,23 @@
import asyncio
from pydoc import describe
import httpx
import json
import base64
import json
from os import getenv
from typing import Optional, Dict, AsyncGenerator
from pydoc import describe
from typing import AsyncGenerator, Dict, Optional
import httpx
from loguru import logger
from lnbits import bolt11 as lnbits_bolt11
from .macaroon import load_macaroon, AESCipher
from .base import (
StatusResponse,
InvoiceResponse,
PaymentResponse,
PaymentStatus,
StatusResponse,
Wallet,
)
from .macaroon import AESCipher, load_macaroon
class LndRestWallet(Wallet):
+6 -6
View File
@@ -1,18 +1,18 @@
import json
import asyncio
from fastapi.exceptions import HTTPException
import httpx
from os import getenv
import json
from http import HTTPStatus
from typing import Optional, Dict, AsyncGenerator
from os import getenv
from typing import AsyncGenerator, Dict, Optional
import httpx
from fastapi.exceptions import HTTPException
from loguru import logger
from .base import (
StatusResponse,
InvoiceResponse,
PaymentResponse,
PaymentStatus,
StatusResponse,
Wallet,
)
+3 -3
View File
@@ -1,16 +1,16 @@
import asyncio
import json
import httpx
from os import getenv
from typing import Optional, Dict, AsyncGenerator
from typing import AsyncGenerator, Dict, Optional
import httpx
from loguru import logger
from .base import (
StatusResponse,
InvoiceResponse,
PaymentResponse,
PaymentStatus,
StatusResponse,
Wallet,
)
+1 -1
View File
@@ -1 +1 @@
from .macaroon import load_macaroon, AESCipher
from .macaroon import AESCipher, load_macaroon
+4 -4
View File
@@ -1,9 +1,9 @@
import base64
import getpass
from hashlib import md5
from Cryptodome import Random
from Cryptodome.Cipher import AES
import base64
from hashlib import md5
import getpass
from loguru import logger
BLOCK_SIZE = 16
+7 -7
View File
@@ -1,22 +1,22 @@
import asyncio
from fastapi.exceptions import HTTPException
from lnbits.helpers import url_for
import hmac
import httpx
from http import HTTPStatus
from os import getenv
from typing import Optional, AsyncGenerator
from typing import AsyncGenerator, Optional
import httpx
from fastapi.exceptions import HTTPException
from loguru import logger
from lnbits.helpers import url_for
from .base import (
StatusResponse,
InvoiceResponse,
PaymentResponse,
PaymentStatus,
Wallet,
StatusResponse,
Unsupported,
Wallet,
)
+19 -6
View File
@@ -1,17 +1,17 @@
import asyncio
import json
import httpx
import random
from os import getenv
from typing import Optional, AsyncGenerator
from typing import AsyncGenerator, Optional
import httpx
from loguru import logger
from .base import (
StatusResponse,
InvoiceResponse,
PaymentResponse,
PaymentStatus,
StatusResponse,
Wallet,
)
@@ -48,9 +48,16 @@ class SparkWallet(Wallet):
self.url + "/rpc",
headers={"X-Access": self.token},
json={"method": key, "params": params},
timeout=40,
timeout=60 * 60 * 24,
)
except (OSError, httpx.ConnectError, httpx.RequestError) as exc:
r.raise_for_status()
except (
OSError,
httpx.ConnectError,
httpx.RequestError,
httpx.HTTPError,
httpx.TimeoutException,
) as exc:
raise UnknownError("error connecting to spark: " + str(exc))
try:
@@ -203,7 +210,13 @@ class SparkWallet(Wallet):
data = json.loads(line[5:])
if "pay_index" in data and data.get("status") == "paid":
yield data["label"]
except (OSError, httpx.ReadError, httpx.ConnectError, httpx.ReadTimeout):
except (
OSError,
httpx.ReadError,
httpx.ConnectError,
httpx.ReadTimeout,
httpx.HTTPError,
):
pass
logger.error("lost connection to spark /stream, retrying in 5 seconds")