use new settings and remove unused amdin extension stuff
This commit is contained in:
parent
9e2f1f6545
commit
1ffc8c3498
|
@ -8,6 +8,8 @@ import click
|
||||||
from genericpath import exists
|
from genericpath import exists
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
from lnbits.settings import Settings, settings
|
||||||
|
|
||||||
from .core import db as core_db
|
from .core import db as core_db
|
||||||
from .core import migrations as core_migrations
|
from .core import migrations as core_migrations
|
||||||
from .db import COCKROACH, POSTGRES, SQLITE
|
from .db import COCKROACH, POSTGRES, SQLITE
|
||||||
|
@ -17,7 +19,8 @@ from .helpers import (
|
||||||
get_valid_extensions,
|
get_valid_extensions,
|
||||||
url_for_vendored,
|
url_for_vendored,
|
||||||
)
|
)
|
||||||
from .settings import LNBITS_PATH
|
|
||||||
|
path = settings.lnbits_path
|
||||||
|
|
||||||
|
|
||||||
@click.command("migrate")
|
@click.command("migrate")
|
||||||
|
@ -36,15 +39,15 @@ def transpile_scss():
|
||||||
warnings.simplefilter("ignore")
|
warnings.simplefilter("ignore")
|
||||||
from scss.compiler import compile_string # type: ignore
|
from scss.compiler import compile_string # type: ignore
|
||||||
|
|
||||||
with open(os.path.join(LNBITS_PATH, "static/scss/base.scss")) as scss:
|
with open(os.path.join(path, "static/scss/base.scss")) as scss:
|
||||||
with open(os.path.join(LNBITS_PATH, "static/css/base.css"), "w") as css:
|
with open(os.path.join(path, "static/css/base.css"), "w") as css:
|
||||||
css.write(compile_string(scss.read()))
|
css.write(compile_string(scss.read()))
|
||||||
|
|
||||||
|
|
||||||
def bundle_vendored():
|
def bundle_vendored():
|
||||||
for getfiles, outputpath in [
|
for getfiles, outputpath in [
|
||||||
(get_js_vendored, os.path.join(LNBITS_PATH, "static/bundle.js")),
|
(get_js_vendored, os.path.join(path, "static/bundle.js")),
|
||||||
(get_css_vendored, os.path.join(LNBITS_PATH, "static/bundle.css")),
|
(get_css_vendored, os.path.join(path, "static/bundle.css")),
|
||||||
]:
|
]:
|
||||||
output = ""
|
output = ""
|
||||||
for path in getfiles():
|
for path in getfiles():
|
||||||
|
@ -54,33 +57,6 @@ def bundle_vendored():
|
||||||
f.write(output)
|
f.write(output)
|
||||||
|
|
||||||
|
|
||||||
async def get_admin_settings():
|
|
||||||
from lnbits.extensions.admin.models import Admin
|
|
||||||
|
|
||||||
try:
|
|
||||||
ext_db = importlib.import_module(f"lnbits.extensions.admin").db
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
async with ext_db.connect() as conn:
|
|
||||||
|
|
||||||
if conn.type == SQLITE:
|
|
||||||
exists = await conn.fetchone(
|
|
||||||
"SELECT * FROM sqlite_master WHERE type='table' AND name='admin'"
|
|
||||||
)
|
|
||||||
elif conn.type in {POSTGRES, COCKROACH}:
|
|
||||||
exists = await conn.fetchone(
|
|
||||||
"SELECT * FROM information_schema.tables WHERE table_name = 'admin'"
|
|
||||||
)
|
|
||||||
|
|
||||||
if not exists:
|
|
||||||
return False
|
|
||||||
|
|
||||||
row = await conn.fetchone("SELECT * from admin.admin")
|
|
||||||
|
|
||||||
return Admin(**row) if row else None
|
|
||||||
|
|
||||||
|
|
||||||
async def migrate_databases():
|
async def migrate_databases():
|
||||||
"""Creates the necessary databases if they don't exist already; or migrates them."""
|
"""Creates the necessary databases if they don't exist already; or migrates them."""
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ from loguru import logger
|
||||||
|
|
||||||
from lnbits import bolt11
|
from lnbits import bolt11
|
||||||
from lnbits.db import COCKROACH, POSTGRES, Connection
|
from lnbits.db import COCKROACH, POSTGRES, Connection
|
||||||
from lnbits.settings import DEFAULT_WALLET_NAME, LNBITS_ADMIN_USERS
|
from lnbits.settings import settings
|
||||||
|
|
||||||
from . import db
|
from . import db
|
||||||
from .models import BalanceCheck, Payment, User, Wallet
|
from .models import BalanceCheck, Payment, User, Wallet
|
||||||
|
@ -63,8 +63,8 @@ async def get_user(user_id: str, conn: Optional[Connection] = None) -> Optional[
|
||||||
email=user["email"],
|
email=user["email"],
|
||||||
extensions=[e[0] for e in extensions],
|
extensions=[e[0] for e in extensions],
|
||||||
wallets=[Wallet(**w) for w in wallets],
|
wallets=[Wallet(**w) for w in wallets],
|
||||||
admin=user["id"] in [x.strip() for x in LNBITS_ADMIN_USERS]
|
admin=user["id"] in settings.lnbits_admin_users
|
||||||
if LNBITS_ADMIN_USERS
|
if settings.lnbits_admin_users
|
||||||
else False,
|
else False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ async def create_wallet(
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
wallet_id,
|
wallet_id,
|
||||||
wallet_name or DEFAULT_WALLET_NAME,
|
wallet_name or settings.lnbits_default_wallet_name,
|
||||||
user_id,
|
user_id,
|
||||||
uuid4().hex,
|
uuid4().hex,
|
||||||
uuid4().hex,
|
uuid4().hex,
|
||||||
|
|
|
@ -21,7 +21,7 @@ from lnbits.decorators import (
|
||||||
)
|
)
|
||||||
from lnbits.helpers import url_for, urlsafe_short_hash
|
from lnbits.helpers import url_for, urlsafe_short_hash
|
||||||
from lnbits.requestvars import g
|
from lnbits.requestvars import g
|
||||||
from lnbits.settings import FAKE_WALLET, RESERVE_FEE_MIN, RESERVE_FEE_PERCENT, WALLET
|
from lnbits.settings import FAKE_WALLET, WALLET, settings
|
||||||
from lnbits.wallets.base import PaymentResponse, PaymentStatus
|
from lnbits.wallets.base import PaymentResponse, PaymentStatus
|
||||||
|
|
||||||
from . import db
|
from . import db
|
||||||
|
@ -381,4 +381,6 @@ async def check_transaction_status(
|
||||||
|
|
||||||
# WARN: this same value must be used for balance check and passed to WALLET.pay_invoice(), it may cause a vulnerability if the values differ
|
# WARN: this same value must be used for balance check and passed to WALLET.pay_invoice(), it may cause a vulnerability if the values differ
|
||||||
def fee_reserve(amount_msat: int) -> int:
|
def fee_reserve(amount_msat: int) -> int:
|
||||||
return max(int(RESERVE_FEE_MIN), int(amount_msat * RESERVE_FEE_PERCENT / 100.0))
|
reserve_min = settings.lnbits_reserve_fee_min
|
||||||
|
reserve_percent = settings.lnbits_reserve_fee_percent
|
||||||
|
return max(int(reserve_min), int(amount_msat * reserve_percent / 100.0))
|
||||||
|
|
|
@ -30,7 +30,7 @@ from lnbits.decorators import (
|
||||||
require_invoice_key,
|
require_invoice_key,
|
||||||
)
|
)
|
||||||
from lnbits.helpers import url_for, urlsafe_short_hash
|
from lnbits.helpers import url_for, urlsafe_short_hash
|
||||||
from lnbits.settings import LNBITS_ADMIN_USERS, LNBITS_SITE_TITLE, WALLET
|
from lnbits.settings import WALLET, settings
|
||||||
from lnbits.utils.exchange_rates import (
|
from lnbits.utils.exchange_rates import (
|
||||||
currencies,
|
currencies,
|
||||||
fiat_amount_as_satoshis,
|
fiat_amount_as_satoshis,
|
||||||
|
@ -76,7 +76,7 @@ async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)):
|
||||||
async def api_update_balance(
|
async def api_update_balance(
|
||||||
amount: int, wallet: WalletTypeInfo = Depends(get_key_type)
|
amount: int, wallet: WalletTypeInfo = Depends(get_key_type)
|
||||||
):
|
):
|
||||||
if wallet.wallet.user not in LNBITS_ADMIN_USERS:
|
if wallet.wallet.user not in settings.lnbits_admin_users:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.FORBIDDEN, detail="Not an admin user"
|
status_code=HTTPStatus.FORBIDDEN, detail="Not an admin user"
|
||||||
)
|
)
|
||||||
|
@ -178,7 +178,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
|
||||||
else:
|
else:
|
||||||
description_hash = b""
|
description_hash = b""
|
||||||
unhashed_description = b""
|
unhashed_description = b""
|
||||||
memo = data.memo or LNBITS_SITE_TITLE
|
memo = data.memo or settings.lnbits_site_title
|
||||||
if data.unit == "sat":
|
if data.unit == "sat":
|
||||||
amount = int(data.amount)
|
amount = int(data.amount)
|
||||||
else:
|
else:
|
||||||
|
@ -678,7 +678,7 @@ async def img(request: Request, data):
|
||||||
|
|
||||||
@core_app.get("/api/v1/audit/")
|
@core_app.get("/api/v1/audit/")
|
||||||
async def api_auditor(wallet: WalletTypeInfo = Depends(get_key_type)):
|
async def api_auditor(wallet: WalletTypeInfo = Depends(get_key_type)):
|
||||||
if wallet.wallet.user not in LNBITS_ADMIN_USERS:
|
if wallet.wallet.user not in settings.lnbits_admin_users:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.FORBIDDEN, detail="Not an admin user"
|
status_code=HTTPStatus.FORBIDDEN, detail="Not an admin user"
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,14 +16,7 @@ from lnbits.core.models import User
|
||||||
from lnbits.decorators import check_user_exists
|
from lnbits.decorators import check_user_exists
|
||||||
from lnbits.helpers import template_renderer, url_for
|
from lnbits.helpers import template_renderer, url_for
|
||||||
from lnbits.requestvars import g
|
from lnbits.requestvars import g
|
||||||
from lnbits.settings import (
|
from lnbits.settings import settings
|
||||||
LNBITS_ADMIN_UI,
|
|
||||||
LNBITS_ADMIN_USERS,
|
|
||||||
LNBITS_ALLOWED_USERS,
|
|
||||||
LNBITS_CUSTOM_LOGO,
|
|
||||||
LNBITS_SITE_TITLE,
|
|
||||||
SERVICE_FEE,
|
|
||||||
)
|
|
||||||
|
|
||||||
from ...helpers import get_valid_extensions
|
from ...helpers import get_valid_extensions
|
||||||
from ..crud import (
|
from ..crud import (
|
||||||
|
@ -119,14 +112,6 @@ async def wallet(
|
||||||
user_id = usr.hex if usr else None
|
user_id = usr.hex if usr else None
|
||||||
wallet_id = wal.hex if wal else None
|
wallet_id = wal.hex if wal else None
|
||||||
wallet_name = nme
|
wallet_name = nme
|
||||||
service_fee = int(SERVICE_FEE) if int(SERVICE_FEE) == SERVICE_FEE else SERVICE_FEE
|
|
||||||
|
|
||||||
if LNBITS_ADMIN_UI:
|
|
||||||
LNBITS_ADMIN_USERS = g().admin_conf.admin_users
|
|
||||||
LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users
|
|
||||||
else:
|
|
||||||
LNBITS_ADMIN_USERS = []
|
|
||||||
LNBITS_ALLOWED_USERS = []
|
|
||||||
|
|
||||||
if not user_id:
|
if not user_id:
|
||||||
user = await get_user((await create_account()).id)
|
user = await get_user((await create_account()).id)
|
||||||
|
@ -137,11 +122,14 @@ async def wallet(
|
||||||
return template_renderer().TemplateResponse(
|
return template_renderer().TemplateResponse(
|
||||||
"error.html", {"request": request, "err": "User does not exist."}
|
"error.html", {"request": request, "err": "User does not exist."}
|
||||||
)
|
)
|
||||||
if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS:
|
if (
|
||||||
|
len(settings.lnbits_allowed_users) > 0
|
||||||
|
and user_id not in settings.lnbits_allowed_users
|
||||||
|
):
|
||||||
return template_renderer().TemplateResponse(
|
return template_renderer().TemplateResponse(
|
||||||
"error.html", {"request": request, "err": "User not authorized."}
|
"error.html", {"request": request, "err": "User not authorized."}
|
||||||
)
|
)
|
||||||
if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS:
|
if user_id in settings.lnbits_admin_users:
|
||||||
user.admin = True
|
user.admin = True
|
||||||
if not wallet_id:
|
if not wallet_id:
|
||||||
if user.wallets and not wallet_name: # type: ignore
|
if user.wallets and not wallet_name: # type: ignore
|
||||||
|
@ -172,7 +160,7 @@ async def wallet(
|
||||||
"request": request,
|
"request": request,
|
||||||
"user": user.dict(), # type: ignore
|
"user": user.dict(), # type: ignore
|
||||||
"wallet": userwallet.dict(),
|
"wallet": userwallet.dict(),
|
||||||
"service_fee": service_fee,
|
"service_fee": settings.lnbits_service_fee,
|
||||||
"web_manifest": f"/manifest/{user.id}.webmanifest", # type: ignore
|
"web_manifest": f"/manifest/{user.id}.webmanifest", # type: ignore
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -194,7 +182,7 @@ async def lnurl_full_withdraw(request: Request):
|
||||||
"k1": "0",
|
"k1": "0",
|
||||||
"minWithdrawable": 1000 if wallet.withdrawable_balance else 0,
|
"minWithdrawable": 1000 if wallet.withdrawable_balance else 0,
|
||||||
"maxWithdrawable": wallet.withdrawable_balance,
|
"maxWithdrawable": wallet.withdrawable_balance,
|
||||||
"defaultDescription": f"{LNBITS_SITE_TITLE} balance withdraw from {wallet.id[0:5]}",
|
"defaultDescription": f"{settings.lnbits_site_title} balance withdraw from {wallet.id[0:5]}",
|
||||||
"balanceCheck": url_for("/withdraw", external=True, usr=user.id, wal=wallet.id),
|
"balanceCheck": url_for("/withdraw", external=True, usr=user.id, wal=wallet.id),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,12 +281,12 @@ async def manifest(usr: str):
|
||||||
raise HTTPException(status_code=HTTPStatus.NOT_FOUND)
|
raise HTTPException(status_code=HTTPStatus.NOT_FOUND)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"short_name": LNBITS_SITE_TITLE,
|
"short_name": settings.lnbits_site_title,
|
||||||
"name": LNBITS_SITE_TITLE + " Wallet",
|
"name": settings.lnbits_site_title + " Wallet",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": LNBITS_CUSTOM_LOGO
|
"src": settings.lnbits_custom_logo
|
||||||
if LNBITS_CUSTOM_LOGO
|
if settings.lnbits_custom_logo
|
||||||
else "https://cdn.jsdelivr.net/gh/lnbits/lnbits@0.3.0/docs/logos/lnbits.png",
|
else "https://cdn.jsdelivr.net/gh/lnbits/lnbits@0.3.0/docs/logos/lnbits.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "900x900",
|
"sizes": "900x900",
|
||||||
|
|
16
lnbits/db.py
16
lnbits/db.py
|
@ -10,7 +10,7 @@ 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
|
||||||
|
|
||||||
from .settings import LNBITS_DATA_FOLDER, LNBITS_DATABASE_URL
|
from lnbits.settings import settings
|
||||||
|
|
||||||
POSTGRES = "POSTGRES"
|
POSTGRES = "POSTGRES"
|
||||||
COCKROACH = "COCKROACH"
|
COCKROACH = "COCKROACH"
|
||||||
|
@ -85,8 +85,8 @@ class Database(Compat):
|
||||||
def __init__(self, db_name: str):
|
def __init__(self, db_name: str):
|
||||||
self.name = db_name
|
self.name = db_name
|
||||||
|
|
||||||
if LNBITS_DATABASE_URL:
|
if settings.lnbits_database_url:
|
||||||
database_uri = LNBITS_DATABASE_URL
|
database_uri = settings.lnbits_database_url
|
||||||
|
|
||||||
if database_uri.startswith("cockroachdb://"):
|
if database_uri.startswith("cockroachdb://"):
|
||||||
self.type = COCKROACH
|
self.type = COCKROACH
|
||||||
|
@ -131,14 +131,16 @@ class Database(Compat):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if os.path.isdir(LNBITS_DATA_FOLDER):
|
if os.path.isdir(settings.lnbits_data_folder):
|
||||||
self.path = os.path.join(LNBITS_DATA_FOLDER, f"{self.name}.sqlite3")
|
self.path = os.path.join(
|
||||||
|
settings.lnbits_data_folder, f"{self.name}.sqlite3"
|
||||||
|
)
|
||||||
database_uri = f"sqlite:///{self.path}"
|
database_uri = f"sqlite:///{self.path}"
|
||||||
self.type = SQLITE
|
self.type = SQLITE
|
||||||
else:
|
else:
|
||||||
raise NotADirectoryError(
|
raise NotADirectoryError(
|
||||||
f"LNBITS_DATA_FOLDER named {LNBITS_DATA_FOLDER} was not created"
|
f"LNBITS_DATA_FOLDER named {settings.lnbits_data_folder} was not created"
|
||||||
f" - please 'mkdir {LNBITS_DATA_FOLDER}' and try again"
|
f" - please 'mkdir {settings.lnbits_data_folder}' and try again"
|
||||||
)
|
)
|
||||||
logger.trace(f"database {self.type} added for {self.name}")
|
logger.trace(f"database {self.type} added for {self.name}")
|
||||||
self.schema = self.name
|
self.schema = self.name
|
||||||
|
|
|
@ -14,12 +14,7 @@ from starlette.requests import Request
|
||||||
from lnbits.core.crud import get_user, get_wallet_for_key
|
from lnbits.core.crud import get_user, get_wallet_for_key
|
||||||
from lnbits.core.models import User, Wallet
|
from lnbits.core.models import User, Wallet
|
||||||
from lnbits.requestvars import g
|
from lnbits.requestvars import g
|
||||||
from lnbits.settings import (
|
from lnbits.settings import settings
|
||||||
LNBITS_ADMIN_EXTENSIONS,
|
|
||||||
LNBITS_ADMIN_UI,
|
|
||||||
LNBITS_ADMIN_USERS,
|
|
||||||
LNBITS_ALLOWED_USERS,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class KeyChecker(SecurityBase):
|
class KeyChecker(SecurityBase):
|
||||||
|
@ -139,11 +134,6 @@ async def get_key_type(
|
||||||
detail="Invoice (or Admin) key required.",
|
detail="Invoice (or Admin) key required.",
|
||||||
)
|
)
|
||||||
|
|
||||||
if LNBITS_ADMIN_UI:
|
|
||||||
LNBITS_ADMIN_USERS = g().admin_conf.admin_users
|
|
||||||
else:
|
|
||||||
LNBITS_ADMIN_USERS = []
|
|
||||||
|
|
||||||
for typenr, WalletChecker in zip(
|
for typenr, WalletChecker in zip(
|
||||||
[0, 1], [WalletAdminKeyChecker, WalletInvoiceKeyChecker]
|
[0, 1], [WalletAdminKeyChecker, WalletInvoiceKeyChecker]
|
||||||
):
|
):
|
||||||
|
@ -156,8 +146,12 @@ async def get_key_type(
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="Wallet does not exist."
|
status_code=HTTPStatus.NOT_FOUND, detail="Wallet does not exist."
|
||||||
)
|
)
|
||||||
if (
|
if (
|
||||||
LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS
|
settings.lnbits_admin_users
|
||||||
) and (LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS):
|
and wallet.wallet.user not in settings.lnbits_admin_users
|
||||||
|
) and (
|
||||||
|
settings.lnbits_admin_extensions
|
||||||
|
and pathname in settings.lnbits_admin_extensions
|
||||||
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.FORBIDDEN,
|
status_code=HTTPStatus.FORBIDDEN,
|
||||||
detail="User not authorized for this extension.",
|
detail="User not authorized for this extension.",
|
||||||
|
@ -233,22 +227,33 @@ async def require_invoice_key(
|
||||||
|
|
||||||
async def check_user_exists(usr: UUID4) -> User:
|
async def check_user_exists(usr: UUID4) -> User:
|
||||||
g().user = await get_user(usr.hex)
|
g().user = await get_user(usr.hex)
|
||||||
|
|
||||||
if not g().user:
|
if not g().user:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="User does not exist."
|
status_code=HTTPStatus.NOT_FOUND, detail="User does not exist."
|
||||||
)
|
)
|
||||||
|
|
||||||
if LNBITS_ADMIN_UI:
|
if (
|
||||||
LNBITS_ADMIN_USERS = g().admin_conf.admin_users
|
len(settings.lnbits_allowed_users) > 0
|
||||||
LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users
|
and g().user.id not in settings.lnbits_allowed_users
|
||||||
else:
|
):
|
||||||
LNBITS_ADMIN_USERS = []
|
|
||||||
LNBITS_ALLOWED_USERS = []
|
|
||||||
|
|
||||||
if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS:
|
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
|
status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
|
||||||
)
|
)
|
||||||
if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS:
|
|
||||||
|
if g().user.id in settings.lnbits_admin_users:
|
||||||
g().user.admin = True
|
g().user.admin = True
|
||||||
|
|
||||||
return g().user
|
return g().user
|
||||||
|
|
||||||
|
|
||||||
|
async def check_admin(usr: UUID4) -> User:
|
||||||
|
user = await check_user_exists(usr)
|
||||||
|
|
||||||
|
if not user.id in settings.lnbits_admin_users:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.UNAUTHORIZED,
|
||||||
|
detail="User not authorized. No admin privileges.",
|
||||||
|
)
|
||||||
|
|
||||||
|
return user
|
||||||
|
|
|
@ -6,9 +6,9 @@ from typing import Any, List, NamedTuple, Optional
|
||||||
import jinja2
|
import jinja2
|
||||||
import shortuuid # type: ignore
|
import shortuuid # type: ignore
|
||||||
|
|
||||||
import lnbits.settings as settings
|
|
||||||
from lnbits.jinja2_templating import Jinja2Templates
|
from lnbits.jinja2_templating import Jinja2Templates
|
||||||
from lnbits.requestvars import g
|
from lnbits.requestvars import g
|
||||||
|
from lnbits.settings import settings
|
||||||
|
|
||||||
|
|
||||||
class Extension(NamedTuple):
|
class Extension(NamedTuple):
|
||||||
|
@ -24,15 +24,10 @@ class Extension(NamedTuple):
|
||||||
|
|
||||||
class ExtensionManager:
|
class ExtensionManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
if settings.LNBITS_ADMIN_UI:
|
self._disabled: List[str] = settings.lnbits_disabled_extensions
|
||||||
settings.LNBITS_DISABLED_EXTENSIONS = g().admin_conf.disabled_ext
|
self._admin_only: List[str] = settings.lnbits_admin_extensions
|
||||||
settings.LNBITS_ADMIN_EXTENSIONS = g().admin_conf.admin_ext
|
|
||||||
self._disabled: List[str] = settings.LNBITS_DISABLED_EXTENSIONS
|
|
||||||
self._admin_only: List[str] = [
|
|
||||||
x.strip(" ") for x in settings.LNBITS_ADMIN_EXTENSIONS
|
|
||||||
]
|
|
||||||
self._extension_folders: List[str] = [
|
self._extension_folders: List[str] = [
|
||||||
x[1] for x in os.walk(os.path.join(settings.LNBITS_PATH, "extensions"))
|
x[1] for x in os.walk(os.path.join(settings.lnbits_path, "extensions"))
|
||||||
][0]
|
][0]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -48,7 +43,7 @@ class ExtensionManager:
|
||||||
try:
|
try:
|
||||||
with open(
|
with open(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
settings.LNBITS_PATH, "extensions", extension, "config.json"
|
settings.lnbits_path, "extensions", extension, "config.json"
|
||||||
)
|
)
|
||||||
) as json_file:
|
) as json_file:
|
||||||
config = json.load(json_file)
|
config = json.load(json_file)
|
||||||
|
@ -120,7 +115,7 @@ def get_css_vendored(prefer_minified: bool = False) -> List[str]:
|
||||||
def get_vendored(ext: str, prefer_minified: bool = False) -> List[str]:
|
def get_vendored(ext: str, prefer_minified: bool = False) -> List[str]:
|
||||||
paths: List[str] = []
|
paths: List[str] = []
|
||||||
for path in glob.glob(
|
for path in glob.glob(
|
||||||
os.path.join(settings.LNBITS_PATH, "static/vendor/**"), recursive=True
|
os.path.join(settings.lnbits_path, "static/vendor/**"), recursive=True
|
||||||
):
|
):
|
||||||
if path.endswith(".min" + ext):
|
if path.endswith(".min" + ext):
|
||||||
# path is minified
|
# path is minified
|
||||||
|
@ -146,7 +141,7 @@ def get_vendored(ext: str, prefer_minified: bool = False) -> List[str]:
|
||||||
|
|
||||||
|
|
||||||
def url_for_vendored(abspath: str) -> str:
|
def url_for_vendored(abspath: str) -> str:
|
||||||
return "/" + os.path.relpath(abspath, settings.LNBITS_PATH)
|
return "/" + os.path.relpath(abspath, settings.lnbits_path)
|
||||||
|
|
||||||
|
|
||||||
def url_for(endpoint: str, external: Optional[bool] = False, **params: Any) -> str:
|
def url_for(endpoint: str, external: Optional[bool] = False, **params: Any) -> str:
|
||||||
|
@ -163,16 +158,6 @@ def removeEmptyString(arr):
|
||||||
|
|
||||||
|
|
||||||
def template_renderer(additional_folders: List = []) -> Jinja2Templates:
|
def template_renderer(additional_folders: List = []) -> Jinja2Templates:
|
||||||
if settings.LNBITS_ADMIN_UI:
|
|
||||||
_ = g().admin_conf
|
|
||||||
settings.LNBITS_AD_SPACE = _.ad_space
|
|
||||||
settings.LNBITS_HIDE_API = _.hide_api
|
|
||||||
settings.LNBITS_SITE_TITLE = _.site_title
|
|
||||||
settings.LNBITS_DENOMINATION = _.denomination
|
|
||||||
settings.LNBITS_SITE_TAGLINE = _.site_tagline
|
|
||||||
settings.LNBITS_SITE_DESCRIPTION = _.site_description
|
|
||||||
settings.LNBITS_THEME_OPTIONS = _.theme
|
|
||||||
settings.LNBITS_CUSTOM_LOGO = _.custom_logo
|
|
||||||
|
|
||||||
t = Jinja2Templates(
|
t = Jinja2Templates(
|
||||||
loader=jinja2.FileSystemLoader(
|
loader=jinja2.FileSystemLoader(
|
||||||
|
@ -180,20 +165,21 @@ def template_renderer(additional_folders: List = []) -> Jinja2Templates:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if settings.LNBITS_AD_SPACE:
|
if settings.lnbits_ad_space:
|
||||||
t.env.globals["AD_SPACE"] = settings.LNBITS_AD_SPACE
|
t.env.globals["AD_SPACE"] = settings.lnbits_ad_space
|
||||||
t.env.globals["HIDE_API"] = settings.LNBITS_HIDE_API
|
|
||||||
t.env.globals["SITE_TITLE"] = settings.LNBITS_SITE_TITLE
|
|
||||||
t.env.globals["LNBITS_DENOMINATION"] = settings.LNBITS_DENOMINATION
|
|
||||||
t.env.globals["SITE_TAGLINE"] = settings.LNBITS_SITE_TAGLINE
|
|
||||||
t.env.globals["SITE_DESCRIPTION"] = settings.LNBITS_SITE_DESCRIPTION
|
|
||||||
t.env.globals["LNBITS_THEME_OPTIONS"] = settings.LNBITS_THEME_OPTIONS
|
|
||||||
t.env.globals["LNBITS_VERSION"] = settings.LNBITS_COMMIT
|
|
||||||
t.env.globals["EXTENSIONS"] = get_valid_extensions()
|
|
||||||
if settings.LNBITS_CUSTOM_LOGO:
|
|
||||||
t.env.globals["USE_CUSTOM_LOGO"] = settings.LNBITS_CUSTOM_LOGO
|
|
||||||
|
|
||||||
if settings.DEBUG:
|
t.env.globals["HIDE_API"] = settings.lnbits_hide_api
|
||||||
|
t.env.globals["SITE_TITLE"] = settings.lnbits_site_title
|
||||||
|
t.env.globals["LNBITS_DENOMINATION"] = settings.lnbits_denomination
|
||||||
|
t.env.globals["SITE_TAGLINE"] = settings.lnbits_site_tagline
|
||||||
|
t.env.globals["SITE_DESCRIPTION"] = settings.lnbits_site_description
|
||||||
|
t.env.globals["LNBITS_THEME_OPTIONS"] = settings.lnbits_theme_options
|
||||||
|
t.env.globals["LNBITS_VERSION"] = settings.lnbits_commit
|
||||||
|
t.env.globals["EXTENSIONS"] = get_valid_extensions()
|
||||||
|
if settings.lnbits_custom_logo:
|
||||||
|
t.env.globals["USE_CUSTOM_LOGO"] = settings.lnbits_custom_logo
|
||||||
|
|
||||||
|
if settings.debug:
|
||||||
t.env.globals["VENDORED_JS"] = map(url_for_vendored, get_js_vendored())
|
t.env.globals["VENDORED_JS"] = map(url_for_vendored, get_js_vendored())
|
||||||
t.env.globals["VENDORED_CSS"] = map(url_for_vendored, get_css_vendored())
|
t.env.globals["VENDORED_CSS"] = map(url_for_vendored, get_css_vendored())
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
|
import asyncio
|
||||||
|
import uvloop
|
||||||
|
uvloop.install()
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
import multiprocessing as mp
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import click
|
import click
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
|
||||||
from lnbits.settings import HOST, PORT
|
from lnbits.settings import settings
|
||||||
|
|
||||||
|
|
||||||
@click.command(
|
@click.command(
|
||||||
|
@ -12,12 +19,13 @@ from lnbits.settings import HOST, PORT
|
||||||
allow_extra_args=True,
|
allow_extra_args=True,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@click.option("--port", default=PORT, help="Port to listen on")
|
@click.option("--port", default=settings.port, help="Port to listen on")
|
||||||
@click.option("--host", default=HOST, help="Host to run LNBits on")
|
@click.option("--host", default=settings.host, help="Host to run LNBits on")
|
||||||
|
@click.option("--reload", is_flag=True, help="Reload LNBits on changes in code")
|
||||||
@click.option("--ssl-keyfile", default=None, help="Path to SSL keyfile")
|
@click.option("--ssl-keyfile", default=None, help="Path to SSL keyfile")
|
||||||
@click.option("--ssl-certfile", default=None, help="Path to SSL certificate")
|
@click.option("--ssl-certfile", default=None, help="Path to SSL certificate")
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def main(ctx, port: int, host: str, ssl_keyfile: str, ssl_certfile: str):
|
def main(ctx, port: int, host: str, ssl_keyfile: str, ssl_certfile: str, reload: bool):
|
||||||
"""Launched with `poetry run lnbits` at root level"""
|
"""Launched with `poetry run lnbits` at root level"""
|
||||||
# this beautiful beast parses all command line arguments and passes them to the uvicorn server
|
# this beautiful beast parses all command line arguments and passes them to the uvicorn server
|
||||||
d = dict()
|
d = dict()
|
||||||
|
@ -33,17 +41,31 @@ def main(ctx, port: int, host: str, ssl_keyfile: str, ssl_certfile: str):
|
||||||
else:
|
else:
|
||||||
d[a.strip("--")] = True # argument like --key
|
d[a.strip("--")] = True # argument like --key
|
||||||
|
|
||||||
config = uvicorn.Config(
|
while True:
|
||||||
"lnbits.__main__:app",
|
# loop = asyncio.new_event_loop()
|
||||||
port=port,
|
config = uvicorn.Config(
|
||||||
host=host,
|
"lnbits.__main__:app",
|
||||||
ssl_keyfile=ssl_keyfile,
|
port=port,
|
||||||
ssl_certfile=ssl_certfile,
|
host=host,
|
||||||
**d
|
reload=reload,
|
||||||
)
|
# loop=loop,
|
||||||
server = uvicorn.Server(config)
|
ssl_keyfile=ssl_keyfile,
|
||||||
server.run()
|
ssl_certfile=ssl_certfile,
|
||||||
|
**d
|
||||||
|
)
|
||||||
|
server = uvicorn.Server(config=config)
|
||||||
|
process = mp.Process(target=server.run)
|
||||||
|
process.start()
|
||||||
|
server_restart.wait()
|
||||||
|
server_restart.clear()
|
||||||
|
server.should_exit = True
|
||||||
|
server.force_exit = True
|
||||||
|
process.terminate()
|
||||||
|
process.join()
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
|
||||||
|
server_restart = mp.Event()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -1,88 +1,220 @@
|
||||||
import importlib
|
import importlib
|
||||||
|
import json
|
||||||
import subprocess
|
import subprocess
|
||||||
from os import path
|
from os import path
|
||||||
from typing import List
|
from sqlite3 import Row
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
from environs import Env
|
from loguru import logger
|
||||||
|
from pydantic import BaseSettings, Field, validator
|
||||||
|
|
||||||
|
|
||||||
|
def list_parse_fallback(v):
|
||||||
|
try:
|
||||||
|
return json.loads(v)
|
||||||
|
except Exception as e:
|
||||||
|
replaced = v.replace(" ", "")
|
||||||
|
if replaced:
|
||||||
|
return replaced.split(",")
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
read_only_variables = ["host", "port", "lnbits_commit"]
|
||||||
|
|
||||||
|
|
||||||
|
class Settings(BaseSettings):
|
||||||
|
|
||||||
|
lnbits_admin_ui: bool = Field(default=False)
|
||||||
|
|
||||||
|
# .env
|
||||||
|
debug: Optional[bool]
|
||||||
|
host: Optional[str]
|
||||||
|
port: Optional[int]
|
||||||
|
lnbits_path: Optional[str] = path.dirname(path.realpath(__file__))
|
||||||
|
lnbits_commit: str = Field(default="unknown")
|
||||||
|
|
||||||
|
# users
|
||||||
|
lnbits_admin_users: List[str] = Field(default=[])
|
||||||
|
lnbits_allowed_users: List[str] = Field(default=[])
|
||||||
|
lnbits_admin_extensions: List[str] = Field(default=[])
|
||||||
|
lnbits_disabled_extensions: List[str] = Field(default=[])
|
||||||
|
|
||||||
|
# Change theme
|
||||||
|
lnbits_site_title: str = Field(default="LNbits")
|
||||||
|
lnbits_site_tagline: str = Field(default="free and open-source lightning wallet")
|
||||||
|
lnbits_site_description: str = Field(default=None)
|
||||||
|
lnbits_default_wallet_name: str = Field(default="LNbits wallet")
|
||||||
|
lnbits_theme_options: List[str] = Field(
|
||||||
|
default=["classic", "flamingo", "mint", "salvador", "monochrome", "autumn"]
|
||||||
|
)
|
||||||
|
lnbits_custom_logo: str = Field(default=None)
|
||||||
|
lnbits_ad_space: List[str]
|
||||||
|
|
||||||
|
# ops
|
||||||
|
lnbits_data_folder: str = Field(default="./data")
|
||||||
|
lnbits_database_url: str = Field(default=None)
|
||||||
|
lnbits_force_https: bool = Field(default=True)
|
||||||
|
lnbits_reserve_fee_min: int = Field(default=4000)
|
||||||
|
lnbits_reserve_fee_percent: float = Field(default=1.0)
|
||||||
|
lnbits_service_fee: float = Field(default=0)
|
||||||
|
lnbits_hide_api: bool = Field(default=False)
|
||||||
|
lnbits_denomination: str = Field(default="sats")
|
||||||
|
|
||||||
|
# funding sources
|
||||||
|
lnbits_backend_wallet_class: str = Field(default="VoidWallet")
|
||||||
|
lnbits_allowed_funding_sources: List[str] = Field(
|
||||||
|
default=[
|
||||||
|
"CLightningWallet",
|
||||||
|
"LndRestWallet",
|
||||||
|
"LndWallet",
|
||||||
|
"LntxbotWallet",
|
||||||
|
"LNPayWallet",
|
||||||
|
"LnbitsWallet",
|
||||||
|
"OpenNodeWallet",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
fake_wallet_secret: str = Field(default="ToTheMoon1")
|
||||||
|
lnbits_endpoint: str = Field(default="https://legend.lnbits.com")
|
||||||
|
lnbits_key: Optional[str] = Field(default=None)
|
||||||
|
cliche_endpoint: Optional[str] = Field(default=None)
|
||||||
|
corelightning_rpc: Optional[str] = Field(default=None)
|
||||||
|
eclair_url: Optional[str] = Field(default=None)
|
||||||
|
eclair_pass: Optional[str] = Field(default=None)
|
||||||
|
lnd_rest_endpoint: Optional[str] = Field(default=None)
|
||||||
|
lnd_rest_cert: Optional[str] = Field(default=None)
|
||||||
|
lnd_rest_macaroon: Optional[str] = Field(default=None)
|
||||||
|
lnpay_api_endpoint: Optional[str] = Field(default=None)
|
||||||
|
lnpay_api_key: Optional[str] = Field(default=None)
|
||||||
|
lnpay_wallet_key: Optional[str] = Field(default=None)
|
||||||
|
lntxbot_api_endpoint: Optional[str] = Field(default=None)
|
||||||
|
lntxbot_key: Optional[str] = Field(default=None)
|
||||||
|
opennode_api_endpoint: Optional[str] = Field(default=None)
|
||||||
|
opennode_key: Optional[str] = Field(default=None)
|
||||||
|
spark_url: Optional[str] = Field(default=None)
|
||||||
|
spark_token: Optional[str] = Field(default=None)
|
||||||
|
|
||||||
|
# boltz
|
||||||
|
boltz_network: str = Field(default="main")
|
||||||
|
boltz_url: str = Field(default="https://boltz.exchange/api")
|
||||||
|
boltz_mempool_space_url: str = Field(default="https://mempool.space")
|
||||||
|
boltz_mempool_space_url_ws: str = Field(default="wss://mempool.space")
|
||||||
|
|
||||||
|
@validator(
|
||||||
|
"lnbits_admin_users",
|
||||||
|
"lnbits_allowed_users",
|
||||||
|
"lnbits_theme_options",
|
||||||
|
"lnbits_ad_space",
|
||||||
|
"lnbits_admin_extensions",
|
||||||
|
"lnbits_disabled_extensions",
|
||||||
|
"lnbits_allowed_funding_sources",
|
||||||
|
pre=True,
|
||||||
|
)
|
||||||
|
def validate(cls, val):
|
||||||
|
if type(val) == str:
|
||||||
|
val = val.split(",") if val else []
|
||||||
|
return val
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_row(cls, row: Row) -> "Settings":
|
||||||
|
data = dict(row)
|
||||||
|
return cls(**data)
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
env_file = ".env"
|
||||||
|
env_file_encoding = "utf-8"
|
||||||
|
case_sensitive = False
|
||||||
|
json_loads = list_parse_fallback
|
||||||
|
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
|
|
||||||
|
settings.lnbits_commit = (
|
||||||
|
subprocess.check_output(
|
||||||
|
["git", "-C", settings.lnbits_path, "rev-parse", "HEAD"],
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
)
|
||||||
|
.strip()
|
||||||
|
.decode("ascii")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if not settings.lnbits_admin_ui:
|
||||||
|
logger.debug(f"Enviroment Settings:")
|
||||||
|
for key, value in settings.dict(exclude_none=True).items():
|
||||||
|
logger.debug(f"{key}: {value}")
|
||||||
|
|
||||||
|
|
||||||
|
async def check_admin_settings():
|
||||||
|
if settings.lnbits_admin_ui:
|
||||||
|
try:
|
||||||
|
ext_db = importlib.import_module(f"lnbits.extensions.admin").db
|
||||||
|
except:
|
||||||
|
logger.error("could not import module lnbits.extensions.admin database")
|
||||||
|
raise
|
||||||
|
|
||||||
|
async with ext_db.connect() as db:
|
||||||
|
try:
|
||||||
|
row = await db.fetchone("SELECT * FROM admin.settings")
|
||||||
|
if not row or len(row) == 0:
|
||||||
|
logger.warning(
|
||||||
|
"admin.settings empty. inserting new settings and creating admin account"
|
||||||
|
)
|
||||||
|
|
||||||
|
from lnbits.core.crud import create_account
|
||||||
|
|
||||||
|
account = await create_account()
|
||||||
|
settings.lnbits_admin_users.insert(0, account.id)
|
||||||
|
keys = []
|
||||||
|
values = ""
|
||||||
|
for key, value in settings.dict(exclude_none=True).items():
|
||||||
|
keys.append(key)
|
||||||
|
if type(value) == list:
|
||||||
|
joined = ",".join(value)
|
||||||
|
values += f"'{joined}'"
|
||||||
|
if type(value) == int or type(value) == float:
|
||||||
|
values += str(value)
|
||||||
|
if type(value) == bool:
|
||||||
|
values += "true" if value else "false"
|
||||||
|
if type(value) == str:
|
||||||
|
value = value.replace("'", "")
|
||||||
|
values += f"'{value}'"
|
||||||
|
values += ","
|
||||||
|
q = ", ".join(keys)
|
||||||
|
v = values.rstrip(",")
|
||||||
|
sql = f"INSERT INTO admin.settings ({q}) VALUES ({v})"
|
||||||
|
await db.execute(sql)
|
||||||
|
logger.warning(
|
||||||
|
"initialized admin.settings from enviroment variables."
|
||||||
|
)
|
||||||
|
|
||||||
|
row = await db.fetchone("SELECT * FROM admin.settings")
|
||||||
|
assert row, "Newly updated settings couldn't be retrieved"
|
||||||
|
|
||||||
|
admin = Settings(**row)
|
||||||
|
|
||||||
|
logger.debug(f"Admin settings:")
|
||||||
|
for key, value in admin.dict(exclude_none=True).items():
|
||||||
|
if not key in read_only_variables:
|
||||||
|
try:
|
||||||
|
setattr(settings, key, value)
|
||||||
|
logger.debug(f"{key}: {value}")
|
||||||
|
except:
|
||||||
|
logger.error(
|
||||||
|
f"error overriding setting: {key}, value: {value}"
|
||||||
|
)
|
||||||
|
|
||||||
|
http = "https" if settings.lnbits_force_https else "http"
|
||||||
|
user = settings.lnbits_admin_users[0]
|
||||||
|
logger.warning(
|
||||||
|
f" ✔️ Access admin user account at: {http}://{settings.host}:{settings.port}/wallet?usr={user}"
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
logger.warning("admin.settings tables does not exist.")
|
||||||
|
raise
|
||||||
|
|
||||||
env = Env()
|
|
||||||
env.read_env()
|
|
||||||
|
|
||||||
wallets_module = importlib.import_module("lnbits.wallets")
|
wallets_module = importlib.import_module("lnbits.wallets")
|
||||||
wallet_class = getattr(
|
wallet_class = getattr(wallets_module, settings.lnbits_backend_wallet_class)
|
||||||
wallets_module, env.str("LNBITS_BACKEND_WALLET_CLASS", default="VoidWallet")
|
|
||||||
)
|
|
||||||
|
|
||||||
DEBUG = env.bool("DEBUG", default=False)
|
|
||||||
|
|
||||||
HOST = env.str("HOST", default="127.0.0.1")
|
|
||||||
PORT = env.int("PORT", default=5000)
|
|
||||||
|
|
||||||
LNBITS_PATH = path.dirname(path.realpath(__file__))
|
|
||||||
LNBITS_DATA_FOLDER = env.str(
|
|
||||||
"LNBITS_DATA_FOLDER", default=path.join(LNBITS_PATH, "data")
|
|
||||||
)
|
|
||||||
LNBITS_DATABASE_URL = env.str("LNBITS_DATABASE_URL", default=None)
|
|
||||||
|
|
||||||
LNBITS_ALLOWED_USERS: List[str] = [
|
|
||||||
x.strip(" ") for x in env.list("LNBITS_ALLOWED_USERS", default=[], subcast=str)
|
|
||||||
]
|
|
||||||
LNBITS_ADMIN_UI = env.bool("LNBITS_ADMIN_UI", default=False)
|
|
||||||
LNBITS_ADMIN_USERS: List[str] = [
|
|
||||||
x.strip(" ") for x in env.list("LNBITS_ADMIN_USERS", default=[], subcast=str)
|
|
||||||
]
|
|
||||||
LNBITS_ADMIN_EXTENSIONS: List[str] = [
|
|
||||||
x.strip(" ") for x in env.list("LNBITS_ADMIN_EXTENSIONS", default=[], subcast=str)
|
|
||||||
]
|
|
||||||
LNBITS_DISABLED_EXTENSIONS: List[str] = [
|
|
||||||
x.strip(" ")
|
|
||||||
for x in env.list("LNBITS_DISABLED_EXTENSIONS", default=[], subcast=str)
|
|
||||||
]
|
|
||||||
|
|
||||||
LNBITS_AD_SPACE = [x.strip(" ") for x in env.list("LNBITS_AD_SPACE", default=[])]
|
|
||||||
LNBITS_HIDE_API = env.bool("LNBITS_HIDE_API", default=False)
|
|
||||||
LNBITS_SITE_TITLE = env.str("LNBITS_SITE_TITLE", default="LNbits")
|
|
||||||
LNBITS_DENOMINATION = env.str("LNBITS_DENOMINATION", default="sats")
|
|
||||||
LNBITS_SITE_TAGLINE = env.str(
|
|
||||||
"LNBITS_SITE_TAGLINE", default="free and open-source lightning wallet"
|
|
||||||
)
|
|
||||||
LNBITS_SITE_DESCRIPTION = env.str("LNBITS_SITE_DESCRIPTION", default="")
|
|
||||||
LNBITS_THEME_OPTIONS: List[str] = [
|
|
||||||
x.strip(" ")
|
|
||||||
for x in env.list(
|
|
||||||
"LNBITS_THEME_OPTIONS",
|
|
||||||
default="classic, flamingo, mint, salvador, monochrome, autumn",
|
|
||||||
subcast=str,
|
|
||||||
)
|
|
||||||
]
|
|
||||||
LNBITS_CUSTOM_LOGO = env.str("LNBITS_CUSTOM_LOGO", default="")
|
|
||||||
|
|
||||||
WALLET = wallet_class()
|
WALLET = wallet_class()
|
||||||
FAKE_WALLET = getattr(wallets_module, "FakeWallet")()
|
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)
|
|
||||||
|
|
||||||
RESERVE_FEE_MIN = env.int("LNBITS_RESERVE_FEE_MIN", default=2000)
|
|
||||||
RESERVE_FEE_PERCENT = env.float("LNBITS_RESERVE_FEE_PERCENT", default=1.0)
|
|
||||||
SERVICE_FEE = env.float("LNBITS_SERVICE_FEE", default=0.0)
|
|
||||||
|
|
||||||
try:
|
|
||||||
LNBITS_COMMIT = (
|
|
||||||
subprocess.check_output(
|
|
||||||
["git", "-C", LNBITS_PATH, "rev-parse", "HEAD"], stderr=subprocess.DEVNULL
|
|
||||||
)
|
|
||||||
.strip()
|
|
||||||
.decode("ascii")
|
|
||||||
)
|
|
||||||
except:
|
|
||||||
LNBITS_COMMIT = "unknown"
|
|
||||||
|
|
||||||
|
|
||||||
BOLTZ_NETWORK = env.str("BOLTZ_NETWORK", default="main")
|
|
||||||
BOLTZ_URL = env.str("BOLTZ_URL", default="https://boltz.exchange/api")
|
|
||||||
BOLTZ_MEMPOOL_SPACE_URL = env.str(
|
|
||||||
"BOLTZ_MEMPOOL_SPACE_URL", default="https://mempool.space"
|
|
||||||
)
|
|
||||||
BOLTZ_MEMPOOL_SPACE_URL_WS = env.str(
|
|
||||||
"BOLTZ_MEMPOOL_SPACE_URL_WS", default="wss://mempool.space"
|
|
||||||
)
|
|
||||||
|
|
|
@ -2,12 +2,12 @@ import asyncio
|
||||||
import hashlib
|
import hashlib
|
||||||
import random
|
import random
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from os import getenv
|
|
||||||
from typing import AsyncGenerator, Dict, Optional
|
from typing import AsyncGenerator, Dict, Optional
|
||||||
|
|
||||||
from environs import Env # type: ignore
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
from lnbits.settings import settings
|
||||||
|
|
||||||
from ..bolt11 import Invoice, decode, encode
|
from ..bolt11 import Invoice, decode, encode
|
||||||
from .base import (
|
from .base import (
|
||||||
InvoiceResponse,
|
InvoiceResponse,
|
||||||
|
@ -17,9 +17,6 @@ from .base import (
|
||||||
Wallet,
|
Wallet,
|
||||||
)
|
)
|
||||||
|
|
||||||
env = Env()
|
|
||||||
env.read_env()
|
|
||||||
|
|
||||||
|
|
||||||
class FakeWallet(Wallet):
|
class FakeWallet(Wallet):
|
||||||
queue: asyncio.Queue = asyncio.Queue(0)
|
queue: asyncio.Queue = asyncio.Queue(0)
|
||||||
|
@ -47,7 +44,7 @@ class FakeWallet(Wallet):
|
||||||
) -> InvoiceResponse:
|
) -> InvoiceResponse:
|
||||||
# we set a default secret since FakeWallet is used for internal=True invoices
|
# we set a default secret since FakeWallet is used for internal=True invoices
|
||||||
# and the user might not have configured a secret yet
|
# and the user might not have configured a secret yet
|
||||||
|
secret = settings.fake_wallet_secret
|
||||||
data: Dict = {
|
data: Dict = {
|
||||||
"out": False,
|
"out": False,
|
||||||
"amount": amount,
|
"amount": amount,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user