Files
lnbits/lnbits/core/extensions/models.py
T
2026-07-09 16:35:40 +03:00

285 lines
7.7 KiB
Python

import json
from typing import Any, Literal
from pydantic import BaseModel, Field, root_validator
class EmptyRequest(BaseModel):
pass
class StorageGetRequest(BaseModel):
table: str = Field(..., min_length=1, max_length=128)
id: str = Field(..., min_length=1, max_length=512)
class StorageGetResponse(BaseModel):
data_json: str | None = None
class StorageSetRequest(BaseModel):
table: str = Field(..., min_length=1, max_length=128)
data: dict[str, Any] = Field(default_factory=dict)
@root_validator(pre=True)
def parse_data_json(cls, values: dict[str, Any]) -> dict[str, Any]:
data_json = values.get("data_json")
if data_json is not None and "data" not in values:
values["data"] = json.loads(data_json)
return values
class StorageSetResponse(BaseModel):
ok: bool = True
class StoragePaginatedRequest(BaseModel):
table: str = Field(..., min_length=1, max_length=128)
filters: dict[str, Any] = Field(default_factory=dict)
search: str | None = Field(None, max_length=256)
search_fields: list[str] = Field(default_factory=list)
sort_by: str | None = Field(None, min_length=1, max_length=128)
descending: bool = False
limit: int = Field(25, ge=1, le=1000)
offset: int = Field(0, ge=0)
@root_validator(pre=True)
def parse_json_fields(cls, values: dict[str, Any]) -> dict[str, Any]:
filters_json = values.get("filters_json")
if filters_json is not None and "filters" not in values:
values["filters"] = json.loads(filters_json)
search_fields_json = values.get("search_fields_json")
if search_fields_json is not None and "search_fields" not in values:
values["search_fields"] = json.loads(search_fields_json)
if values.get("sort_by") == "":
values["sort_by"] = None
return values
class StoragePaginatedResponse(BaseModel):
rows_json: str = "[]"
total: int = 0
class StorageDeleteRequest(BaseModel):
table: str = Field(..., min_length=1, max_length=128)
id: str = Field(..., min_length=1, max_length=512)
class StorageDeleteResponse(BaseModel):
ok: bool = True
class CreateInvoiceRequest(BaseModel):
wallet_id: str = Field(..., min_length=1, max_length=128)
amount: float = Field(..., gt=0)
currency: str = Field("sat", min_length=1, max_length=8)
memo: str = Field(..., max_length=512)
tag: str = Field(..., min_length=1, max_length=64)
extra: dict[str, str] = Field(default_factory=dict)
class CreateInvoicePublicRequest(BaseModel):
source_id: str = Field(..., min_length=1, max_length=512)
amount: float = Field(..., gt=0)
currency: str = Field(..., min_length=1, max_length=8)
memo: str = Field("", max_length=512)
extra: dict[str, Any] = Field(default_factory=dict)
@root_validator
def validate_extra_size(cls, values: dict[str, Any]) -> dict[str, Any]:
extra = values.get("extra") or {}
try:
encoded = json.dumps(extra, separators=(",", ":"))
except TypeError as exc:
raise ValueError("extra must be JSON serializable.") from exc
if len(encoded.encode()) > 4096:
raise ValueError("extra must not exceed 4096 bytes.")
values["extra"] = extra
return values
class CreateInvoiceResponse(BaseModel):
payment_hash: str
payment_request: str
checking_id: str
class UserWalletSummary(BaseModel):
id: str
name: str
currency: str | None = None
class ListUserWalletsResponse(BaseModel):
wallets: list[UserWalletSummary] = Field(default_factory=list)
class HttpRequest(BaseModel):
method: Literal["DELETE", "GET", "HEAD", "PATCH", "POST", "PUT"] = "GET"
url: str = Field(..., min_length=1, max_length=2048)
headers: dict[str, str] = Field(default_factory=dict)
body: str | None = Field(None, max_length=65536)
@root_validator(pre=True)
def normalize_method(cls, values: dict[str, Any]) -> dict[str, Any]:
method = values.get("method")
if isinstance(method, str):
values["method"] = method.upper()
return values
@root_validator
def validate_headers_size(cls, values: dict[str, Any]) -> dict[str, Any]:
headers = values.get("headers") or {}
if len(headers) > 32:
raise ValueError("headers must not contain more than 32 entries.")
for key, value in headers.items():
if len(key) > 128 or len(value) > 4096:
raise ValueError("headers are too large.")
values["headers"] = headers
return values
class HttpResponse(BaseModel):
status_code: int
headers: dict[str, str] = Field(default_factory=dict)
body: str = ""
class ExtensionApiRequest(BaseModel):
extension_id: str = Field(..., min_length=1, max_length=128)
method: Literal["DELETE", "GET", "HEAD", "PATCH", "POST", "PUT"] = "GET"
path: str = Field(..., min_length=1, max_length=2048)
body: str | None = Field(None, max_length=65536)
@root_validator(pre=True)
def normalize_method(cls, values: dict[str, Any]) -> dict[str, Any]:
method = values.get("method")
if isinstance(method, str):
values["method"] = method.upper()
return values
class CurrencyListResponse(BaseModel):
currencies: list[str] = Field(default_factory=list)
class CurrencyRateRequest(BaseModel):
currency: str = Field(..., min_length=1, max_length=8)
class CurrencyRateResponse(BaseModel):
rate: float
price: float
class CurrencyConvertRequest(BaseModel):
amount: float = Field(..., gt=0)
from_currency: str = Field(..., alias="from", min_length=1, max_length=8)
to: str = Field(..., min_length=1, max_length=256)
class Config:
allow_population_by_field_name = True
class CurrencyConvertResponse(BaseModel):
amounts: list[tuple[str, float]] = Field(default_factory=list)
class FiatToSatsRequest(BaseModel):
amount: float = Field(..., gt=0)
currency: str = Field(..., min_length=1, max_length=8)
class FiatToSatsResponse(BaseModel):
amount_sat: int
class SatsToFiatRequest(BaseModel):
amount: float = Field(..., gt=0)
currency: str = Field(..., min_length=1, max_length=8)
class SatsToFiatResponse(BaseModel):
amount: float
class ServerHealthResponse(BaseModel):
server_time: int
up_time: str
class Bolt11Request(BaseModel):
bolt11: str = Field(..., min_length=1, max_length=8192)
class DecodeInvoiceResponse(BaseModel):
valid: bool = True
payment_hash: str | None = None
amount_msat: int | None = None
expiry: int | None = None
expires_at: int | None = None
memo: str | None = None
class ValidateInvoiceResponse(BaseModel):
valid: bool
error: str | None = None
class InvoicePaymentHashResponse(BaseModel):
payment_hash: str
class InvoiceAmountMsatResponse(BaseModel):
amount_msat: int | None = None
class InvoiceExpiryResponse(BaseModel):
expires_at: int | None = None
class InvoiceMemoResponse(BaseModel):
memo: str | None = None
class VerifyPreimageRequest(BaseModel):
preimage: str = Field(..., min_length=64, max_length=64)
payment_hash: str = Field(..., min_length=64, max_length=64)
class VerifyPreimageResponse(BaseModel):
valid: bool
class RandomSecretAndHashRequest(BaseModel):
length: int = Field(32, ge=16, le=64)
class RandomSecretAndHashResponse(BaseModel):
secret: str
hash: str
class RandomIdRequest(BaseModel):
prefix: str = Field(..., min_length=1, max_length=32)
class RandomIdResponse(BaseModel):
id: str
class NowResponse(BaseModel):
timestamp: int
class LogRequest(BaseModel):
level: Literal["debug", "info", "warning", "error"] = "info"
message: str = Field(..., min_length=1, max_length=2048)
class LogResponse(BaseModel):
ok: bool = True