Prettier format pending files

This commit is contained in:
Builder.io 2025-11-09 09:28:34 +00:00
parent b79ffaefbd
commit 81996f1040
10 changed files with 218 additions and 85 deletions

View file

@ -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);
@ -189,7 +202,7 @@ export default async function handler(req: any, res: any) {
const redirectUrl = new URL(
nextPath,
process.env.VITE_API_BASE || "https://aethex.dev"
process.env.VITE_API_BASE || "https://aethex.dev",
);
res.redirect(redirectUrl.toString());
} catch (error) {

View file

@ -41,7 +41,12 @@ export default function DocsHeroSection({
Get Started
</Link>
</Button>
<Button asChild variant="outline" size="lg" className={outlineButtonClass}>
<Button
asChild
variant="outline"
size="lg"
className={outlineButtonClass}
>
<Link to="/docs/tutorials">
<Play className="h-5 w-5 mr-2" />
Watch Tutorials

View file

@ -109,7 +109,7 @@ export default function DocsLayout({
return docNavigation.filter(
(item) =>
item.title.toLowerCase().includes(query) ||
item.description?.toLowerCase().includes(query)
item.description?.toLowerCase().includes(query),
);
}, [searchQuery]);
@ -139,21 +139,26 @@ export default function DocsLayout({
}`}
>
{/* AeThex Branding */}
<div className={`p-6 border-b ${colors.border} flex flex-col items-center justify-center`}>
<div
className={`p-6 border-b ${colors.border} flex flex-col items-center justify-center`}
>
<Link
to="/docs"
className="flex flex-col items-center justify-center gap-4 w-full"
>
<img
src={theme === "professional"
? "https://cdn.builder.io/api/v1/image/assets%2Ffc53d607e21d497595ac97e0637001a1%2Fbac6154f77e94521bcbfe35abd605cd0?format=webp&width=800"
: "https://cdn.builder.io/api/v1/image/assets%2Ffc53d607e21d497595ac97e0637001a1%2F1d007dd573c54339ad35fde9cd637516?format=webp&width=800"
src={
theme === "professional"
? "https://cdn.builder.io/api/v1/image/assets%2Ffc53d607e21d497595ac97e0637001a1%2Fbac6154f77e94521bcbfe35abd605cd0?format=webp&width=800"
: "https://cdn.builder.io/api/v1/image/assets%2Ffc53d607e21d497595ac97e0637001a1%2F1d007dd573c54339ad35fde9cd637516?format=webp&width=800"
}
alt="AeThex Logo"
className="h-16 w-16 object-contain"
/>
<div className="text-center">
<div className={`font-bold text-lg ${colors.headingColor}`}>AeThex</div>
<div className={`font-bold text-lg ${colors.headingColor}`}>
AeThex
</div>
<div className={`text-xs ${colors.textMuted}`}>Documentation</div>
</div>
</Link>
@ -190,7 +195,9 @@ export default function DocsLayout({
{/* Search Bar */}
<div className={`p-4 border-b ${colors.border}`}>
<div className="relative">
<Search className={`absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 ${colors.textMuted}`} />
<Search
className={`absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 ${colors.textMuted}`}
/>
<Input
placeholder="Search docs..."
className={`${colors.inputBg} border-${colors.border} pl-10 pr-4 text-sm ${colors.foreground}`}
@ -202,7 +209,9 @@ export default function DocsLayout({
{/* Navigation */}
<nav className="p-4 space-y-1">
<div className={`text-xs font-semibold ${colors.textMuted} uppercase tracking-wider mb-4 px-2`}>
<div
className={`text-xs font-semibold ${colors.textMuted} uppercase tracking-wider mb-4 px-2`}
>
Documentation
</div>
{filteredNav.map((item) => (
@ -249,7 +258,9 @@ export default function DocsLayout({
{/* Main Content */}
<main className="md:ml-64">
{/* Mobile Header */}
<div className={`md:hidden sticky top-0 z-20 border-b ${colors.border} ${colors.cardBg} p-4 flex items-center justify-between`}>
<div
className={`md:hidden sticky top-0 z-20 border-b ${colors.border} ${colors.cardBg} p-4 flex items-center justify-between`}
>
<button
onClick={() => setSidebarOpen(!sidebarOpen)}
className={`p-2 ${colors.sidebarHover} rounded-lg`}
@ -270,7 +281,11 @@ export default function DocsLayout({
<div className="lg:col-span-3">
{title && (
<div className="mb-8">
<h1 className={`text-5xl font-bold ${colors.headingColor} mb-3`}>{title}</h1>
<h1
className={`text-5xl font-bold ${colors.headingColor} mb-3`}
>
{title}
</h1>
{description && (
<p className={`text-lg ${colors.textMuted}`}>{description}</p>
)}
@ -278,7 +293,9 @@ export default function DocsLayout({
)}
{/* Content - either children (for wrapper) or Outlet (for routing) */}
<div className={`prose max-w-none ${theme === "professional" ? "prose-neutral" : "prose-invert"}`}>
<div
className={`prose max-w-none ${theme === "professional" ? "prose-neutral" : "prose-invert"}`}
>
{children || <Outlet />}
</div>
</div>
@ -287,7 +304,9 @@ export default function DocsLayout({
{tableOfContents.length > 0 && (
<aside className="hidden lg:block">
<div className="sticky top-8">
<div className={`text-xs font-semibold ${colors.textMuted} uppercase tracking-wider mb-4`}>
<div
className={`text-xs font-semibold ${colors.textMuted} uppercase tracking-wider mb-4`}
>
On this page
</div>
<nav className="space-y-2">
@ -315,52 +334,76 @@ export default function DocsLayout({
<div className="max-w-7xl mx-auto px-6 md:px-8 py-8">
{/* AeThex Arms - Grayscale for professional, colored for brand */}
<div className={`mb-8 pb-8 border-b ${colors.border}`}>
<h3 className={`text-sm font-semibold ${colors.headingColor} mb-3`}>AeThex Platforms</h3>
<h3
className={`text-sm font-semibold ${colors.headingColor} mb-3`}
>
AeThex Platforms
</h3>
<div className="flex flex-wrap gap-2">
{theme === "professional" ? (
<>
<div className="flex items-center gap-2 px-3 py-1 rounded-lg bg-gray-200">
<div className="h-2 w-2 rounded-full bg-gray-400"></div>
<span className="text-xs font-medium text-gray-700">Labs</span>
<span className="text-xs font-medium text-gray-700">
Labs
</span>
</div>
<div className="flex items-center gap-2 px-3 py-1 rounded-lg bg-gray-200">
<div className="h-2 w-2 rounded-full bg-gray-400"></div>
<span className="text-xs font-medium text-gray-700">GameForge</span>
<span className="text-xs font-medium text-gray-700">
GameForge
</span>
</div>
<div className="flex items-center gap-2 px-3 py-1 rounded-lg bg-gray-200">
<div className="h-2 w-2 rounded-full bg-gray-400"></div>
<span className="text-xs font-medium text-gray-700">Corp</span>
<span className="text-xs font-medium text-gray-700">
Corp
</span>
</div>
<div className="flex items-center gap-2 px-3 py-1 rounded-lg bg-gray-200">
<div className="h-2 w-2 rounded-full bg-gray-400"></div>
<span className="text-xs font-medium text-gray-700">Foundation</span>
<span className="text-xs font-medium text-gray-700">
Foundation
</span>
</div>
<div className="flex items-center gap-2 px-3 py-1 rounded-lg bg-gray-200">
<div className="h-2 w-2 rounded-full bg-gray-400"></div>
<span className="text-xs font-medium text-gray-700">Dev-Link</span>
<span className="text-xs font-medium text-gray-700">
Dev-Link
</span>
</div>
</>
) : (
<>
<div className="flex items-center gap-2 px-3 py-1 rounded-lg bg-yellow-100">
<div className="h-2 w-2 rounded-full bg-yellow-500"></div>
<span className="text-xs font-medium text-yellow-900">Labs</span>
<span className="text-xs font-medium text-yellow-900">
Labs
</span>
</div>
<div className="flex items-center gap-2 px-3 py-1 rounded-lg bg-green-100">
<div className="h-2 w-2 rounded-full bg-green-500"></div>
<span className="text-xs font-medium text-green-900">GameForge</span>
<span className="text-xs font-medium text-green-900">
GameForge
</span>
</div>
<div className="flex items-center gap-2 px-3 py-1 rounded-lg bg-blue-100">
<div className="h-2 w-2 rounded-full bg-blue-500"></div>
<span className="text-xs font-medium text-blue-900">Corp</span>
<span className="text-xs font-medium text-blue-900">
Corp
</span>
</div>
<div className="flex items-center gap-2 px-3 py-1 rounded-lg bg-red-100">
<div className="h-2 w-2 rounded-full bg-red-500"></div>
<span className="text-xs font-medium text-red-900">Foundation</span>
<span className="text-xs font-medium text-red-900">
Foundation
</span>
</div>
<div className="flex items-center gap-2 px-3 py-1 rounded-lg bg-cyan-100">
<div className="h-2 w-2 rounded-full bg-cyan-500"></div>
<span className="text-xs font-medium text-cyan-900">Dev-Link</span>
<span className="text-xs font-medium text-cyan-900">
Dev-Link
</span>
</div>
</>
)}
@ -369,7 +412,9 @@ export default function DocsLayout({
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
<div>
<h3 className={`font-semibold ${colors.headingColor} mb-4`}>Product</h3>
<h3 className={`font-semibold ${colors.headingColor} mb-4`}>
Product
</h3>
<ul className={`space-y-2 text-sm ${colors.textMuted}`}>
<li>
<Link to="/docs" className={`${colors.accentHover}`}>
@ -377,7 +422,10 @@ export default function DocsLayout({
</Link>
</li>
<li>
<Link to="/docs/platform" className={`${colors.accentHover}`}>
<Link
to="/docs/platform"
className={`${colors.accentHover}`}
>
Platform
</Link>
</li>
@ -389,10 +437,15 @@ export default function DocsLayout({
</ul>
</div>
<div>
<h3 className={`font-semibold ${colors.headingColor} mb-4`}>Resources</h3>
<h3 className={`font-semibold ${colors.headingColor} mb-4`}>
Resources
</h3>
<ul className={`space-y-2 text-sm ${colors.textMuted}`}>
<li>
<Link to="/docs/tutorials" className={`${colors.accentHover}`}>
<Link
to="/docs/tutorials"
className={`${colors.accentHover}`}
>
Tutorials
</Link>
</li>
@ -409,7 +462,9 @@ export default function DocsLayout({
</ul>
</div>
<div>
<h3 className={`font-semibold ${colors.headingColor} mb-4`}>Community</h3>
<h3 className={`font-semibold ${colors.headingColor} mb-4`}>
Community
</h3>
<ul className={`space-y-2 text-sm ${colors.textMuted}`}>
<li>
<a href="#" className={`${colors.accentHover}`}>
@ -429,7 +484,9 @@ export default function DocsLayout({
</ul>
</div>
</div>
<div className={`border-t ${colors.border} mt-8 pt-8 flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 text-sm ${colors.textMuted}`}>
<div
className={`border-t ${colors.border} mt-8 pt-8 flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 text-sm ${colors.textMuted}`}
>
<p>&copy; 2025 AeThex. All rights reserved.</p>
<div className="flex gap-6">
<a href="#" className={`${colors.accentHover}`}>

View file

@ -27,9 +27,7 @@ export default function DocsSupportCTA({
: "border-purple-400/60 text-purple-200";
return (
<div
className={`mt-12 rounded-2xl border ${ctaBg} p-8 text-center`}
>
<div className={`mt-12 rounded-2xl border ${ctaBg} p-8 text-center`}>
<h3 className={`text-3xl font-semibold ${colors.headingColor} mb-4`}>
{title}
</h3>

View file

@ -64,7 +64,9 @@ export default function LearningResourcesGrid({
}: LearningResourcesGridProps) {
const { colors, theme } = useDocsTheme();
const hoverBorderColor =
theme === "professional" ? "hover:border-gray-400" : "hover:border-purple-500/50";
theme === "professional"
? "hover:border-gray-400"
: "hover:border-purple-500/50";
return (
<div className="mb-12">

View file

@ -99,11 +99,15 @@ export default function QuickStartSection({
{cards.map((card, index) => {
const Icon = card.icon;
const hoverBorderColor =
theme === "professional" ? "hover:border-gray-400" : "hover:border-purple-500/50";
theme === "professional"
? "hover:border-gray-400"
: "hover:border-purple-500/50";
const iconColor =
theme === "professional" ? "text-gray-700" : "text-purple-400";
const hoverTitleColor =
theme === "professional" ? "group-hover:text-gray-900" : "group-hover:text-purple-400";
theme === "professional"
? "group-hover:text-gray-900"
: "group-hover:text-purple-400";
return (
<Card

View file

@ -49,7 +49,9 @@ export default function RecentUpdatesSection({
? "border-gray-300 text-black hover:bg-gray-100"
: "border-slate-600 text-white hover:bg-slate-800";
const hoverBorderColor =
theme === "professional" ? "hover:border-gray-400" : "hover:border-purple-500/50";
theme === "professional"
? "hover:border-gray-400"
: "hover:border-purple-500/50";
return (
<div className="mb-12">

View file

@ -93,12 +93,18 @@ export default function ResourceSectionsGrid({
sections = defaultSections,
}: ResourceSectionsGridProps) {
const { colors, theme } = useDocsTheme();
const iconColor = theme === "professional" ? "text-gray-700" : "text-purple-400";
const iconColor =
theme === "professional" ? "text-gray-700" : "text-purple-400";
const hoverBorderColor =
theme === "professional" ? "hover:border-gray-400" : "hover:border-purple-500/50";
theme === "professional"
? "hover:border-gray-400"
: "hover:border-purple-500/50";
const hoverTitleColor =
theme === "professional" ? "group-hover:text-gray-900" : "group-hover:text-purple-400";
const arrowColor = theme === "professional" ? "text-gray-600" : "text-purple-400";
theme === "professional"
? "group-hover:text-gray-900"
: "group-hover:text-purple-400";
const arrowColor =
theme === "professional" ? "text-gray-600" : "text-purple-400";
const explorehoverColor =
theme === "professional"
? "group-hover:text-gray-900"

View file

@ -72,7 +72,7 @@ interface DocsThemeContextType {
}
const DocsThemeContext = createContext<DocsThemeContextType | undefined>(
undefined
undefined,
);
export function DocsThemeProvider({ children }: { children: React.ReactNode }) {

View file

@ -9,6 +9,7 @@ This document provides a comprehensive analysis of the AETHEX project's actual t
## 1. FRONTEND STACK (Vercel)
### Current Implementation
- **Framework**: Vite + React (Custom architecture)
- **NOT Next.js** - This is a significant difference from the development plan
- **Build Tool**: Vite for fast development and optimized production builds
@ -19,6 +20,7 @@ This document provides a comprehensive analysis of the AETHEX project's actual t
### Architecture Components
#### Page Structure
```
code/client/pages/
├── Auth Pages
@ -52,6 +54,7 @@ code/client/pages/
```
#### Context Providers
```
code/client/contexts/
├── AuthContext.tsx (User authentication state)
@ -61,6 +64,7 @@ code/client/contexts/
```
#### Key Components
```
code/client/components/
├── Layout.tsx (Main layout with header, navigation, footer)
@ -87,6 +91,7 @@ code/client/components/
```
### Authentication Methods Supported
1. **Email/Password** (Native Supabase)
2. **GitHub OAuth** (Supabase)
3. **Google OAuth** (Supabase)
@ -95,6 +100,7 @@ code/client/components/
6. **Web3/Ethereum** (Metamask signature verification)
### Critical Notes on Frontend
- ✅ Uses custom Supabase client (NOT the new @supabase/ssr package)
- ⚠️ Does NOT use Next.js App Router or middleware
- ⚠️ CSP headers need configuration in Vite for Discord Activity
@ -108,6 +114,7 @@ code/client/components/
### Database (PostgreSQL via Supabase)
#### Core Tables
```
public.user_profiles
├── id (UUID, PK)
@ -129,6 +136,7 @@ auth.users (Managed by Supabase Auth)
```
#### Discord Integration Tables
```
public.discord_links
├── discord_id (TEXT, PK)
@ -161,6 +169,7 @@ public.discord_user_roles
```
#### Creator Network Tables
```
public.aethex_creators
├── user_id (UUID, FK)
@ -196,6 +205,7 @@ public.aethex_endorsements
```
#### Web3 Integration
```
public.web3_nonces
├── wallet_address (TEXT, PK)
@ -210,6 +220,7 @@ user_profiles extensions:
```
#### Game Integration
```
public.game_auth_tokens
├── id (UUID, PK)
@ -229,6 +240,7 @@ public.game_sessions
### Row Level Security (RLS) Status
**Current State**: Basic RLS policies implemented
```sql
-- Example: Users can see their own profile
CREATE POLICY "User can see their own profile"
@ -242,6 +254,7 @@ CREATE POLICY "User can update their own profile"
```
**Status**: ⚠️ NOT YET OPTIMIZED (Plan recommends performance optimization)
```sql
-- Should be optimized to:
CREATE POLICY "User can see their own profile"
@ -254,14 +267,17 @@ CREATE POLICY "User can see their own profile"
#### Location: `code/api/` directory
##### Discord OAuth
- `code/api/discord/oauth/start.ts` - GET endpoint, redirects to Discord
- `code/api/discord/oauth/callback.ts` - GET endpoint, handles OAuth callback
##### Discord Linking & Verification
- `code/api/discord/verify-code.ts` - POST, verifies 6-digit code from /verify command
- `code/api/discord/link.ts` - POST, links Discord account to user
##### Discord Management
- `code/api/discord/role-mappings.ts` - GET/POST/PUT/DELETE role mapping CRUD
- `code/api/discord/sync-roles.ts` - POST, assigns Discord roles based on arm + user_type
- `code/api/discord/admin-register-commands.ts` - POST, registers slash commands (requires admin token)
@ -269,16 +285,19 @@ CREATE POLICY "User can see their own profile"
- `code/api/discord/verify.ts` - POST, checks if user is linked to Discord
##### Creator Network
- `code/api/creators.ts` - GET/POST/PUT, manage creator profiles
- `code/api/opportunities.ts` - GET/POST/PUT, manage job opportunities
- `code/api/applications.ts` - GET/POST/PUT/DELETE, manage job applications
##### Game Integration
- `code/api/games/game-auth.ts` - POST, unified game authentication (Unity/Unreal/Godot/Roblox)
- `code/api/games/roblox-auth.ts` - POST, Roblox-specific authentication
- `code/api/games/verify-token.ts` - POST, verify game session tokens
##### Other
- `code/api/user/link-roblox.ts` - POST, link Roblox account
- `code/api/user/link-web3.ts` - POST, link Ethereum wallet
- `code/api/web3/nonce.ts` - POST, generate Web3 nonce
@ -289,6 +308,7 @@ CREATE POLICY "User can see their own profile"
**Location**: `code/server/index.ts`
**Responsibilities**:
- Discord slash command handlers (/creators, /opportunities, /nexus)
- Discord interactions endpoint (signature verification)
- Health check endpoints
@ -296,6 +316,7 @@ CREATE POLICY "User can see their own profile"
- Admin functions
**Key Features**:
- ED25519 signature verification for Discord requests
- Slash command routing
- Admin token validation (DISCORD_ADMIN_REGISTER_TOKEN)
@ -306,6 +327,7 @@ CREATE POLICY "User can see their own profile"
## 3. DISCORD BOT STACK (Railway)
### Current Deployment
- **Platform**: Railway (PaaS)
- **Language**: Node.js with discord.js v14
- **Hosting Status**: ✅ Successfully deployed and running
@ -328,6 +350,7 @@ CREATE POLICY "User can see their own profile"
### Implemented Slash Commands
#### 1. `/verify` (Account Linking)
- **File**: `code/discord-bot/commands/verify.js`
- **Function**: Generates 15-minute verification code
- **Flow**:
@ -338,6 +361,7 @@ CREATE POLICY "User can see their own profile"
5. discord_links record created
#### 2. `/set-realm` (Choose Primary Arm)
- **File**: `code/discord-bot/commands/set-realm.js`
- **Function**: Dropdown menu to select primary arm
- **Options**: labs, gameforge, corp, foundation, devlink, nexus
@ -346,15 +370,18 @@ CREATE POLICY "User can see their own profile"
2. Triggers role assignment via roleManager.js
#### 3. `/profile` (Show Profile)
- **File**: `code/discord-bot/commands/profile.js`
- **Function**: Displays linked AeThex profile in Discord
- **Shows**: Username, bio, avatar, primary realm, link to full profile
#### 4. `/unlink` (Disconnect Account)
- **File**: `code/discord-bot/commands/unlink.js`
- **Function**: Removes Discord link and revokes roles
#### 5. `/verify-role` (Check Assigned Roles)
- **File**: `code/discord-bot/commands/verify-role.js`
- **Function**: Shows current Discord roles and expected roles from mappings
@ -363,13 +390,14 @@ CREATE POLICY "User can see their own profile"
```javascript
// code/discord-bot/utils/roleManager.js
{
assignRoleByArm(discord_id, arm, server_id) // Assign role based on arm
getUserArm(discord_id) // Get user's primary arm
syncRolesAcrossGuilds(discord_id) // Sync roles in all servers
assignRoleByArm(discord_id, arm, server_id); // Assign role based on arm
getUserArm(discord_id); // Get user's primary arm
syncRolesAcrossGuilds(discord_id); // Sync roles in all servers
}
```
### Dependencies
```json
{
"discord.js": "^14.13.0",
@ -379,6 +407,7 @@ CREATE POLICY "User can see their own profile"
```
### Environment Variables
```
DISCORD_BOT_TOKEN=<bot token>
DISCORD_CLIENT_ID=578971245454950421
@ -390,6 +419,7 @@ BOT_PORT=3000
```
### Bot Health Check
- **Endpoint**: POST /health
- **Returns**: { status, guilds, commands, uptime, timestamp }
- **Used by**: Frontend `/api/discord/bot-health` proxy
@ -451,7 +481,9 @@ Redirects to /profile/settings
```
### Discord Manifest
**Location**: `code/public/discord-manifest.json`
```json
{
"id": "578971245454950421",
@ -470,6 +502,7 @@ Redirects to /profile/settings
## 5. PLANNED vs. ACTUAL - KEY DIFFERENCES
### Development Plan Says... | Actually Have... | Status
```
┌─────────────────────────────────────────────────────────────────┐
│ FRONTEND │
@ -528,6 +561,7 @@ Redirects to /profile/settings
## 6. ENVIRONMENT VARIABLES (All Set)
### Supabase
```
VITE_SUPABASE_URL=https://kmdeisowhtsalsekkzqd.supabase.co
VITE_SUPABASE_ANON_KEY=sb_publishable_DfTB6qME8BkTmHNJ3dCBew_t1NLATEq
@ -536,6 +570,7 @@ SUPABASE_URL=https://supabase.aethex.tech
```
### Discord OAuth
```
DISCORD_CLIENT_ID=578971245454950421
DISCORD_CLIENT_SECRET=JKlilGzcTWgfmt2wEqiHO8wpCel5VEji
@ -545,6 +580,7 @@ DISCORD_ADMIN_REGISTER_TOKEN=aethex-link
```
### Roblox OAuth
```
ROBLOX_OAUTH_CLIENT_ID=4727645256213520722
ROBLOX_OAUTH_CLIENT_SECRET=RBX-hTAHd1iV-U-JSodk9GDkx0beYrLf00YKdZbWyMPzTWysCsys-UPEvT9ON_qSEeM2
@ -552,11 +588,13 @@ ROBLOX_OAUTH_REDIRECT_URI=https://aethex.dev/roblox-callback
```
### Web3
```
(No env vars needed - signature verification is client-side)
```
### API Base
```
VITE_API_BASE=https://e7c3806a9bfe4bdf9bb8a72a7f0d31cd-324f24a826ec4eb198c1a0eef.fly.dev
```
@ -583,21 +621,25 @@ VITE_API_BASE=https://e7c3806a9bfe4bdf9bb8a72a7f0d31cd-324f24a826ec4eb198c1a0eef
## 8. WHAT NEEDS WORK ⏳
1. **CSP Headers for Discord Activity**
- Status: ⏳ NEEDS FIX
- Action: Add to vite.config.ts and vercel.json
- Priority: HIGH
2. **Discord Embedded App SDK (Dual Auth)**
- Status: ⏳ PARTIALLY DONE
- Action: Implement full dual-auth flow in DiscordActivity.tsx
- Priority: MEDIUM
3. **RLS Policy Optimization**
- Status: ✅ WORKS, CAN OPTIMIZE
- Action: Wrap auth.uid() in (select auth.uid()) for performance
- Priority: LOW
4. **GitHub Actions CI/CD**
- Status: ⏳ NOT IMPLEMENTED
- Action: Create .github/workflows/deploy-supabase.yml
- Priority: MEDIUM
@ -616,18 +658,18 @@ Frontend:
code/client/pages/ - All pages
code/client/contexts/ - Auth, Web3, Discord contexts
code/client/components/ - All UI components
Backend:
code/api/ - All API endpoints
code/server/index.ts - Express server + Discord handlers
Database:
code/supabase/migrations/ - All SQL migrations
Discord Bot:
code/discord-bot/ - All bot code
code/discord-bot/commands/ - Slash commands
Documentation:
code/docs/ - All guides and documentation
```
@ -637,23 +679,27 @@ Documentation:
## 10. DEPLOYMENT CHECKLIST
### Frontend (Vercel)
- [ ] Environment variables set in Vercel dashboard
- [ ] CSP headers configured in vercel.json
- [ ] Branch deployments working
- [ ] Discord OAuth redirect URI set in Discord portal
### Backend (Supabase)
- [ ] All migrations applied
- [ ] RLS policies enabled on all tables
- [ ] Service role key securely stored
### Bot (Railway)
- [ ] Environment variables set
- [ ] Bot token valid and bot is online
- [ ] Slash commands registered
- [ ] Database connection working
### Discord Developer Portal
- [ ] OAuth2 redirect URI: https://aethex.dev/api/discord/oauth/callback
- [ ] Bot invited to test servers
- [ ] Slash commands enabled