diff --git a/api/user/link-mrpiglr-accounts.ts b/api/user/link-mrpiglr-accounts.ts new file mode 100644 index 00000000..069e1a64 --- /dev/null +++ b/api/user/link-mrpiglr-accounts.ts @@ -0,0 +1,248 @@ +import { getAdminClient } from "../_supabase"; + +/** + * Admin-only endpoint to link mrpiglr@aethex.dev to mrpiglr@gmail.com + * Both emails are tied to the same person, so they should share the same account. + * Primary account: mrpiglr@aethex.dev (work email for dev account) + * Linked account: mrpiglr@gmail.com (personal email, merges into work account) + */ +export default async (req: Request) => { + if (req.method !== "POST") { + return new Response(JSON.stringify({ error: "Method not allowed" }), { + status: 405, + headers: { "Content-Type": "application/json" }, + }); + } + + try { + // Check authorization header for admin token + const authHeader = req.headers.get("authorization"); + const adminToken = authHeader?.replace("Bearer ", ""); + + // Simple token check (in production, use more robust auth) + if (adminToken !== "mrpiglr-admin-token") { + return new Response(JSON.stringify({ error: "Unauthorized" }), { + status: 401, + headers: { "Content-Type": "application/json" }, + }); + } + + const supabase = getAdminClient(); + const primaryEmail = "mrpiglr@aethex.dev"; + const linkedEmail = "mrpiglr@gmail.com"; + + console.log(`[Email Linking] Starting mrpiglr account linking...`); + + // Fetch the primary user (work email) + const { data: primaryUser, error: primaryError } = await supabase + .from("user_profiles") + .select("user_id, email") + .eq("email", primaryEmail) + .single(); + + if (primaryError || !primaryUser) { + return new Response( + JSON.stringify({ + error: "Primary email not found", + email: primaryEmail, + details: primaryError?.message, + }), + { status: 404, headers: { "Content-Type": "application/json" } } + ); + } + + // Fetch the linked user (personal email) + const { data: linkedUser, error: linkedError } = await supabase + .from("user_profiles") + .select("user_id, email") + .eq("email", linkedEmail) + .single(); + + if (linkedError || !linkedUser) { + return new Response( + JSON.stringify({ + error: "Linked email not found", + email: linkedEmail, + details: linkedError?.message, + }), + { status: 404, headers: { "Content-Type": "application/json" } } + ); + } + + const sourceUserId = linkedUser.user_id; // gmail account + const targetUserId = primaryUser.user_id; // aethex.dev account + + console.log(`[Email Linking] Merging ${sourceUserId} into ${targetUserId}`); + + // 1. Transfer achievements + console.log(`[Email Linking] Transferring achievements...`); + const { error: achievementError } = await supabase + .from("achievements_earned") + .update({ user_id: targetUserId }) + .eq("user_id", sourceUserId); + if (achievementError) { + console.warn("Achievement transfer warning:", achievementError); + } + + // 2. Transfer aethex_creators profile + console.log(`[Email Linking] Checking for creator profile...`); + const { data: creatorProfile } = await supabase + .from("aethex_creators") + .select("*") + .eq("user_id", sourceUserId) + .single(); + + if (creatorProfile) { + console.log(`[Email Linking] Transferring creator profile...`); + await supabase + .from("aethex_creators") + .update({ user_id: targetUserId }) + .eq("user_id", sourceUserId); + } + + // 3. Transfer applications + console.log(`[Email Linking] Transferring applications...`); + const { error: appError } = await supabase + .from("aethex_applications") + .update({ user_id: targetUserId }) + .eq("user_id", sourceUserId); + if (appError) { + console.warn("Application transfer warning:", appError); + } + + // 4. Transfer discord_links + console.log(`[Email Linking] Checking for discord links...`); + const { data: discordLinks } = await supabase + .from("discord_links") + .select("*") + .eq("user_id", sourceUserId); + + if (discordLinks && discordLinks.length > 0) { + console.log( + `[Email Linking] Found ${discordLinks.length} discord links, transferring...` + ); + for (const link of discordLinks) { + const { data: existing } = await supabase + .from("discord_links") + .select("*") + .eq("user_id", targetUserId); + + if (!existing || existing.length === 0) { + await supabase + .from("discord_links") + .update({ user_id: targetUserId }) + .eq("id", link.id); + } + } + } + + // 5. Transfer web3_wallets if they exist + console.log(`[Email Linking] Checking for web3 wallets...`); + const { data: web3Links } = await supabase + .from("web3_wallets") + .select("*") + .eq("user_id", sourceUserId); + + if (web3Links && web3Links.length > 0) { + console.log( + `[Email Linking] Found ${web3Links.length} web3 wallets, transferring...` + ); + for (const wallet of web3Links) { + const { data: existing } = await supabase + .from("web3_wallets") + .select("*") + .eq("user_id", targetUserId) + .eq("wallet_address", wallet.wallet_address); + + if (!existing || existing.length === 0) { + await supabase + .from("web3_wallets") + .update({ user_id: targetUserId }) + .eq("id", wallet.id); + } + } + } + + // 6. Link both emails in user_email_links + console.log(`[Email Linking] Creating email links...`); + const { data: existingLinks } = await supabase + .from("user_email_links") + .select("*") + .in("email", [primaryEmail, linkedEmail]); + + // Insert primary email + const primaryEmailExists = existingLinks?.some( + (l) => l.email === primaryEmail + ); + if (!primaryEmailExists) { + await supabase.from("user_email_links").insert({ + user_id: targetUserId, + email: primaryEmail, + is_primary: true, + verified_at: new Date().toISOString(), + }); + } + + // Insert linked email + const linkedEmailExists = existingLinks?.some( + (l) => l.email === linkedEmail + ); + if (!linkedEmailExists) { + await supabase.from("user_email_links").insert({ + user_id: targetUserId, + email: linkedEmail, + is_primary: false, + verified_at: new Date().toISOString(), + }); + } + + // 7. Update user_profiles + console.log(`[Email Linking] Updating user profiles...`); + await supabase + .from("user_profiles") + .update({ + primary_email: primaryEmail, + is_dev_account: true, + }) + .eq("user_id", targetUserId); + + // Mark source profile as merged + await supabase + .from("user_profiles") + .update({ + merged_to_user_id: targetUserId, + updated_at: new Date().toISOString(), + }) + .eq("user_id", sourceUserId); + + console.log(`[Email Linking] ✅ Successfully linked mrpiglr accounts`); + + return new Response( + JSON.stringify({ + success: true, + message: "Successfully linked mrpiglr@aethex.dev and mrpiglr@gmail.com", + primaryEmail, + linkedEmail, + targetUserId, + sourceUserId, + note: "Both emails can now log in and will access the same account. Primary account is mrpiglr@aethex.dev (work/dev account).", + }), + { + status: 200, + headers: { "Content-Type": "application/json" }, + } + ); + } catch (error: any) { + console.error("[Email Linking Error]", error); + return new Response( + JSON.stringify({ + error: "Failed to link mrpiglr accounts", + details: error.message, + }), + { + status: 500, + headers: { "Content-Type": "application/json" }, + } + ); + } +};