Merge branch 'main' into gerty

This commit is contained in:
ben 2022-12-19 13:03:19 +00:00
commit fcaf6967fa
8 changed files with 83 additions and 44 deletions

View file

@ -43,9 +43,6 @@ jobs:
with:
poetry-version: ${{ matrix.poetry-version }}
- name: Install dependencies
env:
VIRTUAL_ENV: ./venv
PATH: ${{ env.VIRTUAL_ENV }}/bin:${{ env.PATH }}
run: |
poetry install
- name: Run tests

View file

@ -1,7 +1,7 @@
title: "LNbits docs"
remote_theme: pmarsceill/just-the-docs
logo: "/logos/lnbits-full.png"
color_scheme: dark
logo: "/logos/lnbits-full--inverse.png"
search_enabled: true
url: https://legend.lnbits.org
aux_links:

42
docs/guide/admin_ui.md Normal file
View file

@ -0,0 +1,42 @@
---
layout: default
title: Admin UI
nav_order: 4
---
Admin UI
========
The LNbits Admin UI lets you change LNbits settings via the LNbits frontend.
It is disabled by default and the first time you set the enviroment variable LNBITS_ADMIN_UI=true
the settings are initialized and saved to the database and will be used from there as long the UI is enabled.
From there on the settings from the database are used.
Super User
==========
With the Admin UI we introduced the super user, it is created with the initialisation of the Admin UI and will be shown with a success message in the server logs.
The super user has access to the server and can change settings that may crash the server and make it unresponsive via the frontend and api, like changing funding sources.
Also only the super user can brrrr satoshis to different wallets.
The super user is only stored inside the settings table of the database and after the settings are "reset to defaults" and a restart happened,
a new super user is created.
The super user is never sent over the api and the frontend only receives a bool if you are super user or not.
We also added a decorator for the API routes to check for super user.
There is also the possibility of posting the super user via webhook to another service when it is created. you can look it up here https://github.com/lnbits/lnbits/blob/main/lnbits/settings.py `class SaaSSettings`
Admin Users
===========
enviroment variable: LNBITS_ADMIN_USERS, comma-seperated list of user ids
Admin Users can change settings in the admin ui aswell, with the exception of funding source settings, because they require e server restart and could potentially make the server inaccessable. Also they have access to all the extension defined in LNBITS_ADMIN_EXTENSIONS.
Allowed Users
=============
enviroment variable: LNBITS_ALLOWED_USERS, comma-seperated list of user ids
By defining this users, LNbits will no longer be useable by the public, only defined users and admins can then access the LNbits frontend.

View file

@ -6,7 +6,7 @@ from uuid import uuid4
from lnbits import bolt11
from lnbits.db import COCKROACH, POSTGRES, Connection
from lnbits.settings import AdminSettings, EditableSetings, SuperSettings, settings
from lnbits.settings import AdminSettings, EditableSettings, SuperSettings, settings
from . import db
from .models import BalanceCheck, Payment, User, Wallet
@ -579,7 +579,7 @@ async def delete_admin_settings():
await db.execute("DELETE FROM settings")
async def update_admin_settings(data: EditableSetings):
async def update_admin_settings(data: EditableSettings):
await db.execute(f"UPDATE settings SET editable_settings = ?", (json.dumps(data),))

View file

@ -23,7 +23,7 @@ from lnbits.helpers import url_for, urlsafe_short_hash
from lnbits.requestvars import g
from lnbits.settings import (
FAKE_WALLET,
EditableSetings,
EditableSettings,
get_wallet_class,
readonly_variables,
send_admin_user_to_saas,
@ -474,7 +474,7 @@ async def init_admin_settings(super_user: str = None):
if not account.wallets or len(account.wallets) == 0:
await create_wallet(user_id=account.id)
editable_settings = EditableSetings.from_dict(settings.dict())
editable_settings = EditableSettings.from_dict(settings.dict())
return await create_admin_settings(account.id, editable_settings.dict())

View file

@ -9,7 +9,7 @@ from lnbits.core.models import User
from lnbits.core.services import update_cached_settings, update_wallet_balance
from lnbits.decorators import check_admin, check_super_user
from lnbits.server import server_restart
from lnbits.settings import AdminSettings, EditableSetings
from lnbits.settings import AdminSettings, EditableSettings
from .. import core_app
from ..crud import delete_admin_settings, get_admin_settings, update_admin_settings
@ -28,7 +28,7 @@ async def api_get_settings(
status_code=HTTPStatus.OK,
dependencies=[Depends(check_admin)],
)
async def api_update_settings(data: EditableSetings):
async def api_update_settings(data: EditableSettings):
await update_admin_settings(data)
update_cached_settings(dict(data))
return {"status": "Success"}

View file

@ -8,7 +8,7 @@ from starlette.requests import Request
from starlette.responses import HTMLResponse
from lnbits.core.models import User
from lnbits.decorators import check_admin
from lnbits.decorators import check_user_exists
from lnbits.extensions.satspay.helpers import public_charge
from . import satspay_ext, satspay_renderer
@ -18,7 +18,7 @@ templates = Jinja2Templates(directory="templates")
@satspay_ext.get("/", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_admin)):
async def index(request: Request, user: User = Depends(check_user_exists)):
return satspay_renderer().TemplateResponse(
"satspay/index.html",
{"request": request, "user": user.dict(), "admin": user.admin},

View file

@ -22,7 +22,7 @@ def list_parse_fallback(v):
return []
class LNbitsSetings(BaseSettings):
class LNbitsSettings(BaseSettings):
def validate(cls, val):
if type(val) == str:
val = val.split(",") if val else []
@ -35,14 +35,14 @@ class LNbitsSetings(BaseSettings):
json_loads = list_parse_fallback
class UsersSetings(LNbitsSetings):
class UsersSettings(LNbitsSettings):
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=[])
class ThemesSetings(LNbitsSetings):
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)
@ -58,7 +58,7 @@ class ThemesSetings(LNbitsSetings):
lnbits_ad_space_enabled: bool = Field(default=False)
class OpsSetings(LNbitsSetings):
class OpsSettings(LNbitsSettings):
lnbits_force_https: bool = Field(default=False)
lnbits_reserve_fee_min: int = Field(default=2000)
lnbits_reserve_fee_percent: float = Field(default=1.0)
@ -67,29 +67,29 @@ class OpsSetings(LNbitsSetings):
lnbits_denomination: str = Field(default="sats")
class FakeWalletFundingSource(LNbitsSetings):
class FakeWalletFundingSource(LNbitsSettings):
fake_wallet_secret: str = Field(default="ToTheMoon1")
class LNbitsFundingSource(LNbitsSetings):
class LNbitsFundingSource(LNbitsSettings):
lnbits_endpoint: str = Field(default="https://legend.lnbits.com")
lnbits_key: Optional[str] = Field(default=None)
class ClicheFundingSource(LNbitsSetings):
class ClicheFundingSource(LNbitsSettings):
cliche_endpoint: Optional[str] = Field(default=None)
class CoreLightningFundingSource(LNbitsSetings):
class CoreLightningFundingSource(LNbitsSettings):
corelightning_rpc: Optional[str] = Field(default=None)
class EclairFundingSource(LNbitsSetings):
class EclairFundingSource(LNbitsSettings):
eclair_url: Optional[str] = Field(default=None)
eclair_pass: Optional[str] = Field(default=None)
class LndRestFundingSource(LNbitsSetings):
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)
@ -99,7 +99,7 @@ class LndRestFundingSource(LNbitsSetings):
lnd_invoice_macaroon: Optional[str] = Field(default=None)
class LndGrpcFundingSource(LNbitsSetings):
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)
@ -109,28 +109,28 @@ class LndGrpcFundingSource(LNbitsSetings):
lnd_grpc_macaroon_encrypted: Optional[str] = Field(default=None)
class LnPayFundingSource(LNbitsSetings):
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)
class LnTxtBotFundingSource(LNbitsSetings):
class LnTxtBotFundingSource(LNbitsSettings):
lntxbot_api_endpoint: Optional[str] = Field(default=None)
lntxbot_key: Optional[str] = Field(default=None)
class OpenNodeFundingSource(LNbitsSetings):
class OpenNodeFundingSource(LNbitsSettings):
opennode_api_endpoint: Optional[str] = Field(default=None)
opennode_key: Optional[str] = Field(default=None)
class SparkFundingSource(LNbitsSetings):
class SparkFundingSource(LNbitsSettings):
spark_url: Optional[str] = Field(default=None)
spark_token: Optional[str] = Field(default=None)
class LnTipsFundingSource(LNbitsSetings):
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)
@ -138,14 +138,14 @@ class LnTipsFundingSource(LNbitsSetings):
# todo: must be extracted
class BoltzExtensionSettings(LNbitsSetings):
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")
class FundingSourcesSetings(
class FundingSourcesSettings(
FakeWalletFundingSource,
LNbitsFundingSource,
ClicheFundingSource,
@ -162,11 +162,11 @@ class FundingSourcesSetings(
lnbits_backend_wallet_class: str = Field(default="VoidWallet")
class EditableSetings(
UsersSetings,
ThemesSetings,
OpsSetings,
FundingSourcesSetings,
class EditableSettings(
UsersSettings,
ThemesSettings,
OpsSettings,
FundingSourcesSettings,
BoltzExtensionSettings,
):
@validator(
@ -187,7 +187,7 @@ class EditableSetings(
)
class EnvSettings(LNbitsSetings):
class EnvSettings(LNbitsSettings):
debug: bool = Field(default=False)
host: str = Field(default="127.0.0.1")
port: int = Field(default=5000)
@ -197,18 +197,18 @@ class EnvSettings(LNbitsSetings):
super_user: str = Field(default="")
class SaaSSettings(LNbitsSetings):
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)
class PersistenceSettings(LNbitsSetings):
class PersistenceSettings(LNbitsSettings):
lnbits_data_folder: str = Field(default="./data")
lnbits_database_url: str = Field(default=None)
class SuperUserSettings(LNbitsSetings):
class SuperUserSettings(LNbitsSettings):
lnbits_allowed_funding_sources: List[str] = Field(
default=[
"VoidWallet",
@ -242,18 +242,18 @@ class ReadOnlySettings(
return [f for f in inspect.signature(cls).parameters if not f.startswith("_")]
class Settings(EditableSetings, ReadOnlySettings):
class Settings(EditableSettings, ReadOnlySettings):
@classmethod
def from_row(cls, row: Row) -> "Settings":
data = dict(row)
return cls(**data)
class SuperSettings(EditableSetings):
class SuperSettings(EditableSettings):
super_user: str
class AdminSettings(EditableSetings):
class AdminSettings(EditableSettings):
super_user: bool
lnbits_allowed_funding_sources: Optional[List[str]]