From e94b5be5f9fd2d10f25f58c852729ea9045d9a21 Mon Sep 17 00:00:00 2001 From: Alexopus Date: Sat, 13 Dec 2025 14:48:25 +0100 Subject: [PATCH] Add server-side proxy for Danbooru wiki fetches to avoid CSP violations --- __init__.py | 47 +++++++++++++++++++++++++++++++++++++++++++ web/js/booruTagApi.js | 21 ++++++++++++++----- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/__init__.py b/__init__.py index 873862f..c5a08c9 100644 --- a/__init__.py +++ b/__init__.py @@ -4,6 +4,7 @@ from pathlib import Path import folder_paths from server import PromptServer from aiohttp import web +import aiohttp from .src.config_reader import ConfigReader from .src.save_preview_image import SavePreviewImage @@ -130,3 +131,49 @@ async def serve_image(request): if img_path and Path(img_path).exists(): return web.FileResponse(path=img_path) return web.Response(status=404, text="Image not found") + + +@PromptServer.instance.routes.get(f"{API_PREFIX}/wiki/{{tag}}") +async def fetch_wiki_page(request): + """ + Proxy endpoint to fetch Danbooru wiki pages. + This avoids CSP violations by making the request server-side. + """ + tag = request.match_info["tag"] + if not tag: + return web.Response(status=400, text="Tag is required") + + # Clean the tag similar to frontend logic + tag = tag.strip() + + url = f"https://danbooru.donmai.us/wiki_pages/{tag}" + + try: + async with aiohttp.ClientSession() as session: + async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as response: + if response.status != 200: + return web.json_response({ + "success": False, + "error": { + "status": response.status, + "message": f"HTTP error! status: {response.status}", + "url": url, + } + }, status=response.status) + + html_content = await response.text() + return web.json_response({ + "success": True, + "data": html_content + }) + except aiohttp.ClientError as e: + return web.json_response({ "success": False, "error": { "message": str(e), "url": url, } + }, status=500) + except Exception as e: + return web.json_response({ + "success": False, + "error": { + "message": f"Unexpected error: {str(e)}", + "url": url, + } + }, status=500) diff --git a/web/js/booruTagApi.js b/web/js/booruTagApi.js index dbacbef..3717081 100644 --- a/web/js/booruTagApi.js +++ b/web/js/booruTagApi.js @@ -2,6 +2,8 @@ export class BooruApi { constructor() { this.baseUrl = "https://danbooru.donmai.us"; this.wikiUrl = `${this.baseUrl}/wiki_pages`; + // Use proxy endpoint to avoid CSP violations + this.proxyUrl = "/SyntaxHighlighting/wiki"; this.parser = new DOMParser(); this.fetchCache = {}; @@ -22,7 +24,7 @@ export class BooruApi { return this.fetchCache[url]; } - const { responseType = "json" } = options; + const { responseType = "json", useProxy = false } = options; try { const response = await fetch(url); if (!response.ok) { @@ -37,7 +39,14 @@ export class BooruApi { } let data; - if (responseType === "html") { + if (useProxy) { + // When using proxy, the response is already JSON with {success, data} structure + const jsonResponse = await response.json(); + if (!jsonResponse.success) { + return jsonResponse; + } + data = jsonResponse.data; + } else if (responseType === "html") { data = await response.text(); } else { data = await response.json(); @@ -68,12 +77,14 @@ export class BooruApi { } async getTagDescription(tag) { - const url = `${this.wikiUrl}/${this.cleanTag(tag)}`; - const response = await this.#fetch(url, { responseType: "html" }); + const cleanedTag = this.cleanTag(tag); + // Use proxy endpoint to avoid CSP violations + const url = `${this.proxyUrl}/${cleanedTag}`; + const response = await this.#fetch(url, { responseType: "html", useProxy: true }); if (!response.success) { return null; } const body = this.getElementById(response.data, "wiki-page-body"); - return body.querySelector("p").textContent; + return body?.querySelector("p")?.textContent || null; } }