async postgres
add timestamp conversation pending fixed now statement repair conv remove cleandbsetting
This commit is contained in:
+2
-2
@@ -226,12 +226,12 @@ LNBITS_HIDE_API=false
|
||||
LNBITS_EXTENSIONS_DEFAULT_INSTALL="tpos"
|
||||
|
||||
# Database: to use SQLite, specify LNBITS_DATA_FOLDER
|
||||
# to use PostgreSQL, specify LNBITS_DATABASE_URL=postgres://...
|
||||
# to use PostgreSQL, specify LNBITS_DATABASE_URL=postgresql+asyncpg://...
|
||||
# to use CockroachDB, specify LNBITS_DATABASE_URL=cockroachdb://...
|
||||
# for both PostgreSQL and CockroachDB, you'll need to install
|
||||
# psycopg2 as an additional dependency
|
||||
LNBITS_DATA_FOLDER="./data"
|
||||
# LNBITS_DATABASE_URL="postgres://user:password@host:port/databasename"
|
||||
# LNBITS_DATABASE_URL="postgresql+asyncpg://user:password@host:port/databasename"
|
||||
|
||||
# the service fee (in percent)
|
||||
LNBITS_SERVICE_FEE=0.0
|
||||
|
||||
+2
-1
@@ -62,6 +62,7 @@ from .middleware import (
|
||||
)
|
||||
from .requestvars import g
|
||||
from .tasks import (
|
||||
check_pending_payments,
|
||||
create_task,
|
||||
internal_invoice_listener,
|
||||
invoice_listener,
|
||||
@@ -409,7 +410,7 @@ def register_async_tasks(app: FastAPI):
|
||||
if not settings.lnbits_extensions_deactivate_all:
|
||||
create_task(check_and_register_extensions(app))
|
||||
|
||||
# create_permanent_task(check_pending_payments)
|
||||
create_permanent_task(check_pending_payments)
|
||||
create_permanent_task(invoice_listener)
|
||||
create_permanent_task(internal_invoice_listener)
|
||||
create_permanent_task(cache.invalidate_forever)
|
||||
|
||||
+8
-8
@@ -199,10 +199,7 @@ async def get_user_password(user_id: str) -> Optional[str]:
|
||||
"SELECT pass FROM accounts WHERE id = :user",
|
||||
{"user": user_id},
|
||||
)
|
||||
if not row:
|
||||
return None
|
||||
|
||||
return row[0]
|
||||
return row.get("pass")
|
||||
|
||||
|
||||
# TODO: refactor not a crud function
|
||||
@@ -488,7 +485,7 @@ async def get_user_active_extensions_ids(
|
||||
""",
|
||||
{"user": user_id},
|
||||
)
|
||||
return [e[0] for e in rows]
|
||||
return [e.get("extension", "") for e in rows]
|
||||
|
||||
|
||||
async def update_user_extension_extra(
|
||||
@@ -682,7 +679,7 @@ async def get_wallet_for_key(
|
||||
|
||||
async def get_total_balance(conn: Optional[Connection] = None):
|
||||
row = await (conn or db).fetchone("SELECT SUM(balance) FROM balances")
|
||||
return 0 if row[0] is None else row[0]
|
||||
return row.get("balance", 0)
|
||||
|
||||
|
||||
# wallet payments
|
||||
@@ -1066,10 +1063,13 @@ async def get_payments_history(
|
||||
results.insert(
|
||||
0,
|
||||
PaymentHistoryPoint(
|
||||
balance=balance, date=row[0], income=row[1], spending=row[2]
|
||||
balance=balance,
|
||||
date=row.get("date", 0),
|
||||
income=row.get("income", 0),
|
||||
spending=row.get("spending", 0),
|
||||
),
|
||||
)
|
||||
balance -= row.income - row.spending
|
||||
balance -= row.get("income", 0) - row.get("spending", 0)
|
||||
return results
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import json
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from sqlite3 import Row
|
||||
from typing import Callable, Optional
|
||||
|
||||
from ecdsa import SECP256k1, SigningKey
|
||||
@@ -238,7 +237,7 @@ class Payment(FromRowModel):
|
||||
return self.status == PaymentState.FAILED.value
|
||||
|
||||
@classmethod
|
||||
def from_row(cls, row: Row):
|
||||
def from_row(cls, row: dict):
|
||||
return cls(
|
||||
checking_id=row["checking_id"],
|
||||
payment_hash=row["hash"] or "0" * 64,
|
||||
@@ -341,7 +340,7 @@ class TinyURL(BaseModel):
|
||||
time: float
|
||||
|
||||
@classmethod
|
||||
def from_row(cls, row: Row):
|
||||
def from_row(cls, row: dict):
|
||||
return cls(**dict(row))
|
||||
|
||||
|
||||
|
||||
+60
-55
@@ -7,11 +7,11 @@ import re
|
||||
import time
|
||||
from contextlib import asynccontextmanager
|
||||
from enum import Enum
|
||||
from sqlite3 import Row
|
||||
from typing import Any, Generic, Literal, Optional, TypeVar
|
||||
|
||||
from loguru import logger
|
||||
from pydantic import BaseModel, ValidationError, root_validator
|
||||
from sqlalchemy import event
|
||||
from sqlalchemy.ext.asyncio import AsyncConnection, AsyncEngine, create_async_engine
|
||||
from sqlalchemy.sql import text
|
||||
|
||||
@@ -23,31 +23,16 @@ SQLITE = "SQLITE"
|
||||
|
||||
if settings.lnbits_database_url:
|
||||
database_uri = settings.lnbits_database_url
|
||||
|
||||
if database_uri.startswith("cockroachdb://"):
|
||||
DB_TYPE = COCKROACH
|
||||
else:
|
||||
if not database_uri.startswith("postgresql+asyncpg://"):
|
||||
raise ValueError(
|
||||
"Please use the 'postgresql+asyncpg://...' "
|
||||
"format for the database URL."
|
||||
)
|
||||
DB_TYPE = POSTGRES
|
||||
|
||||
from psycopg2.extensions import DECIMAL, new_type, register_type
|
||||
|
||||
def _parse_timestamp(value, _):
|
||||
if value is None:
|
||||
return None
|
||||
f = "%Y-%m-%d %H:%M:%S.%f"
|
||||
if "." not in value:
|
||||
f = "%Y-%m-%d %H:%M:%S"
|
||||
return time.mktime(datetime.datetime.strptime(value, f).timetuple())
|
||||
|
||||
register_type(
|
||||
new_type(
|
||||
DECIMAL.values,
|
||||
"DEC2FLOAT",
|
||||
lambda value, curs: float(value) if value is not None else None,
|
||||
)
|
||||
)
|
||||
|
||||
register_type(new_type((1184, 1114), "TIMESTAMP2INT", _parse_timestamp))
|
||||
else:
|
||||
if not os.path.isdir(settings.lnbits_data_folder):
|
||||
os.mkdir(settings.lnbits_data_folder)
|
||||
@@ -161,15 +146,17 @@ class Connection(Compat):
|
||||
clean_values[key] = raw_value
|
||||
return clean_values
|
||||
|
||||
async def fetchall(self, query: str, values: Optional[dict] = None) -> list:
|
||||
async def fetchall(self, query: str, values: Optional[dict] = None) -> list[dict]:
|
||||
params = self.rewrite_values(values) if values else {}
|
||||
result = await self.conn.execute(text(self.rewrite_query(query)), params)
|
||||
return result.fetchall()
|
||||
row = result.mappings().all()
|
||||
result.close()
|
||||
return row
|
||||
|
||||
async def fetchone(self, query: str, values: Optional[dict] = None):
|
||||
async def fetchone(self, query: str, values: Optional[dict] = None) -> dict:
|
||||
params = self.rewrite_values(values) if values else {}
|
||||
result = await self.conn.execute(text(self.rewrite_query(query)), params)
|
||||
row = result.fetchone()
|
||||
row = result.mappings().first()
|
||||
result.close()
|
||||
return row
|
||||
|
||||
@@ -209,9 +196,9 @@ class Connection(Compat):
|
||||
if rows:
|
||||
# no need for extra query if no pagination is specified
|
||||
if filters.offset or filters.limit:
|
||||
count = await self.fetchone(
|
||||
result = await self.fetchone(
|
||||
f"""
|
||||
SELECT COUNT(*) FROM (
|
||||
SELECT COUNT(*) as count FROM (
|
||||
{query}
|
||||
{clause}
|
||||
{group_by_string}
|
||||
@@ -219,20 +206,22 @@ class Connection(Compat):
|
||||
""",
|
||||
parsed_values,
|
||||
)
|
||||
count = int(count[0])
|
||||
count = int(result.get("count", 0))
|
||||
else:
|
||||
count = len(rows)
|
||||
else:
|
||||
count = 0
|
||||
|
||||
return Page(
|
||||
data=[model.from_row(row) for row in rows] if model else rows,
|
||||
data=[model.from_row(row) for row in rows] if model else [],
|
||||
total=count,
|
||||
)
|
||||
|
||||
async def execute(self, query: str, values: Optional[dict] = None):
|
||||
params = self.rewrite_values(values) if values else {}
|
||||
return await self.conn.execute(text(self.rewrite_query(query)), params)
|
||||
result = await self.conn.execute(text(self.rewrite_query(query)), params)
|
||||
await self.conn.commit()
|
||||
return result
|
||||
|
||||
|
||||
class Database(Compat):
|
||||
@@ -257,6 +246,28 @@ class Database(Compat):
|
||||
self.engine: AsyncEngine = create_async_engine(
|
||||
database_uri, echo=settings.debug_database
|
||||
)
|
||||
|
||||
if self.type in {POSTGRES, COCKROACH}:
|
||||
|
||||
@event.listens_for(self.engine.sync_engine, "connect")
|
||||
def register_custom_types(dbapi_connection, *_):
|
||||
def _parse_timestamp(value):
|
||||
if value is None:
|
||||
return None
|
||||
f = "%Y-%m-%d %H:%M:%S.%f"
|
||||
if "." not in value:
|
||||
f = "%Y-%m-%d %H:%M:%S"
|
||||
return time.mktime(datetime.datetime.strptime(value, f).timetuple())
|
||||
|
||||
dbapi_connection.run_async(
|
||||
lambda connection: connection.set_type_codec(
|
||||
"TIMESTAMP",
|
||||
encoder=datetime.datetime.timestamp,
|
||||
decoder=_parse_timestamp,
|
||||
schema="pg_catalog",
|
||||
)
|
||||
)
|
||||
|
||||
self.lock = asyncio.Lock()
|
||||
|
||||
logger.trace(f"database {self.type} added for {self.name}")
|
||||
@@ -274,28 +285,22 @@ class Database(Compat):
|
||||
if self.schema:
|
||||
if self.type in {POSTGRES, COCKROACH}:
|
||||
await wconn.execute(
|
||||
f"CREATE SCHEMA IF NOT EXISTS {self.schema}", {}
|
||||
f"CREATE SCHEMA IF NOT EXISTS {self.schema}"
|
||||
)
|
||||
elif self.type == SQLITE:
|
||||
await wconn.execute(
|
||||
f"ATTACH '{self.path}' AS {self.schema}", {}
|
||||
)
|
||||
await wconn.execute(f"ATTACH '{self.path}' AS {self.schema}")
|
||||
|
||||
yield wconn
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
async def fetchall(self, query: str, values: Optional[dict] = None) -> list:
|
||||
async def fetchall(self, query: str, values: Optional[dict] = None) -> list[dict]:
|
||||
async with self.connect() as conn:
|
||||
result = await conn.execute(query, values)
|
||||
return result.fetchall()
|
||||
return await conn.fetchall(query, values)
|
||||
|
||||
async def fetchone(self, query: str, values: Optional[dict] = None):
|
||||
async def fetchone(self, query: str, values: Optional[dict] = None) -> dict:
|
||||
async with self.connect() as conn:
|
||||
result = await conn.execute(query, values)
|
||||
row = result.fetchone()
|
||||
result.close()
|
||||
return row
|
||||
return await conn.fetchone(query, values)
|
||||
|
||||
async def fetch_page(
|
||||
self,
|
||||
@@ -367,8 +372,8 @@ class Operator(Enum):
|
||||
|
||||
class FromRowModel(BaseModel):
|
||||
@classmethod
|
||||
def from_row(cls, row: Row):
|
||||
return cls(**dict(row))
|
||||
def from_row(cls, row: dict):
|
||||
return cls(**row)
|
||||
|
||||
|
||||
class FilterModel(BaseModel):
|
||||
@@ -421,14 +426,14 @@ class Filter(BaseModel, Generic[TFilterModel]):
|
||||
|
||||
return cls(field=field, op=op, values=values, model=model)
|
||||
|
||||
# @property
|
||||
# def statement(self):
|
||||
# if self.op in (Operator.INCLUDE, Operator.EXCLUDE):
|
||||
# placeholders = ", ".join([placeholder] * len(self.values))
|
||||
# stmt = [f"{self.field} {self.op.as_sql} ({placeholders})"]
|
||||
# else:
|
||||
# stmt = [f"{self.field} {self.op.as_sql} {placeholder}"] * len(self.values)
|
||||
# return " OR ".join(stmt)
|
||||
@property
|
||||
def statement(self):
|
||||
if self.op in (Operator.INCLUDE, Operator.EXCLUDE):
|
||||
placeholders = ", ".join([])
|
||||
stmt = [f"{self.field} {self.op.as_sql} ({placeholders})"]
|
||||
else:
|
||||
stmt = [f"{self.field} {self.op.as_sql} :{self.field}"]
|
||||
return " OR ".join(stmt)
|
||||
|
||||
|
||||
class Filters(BaseModel, Generic[TFilterModel]):
|
||||
@@ -474,9 +479,9 @@ class Filters(BaseModel, Generic[TFilterModel]):
|
||||
def where(self, where_stmts: Optional[list[str]] = None) -> str:
|
||||
if not where_stmts:
|
||||
where_stmts = []
|
||||
# if self.filters:
|
||||
# for page_filter in self.filters:
|
||||
# where_stmts.append(page_filter.statement)
|
||||
if self.filters:
|
||||
for page_filter in self.filters:
|
||||
where_stmts.append(page_filter.statement)
|
||||
if self.search and self.model:
|
||||
fields = self.model.__search_fields__
|
||||
if DB_TYPE == POSTGRES:
|
||||
|
||||
Generated
+69
-1
@@ -65,6 +65,74 @@ files = [
|
||||
{file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-timeout"
|
||||
version = "4.0.3"
|
||||
description = "Timeout context manager for asyncio programs"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"},
|
||||
{file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asyncpg"
|
||||
version = "0.29.0"
|
||||
description = "An asyncio PostgreSQL driver"
|
||||
optional = false
|
||||
python-versions = ">=3.8.0"
|
||||
files = [
|
||||
{file = "asyncpg-0.29.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72fd0ef9f00aeed37179c62282a3d14262dbbafb74ec0ba16e1b1864d8a12169"},
|
||||
{file = "asyncpg-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52e8f8f9ff6e21f9b39ca9f8e3e33a5fcdceaf5667a8c5c32bee158e313be385"},
|
||||
{file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e6823a7012be8b68301342ba33b4740e5a166f6bbda0aee32bc01638491a22"},
|
||||
{file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:746e80d83ad5d5464cfbf94315eb6744222ab00aa4e522b704322fb182b83610"},
|
||||
{file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ff8e8109cd6a46ff852a5e6bab8b0a047d7ea42fcb7ca5ae6eaae97d8eacf397"},
|
||||
{file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97eb024685b1d7e72b1972863de527c11ff87960837919dac6e34754768098eb"},
|
||||
{file = "asyncpg-0.29.0-cp310-cp310-win32.whl", hash = "sha256:5bbb7f2cafd8d1fa3e65431833de2642f4b2124be61a449fa064e1a08d27e449"},
|
||||
{file = "asyncpg-0.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:76c3ac6530904838a4b650b2880f8e7af938ee049e769ec2fba7cd66469d7772"},
|
||||
{file = "asyncpg-0.29.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4900ee08e85af01adb207519bb4e14b1cae8fd21e0ccf80fac6aa60b6da37b4"},
|
||||
{file = "asyncpg-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a65c1dcd820d5aea7c7d82a3fdcb70e096f8f70d1a8bf93eb458e49bfad036ac"},
|
||||
{file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b52e46f165585fd6af4863f268566668407c76b2c72d366bb8b522fa66f1870"},
|
||||
{file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc600ee8ef3dd38b8d67421359779f8ccec30b463e7aec7ed481c8346decf99f"},
|
||||
{file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:039a261af4f38f949095e1e780bae84a25ffe3e370175193174eb08d3cecab23"},
|
||||
{file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6feaf2d8f9138d190e5ec4390c1715c3e87b37715cd69b2c3dfca616134efd2b"},
|
||||
{file = "asyncpg-0.29.0-cp311-cp311-win32.whl", hash = "sha256:1e186427c88225ef730555f5fdda6c1812daa884064bfe6bc462fd3a71c4b675"},
|
||||
{file = "asyncpg-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfe73ffae35f518cfd6e4e5f5abb2618ceb5ef02a2365ce64f132601000587d3"},
|
||||
{file = "asyncpg-0.29.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6011b0dc29886ab424dc042bf9eeb507670a3b40aece3439944006aafe023178"},
|
||||
{file = "asyncpg-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b544ffc66b039d5ec5a7454667f855f7fec08e0dfaf5a5490dfafbb7abbd2cfb"},
|
||||
{file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d84156d5fb530b06c493f9e7635aa18f518fa1d1395ef240d211cb563c4e2364"},
|
||||
{file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54858bc25b49d1114178d65a88e48ad50cb2b6f3e475caa0f0c092d5f527c106"},
|
||||
{file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bde17a1861cf10d5afce80a36fca736a86769ab3579532c03e45f83ba8a09c59"},
|
||||
{file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:37a2ec1b9ff88d8773d3eb6d3784dc7e3fee7756a5317b67f923172a4748a175"},
|
||||
{file = "asyncpg-0.29.0-cp312-cp312-win32.whl", hash = "sha256:bb1292d9fad43112a85e98ecdc2e051602bce97c199920586be83254d9dafc02"},
|
||||
{file = "asyncpg-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:2245be8ec5047a605e0b454c894e54bf2ec787ac04b1cb7e0d3c67aa1e32f0fe"},
|
||||
{file = "asyncpg-0.29.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0009a300cae37b8c525e5b449233d59cd9868fd35431abc470a3e364d2b85cb9"},
|
||||
{file = "asyncpg-0.29.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cad1324dbb33f3ca0cd2074d5114354ed3be2b94d48ddfd88af75ebda7c43cc"},
|
||||
{file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:012d01df61e009015944ac7543d6ee30c2dc1eb2f6b10b62a3f598beb6531548"},
|
||||
{file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000c996c53c04770798053e1730d34e30cb645ad95a63265aec82da9093d88e7"},
|
||||
{file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e0bfe9c4d3429706cf70d3249089de14d6a01192d617e9093a8e941fea8ee775"},
|
||||
{file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:642a36eb41b6313ffa328e8a5c5c2b5bea6ee138546c9c3cf1bffaad8ee36dd9"},
|
||||
{file = "asyncpg-0.29.0-cp38-cp38-win32.whl", hash = "sha256:a921372bbd0aa3a5822dd0409da61b4cd50df89ae85150149f8c119f23e8c408"},
|
||||
{file = "asyncpg-0.29.0-cp38-cp38-win_amd64.whl", hash = "sha256:103aad2b92d1506700cbf51cd8bb5441e7e72e87a7b3a2ca4e32c840f051a6a3"},
|
||||
{file = "asyncpg-0.29.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5340dd515d7e52f4c11ada32171d87c05570479dc01dc66d03ee3e150fb695da"},
|
||||
{file = "asyncpg-0.29.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e17b52c6cf83e170d3d865571ba574577ab8e533e7361a2b8ce6157d02c665d3"},
|
||||
{file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f100d23f273555f4b19b74a96840aa27b85e99ba4b1f18d4ebff0734e78dc090"},
|
||||
{file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48e7c58b516057126b363cec8ca02b804644fd012ef8e6c7e23386b7d5e6ce83"},
|
||||
{file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f9ea3f24eb4c49a615573724d88a48bd1b7821c890c2effe04f05382ed9e8810"},
|
||||
{file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8d36c7f14a22ec9e928f15f92a48207546ffe68bc412f3be718eedccdf10dc5c"},
|
||||
{file = "asyncpg-0.29.0-cp39-cp39-win32.whl", hash = "sha256:797ab8123ebaed304a1fad4d7576d5376c3a006a4100380fb9d517f0b59c1ab2"},
|
||||
{file = "asyncpg-0.29.0-cp39-cp39-win_amd64.whl", hash = "sha256:cce08a178858b426ae1aa8409b5cc171def45d4293626e7aa6510696d46decd8"},
|
||||
{file = "asyncpg-0.29.0.tar.gz", hash = "sha256:d1c49e1f44fffafd9a55e1a9b101590859d881d639ea2922516f5d9c512d354e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
async-timeout = {version = ">=4.0.3", markers = "python_version < \"3.12.0\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["Sphinx (>=5.3.0,<5.4.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"]
|
||||
test = ["flake8 (>=6.1,<7.0)", "uvloop (>=0.15.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "23.2.0"
|
||||
@@ -3183,4 +3251,4 @@ liquid = ["wallycore"]
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.10 | ^3.9"
|
||||
content-hash = "56cbae093e02e5165df2c73d62b054e71f9d431fb704726b625473720496d473"
|
||||
content-hash = "e722e73e2efaad876ac742628e068b5da1596f76a83c2188d39bf9719d08a28b"
|
||||
|
||||
+3
-2
@@ -20,7 +20,6 @@ fastapi = "0.112.0"
|
||||
httpx = "0.27.0"
|
||||
jinja2 = "3.1.4"
|
||||
lnurl = "0.5.3"
|
||||
psycopg2-binary = "2.9.9"
|
||||
pydantic = "1.10.17"
|
||||
pyqrcode = "1.2.1"
|
||||
shortuuid = "1.0.13"
|
||||
@@ -28,6 +27,9 @@ sse-starlette = "1.8.2"
|
||||
typing-extensions = "4.12.2"
|
||||
uvicorn = "0.30.5"
|
||||
sqlalchemy = "1.4.52"
|
||||
aiosqlite = "^0.20.0"
|
||||
asyncpg = "^0.29.0"
|
||||
sse-starlette = "1.6.5"
|
||||
uvloop = "0.19.0"
|
||||
websockets = "11.0.3"
|
||||
loguru = "0.7.2"
|
||||
@@ -59,7 +61,6 @@ wallycore = {version = "1.3.0", optional = true}
|
||||
# needed for breez funding source
|
||||
breez-sdk = {version = "0.5.2", optional = true}
|
||||
|
||||
aiosqlite = "^0.20.0"
|
||||
[tool.poetry.extras]
|
||||
breez = ["breez-sdk"]
|
||||
liquid = ["wallycore"]
|
||||
|
||||
@@ -25,7 +25,6 @@ from lnbits.core.views.payment_api import api_payments_create_invoice
|
||||
from lnbits.db import DB_TYPE, SQLITE, Database
|
||||
from lnbits.settings import settings
|
||||
from tests.helpers import (
|
||||
clean_database,
|
||||
get_random_invoice_data,
|
||||
)
|
||||
|
||||
@@ -47,7 +46,6 @@ def event_loop():
|
||||
# use session scope to run once before and once after all tests
|
||||
@pytest_asyncio.fixture(scope="session")
|
||||
async def app():
|
||||
clean_database(settings)
|
||||
app = create_app()
|
||||
async with LifespanManager(app) as manager:
|
||||
settings.first_install = False
|
||||
|
||||
+1
-23
@@ -2,11 +2,7 @@ import random
|
||||
import string
|
||||
from typing import Optional
|
||||
|
||||
from psycopg2 import connect
|
||||
from psycopg2.errors import InvalidCatalogName
|
||||
|
||||
from lnbits import core
|
||||
from lnbits.db import DB_TYPE, POSTGRES, FromRowModel
|
||||
from lnbits.db import FromRowModel
|
||||
from lnbits.wallets import get_funding_source, set_funding_source
|
||||
|
||||
|
||||
@@ -35,21 +31,3 @@ set_funding_source()
|
||||
funding_source = get_funding_source()
|
||||
is_fake: bool = funding_source.__class__.__name__ == "FakeWallet"
|
||||
is_regtest: bool = not is_fake
|
||||
|
||||
|
||||
def clean_database(settings):
|
||||
if DB_TYPE == POSTGRES:
|
||||
conn = connect(settings.lnbits_database_url)
|
||||
conn.autocommit = True
|
||||
with conn.cursor() as cur:
|
||||
try:
|
||||
cur.execute("DROP DATABASE lnbits_test")
|
||||
except InvalidCatalogName:
|
||||
pass
|
||||
cur.execute("CREATE DATABASE lnbits_test")
|
||||
core.db.__init__("database")
|
||||
conn.close()
|
||||
else:
|
||||
# TODO: do this once mock data is removed from test data folder
|
||||
# os.remove(settings.lnbits_data_folder + "/database.sqlite3")
|
||||
pass
|
||||
|
||||
+9
-5
@@ -1,5 +1,5 @@
|
||||
# Python script to migrate an LNbits SQLite DB to Postgres
|
||||
# All credits to @Fritz446 for the awesome work
|
||||
# credits to @Fritz446 for the awesome work
|
||||
|
||||
# pip install psycopg2 OR psycopg2-binary
|
||||
|
||||
@@ -9,10 +9,14 @@ import sqlite3
|
||||
import sys
|
||||
from typing import List, Optional
|
||||
|
||||
import psycopg2
|
||||
|
||||
from lnbits.settings import settings
|
||||
|
||||
try:
|
||||
import psycopg2
|
||||
except ImportError:
|
||||
print("Please install psycopg2")
|
||||
sys.exit(1)
|
||||
|
||||
sqfolder = settings.lnbits_data_folder
|
||||
db_url = settings.lnbits_database_url
|
||||
|
||||
@@ -55,8 +59,8 @@ def check_db_versions(sqdb):
|
||||
version = dbpost[key]
|
||||
if value != version:
|
||||
raise Exception(
|
||||
f"sqlite database version ({value}) of {key} doesn't match postgres"
|
||||
f" database version {version}"
|
||||
f"sqlite database version ({value}) of {key} doesn't match "
|
||||
f"postgres database version {version}"
|
||||
)
|
||||
|
||||
connection = postgres.connection
|
||||
|
||||
Reference in New Issue
Block a user