Logging with loguru (#708)
* logging * requirements * add loguru dependency * restore it * add loguru * set log level in .env file * remove service fee print * set log level * more logging * more logging * more logging * pyament.checking_id * fix
This commit is contained in:
parent
847fd18796
commit
089313f613
40 changed files with 202 additions and 81 deletions
|
|
@ -1,10 +1,8 @@
|
||||||
QUART_APP=lnbits.app:create_app()
|
|
||||||
QUART_ENV=development
|
|
||||||
QUART_DEBUG=true
|
|
||||||
|
|
||||||
HOST=127.0.0.1
|
HOST=127.0.0.1
|
||||||
PORT=5000
|
PORT=5000
|
||||||
|
|
||||||
|
DEBUG=true
|
||||||
|
|
||||||
LNBITS_ALLOWED_USERS=""
|
LNBITS_ALLOWED_USERS=""
|
||||||
LNBITS_ADMIN_USERS=""
|
LNBITS_ADMIN_USERS=""
|
||||||
# Extensions only admin can access
|
# Extensions only admin can access
|
||||||
|
|
|
||||||
1
Pipfile
1
Pipfile
|
|
@ -12,6 +12,7 @@ cerberus = "*"
|
||||||
ecdsa = "*"
|
ecdsa = "*"
|
||||||
environs = "*"
|
environs = "*"
|
||||||
lnurl = "==0.3.6"
|
lnurl = "==0.3.6"
|
||||||
|
loguru = "*"
|
||||||
pyscss = "*"
|
pyscss = "*"
|
||||||
shortuuid = "*"
|
shortuuid = "*"
|
||||||
typing-extensions = "*"
|
typing-extensions = "*"
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,19 @@ import asyncio
|
||||||
import uvloop
|
import uvloop
|
||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from .commands import bundle_vendored, migrate_databases, transpile_scss
|
from .commands import bundle_vendored, migrate_databases, transpile_scss
|
||||||
from .settings import (
|
from .settings import (
|
||||||
DEBUG,
|
DEBUG,
|
||||||
LNBITS_COMMIT,
|
LNBITS_COMMIT,
|
||||||
LNBITS_DATA_FOLDER,
|
LNBITS_DATA_FOLDER,
|
||||||
LNBITS_SITE_TITLE,
|
LNBITS_SITE_TITLE,
|
||||||
|
HOST,
|
||||||
PORT,
|
PORT,
|
||||||
SERVICE_FEE,
|
|
||||||
WALLET,
|
WALLET,
|
||||||
|
LNBITS_DATABASE_URL,
|
||||||
|
LNBITS_DATA_FOLDER,
|
||||||
)
|
)
|
||||||
|
|
||||||
uvloop.install()
|
uvloop.install()
|
||||||
|
|
@ -24,13 +28,15 @@ from .app import create_app
|
||||||
|
|
||||||
app = create_app()
|
app = create_app()
|
||||||
|
|
||||||
print(
|
logger.info("Starting LNbits")
|
||||||
f"""Starting LNbits with
|
logger.info(f"Host: {HOST}")
|
||||||
- git version: {LNBITS_COMMIT}
|
logger.info(f"Port: {PORT}")
|
||||||
- site title: {LNBITS_SITE_TITLE}
|
logger.info(f"Debug: {DEBUG}")
|
||||||
- debug: {DEBUG}
|
logger.info(f"Site title: {LNBITS_SITE_TITLE}")
|
||||||
- data folder: {LNBITS_DATA_FOLDER}
|
logger.info(f"Funding source: {WALLET.__class__.__name__}")
|
||||||
- funding source: {WALLET.__class__.__name__}
|
logger.info(
|
||||||
- service fee: {SERVICE_FEE}
|
f"Database: {'PostgreSQL' if LNBITS_DATABASE_URL.startswith('postgres://') else 'CockroachDB' if LNBITS_DATABASE_URL.startswith('cockroachdb://') else 'SQLite'}"
|
||||||
"""
|
|
||||||
)
|
)
|
||||||
|
logger.info(f"Data folder: {LNBITS_DATA_FOLDER}")
|
||||||
|
logger.info(f"Git version: {LNBITS_COMMIT}")
|
||||||
|
# logger.info(f"Service fee: {SERVICE_FEE}")
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@ import importlib
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
|
|
||||||
from fastapi import FastAPI, Request
|
from fastapi import FastAPI, Request
|
||||||
|
|
@ -41,6 +44,8 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
|
||||||
"""Create application factory.
|
"""Create application factory.
|
||||||
:param config_object: The configuration object to use.
|
:param config_object: The configuration object to use.
|
||||||
"""
|
"""
|
||||||
|
set_logging_level()
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
app.mount("/static", StaticFiles(directory="lnbits/static"), name="static")
|
app.mount("/static", StaticFiles(directory="lnbits/static"), name="static")
|
||||||
app.mount(
|
app.mount(
|
||||||
|
|
@ -94,14 +99,14 @@ def check_funding_source(app: FastAPI) -> None:
|
||||||
error_message, balance = await WALLET.status()
|
error_message, balance = await WALLET.status()
|
||||||
if not error_message:
|
if not error_message:
|
||||||
break
|
break
|
||||||
warnings.warn(
|
logger.error(
|
||||||
f" × The backend for {WALLET.__class__.__name__} isn't working properly: '{error_message}'",
|
f"The backend for {WALLET.__class__.__name__} isn't working properly: '{error_message}'",
|
||||||
RuntimeWarning,
|
RuntimeWarning,
|
||||||
)
|
)
|
||||||
print("Retrying connection to backend in 5 seconds...")
|
logger.info("Retrying connection to backend in 5 seconds...")
|
||||||
await asyncio.sleep(5)
|
await asyncio.sleep(5)
|
||||||
print(
|
logger.info(
|
||||||
f" ✔️ {WALLET.__class__.__name__} seems to be connected and with a balance of {balance} msat."
|
f"✔️ Backend {WALLET.__class__.__name__} connected and with a balance of {balance} msat."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -124,9 +129,10 @@ def register_routes(app: FastAPI) -> None:
|
||||||
for s in ext_statics:
|
for s in ext_statics:
|
||||||
app.mount(s["path"], s["app"], s["name"])
|
app.mount(s["path"], s["app"], s["name"])
|
||||||
|
|
||||||
|
logger.trace(f"adding route for extension {ext_module}")
|
||||||
app.include_router(ext_route)
|
app.include_router(ext_route)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(str(e))
|
logger.error(str(e))
|
||||||
raise ImportError(
|
raise ImportError(
|
||||||
f"Please make sure that the extension `{ext.code}` follows conventions."
|
f"Please make sure that the extension `{ext.code}` follows conventions."
|
||||||
)
|
)
|
||||||
|
|
@ -173,8 +179,8 @@ def register_async_tasks(app):
|
||||||
def register_exception_handlers(app: FastAPI):
|
def register_exception_handlers(app: FastAPI):
|
||||||
@app.exception_handler(Exception)
|
@app.exception_handler(Exception)
|
||||||
async def basic_error(request: Request, err):
|
async def basic_error(request: Request, err):
|
||||||
print("handled error", traceback.format_exc())
|
logger.error("handled error", traceback.format_exc())
|
||||||
print("ERROR:", err)
|
logger.error("ERROR:", err)
|
||||||
etype, _, tb = sys.exc_info()
|
etype, _, tb = sys.exc_info()
|
||||||
traceback.print_exception(etype, err, tb)
|
traceback.print_exception(etype, err, tb)
|
||||||
exc = traceback.format_exc()
|
exc = traceback.format_exc()
|
||||||
|
|
@ -188,3 +194,10 @@ def register_exception_handlers(app: FastAPI):
|
||||||
status_code=HTTPStatus.NO_CONTENT,
|
status_code=HTTPStatus.NO_CONTENT,
|
||||||
content={"detail": err},
|
content={"detail": err},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def set_logging_level() -> None:
|
||||||
|
"""Set the logging level for the application."""
|
||||||
|
logger.remove()
|
||||||
|
log_level: str = "DEBUG" if lnbits.settings.DEBUG else "INFO"
|
||||||
|
logger.add(sys.stderr, level=log_level)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import importlib
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from .db import SQLITE, POSTGRES, COCKROACH
|
from .db import SQLITE, POSTGRES, COCKROACH
|
||||||
from .core import db as core_db, migrations as core_migrations
|
from .core import db as core_db, migrations as core_migrations
|
||||||
from .helpers import (
|
from .helpers import (
|
||||||
|
|
@ -69,7 +71,7 @@ async def migrate_databases():
|
||||||
if match:
|
if match:
|
||||||
version = int(match.group(1))
|
version = int(match.group(1))
|
||||||
if version > current_versions.get(db_name, 0):
|
if version > current_versions.get(db_name, 0):
|
||||||
print(f"running migration {db_name}.{version}")
|
logger.debug(f"running migration {db_name}.{version}")
|
||||||
await migrate(db)
|
await migrate(db)
|
||||||
|
|
||||||
if db.schema == None:
|
if db.schema == None:
|
||||||
|
|
@ -110,4 +112,4 @@ async def migrate_databases():
|
||||||
async with ext_db.connect() as ext_conn:
|
async with ext_db.connect() as ext_conn:
|
||||||
await run_migration(ext_conn, ext_migrations)
|
await run_migration(ext_conn, ext_migrations)
|
||||||
|
|
||||||
print(" ✔️ All migrations done.")
|
logger.info("✔️ All migrations done.")
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@ from lnurl import encode as lnurl_encode # type: ignore
|
||||||
from typing import List, NamedTuple, Optional, Dict
|
from typing import List, NamedTuple, Optional, Dict
|
||||||
from sqlite3 import Row
|
from sqlite3 import Row
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from lnbits.settings import WALLET
|
from lnbits.settings import WALLET
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -142,10 +145,12 @@ class Payment(BaseModel):
|
||||||
status = await WALLET.get_invoice_status(self.checking_id)
|
status = await WALLET.get_invoice_status(self.checking_id)
|
||||||
|
|
||||||
if self.is_out and status.failed:
|
if self.is_out and status.failed:
|
||||||
print(f" - deleting outgoing failed payment {self.checking_id}: {status}")
|
logger.info(
|
||||||
|
f" - deleting outgoing failed payment {self.checking_id}: {status}"
|
||||||
|
)
|
||||||
await self.delete()
|
await self.delete()
|
||||||
elif not status.pending:
|
elif not status.pending:
|
||||||
print(
|
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.set_pending(status.pending)
|
await self.set_pending(status.pending)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@ import json
|
||||||
from binascii import unhexlify
|
from binascii import unhexlify
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import Dict, Optional, Tuple
|
from typing import Dict, Optional, Tuple
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from urllib.parse import parse_qs, urlparse
|
from urllib.parse import parse_qs, urlparse
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
@ -120,6 +123,7 @@ async def pay_invoice(
|
||||||
# check_internal() returns the checking_id of the invoice we're waiting for
|
# check_internal() returns the checking_id of the invoice we're waiting for
|
||||||
internal_checking_id = await check_internal(invoice.payment_hash, conn=conn)
|
internal_checking_id = await check_internal(invoice.payment_hash, conn=conn)
|
||||||
if internal_checking_id:
|
if internal_checking_id:
|
||||||
|
logger.debug(f"creating temporary internal payment with id {internal_id}")
|
||||||
# create a new payment from this wallet
|
# create a new payment from this wallet
|
||||||
await create_payment(
|
await create_payment(
|
||||||
checking_id=internal_id,
|
checking_id=internal_id,
|
||||||
|
|
@ -129,6 +133,7 @@ async def pay_invoice(
|
||||||
**payment_kwargs,
|
**payment_kwargs,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
logger.debug(f"creating temporary payment with id {temp_id}")
|
||||||
# create a temporary payment here so we can check if
|
# create a temporary payment here so we can check if
|
||||||
# the balance is enough in the next step
|
# the balance is enough in the next step
|
||||||
await create_payment(
|
await create_payment(
|
||||||
|
|
@ -142,6 +147,7 @@ async def pay_invoice(
|
||||||
wallet = await get_wallet(wallet_id, conn=conn)
|
wallet = await get_wallet(wallet_id, conn=conn)
|
||||||
assert wallet
|
assert wallet
|
||||||
if wallet.balance_msat < 0:
|
if wallet.balance_msat < 0:
|
||||||
|
logger.debug("balance is too low, deleting temporary payment")
|
||||||
if not internal_checking_id and wallet.balance_msat > -fee_reserve_msat:
|
if not internal_checking_id and wallet.balance_msat > -fee_reserve_msat:
|
||||||
raise PaymentFailure(
|
raise PaymentFailure(
|
||||||
f"You must reserve at least 1% ({round(fee_reserve_msat/1000)} sat) to cover potential routing fees."
|
f"You must reserve at least 1% ({round(fee_reserve_msat/1000)} sat) to cover potential routing fees."
|
||||||
|
|
@ -149,6 +155,7 @@ async def pay_invoice(
|
||||||
raise PermissionError("Insufficient balance.")
|
raise PermissionError("Insufficient balance.")
|
||||||
|
|
||||||
if internal_checking_id:
|
if internal_checking_id:
|
||||||
|
logger.debug(f"marking temporary payment as not pending {internal_checking_id}")
|
||||||
# mark the invoice from the other side as not pending anymore
|
# mark the invoice from the other side as not pending anymore
|
||||||
# so the other side only has access to his new money when we are sure
|
# so the other side only has access to his new money when we are sure
|
||||||
# the payer has enough to deduct from
|
# the payer has enough to deduct from
|
||||||
|
|
@ -163,11 +170,14 @@ async def pay_invoice(
|
||||||
|
|
||||||
await internal_invoice_queue.put(internal_checking_id)
|
await internal_invoice_queue.put(internal_checking_id)
|
||||||
else:
|
else:
|
||||||
|
logger.debug(f"backend: sending payment {temp_id}")
|
||||||
# actually pay the external invoice
|
# actually pay the external invoice
|
||||||
payment: PaymentResponse = await WALLET.pay_invoice(
|
payment: PaymentResponse = await WALLET.pay_invoice(
|
||||||
payment_request, fee_reserve_msat
|
payment_request, fee_reserve_msat
|
||||||
)
|
)
|
||||||
|
logger.debug(f"backend: pay_invoice finished {temp_id}")
|
||||||
if payment.checking_id:
|
if payment.checking_id:
|
||||||
|
logger.debug(f"creating final payment {payment.checking_id}")
|
||||||
async with db.connect() as conn:
|
async with db.connect() as conn:
|
||||||
await create_payment(
|
await create_payment(
|
||||||
checking_id=payment.checking_id,
|
checking_id=payment.checking_id,
|
||||||
|
|
@ -177,15 +187,18 @@ async def pay_invoice(
|
||||||
conn=conn,
|
conn=conn,
|
||||||
**payment_kwargs,
|
**payment_kwargs,
|
||||||
)
|
)
|
||||||
|
logger.debug(f"deleting temporary payment {temp_id}")
|
||||||
await delete_payment(temp_id, conn=conn)
|
await delete_payment(temp_id, conn=conn)
|
||||||
else:
|
else:
|
||||||
|
logger.debug(f"backend payment failed, no checking_id {temp_id}")
|
||||||
async with db.connect() as conn:
|
async with db.connect() as conn:
|
||||||
|
logger.debug(f"deleting temporary payment {temp_id}")
|
||||||
await delete_payment(temp_id, conn=conn)
|
await delete_payment(temp_id, conn=conn)
|
||||||
raise PaymentFailure(
|
raise PaymentFailure(
|
||||||
payment.error_message
|
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."
|
||||||
)
|
)
|
||||||
|
logger.debug(f"payment successful {payment.checking_id}")
|
||||||
return invoice.payment_hash
|
return invoice.payment_hash
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -216,7 +229,7 @@ async def redeem_lnurl_withdraw(
|
||||||
conn=conn,
|
conn=conn,
|
||||||
)
|
)
|
||||||
except:
|
except:
|
||||||
print(
|
logger.warn(
|
||||||
f"failed to create invoice on redeem_lnurl_withdraw from {lnurl}. params: {res}"
|
f"failed to create invoice on redeem_lnurl_withdraw from {lnurl}. params: {res}"
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
|
@ -325,11 +338,11 @@ async def check_invoice_status(
|
||||||
if not payment.pending:
|
if not payment.pending:
|
||||||
return status
|
return status
|
||||||
if payment.is_out and status.failed:
|
if payment.is_out and status.failed:
|
||||||
print(f" - deleting outgoing failed payment {payment.checking_id}: {status}")
|
logger.info(f"deleting outgoing failed payment {payment.checking_id}: {status}")
|
||||||
await payment.delete()
|
await payment.delete()
|
||||||
elif not status.pending:
|
elif not status.pending:
|
||||||
print(
|
logger.info(
|
||||||
f" - marking '{'in' if payment.is_in else 'out'}' {payment.checking_id} as not pending anymore: {status}"
|
f"marking '{'in' if payment.is_in else 'out'}' {payment.checking_id} as not pending anymore: {status}"
|
||||||
)
|
)
|
||||||
await payment.set_pending(status.pending)
|
await payment.set_pending(status.pending)
|
||||||
return status
|
return status
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ import asyncio
|
||||||
import httpx
|
import httpx
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from lnbits.tasks import register_invoice_listener
|
from lnbits.tasks import register_invoice_listener
|
||||||
|
|
||||||
from . import db
|
from . import db
|
||||||
|
|
@ -20,7 +22,7 @@ async def register_task_listeners():
|
||||||
async def wait_for_paid_invoices(invoice_paid_queue: asyncio.Queue):
|
async def wait_for_paid_invoices(invoice_paid_queue: asyncio.Queue):
|
||||||
while True:
|
while True:
|
||||||
payment = await invoice_paid_queue.get()
|
payment = await invoice_paid_queue.get()
|
||||||
|
logger.debug("received invoice paid event")
|
||||||
# send information to sse channel
|
# send information to sse channel
|
||||||
await dispatch_invoice_listener(payment)
|
await dispatch_invoice_listener(payment)
|
||||||
|
|
||||||
|
|
@ -44,7 +46,7 @@ async def dispatch_invoice_listener(payment: Payment):
|
||||||
try:
|
try:
|
||||||
send_channel.put_nowait(payment)
|
send_channel.put_nowait(payment)
|
||||||
except asyncio.QueueFull:
|
except asyncio.QueueFull:
|
||||||
print("removing sse listener", send_channel)
|
logger.debug("removing sse listener", send_channel)
|
||||||
api_invoice_listeners.remove(send_channel)
|
api_invoice_listeners.remove(send_channel)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -52,6 +54,7 @@ async def dispatch_webhook(payment: Payment):
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
data = payment.dict()
|
data = payment.dict()
|
||||||
try:
|
try:
|
||||||
|
logger.debug("sending webhook", payment.webhook)
|
||||||
r = await client.post(payment.webhook, json=data, timeout=40)
|
r = await client.post(payment.webhook, json=data, timeout=40)
|
||||||
await mark_webhook_sent(payment, r.status_code)
|
await mark_webhook_sent(payment, r.status_code)
|
||||||
except (httpx.ConnectError, httpx.RequestError):
|
except (httpx.ConnectError, httpx.RequestError):
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@ from typing import Dict, List, Optional, Union
|
||||||
from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse
|
from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from fastapi import Header, Query, Request
|
from fastapi import Header, Query, Request
|
||||||
from fastapi.exceptions import HTTPException
|
from fastapi.exceptions import HTTPException
|
||||||
from fastapi.param_functions import Depends
|
from fastapi.param_functions import Depends
|
||||||
|
|
@ -347,7 +350,7 @@ async def subscribe(request: Request, wallet: Wallet):
|
||||||
|
|
||||||
payment_queue: asyncio.Queue[Payment] = asyncio.Queue(0)
|
payment_queue: asyncio.Queue[Payment] = asyncio.Queue(0)
|
||||||
|
|
||||||
print("adding sse listener", payment_queue)
|
logger.debug("adding sse listener", payment_queue)
|
||||||
api_invoice_listeners.append(payment_queue)
|
api_invoice_listeners.append(payment_queue)
|
||||||
|
|
||||||
send_queue: asyncio.Queue[tuple[str, Payment]] = asyncio.Queue(0)
|
send_queue: asyncio.Queue[tuple[str, Payment]] = asyncio.Queue(0)
|
||||||
|
|
@ -356,6 +359,7 @@ async def subscribe(request: Request, wallet: Wallet):
|
||||||
while True:
|
while True:
|
||||||
payment: Payment = await payment_queue.get()
|
payment: Payment = await payment_queue.get()
|
||||||
if payment.wallet_id == this_wallet_id:
|
if payment.wallet_id == this_wallet_id:
|
||||||
|
logger.debug("payment receieved", payment)
|
||||||
await send_queue.put(("payment-received", payment))
|
await send_queue.put(("payment-received", payment))
|
||||||
|
|
||||||
asyncio.create_task(payment_received())
|
asyncio.create_task(payment_received())
|
||||||
|
|
@ -391,7 +395,7 @@ async def api_payment(payment_hash, X_Api_Key: Optional[str] = Header(None)):
|
||||||
wallet = None
|
wallet = None
|
||||||
try:
|
try:
|
||||||
if X_Api_Key.extra:
|
if X_Api_Key.extra:
|
||||||
print("No key")
|
logger.warn("No key")
|
||||||
except:
|
except:
|
||||||
wallet = await get_wallet_for_key(X_Api_Key)
|
wallet = await get_wallet_for_key(X_Api_Key)
|
||||||
payment = await get_standalone_payment(payment_hash)
|
payment = await get_standalone_payment(payment_hash)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ from fastapi.routing import APIRouter
|
||||||
from pydantic.types import UUID4
|
from pydantic.types import UUID4
|
||||||
from starlette.responses import HTMLResponse, JSONResponse
|
from starlette.responses import HTMLResponse, JSONResponse
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from lnbits.core import db
|
from lnbits.core import db
|
||||||
from lnbits.core.models import User
|
from lnbits.core.models import User
|
||||||
from lnbits.decorators import check_user_exists
|
from lnbits.decorators import check_user_exists
|
||||||
|
|
@ -66,10 +68,12 @@ async def extensions(
|
||||||
)
|
)
|
||||||
|
|
||||||
if extension_to_enable:
|
if extension_to_enable:
|
||||||
|
logger.info(f"Enabling extension: {extension_to_enable} for user {user.id}")
|
||||||
await update_user_extension(
|
await update_user_extension(
|
||||||
user_id=user.id, extension=extension_to_enable, active=True
|
user_id=user.id, extension=extension_to_enable, active=True
|
||||||
)
|
)
|
||||||
elif extension_to_disable:
|
elif extension_to_disable:
|
||||||
|
logger.info(f"Disabling extension: {extension_to_disable} for user {user.id}")
|
||||||
await update_user_extension(
|
await update_user_extension(
|
||||||
user_id=user.id, extension=extension_to_disable, active=False
|
user_id=user.id, extension=extension_to_disable, active=False
|
||||||
)
|
)
|
||||||
|
|
@ -109,6 +113,7 @@ async def wallet(
|
||||||
|
|
||||||
if not user_id:
|
if not user_id:
|
||||||
user = await get_user((await create_account()).id)
|
user = await get_user((await create_account()).id)
|
||||||
|
logger.info(f"Created new account for user {user.id}")
|
||||||
else:
|
else:
|
||||||
user = await get_user(user_id)
|
user = await get_user(user_id)
|
||||||
if not user:
|
if not user:
|
||||||
|
|
@ -126,12 +131,16 @@ async def wallet(
|
||||||
wallet = user.wallets[0]
|
wallet = user.wallets[0]
|
||||||
else:
|
else:
|
||||||
wallet = await create_wallet(user_id=user.id, wallet_name=wallet_name)
|
wallet = await create_wallet(user_id=user.id, wallet_name=wallet_name)
|
||||||
|
logger.info(
|
||||||
|
f"Created new wallet {wallet_name if wallet_name else '(no name)'} for user {user.id}"
|
||||||
|
)
|
||||||
|
|
||||||
return RedirectResponse(
|
return RedirectResponse(
|
||||||
f"/wallet?usr={user.id}&wal={wallet.id}",
|
f"/wallet?usr={user.id}&wal={wallet.id}",
|
||||||
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
|
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
logger.info(f"Access wallet {wallet_name} of user {user.id}")
|
||||||
wallet = user.get_wallet(wallet_id)
|
wallet = user.get_wallet(wallet_id)
|
||||||
if not wallet:
|
if not wallet:
|
||||||
return template_renderer().TemplateResponse(
|
return template_renderer().TemplateResponse(
|
||||||
|
|
@ -202,13 +211,13 @@ async def lnurl_full_withdraw_callback(request: Request):
|
||||||
async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)):
|
async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)):
|
||||||
user = await get_user(usr)
|
user = await get_user(usr)
|
||||||
user_wallet_ids = [u.id for u in user.wallets]
|
user_wallet_ids = [u.id for u in user.wallets]
|
||||||
print("USR", user_wallet_ids)
|
|
||||||
|
|
||||||
if wal not in user_wallet_ids:
|
if wal not in user_wallet_ids:
|
||||||
raise HTTPException(HTTPStatus.FORBIDDEN, "Not your wallet.")
|
raise HTTPException(HTTPStatus.FORBIDDEN, "Not your wallet.")
|
||||||
else:
|
else:
|
||||||
await delete_wallet(user_id=user.id, wallet_id=wal)
|
await delete_wallet(user_id=user.id, wallet_id=wal)
|
||||||
user_wallet_ids.remove(wal)
|
user_wallet_ids.remove(wal)
|
||||||
|
logger.debug("Deleted wallet {wal} of user {user.id}")
|
||||||
|
|
||||||
if user_wallet_ids:
|
if user_wallet_ids:
|
||||||
return RedirectResponse(
|
return RedirectResponse(
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ from fastapi import HTTPException
|
||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
from starlette.responses import HTMLResponse
|
from starlette.responses import HTMLResponse
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from lnbits import bolt11
|
from lnbits import bolt11
|
||||||
|
|
||||||
from .. import core_app
|
from .. import core_app
|
||||||
|
|
@ -45,7 +47,7 @@ async def api_public_payment_longpolling(payment_hash):
|
||||||
|
|
||||||
payment_queue = asyncio.Queue(0)
|
payment_queue = asyncio.Queue(0)
|
||||||
|
|
||||||
print("adding standalone invoice listener", payment_hash, payment_queue)
|
logger.debug("adding standalone invoice listener", payment_hash, payment_queue)
|
||||||
api_invoice_listeners.append(payment_queue)
|
api_invoice_listeners.append(payment_queue)
|
||||||
|
|
||||||
response = None
|
response = None
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import time
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
from sqlalchemy_aio.base import AsyncConnection
|
from sqlalchemy_aio.base import AsyncConnection
|
||||||
from sqlalchemy_aio.strategy import ASYNCIO_STRATEGY # type: ignore
|
from sqlalchemy_aio.strategy import ASYNCIO_STRATEGY # type: ignore
|
||||||
|
|
@ -139,7 +141,7 @@ class Database(Compat):
|
||||||
f"LNBITS_DATA_FOLDER named {LNBITS_DATA_FOLDER} was not created"
|
f"LNBITS_DATA_FOLDER named {LNBITS_DATA_FOLDER} was not created"
|
||||||
f" - please 'mkdir {LNBITS_DATA_FOLDER}' and try again"
|
f" - please 'mkdir {LNBITS_DATA_FOLDER}' and try again"
|
||||||
)
|
)
|
||||||
|
logger.trace(f"database {self.type} added for {self.name}")
|
||||||
self.schema = self.name
|
self.schema = self.name
|
||||||
if self.name.startswith("ext_"):
|
if self.name.startswith("ext_"):
|
||||||
self.schema = self.name[4:]
|
self.schema = self.name[4:]
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ from http import HTTPStatus
|
||||||
|
|
||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from . import bleskomat_ext
|
from . import bleskomat_ext
|
||||||
from .crud import (
|
from .crud import (
|
||||||
create_bleskomat_lnurl,
|
create_bleskomat_lnurl,
|
||||||
|
|
@ -122,7 +124,7 @@ async def api_bleskomat_lnurl(req: Request):
|
||||||
except LnurlHttpError as e:
|
except LnurlHttpError as e:
|
||||||
return {"status": "ERROR", "reason": str(e)}
|
return {"status": "ERROR", "reason": str(e)}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(str(e))
|
logger.error(str(e))
|
||||||
return {"status": "ERROR", "reason": "Unexpected error"}
|
return {"status": "ERROR", "reason": "Unexpected error"}
|
||||||
|
|
||||||
return {"status": "OK"}
|
return {"status": "OK"}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ from fastapi.params import Query
|
||||||
from pydantic import BaseModel, validator
|
from pydantic import BaseModel, validator
|
||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from lnbits import bolt11
|
from lnbits import bolt11
|
||||||
from lnbits.core.services import pay_invoice, PaymentFailure
|
from lnbits.core.services import pay_invoice, PaymentFailure
|
||||||
|
|
||||||
|
|
@ -125,7 +127,7 @@ class BleskomatLnurl(BaseModel):
|
||||||
except (ValueError, PermissionError, PaymentFailure) as e:
|
except (ValueError, PermissionError, PaymentFailure) as e:
|
||||||
raise LnurlValidationError("Failed to pay invoice: " + str(e))
|
raise LnurlValidationError("Failed to pay invoice: " + str(e))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(str(e))
|
logger.error(str(e))
|
||||||
raise LnurlValidationError("Unexpected error")
|
raise LnurlValidationError("Unexpected error")
|
||||||
|
|
||||||
async def use(self, conn) -> bool:
|
async def use(self, conn) -> bool:
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ from http import HTTPStatus
|
||||||
from fastapi import Depends, Query
|
from fastapi import Depends, Query
|
||||||
from starlette.exceptions import HTTPException
|
from starlette.exceptions import HTTPException
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from lnbits.core.crud import get_user
|
from lnbits.core.crud import get_user
|
||||||
from lnbits.decorators import WalletTypeInfo, require_admin_key
|
from lnbits.decorators import WalletTypeInfo, require_admin_key
|
||||||
from lnbits.extensions.bleskomat.models import CreateBleskomat
|
from lnbits.extensions.bleskomat.models import CreateBleskomat
|
||||||
|
|
@ -60,7 +62,7 @@ async def api_bleskomat_create_or_update(
|
||||||
currency=fiat_currency, provider=exchange_rate_provider
|
currency=fiat_currency, provider=exchange_rate_provider
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
logger.error(e)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||||
detail=f'Failed to fetch BTC/{fiat_currency} currency pair from "{exchange_rate_provider}"',
|
detail=f'Failed to fetch BTC/{fiat_currency} currency pair from "{exchange_rate_provider}"',
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from lnbits.core import db as core_db
|
from lnbits.core import db as core_db
|
||||||
from lnbits.core.crud import create_payment
|
from lnbits.core.crud import create_payment
|
||||||
from lnbits.core.models import Payment
|
from lnbits.core.models import Payment
|
||||||
|
|
@ -26,11 +28,11 @@ async def on_invoice_paid(payment: Payment) -> None:
|
||||||
|
|
||||||
track = await get_track(payment.extra.get("track", -1))
|
track = await get_track(payment.extra.get("track", -1))
|
||||||
if not track:
|
if not track:
|
||||||
print("this should never happen", payment)
|
logger.error("this should never happen", payment)
|
||||||
return
|
return
|
||||||
|
|
||||||
if payment.extra.get("shared_with"):
|
if payment.extra.get("shared_with"):
|
||||||
print("payment was shared already", payment)
|
logger.error("payment was shared already", payment)
|
||||||
return
|
return
|
||||||
|
|
||||||
producer = await get_producer(track.producer)
|
producer = await get_producer(track.producer)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import List, Optional, Union
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from lnbits.helpers import urlsafe_short_hash
|
from lnbits.helpers import urlsafe_short_hash
|
||||||
|
|
||||||
from . import db
|
from . import db
|
||||||
|
|
@ -186,9 +188,9 @@ async def purge_addresses(domain_id: str):
|
||||||
) # give user 1 day to topup is address
|
) # give user 1 day to topup is address
|
||||||
|
|
||||||
if not paid and pay_expire:
|
if not paid and pay_expire:
|
||||||
print("DELETE UNP_PAY_EXP", r["username"])
|
logger.debug("DELETE UNP_PAY_EXP", r["username"])
|
||||||
await delete_address(r["id"])
|
await delete_address(r["id"])
|
||||||
|
|
||||||
if paid and expired:
|
if paid and expired:
|
||||||
print("DELETE PAID_EXP", r["username"])
|
logger.debug("DELETE PAID_EXP", r["username"])
|
||||||
await delete_address(r["id"])
|
await delete_address(r["id"])
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@ import json
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from fastapi.params import Query
|
from fastapi.params import Query
|
||||||
from lnurl import ( # type: ignore
|
from lnurl import ( # type: ignore
|
||||||
LnurlErrorResponse,
|
LnurlErrorResponse,
|
||||||
|
|
@ -38,13 +41,12 @@ async def lnurl_response(username: str, domain: str, request: Request):
|
||||||
"maxSendable": 1000000000,
|
"maxSendable": 1000000000,
|
||||||
}
|
}
|
||||||
|
|
||||||
print("RESP", resp)
|
logger.debug("RESP", resp)
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
|
||||||
@lnaddress_ext.get("/lnurl/cb/{address_id}", name="lnaddress.lnurl_callback")
|
@lnaddress_ext.get("/lnurl/cb/{address_id}", name="lnaddress.lnurl_callback")
|
||||||
async def lnurl_callback(address_id, amount: int = Query(...)):
|
async def lnurl_callback(address_id, amount: int = Query(...)):
|
||||||
print("PING")
|
|
||||||
address = await get_address(address_id)
|
address = await get_address(address_id)
|
||||||
if not address:
|
if not address:
|
||||||
return LnurlErrorResponse(reason=f"Address not found").dict()
|
return LnurlErrorResponse(reason=f"Address not found").dict()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from lnbits.core.models import Payment
|
from lnbits.core.models import Payment
|
||||||
from lnbits.tasks import register_invoice_listener
|
from lnbits.tasks import register_invoice_listener
|
||||||
|
|
||||||
|
|
@ -22,7 +24,7 @@ async def on_invoice_paid(payment: Payment) -> None:
|
||||||
|
|
||||||
ticket = await get_ticket(payment.checking_id)
|
ticket = await get_ticket(payment.checking_id)
|
||||||
if not ticket:
|
if not ticket:
|
||||||
print("this should never happen", payment)
|
logger.error("this should never happen", payment)
|
||||||
return
|
return
|
||||||
|
|
||||||
await payment.set_pending(False)
|
await payment.set_pending(False)
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ async def lnurl_v1_params(
|
||||||
"defaultDescription": device.title,
|
"defaultDescription": device.title,
|
||||||
}
|
}
|
||||||
price_msat = int(price_msat * ((device.profit / 100) + 1) / 1000)
|
price_msat = int(price_msat * ((device.profit / 100) + 1) / 1000)
|
||||||
print(price_msat)
|
|
||||||
lnurldevicepayment = await create_lnurldevicepayment(
|
lnurldevicepayment = await create_lnurldevicepayment(
|
||||||
deviceid=device.id,
|
deviceid=device.id,
|
||||||
payload=p,
|
payload=p,
|
||||||
|
|
@ -204,7 +204,7 @@ async def lnurl_callback(
|
||||||
extra={"tag": "withdraw"},
|
extra={"tag": "withdraw"},
|
||||||
)
|
)
|
||||||
return {"status": "OK"}
|
return {"status": "OK"}
|
||||||
print(lnurldevicepayment.sats)
|
|
||||||
payment_hash, payment_request = await create_invoice(
|
payment_hash, payment_request = await create_invoice(
|
||||||
wallet_id=device.wallet,
|
wallet_id=device.wallet,
|
||||||
amount=lnurldevicepayment.sats / 1000,
|
amount=lnurldevicepayment.sats / 1000,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,9 @@ import asyncio
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from starlette.exceptions import HTTPException
|
from starlette.exceptions import HTTPException
|
||||||
|
|
||||||
from lnbits.core import db as core_db
|
from lnbits.core import db as core_db
|
||||||
|
|
@ -27,7 +30,7 @@ async def on_invoice_paid(payment: Payment) -> None:
|
||||||
try:
|
try:
|
||||||
# Check its got a payout associated with it
|
# Check its got a payout associated with it
|
||||||
lnurlpayout_link = await get_lnurlpayout_from_wallet(payment.wallet_id)
|
lnurlpayout_link = await get_lnurlpayout_from_wallet(payment.wallet_id)
|
||||||
print("LNURLpayout", lnurlpayout_link)
|
logger.debug("LNURLpayout", lnurlpayout_link)
|
||||||
if lnurlpayout_link:
|
if lnurlpayout_link:
|
||||||
|
|
||||||
# Check the wallet balance is more than the threshold
|
# Check the wallet balance is more than the threshold
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from lnbits.core.models import Payment
|
from lnbits.core.models import Payment
|
||||||
from lnbits.extensions.satspay.crud import check_address_balance, get_charge
|
from lnbits.extensions.satspay.crud import check_address_balance, get_charge
|
||||||
from lnbits.tasks import register_invoice_listener
|
from lnbits.tasks import register_invoice_listener
|
||||||
|
|
@ -23,7 +25,7 @@ async def on_invoice_paid(payment: Payment) -> None:
|
||||||
|
|
||||||
charge = await get_charge(payment.memo)
|
charge = await get_charge(payment.memo)
|
||||||
if not charge:
|
if not charge:
|
||||||
print("this should never happen", payment)
|
logger.error("this should never happen", payment)
|
||||||
return
|
return
|
||||||
|
|
||||||
await payment.set_pending(False)
|
await payment.set_pending(False)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from lnbits.core import db as core_db
|
from lnbits.core import db as core_db
|
||||||
from lnbits.core.crud import create_payment
|
from lnbits.core.crud import create_payment
|
||||||
from lnbits.core.models import Payment
|
from lnbits.core.models import Payment
|
||||||
|
|
@ -34,7 +36,9 @@ async def on_invoice_paid(payment: Payment) -> None:
|
||||||
amount_left = payment.amount - sum([amount for _, amount in transfers])
|
amount_left = payment.amount - sum([amount for _, amount in transfers])
|
||||||
|
|
||||||
if amount_left < 0:
|
if amount_left < 0:
|
||||||
print("splitpayments failure: amount_left is negative.", payment.payment_hash)
|
logger.error(
|
||||||
|
"splitpayments failure: amount_left is negative.", payment.payment_hash
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not targets:
|
if not targets:
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ from fastapi import Query
|
||||||
from fastapi.params import Depends
|
from fastapi.params import Depends
|
||||||
from starlette.exceptions import HTTPException
|
from starlette.exceptions import HTTPException
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from lnbits.core.crud import get_user
|
from lnbits.core.crud import get_user
|
||||||
from lnbits.core.services import create_invoice
|
from lnbits.core.services import create_invoice
|
||||||
from lnbits.core.views.api import api_payment
|
from lnbits.core.views.api import api_payment
|
||||||
|
|
@ -88,6 +90,6 @@ async def api_tpos_check_invoice(tpos_id: str, payment_hash: str):
|
||||||
status = await api_payment(payment_hash)
|
status = await api_payment(payment_hash)
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print(exc)
|
logger.error(exc)
|
||||||
return {"paid": False}
|
return {"paid": False}
|
||||||
return status
|
return status
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import httpx
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
import shortuuid # type: ignore
|
import shortuuid # type: ignore
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from fastapi.param_functions import Query
|
from fastapi.param_functions import Query
|
||||||
|
|
@ -136,13 +138,13 @@ async def api_lnurl_callback(
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
# webhook fails shouldn't cause the lnurlw to fail since invoice is already paid
|
# webhook fails shouldn't cause the lnurlw to fail since invoice is already paid
|
||||||
print("Caught exception when dispatching webhook url:", exc)
|
logger.error("Caught exception when dispatching webhook url:", exc)
|
||||||
|
|
||||||
return {"status": "OK"}
|
return {"status": "OK"}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await update_withdraw_link(link.id, **changesback)
|
await update_withdraw_link(link.id, **changesback)
|
||||||
print(traceback.format_exc())
|
logger.error(traceback.format_exc())
|
||||||
return {"status": "ERROR", "reason": "Link not working"}
|
return {"status": "ERROR", "reason": "Link not working"}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ wallet_class = getattr(
|
||||||
wallets_module, env.str("LNBITS_BACKEND_WALLET_CLASS", default="VoidWallet")
|
wallets_module, env.str("LNBITS_BACKEND_WALLET_CLASS", default="VoidWallet")
|
||||||
)
|
)
|
||||||
|
|
||||||
ENV = env.str("QUART_ENV", default="production")
|
DEBUG = env.bool("DEBUG", default=False)
|
||||||
DEBUG = env.bool("QUART_DEBUG", default=False) or ENV == "development"
|
|
||||||
HOST = env.str("HOST", default="127.0.0.1")
|
HOST = env.str("HOST", default="127.0.0.1")
|
||||||
PORT = env.int("PORT", default=5000)
|
PORT = env.int("PORT", default=5000)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import traceback
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from typing import List, Callable
|
from typing import List, Callable
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from fastapi.exceptions import HTTPException
|
from fastapi.exceptions import HTTPException
|
||||||
|
|
||||||
from lnbits.settings import WALLET
|
from lnbits.settings import WALLET
|
||||||
|
|
@ -37,9 +39,9 @@ async def catch_everything_and_restart(func):
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
raise # because we must pass this up
|
raise # because we must pass this up
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print("caught exception in background task:", exc)
|
logger.error("caught exception in background task:", exc)
|
||||||
print(traceback.format_exc())
|
logger.error(traceback.format_exc())
|
||||||
print("will restart the task in 5 seconds.")
|
logger.error("will restart the task in 5 seconds.")
|
||||||
await asyncio.sleep(5)
|
await asyncio.sleep(5)
|
||||||
await catch_everything_and_restart(func)
|
await catch_everything_and_restart(func)
|
||||||
|
|
||||||
|
|
@ -77,7 +79,7 @@ async def internal_invoice_listener():
|
||||||
|
|
||||||
async def invoice_listener():
|
async def invoice_listener():
|
||||||
async for checking_id in WALLET.paid_invoices_stream():
|
async for checking_id in WALLET.paid_invoices_stream():
|
||||||
print("> got a payment notification", checking_id)
|
logger.info("> got a payment notification", checking_id)
|
||||||
asyncio.create_task(invoice_callback_dispatcher(checking_id))
|
asyncio.create_task(invoice_callback_dispatcher(checking_id))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -117,6 +119,7 @@ async def perform_balance_checks():
|
||||||
async def invoice_callback_dispatcher(checking_id: str):
|
async def invoice_callback_dispatcher(checking_id: str):
|
||||||
payment = await get_standalone_payment(checking_id, incoming=True)
|
payment = await get_standalone_payment(checking_id, incoming=True)
|
||||||
if payment and payment.is_in:
|
if payment and payment.is_in:
|
||||||
|
logger.trace("sending invoice callback for payment", checking_id)
|
||||||
await payment.set_pending(False)
|
await payment.set_pending(False)
|
||||||
for send_chan in invoice_listeners:
|
for send_chan in invoice_listeners:
|
||||||
await send_chan.put(payment)
|
await send_chan.put(payment)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import Callable, NamedTuple
|
from typing import Callable, NamedTuple
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
currencies = {
|
currencies = {
|
||||||
|
|
@ -280,7 +282,7 @@ async def btc_price(currency: str) -> float:
|
||||||
if not rates:
|
if not rates:
|
||||||
return 9999999999
|
return 9999999999
|
||||||
elif len(rates) == 1:
|
elif len(rates) == 1:
|
||||||
print("Warning could only fetch one Bitcoin price.")
|
logger.warn("Could only fetch one Bitcoin price.")
|
||||||
|
|
||||||
return sum([rate for rate in rates]) / len(rates)
|
return sum([rate for rate in rates]) / len(rates)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import urllib.parse
|
||||||
from os import getenv
|
from os import getenv
|
||||||
from typing import AsyncGenerator, Dict, Optional
|
from typing import AsyncGenerator, Dict, Optional
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from websockets import connect
|
from websockets import connect
|
||||||
from websockets.exceptions import (
|
from websockets.exceptions import (
|
||||||
|
|
@ -193,8 +195,8 @@ class EclairWallet(Wallet):
|
||||||
ConnectionClosedError,
|
ConnectionClosedError,
|
||||||
ConnectionClosed,
|
ConnectionClosed,
|
||||||
) as ose:
|
) as ose:
|
||||||
print("OSE", ose)
|
logger.error("OSE", ose)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
print("lost connection to eclair's websocket, retrying in 5 seconds")
|
logger.error("lost connection to eclair's websocket, retrying in 5 seconds")
|
||||||
await asyncio.sleep(5)
|
await asyncio.sleep(5)
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
|
||||||
import httpx
|
|
||||||
from os import getenv
|
from os import getenv
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime
|
||||||
from typing import Optional, Dict, AsyncGenerator
|
from typing import Optional, Dict, AsyncGenerator
|
||||||
import random
|
import random
|
||||||
import string
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from lnbits.helpers import urlsafe_short_hash
|
from lnbits.helpers import urlsafe_short_hash
|
||||||
import hashlib
|
import hashlib
|
||||||
from ..bolt11 import encode, decode
|
from ..bolt11 import encode, decode
|
||||||
|
|
@ -20,7 +21,7 @@ from .base import (
|
||||||
|
|
||||||
class FakeWallet(Wallet):
|
class FakeWallet(Wallet):
|
||||||
async def status(self) -> StatusResponse:
|
async def status(self) -> StatusResponse:
|
||||||
print(
|
logger.info(
|
||||||
"FakeWallet funding source is for using LNbits as a centralised, stand-alone payment system with brrrrrr."
|
"FakeWallet funding source is for using LNbits as a centralised, stand-alone payment system with brrrrrr."
|
||||||
)
|
)
|
||||||
return StatusResponse(None, float("inf"))
|
return StatusResponse(None, float("inf"))
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import httpx
|
||||||
from os import getenv
|
from os import getenv
|
||||||
from typing import Optional, Dict, AsyncGenerator
|
from typing import Optional, Dict, AsyncGenerator
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from .base import (
|
from .base import (
|
||||||
StatusResponse,
|
StatusResponse,
|
||||||
InvoiceResponse,
|
InvoiceResponse,
|
||||||
|
|
@ -144,5 +146,7 @@ class LNbitsWallet(Wallet):
|
||||||
except (OSError, httpx.ReadError, httpx.ConnectError, httpx.ReadTimeout):
|
except (OSError, httpx.ReadError, httpx.ConnectError, httpx.ReadTimeout):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
print("lost connection to lnbits /payments/sse, retrying in 5 seconds")
|
logger.error(
|
||||||
|
"lost connection to lnbits /payments/sse, retrying in 5 seconds"
|
||||||
|
)
|
||||||
await asyncio.sleep(5)
|
await asyncio.sleep(5)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@ import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
from os import environ, error, getenv
|
from os import environ, error, getenv
|
||||||
from typing import Optional, Dict, AsyncGenerator
|
from typing import Optional, Dict, AsyncGenerator
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from .macaroon import load_macaroon, AESCipher
|
from .macaroon import load_macaroon, AESCipher
|
||||||
|
|
||||||
if imports_ok:
|
if imports_ok:
|
||||||
|
|
@ -187,6 +190,8 @@ class LndWallet(Wallet):
|
||||||
checking_id = stringify_checking_id(i.r_hash)
|
checking_id = stringify_checking_id(i.r_hash)
|
||||||
yield checking_id
|
yield checking_id
|
||||||
except error:
|
except error:
|
||||||
print(error)
|
logger.error(error)
|
||||||
|
|
||||||
print("lost connection to lnd InvoiceSubscription, please restart lnbits.")
|
logger.error(
|
||||||
|
"lost connection to lnd InvoiceSubscription, please restart lnbits."
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import base64
|
||||||
from os import getenv
|
from os import getenv
|
||||||
from typing import Optional, Dict, AsyncGenerator
|
from typing import Optional, Dict, AsyncGenerator
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from lnbits import bolt11 as lnbits_bolt11
|
from lnbits import bolt11 as lnbits_bolt11
|
||||||
from .macaroon import load_macaroon, AESCipher
|
from .macaroon import load_macaroon, AESCipher
|
||||||
|
|
||||||
|
|
@ -191,5 +193,7 @@ class LndRestWallet(Wallet):
|
||||||
except (OSError, httpx.ConnectError, httpx.ReadError):
|
except (OSError, httpx.ConnectError, httpx.ReadError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
print("lost connection to lnd invoices stream, retrying in 5 seconds")
|
logger.error(
|
||||||
|
"lost connection to lnd invoices stream, retrying in 5 seconds"
|
||||||
|
)
|
||||||
await asyncio.sleep(5)
|
await asyncio.sleep(5)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ from os import getenv
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from typing import Optional, Dict, AsyncGenerator
|
from typing import Optional, Dict, AsyncGenerator
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from .base import (
|
from .base import (
|
||||||
StatusResponse,
|
StatusResponse,
|
||||||
InvoiceResponse,
|
InvoiceResponse,
|
||||||
|
|
@ -127,7 +129,7 @@ class LNPayWallet(Wallet):
|
||||||
try:
|
try:
|
||||||
data = json.loads(text)
|
data = json.loads(text)
|
||||||
except json.decoder.JSONDecodeError:
|
except json.decoder.JSONDecodeError:
|
||||||
print(f"got something wrong on lnpay webhook endpoint: {text[:200]}")
|
logger.error(f"got something wrong on lnpay webhook endpoint: {text[:200]}")
|
||||||
data = None
|
data = None
|
||||||
if (
|
if (
|
||||||
type(data) is not dict
|
type(data) is not dict
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import httpx
|
||||||
from os import getenv
|
from os import getenv
|
||||||
from typing import Optional, Dict, AsyncGenerator
|
from typing import Optional, Dict, AsyncGenerator
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from .base import (
|
from .base import (
|
||||||
StatusResponse,
|
StatusResponse,
|
||||||
InvoiceResponse,
|
InvoiceResponse,
|
||||||
|
|
@ -143,5 +145,7 @@ class LntxbotWallet(Wallet):
|
||||||
except (OSError, httpx.ReadError, httpx.ReadTimeout, httpx.ConnectError):
|
except (OSError, httpx.ReadError, httpx.ReadTimeout, httpx.ConnectError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
print("lost connection to lntxbot /payments/stream, retrying in 5 seconds")
|
logger.error(
|
||||||
|
"lost connection to lntxbot /payments/stream, retrying in 5 seconds"
|
||||||
|
)
|
||||||
await asyncio.sleep(5)
|
await asyncio.sleep(5)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import base64
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
import getpass
|
import getpass
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
BLOCK_SIZE = 16
|
BLOCK_SIZE = 16
|
||||||
import getpass
|
import getpass
|
||||||
|
|
||||||
|
|
@ -103,5 +105,5 @@ if __name__ == "__main__":
|
||||||
macaroon = input("Enter macaroon: ")
|
macaroon = input("Enter macaroon: ")
|
||||||
macaroon = load_macaroon(macaroon)
|
macaroon = load_macaroon(macaroon)
|
||||||
macaroon = AESCipher(description="encryption").encrypt(macaroon.encode())
|
macaroon = AESCipher(description="encryption").encrypt(macaroon.encode())
|
||||||
print("Encrypted macaroon:")
|
logger.info("Encrypted macaroon:")
|
||||||
print(macaroon)
|
logger.info(macaroon)
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ from http import HTTPStatus
|
||||||
from os import getenv
|
from os import getenv
|
||||||
from typing import Optional, AsyncGenerator
|
from typing import Optional, AsyncGenerator
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from .base import (
|
from .base import (
|
||||||
StatusResponse,
|
StatusResponse,
|
||||||
InvoiceResponse,
|
InvoiceResponse,
|
||||||
|
|
@ -139,7 +141,7 @@ class OpenNodeWallet(Wallet):
|
||||||
x = hmac.new(self.auth["Authorization"].encode("ascii"), digestmod="sha256")
|
x = hmac.new(self.auth["Authorization"].encode("ascii"), digestmod="sha256")
|
||||||
x.update(charge_id.encode("ascii"))
|
x.update(charge_id.encode("ascii"))
|
||||||
if x.hexdigest() != data["hashed_order"]:
|
if x.hexdigest() != data["hashed_order"]:
|
||||||
print("invalid webhook, not from opennode")
|
logger.error("invalid webhook, not from opennode")
|
||||||
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
|
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
|
||||||
|
|
||||||
await self.queue.put(charge_id)
|
await self.queue.put(charge_id)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import random
|
||||||
from os import getenv
|
from os import getenv
|
||||||
from typing import Optional, AsyncGenerator
|
from typing import Optional, AsyncGenerator
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from .base import (
|
from .base import (
|
||||||
StatusResponse,
|
StatusResponse,
|
||||||
InvoiceResponse,
|
InvoiceResponse,
|
||||||
|
|
@ -204,5 +206,5 @@ class SparkWallet(Wallet):
|
||||||
except (OSError, httpx.ReadError, httpx.ConnectError, httpx.ReadTimeout):
|
except (OSError, httpx.ReadError, httpx.ConnectError, httpx.ReadTimeout):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
print("lost connection to spark /stream, retrying in 5 seconds")
|
logger.error("lost connection to spark /stream, retrying in 5 seconds")
|
||||||
await asyncio.sleep(5)
|
await asyncio.sleep(5)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
from typing import AsyncGenerator, Optional
|
from typing import AsyncGenerator, Optional
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from .base import (
|
from .base import (
|
||||||
InvoiceResponse,
|
InvoiceResponse,
|
||||||
PaymentResponse,
|
PaymentResponse,
|
||||||
|
|
@ -20,7 +22,7 @@ class VoidWallet(Wallet):
|
||||||
raise Unsupported("")
|
raise Unsupported("")
|
||||||
|
|
||||||
async def status(self) -> StatusResponse:
|
async def status(self) -> StatusResponse:
|
||||||
print(
|
logger.info(
|
||||||
"This backend does nothing, it is here just as a placeholder, you must configure an actual backend before being able to do anything useful with LNbits."
|
"This backend does nothing, it is here just as a placeholder, you must configure an actual backend before being able to do anything useful with LNbits."
|
||||||
)
|
)
|
||||||
return StatusResponse(None, 0)
|
return StatusResponse(None, 0)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ idna==3.2
|
||||||
importlib-metadata==4.8.1
|
importlib-metadata==4.8.1
|
||||||
jinja2==3.0.1
|
jinja2==3.0.1
|
||||||
lnurl==0.3.6
|
lnurl==0.3.6
|
||||||
|
loguru==0.6.0
|
||||||
markupsafe==2.0.1
|
markupsafe==2.0.1
|
||||||
marshmallow==3.13.0
|
marshmallow==3.13.0
|
||||||
outcome==1.1.0
|
outcome==1.1.0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue