lnbits-legend/lnbits/settings.py

392 lines
12 KiB
Python
Raw Normal View History

import importlib
import importlib.metadata
2022-12-09 12:14:22 +01:00
import inspect
import json
import subprocess
from os import path
from sqlite3 import Row
from typing import Any, List, Optional
2022-10-12 13:08:59 +02:00
import httpx
from loguru import logger
from pydantic import BaseSettings, Extra, Field, validator
2020-01-15 16:19:54 +01:00
def list_parse_fallback(v):
try:
return json.loads(v)
2022-10-12 13:08:59 +02:00
except Exception:
replaced = v.replace(" ", "")
if replaced:
return replaced.split(",")
else:
return []
2022-12-19 09:10:41 +01:00
class LNbitsSettings(BaseSettings):
@classmethod
def validate(cls, val):
if type(val) == str:
val = val.split(",") if val else []
return val
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
case_sensitive = False
json_loads = list_parse_fallback
extra = Extra.ignore
2022-12-19 09:10:41 +01:00
class UsersSettings(LNbitsSettings):
lnbits_admin_users: List[str] = Field(default=[])
lnbits_allowed_users: List[str] = Field(default=[])
class ExtensionsSettings(LNbitsSettings):
lnbits_admin_extensions: List[str] = Field(default=[])
lnbits_extensions_manifests: List[str] = Field(
default=[
"https://raw.githubusercontent.com/lnbits/lnbits-extensions/main/extensions.json"
]
)
lnbits_extensions_default_install: List[str] = Field(default=[])
# required due to GitHUb rate-limit
lnbits_ext_github_token: str = Field(default="")
class InstalledExtensionsSettings(LNbitsSettings):
# installed extensions that have been deactivated
lnbits_deactivated_extensions: List[str] = Field(default=[])
# upgraded extensions that require API redirects
2022-12-22 16:03:26 +01:00
lnbits_upgraded_extensions: List[str] = Field(default=[])
# list of redirects that extensions want to perform
lnbits_extensions_redirects: List[Any] = Field(default=[])
2022-12-22 16:07:27 +01:00
2022-12-19 09:10:41 +01:00
class ThemesSettings(LNbitsSettings):
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(
2023-02-20 23:45:52 +01:00
default=[
"classic",
"freedom",
"mint",
"salvador",
"monochrome",
"autumn",
"cyber",
]
)
lnbits_custom_logo: str = Field(default=None)
lnbits_ad_space_title: str = Field(default="Supported by")
2022-12-02 15:38:21 +01:00
lnbits_ad_space: str = Field(
2022-12-02 15:59:02 +01:00
default="https://shop.lnbits.com/;/static/images/lnbits-shop-light.png;/static/images/lnbits-shop-dark.png"
2022-12-02 15:38:21 +01:00
) # sneaky sneaky
2022-12-02 15:36:09 +01:00
lnbits_ad_space_enabled: bool = Field(default=False)
2019-12-18 14:44:58 +01:00
2022-12-19 09:10:41 +01:00
class OpsSettings(LNbitsSettings):
lnbits_baseurl: str = Field(default="http://127.0.0.1:5000/")
lnbits_reserve_fee_min: int = Field(default=2000)
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")
2022-12-19 09:10:41 +01:00
class FakeWalletFundingSource(LNbitsSettings):
fake_wallet_secret: str = Field(default="ToTheMoon1")
2022-12-19 09:10:41 +01:00
class LNbitsFundingSource(LNbitsSettings):
lnbits_endpoint: str = Field(default="https://legend.lnbits.com")
lnbits_key: Optional[str] = Field(default=None)
lnbits_admin_key: Optional[str] = Field(default=None)
lnbits_invoice_key: Optional[str] = Field(default=None)
2022-12-19 09:10:41 +01:00
class ClicheFundingSource(LNbitsSettings):
cliche_endpoint: Optional[str] = Field(default=None)
2022-12-19 09:10:41 +01:00
class CoreLightningFundingSource(LNbitsSettings):
corelightning_rpc: Optional[str] = Field(default=None)
2022-12-19 15:54:31 +01:00
clightning_rpc: Optional[str] = Field(default=None)
2022-12-19 09:10:41 +01:00
class EclairFundingSource(LNbitsSettings):
eclair_url: Optional[str] = Field(default=None)
eclair_pass: Optional[str] = Field(default=None)
2022-12-19 09:10:41 +01:00
class LndRestFundingSource(LNbitsSettings):
lnd_rest_endpoint: Optional[str] = Field(default=None)
lnd_rest_cert: Optional[str] = Field(default=None)
lnd_rest_macaroon: Optional[str] = Field(default=None)
lnd_rest_macaroon_encrypted: Optional[str] = Field(default=None)
lnd_cert: Optional[str] = Field(default=None)
lnd_admin_macaroon: Optional[str] = Field(default=None)
lnd_invoice_macaroon: Optional[str] = Field(default=None)
2023-01-10 17:25:12 +01:00
lnd_rest_admin_macaroon: Optional[str] = Field(default=None)
lnd_rest_invoice_macaroon: Optional[str] = Field(default=None)
2022-12-19 09:10:41 +01:00
class LndGrpcFundingSource(LNbitsSettings):
lnd_grpc_endpoint: Optional[str] = Field(default=None)
lnd_grpc_cert: Optional[str] = Field(default=None)
lnd_grpc_port: Optional[int] = Field(default=None)
lnd_grpc_admin_macaroon: Optional[str] = Field(default=None)
lnd_grpc_invoice_macaroon: Optional[str] = Field(default=None)
2022-11-24 15:43:15 +01:00
lnd_grpc_macaroon: Optional[str] = Field(default=None)
lnd_grpc_macaroon_encrypted: Optional[str] = Field(default=None)
2022-12-19 09:10:41 +01:00
class LnPayFundingSource(LNbitsSettings):
lnpay_api_endpoint: Optional[str] = Field(default=None)
lnpay_api_key: Optional[str] = Field(default=None)
lnpay_wallet_key: Optional[str] = Field(default=None)
lnpay_admin_key: Optional[str] = Field(default=None)
2022-12-19 09:10:41 +01:00
class OpenNodeFundingSource(LNbitsSettings):
opennode_api_endpoint: Optional[str] = Field(default=None)
opennode_key: Optional[str] = Field(default=None)
opennode_admin_key: Optional[str] = Field(default=None)
opennode_invoice_key: Optional[str] = Field(default=None)
2022-12-19 09:10:41 +01:00
class SparkFundingSource(LNbitsSettings):
spark_url: Optional[str] = Field(default=None)
spark_token: Optional[str] = Field(default=None)
2022-12-19 09:10:41 +01:00
class LnTipsFundingSource(LNbitsSettings):
lntips_api_endpoint: Optional[str] = Field(default=None)
lntips_api_key: Optional[str] = Field(default=None)
lntips_admin_key: Optional[str] = Field(default=None)
lntips_invoice_key: Optional[str] = Field(default=None)
Boltz.exchange Extension (#922) * initial commit and still draft, ready for review * forgot to uncomment this line * fee estimation and blockheight * resolve conversation with michael, to use mempool websockets instead of boltz status event * Update lnbits/extensions/boltz/boltz.py Co-authored-by: michael1011 <me@michael1011.at> * add status to swaps, add sorting and data into listing * add swap status checks, change urls to docker test setup, dynamic minimum and maximum limits * fix docker hosts for development * add api endpoints to _api_docs * add wallet name and id, to list and status information * fix status_update for reverse_swaps * chore: format with black * more blackformatting and refactoring create_swap() * fix variable bug * check if swap is already refunded * use create_task instead of ensure_future * add mempool and boltz urls depending on DEBUG .env * raise exception in mempool fails * fix onchain txs, sending funds to wrong address and add a refund address for normal swaps beforehand * add status to swaps, add sorting and data into listing * add swap status checks, change urls to docker test setup, dynamic minimum and maximum limits * add wallet name and id, to list and status information * fix status_update for reverse_swaps * chore: format with black * use create_task instead of ensure_future * add mempool and boltz urls depending on DEBUG .env * fix onchain txs, sending funds to wrong address and add a refund address for normal swaps beforehand * black formatting * add some logging with loguru, and remove function duplication * cleanup readme * updates/suggestions from calle Co-authored-by: calle <93376500+callebtc@users.noreply.github.com> * remove unused comments * Update API Endpoints Co-authored-by: calle <93376500+callebtc@users.noreply.github.com> * un-factor get_boltz_pairs * added a explaination for the onchain tx * remove unused template file * rename api endpoints * fix isort and prettier * more verbose logging!! * add boltz to mock_data.zip * new mockdata * remove comment * better readme * fix mempool urls * change /refund /check /status to post requests * first step in tests2 * add first tests * change refund,check,status to post requests * next try on tests * overall code improvements * just testing tests * throw http exceptions in views_api * require admincheck for refund,check,status and added fastapi documentation for those * added more tests * black * many code improvements * adding tests * temp fix test * fix race condition when pay_invoice fails * test are working * add boltz env variables * add startup check, bugfixes, improvements * improve on status checking * remove check_invoice_status * more fixes and tests * testing testing testing * make tests run again inside regtest * fix bad error :O * fix postgres boolean bug and add swap test * Update README.md Update README.md Update README.md Update README.md * some mypy * blacked * the missing commit? * fix api_docs readme link * better refunding error catching fix * check swaps now also shows pending reverse swap, ui improvements, tooltips * add backend check for boltz limits fixup * many improvements, startup check for swaps working, reverse needs more testing * little last fixes * remove unused logic * fastapi documentation fixup * formatting and remove unused tests * fix test * fix swapstatus model * Update lnbits/extensions/boltz/tasks.py Co-authored-by: calle <93376500+callebtc@users.noreply.github.com> * Update lnbits/extensions/boltz/views_api.py Co-authored-by: calle <93376500+callebtc@users.noreply.github.com> * balance check msg, format * fix mypy data override * fix swapstatus, remove can refund column * Update lnbits/extensions/boltz/README.md Co-authored-by: michael1011 <me@michael1011.at> * empty lines * fix error message when swap is not found * remove preimage_hash from database * fix api_docs html fix api_docs html * catch boltz network exceptions better * formatting * check for timeout on swap at get request Co-authored-by: michael1011 <me@michael1011.at> Co-authored-by: fusion44 <some.fusion@gmail.com> Co-authored-by: calle <93376500+callebtc@users.noreply.github.com>
2022-08-30 12:51:17 +02:00
# todo: must be extracted
2022-12-19 09:10:41 +01:00
class BoltzExtensionSettings(LNbitsSettings):
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")
Boltz.exchange Extension (#922) * initial commit and still draft, ready for review * forgot to uncomment this line * fee estimation and blockheight * resolve conversation with michael, to use mempool websockets instead of boltz status event * Update lnbits/extensions/boltz/boltz.py Co-authored-by: michael1011 <me@michael1011.at> * add status to swaps, add sorting and data into listing * add swap status checks, change urls to docker test setup, dynamic minimum and maximum limits * fix docker hosts for development * add api endpoints to _api_docs * add wallet name and id, to list and status information * fix status_update for reverse_swaps * chore: format with black * more blackformatting and refactoring create_swap() * fix variable bug * check if swap is already refunded * use create_task instead of ensure_future * add mempool and boltz urls depending on DEBUG .env * raise exception in mempool fails * fix onchain txs, sending funds to wrong address and add a refund address for normal swaps beforehand * add status to swaps, add sorting and data into listing * add swap status checks, change urls to docker test setup, dynamic minimum and maximum limits * add wallet name and id, to list and status information * fix status_update for reverse_swaps * chore: format with black * use create_task instead of ensure_future * add mempool and boltz urls depending on DEBUG .env * fix onchain txs, sending funds to wrong address and add a refund address for normal swaps beforehand * black formatting * add some logging with loguru, and remove function duplication * cleanup readme * updates/suggestions from calle Co-authored-by: calle <93376500+callebtc@users.noreply.github.com> * remove unused comments * Update API Endpoints Co-authored-by: calle <93376500+callebtc@users.noreply.github.com> * un-factor get_boltz_pairs * added a explaination for the onchain tx * remove unused template file * rename api endpoints * fix isort and prettier * more verbose logging!! * add boltz to mock_data.zip * new mockdata * remove comment * better readme * fix mempool urls * change /refund /check /status to post requests * first step in tests2 * add first tests * change refund,check,status to post requests * next try on tests * overall code improvements * just testing tests * throw http exceptions in views_api * require admincheck for refund,check,status and added fastapi documentation for those * added more tests * black * many code improvements * adding tests * temp fix test * fix race condition when pay_invoice fails * test are working * add boltz env variables * add startup check, bugfixes, improvements * improve on status checking * remove check_invoice_status * more fixes and tests * testing testing testing * make tests run again inside regtest * fix bad error :O * fix postgres boolean bug and add swap test * Update README.md Update README.md Update README.md Update README.md * some mypy * blacked * the missing commit? * fix api_docs readme link * better refunding error catching fix * check swaps now also shows pending reverse swap, ui improvements, tooltips * add backend check for boltz limits fixup * many improvements, startup check for swaps working, reverse needs more testing * little last fixes * remove unused logic * fastapi documentation fixup * formatting and remove unused tests * fix test * fix swapstatus model * Update lnbits/extensions/boltz/tasks.py Co-authored-by: calle <93376500+callebtc@users.noreply.github.com> * Update lnbits/extensions/boltz/views_api.py Co-authored-by: calle <93376500+callebtc@users.noreply.github.com> * balance check msg, format * fix mypy data override * fix swapstatus, remove can refund column * Update lnbits/extensions/boltz/README.md Co-authored-by: michael1011 <me@michael1011.at> * empty lines * fix error message when swap is not found * remove preimage_hash from database * fix api_docs html fix api_docs html * catch boltz network exceptions better * formatting * check for timeout on swap at get request Co-authored-by: michael1011 <me@michael1011.at> Co-authored-by: fusion44 <some.fusion@gmail.com> Co-authored-by: calle <93376500+callebtc@users.noreply.github.com>
2022-08-30 12:51:17 +02:00
class LightningSettings(LNbitsSettings):
lightning_invoice_expiry: int = Field(default=600)
2022-12-19 09:10:41 +01:00
class FundingSourcesSettings(
FakeWalletFundingSource,
LNbitsFundingSource,
ClicheFundingSource,
CoreLightningFundingSource,
EclairFundingSource,
LndRestFundingSource,
LndGrpcFundingSource,
LnPayFundingSource,
OpenNodeFundingSource,
SparkFundingSource,
LnTipsFundingSource,
):
lnbits_backend_wallet_class: str = Field(default="VoidWallet")
2022-12-19 09:10:41 +01:00
class EditableSettings(
UsersSettings,
ExtensionsSettings,
2022-12-19 09:10:41 +01:00
ThemesSettings,
OpsSettings,
FundingSourcesSettings,
BoltzExtensionSettings,
LightningSettings,
):
@validator(
"lnbits_admin_users",
"lnbits_allowed_users",
"lnbits_theme_options",
"lnbits_admin_extensions",
pre=True,
)
@classmethod
def validate_editable_settings(cls, val):
return super().validate(val)
2022-12-09 12:14:22 +01:00
@classmethod
def from_dict(cls, d: dict):
return cls(
**{k: v for k, v in d.items() if k in inspect.signature(cls).parameters}
)
2022-12-19 09:10:41 +01:00
class EnvSettings(LNbitsSettings):
debug: bool = Field(default=False)
bundle_assets: bool = Field(default=True)
host: str = Field(default="127.0.0.1")
port: int = Field(default=5000)
forwarded_allow_ips: str = Field(default="*")
lnbits_path: str = Field(default=".")
lnbits_commit: str = Field(default="unknown")
super_user: str = Field(default="")
version: str = Field(default="0.0.0")
2022-12-09 14:13:58 +01:00
2022-12-19 09:10:41 +01:00
class SaaSSettings(LNbitsSettings):
lnbits_saas_callback: Optional[str] = Field(default=None)
lnbits_saas_secret: Optional[str] = Field(default=None)
lnbits_saas_instance_id: Optional[str] = Field(default=None)
2022-12-09 14:13:58 +01:00
2022-12-19 09:10:41 +01:00
class PersistenceSettings(LNbitsSettings):
lnbits_data_folder: str = Field(default="./data")
lnbits_database_url: str = Field(default=None)
2022-12-09 14:13:58 +01:00
2022-12-19 09:10:41 +01:00
class SuperUserSettings(LNbitsSettings):
lnbits_allowed_funding_sources: List[str] = Field(
default=[
"VoidWallet",
"FakeWallet",
"CoreLightningWallet",
"LndRestWallet",
"EclairWallet",
"LndWallet",
"LnTipsWallet",
"LNPayWallet",
"LNbitsWallet",
"OpenNodeWallet",
]
)
2022-12-09 14:13:58 +01:00
class TransientSettings(InstalledExtensionsSettings):
# Transient Settings:
# - are initialized, updated and used at runtime
# - are not read from a file or from the `settings` table
2023-01-20 17:26:58 +01:00
# - are not persisted in the `settings` table when the settings are updated
# - are cleared on server restart
@classmethod
def readonly_fields(cls):
return [f for f in inspect.signature(cls).parameters if not f.startswith("_")]
2022-12-09 14:13:58 +01:00
class ReadOnlySettings(
EnvSettings,
SaaSSettings,
PersistenceSettings,
SuperUserSettings,
2022-12-09 14:13:58 +01:00
):
lnbits_admin_ui: bool = Field(default=False)
@validator(
"lnbits_allowed_funding_sources",
pre=True,
)
@classmethod
def validate_readonly_settings(cls, val):
return super().validate(val)
@classmethod
def readonly_fields(cls):
return [f for f in inspect.signature(cls).parameters if not f.startswith("_")]
class Settings(EditableSettings, ReadOnlySettings, TransientSettings):
@classmethod
def from_row(cls, row: Row) -> "Settings":
data = dict(row)
return cls(**data)
2022-12-19 09:10:41 +01:00
class SuperSettings(EditableSettings):
super_user: str
2022-12-19 09:10:41 +01:00
class AdminSettings(EditableSettings):
super_user: bool
lnbits_allowed_funding_sources: Optional[List[str]]
2022-10-03 23:33:42 +02:00
def set_cli_settings(**kwargs):
for key, value in kwargs.items():
setattr(settings, key, value)
2022-10-03 23:41:04 +02:00
# set wallet class after settings are loaded
def set_wallet_class(class_name: Optional[str] = None):
backend_wallet_class = class_name or settings.lnbits_backend_wallet_class
wallet_class = getattr(wallets_module, backend_wallet_class)
global WALLET
WALLET = wallet_class()
def get_wallet_class():
# wallet_class = getattr(wallets_module, settings.lnbits_backend_wallet_class)
return WALLET
2022-12-05 15:43:26 +01:00
def send_admin_user_to_saas():
2022-12-05 16:10:35 +01:00
if settings.lnbits_saas_callback:
with httpx.Client() as client:
headers = {
"Content-Type": "application/json; charset=utf-8",
"X-API-KEY": settings.lnbits_saas_secret,
}
payload = {
"instance_id": settings.lnbits_saas_instance_id,
"adminuser": settings.super_user,
2022-12-05 16:10:35 +01:00
}
try:
client.post(
settings.lnbits_saas_callback,
headers=headers,
json=payload,
)
2022-12-12 12:27:52 +01:00
logger.success("sent super_user to saas application")
2022-12-12 09:20:27 +01:00
except Exception as e:
2022-12-05 16:10:35 +01:00
logger.error(
2022-12-12 12:27:52 +01:00
f"error sending super_user to saas: {settings.lnbits_saas_callback}. Error: {str(e)}"
2022-12-05 16:10:35 +01:00
)
readonly_variables = ReadOnlySettings.readonly_fields()
transient_variables = TransientSettings.readonly_fields()
settings = Settings()
settings.lnbits_path = str(path.dirname(path.realpath(__file__)))
try:
settings.lnbits_commit = (
subprocess.check_output(
["git", "-C", settings.lnbits_path, "rev-parse", "HEAD"],
stderr=subprocess.DEVNULL,
)
.strip()
.decode("ascii")
)
except:
settings.lnbits_commit = "docker"
settings.version = importlib.metadata.version("lnbits")
# printing environment variable for debugging
if not settings.lnbits_admin_ui:
logger.debug("Environment Settings:")
for key, value in settings.dict(exclude_none=True).items():
logger.debug(f"{key}: {value}")
wallets_module = importlib.import_module("lnbits.wallets")
FAKE_WALLET = getattr(wallets_module, "FakeWallet")()
# initialize as fake wallet
WALLET = FAKE_WALLET