From 91a5f7d2143ac4e2eb4f58607a062c1386bea6e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 10 Oct 2022 23:27:46 +0200 Subject: [PATCH 1/2] add callback for saas app --- lnbits/app.py | 2 +- lnbits/extensions/admin/migrations.py | 3 +++ lnbits/extensions/admin/models.py | 10 ++++----- lnbits/extensions/admin/views_api.py | 5 ----- lnbits/settings.py | 32 +++++++++++++++++++++++---- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index a8371950..6fa5cf5d 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -64,7 +64,7 @@ def create_app() -> FastAPI: # TODO: why those 2? g().config = settings - # g().base_url = f"http://{settings.host}:{settings.port}" + g().base_url = f"http://{settings.host}:{settings.port}" app.add_middleware(GZipMiddleware, minimum_size=1000) diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index c4bc98d8..ea698c27 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -6,6 +6,9 @@ async def m001_create_admin_settings_table(db): debug TEXT, host TEXT, port INTEGER, + lnbits_saas_instance_id TEXT, + lnbits_saas_callback TEXT, + lnbits_saas_secret TEXT, lnbits_path TEXT, lnbits_commit TEXT, lnbits_admin_users TEXT, diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index 94fa56bb..ada84e9d 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -6,10 +6,10 @@ from pydantic import BaseModel class UpdateSettings(BaseModel): lnbits_backend_wallet_class: str = Query(None) - lnbits_admin_users: List[str] = Query(None) - lnbits_allowed_users: List[str] = Query(None) - lnbits_admin_ext: List[str] = Query(None) - lnbits_disabled_ext: List[str] = Query(None) + lnbits_admin_users: List[str] = Query(None) + lnbits_allowed_users: List[str] = Query(None) + lnbits_admin_ext: List[str] = Query(None) + lnbits_disabled_ext: List[str] = Query(None) lnbits_funding_source: str = Query(None) lnbits_force_https: bool = Query(None) lnbits_reserve_fee_min: int = Query(None, ge=0) @@ -23,4 +23,4 @@ class UpdateSettings(BaseModel): lnbits_denomination: str = Query(None) lnbits_theme: str = Query(None) lnbits_custom_logo: str = Query(None) - lnbits_ad_space: List[str] = Query(None) + lnbits_ad_space: List[str] = Query(None) diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index 19b52e35..ae2959bc 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -53,8 +53,3 @@ async def api_delete_settings( ): await delete_settings() return {"status": "Success"} - - -@admin_ext.get("/api/v1/backup/", status_code=HTTPStatus.OK) -async def api_backup(user: User = Depends(check_admin)): - return {"status": "not implemented"} diff --git a/lnbits/settings.py b/lnbits/settings.py index ffcdcc0a..f183211c 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -1,6 +1,7 @@ import importlib import json import subprocess +import httpx from os import path from sqlite3 import Row from typing import List, Optional @@ -9,6 +10,7 @@ from loguru import logger from pydantic import BaseSettings, Field, validator + def list_parse_fallback(v): try: return json.loads(v) @@ -34,6 +36,11 @@ class Settings(BaseSettings): 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) + # users lnbits_admin_users: List[str] = Field(default=[]) lnbits_allowed_users: List[str] = Field(default=[]) @@ -230,11 +237,28 @@ async def check_admin_settings(): 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}" - ) + + admin_url = f"{http}://{settings.host}:{settings.port}/wallet?usr={user}" + logger.warning(f"✔️ Access admin user account at: {admin_url}") + + if settings.lnbits_saas_callback and settings.lnbits_saas_secret and settings.lnbits_saas_instance_id: + 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": user + } + try: + r = client.post(settings.lnbits_saas_callback, headers=headers, json=payload) + logger.warning("sent admin user to saas application") + except: + logger.error(f"error sending admin user to saas: {settings.lnbits_saas_callback}") + except: - logger.warning("admin.settings tables does not exist.") + logger.error("admin.settings tables does not exist.") raise From c9ead25d50cc1c8cd019e51d7147c4febb71b635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Wed, 12 Oct 2022 13:08:59 +0200 Subject: [PATCH 2/2] bugfixes and fix topup wallet --- lnbits/app.py | 1 + .../admin/templates/admin/_tab_funding.html | 4 +-- .../admin/templates/admin/index.html | 28 +++++++++------ lnbits/extensions/admin/views_api.py | 36 ++++++++++--------- lnbits/server.py | 1 + lnbits/settings.py | 29 ++++++++++----- lnbits/wallets/fake.py | 2 +- 7 files changed, 60 insertions(+), 41 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index 6fa5cf5d..49ad8d77 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -84,6 +84,7 @@ async def check_funding_source() -> None: def signal_handler(signal, frame): logger.debug(f"SIGINT received, terminating LNbits.") sys.exit(1) + signal.signal(signal.SIGINT, signal_handler) WALLET = get_wallet_class() diff --git a/lnbits/extensions/admin/templates/admin/_tab_funding.html b/lnbits/extensions/admin/templates/admin/_tab_funding.html index 8b5456f1..34162a9f 100644 --- a/lnbits/extensions/admin/templates/admin/_tab_funding.html +++ b/lnbits/extensions/admin/templates/admin/_tab_funding.html @@ -8,9 +8,7 @@

Funding Source Info

diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index d8111595..d3e93a71 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -3,7 +3,13 @@
- + u !== user - ) + this.settings.lnbits_admin_users = admin_users.filter(u => u !== user) }, addAllowedUser() { let addUser = this.formData.allowed_users_add @@ -155,7 +159,9 @@ }, removeAllowedUser(user) { let allowed_users = this.settings.lnbits_allowed_users - this.settings.lnbits_allowed_users = allowed_users.filter(u => u !== user) + this.settings.lnbits_allowed_users = allowed_users.filter( + u => u !== user + ) }, addAdSpace() { let adSpace = this.formData.ad_space_add @@ -187,10 +193,10 @@ topupWallet() { LNbits.api .request( - 'POST', + 'PUT', '/admin/api/v1/topup/?usr=' + this.g.user.id, - this.wallet.id, - this.wallet.amount + this.g.user.wallets[0].adminkey, + this.wallet ) .then(response => { this.$q.notify({ @@ -209,7 +215,7 @@ }) }, updateSettings() { - let data = { + let data = { ...this.formData } LNbits.api @@ -262,7 +268,7 @@ LNbits.utils.notifyApiError(error) }) } - }, + } }) {% endblock %} diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index ae2959bc..63ed5b3c 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -1,7 +1,6 @@ from http import HTTPStatus -from fastapi import Body, Depends, Request -from loguru import logger +from fastapi import Body, Depends, Query from starlette.exceptions import HTTPException from lnbits.core.crud import get_wallet @@ -9,47 +8,50 @@ from lnbits.core.models import User from lnbits.decorators import check_admin from lnbits.extensions.admin import admin_ext from lnbits.extensions.admin.models import UpdateSettings -from lnbits.requestvars import g from lnbits.server import server_restart -from lnbits.settings import settings from .crud import delete_settings, update_settings, update_wallet_balance -@admin_ext.get("/api/v1/restart/", status_code=HTTPStatus.OK) -async def api_restart_server(user: User = Depends(check_admin)): +@admin_ext.get( + "/api/v1/restart/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)] +) +async def api_restart_server() -> dict[str, str]: server_restart.set() return {"status": "Success"} -@admin_ext.put("/api/v1/topup/", status_code=HTTPStatus.OK) +@admin_ext.put( + "/api/v1/topup/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)] +) async def api_update_balance( - wallet_id, topup_amount: int, user: User = Depends(check_admin) -): + id: str = Body(...), amount: int = Body(...) +) -> dict[str, str]: try: - wallet = await get_wallet(wallet_id) + await get_wallet(id) except: raise HTTPException( status_code=HTTPStatus.FORBIDDEN, detail="wallet does not exist." ) - await update_wallet_balance(wallet_id=wallet_id, amount=int(topup_amount)) + await update_wallet_balance(wallet_id=id, amount=int(amount)) return {"status": "Success"} -@admin_ext.put("/api/v1/settings/", status_code=HTTPStatus.OK) +@admin_ext.put( + "/api/v1/settings/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)] +) async def api_update_settings( - user: User = Depends(check_admin), data: UpdateSettings = Body(...), ): settings = await update_settings(data) return {"status": "Success", "settings": settings.dict()} -@admin_ext.delete("/api/v1/settings/", status_code=HTTPStatus.OK) -async def api_delete_settings( - user: User = Depends(check_admin), -): +@admin_ext.delete( + "/api/v1/settings/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)] +) +async def api_delete_settings() -> dict[str, str]: await delete_settings() return {"status": "Success"} diff --git a/lnbits/server.py b/lnbits/server.py index 79af8112..6d4cd2e7 100644 --- a/lnbits/server.py +++ b/lnbits/server.py @@ -52,6 +52,7 @@ def main(ctx, port: int, host: str, ssl_keyfile: str, ssl_certfile: str, reload: port=port, host=host, reload=reload, + forwarded_allow_ips="*", ssl_keyfile=ssl_keyfile, ssl_certfile=ssl_certfile, **d diff --git a/lnbits/settings.py b/lnbits/settings.py index f183211c..61dbd6f2 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -1,20 +1,19 @@ import importlib import json import subprocess -import httpx 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 as e: + except Exception: replaced = v.replace(" ", "") if replaced: return replaced.split(",") @@ -238,24 +237,36 @@ async def check_admin_settings(): http = "https" if settings.lnbits_force_https else "http" user = settings.lnbits_admin_users[0] - admin_url = f"{http}://{settings.host}:{settings.port}/wallet?usr={user}" + admin_url = ( + f"{http}://{settings.host}:{settings.port}/wallet?usr={user}" + ) logger.warning(f"✔️ Access admin user account at: {admin_url}") - if settings.lnbits_saas_callback and settings.lnbits_saas_secret and settings.lnbits_saas_instance_id: + if ( + settings.lnbits_saas_callback + and settings.lnbits_saas_secret + and settings.lnbits_saas_instance_id + ): with httpx.Client() as client: headers = { "Content-Type": "application/json; charset=utf-8", - "X-API-KEY": settings.lnbits_saas_secret + "X-API-KEY": settings.lnbits_saas_secret, } payload = { "instance_id": settings.lnbits_saas_instance_id, - "adminuser": user + "adminuser": user, } try: - r = client.post(settings.lnbits_saas_callback, headers=headers, json=payload) + client.post( + settings.lnbits_saas_callback, + headers=headers, + json=payload, + ) logger.warning("sent admin user to saas application") except: - logger.error(f"error sending admin user to saas: {settings.lnbits_saas_callback}") + logger.error( + f"error sending admin user to saas: {settings.lnbits_saas_callback}" + ) except: logger.error("admin.settings tables does not exist.") diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py index 73458e8c..94ff5f48 100644 --- a/lnbits/wallets/fake.py +++ b/lnbits/wallets/fake.py @@ -19,7 +19,6 @@ from .base import ( class FakeWallet(Wallet): - queue: asyncio.Queue = asyncio.Queue(0) secret: str = settings.fake_wallet_secret privkey: str = hashlib.pbkdf2_hmac( "sha256", @@ -98,6 +97,7 @@ class FakeWallet(Wallet): return PaymentStatus(None) async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: + self.queue: asyncio.Queue = asyncio.Queue(0) while True: value: Invoice = await self.queue.get() yield value.payment_hash