mirror of
https://github.com/lnbits/lnbits.git
synced 2025-03-17 13:21:48 +01:00
fix: python versions were defined incorrectly and remove 3.9 (#3006)
* chore: update requirements to python3.10 * chore: format RUF007 rule for py310 * wrongly specified python version python3.13 there is a greenlet build error
This commit is contained in:
parent
4ce14e2312
commit
c5964436b5
@ -226,7 +226,7 @@ Problems installing? These commands have helped us install LNbits.
|
|||||||
sudo apt install pkg-config libffi-dev libpq-dev
|
sudo apt install pkg-config libffi-dev libpq-dev
|
||||||
|
|
||||||
# build essentials for debian/ubuntu
|
# build essentials for debian/ubuntu
|
||||||
sudo apt install python3.9-dev gcc build-essential
|
sudo apt install python3.10-dev gcc build-essential
|
||||||
|
|
||||||
# if the secp256k1 build fails:
|
# if the secp256k1 build fails:
|
||||||
# if you used poetry
|
# if you used poetry
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
@ -10,16 +9,16 @@ from lnbits.settings import settings
|
|||||||
|
|
||||||
|
|
||||||
class AuditEntry(BaseModel):
|
class AuditEntry(BaseModel):
|
||||||
component: Optional[str] = None
|
component: str | None = None
|
||||||
ip_address: Optional[str] = None
|
ip_address: str | None = None
|
||||||
user_id: Optional[str] = None
|
user_id: str | None = None
|
||||||
path: Optional[str] = None
|
path: str | None = None
|
||||||
request_type: Optional[str] = None
|
request_type: str | None = None
|
||||||
request_method: Optional[str] = None
|
request_method: str | None = None
|
||||||
request_details: Optional[str] = None
|
request_details: str | None = None
|
||||||
response_code: Optional[str] = None
|
response_code: str | None = None
|
||||||
duration: float
|
duration: float
|
||||||
delete_at: Optional[datetime] = None
|
delete_at: datetime | None = None
|
||||||
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||||
|
|
||||||
def __init__(self, **data):
|
def __init__(self, **data):
|
||||||
@ -42,12 +41,12 @@ class AuditFilters(FilterModel):
|
|||||||
"duration",
|
"duration",
|
||||||
]
|
]
|
||||||
|
|
||||||
ip_address: Optional[str] = None
|
ip_address: str | None = None
|
||||||
user_id: Optional[str] = None
|
user_id: str | None = None
|
||||||
path: Optional[str] = None
|
path: str | None = None
|
||||||
request_method: Optional[str] = None
|
request_method: str | None = None
|
||||||
response_code: Optional[str] = None
|
response_code: str | None = None
|
||||||
component: Optional[str] = None
|
component: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class AuditCountStat(BaseModel):
|
class AuditCountStat(BaseModel):
|
||||||
|
@ -7,7 +7,7 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
import zipfile
|
import zipfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Optional
|
from typing import Any
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
@ -29,17 +29,17 @@ class ExplicitRelease(BaseModel):
|
|||||||
archive: str
|
archive: str
|
||||||
hash: str
|
hash: str
|
||||||
dependencies: list[str] = []
|
dependencies: list[str] = []
|
||||||
repo: Optional[str]
|
repo: str | None
|
||||||
icon: Optional[str]
|
icon: str | None
|
||||||
short_description: Optional[str]
|
short_description: str | None
|
||||||
min_lnbits_version: Optional[str]
|
min_lnbits_version: str | None
|
||||||
max_lnbits_version: Optional[str]
|
max_lnbits_version: str | None
|
||||||
html_url: Optional[str] # todo: release_url
|
html_url: str | None # todo: release_url
|
||||||
warning: Optional[str]
|
warning: str | None
|
||||||
info_notification: Optional[str]
|
info_notification: str | None
|
||||||
critical_notification: Optional[str]
|
critical_notification: str | None
|
||||||
details_link: Optional[str]
|
details_link: str | None
|
||||||
pay_link: Optional[str]
|
pay_link: str | None
|
||||||
|
|
||||||
def is_version_compatible(self):
|
def is_version_compatible(self):
|
||||||
return is_lnbits_version_ok(self.min_lnbits_version, self.max_lnbits_version)
|
return is_lnbits_version_ok(self.min_lnbits_version, self.max_lnbits_version)
|
||||||
@ -77,9 +77,9 @@ class ExtensionConfig(BaseModel):
|
|||||||
name: str
|
name: str
|
||||||
short_description: str
|
short_description: str
|
||||||
tile: str = ""
|
tile: str = ""
|
||||||
warning: Optional[str] = ""
|
warning: str | None = ""
|
||||||
min_lnbits_version: Optional[str]
|
min_lnbits_version: str | None
|
||||||
max_lnbits_version: Optional[str]
|
max_lnbits_version: str | None
|
||||||
|
|
||||||
def is_version_compatible(self) -> bool:
|
def is_version_compatible(self) -> bool:
|
||||||
return is_lnbits_version_ok(self.min_lnbits_version, self.max_lnbits_version)
|
return is_lnbits_version_ok(self.min_lnbits_version, self.max_lnbits_version)
|
||||||
@ -87,7 +87,7 @@ class ExtensionConfig(BaseModel):
|
|||||||
@classmethod
|
@classmethod
|
||||||
async def fetch_github_release_config(
|
async def fetch_github_release_config(
|
||||||
cls, org: str, repo: str, tag_name: str
|
cls, org: str, repo: str, tag_name: str
|
||||||
) -> Optional[ExtensionConfig]:
|
) -> ExtensionConfig | None:
|
||||||
config_url = (
|
config_url = (
|
||||||
f"https://raw.githubusercontent.com/{org}/{repo}/{tag_name}/config.json"
|
f"https://raw.githubusercontent.com/{org}/{repo}/{tag_name}/config.json"
|
||||||
)
|
)
|
||||||
@ -97,28 +97,28 @@ class ExtensionConfig(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class ReleasePaymentInfo(BaseModel):
|
class ReleasePaymentInfo(BaseModel):
|
||||||
amount: Optional[int] = None
|
amount: int | None = None
|
||||||
pay_link: Optional[str] = None
|
pay_link: str | None = None
|
||||||
payment_hash: Optional[str] = None
|
payment_hash: str | None = None
|
||||||
payment_request: Optional[str] = None
|
payment_request: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class PayToEnableInfo(BaseModel):
|
class PayToEnableInfo(BaseModel):
|
||||||
amount: int = 0
|
amount: int = 0
|
||||||
required: bool = False
|
required: bool = False
|
||||||
wallet: Optional[str] = None
|
wallet: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class UserExtensionInfo(BaseModel):
|
class UserExtensionInfo(BaseModel):
|
||||||
paid_to_enable: Optional[bool] = False
|
paid_to_enable: bool | None = False
|
||||||
payment_hash_to_enable: Optional[str] = None
|
payment_hash_to_enable: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class UserExtension(BaseModel):
|
class UserExtension(BaseModel):
|
||||||
user: str
|
user: str
|
||||||
extension: str
|
extension: str
|
||||||
active: bool
|
active: bool
|
||||||
extra: Optional[UserExtensionInfo] = None
|
extra: UserExtensionInfo | None = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_paid(self) -> bool:
|
def is_paid(self) -> bool:
|
||||||
@ -140,10 +140,10 @@ class UserExtension(BaseModel):
|
|||||||
class Extension(BaseModel):
|
class Extension(BaseModel):
|
||||||
code: str
|
code: str
|
||||||
is_valid: bool
|
is_valid: bool
|
||||||
name: Optional[str] = None
|
name: str | None = None
|
||||||
short_description: Optional[str] = None
|
short_description: str | None = None
|
||||||
tile: Optional[str] = None
|
tile: str | None = None
|
||||||
upgrade_hash: Optional[str] = ""
|
upgrade_hash: str | None = ""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def module_name(self) -> str:
|
def module_name(self) -> str:
|
||||||
@ -176,21 +176,21 @@ class ExtensionRelease(BaseModel):
|
|||||||
archive: str
|
archive: str
|
||||||
source_repo: str
|
source_repo: str
|
||||||
is_github_release: bool = False
|
is_github_release: bool = False
|
||||||
hash: Optional[str] = None
|
hash: str | None = None
|
||||||
min_lnbits_version: Optional[str] = None
|
min_lnbits_version: str | None = None
|
||||||
max_lnbits_version: Optional[str] = None
|
max_lnbits_version: str | None = None
|
||||||
is_version_compatible: Optional[bool] = True
|
is_version_compatible: bool | None = True
|
||||||
html_url: Optional[str] = None
|
html_url: str | None = None
|
||||||
description: Optional[str] = None
|
description: str | None = None
|
||||||
warning: Optional[str] = None
|
warning: str | None = None
|
||||||
repo: Optional[str] = None
|
repo: str | None = None
|
||||||
icon: Optional[str] = None
|
icon: str | None = None
|
||||||
details_link: Optional[str] = None
|
details_link: str | None = None
|
||||||
|
|
||||||
pay_link: Optional[str] = None
|
pay_link: str | None = None
|
||||||
cost_sats: Optional[int] = None
|
cost_sats: int | None = None
|
||||||
paid_sats: Optional[int] = 0
|
paid_sats: int | None = 0
|
||||||
payment_hash: Optional[str] = None
|
payment_hash: str | None = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def archive_url(self) -> str:
|
def archive_url(self) -> str:
|
||||||
@ -208,8 +208,8 @@ class ExtensionRelease(BaseModel):
|
|||||||
self.cost_sats = payment_info.amount if payment_info else None
|
self.cost_sats = payment_info.amount if payment_info else None
|
||||||
|
|
||||||
async def fetch_release_payment_info(
|
async def fetch_release_payment_info(
|
||||||
self, amount: Optional[int] = None
|
self, amount: int | None = None
|
||||||
) -> Optional[ReleasePaymentInfo]:
|
) -> ReleasePaymentInfo | None:
|
||||||
url = f"{self.pay_link}?amount={amount}" if amount else self.pay_link
|
url = f"{self.pay_link}?amount={amount}" if amount else self.pay_link
|
||||||
assert url, "Missing URL for payment info."
|
assert url, "Missing URL for payment info."
|
||||||
try:
|
try:
|
||||||
@ -281,7 +281,7 @@ class ExtensionRelease(BaseModel):
|
|||||||
return [GitHubRepoRelease.parse_obj(r) for r in releases]
|
return [GitHubRepoRelease.parse_obj(r) for r in releases]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def fetch_release_details(cls, details_link: str) -> Optional[dict]:
|
async def fetch_release_details(cls, details_link: str) -> dict | None:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
@ -300,12 +300,12 @@ class ExtensionRelease(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class ExtensionMeta(BaseModel):
|
class ExtensionMeta(BaseModel):
|
||||||
installed_release: Optional[ExtensionRelease] = None
|
installed_release: ExtensionRelease | None = None
|
||||||
latest_release: Optional[ExtensionRelease] = None
|
latest_release: ExtensionRelease | None = None
|
||||||
pay_to_enable: Optional[PayToEnableInfo] = None
|
pay_to_enable: PayToEnableInfo | None = None
|
||||||
payments: list[ReleasePaymentInfo] = []
|
payments: list[ReleasePaymentInfo] = []
|
||||||
dependencies: list[str] = []
|
dependencies: list[str] = []
|
||||||
archive: Optional[str] = None
|
archive: str | None = None
|
||||||
featured: bool = False
|
featured: bool = False
|
||||||
|
|
||||||
|
|
||||||
@ -313,11 +313,11 @@ class InstallableExtension(BaseModel):
|
|||||||
id: str
|
id: str
|
||||||
name: str
|
name: str
|
||||||
version: str
|
version: str
|
||||||
active: Optional[bool] = False
|
active: bool | None = False
|
||||||
short_description: Optional[str] = None
|
short_description: str | None = None
|
||||||
icon: Optional[str] = None
|
icon: str | None = None
|
||||||
stars: int = 0
|
stars: int = 0
|
||||||
meta: Optional[ExtensionMeta] = None
|
meta: ExtensionMeta | None = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hash(self) -> str:
|
def hash(self) -> str:
|
||||||
@ -452,7 +452,7 @@ class InstallableExtension(BaseModel):
|
|||||||
|
|
||||||
shutil.rmtree(self.ext_upgrade_dir, True)
|
shutil.rmtree(self.ext_upgrade_dir, True)
|
||||||
|
|
||||||
def check_latest_version(self, release: Optional[ExtensionRelease]):
|
def check_latest_version(self, release: ExtensionRelease | None):
|
||||||
if not release:
|
if not release:
|
||||||
return
|
return
|
||||||
if not self.meta or not self.meta.latest_release:
|
if not self.meta or not self.meta.latest_release:
|
||||||
@ -465,9 +465,7 @@ class InstallableExtension(BaseModel):
|
|||||||
):
|
):
|
||||||
self.meta.latest_release = release
|
self.meta.latest_release = release
|
||||||
|
|
||||||
def find_existing_payment(
|
def find_existing_payment(self, pay_link: str | None) -> ReleasePaymentInfo | None:
|
||||||
self, pay_link: Optional[str]
|
|
||||||
) -> Optional[ReleasePaymentInfo]:
|
|
||||||
if not pay_link or not self.meta or not self.meta.payments:
|
if not pay_link or not self.meta or not self.meta.payments:
|
||||||
return None
|
return None
|
||||||
return next(
|
return next(
|
||||||
@ -507,7 +505,7 @@ class InstallableExtension(BaseModel):
|
|||||||
@classmethod
|
@classmethod
|
||||||
async def from_github_release(
|
async def from_github_release(
|
||||||
cls, github_release: GitHubRelease
|
cls, github_release: GitHubRelease
|
||||||
) -> Optional[InstallableExtension]:
|
) -> InstallableExtension | None:
|
||||||
try:
|
try:
|
||||||
repo, latest_release, config = await cls.fetch_github_repo_info(
|
repo, latest_release, config = await cls.fetch_github_repo_info(
|
||||||
github_release.organisation, github_release.repository
|
github_release.organisation, github_release.repository
|
||||||
@ -546,7 +544,7 @@ class InstallableExtension(BaseModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_ext_dir(cls, ext_id: str) -> Optional[InstallableExtension]:
|
def from_ext_dir(cls, ext_id: str) -> InstallableExtension | None:
|
||||||
try:
|
try:
|
||||||
conf_path = Path(
|
conf_path = Path(
|
||||||
settings.lnbits_extensions_path, "extensions", ext_id, "config.json"
|
settings.lnbits_extensions_path, "extensions", ext_id, "config.json"
|
||||||
@ -657,7 +655,7 @@ class InstallableExtension(BaseModel):
|
|||||||
@classmethod
|
@classmethod
|
||||||
async def get_extension_release(
|
async def get_extension_release(
|
||||||
cls, ext_id: str, source_repo: str, archive: str, version: str
|
cls, ext_id: str, source_repo: str, archive: str, version: str
|
||||||
) -> Optional[ExtensionRelease]:
|
) -> ExtensionRelease | None:
|
||||||
all_releases: list[ExtensionRelease] = (
|
all_releases: list[ExtensionRelease] = (
|
||||||
await InstallableExtension.get_extension_releases(ext_id)
|
await InstallableExtension.get_extension_releases(ext_id)
|
||||||
)
|
)
|
||||||
@ -708,8 +706,8 @@ class CreateExtension(BaseModel):
|
|||||||
archive: str
|
archive: str
|
||||||
source_repo: str
|
source_repo: str
|
||||||
version: str
|
version: str
|
||||||
cost_sats: Optional[int] = 0
|
cost_sats: int | None = 0
|
||||||
payment_hash: Optional[str] = None
|
payment_hash: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class ExtensionDetailsRequest(BaseModel):
|
class ExtensionDetailsRequest(BaseModel):
|
||||||
@ -718,7 +716,7 @@ class ExtensionDetailsRequest(BaseModel):
|
|||||||
version: str
|
version: str
|
||||||
|
|
||||||
|
|
||||||
async def github_api_get(url: str, error_msg: Optional[str]) -> Any:
|
async def github_api_get(url: str, error_msg: str | None) -> Any:
|
||||||
headers = {"User-Agent": settings.user_agent}
|
headers = {"User-Agent": settings.user_agent}
|
||||||
if settings.lnbits_ext_github_token:
|
if settings.lnbits_ext_github_token:
|
||||||
headers["Authorization"] = f"Bearer {settings.lnbits_ext_github_token}"
|
headers["Authorization"] = f"Bearer {settings.lnbits_ext_github_token}"
|
||||||
@ -730,7 +728,7 @@ async def github_api_get(url: str, error_msg: Optional[str]) -> Any:
|
|||||||
return resp.json()
|
return resp.json()
|
||||||
|
|
||||||
|
|
||||||
def icon_to_github_url(source_repo: str, path: Optional[str]) -> str:
|
def icon_to_github_url(source_repo: str, path: str | None) -> str:
|
||||||
if not path:
|
if not path:
|
||||||
return ""
|
return ""
|
||||||
_, _, *rest = path.split("/")
|
_, _, *rest = path.split("/")
|
||||||
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Literal, Optional
|
from typing import Literal
|
||||||
|
|
||||||
from fastapi import Query
|
from fastapi import Query
|
||||||
from pydantic import BaseModel, Field, validator
|
from pydantic import BaseModel, Field, validator
|
||||||
@ -28,16 +28,16 @@ class PaymentState(str, Enum):
|
|||||||
|
|
||||||
|
|
||||||
class PaymentExtra(BaseModel):
|
class PaymentExtra(BaseModel):
|
||||||
comment: Optional[str] = None
|
comment: str | None = None
|
||||||
success_action: Optional[str] = None
|
success_action: str | None = None
|
||||||
lnurl_response: Optional[str] = None
|
lnurl_response: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class PayInvoice(BaseModel):
|
class PayInvoice(BaseModel):
|
||||||
payment_request: str
|
payment_request: str
|
||||||
description: Optional[str] = None
|
description: str | None = None
|
||||||
max_sat: Optional[int] = None
|
max_sat: int | None = None
|
||||||
extra: Optional[dict] = {}
|
extra: dict | None = {}
|
||||||
|
|
||||||
|
|
||||||
class CreatePayment(BaseModel):
|
class CreatePayment(BaseModel):
|
||||||
@ -46,10 +46,10 @@ class CreatePayment(BaseModel):
|
|||||||
bolt11: str
|
bolt11: str
|
||||||
amount_msat: int
|
amount_msat: int
|
||||||
memo: str
|
memo: str
|
||||||
extra: Optional[dict] = {}
|
extra: dict | None = {}
|
||||||
preimage: Optional[str] = None
|
preimage: str | None = None
|
||||||
expiry: Optional[datetime] = None
|
expiry: datetime | None = None
|
||||||
webhook: Optional[str] = None
|
webhook: str | None = None
|
||||||
fee: int = 0
|
fee: int = 0
|
||||||
|
|
||||||
|
|
||||||
@ -61,13 +61,13 @@ class Payment(BaseModel):
|
|||||||
fee: int
|
fee: int
|
||||||
bolt11: str
|
bolt11: str
|
||||||
status: str = PaymentState.PENDING
|
status: str = PaymentState.PENDING
|
||||||
memo: Optional[str] = None
|
memo: str | None = None
|
||||||
expiry: Optional[datetime] = None
|
expiry: datetime | None = None
|
||||||
webhook: Optional[str] = None
|
webhook: str | None = None
|
||||||
webhook_status: Optional[int] = None
|
webhook_status: int | None = None
|
||||||
preimage: Optional[str] = None
|
preimage: str | None = None
|
||||||
tag: Optional[str] = None
|
tag: str | None = None
|
||||||
extension: Optional[str] = None
|
extension: str | None = None
|
||||||
time: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
time: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||||
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||||
updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||||
@ -129,16 +129,16 @@ class PaymentFilters(FilterModel):
|
|||||||
|
|
||||||
__sort_fields__ = ["created_at", "amount", "fee", "memo", "time", "tag"]
|
__sort_fields__ = ["created_at", "amount", "fee", "memo", "time", "tag"]
|
||||||
|
|
||||||
status: Optional[str]
|
status: str | None
|
||||||
tag: Optional[str]
|
tag: str | None
|
||||||
checking_id: Optional[str]
|
checking_id: str | None
|
||||||
amount: int
|
amount: int
|
||||||
fee: int
|
fee: int
|
||||||
memo: Optional[str]
|
memo: str | None
|
||||||
time: datetime
|
time: datetime
|
||||||
preimage: Optional[str]
|
preimage: str | None
|
||||||
payment_hash: Optional[str]
|
payment_hash: str | None
|
||||||
wallet_id: Optional[str]
|
wallet_id: str | None
|
||||||
|
|
||||||
|
|
||||||
class PaymentDataPoint(BaseModel):
|
class PaymentDataPoint(BaseModel):
|
||||||
@ -173,11 +173,11 @@ class PaymentWalletStats(BaseModel):
|
|||||||
class PaymentDailyStats(BaseModel):
|
class PaymentDailyStats(BaseModel):
|
||||||
date: datetime
|
date: datetime
|
||||||
balance: float = 0
|
balance: float = 0
|
||||||
balance_in: Optional[float] = 0
|
balance_in: float | None = 0
|
||||||
balance_out: Optional[float] = 0
|
balance_out: float | None = 0
|
||||||
payments_count: int = 0
|
payments_count: int = 0
|
||||||
count_in: Optional[int] = 0
|
count_in: int | None = 0
|
||||||
count_out: Optional[int] = 0
|
count_out: int | None = 0
|
||||||
fee: float = 0
|
fee: float = 0
|
||||||
|
|
||||||
|
|
||||||
@ -190,7 +190,7 @@ class PaymentHistoryPoint(BaseModel):
|
|||||||
|
|
||||||
class DecodePayment(BaseModel):
|
class DecodePayment(BaseModel):
|
||||||
data: str
|
data: str
|
||||||
filter_fields: Optional[list[str]] = []
|
filter_fields: list[str] | None = []
|
||||||
|
|
||||||
|
|
||||||
class CreateInvoice(BaseModel):
|
class CreateInvoice(BaseModel):
|
||||||
@ -198,14 +198,14 @@ class CreateInvoice(BaseModel):
|
|||||||
internal: bool = False
|
internal: bool = False
|
||||||
out: bool = True
|
out: bool = True
|
||||||
amount: float = Query(None, ge=0)
|
amount: float = Query(None, ge=0)
|
||||||
memo: Optional[str] = None
|
memo: str | None = None
|
||||||
description_hash: Optional[str] = None
|
description_hash: str | None = None
|
||||||
unhashed_description: Optional[str] = None
|
unhashed_description: str | None = None
|
||||||
expiry: Optional[int] = None
|
expiry: int | None = None
|
||||||
extra: Optional[dict] = None
|
extra: dict | None = None
|
||||||
webhook: Optional[str] = None
|
webhook: str | None = None
|
||||||
bolt11: Optional[str] = None
|
bolt11: str | None = None
|
||||||
lnurl_callback: Optional[str] = None
|
lnurl_callback: str | None = None
|
||||||
|
|
||||||
@validator("unit")
|
@validator("unit")
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from typing import Optional
|
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from fastapi import Query
|
from fastapi import Query
|
||||||
@ -17,16 +16,16 @@ from .wallets import Wallet
|
|||||||
|
|
||||||
|
|
||||||
class UserExtra(BaseModel):
|
class UserExtra(BaseModel):
|
||||||
email_verified: Optional[bool] = False
|
email_verified: bool | None = False
|
||||||
first_name: Optional[str] = None
|
first_name: str | None = None
|
||||||
last_name: Optional[str] = None
|
last_name: str | None = None
|
||||||
display_name: Optional[str] = None
|
display_name: str | None = None
|
||||||
picture: Optional[str] = None
|
picture: str | None = None
|
||||||
# Auth provider, possible values:
|
# Auth provider, possible values:
|
||||||
# - "env": the user was created automatically by the system
|
# - "env": the user was created automatically by the system
|
||||||
# - "lnbits": the user was created via register form (username/pass or user_id only)
|
# - "lnbits": the user was created via register form (username/pass or user_id only)
|
||||||
# - "google | github | ...": the user was created using an SSO provider
|
# - "google | github | ...": the user was created using an SSO provider
|
||||||
provider: Optional[str] = "lnbits" # auth provider
|
provider: str | None = "lnbits" # auth provider
|
||||||
|
|
||||||
|
|
||||||
class EndpointAccess(BaseModel):
|
class EndpointAccess(BaseModel):
|
||||||
@ -50,13 +49,13 @@ class AccessControlList(BaseModel):
|
|||||||
endpoints: list[EndpointAccess] = []
|
endpoints: list[EndpointAccess] = []
|
||||||
token_id_list: list[SimpleItem] = []
|
token_id_list: list[SimpleItem] = []
|
||||||
|
|
||||||
def get_endpoint(self, path: str) -> Optional[EndpointAccess]:
|
def get_endpoint(self, path: str) -> EndpointAccess | None:
|
||||||
for e in self.endpoints:
|
for e in self.endpoints:
|
||||||
if e.path == path:
|
if e.path == path:
|
||||||
return e
|
return e
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_token_by_id(self, token_id: str) -> Optional[SimpleItem]:
|
def get_token_by_id(self, token_id: str) -> SimpleItem | None:
|
||||||
for t in self.token_id_list:
|
for t in self.token_id_list:
|
||||||
if t.id == token_id:
|
if t.id == token_id:
|
||||||
return t
|
return t
|
||||||
@ -71,7 +70,7 @@ class UserAcls(BaseModel):
|
|||||||
access_control_list: list[AccessControlList] = []
|
access_control_list: list[AccessControlList] = []
|
||||||
updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||||
|
|
||||||
def get_acl_by_id(self, acl_id: str) -> Optional[AccessControlList]:
|
def get_acl_by_id(self, acl_id: str) -> AccessControlList | None:
|
||||||
for acl in self.access_control_list:
|
for acl in self.access_control_list:
|
||||||
if acl.id == acl_id:
|
if acl.id == acl_id:
|
||||||
return acl
|
return acl
|
||||||
@ -82,7 +81,7 @@ class UserAcls(BaseModel):
|
|||||||
acl for acl in self.access_control_list if acl.id != acl_id
|
acl for acl in self.access_control_list if acl.id != acl_id
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_acl_by_token_id(self, token_id: str) -> Optional[AccessControlList]:
|
def get_acl_by_token_id(self, token_id: str) -> AccessControlList | None:
|
||||||
for acl in self.access_control_list:
|
for acl in self.access_control_list:
|
||||||
if acl.get_token_by_id(token_id):
|
if acl.get_token_by_id(token_id):
|
||||||
return acl
|
return acl
|
||||||
@ -91,10 +90,10 @@ class UserAcls(BaseModel):
|
|||||||
|
|
||||||
class Account(BaseModel):
|
class Account(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
username: Optional[str] = None
|
username: str | None = None
|
||||||
password_hash: Optional[str] = None
|
password_hash: str | None = None
|
||||||
pubkey: Optional[str] = None
|
pubkey: str | None = None
|
||||||
email: Optional[str] = None
|
email: str | None = None
|
||||||
extra: UserExtra = UserExtra()
|
extra: UserExtra = UserExtra()
|
||||||
|
|
||||||
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||||
@ -134,10 +133,10 @@ class Account(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class AccountOverview(Account):
|
class AccountOverview(Account):
|
||||||
transaction_count: Optional[int] = 0
|
transaction_count: int | None = 0
|
||||||
wallet_count: Optional[int] = 0
|
wallet_count: int | None = 0
|
||||||
balance_msat: Optional[int] = 0
|
balance_msat: int | None = 0
|
||||||
last_payment: Optional[datetime] = None
|
last_payment: datetime | None = None
|
||||||
|
|
||||||
|
|
||||||
class AccountFilters(FilterModel):
|
class AccountFilters(FilterModel):
|
||||||
@ -151,20 +150,20 @@ class AccountFilters(FilterModel):
|
|||||||
"last_payment",
|
"last_payment",
|
||||||
]
|
]
|
||||||
|
|
||||||
email: Optional[str] = None
|
email: str | None = None
|
||||||
user: Optional[str] = None
|
user: str | None = None
|
||||||
username: Optional[str] = None
|
username: str | None = None
|
||||||
pubkey: Optional[str] = None
|
pubkey: str | None = None
|
||||||
wallet_id: Optional[str] = None
|
wallet_id: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class User(BaseModel):
|
class User(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
updated_at: datetime
|
updated_at: datetime
|
||||||
email: Optional[str] = None
|
email: str | None = None
|
||||||
username: Optional[str] = None
|
username: str | None = None
|
||||||
pubkey: Optional[str] = None
|
pubkey: str | None = None
|
||||||
extensions: list[str] = []
|
extensions: list[str] = []
|
||||||
wallets: list[Wallet] = []
|
wallets: list[Wallet] = []
|
||||||
admin: bool = False
|
admin: bool = False
|
||||||
@ -176,7 +175,7 @@ class User(BaseModel):
|
|||||||
def wallet_ids(self) -> list[str]:
|
def wallet_ids(self) -> list[str]:
|
||||||
return [wallet.id for wallet in self.wallets]
|
return [wallet.id for wallet in self.wallets]
|
||||||
|
|
||||||
def get_wallet(self, wallet_id: str) -> Optional[Wallet]:
|
def get_wallet(self, wallet_id: str) -> Wallet | None:
|
||||||
w = [wallet for wallet in self.wallets if wallet.id == wallet_id]
|
w = [wallet for wallet in self.wallets if wallet.id == wallet_id]
|
||||||
return w[0] if w else None
|
return w[0] if w else None
|
||||||
|
|
||||||
@ -192,33 +191,33 @@ class User(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class RegisterUser(BaseModel):
|
class RegisterUser(BaseModel):
|
||||||
email: Optional[str] = Query(default=None)
|
email: str | None = Query(default=None)
|
||||||
username: str = Query(default=..., min_length=2, max_length=20)
|
username: str = Query(default=..., min_length=2, max_length=20)
|
||||||
password: str = Query(default=..., min_length=8, max_length=50)
|
password: str = Query(default=..., min_length=8, max_length=50)
|
||||||
password_repeat: str = Query(default=..., min_length=8, max_length=50)
|
password_repeat: str = Query(default=..., min_length=8, max_length=50)
|
||||||
|
|
||||||
|
|
||||||
class CreateUser(BaseModel):
|
class CreateUser(BaseModel):
|
||||||
id: Optional[str] = Query(default=None)
|
id: str | None = Query(default=None)
|
||||||
email: Optional[str] = Query(default=None)
|
email: str | None = Query(default=None)
|
||||||
username: Optional[str] = Query(default=None, min_length=2, max_length=20)
|
username: str | None = Query(default=None, min_length=2, max_length=20)
|
||||||
password: Optional[str] = Query(default=None, min_length=8, max_length=50)
|
password: str | None = Query(default=None, min_length=8, max_length=50)
|
||||||
password_repeat: Optional[str] = Query(default=None, min_length=8, max_length=50)
|
password_repeat: str | None = Query(default=None, min_length=8, max_length=50)
|
||||||
pubkey: str = Query(default=None, max_length=64)
|
pubkey: str = Query(default=None, max_length=64)
|
||||||
extensions: Optional[list[str]] = None
|
extensions: list[str] | None = None
|
||||||
extra: Optional[UserExtra] = None
|
extra: UserExtra | None = None
|
||||||
|
|
||||||
|
|
||||||
class UpdateUser(BaseModel):
|
class UpdateUser(BaseModel):
|
||||||
user_id: str
|
user_id: str
|
||||||
email: Optional[str] = Query(default=None)
|
email: str | None = Query(default=None)
|
||||||
username: Optional[str] = Query(default=..., min_length=2, max_length=20)
|
username: str | None = Query(default=..., min_length=2, max_length=20)
|
||||||
extra: Optional[UserExtra] = None
|
extra: UserExtra | None = None
|
||||||
|
|
||||||
|
|
||||||
class UpdateUserPassword(BaseModel):
|
class UpdateUserPassword(BaseModel):
|
||||||
user_id: str
|
user_id: str
|
||||||
password_old: Optional[str] = None
|
password_old: str | None = None
|
||||||
password: str = Query(default=..., min_length=8, max_length=50)
|
password: str = Query(default=..., min_length=8, max_length=50)
|
||||||
password_repeat: str = Query(default=..., min_length=8, max_length=50)
|
password_repeat: str = Query(default=..., min_length=8, max_length=50)
|
||||||
username: str = Query(default=..., min_length=2, max_length=20)
|
username: str = Query(default=..., min_length=2, max_length=20)
|
||||||
@ -252,10 +251,10 @@ class LoginUsernamePassword(BaseModel):
|
|||||||
|
|
||||||
class AccessTokenPayload(BaseModel):
|
class AccessTokenPayload(BaseModel):
|
||||||
sub: str
|
sub: str
|
||||||
usr: Optional[str] = None
|
usr: str | None = None
|
||||||
email: Optional[str] = None
|
email: str | None = None
|
||||||
auth_time: Optional[int] = 0
|
auth_time: int | None = 0
|
||||||
api_token_id: Optional[str] = None
|
api_token_id: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class UpdateBalance(BaseModel):
|
class UpdateBalance(BaseModel):
|
||||||
|
@ -5,7 +5,6 @@ import hmac
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from ecdsa import SECP256k1, SigningKey
|
from ecdsa import SECP256k1, SigningKey
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
@ -37,7 +36,7 @@ class Wallet(BaseModel):
|
|||||||
deleted: bool = False
|
deleted: bool = False
|
||||||
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||||
updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||||
currency: Optional[str] = None
|
currency: str | None = None
|
||||||
balance_msat: int = Field(default=0, no_database=True)
|
balance_msat: int = Field(default=0, no_database=True)
|
||||||
extra: WalletExtra = WalletExtra()
|
extra: WalletExtra = WalletExtra()
|
||||||
|
|
||||||
@ -67,7 +66,7 @@ class Wallet(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class CreateWallet(BaseModel):
|
class CreateWallet(BaseModel):
|
||||||
name: Optional[str] = None
|
name: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class KeyType(Enum):
|
class KeyType(Enum):
|
||||||
|
70
lnbits/db.py
70
lnbits/db.py
@ -8,7 +8,7 @@ import time
|
|||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any, Generic, Literal, Optional, TypeVar, Union, get_origin
|
from typing import Any, Generic, Literal, TypeVar, get_origin
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from pydantic import BaseModel, ValidationError, root_validator
|
from pydantic import BaseModel, ValidationError, root_validator
|
||||||
@ -65,8 +65,8 @@ def get_placeholder(model: Any, field: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
class Compat:
|
class Compat:
|
||||||
type: Optional[str] = "<inherited>"
|
type: str | None = "<inherited>"
|
||||||
schema: Optional[str] = "<inherited>"
|
schema: str | None = "<inherited>"
|
||||||
|
|
||||||
def interval_seconds(self, seconds: int) -> str:
|
def interval_seconds(self, seconds: int) -> str:
|
||||||
if self.type in {POSTGRES, COCKROACH}:
|
if self.type in {POSTGRES, COCKROACH}:
|
||||||
@ -167,8 +167,8 @@ class Connection(Compat):
|
|||||||
async def fetchall(
|
async def fetchall(
|
||||||
self,
|
self,
|
||||||
query: str,
|
query: str,
|
||||||
values: Optional[dict] = None,
|
values: dict | None = None,
|
||||||
model: Optional[type[TModel]] = None,
|
model: type[TModel] | None = None,
|
||||||
) -> list[TModel]:
|
) -> list[TModel]:
|
||||||
params = self.rewrite_values(values) if values else {}
|
params = self.rewrite_values(values) if values else {}
|
||||||
result = await self.conn.execute(text(self.rewrite_query(query)), params)
|
result = await self.conn.execute(text(self.rewrite_query(query)), params)
|
||||||
@ -183,8 +183,8 @@ class Connection(Compat):
|
|||||||
async def fetchone(
|
async def fetchone(
|
||||||
self,
|
self,
|
||||||
query: str,
|
query: str,
|
||||||
values: Optional[dict] = None,
|
values: dict | None = None,
|
||||||
model: Optional[type[TModel]] = None,
|
model: type[TModel] | None = None,
|
||||||
) -> TModel:
|
) -> TModel:
|
||||||
params = self.rewrite_values(values) if values else {}
|
params = self.rewrite_values(values) if values else {}
|
||||||
result = await self.conn.execute(text(self.rewrite_query(query)), params)
|
result = await self.conn.execute(text(self.rewrite_query(query)), params)
|
||||||
@ -211,11 +211,11 @@ class Connection(Compat):
|
|||||||
async def fetch_page(
|
async def fetch_page(
|
||||||
self,
|
self,
|
||||||
query: str,
|
query: str,
|
||||||
where: Optional[list[str]] = None,
|
where: list[str] | None = None,
|
||||||
values: Optional[dict] = None,
|
values: dict | None = None,
|
||||||
filters: Optional[Filters] = None,
|
filters: Filters | None = None,
|
||||||
model: Optional[type[TModel]] = None,
|
model: type[TModel] | None = None,
|
||||||
group_by: Optional[list[str]] = None,
|
group_by: list[str] | None = None,
|
||||||
) -> Page[TModel]:
|
) -> Page[TModel]:
|
||||||
if not filters:
|
if not filters:
|
||||||
filters = Filters()
|
filters = Filters()
|
||||||
@ -268,7 +268,7 @@ class Connection(Compat):
|
|||||||
total=count,
|
total=count,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def execute(self, query: str, values: Optional[dict] = None):
|
async def execute(self, query: str, values: dict | None = None):
|
||||||
params = self.rewrite_values(values) if values else {}
|
params = self.rewrite_values(values) if values else {}
|
||||||
result = 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()
|
await self.conn.commit()
|
||||||
@ -350,8 +350,8 @@ class Database(Compat):
|
|||||||
async def fetchall(
|
async def fetchall(
|
||||||
self,
|
self,
|
||||||
query: str,
|
query: str,
|
||||||
values: Optional[dict] = None,
|
values: dict | None = None,
|
||||||
model: Optional[type[TModel]] = None,
|
model: type[TModel] | None = None,
|
||||||
) -> list[TModel]:
|
) -> list[TModel]:
|
||||||
async with self.connect() as conn:
|
async with self.connect() as conn:
|
||||||
return await conn.fetchall(query, values, model)
|
return await conn.fetchall(query, values, model)
|
||||||
@ -359,8 +359,8 @@ class Database(Compat):
|
|||||||
async def fetchone(
|
async def fetchone(
|
||||||
self,
|
self,
|
||||||
query: str,
|
query: str,
|
||||||
values: Optional[dict] = None,
|
values: dict | None = None,
|
||||||
model: Optional[type[TModel]] = None,
|
model: type[TModel] | None = None,
|
||||||
) -> TModel:
|
) -> TModel:
|
||||||
async with self.connect() as conn:
|
async with self.connect() as conn:
|
||||||
return await conn.fetchone(query, values, model)
|
return await conn.fetchone(query, values, model)
|
||||||
@ -378,16 +378,16 @@ class Database(Compat):
|
|||||||
async def fetch_page(
|
async def fetch_page(
|
||||||
self,
|
self,
|
||||||
query: str,
|
query: str,
|
||||||
where: Optional[list[str]] = None,
|
where: list[str] | None = None,
|
||||||
values: Optional[dict] = None,
|
values: dict | None = None,
|
||||||
filters: Optional[Filters] = None,
|
filters: Filters | None = None,
|
||||||
model: Optional[type[TModel]] = None,
|
model: type[TModel] | None = None,
|
||||||
group_by: Optional[list[str]] = None,
|
group_by: list[str] | None = None,
|
||||||
) -> Page[TModel]:
|
) -> Page[TModel]:
|
||||||
async with self.connect() as conn:
|
async with self.connect() as conn:
|
||||||
return await conn.fetch_page(query, where, values, filters, model, group_by)
|
return await conn.fetch_page(query, where, values, filters, model, group_by)
|
||||||
|
|
||||||
async def execute(self, query: str, values: Optional[dict] = None):
|
async def execute(self, query: str, values: dict | None = None):
|
||||||
async with self.connect() as conn:
|
async with self.connect() as conn:
|
||||||
return await conn.execute(query, values)
|
return await conn.execute(query, values)
|
||||||
|
|
||||||
@ -445,7 +445,7 @@ class Operator(Enum):
|
|||||||
|
|
||||||
class FilterModel(BaseModel):
|
class FilterModel(BaseModel):
|
||||||
__search_fields__: list[str] = []
|
__search_fields__: list[str] = []
|
||||||
__sort_fields__: Optional[list[str]] = None
|
__sort_fields__: list[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
@ -461,8 +461,8 @@ class Page(BaseModel, Generic[T]):
|
|||||||
class Filter(BaseModel, Generic[TFilterModel]):
|
class Filter(BaseModel, Generic[TFilterModel]):
|
||||||
field: str
|
field: str
|
||||||
op: Operator = Operator.EQ
|
op: Operator = Operator.EQ
|
||||||
model: Optional[type[TFilterModel]]
|
model: type[TFilterModel] | None
|
||||||
values: Optional[dict] = None
|
values: dict | None = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse_query(
|
def parse_query(
|
||||||
@ -517,15 +517,15 @@ class Filters(BaseModel, Generic[TFilterModel]):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
filters: list[Filter[TFilterModel]] = []
|
filters: list[Filter[TFilterModel]] = []
|
||||||
search: Optional[str] = None
|
search: str | None = None
|
||||||
|
|
||||||
offset: Optional[int] = None
|
offset: int | None = None
|
||||||
limit: Optional[int] = None
|
limit: int | None = None
|
||||||
|
|
||||||
sortby: Optional[str] = None
|
sortby: str | None = None
|
||||||
direction: Optional[Literal["asc", "desc"]] = None
|
direction: Literal["asc", "desc"] | None = None
|
||||||
|
|
||||||
model: Optional[type[TFilterModel]] = None
|
model: type[TFilterModel] | None = None
|
||||||
|
|
||||||
@root_validator(pre=True)
|
@root_validator(pre=True)
|
||||||
def validate_sortby(cls, values):
|
def validate_sortby(cls, values):
|
||||||
@ -547,7 +547,7 @@ class Filters(BaseModel, Generic[TFilterModel]):
|
|||||||
stmt += f"OFFSET {self.offset}"
|
stmt += f"OFFSET {self.offset}"
|
||||||
return stmt
|
return stmt
|
||||||
|
|
||||||
def where(self, where_stmts: Optional[list[str]] = None) -> str:
|
def where(self, where_stmts: list[str] | None = None) -> str:
|
||||||
if not where_stmts:
|
if not where_stmts:
|
||||||
where_stmts = []
|
where_stmts = []
|
||||||
if self.filters:
|
if self.filters:
|
||||||
@ -567,7 +567,7 @@ class Filters(BaseModel, Generic[TFilterModel]):
|
|||||||
return f"ORDER BY {self.sortby} {self.direction or 'asc'}"
|
return f"ORDER BY {self.sortby} {self.direction or 'asc'}"
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def values(self, values: Optional[dict] = None) -> dict:
|
def values(self, values: dict | None = None) -> dict:
|
||||||
if not values:
|
if not values:
|
||||||
values = {}
|
values = {}
|
||||||
if self.filters:
|
if self.filters:
|
||||||
@ -641,7 +641,7 @@ def model_to_dict(model: BaseModel) -> dict:
|
|||||||
return _dict
|
return _dict
|
||||||
|
|
||||||
|
|
||||||
def dict_to_submodel(model: type[TModel], value: Union[dict, str]) -> Optional[TModel]:
|
def dict_to_submodel(model: type[TModel], value: dict | str) -> TModel | None:
|
||||||
"""convert a dictionary or JSON string to a Pydantic model"""
|
"""convert a dictionary or JSON string to a Pydantic model"""
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
if value == "null":
|
if value == "null":
|
||||||
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
@ -15,10 +15,10 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
class NodePeerInfo(BaseModel):
|
class NodePeerInfo(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
alias: Optional[str] = None
|
alias: str | None = None
|
||||||
color: Optional[str] = None
|
color: str | None = None
|
||||||
last_timestamp: Optional[int] = None
|
last_timestamp: int | None = None
|
||||||
addresses: Optional[list[str]] = None
|
addresses: list[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
class ChannelState(Enum):
|
class ChannelState(Enum):
|
||||||
@ -47,20 +47,20 @@ class NodeChannel(BaseModel):
|
|||||||
balance: ChannelBalance
|
balance: ChannelBalance
|
||||||
state: ChannelState
|
state: ChannelState
|
||||||
# could be optional for closing/pending channels on lndrest
|
# could be optional for closing/pending channels on lndrest
|
||||||
id: Optional[str] = None
|
id: str | None = None
|
||||||
short_id: Optional[str] = None
|
short_id: str | None = None
|
||||||
point: Optional[ChannelPoint] = None
|
point: ChannelPoint | None = None
|
||||||
name: Optional[str] = None
|
name: str | None = None
|
||||||
color: Optional[str] = None
|
color: str | None = None
|
||||||
fee_ppm: Optional[int] = None
|
fee_ppm: int | None = None
|
||||||
fee_base_msat: Optional[int] = None
|
fee_base_msat: int | None = None
|
||||||
|
|
||||||
|
|
||||||
class ChannelStats(BaseModel):
|
class ChannelStats(BaseModel):
|
||||||
counts: dict[ChannelState, int]
|
counts: dict[ChannelState, int]
|
||||||
avg_size: int
|
avg_size: int
|
||||||
biggest_size: Optional[int]
|
biggest_size: int | None
|
||||||
smallest_size: Optional[int]
|
smallest_size: int | None
|
||||||
total_capacity: int
|
total_capacity: int
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -95,9 +95,9 @@ class ChannelStats(BaseModel):
|
|||||||
|
|
||||||
class NodeFees(BaseModel):
|
class NodeFees(BaseModel):
|
||||||
total_msat: int
|
total_msat: int
|
||||||
daily_msat: Optional[int] = None
|
daily_msat: int | None = None
|
||||||
weekly_msat: Optional[int] = None
|
weekly_msat: int | None = None
|
||||||
monthly_msat: Optional[int] = None
|
monthly_msat: int | None = None
|
||||||
|
|
||||||
|
|
||||||
class PublicNodeInfo(BaseModel):
|
class PublicNodeInfo(BaseModel):
|
||||||
@ -121,25 +121,25 @@ class NodeInfoResponse(PublicNodeInfo):
|
|||||||
class NodePayment(BaseModel):
|
class NodePayment(BaseModel):
|
||||||
pending: bool
|
pending: bool
|
||||||
amount: int
|
amount: int
|
||||||
fee: Optional[int] = None
|
fee: int | None = None
|
||||||
memo: Optional[str] = None
|
memo: str | None = None
|
||||||
time: int
|
time: int
|
||||||
bolt11: Optional[str] = None
|
bolt11: str | None = None
|
||||||
preimage: Optional[str]
|
preimage: str | None
|
||||||
payment_hash: str
|
payment_hash: str
|
||||||
expiry: Optional[float] = None
|
expiry: float | None = None
|
||||||
destination: Optional[NodePeerInfo] = None
|
destination: NodePeerInfo | None = None
|
||||||
|
|
||||||
|
|
||||||
class NodeInvoice(BaseModel):
|
class NodeInvoice(BaseModel):
|
||||||
pending: bool
|
pending: bool
|
||||||
amount: int
|
amount: int
|
||||||
memo: Optional[str]
|
memo: str | None
|
||||||
bolt11: str
|
bolt11: str
|
||||||
preimage: Optional[str]
|
preimage: str | None
|
||||||
payment_hash: str
|
payment_hash: str
|
||||||
paid_at: Optional[int] = None
|
paid_at: int | None = None
|
||||||
expiry: Optional[int] = None
|
expiry: int | None = None
|
||||||
|
|
||||||
|
|
||||||
class NodeInvoiceFilters(FilterModel):
|
class NodeInvoiceFilters(FilterModel):
|
||||||
@ -154,7 +154,7 @@ class Node(ABC):
|
|||||||
|
|
||||||
def __init__(self, wallet: Wallet):
|
def __init__(self, wallet: Wallet):
|
||||||
self.wallet = wallet
|
self.wallet = wallet
|
||||||
self.id: Optional[str] = None
|
self.id: str | None = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@ -203,22 +203,22 @@ class Node(ABC):
|
|||||||
self,
|
self,
|
||||||
peer_id: str,
|
peer_id: str,
|
||||||
local_amount: int,
|
local_amount: int,
|
||||||
push_amount: Optional[int] = None,
|
push_amount: int | None = None,
|
||||||
fee_rate: Optional[int] = None,
|
fee_rate: int | None = None,
|
||||||
) -> ChannelPoint:
|
) -> ChannelPoint:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def close_channel(
|
async def close_channel(
|
||||||
self,
|
self,
|
||||||
short_id: Optional[str] = None,
|
short_id: str | None = None,
|
||||||
point: Optional[ChannelPoint] = None,
|
point: ChannelPoint | None = None,
|
||||||
force: bool = False,
|
force: bool = False,
|
||||||
):
|
):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def get_channel(self, channel_id: str) -> Optional[NodeChannel]:
|
async def get_channel(self, channel_id: str) -> NodeChannel | None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
|
|
||||||
@ -119,8 +119,8 @@ class CoreLightningNode(Node):
|
|||||||
self,
|
self,
|
||||||
peer_id: str,
|
peer_id: str,
|
||||||
local_amount: int,
|
local_amount: int,
|
||||||
push_amount: Optional[int] = None,
|
push_amount: int | None = None,
|
||||||
fee_rate: Optional[int] = None,
|
fee_rate: int | None = None,
|
||||||
) -> ChannelPoint:
|
) -> ChannelPoint:
|
||||||
try:
|
try:
|
||||||
result = await self.ln_rpc(
|
result = await self.ln_rpc(
|
||||||
@ -173,8 +173,8 @@ class CoreLightningNode(Node):
|
|||||||
@catch_rpc_errors
|
@catch_rpc_errors
|
||||||
async def close_channel(
|
async def close_channel(
|
||||||
self,
|
self,
|
||||||
short_id: Optional[str] = None,
|
short_id: str | None = None,
|
||||||
point: Optional[ChannelPoint] = None,
|
point: ChannelPoint | None = None,
|
||||||
force: bool = False,
|
force: bool = False,
|
||||||
):
|
):
|
||||||
if not short_id:
|
if not short_id:
|
||||||
@ -229,7 +229,7 @@ class CoreLightningNode(Node):
|
|||||||
await self.ln_rpc("setchannel", channel_id, feebase=base_msat, feeppm=ppm)
|
await self.ln_rpc("setchannel", channel_id, feebase=base_msat, feeppm=ppm)
|
||||||
|
|
||||||
@catch_rpc_errors
|
@catch_rpc_errors
|
||||||
async def get_channel(self, channel_id: str) -> Optional[NodeChannel]:
|
async def get_channel(self, channel_id: str) -> NodeChannel | None:
|
||||||
channels = await self.get_channels()
|
channels = await self.get_channels()
|
||||||
for channel in channels:
|
for channel in channels:
|
||||||
if channel.id == channel_id:
|
if channel.id == channel_id:
|
||||||
|
@ -4,7 +4,7 @@ import asyncio
|
|||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from httpx import HTTPStatusError
|
from httpx import HTTPStatusError
|
||||||
@ -60,9 +60,7 @@ def _parse_channel_point(raw: str) -> ChannelPoint:
|
|||||||
class LndRestNode(Node):
|
class LndRestNode(Node):
|
||||||
wallet: LndRestWallet
|
wallet: LndRestWallet
|
||||||
|
|
||||||
async def request(
|
async def request(self, method: str, path: str, json: dict | None = None, **kwargs):
|
||||||
self, method: str, path: str, json: Optional[dict] = None, **kwargs
|
|
||||||
):
|
|
||||||
response = await self.wallet.client.request(
|
response = await self.wallet.client.request(
|
||||||
method, f"{self.wallet.endpoint}{path}", json=json, **kwargs
|
method, f"{self.wallet.endpoint}{path}", json=json, **kwargs
|
||||||
)
|
)
|
||||||
@ -131,8 +129,8 @@ class LndRestNode(Node):
|
|||||||
self,
|
self,
|
||||||
peer_id: str,
|
peer_id: str,
|
||||||
local_amount: int,
|
local_amount: int,
|
||||||
push_amount: Optional[int] = None,
|
push_amount: int | None = None,
|
||||||
fee_rate: Optional[int] = None,
|
fee_rate: int | None = None,
|
||||||
) -> ChannelPoint:
|
) -> ChannelPoint:
|
||||||
response = await self.request(
|
response = await self.request(
|
||||||
"POST",
|
"POST",
|
||||||
@ -176,8 +174,8 @@ class LndRestNode(Node):
|
|||||||
|
|
||||||
async def close_channel(
|
async def close_channel(
|
||||||
self,
|
self,
|
||||||
short_id: Optional[str] = None,
|
short_id: str | None = None,
|
||||||
point: Optional[ChannelPoint] = None,
|
point: ChannelPoint | None = None,
|
||||||
force: bool = False,
|
force: bool = False,
|
||||||
):
|
):
|
||||||
if short_id:
|
if short_id:
|
||||||
@ -218,7 +216,7 @@ class LndRestNode(Node):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
async def get_channel(self, channel_id: str) -> Optional[NodeChannel]:
|
async def get_channel(self, channel_id: str) -> NodeChannel | None:
|
||||||
channel_info = await self.get(f"/v1/graph/edge/{channel_id}")
|
channel_info = await self.get(f"/v1/graph/edge/{channel_id}")
|
||||||
info = await self.get("/v1/getinfo")
|
info = await self.get("/v1/getinfo")
|
||||||
if info["identity_pubkey"] == channel_info["node1_pub"]:
|
if info["identity_pubkey"] == channel_info["node1_pub"]:
|
||||||
|
@ -11,7 +11,7 @@ from hashlib import sha256
|
|||||||
from os import path
|
from os import path
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from time import gmtime, strftime, time
|
from time import gmtime, strftime, time
|
||||||
from typing import Any, Optional
|
from typing import Any
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
@ -77,7 +77,7 @@ class RedirectPath(BaseModel):
|
|||||||
other.from_path, list(other.header_filters.items())
|
other.from_path, list(other.header_filters.items())
|
||||||
) or other.redirect_matches(self.from_path, list(self.header_filters.items()))
|
) or other.redirect_matches(self.from_path, list(self.header_filters.items()))
|
||||||
|
|
||||||
def find_in_conflict(self, others: list[RedirectPath]) -> Optional[RedirectPath]:
|
def find_in_conflict(self, others: list[RedirectPath]) -> RedirectPath | None:
|
||||||
for other in others:
|
for other in others:
|
||||||
if self.in_conflict(other):
|
if self.in_conflict(other):
|
||||||
return other
|
return other
|
||||||
@ -153,7 +153,7 @@ class InstalledExtensionsSettings(LNbitsSettings):
|
|||||||
|
|
||||||
def find_extension_redirect(
|
def find_extension_redirect(
|
||||||
self, path: str, req_headers: list[tuple[bytes, bytes]]
|
self, path: str, req_headers: list[tuple[bytes, bytes]]
|
||||||
) -> Optional[RedirectPath]:
|
) -> RedirectPath | None:
|
||||||
headers = [(k.decode(), v.decode()) for k, v in req_headers]
|
headers = [(k.decode(), v.decode()) for k, v in req_headers]
|
||||||
return next(
|
return next(
|
||||||
(
|
(
|
||||||
@ -167,8 +167,8 @@ class InstalledExtensionsSettings(LNbitsSettings):
|
|||||||
def activate_extension_paths(
|
def activate_extension_paths(
|
||||||
self,
|
self,
|
||||||
ext_id: str,
|
ext_id: str,
|
||||||
upgrade_hash: Optional[str] = None,
|
upgrade_hash: str | None = None,
|
||||||
ext_redirects: Optional[list[dict]] = None,
|
ext_redirects: list[dict] | None = None,
|
||||||
):
|
):
|
||||||
self.lnbits_deactivated_extensions.discard(ext_id)
|
self.lnbits_deactivated_extensions.discard(ext_id)
|
||||||
|
|
||||||
@ -231,12 +231,12 @@ class ExchangeHistorySettings(LNbitsSettings):
|
|||||||
class ThemesSettings(LNbitsSettings):
|
class ThemesSettings(LNbitsSettings):
|
||||||
lnbits_site_title: str = Field(default="LNbits")
|
lnbits_site_title: str = Field(default="LNbits")
|
||||||
lnbits_site_tagline: str = Field(default="free and open-source lightning wallet")
|
lnbits_site_tagline: str = Field(default="free and open-source lightning wallet")
|
||||||
lnbits_site_description: Optional[str] = Field(
|
lnbits_site_description: str | None = Field(
|
||||||
default="The world's most powerful suite of bitcoin tools."
|
default="The world's most powerful suite of bitcoin tools."
|
||||||
)
|
)
|
||||||
lnbits_show_home_page_elements: bool = Field(default=True)
|
lnbits_show_home_page_elements: bool = Field(default=True)
|
||||||
lnbits_default_wallet_name: str = Field(default="LNbits wallet")
|
lnbits_default_wallet_name: str = Field(default="LNbits wallet")
|
||||||
lnbits_custom_badge: Optional[str] = Field(default=None)
|
lnbits_custom_badge: str | None = Field(default=None)
|
||||||
lnbits_custom_badge_color: str = Field(default="warning")
|
lnbits_custom_badge_color: str = Field(default="warning")
|
||||||
lnbits_theme_options: list[str] = Field(
|
lnbits_theme_options: list[str] = Field(
|
||||||
default=[
|
default=[
|
||||||
@ -251,17 +251,15 @@ class ThemesSettings(LNbitsSettings):
|
|||||||
"bitcoin",
|
"bitcoin",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
lnbits_custom_logo: Optional[str] = Field(default=None)
|
lnbits_custom_logo: str | None = Field(default=None)
|
||||||
lnbits_custom_image: Optional[str] = Field(
|
lnbits_custom_image: str | None = Field(default="/static/images/logos/lnbits.svg")
|
||||||
default="/static/images/logos/lnbits.svg"
|
|
||||||
)
|
|
||||||
lnbits_ad_space_title: str = Field(default="Supported by")
|
lnbits_ad_space_title: str = Field(default="Supported by")
|
||||||
lnbits_ad_space: str = Field(
|
lnbits_ad_space: str = Field(
|
||||||
default="https://shop.lnbits.com/;/static/images/bitcoin-shop-banner.png;/static/images/bitcoin-shop-banner.png,https://affil.trezor.io/aff_c?offer_id=169&aff_id=33845;/static/images/bitcoin-hardware-wallet.png;/static/images/bitcoin-hardware-wallet.png,https://opensats.org/;/static/images/open-sats.png;/static/images/open-sats.png"
|
default="https://shop.lnbits.com/;/static/images/bitcoin-shop-banner.png;/static/images/bitcoin-shop-banner.png,https://affil.trezor.io/aff_c?offer_id=169&aff_id=33845;/static/images/bitcoin-hardware-wallet.png;/static/images/bitcoin-hardware-wallet.png,https://opensats.org/;/static/images/open-sats.png;/static/images/open-sats.png"
|
||||||
) # sneaky sneaky
|
) # sneaky sneaky
|
||||||
lnbits_ad_space_enabled: bool = Field(default=False)
|
lnbits_ad_space_enabled: bool = Field(default=False)
|
||||||
lnbits_allowed_currencies: list[str] = Field(default=[])
|
lnbits_allowed_currencies: list[str] = Field(default=[])
|
||||||
lnbits_default_accounting_currency: Optional[str] = Field(default=None)
|
lnbits_default_accounting_currency: str | None = Field(default=None)
|
||||||
lnbits_qr_logo: str = Field(default="/static/images/logos/lnbits.png")
|
lnbits_qr_logo: str = Field(default="/static/images/logos/lnbits.png")
|
||||||
lnbits_default_reaction: str = Field(default="confettiBothSides")
|
lnbits_default_reaction: str = Field(default="confettiBothSides")
|
||||||
lnbits_default_theme: str = Field(default="salvador")
|
lnbits_default_theme: str = Field(default="salvador")
|
||||||
@ -282,7 +280,7 @@ class FeeSettings(LNbitsSettings):
|
|||||||
lnbits_service_fee: float = Field(default=0)
|
lnbits_service_fee: float = Field(default=0)
|
||||||
lnbits_service_fee_ignore_internal: bool = Field(default=True)
|
lnbits_service_fee_ignore_internal: bool = Field(default=True)
|
||||||
lnbits_service_fee_max: int = Field(default=0)
|
lnbits_service_fee_max: int = Field(default=0)
|
||||||
lnbits_service_fee_wallet: Optional[str] = Field(default=None)
|
lnbits_service_fee_wallet: str | None = Field(default=None)
|
||||||
|
|
||||||
# WARN: this same value must be used for balance check and passed to
|
# WARN: this same value must be used for balance check and passed to
|
||||||
# funding_source.pay_invoice(), it may cause a vulnerability if the values differ
|
# funding_source.pay_invoice(), it may cause a vulnerability if the values differ
|
||||||
@ -413,121 +411,121 @@ class FakeWalletFundingSource(LNbitsSettings):
|
|||||||
|
|
||||||
class LNbitsFundingSource(LNbitsSettings):
|
class LNbitsFundingSource(LNbitsSettings):
|
||||||
lnbits_endpoint: str = Field(default="https://demo.lnbits.com")
|
lnbits_endpoint: str = Field(default="https://demo.lnbits.com")
|
||||||
lnbits_key: Optional[str] = Field(default=None)
|
lnbits_key: str | None = Field(default=None)
|
||||||
lnbits_admin_key: Optional[str] = Field(default=None)
|
lnbits_admin_key: str | None = Field(default=None)
|
||||||
lnbits_invoice_key: Optional[str] = Field(default=None)
|
lnbits_invoice_key: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class ClicheFundingSource(LNbitsSettings):
|
class ClicheFundingSource(LNbitsSettings):
|
||||||
cliche_endpoint: Optional[str] = Field(default=None)
|
cliche_endpoint: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class CoreLightningFundingSource(LNbitsSettings):
|
class CoreLightningFundingSource(LNbitsSettings):
|
||||||
corelightning_rpc: Optional[str] = Field(default=None)
|
corelightning_rpc: str | None = Field(default=None)
|
||||||
corelightning_pay_command: str = Field(default="pay")
|
corelightning_pay_command: str = Field(default="pay")
|
||||||
clightning_rpc: Optional[str] = Field(default=None)
|
clightning_rpc: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class CoreLightningRestFundingSource(LNbitsSettings):
|
class CoreLightningRestFundingSource(LNbitsSettings):
|
||||||
corelightning_rest_url: Optional[str] = Field(default=None)
|
corelightning_rest_url: str | None = Field(default=None)
|
||||||
corelightning_rest_macaroon: Optional[str] = Field(default=None)
|
corelightning_rest_macaroon: str | None = Field(default=None)
|
||||||
corelightning_rest_cert: Optional[str] = Field(default=None)
|
corelightning_rest_cert: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class EclairFundingSource(LNbitsSettings):
|
class EclairFundingSource(LNbitsSettings):
|
||||||
eclair_url: Optional[str] = Field(default=None)
|
eclair_url: str | None = Field(default=None)
|
||||||
eclair_pass: Optional[str] = Field(default=None)
|
eclair_pass: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class LndRestFundingSource(LNbitsSettings):
|
class LndRestFundingSource(LNbitsSettings):
|
||||||
lnd_rest_endpoint: Optional[str] = Field(default=None)
|
lnd_rest_endpoint: str | None = Field(default=None)
|
||||||
lnd_rest_cert: Optional[str] = Field(default=None)
|
lnd_rest_cert: str | None = Field(default=None)
|
||||||
lnd_rest_macaroon: Optional[str] = Field(default=None)
|
lnd_rest_macaroon: str | None = Field(default=None)
|
||||||
lnd_rest_macaroon_encrypted: Optional[str] = Field(default=None)
|
lnd_rest_macaroon_encrypted: str | None = Field(default=None)
|
||||||
lnd_rest_route_hints: bool = Field(default=True)
|
lnd_rest_route_hints: bool = Field(default=True)
|
||||||
lnd_rest_allow_self_payment: bool = Field(default=False)
|
lnd_rest_allow_self_payment: bool = Field(default=False)
|
||||||
lnd_cert: Optional[str] = Field(default=None)
|
lnd_cert: str | None = Field(default=None)
|
||||||
lnd_admin_macaroon: Optional[str] = Field(default=None)
|
lnd_admin_macaroon: str | None = Field(default=None)
|
||||||
lnd_invoice_macaroon: Optional[str] = Field(default=None)
|
lnd_invoice_macaroon: str | None = Field(default=None)
|
||||||
lnd_rest_admin_macaroon: Optional[str] = Field(default=None)
|
lnd_rest_admin_macaroon: str | None = Field(default=None)
|
||||||
lnd_rest_invoice_macaroon: Optional[str] = Field(default=None)
|
lnd_rest_invoice_macaroon: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class LndGrpcFundingSource(LNbitsSettings):
|
class LndGrpcFundingSource(LNbitsSettings):
|
||||||
lnd_grpc_endpoint: Optional[str] = Field(default=None)
|
lnd_grpc_endpoint: str | None = Field(default=None)
|
||||||
lnd_grpc_cert: Optional[str] = Field(default=None)
|
lnd_grpc_cert: str | None = Field(default=None)
|
||||||
lnd_grpc_port: Optional[int] = Field(default=None)
|
lnd_grpc_port: int | None = Field(default=None)
|
||||||
lnd_grpc_admin_macaroon: Optional[str] = Field(default=None)
|
lnd_grpc_admin_macaroon: str | None = Field(default=None)
|
||||||
lnd_grpc_invoice_macaroon: Optional[str] = Field(default=None)
|
lnd_grpc_invoice_macaroon: str | None = Field(default=None)
|
||||||
lnd_grpc_macaroon: Optional[str] = Field(default=None)
|
lnd_grpc_macaroon: str | None = Field(default=None)
|
||||||
lnd_grpc_macaroon_encrypted: Optional[str] = Field(default=None)
|
lnd_grpc_macaroon_encrypted: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class LnPayFundingSource(LNbitsSettings):
|
class LnPayFundingSource(LNbitsSettings):
|
||||||
lnpay_api_endpoint: Optional[str] = Field(default=None)
|
lnpay_api_endpoint: str | None = Field(default=None)
|
||||||
lnpay_api_key: Optional[str] = Field(default=None)
|
lnpay_api_key: str | None = Field(default=None)
|
||||||
lnpay_wallet_key: Optional[str] = Field(default=None)
|
lnpay_wallet_key: str | None = Field(default=None)
|
||||||
lnpay_admin_key: Optional[str] = Field(default=None)
|
lnpay_admin_key: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class BlinkFundingSource(LNbitsSettings):
|
class BlinkFundingSource(LNbitsSettings):
|
||||||
blink_api_endpoint: Optional[str] = Field(default="https://api.blink.sv/graphql")
|
blink_api_endpoint: str | None = Field(default="https://api.blink.sv/graphql")
|
||||||
blink_ws_endpoint: Optional[str] = Field(default="wss://ws.blink.sv/graphql")
|
blink_ws_endpoint: str | None = Field(default="wss://ws.blink.sv/graphql")
|
||||||
blink_token: Optional[str] = Field(default=None)
|
blink_token: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class ZBDFundingSource(LNbitsSettings):
|
class ZBDFundingSource(LNbitsSettings):
|
||||||
zbd_api_endpoint: Optional[str] = Field(default="https://api.zebedee.io/v0/")
|
zbd_api_endpoint: str | None = Field(default="https://api.zebedee.io/v0/")
|
||||||
zbd_api_key: Optional[str] = Field(default=None)
|
zbd_api_key: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class PhoenixdFundingSource(LNbitsSettings):
|
class PhoenixdFundingSource(LNbitsSettings):
|
||||||
phoenixd_api_endpoint: Optional[str] = Field(default="http://localhost:9740/")
|
phoenixd_api_endpoint: str | None = Field(default="http://localhost:9740/")
|
||||||
phoenixd_api_password: Optional[str] = Field(default=None)
|
phoenixd_api_password: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class AlbyFundingSource(LNbitsSettings):
|
class AlbyFundingSource(LNbitsSettings):
|
||||||
alby_api_endpoint: Optional[str] = Field(default="https://api.getalby.com/")
|
alby_api_endpoint: str | None = Field(default="https://api.getalby.com/")
|
||||||
alby_access_token: Optional[str] = Field(default=None)
|
alby_access_token: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class OpenNodeFundingSource(LNbitsSettings):
|
class OpenNodeFundingSource(LNbitsSettings):
|
||||||
opennode_api_endpoint: Optional[str] = Field(default=None)
|
opennode_api_endpoint: str | None = Field(default=None)
|
||||||
opennode_key: Optional[str] = Field(default=None)
|
opennode_key: str | None = Field(default=None)
|
||||||
opennode_admin_key: Optional[str] = Field(default=None)
|
opennode_admin_key: str | None = Field(default=None)
|
||||||
opennode_invoice_key: Optional[str] = Field(default=None)
|
opennode_invoice_key: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class SparkFundingSource(LNbitsSettings):
|
class SparkFundingSource(LNbitsSettings):
|
||||||
spark_url: Optional[str] = Field(default=None)
|
spark_url: str | None = Field(default=None)
|
||||||
spark_token: Optional[str] = Field(default=None)
|
spark_token: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class LnTipsFundingSource(LNbitsSettings):
|
class LnTipsFundingSource(LNbitsSettings):
|
||||||
lntips_api_endpoint: Optional[str] = Field(default=None)
|
lntips_api_endpoint: str | None = Field(default=None)
|
||||||
lntips_api_key: Optional[str] = Field(default=None)
|
lntips_api_key: str | None = Field(default=None)
|
||||||
lntips_admin_key: Optional[str] = Field(default=None)
|
lntips_admin_key: str | None = Field(default=None)
|
||||||
lntips_invoice_key: Optional[str] = Field(default=None)
|
lntips_invoice_key: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class NWCFundingSource(LNbitsSettings):
|
class NWCFundingSource(LNbitsSettings):
|
||||||
nwc_pairing_url: Optional[str] = Field(default=None)
|
nwc_pairing_url: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class BreezSdkFundingSource(LNbitsSettings):
|
class BreezSdkFundingSource(LNbitsSettings):
|
||||||
breez_api_key: Optional[str] = Field(default=None)
|
breez_api_key: str | None = Field(default=None)
|
||||||
breez_greenlight_seed: Optional[str] = Field(default=None)
|
breez_greenlight_seed: str | None = Field(default=None)
|
||||||
breez_greenlight_invite_code: Optional[str] = Field(default=None)
|
breez_greenlight_invite_code: str | None = Field(default=None)
|
||||||
breez_greenlight_device_key: Optional[str] = Field(default=None)
|
breez_greenlight_device_key: str | None = Field(default=None)
|
||||||
breez_greenlight_device_cert: Optional[str] = Field(default=None)
|
breez_greenlight_device_cert: str | None = Field(default=None)
|
||||||
breez_use_trampoline: bool = Field(default=True)
|
breez_use_trampoline: bool = Field(default=True)
|
||||||
|
|
||||||
|
|
||||||
class BoltzFundingSource(LNbitsSettings):
|
class BoltzFundingSource(LNbitsSettings):
|
||||||
boltz_client_endpoint: Optional[str] = Field(default="127.0.0.1:9002")
|
boltz_client_endpoint: str | None = Field(default="127.0.0.1:9002")
|
||||||
boltz_client_macaroon: Optional[str] = Field(default=None)
|
boltz_client_macaroon: str | None = Field(default=None)
|
||||||
boltz_client_wallet: Optional[str] = Field(default="lnbits")
|
boltz_client_wallet: str | None = Field(default="lnbits")
|
||||||
boltz_client_cert: Optional[str] = Field(default=None)
|
boltz_client_cert: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class LightningSettings(LNbitsSettings):
|
class LightningSettings(LNbitsSettings):
|
||||||
@ -562,8 +560,8 @@ class FundingSourcesSettings(
|
|||||||
|
|
||||||
|
|
||||||
class WebPushSettings(LNbitsSettings):
|
class WebPushSettings(LNbitsSettings):
|
||||||
lnbits_webpush_pubkey: Optional[str] = Field(default=None)
|
lnbits_webpush_pubkey: str | None = Field(default=None)
|
||||||
lnbits_webpush_privkey: Optional[str] = Field(default=None)
|
lnbits_webpush_privkey: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class NodeUISettings(LNbitsSettings):
|
class NodeUISettings(LNbitsSettings):
|
||||||
@ -669,9 +667,9 @@ class AuditSettings(LNbitsSettings):
|
|||||||
|
|
||||||
def audit_http_request(
|
def audit_http_request(
|
||||||
self,
|
self,
|
||||||
http_method: Optional[str] = None,
|
http_method: str | None = None,
|
||||||
path: Optional[str] = None,
|
path: str | None = None,
|
||||||
http_response_code: Optional[str] = None,
|
http_response_code: str | None = None,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
if not self.lnbits_audit_enabled:
|
if not self.lnbits_audit_enabled:
|
||||||
return False
|
return False
|
||||||
@ -689,7 +687,7 @@ class AuditSettings(LNbitsSettings):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _is_http_request_path_auditable(self, path: Optional[str]):
|
def _is_http_request_path_auditable(self, path: str | None):
|
||||||
if len(self.lnbits_audit_exclude_paths) != 0 and path:
|
if len(self.lnbits_audit_exclude_paths) != 0 and path:
|
||||||
for exclude_path in self.lnbits_audit_exclude_paths:
|
for exclude_path in self.lnbits_audit_exclude_paths:
|
||||||
if _re_fullmatch_safe(exclude_path, path):
|
if _re_fullmatch_safe(exclude_path, path):
|
||||||
@ -706,9 +704,7 @@ class AuditSettings(LNbitsSettings):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _is_http_response_code_auditable(
|
def _is_http_response_code_auditable(self, http_response_code: str | None) -> bool:
|
||||||
self, http_response_code: Optional[str]
|
|
||||||
) -> bool:
|
|
||||||
if not http_response_code:
|
if not http_response_code:
|
||||||
# No response code means only request filters should apply
|
# No response code means only request filters should apply
|
||||||
return True
|
return True
|
||||||
@ -805,9 +801,9 @@ class EnvSettings(LNbitsSettings):
|
|||||||
|
|
||||||
|
|
||||||
class SaaSSettings(LNbitsSettings):
|
class SaaSSettings(LNbitsSettings):
|
||||||
lnbits_saas_callback: Optional[str] = Field(default=None)
|
lnbits_saas_callback: str | None = Field(default=None)
|
||||||
lnbits_saas_secret: Optional[str] = Field(default=None)
|
lnbits_saas_secret: str | None = Field(default=None)
|
||||||
lnbits_saas_instance_id: Optional[str] = Field(default=None)
|
lnbits_saas_instance_id: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class PersistenceSettings(LNbitsSettings):
|
class PersistenceSettings(LNbitsSettings):
|
||||||
@ -905,7 +901,7 @@ class Settings(EditableSettings, ReadOnlySettings, TransientSettings, BaseSettin
|
|||||||
or user_id == self.super_user
|
or user_id == self.super_user
|
||||||
)
|
)
|
||||||
|
|
||||||
def is_super_user(self, user_id: Optional[str] = None) -> bool:
|
def is_super_user(self, user_id: str | None = None) -> bool:
|
||||||
return user_id == self.super_user
|
return user_id == self.super_user
|
||||||
|
|
||||||
def is_admin_user(self, user_id: str) -> bool:
|
def is_admin_user(self, user_id: str) -> bool:
|
||||||
@ -924,12 +920,12 @@ class SuperSettings(EditableSettings):
|
|||||||
|
|
||||||
class AdminSettings(EditableSettings):
|
class AdminSettings(EditableSettings):
|
||||||
is_super_user: bool
|
is_super_user: bool
|
||||||
lnbits_allowed_funding_sources: Optional[list[str]]
|
lnbits_allowed_funding_sources: list[str] | None
|
||||||
|
|
||||||
|
|
||||||
class SettingsField(BaseModel):
|
class SettingsField(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
value: Optional[Any]
|
value: Any | None
|
||||||
tag: str = "core"
|
tag: str = "core"
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from time import time
|
from time import time
|
||||||
from typing import Any, NamedTuple, Optional
|
from typing import Any, NamedTuple
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ class Cache:
|
|||||||
self.interval = interval
|
self.interval = interval
|
||||||
self._values: dict[Any, Cached] = {}
|
self._values: dict[Any, Cached] = {}
|
||||||
|
|
||||||
def get(self, key: str, default=None) -> Optional[Any]:
|
def get(self, key: str, default=None) -> Any | None:
|
||||||
cached = self._values.get(key)
|
cached = self._values.get(key)
|
||||||
if cached is not None:
|
if cached is not None:
|
||||||
if cached.expiry > time():
|
if cached.expiry > time():
|
||||||
@ -35,7 +35,7 @@ class Cache:
|
|||||||
def set(self, key: str, value: Any, expiry: float = 10):
|
def set(self, key: str, value: Any, expiry: float = 10):
|
||||||
self._values[key] = Cached(value, time() + expiry)
|
self._values[key] = Cached(value, time() + expiry)
|
||||||
|
|
||||||
def pop(self, key: str, default=None) -> Optional[Any]:
|
def pop(self, key: str, default=None) -> Any | None:
|
||||||
cached = self._values.pop(key, None)
|
cached = self._values.pop(key, None)
|
||||||
if cached and cached.expiry > time():
|
if cached and cached.expiry > time():
|
||||||
return cached.value
|
return cached.value
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import importlib
|
import importlib
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from lnbits.nodes import set_node_class
|
from lnbits.nodes import set_node_class
|
||||||
from lnbits.settings import settings
|
from lnbits.settings import settings
|
||||||
@ -33,7 +32,7 @@ from .void import VoidWallet
|
|||||||
from .zbd import ZBDWallet
|
from .zbd import ZBDWallet
|
||||||
|
|
||||||
|
|
||||||
def set_funding_source(class_name: Optional[str] = None):
|
def set_funding_source(class_name: str | None = None):
|
||||||
backend_wallet_class = class_name or settings.lnbits_backend_wallet_class
|
backend_wallet_class = class_name or settings.lnbits_backend_wallet_class
|
||||||
funding_source_constructor = getattr(wallets_module, backend_wallet_class)
|
funding_source_constructor = getattr(wallets_module, backend_wallet_class)
|
||||||
global funding_source
|
global funding_source
|
||||||
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import TYPE_CHECKING, AsyncGenerator, Coroutine, NamedTuple, Optional
|
from typing import TYPE_CHECKING, AsyncGenerator, Coroutine, NamedTuple
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
@ -11,15 +11,15 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
|
|
||||||
class StatusResponse(NamedTuple):
|
class StatusResponse(NamedTuple):
|
||||||
error_message: Optional[str]
|
error_message: str | None
|
||||||
balance_msat: int
|
balance_msat: int
|
||||||
|
|
||||||
|
|
||||||
class InvoiceResponse(NamedTuple):
|
class InvoiceResponse(NamedTuple):
|
||||||
ok: bool
|
ok: bool
|
||||||
checking_id: Optional[str] = None # payment_hash, rpc_id
|
checking_id: str | None = None # payment_hash, rpc_id
|
||||||
payment_request: Optional[str] = None
|
payment_request: str | None = None
|
||||||
error_message: Optional[str] = None
|
error_message: str | None = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def success(self) -> bool:
|
def success(self) -> bool:
|
||||||
@ -36,11 +36,11 @@ class InvoiceResponse(NamedTuple):
|
|||||||
|
|
||||||
class PaymentResponse(NamedTuple):
|
class PaymentResponse(NamedTuple):
|
||||||
# when ok is None it means we don't know if this succeeded
|
# when ok is None it means we don't know if this succeeded
|
||||||
ok: Optional[bool] = None
|
ok: bool | None = None
|
||||||
checking_id: Optional[str] = None # payment_hash, rcp_id
|
checking_id: str | None = None # payment_hash, rcp_id
|
||||||
fee_msat: Optional[int] = None
|
fee_msat: int | None = None
|
||||||
preimage: Optional[str] = None
|
preimage: str | None = None
|
||||||
error_message: Optional[str] = None
|
error_message: str | None = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def success(self) -> bool:
|
def success(self) -> bool:
|
||||||
@ -56,9 +56,9 @@ class PaymentResponse(NamedTuple):
|
|||||||
|
|
||||||
|
|
||||||
class PaymentStatus(NamedTuple):
|
class PaymentStatus(NamedTuple):
|
||||||
paid: Optional[bool] = None
|
paid: bool | None = None
|
||||||
fee_msat: Optional[int] = None
|
fee_msat: int | None = None
|
||||||
preimage: Optional[str] = None
|
preimage: str | None = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def success(self) -> bool:
|
def success(self) -> bool:
|
||||||
@ -94,7 +94,7 @@ class PaymentPendingStatus(PaymentStatus):
|
|||||||
|
|
||||||
class Wallet(ABC):
|
class Wallet(ABC):
|
||||||
|
|
||||||
__node_cls__: Optional[type[Node]] = None
|
__node_cls__: type[Node] | None = None
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.pending_invoices: list[str] = []
|
self.pending_invoices: list[str] = []
|
||||||
@ -111,9 +111,9 @@ class Wallet(ABC):
|
|||||||
def create_invoice(
|
def create_invoice(
|
||||||
self,
|
self,
|
||||||
amount: int,
|
amount: int,
|
||||||
memo: Optional[str] = None,
|
memo: str | None = None,
|
||||||
description_hash: Optional[bytes] = None,
|
description_hash: bytes | None = None,
|
||||||
unhashed_description: Optional[bytes] = None,
|
unhashed_description: bytes | None = None,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> Coroutine[None, None, InvoiceResponse]:
|
) -> Coroutine[None, None, InvoiceResponse]:
|
||||||
pass
|
pass
|
||||||
|
23
poetry.lock
generated
23
poetry.lock
generated
@ -1395,9 +1395,6 @@ files = [
|
|||||||
{file = "importlib_resources-6.1.1.tar.gz", hash = "sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a"},
|
{file = "importlib_resources-6.1.1.tar.gz", hash = "sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
|
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
|
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||||
testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff", "zipp (>=3.17)"]
|
testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff", "zipp (>=3.17)"]
|
||||||
@ -2916,7 +2913,6 @@ files = [
|
|||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
anyio = ">=3.4.0,<5"
|
anyio = ">=3.4.0,<5"
|
||||||
typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
|
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"]
|
full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"]
|
||||||
@ -3400,26 +3396,11 @@ files = [
|
|||||||
{file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"},
|
{file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zipp"
|
|
||||||
version = "3.19.2"
|
|
||||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"},
|
|
||||||
{file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
|
||||||
test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
|
|
||||||
|
|
||||||
[extras]
|
[extras]
|
||||||
breez = ["breez-sdk"]
|
breez = ["breez-sdk"]
|
||||||
liquid = ["wallycore"]
|
liquid = ["wallycore"]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.13 | ^3.12 | ^3.11 | ^3.10 | ^3.9"
|
python-versions = "~3.12 | ~3.11 | ~3.10"
|
||||||
content-hash = "e263865649975ea7e977b3cbf6cb453c3653de115523d026e3863605ab48a463"
|
content-hash = "96dd180aaa4fbfeb34fa6f9647c8684fce183a72b1b41d22101a9dd4b962fa2e"
|
||||||
|
@ -12,7 +12,7 @@ packages = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.13 | ^3.12 | ^3.11 | ^3.10 | ^3.9"
|
python = "~3.12 | ~3.11 | ~3.10"
|
||||||
bech32 = "1.2.0"
|
bech32 = "1.2.0"
|
||||||
click = "8.1.7"
|
click = "8.1.7"
|
||||||
ecdsa = "0.19.0"
|
ecdsa = "0.19.0"
|
||||||
@ -192,7 +192,7 @@ extend-exclude = [
|
|||||||
select = ["F", "E", "W", "I", "A", "C", "N", "UP", "RUF", "B"]
|
select = ["F", "E", "W", "I", "A", "C", "N", "UP", "RUF", "B"]
|
||||||
# UP007: pyupgrade: use X | Y instead of Optional. (python3.10)
|
# UP007: pyupgrade: use X | Y instead of Optional. (python3.10)
|
||||||
# RUF012: mutable-class-default
|
# RUF012: mutable-class-default
|
||||||
ignore = ["UP007", "RUF012"]
|
ignore = ["RUF012"]
|
||||||
|
|
||||||
# Allow autofix for all enabled rules (when `--fix`) is provided.
|
# Allow autofix for all enabled rules (when `--fix`) is provided.
|
||||||
fixable = ["ALL"]
|
fixable = ["ALL"]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user