add dbversion model
This commit is contained in:
+2
-2
@@ -17,7 +17,7 @@ from slowapi.util import get_remote_address
|
||||
from starlette.middleware.sessions import SessionMiddleware
|
||||
|
||||
from lnbits.core.crud import (
|
||||
get_dbversions,
|
||||
get_db_version,
|
||||
get_installed_extensions,
|
||||
update_installed_extension_state,
|
||||
)
|
||||
@@ -313,7 +313,7 @@ async def restore_installed_extension(app: FastAPI, ext: InstallableExtension):
|
||||
extension = Extension.from_installable_ext(ext)
|
||||
register_ext_routes(app, extension)
|
||||
|
||||
current_version = (await get_dbversions()).get(ext.id, 0)
|
||||
current_version = await get_db_version(ext.id)
|
||||
await migrate_extension_database(ext, current_version)
|
||||
|
||||
# mount routes for the new version
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ from lnbits.core.crud import (
|
||||
delete_unused_wallets,
|
||||
delete_wallet_by_id,
|
||||
delete_wallet_payment,
|
||||
get_dbversions,
|
||||
get_db_versions,
|
||||
get_installed_extension,
|
||||
get_installed_extensions,
|
||||
get_payments,
|
||||
@@ -122,7 +122,7 @@ def database_migrate():
|
||||
async def db_versions():
|
||||
"""Show current database versions"""
|
||||
async with core_db.connect() as conn:
|
||||
click.echo(await get_dbversions(conn))
|
||||
click.echo(await get_db_versions(conn))
|
||||
|
||||
|
||||
@db.command("cleanup-wallets")
|
||||
|
||||
+25
-16
@@ -25,6 +25,7 @@ from .models import (
|
||||
AccountFilters,
|
||||
AccountOverview,
|
||||
CreatePayment,
|
||||
DbVersion,
|
||||
Payment,
|
||||
PaymentFilters,
|
||||
PaymentHistoryPoint,
|
||||
@@ -842,35 +843,37 @@ async def check_internal(
|
||||
Returns the checking_id of the internal payment if it exists,
|
||||
otherwise None
|
||||
"""
|
||||
row: dict = await (conn or db).fetchone(
|
||||
payment = await (conn or db).fetchone(
|
||||
f"""
|
||||
SELECT checking_id FROM apipayments
|
||||
SELECT * FROM apipayments
|
||||
WHERE payment_hash = :hash AND status = '{PaymentState.PENDING}' AND amount > 0
|
||||
""",
|
||||
{"hash": payment_hash},
|
||||
Payment,
|
||||
)
|
||||
if not row:
|
||||
if not payment:
|
||||
return None
|
||||
else:
|
||||
return row["checking_id"]
|
||||
return payment.checking_id
|
||||
|
||||
|
||||
async def check_internal_status(
|
||||
async def is_internal_status_success(
|
||||
payment_hash: str, conn: Optional[Connection] = None
|
||||
) -> bool:
|
||||
"""
|
||||
Returns True if the internal payment was successful
|
||||
Returns True if the internal payment was found and has the given status,
|
||||
"""
|
||||
row: dict = await (conn or db).fetchone(
|
||||
payment = await (conn or db).fetchone(
|
||||
"""
|
||||
SELECT status FROM apipayments
|
||||
SELECT * FROM apipayments
|
||||
WHERE payment_hash = :payment_hash AND amount > 0
|
||||
""",
|
||||
{"payment_hash": payment_hash},
|
||||
Payment,
|
||||
)
|
||||
if not row:
|
||||
if not payment:
|
||||
return True
|
||||
return row["status"] == PaymentState.SUCCESS.value
|
||||
return payment.status == PaymentState.SUCCESS.value
|
||||
|
||||
|
||||
async def mark_webhook_sent(payment_hash: str, status: int) -> None:
|
||||
@@ -950,12 +953,18 @@ async def create_admin_settings(super_user: str, new_settings: dict):
|
||||
|
||||
# db versions
|
||||
# --------------
|
||||
async def get_dbversions(conn: Optional[Connection] = None) -> dict:
|
||||
result = await (conn or db).execute("SELECT db, version FROM dbversions")
|
||||
_dict = {}
|
||||
for row in result.mappings().all():
|
||||
_dict[row["db"]] = row["version"]
|
||||
return _dict
|
||||
async def get_db_version(
|
||||
ext_id: str, conn: Optional[Connection] = None
|
||||
) -> Optional[DbVersion]:
|
||||
return await (conn or db).fetchone(
|
||||
"SELECT * FROM dbversions WHERE db = :ext_id",
|
||||
{"ext_id": ext_id},
|
||||
model=DbVersion,
|
||||
)
|
||||
|
||||
|
||||
async def get_db_versions(conn: Optional[Connection] = None) -> list[DbVersion]:
|
||||
return await (conn or db).fetchall("SELECT * FROM dbversions", model=DbVersion)
|
||||
|
||||
|
||||
async def update_migration_version(conn, db_name, version):
|
||||
|
||||
@@ -7,7 +7,7 @@ from lnbits.core import core_app_extra
|
||||
from lnbits.core.crud import (
|
||||
create_installed_extension,
|
||||
delete_installed_extension,
|
||||
get_dbversions,
|
||||
get_db_version,
|
||||
get_installed_extension,
|
||||
update_installed_extension_state,
|
||||
)
|
||||
@@ -28,7 +28,7 @@ async def install_extension(ext_info: InstallableExtension) -> Extension:
|
||||
|
||||
ext_info.extract_archive()
|
||||
|
||||
db_version = (await get_dbversions()).get(ext_id, 0)
|
||||
db_version = await get_db_version(ext_id)
|
||||
await migrate_extension_database(ext_info, db_version)
|
||||
|
||||
await create_installed_extension(ext_info)
|
||||
|
||||
+20
-8
@@ -1,6 +1,6 @@
|
||||
import importlib
|
||||
import re
|
||||
from typing import Any
|
||||
from typing import Any, Optional
|
||||
from urllib.parse import urlparse
|
||||
from uuid import UUID
|
||||
|
||||
@@ -8,17 +8,20 @@ from loguru import logger
|
||||
|
||||
from lnbits.core import migrations as core_migrations
|
||||
from lnbits.core.crud import (
|
||||
get_dbversions,
|
||||
get_db_versions,
|
||||
get_installed_extensions,
|
||||
update_migration_version,
|
||||
)
|
||||
from lnbits.core.db import db as core_db
|
||||
from lnbits.core.extensions.models import InstallableExtension
|
||||
from lnbits.core.models import DbVersion
|
||||
from lnbits.db import COCKROACH, POSTGRES, SQLITE, Connection
|
||||
from lnbits.settings import settings
|
||||
|
||||
|
||||
async def migrate_extension_database(ext: InstallableExtension, current_version: int):
|
||||
async def migrate_extension_database(
|
||||
ext: InstallableExtension, current_version: Optional[DbVersion] = None
|
||||
):
|
||||
|
||||
try:
|
||||
ext_migrations = importlib.import_module(f"{ext.module_name}.migrations")
|
||||
@@ -32,14 +35,17 @@ async def migrate_extension_database(ext: InstallableExtension, current_version:
|
||||
|
||||
|
||||
async def run_migration(
|
||||
db: Connection, migrations_module: Any, db_name: str, current_version: int
|
||||
db: Connection,
|
||||
migrations_module: Any,
|
||||
db_name: str,
|
||||
current_version: Optional[DbVersion] = None,
|
||||
):
|
||||
matcher = re.compile(r"^m(\d\d\d)_")
|
||||
for key, migrate in migrations_module.__dict__.items():
|
||||
match = matcher.match(key)
|
||||
if match:
|
||||
version = int(match.group(1))
|
||||
if version > current_version:
|
||||
if not current_version or version > current_version.version:
|
||||
logger.debug(f"running migration {db_name}.{version}")
|
||||
print(f"running migration {db_name}.{version}")
|
||||
await migrate(db)
|
||||
@@ -71,7 +77,7 @@ async def load_disabled_extension_list() -> None:
|
||||
async def migrate_databases():
|
||||
"""Creates the necessary databases if they don't exist already; or migrates them."""
|
||||
|
||||
current_versions = await get_dbversions()
|
||||
current_versions = await get_db_versions()
|
||||
async with core_db.connect() as conn:
|
||||
exists = False
|
||||
if conn.type == SQLITE:
|
||||
@@ -87,7 +93,10 @@ async def migrate_databases():
|
||||
if not exists:
|
||||
await core_migrations.m000_create_migrations_table(conn)
|
||||
|
||||
core_version = current_versions.get("core", 0)
|
||||
core_version = next(
|
||||
(v for v in current_versions if v.db == "core"),
|
||||
DbVersion(db="core", version=0),
|
||||
)
|
||||
await run_migration(conn, core_migrations, "core", core_version)
|
||||
|
||||
# here is the first place we can be sure that the
|
||||
@@ -95,7 +104,10 @@ async def migrate_databases():
|
||||
await load_disabled_extension_list()
|
||||
|
||||
for ext in await get_installed_extensions():
|
||||
current_version = current_versions.get(ext.id)
|
||||
current_version = next(
|
||||
(v for v in current_versions if v.db == ext.id),
|
||||
DbVersion(db=ext.id, version=0),
|
||||
)
|
||||
if current_version is None:
|
||||
logger.warning(
|
||||
f"Extension {ext.id} has no migration version. This should not happen."
|
||||
|
||||
@@ -469,3 +469,8 @@ class BalanceDelta(BaseModel):
|
||||
class SimpleStatus(BaseModel):
|
||||
success: bool
|
||||
message: str
|
||||
|
||||
|
||||
class DbVersion(BaseModel):
|
||||
db: str
|
||||
version: int
|
||||
|
||||
@@ -46,7 +46,6 @@ from lnbits.wallets.base import (
|
||||
|
||||
from .crud import (
|
||||
check_internal,
|
||||
check_internal_status,
|
||||
create_account,
|
||||
create_admin_settings,
|
||||
create_payment,
|
||||
@@ -62,6 +61,7 @@ from .crud import (
|
||||
get_user,
|
||||
get_wallet,
|
||||
get_wallet_payment,
|
||||
is_internal_status_success,
|
||||
update_admin_settings,
|
||||
update_payment_details,
|
||||
update_payment_status,
|
||||
@@ -247,9 +247,7 @@ async def pay_invoice(
|
||||
extra=extra,
|
||||
)
|
||||
|
||||
# we check if an internal invoice exists that has already been paid
|
||||
# (not pending anymore)
|
||||
if await check_internal_status(invoice.payment_hash, conn=conn):
|
||||
if await is_internal_status_success(invoice.payment_hash, conn=conn):
|
||||
raise PaymentError("Internal invoice already paid.", status="failed")
|
||||
|
||||
# check_internal() returns the checking_id of the invoice we're waiting for
|
||||
|
||||
@@ -40,7 +40,7 @@ from ..crud import (
|
||||
create_user_extension,
|
||||
delete_dbversion,
|
||||
drop_extension_db,
|
||||
get_dbversions,
|
||||
get_db_version,
|
||||
get_installed_extension,
|
||||
get_installed_extensions,
|
||||
get_user_extension,
|
||||
@@ -474,7 +474,7 @@ async def get_extension_release(org: str, repo: str, tag_name: str):
|
||||
)
|
||||
async def delete_extension_db(ext_id: str):
|
||||
try:
|
||||
db_version = (await get_dbversions()).get(ext_id, None)
|
||||
db_version = await get_db_version(ext_id)
|
||||
if not db_version:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST,
|
||||
|
||||
@@ -23,7 +23,7 @@ from lnbits.wallets import get_funding_source
|
||||
from ...utils.exchange_rates import allowed_currencies, currencies
|
||||
from ..crud import (
|
||||
create_wallet,
|
||||
get_dbversions,
|
||||
get_db_versions,
|
||||
get_installed_extensions,
|
||||
get_user_by_id,
|
||||
get_wallet,
|
||||
@@ -104,7 +104,8 @@ async def extensions(request: Request, user: User = Depends(check_user_exists)):
|
||||
|
||||
all_ext_ids = [ext.code for ext in Extension.get_valid_extensions()]
|
||||
inactive_extensions = [e.id for e in await get_installed_extensions(active=False)]
|
||||
db_version = await get_dbversions()
|
||||
db_versions = await get_db_versions()
|
||||
|
||||
extensions = [
|
||||
{
|
||||
"id": ext.id,
|
||||
@@ -115,7 +116,9 @@ async def extensions(request: Request, user: User = Depends(check_user_exists)):
|
||||
"isFeatured": ext.meta.featured if ext.meta else False,
|
||||
"dependencies": ext.meta.dependencies if ext.meta else "",
|
||||
"isInstalled": ext.id in installed_exts_ids,
|
||||
"hasDatabaseTables": ext.id in db_version,
|
||||
"hasDatabaseTables": next(
|
||||
(True for version in db_versions if version.db == ext.id), False
|
||||
),
|
||||
"isAvailable": ext.id in all_ext_ids,
|
||||
"isAdminOnly": ext.id in settings.lnbits_admin_extensions,
|
||||
"isActive": ext.id not in inactive_extensions,
|
||||
|
||||
Reference in New Issue
Block a user