import importlib import json import subprocess from os import path from sqlite3 import Row from typing import List, Optional import httpx from loguru import logger from pydantic import BaseSettings, Field, validator def list_parse_fallback(v): try: return json.loads(v) except Exception: replaced = v.replace(" ", "") if replaced: return replaced.split(",") else: return [] readonly_variables = [ "host", "port", "debug", "lnbits_data_folder", "lnbits_database_url", "lnbits_allowed_funding_sources", "lnbits_saas_secret", "lnbits_saas_callback", "lnbits_saas_instance_id", "lnbits_admin_ui", "lnbits_commit", "lnbits_path", "forwarded_allow_ips", ] class EditableSetings(BaseSettings): # 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_title: str = Field(default="Supported by") lnbits_ad_space: str = Field( default="https://shop.lnbits.com/;/static/images/lnbits-shop-light.png;/static/images/lnbits-shop-dark.png" ) # sneaky sneaky lnbits_ad_space_enabled: bool = Field(default=False) # ops lnbits_force_https: bool = Field(default=False) 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") # funding sources lnbits_backend_wallet_class: str = Field(default="VoidWallet") 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) 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) 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) lnd_grpc_macaroon: Optional[str] = Field(default=None) lnd_grpc_macaroon_encrypted: 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) 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 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") class ReadOnlySettings(BaseSettings): lnbits_admin_ui: bool = Field(default=False) # .env debug: bool = Field(default=False) 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") # saas 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) # ops lnbits_data_folder: str = Field(default="./data") lnbits_database_url: str = Field(default=None) class Settings(EditableSetings, ReadOnlySettings): @validator( "lnbits_admin_users", "lnbits_allowed_users", "lnbits_theme_options", "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 class SuperSettings(EditableSetings): super_user: str class AdminSettings(EditableSetings): super_user: bool lnbits_allowed_funding_sources: List[str] = Field( default=[ "VoidWallet", "FakeWallet", "CLightningWallet", "LndRestWallet", "LndWallet", "LntxbotWallet", "LNPayWallet", "LNbitsWallet", "OpenNodeWallet", "LnTipsWallet", ] ) 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" # printing enviroment variable for debugging 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}") def set_cli_settings(**kwargs): for key, value in kwargs.items(): setattr(settings, key, value) wallets_module = importlib.import_module("lnbits.wallets") FAKE_WALLET = getattr(wallets_module, "FakeWallet")() # initialize as fake wallet WALLET = FAKE_WALLET # set wallet class after settings are loaded def set_wallet_class(): wallet_class = getattr(wallets_module, settings.lnbits_backend_wallet_class) global WALLET WALLET = wallet_class() def get_wallet_class(): # wallet_class = getattr(wallets_module, settings.lnbits_backend_wallet_class) return WALLET def send_admin_user_to_saas(): 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, } try: client.post( settings.lnbits_saas_callback, headers=headers, json=payload, ) logger.success("sent admin user to saas application") except: logger.error( f"error sending admin user to saas: {settings.lnbits_saas_callback}" )