Spaces:
Running
Running
remove debug endpoints before release
Browse filesRemove publicly-accessible debug endpoints that exposed sensitive data:
- /app/debug-logs (API keys, Redis oauth tokens, backend logs)
- /app/test-request-token (live OAuth token generation)
- /app/test-twitter (raw API connectivity probe)
- /app/read-x-provider (container source exposure)
- X OAuth callback Redis snapshot logging (/tmp/x-oauth-callbacks.log)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- health-server.js +0 -345
health-server.js
CHANGED
|
@@ -2085,314 +2085,6 @@ const server = http.createServer((req, res) => {
|
|
| 2085 |
return;
|
| 2086 |
}
|
| 2087 |
|
| 2088 |
-
// ββ /app/test-twitter β raw connectivity probe to api.twitter.com ββββββββ
|
| 2089 |
-
if (pathname === "/app/test-twitter") {
|
| 2090 |
-
const https = require("https");
|
| 2091 |
-
const lines = [];
|
| 2092 |
-
lines.push(`NODE_OPTIONS : ${process.env.NODE_OPTIONS || "(not set)"}`);
|
| 2093 |
-
lines.push(`CLOUDFLARE_PROXY_URL : ${process.env.CLOUDFLARE_PROXY_URL || "(not set)"}`);
|
| 2094 |
-
lines.push("");
|
| 2095 |
-
// Two probes:
|
| 2096 |
-
// A) GET api.twitter.com/ β basic reachability + proxy detection
|
| 2097 |
-
// B) POST oauth/access_token (no auth) β 4xx+JSON if IP allowed, 500 HTML if IP blocked
|
| 2098 |
-
const makeProbe = (opts, postBody) => new Promise((resolve) => {
|
| 2099 |
-
const t0 = Date.now();
|
| 2100 |
-
const req = https.request({ ...opts, timeout: 10000 }, (r) => {
|
| 2101 |
-
let body = "";
|
| 2102 |
-
r.setEncoding("utf8");
|
| 2103 |
-
r.on("data", (c) => { body += c; if (body.length > 800) r.destroy(); });
|
| 2104 |
-
r.on("close", () => resolve({ status: r.statusCode, headers: r.headers, body: body.trim(), ms: Date.now() - t0 }));
|
| 2105 |
-
r.on("end", () => resolve({ status: r.statusCode, headers: r.headers, body: body.trim(), ms: Date.now() - t0 }));
|
| 2106 |
-
});
|
| 2107 |
-
req.on("error", (e) => resolve({ error: e.message, ms: Date.now() - t0 }));
|
| 2108 |
-
req.on("timeout", () => { req.destroy(); resolve({ error: "TIMEOUT 10s", ms: Date.now() - t0 }); });
|
| 2109 |
-
if (postBody) req.write(postBody);
|
| 2110 |
-
req.end();
|
| 2111 |
-
});
|
| 2112 |
-
|
| 2113 |
-
(async () => {
|
| 2114 |
-
// Probe A
|
| 2115 |
-
lines.push("ββ Probe A: GET https://api.twitter.com/ ββ");
|
| 2116 |
-
const a = await makeProbe({ hostname: "api.twitter.com", path: "/", method: "GET",
|
| 2117 |
-
headers: { "User-Agent": "curl/8.0", "Accept": "*/*" } });
|
| 2118 |
-
if (a.error) {
|
| 2119 |
-
lines.push(`ERROR: ${a.error}`);
|
| 2120 |
-
} else {
|
| 2121 |
-
lines.push(`Status : ${a.status} (${a.ms}ms)`);
|
| 2122 |
-
for (const [k, v] of Object.entries(a.headers)) lines.push(` ${k}: ${v}`);
|
| 2123 |
-
lines.push(`cf-ray in headers? : ${a.headers["cf-ray"] ? "YES β request went through Cloudflare" : "no"}`);
|
| 2124 |
-
lines.push(`Body (first 300) : ${a.body.slice(0, 300)}`);
|
| 2125 |
-
}
|
| 2126 |
-
|
| 2127 |
-
// Probe B
|
| 2128 |
-
lines.push("");
|
| 2129 |
-
lines.push("ββ Probe B: POST https://api.twitter.com/oauth/access_token (no auth) ββ");
|
| 2130 |
-
lines.push("Expected: 400/401 JSON if IP allowed by X, 500 HTML if IP blocked");
|
| 2131 |
-
const postBody = "oauth_verifier=test";
|
| 2132 |
-
const b = await makeProbe({ hostname: "api.twitter.com", path: "/oauth/access_token",
|
| 2133 |
-
method: "POST",
|
| 2134 |
-
headers: { "User-Agent": "curl/8.0", "Content-Type": "application/x-www-form-urlencoded",
|
| 2135 |
-
"Content-Length": Buffer.byteLength(postBody) } }, postBody);
|
| 2136 |
-
if (b.error) {
|
| 2137 |
-
lines.push(`ERROR: ${b.error}`);
|
| 2138 |
-
} else {
|
| 2139 |
-
lines.push(`Status : ${b.status} (${b.ms}ms)`);
|
| 2140 |
-
for (const [k, v] of Object.entries(b.headers)) lines.push(` ${k}: ${v}`);
|
| 2141 |
-
const isHtml = b.body.includes("<html") || b.body.includes("<!DOCTYPE");
|
| 2142 |
-
lines.push(`Body type : ${isHtml ? "HTML β IP probably blocked" : "text/JSON β IP allowed"}`);
|
| 2143 |
-
lines.push(`Body (first 500) : ${b.body.slice(0, 500)}`);
|
| 2144 |
-
}
|
| 2145 |
-
|
| 2146 |
-
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
|
| 2147 |
-
res.end(lines.join("\n"));
|
| 2148 |
-
})();
|
| 2149 |
-
return;
|
| 2150 |
-
}
|
| 2151 |
-
|
| 2152 |
-
// ββ /app/test-request-token β Test generateAuthLink via CF proxy βββββββββ
|
| 2153 |
-
if (pathname === "/app/test-request-token" || pathname === "/app/test-request-token/") {
|
| 2154 |
-
(async () => {
|
| 2155 |
-
const lines = ["=== TEST: generateAuthLink via CF proxy ===", ""];
|
| 2156 |
-
try {
|
| 2157 |
-
const apiKey = process.env.X_API_KEY || "";
|
| 2158 |
-
const apiSecret = process.env.X_API_SECRET || "";
|
| 2159 |
-
lines.push(`X_API_KEY : ${apiKey.slice(0,6)}... (len=${apiKey.length})`);
|
| 2160 |
-
lines.push(`X_API_SECRET : ${apiSecret.slice(0,6)}... (len=${apiSecret.length})`);
|
| 2161 |
-
lines.push(`CLOUDFLARE_PROXY_URL : ${process.env.CLOUDFLARE_PROXY_URL || "(not set)"}`);
|
| 2162 |
-
lines.push(`NODE_OPTIONS : ${process.env.NODE_OPTIONS || "(not set)"}`);
|
| 2163 |
-
lines.push("");
|
| 2164 |
-
|
| 2165 |
-
// Require twitter-api-v2 from Postiz's node_modules
|
| 2166 |
-
const { TwitterApi } = require("/app/node_modules/twitter-api-v2");
|
| 2167 |
-
const callbackUrl = (process.env.FRONTEND_URL || "https://somratpro-huggingpost.hf.space") + "/integrations/social/x";
|
| 2168 |
-
lines.push(`callbackUrl : ${callbackUrl}`);
|
| 2169 |
-
lines.push("Calling generateAuthLink...");
|
| 2170 |
-
|
| 2171 |
-
const client = new TwitterApi({ appKey: apiKey, appSecret: apiSecret });
|
| 2172 |
-
const result = await client.generateAuthLink(callbackUrl, { authAccessType: "write", linkMode: "authenticate", forceLogin: false });
|
| 2173 |
-
|
| 2174 |
-
lines.push("");
|
| 2175 |
-
lines.push(`oauth_token : ${result.oauth_token ? result.oauth_token.slice(0,20) + "... (len=" + result.oauth_token.length + ")" : "(EMPTY!)"}`);
|
| 2176 |
-
lines.push(`oauth_token_secret : ${result.oauth_token_secret ? result.oauth_token_secret.slice(0,10) + "... (len=" + result.oauth_token_secret.length + ")" : "(EMPTY!)"}`);
|
| 2177 |
-
lines.push(`url : ${result.url ? result.url.slice(0,80) : "(EMPTY!)"}`);
|
| 2178 |
-
lines.push("");
|
| 2179 |
-
lines.push("codeVerifier = oauth_token + ':' + oauth_token_secret:");
|
| 2180 |
-
const cv = result.oauth_token + ":" + result.oauth_token_secret;
|
| 2181 |
-
lines.push(` "${cv.slice(0,40)}..." (len=${cv.length})`);
|
| 2182 |
-
if (!result.oauth_token_secret) {
|
| 2183 |
-
lines.push(" β EMPTY SECRET β CF proxy or X returning malformed request_token response!");
|
| 2184 |
-
} else {
|
| 2185 |
-
lines.push(" β secret present");
|
| 2186 |
-
}
|
| 2187 |
-
} catch (e) {
|
| 2188 |
-
lines.push(`ERROR: ${e.message}`);
|
| 2189 |
-
lines.push(e.stack || "");
|
| 2190 |
-
}
|
| 2191 |
-
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
|
| 2192 |
-
res.end(lines.join("\n"));
|
| 2193 |
-
})();
|
| 2194 |
-
return;
|
| 2195 |
-
}
|
| 2196 |
-
|
| 2197 |
-
// ββ /app/read-x-provider β Show x.provider.ts source from container βββββ
|
| 2198 |
-
if (pathname === "/app/read-x-provider") {
|
| 2199 |
-
try {
|
| 2200 |
-
const { execSync } = require("child_process");
|
| 2201 |
-
const rc = (cmd) => { try { return execSync(cmd, { timeout: 10000 }).toString().trim(); } catch(e) { return "(err: " + e.message.slice(0,120) + ")"; } };
|
| 2202 |
-
// Find all x.provider files
|
| 2203 |
-
const found = rc("find /app -name 'x.provider.*' -o -name 'twitter.provider.*' 2>/dev/null | grep -v node_modules | grep -v .next");
|
| 2204 |
-
const lines = ["=== X/Twitter Provider Files ===", found || "(none found)", ""];
|
| 2205 |
-
// Also search for 'external:' usage in backend source
|
| 2206 |
-
const externalUsage = rc("grep -r 'external:' /app/apps /app/libs --include='*.ts' --include='*.js' -n 2>/dev/null | grep -v node_modules | grep -v .next | grep -v dist | head -30");
|
| 2207 |
-
lines.push("=== 'external:' Redis key usage in source ===", externalUsage || "(none)", "");
|
| 2208 |
-
// Show content of x.provider file
|
| 2209 |
-
const xProviderFile = rc("find /app -name 'x.provider.ts' -not -path '*/node_modules/*' -not -path '*/.next/*' 2>/dev/null | head -1");
|
| 2210 |
-
if (xProviderFile && !xProviderFile.startsWith("(err")) {
|
| 2211 |
-
lines.push(`=== Content of ${xProviderFile} ===`);
|
| 2212 |
-
const content = rc(`cat "${xProviderFile}"`);
|
| 2213 |
-
lines.push(content.slice(0, 8000));
|
| 2214 |
-
}
|
| 2215 |
-
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
|
| 2216 |
-
res.end(lines.join("\n"));
|
| 2217 |
-
} catch(e) {
|
| 2218 |
-
res.writeHead(500, { "Content-Type": "text/plain" });
|
| 2219 |
-
res.end("Error: " + e.message);
|
| 2220 |
-
}
|
| 2221 |
-
return;
|
| 2222 |
-
}
|
| 2223 |
-
|
| 2224 |
-
// ββ /app/debug-logs β Temporary endpoint for debugging βββββββββββββββββββ
|
| 2225 |
-
if (pathname === "/app/debug-logs") {
|
| 2226 |
-
try {
|
| 2227 |
-
// tail helper β last N lines of a file
|
| 2228 |
-
const tailLines = (path, n) => {
|
| 2229 |
-
if (!fs.existsSync(path)) return `(no file: ${path})`;
|
| 2230 |
-
const lines = fs.readFileSync(path, "utf8").split("\n");
|
| 2231 |
-
return lines.slice(-n).join("\n");
|
| 2232 |
-
};
|
| 2233 |
-
|
| 2234 |
-
const errLog = tailLines("/root/.pm2/logs/backend-error.log", 150);
|
| 2235 |
-
const outLog = tailLines("/root/.pm2/logs/backend-out.log", 80);
|
| 2236 |
-
|
| 2237 |
-
// masked credential diagnostics β first 6 chars + length
|
| 2238 |
-
const maskCred = (val) => {
|
| 2239 |
-
if (!val) return "(not set)";
|
| 2240 |
-
const clean = val.trim();
|
| 2241 |
-
if (clean.length === 0) return "(empty)";
|
| 2242 |
-
return `${clean.slice(0, 6)}... (len=${clean.length})`;
|
| 2243 |
-
};
|
| 2244 |
-
const xKey = maskCred(process.env.X_API_KEY);
|
| 2245 |
-
const xSecret = maskCred(process.env.X_API_SECRET);
|
| 2246 |
-
const frontendUrl = process.env.FRONTEND_URL || "(not set)";
|
| 2247 |
-
|
| 2248 |
-
// Heuristic: X Consumer Key is ~25 alphanum, Consumer Secret ~50 alphanum.
|
| 2249 |
-
// OAuth 2.0 Client ID is base64url and usually longer (36+).
|
| 2250 |
-
const xKeyNote = (() => {
|
| 2251 |
-
const v = (process.env.X_API_KEY || "").trim();
|
| 2252 |
-
if (!v) return "MISSING";
|
| 2253 |
-
if (v.length > 35) return "β LOOKS LIKE OAuth2 CLIENT ID (too long) β should be ~25 chars";
|
| 2254 |
-
if (v.includes("=") || v.includes(":")) return "β LOOKS WRONG (contains = or :)";
|
| 2255 |
-
return "β length ok";
|
| 2256 |
-
})();
|
| 2257 |
-
const xSecretNote = (() => {
|
| 2258 |
-
const v = (process.env.X_API_SECRET || "").trim();
|
| 2259 |
-
if (!v) return "MISSING";
|
| 2260 |
-
if (v.length < 40) return "β TOO SHORT β Consumer Secret is ~50 chars";
|
| 2261 |
-
if (v.length > 70) return "β TOO LONG β might be OAuth2 Client Secret or Access Token Secret";
|
| 2262 |
-
if (v.includes("=")) return "β CONTAINS = β might be base64 encoded (wrong key type)";
|
| 2263 |
-
return "β length ok";
|
| 2264 |
-
})();
|
| 2265 |
-
|
| 2266 |
-
const credSection = [
|
| 2267 |
-
"=== X CREDENTIAL CHECK ===",
|
| 2268 |
-
`X_API_KEY : ${xKey} [${xKeyNote}]`,
|
| 2269 |
-
`X_API_SECRET : ${xSecret} [${xSecretNote}]`,
|
| 2270 |
-
`FRONTEND_URL : ${frontendUrl}`,
|
| 2271 |
-
`Expected callback URL: ${frontendUrl}/integrations/social/x`,
|
| 2272 |
-
].join("\n");
|
| 2273 |
-
|
| 2274 |
-
const cfProxyLog = fs.existsSync("/tmp/huggingpost-cloudflare-proxy.env")
|
| 2275 |
-
? fs.readFileSync("/tmp/huggingpost-cloudflare-proxy.env", "utf8").replace(/(SECRET|TOKEN)=\S+/gi, "$1=(redacted)")
|
| 2276 |
-
: "(file not found β proxy not deployed; CLOUDFLARE_WORKERS_TOKEN not set?)";
|
| 2277 |
-
|
| 2278 |
-
const cfProxySection = [
|
| 2279 |
-
"=== CLOUDFLARE PROXY STATUS ===",
|
| 2280 |
-
`CLOUDFLARE_WORKERS_TOKEN : ${process.env.CLOUDFLARE_WORKERS_TOKEN ? "β set (len=" + process.env.CLOUDFLARE_WORKERS_TOKEN.length + ")" : "β NOT SET β proxy never deployed, X traffic goes direct"}`,
|
| 2281 |
-
`CLOUDFLARE_PROXY_URL : ${process.env.CLOUDFLARE_PROXY_URL ? maskCred(process.env.CLOUDFLARE_PROXY_URL) : "β NOT SET β api.twitter.com calls NOT proxied"}`,
|
| 2282 |
-
`CLOUDFLARE_PROXY_DEBUG : ${process.env.CLOUDFLARE_PROXY_DEBUG || "(not set)"}`,
|
| 2283 |
-
`NODE_OPTIONS : ${process.env.NODE_OPTIONS || "(not set β cloudflare-proxy.js NOT loaded in backend!)"}`,
|
| 2284 |
-
`/opt/cloudflare-proxy.js : ${fs.existsSync("/opt/cloudflare-proxy.js") ? "β exists" : "β MISSING"}`,
|
| 2285 |
-
`/tmp/huggingpost-cloudflare-proxy.env contents:`,
|
| 2286 |
-
cfProxyLog,
|
| 2287 |
-
`cf-proxy-banner-shown : ${fs.existsSync("/tmp/.cf-proxy-banner-shown") ? "β (proxy init ran at least once)" : "(not found)"}`,
|
| 2288 |
-
].join("\n");
|
| 2289 |
-
|
| 2290 |
-
const xCallbackLog = fs.existsSync("/tmp/x-oauth-callbacks.log")
|
| 2291 |
-
? fs.readFileSync("/tmp/x-oauth-callbacks.log", "utf8")
|
| 2292 |
-
: "(no X callbacks recorded yet β try adding X channel now)";
|
| 2293 |
-
|
| 2294 |
-
// Redis check: look up login:* keys and verify codeVerifier structure.
|
| 2295 |
-
// Root cause check: oauth_token_secret must be present after split on ':'.
|
| 2296 |
-
let redisSection = "=== REDIS codeVerifier CHECK ===\n";
|
| 2297 |
-
try {
|
| 2298 |
-
const { execSync } = require("child_process");
|
| 2299 |
-
const rc = (cmd) => { try { return execSync(cmd, { timeout: 5000 }).toString().trim(); } catch(e) { return "(err: " + e.message.slice(0,60) + ")"; } };
|
| 2300 |
-
|
| 2301 |
-
const keyspace = rc("redis-cli -h 127.0.0.1 -p 6379 info keyspace 2>/dev/null");
|
| 2302 |
-
redisSection += `Redis keyspace:\n${keyspace || "(empty)"}\n\n`;
|
| 2303 |
-
|
| 2304 |
-
// Check DB 0 (default) and DB 1 for all keys
|
| 2305 |
-
// Also inspect external:* keys β Postiz stores codeVerifier under external:{oauth_token}
|
| 2306 |
-
const inspectKeys = (db, pattern) => {
|
| 2307 |
-
const dbFlag = db === 0 ? "" : `-n ${db}`;
|
| 2308 |
-
const keys = rc(`redis-cli -h 127.0.0.1 -p 6379 ${dbFlag} keys "${pattern}" 2>/dev/null`);
|
| 2309 |
-
if (!keys || keys.startsWith("(")) return ` ${pattern}: (none)\n`;
|
| 2310 |
-
let out = ` ${pattern} keys found:\n`;
|
| 2311 |
-
for (const key of keys.split("\n").filter(Boolean).slice(0, 5)) {
|
| 2312 |
-
const type = rc(`redis-cli -h 127.0.0.1 -p 6379 ${dbFlag} type "${key}"`);
|
| 2313 |
-
const ttl = rc(`redis-cli -h 127.0.0.1 -p 6379 ${dbFlag} ttl "${key}" 2>/dev/null`);
|
| 2314 |
-
let val;
|
| 2315 |
-
if (type === "hash") {
|
| 2316 |
-
val = rc(`redis-cli -h 127.0.0.1 -p 6379 ${dbFlag} hgetall "${key}" 2>/dev/null`);
|
| 2317 |
-
} else if (type === "string") {
|
| 2318 |
-
val = rc(`redis-cli -h 127.0.0.1 -p 6379 ${dbFlag} get "${key}" 2>/dev/null`);
|
| 2319 |
-
} else {
|
| 2320 |
-
out += ` ${key} [type=${type} TTL=${ttl}]: (unhandled type)\n`; continue;
|
| 2321 |
-
}
|
| 2322 |
-
if (!val) { out += ` ${key} [type=${type} TTL=${ttl}]: VALUE IS EMPTY STRING\n`; continue; }
|
| 2323 |
-
if (type === "hash") {
|
| 2324 |
-
out += ` ${key} [type=hash TTL=${ttl}]:\n ${val.replace(/\n/g, "\n ").slice(0,300)}\n`;
|
| 2325 |
-
continue;
|
| 2326 |
-
}
|
| 2327 |
-
const colonIdx = val.indexOf(":");
|
| 2328 |
-
const token = colonIdx > 0 ? val.slice(0, colonIdx) : "";
|
| 2329 |
-
const secret = colonIdx > 0 ? val.slice(colonIdx + 1) : "";
|
| 2330 |
-
const extraColons = (val.match(/:/g) || []).length - 1;
|
| 2331 |
-
const note = colonIdx < 0 ? "β NO COLON" : extraColons > 0 ? `β ${extraColons} extra colon(s)` : secret.length < 30 ? "β secret short" : "β ok";
|
| 2332 |
-
out += ` ${key} [type=string TTL=${ttl}s]\n token: ${token.slice(0,12)}... (len=${token.length})\n secret: ${secret.slice(0,12)}... (len=${secret.length})\n note: ${note}\n`;
|
| 2333 |
-
}
|
| 2334 |
-
return out;
|
| 2335 |
-
};
|
| 2336 |
-
|
| 2337 |
-
for (const db of [0, 1, 2]) {
|
| 2338 |
-
const dbFlag = db === 0 ? "" : `-n ${db}`;
|
| 2339 |
-
const allKeys = rc(`redis-cli -h 127.0.0.1 -p 6379 ${dbFlag} keys "*" 2>/dev/null`);
|
| 2340 |
-
redisSection += `DB ${db}: total keys preview: ${allKeys ? allKeys.split("\n").slice(0,8).join(", ") : "(none)"}\n`;
|
| 2341 |
-
redisSection += inspectKeys(db, "login:*");
|
| 2342 |
-
redisSection += inspectKeys(db, "external:*");
|
| 2343 |
-
redisSection += "\n";
|
| 2344 |
-
}
|
| 2345 |
-
} catch (e) {
|
| 2346 |
-
redisSection += `(redis-cli error: ${e.message})`;
|
| 2347 |
-
}
|
| 2348 |
-
|
| 2349 |
-
// Grep Postiz source for Redis key patterns + x.provider content
|
| 2350 |
-
let sourceSection = "=== POSTIZ SOURCE: Redis key patterns ===\n";
|
| 2351 |
-
try {
|
| 2352 |
-
const { execSync } = require("child_process");
|
| 2353 |
-
const rc2 = (cmd) => { try { return execSync(cmd, { timeout: 10000 }).toString().trim(); } catch(e) { return "(err: " + e.message.slice(0,80) + ")"; } };
|
| 2354 |
-
const grepRedis = rc2("grep -rn 'ioRedis.set\\|redis.set' /app/apps/backend/src /app/libs --include='*.ts' 2>/dev/null | grep -v node_modules | grep -E 'login:|external:|refresh:' | head -20");
|
| 2355 |
-
sourceSection += `Redis set calls with login:/external: prefix:\n${grepRedis || "(none found)"}\n\n`;
|
| 2356 |
-
// Find and show x.provider.ts generateAuthUrl
|
| 2357 |
-
const xProvFile = rc2("find /app -name 'x.provider.ts' -not -path '*/node_modules/*' -not -path '*/.next/*' 2>/dev/null | head -1");
|
| 2358 |
-
sourceSection += `x.provider.ts path: ${xProvFile || "(not found)"}\n`;
|
| 2359 |
-
if (xProvFile && !xProvFile.startsWith("(err")) {
|
| 2360 |
-
const xContent = rc2(`grep -n 'generateAuthUrl\\|codeVerifier\\|oauth_token\\|ioRedis\\|redis' "${xProvFile}" | head -40`);
|
| 2361 |
-
sourceSection += `x.provider.ts relevant lines:\n${xContent || "(none)"}\n`;
|
| 2362 |
-
}
|
| 2363 |
-
// Also check integrations controller
|
| 2364 |
-
const ctrlSearch = rc2("grep -rn 'external:\\|login:\\|codeVerifier\\|generateAuthUrl' /app/apps/backend/src --include='*.ts' 2>/dev/null | grep -v node_modules | head -30");
|
| 2365 |
-
sourceSection += `\nIntegrations controller Redis/codeVerifier usage:\n${ctrlSearch || "(none)"}\n`;
|
| 2366 |
-
} catch(e) {
|
| 2367 |
-
sourceSection += `(error: ${e.message})`;
|
| 2368 |
-
}
|
| 2369 |
-
|
| 2370 |
-
const out = [
|
| 2371 |
-
credSection,
|
| 2372 |
-
"",
|
| 2373 |
-
cfProxySection,
|
| 2374 |
-
"",
|
| 2375 |
-
"=== X OAUTH CALLBACKS (from health-server) ===",
|
| 2376 |
-
xCallbackLog,
|
| 2377 |
-
"",
|
| 2378 |
-
redisSection,
|
| 2379 |
-
"",
|
| 2380 |
-
sourceSection,
|
| 2381 |
-
"",
|
| 2382 |
-
"=== BACKEND ERROR LOG (last 150 lines) ===",
|
| 2383 |
-
errLog,
|
| 2384 |
-
"",
|
| 2385 |
-
"=== BACKEND OUT LOG (last 80 lines) ===",
|
| 2386 |
-
outLog,
|
| 2387 |
-
].join("\n");
|
| 2388 |
-
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
|
| 2389 |
-
res.end(out);
|
| 2390 |
-
} catch (e) {
|
| 2391 |
-
res.writeHead(500, { "Content-Type": "text/plain" });
|
| 2392 |
-
res.end("Error reading logs: " + e.message);
|
| 2393 |
-
}
|
| 2394 |
-
return;
|
| 2395 |
-
}
|
| 2396 |
|
| 2397 |
// ββ Dashboard at exact / βββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 2398 |
if (pathname === "/" || pathname === "") {
|
|
@@ -2472,43 +2164,6 @@ const server = http.createServer((req, res) => {
|
|
| 2472 |
// the /app basePath prefix (e.g. /launches, /analytics, /api/...).
|
| 2473 |
// Redirect those here rather than 404-ing so the browser lands correctly.
|
| 2474 |
|
| 2475 |
-
// Log OAuth callbacks for debugging.
|
| 2476 |
-
if (pathname === "/integrations/social/x") {
|
| 2477 |
-
const oauthToken = parsedUrl.searchParams.get("oauth_token") || "(missing)";
|
| 2478 |
-
const oauthVerifier = parsedUrl.searchParams.get("oauth_verifier") || "(MISSING!)";
|
| 2479 |
-
const denied = parsedUrl.searchParams.get("denied");
|
| 2480 |
-
const ts = new Date().toISOString();
|
| 2481 |
-
const msg = denied
|
| 2482 |
-
? `[${ts}] X OAuth DENIED β denied=${denied}`
|
| 2483 |
-
: `[${ts}] X OAuth callback β oauth_token=${oauthToken.slice(0,20)}... oauth_verifier=${oauthVerifier === "(MISSING!)" ? "(MISSING! β X did not send verifier)" : oauthVerifier.slice(0,10) + "... (present β)"}`;
|
| 2484 |
-
console.log(msg);
|
| 2485 |
-
// Append to a file the debug-logs endpoint can read.
|
| 2486 |
-
try {
|
| 2487 |
-
fs.appendFileSync("/tmp/x-oauth-callbacks.log", msg + "\n");
|
| 2488 |
-
} catch(_) {}
|
| 2489 |
-
|
| 2490 |
-
// Snapshot Redis 100ms, 500ms, 1500ms after callback to catch login: key before it's deleted
|
| 2491 |
-
if (!denied && oauthToken !== "(missing)") {
|
| 2492 |
-
const snapshots = [];
|
| 2493 |
-
const doSnap = (delay) => setTimeout(() => {
|
| 2494 |
-
try {
|
| 2495 |
-
const { execSync } = require("child_process");
|
| 2496 |
-
const rc = (cmd) => { try { return execSync(cmd, { timeout: 3000 }).toString().trim(); } catch(e) { return "(err)"; } };
|
| 2497 |
-
const loginVal = rc(`redis-cli -h 127.0.0.1 -p 6379 get "login:${oauthToken}" 2>/dev/null`);
|
| 2498 |
-
const extVal = rc(`redis-cli -h 127.0.0.1 -p 6379 get "external:${oauthToken}" 2>/dev/null`);
|
| 2499 |
-
const loginTtl = rc(`redis-cli -h 127.0.0.1 -p 6379 ttl "login:${oauthToken}" 2>/dev/null`);
|
| 2500 |
-
const extTtl = rc(`redis-cli -h 127.0.0.1 -p 6379 ttl "external:${oauthToken}" 2>/dev/null`);
|
| 2501 |
-
const snap = ` [+${delay}ms] login:TOKEN=${loginVal ? loginVal.slice(0,40) + "...(len=" + loginVal.length + ")" : "(empty/missing)"} ttl=${loginTtl} | external:TOKEN=${extVal ? extVal.slice(0,40) + "...(len=" + extVal.length + ")" : "(empty/missing)"} ttl=${extTtl}`;
|
| 2502 |
-
snapshots.push(snap);
|
| 2503 |
-
fs.appendFileSync("/tmp/x-oauth-callbacks.log", snap + "\n");
|
| 2504 |
-
} catch(_) {}
|
| 2505 |
-
}, delay);
|
| 2506 |
-
doSnap(100);
|
| 2507 |
-
doSnap(500);
|
| 2508 |
-
doSnap(1500);
|
| 2509 |
-
}
|
| 2510 |
-
}
|
| 2511 |
-
|
| 2512 |
res.writeHead(302, {
|
| 2513 |
Location: "/app" + pathname + (parsedUrl.search || ""),
|
| 2514 |
});
|
|
|
|
| 2085 |
return;
|
| 2086 |
}
|
| 2087 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2088 |
|
| 2089 |
// ββ Dashboard at exact / βββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 2090 |
if (pathname === "/" || pathname === "") {
|
|
|
|
| 2164 |
// the /app basePath prefix (e.g. /launches, /analytics, /api/...).
|
| 2165 |
// Redirect those here rather than 404-ing so the browser lands correctly.
|
| 2166 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2167 |
res.writeHead(302, {
|
| 2168 |
Location: "/app" + pathname + (parsedUrl.search || ""),
|
| 2169 |
});
|