feat(platform): add Discord onboarding and Velocity gate

This commit is contained in:
dmg
2026-08-01 13:45:18 -04:00
parent 9d305e5dc9
commit c5de0a1810
80 changed files with 4840 additions and 1572 deletions
+51
View File
@@ -0,0 +1,51 @@
import { exchangeMagicLink, InvalidLoginCodeError, SESSION_COOKIE_NAME } from "@minecraft-account-manager/auth";
import { createAuthRepository, ipObservations, recordEvent } from "@minecraft-account-manager/database";
import { getClientIp } from "@minecraft-account-manager/network";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import { db } from "@/lib/database";
export async function GET(request: NextRequest) {
const code = request.nextUrl.searchParams.get("code") ?? "";
try {
const result = await exchangeMagicLink(code, {
repository: createAuthRepository(db),
});
const ipAddress = getClientIp(request.headers, process.env.TRUST_PROXY === "true");
await Promise.all([
recordEvent(db, {
type: "games.minecraft.account-manager.auth.magic-link.consumed",
source: "/web/auth/discord",
subject: `user/${result.user.id}`,
actorUserId: result.user.id,
ipAddress: ipAddress ?? undefined,
data: { isNewUser: result.isNewUser },
}),
ipAddress
? db.insert(ipObservations).values({
userId: result.user.id,
source: "web",
ipAddress,
classification: "unknown",
})
: Promise.resolve(),
]);
const destination = result.user.firstName ? "/account" : "/welcome";
const response = NextResponse.redirect(new URL(destination, request.url));
response.cookies.set(SESSION_COOKIE_NAME, result.sessionToken, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
path: "/",
expires: result.sessionExpiresAt,
});
return response;
} catch (error) {
if (error instanceof InvalidLoginCodeError) {
return NextResponse.redirect(new URL("/auth/error", request.url));
}
throw error;
}
}