[feat] Pay to enable extension (#2516)
* feat: add payment tab
* feat: add buttons
* feat: persist `pay to enable` changes
* fix: do not disable extension on upgrade
* fix: show releases tab first
* feat: extract `enableExtension` logic
* refactor: rename routes
* feat: show dialog for paying extension
* feat: create invoice to enable
* refactor: extract enable/disable extension logic
* feat: add extra info to UserExtensions
* feat: check payment for extension enable
* fix: parsing
* feat: admins must not pay
* fix: code checks
* fix: test
* refactor: extract extension activate/deactivate to the `api` side
* feat: add `get_user_extensions `
* feat: return explicit `requiresPayment`
* feat: add `isPaymentRequired` to extension list
* fix: `paid_to_enable` status
* fix: ui layout
* feat: show QR Code
* feat: wait for invoice to be paid
* test: removed deprecated test and dead code
* feat: add re-check button
* refactor: rename paths for endpoints
* feat: i18n
* feat: add `{"success": True}`
* test: fix listener
* fix: rebase errors
* chore: update bundle
* fix: return error status code for the HTML error pages
* fix: active extension loading from file system
* chore: temp commit
* fix: premature optimisation
* chore: make check
* refactor: remove extracted logic
* chore: code format
* fix: enable by default after install
* fix: use `discard` instead of `remove` for `set`
* chore: code format
* fix: better error code
* fix: check for stop function before invoking
* feat: check if the wallet belongs to the admin user
* refactor: return 402 Requires Payment
* chore: more typing
* chore: temp checkout different branch for tests
* fix: too much typing
* fix: remove try-except
* fix: typo
* fix: manual format
* fix: merge issue
* remove this line
---------
Co-authored-by: dni ⚡ <office@dnilabs.com>
This commit is contained in:
+67
-10
@@ -9,7 +9,12 @@ from passlib.context import CryptContext
|
||||
|
||||
from lnbits.core.db import db
|
||||
from lnbits.db import DB_TYPE, SQLITE, Connection, Database, Filters, Page
|
||||
from lnbits.extension_manager import InstallableExtension
|
||||
from lnbits.extension_manager import (
|
||||
InstallableExtension,
|
||||
PayToEnableInfo,
|
||||
UserExtension,
|
||||
UserExtensionInfo,
|
||||
)
|
||||
from lnbits.settings import (
|
||||
AdminSettings,
|
||||
EditableSettings,
|
||||
@@ -364,6 +369,7 @@ async def add_installed_extension(
|
||||
"installed_release": (
|
||||
dict(ext.installed_release) if ext.installed_release else None
|
||||
),
|
||||
"pay_to_enable": (dict(ext.pay_to_enable) if ext.pay_to_enable else None),
|
||||
"dependencies": ext.dependencies,
|
||||
"payments": [dict(p) for p in ext.payments] if ext.payments else None,
|
||||
}
|
||||
@@ -373,8 +379,8 @@ async def add_installed_extension(
|
||||
await (conn or db).execute(
|
||||
"""
|
||||
INSERT INTO installed_extensions
|
||||
(id, version, name, short_description, icon, stars, meta)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT (id) DO UPDATE SET
|
||||
(id, version, name, active, short_description, icon, stars, meta)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT (id) DO UPDATE SET
|
||||
(version, name, active, short_description, icon, stars, meta) =
|
||||
(?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
@@ -382,13 +388,14 @@ async def add_installed_extension(
|
||||
ext.id,
|
||||
version,
|
||||
ext.name,
|
||||
ext.active,
|
||||
ext.short_description,
|
||||
ext.icon,
|
||||
ext.stars,
|
||||
json.dumps(meta),
|
||||
version,
|
||||
ext.name,
|
||||
False,
|
||||
ext.active,
|
||||
ext.short_description,
|
||||
ext.icon,
|
||||
ext.stars,
|
||||
@@ -408,6 +415,17 @@ async def update_installed_extension_state(
|
||||
)
|
||||
|
||||
|
||||
async def update_extension_pay_to_enable(
|
||||
ext_id: str, payment_info: PayToEnableInfo, conn: Optional[Connection] = None
|
||||
) -> None:
|
||||
ext = await get_installed_extension(ext_id, conn)
|
||||
if not ext:
|
||||
return
|
||||
ext.pay_to_enable = payment_info
|
||||
|
||||
await add_installed_extension(ext, conn)
|
||||
|
||||
|
||||
async def delete_installed_extension(
|
||||
*, ext_id: str, conn: Optional[Connection] = None
|
||||
) -> None:
|
||||
@@ -450,21 +468,44 @@ async def get_installed_extension(
|
||||
|
||||
|
||||
async def get_installed_extensions(
|
||||
active: Optional[bool] = None,
|
||||
conn: Optional[Connection] = None,
|
||||
) -> List["InstallableExtension"]:
|
||||
rows = await (conn or db).fetchall(
|
||||
"SELECT * FROM installed_extensions",
|
||||
(),
|
||||
)
|
||||
return [InstallableExtension.from_row(row) for row in rows]
|
||||
all_extensions = [InstallableExtension.from_row(row) for row in rows]
|
||||
if active is None:
|
||||
return all_extensions
|
||||
|
||||
return [e for e in all_extensions if e.active == active]
|
||||
|
||||
|
||||
async def get_inactive_extensions(*, conn: Optional[Connection] = None) -> List[str]:
|
||||
inactive_extensions = await (conn or db).fetchall(
|
||||
"""SELECT id FROM installed_extensions WHERE NOT active""",
|
||||
(),
|
||||
async def get_user_extension(
|
||||
user_id: str, extension: str, conn: Optional[Connection] = None
|
||||
) -> Optional[UserExtension]:
|
||||
row = await (conn or db).fetchone(
|
||||
"""
|
||||
SELECT extension, active, extra as _extra FROM extensions
|
||||
WHERE "user" = ? AND extension = ?
|
||||
""",
|
||||
(user_id, extension),
|
||||
)
|
||||
return [ext[0] for ext in inactive_extensions]
|
||||
return UserExtension.from_row(row) if row else None
|
||||
|
||||
|
||||
async def get_user_extensions(
|
||||
user_id: str, conn: Optional[Connection] = None
|
||||
) -> List[UserExtension]:
|
||||
rows = await (conn or db).fetchall(
|
||||
"""
|
||||
SELECT extension, active, extra as _extra FROM extensions
|
||||
WHERE "user" = ?
|
||||
""",
|
||||
(user_id,),
|
||||
)
|
||||
return [UserExtension.from_row(row) for row in rows]
|
||||
|
||||
|
||||
async def update_user_extension(
|
||||
@@ -489,6 +530,22 @@ async def get_user_active_extensions_ids(
|
||||
return [e[0] for e in rows]
|
||||
|
||||
|
||||
async def update_user_extension_extra(
|
||||
user_id: str,
|
||||
extension: str,
|
||||
extra: UserExtensionInfo,
|
||||
conn: Optional[Connection] = None,
|
||||
) -> None:
|
||||
extra_json = json.dumps(dict(extra))
|
||||
await (conn or db).execute(
|
||||
"""
|
||||
INSERT INTO extensions ("user", extension, extra) VALUES (?, ?, ?)
|
||||
ON CONFLICT ("user", extension) DO UPDATE SET extra = ?
|
||||
""",
|
||||
(user_id, extension, extra_json, extra_json),
|
||||
)
|
||||
|
||||
|
||||
# wallets
|
||||
# -------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user