Sessions 5-7a: 955 tests, deployment ready
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getServiceRoleSupabase } from '@/lib/supabase';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
interface Body {
|
||||
sport?: 'NBA' | 'MLB' | 'WNBA';
|
||||
player?: string;
|
||||
stat?: string;
|
||||
line?: number;
|
||||
direction?: 'over' | 'under';
|
||||
}
|
||||
|
||||
const VALID_SPORTS = new Set(['NBA', 'MLB', 'WNBA']);
|
||||
const VALID_DIRS = new Set(['over', 'under']);
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
let body: Body;
|
||||
try {
|
||||
body = (await req.json()) as Body;
|
||||
} catch {
|
||||
return NextResponse.json({ error: 'Invalid JSON.' }, { status: 400 });
|
||||
}
|
||||
|
||||
if (
|
||||
!body.sport || !VALID_SPORTS.has(body.sport) ||
|
||||
!body.player || typeof body.player !== 'string' ||
|
||||
!body.stat ||
|
||||
typeof body.line !== 'number' || !Number.isFinite(body.line) ||
|
||||
!body.direction || !VALID_DIRS.has(body.direction)
|
||||
) {
|
||||
return NextResponse.json({ error: 'Missing or invalid leg fields.' }, { status: 400 });
|
||||
}
|
||||
|
||||
const sb = getServiceRoleSupabase();
|
||||
if (!sb) return NextResponse.json({ ok: true, persisted: false });
|
||||
|
||||
// RPC increments scan or parlay counter atomically.
|
||||
await sb.rpc('increment_parlay_leg_frequency', {
|
||||
p_player: body.player,
|
||||
p_stat: body.stat,
|
||||
p_line: body.line,
|
||||
p_dir: body.direction,
|
||||
p_sport: body.sport,
|
||||
p_scan_delta: 0,
|
||||
p_parlay_delta: 1,
|
||||
});
|
||||
|
||||
return NextResponse.json({ ok: true });
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getUserFromRequest, jsonError } from '@/lib/auth-helpers';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
const BACKEND_URL = process.env.BACKEND_URL || 'http://localhost:3000';
|
||||
|
||||
interface Leg {
|
||||
sport?: 'NBA' | 'MLB' | 'WNBA';
|
||||
player: string;
|
||||
stat_type: string;
|
||||
line: number;
|
||||
direction: 'over' | 'under';
|
||||
}
|
||||
|
||||
interface Body {
|
||||
legs: Leg[];
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const user = await getUserFromRequest(req);
|
||||
if (!user) return jsonError(401, 'Log in to grade parlays.');
|
||||
|
||||
let body: Body;
|
||||
try {
|
||||
body = (await req.json()) as Body;
|
||||
} catch {
|
||||
return jsonError(400, 'Invalid JSON.');
|
||||
}
|
||||
|
||||
if (!Array.isArray(body.legs) || body.legs.length < 2 || body.legs.length > 12) {
|
||||
return jsonError(400, 'Send 2–12 legs.');
|
||||
}
|
||||
|
||||
try {
|
||||
const upstream = await fetch(`${BACKEND_URL}/api/scan/parlay`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(req.headers.get('authorization') ? { Authorization: req.headers.get('authorization')! } : {}),
|
||||
},
|
||||
body: JSON.stringify({ legs: body.legs }),
|
||||
});
|
||||
|
||||
const data = await upstream.json().catch(() => ({}));
|
||||
if (!upstream.ok) {
|
||||
return NextResponse.json(
|
||||
{ error: (data as { error?: string }).error || 'The engine hit a wall on this parlay.' },
|
||||
{ status: upstream.status },
|
||||
);
|
||||
}
|
||||
return NextResponse.json(data);
|
||||
} catch {
|
||||
return jsonError(502, 'The engine hit a wall on this parlay.');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user