From d87d9ed59f1ca9f7fb0d1d5d0c8695fb1b11953b Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Mon, 14 Jul 2025 11:43:09 -0400 Subject: [PATCH] Add notifications API endpoint --- .../01-getting-started/02-api_reference.md | 12 +++++++++++- src/seedpass/api.py | 16 ++++++++++++++++ src/tests/test_api_notifications.py | 18 ++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 src/tests/test_api_notifications.py diff --git a/docs/docs/content/01-getting-started/02-api_reference.md b/docs/docs/content/01-getting-started/02-api_reference.md index 9c0e30c..07a23d9 100644 --- a/docs/docs/content/01-getting-started/02-api_reference.md +++ b/docs/docs/content/01-getting-started/02-api_reference.md @@ -31,6 +31,7 @@ Keep this token secret. Every request must include it in the `Authorization` hea - `GET /api/v1/totp/export` – Export all TOTP entries as JSON. - `GET /api/v1/totp` – Return current TOTP codes and remaining time. - `GET /api/v1/stats` – Return statistics about the active seed profile. +- `GET /api/v1/notifications` – Retrieve and clear queued notifications. - `GET /api/v1/parent-seed` – Reveal the parent seed or save it with `?file=`. - `GET /api/v1/nostr/pubkey` – Fetch the Nostr public key for the active seed. - `POST /api/v1/checksum/verify` – Verify the checksum of the running script. @@ -186,7 +187,16 @@ Get profile stats such as entry counts with `GET /api/v1/stats`: ```bash curl -H "Authorization: Bearer " \ - http://127.0.0.1:8000/api/v1/stats + http://127.0.0.1:8000/api/v1/stats +``` + +### Checking Notifications + +Get queued messages with `GET /api/v1/notifications`: + +```bash +curl -H "Authorization: Bearer " \ + http://127.0.0.1:8000/api/v1/notifications ``` ### Changing the Master Password diff --git a/src/seedpass/api.py b/src/seedpass/api.py index 77ad26e..fdc748c 100644 --- a/src/seedpass/api.py +++ b/src/seedpass/api.py @@ -6,6 +6,7 @@ import os import tempfile from pathlib import Path import secrets +import queue from typing import Any, List, Optional from fastapi import FastAPI, Header, HTTPException, Request, Response @@ -379,6 +380,21 @@ def get_profile_stats(authorization: str | None = Header(None)) -> dict: return _pm.get_profile_stats() +@app.get("/api/v1/notifications") +def get_notifications(authorization: str | None = Header(None)) -> List[dict]: + """Return and clear queued notifications.""" + _check_token(authorization) + assert _pm is not None + notes = [] + while True: + try: + note = _pm.notifications.get_nowait() + except queue.Empty: + break + notes.append({"level": note.level, "message": note.message}) + return notes + + @app.get("/api/v1/parent-seed") def get_parent_seed( authorization: str | None = Header(None), file: str | None = None diff --git a/src/tests/test_api_notifications.py b/src/tests/test_api_notifications.py new file mode 100644 index 0000000..06cc9a1 --- /dev/null +++ b/src/tests/test_api_notifications.py @@ -0,0 +1,18 @@ +from test_api import client +from types import SimpleNamespace +import queue +import seedpass.api as api + + +def test_notifications_endpoint(client): + cl, token = client + api._pm.notifications = queue.Queue() + api._pm.notifications.put(SimpleNamespace(message="m1", level="INFO")) + api._pm.notifications.put(SimpleNamespace(message="m2", level="WARNING")) + res = cl.get("/api/v1/notifications", headers={"Authorization": f"Bearer {token}"}) + assert res.status_code == 200 + assert res.json() == [ + {"level": "INFO", "message": "m1"}, + {"level": "WARNING", "message": "m2"}, + ] + assert api._pm.notifications.empty()