feat(ci): switch Cloudflare bypass from FlareSolverr to Byparr
CI / check (push) Successful in 3m59s
CI / deploy (push) Successful in 2m13s

This commit is contained in:
openhands committed 2026-09-08 16:02:12 +02:00
1 parent fabd160250
commit 25f4d74209
10 files changed
+138 -131

No files matched your search

+9 -9
View File
@@ -5,9 +5,9 @@ import { autoDetectInteraction } from "@/lib/furni/auto-interaction";
import { normalizeClassname } from "@/lib/furni/classname";
import { HABBO_GAMEDATA_HOTEL_SETTING_KEY } from "@/lib/habbo-gamedata-hotel";
import { logger } from "@/lib/logger";
import { fetchWithByparr } from "@/lib/services/byparr";
import { extractFurniIconPng } from "@/lib/services/clone-icon";
import type { CloneSource } from "@/lib/services/clone-sources";
import { fetchWithFlareSolver } from "@/lib/services/flare-solver";
import { getFurniAssetDirs } from "@/lib/services/furni-asset-dirs";
import { appendFurniEntry } from "@/lib/services/furni-data";
import {
@@ -101,7 +101,7 @@ const TTL = 5 * 60 * 1000;
/**
* Some sources serve their furnidata wrapped in an HTML document (e.g. Leet's
* JSON is embedded in a `<pre>` block inside the page, often surfaced by the
* FlareSolverr fallback). Extract the JSON payload before giving up.
* Byparr fallback). Extract the JSON payload before giving up.
*/
function extractJsonFromHtml(body: string): string | null {
const pre = body.match(/<pre[^>]*>([\s\S]*?)<\/pre>/);
@@ -117,20 +117,20 @@ function extractJsonFromHtml(body: string): string | null {
}
/**
* Fetch a source's furnidata via FlareSolverr, falling back to a curl
* Fetch a source's furnidata via Byparr, falling back to a curl
* subprocess. Some CDNs (e.g. Leet.city) block Node's TLS fingerprint while
* the same request succeeds from curl — so curl is a useful second fallback
* when FlareSolverr is unavailable.
* when Byparr is unavailable.
*/
async function fetchWithFallback(url: string): Promise<string> {
try {
return await fetchWithFlareSolver(url);
return await fetchWithByparr(url);
} catch {
try {
return await curlFetchText(url);
} catch (err) {
throw new Error(
`FlareSolverr + curl fallback failed for ${url}: ${(err as Error).message}`,
`Byparr + curl fallback failed for ${url}: ${(err as Error).message}`,
);
}
}
@@ -150,8 +150,8 @@ export async function fetchSourceFurnidata(
});
if (res.ok) {
// Some sources (e.g. GitHub raw) serve JSON with a text/plain
// content-type — only route to FlareSolverr when the body is
// actually HTML, otherwise parse the JSON directly.
// content-type — only route to Byparr when the body is actually
// HTML, otherwise parse the JSON directly.
const text = await res.text();
if (
text.trimStart().startsWith("{") ||
@@ -171,7 +171,7 @@ export async function fetchSourceFurnidata(
const extracted = extractJsonFromHtml(body);
if (!extracted) {
throw new Error(
`Source ${url} is behind a Cloudflare challenge that could not be bypassed. Start FlareSolverr and check its logs.`,
`Source ${url} is behind a Cloudflare challenge that could not be bypassed. Start Byparr and check its logs.`,
);
}
body = extracted;