From 65e1f19ed1124340d2a9ba97b4420c099896488c Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Sat, 12 Mar 2022 14:18:09 +0000 Subject: [PATCH] convert to FastAPI --- lnbits/extensions/admin/__init__.py | 11 +++- lnbits/extensions/admin/crud.py | 50 +++++++-------- lnbits/extensions/admin/migrations.py | 23 ++++--- lnbits/extensions/admin/models.py | 51 ++++++++++------ .../admin/templates/admin/index.html | 23 +++++-- lnbits/extensions/admin/views.py | 39 ++++++++---- lnbits/extensions/admin/views_api.py | 61 ++++++++++--------- 7 files changed, 151 insertions(+), 107 deletions(-) diff --git a/lnbits/extensions/admin/__init__.py b/lnbits/extensions/admin/__init__.py index d5f26c90d..6a56b2bb1 100644 --- a/lnbits/extensions/admin/__init__.py +++ b/lnbits/extensions/admin/__init__.py @@ -1,10 +1,15 @@ -from quart import Blueprint +from fastapi import APIRouter + from lnbits.db import Database +from lnbits.helpers import template_renderer db = Database("ext_admin") -admin_ext: Blueprint = Blueprint("admin", __name__, static_folder="static", template_folder="templates") +admin_ext: APIRouter = APIRouter(prefix="/admin", tags=["admin"]) + +def admin_renderer(): + return template_renderer(["lnbits/extensions/admin/templates"]) -from .views_api import * # noqa from .views import * # noqa +from .views_api import * # noqa diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index cb8f9b5be..872d6c97b 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -1,11 +1,11 @@ from typing import List, Optional +from lnbits.core.crud import create_payment +from lnbits.helpers import urlsafe_short_hash +from lnbits.settings import * + from . import db from .models import Admin, Funding -from lnbits.settings import * -from lnbits.helpers import urlsafe_short_hash -from lnbits.core.crud import create_payment -from lnbits.db import Connection def update_wallet_balance(wallet_id: str, amount: int) -> str: @@ -22,38 +22,30 @@ def update_wallet_balance(wallet_id: str, amount: int) -> str: ) return "success" - -async def update_admin( -) -> Optional[Admin]: - if not CLightningWallet: - print("poo") - await db.execute( - """ - UPDATE admin - SET user = ?, site_title = ?, site_tagline = ?, site_description = ?, allowed_users = ?, default_wallet_name = ?, data_folder = ?, disabled_ext = ?, force_https = ?, service_fee = ?, funding_source = ? - WHERE 1 - """, - ( - - ), - ) - row = await db.fetchone("SELECT * FROM admin WHERE 1") - return Admin.from_row(row) if row else None - -async def update_admin(admin_id: str, **kwargs) -> Optional[Admin]: +async def update_admin(user: str, **kwargs) -> Admin: q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) + print("UPDATE", q) await db.execute( - f"UPDATE jukebox.jukebox SET {q} WHERE id = ?", (*kwargs.values(), juke_id) + f'UPDATE admin SET {q} WHERE "user" = ?', (*kwargs.values(), user) ) - row = await db.fetchone("SELECT * FROM jukebox.jukebox WHERE id = ?", (juke_id,)) - return Jukebox(**row) if row else None + row = await db.fetchone('SELECT * FROM admin WHERE "user" = ?', (user,)) + assert row, "Newly updated settings couldn't be retrieved" + return Admin(**row) if row else None + +# async def update_admin(user: str, **kwargs) -> Optional[Admin]: +# q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) +# await db.execute( +# f"UPDATE admin SET {q} WHERE user = ?", (*kwargs.values(), user) +# ) +# new_settings = await get_admin() +# return new_settings async def get_admin() -> List[Admin]: - row = await db.fetchone("SELECT * FROM admin WHERE 1") - return Admin.from_row(row) if row else None + row = await db.fetchone("SELECT * FROM admin") + return Admin(**row) if row else None async def get_funding() -> List[Funding]: rows = await db.fetchall("SELECT * FROM funding") - return [Funding.from_row(row) for row in rows] + return [Funding(**row) for row in rows] diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index 82d934cb6..13b769232 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -1,5 +1,7 @@ -from sqlalchemy.exc import OperationalError # type: ignore from os import getenv + +from sqlalchemy.exc import OperationalError # type: ignore + from lnbits.helpers import urlsafe_short_hash @@ -9,7 +11,7 @@ async def m001_create_admin_table(db): site_tagline = None site_description = None allowed_users = None - admin_user = None + admin_users = None default_wallet_name = None data_folder = None disabled_ext = None @@ -29,8 +31,9 @@ async def m001_create_admin_table(db): if getenv("LNBITS_ALLOWED_USERS"): allowed_users = getenv("LNBITS_ALLOWED_USERS") - if getenv("LNBITS_ADMIN_USER"): - admin_user = getenv("LNBITS_ADMIN_USER") + if getenv("LNBITS_ADMIN_USERS"): + admin_users = "".join(getenv("LNBITS_ADMIN_USERS").split()) + user = admin_users.split(',')[0] if getenv("LNBITS_DEFAULT_WALLET_NAME"): default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME") @@ -53,32 +56,32 @@ async def m001_create_admin_table(db): await db.execute( """ CREATE TABLE IF NOT EXISTS admin ( - user TEXT, + "user" TEXT, site_title TEXT, site_tagline TEXT, site_description TEXT, - admin_user TEXT, + admin_users TEXT, allowed_users TEXT, default_wallet_name TEXT, data_folder TEXT, disabled_ext TEXT, force_https BOOLEAN, - service_fee INT, + service_fee REAL, funding_source TEXT ); """ ) await db.execute( """ - INSERT INTO admin (user, site_title, site_tagline, site_description, admin_user, allowed_users, default_wallet_name, data_folder, disabled_ext, force_https, service_fee, funding_source) + INSERT INTO admin ("user", site_title, site_tagline, site_description, admin_users, allowed_users, default_wallet_name, data_folder, disabled_ext, force_https, service_fee, funding_source) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( - user, + user.strip(), site_title, site_tagline, site_description, - admin_user, + admin_users[1:], allowed_users, default_wallet_name, data_folder, diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index c38f17f48..4080ff018 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -1,18 +1,35 @@ -from typing import NamedTuple from sqlite3 import Row +from typing import List, Optional -class Admin(NamedTuple): +from fastapi import Query +from pydantic import BaseModel + + +class UpdateAdminSettings(BaseModel): + site_title: Optional[str] + site_tagline: Optional[str] + site_description: Optional[str] + allowed_users: Optional[str] + admin_users: Optional[str] + default_wallet_name: Optional[str] + data_folder: Optional[str] + disabled_ext: Optional[str] + force_https: Optional[bool] + service_fee: Optional[float] + funding_source: Optional[str] + +class Admin(BaseModel): user: str - site_title: str - site_tagline: str - site_description:str - allowed_users: str - admin_user: str + site_title: Optional[str] + site_tagline: Optional[str] + site_description: Optional[str] + allowed_users: Optional[str] + admin_users: str default_wallet_name: str data_folder: str disabled_ext: str - force_https: str - service_fee: str + force_https: Optional[bool] = Query(True) + service_fee: float funding_source: str @classmethod @@ -20,16 +37,16 @@ class Admin(NamedTuple): data = dict(row) return cls(**data) -class Funding(NamedTuple): +class Funding(BaseModel): id: str backend_wallet: str - endpoint: str - port: str - read_key: str - invoice_key: str - admin_key: str - cert: str - balance: int + endpoint: str = Query(None) + port: str = Query(None) + read_key: str = Query(None) + invoice_key: str = Query(None) + admin_key: str = Query(None) + cert: str = Query(None) + balance: int = Query(None) selected: int @classmethod diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 87cf09efa..a6b456259 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -87,7 +87,7 @@ @@ -442,13 +442,14 @@ site_title: '{{admin.site_title}}', tagline: '{{admin.site_tagline}}', description: '{{admin.site_description}}', - admin_user: '{{admin.admin_user}}', - service_fee: parseInt('{{admin.service_fee}}'), + admin_users: '{{admin.admin_users}}', + service_fee: parseFloat('{{admin.service_fee}}'), default_wallet_name: '{{admin.default_wallet_name}}', data_folder: '{{admin.data_folder}}', funding_source_primary: '{{admin.funding_source}}', disabled_ext: '{{admin.disabled_ext}}'.split(','), edited: [], + funding: {}, senddata: {} } }, @@ -528,15 +529,27 @@ }, UpdateLNbits: function () { var self = this - console.log(self.data.admin) + let {site_title, admin_users, default_wallet_name, data_folder, disabled_ext, service_fee, funding_source_primary} = this.data.admin + let data = { + site_title, + site_tagline: this.data.admin.tagline, + site_description: this.data.admin.description, + admin_users: admin_users.toString(), + default_wallet_name, + data_folder, + disabled_ext: disabled_ext.toString(), + service_fee, + funding_source: funding_source_primary} + console.log(data) LNbits.api .request( 'POST', '/admin/api/v1/admin/', self.g.user.wallets[0].adminkey, - self.data.admin + data ) .then(function (response) { + console.log(response.data) self.$q.notify({ type: 'positive', message: diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py index 5e17919c5..00a0c99fc 100644 --- a/lnbits/extensions/admin/views.py +++ b/lnbits/extensions/admin/views.py @@ -1,20 +1,33 @@ -from quart import g, render_template, request, jsonify -import json +from email.policy import default +from os import getenv -from lnbits.decorators import check_user_exists, validate_uuids +from fastapi import Request +from fastapi.params import Depends +from fastapi.templating import Jinja2Templates +from starlette.responses import HTMLResponse + +from lnbits.core.models import User +from lnbits.decorators import check_user_exists from lnbits.extensions.admin import admin_ext -from lnbits.core.crud import get_user, create_account +from lnbits.requestvars import g + +from . import admin_ext, admin_renderer from .crud import get_admin, get_funding -from lnbits.settings import WALLET +templates = Jinja2Templates(directory="templates") -@admin_ext.route("/") -@validate_uuids(["usr"], required=True) -@check_user_exists() -async def index(): - user_id = g.user +@admin_ext.get("/", response_class=HTMLResponse) +async def index(request: Request, user: User = Depends(check_user_exists)): admin = await get_admin() + print(g()) + funding = [f.dict() for f in await get_funding()] - funding = [{**funding._asdict()} for funding in await get_funding()] - - return await render_template("admin/index.html", user=g.user, admin=admin, funding=funding) + print("ADMIN", admin.dict()) + return admin_renderer().TemplateResponse( + "admin/index.html", { + "request": request, + "user": user.dict(), + "admin": admin.dict(), + "funding": funding + } + ) diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index 2a61b6f55..b2c65be25 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -1,41 +1,42 @@ -from quart import jsonify, g, request from http import HTTPStatus -from .crud import update_wallet_balance -from lnbits.extensions.admin import admin_ext -from lnbits.decorators import api_check_wallet_key, api_validate_post_request -from lnbits.core.crud import get_wallet -from .crud import get_admin,update_admin -import json -@admin_ext.route("/api/v1/admin//", methods=["GET"]) -@api_check_wallet_key("admin") -async def api_update_balance(wallet_id, topup_amount): - print(g.data.wallet) +from fastapi import Body, Depends, Request +from starlette.exceptions import HTTPException + +from lnbits.core.crud import get_wallet +from lnbits.decorators import WalletTypeInfo, require_admin_key +from lnbits.extensions.admin import admin_ext +from lnbits.extensions.admin.models import Admin, UpdateAdminSettings + +from .crud import get_admin, update_admin, update_wallet_balance + + +@admin_ext.get("/api/v1/admin/{wallet_id}/{topup_amount}", status_code=HTTPStatus.OK) +async def api_update_balance(wallet_id, topup_amount, g: WalletTypeInfo = Depends(require_admin_key)): + print(g.wallet) try: wallet = await get_wallet(wallet_id) except: - return ( - jsonify({"error": "Not allowed: not an admin"}), - HTTPStatus.FORBIDDEN, - ) + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" + ) print(wallet) print(topup_amount) - return jsonify({"status": "Success"}), HTTPStatus.OK + return {"status": "Success"} -@admin_ext.route("/api/v1/admin/", methods=["POST"]) -@api_check_wallet_key("admin") -@api_validate_post_request(schema={}) -async def api_update_admin(): - body = await request.get_json() +@admin_ext.post("/api/v1/admin/", status_code=HTTPStatus.OK) +async def api_update_admin( + request: Request, + data: UpdateAdminSettings = Body(...), + g: WalletTypeInfo = Depends(require_admin_key) + ): admin = await get_admin() - print(g.wallet[2]) - print(body["admin_user"]) - if not admin.admin_user == g.wallet[2] and admin.admin_user != None: - return ( - jsonify({"error": "Not allowed: not an admin"}), - HTTPStatus.FORBIDDEN, - ) - updated = await update_admin(body) + print(data) + if not admin.user == g.wallet.user: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" + ) + updated = await update_admin(user=g.wallet.user, **data.dict()) print(updated) - return jsonify({"status": "Success"}), HTTPStatus.OK \ No newline at end of file + return {"status": "Success"}