9b395f5cc3
E2E Headed Chrome / e2e-headed (macos-15) (push) Has been cancelled
E2E Headed Chrome / e2e-headed (ubuntu-latest) (push) Has been cancelled
E2E Headed Chrome / e2e-headed (windows-latest) (push) Has been cancelled
CI / build (macos-latest) (push) Has been cancelled
CI / build (ubuntu-latest) (push) Has been cancelled
CI / build (windows-latest) (push) Has been cancelled
CI / unit-test (push) Has been cancelled
CI / bun-test (push) Has been cancelled
CI / adapter-test (push) Has been cancelled
CI / smoke-test (macos-latest) (push) Has been cancelled
CI / smoke-test (ubuntu-latest) (push) Has been cancelled
Security Audit / audit (push) Has been cancelled
Build Chrome Extension / build (push) Has been cancelled
Trigger Website Rebuild (Docs Updated) / dispatch (push) Has been cancelled
67 lines
2.8 KiB
JavaScript
67 lines
2.8 KiB
JavaScript
import { AuthRequiredError, CommandExecutionError } from '@jackwener/opencli/errors';
|
|
import { unwrapEvaluateResult } from './evaluate-result.js';
|
|
|
|
function isAuthLikeError(code, message) {
|
|
const text = String(message ?? '');
|
|
return code === 401 || code === 403 || /login|cookie|auth|captcha|verify|forbidden|permission|登录|登陆|权限|验证|验证码/i.test(text);
|
|
}
|
|
|
|
/**
|
|
* Execute a fetch() call inside the Chrome browser context via page.evaluate.
|
|
* This ensures a_bogus signing and cookies are handled automatically by the browser.
|
|
*/
|
|
export async function browserFetch(page, method, url, options = {}) {
|
|
const js = `
|
|
(async () => {
|
|
const controller = new AbortController();
|
|
const timer = setTimeout(() => controller.abort(), ${Number(options.timeoutMs ?? 30000)});
|
|
try {
|
|
const res = await fetch(${JSON.stringify(url)}, {
|
|
method: ${JSON.stringify(method)},
|
|
credentials: 'include',
|
|
signal: controller.signal,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
...${JSON.stringify(options.headers ?? {})}
|
|
},
|
|
${options.body ? `body: JSON.stringify(${JSON.stringify(options.body)}),` : ''}
|
|
});
|
|
const text = await res.text();
|
|
try {
|
|
return JSON.parse(text);
|
|
} catch (error) {
|
|
return { status_code: res.ok ? -2 : res.status, status_msg: \`JSON parse failed: \${text.slice(0, 500) || String(error && error.message || error)}\` };
|
|
}
|
|
} catch (error) {
|
|
return { status_code: -1, status_msg: String(error && error.message || error) };
|
|
} finally {
|
|
clearTimeout(timer);
|
|
}
|
|
})()
|
|
`;
|
|
let result;
|
|
try {
|
|
result = unwrapEvaluateResult(await page.evaluate(js));
|
|
}
|
|
catch (error) {
|
|
throw new CommandExecutionError(`Douyin API request failed (${method} ${url}): ${error instanceof Error ? error.message : String(error)}`);
|
|
}
|
|
if (result == null) {
|
|
throw new CommandExecutionError(`Empty response from Douyin API (${method} ${url})`);
|
|
}
|
|
if (Array.isArray(result) || typeof result !== 'object') {
|
|
throw new CommandExecutionError(`Malformed response from Douyin API (${method} ${url})`);
|
|
}
|
|
if (result && typeof result === 'object' && 'status_code' in result) {
|
|
const code = result.status_code;
|
|
if (code !== 0) {
|
|
const msg = result.status_msg ?? result.message ?? 'unknown error';
|
|
if (isAuthLikeError(code, msg)) {
|
|
throw new AuthRequiredError('creator.douyin.com', `Douyin API auth/permission error ${code} at ${method} ${url}: ${msg}`);
|
|
}
|
|
throw new CommandExecutionError(`Douyin API error ${code} at ${method} ${url}: ${msg}`);
|
|
}
|
|
}
|
|
return result;
|
|
}
|