This commit is contained in:
2025-12-04 11:54:23 +01:00
parent c0fdae3d3c
commit 9d5caa87f3
83 changed files with 1579 additions and 2151 deletions

View File

@@ -0,0 +1,41 @@
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';
import { getAuthService } from '../../../../lib/auth';
const SESSION_COOKIE = 'gp_demo_session';
const STATE_COOKIE = 'gp_demo_auth_state';
export async function GET(request: Request) {
const url = new URL(request.url);
const code = url.searchParams.get('code') ?? undefined;
const state = url.searchParams.get('state') ?? undefined;
const returnTo = url.searchParams.get('returnTo') ?? undefined;
if (!code || !state) {
return NextResponse.redirect('/auth/iracing');
}
const cookieStore = await cookies();
const storedState = cookieStore.get(STATE_COOKIE)?.value;
if (!storedState || storedState !== state) {
return NextResponse.redirect('/auth/iracing');
}
const authService = getAuthService();
const session = await authService.loginWithIracingCallback({ code, state, returnTo });
cookieStore.set(SESSION_COOKIE, JSON.stringify(session), {
httpOnly: true,
sameSite: 'lax',
path: '/',
secure: process.env.NODE_ENV === 'production',
});
cookieStore.delete(STATE_COOKIE);
const redirectTarget = returnTo || '/dashboard';
const absoluteRedirect = new URL(redirectTarget, url.origin).toString();
return NextResponse.redirect(absoluteRedirect);
}

View File

@@ -0,0 +1,30 @@
import Link from 'next/link';
interface IracingAuthPageProps {
searchParams: Promise<{
returnTo?: string;
}>;
}
export default async function IracingAuthPage({ searchParams }: IracingAuthPageProps) {
const params = await searchParams;
const returnTo = params.returnTo ?? '/dashboard';
const startUrl = `/auth/iracing/start?returnTo=${encodeURIComponent(returnTo)}`;
return (
<main className="min-h-screen flex items-center justify-center bg-deep-graphite">
<div className="max-w-md w-full px-6 py-8 bg-iron-gray/80 rounded-lg border border-white/10 shadow-xl">
<h1 className="text-2xl font-semibold text-white mb-4">Authenticate with iRacing</h1>
<p className="text-sm text-gray-300 mb-6">
Connect a demo iRacing identity to explore the GridPilot dashboard with seeded data.
</p>
<Link
href={startUrl}
className="inline-flex items-center justify-center px-4 py-2 rounded-md bg-primary-blue text-sm font-medium text-white hover:bg-primary-blue/90 transition-colors w-full"
>
Start iRacing demo login
</Link>
</div>
</main>
);
}

View File

@@ -0,0 +1,23 @@
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';
import { getAuthService } from '../../../../lib/auth';
export async function GET(request: Request) {
const url = new URL(request.url);
const returnTo = url.searchParams.get('returnTo') ?? undefined;
const authService = getAuthService();
const { redirectUrl, state } = await authService.startIracingAuthRedirect(returnTo);
const cookieStore = await cookies();
cookieStore.set('gp_demo_auth_state', state, {
httpOnly: true,
sameSite: 'lax',
path: '/',
secure: process.env.NODE_ENV === 'production',
});
const absoluteRedirect = new URL(redirectUrl, url.origin).toString();
return NextResponse.redirect(absoluteRedirect);
}

View File

@@ -0,0 +1,11 @@
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';
export async function POST(request: Request) {
const cookieStore = await cookies();
cookieStore.delete('gp_demo_session');
const url = new URL(request.url);
const redirectUrl = new URL('/', url.origin);
return NextResponse.redirect(redirectUrl);
}