Merge pull request #2894 — send Joplin token in Authorization header

# Conflicts:
#	CHANGELOG.md
This commit is contained in:
nesquena-hermes
2026-05-25 01:47:23 +00:00
3 changed files with 37 additions and 3 deletions
+1
View File
@@ -8,6 +8,7 @@
- Auxiliary model settings now reject unknown task slots instead of allowing arbitrary keys under `config.yaml`'s `auxiliary` block. Valid slots and the `__reset__` sentinel continue to work.
- Update Now no longer reports success or enters the restart wait flow when no WebUI or Agent update target is selected.
- Cached WebUI agents no longer overwrite `prefill_messages` with an empty list when a later request does not include explicit prefill context.
- Joplin notes drawer API calls now send the Web Clipper token in an `Authorization` header instead of placing it in the request URL query string.
## [v0.51.132] — 2026-05-24 — Release DD (stage-batch14 — 4-PR replayed-context + interrupted-response + shutdown affordance + passkey opt-in)
+3 -3
View File
@@ -12534,7 +12534,7 @@ def _joplin_connection_from_config() -> tuple[str, str]:
def _joplin_api_get(path: str, params: dict | None = None) -> dict:
"""Call the local Joplin Web Clipper API without logging credentials."""
from urllib.parse import urlencode
from urllib.request import urlopen
from urllib.request import Request, urlopen
from urllib.error import HTTPError, URLError
base_url, token = _joplin_connection_from_config()
@@ -12542,10 +12542,10 @@ def _joplin_api_get(path: str, params: dict | None = None) -> dict:
raise ValueError("Joplin token is not configured")
safe_path = "/" + str(path or "").lstrip("/")
query = dict(params or {})
query["token"] = token
url = f"{base_url}{safe_path}?{urlencode(query)}"
request = Request(url, headers={"Authorization": f"token {token}"})
try:
with urlopen(url, timeout=8) as response:
with urlopen(request, timeout=8) as response:
raw = response.read(2_000_000).decode("utf-8", errors="replace")
except HTTPError as exc:
raise ValueError(f"Joplin API returned HTTP {exc.code}") from None
+33
View File
@@ -132,6 +132,39 @@ def test_joplin_get_note_validates_id_and_truncates_body(monkeypatch):
assert "Preview truncated" in note["body"]
def test_joplin_api_get_uses_authorization_header(monkeypatch):
from api import routes
captured = {}
class FakeResponse:
def __enter__(self):
return self
def __exit__(self, *_exc):
return False
def read(self, _limit):
return b'{"ok": true}'
def fake_urlopen(request, timeout):
captured["url"] = request.full_url
captured["authorization"] = request.get_header("Authorization")
captured["timeout"] = timeout
return FakeResponse()
monkeypatch.setattr(routes, "_joplin_connection_from_config", lambda: ("http://127.0.0.1:41184", "secret-token"))
monkeypatch.setattr("urllib.request.urlopen", fake_urlopen)
data = routes._joplin_api_get("/notes", {"query": "hello world"})
assert data == {"ok": True}
assert captured["timeout"] == 8
assert "token=" not in captured["url"]
assert "query=hello+world" in captured["url"]
assert captured["authorization"] == "token secret-token"
def test_joplin_recent_ai_notes_uses_configured_prefill_script(monkeypatch, tmp_path):
from api import routes