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 loguru import logger
|
||||
|
||||
from lnbits.settings import Settings, settings
|
||||
|
||||
from .core import db as core_db
|
||||
from .core import migrations as core_migrations
|
||||
from .db import COCKROACH, POSTGRES, SQLITE
|
||||
|
@ -17,7 +19,8 @@ from .helpers import (
|
|||
get_valid_extensions,
|
||||
url_for_vendored,
|
||||
)
|
||||
from .settings import LNBITS_PATH
|
||||
|
||||
path = settings.lnbits_path
|
||||
|
||||
|
||||
@click.command("migrate")
|
||||
|
@ -36,15 +39,15 @@ def transpile_scss():
|
|||
warnings.simplefilter("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(LNBITS_PATH, "static/css/base.css"), "w") as css:
|
||||
with open(os.path.join(path, "static/scss/base.scss")) as scss:
|
||||
with open(os.path.join(path, "static/css/base.css"), "w") as css:
|
||||
css.write(compile_string(scss.read()))
|
||||
|
||||
|
||||
def bundle_vendored():
|
||||
for getfiles, outputpath in [
|
||||
(get_js_vendored, os.path.join(LNBITS_PATH, "static/bundle.js")),
|
||||
(get_css_vendored, os.path.join(LNBITS_PATH, "static/bundle.css")),
|
||||
(get_js_vendored, os.path.join(path, "static/bundle.js")),
|
||||
(get_css_vendored, os.path.join(path, "static/bundle.css")),
|
||||
]:
|
||||
output = ""
|
||||
for path in getfiles():
|
||||
|
@ -54,33 +57,6 @@ def bundle_vendored():
|
|||
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():
|
||||
"""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.db import COCKROACH, POSTGRES, Connection
|
||||
from lnbits.settings import DEFAULT_WALLET_NAME, LNBITS_ADMIN_USERS
|
||||
from lnbits.settings import settings
|
||||
|
||||
from . import db
|
||||
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"],
|
||||
extensions=[e[0] for e in extensions],
|
||||
wallets=[Wallet(**w) for w in wallets],
|
||||
admin=user["id"] in [x.strip() for x in LNBITS_ADMIN_USERS]
|
||||
if LNBITS_ADMIN_USERS
|
||||
admin=user["id"] in settings.lnbits_admin_users
|
||||
if settings.lnbits_admin_users
|
||||
else False,
|
||||
)
|
||||
|
||||
|
@ -99,7 +99,7 @@ async def create_wallet(
|
|||
""",
|
||||
(
|
||||
wallet_id,
|
||||
wallet_name or DEFAULT_WALLET_NAME,
|
||||
wallet_name or settings.lnbits_default_wallet_name,
|
||||
user_id,
|
||||
uuid4().hex,
|
||||
uuid4().hex,
|
||||
|
|
|
@ -21,7 +21,7 @@ from lnbits.decorators import (
|
|||
)
|
||||
from lnbits.helpers import url_for, urlsafe_short_hash
|
||||
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 . 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
|
||||
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,
|
||||
)
|
||||
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 (
|
||||
currencies,
|
||||
fiat_amount_as_satoshis,
|
||||
|
@ -76,7 +76,7 @@ async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)):
|
|||
async def api_update_balance(
|
||||
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(
|
||||
status_code=HTTPStatus.FORBIDDEN, detail="Not an admin user"
|
||||
)
|
||||
|
@ -178,7 +178,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
|
|||
else:
|
||||
description_hash = b""
|
||||
unhashed_description = b""
|
||||
memo = data.memo or LNBITS_SITE_TITLE
|
||||
memo = data.memo or settings.lnbits_site_title
|
||||
if data.unit == "sat":
|
||||
amount = int(data.amount)
|
||||
else:
|
||||
|
@ -678,7 +678,7 @@ async def img(request: Request, data):
|
|||
|
||||
@core_app.get("/api/v1/audit/")
|
||||
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(
|
||||
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.helpers import template_renderer, url_for
|
||||
from lnbits.requestvars import g
|
||||
from lnbits.settings import (
|
||||
LNBITS_ADMIN_UI,
|
||||
LNBITS_ADMIN_USERS,
|
||||
LNBITS_ALLOWED_USERS,
|
||||
LNBITS_CUSTOM_LOGO,
|
||||
LNBITS_SITE_TITLE,
|
||||
SERVICE_FEE,
|
||||
)
|
||||
from lnbits.settings import settings
|
||||
|
||||
from ...helpers import get_valid_extensions
|
||||
from ..crud import (
|
||||
|
@ -119,14 +112,6 @@ async def wallet(
|
|||
user_id = usr.hex if usr else None
|
||||
wallet_id = wal.hex if wal else None
|
||||
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:
|
||||
user = await get_user((await create_account()).id)
|
||||
|
@ -137,11 +122,14 @@ async def wallet(
|
|||
return template_renderer().TemplateResponse(
|
||||
"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(
|
||||
"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
|
||||
if not wallet_id:
|
||||
if user.wallets and not wallet_name: # type: ignore
|
||||
|
@ -172,7 +160,7 @@ async def wallet(
|
|||
"request": request,
|
||||
"user": user.dict(), # type: ignore
|
||||
"wallet": userwallet.dict(),
|
||||
"service_fee": service_fee,
|
||||
"service_fee": settings.lnbits_service_fee,
|
||||
"web_manifest": f"/manifest/{user.id}.webmanifest", # type: ignore
|
||||
},
|
||||
)
|
||||
|
@ -194,7 +182,7 @@ async def lnurl_full_withdraw(request: Request):
|
|||
"k1": "0",
|
||||
"minWithdrawable": 1000 if wallet.withdrawable_balance else 0,
|
||||
"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),
|
||||
}
|
||||
|
||||
|
@ -293,12 +281,12 @@ async def manifest(usr: str):
|
|||
raise HTTPException(status_code=HTTPStatus.NOT_FOUND)
|
||||
|
||||
return {
|
||||
"short_name": LNBITS_SITE_TITLE,
|
||||
"name": LNBITS_SITE_TITLE + " Wallet",
|
||||
"short_name": settings.lnbits_site_title,
|
||||
"name": settings.lnbits_site_title + " Wallet",
|
||||
"icons": [
|
||||
{
|
||||
"src": LNBITS_CUSTOM_LOGO
|
||||
if LNBITS_CUSTOM_LOGO
|
||||
"src": settings.lnbits_custom_logo
|
||||
if settings.lnbits_custom_logo
|
||||
else "https://cdn.jsdelivr.net/gh/lnbits/lnbits@0.3.0/docs/logos/lnbits.png",
|
||||
"type": "image/png",
|
||||
"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.strategy import ASYNCIO_STRATEGY # type: ignore
|
||||
|
||||
from .settings import LNBITS_DATA_FOLDER, LNBITS_DATABASE_URL
|
||||
from lnbits.settings import settings
|
||||
|
||||
POSTGRES = "POSTGRES"
|
||||
COCKROACH = "COCKROACH"
|
||||
|
@ -85,8 +85,8 @@ class Database(Compat):
|
|||
def __init__(self, db_name: str):
|
||||
self.name = db_name
|
||||
|
||||
if LNBITS_DATABASE_URL:
|
||||
database_uri = LNBITS_DATABASE_URL
|
||||
if settings.lnbits_database_url:
|
||||
database_uri = settings.lnbits_database_url
|
||||
|
||||
if database_uri.startswith("cockroachdb://"):
|
||||
self.type = COCKROACH
|
||||
|
@ -131,14 +131,16 @@ class Database(Compat):
|
|||
)
|
||||
)
|
||||
else:
|
||||
if os.path.isdir(LNBITS_DATA_FOLDER):
|
||||
self.path = os.path.join(LNBITS_DATA_FOLDER, f"{self.name}.sqlite3")
|
||||
if os.path.isdir(settings.lnbits_data_folder):
|
||||
self.path = os.path.join(
|
||||
settings.lnbits_data_folder, f"{self.name}.sqlite3"
|
||||
)
|
||||
database_uri = f"sqlite:///{self.path}"
|
||||
self.type = SQLITE
|
||||
else:
|
||||
raise NotADirectoryError(
|
||||
f"LNBITS_DATA_FOLDER named {LNBITS_DATA_FOLDER} was not created"
|
||||
f" - please 'mkdir {LNBITS_DATA_FOLDER}' and try again"
|
||||
f"LNBITS_DATA_FOLDER named {settings.lnbits_data_folder} was not created"
|
||||
f" - please 'mkdir {settings.lnbits_data_folder}' and try again"
|
||||
)
|
||||
logger.trace(f"database {self.type} added for {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.models import User, Wallet
|
||||
from lnbits.requestvars import g
|
||||
from lnbits.settings import (
|
||||
LNBITS_ADMIN_EXTENSIONS,
|
||||
LNBITS_ADMIN_UI,
|
||||
LNBITS_ADMIN_USERS,
|
||||
LNBITS_ALLOWED_USERS,
|
||||
)
|
||||
from lnbits.settings import settings
|
||||
|
||||
|
||||
class KeyChecker(SecurityBase):
|
||||
|
@ -139,11 +134,6 @@ async def get_key_type(
|
|||
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(
|
||||
[0, 1], [WalletAdminKeyChecker, WalletInvoiceKeyChecker]
|
||||
):
|
||||
|
@ -156,8 +146,12 @@ async def get_key_type(
|
|||
status_code=HTTPStatus.NOT_FOUND, detail="Wallet does not exist."
|
||||
)
|
||||
if (
|
||||
LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS
|
||||
) and (LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS):
|
||||
settings.lnbits_admin_users
|
||||
and wallet.wallet.user not in settings.lnbits_admin_users
|
||||
) and (
|
||||
settings.lnbits_admin_extensions
|
||||
and pathname in settings.lnbits_admin_extensions
|
||||
):
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.FORBIDDEN,
|
||||
detail="User not authorized for this extension.",
|
||||
|
@ -233,22 +227,33 @@ async def require_invoice_key(
|
|||
|
||||
async def check_user_exists(usr: UUID4) -> User:
|
||||
g().user = await get_user(usr.hex)
|
||||
|
||||
if not g().user:
|
||||
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:
|
||||
LNBITS_ADMIN_USERS = g().admin_conf.admin_users
|
||||
LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users
|
||||
else:
|
||||
LNBITS_ADMIN_USERS = []
|
||||
LNBITS_ALLOWED_USERS = []
|
||||
|
||||
if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS:
|
||||
if (
|
||||
len(settings.lnbits_allowed_users) > 0
|
||||
and g().user.id not in settings.lnbits_allowed_users
|
||||
):
|
||||
raise HTTPException(
|
||||
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
|
||||
|
||||
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 shortuuid # type: ignore
|
||||
|
||||
import lnbits.settings as settings
|
||||
from lnbits.jinja2_templating import Jinja2Templates
|
||||
from lnbits.requestvars import g
|
||||
from lnbits.settings import settings
|
||||
|
||||
|
||||
class Extension(NamedTuple):
|
||||
|
@ -24,15 +24,10 @@ class Extension(NamedTuple):
|
|||
|
||||
class ExtensionManager:
|
||||
def __init__(self):
|
||||
if settings.LNBITS_ADMIN_UI:
|
||||
settings.LNBITS_DISABLED_EXTENSIONS = g().admin_conf.disabled_ext
|
||||
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._disabled: List[str] = settings.lnbits_disabled_extensions
|
||||
self._admin_only: List[str] = settings.lnbits_admin_extensions
|
||||
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]
|
||||
|
||||
@property
|
||||
|
@ -48,7 +43,7 @@ class ExtensionManager:
|
|||
try:
|
||||
with open(
|
||||
os.path.join(
|
||||
settings.LNBITS_PATH, "extensions", extension, "config.json"
|
||||
settings.lnbits_path, "extensions", extension, "config.json"
|
||||
)
|
||||
) as 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]:
|
||||
paths: List[str] = []
|
||||
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):
|
||||
# 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:
|
||||
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:
|
||||
|
@ -163,16 +158,6 @@ def removeEmptyString(arr):
|
|||
|
||||
|
||||
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(
|
||||
loader=jinja2.FileSystemLoader(
|
||||
|
@ -180,20 +165,21 @@ def template_renderer(additional_folders: List = []) -> Jinja2Templates:
|
|||
)
|
||||
)
|
||||
|
||||
if 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.lnbits_ad_space:
|
||||
t.env.globals["AD_SPACE"] = settings.lnbits_ad_space
|
||||
|
||||
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_CSS"] = map(url_for_vendored, get_css_vendored())
|
||||
else:
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
import asyncio
|
||||
import uvloop
|
||||
uvloop.install()
|
||||
|
||||
import contextlib
|
||||
import multiprocessing as mp
|
||||
import sys
|
||||
import time
|
||||
|
||||
import click
|
||||
import uvicorn
|
||||
|
||||
from lnbits.settings import HOST, PORT
|
||||
from lnbits.settings import settings
|
||||
|
||||
|
||||
@click.command(
|
||||
|
@ -12,12 +19,13 @@ from lnbits.settings import HOST, PORT
|
|||
allow_extra_args=True,
|
||||
)
|
||||
)
|
||||
@click.option("--port", default=PORT, help="Port to listen on")
|
||||
@click.option("--host", default=HOST, help="Host to run LNBits on")
|
||||
@click.option("--port", default=settings.port, help="Port to listen 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-certfile", default=None, help="Path to SSL certificate")
|
||||
@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"""
|
||||
# this beautiful beast parses all command line arguments and passes them to the uvicorn server
|
||||
d = dict()
|
||||
|
@ -33,17 +41,31 @@ def main(ctx, port: int, host: str, ssl_keyfile: str, ssl_certfile: str):
|
|||
else:
|
||||
d[a.strip("--")] = True # argument like --key
|
||||
|
||||
config = uvicorn.Config(
|
||||
"lnbits.__main__:app",
|
||||
port=port,
|
||||
host=host,
|
||||
ssl_keyfile=ssl_keyfile,
|
||||
ssl_certfile=ssl_certfile,
|
||||
**d
|
||||
)
|
||||
server = uvicorn.Server(config)
|
||||
server.run()
|
||||
while True:
|
||||
# loop = asyncio.new_event_loop()
|
||||
config = uvicorn.Config(
|
||||
"lnbits.__main__:app",
|
||||
port=port,
|
||||
host=host,
|
||||
reload=reload,
|
||||
# loop=loop,
|
||||
ssl_keyfile=ssl_keyfile,
|
||||
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__":
|
||||
main()
|
||||
|
|
|
@ -1,88 +1,220 @@
|
|||
import importlib
|
||||
import json
|
||||
import subprocess
|
||||
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")
|
||||
wallet_class = getattr(
|
||||
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_class = getattr(wallets_module, settings.lnbits_backend_wallet_class)
|
||||
WALLET = wallet_class()
|
||||
FAKE_WALLET = getattr(wallets_module, "FakeWallet")()
|
||||
DEFAULT_WALLET_NAME = env.str("LNBITS_DEFAULT_WALLET_NAME", default="LNbits wallet")
|
||||
PREFER_SECURE_URLS = env.bool("LNBITS_FORCE_HTTPS", default=True)
|
||||
|
||||
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 random
|
||||
from datetime import datetime
|
||||
from os import getenv
|
||||
from typing import AsyncGenerator, Dict, Optional
|
||||
|
||||
from environs import Env # type: ignore
|
||||
from loguru import logger
|
||||
|
||||
from lnbits.settings import settings
|
||||
|
||||
from ..bolt11 import Invoice, decode, encode
|
||||
from .base import (
|
||||
InvoiceResponse,
|
||||
|
@ -17,9 +17,6 @@ from .base import (
|
|||
Wallet,
|
||||
)
|
||||
|
||||
env = Env()
|
||||
env.read_env()
|
||||
|
||||
|
||||
class FakeWallet(Wallet):
|
||||
queue: asyncio.Queue = asyncio.Queue(0)
|
||||
|
@ -47,7 +44,7 @@ class FakeWallet(Wallet):
|
|||
) -> InvoiceResponse:
|
||||
# we set a default secret since FakeWallet is used for internal=True invoices
|
||||
# and the user might not have configured a secret yet
|
||||
|
||||
secret = settings.fake_wallet_secret
|
||||
data: Dict = {
|
||||
"out": False,
|
||||
"amount": amount,
|
||||
|
|
Loading…
Reference in New Issue
Block a user