From f94d094b7cc320def48c3ba249f8bb9c20750908 Mon Sep 17 00:00:00 2001 From: Clawd Date: Tue, 14 Jul 2026 05:08:19 -0500 Subject: [PATCH] Fix #264: Attach signed headers + API key to remaining keyless Skaldleita call sites api_book_detail, api_author_detail, api_series_detail, and identify_ebook_from_filename still called the Skaldleita API with no headers, guaranteeing 401s against the key-or-401 gate. Each now resolves the API key the same way sibling code does (config / load_secrets with BOOKDB_PUBLIC_KEY fallback) and sends get_signed_headers() + X-API-Key. Version bumped to 0.9.0-beta.154. --- CHANGELOG.md | 5 +++++ app.py | 28 +++++++++++++++++++++------- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9087539..3376809 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to Library Manager will be documented in this file. +## [0.9.0-beta.154] - 2026-07-14 + +### Fixed +- **#264: Auth headers on all Skaldleita API calls** — Added signed headers + API key to 4 call sites that previously sent keyless requests to the Skaldleita API: `api_book_detail`, `api_author_detail`, `api_series_detail`, and `identify_ebook_from_filename`. These endpoints now require authentication after the gate was changed to key-or-401. + ## [0.9.0-beta.153] - 2026-06-12 ### Security diff --git a/app.py b/app.py index 6b53fb0..c1bcd52 100644 --- a/app.py +++ b/app.py @@ -11,7 +11,7 @@ - Multi-provider AI (Gemini, OpenRouter, Ollama) """ -APP_VERSION = "0.9.0-beta.153" +APP_VERSION = "0.9.0-beta.154" GITHUB_REPO = "deucebucket/library-manager" # Your GitHub repo # Versioning Guide: @@ -2295,9 +2295,14 @@ def identify_ebook_from_filename(filename, folder_path, config): search_query = f"{author} {title}" if author else title logger.debug(f"[EBOOK] Searching BookDB for: {search_query}") + api_key = config.get('bookdb_api_key') or BOOKDB_PUBLIC_KEY + headers = get_signed_headers() or {} + headers['X-API-Key'] = api_key + resp = requests.get( f"{BOOKDB_API_URL}/search", params={'q': search_query[:100]}, # Limit query length + headers=headers, timeout=10 ) @@ -11902,11 +11907,14 @@ def api_book_detail(book_id): """ Get full book details from BookBucket + ABS status. Used for hover cards and detail modals. - Uses public endpoint - no API key required. """ try: # Fetch full book details from BookBucket - resp = requests.get(f"{BOOKDB_API_URL}/book/{book_id}", timeout=10) + secrets = load_secrets() + api_key = secrets.get('bookdb_api_key') or BOOKDB_PUBLIC_KEY + headers = get_signed_headers() or {} + headers['X-API-Key'] = api_key + resp = requests.get(f"{BOOKDB_API_URL}/book/{book_id}", headers=headers, timeout=10) if resp.status_code != 200: return jsonify({'error': f'Book not found (status {resp.status_code})'}) @@ -11977,10 +11985,13 @@ def api_author_detail(author_id): """ Get author details from BookBucket. Used for hover cards on author search results. - Uses public endpoint - no API key required. """ try: - resp = requests.get(f"{BOOKDB_API_URL}/author/{author_id}", timeout=10) + secrets = load_secrets() + api_key = secrets.get('bookdb_api_key') or BOOKDB_PUBLIC_KEY + headers = get_signed_headers() or {} + headers['X-API-Key'] = api_key + resp = requests.get(f"{BOOKDB_API_URL}/author/{author_id}", headers=headers, timeout=10) if resp.status_code != 200: return jsonify({'error': f'Author not found (status {resp.status_code})'}) @@ -12000,10 +12011,13 @@ def api_series_detail(series_id): """ Get series details from BookBucket. Used for hover cards on series search results. - Uses public endpoint - no API key required. """ try: - resp = requests.get(f"{BOOKDB_API_URL}/series/{series_id}", timeout=10) + secrets = load_secrets() + api_key = secrets.get('bookdb_api_key') or BOOKDB_PUBLIC_KEY + headers = get_signed_headers() or {} + headers['X-API-Key'] = api_key + resp = requests.get(f"{BOOKDB_API_URL}/series/{series_id}", headers=headers, timeout=10) if resp.status_code != 200: return jsonify({'error': f'Series not found (status {resp.status_code})'})