Container-ready via docker/ compose (frontend nginx + backend Node). Compose adjusted for Coolify on the prod server: frontend uses expose:80 (no host binding — host 8080 is taken by the Coolify proxy; Traefik routes visigine.de), backend ALLOWED_ORIGINS=https://visigine.de. Secrets stay in server/.env (git-ignored); see server/.env.example. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
44 lines
1.2 KiB
JavaScript
44 lines
1.2 KiB
JavaScript
// Group F — probe the target with AI bot user agents to detect WAF/CDN blocking.
|
|
const BLOCKED_STATUSES = new Set([403, 429, 451, 503, 0])
|
|
|
|
const PROBES = [
|
|
{
|
|
id: 'ai-reach.claudebot',
|
|
title: 'ClaudeBot wird blockiert (Cloudflare oder Firewall)',
|
|
userAgent: 'ClaudeBot/1.0 (+https://www.anthropic.com)',
|
|
},
|
|
{
|
|
id: 'ai-reach.gptbot',
|
|
title: 'GPTBot wird blockiert (Cloudflare oder Firewall)',
|
|
userAgent: 'GPTBot/1.0 (+https://openai.com/gptbot)',
|
|
},
|
|
]
|
|
|
|
export async function runAiReachabilityChecks({ targetUrl, mainStatus, fetchPage }) {
|
|
// If the baseline fetch already failed, the probes would be misleading —
|
|
// mark both as failed but skip the network calls.
|
|
if (mainStatus !== 200) {
|
|
return PROBES.map((p) => ({
|
|
id: p.id,
|
|
title: p.title,
|
|
severity: 'high',
|
|
passed: false,
|
|
}))
|
|
}
|
|
|
|
const probeResults = await Promise.all(
|
|
PROBES.map((p) => fetchPage(targetUrl, { userAgent: p.userAgent }))
|
|
)
|
|
|
|
return PROBES.map((p, i) => {
|
|
const status = probeResults[i].status
|
|
const blocked = BLOCKED_STATUSES.has(status)
|
|
return {
|
|
id: p.id,
|
|
title: p.title,
|
|
severity: 'high',
|
|
passed: !blocked,
|
|
}
|
|
})
|
|
}
|