The warmup consumer already avoids the old .dict()-on-HTTPException crash,
but HTTPException/Err results fell through the 'unknown data type' arm,
double-logging and mislabelling known errors. Give them explicit match
arms: HTTPException logs at info (the real error is already logged in
_convert_warmup_exceptions) and Err forwards report.format() instead of
its repr. Error event payload is unchanged ({"error": <string>}).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Move the in-process startup-state singleton out of app.main into
app.api.startup_status so app.system can read it without a circular
import. No behavior change - main mutates the same shared object.
Also lands the design spec and implementation plan for the
system/health endpoint work (#145).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Route HTTPException (app- and framework-raised), RequestValidationError
and uncaught exceptions through build_error_response so every error body
is {detail, error_code, report?, trace?}. Fixes the bare-string body that
string HTTPException details produced, and registers the handler on the
Starlette base HTTPException so framework 404/405/415 also carry the full
envelope.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the /sse/subscribe endpoint with a /ws WebSocket endpoint backed
by ws_mgr, and port the warmup helper (_send_sse_event -> _send_ws_event,
SSE -> Event, broadcast_sse_msg -> broadcast_msg). Also fixes two tests
left over from the prior SSE->Event/broadcast_msg rename that still
referenced the old names and were failing collection/execution.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
get_event_loop() is deprecated on Python 3.11+ when there is no running
loop and is slated to change behaviour further.
- SSEManager.setup() ran at import time (app.api.utils) via
get_event_loop(); this only worked because uvicorn imports the app
inside its loop and would break when imported without a running loop
(e.g. a Celery worker). Start the broadcast consumer lazily from
within a running loop instead.
- everywhere else the pattern was get_event_loop().create_task(x)
inside a coroutine; replace with asyncio.create_task(x).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The WebUI only listens for the app_state_update_message SSE event to
populate the Apps tab. In bitcoin-only mode the warmup data was sent
under installed_app_status, which no client listens to, so the Apps
tab was stuck on the loading screen whenever the app status cache was
warm. Installing LND made it work again because the lightning warmup
path already used the correct event (raspiblitz#3608, raspiblitz#5141).
Also hardens the warmup path:
- reset the warmup_running flag on errors so a single failure no
longer starves all future SSE clients of warmup data
- convert per-source exceptions in the bitcoin-only warmup gather
instead of discarding the whole data set
- don't fall through to the partial-data branches when the API is
fully initialized with lightning disabled
- remove the now-unused INSTALLED_APP_STATUS event and the dead
cached_status_raw variable
Adds regression tests plus a conftest.py providing test env defaults
so the suite runs without a developer .env file.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This is a feature that allows to fetch the app status via a celery
task which stores the result in the Redis database and notifies the API
of the change. The API sends a notification to connected clients via the
SSE mechanism.
Using a background task allows to avoid crashing the whole API if the
script call fails.
The by default the cache is refreshed every 30 minutes. This can be
changed by setting the `BAPI_APP_STATUS_UPDATE_INTERVAL_MIN` environment
variable.
refs #123
If the environment variable BAPI_ENV_PATH is set, the config system will
try to read configs from the given path instead of .env in pwd
If no file is found .env in pwd will be used as a fallback
Env variables will always override settings in .env files.
* return cookie on login and check cookie in sse/subscribe
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* allow cookie & JWT auth for sse/subscribe
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* return token object on login to preserve compatibility
* remove unused import
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
When neither BTC nor LN is running:
send hardware_info
When BTC is running and LN is not:
send hardware_info and btc_info
When both are running:
send the full package
This code doesn't keep track of which connection has received
which data already, so it may send data twice data to the client
when the startup state changes. Especially the hardware info
is rather data intensive. This is OK for now, to keep the code simple.
closes#110
During startup the API will try to connect to Bitcoin Core and the
Lightning Node. If it can't connect it will check every "n" seconds
(currently 2s) and connect when available. A new SSE event
called "system_startup_info" is introduced. This event contains
all startup status information during the startup procedure.
The old wallet_locked event is obsolete.
Sample:
--------------------------
event: system_startup_info
data: {"bitcoin": "offline", "bitcoin_msg": "", "lightning": "offline", "lightning_msg": "Unable to connect to LND daemon, waiting..."}
--------------------------
event: system_startup_info
data: {"bitcoin": "done", "bitcoin_msg": "", "lightning": "offline", "lightning_msg": "Unable to connect to LND daemon, waiting..."}
--------------------------
event: system_startup_info
data: {"bitcoin": "done", "bitcoin_msg": "", "lightning": "locked", "lightning_msg": "Wallet locked, unlock it to enable full RPC access"}
--------------------------
event: system_startup_info
data: {"bitcoin": "done", "bitcoin_msg": "", "lightning": "done", "lightning_msg": ""}
--------------------------
event: system_startup_info
data: {"bitcoin": "done", "bitcoin_msg": "", "lightning": "bootstraping", "lightning_msg": "RPC not yet available"}
--------------------------
refs #97
This really applies only to RaspiBlitz. Depending on the current
setup step there still isn't a Bitcoin Deamon or Lightning Node running.
We must defer the registration of all those handlers until later when
everything is properly setup.
Since there is a final reboot after the setup there is no need to
check in the background whether setup is finished. The API server
is restartet anyway.
refs #71