Spaces:
Running
Running
add /app/test-twitter probe + fix Worker redirect for POST
Browse fileshealth-server.js:
- /app/test-twitter: probes api.twitter.com GET (reachability) and
POST oauth/access_token (no auth) to distinguish IP-blocked (500 HTML)
from IP-allowed (400/401 JSON). Shows NODE_OPTIONS + CLOUDFLARE_PROXY_URL.
cloudflare-proxy-setup.py:
- Worker: change redirect:"follow" β redirect:"manual" so POST bodies
are not silently dropped when X returns a 3xx (fetch spec converts
POSTβGET on 301/302, losing OAuth-signed body β signature mismatch).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- cloudflare-proxy-setup.py +5 -1
- health-server.js +64 -0
cloudflare-proxy-setup.py
CHANGED
|
@@ -147,11 +147,15 @@ async function handleRequest(request) {{
|
|
| 147 |
headers.delete("x-target-host");
|
| 148 |
headers.delete("x-proxy-key");
|
| 149 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
const proxiedRequest = new Request(targetUrl, {{
|
| 151 |
method: request.method,
|
| 152 |
headers,
|
| 153 |
body: request.body,
|
| 154 |
-
redirect: "
|
| 155 |
}});
|
| 156 |
|
| 157 |
try {{
|
|
|
|
| 147 |
headers.delete("x-target-host");
|
| 148 |
headers.delete("x-proxy-key");
|
| 149 |
|
| 150 |
+
// Use redirect:"manual" so POST bodies are NOT silently dropped when X
|
| 151 |
+
// returns a 3xx (fetch spec converts POSTβGET on 301/302 redirect, losing
|
| 152 |
+
// the OAuth-signed body and causing a signature mismatch β 500 from X).
|
| 153 |
+
// The Node.js client receives the raw 3xx and handles it with body intact.
|
| 154 |
const proxiedRequest = new Request(targetUrl, {{
|
| 155 |
method: request.method,
|
| 156 |
headers,
|
| 157 |
body: request.body,
|
| 158 |
+
redirect: "manual",
|
| 159 |
}});
|
| 160 |
|
| 161 |
try {{
|
health-server.js
CHANGED
|
@@ -1775,6 +1775,70 @@ const server = http.createServer((req, res) => {
|
|
| 1775 |
return;
|
| 1776 |
}
|
| 1777 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1778 |
// ββ /app/debug-logs β Temporary endpoint for debugging βββββββββββββββββββ
|
| 1779 |
if (pathname === "/app/debug-logs") {
|
| 1780 |
try {
|
|
|
|
| 1775 |
return;
|
| 1776 |
}
|
| 1777 |
|
| 1778 |
+
// ββ /app/test-twitter β raw connectivity probe to api.twitter.com ββββββββ
|
| 1779 |
+
if (pathname === "/app/test-twitter") {
|
| 1780 |
+
const https = require("https");
|
| 1781 |
+
const lines = [];
|
| 1782 |
+
lines.push(`NODE_OPTIONS : ${process.env.NODE_OPTIONS || "(not set)"}`);
|
| 1783 |
+
lines.push(`CLOUDFLARE_PROXY_URL : ${process.env.CLOUDFLARE_PROXY_URL || "(not set)"}`);
|
| 1784 |
+
lines.push("");
|
| 1785 |
+
// Two probes:
|
| 1786 |
+
// A) GET api.twitter.com/ β basic reachability + proxy detection
|
| 1787 |
+
// B) POST oauth/access_token (no auth) β 4xx+JSON if IP allowed, 500 HTML if IP blocked
|
| 1788 |
+
const makeProbe = (opts, postBody) => new Promise((resolve) => {
|
| 1789 |
+
const t0 = Date.now();
|
| 1790 |
+
const req = https.request({ ...opts, timeout: 10000 }, (r) => {
|
| 1791 |
+
let body = "";
|
| 1792 |
+
r.setEncoding("utf8");
|
| 1793 |
+
r.on("data", (c) => { body += c; if (body.length > 800) r.destroy(); });
|
| 1794 |
+
r.on("close", () => resolve({ status: r.statusCode, headers: r.headers, body: body.trim(), ms: Date.now() - t0 }));
|
| 1795 |
+
r.on("end", () => resolve({ status: r.statusCode, headers: r.headers, body: body.trim(), ms: Date.now() - t0 }));
|
| 1796 |
+
});
|
| 1797 |
+
req.on("error", (e) => resolve({ error: e.message, ms: Date.now() - t0 }));
|
| 1798 |
+
req.on("timeout", () => { req.destroy(); resolve({ error: "TIMEOUT 10s", ms: Date.now() - t0 }); });
|
| 1799 |
+
if (postBody) req.write(postBody);
|
| 1800 |
+
req.end();
|
| 1801 |
+
});
|
| 1802 |
+
|
| 1803 |
+
(async () => {
|
| 1804 |
+
// Probe A
|
| 1805 |
+
lines.push("ββ Probe A: GET https://api.twitter.com/ ββ");
|
| 1806 |
+
const a = await makeProbe({ hostname: "api.twitter.com", path: "/", method: "GET",
|
| 1807 |
+
headers: { "User-Agent": "curl/8.0", "Accept": "*/*" } });
|
| 1808 |
+
if (a.error) {
|
| 1809 |
+
lines.push(`ERROR: ${a.error}`);
|
| 1810 |
+
} else {
|
| 1811 |
+
lines.push(`Status : ${a.status} (${a.ms}ms)`);
|
| 1812 |
+
for (const [k, v] of Object.entries(a.headers)) lines.push(` ${k}: ${v}`);
|
| 1813 |
+
lines.push(`cf-ray in headers? : ${a.headers["cf-ray"] ? "YES β request went through Cloudflare" : "no"}`);
|
| 1814 |
+
lines.push(`Body (first 300) : ${a.body.slice(0, 300)}`);
|
| 1815 |
+
}
|
| 1816 |
+
|
| 1817 |
+
// Probe B
|
| 1818 |
+
lines.push("");
|
| 1819 |
+
lines.push("ββ Probe B: POST https://api.twitter.com/oauth/access_token (no auth) ββ");
|
| 1820 |
+
lines.push("Expected: 400/401 JSON if IP allowed by X, 500 HTML if IP blocked");
|
| 1821 |
+
const postBody = "oauth_verifier=test";
|
| 1822 |
+
const b = await makeProbe({ hostname: "api.twitter.com", path: "/oauth/access_token",
|
| 1823 |
+
method: "POST",
|
| 1824 |
+
headers: { "User-Agent": "curl/8.0", "Content-Type": "application/x-www-form-urlencoded",
|
| 1825 |
+
"Content-Length": Buffer.byteLength(postBody) } }, postBody);
|
| 1826 |
+
if (b.error) {
|
| 1827 |
+
lines.push(`ERROR: ${b.error}`);
|
| 1828 |
+
} else {
|
| 1829 |
+
lines.push(`Status : ${b.status} (${b.ms}ms)`);
|
| 1830 |
+
for (const [k, v] of Object.entries(b.headers)) lines.push(` ${k}: ${v}`);
|
| 1831 |
+
const isHtml = b.body.includes("<html") || b.body.includes("<!DOCTYPE");
|
| 1832 |
+
lines.push(`Body type : ${isHtml ? "HTML β IP probably blocked" : "text/JSON β IP allowed"}`);
|
| 1833 |
+
lines.push(`Body (first 500) : ${b.body.slice(0, 500)}`);
|
| 1834 |
+
}
|
| 1835 |
+
|
| 1836 |
+
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
|
| 1837 |
+
res.end(lines.join("\n"));
|
| 1838 |
+
})();
|
| 1839 |
+
return;
|
| 1840 |
+
}
|
| 1841 |
+
|
| 1842 |
// ββ /app/debug-logs β Temporary endpoint for debugging βββββββββββββββββββ
|
| 1843 |
if (pathname === "/app/debug-logs") {
|
| 1844 |
try {
|