mirror of
https://github.com/fusion44/blitz_api.git
synced 2026-08-16 12:20:14 +02:00
refactor: switch to domain driven directory layout
This commit is contained in:
parent
e1d5e23db7
commit
ba453e7bdb
68 changed files with 340 additions and 357 deletions
|
|
@ -12,8 +12,8 @@ from typing import Dict, Optional
|
|||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi_plugins import redis_plugin
|
||||
|
||||
from app.api.sse_manager import SSEManager
|
||||
from app.external.sse_starlette import ServerSentEvent
|
||||
from app.sse_manager import SSEManager
|
||||
|
||||
sse_mgr = SSEManager()
|
||||
sse_mgr.setup()
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
import asyncio
|
||||
from typing import List
|
||||
|
||||
from app.repositories.bitcoin import get_btc_info
|
||||
from app.repositories.lightning import (
|
||||
from app.bitcoind.service import get_btc_info
|
||||
from app.lightning.service import (
|
||||
get_fee_revenue,
|
||||
get_ln_info,
|
||||
get_ln_info_lite,
|
||||
get_wallet_balance,
|
||||
)
|
||||
from app.repositories.system import get_hardware_info, get_system_info
|
||||
from app.system.service import get_hardware_info, get_system_info
|
||||
|
||||
|
||||
async def get_bitcoin_client_warmup_data() -> List:
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from app.repositories.apps_impl.apps_base import AppsBase
|
||||
from app.apps.impl.apps_base import AppsBase
|
||||
|
||||
|
||||
class NativePythonApps(AppsBase):
|
||||
|
|
@ -9,14 +9,20 @@ from decouple import config
|
|||
from fastapi import HTTPException, status
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
|
||||
from app.core_utils import (
|
||||
SSE,
|
||||
broadcast_sse_msg,
|
||||
call_sudo_script,
|
||||
parse_key_value_text,
|
||||
)
|
||||
from app.repositories.apps_impl.apps_base import AppsBase
|
||||
from app.repositories.utils.raspiblitz import available_app_ids
|
||||
from app.api.utils import SSE, broadcast_sse_msg, call_sudo_script, parse_key_value_text
|
||||
from app.apps.impl import AppsBase
|
||||
|
||||
available_app_ids = {
|
||||
"btc-rpc-explorer",
|
||||
"rtl",
|
||||
# Specter is deactivated for now because it uses its own self signed HTTPS cert that makes trouble in Chrome on last test
|
||||
# "specter",
|
||||
"btcpayserver",
|
||||
"lnbits",
|
||||
"mempool",
|
||||
"thunderhub",
|
||||
}
|
||||
|
||||
|
||||
SHELL_SCRIPT_PATH = config("shell_script_path")
|
||||
|
||||
|
|
@ -2,8 +2,8 @@ from fastapi import APIRouter, HTTPException, status
|
|||
from fastapi.params import Depends
|
||||
from pydantic import BaseModel
|
||||
|
||||
import app.repositories.apps as repo
|
||||
import app.routers.apps_docs as docs
|
||||
import app.apps.docs as docs
|
||||
import app.apps.service as repo
|
||||
from app.auth.auth_bearer import JWTBearer
|
||||
from app.external.sse_starlette import EventSourceResponse
|
||||
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
from decouple import config
|
||||
|
||||
from app.models.system import APIPlatform
|
||||
from app.system.models import APIPlatform
|
||||
|
||||
PLATFORM = config("platform", default=APIPlatform.RASPIBLITZ)
|
||||
apps = None
|
||||
|
||||
if PLATFORM == APIPlatform.RASPIBLITZ:
|
||||
from .apps_impl.raspiblitz import RaspiBlitzApps as Apps
|
||||
from app.apps.impl.raspiblitz import RaspiBlitzApps as Apps
|
||||
elif PLATFORM == APIPlatform.NATIVE_PYTHON:
|
||||
from .apps_impl.native_python import NativePythonApps as Apps
|
||||
from app.apps.impl.native_python import NativePythonApps as Apps
|
||||
|
||||
apps = Apps()
|
||||
|
||||
|
|
@ -2,17 +2,17 @@ from fastapi import APIRouter, HTTPException, Request, status
|
|||
from fastapi.params import Depends, Query
|
||||
|
||||
from app.auth.auth_bearer import JWTBearer
|
||||
from app.external.sse_starlette import EventSourceResponse
|
||||
from app.models.bitcoind import BlockchainInfo, BtcInfo, FeeEstimationMode, NetworkInfo
|
||||
from app.repositories.bitcoin import (
|
||||
from app.bitcoind.docs import blocks_sub_doc, estimate_fee_mode_desc
|
||||
from app.bitcoind.models import BlockchainInfo, BtcInfo, FeeEstimationMode, NetworkInfo
|
||||
from app.bitcoind.service import (
|
||||
estimate_fee,
|
||||
get_blockchain_info,
|
||||
get_btc_info,
|
||||
get_network_info,
|
||||
handle_block_sub,
|
||||
)
|
||||
from app.repositories.utils.bitcoin import bitcoin_rpc
|
||||
from app.routers.bitcoin_docs import blocks_sub_doc, estimate_fee_mode_desc
|
||||
from app.bitcoind.utils import bitcoin_rpc
|
||||
from app.external.sse_starlette import EventSourceResponse
|
||||
|
||||
_PREFIX = "bitcoin"
|
||||
|
||||
|
|
@ -10,15 +10,15 @@ from fastapi import Request
|
|||
from fastapi.exceptions import HTTPException
|
||||
from starlette import status
|
||||
|
||||
from app.core_utils import SSE, broadcast_sse_msg
|
||||
from app.models.bitcoind import (
|
||||
from app.api.utils import SSE, broadcast_sse_msg
|
||||
from app.bitcoind.models import (
|
||||
BlockchainInfo,
|
||||
BlockRpcFunc,
|
||||
BtcInfo,
|
||||
FeeEstimationMode,
|
||||
NetworkInfo,
|
||||
)
|
||||
from app.repositories.utils.bitcoin import bitcoin_config, bitcoin_rpc_async
|
||||
from app.bitcoind.utils import bitcoin_config, bitcoin_rpc_async
|
||||
|
||||
_initialized = False
|
||||
|
||||
|
|
@ -6,7 +6,7 @@ import requests
|
|||
from decouple import config
|
||||
from starlette import status
|
||||
|
||||
from app.models.bitcoind import BlockRpcFunc
|
||||
from app.bitcoind.models import BlockRpcFunc
|
||||
|
||||
|
||||
class _BitcoinConfig:
|
||||
|
|
@ -1,3 +1,68 @@
|
|||
tx_id_desc = """
|
||||
Unique identifier for this transaction.
|
||||
|
||||
Depending on the type of the transaction it will be different:
|
||||
#### On-chain
|
||||
The transaction hash
|
||||
|
||||
#### Lightning Invoice and Payment
|
||||
The payment request
|
||||
"""
|
||||
|
||||
tx_amount_desc = """
|
||||
The value of the transaction, depending on the category in satoshis or millisatoshis.
|
||||
|
||||
#### On-chain
|
||||
Transaction amount in satoshis
|
||||
|
||||
#### Lightning Invoice
|
||||
* value in millisatoshis of the invoice if *unsettled*
|
||||
* amount in millisatoshis paid if invoice is *settled*
|
||||
|
||||
#### Lightning Payment
|
||||
* amount sent in millisatoshis
|
||||
|
||||
"""
|
||||
|
||||
tx_status_desc = """
|
||||
The status of the transaction. Depending on the transaction category this can be different values:
|
||||
|
||||
May have different meanings in different situations:
|
||||
#### unknown
|
||||
An unknown state was found.
|
||||
|
||||
#### in_flight
|
||||
* A lightning payment is being sent
|
||||
* An invoice is waiting for the incoming payment
|
||||
* An on-chain transaction is waiting in the mempool
|
||||
|
||||
#### succeeded
|
||||
* A lighting payment was successfully sent
|
||||
* An incoming payment was received for an invoice
|
||||
* An on-chain transaction was included in a block
|
||||
|
||||
#### failed
|
||||
* A lightning payment attempt which could not be completed (no route found, insufficient funds, ...)
|
||||
* An invoice is expired or some other error happened
|
||||
"""
|
||||
|
||||
tx_time_stamp_desc = """
|
||||
The unix timestamp in seconds for the transaction.
|
||||
|
||||
The timestamp can mean different things in different situations:
|
||||
|
||||
#### Lightning Invoice
|
||||
* Creation date for in-flight or failed invoices
|
||||
* Settle date for succeeded invoices
|
||||
|
||||
#### On-chain
|
||||
* Creation date for transaction waiting in the mempool
|
||||
* Timestamp of the block where this transaction is included
|
||||
|
||||
#### Lightning Payment
|
||||
|
||||
"""
|
||||
|
||||
add_invoice_desc = """
|
||||
Adds a new invoice to the database.
|
||||
|
||||
|
|
@ -11,11 +11,13 @@ from decouple import config
|
|||
from fastapi.exceptions import HTTPException
|
||||
from starlette import status
|
||||
|
||||
import app.repositories.ln_impl.protos.cln.node_pb2 as ln
|
||||
import app.repositories.ln_impl.protos.cln.node_pb2_grpc as clnrpc
|
||||
import app.repositories.ln_impl.protos.cln.primitives_pb2 as lnp
|
||||
from app.core_utils import config_get_hex_str, next_push_id
|
||||
from app.models.lightning import (
|
||||
import app.lightning.impl.protos.cln.node_pb2 as ln
|
||||
import app.lightning.impl.protos.cln.node_pb2_grpc as clnrpc
|
||||
import app.lightning.impl.protos.cln.primitives_pb2 as lnp
|
||||
from app.api.utils import config_get_hex_str, next_push_id
|
||||
from app.bitcoind.utils import bitcoin_rpc_async
|
||||
from app.lightning.impl.ln_base import LightningNodeBase
|
||||
from app.lightning.models import (
|
||||
Channel,
|
||||
FeeRevenue,
|
||||
ForwardSuccessEvent,
|
||||
|
|
@ -35,8 +37,6 @@ from app.models.lightning import (
|
|||
TxStatus,
|
||||
WalletBalance,
|
||||
)
|
||||
from app.repositories.ln_impl.ln_base import LightningNodeBase
|
||||
from app.repositories.utils.bitcoin import bitcoin_rpc_async
|
||||
|
||||
|
||||
async def _make_local_call(cmd: str):
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
from abc import abstractmethod
|
||||
from typing import AsyncGenerator, List, Optional
|
||||
|
||||
from app.models.lightning import (
|
||||
from app.lightning.models import (
|
||||
Channel,
|
||||
FeeRevenue,
|
||||
ForwardSuccessEvent,
|
||||
|
|
@ -14,7 +14,7 @@ import app.repositories.ln_impl.protos.lnd.router_pb2 as router
|
|||
import app.repositories.ln_impl.protos.lnd.router_pb2_grpc as routerrpc
|
||||
import app.repositories.ln_impl.protos.lnd.walletunlocker_pb2 as unlocker
|
||||
import app.repositories.ln_impl.protos.lnd.walletunlocker_pb2_grpc as unlockerrpc
|
||||
from app.core_utils import SSE, broadcast_sse_msg, config_get_hex_str
|
||||
from app.api.utils import SSE, broadcast_sse_msg, config_get_hex_str
|
||||
from app.models.lightning import (
|
||||
Channel,
|
||||
FeeRevenue,
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -2,7 +2,7 @@
|
|||
"""Client and server classes corresponding to protobuf-defined services."""
|
||||
import grpc
|
||||
|
||||
import app.repositories.ln_impl.protos.cln.node_pb2 as node__pb2
|
||||
import app.lightning.impl.protos.cln.node_pb2 as node__pb2
|
||||
|
||||
|
||||
class NodeStub(object):
|
||||
|
|
@ -6,7 +6,7 @@ from decouple import config
|
|||
from fastapi.exceptions import HTTPException
|
||||
from starlette import status
|
||||
|
||||
from app.core_utils import call_script2, redis_get
|
||||
from app.api.utils import call_script2, redis_get
|
||||
from app.models.lightning import (
|
||||
Channel,
|
||||
FeeRevenue,
|
||||
|
|
@ -7,7 +7,7 @@ from fastapi.param_functions import Query
|
|||
from pydantic import BaseModel
|
||||
from pydantic.types import conint
|
||||
|
||||
import app.models.lightning_docs as docs
|
||||
import app.lightning.docs as docs
|
||||
|
||||
|
||||
class LnInitState(str, Enum):
|
||||
|
|
@ -4,7 +4,15 @@ from fastapi import APIRouter, HTTPException, Query, status
|
|||
from fastapi.params import Depends
|
||||
|
||||
from app.auth.auth_bearer import JWTBearer
|
||||
from app.models.lightning import (
|
||||
from app.lightning.docs import (
|
||||
get_balance_response_desc,
|
||||
new_address_desc,
|
||||
open_channel_desc,
|
||||
send_coins_desc,
|
||||
send_payment_desc,
|
||||
unlock_wallet_desc,
|
||||
)
|
||||
from app.lightning.models import (
|
||||
Channel,
|
||||
FeeRevenue,
|
||||
GenericTx,
|
||||
|
|
@ -20,7 +28,7 @@ from app.models.lightning import (
|
|||
UnlockWalletInput,
|
||||
WalletBalance,
|
||||
)
|
||||
from app.repositories.lightning import (
|
||||
from app.lightning.service import (
|
||||
add_invoice,
|
||||
channel_close,
|
||||
channel_list,
|
||||
|
|
@ -39,14 +47,6 @@ from app.repositories.lightning import (
|
|||
send_payment,
|
||||
unlock_wallet,
|
||||
)
|
||||
from app.routers.lightning_docs import (
|
||||
get_balance_response_desc,
|
||||
new_address_desc,
|
||||
open_channel_desc,
|
||||
send_coins_desc,
|
||||
send_payment_desc,
|
||||
unlock_wallet_desc,
|
||||
)
|
||||
|
||||
_PREFIX = "lightning"
|
||||
|
||||
|
|
@ -6,8 +6,8 @@ from decouple import config
|
|||
from fastapi import status
|
||||
from fastapi.exceptions import HTTPException
|
||||
|
||||
from app.core_utils import SSE, broadcast_sse_msg, redis_get
|
||||
from app.models.lightning import (
|
||||
from app.api.utils import SSE, broadcast_sse_msg, redis_get
|
||||
from app.lightning.models import (
|
||||
Channel,
|
||||
FeeRevenue,
|
||||
GenericTx,
|
||||
|
|
@ -22,17 +22,17 @@ from app.models.lightning import (
|
|||
SendCoinsInput,
|
||||
SendCoinsResponse,
|
||||
)
|
||||
from app.models.system import APIPlatform
|
||||
from app.system.models import APIPlatform
|
||||
|
||||
PLATFORM = config("platform", cast=str)
|
||||
|
||||
ln_node = config("ln_node")
|
||||
if ln_node == "lnd_grpc":
|
||||
from app.repositories.ln_impl.lnd_grpc import LnNodeLNDgRPC as LnNode
|
||||
from app.lightning.impl.lnd_grpc import LnNodeLNDgRPC as LnNode
|
||||
elif ln_node == "cln_grpc" and PLATFORM != APIPlatform.RASPIBLITZ:
|
||||
from app.repositories.ln_impl.cln_grpc import LnNodeCLNgRPC as LnNode
|
||||
from app.lightning.impl.cln_grpc import LnNodeCLNgRPC as LnNode
|
||||
elif ln_node == "cln_grpc" and PLATFORM == APIPlatform.RASPIBLITZ:
|
||||
from app.repositories.ln_impl.specializations.cln_grpc_blitz import (
|
||||
from app.lightning.impl.specializations.cln_grpc_blitz import (
|
||||
LnNodeCLNgRPCBlitz as LnNode,
|
||||
)
|
||||
elif ln_node == "none":
|
||||
43
app/main.py
43
app/main.py
|
|
@ -15,29 +15,34 @@ from starlette import status
|
|||
from starlette.middleware.cors import CORSMiddleware
|
||||
from starlette.responses import RedirectResponse
|
||||
|
||||
from app.api.models import ApiStartupStatus, StartupState
|
||||
from app.api.utils import SSE, broadcast_sse_msg, build_sse_event, sse_mgr
|
||||
from app.api.warmup import (
|
||||
get_bitcoin_client_warmup_data,
|
||||
get_full_client_warmup_data,
|
||||
get_full_client_warmup_data_bitcoinonly,
|
||||
)
|
||||
from app.apps import router
|
||||
from app.apps.router import router as app_router
|
||||
from app.auth.auth_bearer import JWTBearer
|
||||
from app.auth.auth_handler import (
|
||||
handle_local_cookie,
|
||||
register_cookie_updater,
|
||||
remove_local_cookie,
|
||||
)
|
||||
from app.core_utils import SSE, broadcast_sse_msg, build_sse_event, sse_mgr
|
||||
from app.external.fastapi_versioning import VersionedFastAPI
|
||||
from app.models.api import ApiStartupStatus, StartupState
|
||||
from app.models.lightning import LnInitState
|
||||
from app.repositories.bitcoin import (
|
||||
from app.bitcoind.router import router as bitcoin_router
|
||||
from app.bitcoind.service import (
|
||||
initialize_bitcoin_repo,
|
||||
register_bitcoin_status_gatherer,
|
||||
register_bitcoin_zmq_sub,
|
||||
)
|
||||
from app.repositories.lightning import initialize_ln_repo, register_lightning_listener
|
||||
from app.repositories.system import get_hardware_info, register_hardware_info_gatherer
|
||||
from app.routers import apps, bitcoin, lightning, setup, system
|
||||
from app.warmup import (
|
||||
get_bitcoin_client_warmup_data,
|
||||
get_full_client_warmup_data,
|
||||
get_full_client_warmup_data_bitcoinonly,
|
||||
)
|
||||
from app.external.fastapi_versioning import VersionedFastAPI
|
||||
from app.lightning.models import LnInitState
|
||||
from app.lightning.router import router as ln_router
|
||||
from app.lightning.service import initialize_ln_repo, register_lightning_listener
|
||||
from app.setup.router import router as setup_router
|
||||
from app.system.router import router as system_router
|
||||
from app.system.service import get_hardware_info, register_hardware_info_gatherer
|
||||
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
|
||||
|
|
@ -52,13 +57,13 @@ class AppSettings(RedisSettings):
|
|||
unversioned_app = FastAPI()
|
||||
config = get_config()
|
||||
|
||||
unversioned_app.include_router(apps.router)
|
||||
unversioned_app.include_router(bitcoin.router)
|
||||
unversioned_app.include_router(app_router)
|
||||
unversioned_app.include_router(bitcoin_router)
|
||||
if node_type != "none":
|
||||
unversioned_app.include_router(lightning.router)
|
||||
unversioned_app.include_router(system.router)
|
||||
if setup.router is not None:
|
||||
unversioned_app.include_router(setup.router)
|
||||
unversioned_app.include_router(ln_router)
|
||||
unversioned_app.include_router(system_router)
|
||||
if setup_router is not None:
|
||||
unversioned_app.include_router(setup_router)
|
||||
|
||||
|
||||
app = VersionedFastAPI(
|
||||
|
|
|
|||
|
|
@ -1,64 +0,0 @@
|
|||
tx_id_desc = """
|
||||
Unique identifier for this transaction.
|
||||
|
||||
Depending on the type of the transaction it will be different:
|
||||
#### On-chain
|
||||
The transaction hash
|
||||
|
||||
#### Lightning Invoice and Payment
|
||||
The payment request
|
||||
"""
|
||||
|
||||
tx_amount_desc = """
|
||||
The value of the transaction, depending on the category in satoshis or millisatoshis.
|
||||
|
||||
#### On-chain
|
||||
Transaction amount in satoshis
|
||||
|
||||
#### Lightning Invoice
|
||||
* value in millisatoshis of the invoice if *unsettled*
|
||||
* amount in millisatoshis paid if invoice is *settled*
|
||||
|
||||
#### Lightning Payment
|
||||
* amount sent in millisatoshis
|
||||
|
||||
"""
|
||||
|
||||
tx_status_desc = """
|
||||
The status of the transaction. Depending on the transaction category this can be different values:
|
||||
|
||||
May have different meanings in different situations:
|
||||
#### unknown
|
||||
An unknown state was found.
|
||||
|
||||
#### in_flight
|
||||
* A lightning payment is being sent
|
||||
* An invoice is waiting for the incoming payment
|
||||
* An on-chain transaction is waiting in the mempool
|
||||
|
||||
#### succeeded
|
||||
* A lighting payment was successfully sent
|
||||
* An incoming payment was received for an invoice
|
||||
* An on-chain transaction was included in a block
|
||||
|
||||
#### failed
|
||||
* A lightning payment attempt which could not be completed (no route found, insufficient funds, ...)
|
||||
* An invoice is expired or some other error happened
|
||||
"""
|
||||
|
||||
tx_time_stamp_desc = """
|
||||
The unix timestamp in seconds for the transaction.
|
||||
|
||||
The timestamp can mean different things in different situations:
|
||||
|
||||
#### Lightning Invoice
|
||||
* Creation date for in-flight or failed invoices
|
||||
* Settle date for succeeded invoices
|
||||
|
||||
#### On-chain
|
||||
* Creation date for transaction waiting in the mempool
|
||||
* Timestamp of the block where this transaction is included
|
||||
|
||||
#### Lightning Payment
|
||||
|
||||
"""
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
from abc import abstractmethod
|
||||
|
||||
|
||||
class HardwareBase:
|
||||
@abstractmethod
|
||||
async def get_hardware_info(self) -> map:
|
||||
raise NotImplementedError()
|
||||
|
||||
@abstractmethod
|
||||
def get_hardware_info_yield_time(self) -> float:
|
||||
raise NotImplementedError()
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
import time
|
||||
|
||||
from app.core_utils import redis_get
|
||||
from app.repositories.hardware_impl.hardware_base import HardwareBase
|
||||
|
||||
_HW_INFO_YIELD_TIME = 2
|
||||
|
||||
|
||||
class RaspiBlitzHardware(HardwareBase):
|
||||
async def get_hardware_info(self) -> map:
|
||||
info = {}
|
||||
|
||||
loads = (await redis_get("system_cpu_load")).split(",")
|
||||
iloads = []
|
||||
total = 0
|
||||
for l in loads:
|
||||
value = float(l)
|
||||
total += value
|
||||
iloads.append(value)
|
||||
info["cpu_overall_percent"] = round(total / len(loads), 2)
|
||||
info["cpu_per_cpu_percent"] = iloads
|
||||
|
||||
info["vram_total_bytes"] = int(await redis_get("system_ram_mb")) * 1000 * 1000
|
||||
|
||||
info["vram_available_bytes"] = (
|
||||
int(await redis_get("system_ram_available_mb")) * 1000 * 1000
|
||||
)
|
||||
|
||||
info["vram_used_bytes"] = (
|
||||
info["vram_total_bytes"] - info["vram_available_bytes"]
|
||||
)
|
||||
info["vram_usage_percent"] = round(
|
||||
(100 / info["vram_total_bytes"]) * info["vram_used_bytes"], 2
|
||||
)
|
||||
|
||||
info["temperatures_celsius"] = {
|
||||
"system_temp": float(await redis_get("system_temp_celsius")),
|
||||
"coretemp": [],
|
||||
}
|
||||
|
||||
now = time.time()
|
||||
boot = float(await redis_get("system_up"))
|
||||
info["boot_time_timestamp"] = now - boot
|
||||
|
||||
info["networks"] = {
|
||||
"internet_online": await redis_get("internet_online"),
|
||||
"tor_web_addr": await redis_get("tor_web_addr"),
|
||||
"internet_localip": await redis_get("internet_localip"),
|
||||
"internet_localiprange": await redis_get("internet_localiprange"),
|
||||
}
|
||||
|
||||
# the following is just available when setup is done
|
||||
setup_phase = await redis_get("setupPhase")
|
||||
info["disks"] = []
|
||||
if setup_phase == "done":
|
||||
total = int(await redis_get("hdd_capacity_bytes"))
|
||||
free = int(await redis_get("hdd_free_bytes"))
|
||||
info["disks"] = [
|
||||
{
|
||||
"device": "/",
|
||||
"mountpoint": "/",
|
||||
"filesystem_type": "ext4",
|
||||
"partition_total_bytes": total,
|
||||
"partition_used_bytes": total - free,
|
||||
"partition_free_bytes": free,
|
||||
"partition_percent": round((100 / total) * free, 2),
|
||||
}
|
||||
]
|
||||
|
||||
return info
|
||||
|
||||
def get_hardware_info_yield_time(self) -> float:
|
||||
return _HW_INFO_YIELD_TIME
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
from decouple import config
|
||||
|
||||
from app.models.system import APIPlatform
|
||||
|
||||
router = None
|
||||
|
||||
_PLATFORM = config("platform", default=APIPlatform.RASPIBLITZ)
|
||||
if _PLATFORM == APIPlatform.RASPIBLITZ:
|
||||
from app.repositories.setup_impl.raspiblitz.router import router
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
### Setup
|
||||
Since the setup is not highly specific to the underlying platform, we don't provide an abstraction to it like in the other parts of the framework. Instead, the platform should provide a router that is simply attached to the main router in `main.py` by the API.
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
import logging
|
||||
import secrets
|
||||
from typing import Dict
|
||||
|
||||
from decouple import config
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
from app.auth.auth_handler import sign_jwt
|
||||
from app.constants import API_VERSION
|
||||
from app.models.system import (
|
||||
APIPlatform,
|
||||
ConnectionInfo,
|
||||
LoginInput,
|
||||
RawDebugLogData,
|
||||
SystemInfo,
|
||||
)
|
||||
from app.repositories.lightning import get_ln_info
|
||||
from app.repositories.system_impl.system_base import SystemBase
|
||||
|
||||
|
||||
class NativePythonSystem(SystemBase):
|
||||
async def get_system_info(self) -> SystemInfo:
|
||||
lninfo = await get_ln_info()
|
||||
|
||||
version = config("np_version", default="")
|
||||
|
||||
tor_api = config("np_tor_address_api_endpoint", default="")
|
||||
tor_api_docs = config("np_tor_address_api_docs", default="")
|
||||
|
||||
lan_api = config("np_local_address_api_endpoint", default="")
|
||||
lan_api_docs = config("np_local_address_api_docs", default="")
|
||||
|
||||
ssh_address = config("np_ssh_address", default="")
|
||||
|
||||
return SystemInfo(
|
||||
alias=lninfo.alias,
|
||||
color=lninfo.color,
|
||||
platform=APIPlatform.NATIVE_PYTHON,
|
||||
platform_version=version,
|
||||
api_version=API_VERSION,
|
||||
tor_web_ui=tor_api_docs,
|
||||
tor_api=tor_api,
|
||||
lan_web_ui=lan_api_docs,
|
||||
lan_api=lan_api,
|
||||
ssh_address=ssh_address,
|
||||
chain=lninfo.chains[0].network,
|
||||
)
|
||||
|
||||
async def shutdown(self, reboot: bool) -> bool:
|
||||
logging.info("Shutdown / reboot not supported in native_python mode.")
|
||||
return False
|
||||
|
||||
async def get_connection_info(self) -> ConnectionInfo:
|
||||
# return an empty connection info object for now
|
||||
return ConnectionInfo()
|
||||
|
||||
async def login(self, i: LoginInput) -> Dict[str, str]:
|
||||
matches = secrets.compare_digest(i.password, config("login_password", cast=str))
|
||||
if matches:
|
||||
return sign_jwt()
|
||||
|
||||
raise HTTPException(
|
||||
status.HTTP_401_UNAUTHORIZED, detail="Password is incorrect"
|
||||
)
|
||||
|
||||
async def change_password(self, type: str, old_password: str, new_password: str):
|
||||
raise NotImplementedError()
|
||||
|
||||
async def get_debug_logs_raw(self) -> RawDebugLogData:
|
||||
raise NotImplementedError()
|
||||
|
|
@ -1 +0,0 @@
|
|||
from app.repositories.setup import router
|
||||
2
app/setup/README.md
Normal file
2
app/setup/README.md
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
### Setup
|
||||
Since the setup is not highly specific to the underlying platform, we don't provide an abstraction to it like in the other parts of the framework. Instead, the platform implementation itself should provide a router that is simply attached to the main router in `main.py` by the API.
|
||||
3
app/setup/impl/raspiblitz/README.md
Normal file
3
app/setup/impl/raspiblitz/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# TODO
|
||||
|
||||
Split router into `router.py` and `service.py` (or something like that)
|
||||
|
|
@ -4,11 +4,11 @@ from fastapi import APIRouter, HTTPException, status
|
|||
from fastapi.params import Depends
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.api.utils import call_script, parse_key_value_lines, redis_get
|
||||
from app.auth.auth_bearer import JWTBearer
|
||||
from app.auth.auth_handler import sign_jwt
|
||||
from app.core_utils import call_script, parse_key_value_lines, redis_get
|
||||
from app.repositories.system_impl.raspiblitz import RaspiBlitzSystem
|
||||
from app.repositories.utils.raspiblitz import name_valid, password_valid
|
||||
from app.system.impl.raspiblitz import RaspiBlitzSystem
|
||||
from app.system.impl.raspiblitz_utils import name_valid, password_valid
|
||||
|
||||
router = APIRouter(prefix="/setup", tags=["RaspiBlitz Setup"])
|
||||
|
||||
9
app/setup/router.py
Normal file
9
app/setup/router.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
from decouple import config
|
||||
|
||||
from app.system.models import APIPlatform
|
||||
|
||||
router = None
|
||||
|
||||
_PLATFORM = config("platform", default=APIPlatform.RASPIBLITZ)
|
||||
if _PLATFORM != APIPlatform.RASPIBLITZ:
|
||||
from app.setup.impl.raspiblitz.router import router
|
||||
|
|
@ -1,14 +1,79 @@
|
|||
import logging
|
||||
import secrets
|
||||
from typing import Dict
|
||||
|
||||
import psutil
|
||||
from decouple import config
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
from app.repositories.hardware_impl.hardware_base import HardwareBase
|
||||
from app.api.constants import API_VERSION
|
||||
from app.auth.auth_handler import sign_jwt
|
||||
from app.lightning.service import get_ln_info
|
||||
from app.system.impl.system_base import SystemBase
|
||||
from app.system.models import (
|
||||
APIPlatform,
|
||||
ConnectionInfo,
|
||||
LoginInput,
|
||||
RawDebugLogData,
|
||||
SystemInfo,
|
||||
)
|
||||
|
||||
_SLEEP_TIME = config("gather_hw_info_interval", default=2, cast=float)
|
||||
_CPU_AVG_PERIOD = config("cpu_usage_averaging_period", default=0.5, cast=float)
|
||||
_HW_INFO_YIELD_TIME = _SLEEP_TIME + _CPU_AVG_PERIOD
|
||||
|
||||
|
||||
class NativePythonHardware(HardwareBase):
|
||||
class NativePythonSystem(SystemBase):
|
||||
async def get_system_info(self) -> SystemInfo:
|
||||
lninfo = await get_ln_info()
|
||||
|
||||
version = config("np_version", default="")
|
||||
|
||||
tor_api = config("np_tor_address_api_endpoint", default="")
|
||||
tor_api_docs = config("np_tor_address_api_docs", default="")
|
||||
|
||||
lan_api = config("np_local_address_api_endpoint", default="")
|
||||
lan_api_docs = config("np_local_address_api_docs", default="")
|
||||
|
||||
ssh_address = config("np_ssh_address", default="")
|
||||
|
||||
return SystemInfo(
|
||||
alias=lninfo.alias,
|
||||
color=lninfo.color,
|
||||
platform=APIPlatform.NATIVE_PYTHON,
|
||||
platform_version=version,
|
||||
api_version=API_VERSION,
|
||||
tor_web_ui=tor_api_docs,
|
||||
tor_api=tor_api,
|
||||
lan_web_ui=lan_api_docs,
|
||||
lan_api=lan_api,
|
||||
ssh_address=ssh_address,
|
||||
chain=lninfo.chains[0].network,
|
||||
)
|
||||
|
||||
async def shutdown(self, reboot: bool) -> bool:
|
||||
logging.info("Shutdown / reboot not supported in native_python mode.")
|
||||
return False
|
||||
|
||||
async def get_connection_info(self) -> ConnectionInfo:
|
||||
# return an empty connection info object for now
|
||||
return ConnectionInfo()
|
||||
|
||||
async def login(self, i: LoginInput) -> Dict[str, str]:
|
||||
matches = secrets.compare_digest(i.password, config("login_password", cast=str))
|
||||
if matches:
|
||||
return sign_jwt()
|
||||
|
||||
raise HTTPException(
|
||||
status.HTTP_401_UNAUTHORIZED, detail="Password is incorrect"
|
||||
)
|
||||
|
||||
async def change_password(self, type: str, old_password: str, new_password: str):
|
||||
raise NotImplementedError()
|
||||
|
||||
async def get_debug_logs_raw(self) -> RawDebugLogData:
|
||||
raise NotImplementedError()
|
||||
|
||||
async def get_hardware_info(self) -> map:
|
||||
info = {}
|
||||
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
from typing import Dict
|
||||
|
||||
from decouple import config
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
from app.auth.auth_handler import sign_jwt
|
||||
from app.constants import API_VERSION
|
||||
from app.core_utils import (
|
||||
from app.api.constants import API_VERSION
|
||||
from app.api.utils import (
|
||||
SSE,
|
||||
broadcast_sse_msg,
|
||||
call_script,
|
||||
|
|
@ -16,16 +16,19 @@ from app.core_utils import (
|
|||
parse_key_value_text,
|
||||
redis_get,
|
||||
)
|
||||
from app.models.system import (
|
||||
from app.auth.auth_handler import sign_jwt
|
||||
from app.lightning.service import get_ln_info
|
||||
from app.system.impl.raspiblitz_utils import password_valid
|
||||
from app.system.impl.system_base import SystemBase
|
||||
from app.system.models import (
|
||||
APIPlatform,
|
||||
ConnectionInfo,
|
||||
LoginInput,
|
||||
RawDebugLogData,
|
||||
SystemInfo,
|
||||
)
|
||||
from app.repositories.lightning import get_ln_info
|
||||
from app.repositories.system_impl.system_base import SystemBase
|
||||
from app.repositories.utils.raspiblitz import password_valid
|
||||
|
||||
_HW_INFO_YIELD_TIME = 2
|
||||
|
||||
SHELL_SCRIPT_PATH = config("shell_script_path")
|
||||
GET_DEBUG_LOG_SCRIPT = os.path.join(
|
||||
|
|
@ -288,3 +291,68 @@ class RaspiBlitzSystem(SystemBase):
|
|||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"{cmd} returned no error and no output.",
|
||||
)
|
||||
|
||||
async def get_hardware_info(self) -> map:
|
||||
info = {}
|
||||
|
||||
loads = (await redis_get("system_cpu_load")).split(",")
|
||||
iloads = []
|
||||
total = 0
|
||||
for l in loads:
|
||||
value = float(l)
|
||||
total += value
|
||||
iloads.append(value)
|
||||
info["cpu_overall_percent"] = round(total / len(loads), 2)
|
||||
info["cpu_per_cpu_percent"] = iloads
|
||||
|
||||
info["vram_total_bytes"] = int(await redis_get("system_ram_mb")) * 1000 * 1000
|
||||
|
||||
info["vram_available_bytes"] = (
|
||||
int(await redis_get("system_ram_available_mb")) * 1000 * 1000
|
||||
)
|
||||
|
||||
info["vram_used_bytes"] = (
|
||||
info["vram_total_bytes"] - info["vram_available_bytes"]
|
||||
)
|
||||
info["vram_usage_percent"] = round(
|
||||
(100 / info["vram_total_bytes"]) * info["vram_used_bytes"], 2
|
||||
)
|
||||
|
||||
info["temperatures_celsius"] = {
|
||||
"system_temp": float(await redis_get("system_temp_celsius")),
|
||||
"coretemp": [],
|
||||
}
|
||||
|
||||
now = time.time()
|
||||
boot = float(await redis_get("system_up"))
|
||||
info["boot_time_timestamp"] = now - boot
|
||||
|
||||
info["networks"] = {
|
||||
"internet_online": await redis_get("internet_online"),
|
||||
"tor_web_addr": await redis_get("tor_web_addr"),
|
||||
"internet_localip": await redis_get("internet_localip"),
|
||||
"internet_localiprange": await redis_get("internet_localiprange"),
|
||||
}
|
||||
|
||||
# the following is just available when setup is done
|
||||
setup_phase = await redis_get("setupPhase")
|
||||
info["disks"] = []
|
||||
if setup_phase == "done":
|
||||
total = int(await redis_get("hdd_capacity_bytes"))
|
||||
free = int(await redis_get("hdd_free_bytes"))
|
||||
info["disks"] = [
|
||||
{
|
||||
"device": "/",
|
||||
"mountpoint": "/",
|
||||
"filesystem_type": "ext4",
|
||||
"partition_total_bytes": total,
|
||||
"partition_used_bytes": total - free,
|
||||
"partition_free_bytes": free,
|
||||
"partition_percent": round((100 / total) * free, 2),
|
||||
}
|
||||
]
|
||||
|
||||
return info
|
||||
|
||||
def get_hardware_info_yield_time(self) -> float:
|
||||
return _HW_INFO_YIELD_TIME
|
||||
|
|
@ -1,20 +1,5 @@
|
|||
import re
|
||||
|
||||
from decouple import config
|
||||
|
||||
SHELL_SCRIPT_PATH = config("shell_script_path")
|
||||
|
||||
available_app_ids = {
|
||||
"btc-rpc-explorer",
|
||||
"rtl",
|
||||
# Specter is deactivated for now because it uses its own self signed HTTPS cert that makes trouble in Chrome on last test
|
||||
# "specter",
|
||||
"btcpayserver",
|
||||
"lnbits",
|
||||
"mempool",
|
||||
"thunderhub",
|
||||
}
|
||||
|
||||
|
||||
def password_valid(password: str):
|
||||
if len(password) < 8:
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
from abc import abstractmethod
|
||||
from typing import Dict
|
||||
|
||||
from app.models.system import ConnectionInfo, LoginInput, RawDebugLogData, SystemInfo
|
||||
from app.system.models import ConnectionInfo, LoginInput, RawDebugLogData, SystemInfo
|
||||
|
||||
|
||||
class SystemBase:
|
||||
|
|
@ -28,3 +28,11 @@ class SystemBase:
|
|||
@abstractmethod
|
||||
async def get_debug_logs_raw(self) -> RawDebugLogData:
|
||||
raise NotImplementedError()
|
||||
|
||||
@abstractmethod
|
||||
async def get_hardware_info(self) -> map:
|
||||
raise NotImplementedError()
|
||||
|
||||
@abstractmethod
|
||||
def get_hardware_info_yield_time(self) -> float:
|
||||
raise NotImplementedError()
|
||||
|
|
@ -7,7 +7,7 @@ from fastapi.param_functions import Query
|
|||
from pydantic import BaseModel
|
||||
from pydantic.types import constr
|
||||
|
||||
from app.routers.system_docs import get_debug_data_sample_str
|
||||
from app.system.docs import get_debug_data_sample_str
|
||||
|
||||
|
||||
class LoginInput(BaseModel):
|
||||
|
|
@ -3,12 +3,18 @@ from typing import Optional
|
|||
from fastapi import APIRouter, HTTPException, Request, status
|
||||
from fastapi.params import Depends, Query
|
||||
|
||||
from app.api.utils import SSE
|
||||
from app.auth.auth_bearer import JWTBearer
|
||||
from app.auth.auth_handler import sign_jwt
|
||||
from app.core_utils import SSE
|
||||
from app.external.sse_starlette import EventSourceResponse
|
||||
from app.models.system import ConnectionInfo, LoginInput, RawDebugLogData, SystemInfo
|
||||
from app.repositories.system import (
|
||||
from app.system.docs import (
|
||||
get_debug_logs_raw_desc,
|
||||
get_debug_logs_raw_resp_desc,
|
||||
get_debug_logs_raw_summary,
|
||||
get_hw_info_json,
|
||||
)
|
||||
from app.system.models import ConnectionInfo, LoginInput, RawDebugLogData, SystemInfo
|
||||
from app.system.service import (
|
||||
HW_INFO_YIELD_TIME,
|
||||
change_password,
|
||||
get_connection_info,
|
||||
|
|
@ -19,12 +25,6 @@ from app.repositories.system import (
|
|||
shutdown,
|
||||
subscribe_hardware_info,
|
||||
)
|
||||
from app.routers.system_docs import (
|
||||
get_debug_logs_raw_desc,
|
||||
get_debug_logs_raw_resp_desc,
|
||||
get_debug_logs_raw_summary,
|
||||
get_hw_info_json,
|
||||
)
|
||||
|
||||
_PREFIX = "system"
|
||||
|
||||
|
|
@ -4,8 +4,8 @@ from typing import Dict, Optional
|
|||
from decouple import config
|
||||
from fastapi import HTTPException, Request, status
|
||||
|
||||
from app.core_utils import SSE, broadcast_sse_msg
|
||||
from app.models.system import (
|
||||
from app.api.utils import SSE, broadcast_sse_msg
|
||||
from app.system.models import (
|
||||
APIPlatform,
|
||||
ConnectionInfo,
|
||||
LoginInput,
|
||||
|
|
@ -15,20 +15,17 @@ from app.models.system import (
|
|||
|
||||
PLATFORM = config("platform", default=APIPlatform.RASPIBLITZ)
|
||||
if PLATFORM == APIPlatform.RASPIBLITZ:
|
||||
from .hardware_impl.raspiblitz import RaspiBlitzHardware as Hardware
|
||||
from .system_impl.raspiblitz import RaspiBlitzSystem as System
|
||||
from app.system.impl.raspiblitz import RaspiBlitzSystem as System
|
||||
elif PLATFORM == APIPlatform.NATIVE_PYTHON:
|
||||
from .hardware_impl.native_python import NativePythonHardware as Hardware
|
||||
from .system_impl.native_python import NativePythonSystem as System
|
||||
from app.system.impl.native_python import NativePythonSystem as System
|
||||
|
||||
|
||||
system = System()
|
||||
hw = Hardware()
|
||||
|
||||
if hw is None or system is None:
|
||||
if system is None:
|
||||
raise RuntimeError(f"Unknown platform {PLATFORM}")
|
||||
|
||||
HW_INFO_YIELD_TIME = hw.get_hardware_info_yield_time()
|
||||
HW_INFO_YIELD_TIME = system.get_hardware_info_yield_time()
|
||||
|
||||
|
||||
async def change_password(type: Optional[str], old_password: str, new_password: str):
|
||||
|
|
@ -51,7 +48,7 @@ async def get_system_info() -> SystemInfo:
|
|||
|
||||
async def get_hardware_info() -> map:
|
||||
try:
|
||||
return await hw.get_hardware_info()
|
||||
return await system.get_hardware_info()
|
||||
except HTTPException as r:
|
||||
raise
|
||||
except NotImplementedError as r:
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from app.models.bitcoind import *
|
||||
from app.bitcoind.models import *
|
||||
|
||||
blockhain_info = {
|
||||
"chain": "main",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import pytest
|
|||
from pydantic import AnyStrMinLengthError, ValidationError
|
||||
|
||||
import app.repositories.system_impl.native_python as npy
|
||||
from app.models.system import LoginInput
|
||||
from app.system.models import LoginInput
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import pytest
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
import app.repositories.system as sys
|
||||
from app.models.system import LoginInput
|
||||
import app.system.service as sys
|
||||
from app.system.models import LoginInput
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue