From 9a2cce4d854be4f1b0d4d7340d084e24ab238c90 Mon Sep 17 00:00:00 2001 From: Soxoj <31013580+soxoj@users.noreply.github.com> Date: Mon, 25 May 2026 18:53:18 +0200 Subject: [PATCH] Add Cloudflare bypass (FlareSolverr) support to web UI + docs (#2700) --- docs/source/features.rst | 4 + .../locale/zh_CN/LC_MESSAGES/features.po | 198 ++++++------ .../locale/zh_CN/LC_MESSAGES/settings.po | 303 ++++++++++-------- docs/source/settings.rst | 38 ++- maigret/web/app.py | 15 + tests/test_web.py | 81 +++++ 6 files changed, 401 insertions(+), 238 deletions(-) diff --git a/docs/source/features.rst b/docs/source/features.rst index 51aac34..e58968d 100644 --- a/docs/source/features.rst +++ b/docs/source/features.rst @@ -292,6 +292,10 @@ Activate the bypass either with the CLI flag:: maigret --cloudflare-bypass or by setting ``cloudflare_bypass.enabled`` to ``true`` in ``settings.json``. +The web UI (``python -m maigret.web.app``) reads the same setting — there is +no separate toggle in the form, so flipping ``enabled`` is what activates +the bypass for browser-driven runs. + The bypass only fires for sites whose ``protection`` field intersects ``cloudflare_bypass.trigger_protection`` (default ``["cf_js_challenge", "cf_firewall", "webgate"]``); all other sites use the diff --git a/docs/source/locale/zh_CN/LC_MESSAGES/features.po b/docs/source/locale/zh_CN/LC_MESSAGES/features.po index 8826486..183b1d1 100644 --- a/docs/source/locale/zh_CN/LC_MESSAGES/features.po +++ b/docs/source/locale/zh_CN/LC_MESSAGES/features.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: Maigret 0.6\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-05-23 09:20+0200\n" +"POT-Creation-Date: 2026-05-25 18:02+0200\n" "PO-Revision-Date: 2026-05-22 21:00+0200\n" "Last-Translator: \n" "Language: zh_CN\n" @@ -17,86 +17,85 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.18.0\n" -#: ../../source/features.rst:4 c5c6b9457ad64524a5e85fce2a39162e +#: ../../source/features.rst:4 eca41ada7a9f4545800a2d110503a14a msgid "Features" msgstr "特性" -#: ../../source/features.rst:6 db9219d1ce9f4d65ac3e8bbf94388511 +#: ../../source/features.rst:6 b24b658bc3bd4954af7bd8307d12dd42 msgid "This is the list of Maigret features." msgstr "以下是 Maigret 的特性列表。" -#: ../../source/features.rst:11 4e6480fbc44c46a29ba7733626bbb429 +#: ../../source/features.rst:11 8d83920098174365a4969a42ba7ceac6 msgid "Web Interface" msgstr "Web 界面" -#: ../../source/features.rst:13 d8e360bd959d4492879ccbe03cdb12f1 +#: ../../source/features.rst:13 5957db1bd94f4d7ab214b92c06d67e73 msgid "" "You can run Maigret with a web interface, where you can view the graph " "with results and download reports of all formats on a single page." msgstr "你可以以 Web 界面的方式运行 Maigret —— 在同一个页面里查看结果图谱、并下载所有格式的报告。" -#: ../../source/features.rst:16 276bbc14f19943adacaa55e2ad826c3c +#: ../../source/features.rst:16 7631566c1cfd402b80ba138921568711 msgid "Web interface: how to start" msgstr "Web 界面:启动页" -#: ../../source/features.rst:20 e870c6257bce4c6584b0fb369f2e153e +#: ../../source/features.rst:20 deda308a1ecb4354a06c77ae44d74f64 msgid "Web interface: results" msgstr "Web 界面:结果页" -#: ../../source/features.rst:24 f01248c0e652464fb715af3d02c7526e +#: ../../source/features.rst:24 035b97ee389442efa904d58e7675819d msgid "Instructions:" msgstr "使用步骤:" -#: ../../source/features.rst:26 3b4eb508b7f04665902a64c8b72ed143 +#: ../../source/features.rst:26 7d8d5e3519cd45f8996e3901d59b4745 msgid "Run Maigret with the ``--web`` flag and specify the port number." msgstr "使用 ``--web`` 参数运行 Maigret,并指定端口号。" -#: ../../source/features.rst:32 98ddaf990579422ba19473f5c3281d12 +#: ../../source/features.rst:32 f99c4e96a1794fdebbca1eb8de276e7e msgid "" "Open http://127.0.0.1:5000 in your browser and enter one or more " "usernames to make a search." msgstr "在浏览器中打开 http://127.0.0.1:5000,输入一个或多个用户名以发起搜索。" -#: ../../source/features.rst:34 9919b43f50d247a09834623389e7ff21 +#: ../../source/features.rst:34 d7e33c3780724f95bf553821485840b8 msgid "" "Wait a bit for the search to complete and view the graph with results, " "the table with all accounts found, and download reports of all formats." msgstr "稍等片刻搜索完成后,即可查看结果图谱、列出全部已发现账号的表格,并下载所有格式的报告。" -#: ../../source/features.rst:39 34b07aff9d134c4386412945cf1fb0dc +#: ../../source/features.rst:39 20f45c4b369b419e883745ee1d31152f msgid "Telegram bot" msgstr "Telegram 机器人" -#: ../../source/features.rst:41 ed98e98c542e493eb41515251fa9c79f +#: ../../source/features.rst:41 06eb5827613b479583d051a01176b920 msgid "" "A community-maintained Telegram bot lets you run Maigret without " "installing anything locally." -msgstr "" -"社区维护的一个 Telegram 机器人让你无需在本地安装就能运行 Maigret。" +msgstr "社区维护的一个 Telegram 机器人让你无需在本地安装就能运行 Maigret。" -#: ../../source/features.rst:44 5ae5c3250cb442f9bffc21d41afbb5ee +#: ../../source/features.rst:44 7465f025cb9141fbb92af0576662eccc msgid "" "Working instance: `sites.google.com/view/maigret-bot-link " "`_ (redirect — the hosted" " bot may move between providers)." msgstr "" -"在线实例:`sites.google.com/view/maigret-bot-link `_\\ (跳转链接 —— 托管实例可能会在不同服务商" -"之间迁移)。" +"在线实例:`sites.google.com/view/maigret-bot-link " +"`_\\ (跳转链接 —— " +"托管实例可能会在不同服务商之间迁移)。" -#: ../../source/features.rst:47 1fa5f0bc407a4c359d6041a67d1b3cec +#: ../../source/features.rst:47 eb79c93a7e8b4de4a207568ffdd52b35 msgid "" "Source code: `github.com/soxoj/maigret-tg-bot `_." msgstr "" -"源代码:`github.com/soxoj/maigret-tg-bot `_。" +"源代码:`github.com/soxoj/maigret-tg-bot `_。" -#: ../../source/features.rst:51 96f177df09cc44baa106d38c929bdfda +#: ../../source/features.rst:51 f83b69f1fcba4abeb66a12a711d151f1 msgid "Personal info gathering" msgstr "个人信息收集" -#: ../../source/features.rst:53 0a261aa82b064f3f9ecf8e5bd1ddd850 +#: ../../source/features.rst:53 2913acff61f8466e80dc03285b75c54b msgid "" "Maigret does the `parsing of accounts webpages and extraction " "`_ of personal info, links to " @@ -108,15 +107,15 @@ msgstr "" "个人信息、指向其它主页的链接等内容。抽取结果会以附加信息的形式出现在 CLI 输出中,并在 HTML 和 PDF " "报告中以表格呈现。此外,Maigret 还会用从链接中发现的 ID 和用户名,启动递归搜索。" -#: ../../source/features.rst:57 2cadf584fa4c4b918a60b0f9cbc0cfd8 +#: ../../source/features.rst:57 774c604cfa0d48ac83687ae40bfb8098 msgid "Enabled by default, can be disabled with ``--no extracting``." msgstr "默认启用,可通过 ``--no extracting`` 关闭。" -#: ../../source/features.rst:83 c84af4829ecf4e3ca0e8ee7b41545291 +#: ../../source/features.rst:83 3e586217eafe436fbd566dc44751ae72 msgid "Recursive search" msgstr "递归搜索" -#: ../../source/features.rst:85 62f83ca52efa42e1abfab667eaa1c3e8 +#: ../../source/features.rst:85 98a5a5561deb463ca15440072947bd04 msgid "" "Maigret has the ability to scan account pages for :ref:`common " "identifiers ` and usernames found in links. " @@ -129,15 +128,15 @@ msgstr "" "和链接中出现的用户名。当用户在主页里贴出指向其它社交账号的链接时,Maigret " "会自动识别并对这些账号发起新的搜索。在此过程中发现的全部信息,都会同时显示在命令行输出和生成的报告中。" -#: ../../source/features.rst:89 88d27a1d758f4ce5a9438650388db000 +#: ../../source/features.rst:89 305f7071bf074687aaca8511531ea186 msgid "Enabled by default, can be disabled with ``--no-recursion``." msgstr "默认启用,可通过 ``--no-recursion`` 关闭。" -#: ../../source/features.rst:122 17dec64423374faba021419561ffbd67 +#: ../../source/features.rst:122 a1ec13a8b3fc4091a9d9aad8fc4536e2 msgid "Username permutations" msgstr "用户名变体生成" -#: ../../source/features.rst:124 fbddaa705f9d4d43b8f753c671a7c06f +#: ../../source/features.rst:124 e4c00d35553049a9b73c4203e77df5f2 msgid "" "Maigret can generate permutations of usernames. Just pass a few usernames" " in the CLI and use ``--permute`` flag. Thanks to `@balestek " @@ -146,49 +145,49 @@ msgstr "" "Maigret 可以生成用户名的各种变体组合。只需在命令行传入几个用户名并加上 ``--permute`` 参数即可。感谢 `@balestek " "`_ 提供的想法和实现。" -#: ../../source/features.rst:149 8e73737273224c5db83f5c26f301e909 +#: ../../source/features.rst:149 9dad12744e69420aa749fcf646f5fc8f msgid "Reports" msgstr "报告" -#: ../../source/features.rst:151 3a1366d6fa6549eb87c68ed60154f956 +#: ../../source/features.rst:151 e70802a82a1f4c7d978558399b52c3ed msgid "" "Maigret currently supports HTML, PDF, TXT, XMind 8 mindmap, and JSON " "reports." msgstr "Maigret 目前支持 HTML、PDF、TXT、XMind 8 思维导图和 JSON 等格式的报告。" -#: ../../source/features.rst:153 e4c9654ef05c4431bddbfe36654d5950 +#: ../../source/features.rst:153 f81c37a3ae254c37a69c1e8fad6a3fa0 msgid "HTML/PDF reports contain:" msgstr "HTML / PDF 报告包含以下内容:" -#: ../../source/features.rst:155 3154eb754c154af1bfdc278a7129ff13 +#: ../../source/features.rst:155 4c8b2dab41734e20b0266c76890bcd89 msgid "profile photo" msgstr "头像" -#: ../../source/features.rst:156 fc3c96fc9c744f849c07f1d00d716fc3 +#: ../../source/features.rst:156 c642caf9ffcf4136ae84e93354e19313 msgid "all the gathered personal info" msgstr "已收集到的全部个人信息" -#: ../../source/features.rst:157 16214e18017942f8966cd55273911183 +#: ../../source/features.rst:157 9744f896e29f47a09724faa36f4f126b msgid "" "additional information about supposed personal data (full name, gender, " "location), resulting from statistics of all found accounts" msgstr "基于所有已发现账号的统计,推测出的额外个人数据(真实姓名、性别、所在地)" -#: ../../source/features.rst:159 0a6ebe62f3b1499d9ae636affffe0baa +#: ../../source/features.rst:159 cdfdc97558fa4c278e462f6bcf9c2fd1 msgid "" "Also, there is a short text report in the CLI output after the end of a " "searching phase." msgstr "此外,在搜索结束后,CLI 输出中也会附上一份简短的文本报告。" -#: ../../source/features.rst:162 af16c71f5d2a4415804baf234cb0d5d0 +#: ../../source/features.rst:162 1389dbc9efba49b499252807bb1eb12b msgid "XMind 8 mindmaps are incompatible with XMind 2022!" msgstr "XMind 8 思维导图与 XMind 2022 不兼容!" -#: ../../source/features.rst:165 b806d9cc087444cb9d4b4d0f6224bd02 +#: ../../source/features.rst:165 7855ecf44a5a4139ac24cfac44f659ff msgid "AI analysis" msgstr "AI 分析" -#: ../../source/features.rst:167 d23580679b6d4eed8e4c09d117242bcf +#: ../../source/features.rst:167 a7bc7ddbf02841a7acf6773a6aec39b5 msgid "" "Maigret can produce a short, human-readable investigation summary on top " "of the raw search results using the ``--ai`` flag. It builds the internal" @@ -198,7 +197,7 @@ msgstr "" "通过 ``--ai`` 参数,Maigret 可以基于原始搜索结果生成一份简短、易读的调查摘要。它会先构建一份内部 Markdown " "报告,将其发送至一个 OpenAI 兼容的 chat completion 接口,并把模型的回复以流式方式直接输出到终端。" -#: ../../source/features.rst:178 53834075a9c847eca357f109a7cf8ed1 +#: ../../source/features.rst:178 47a99e86b6ae4a1588e09ef7fd44ea0c msgid "" "The summary uses a fixed format with the most likely real name, location," " occupation, interests, languages, main website, username variants, " @@ -210,7 +209,7 @@ msgstr "" "该摘要采用固定格式,包含最可能的真实姓名、所在地、职业、兴趣、语言、主要使用的网站、用户名变体、出现过的平台数量、活跃年份、置信度评级,以及一份简短的后续线索清单。\\" " ``--ai`` 启用期间,逐站点进度和简短文本报告都会被抑制,以让这份流式摘要成为主要输出。" -#: ../../source/features.rst:185 d2ca79ef0cbb4c0db9dd7a9dc033e670 +#: ../../source/features.rst:185 85733bb1fa11414dba02e6f0627cb59e msgid "" "The endpoint, model, and API key are configured via ``settings.json`` " "(``openai_api_key``, ``openai_model``, ``openai_api_base_url``) or the " @@ -223,11 +222,11 @@ msgstr "" "环境变量进行配置。可使用任意 OpenAI 兼容接口(Azure OpenAI、OpenRouter、本地推理服务等)。详情参见 :ref" ":`ai-analysis` 与 :ref:`settings`\\ 。" -#: ../../source/features.rst:192 581b6b0436ae4a5781eeb486e6588f26 +#: ../../source/features.rst:192 8bba3181c9ec4009badf240909ff5ba2 msgid "Tags" msgstr "标签" -#: ../../source/features.rst:194 b3e0b1b4404e4830aeb52cee70add04d +#: ../../source/features.rst:194 ac119f2a446745e19e1e0fc51f4ca717 msgid "" "The Maigret sites database very big (and will be bigger), and it is maybe" " an overhead to run a search for all the sites. Also, it is often hard to" @@ -237,22 +236,22 @@ msgstr "" "Maigret " "的站点数据库已经很庞大(并且还会继续扩张),对所有站点都跑一遍搜索未必划算。同时,在调查某个具体人物时,也不容易判断哪些站点更值得关注。" -#: ../../source/features.rst:197 de59a8e8f58446629fb73e95a3ee98d3 +#: ../../source/features.rst:197 c159b2fb30c0405d83ac51d2f7501c02 msgid "" "Tags markup allows selecting a subset of sites by interests (photo, " "messaging, finance, etc.) or by country. Tags of found accounts grouped " "and displayed in the reports." msgstr "标签体系允许按兴趣(摄影、即时通讯、金融等)或国家挑选站点子集。已发现账号上的标签会被分组,并显示在报告中。" -#: ../../source/features.rst:199 6a15bc91037e4e5382b9777dd48b216c +#: ../../source/features.rst:199 ed4a4e7541064e3687afe3712d64d77d msgid "See full description :doc:`in the Tags Wiki page `." msgstr "完整说明请参见 :doc:`标签页 `\\ 。" -#: ../../source/features.rst:202 899b6de94b0d42b1b81f46c4f37537df +#: ../../source/features.rst:202 96dc25e63f79489ba87542fdedd8123f msgid "Censorship and captcha detection" msgstr "审查和 CAPTCHA 检测" -#: ../../source/features.rst:204 e58d04de05374755928b836321eaf876 +#: ../../source/features.rst:204 c05b9f998478422b9c480a2191f383dd #, python-format msgid "" "Maigret can detect common errors such as censorship stub pages, " @@ -263,25 +262,25 @@ msgstr "" "Maigret 能够识别一些常见错误,例如审查占位页、CloudFlare 验证码页等等。如果在一次会话中某类错误超过了 3%,CLI " "输出里就会打印一条警告,并附上用于改善表现、规避问题的建议。" -#: ../../source/features.rst:208 244105ddbc1a4938adff1cdec3eacacd +#: ../../source/features.rst:208 ad8458e0de4a4b019049b57af0ee2460 msgid "Retries" msgstr "重试" -#: ../../source/features.rst:210 9057491007a24db09461ba415c21082c +#: ../../source/features.rst:210 64853719d00a4542a5a3c4e33e5fda82 msgid "" "Maigret will do retries of the requests with temporary errors got " "(connection failures, proxy errors, etc.)." msgstr "对于遇到临时性错误(连接失败、代理错误等)的请求,Maigret 会进行重试。" -#: ../../source/features.rst:212 f271dec6df984fd1bdc0c82c5cfb19e8 +#: ../../source/features.rst:212 712591ee78254a809879ad313f69220f msgid "One attempt by default, can be changed with option ``--retries N``." msgstr "默认尝试一次,可通过 ``--retries N`` 参数调整。" -#: ../../source/features.rst:215 a60841b531994f95830885b0002746e6 +#: ../../source/features.rst:215 e20338540d6847bda34963bb81d8db8f msgid "Database self-check" msgstr "数据库自检" -#: ../../source/features.rst:217 799c17e506ad4192af4c506619da7710 +#: ../../source/features.rst:217 37eb44c6abd440708fbbbd72785d0d9d msgid "" "Maigret includes a self-check mode (``--self-check``) that validates " "every site in the database by looking up its known-claimed and known-" @@ -291,7 +290,7 @@ msgstr "" "Maigret 内置了一个自检模式(``--self-" "check``),它会针对数据库中每个站点,分别查询已知被占用和已知未被占用的用户名,并验证检测结果是否符合预期。" -#: ../../source/features.rst:221 e79e8d1013f74190b47ff0ec7eb216d9 +#: ../../source/features.rst:221 d1e875423487428381e6bc8fa17867bd msgid "" "The self-check is **error-resilient**: if an individual site check raises" " an unexpected exception (e.g. a network error or a parsing failure), the" @@ -304,7 +303,7 @@ msgstr "" "**容错的**:如果某个站点的检查抛出意外异常(例如网络错误或解析失败),该错误会被捕获并记录为一个问题项,而其余站点会继续被检查,不会被中断。因此,即便使用" " ``-a --self-check`` 检查上百个站点,该流程也总能顺利跑完。" -#: ../../source/features.rst:227 61c6a70829654399ae672d4be9703860 +#: ../../source/features.rst:227 d75031d71d3941528aa337fbf56cbc8d msgid "" "Use ``--auto-disable`` together with ``--self-check`` to automatically " "disable sites that fail checks. Without it, issues are only reported. Use" @@ -315,21 +314,21 @@ msgstr "" "可以把检查失败的站点自动禁用;不加该参数时,问题项只会被报告出来,而不会真正禁用。使用 ``--diagnose`` " "则可以打印每个站点的详细诊断信息,包括检查类型、具体问题和修复建议。" -#: ../../source/features.rst:244 32d1a694a4894df589b223cadb2b50a8 +#: ../../source/features.rst:244 97ad227189b34c2b8e80b9055dd0f48e msgid "Archives and mirrors checking" msgstr "归档站与镜像站检查" -#: ../../source/features.rst:246 ab9928e811e14c719e0c3ffe5e6b09b5 +#: ../../source/features.rst:246 cab6bb0dc9e34379ae002ec46981c079 msgid "" "The Maigret database contains not only the original websites, but also " "mirrors, archives, and aggregators. For example:" msgstr "Maigret 数据库中不仅包含原始网站,还包含镜像、归档和聚合站点。例如:" -#: ../../source/features.rst:248 2d146741016c460f88b7fd07fc2898f2 +#: ../../source/features.rst:248 500d9a939ca14b2a981b1c9435cdcd73 msgid "`Picuki `_, Instagram mirror" msgstr "`Picuki `_,Instagram 镜像" -#: ../../source/features.rst:249 ebb266ec7008400790a3c21e1d53932b +#: ../../source/features.rst:249 53f6db7eb7044cf294720e6239282686 msgid "" "(no longer available) `Reddit BigData search `_" @@ -337,22 +336,22 @@ msgstr "" "(已不再可用)\\ `Reddit BigData search `_" -#: ../../source/features.rst:250 16c765fe6684414486f0c25dacfb9431 +#: ../../source/features.rst:250 60124ff7e1e244d79c62846abb0cabbe msgid "(no longer available) `Twitter shadowban `_ checker" msgstr "(已不再可用)\\ `Twitter shadowban `_ 检查器" -#: ../../source/features.rst:252 dd8a821fe2cd406eb2f803dd06d15733 +#: ../../source/features.rst:252 44ebab596acb41229bbe2ea989503b85 msgid "" "It allows getting additional info about the person and checking the " "existence of the account even if the main site is unavailable (bot " "protection, captcha, etc.)" msgstr "通过这些站点,可以获取关于目标人物的额外信息;即便主站点不可用(机器人防护、CAPTCHA 等),也仍能验证账号是否存在。" -#: ../../source/features.rst:257 ad050645ea194a0c8959fc56a0444251 +#: ../../source/features.rst:257 ff0ab7835ec44e148d82397581bb4290 msgid "Cloudflare webgate bypass" msgstr "Cloudflare webgate 绕过" -#: ../../source/features.rst:261 8c65965e0e1842ee98ca9b1e5a8a6898 +#: ../../source/features.rst:261 99737687941844dfae996c262a4b1a85 msgid "" "**Experimental feature.** The Cloudflare webgate is under active " "development. The configuration schema, CLI flag behaviour, and the set of" @@ -363,7 +362,7 @@ msgstr "" "**实验性特性。** Cloudflare webgate 正处于积极开发阶段。配置项、CLI " "参数行为以及经由其路由的站点集合都可能发生变化,不保证向后兼容。你可能会遇到一些不完善之处(CF 限速、求解器偶尔失败等),欢迎反馈以便逐步打磨。" -#: ../../source/features.rst:267 b878aff525094a388b38de75fd85a4b6 +#: ../../source/features.rst:267 d67e2b25a2544422861e89866939e627 msgid "" "Some sites sit behind a full Cloudflare JavaScript challenge or a CF " "firewall hard block — these are tagged ``protection: " @@ -375,7 +374,7 @@ msgstr "" "``protection: [\"cf_js_challenge\"]`` 或 ``protection: [\"cf_firewall\"]``" " 标记,并通常被禁用,因为 aiohttp 和 curl_cffi 都无法自行解出 JS 挑战。" -#: ../../source/features.rst:272 1100a4e9984f4157856ac897203dd9f3 +#: ../../source/features.rst:272 887722f5e6c9401fb789e6ada2e62b2c msgid "" "Maigret can offload these checks to a local Chrome-based solver. Two " "backends are supported, configured in ``settings.json`` under " @@ -385,7 +384,7 @@ msgstr "" "Maigret 可以把这类检查转交给本地的 Chrome 类求解器。目前支持两种后端,通过 ``settings.json`` 中的 " "``cloudflare_bypass.modules`` 配置(第一个能连通的模块生效,后续模块作为兜底链依次尝试):" -#: ../../source/features.rst:277 7cc6522b5c0d492db9e63f7491da3cea +#: ../../source/features.rst:277 603de15a7c18429a94037f714af3e69f msgid "" "**FlareSolverr** (recommended). Runs a real Chrome instance and exposes a" " JSON API. The upstream HTTP status, headers and final URL are preserved," @@ -396,7 +395,7 @@ msgstr "" "状态码、响应头和最终 URL 都会被保留,因此 ``checkType: status_code`` 和 ``checkType: " "response_url`` 在绕过模式下依然能正常工作。" -#: ../../source/features.rst:286 8586101867fc4191baf5f5bf1a9878bb +#: ../../source/features.rst:286 54d61596bbce43e8a82cb1141379a276 msgid "" "**CloudflareBypassForScraping** (legacy fallback). Returns rendered HTML " "only, so the upstream status code is lost — ``checkType: message`` keeps " @@ -405,24 +404,33 @@ msgstr "" "**CloudflareBypassForScraping**\\ (旧版兜底)。只返回渲染后的 HTML,因此上游真实状态码会丢失 —— " "``checkType: message`` 仍然可用,但 ``status_code`` 类检查会失准(成功时会被视为 200)。" -#: ../../source/features.rst:290 204a36006bb14825a7acbbe54faf8f0c +#: ../../source/features.rst:290 d6f5bd6b3ac043e1a9153827b0492c00 msgid "Activate the bypass either with the CLI flag::" msgstr "启用绕过有两种方式,一是命令行参数::" -#: ../../source/features.rst:294 0285b3904ee545c59536eb8566191c95 +#: ../../source/features.rst:294 8a9237637b414665bc1a30722539d650 msgid "" "or by setting ``cloudflare_bypass.enabled`` to ``true`` in " -"``settings.json``. The bypass only fires for sites whose ``protection`` " -"field intersects ``cloudflare_bypass.trigger_protection`` (default " -"``[\"cf_js_challenge\", \"cf_firewall\", \"webgate\"]``); all other sites" -" use the normal aiohttp / curl_cffi path." +"``settings.json``. The web UI (``python -m maigret.web.app``) reads the " +"same setting — there is no separate toggle in the form, so flipping " +"``enabled`` is what activates the bypass for browser-driven runs." msgstr "" -"二是在 ``settings.json`` 中将 ``cloudflare_bypass.enabled`` 设为 ``true``\\ " -"。该绕过只对 ``protection`` 字段与 ``cloudflare_bypass.trigger_protection``\\ (默认 " -"``[\"cf_js_challenge\", \"cf_firewall\", " -"\"webgate\"]``)有交集的站点生效;其它站点仍走常规的 aiohttp / curl_cffi 路径。" +"或在 ``settings.json`` 中将 ``cloudflare_bypass.enabled`` 设为 ``true``。" +"Web 界面(``python -m maigret.web.app``)读取的是同一份配置 —— 表单中" +"没有单独的开关,因此切换 ``enabled`` 就是浏览器场景下启用绕过的唯一方式。" -#: ../../source/features.rst:300 60fdeed8e1c8458bb584e5abe4c524a5 +#: ../../source/features.rst:299 8cd7c8ef31e845cfa7bef77caa8b4a47 +msgid "" +"The bypass only fires for sites whose ``protection`` field intersects " +"``cloudflare_bypass.trigger_protection`` (default ``[\"cf_js_challenge\"," +" \"cf_firewall\", \"webgate\"]``); all other sites use the normal aiohttp" +" / curl_cffi path." +msgstr "" +"该绕过只对 ``protection`` 字段与 ``cloudflare_bypass.trigger_protection``" +"\\ (默认 ``[\"cf_js_challenge\", \"cf_firewall\", \"webgate\"]``)有交集" +"的站点生效;其它站点仍走常规的 aiohttp / curl_cffi 路径。" + +#: ../../source/features.rst:304 98d59ab2376c44fc89adf426a612366d msgid "" "If all configured modules are unreachable, affected sites get an UNKNOWN " "status with an actionable error pointing at the first module's URL — the " @@ -431,7 +439,7 @@ msgstr "" "如果所有配置的模块都不可达,受影响的站点会被标记为 UNKNOWN,并附上一条可操作的错误信息,指向首个模块的 URL —— " "绝大多数情况下,只需把 FlareSolverr 容器跑起来就能解决。" -#: ../../source/features.rst:304 79fdbe6283274bb39d174b5b06f30251 +#: ../../source/features.rst:308 2ee4a6ee33f847ce87472cd442bcd18e msgid "" "FlareSolverr session reuse is automatic: Maigret pins a single ``session:" " -`` per run, so cf_clearance cookies are shared " @@ -442,60 +450,60 @@ msgstr "" "-``,这样对同一域名的多次检查就会共享 cf_clearance cookie(后续对该主机的请求会快" " 5–10 倍)。" -#: ../../source/features.rst:310 50e0d736536e431ca335765c839c23a2 +#: ../../source/features.rst:314 3f43bfb6c70e48e285a7cc862d7988e6 msgid "Activation" msgstr "激活机制" -#: ../../source/features.rst:311 0cae44249d6243bf929b4c6ee402df8b +#: ../../source/features.rst:315 f3fbc5aaaadc475cb27e6050ed392c72 msgid "" "The activation mechanism helps make requests to sites requiring " "additional authentication like cookies, JWT tokens, or custom headers." msgstr "激活机制用于向那些需要额外认证(cookie、JWT token、自定义请求头等)的站点发起请求。" -#: ../../source/features.rst:313 7e2bcbdc83f84cb183bc15bed13a315d +#: ../../source/features.rst:317 8904ac193b06468aa8c2a0eac03387f0 msgid "It works by implementing a custom function that:" msgstr "其实现方式是通过一个自定义函数,完成以下三步:" -#: ../../source/features.rst:315 96d50906bb3f4fd38eb20ceeca581706 +#: ../../source/features.rst:319 9ff5a4ab39284a55aed726ed75aebe6e msgid "Makes a specialized HTTP request to a specific website endpoint" msgstr "向某个特定网站端点发起一个专门的 HTTP 请求" -#: ../../source/features.rst:316 7ac14f5c64c6423d9a677013143ab93b +#: ../../source/features.rst:320 c96edc4ff8654074b71ce45af7fa84c1 msgid "Processes the response" msgstr "处理响应内容" -#: ../../source/features.rst:317 f570a98e68314397a84ad404efa6adb0 +#: ../../source/features.rst:321 418b7eb1dd824175b3fad35d3ea9d33c msgid "Updates the headers/cookies for that site in the local Maigret database" msgstr "在本地 Maigret 数据库中更新该站点对应的请求头/cookie" -#: ../../source/features.rst:319 d399ee0fa3a3466792a51a418f6bc12a +#: ../../source/features.rst:323 30f2c88b051546539456ce0c98957a82 msgid "" "Since activation only triggers after encountering specific errors, a " "retry (or another Maigret run) is needed to obtain a valid response with " "the updated authentication." msgstr "由于激活只在遇到特定错误后才会触发,因此要拿到使用新认证信息的有效响应,还需要再触发一次重试(或重新运行一次 Maigret)。" -#: ../../source/features.rst:321 a8c2255fea80429b8d50db28a5e5c482 +#: ../../source/features.rst:325 9da1a6a8d5d848e7a9987a6e864d6da5 msgid "" "The activation mechanism is enabled by default, and cannot be disabled at" " the moment." msgstr "激活机制默认启用,目前无法关闭。" -#: ../../source/features.rst:323 92b566a8ed624f179a1bf92f92276078 +#: ../../source/features.rst:327 0573ae8856364e819e56931340bc6fb7 msgid "See for more details in Development section :ref:`activation-mechanism`." msgstr "更多细节请参见开发文档中的 :ref:`activation-mechanism` 一节。" -#: ../../source/features.rst:328 07d22d7c68294f60bb9338352c0f5317 +#: ../../source/features.rst:332 efe6f17c461a44e099b06d44a597b5b2 msgid "Extraction of information from account pages" msgstr "从账号页面抽取信息" -#: ../../source/features.rst:330 d6278ffe262349838922c615cb64b539 +#: ../../source/features.rst:334 c820edcca1184d4382c8a57eded6cc47 msgid "" "Maigret can parse URLs and content of web pages by URLs to extract info " "about account owner and other meta information." msgstr "Maigret 可以根据给定 URL 解析网页地址及其内容,抽取账号所有者及其它元信息。" -#: ../../source/features.rst:332 0cf168608d0e41e0a5cf39f1be547372 +#: ../../source/features.rst:336 540ba0732e5348e0838f7cdafcdee244 msgid "" "You must specify the URL with the option ``--parse``, it's can be a link " "to an account or an online document. List of supported sites `see here " @@ -504,7 +512,7 @@ msgstr "" "需要使用 ``--parse`` 选项指定 URL,可以是某个账号的链接,也可以是在线文档的链接。支持的站点列表请\\ `查看此处 " "`_。" -#: ../../source/features.rst:334 2f78bb887c3c432890a69b15f6c37470 +#: ../../source/features.rst:338 7db26f80206f49e4ac99971c8829c8ef msgid "" "After the end of the parsing phase, Maigret will start the search phase " "by :doc:`supported identifiers ` found " @@ -513,11 +521,11 @@ msgstr "" "解析阶段结束后,Maigret 会根据从中找到的 :doc:`受支持标识符 `\\ " "(用户名、ID 等)进入搜索阶段。" -#: ../../source/features.rst:362 2946ce8afc394246b8f780f51bd86b19 +#: ../../source/features.rst:366 a1bd40f9e8a24be2b76cb9fbdb3c03e1 msgid "Simple API" msgstr "简单 API" -#: ../../source/features.rst:364 6483467ea4d146148ede1d9d71ce727c +#: ../../source/features.rst:368 c032748ecb4847ecb5590a4fa6629ba0 msgid "" "Maigret can be easily integrated with the use of Python package `maigret " "`_." @@ -525,7 +533,7 @@ msgstr "" "借助 Python 包 `maigret `_,可以很方便地把 " "Maigret 集成到自己的项目中。" -#: ../../source/features.rst:366 a12e865e64e54d4d949680aa3db2d51e +#: ../../source/features.rst:370 115fa7827cea4dc68871f2a516999f7d msgid "" "Example: the community `Telegram bot `_" diff --git a/docs/source/locale/zh_CN/LC_MESSAGES/settings.po b/docs/source/locale/zh_CN/LC_MESSAGES/settings.po index 02ad029..7c95b9e 100644 --- a/docs/source/locale/zh_CN/LC_MESSAGES/settings.po +++ b/docs/source/locale/zh_CN/LC_MESSAGES/settings.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: Maigret 0.6\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-05-22 20:48+0200\n" +"POT-Creation-Date: 2026-05-25 18:02+0200\n" "PO-Revision-Date: 2026-05-22 21:00+0200\n" "Last-Translator: \n" "Language: zh_CN\n" @@ -17,296 +17,308 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.18.0\n" -#: ../../source/settings.rst:4 c390d741629a40d28a83af5434f8cbeb +#: ../../source/settings.rst:4 fe76c47d0aa1445c891f588fa40b146c msgid "Settings" msgstr "配置" -#: ../../source/settings.rst:7 85c1b59773954c879b7ec2854d9fec0f +#: ../../source/settings.rst:7 53c34f2bdb7d4be498b654113d7d6231 msgid "The settings system is under development and may be subject to change." msgstr "配置系统仍在开发中,后续可能发生变化。" -#: ../../source/settings.rst:9 ddb12727febb4a9ba5511f6c53f5f040 +#: ../../source/settings.rst:9 4d4fd09541ce4883bfcdc64d646caf16 msgid "" "Options are also configurable through settings files. See `settings JSON " "file " "`_" " for the list of currently supported options." msgstr "" -"各项选项也可以通过配置文件进行设置。当前支持的完整选项列表请参见 `settings JSON 文件 `_。" +"各项选项也可以通过配置文件进行设置。当前支持的完整选项列表请参见 `settings JSON 文件 " +"`_。" -#: ../../source/settings.rst:13 f7de8fdd9407425d9164de1696eb9a2c +#: ../../source/settings.rst:13 0c0a9cd3c69b41c1b577466ebc33e36d msgid "" "After start Maigret tries to load configuration from the following " "sources in exactly the same order:" -msgstr "" -"启动后,Maigret 会\ **按以下顺序**\ 依次尝试从这些位置加载配置:" +msgstr "启动后,Maigret 会\\ **按以下顺序**\\ 依次尝试从这些位置加载配置:" -#: ../../source/settings.rst:26 75bc73e45f7a4b41a13de7b62f03e710 +#: ../../source/settings.rst:26 15ad0a7667b246b081ad08074d479d8d msgid "" "Missing any of these files is not an error. If the next settings file " "contains already known option, this option will be rewrited. So it is " "possible to make custom configuration for different users and " "directories." -msgstr "" -"上述文件中任何一个不存在都不算错误。如果后一个配置文件包含了已经出现过的" -"选项,该选项就会被覆盖。因此你可以针对不同用户和不同目录设置不同的自定义" -"配置。" +msgstr "上述文件中任何一个不存在都不算错误。如果后一个配置文件包含了已经出现过的选项,该选项就会被覆盖。因此你可以针对不同用户和不同目录设置不同的自定义配置。" -#: ../../source/settings.rst:34 3b458bc88d694d769ab609dd698a840a +#: ../../source/settings.rst:34 018bbabf2f414554a59579239aa70d06 msgid "Database auto-update" msgstr "数据库自动更新" -#: ../../source/settings.rst:36 e538967a9d634e48bd61d789ba23e6f3 +#: ../../source/settings.rst:36 652f6ed1943a4bbf95290f956eb7b28a msgid "" "Maigret ships with a bundled site database, but it gets outdated between " "releases. To keep the database current, Maigret automatically checks for " "updates on startup." -msgstr "" -"Maigret 自带一份站点数据库,但它会随着新版本发布而逐渐过时。为了保持数据库" -"是最新的,Maigret 在启动时会自动检查更新。" +msgstr "Maigret 自带一份站点数据库,但它会随着新版本发布而逐渐过时。为了保持数据库是最新的,Maigret 在启动时会自动检查更新。" -#: ../../source/settings.rst:38 d324c3899a824d87b25596a4d57435ca +#: ../../source/settings.rst:38 c9681e2aab35468e9af30b1b684301a0 msgid "**How it works:**" msgstr "**工作机制:**" -#: ../../source/settings.rst:40 f8e9ee638abc4864b6af65c023e3e5b8 +#: ../../source/settings.rst:40 def91bc9f8e84dcebce7e2fa602ae0f5 msgid "" "On startup, Maigret checks if more than 24 hours have passed since the " "last update check." -msgstr "" -"启动时,Maigret 会检查距离上次更新检查是否已超过 24 小时。" +msgstr "启动时,Maigret 会检查距离上次更新检查是否已超过 24 小时。" -#: ../../source/settings.rst:41 e38f9607201a4475996b5005112e1c0e +#: ../../source/settings.rst:41 dfab33e815164c86a4ebe0e7e78ed6ee msgid "" "If so, it fetches a lightweight metadata file (~200 bytes) from GitHub to" " see if a newer database is available." -msgstr "" -"如果超过了,就从 GitHub 拉取一份轻量元数据文件(约 200 字节),查看是否有" -"更新版本的数据库。" +msgstr "如果超过了,就从 GitHub 拉取一份轻量元数据文件(约 200 字节),查看是否有更新版本的数据库。" -#: ../../source/settings.rst:42 baf3af441dc74128a2b754ccbffa5f4e +#: ../../source/settings.rst:42 209b0131bfdd48c2b68e7088c2cb6014 msgid "" "If a newer, compatible database exists, Maigret downloads it to " "``~/.maigret/data.json`` and uses it instead of the bundled copy." -msgstr "" -"如果存在更新且兼容的数据库,Maigret 会把它下载到 " -"``~/.maigret/data.json``,并用它替代内置的版本。" +msgstr "如果存在更新且兼容的数据库,Maigret 会把它下载到 ``~/.maigret/data.json``,并用它替代内置的版本。" -#: ../../source/settings.rst:43 95539bb0b235488baf6edfac2a6fbbc8 +#: ../../source/settings.rst:43 d8aa5049e4ee40929e746b6f5b4c6be8 msgid "" "If the download fails or the new database is incompatible with your " "Maigret version, the bundled database is used as a fallback." -msgstr "" -"如果下载失败,或新数据库与你的 Maigret 版本不兼容,就回退到内置数据库。" +msgstr "如果下载失败,或新数据库与你的 Maigret 版本不兼容,就回退到内置数据库。" -#: ../../source/settings.rst:45 6f412fc0cf87469c8742681587d1dd29 +#: ../../source/settings.rst:45 26696d84d93342dfbd89b6ca614f29a5 msgid "" "The downloaded database has **higher priority** than the bundled one — it" " replaces, not overlays." -msgstr "" -"下载下来的数据库\ **优先级高于**\ 内置数据库 —— 它是替换关系,而不是叠加。" +msgstr "下载下来的数据库\\ **优先级高于**\\ 内置数据库 —— 它是替换关系,而不是叠加。" -#: ../../source/settings.rst:47 fe13cb78d21944ee86c63919042591c8 +#: ../../source/settings.rst:47 93ce61948c464569a7efdd07ae269fb2 msgid "**Status messages** are printed only when an action occurs:" -msgstr "**状态消息**\ 仅在实际发生某种动作时才会打印:" +msgstr "**状态消息**\\ 仅在实际发生某种动作时才会打印:" -#: ../../source/settings.rst:56 24e10dd219b6407ebda94bd3bf48b162 +#: ../../source/settings.rst:56 0845742ea7ed4b44a6194a7a9f8faca2 msgid "**Forcing an update:**" msgstr "**强制更新:**" -#: ../../source/settings.rst:58 872077b7b1164f87b59d385a170f7317 +#: ../../source/settings.rst:58 ee8fcb44764949dcb422d9523f203837 msgid "" "Use the ``--force-update`` flag to check for updates immediately, " "ignoring the check interval:" -msgstr "" -"使用 ``--force-update`` 参数会忽略检查间隔,立刻检查更新:" +msgstr "使用 ``--force-update`` 参数会忽略检查间隔,立刻检查更新:" -#: ../../source/settings.rst:64 1d278924d02a4df49e5532f9d97ef0e2 +#: ../../source/settings.rst:64 abcac12715f6447283a54b1d21eb3f26 msgid "" "The update happens at startup, then the search continues normally with " "the freshly downloaded database." -msgstr "" -"更新只在启动时进行,完成后即用新下载的数据库继续执行正常搜索。" +msgstr "更新只在启动时进行,完成后即用新下载的数据库继续执行正常搜索。" -#: ../../source/settings.rst:66 bb9d37cbb1c34d4e92363252d84e48ec +#: ../../source/settings.rst:66 8dfdb9ddb94742d6916accf6a637b621 msgid "**Disabling auto-update:**" msgstr "**禁用自动更新:**" -#: ../../source/settings.rst:68 10eaed158bc94a3086a3dabfb00b86b2 +#: ../../source/settings.rst:68 e63059a385e042989dbffc8faa6620be msgid "Use the ``--no-autoupdate`` flag to skip the update check entirely:" msgstr "使用 ``--no-autoupdate`` 参数可以完全跳过更新检查:" -#: ../../source/settings.rst:74 13eb1b9bc7ff4414a1a561fbff005cfb +#: ../../source/settings.rst:74 4538e0b662e74117ac60feb580ea14cf msgid "Or set it permanently in ``~/.maigret/settings.json``:" msgstr "或者在 ``~/.maigret/settings.json`` 中永久设置:" -#: ../../source/settings.rst:82 37c3a67aa7724ec5849c2f4cfc4b76d4 +#: ../../source/settings.rst:82 01fb39cf9f6648cb818421edd235e9bd msgid "" "This is recommended for **Docker containers**, **CI pipelines**, and " "**air-gapped environments**." -msgstr "" -"该选项推荐用于 **Docker 容器**\ 、\ **CI 流水线**\ 以及\ **离线隔离环境**\ 。" +msgstr "该选项推荐用于 **Docker 容器**\\ 、\\ **CI 流水线**\\ 以及\\ **离线隔离环境**\\ 。" -#: ../../source/settings.rst:84 a168f36ac5f14c979cec5dc24a649245 +#: ../../source/settings.rst:84 4d8f3a1e5313475ab522313771b11096 msgid "**Configuration options** (in ``settings.json``):" -msgstr "**配置项**\ (位于 ``settings.json``):" +msgstr "**配置项**\\ (位于 ``settings.json``):" -#: ../../source/settings.rst:90 ../../source/settings.rst:206 -#: 65aee9ef96d44ff4a5dcf3cc4c3cd7ae d3c2c0cf24c24046ba731c1dc3aa90a6 +#: ../../source/settings.rst:90 ../../source/settings.rst:242 +#: 8ee43c194a614f57b2f10cf306622d9d 9bce6a59ddf14978a8a6b536b266d6cb msgid "Setting" msgstr "选项" -#: ../../source/settings.rst:91 ../../source/settings.rst:207 -#: 2b777384ccc84eabb71ccbbd9136ae70 ecaf3eef672e4b2e8f45030decfa8c41 +#: ../../source/settings.rst:91 ../../source/settings.rst:243 +#: 0961df2fea32498a82984fcd2a6cd428 e8446230ddf34848bbbc331cfefb4745 msgid "Default" msgstr "默认值" -#: ../../source/settings.rst:92 ../../source/settings.rst:147 -#: ../../source/settings.rst:208 80aeb637a3dd4d729d276a989cb24d0a -#: a4edc383a0004df693e18734690b8306 e97723cec40d4e67a7f9485589ed8962 +#: ../../source/settings.rst:92 ../../source/settings.rst:183 +#: ../../source/settings.rst:244 2dbd3a0f5c00431a908b42aec54f648c +#: 6d42f72550974316980c6bf1806ff29c e84d99a18c264735a2adfa5099d4b0fd msgid "Description" msgstr "说明" -#: ../../source/settings.rst:93 b2217c7eaa2547dca6d93cea931c26de +#: ../../source/settings.rst:93 ffec75c5cdea4c9aa0c69125f639339a msgid "``no_autoupdate``" msgstr "``no_autoupdate``" -#: ../../source/settings.rst:94 6119dbf49ef344d58ffbf2bbfbdb944a +#: ../../source/settings.rst:94 16b9c70c0b6a412d9bde98cb2a60e216 msgid "``false``" msgstr "``false``" -#: ../../source/settings.rst:95 d47676ca870649b19c79e5d1bdaacbfa +#: ../../source/settings.rst:95 2381445bb42b40fdacaebd43988daa49 msgid "Disable auto-update entirely" msgstr "完全禁用自动更新" -#: ../../source/settings.rst:96 e14808e21c5b4051bb1bc7ebd2e3f70d +#: ../../source/settings.rst:96 c6ad3238aab14a3d9070588c11ae8ed4 msgid "``autoupdate_check_interval_hours``" msgstr "``autoupdate_check_interval_hours``" -#: ../../source/settings.rst:97 5195c80256c14e778a3cf45f8dd0a4b4 +#: ../../source/settings.rst:97 6cf231184c7740ffb6e8116d11387e4f msgid "``24``" msgstr "``24``" -#: ../../source/settings.rst:98 5042d467e19542f18549b80dd7a8f855 +#: ../../source/settings.rst:98 3c1ea4469c8d425492ec6cccce8aac55 msgid "How often to check for updates (in hours)" msgstr "检查更新的间隔(单位:小时)" -#: ../../source/settings.rst:99 b52e153b8fa943c7bddfc730b2465564 +#: ../../source/settings.rst:99 efb1d0e86cac42c3adf1f3ef4c76c91a msgid "``db_update_meta_url``" msgstr "``db_update_meta_url``" -#: ../../source/settings.rst:100 c6b3db2ba4534c8ba25f80a1d4d61818 +#: ../../source/settings.rst:100 1a0cf0e2a938494689e926cdf7f7ac9e msgid "GitHub raw URL" msgstr "GitHub raw URL" -#: ../../source/settings.rst:101 fd5b0e773d5049c2b29f0b19acb02216 +#: ../../source/settings.rst:101 25a72fbd4eb74ff7b5b0b28e8e43addf msgid "URL of the metadata file (for custom mirrors)" msgstr "元数据文件的 URL(用于自建镜像)" -#: ../../source/settings.rst:103 96e490bb647447f0afce8e2501cb926b +#: ../../source/settings.rst:103 4a02e3e5680c477d8a03b2c5a1b5ca8c msgid "" "**Using a custom database** with ``--db`` always skips auto-update — you " "are explicitly choosing your data source." -msgstr "" -"使用 ``--db`` 指定自定义数据库时,\ **总是会跳过**\ 自动更新 —— 因为你已经" -"明确选择了数据源。" +msgstr "使用 ``--db`` 指定自定义数据库时,\\ **总是会跳过**\\ 自动更新 —— 因为你已经明确选择了数据源。" -#: ../../source/settings.rst:106 b5544b3530334be19b1888f225cdb091 +#: ../../source/settings.rst:106 f7ea5acf3a354a2681d7a150e277f086 msgid "Cloudflare webgate" msgstr "Cloudflare webgate" -#: ../../source/settings.rst:110 2ae8dfcef81846a887bfc0321b341066 +#: ../../source/settings.rst:110 9a059aba51304c1aa1c47c4e681511e6 msgid "" "**Experimental.** The ``cloudflare_bypass`` block is under active " "development; field names, defaults, and the trigger-protection routing " "rules may change without backwards-compatibility guarantees." msgstr "" -"**实验特性。** ``cloudflare_bypass`` 这一节仍在积极开发中;字段名、默认值" -"以及 trigger-protection 的路由规则都可能发生变化,不保证向后兼容。" +"**实验特性。** ``cloudflare_bypass`` 这一节仍在积极开发中;字段名、默认值以及 trigger-protection " +"的路由规则都可能发生变化,不保证向后兼容。" -#: ../../source/settings.rst:114 ab108db50bd244cf8c5dc08ca12227dc +#: ../../source/settings.rst:114 3121d43837664c1f9818c7099732eb09 msgid "" "The ``cloudflare_bypass`` block in ``settings.json`` configures the " -"optional bypass described in :ref:`cloudflare-bypass`. Default value:" +"optional bypass described in :ref:`cloudflare-bypass`. The same block is " +"honoured by the CLI, the Python API, and the web UI (``python -m " +"maigret.web.app``); set ``enabled: true`` once and every entry point " +"routes cf-protected sites through the solver." msgstr "" "``settings.json`` 中的 ``cloudflare_bypass`` 配置块用于配置 " -":ref:`cloudflare-bypass` 所述的可选绕过机制。默认值如下:" +":ref:`cloudflare-bypass` 所述的可选绕过机制。CLI、Python API 以及 Web 界面" +"(``python -m maigret.web.app``)都会读取同一份配置:只需将 ``enabled`` 设为" +" ``true``,所有入口都会把受 cf 保护的站点转发给求解器。" -#: ../../source/settings.rst:140 293518d17b904d058aedfa946717d649 +#: ../../source/settings.rst:120 a98ec36b3f954c30af38363e9bea6ec3 +msgid "" +"**Minimal FlareSolverr setup.** Start the solver in Docker, then drop the" +" following snippet into ``~/.maigret/settings.json`` (or any path listed " +"in :ref:`settings`):" +msgstr "" +"**FlareSolverr 最小化配置。** 先用 Docker 启动求解器,然后把下面这段配置" +"写入 ``~/.maigret/settings.json``\\ (或 :ref:`settings` 中列出的任意路径):" + +#: ../../source/settings.rst:145 d8dc102a6ea247d991b6c09f917bc37a +msgid "" +"That is enough — ``session_prefix`` and ``trigger_protection`` fall back " +"to sensible defaults (``\"maigret\"`` and ``[\"cf_js_challenge\", " +"\"cf_firewall\", \"webgate\"]`` respectively). On the next run, Maigret " +"logs ``Cloudflare webgate active: ...`` and routes matching sites through" +" the solver." +msgstr "" +"这样就够了 —— ``session_prefix`` 和 ``trigger_protection`` 会回退到合理的" +"默认值(分别为 ``\"maigret\"`` 和 " +"``[\"cf_js_challenge\", \"cf_firewall\", \"webgate\"]``)。下次运行时," +"Maigret 会输出 ``Cloudflare webgate active: ...`` 日志,并把命中的站点" +"路由到求解器。" + +#: ../../source/settings.rst:151 bfff9d4907664fc3862b5a6a20da88be +msgid "Default value (full schema with every supported field):" +msgstr "默认值(包含全部受支持字段的完整结构):" + +#: ../../source/settings.rst:176 46b6a3845ba548aa8823e049281bda6d msgid "**Fields.**" msgstr "**字段说明。**" -#: ../../source/settings.rst:146 2ca7704b61aa4258b0cdce8157f9ce6f +#: ../../source/settings.rst:182 bad54b56ab2945058697d7054a6055fa msgid "Field" msgstr "字段" -#: ../../source/settings.rst:148 66779ca136dc4c7bb02a693f7a8e24b2 +#: ../../source/settings.rst:184 610f1da5f28040079ae2bcb10618573c msgid "``enabled``" msgstr "``enabled``" -#: ../../source/settings.rst:149 74155cf90696425e9845ce3b54b48ac3 +#: ../../source/settings.rst:185 c3149bfa418c4e3b95dde5611ef4717b msgid "" "When ``true``, the bypass is active for every run; when ``false`` (the " "default), it activates only on ``--cloudflare-bypass``." msgstr "" -"为 ``true`` 时,每次运行都会启用绕过;为 ``false``\ (默认)时,仅在传入 " -"``--cloudflare-bypass`` 时才启用。" +"为 ``true`` 时,每次运行都会启用绕过;为 ``false``\\ (默认)时,仅在传入 ``--cloudflare-bypass`` " +"时才启用。" -#: ../../source/settings.rst:151 a7643702bf5f428bb8ee0cd317d3126e +#: ../../source/settings.rst:187 5c92e71e8ec144be83f8abbca6e37d01 msgid "``trigger_protection``" msgstr "``trigger_protection``" -#: ../../source/settings.rst:152 c33771acb2ba4f098c6dfc1aa53d4744 +#: ../../source/settings.rst:188 38557548995949a5b8121d81ffc51178 msgid "" "List of ``site.protection`` values that route a check through the " "webgate. Sites whose protection is empty or doesn't intersect this list " "use the default (aiohttp / curl_cffi) checker." msgstr "" -"一个 ``site.protection`` 值的列表;命中其中任何一个时,检查请求都会经由" -"webgate 转发。\ ``protection`` 为空、或与本列表无交集的站点,仍使用默认的" -"检查器(aiohttp / curl_cffi)。" +"一个 ``site.protection`` 值的列表;命中其中任何一个时,检查请求都会经由webgate 转发。\\ " +"``protection`` 为空、或与本列表无交集的站点,仍使用默认的检查器(aiohttp / curl_cffi)。" -#: ../../source/settings.rst:155 9fc5f3ea49664010af05dbea49ad633b +#: ../../source/settings.rst:191 4b93a27e05b34050837eccffbcb40bed msgid "``session_prefix``" msgstr "``session_prefix``" -#: ../../source/settings.rst:156 0b6e098ac4314fa2ac21955e839f232e +#: ../../source/settings.rst:192 c54f79f2b5ca4c42805b948cbbb3c3d2 msgid "" "Prefix for the FlareSolverr ``session`` field. Maigret appends the " "process PID so concurrent runs don't collide. Reusing a session caches " "cf_clearance between checks of the same domain." msgstr "" -"FlareSolverr ``session`` 字段的前缀。Maigret 会在其后追加进程 PID,避免并发" -"运行相互冲突。复用同一个 session 会在对同一域名的多次检查之间缓存 " -"cf_clearance。" +"FlareSolverr ``session`` 字段的前缀。Maigret 会在其后追加进程 PID,避免并发运行相互冲突。复用同一个 " +"session 会在对同一域名的多次检查之间缓存 cf_clearance。" -#: ../../source/settings.rst:159 f810495bb01a415da4400ccfd553f6d3 +#: ../../source/settings.rst:195 9742c805b326433aac2ad92e1e48a880 msgid "``modules``" msgstr "``modules``" -#: ../../source/settings.rst:160 96a91e5c374f4e21a710b75b9e4bff5d +#: ../../source/settings.rst:196 8c7a26caf24040fc9afce140b739ba89 msgid "" "Ordered list of backend modules. The first reachable module handles the " "check; later ones serve as a fallback chain." -msgstr "" -"后端模块的有序列表。第一个能连通的模块负责处理该检查,后续模块作为兜底链。" +msgstr "后端模块的有序列表。第一个能连通的模块负责处理该检查,后续模块作为兜底链。" -#: ../../source/settings.rst:163 15176838fce043c180896ec24f8c3e12 +#: ../../source/settings.rst:199 262a0c13a9ae4815962b16733ef6016e msgid "**Module methods.**" msgstr "**模块方法。**" -#: ../../source/settings.rst:165 227d24158d2643b7a5f529b69d909507 +#: ../../source/settings.rst:201 a3145c584a0e453aac0ca51f67ee12db msgid "" "``json_api`` — FlareSolverr-compatible POST endpoint at ``url``. " "Preserves real upstream HTTP status, headers and final URL. Optional " "``max_timeout_ms`` (default ``60000``) is the per-request budget the " "solver is allowed to spend on the JS challenge." msgstr "" -"``json_api`` —— 在 ``url`` 上提供的 FlareSolverr 兼容 POST 接口。会保留上游" -"真实的 HTTP 状态码、响应头和最终 URL。可选字段 ``max_timeout_ms``\ (默认 " -"``60000``)是允许求解器在 JS 挑战上花费的单次请求时间预算。" +"``json_api`` —— 在 ``url`` 上提供的 FlareSolverr 兼容 POST 接口。会保留上游真实的 HTTP " +"状态码、响应头和最终 URL。可选字段 ``max_timeout_ms``\\ (默认 ``60000``)是允许求解器在 JS " +"挑战上花费的单次请求时间预算。" -#: ../../source/settings.rst:169 1db9fddba6a743149e97c415f66532fd +#: ../../source/settings.rst:205 fe03ccdb70144c77adc1b7280d9f71ca #, python-brace-format msgid "" "``url_rewrite`` — legacy CloudflareBypassForScraping endpoint. The " @@ -315,84 +327,81 @@ msgid "" " status_code`` and ``response_url`` checks misfire under this method " "(treated as a synthetic HTTP 200 on success)." msgstr "" -"``url_rewrite`` —— 旧版 CloudflareBypassForScraping 接口。\ ``url`` 中必须" -"包含 ``{url}`` 占位符;原始探测 URL 会被 URL 编码后代入。该方式仅返回渲染后" -"的 HTML —— 因此在此方法下,\ ``checkType: status_code`` 和 ``response_url`` " -"两类检查会失准(成功时会被当作一个虚构的 HTTP 200)。" +"``url_rewrite`` —— 旧版 CloudflareBypassForScraping 接口。\\ ``url`` 中必须包含 " +"``{url}`` 占位符;原始探测 URL 会被 URL 编码后代入。该方式仅返回渲染后的 HTML —— 因此在此方法下,\\ " +"``checkType: status_code`` 和 ``response_url`` 两类检查会失准(成功时会被当作一个虚构的 HTTP " +"200)。" -#: ../../source/settings.rst:175 663c371e2b7b4ebc832be8d0e2f3b773 +#: ../../source/settings.rst:211 7dd51af2a40f4b64ab35e75a7291ad88 msgid "**Optional ``proxy`` field (``json_api`` only).**" msgstr "**可选的 ``proxy`` 字段(仅 ``json_api`` 支持)。**" -#: ../../source/settings.rst:177 0e0bd3b653784dd49f95cb942fca57f4 +#: ../../source/settings.rst:213 08bf49a34630456db598e3e585a3e128 msgid "" "A module may carry a ``proxy`` entry that the solver routes the upstream " "request through. Useful when a site enforces ``ip_reputation`` rules that" " block the solver host. Two forms are accepted:" msgstr "" -"每个模块都可以带一个 ``proxy`` 字段 —— 求解器会通过该代理转发上游请求。" -"在站点启用了会封锁求解器主机的 ``ip_reputation`` 规则时,这会非常有用。" -"接受两种写法:" +"每个模块都可以带一个 ``proxy`` 字段 —— 求解器会通过该代理转发上游请求。在站点启用了会封锁求解器主机的 " +"``ip_reputation`` 规则时,这会非常有用。接受两种写法:" -#: ../../source/settings.rst:191 9720e46d246347faa4ccd5cbab26b12c +#: ../../source/settings.rst:227 7e0856096ecb44599b40a1e0efbb7fbb msgid "" "Only ``url``/``username``/``password`` are forwarded; other keys are " "dropped. Cloudflare ``Error 1015 / 1020`` responses indicate the IP is " "rate-limited or banned — switch the proxy rather than retrying. .. _ai-" "analysis-settings:" msgstr "" -"仅 ``url``/``username``/``password`` 会被转发;其它字段会被丢弃。Cloudflare " -"``Error 1015 / 1020`` 响应表示该 IP 被限速或被封 —— 此时请更换代理,而不是" -"反复重试。 .. _ai-analysis-settings:" +"仅 ``url``/``username``/``password`` 会被转发;其它字段会被丢弃。Cloudflare ``Error 1015" +" / 1020`` 响应表示该 IP 被限速或被封 —— 此时请更换代理,而不是反复重试。 .. _ai-analysis-settings:" -#: ../../source/settings.rst:197 5289b3aac94445e59bab01015a6c4c21 +#: ../../source/settings.rst:233 c0352f79365140edadb45dfe6e0373d3 msgid "AI analysis" msgstr "AI 分析" -#: ../../source/settings.rst:199 da442ac518a047c390afff9ddb1e4f01 +#: ../../source/settings.rst:235 bf3d82f2b15745cb8a1b4dfed7c9f836 msgid "" "The ``--ai`` flag (see :ref:`ai-analysis`) talks to an OpenAI-compatible " "chat completion API. Three settings control how that request is made:" msgstr "" -"``--ai`` 参数(参见 :ref:`ai-analysis`)会调用一个 OpenAI 兼容的 chat " -"completion 接口。以下三个配置项决定该请求的具体行为:" +"``--ai`` 参数(参见 :ref:`ai-analysis`)会调用一个 OpenAI 兼容的 chat completion " +"接口。以下三个配置项决定该请求的具体行为:" -#: ../../source/settings.rst:209 4fb53837481444ee8d74ecb2a793363b +#: ../../source/settings.rst:245 5fbd903f98e64b349c146792cc7ab2e9 msgid "``openai_api_key``" msgstr "``openai_api_key``" -#: ../../source/settings.rst:210 4c5a241e1ac848819390ba0845e3fb7a +#: ../../source/settings.rst:246 c73824b01b8e4cb8aabfed01c94ea2ad msgid "``\"\"`` (empty)" -msgstr "``\"\"``\ (空)" +msgstr "``\"\"``\\ (空)" -#: ../../source/settings.rst:211 6994e781bc5e4988a804a239cc20630e +#: ../../source/settings.rst:247 82aaaef9d7cc4665813cdd874130bdb7 msgid "" "API key. If empty, Maigret falls back to the ``OPENAI_API_KEY`` " "environment variable." -msgstr "" -"API key。为空时,Maigret 会回退到 ``OPENAI_API_KEY`` 环境变量。" +msgstr "API key。为空时,Maigret 会回退到 ``OPENAI_API_KEY`` 环境变量。" -#: ../../source/settings.rst:213 ae8fc24725884c4cbe99db369c197682 +#: ../../source/settings.rst:249 037412797a4c42519aef357c77d1833f msgid "``openai_model``" msgstr "``openai_model``" -#: ../../source/settings.rst:214 a685b4608f0b4e6dbdfafd7445732916 +#: ../../source/settings.rst:250 2562a1def3be41889c3b05620eb1870b msgid "``gpt-4o``" msgstr "``gpt-4o``" -#: ../../source/settings.rst:215 bc3d800a38de4978811fee2bf3ced6f6 +#: ../../source/settings.rst:251 0d3623bf37a84a0399c893f90dae649a msgid "Default model name. Overridable per-run with ``--ai-model``." msgstr "默认模型名。可在单次运行中通过 ``--ai-model`` 覆盖。" -#: ../../source/settings.rst:216 c12beb71258348c7bb3e1e35d701d48a +#: ../../source/settings.rst:252 f6ae5711a47f42e4a6effae3c90c2560 msgid "``openai_api_base_url``" msgstr "``openai_api_base_url``" -#: ../../source/settings.rst:217 13d8d8a7bd3a47bdbdaaeaf2b9f6de35 +#: ../../source/settings.rst:253 4aac62c2d35148328d255646e18623d4 msgid "``https://api.openai.com/v1``" msgstr "``https://api.openai.com/v1``" -#: ../../source/settings.rst:218 c4ed64bb43504c85bebd71ecc77f11a3 +#: ../../source/settings.rst:254 6096b4d81ac248a09f6f49f5d8303676 msgid "" "Base URL of the chat completion API. Point this at any OpenAI-compatible " "service (Azure OpenAI, OpenRouter, a local server, …) to use it instead " @@ -401,23 +410,33 @@ msgstr "" "chat completion 接口的基础 URL。把它指向任何 OpenAI 兼容的服务(Azure " "OpenAI、OpenRouter、本地推理服务等),即可用其代替 OpenAI 官方服务。" -#: ../../source/settings.rst:222 edc77536812e4e1e9dce3a93f8b41699 +#: ../../source/settings.rst:258 186158107d2b4ee1af1a46a7e45f6c1a msgid "Example ``~/.maigret/settings.json`` snippet using a non-OpenAI endpoint:" msgstr "下面是一个使用非 OpenAI 接口的 ``~/.maigret/settings.json`` 片段示例:" -#: ../../source/settings.rst:233 c518d020066741ce9af7f0a12b4e7815 +#: ../../source/settings.rst:269 2e37565d7dd74e9898cacf648c27476a msgid "" "The key resolution order is ``settings.openai_api_key`` → " "``OPENAI_API_KEY`` environment variable; the first non-empty value wins." msgstr "" -"API key 的取值顺序为:``settings.openai_api_key`` → ``OPENAI_API_KEY`` 环境" -"变量;以第一个非空值为准。" +"API key 的取值顺序为:``settings.openai_api_key`` → ``OPENAI_API_KEY`` " +"环境变量;以第一个非空值为准。" -#: ../../source/settings.rst:238 d75ced8c84484711b4db002a082d5c8e +#: ../../source/settings.rst:274 0bb83aab896446628c567ec9e33644a1 msgid "" "``--ai`` sends the full internal Markdown report (which contains the " "gathered profile data) to the configured endpoint. Only use providers and" " accounts you trust with that data." msgstr "" -"``--ai`` 会把完整的内部 Markdown 报告(其中包含收集到的所有个人主页数据)" -"发送至所配置的接口。请仅在你信任的服务商和账号上使用该功能。" +"``--ai`` 会把完整的内部 Markdown " +"报告(其中包含收集到的所有个人主页数据)发送至所配置的接口。请仅在你信任的服务商和账号上使用该功能。" + +#~ msgid "" +#~ "The ``cloudflare_bypass`` block in " +#~ "``settings.json`` configures the optional " +#~ "bypass described in :ref:`cloudflare-bypass`." +#~ " Default value:" +#~ msgstr "" +#~ "``settings.json`` 中的 ``cloudflare_bypass`` 配置块用于配置" +#~ " :ref:`cloudflare-bypass` 所述的可选绕过机制。默认值如下:" + diff --git a/docs/source/settings.rst b/docs/source/settings.rst index 7c2b172..a451927 100644 --- a/docs/source/settings.rst +++ b/docs/source/settings.rst @@ -112,7 +112,43 @@ Cloudflare webgate rules may change without backwards-compatibility guarantees. The ``cloudflare_bypass`` block in ``settings.json`` configures the optional -bypass described in :ref:`cloudflare-bypass`. Default value: +bypass described in :ref:`cloudflare-bypass`. The same block is honoured by +the CLI, the Python API, and the web UI (``python -m maigret.web.app``); set +``enabled: true`` once and every entry point routes cf-protected sites +through the solver. + +**Minimal FlareSolverr setup.** Start the solver in Docker, then drop the +following snippet into ``~/.maigret/settings.json`` (or any path listed in +:ref:`settings`): + +.. code-block:: bash + + docker run -d -p 8191:8191 --name flaresolverr \ + ghcr.io/flaresolverr/flaresolverr:latest + +.. code-block:: json + + { + "cloudflare_bypass": { + "enabled": true, + "modules": [ + { + "name": "flaresolverr", + "method": "json_api", + "url": "http://localhost:8191/v1", + "max_timeout_ms": 60000 + } + ] + } + } + +That is enough — ``session_prefix`` and ``trigger_protection`` fall back to +sensible defaults (``"maigret"`` and +``["cf_js_challenge", "cf_firewall", "webgate"]`` respectively). On the next +run, Maigret logs ``Cloudflare webgate active: ...`` and routes matching +sites through the solver. + +Default value (full schema with every supported field): .. code-block:: json diff --git a/maigret/web/app.py b/maigret/web/app.py index a0b8fa3..73d00fd 100644 --- a/maigret/web/app.py +++ b/maigret/web/app.py @@ -17,6 +17,7 @@ from threading import Thread from typing import Any, Dict import maigret import maigret.settings +from maigret.checking import build_cloudflare_bypass_config from maigret.sites import MaigretDatabase from maigret.report import generate_report_context @@ -44,6 +45,19 @@ def setup_logger(log_level, name): async def maigret_search(username, options): logger = setup_logger(logging.WARNING, 'maigret') try: + settings = maigret.settings.Settings() + settings.load() + cf_bypass_config = build_cloudflare_bypass_config(settings) + if cf_bypass_config: + modules_summary = ", ".join( + f"{m.get('name', m.get('method'))}({m.get('url')})" + for m in cf_bypass_config["modules"] + ) + logger.info( + f"Cloudflare webgate active: triggers={cf_bypass_config['trigger_protection']}, " + f"modules=[{modules_summary}]" + ) + db = MaigretDatabase().load_from_path(app.config["MAIGRET_DB_FILE"]) top_sites = int(options.get('top_sites') or 500) @@ -81,6 +95,7 @@ async def maigret_search(username, options): proxy=options.get('proxy', None), tor_proxy=options.get('tor_proxy', None), i2p_proxy=options.get('i2p_proxy', None), + cloudflare_bypass=cf_bypass_config, ) return results except Exception as e: diff --git a/tests/test_web.py b/tests/test_web.py index 09a5498..d34dfdc 100644 --- a/tests/test_web.py +++ b/tests/test_web.py @@ -12,6 +12,7 @@ import pytest import maigret import maigret.report +import maigret.settings from maigret.web import app as web_app_module @@ -189,6 +190,86 @@ def test_download_report_blocks_absolute_path(client, web_app, tmp_path): assert b'ABSOLUTE' not in resp.get_data() +def test_search_passes_cloudflare_bypass_from_settings(client, web_app, monkeypatch): + """If settings.json enables cloudflare_bypass with a valid FlareSolverr module, + the web search must forward that config to maigret.search via the + cloudflare_bypass kwarg. Guards the wiring in maigret_search().""" + + captured = {} + + async def fake_search(*args, **kwargs): + captured.update(kwargs) + return {} + + def fake_load(self, paths=None): + self.cloudflare_bypass = { + "enabled": True, + "session_prefix": "test-prefix", + "trigger_protection": ["cf_js_challenge"], + "modules": [ + { + "name": "flaresolverr", + "method": "json_api", + "url": "http://flare.test:8191/v1", + "max_timeout_ms": 60000, + } + ], + } + return True, "" + + monkeypatch.setattr(maigret.settings.Settings, 'load', fake_load) + monkeypatch.setattr(maigret, 'search', fake_search) + monkeypatch.setattr(maigret.report, 'save_graph_report', lambda *a, **kw: None) + monkeypatch.setattr(maigret.report, 'save_csv_report', lambda *a, **kw: None) + monkeypatch.setattr(maigret.report, 'save_json_report', lambda *a, **kw: None) + monkeypatch.setattr(maigret.report, 'save_pdf_report', lambda *a, **kw: None) + monkeypatch.setattr(maigret.report, 'save_html_report', lambda *a, **kw: None) + monkeypatch.setattr(maigret.report, 'generate_report_context', lambda *a, **kw: {}) + monkeypatch.setattr(web_app, 'Thread', _SyncThread) + + client.post('/search', data={'usernames': 'testuser'}) + + assert 'cloudflare_bypass' in captured, ( + 'maigret.search was not given a cloudflare_bypass kwarg' + ) + cf = captured['cloudflare_bypass'] + assert cf is not None + assert cf['session_prefix'] == 'test-prefix' + assert cf['trigger_protection'] == ['cf_js_challenge'] + assert len(cf['modules']) == 1 + assert cf['modules'][0]['url'] == 'http://flare.test:8191/v1' + assert cf['modules'][0]['method'] == 'json_api' + + +def test_search_omits_cloudflare_bypass_when_disabled(client, web_app, monkeypatch): + """When settings has no cloudflare_bypass (or enabled=false), the kwarg + must be None so the default checker pipeline runs.""" + + captured = {} + + async def fake_search(*args, **kwargs): + captured.update(kwargs) + return {} + + def fake_load(self, paths=None): + # no cloudflare_bypass attribute at all + return True, "" + + monkeypatch.setattr(maigret.settings.Settings, 'load', fake_load) + monkeypatch.setattr(maigret, 'search', fake_search) + monkeypatch.setattr(maigret.report, 'save_graph_report', lambda *a, **kw: None) + monkeypatch.setattr(maigret.report, 'save_csv_report', lambda *a, **kw: None) + monkeypatch.setattr(maigret.report, 'save_json_report', lambda *a, **kw: None) + monkeypatch.setattr(maigret.report, 'save_pdf_report', lambda *a, **kw: None) + monkeypatch.setattr(maigret.report, 'save_html_report', lambda *a, **kw: None) + monkeypatch.setattr(maigret.report, 'generate_report_context', lambda *a, **kw: {}) + monkeypatch.setattr(web_app, 'Thread', _SyncThread) + + client.post('/search', data={'usernames': 'testuser'}) + + assert captured.get('cloudflare_bypass') is None + + def test_real_report_generation_does_not_crash(client, web_app, monkeypatch): """End-to-end with mocked maigret.search but REAL report generation.