From 2df17dc8ec3eb1c7a9fe1a560b6e98d748f3b529 Mon Sep 17 00:00:00 2001 From: "Builder.io" Date: Wed, 5 Nov 2025 03:44:40 +0000 Subject: [PATCH] Todo list updated cgen-7dee112ec2a44f7eba17438c4a8b4675 --- client/contexts/AuthContext.tsx | 50 ++++++++++++++++++++++++++++---- server/index.ts | 51 +++++++++++++++++++++++++++++---- 2 files changed, 89 insertions(+), 12 deletions(-) diff --git a/client/contexts/AuthContext.tsx b/client/contexts/AuthContext.tsx index 45ff40cc..572641fe 100644 --- a/client/contexts/AuthContext.tsx +++ b/client/contexts/AuthContext.tsx @@ -553,15 +553,53 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ if (error) throw error; - // Supabase sends the confirmation email automatically (SMTP or default provider) - let emailSent = true; + // Send verification email via custom SMTP (fallback: Supabase auth email) + let emailSent = false; let verificationUrl: string | undefined; if (data.user) { - aethexToast.success({ - title: "Verify your email", - description: `We sent a confirmation to ${email}.`, - }); + try { + // Try to send via custom SMTP server + const verifyResponse = await fetch("/api/auth/send-verification-email", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + email, + redirectTo, + fullName: metadata.full_name || null, + }), + }); + + const verifyPayload = await verifyResponse.json().catch(() => ({})); + + if (verifyResponse.ok && verifyPayload?.sent) { + emailSent = true; + aethexToast.success({ + title: "Verify your email", + description: `We sent a confirmation to ${email}.`, + }); + } else { + // Custom SMTP failed, but provide manual link if available + verificationUrl = verifyPayload?.verificationUrl || undefined; + if (verificationUrl) { + aethexToast.warning({ + title: "Verify your email", + description: `We couldn't send the email automatically. Use the manual verification link in your account settings.`, + }); + } else { + aethexToast.info({ + title: "Account created", + description: `Please check your email to verify your account.`, + }); + } + } + } catch (emailErr) { + console.warn("[Auth] Failed to send verification email:", emailErr); + aethexToast.info({ + title: "Account created", + description: `Please check your email to verify your account.`, + }); + } } return { emailSent, verificationUrl } as const; diff --git a/server/index.ts b/server/index.ts index f758a00e..2e1224b0 100644 --- a/server/index.ts +++ b/server/index.ts @@ -536,23 +536,62 @@ export function createServer() { app.post("/api/posts", async (req, res) => { const payload = req.body || {}; + + // Validation + if (!payload.author_id) { + return res.status(400).json({ error: "author_id is required" }); + } + if (!payload.title || typeof payload.title !== "string" || !payload.title.trim()) { + return res.status(400).json({ error: "title is required and must be a non-empty string" }); + } + if (!payload.content || typeof payload.content !== "string" || !payload.content.trim()) { + return res.status(400).json({ error: "content is required and must be a non-empty string" }); + } + + // Validate author_id is a valid UUID format + const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + if (!uuidRegex.test(String(payload.author_id))) { + return res.status(400).json({ error: "author_id must be a valid UUID" }); + } + try { + // Verify author exists + const { data: author, error: authorError } = await adminSupabase + .from("user_profiles") + .select("id") + .eq("id", payload.author_id) + .single(); + + if (authorError || !author) { + return res.status(404).json({ error: "Author not found" }); + } + const { data, error } = await adminSupabase .from("community_posts") .insert({ author_id: payload.author_id, - title: payload.title, - content: payload.content, - category: payload.category, - tags: payload.tags, + title: String(payload.title).trim(), + content: String(payload.content).trim(), + category: payload.category ? String(payload.category).trim() : null, + tags: Array.isArray(payload.tags) ? payload.tags.map((t: any) => String(t).trim()) : [], is_published: payload.is_published ?? true, }) .select() .single(); - if (error) return res.status(500).json({ error: error.message }); + + if (error) { + console.error("[API] /api/posts insert error:", { + code: error.code, + message: error.message, + details: (error as any).details, + }); + return res.status(500).json({ error: error.message || "Failed to create post" }); + } + res.json(data); } catch (e: any) { - res.status(500).json({ error: e?.message || String(e) }); + console.error("[API] /api/posts exception:", e?.message || String(e)); + res.status(500).json({ error: e?.message || "Failed to create post" }); } });