2026-07-11 12:22:31 +02:00
|
|
|
from fastapi import APIRouter, HTTPException, Response, status
|
2022-10-19 21:32:47 +02:00
|
|
|
from fastapi.params import Depends, Query
|
2022-03-20 17:20:56 +01:00
|
|
|
|
2026-07-11 12:00:13 +02:00
|
|
|
from app.api.utils import Event
|
2021-06-10 21:13:26 +02:00
|
|
|
from app.auth.auth_bearer import JWTBearer
|
2022-03-23 21:03:50 +01:00
|
|
|
from app.auth.auth_handler import sign_jwt
|
2022-10-03 20:22:00 +02:00
|
|
|
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,
|
|
|
|
|
)
|
2024-02-20 19:56:11 +01:00
|
|
|
from app.system.models import (
|
2026-07-03 21:42:25 +02:00
|
|
|
ChangePasswordInput,
|
2024-02-20 19:56:11 +01:00
|
|
|
ConnectionInfo,
|
|
|
|
|
LoginInput,
|
|
|
|
|
RawDebugLogData,
|
|
|
|
|
SystemHealthInfo,
|
2024-04-01 19:41:18 +00:00
|
|
|
SystemInfo,
|
2024-02-20 19:56:11 +01:00
|
|
|
)
|
2022-10-03 20:22:00 +02:00
|
|
|
from app.system.service import (
|
2022-09-11 10:33:12 +02:00
|
|
|
change_password,
|
2022-05-18 18:20:13 +02:00
|
|
|
get_connection_info,
|
2021-09-20 11:22:50 +02:00
|
|
|
get_debug_logs_raw,
|
2021-09-05 08:56:53 +02:00
|
|
|
get_hardware_info,
|
2021-09-05 20:23:59 +02:00
|
|
|
get_system_info,
|
2022-08-24 20:56:40 +02:00
|
|
|
login,
|
2022-03-24 20:17:56 +01:00
|
|
|
shutdown,
|
2024-02-20 19:56:11 +01:00
|
|
|
system_health,
|
2021-09-05 08:56:53 +02:00
|
|
|
)
|
2021-06-10 21:13:26 +02:00
|
|
|
|
2021-09-26 21:57:55 +02:00
|
|
|
_PREFIX = "system"
|
|
|
|
|
|
|
|
|
|
router = APIRouter(prefix=f"/{_PREFIX}", tags=["System"])
|
2021-06-10 21:13:26 +02:00
|
|
|
|
|
|
|
|
|
2021-09-05 08:56:53 +02:00
|
|
|
@router.post(
|
|
|
|
|
"/login",
|
2021-09-26 21:57:55 +02:00
|
|
|
name=f"{_PREFIX}.login",
|
2021-09-05 08:56:53 +02:00
|
|
|
summary="Logs the user in with the current password",
|
|
|
|
|
response_description="JWT token for the current session.",
|
|
|
|
|
status_code=status.HTTP_200_OK,
|
|
|
|
|
)
|
2022-10-22 11:08:54 +02:00
|
|
|
async def login_path(i: LoginInput, response: Response):
|
2022-03-23 00:52:37 +01:00
|
|
|
try:
|
2022-10-22 11:08:54 +02:00
|
|
|
token = await login(i)
|
|
|
|
|
response.set_cookie("access_token", token)
|
|
|
|
|
return token
|
2023-05-17 16:02:28 +02:00
|
|
|
except HTTPException:
|
2022-08-24 20:56:40 +02:00
|
|
|
raise
|
|
|
|
|
except NotImplementedError as r:
|
|
|
|
|
raise HTTPException(status.HTTP_501_NOT_IMPLEMENTED, detail=r.args[0])
|
2021-06-10 21:13:26 +02:00
|
|
|
|
2022-03-23 21:03:50 +01:00
|
|
|
|
2021-11-19 18:32:30 +01:00
|
|
|
@router.post(
|
|
|
|
|
"/refresh-token",
|
|
|
|
|
name=f"{_PREFIX}.refresh-token",
|
|
|
|
|
summary="Endpoint to refresh an authentication token",
|
|
|
|
|
response_description="Returns a fresh JWT token.",
|
|
|
|
|
dependencies=[Depends(JWTBearer())],
|
|
|
|
|
)
|
|
|
|
|
def refresh_token():
|
2022-03-23 21:03:50 +01:00
|
|
|
return sign_jwt()
|
2021-11-19 18:32:30 +01:00
|
|
|
|
|
|
|
|
|
2022-05-09 19:32:47 +02:00
|
|
|
@router.post(
|
|
|
|
|
"/change-password",
|
|
|
|
|
name=f"{_PREFIX}.change-password",
|
2022-09-11 10:33:12 +02:00
|
|
|
summary="Endpoint to change your password",
|
2022-05-09 19:32:47 +02:00
|
|
|
response_description="if 200 OK - password change worked",
|
|
|
|
|
dependencies=[Depends(JWTBearer())],
|
|
|
|
|
)
|
2026-07-03 21:42:25 +02:00
|
|
|
async def change_password_impl(data: ChangePasswordInput):
|
|
|
|
|
return await change_password(
|
|
|
|
|
data.type, data.old_password, data.new_password
|
|
|
|
|
)
|
2022-05-09 19:32:47 +02:00
|
|
|
|
2022-05-18 18:20:13 +02:00
|
|
|
|
2021-09-05 20:23:59 +02:00
|
|
|
@router.get(
|
2021-09-26 21:57:55 +02:00
|
|
|
"/get-system-info",
|
|
|
|
|
name=f"{_PREFIX}.get-system-info",
|
2021-09-05 20:23:59 +02:00
|
|
|
summary="Get system status information",
|
|
|
|
|
dependencies=[Depends(JWTBearer())],
|
|
|
|
|
response_model=SystemInfo,
|
2021-11-24 11:12:10 +01:00
|
|
|
responses={
|
|
|
|
|
423: {"description": "Wallet is locked. Unlock via /lightning/unlock-wallet"}
|
|
|
|
|
},
|
2021-09-05 20:23:59 +02:00
|
|
|
)
|
|
|
|
|
async def get_system_info_path():
|
2021-11-24 11:12:10 +01:00
|
|
|
try:
|
|
|
|
|
return await get_system_info()
|
2023-05-17 16:02:28 +02:00
|
|
|
except HTTPException:
|
2021-11-24 11:12:10 +01:00
|
|
|
raise
|
|
|
|
|
except NotImplementedError as r:
|
|
|
|
|
raise HTTPException(status.HTTP_501_NOT_IMPLEMENTED, detail=r.args[0])
|
2021-09-05 20:23:59 +02:00
|
|
|
|
|
|
|
|
|
2021-09-05 08:56:53 +02:00
|
|
|
@router.get(
|
2021-09-26 21:57:55 +02:00
|
|
|
"/hardware-info",
|
|
|
|
|
name=f"{_PREFIX}.hardware-info",
|
2021-09-05 08:56:53 +02:00
|
|
|
summary="Get hardware status information.",
|
|
|
|
|
response_description="Returns a JSON string with hardware information:\n"
|
|
|
|
|
+ get_hw_info_json,
|
|
|
|
|
dependencies=[Depends(JWTBearer())],
|
|
|
|
|
status_code=status.HTTP_200_OK,
|
|
|
|
|
)
|
2023-03-12 16:33:41 +01:00
|
|
|
async def hw_info():
|
2021-12-15 21:11:46 +01:00
|
|
|
return await get_hardware_info()
|
2021-06-12 15:07:35 +02:00
|
|
|
|
2022-05-18 18:20:13 +02:00
|
|
|
|
2022-05-12 19:49:01 +02:00
|
|
|
@router.get(
|
|
|
|
|
"/connection-info",
|
|
|
|
|
name=f"{_PREFIX}.connection-info",
|
2022-06-04 14:46:03 +02:00
|
|
|
summary="Get credential information to connect external apps.",
|
2022-05-12 19:49:01 +02:00
|
|
|
response_description="Returns a JSON string with credential information.",
|
|
|
|
|
response_model=ConnectionInfo,
|
|
|
|
|
dependencies=[Depends(JWTBearer())],
|
|
|
|
|
status_code=status.HTTP_200_OK,
|
|
|
|
|
)
|
2023-03-12 16:33:41 +01:00
|
|
|
async def connection_info():
|
2022-05-12 19:49:01 +02:00
|
|
|
return await get_connection_info()
|
2021-06-12 15:07:35 +02:00
|
|
|
|
2022-05-18 18:20:13 +02:00
|
|
|
|
2021-09-20 11:22:50 +02:00
|
|
|
@router.get(
|
2021-09-26 21:57:55 +02:00
|
|
|
"/get-debug-logs-raw",
|
|
|
|
|
name=f"{_PREFIX}.get-debug-logs-raw",
|
2021-09-20 11:22:50 +02:00
|
|
|
summary=get_debug_logs_raw_summary,
|
|
|
|
|
description=get_debug_logs_raw_desc,
|
|
|
|
|
response_description=get_debug_logs_raw_resp_desc,
|
|
|
|
|
response_model=RawDebugLogData,
|
|
|
|
|
dependencies=[Depends(JWTBearer())],
|
|
|
|
|
)
|
|
|
|
|
async def get_debug_logs_raw_route() -> RawDebugLogData:
|
|
|
|
|
return await get_debug_logs_raw()
|
|
|
|
|
|
|
|
|
|
|
2024-02-20 19:56:11 +01:00
|
|
|
@router.get(
|
|
|
|
|
"/health",
|
|
|
|
|
name=f"{_PREFIX}.health",
|
2026-07-12 17:11:04 +02:00
|
|
|
summary="Returns info about the system's health",
|
|
|
|
|
response_model=SystemHealthInfo,
|
|
|
|
|
responses={503: {"description": "One or more subsystems are not ready"}},
|
2024-02-20 19:56:11 +01:00
|
|
|
)
|
|
|
|
|
async def get_system_health(
|
2026-07-12 17:11:04 +02:00
|
|
|
response: Response,
|
2024-02-20 19:56:11 +01:00
|
|
|
verbose: bool = Query(
|
|
|
|
|
False,
|
2026-07-12 17:11:04 +02:00
|
|
|
description="If true, include a per-subsystem (api, bitcoind, lightning) health breakdown.",
|
2024-02-20 19:56:11 +01:00
|
|
|
),
|
|
|
|
|
) -> SystemHealthInfo:
|
2026-07-12 17:11:04 +02:00
|
|
|
result = await system_health(verbose)
|
|
|
|
|
if not result.healthy:
|
|
|
|
|
response.status_code = status.HTTP_503_SERVICE_UNAVAILABLE
|
|
|
|
|
return result
|
2024-02-20 19:56:11 +01:00
|
|
|
|
|
|
|
|
|
2021-09-05 08:56:53 +02:00
|
|
|
@router.post(
|
|
|
|
|
"/reboot",
|
2021-09-26 21:57:55 +02:00
|
|
|
name=f"{_PREFIX}.reboot",
|
2021-09-05 08:56:53 +02:00
|
|
|
summary="Reboots the system",
|
2022-03-24 20:17:56 +01:00
|
|
|
description=f"""Attempts to reboot the system.
|
2026-07-11 12:00:13 +02:00
|
|
|
Will send a `{Event.SYSTEM_REBOOT_NOTICE}` SSE message immediately to
|
2022-03-24 20:17:56 +01:00
|
|
|
all connected clients.
|
|
|
|
|
""",
|
|
|
|
|
response_description=f"""True if successful. False on failure.
|
2026-07-11 12:00:13 +02:00
|
|
|
A failure will also send an error message with id `{Event.SYSTEM_REBOOT_ERROR}`
|
2022-03-24 20:17:56 +01:00
|
|
|
to all connected clients.
|
|
|
|
|
""",
|
2021-09-05 08:56:53 +02:00
|
|
|
dependencies=[Depends(JWTBearer())],
|
|
|
|
|
)
|
2022-03-23 20:20:58 +01:00
|
|
|
async def reboot_system() -> bool:
|
2022-07-23 13:59:38 +02:00
|
|
|
return await shutdown(reboot=True)
|
2021-06-12 15:07:35 +02:00
|
|
|
|
|
|
|
|
|
2021-09-05 08:56:53 +02:00
|
|
|
@router.post(
|
|
|
|
|
"/shutdown",
|
2021-09-26 21:57:55 +02:00
|
|
|
name=f"{_PREFIX}.shutdown",
|
2021-09-05 08:56:53 +02:00
|
|
|
summary="Shuts the system down",
|
2022-03-24 20:17:56 +01:00
|
|
|
description=f"""Attempts to shutdown the system.
|
2026-07-11 12:00:13 +02:00
|
|
|
Will send a `{Event.SYSTEM_SHUTDOWN_NOTICE}` SSE message immediately to all
|
2022-03-24 20:17:56 +01:00
|
|
|
connected clients.
|
|
|
|
|
""",
|
|
|
|
|
response_description=f"""True if successful. False on failure.
|
2026-07-11 12:00:13 +02:00
|
|
|
A failure will also send an error message with id {Event.SYSTEM_SHUTDOWN_ERROR}
|
2022-03-24 20:17:56 +01:00
|
|
|
to all connected clients.
|
|
|
|
|
""",
|
2021-09-05 08:56:53 +02:00
|
|
|
dependencies=[Depends(JWTBearer())],
|
|
|
|
|
)
|
2022-03-24 20:17:56 +01:00
|
|
|
async def shutdown_path() -> bool:
|
|
|
|
|
return await shutdown(False)
|