Prettier format pending files
This commit is contained in:
parent
34f8661d1b
commit
7f4dc5c67b
8 changed files with 131 additions and 72 deletions
|
|
@ -49,19 +49,22 @@ export default async function handler(req: any, res: any) {
|
|||
const redirectUri = `${process.env.VITE_API_BASE || "https://aethex.dev"}/api/discord/oauth/callback`;
|
||||
|
||||
// Exchange code for access token
|
||||
const tokenResponse = await fetch("https://discord.com/api/v10/oauth2/token", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
const tokenResponse = await fetch(
|
||||
"https://discord.com/api/v10/oauth2/token",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
grant_type: "authorization_code",
|
||||
code,
|
||||
redirect_uri: redirectUri,
|
||||
}).toString(),
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
grant_type: "authorization_code",
|
||||
code,
|
||||
redirect_uri: redirectUri,
|
||||
}).toString(),
|
||||
});
|
||||
);
|
||||
|
||||
if (!tokenResponse.ok) {
|
||||
const errorData = await tokenResponse.json();
|
||||
|
|
@ -115,19 +118,23 @@ export default async function handler(req: any, res: any) {
|
|||
} else {
|
||||
// Create new user
|
||||
// First create auth user
|
||||
const { data: authData, error: authError } = await supabase.auth.admin.createUser({
|
||||
email: discordUser.email,
|
||||
email_confirm: true,
|
||||
user_metadata: {
|
||||
full_name: discordUser.username,
|
||||
avatar_url: discordUser.avatar
|
||||
? `https://cdn.discordapp.com/avatars/${discordUser.id}/${discordUser.avatar}.png`
|
||||
: null,
|
||||
},
|
||||
});
|
||||
const { data: authData, error: authError } =
|
||||
await supabase.auth.admin.createUser({
|
||||
email: discordUser.email,
|
||||
email_confirm: true,
|
||||
user_metadata: {
|
||||
full_name: discordUser.username,
|
||||
avatar_url: discordUser.avatar
|
||||
? `https://cdn.discordapp.com/avatars/${discordUser.id}/${discordUser.avatar}.png`
|
||||
: null,
|
||||
},
|
||||
});
|
||||
|
||||
if (authError || !authData.user) {
|
||||
console.error("[Discord OAuth] Auth user creation failed:", authError);
|
||||
console.error(
|
||||
"[Discord OAuth] Auth user creation failed:",
|
||||
authError,
|
||||
);
|
||||
return res.redirect("/login?error=auth_create");
|
||||
}
|
||||
|
||||
|
|
@ -135,17 +142,22 @@ export default async function handler(req: any, res: any) {
|
|||
isNewUser = true;
|
||||
|
||||
// Create user profile
|
||||
const { error: profileError } = await supabase.from("user_profiles").insert({
|
||||
id: userId,
|
||||
email: discordUser.email,
|
||||
full_name: discordUser.username,
|
||||
avatar_url: discordUser.avatar
|
||||
? `https://cdn.discordapp.com/avatars/${discordUser.id}/${discordUser.avatar}.png`
|
||||
: null,
|
||||
});
|
||||
const { error: profileError } = await supabase
|
||||
.from("user_profiles")
|
||||
.insert({
|
||||
id: userId,
|
||||
email: discordUser.email,
|
||||
full_name: discordUser.username,
|
||||
avatar_url: discordUser.avatar
|
||||
? `https://cdn.discordapp.com/avatars/${discordUser.id}/${discordUser.avatar}.png`
|
||||
: null,
|
||||
});
|
||||
|
||||
if (profileError) {
|
||||
console.error("[Discord OAuth] Profile creation failed:", profileError);
|
||||
console.error(
|
||||
"[Discord OAuth] Profile creation failed:",
|
||||
profileError,
|
||||
);
|
||||
return res.redirect("/login?error=profile_create");
|
||||
}
|
||||
}
|
||||
|
|
@ -164,9 +176,10 @@ export default async function handler(req: any, res: any) {
|
|||
}
|
||||
|
||||
// Generate session token
|
||||
const { data: sessionData, error: sessionError } = await supabase.auth.admin.createSession({
|
||||
user_id: userId,
|
||||
});
|
||||
const { data: sessionData, error: sessionError } =
|
||||
await supabase.auth.admin.createSession({
|
||||
user_id: userId,
|
||||
});
|
||||
|
||||
if (sessionError || !sessionData.session) {
|
||||
console.error("[Discord OAuth] Session creation failed:", sessionError);
|
||||
|
|
@ -174,17 +187,25 @@ export default async function handler(req: any, res: any) {
|
|||
}
|
||||
|
||||
// Redirect to next page with session
|
||||
const nextPath = state && typeof state === "string" && state.startsWith("/") ? state : isNewUser ? "/onboarding" : "/dashboard";
|
||||
const redirectUrl = new URL(nextPath, process.env.VITE_API_BASE || "https://aethex.dev");
|
||||
|
||||
const nextPath =
|
||||
state && typeof state === "string" && state.startsWith("/")
|
||||
? state
|
||||
: isNewUser
|
||||
? "/onboarding"
|
||||
: "/dashboard";
|
||||
const redirectUrl = new URL(
|
||||
nextPath,
|
||||
process.env.VITE_API_BASE || "https://aethex.dev",
|
||||
);
|
||||
|
||||
// Set cookies for session (similar to how Supabase does it)
|
||||
res.setHeader(
|
||||
"Set-Cookie",
|
||||
`sb-access-token=${sessionData.session.access_token}; Path=/; HttpOnly; Secure; SameSite=Lax`
|
||||
`sb-access-token=${sessionData.session.access_token}; Path=/; HttpOnly; Secure; SameSite=Lax`,
|
||||
);
|
||||
res.setHeader(
|
||||
"Set-Cookie",
|
||||
`sb-refresh-token=${sessionData.session.refresh_token}; Path=/; HttpOnly; Secure; SameSite=Lax`
|
||||
`sb-refresh-token=${sessionData.session.refresh_token}; Path=/; HttpOnly; Secure; SameSite=Lax`,
|
||||
);
|
||||
|
||||
res.redirect(redirectUrl.toString());
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ export default function handler(req: any, res: any) {
|
|||
}
|
||||
|
||||
const redirectUri = `${process.env.VITE_API_BASE || "https://aethex.dev"}/api/discord/oauth/callback`;
|
||||
|
||||
|
||||
// Get the next URL from query params (where to redirect after login)
|
||||
const next = req.query.state || "/dashboard";
|
||||
|
||||
|
||||
const params = new URLSearchParams({
|
||||
client_id: clientId,
|
||||
redirect_uri: redirectUri,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ export default async function handler(req: any, res: any) {
|
|||
const { verification_code, user_id } = req.body;
|
||||
|
||||
if (!verification_code || !user_id) {
|
||||
return res.status(400).json({ message: "Missing verification code or user ID" });
|
||||
return res
|
||||
.status(400)
|
||||
.json({ message: "Missing verification code or user ID" });
|
||||
}
|
||||
|
||||
const supabaseUrl = process.env.VITE_SUPABASE_URL;
|
||||
|
|
@ -35,7 +37,8 @@ export default async function handler(req: any, res: any) {
|
|||
|
||||
if (verifyError || !verification) {
|
||||
return res.status(400).json({
|
||||
message: "Invalid or expired verification code. Please try /verify again.",
|
||||
message:
|
||||
"Invalid or expired verification code. Please try /verify again.",
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -50,7 +53,8 @@ export default async function handler(req: any, res: any) {
|
|||
|
||||
if (existingLink && existingLink.user_id !== user_id) {
|
||||
return res.status(400).json({
|
||||
message: "This Discord account is already linked to another AeThex account.",
|
||||
message:
|
||||
"This Discord account is already linked to another AeThex account.",
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -63,7 +67,9 @@ export default async function handler(req: any, res: any) {
|
|||
|
||||
if (linkError) {
|
||||
console.error("[Discord Verify] Link creation failed:", linkError);
|
||||
return res.status(500).json({ message: "Failed to link Discord account" });
|
||||
return res
|
||||
.status(500)
|
||||
.json({ message: "Failed to link Discord account" });
|
||||
}
|
||||
|
||||
// Delete used verification code
|
||||
|
|
|
|||
|
|
@ -166,7 +166,6 @@ export default function ArmSwitcher() {
|
|||
onClose={() => setIsModalOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,8 @@ const ARM_DESCRIPTIONS: Record<string, string> = {
|
|||
gameforge:
|
||||
"Game Development - Shipping games at the speed of thought with monthly cycles",
|
||||
corp: "Enterprise Solutions - Consulting for large-scale transformations",
|
||||
foundation: "Community & Education - Building open-source and talent pipelines",
|
||||
foundation:
|
||||
"Community & Education - Building open-source and talent pipelines",
|
||||
devlink:
|
||||
"Professional Networking - LinkedIn for Roblox developers and creators",
|
||||
nexus: "Talent Marketplace - Cross-arm collaboration and opportunities",
|
||||
|
|
@ -128,7 +129,9 @@ export default function ArmSwitcherModal({
|
|||
onClose();
|
||||
};
|
||||
|
||||
const selectedArmData = selectedArm ? ARMS.find((a) => a.id === selectedArm) : null;
|
||||
const selectedArmData = selectedArm
|
||||
? ARMS.find((a) => a.id === selectedArm)
|
||||
: null;
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
|
|
@ -179,7 +182,9 @@ export default function ArmSwitcherModal({
|
|||
{ARM_DESCRIPTIONS[arm.id]}
|
||||
</p>
|
||||
</div>
|
||||
<div className={`w-2 h-2 rounded-full mt-1 ${arm.textColor}`} />
|
||||
<div
|
||||
className={`w-2 h-2 rounded-full mt-1 ${arm.textColor}`}
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
|
|
@ -213,13 +218,17 @@ export default function ArmSwitcherModal({
|
|||
</div>
|
||||
|
||||
{/* Features */}
|
||||
<div className={`p-4 rounded-lg ${selectedArmData.bgColor} border border-gray-700`}>
|
||||
<div
|
||||
className={`p-4 rounded-lg ${selectedArmData.bgColor} border border-gray-700`}
|
||||
>
|
||||
<h3 className="text-sm font-semibold text-gray-200 mb-3">
|
||||
What you'll get:
|
||||
</h3>
|
||||
<ul className="space-y-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<span className={`text-lg mt-0.5 ${selectedArmData.textColor}`}>
|
||||
<span
|
||||
className={`text-lg mt-0.5 ${selectedArmData.textColor}`}
|
||||
>
|
||||
✨
|
||||
</span>
|
||||
<span className="text-sm text-gray-300">
|
||||
|
|
@ -227,7 +236,9 @@ export default function ArmSwitcherModal({
|
|||
</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<span className={`text-lg mt-0.5 ${selectedArmData.textColor}`}>
|
||||
<span
|
||||
className={`text-lg mt-0.5 ${selectedArmData.textColor}`}
|
||||
>
|
||||
🚀
|
||||
</span>
|
||||
<span className="text-sm text-gray-300">
|
||||
|
|
@ -235,7 +246,9 @@ export default function ArmSwitcherModal({
|
|||
</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<span className={`text-lg mt-0.5 ${selectedArmData.textColor}`}>
|
||||
<span
|
||||
className={`text-lg mt-0.5 ${selectedArmData.textColor}`}
|
||||
>
|
||||
🎯
|
||||
</span>
|
||||
<span className="text-sm text-gray-300">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
import { useState, useEffect } from "react";
|
||||
import { AlertCircle, CheckCircle, AlertTriangle, RefreshCw } from "lucide-react";
|
||||
import {
|
||||
AlertCircle,
|
||||
CheckCircle,
|
||||
AlertTriangle,
|
||||
RefreshCw,
|
||||
} from "lucide-react";
|
||||
|
||||
interface DiagnosticData {
|
||||
timestamp: string;
|
||||
|
|
@ -46,7 +51,7 @@ export default function AdminDiscordDiagnostic() {
|
|||
setDiagnostic(data);
|
||||
} catch (err) {
|
||||
setError(
|
||||
err instanceof Error ? err.message : "Failed to fetch diagnostic"
|
||||
err instanceof Error ? err.message : "Failed to fetch diagnostic",
|
||||
);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
|
@ -60,7 +65,9 @@ export default function AdminDiscordDiagnostic() {
|
|||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-lg font-bold text-white">Discord Configuration Diagnostic</h3>
|
||||
<h3 className="text-lg font-bold text-white">
|
||||
Discord Configuration Diagnostic
|
||||
</h3>
|
||||
<button
|
||||
onClick={fetchDiagnostic}
|
||||
disabled={loading}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,9 @@ export default function DiscordVerify() {
|
|||
}, 3000);
|
||||
} catch (err) {
|
||||
setError(
|
||||
err instanceof Error ? err.message : "An error occurred. Please try again."
|
||||
err instanceof Error
|
||||
? err.message
|
||||
: "An error occurred. Please try again.",
|
||||
);
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
|
@ -129,7 +131,8 @@ export default function DiscordVerify() {
|
|||
Discord User
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{discordUser.username}#{discordUser.discriminator || "0000"}
|
||||
{discordUser.username}#
|
||||
{discordUser.discriminator || "0000"}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -153,8 +156,15 @@ export default function DiscordVerify() {
|
|||
</p>
|
||||
<ol className="text-sm text-muted-foreground space-y-1 list-decimal list-inside">
|
||||
<li>Open Discord</li>
|
||||
<li>Go to any server where the AeThex bot is installed</li>
|
||||
<li>Type <code className="bg-background/50 px-2 py-1 rounded">/verify</code></li>
|
||||
<li>
|
||||
Go to any server where the AeThex bot is installed
|
||||
</li>
|
||||
<li>
|
||||
Type{" "}
|
||||
<code className="bg-background/50 px-2 py-1 rounded">
|
||||
/verify
|
||||
</code>
|
||||
</li>
|
||||
<li>Copy the 6-digit code from the bot's response</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
|
@ -213,8 +223,8 @@ export default function DiscordVerify() {
|
|||
{/* Info Box */}
|
||||
<div className="mt-6 p-4 rounded-lg bg-secondary/20 border border-border/50">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
💡 <strong>Tip:</strong> You can also sign in directly with Discord
|
||||
on the login page if you're creating a new account.
|
||||
💡 <strong>Tip:</strong> You can also sign in directly with
|
||||
Discord on the login page if you're creating a new account.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1500,21 +1500,21 @@ export function createServer() {
|
|||
// Add recommendations based on validation
|
||||
if (!botToken) {
|
||||
diagnostics.recommendations.push(
|
||||
"❌ DISCORD_BOT_TOKEN not set. Set it in environment variables."
|
||||
"❌ DISCORD_BOT_TOKEN not set. Set it in environment variables.",
|
||||
);
|
||||
} else if ((botToken?.length || 0) < 20) {
|
||||
diagnostics.recommendations.push(
|
||||
`❌ DISCORD_BOT_TOKEN appears invalid (length: ${botToken?.length}). Should be 60+ characters.`
|
||||
`❌ DISCORD_BOT_TOKEN appears invalid (length: ${botToken?.length}). Should be 60+ characters.`,
|
||||
);
|
||||
} else {
|
||||
diagnostics.recommendations.push(
|
||||
"✅ DISCORD_BOT_TOKEN format looks valid"
|
||||
"✅ DISCORD_BOT_TOKEN format looks valid",
|
||||
);
|
||||
}
|
||||
|
||||
if (!clientId) {
|
||||
diagnostics.recommendations.push(
|
||||
"❌ DISCORD_CLIENT_ID not set. Set it to your application's ID."
|
||||
"❌ DISCORD_CLIENT_ID not set. Set it to your application's ID.",
|
||||
);
|
||||
} else {
|
||||
diagnostics.recommendations.push("✅ DISCORD_CLIENT_ID is set");
|
||||
|
|
@ -1522,7 +1522,7 @@ export function createServer() {
|
|||
|
||||
if (!publicKey) {
|
||||
diagnostics.recommendations.push(
|
||||
"❌ DISCORD_PUBLIC_KEY not set. Needed for signature verification."
|
||||
"❌ DISCORD_PUBLIC_KEY not set. Needed for signature verification.",
|
||||
);
|
||||
} else {
|
||||
diagnostics.recommendations.push("✅ DISCORD_PUBLIC_KEY is set");
|
||||
|
|
@ -1537,22 +1537,25 @@ export function createServer() {
|
|||
headers: {
|
||||
Authorization: `Bot ${botToken}`,
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
diagnostics.testRequest = {
|
||||
...diagnostics.testRequest,
|
||||
status: testResponse.status === 200 ? "✅ Success" : `❌ Failed (${testResponse.status})`,
|
||||
status:
|
||||
testResponse.status === 200
|
||||
? "✅ Success"
|
||||
: `❌ Failed (${testResponse.status})`,
|
||||
responseCode: testResponse.status,
|
||||
};
|
||||
|
||||
if (testResponse.status === 401) {
|
||||
diagnostics.recommendations.push(
|
||||
"❌ Token authentication failed (401). The token may be invalid or revoked."
|
||||
"❌ Token authentication failed (401). The token may be invalid or revoked.",
|
||||
);
|
||||
} else if (testResponse.status === 200) {
|
||||
diagnostics.recommendations.push(
|
||||
"✅ Token authentication successful with Discord API!"
|
||||
"✅ Token authentication successful with Discord API!",
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue