(req: NextRequest)
| 25 | * This is a DRY RUN — it reports suspects via email and never bans anyone. |
| 26 | */ |
| 27 | export async function POST(req: NextRequest) { |
| 28 | const secret = env.BOT_SWEEP_SECRET |
| 29 | if (!secret) { |
| 30 | return NextResponse.json( |
| 31 | { error: 'bot-sweep not configured (BOT_SWEEP_SECRET missing)' }, |
| 32 | { status: 503 }, |
| 33 | ) |
| 34 | } |
| 35 | |
| 36 | const authHeader = req.headers.get('Authorization') ?? '' |
| 37 | const expected = `Bearer ${secret}` |
| 38 | const a = Buffer.from(authHeader) |
| 39 | const b = Buffer.from(expected) |
| 40 | if (a.length !== b.length || !timingSafeEqual(a, b)) { |
| 41 | return NextResponse.json({ error: 'unauthorized' }, { status: 401 }) |
| 42 | } |
| 43 | |
| 44 | try { |
| 45 | const report = await identifyBotSuspects({ logger }) |
| 46 | const { subject, message } = formatSweepReport(report) |
| 47 | |
| 48 | // Second-pass agent review. Advisory only — if it fails or returns |
| 49 | // null we still send the rule-based report. Lead with the agent's |
| 50 | // tiered recommendation since that's the actionable part; raw |
| 51 | // rule-based data follows as supporting detail. |
| 52 | const agentReview = await reviewSuspects({ report, logger }) |
| 53 | const fullMessage = agentReview |
| 54 | ? `=== AGENT REVIEW (Claude Sonnet 4.6) ===\n\n${agentReview}\n\n=== RAW RULE-BASED DATA ===\n\n${message}` |
| 55 | : message |
| 56 | |
| 57 | const emailResult = await sendBasicEmail({ |
| 58 | email: REPORT_RECIPIENT, |
| 59 | data: { subject, message: fullMessage }, |
| 60 | logger, |
| 61 | }) |
| 62 | |
| 63 | if (!emailResult.success) { |
| 64 | logger.error( |
| 65 | { error: emailResult.error }, |
| 66 | 'Failed to email bot-sweep report', |
| 67 | ) |
| 68 | } |
| 69 | |
| 70 | return NextResponse.json({ |
| 71 | ok: true, |
| 72 | totalSessions: report.totalSessions, |
| 73 | suspectCount: report.suspects.length, |
| 74 | highTierCount: report.suspects.filter((s) => s.tier === 'high').length, |
| 75 | emailSent: emailResult.success, |
| 76 | agentReview, |
| 77 | }) |
| 78 | } catch (error) { |
| 79 | logger.error({ error }, 'bot-sweep failed') |
| 80 | return NextResponse.json({ error: 'sweep failed' }, { status: 500 }) |
| 81 | } |
| 82 | } |
nothing calls this directly
no test coverage detected