Files
buzz/app/(ui)/rubgram/callback/route.ts
T

130 lines
3.8 KiB
TypeScript
Executable File

// app/api/auth/discord/callback/route.ts
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { type NextRequest, NextResponse } from "next/server";
import { stringify } from "yaml";
import { db } from "@/lib/db"; // your drizzle db instance
import { endgameDiscord } from "@/lib/db/schema"; // your schema
const DISCORD_CLIENT_ID = process.env.DISCORD_CLIENT_ID!;
const DISCORD_CLIENT_SECRET = process.env.DISCORD_CLIENT_SECRET!;
const DISCORD_REDIRECT_URI = process.env.DISCORD_REDIRECT_URI!;
const DISCORD_GUILD_ID = process.env.DISCORD_GUILD_ID!;
const DISCORD_BOT_TOKEN = process.env.DISCORD_BOT_TOKEN!;
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const cookieStore = await cookies();
const code = searchParams.get("code");
if (!code) {
if (searchParams.has("error")) redirect("/rubgram");
return NextResponse.json({ error: "No code provided" }, { status: 400 });
}
try {
// Exchange code for access token
const tokenResponse = await fetch("https://discord.com/api/oauth2/token", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
// Authorization: `Basic ${btoa(`${DISCORD_CLIENT_ID}:${DISCORD_CLIENT_SECRET}`)}`,
},
body: new URLSearchParams({
client_id: DISCORD_CLIENT_ID,
client_secret: DISCORD_CLIENT_SECRET,
grant_type: "authorization_code",
code,
redirect_uri: DISCORD_REDIRECT_URI,
}),
});
if (!tokenResponse.ok) {
throw new Error(
`Failed to exchange code for token: ${tokenResponse.status}\n${stringify(await tokenResponse.json())}`,
);
}
const tokenData = await tokenResponse.json();
const accessToken = tokenData.access_token;
// Get user information
const userResponse = await fetch("https://discord.com/api/users/@me", {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
if (!userResponse.ok) {
throw new Error("Failed to fetch user data");
}
const userData = await userResponse.json();
// Check if user is in guild
const guildMemberResponse = await fetch(
`https://discord.com/api/users/@me/guilds/${DISCORD_GUILD_ID}/member`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
);
const isInGuild = guildMemberResponse.ok;
// If not in guild, add them using bot token
if (!isInGuild) {
const addMemberResponse = await fetch(
`https://discord.com/api/guilds/${DISCORD_GUILD_ID}/members/${userData.id}`,
{
method: "PUT",
headers: {
Authorization: `Bot ${DISCORD_BOT_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
access_token: accessToken,
}),
},
);
if (!addMemberResponse.ok) {
console.error("Failed to add user to guild");
// Continue anyway - they might join manually
}
}
// Store/update user in database
const display = userData.global_name || userData.username;
const [{ token }] = await db
.insert(endgameDiscord)
.values({
uid: userData.id,
display,
username: userData.username,
})
.onConflictDoUpdate({
target: endgameDiscord.uid,
set: {
display,
username: userData.username,
},
})
.returning({ token: endgameDiscord.token });
// Redirect to success page or dashboard
cookieStore.set("discord", token);
} catch (error) {
console.error("Discord OAuth error:", (error as Error)?.message || error);
return NextResponse.json(
{ error: "Authentication failed" },
{ status: 500 },
);
}
redirect("/rubgram");
}