@amogads/ui 1.0.2 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/chat.handler.ts","../src/server/geocode.handler.ts","../src/server/shorten.handler.ts","../src/lib/short-url-store.ts","../src/server/vouchers.handler.ts","../src/lib/supabase/server.ts","../src/lib/auth.ts","../src/server/search.handler.ts","../src/services/ai-search.service.ts","../src/server/messages.handler.ts","../src/lib/supabase/client.ts","../src/stores/auth-store.ts","../src/lib/cookies.ts","../src/features/chattemplate/shared/api/auth.ts","../src/features/chattemplate/shared/api/headers.ts","../src/features/chattemplate/shared/api/errorHandler.ts","../src/features/chattemplate/shared/api/apiClient.ts","../src/features/chattemplate/shared/api/queryBuilder.ts","../src/features/chattemplate/chat/api/messages.api.ts","../src/features/chattemplate/chat/repositories/message-repository.ts","../src/features/chattemplate/chat/api/conversations.api.ts","../src/features/chattemplate/chat/repositories/conversation-repository.ts","../src/server/contacts.handler.ts","../src/features/chattemplate/contacts/api/contacts.mapper.ts","../src/lib/db-alerts/types/db-alert.ts","../src/services/db-alert.service.ts","../src/features/chattemplate/chat/services/chat-storage.service.ts","../src/features/chattemplate/contacts/api/contacts.api.ts","../src/features/chattemplate/contacts/repositories/contact-repository.ts","../src/server/conversations.handler.ts","../src/server/groups.handler.ts","../src/features/chattemplate/groups/api/groups.mapper.ts","../src/features/chattemplate/groups/api/groups.api.ts","../src/features/chattemplate/groups/repositories/group-repository.ts","../src/server/profiles.handler.ts","../src/features/chattemplate/chat/api/profiles.api.ts","../src/features/chattemplate/chat/repositories/profile-repository.ts","../src/server/notifications.handler.ts","../src/server/mail.handler.ts","../src/lib/email/imap.ts","../config/mail.json","../src/lib/email/email-parser.ts","../src/lib/email/mailer.ts","../src/lib/email/attachment-storage.ts"],"sourcesContent":["import { createOpenRouter } from '@openrouter/ai-sdk-provider'\nimport { generateText } from 'ai'\nimport { NextRequest, NextResponse } from 'next/server'\n\nexport const UI_RENDER_SYSTEM_PROMPT = `\nYou are a UI Schema Generator. Your task is to generate a valid UI schema in JSON format based on the user's request.\nYou MUST output ONLY valid JSON. Do not write any explanations, do not wrap it in markdown code blocks, do not write anything else.\n\nIf the user provides an OCR text extraction payload (containing fields like invoice, date, total, business details, customer name, lines, etc.), you MUST analyze the text, extract the key values, and construct an editable Form containing corresponding inputs (e.g. Input with defaultValue, Textarea) so the user can verify, edit, and submit the extracted details.\n\nThe schema MUST follow this exact TypeScript interface:\ninterface UiSchema {\n root: string; // The ID of the root element (usually \"root\")\n elements: {\n [elementId: string]: {\n type: 'Stack' | 'Card' | 'Form' | 'Input' | 'Textarea' | 'Button' | 'Checkbox' | 'Badge' | 'Alert' | 'Separator' | 'Progress' | 'Heading' | 'Text' | 'Price' | 'FeatureList' | 'Tabs' | 'Calendar' | 'Switch' | 'RadioGroup' | 'PremiumStats';\n props?: Record<string, any>;\n children?: string[]; // Array of element IDs that are children of this element\n }\n }\n}\n\nCommon Components & Props:\n1. Stack: props: { direction: 'vertical' | 'horizontal', gap: 'xs' | 'sm' | 'md' | 'lg' | 'xl', align: 'start' | 'center' | 'end' }\n2. Form: props: { onSubmit?: string }\n3. Card: props: { title?: string, description?: string, className?: string, maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl' | 'full', centered?: boolean }\n4. Input: props: { label?: string, name?: string, placeholder?: string, required?: boolean, type?: string, defaultValue?: any }\n5. Textarea: props: { label?: string, name?: string, placeholder?: string, required?: boolean, defaultValue?: any }\n6. Button: props: { label: string, type?: 'button' | 'submit', variant?: 'default' | 'outline' | 'destructive' | 'ghost', className?: string }\n7. Heading: props: { level: '1' | '2' | '3' | '4' | '5' | '6', children: string }\n8. Text: props: { children: string, size?: 'sm' | 'base' | 'lg' | 'xl', className?: string }\n`\n\nexport async function handleChatPost(request: NextRequest) {\n try {\n const { message, model, tool } = await request.json()\n\n if (!message) {\n return NextResponse.json({ error: 'Message is required' }, { status: 400 })\n }\n\n const openRouterApiKey = process.env.OPENROUTER_API_KEY\n if (!openRouterApiKey) {\n return NextResponse.json(\n { error: 'OpenRouter API key is not configured' },\n { status: 500 }\n )\n }\n\n const openrouter = createOpenRouter({\n apiKey: openRouterApiKey,\n })\n\n const isUiRender = tool === 'ui-render'\n\n const { text } = await generateText({\n model: openrouter.chat(model || 'google/gemini-2.5-flash'),\n system: isUiRender ? UI_RENDER_SYSTEM_PROMPT : undefined,\n prompt: message,\n })\n\n return NextResponse.json({ text })\n } catch (error: any) {\n console.error('Error in handleChatPost:', error)\n return NextResponse.json(\n { error: error?.message || 'Failed to generate response' },\n { status: 500 }\n )\n }\n}\n","import { NextRequest, NextResponse } from 'next/server'\n\nexport async function handleGeocodeRequest(req: NextRequest) {\n const { searchParams } = new URL(req.url)\n const lat = searchParams.get('lat')\n const lon = searchParams.get('lon')\n\n if (!lat || !lon) {\n return NextResponse.json({ error: 'Latitude and Longitude are required' }, { status: 400 })\n }\n\n try {\n const response = await fetch(\n `https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${lon}`,\n {\n headers: {\n 'User-Agent': 'AmogaDS/1.0 (contact@amoga.io)',\n 'Accept-Language': 'en'\n },\n next: { revalidate: 3600 }\n }\n )\n\n if (!response.ok) {\n throw new Error(`Nominatim returned status ${response.status}`)\n }\n\n const data = await response.json()\n return NextResponse.json(data)\n } catch (error: any) {\n console.error('Server-side geocoding failed:', error)\n return NextResponse.json({ error: error.message || 'Geocoding failed' }, { status: 500 })\n }\n}\n","import { NextRequest, NextResponse } from 'next/server'\nimport { saveShortUrl } from '@/lib/short-url-store'\n\nfunction getOrigin(request: NextRequest): string {\n const host = request.headers.get('x-forwarded-host') ?? request.headers.get('host')\n const proto = request.headers.get('x-forwarded-proto') ?? 'https'\n if (host) return `${proto}://${host}`\n if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`\n return request.nextUrl.origin\n}\n\nexport async function handleShortenPost(request: NextRequest) {\n try {\n const body = await request.json()\n const { url, durationHours = 1 } = body\n\n if (!url || typeof url !== 'string') {\n return NextResponse.json({ error: 'URL is required' }, { status: 400 })\n }\n\n const expiresAtMs = Date.now() + durationHours * 60 * 60 * 1000\n const expiresAt = new Date(expiresAtMs).toISOString()\n\n let urlWithExpiration = url\n if (url.includes('/l/')) {\n const parts = url.split('/l/')\n const domain = parts[0]\n const config = parts[1]\n urlWithExpiration = `${domain}/l?c=${config}&exp=${expiresAtMs}`\n } else if (url.includes('/l?')) {\n const parsed = new URL(url)\n parsed.searchParams.set('exp', String(expiresAtMs))\n urlWithExpiration = parsed.toString()\n } else {\n const separator = url.includes('?') ? '&' : '?'\n urlWithExpiration = `${url}${separator}exp=${expiresAtMs}`\n }\n\n const origin = getOrigin(request)\n const { shortUrlSuffix } = await saveShortUrl(urlWithExpiration, expiresAtMs)\n const shortUrl = `${origin}/go/${shortUrlSuffix}`\n\n return NextResponse.json(\n { shortUrl, expiresAt },\n {\n status: 200,\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'POST, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type',\n },\n }\n )\n } catch (err) {\n console.error('URL shortening API exception:', err)\n return NextResponse.json(\n { error: err instanceof Error ? err.message : 'Internal Server Error' },\n { status: 500 }\n )\n }\n}\n\nexport async function handleShortenOptions() {\n return new NextResponse(null, {\n status: 204,\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'POST, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type',\n },\n })\n}\n","import { promises as fs } from 'fs'\r\nimport path from 'path'\r\nimport { createClient } from '@supabase/supabase-js'\r\n\r\nexport interface ShortUrlEntry {\r\n id: string\r\n targetUrl: string\r\n createdAt: string\r\n expiresAt: string\r\n}\r\n\r\nconst supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL\r\nconst supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY\r\n\r\nconst supabase = supabaseUrl && supabaseAnonKey\r\n ? createClient(supabaseUrl, supabaseAnonKey)\r\n : null\r\n\r\nconst URLS_FILE = path.join(process.cwd(), 'src/features/link-builder/data/urls.json')\r\nconst ID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'\r\n\r\ntype GlobalStore = typeof globalThis & { __shortUrlStore?: Map<string, ShortUrlEntry> }\r\n\r\nfunction getMemoryStore(): Map<string, ShortUrlEntry> {\r\n const g = globalThis as GlobalStore\r\n if (!g.__shortUrlStore) {\r\n g.__shortUrlStore = new Map()\r\n }\r\n return g.__shortUrlStore\r\n}\r\n\r\nexport function generateShortId(length = 6): string {\r\n let result = ''\r\n for (let i = 0; i < length; i++) {\r\n result += ID_CHARS.charAt(Math.floor(Math.random() * ID_CHARS.length))\r\n }\r\n return result\r\n}\r\n\r\nfunction isKvConfigured(): boolean {\r\n return Boolean(process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN)\r\n}\r\n\r\nasync function kvGet(key: string): Promise<ShortUrlEntry | null> {\r\n if (!isKvConfigured()) return null\r\n try {\r\n const res = await fetch(process.env.KV_REST_API_URL!, {\r\n method: 'POST',\r\n headers: {\r\n Authorization: `Bearer ${process.env.KV_REST_API_TOKEN}`,\r\n 'Content-Type': 'application/json',\r\n },\r\n body: JSON.stringify(['GET', key]),\r\n cache: 'no-store',\r\n })\r\n if (!res.ok) return null\r\n const data = await res.json()\r\n if (!data.result) return null\r\n return JSON.parse(data.result as string) as ShortUrlEntry\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\nasync function kvSet(key: string, entry: ShortUrlEntry, ttlSeconds: number): Promise<boolean> {\r\n if (!isKvConfigured()) return false\r\n try {\r\n const res = await fetch(process.env.KV_REST_API_URL!, {\r\n method: 'POST',\r\n headers: {\r\n Authorization: `Bearer ${process.env.KV_REST_API_TOKEN}`,\r\n 'Content-Type': 'application/json',\r\n },\r\n body: JSON.stringify(['SET', key, JSON.stringify(entry), 'EX', ttlSeconds]),\r\n cache: 'no-store',\r\n })\r\n return res.ok\r\n } catch {\r\n return false\r\n }\r\n}\r\n\r\nasync function readFileStore(): Promise<ShortUrlEntry[]> {\r\n try {\r\n const raw = await fs.readFile(URLS_FILE, 'utf-8')\r\n return JSON.parse(raw) as ShortUrlEntry[]\r\n } catch {\r\n return []\r\n }\r\n}\r\n\r\nasync function writeFileStore(entries: ShortUrlEntry[]): Promise<boolean> {\r\n try {\r\n await fs.writeFile(URLS_FILE, JSON.stringify(entries, null, 2), 'utf-8')\r\n return true\r\n } catch {\r\n return false\r\n }\r\n}\r\n\r\nexport async function saveShortUrl(\r\n targetUrl: string,\r\n expiresAtMs: number\r\n): Promise<{ id: string; entry: ShortUrlEntry; shortUrlSuffix: string }> {\r\n const id = generateShortId()\r\n const entry: ShortUrlEntry = {\r\n id,\r\n targetUrl,\r\n createdAt: new Date().toISOString(),\r\n expiresAt: new Date(expiresAtMs).toISOString(),\r\n }\r\n\r\n const memory = getMemoryStore()\r\n memory.set(id, entry)\r\n\r\n const ttlSeconds = Math.max(60, Math.ceil((expiresAtMs - Date.now()) / 1000))\r\n const kvSaved = await kvSet(`short:${id}`, entry, ttlSeconds)\r\n\r\n const existing = await readFileStore()\r\n existing.push(entry)\r\n const fileSaved = await writeFileStore(existing)\r\n\r\n let supabaseSaved = false\r\n if (supabase) {\r\n try {\r\n const { error } = await supabase\r\n .from('short_urls')\r\n .insert({\r\n id,\r\n target_url: targetUrl,\r\n expires_at: new Date(expiresAtMs).toISOString()\r\n })\r\n if (error) {\r\n console.error('Supabase saveShortUrl error:', error)\r\n } else {\r\n supabaseSaved = true\r\n console.log('Supabase saveShortUrl success:', id)\r\n }\r\n } catch (e) {\r\n console.error('Supabase saveShortUrl exception:', e)\r\n }\r\n }\r\n\r\n // When no persistent store is available (typical on Vercel without KV or Supabase),\r\n // embed the redirect target so the link survives cold starts.\r\n const needsFallback = !kvSaved && !fileSaved && !supabaseSaved\r\n const shortUrlSuffix = needsFallback\r\n ? `${id}?r=${Buffer.from(targetUrl).toString('base64url')}`\r\n : id\r\n\r\n return { id, entry, shortUrlSuffix }\r\n}\r\n\r\nlet fileStoreLoaded = false\r\n\r\nasync function ensureFileStoreLoaded(): Promise<void> {\r\n if (fileStoreLoaded) return\r\n fileStoreLoaded = true\r\n const entries = await readFileStore()\r\n const memory = getMemoryStore()\r\n for (const entry of entries) {\r\n memory.set(entry.id, entry)\r\n }\r\n}\r\n\r\nexport async function getShortUrl(id: string): Promise<ShortUrlEntry | null> {\r\n await ensureFileStoreLoaded()\r\n\r\n const memory = getMemoryStore()\r\n const cached = memory.get(id)\r\n if (cached) return cached\r\n\r\n // Check Supabase first as it is our primary persistent store on production\r\n if (supabase) {\r\n try {\r\n const { data, error } = await supabase\r\n .from('short_urls')\r\n .select('*')\r\n .eq('id', id)\r\n .single()\r\n \r\n if (!error && data) {\r\n const entry: ShortUrlEntry = {\r\n id: data.id,\r\n targetUrl: data.target_url,\r\n createdAt: data.created_at,\r\n expiresAt: data.expires_at,\r\n }\r\n memory.set(id, entry)\r\n return entry\r\n }\r\n } catch (e) {\r\n console.error('Supabase getShortUrl exception:', e)\r\n }\r\n }\r\n\r\n const fromKv = await kvGet(`short:${id}`)\r\n if (fromKv) {\r\n memory.set(id, fromKv)\r\n return fromKv\r\n }\r\n\r\n const fromFile = await readFileStore()\r\n const entry = fromFile.find((e) => e.id === id) ?? null\r\n if (entry) {\r\n memory.set(id, entry)\r\n }\r\n return entry\r\n}\r\n","import { NextRequest, NextResponse } from 'next/server'\nimport { createClient } from '@/lib/supabase/server'\nimport { getServerSession } from 'next-auth'\nimport { authOptions, stringToUuid } from '@/lib/auth'\n\nexport async function handleVouchersGet() {\n try {\n const session = await getServerSession(authOptions)\n const supabase = await createClient()\n\n let userId: string | null = null\n let userEmail: string | null = null\n\n if (session?.user) {\n const user = session.user as any\n userEmail = user.email ? user.email.toLowerCase() : null\n userId = stringToUuid(user.id || user.email)\n } else {\n const { data: { user } } = await supabase.auth.getUser()\n if (user?.id) {\n userId = user.id\n userEmail = user.email ? user.email.toLowerCase() : null\n }\n }\n\n let voucherRows: any[] = []\n try {\n if (userId) {\n const { data: vData } = await supabase\n .from('vouchers')\n .select('*')\n .eq('user_id', userId)\n .order('created_at', { ascending: false })\n .limit(100)\n\n if (vData && vData.length > 0) {\n voucherRows = vData\n } else if (userEmail) {\n const { data: profileRow } = await supabase\n .from('profiles')\n .select('id')\n .eq('email', userEmail)\n .maybeSingle()\n\n if (profileRow?.id) {\n const { data: vByProfile } = await supabase\n .from('vouchers')\n .select('*')\n .eq('user_id', profileRow.id)\n .order('created_at', { ascending: false })\n .limit(100)\n if (vByProfile && vByProfile.length > 0) {\n voucherRows = vByProfile\n }\n }\n }\n }\n\n if (voucherRows.length === 0) {\n const { data: fallbackVData } = await supabase\n .from('vouchers')\n .select('*')\n .order('created_at', { ascending: false })\n .limit(100)\n if (fallbackVData) voucherRows = fallbackVData\n }\n } catch (e) {\n console.warn('[GET /api/vouchers] Vouchers table fetch warning:', e)\n }\n\n let chatFileRows: any[] = []\n try {\n if (userId) {\n const [rOwner, rSender] = await Promise.all([\n supabase\n .from('chat_messages')\n .select('*')\n .eq('owner_user_id', userId)\n .not('file_url', 'is', null)\n .order('created_at', { ascending: false })\n .limit(100),\n supabase\n .from('chat_messages')\n .select('*')\n .eq('sender_user_id', userId)\n .not('file_url', 'is', null)\n .order('created_at', { ascending: false })\n .limit(100),\n ])\n\n const userMsgs = [...(rOwner.data ?? []), ...(rSender.data ?? [])]\n if (userMsgs.length > 0) {\n chatFileRows = userMsgs.map((msg: any) => ({\n id: `chat-file-${msg.id}`,\n voucher_no: msg.id ? String(msg.id).slice(0, 8) : 'file',\n file_name: msg.file_name || 'Attached File',\n original_file_url: msg.file_url ?? undefined,\n edited_file_url: msg.file_url ?? undefined,\n vendor_name: msg.sender_name || 'Uploaded Document',\n customer_name: userEmail ? userEmail.split('@')[0] : 'User',\n user_name: userEmail ? userEmail.split('@')[0] : 'User',\n created_at: msg.created_at || new Date().toISOString(),\n status: msg.processing_status || 'Active',\n edited_json: msg.file_content_json || null,\n }))\n }\n }\n\n if (chatFileRows.length === 0) {\n const { data: fallbackMsgs } = await supabase\n .from('chat_messages')\n .select('*')\n .not('file_url', 'is', null)\n .order('created_at', { ascending: false })\n .limit(100)\n\n if (fallbackMsgs && fallbackMsgs.length > 0) {\n chatFileRows = fallbackMsgs.map((msg: any) => ({\n id: `chat-file-${msg.id}`,\n voucher_no: msg.id ? String(msg.id).slice(0, 8) : 'file',\n file_name: msg.file_name || 'Attached File',\n original_file_url: msg.file_url ?? undefined,\n edited_file_url: msg.file_url ?? undefined,\n vendor_name: msg.sender_name || 'Uploaded Document',\n customer_name: userEmail ? userEmail.split('@')[0] : 'User',\n user_name: userEmail ? userEmail.split('@')[0] : 'User',\n created_at: msg.created_at || new Date().toISOString(),\n status: msg.processing_status || 'Active',\n edited_json: msg.file_content_json || null,\n }))\n }\n }\n } catch (e) {\n console.warn('[GET /api/vouchers] Chat files fetch warning:', e)\n }\n\n const allFiles = [...voucherRows, ...chatFileRows]\n allFiles.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())\n\n const seen = new Set<string>()\n const uniqueFiles: any[] = []\n for (const f of allFiles) {\n const key = `${f.id}|${f.file_name ?? ''}|${f.original_file_url ?? ''}`\n if (!seen.has(key)) {\n seen.add(key)\n uniqueFiles.push(f)\n }\n }\n\n return NextResponse.json({ success: true, data: uniqueFiles })\n } catch (err: any) {\n console.error('[GET /api/vouchers] Internal error:', err)\n return NextResponse.json({ error: err.message || 'Internal error' }, { status: 500 })\n }\n}\n\nexport async function handleVouchersPost(request: NextRequest) {\n try {\n const session = await getServerSession(authOptions)\n const supabase = await createClient()\n\n let userId: string | null = null\n let userEmail: string | null = null\n\n if (session?.user) {\n const user = session.user as any\n userEmail = user.email ? user.email.toLowerCase() : null\n userId = stringToUuid(user.id || user.email)\n } else {\n const { data: { user } } = await supabase.auth.getUser()\n if (user?.id) {\n userId = user.id\n userEmail = user.email?.toLowerCase() ?? null\n }\n }\n\n if (!userId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n if (userEmail) {\n const { data: profileRow } = await supabase\n .from('profiles')\n .select('id')\n .eq('email', userEmail)\n .maybeSingle()\n if (profileRow?.id) userId = profileRow.id\n }\n\n const body = await request.json()\n const {\n voucher_no,\n file_name,\n original_file_url,\n edited_file_url,\n edited_json,\n vendor_name,\n customer_name,\n invoice_date,\n total,\n currency,\n } = body\n\n if (!voucher_no || !file_name) {\n return NextResponse.json({ error: 'voucher_no and file_name are required' }, { status: 400 })\n }\n\n const { data: row, error } = await supabase\n .from('vouchers')\n .insert({\n user_id: userId,\n voucher_no,\n file_name,\n original_file_url: original_file_url || null,\n edited_file_url: edited_file_url || null,\n edited_json: edited_json || null,\n vendor_name: vendor_name || null,\n customer_name: customer_name || null,\n invoice_date: invoice_date || null,\n total: total || null,\n currency: currency || 'USD',\n status: 'Active',\n })\n .select('*')\n .single()\n\n if (error) {\n return NextResponse.json({ error: error.message }, { status: 500 })\n }\n\n return NextResponse.json({ success: true, data: row }, { status: 201 })\n } catch (err: any) {\n console.error('[POST /api/vouchers] Internal error:', err)\n return NextResponse.json({ error: err.message || 'Internal error' }, { status: 500 })\n }\n}\n","import { createServerClient } from '@supabase/ssr'\r\nimport { cookies } from 'next/headers'\r\n\r\nexport async function createClient() {\r\n const cookieStore = await cookies()\r\n\r\n return createServerClient(\r\n process.env.NEXT_PUBLIC_SUPABASE_URL!,\r\n process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,\r\n {\r\n cookies: {\r\n getAll() {\r\n return cookieStore.getAll()\r\n },\r\n setAll(cookiesToSet) {\r\n try {\r\n cookiesToSet.forEach(({ name, value, options }) =>\r\n cookieStore.set(name, value, options)\r\n )\r\n } catch {\r\n // The `setAll` method was called from a Server Component.\r\n // This can be ignored if you have middleware refreshing\r\n // user sessions.\r\n }\r\n },\r\n },\r\n }\r\n )\r\n}\r\n","import { NextAuthOptions } from 'next-auth'\nimport GoogleProvider from 'next-auth/providers/google'\nimport { createClient as createSupabaseClient } from '@supabase/supabase-js'\nimport crypto from 'crypto'\n\nconst SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL || ''\nconst SUPABASE_KEY = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY || ''\n\n/**\n * Converts any string (like a Google numeric ID or email) into a deterministic, valid UUID string.\n * Leaves string untouched if it is already a valid UUID v4 format.\n */\nexport function stringToUuid(str: string): string {\n if (!str) return crypto.randomUUID()\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i\n if (uuidRegex.test(str)) {\n return str\n }\n const hash = crypto.createHash('md5').update(str).digest('hex')\n return `${hash.substring(0, 8)}-${hash.substring(8, 12)}-4${hash.substring(13, 16)}-a${hash.substring(17, 20)}-${hash.substring(20, 32)}`\n}\n\nexport const authOptions: NextAuthOptions = {\n providers: [\n GoogleProvider({\n clientId: process.env.GOOGLE_CLIENT_ID || process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || '',\n clientSecret: process.env.GOOGLE_CLIENT_SECRET || process.env.NEXT_PUBLIC_GOOGLE_SECRET_ID || '',\n authorization: {\n params: {\n prompt: 'select_account',\n access_type: 'offline',\n response_type: 'code',\n },\n },\n checks: ['none'],\n }),\n ],\n secret: process.env.NEXTAUTH_SECRET || 'secret_next_auth_shadcn_admin_key_2026_super_secure',\n session: {\n strategy: 'jwt',\n maxAge: 30 * 24 * 60 * 60, // 30 days\n }, \n callbacks: {\n async redirect({ url, baseUrl }) {\n try {\n const { cookies } = await import('next/headers')\n const cookieStore = await cookies()\n const isMobileAuth = cookieStore.get('mobile_auth')?.value === 'true' || url.includes('is_mobile=true')\n if (isMobileAuth) {\n console.log('📱 [NextAuth Redirect Callback] Mobile auth detected. Redirecting to /auth/callback?is_mobile=true')\n return `${baseUrl}/auth/callback?is_mobile=true&next=/`\n }\n } catch (err) {\n console.error('❌ [NextAuth Redirect Callback] Error inspecting cookies:', err)\n }\n\n if (url.includes('/auth/callback')) return url\n if (url.startsWith('/')) return `${baseUrl}${url}`\n else if (new URL(url).origin === baseUrl) return url\n return baseUrl\n },\n async signIn({ user }) {\n if (!user.email) return false\n\n try {\n if (SUPABASE_URL && SUPABASE_KEY) {\n const supabase = createSupabaseClient(SUPABASE_URL, SUPABASE_KEY)\n const fallbackUuid = stringToUuid(user.id || user.email)\n\n // Check if profile exists by email first (to preserve existing UUID) or by ID\n const { data: existingProfile } = await supabase\n .from('profiles')\n .select('id, auth_user_id')\n .eq('email', user.email.toLowerCase())\n .maybeSingle()\n\n const profileId = existingProfile?.id || fallbackUuid\n\n const profileData: any = {\n id: profileId,\n name: user.name || user.email.split('@')[0],\n email: user.email.toLowerCase(),\n avatar: user.image || null,\n updated_at: new Date().toISOString(),\n }\n\n profileData.auth_user_id = existingProfile?.auth_user_id || profileId\n\n if (existingProfile) {\n await supabase\n .from('profiles')\n .update(profileData)\n .eq('id', existingProfile.id)\n } else {\n await supabase\n .from('profiles')\n .insert(profileData)\n }\n\n // Attach canonical valid UUID to user object\n user.id = profileId\n }\n } catch (err) {\n console.error('[NextAuth] Error syncing user to profiles table:', err)\n }\n\n return true\n },\n async jwt({ token, user }) {\n if (user) {\n token.id = stringToUuid(user.id || user.email!)\n token.email = user.email\n token.name = user.name\n token.picture = user.image\n } else if (token.sub && !stringToUuid(token.sub)) {\n token.id = stringToUuid(token.sub)\n }\n return token\n },\n async session({ session, token }) {\n if (session.user) {\n const canonicalId = stringToUuid((token.id || token.sub || session.user.email) as string)\n ;(session.user as any).id = canonicalId\n ;(session.user as any).picture = token.picture || token.image\n }\n return session\n },\n },\n pages: {\n signIn: '/sign-in',\n error: '/sign-in',\n },\n}\n","import { NextRequest, NextResponse } from 'next/server'\nimport { AiSearchService } from '../services/ai-search.service'\n\nexport async function handleSearchPost(request: NextRequest) {\n try {\n const body = await request.json()\n const { query, toolPrompt, toolId } = body\n\n if (!query || typeof query !== 'string') {\n return NextResponse.json({ error: 'Query is required' }, { status: 400 })\n }\n\n const tavilyKey = process.env.TAVILY_API_KEY || process.env.NEXT_PUBLIC_TAVILY_API_KEY || ''\n const geminiKey = process.env.GEMINI_API_KEY || process.env.NEXT_PUBLIC_GEMINI_API_KEY || ''\n\n if (!tavilyKey) {\n return NextResponse.json({ error: 'Tavily API key is not configured' }, { status: 500 })\n }\n\n const { results, images } = await AiSearchService.searchWeb(query, tavilyKey)\n\n if (results.length === 0) {\n return NextResponse.json({\n answer: 'No search results found for your query. Please try a different search term.',\n sources: [],\n images: [],\n })\n }\n\n const context = results\n .map((item: any) => `Title: ${item.title}\\nContent: ${item.content}\\nURL: ${item.url}`)\n .join('\\n\\n')\n\n const systemPrompt = toolPrompt || 'You are an AI Search Assistant. Give comprehensive answers using the search results provided. Use headings and bullet points when useful, and always cite your sources.'\n\n const prompt = `\n${systemPrompt}\n\nQuestion:\n${query}\n\nSearch Results:\n${context}\n\nInstructions:\n- Give a comprehensive answer based on search results.\n- Use headings and bullet points for readability.\n- Cite sources accurately.\n`\n\n let answer = ''\n if (geminiKey) {\n answer = await AiSearchService.generateAnswer(prompt, geminiKey)\n }\n\n return NextResponse.json({\n answer,\n sources: results,\n images,\n })\n } catch (err: any) {\n console.error('Error in handleSearchPost:', err)\n return NextResponse.json(\n { error: err.message || 'Failed to process AI search request' },\n { status: 500 }\n )\n }\n}\n","/**\n * AI Search Service\n * Provides web search through Tavily API and LLM analysis through Gemini.\n */\n\nimport axios from 'axios'\n\nexport interface SearchSource {\n title: string\n url: string\n content: string\n}\n\nexport interface SearchResponse {\n answer: string\n sources: SearchSource[]\n images: string[]\n}\n\nexport class AiSearchService {\n /**\n * Executes web search through Tavily API.\n */\n static async searchWeb(query: string, apiKey: string): Promise<{ results: SearchSource[]; images: string[] }> {\n if (!apiKey) {\n throw new Error('Tavily API key is not configured.')\n }\n\n const res = await axios.post(\n 'https://api.tavily.com/search',\n {\n api_key: apiKey,\n query,\n search_depth: 'advanced',\n max_results: 10,\n include_images: true,\n },\n { timeout: 15000 }\n )\n\n return {\n results: res.data?.results || [],\n images: res.data?.images || [],\n }\n }\n\n /**\n * Generates answer analysis using Gemini API.\n */\n static async generateAnswer(prompt: string, apiKey: string): Promise<string> {\n if (!apiKey) {\n throw new Error('Gemini API key is not configured.')\n }\n\n const res = await axios.post(\n `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${apiKey}`,\n {\n contents: [\n {\n parts: [{ text: prompt }],\n },\n ],\n },\n { timeout: 30000 }\n )\n\n return res.data?.candidates?.[0]?.content?.parts?.[0]?.text || ''\n }\n}\n","import { NextRequest, NextResponse } from 'next/server'\nimport { getConversationMessages, createMessage } from '@/features/chattemplate/chat/repositories/message-repository'\nimport { getOrCreateDirectConversation } from '@/features/chattemplate/chat/repositories/conversation-repository'\n\nexport async function handleMessagesGet(request: NextRequest) {\n try {\n const { searchParams } = new URL(request.url)\n const conversationId = searchParams.get('conversationId')\n const recipientId = searchParams.get('recipientId')\n const senderId = searchParams.get('senderId')\n\n if (!conversationId && !recipientId) {\n return NextResponse.json({ error: 'conversationId or recipientId is required' }, { status: 400 })\n }\n\n if (!senderId) {\n return NextResponse.json({ error: 'senderId (owner_user_id) is required' }, { status: 400 })\n }\n\n let targetConvoId = conversationId\n\n if (!targetConvoId && recipientId) {\n targetConvoId = await getOrCreateDirectConversation(senderId, recipientId)\n }\n\n if (!targetConvoId) {\n return NextResponse.json([])\n }\n\n const messages = await getConversationMessages(targetConvoId, senderId)\n return NextResponse.json(messages)\n } catch (err) {\n console.error('GET messages error:', err)\n return NextResponse.json(\n { error: err instanceof Error ? err.message : 'Failed to fetch messages' },\n { status: 500 }\n )\n }\n}\n\nexport async function handleMessagesPost(request: NextRequest) {\n try {\n const body = await request.json()\n\n const isAttachment = body.messageType && body.messageType !== 'text'\n const hasContent = body.message?.trim() || (isAttachment && body.fileUrl)\n\n if (!hasContent) {\n return NextResponse.json(\n { error: 'message (or fileUrl for attachments) is required' },\n { status: 400 }\n )\n }\n\n let targetConvoId = body.conversationId\n\n if (!targetConvoId && body.recipientId) {\n const senderId = body.senderId\n if (!senderId) {\n return NextResponse.json(\n { error: 'senderId is required to resolve conversation' },\n { status: 400 }\n )\n }\n targetConvoId = await getOrCreateDirectConversation(senderId, body.recipientId)\n }\n\n if (!targetConvoId) {\n return NextResponse.json(\n { error: 'Could not resolve or create conversation' },\n { status: 400 }\n )\n }\n\n const msg = await createMessage({\n conversationId: targetConvoId,\n senderId: body.senderId,\n message: body.message,\n messageType: body.messageType || 'text',\n fileUrl: body.fileUrl,\n fileName: body.fileName,\n replyToMessageId: body.replyToMessageId,\n forwardedFromMessageId: body.forwardedFromMessageId,\n locationData: body.location,\n })\n\n if (!msg) {\n return NextResponse.json(\n { error: 'Failed to create message' },\n { status: 500 }\n )\n }\n\n return NextResponse.json(msg, { status: 201 })\n } catch (err) {\n console.error('POST message error:', err)\n return NextResponse.json(\n { error: err instanceof Error ? err.message : 'Failed to create message' },\n { status: 500 }\n )\n }\n}\n","import { createBrowserClient } from '@supabase/ssr'\r\n\r\nlet clientSingleton: ReturnType<typeof createBrowserClient> | null = null\r\n\r\nexport function createClient() {\r\n if (typeof window === 'undefined') {\r\n return createBrowserClient(\r\n process.env.NEXT_PUBLIC_SUPABASE_URL!,\r\n process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!\r\n )\r\n }\r\n\r\n if (!clientSingleton) {\r\n clientSingleton = createBrowserClient(\r\n process.env.NEXT_PUBLIC_SUPABASE_URL!,\r\n process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!\r\n )\r\n }\r\n return clientSingleton;\r\n}\r\n","import { create } from 'zustand'\r\nimport { getCookie, setCookie, removeCookie } from '@/lib/cookies'\r\n\r\nconst ACCESS_TOKEN = 'thisisjustarandomstring'\r\nconst USER_DATA = 'auth_user_data'\r\n\r\ninterface AuthUser {\r\n id: string // 👈 ADD THIS - the auth.uid() from Supabase\r\n accountNo: string\r\n email: string\r\n name?: string\r\n picture?: string\r\n role: string[]\r\n exp: number\r\n}\r\n\r\ninterface AuthState {\r\n auth: {\r\n user: AuthUser | null\r\n setUser: (user: AuthUser | null) => void\r\n accessToken: string\r\n setAccessToken: (accessToken: string) => void\r\n resetAccessToken: () => void\r\n reset: () => void\r\n }\r\n}\r\n\r\nexport const useAuthStore = create<AuthState>()((set) => {\r\n const cookieState = getCookie(ACCESS_TOKEN)\r\n let initToken = ''\r\n if (cookieState) {\r\n try {\r\n initToken = JSON.parse(cookieState)\r\n } catch {\r\n removeCookie(ACCESS_TOKEN)\r\n }\r\n }\r\n\r\n const userCookie = getCookie(USER_DATA)\r\n let initUser: AuthUser | null = null\r\n if (userCookie) {\r\n try {\r\n const parsed = JSON.parse(decodeURIComponent(userCookie))\r\n if (parsed.exp && parsed.exp > Date.now()) {\r\n initUser = parsed\r\n } else {\r\n removeCookie(ACCESS_TOKEN)\r\n removeCookie(USER_DATA)\r\n }\r\n } catch {\r\n initUser = null\r\n removeCookie(USER_DATA)\r\n }\r\n }\r\n\r\n return {\r\n auth: {\r\n user: initUser,\r\n setUser: (user) =>\r\n set((state) => {\r\n if (user) {\r\n setCookie(USER_DATA, encodeURIComponent(JSON.stringify(user)))\r\n } else {\r\n removeCookie(USER_DATA)\r\n }\r\n return { ...state, auth: { ...state.auth, user } }\r\n }),\r\n accessToken: initUser ? initToken : '',\r\n setAccessToken: (accessToken) =>\r\n set((state) => {\r\n setCookie(ACCESS_TOKEN, JSON.stringify(accessToken))\r\n return { ...state, auth: { ...state.auth, accessToken } }\r\n }),\r\n resetAccessToken: () =>\r\n set((state) => {\r\n removeCookie(ACCESS_TOKEN)\r\n return { ...state, auth: { ...state.auth, accessToken: '' } }\r\n }),\r\n reset: () =>\r\n set((state) => {\r\n removeCookie(ACCESS_TOKEN)\r\n removeCookie(USER_DATA)\r\n return {\r\n ...state,\r\n auth: { ...state.auth, user: null, accessToken: '' },\r\n }\r\n }),\r\n },\r\n }\r\n})","/**\r\n * Cookie utility functions using manual document.cookie approach\r\n * Replaces js-cookie dependency for better consistency\r\n */\r\n\r\nconst DEFAULT_MAX_AGE = 60 * 60 * 24 * 7 // 7 days\r\n\r\n/**\r\n * Get a cookie value by name\r\n */\r\nexport function getCookie(name: string): string | undefined {\r\n if (typeof document === 'undefined') return undefined\r\n\r\n const value = `; ${document.cookie}`\r\n const parts = value.split(`; ${name}=`)\r\n if (parts.length === 2) {\r\n const cookieValue = parts.pop()?.split(';').shift()\r\n return cookieValue\r\n }\r\n return undefined\r\n}\r\n\r\n/**\r\n * Set a cookie with name, value, and optional max age\r\n */\r\nexport function setCookie(\r\n name: string,\r\n value: string,\r\n maxAge: number = DEFAULT_MAX_AGE\r\n): void {\r\n if (typeof document === 'undefined') return\r\n\r\n document.cookie = `${name}=${value}; path=/; max-age=${maxAge}`\r\n}\r\n\r\n/**\r\n * Remove a cookie by setting its max age to 0\r\n */\r\nexport function removeCookie(name: string): void {\r\n if (typeof document === 'undefined') return\r\n\r\n document.cookie = `${name}=; path=/; max-age=0`\r\n}\r\n","import { createClient } from '@/lib/supabase/client'\nimport { useAuthStore } from '@/stores/auth-store'\nimport { stringToUuid } from '@/lib/auth'\n\n/**\n * Retrieves the current session's access token (JWT) from Supabase auth or NextAuth store token.\n * Falls back to the publishable anon key if no active valid Supabase JWT session exists.\n */\nexport async function getAccessToken(): Promise<string> {\n const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY || ''\n try {\n const storeToken = useAuthStore.getState().auth.accessToken\n if (storeToken && typeof storeToken === 'string' && storeToken.split('.').length === 3) {\n return storeToken\n }\n\n const supabase = createClient()\n const { data: { session } } = await supabase.auth.getSession()\n if (session?.access_token && session.access_token.split('.').length === 3) {\n return session.access_token\n }\n\n return supabaseKey\n } catch (error) {\n return supabaseKey\n }\n}\n\n/**\n * Retrieves the current authenticated user's profile details.\n * Resolves the actual profile UUID from Supabase by email to ensure it matches\n * the UUID stored in chat_messages, vouchers, contacts, etc.\n */\nexport async function getCurrentUser() {\n try {\n // Try Supabase session first (most reliable UUID)\n const supabase = createClient()\n const { data: { user: supabaseUser } } = await supabase.auth.getUser()\n if (supabaseUser) return supabaseUser\n\n // NextAuth path: resolve profile UUID via email\n const storeUser = useAuthStore.getState().auth.user\n if (!storeUser) return null\n\n const email = storeUser.email?.toLowerCase()\n if (email) {\n try {\n const { data: profileRow } = await supabase\n .from('profiles')\n .select('id, name, email, avatar')\n .eq('email', email)\n .maybeSingle()\n\n if (profileRow?.id) {\n return {\n id: profileRow.id,\n email: profileRow.email,\n user_metadata: {\n name: profileRow.name,\n avatar_url: profileRow.avatar,\n },\n }\n }\n } catch {}\n }\n\n // Last resort: use stringToUuid on storeUser.id\n const validId = stringToUuid(storeUser.id || storeUser.email)\n return {\n id: validId,\n email: storeUser.email,\n user_metadata: {\n name: storeUser.name,\n avatar_url: storeUser.picture,\n },\n }\n } catch (error) {\n console.error('[Auth Core] Failed to get current user:', error)\n return null\n }\n}\n\n","import { getAccessToken } from './auth'\n\n/**\n * Builds HTTP headers required for Supabase PostgREST API requests.\n * Automatically attaches apikey, Authorization, and default content-types.\n * Validates JWT tokens so invalid tokens (like 'next-auth-session') fallback to Supabase anon key.\n */\nexport async function getHeaders(customHeaders: Record<string, string> = {}): Promise<Record<string, string>> {\n const token = await getAccessToken()\n const apiKey = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY || ''\n \n const is3PartJwt = typeof token === 'string' && token.split('.').length === 3\n const validBearerToken = is3PartJwt ? token : apiKey\n\n return {\n 'apikey': apiKey,\n 'Authorization': `Bearer ${validBearerToken}`,\n 'Content-Type': 'application/json',\n ...customHeaders\n }\n}\n","export class ApiError extends Error {\r\n code?: string\r\n details?: string | null\r\n hint?: string | null\r\n\r\n constructor(message: string, code?: string, details?: string | null, hint?: string | null) {\r\n super(message)\r\n this.name = 'ApiError'\r\n this.code = code\r\n this.details = details\r\n this.hint = hint\r\n }\r\n}\r\n\r\n/**\r\n * Handles error responses from PostgREST API requests by extracting error details and throwing ApiError.\r\n */\r\nexport async function handleError(response: Response): Promise<never> {\r\n let errorData: any\r\n try {\r\n errorData = await response.json()\r\n } catch {\r\n throw new ApiError(response.statusText || 'An unknown network error occurred')\r\n }\r\n\r\n const message = errorData?.message || errorData?.error_description || 'API request failed'\r\n const code = errorData?.code\r\n const details = errorData?.details\r\n const hint = errorData?.hint\r\n\r\n throw new ApiError(message, code, details, hint)\r\n}\r\n","import { getHeaders } from './headers'\r\nimport { handleError } from './errorHandler'\r\n\r\nconst SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL\r\n\r\nif (!SUPABASE_URL) {\r\n throw new Error('Missing environment variable: NEXT_PUBLIC_SUPABASE_URL')\r\n}\r\n\r\n// Clean base URL trailing slash\r\nconst BASE_URL = SUPABASE_URL.endsWith('/') ? SUPABASE_URL.slice(0, -1) : SUPABASE_URL\r\n\r\ninterface RequestOptions {\r\n headers?: Record<string, string>\r\n body?: any\r\n}\r\n\r\n/**\r\n * Helper to safely parse JSON response, handling empty bodies.\r\n */\r\nasync function parseResponse<T>(response: Response): Promise<T> {\r\n const text = await response.text()\r\n return (text ? JSON.parse(text) : {}) as T\r\n}\r\n\r\n/**\r\n * postgREST API Client wrapper using standard HTTP calls via fetch.\r\n */\r\nexport const apiClient = {\r\n async get<T>(path: string, options?: RequestOptions): Promise<T> {\r\n const headers = await getHeaders(options?.headers)\r\n const response = await fetch(`${BASE_URL}${path}`, {\r\n method: 'GET',\r\n headers,\r\n })\r\n \r\n if (!response.ok) {\r\n await handleError(response)\r\n }\r\n \r\n return parseResponse<T>(response)\r\n },\r\n\r\n async post<T>(path: string, body: any, options?: RequestOptions): Promise<T> {\r\n const defaultHeaders = {\r\n 'Prefer': 'return=representation'\r\n }\r\n const headers = await getHeaders({ ...defaultHeaders, ...options?.headers })\r\n const response = await fetch(`${BASE_URL}${path}`, {\r\n method: 'POST',\r\n headers,\r\n body: JSON.stringify(body),\r\n })\r\n\r\n if (!response.ok) {\r\n await handleError(response)\r\n }\r\n\r\n return parseResponse<T>(response)\r\n },\r\n\r\n async patch<T>(path: string, body: any, options?: RequestOptions): Promise<T> {\r\n const defaultHeaders = {\r\n 'Prefer': 'return=representation'\r\n }\r\n const headers = await getHeaders({ ...defaultHeaders, ...options?.headers })\r\n const response = await fetch(`${BASE_URL}${path}`, {\r\n method: 'PATCH',\r\n headers,\r\n body: JSON.stringify(body),\r\n })\r\n\r\n if (!response.ok) {\r\n await handleError(response)\r\n }\r\n\r\n return parseResponse<T>(response)\r\n },\r\n\r\n async delete<T>(path: string, options?: RequestOptions): Promise<T> {\r\n const defaultHeaders = {\r\n 'Prefer': 'return=representation'\r\n }\r\n const headers = await getHeaders({ ...defaultHeaders, ...options?.headers })\r\n const response = await fetch(`${BASE_URL}${path}`, {\r\n method: 'DELETE',\r\n headers,\r\n })\r\n\r\n if (!response.ok) {\r\n await handleError(response)\r\n }\r\n\r\n return parseResponse<T>(response)\r\n }\r\n}\r\n","/**\n * Standard chainable helper to construct PostgREST query parameters.\n * Eliminates raw string manipulation for URLs.\n */\nexport class QueryBuilder {\n private params: URLSearchParams = new URLSearchParams()\n\n /**\n * Embeds resource relationships or filters output columns.\n */\n select(fields: string): this {\n // Strip whitespace and newlines for a clean query parameter\n const cleaned = fields.replace(/\\s+/g, '')\n this.params.set('select', cleaned)\n return this\n }\n\n /**\n * Equality filter.\n */\n eq(column: string, value: any): this {\n this.params.append(column, `eq.${value}`)\n return this\n }\n\n lt(column: string, value: any): this {\n this.params.append(column, `lt.${value}`)\n return this\n }\n\n /**\n * Case-insensitive pattern matching filter.\n */\n ilike(column: string, value: string): this {\n this.params.append(column, `ilike.${value}`)\n return this\n }\n\n /**\n * IN filter matching array of values.\n */\n in(column: string, values: any[]): this {\n const formatted = values.map((v) => `${v}`).join(',')\n this.params.append(column, `in.(${formatted})`)\n return this\n }\n\n /**\n * Logical OR filter matching multiple column criteria.\n * Example string: \"email.ilike.test@gmail.com,contact_user_id.eq.some-uuid\"\n */\n or(filterString: string): this {\n this.params.set('or', `(${filterString})`)\n return this\n }\n\n /**\n * Sorting filter.\n */\n order(column: string, options?: { ascending?: boolean }): this {\n const dir = options?.ascending !== false ? 'asc' : 'desc'\n this.params.set('order', `${column}.${dir}`)\n return this\n }\n\n /**\n * Limit result size.\n */\n limit(n: number): this {\n this.params.set('limit', n.toString())\n return this\n }\n\n /**\n * Offset result index.\n */\n offset(n: number): this {\n this.params.set('offset', n.toString())\n return this\n }\n\n /**\n * Generates the final query parameter string (e.g. \"?owner_id=eq.123\").\n */\n toString(): string {\n const q = this.params.toString()\n return q ? `?${q}` : ''\n }\n}\n\n/**\n * Instantiates a new QueryBuilder query chain.\n */\nexport function createQuery(): QueryBuilder {\n return new QueryBuilder()\n}\n","import { apiClient } from '../../shared/api/apiClient'\r\nimport { createQuery } from '../../shared/api/queryBuilder'\r\nimport { Message } from '../types/chat.types'\r\n\r\n/**\r\n * Fetch all messages for a specific conversation and user copy.\r\n */\r\nexport async function getConversationMessages(\r\n conversationId: string,\r\n userId: string,\r\n options?: { limit?: number; before?: string }\r\n): Promise<Message[]> {\r\n try {\r\n const query = createQuery()\r\n .select(\r\n `\r\n *,\r\n sender:profiles!sender_user_id (\r\n id,\r\n name,\r\n email,\r\n avatar\r\n )\r\n `\r\n )\r\n .eq('conversation_id', conversationId)\r\n .eq('owner_user_id', userId)\r\n .or('deleted.eq.false,deleted_by.not.is.null')\r\n\r\n if (options?.before) query.lt('created_at', options.before)\r\n query.order('created_at', { ascending: !options?.limit })\r\n if (options?.limit) query.limit(options.limit)\r\n\r\n const response = await apiClient.get<any[]>(\r\n `/rest/v1/chat_messages${query.toString()}`\r\n )\r\n const data = options?.limit ? [...response].reverse() : response\r\n if (!data) return []\r\n\r\n const messages: Message[] = data.map((d: any) => ({\r\n id: d.id,\r\n conversation_id: d.conversation_id,\r\n owner_user_id: d.owner_user_id,\r\n sender_user_id: d.sender_user_id,\r\n message: d.message,\r\n message_type: d.message_type,\r\n direction: d.direction,\r\n sent: d.sent,\r\n received: d.received,\r\n created_at: d.created_at,\r\n\r\n message_status: d.message_status || undefined,\r\n client_message_id: d.client_message_id || undefined,\r\n queued_at: d.queued_at || undefined,\r\n delivered_at: d.delivered_at || undefined,\r\n read_at: d.read_at || undefined,\r\n retry_count: d.retry_count ? Number(d.retry_count) : undefined,\r\n\r\n file_url: d.file_url || undefined,\r\n file_name: d.file_name || undefined,\r\n file_size: d.file_size ? Number(d.file_size) : undefined,\r\n mime_type: d.mime_type || undefined,\r\n duration: d.duration ? Number(d.duration) : undefined,\r\n thumbnail: d.thumbnail || undefined,\r\n\r\n file_content_text: d.file_content_text || undefined,\r\n file_content_json: d.file_content_json || undefined,\r\n processing_status: d.processing_status || undefined,\r\n\r\n thumb: !!d.thumb,\r\n favorite: !!d.favorite,\r\n flag: !!d.flag,\r\n star: !!d.star,\r\n pin: !!d.pin,\r\n archive: !!d.archive,\r\n deleted: !!d.deleted,\r\n action_this: !!d.action_this,\r\n reply: !!d.reply,\r\n forward: !!d.forward,\r\n\r\n deleted_at: d.deleted_at || undefined,\r\n deleted_by: d.deleted_by || undefined,\r\n\r\n replyemoji: d.replyemoji || undefined,\r\n replyto_message_id: d.replyto_message_id || undefined,\r\n replyto_user_id: d.replyto_user_id || undefined,\r\n parent_message_id: d.parent_message_id || undefined,\r\n\r\n forwardemoji: d.forwardemoji || undefined,\r\n forwardto_message_id: d.forwardto_message_id || undefined,\r\n forwardto_user_id: d.forwardto_user_id || undefined,\r\n\r\n sender_message_id: d.sender_message_id || undefined,\r\n\r\n sender: d.sender\r\n ? {\r\n id: d.sender.id,\r\n name: d.sender.name,\r\n email: d.sender.email,\r\n avatar_url: d.sender.avatar || undefined,\r\n }\r\n : undefined,\r\n location_data: d.location_data || undefined,\r\n location_type: d.location_type || undefined,\r\n }))\r\n\r\n // Resolve reply references against the current user's message copies first.\r\n // Every member owns a different row id, while reply_to_message_id points to\r\n // the sender's canonical copy. Matching sender_message_id keeps replies\r\n // visible for both the sender and every receiver without duplicating rows.\r\n for (const msg of messages) {\r\n if (msg.reply && msg.replyto_message_id) {\r\n const localReply = messages.find(\r\n (candidate) =>\r\n candidate.id === msg.replyto_message_id ||\r\n candidate.sender_message_id === msg.replyto_message_id\r\n )\r\n\r\n if (localReply) {\r\n msg.replyto_message = localReply\r\n msg.replyMetadata = {\r\n replyemoji: msg.replyemoji || null,\r\n replyto_message_id: msg.replyto_message_id,\r\n replyto_user_id: msg.replyto_user_id || null,\r\n parent_message_id: msg.parent_message_id || null,\r\n replyMessageText: localReply.deleted\r\n ? 'Original message unavailable'\r\n : localReply.message_type === 'text'\r\n ? localReply.message || ''\r\n : `Attachment: ${localReply.file_name || 'File'}`,\r\n replySenderName: localReply.sender?.name || 'User',\r\n }\r\n continue\r\n }\r\n\r\n const replyQuery = createQuery()\r\n .select(\r\n `\r\n id,\r\n sender_user_id,\r\n created_at,\r\n message,\r\n message_type,\r\n file_name,\r\n deleted,\r\n sender:profiles!sender_user_id(name)\r\n `\r\n )\r\n .eq('id', msg.replyto_message_id)\r\n .limit(1)\r\n\r\n try {\r\n const replyMsgs = await apiClient.get<any[]>(\r\n `/rest/v1/chat_messages${replyQuery.toString()}`\r\n )\r\n const replyMsg = replyMsgs[0] || null\r\n\r\n if (replyMsg) {\r\n msg.replyto_message = {\r\n id: replyMsg.id,\r\n conversation_id: msg.conversation_id,\r\n owner_user_id: msg.owner_user_id,\r\n sender_user_id: replyMsg.sender_user_id,\r\n message: replyMsg.deleted\r\n ? 'Original message unavailable'\r\n : replyMsg.message,\r\n message_type: replyMsg.message_type || 'text',\r\n direction: 'Received',\r\n sent: true,\r\n received: true,\r\n created_at: replyMsg.created_at || msg.created_at,\r\n file_name: replyMsg.file_name || undefined,\r\n thumb: false,\r\n favorite: false,\r\n flag: false,\r\n star: false,\r\n pin: false,\r\n archive: false,\r\n deleted: !!replyMsg.deleted,\r\n action_this: false,\r\n reply: false,\r\n forward: false,\r\n sender: replyMsg.sender\r\n ? {\r\n id: replyMsg.sender_user_id,\r\n name: replyMsg.sender.name || 'User',\r\n email: '',\r\n }\r\n : undefined,\r\n }\r\n msg.replyMetadata = {\r\n replyemoji: msg.replyemoji || null,\r\n replyto_message_id: msg.replyto_message_id,\r\n replyto_user_id: msg.replyto_user_id || null,\r\n parent_message_id: msg.parent_message_id || null,\r\n replyMessageText: replyMsg.deleted\r\n ? 'Original message unavailable'\r\n : replyMsg.message_type === 'text'\r\n ? replyMsg.message\r\n : `Attachment: ${replyMsg.file_name || 'File'}`,\r\n replySenderName: replyMsg.sender?.name || 'User',\r\n }\r\n } else {\r\n msg.replyMetadata = {\r\n replyemoji: msg.replyemoji || null,\r\n replyto_message_id: msg.replyto_message_id,\r\n replyto_user_id: msg.replyto_user_id || null,\r\n parent_message_id: msg.parent_message_id || null,\r\n replyMessageText: 'Original message unavailable',\r\n replySenderName: 'User',\r\n }\r\n }\r\n } catch (e) {\r\n console.warn(\r\n '[Messages API] Failed to fetch reply message details:',\r\n e\r\n )\r\n }\r\n }\r\n }\r\n\r\n return messages\r\n } catch (e) {\r\n console.error('[Messages API] Failed to get conversation messages:', e)\r\n return []\r\n }\r\n}\r\n\r\n/**\r\n * Creates individual message copies for all conversation members.\r\n */\r\nexport async function createMessage(msg: {\r\n id?: string\r\n conversationId: string\r\n senderId: string\r\n message: string\r\n messageType:\r\n | 'text'\r\n | 'image'\r\n | 'video'\r\n | 'audio'\r\n | 'document'\r\n | 'system'\r\n | 'other'\r\n | 'location'\r\n fileUrl?: string\r\n fileName?: string\r\n fileSize?: number\r\n mimeType?: string\r\n duration?: number\r\n thumbnail?: string\r\n replyMetadata?: {\r\n replyemoji?: string | null\r\n replyto_message_id: string\r\n replyto_user_id: string | null\r\n parent_message_id: string | null\r\n }\r\n clientMessageId?: string\r\n fileContentText?: string\r\n fileContentJson?: any\r\n systemMetadata?: {\r\n type: 'group_created' | 'members_added'\r\n groupName?: string\r\n creatorName?: string\r\n }\r\n locationData?: any\r\n locationType?: 'current' | 'live'\r\n}): Promise<Message | null> {\r\n try {\r\n // 1. Get conversation members\r\n const memberQuery = createQuery()\r\n .select('user_id')\r\n .eq('conversation_id', msg.conversationId)\r\n const members = await apiClient.get<any[]>(\r\n `/rest/v1/conversation_members${memberQuery.toString()}`\r\n )\r\n\r\n if (!members || members.length === 0) {\r\n throw new Error('No members found in conversation')\r\n }\r\n\r\n const senderMsgId = msg.id || crypto.randomUUID()\r\n const now = new Date().toISOString()\r\n\r\n // Resolve reply parent message ID if replying\r\n let resolvedReplyToId: string | null = null\r\n if (msg.replyMetadata?.replyto_message_id) {\r\n const parentMsgQuery = createQuery()\r\n .select('id, sender_message_id')\r\n .eq('id', msg.replyMetadata.replyto_message_id)\r\n .limit(1)\r\n\r\n const replyMsgs = await apiClient.get<any[]>(\r\n `/rest/v1/chat_messages${parentMsgQuery.toString()}`\r\n )\r\n const replyMsg = replyMsgs[0] || null\r\n resolvedReplyToId = replyMsg\r\n ? replyMsg.sender_message_id || replyMsg.id\r\n : msg.replyMetadata.replyto_message_id\r\n }\r\n\r\n // 2. Map copies for all conversation members\r\n const records: any[] = []\r\n for (const member of members) {\r\n const isSender = member.user_id === msg.senderId\r\n const msgId = isSender ? senderMsgId : crypto.randomUUID()\r\n\r\n let finalMessage = msg.message\r\n\r\n // Customize system message copies based on the recipient's copy context\r\n if (msg.messageType === 'system' && msg.systemMetadata) {\r\n const { type: sysType, groupName, creatorName } = msg.systemMetadata\r\n if (sysType === 'group_created') {\r\n finalMessage = isSender\r\n ? `You created group \"${groupName}\"`\r\n : `${creatorName} created group \"${groupName}\"`\r\n } else if (sysType === 'members_added') {\r\n if (isSender) {\r\n continue\r\n }\r\n finalMessage = `${creatorName} added you`\r\n }\r\n }\r\n\r\n const hasPreparsedData = !!(msg.fileContentText || msg.fileContentJson)\r\n\r\n records.push({\r\n id: msgId,\r\n conversation_id: msg.conversationId,\r\n owner_user_id: member.user_id,\r\n sender_user_id: msg.senderId,\r\n message: finalMessage,\r\n message_type: msg.messageType,\r\n direction: isSender ? 'Sent' : 'Received',\r\n sent: true,\r\n received: isSender,\r\n created_at: now,\r\n\r\n file_url: msg.fileUrl || null,\r\n file_name: msg.fileName || null,\r\n file_size: msg.fileSize || null,\r\n mime_type: msg.mimeType || null,\r\n duration: msg.duration || null,\r\n thumbnail: msg.thumbnail || null,\r\n\r\n thumb: false,\r\n favorite: false,\r\n flag: false,\r\n star: false,\r\n pin: false,\r\n archive: false,\r\n deleted: false,\r\n action_this: false,\r\n reply: !!msg.replyMetadata,\r\n forward: false,\r\n\r\n replyemoji: msg.replyMetadata?.replyemoji || null,\r\n replyto_message_id: resolvedReplyToId,\r\n replyto_user_id: msg.replyMetadata?.replyto_user_id || null,\r\n parent_message_id: msg.replyMetadata?.parent_message_id || null,\r\n\r\n sender_message_id: isSender ? null : senderMsgId,\r\n client_message_id: msg.clientMessageId || null,\r\n message_status: 'sent',\r\n location_data: msg.locationData || null,\r\n location_type: msg.locationType || null,\r\n file_content_text: msg.fileContentText || null,\r\n file_content_json: msg.fileContentJson || null,\r\n processing_status: hasPreparsedData\r\n ? 'completed'\r\n : (msg.messageType === 'document' && (msg.fileName?.toLowerCase().endsWith('.pdf') || msg.mimeType === 'application/pdf'))\r\n ? 'pending'\r\n : null,\r\n })\r\n }\r\n\r\n // 3. Bulk insert copies into the DB\r\n if (records.length > 0) {\r\n await apiClient.post('/rest/v1/chat_messages', records)\r\n \r\n // Trigger background PDF processing if it's a PDF document\r\n const isPdf = msg.messageType === 'document' && (msg.fileName?.toLowerCase().endsWith('.pdf') || msg.mimeType === 'application/pdf');\r\n if (isPdf && typeof window !== 'undefined') {\r\n fetch('/api/process-pdf', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n messageId: senderMsgId,\r\n }),\r\n }).catch((err) => console.warn('[Messages API] Asynchronous PDF processing dispatch error:', err))\r\n }\r\n \r\n // Trigger FCM Push notification to offline/backgrounded recipient devices\r\n if (typeof window !== 'undefined') {\r\n fetch('/api/notifications/push', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n senderId: msg.senderId,\r\n conversationId: msg.conversationId,\r\n message: msg.message,\r\n messageType: msg.messageType,\r\n fileName: msg.fileName,\r\n }),\r\n }).catch((err) => console.warn('[Messages API] Asynchronous FCM push dispatch error:', err))\r\n }\r\n }\r\n\r\n // 4. Return the sender's own copy of the message, constructed locally\r\n const senderRecord = records.find(\r\n (r: any) => r.owner_user_id === msg.senderId\r\n )\r\n if (!senderRecord) return null\r\n\r\n // Load sender details from profiles table to attach to the returned copy\r\n const profileQuery = createQuery()\r\n .select('id, name, email, avatar')\r\n .eq('id', msg.senderId)\r\n .limit(1)\r\n const profiles = await apiClient.get<any[]>(\r\n `/rest/v1/profiles${profileQuery.toString()}`\r\n )\r\n const profile = profiles[0] || null\r\n\r\n return {\r\n id: senderRecord.id,\r\n conversation_id: senderRecord.conversation_id,\r\n owner_user_id: senderRecord.owner_user_id,\r\n sender_user_id: senderRecord.sender_user_id,\r\n message: senderRecord.message,\r\n message_type: senderRecord.message_type,\r\n direction: senderRecord.direction,\r\n sent: senderRecord.sent,\r\n received: senderRecord.received,\r\n created_at: senderRecord.created_at,\r\n file_url: senderRecord.file_url || undefined,\r\n file_name: senderRecord.file_name || undefined,\r\n file_size: senderRecord.file_size\r\n ? Number(senderRecord.file_size)\r\n : undefined,\r\n mime_type: senderRecord.mime_type || undefined,\r\n duration: senderRecord.duration\r\n ? Number(senderRecord.duration)\r\n : undefined,\r\n thumbnail: senderRecord.thumbnail || undefined,\r\n thumb: senderRecord.thumb,\r\n favorite: senderRecord.favorite,\r\n flag: senderRecord.flag,\r\n star: senderRecord.star,\r\n pin: senderRecord.pin,\r\n archive: senderRecord.archive,\r\n deleted: senderRecord.deleted,\r\n action_this: senderRecord.action_this,\r\n reply: senderRecord.reply,\r\n forward: senderRecord.forward,\r\n replyemoji: senderRecord.replyemoji || undefined,\r\n replyto_message_id: senderRecord.replyto_message_id || undefined,\r\n replyto_user_id: senderRecord.replyto_user_id || undefined,\r\n parent_message_id: senderRecord.parent_message_id || undefined,\r\n message_status: senderRecord.message_status,\r\n client_message_id: senderRecord.client_message_id || undefined,\r\n location_data: senderRecord.location_data || undefined,\r\n location_type: senderRecord.location_type || undefined,\r\n file_content_text: senderRecord.file_content_text || undefined,\r\n file_content_json: senderRecord.file_content_json || undefined,\r\n processing_status: senderRecord.processing_status || undefined,\r\n sender: profile\r\n ? {\r\n id: profile.id,\r\n name: profile.name || profile.email.split('@')[0],\r\n email: profile.email,\r\n avatar_url: profile.avatar || undefined,\r\n }\r\n : undefined,\r\n }\r\n } catch (err) {\r\n console.error('[Messages API] Failed to create message copies:', err)\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * Updates Boolean flags (likes, flags, stars, pin, archive) of a message copy.\r\n */\r\nexport async function updateMessageBooleanAction(\r\n messageId: string,\r\n action:\r\n 'thumb' | 'favorite' | 'flag' | 'star' | 'pin' | 'archive' | 'action_this',\r\n value: boolean\r\n): Promise<boolean> {\r\n try {\r\n const query = createQuery().eq('id', messageId)\r\n await apiClient.patch(`/rest/v1/chat_messages${query.toString()}`, {\r\n [action]: value,\r\n })\r\n return true\r\n } catch (err) {\r\n console.error(\r\n `[Messages API] Failed to update message action ${action}:`,\r\n err\r\n )\r\n return false\r\n }\r\n}\r\n\r\n/** Updates the text on every member copy of a sender-owned text message. */\r\nexport async function editMessage(\r\n messageId: string,\r\n senderId: string,\r\n message: string\r\n): Promise<boolean> {\r\n try {\r\n const fetchQuery = createQuery()\r\n .select('id,sender_user_id,sender_message_id,message_type')\r\n .eq('id', messageId)\r\n .limit(1)\r\n const rows = await apiClient.get<any[]>(\r\n `/rest/v1/chat_messages${fetchQuery.toString()}`\r\n )\r\n const original = rows[0]\r\n if (\r\n !original ||\r\n original.sender_user_id !== senderId ||\r\n original.message_type !== 'text'\r\n ) {\r\n return false\r\n }\r\n\r\n const senderMessageId = original.sender_message_id || original.id\r\n const updateQuery = createQuery().or(\r\n `id.eq.${senderMessageId},sender_message_id.eq.${senderMessageId}`\r\n )\r\n await apiClient.patch(`/rest/v1/chat_messages${updateQuery.toString()}`, {\r\n message,\r\n })\r\n return true\r\n } catch (error) {\r\n console.error('[Messages API] Failed to edit message:', error)\r\n return false\r\n }\r\n}\r\n\r\n/**\r\n * Soft deletes a message copy for a specific user (delete for me).\r\n */\r\nexport async function deleteMessageForMe(messageId: string): Promise<boolean> {\r\n try {\r\n const query = createQuery().eq('id', messageId)\r\n await apiClient.patch(`/rest/v1/chat_messages${query.toString()}`, {\r\n deleted: true,\r\n })\r\n return true\r\n } catch (err) {\r\n console.error('[Messages API] Failed to delete message for me:', err)\r\n return false\r\n }\r\n}\r\n\r\n/**\r\n * Deletes a message and all its member copies for everyone in a conversation.\r\n */\r\nexport async function deleteMessageForEveryone(\r\n messageId: string,\r\n senderId: string\r\n): Promise<boolean> {\r\n try {\r\n // 1. Fetch message details to find sender_message_id\r\n const fetchQuery = createQuery()\r\n .select('id, sender_user_id, sender_message_id')\r\n .eq('id', messageId)\r\n .limit(1)\r\n const msgs = await apiClient.get<any[]>(\r\n `/rest/v1/chat_messages${fetchQuery.toString()}`\r\n )\r\n const msg = msgs[0] || null\r\n\r\n if (!msg) return false\r\n if (msg.sender_user_id !== senderId) {\r\n console.error(\r\n '[Messages API] Only the sender can delete a message for everyone'\r\n )\r\n return false\r\n }\r\n\r\n const senderMsgId = msg.sender_message_id || msg.id\r\n\r\n // 2. Update all message copies associated with this message\r\n const updateQuery = createQuery().or(\r\n `id.eq.${senderMsgId},sender_message_id.eq.${senderMsgId}`\r\n )\r\n await apiClient.patch(`/rest/v1/chat_messages${updateQuery.toString()}`, {\r\n message: 'This message was deleted.',\r\n deleted: true,\r\n deleted_at: new Date().toISOString(),\r\n deleted_by: senderId,\r\n })\r\n\r\n return true\r\n } catch (err) {\r\n console.error('[Messages API] Failed to delete message for everyone:', err)\r\n return false\r\n }\r\n}\r\n\r\n/**\r\n * Forwards a message to multiple conversations.\r\n */\r\nexport async function forwardMessage(\r\n messageId: string,\r\n targetConversationIds: string[],\r\n senderId: string\r\n): Promise<boolean> {\r\n try {\r\n // 1. Fetch the original message copy\r\n const fetchQuery = createQuery().select('*').eq('id', messageId).limit(1)\r\n const msgs = await apiClient.get<any[]>(\r\n `/rest/v1/chat_messages${fetchQuery.toString()}`\r\n )\r\n const original = msgs[0] || null\r\n\r\n if (!original) return false\r\n\r\n // 2. Loop and duplicate message copies for target conversations\r\n for (const conversationId of targetConversationIds) {\r\n const memberQuery = createQuery()\r\n .select('user_id')\r\n .eq('conversation_id', conversationId)\r\n const members = await apiClient.get<any[]>(\r\n `/rest/v1/conversation_members${memberQuery.toString()}`\r\n )\r\n\r\n if (!members || members.length === 0) continue\r\n\r\n const senderMsgId = crypto.randomUUID()\r\n const now = new Date().toISOString()\r\n\r\n const records = members.map((member: any) => {\r\n const isSender = member.user_id === senderId\r\n const msgId = isSender ? senderMsgId : crypto.randomUUID()\r\n return {\r\n id: msgId,\r\n conversation_id: conversationId,\r\n owner_user_id: member.user_id,\r\n sender_user_id: senderId,\r\n message: original.message,\r\n message_type: original.message_type,\r\n direction: isSender ? 'Sent' : 'Received',\r\n sent: true,\r\n received: isSender,\r\n created_at: now,\r\n\r\n file_url: original.file_url || null,\r\n file_name: original.file_name || null,\r\n file_size: original.file_size || null,\r\n mime_type: original.mime_type || null,\r\n duration: original.duration || null,\r\n thumbnail: original.thumbnail || null,\r\n location_data: original.location_data || null,\r\n location_type: original.location_type || null,\r\n\r\n thumb: false,\r\n favorite: false,\r\n flag: false,\r\n star: false,\r\n pin: false,\r\n archive: false,\r\n deleted: false,\r\n action_this: false,\r\n reply: false,\r\n forward: true,\r\n\r\n forwardto_message_id: original.id,\r\n forwardto_user_id: original.sender_user_id,\r\n\r\n sender_message_id: isSender ? null : senderMsgId,\r\n }\r\n })\r\n\r\n await apiClient.post('/rest/v1/chat_messages', records)\r\n }\r\n\r\n return true\r\n } catch (err) {\r\n console.error('[Messages API] Failed to forward message:', err)\r\n return false\r\n }\r\n}\r\n","import * as messagesApi from '../api/messages.api'\nimport { Message } from '../types/chat.types'\n\nexport async function getConversationMessages(\n conversationId: string,\n userId: string,\n options?: { limit?: number; before?: string }\n): Promise<Message[]> {\n return messagesApi.getConversationMessages(conversationId, userId, options)\n}\n\nexport async function createMessage(msg: {\n id?: string\n conversationId: string\n senderId: string\n message: string\n messageType:\n | 'text'\n | 'image'\n | 'video'\n | 'audio'\n | 'document'\n | 'system'\n | 'other'\n | 'location'\n fileUrl?: string\n fileName?: string\n fileSize?: number\n mimeType?: string\n duration?: number\n thumbnail?: string\n replyMetadata?: {\n replyemoji?: string | null\n replyto_message_id: string\n replyto_user_id: string | null\n parent_message_id: string | null\n }\n clientMessageId?: string\n fileContentText?: string\n fileContentJson?: any\n systemMetadata?: {\n type: 'group_created' | 'members_added'\n groupName?: string\n creatorName?: string\n }\n locationData?: any\n locationType?: 'current' | 'live'\n}): Promise<Message | null> {\n return messagesApi.createMessage(msg)\n}\n\nexport async function updateMessageBooleanAction(\n messageId: string,\n action:\n 'thumb' | 'favorite' | 'flag' | 'star' | 'pin' | 'archive' | 'action_this',\n value: boolean\n): Promise<boolean> {\n return messagesApi.updateMessageBooleanAction(messageId, action, value)\n}\n\nexport async function editMessage(\n messageId: string,\n senderId: string,\n message: string\n): Promise<boolean> {\n return messagesApi.editMessage(messageId, senderId, message)\n}\n\nexport async function deleteMessageForMe(messageId: string): Promise<boolean> {\n return messagesApi.deleteMessageForMe(messageId)\n}\n\nexport async function deleteMessageForEveryone(\n messageId: string,\n senderId: string\n): Promise<boolean> {\n return messagesApi.deleteMessageForEveryone(messageId, senderId)\n}\n\nexport async function forwardMessage(\n messageId: string,\n targetConversationIds: string[],\n senderId: string\n): Promise<boolean> {\n return messagesApi.forwardMessage(messageId, targetConversationIds, senderId)\n}\n","import { apiClient } from '../../shared/api/apiClient'\r\nimport { createQuery } from '../../shared/api/queryBuilder'\r\nimport { Conversation, Profile, Message } from '../types/chat.types'\r\nimport { createMessage } from './messages.api'\r\nimport { stringToUuid } from '@/lib/auth'\r\n\r\n/**\r\n * Fetch all conversations for a specific user.\r\n */\r\nexport async function getUserConversations(userId: string): Promise<Conversation[]> {\r\n try {\r\n const businessUserId = stringToUuid(userId)\r\n\r\n // 1. Get the list of conversation IDs the user belongs to\r\n const memberQuery = createQuery().select('conversation_id').eq('user_id', businessUserId)\r\n const memberOf = await apiClient.get<any[]>(`/rest/v1/conversation_members${memberQuery.toString()}`)\r\n \r\n if (!memberOf || memberOf.length === 0) return []\r\n const convoIds = memberOf.map((m: any) => m.conversation_id)\r\n\r\n // 2. Fetch conversations including their members and message copies for this user\r\n // We construct a specific nested resource selection using PostgREST syntax\r\n const convosQuery = createQuery()\r\n .select(`\r\n *,\r\n conversation_members (\r\n role,\r\n joined_at,\r\n user_id,\r\n unread_count,\r\n profiles (\r\n id,\r\n name,\r\n email,\r\n avatar,\r\n last_seen\r\n )\r\n ),\r\n chat_messages (\r\n id,\r\n conversation_id,\r\n sender_user_id,\r\n message,\r\n message_type,\r\n file_url,\r\n file_name,\r\n file_size,\r\n mime_type,\r\n duration,\r\n created_at,\r\n deleted,\r\n deleted_by,\r\n message_status\r\n )\r\n `)\r\n .in('id', convoIds)\r\n .eq('chat_messages.owner_user_id', businessUserId)\r\n\r\n const convos = await apiClient.get<any[]>(`/rest/v1/conversations${convosQuery.toString()}`)\r\n if (!convos) return []\r\n\r\n const mapped = convos.map((c: any) => {\r\n const members: Profile[] = (c.conversation_members || [])\r\n .map((cm: any) => {\r\n if (!cm.profiles) return null\r\n return {\r\n id: cm.profiles.id,\r\n name: cm.profiles.name,\r\n email: cm.profiles.email,\r\n avatar_url: cm.profiles.avatar || undefined,\r\n last_seen: cm.profiles.last_seen || undefined,\r\n }\r\n })\r\n .filter(Boolean)\r\n\r\n // Sort message copies and filter out messages deleted for me\r\n const messageCopies = [...(c.chat_messages || [])]\r\n .filter((m: any) => !m.deleted || m.deleted_by !== null)\r\n .sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())\r\n \r\n const lastMessage = messageCopies[0] || undefined\r\n\r\n let displayName = c.name || ''\r\n let displayImage = c.image || ''\r\n\r\n if (c.type === 'direct') {\r\n const otherMember = members.find(m => m.id !== businessUserId)\r\n if (otherMember) {\r\n displayName = otherMember.name\r\n displayImage = otherMember.avatar_url || ''\r\n } else {\r\n displayName = 'Chat Note (You)'\r\n const selfMember = members.find(m => m.id === businessUserId)\r\n displayImage = selfMember?.avatar_url || ''\r\n }\r\n }\r\n\r\n // Get unread count for current user\r\n const selfMemberRecord = (c.conversation_members || []).find((cm: any) => cm.user_id === businessUserId)\r\n const unreadCount = selfMemberRecord?.unread_count || 0\r\n\r\n return {\r\n id: c.id,\r\n type: c.type,\r\n name: displayName,\r\n image: displayImage,\r\n created_by: c.created_by || undefined,\r\n created_at: c.created_at,\r\n lastMessage: lastMessage ? {\r\n id: lastMessage.id,\r\n conversation_id: lastMessage.conversation_id,\r\n sender_user_id: lastMessage.sender_user_id || 'system',\r\n message: lastMessage.message || '',\r\n message_type: lastMessage.message_type,\r\n file_url: lastMessage.file_url || undefined,\r\n file_name: lastMessage.file_name || undefined,\r\n file_size: lastMessage.file_size || undefined,\r\n mime_type: lastMessage.mime_type || undefined,\r\n duration: lastMessage.duration || undefined,\r\n created_at: lastMessage.created_at,\r\n deleted: !!lastMessage.deleted,\r\n } as Message : undefined,\r\n unreadCount,\r\n members,\r\n }\r\n })\r\n\r\n // Deduplicate direct conversations\r\n const seenDirectRecipients = new Set<string>()\r\n const deduplicated: Conversation[] = []\r\n\r\n // Sort by last message time DESC first to keep the most active direct conversation\r\n const sorted = [...mapped].sort((a, b) => {\r\n const timeA = a.lastMessage ? new Date(a.lastMessage.created_at).getTime() : new Date(a.created_at).getTime()\r\n const timeB = b.lastMessage ? new Date(b.lastMessage.created_at).getTime() : new Date(b.created_at).getTime()\r\n return timeB - timeA\r\n })\r\n\r\n for (const convo of sorted) {\r\n if (convo.type === 'direct') {\r\n const otherMember = convo.members.find((m: any) => m.id !== businessUserId)\r\n const recipientId = otherMember ? otherMember.id : businessUserId\r\n \r\n if (seenDirectRecipients.has(recipientId)) {\r\n continue\r\n }\r\n seenDirectRecipients.add(recipientId)\r\n }\r\n deduplicated.push(convo)\r\n }\r\n\r\n return deduplicated\r\n } catch (err) {\r\n console.error('[Conversations API] Failed to get user conversations:', err)\r\n return []\r\n }\r\n}\r\n\r\n/**\r\n * Gets an existing direct conversation or creates a new one.\r\n */\r\nexport async function getOrCreateDirectConversation(userAId: string, userBId: string): Promise<string | null> {\r\n try {\r\n const businessUserAId = userAId\r\n const businessUserBId = userBId\r\n\r\n // 1. Fetch direct conversations userA belongs to\r\n const queryA = createQuery()\r\n .select('conversation_id, conversations!inner(*)')\r\n .eq('user_id', businessUserAId)\r\n .eq('conversations.type', 'direct')\r\n\r\n const membersA = await apiClient.get<any[]>(`/rest/v1/conversation_members${queryA.toString()}`)\r\n\r\n if (membersA && membersA.length > 0) {\r\n const convoIds = membersA.map((m: any) => m.conversation_id)\r\n\r\n // 2. See if userB belongs to any of those direct conversations\r\n const queryB = createQuery()\r\n .select('conversation_id')\r\n .in('conversation_id', convoIds)\r\n .eq('user_id', businessUserBId)\r\n\r\n const membersB = await apiClient.get<any[]>(`/rest/v1/conversation_members${queryB.toString()}`)\r\n\r\n if (membersB && membersB.length > 0) {\r\n return membersB[0].conversation_id\r\n }\r\n }\r\n\r\n // 3. Create a new direct conversation if none exists\r\n const insertedConvos = await apiClient.post<any[]>('/rest/v1/conversations', {\r\n type: 'direct',\r\n created_by: businessUserAId,\r\n })\r\n const newConvo = insertedConvos[0] || null\r\n\r\n if (!newConvo) {\r\n throw new Error('Failed to create conversation.')\r\n }\r\n\r\n // 4. Add both members\r\n const membersToInsert = [\r\n { conversation_id: newConvo.id, user_id: businessUserAId },\r\n ]\r\n if (businessUserAId !== businessUserBId) {\r\n membersToInsert.push({ conversation_id: newConvo.id, user_id: businessUserBId })\r\n }\r\n\r\n await apiClient.post('/rest/v1/conversation_members', membersToInsert)\r\n\r\n return newConvo.id\r\n } catch (err) {\r\n console.error('[Conversations API] Failed to get or create direct conversation:', err)\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * Creates a new group conversation and initializes system messages.\r\n */\r\nexport async function createGroupConversation(\r\n groupName: string,\r\n groupImage: string | null,\r\n memberIds: string[],\r\n creatorId: string,\r\n type: 'group' | 'channel_group' | 'message_group' = 'group'\r\n): Promise<Conversation | null> {\r\n try {\r\n const businessCreatorId = creatorId\r\n const resolvedMemberIds = memberIds\r\n\r\n // 1. Insert Group Conversation\r\n const insertedConvos = await apiClient.post<any[]>('/rest/v1/conversations', {\r\n type,\r\n name: groupName,\r\n image: groupImage,\r\n created_by: businessCreatorId,\r\n })\r\n const newConvo = insertedConvos[0] || null\r\n\r\n if (!newConvo) {\r\n throw new Error('Failed to create group conversation.')\r\n }\r\n\r\n // 2. Add Members to Group\r\n const allMemberIds = Array.from(new Set([...resolvedMemberIds, businessCreatorId]))\r\n const membersToInsert = allMemberIds.map(userId => ({\r\n conversation_id: newConvo.id,\r\n user_id: userId,\r\n }))\r\n\r\n await apiClient.post('/rest/v1/conversation_members', membersToInsert)\r\n\r\n // 3. Insert initial system messages\r\n try {\r\n const creatorQuery = createQuery().select('name, email').eq('id', businessCreatorId).limit(1)\r\n const creatorProfiles = await apiClient.get<any[]>(`/rest/v1/profiles${creatorQuery.toString()}`)\r\n const creatorProfile = creatorProfiles[0] || null\r\n const creatorName = creatorProfile?.name || creatorProfile?.email?.split('@')[0] || 'Someone'\r\n\r\n // 3.1. Group Creation pill\r\n await createMessage({\r\n conversationId: newConvo.id,\r\n senderId: businessCreatorId,\r\n message: `${creatorName} created group \"${groupName}\"`,\r\n messageType: 'system',\r\n systemMetadata: {\r\n type: 'group_created',\r\n groupName,\r\n creatorName,\r\n }\r\n })\r\n\r\n // 3.2. Member Addition pill\r\n await createMessage({\r\n conversationId: newConvo.id,\r\n senderId: businessCreatorId,\r\n message: `${creatorName} added you`,\r\n messageType: 'system',\r\n systemMetadata: {\r\n type: 'members_added',\r\n creatorName,\r\n }\r\n })\r\n } catch (msgErr) {\r\n console.error('[Conversations API] Failed to create initial group system messages:', msgErr)\r\n }\r\n\r\n return {\r\n id: newConvo.id,\r\n type: newConvo.type,\r\n name: newConvo.name,\r\n image: newConvo.image || undefined,\r\n created_by: newConvo.created_by,\r\n created_at: newConvo.created_at,\r\n members: [],\r\n }\r\n } catch (err) {\r\n console.error('[Conversations API] Failed to create group conversation:', err)\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * Resets unread message counter for a member of a conversation.\r\n */\r\nexport async function clearConversationUnreadCount(conversationId: string, userId: string): Promise<boolean> {\r\n try {\r\n const query = createQuery().eq('conversation_id', conversationId).eq('user_id', userId)\r\n await apiClient.patch(`/rest/v1/conversation_members${query.toString()}`, { unread_count: 0 })\r\n return true\r\n } catch (err) {\r\n console.error('[Conversations API] Failed to clear unread count:', err)\r\n return false\r\n }\r\n}\r\n\r\n/**\r\n * Removes a member from a group conversation.\r\n */\r\nexport async function removeGroupMember(conversationId: string, memberId: string): Promise<boolean> {\r\n try {\r\n const query = createQuery()\r\n .eq('conversation_id', conversationId)\r\n .eq('user_id', memberId)\r\n\r\n await apiClient.delete(`/rest/v1/conversation_members${query.toString()}`)\r\n return true\r\n } catch (err) {\r\n console.error('[Conversations API] Failed to remove group member:', err)\r\n return false\r\n }\r\n}\r\n\r\n/**\r\n * Deletes a conversation for a specific user (removes membership and local message copies).\r\n */\r\nexport async function deleteConversation(conversationId: string, userId: string): Promise<boolean> {\r\n try {\r\n // 1. Remove user from conversation_members\r\n const memberQuery = createQuery()\r\n .eq('conversation_id', conversationId)\r\n .eq('user_id', userId)\r\n\r\n await apiClient.delete(`/rest/v1/conversation_members${memberQuery.toString()}`)\r\n\r\n // 2. Delete message copies owned by this user for this conversation\r\n const msgQuery = createQuery()\r\n .eq('conversation_id', conversationId)\r\n .eq('owner_user_id', userId)\r\n\r\n await apiClient.delete(`/rest/v1/chat_messages${msgQuery.toString()}`)\r\n return true\r\n } catch (err) {\r\n console.error('[Conversations API] Failed to delete conversation:', err)\r\n return false\r\n }\r\n}\r\n\r\n","import { Conversation } from '../types/chat.types'\r\nimport * as conversationsApi from '../api/conversations.api'\r\n\r\nexport async function getUserConversations(userId: string): Promise<Conversation[]> {\r\n return conversationsApi.getUserConversations(userId)\r\n}\r\n\r\nexport async function getOrCreateDirectConversation(userAId: string, userBId: string): Promise<string | null> {\r\n return conversationsApi.getOrCreateDirectConversation(userAId, userBId)\r\n}\r\n\r\nexport async function createGroupConversation(\r\n groupName: string,\r\n groupImage: string | null,\r\n memberIds: string[],\r\n creatorId: string,\r\n type: 'group' | 'channel_group' | 'message_group' = 'group'\r\n): Promise<Conversation | null> {\r\n return conversationsApi.createGroupConversation(groupName, groupImage, memberIds, creatorId, type)\r\n}\r\n\r\nexport async function clearConversationUnreadCount(conversationId: string, userId: string): Promise<boolean> {\r\n return conversationsApi.clearConversationUnreadCount(conversationId, userId)\r\n}\r\n\r\nexport async function removeGroupMember(conversationId: string, memberId: string): Promise<boolean> {\r\n return conversationsApi.removeGroupMember(conversationId, memberId)\r\n}\r\n\r\nexport async function deleteConversation(conversationId: string, userId: string): Promise<boolean> {\r\n return conversationsApi.deleteConversation(conversationId, userId)\r\n}\r","import { NextRequest, NextResponse } from 'next/server'\nimport {\n getUserContacts,\n createContact,\n} from '@/features/chattemplate/contacts/repositories/contact-repository'\n\nexport async function handleContactsGet(request: NextRequest) {\n try {\n const { searchParams } = new URL(request.url)\n const userId = searchParams.get('userId')\n\n if (!userId) {\n return NextResponse.json({ error: 'userId query parameter is required' }, { status: 400 })\n }\n\n const contacts = await getUserContacts(userId)\n return NextResponse.json(contacts)\n } catch (err) {\n console.error('GET contacts error:', err)\n return NextResponse.json(\n { error: err instanceof Error ? err.message : 'Failed to fetch contacts' },\n { status: 500 }\n )\n }\n}\n\nexport async function handleContactsPost(request: NextRequest) {\n try {\n const body = await request.json()\n\n if (!body.ownerId) {\n return NextResponse.json({ error: 'ownerId is required' }, { status: 400 })\n }\n\n if (!body.email) {\n return NextResponse.json({ error: 'email is required' }, { status: 400 })\n }\n\n const result = await createContact(body.ownerId, body.email, body.nickname)\n\n if (!result.success) {\n return NextResponse.json({ error: result.error || 'Failed to create contact' }, { status: 409 })\n }\n\n return NextResponse.json({ success: true, message: 'Contact added successfully' }, { status: 201 })\n } catch (err) {\n console.error('POST contact error:', err)\n return NextResponse.json(\n { error: err instanceof Error ? err.message : 'Failed to create contact' },\n { status: 500 }\n )\n }\n}\n","import { Contact } from './contacts.types'\r\n\r\n/**\r\n * Maps database contact record objects into the application Contact representation.\r\n */\r\nexport function mapToContact(dbRecord: any): Contact {\r\n const u = dbRecord.contact_user || {}\r\n const contactEmail = dbRecord.email || u.email || ''\r\n \r\n return {\r\n id: dbRecord.id,\r\n ownerId: dbRecord.owner_id,\r\n contactUserId: dbRecord.contact_user_id,\r\n fullName: dbRecord.nickname || u.name || contactEmail.split('@')[0] || 'Unknown',\r\n email: contactEmail,\r\n avatarUrl: u.avatar || undefined,\r\n company: u.company || undefined,\r\n mobile: u.mobile || undefined,\r\n status: 'Active',\r\n nickname: dbRecord.nickname || undefined,\r\n createdAt: dbRecord.created_at,\r\n }\r\n}\r\n","export interface DBAlertConfig {\r\n groupName: string\r\n groupImage?: string\r\n adminEmails: string[]\r\n}\r\n\r\nexport type DBAlertType = 'contact' | 'group'\r\nexport type DBAlertAction = 'create' | 'update' | 'delete'\r\n\r\nexport const DB_ALERTS_CONFIG: DBAlertConfig = {\r\n groupName: 'DB Alerts',\r\n groupImage: 'https://images.unsplash.com/photo-1598257006458-087169a1f08d?w=128&h=128&fit=crop&auto=format&q=80',\r\n adminEmails: [\r\n 'itsaman00786@gmail.com',\r\n 'amanmicropay@gmail.com',\r\n 'n.rajukrishna@gmail.com'\r\n ],\r\n}\r\n","import { createClient } from '../lib/supabase/client'\r\nimport { createMessage } from '../features/chattemplate/chat/repositories/message-repository'\r\nimport { DB_ALERTS_CONFIG, DBAlertAction } from '../lib/db-alerts/types/db-alert'\r\n\r\n// Helper to format date like \"15 Jul 2026 10:45 AM\"\r\nfunction formatAlertTime(date: Date): string {\r\n const day = date.getDate()\r\n const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']\r\n const month = months[date.getMonth()]\r\n const year = date.getFullYear()\r\n let hours = date.getHours()\r\n const minutes = date.getMinutes().toString().padStart(2, '0')\r\n const ampm = hours >= 12 ? 'AM' : 'PM'\r\n hours = hours % 12\r\n hours = hours ? hours : 12 // the hour '0' should be '12'\r\n return `${day} ${month} ${year} ${hours}:${minutes} ${ampm}`\r\n}\r\n\r\n/**\r\n * Returns the conversation ID of the DB Alerts group conversation.\r\n * Creates it if it doesn't already exist.\r\n */\r\nexport async function getOrCreateAlertConversation(): Promise<string | null> {\r\n const supabase = createClient()\r\n try {\r\n // 1. Look for existing group conversation named \"DB Alerts\"\r\n const { data: existing, error } = await supabase\r\n .from('conversations')\r\n .select('id')\r\n .eq('name', DB_ALERTS_CONFIG.groupName)\r\n .eq('type', 'group')\r\n .maybeSingle()\r\n\r\n if (error) throw error\r\n if (existing) return existing.id\r\n\r\n // 2. Not found - create conversation\r\n const { data: newConvo, error: createError } = await supabase\r\n .from('conversations')\r\n .insert({\r\n type: 'group',\r\n name: DB_ALERTS_CONFIG.groupName,\r\n image: DB_ALERTS_CONFIG.groupImage || null,\r\n })\r\n .select('id')\r\n .single()\r\n\r\n if (createError) throw createError\r\n if (!newConvo) return null\r\n\r\n // 3. Add configured administrators as members\r\n const { data: adminProfiles, error: profilesError } = await supabase\r\n .from('profiles')\r\n .select('id, email')\r\n .in('email', DB_ALERTS_CONFIG.adminEmails)\r\n\r\n if (profilesError) throw profilesError\r\n\r\n if (adminProfiles && adminProfiles.length > 0) {\r\n const membersToInsert = adminProfiles.map((p: { id: string; email?: string | null }) => ({\r\n conversation_id: newConvo.id,\r\n user_id: p.id,\r\n role: 'member',\r\n }))\r\n\r\n const { error: insertError } = await supabase\r\n .from('conversation_members')\r\n .insert(membersToInsert)\r\n\r\n if (insertError) {\r\n console.error('[DB Alerts] Failed to insert initial admin members:', insertError)\r\n } else {\r\n console.log(`[DB Alerts] Created group with ${adminProfiles.length} configured admin members`)\r\n }\r\n }\r\n\r\n return newConvo.id\r\n } catch (err) {\r\n console.error('[DB Alerts] Error in getOrCreateAlertConversation:', err)\r\n return null\r\n }\r\n}\r\n\r\nconst subscribedUsers = new Set<string>()\r\n\r\n/**\r\n * Automatically subscribes a user to the DB Alerts conversation if their email matches\r\n * the admin emails configuration list.\r\n */\r\nexport async function autoSubscribeAdmin(userId: string, email: string): Promise<void> {\r\n if (subscribedUsers.has(userId)) return\r\n subscribedUsers.add(userId)\r\n\r\n const emailLower = email.trim().toLowerCase()\r\n const isAdmin = DB_ALERTS_CONFIG.adminEmails.some(\r\n (adminEmail) => adminEmail.toLowerCase() === emailLower\r\n )\r\n\r\n if (!isAdmin) return\r\n\r\n const supabase = createClient()\r\n try {\r\n const convoId = await getOrCreateAlertConversation()\r\n if (!convoId) return\r\n\r\n // Check if already a member\r\n const { data: existing, error: checkError } = await supabase\r\n .from('conversation_members')\r\n .select('id')\r\n .eq('conversation_id', convoId)\r\n .eq('user_id', userId)\r\n .maybeSingle()\r\n\r\n if (checkError) throw checkError\r\n\r\n if (!existing) {\r\n const { error: insertError } = await supabase\r\n .from('conversation_members')\r\n .insert({\r\n conversation_id: convoId,\r\n user_id: userId,\r\n role: 'member',\r\n })\r\n\r\n if (insertError) throw insertError\r\n console.log(`[DB Alerts] Auto-subscribed admin user: ${email} (${userId})`)\r\n }\r\n\r\n // Ensure there is at least one message to make the conversation visible in the sidebar list.\r\n // If the user has no message copy, insert the initialization system message copy.\r\n const { data: msgs, error: msgsError } = await supabase\r\n .from('chat_messages')\r\n .select('id')\r\n .eq('conversation_id', convoId)\r\n .eq('owner_user_id', userId)\r\n .limit(1)\r\n\r\n if (!msgsError && (!msgs || msgs.length === 0)) {\r\n await createMessage({\r\n conversationId: convoId,\r\n senderId: userId,\r\n message: '🚨 DB Alerts Channel Initialized. Administrative events will be logged here.',\r\n messageType: 'system',\r\n })\r\n console.log(`[DB Alerts] Initialized message feed for user: ${userId}`)\r\n }\r\n } catch (err) {\r\n console.error('[DB Alerts] Failed to auto-subscribe admin user:', err)\r\n }\r\n}\r\n\r\n/**\r\n * Triggers a DB Alert message for contact operations.\r\n */\r\nexport async function triggerContactAlert(\r\n action: DBAlertAction,\r\n ownerId: string,\r\n contactUserId: string\r\n): Promise<void> {\r\n const supabase = createClient()\r\n try {\r\n const convoId = await getOrCreateAlertConversation()\r\n if (!convoId) return\r\n\r\n // 1. Resolve owner display name\r\n const { data: ownerProfile } = await supabase\r\n .from('profiles')\r\n .select('name, email')\r\n .eq('id', ownerId)\r\n .maybeSingle()\r\n\r\n const ownerName = ownerProfile?.name || ownerProfile?.email?.split('@')[0] || 'System'\r\n\r\n // 2. Resolve contact display name\r\n const { data: contactProfile } = await supabase\r\n .from('profiles')\r\n .select('name, email')\r\n .eq('id', contactUserId)\r\n .maybeSingle()\r\n\r\n const contactName = contactProfile?.name || contactProfile?.email?.split('@')[0] || 'System'\r\n\r\n // 3. Format message based on action\r\n const timeStr = formatAlertTime(new Date())\r\n let formattedMessage = ''\r\n\r\n if (action === 'create') {\r\n formattedMessage = `Contact Created\\n🟢 Contact Added\\nBy: ${ownerName}\\nContact: ${contactName}\\nTime: ${timeStr}`\r\n } else if (action === 'delete') {\r\n formattedMessage = `Contact Deleted\\n🔴 Contact Deleted\\nBy: ${ownerName}\\nContact: ${contactName}`\r\n } else if (action === 'update') {\r\n formattedMessage = `Contact Updated\\n🟡 Contact Updated\\nBy: ${ownerName}\\nContact: ${contactName}\\nTime: ${timeStr}`\r\n }\r\n\r\n // 4. Send message copy to DB Alerts conversation\r\n await createMessage({\r\n conversationId: convoId,\r\n senderId: ownerId,\r\n message: formattedMessage,\r\n messageType: 'system',\r\n })\r\n } catch (err) {\r\n console.error('[DB Alerts] Failed to trigger contact alert:', err)\r\n }\r\n}\r\n\r\n/**\r\n * Triggers a DB Alert message for group operations.\r\n */\r\nexport async function triggerGroupAlert(\r\n action: DBAlertAction,\r\n actorId: string,\r\n groupName: string\r\n): Promise<void> {\r\n const supabase = createClient()\r\n try {\r\n const convoId = await getOrCreateAlertConversation()\r\n if (!convoId) return\r\n\r\n // 1. Resolve actor display name\r\n let actorName = 'System'\r\n if (actorId) {\r\n const { data: actorProfile } = await supabase\r\n .from('profiles')\r\n .select('name, email')\r\n .eq('id', actorId)\r\n .maybeSingle()\r\n\r\n actorName = actorProfile?.name || actorProfile?.email?.split('@')[0] || 'System'\r\n }\r\n\r\n // 2. Format message based on action\r\n let formattedMessage = ''\r\n\r\n if (action === 'create') {\r\n formattedMessage = `Group Created\\n🟢 Group Created\\nBy: ${actorName}\\nGroup: ${groupName}`\r\n } else if (action === 'delete') {\r\n formattedMessage = `Group Deleted\\n🔴 Group Deleted\\nBy: ${actorName}\\nGroup: ${groupName}`\r\n } else if (action === 'update') {\r\n formattedMessage = `Group Updated\\n🟡 Group Updated\\nBy: ${actorName}\\nGroup: ${groupName}`\r\n }\r\n\r\n // 3. Send message copy to DB Alerts conversation\r\n // Actor ID can fallback to any member of DB Alerts or active user if actorId is empty\r\n let senderId = actorId\r\n if (!senderId) {\r\n // Find a member of the conversation to act as sender (must satisfy RLS that sender is member)\r\n const { data: firstMember } = await supabase\r\n .from('conversation_members')\r\n .select('user_id')\r\n .eq('conversation_id', convoId)\r\n .limit(1)\r\n .maybeSingle()\r\n\r\n senderId = firstMember?.user_id || ''\r\n }\r\n\r\n if (!senderId) {\r\n console.warn('[DB Alerts] Cannot send alert because no valid sender ID is available.')\r\n return\r\n }\r\n\r\n await createMessage({\r\n conversationId: convoId,\r\n senderId: senderId,\r\n message: formattedMessage,\r\n messageType: 'system',\r\n })\r\n } catch (err) {\r\n console.error('[DB Alerts] Failed to trigger group alert:', err)\r\n }\r\n}\r\n","import { createClient } from '../../../../lib/supabase/client'\r\n\r\nexport type ChatFileCategory =\r\n | 'Doc'\r\n | 'Xls'\r\n | 'Ppt'\r\n | 'Pdf'\r\n | 'Txt'\r\n | 'Csv'\r\n | 'Images'\r\n | 'Videos'\r\n | 'Zip'\r\n | 'Other'\r\n\r\nexport type AppStorageSection =\r\n | 'Files'\r\n | 'Chat'\r\n | 'Email'\r\n | 'AI Chat'\r\n | 'Order'\r\n\r\nexport const APP_STORAGE_SECTIONS: AppStorageSection[] = [\r\n 'Files',\r\n 'Chat',\r\n 'Email',\r\n 'AI Chat',\r\n 'Order',\r\n]\r\n\r\nexport const CHAT_FILE_CATEGORIES: ChatFileCategory[] = [\r\n 'Doc',\r\n 'Xls',\r\n 'Ppt',\r\n 'Pdf',\r\n 'Txt',\r\n 'Csv',\r\n 'Images',\r\n 'Videos',\r\n 'Zip',\r\n 'Other',\r\n]\r\n\r\n/**\r\n * Normalizes an email address by trimming whitespace and converting to lowercase.\r\n * Returns null if the email is invalid or empty.\r\n */\r\nexport function normalizeContactEmail(email: string | null | undefined): string | null {\r\n if (!email || !email.trim()) return null\r\n const cleaned = email.trim().toLowerCase()\r\n if (cleaned.includes('@') && !cleaned.includes('anonymous')) {\r\n return cleaned\r\n }\r\n return null\r\n}\r\n\r\n/**\r\n * Reusable function to categorize an uploaded file by its MIME type and extension.\r\n */\r\nexport function getChatFileCategory(file: { name: string; type?: string }): ChatFileCategory {\r\n const ext = file.name.includes('.') ? file.name.split('.').pop()?.toLowerCase() || '' : ''\r\n const mime = (file.type || '').toLowerCase()\r\n\r\n // 1. Doc / Docx\r\n if (['doc', 'docx'].includes(ext) || mime.includes('wordprocessingml') || mime === 'application/msword') {\r\n return 'Doc'\r\n }\r\n\r\n // 2. Xls / Xlsx\r\n if (['xls', 'xlsx'].includes(ext) || mime.includes('spreadsheetml') || mime === 'application/vnd.ms-excel') {\r\n return 'Xls'\r\n }\r\n\r\n // 3. Ppt / Pptx\r\n if (['ppt', 'pptx'].includes(ext) || mime.includes('presentationml') || mime === 'application/vnd.ms-powerpoint') {\r\n return 'Ppt'\r\n }\r\n\r\n // 4. Pdf\r\n if (ext === 'pdf' || mime === 'application/pdf') {\r\n return 'Pdf'\r\n }\r\n\r\n // 5. Txt\r\n if (ext === 'txt' || mime === 'text/plain') {\r\n return 'Txt'\r\n }\r\n\r\n // 6. Csv\r\n if (ext === 'csv' || mime === 'text/csv' || mime === 'application/csv') {\r\n return 'Csv'\r\n }\r\n\r\n // 7. Images\r\n if (\r\n ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico', 'tiff', 'heic', 'heif'].includes(ext) ||\r\n mime.startsWith('image/')\r\n ) {\r\n return 'Images'\r\n }\r\n\r\n // 8. Videos\r\n if (\r\n ['mp4', 'mov', 'avi', 'mkv', 'webm', 'm4v', '3gp', 'flv', 'wmv'].includes(ext) ||\r\n mime.startsWith('video/')\r\n ) {\r\n return 'Videos'\r\n }\r\n\r\n // 9. Zip\r\n if (['zip', 'rar', '7z', 'tar', 'gz'].includes(ext) || mime.includes('zip') || mime.includes('compressed')) {\r\n return 'Zip'\r\n }\r\n\r\n // 10. Anything unknown / unsupported\r\n return 'Other'\r\n}\r\n\r\n/**\r\n * Generates a safe unique file name while preserving original extension.\r\n */\r\nexport function generateUniqueFileName(originalName: string): string {\r\n const ext = originalName.includes('.') ? originalName.split('.').pop()?.toLowerCase() || '' : ''\r\n const baseName = originalName.substring(0, originalName.lastIndexOf('.') > 0 ? originalName.lastIndexOf('.') : originalName.length)\r\n const sanitizedBase = baseName.replace(/[^a-zA-Z0-9_-]/g, '_').substring(0, 50) || 'file'\r\n \r\n const uuid = typeof crypto !== 'undefined' && crypto.randomUUID\r\n ? crypto.randomUUID()\r\n : 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\r\n const r = Math.random() * 16 | 0\r\n const v = c === 'x' ? r : (r & 0x3 | 0x8)\r\n return v.toString(16)\r\n })\r\n\r\n const timestamp = Date.now()\r\n return ext ? `${timestamp}-${uuid}-${sanitizedBase}.${ext}` : `${timestamp}-${uuid}-${sanitizedBase}`\r\n}\r\n\r\n/**\r\n * Universal storage path generator for contact file spaces:\r\n * {contact-email}/{section}/{file-type}/{filename}\r\n */\r\nexport function getStoragePath(\r\n contactEmail: string,\r\n section: AppStorageSection,\r\n fileType: ChatFileCategory,\r\n fileName: string\r\n): string | null {\r\n const normalizedEmail = normalizeContactEmail(contactEmail)\r\n if (!normalizedEmail) return null\r\n return `${normalizedEmail}/${section}/${fileType}/${fileName}`\r\n}\r\n\r\n/**\r\n * Generates the full storage path for a chat file belonging to a contact:\r\n * {contact-email}/Chat/{file-type}/{unique-file-name}\r\n */\r\nexport function generateChatFilePath(contactEmail: string, file: { name: string; type?: string }): string | null {\r\n const category = getChatFileCategory(file)\r\n const uniqueName = generateUniqueFileName(file.name)\r\n return getStoragePath(contactEmail, 'Chat', category, uniqueName)\r\n}\r\n\r\n/**\r\n * Legacy initialization helper (no-op as per user instruction: folders will be created dynamically when files are uploaded, without .keep placeholders).\r\n */\r\nexport async function initializeContactStorage(\r\n contactEmail: string\r\n): Promise<void> {\r\n // Folders are created dynamically upon file upload — no .keep placeholders generated.\r\n return Promise.resolve()\r\n}\r\n\r\nexport async function initializeUserStorage(userEmail: string): Promise<void> {\r\n return Promise.resolve()\r\n}\r\n\r\nexport async function ensureChatStorageStructure(userEmail: string): Promise<void> {\r\n return Promise.resolve()\r\n}\r\n","import { apiClient } from '../../shared/api/apiClient'\r\nimport { createQuery } from '../../shared/api/queryBuilder'\r\nimport { Contact } from './contacts.types'\r\nimport { mapToContact } from './contacts.mapper'\r\nimport { triggerContactAlert } from '@/services/db-alert.service'\r\nimport { stringToUuid } from '@/lib/auth'\r\n\r\n/**\r\n * Fetch all contacts for a specific owner/user.\r\n */\r\nexport async function getContacts(userId: string): Promise<Contact[]> {\r\n try {\r\n const validUserId = stringToUuid(userId)\r\n const query = createQuery()\r\n .select(`\r\n id,\r\n owner_id,\r\n contact_user_id,\r\n nickname,\r\n email,\r\n created_at,\r\n contact_user:profiles!contacts_contact_user_id_fkey (\r\n id,\r\n name,\r\n email,\r\n avatar,\r\n company,\r\n mobile\r\n )\r\n `)\r\n .eq('owner_id', validUserId)\r\n .order('created_at', { ascending: false })\r\n\r\n const data = await apiClient.get<any[]>(`/rest/v1/contacts${query.toString()}`)\r\n if (!data) return []\r\n\r\n return data.map(mapToContact)\r\n } catch (error) {\r\n console.error('[Contacts API] Failed to get user contacts:', error)\r\n return []\r\n }\r\n}\r\n\r\nimport { initializeContactStorage } from '../../chat/services/chat-storage.service'\r\n\r\n/**\r\n * Creates a new contact for the specified owner.\r\n */\r\nexport async function createContact(\r\n ownerId: string,\r\n contactEmail: string,\r\n nickname?: string,\r\n ownerEmail?: string\r\n): Promise<{ success: boolean; error?: string }> {\r\n try {\r\n const validOwnerId = stringToUuid(ownerId)\r\n const emailLower = contactEmail.trim().toLowerCase()\r\n\r\n // 1. Search for existing profile by email\r\n const profileQuery = createQuery()\r\n .select('id, name, email, avatar, company, mobile')\r\n .eq('email', emailLower)\r\n .limit(1)\r\n\r\n const profiles = await apiClient.get<any[]>(`/rest/v1/profiles${profileQuery.toString()}`)\r\n const profile = profiles[0] || null\r\n\r\n // 2. Prevent adding yourself\r\n if (profile && profile.id === validOwnerId) {\r\n return { success: false, error: 'You cannot add yourself as a contact.' }\r\n }\r\n\r\n // 3. Check if contact already exists (by email case-insensitively OR by contact_user_id)\r\n if (profile) {\r\n const existingQuery = createQuery()\r\n .select('id')\r\n .eq('owner_id', validOwnerId)\r\n .or(`email.ilike.${emailLower},contact_user_id.eq.${profile.id}`)\r\n .limit(1)\r\n\r\n const existing = await apiClient.get<any[]>(`/rest/v1/contacts${existingQuery.toString()}`)\r\n if (existing.length > 0) {\r\n return { success: false, error: 'This contact is already in your list.' }\r\n }\r\n } else {\r\n const existingQuery = createQuery()\r\n .select('id')\r\n .eq('owner_id', validOwnerId)\r\n .ilike('email', emailLower)\r\n .limit(1)\r\n\r\n const existing = await apiClient.get<any[]>(`/rest/v1/contacts${existingQuery.toString()}`)\r\n if (existing.length > 0) {\r\n return { success: false, error: 'This contact is already in your list.' }\r\n }\r\n }\r\n\r\n let contactUserId: string\r\n\r\n if (profile) {\r\n // User is registered - use existing profile ID\r\n contactUserId = profile.id\r\n } else {\r\n // User is NOT registered - create a new profile for them\r\n const displayName = nickname || emailLower.split('@')[0]\r\n \r\n const newUserId = crypto.randomUUID ? crypto.randomUUID() : \r\n 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\r\n const r = Math.random() * 16 | 0;\r\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\r\n return v.toString(16);\r\n })\r\n\r\n // Insert new profile for unregistered user\r\n const newProfileData = {\r\n id: newUserId,\r\n email: emailLower,\r\n name: displayName,\r\n avatar: null,\r\n company: null,\r\n mobile: null,\r\n status: 'offline',\r\n online: false,\r\n offline: true,\r\n last_seen: new Date().toISOString(),\r\n updated_at: new Date().toISOString()\r\n }\r\n\r\n const insertedProfiles = await apiClient.post<any[]>('/rest/v1/profiles', newProfileData)\r\n const newProfile = insertedProfiles[0] || null\r\n\r\n if (!newProfile) {\r\n return { success: false, error: 'Failed to create contact profile.' }\r\n }\r\n\r\n contactUserId = newProfile.id\r\n }\r\n\r\n // 4. Create the contact entry with user_uuid\r\n const contactData = {\r\n owner_id: validOwnerId,\r\n contact_user_id: contactUserId,\r\n nickname: nickname?.trim() || null,\r\n email: emailLower,\r\n user_uuid: validOwnerId\r\n }\r\n\r\n await apiClient.post('/rest/v1/contacts', contactData)\r\n\r\n // Trigger DB Alert (async)\r\n triggerContactAlert('create', ownerId, contactUserId).catch((err) =>\r\n console.error('[DB Alerts] Error sending contact created alert:', err)\r\n )\r\n\r\n // Initialize dedicated Contact File Space for the created contact (and owner)\r\n if (emailLower) {\r\n void initializeContactStorage(emailLower).catch((err) =>\r\n console.error('[Contact Storage] Error initializing contact file space:', err)\r\n )\r\n }\r\n if (ownerEmail) {\r\n void initializeContactStorage(ownerEmail).catch((err) =>\r\n console.error('[Contact Storage] Error initializing owner file space:', err)\r\n )\r\n }\r\n\r\n return { success: true }\r\n } catch (error) {\r\n console.error('[Contacts API] Failed to create contact:', error)\r\n return { success: false, error: error instanceof Error ? error.message : 'Failed to create contact' }\r\n }\r\n}\r\n\r\n/**\r\n * Updates a contact's nickname.\r\n */\r\nexport async function updateContact(\r\n contactId: string,\r\n ownerId: string,\r\n nickname: string\r\n): Promise<boolean> {\r\n try {\r\n const validOwnerId = stringToUuid(ownerId)\r\n // Fetch contact details to resolve contactUserId for alert\r\n const contactQuery = createQuery()\r\n .select('contact_user_id')\r\n .eq('id', contactId)\r\n .limit(1)\r\n\r\n const contacts = await apiClient.get<any[]>(`/rest/v1/contacts${contactQuery.toString()}`)\r\n const contactData = contacts[0] || null\r\n\r\n // Update nickname\r\n const updateQuery = createQuery()\r\n .eq('id', contactId)\r\n .eq('owner_id', validOwnerId)\r\n\r\n await apiClient.patch(`/rest/v1/contacts${updateQuery.toString()}`, {\r\n nickname: nickname.trim() || null\r\n })\r\n\r\n if (contactData) {\r\n triggerContactAlert('update', validOwnerId, contactData.contact_user_id).catch((err) =>\r\n console.error('[DB Alerts] Error sending contact updated alert:', err)\r\n )\r\n }\r\n\r\n return true\r\n } catch (error) {\r\n console.error('[Contacts API] Failed to update contact nickname:', error)\r\n return false\r\n }\r\n}\r\n\r\n/**\r\n * Deletes a contact.\r\n */\r\nexport async function deleteContact(contactId: string, ownerId: string): Promise<boolean> {\r\n try {\r\n const validOwnerId = stringToUuid(ownerId)\r\n // Fetch contact details to resolve contactUserId for alert\r\n const contactQuery = createQuery()\r\n .select('contact_user_id')\r\n .eq('id', contactId)\r\n .limit(1)\r\n\r\n const contacts = await apiClient.get<any[]>(`/rest/v1/contacts${contactQuery.toString()}`)\r\n const contactData = contacts[0] || null\r\n\r\n // Delete contact\r\n const deleteQuery = createQuery()\r\n .eq('id', contactId)\r\n .eq('owner_id', validOwnerId)\r\n\r\n await apiClient.delete(`/rest/v1/contacts${deleteQuery.toString()}`)\r\n\r\n if (contactData) {\r\n triggerContactAlert('delete', validOwnerId, contactData.contact_user_id).catch((err) =>\r\n console.error('[DB Alerts] Error sending contact deleted alert:', err)\r\n )\r\n }\r\n\r\n return true\r\n } catch (error) {\r\n console.error('[Contacts API] Failed to delete contact:', error)\r\n return false\r\n }\r\n}\r\n","import { Contact } from '../api/contacts.types'\r\nimport {\r\n getContacts as apiGetContacts,\r\n createContact as apiCreateContact,\r\n updateContact as apiUpdateContact,\r\n deleteContact as apiDeleteContact\r\n} from '../api/contacts.api'\r\n\r\nexport async function getUserContacts(userId: string): Promise<Contact[]> {\r\n return apiGetContacts(userId)\r\n}\r\n\r\nexport async function createContact(\r\n ownerId: string,\r\n contactEmail: string,\r\n nickname?: string,\r\n ownerEmail?: string\r\n): Promise<{ success: boolean; error?: string }> {\r\n return apiCreateContact(ownerId, contactEmail, nickname, ownerEmail)\r\n}\r\n\r\nexport async function updateContactNickname(\r\n contactId: string,\r\n ownerId: string,\r\n nickname: string\r\n): Promise<boolean> {\r\n return apiUpdateContact(contactId, ownerId, nickname)\r\n}\r\n\r\nexport async function deleteContact(contactId: string, ownerId: string): Promise<boolean> {\r\n return apiDeleteContact(contactId, ownerId)\r\n}","import { NextRequest, NextResponse } from 'next/server'\nimport { getUserConversations } from '@/features/chattemplate/chat/repositories/conversation-repository'\n\nexport async function handleConversationsGet(request: NextRequest) {\n try {\n const { searchParams } = new URL(request.url)\n const userId = searchParams.get('userId')\n\n if (!userId) {\n return NextResponse.json({ error: 'userId query parameter is required' }, { status: 400 })\n }\n\n const conversations = await getUserConversations(userId)\n return NextResponse.json(conversations)\n } catch (err) {\n console.error('GET conversations error:', err)\n return NextResponse.json(\n { error: err instanceof Error ? err.message : 'Failed to fetch conversations' },\n { status: 500 }\n )\n }\n}\n","import { NextRequest, NextResponse } from 'next/server'\nimport { saveGroup } from '@/features/chattemplate/groups/repositories/group-repository'\nimport { createClient } from '@supabase/supabase-js'\n\nexport async function handleGroupsGet(request: NextRequest) {\n try {\n const { searchParams } = new URL(request.url)\n const email = searchParams.get('email')\n\n if (!email) {\n return NextResponse.json([])\n }\n\n const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL\n const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY\n if (!supabaseUrl || !supabaseAnonKey) {\n return NextResponse.json([])\n }\n const supabase = createClient(supabaseUrl, supabaseAnonKey)\n\n const { data, error } = await supabase\n .from('chat_group')\n .select('*')\n .contains('users', JSON.stringify([email]))\n .order('created_at', { ascending: false })\n\n if (error) throw error\n\n const mapped = (data || []).map((d: any) => ({\n id: d.id,\n groupName: d.name,\n description: d.description || '',\n groupImage: d.image_url || '',\n users: Array.isArray(d.users) ? d.users : [],\n status: d.status as 'Active' | 'Inactive',\n email: d.email || undefined,\n userUuid: d.user_uuid || undefined,\n createdAt: d.created_at,\n updatedAt: d.updated_at,\n }))\n\n return NextResponse.json(mapped)\n } catch (err) {\n console.error('GET groups error:', err)\n return NextResponse.json(\n { error: err instanceof Error ? err.message : 'Failed to fetch groups' },\n { status: 500 }\n )\n }\n}\n\nexport async function handleGroupsPost(request: NextRequest) {\n try {\n const body = await request.json()\n if (!body.groupName || !body.users || !body.status || !body.email) {\n return NextResponse.json(\n { error: 'groupName, users, status, and email are required fields' },\n { status: 400 }\n )\n }\n\n const saved = await saveGroup({\n groupName: body.groupName,\n description: body.description,\n groupImage: body.groupImage,\n users: body.users,\n status: body.status,\n email: body.email,\n userUuid: body.userUuid,\n })\n\n if (!saved) {\n return NextResponse.json({ error: 'Failed to create group in database' }, { status: 500 })\n }\n\n return NextResponse.json(saved, { status: 201 })\n } catch (err) {\n console.error('POST group error:', err)\n return NextResponse.json(\n { error: err instanceof Error ? err.message : 'Internal server error while creating group' },\n { status: 500 }\n )\n }\n}\n","import { Group } from './groups.types'\r\n\r\n/**\r\n * Maps database group record objects into the application Group representation.\r\n */\r\nexport function mapToGroup(dbRecord: any): Group {\r\n return {\r\n id: dbRecord.id,\r\n groupName: dbRecord.name,\r\n description: dbRecord.description || '',\r\n groupImage: dbRecord.image_url || '',\r\n users: Array.isArray(dbRecord.users) ? dbRecord.users : [],\r\n status: dbRecord.status as 'Active' | 'Inactive',\r\n email: dbRecord.email || undefined,\r\n userUuid: dbRecord.user_uuid || undefined,\r\n createdAt: dbRecord.created_at,\r\n updatedAt: dbRecord.updated_at,\r\n }\r\n}\r\n","import { apiClient } from '../../shared/api/apiClient'\r\nimport { createQuery } from '../../shared/api/queryBuilder'\r\nimport { getCurrentUser } from '../../shared/api/auth'\r\nimport { Group } from './groups.types'\r\nimport { mapToGroup } from './groups.mapper'\r\nimport { triggerGroupAlert } from '@/services/db-alert.service'\r\n\r\n/**\r\n * Fetch all chat groups.\r\n */\r\nexport async function getGroups(): Promise<Group[]> {\r\n try {\r\n const query = createQuery().order('created_at', { ascending: false })\r\n const data = await apiClient.get<any[]>(`/rest/v1/chat_group${query.toString()}`)\r\n if (!data) return []\r\n\r\n return data.map(mapToGroup)\r\n } catch (error) {\r\n console.error('[Groups API] Supabase getGroups error:', error)\r\n return []\r\n }\r\n}\r\n\r\n/**\r\n * Saves a group (Create or Update).\r\n */\r\nexport async function saveGroup(group: Omit<Group, 'id'> & { id?: string }): Promise<Group | null> {\r\n const id = group.id || crypto.randomUUID()\r\n const now = new Date().toISOString()\r\n \r\n let userUuid: string | null = null\r\n const groupEmail = group.email?.trim().toLowerCase()\r\n \r\n // 1. Check if userUuid is already provided\r\n if (group.userUuid) {\r\n userUuid = group.userUuid\r\n } else if (groupEmail) {\r\n // If not provided, try to find/create from email\r\n try {\r\n const profileQuery = createQuery()\r\n .select('id')\r\n .eq('email', groupEmail)\r\n .limit(1)\r\n\r\n const profiles = await apiClient.get<any[]>(`/rest/v1/profiles${profileQuery.toString()}`)\r\n const existing = profiles[0] || null\r\n\r\n if (existing) {\r\n userUuid = existing.id\r\n } else {\r\n // Create new profile if it doesn't exist\r\n const newUserId = crypto.randomUUID ? crypto.randomUUID() : \r\n 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\r\n const r = Math.random() * 16 | 0;\r\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\r\n return v.toString(16);\r\n })\r\n\r\n const newProfile = {\r\n id: newUserId,\r\n email: groupEmail,\r\n name: group.groupName || groupEmail.split('@')[0],\r\n avatar: group.groupImage || null,\r\n status: 'offline',\r\n online: false,\r\n offline: true,\r\n last_seen: now,\r\n updated_at: now\r\n }\r\n\r\n const inserted = await apiClient.post<any[]>('/rest/v1/profiles', newProfile)\r\n if (inserted && inserted[0]) {\r\n userUuid = inserted[0].id\r\n }\r\n }\r\n } catch (err) {\r\n console.error('[Groups API] Failed to get or create group user:', err)\r\n }\r\n }\r\n\r\n console.log('[Groups API] Saving group with userUuid:', userUuid)\r\n\r\n // 2. Prepare group record\r\n const record = {\r\n id,\r\n name: group.groupName,\r\n description: group.description || null,\r\n image_url: group.groupImage || null,\r\n users: group.users || [],\r\n status: group.status || 'Active',\r\n email: groupEmail || null,\r\n user_uuid: userUuid,\r\n created_at: group.createdAt || now,\r\n updated_at: now\r\n }\r\n\r\n // Check if group exists to determine create vs update\r\n let isNew = true\r\n try {\r\n const checkQuery = createQuery()\r\n .select('id')\r\n .eq('id', id)\r\n .limit(1)\r\n\r\n const existing = await apiClient.get<any[]>(`/rest/v1/chat_group${checkQuery.toString()}`)\r\n if (existing && existing.length > 0) {\r\n isNew = false\r\n }\r\n } catch (err) {\r\n // Ignore and assume new\r\n }\r\n\r\n try {\r\n // Perform Upsert in PostgREST using resolution=merge-duplicates Prefer header\r\n const data = await apiClient.post<any[]>('/rest/v1/chat_group', record, {\r\n headers: {\r\n 'Prefer': 'resolution=merge-duplicates,return=representation'\r\n }\r\n })\r\n \r\n const savedRecord = data && data[0] ? data[0] : null\r\n if (!savedRecord) {\r\n return null\r\n }\r\n\r\n // Trigger Group Alert (async)\r\n triggerGroupAlert(isNew ? 'create' : 'update', userUuid || '', group.groupName).catch((err) =>\r\n console.error('[DB Alerts] Error sending group saved alert:', err)\r\n )\r\n\r\n return mapToGroup(savedRecord)\r\n } catch (error) {\r\n console.error('[Groups API] Supabase saveGroup error:', error)\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * Deletes a chat group.\r\n */\r\nexport async function deleteGroup(id: string): Promise<boolean> {\r\n try {\r\n // Fetch group details before deleting to resolve name for alert\r\n const checkQuery = createQuery()\r\n .select('name, user_uuid')\r\n .eq('id', id)\r\n .limit(1)\r\n\r\n const groups = await apiClient.get<any[]>(`/rest/v1/chat_group${checkQuery.toString()}`)\r\n const groupData = groups[0] || null\r\n\r\n // Delete group\r\n const deleteQuery = createQuery().eq('id', id)\r\n await apiClient.delete(`/rest/v1/chat_group${deleteQuery.toString()}`)\r\n\r\n // Fetch current user details to determine actorId\r\n let actorId = groupData?.user_uuid || ''\r\n try {\r\n const user = await getCurrentUser()\r\n if (user) {\r\n actorId = user.id\r\n }\r\n } catch {\r\n // Ignore\r\n }\r\n\r\n if (groupData) {\r\n triggerGroupAlert('delete', actorId, groupData.name).catch((err) =>\r\n console.error('[DB Alerts] Error sending group deleted alert:', err)\r\n )\r\n }\r\n\r\n return true\r\n } catch (error) {\r\n console.error('[Groups API] Supabase deleteGroup error:', error)\r\n return false\r\n }\r\n}\r\n","import { Group } from '../api/groups.types'\r\nimport {\r\n getGroups as apiGetGroups,\r\n saveGroup as apiSaveGroup,\r\n deleteGroup as apiDeleteGroup\r\n} from '../api/groups.api'\r\n\r\nexport async function getGroups(): Promise<Group[]> {\r\n return apiGetGroups()\r\n}\r\n\r\nexport async function saveGroup(group: Omit<Group, 'id'> & { id?: string }): Promise<Group | null> {\r\n return apiSaveGroup(group)\r\n}\r\n\r\nexport async function deleteGroup(id: string): Promise<boolean> {\r\n return apiDeleteGroup(id)\r\n}","import { NextRequest, NextResponse } from 'next/server'\nimport {\n getAllProfiles,\n getProfileByEmail,\n} from '@/features/chattemplate/chat/repositories/profile-repository'\n\nexport async function handleProfilesGet(request: NextRequest) {\n try {\n const { searchParams } = new URL(request.url)\n const email = searchParams.get('email')\n\n if (email) {\n const profile = await getProfileByEmail(email)\n if (!profile) {\n return NextResponse.json({ error: 'Profile not found' }, { status: 404 })\n }\n return NextResponse.json(profile)\n }\n\n const profiles = await getAllProfiles()\n return NextResponse.json(profiles)\n } catch (err) {\n console.error('GET profiles error:', err)\n return NextResponse.json(\n { error: err instanceof Error ? err.message : 'Failed to fetch profiles' },\n { status: 500 }\n )\n }\n}\n","import { apiClient } from '../../shared/api/apiClient'\r\nimport { createQuery } from '../../shared/api/queryBuilder'\r\nimport { Profile } from '../types/chat.types'\r\nimport { autoSubscribeAdmin } from '@/services/db-alert.service'\r\n\r\n/**\r\n * Ensures a profile exists for a user and triggers alerts if they are an admin.\r\n */\r\nexport async function ensureProfileExists(user: {\r\n accountNo: string\r\n email: string\r\n name?: string\r\n picture?: string\r\n mobile?: string\r\n}): Promise<Profile | null> {\r\n const profile = await ensureProfileExistsInternal(user)\r\n if (profile) {\r\n autoSubscribeAdmin(profile.id, profile.email).catch((err) =>\r\n console.error('[DB Alerts] Error checking admin auto-subscribe:', err)\r\n )\r\n }\r\n return profile\r\n}\r\n\r\nasync function ensureProfileExistsInternal(user: {\r\n accountNo: string\r\n email: string\r\n name?: string\r\n picture?: string\r\n mobile?: string\r\n}): Promise<Profile | null> {\r\n try {\r\n // 1. First, check if the profile exists by ID\r\n const checkIdQuery = createQuery().select('*').eq('id', user.accountNo).limit(1)\r\n const profilesById = await apiClient.get<any[]>(`/rest/v1/profiles${checkIdQuery.toString()}`)\r\n const byId = profilesById[0] || null\r\n\r\n if (byId) {\r\n // Update mobile if missing\r\n if (user.mobile && !byId.mobile) {\r\n updateProfile(byId.id, { mobile: user.mobile }).catch(() => {})\r\n }\r\n return {\r\n id: byId.id,\r\n name: byId.name || user.name || (user.email ? user.email.split('@')[0] : user.mobile || 'User'),\r\n email: byId.email || user.email,\r\n avatar_url: byId.avatar || user.picture || undefined,\r\n }\r\n }\r\n\r\n // 2. If email is provided, check if a profile with this email already exists case-insensitively\r\n if (user.email) {\r\n const checkEmailQuery = createQuery().select('*').ilike('email', user.email.trim()).limit(1)\r\n const profilesByEmail = await apiClient.get<any[]>(`/rest/v1/profiles${checkEmailQuery.toString()}`)\r\n const byEmail = profilesByEmail[0] || null\r\n\r\n if (byEmail) {\r\n console.log(`[profiles.api] Profile with email ${user.email} already exists with ID: ${byEmail.id}. Attempting to update ID.`)\r\n \r\n const updateQuery = createQuery().eq('id', byEmail.id).limit(1)\r\n try {\r\n const updated = await apiClient.patch<any[]>(`/rest/v1/profiles${updateQuery.toString()}`, {\r\n id: user.accountNo,\r\n ...(user.mobile ? { mobile: user.mobile } : {})\r\n })\r\n const updatedProfile = updated[0] || null\r\n\r\n if (updatedProfile) {\r\n return {\r\n id: updatedProfile.id,\r\n name: updatedProfile.name || user.name || user.email.split('@')[0],\r\n email: updatedProfile.email,\r\n avatar_url: updatedProfile.avatar || user.picture || undefined,\r\n }\r\n }\r\n } catch (err) {\r\n // If update failed (e.g. FK constraint block), return the existing profile\r\n console.warn('[profiles.api] FK constraint prevent ID update, returning existing profile.')\r\n }\r\n\r\n return {\r\n id: byEmail.id,\r\n name: byEmail.name || user.name || user.email.split('@')[0],\r\n email: byEmail.email,\r\n avatar_url: byEmail.avatar || user.picture || undefined,\r\n }\r\n }\r\n }\r\n\r\n // 3. Insert new profile\r\n const name = user.name || (user.email ? user.email.split('@')[0] : user.mobile || 'User')\r\n const avatar = user.picture || null\r\n\r\n try {\r\n let inserted: any[] = []\r\n try {\r\n inserted = await apiClient.post<any[]>('/rest/v1/profiles', {\r\n id: user.accountNo,\r\n auth_user_id: user.accountNo,\r\n name,\r\n avatar,\r\n email: user.email,\r\n mobile: user.mobile || null,\r\n })\r\n } catch (err) {\r\n inserted = await apiClient.post<any[]>('/rest/v1/profiles', {\r\n id: user.accountNo,\r\n name,\r\n avatar,\r\n email: user.email,\r\n mobile: user.mobile || null,\r\n })\r\n }\r\n const newProfile = inserted[0] || null\r\n\r\n if (!newProfile) {\r\n throw new Error('Failed to create new profile record.')\r\n }\r\n\r\n return {\r\n id: newProfile.id,\r\n name: newProfile.name,\r\n email: newProfile.email,\r\n avatar_url: newProfile.avatar || undefined,\r\n }\r\n } catch (insertError: any) {\r\n if (insertError.code === '23505' && user.email) {\r\n // Fallback select by email in case of race condition\r\n const fallbackQuery = createQuery().select('*').ilike('email', user.email.trim()).limit(1)\r\n const fallbacks = await apiClient.get<any[]>(`/rest/v1/profiles${fallbackQuery.toString()}`)\r\n const fallback = fallbacks[0] || null\r\n \r\n if (fallback) {\r\n return {\r\n id: fallback.id,\r\n name: fallback.name || name,\r\n email: fallback.email,\r\n avatar_url: fallback.avatar || undefined,\r\n }\r\n }\r\n }\r\n throw insertError\r\n }\r\n } catch (err) {\r\n console.error('[profiles.api] Failed in ensureProfileExists:', err)\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * Fetch profile by email.\r\n */\r\nexport async function getProfileByEmail(email: string): Promise<Profile | null> {\r\n try {\r\n const query = createQuery().select('*').eq('email', email).limit(1)\r\n const profiles = await apiClient.get<any[]>(`/rest/v1/profiles${query.toString()}`)\r\n const data = profiles[0] || null\r\n if (!data) return null\r\n\r\n return {\r\n id: data.id,\r\n name: data.name,\r\n email: data.email,\r\n avatar_url: data.avatar || undefined,\r\n }\r\n } catch (err) {\r\n console.error('[profiles.api] Failed to get profile by email:', err)\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * Fetch all profiles.\r\n */\r\nexport async function getAllProfiles(): Promise<Profile[]> {\r\n try {\r\n const query = createQuery().select('*').order('name', { ascending: true })\r\n const data = await apiClient.get<any[]>(`/rest/v1/profiles${query.toString()}`)\r\n if (!data) return []\r\n\r\n return data.map((d: any) => ({\r\n id: d.id,\r\n name: d.name,\r\n email: d.email,\r\n avatar_url: d.avatar || undefined,\r\n }))\r\n } catch (err) {\r\n console.error('[profiles.api] Failed to get all profiles:', err)\r\n return []\r\n }\r\n}\r\n\r\n/**\r\n * Get or create a placeholder profile for a contact.\r\n */\r\nexport async function getOrCreateProfileForContact(email: string, name: string): Promise<Profile | null> {\r\n const existing = await getProfileByEmail(email)\r\n if (existing) return existing\r\n\r\n const placeholderId = crypto.randomUUID()\r\n try {\r\n const data = await apiClient.post<any[]>('/rest/v1/profiles', {\r\n id: placeholderId,\r\n name,\r\n email,\r\n })\r\n const inserted = data[0] || null\r\n if (inserted) {\r\n return {\r\n id: inserted.id,\r\n name: inserted.name,\r\n email: inserted.email,\r\n avatar_url: inserted.avatar || undefined,\r\n }\r\n }\r\n } catch (err) {\r\n console.error('[profiles.api] Failed to create placeholder profile:', err)\r\n }\r\n return null\r\n}\r\n\r\n/**\r\n * Update a profile's details or presence status.\r\n */\r\nexport async function updateProfile(\r\n id: string,\r\n updates: {\r\n status?: 'online' | 'offline'\r\n online?: boolean\r\n offline?: boolean\r\n last_seen?: string\r\n updated_at?: string\r\n name?: string\r\n avatar?: string | null\r\n company?: string | null\r\n mobile?: string | null\r\n fcm_token?: string | null\r\n }\r\n): Promise<boolean> {\r\n try {\r\n const query = createQuery().eq('id', id)\r\n await apiClient.patch(`/rest/v1/profiles${query.toString()}`, updates)\r\n return true\r\n } catch (err) {\r\n console.error('[profiles.api] Failed to update profile:', err)\r\n return false\r\n }\r\n}\r\n\r\n","import { Profile } from '../types/chat.types'\r\nimport * as profilesApi from '../api/profiles.api'\r\n\r\nexport async function ensureProfileExists(user: {\r\n accountNo: string\r\n email: string\r\n name?: string\r\n picture?: string\r\n mobile?: string\r\n}): Promise<Profile | null> {\r\n return profilesApi.ensureProfileExists(user)\r\n}\r\n\r\nexport async function getProfileByEmail(email: string): Promise<Profile | null> {\r\n return profilesApi.getProfileByEmail(email)\r\n}\r\n\r\nexport async function getAllProfiles(): Promise<Profile[]> {\r\n return profilesApi.getAllProfiles()\r\n}\r\n\r\nexport async function getOrCreateProfileForContact(email: string, name: string): Promise<Profile | null> {\r\n return profilesApi.getOrCreateProfileForContact(email, name)\r\n}\r\n\r\nexport async function updateProfile(\r\n id: string,\r\n updates: Parameters<typeof profilesApi.updateProfile>[1]\r\n): Promise<boolean> {\r\n return profilesApi.updateProfile(id, updates)\r\n}\r\n\r\n","import { NextRequest, NextResponse } from 'next/server'\nimport { apiClient } from '@/features/chattemplate/shared/api/apiClient'\nimport { createQuery } from '@/features/chattemplate/shared/api/queryBuilder'\n\nexport async function handleNotificationsGet(request: NextRequest) {\n try {\n const { searchParams } = new URL(request.url)\n const userId = searchParams.get('userId')\n const readFilter = searchParams.get('read')\n const limit = searchParams.get('limit')\n\n if (!userId) {\n return NextResponse.json({ error: 'userId query parameter is required' }, { status: 400 })\n }\n\n const query = createQuery()\n .select('*')\n .eq('user_id', userId)\n .order('created_at', { ascending: false })\n\n if (readFilter !== null) {\n query.eq('read', readFilter)\n }\n\n if (limit) {\n query.limit(parseInt(limit, 10))\n }\n\n const notifications = await apiClient.get<any[]>(`/rest/v1/notifications${query.toString()}`)\n return NextResponse.json(notifications)\n } catch (err) {\n console.error('GET notifications error:', err)\n return NextResponse.json(\n { error: err instanceof Error ? err.message : 'Failed to fetch notifications' },\n { status: 500 }\n )\n }\n}\n\nexport async function handleNotificationsPost(request: NextRequest) {\n try {\n const body = await request.json()\n\n if (!body.userId || !body.messageText) {\n return NextResponse.json(\n { error: 'userId and messageText are required' },\n { status: 400 }\n )\n }\n\n const newNotification = await apiClient.post<any>('/rest/v1/notifications', {\n user_id: body.userId,\n sender_id: body.senderId || null,\n message_id: body.messageId || null,\n message_text: body.messageText,\n read: false,\n })\n\n return NextResponse.json(newNotification, { status: 201 })\n } catch (err) {\n console.error('POST notification error:', err)\n return NextResponse.json(\n { error: err instanceof Error ? err.message : 'Failed to create notification' },\n { status: 500 }\n )\n }\n}\n\nexport async function handleNotificationsPatch(request: NextRequest) {\n try {\n const body = await request.json()\n\n if (!body.id) {\n return NextResponse.json({ error: 'id is required' }, { status: 400 })\n }\n\n const query = createQuery().eq('id', body.id)\n const updated = await apiClient.patch<any>(`/rest/v1/notifications${query.toString()}`, {\n read: body.read !== undefined ? body.read : true,\n })\n\n return NextResponse.json(updated)\n } catch (err) {\n console.error('PATCH notification error:', err)\n return NextResponse.json(\n { error: err instanceof Error ? err.message : 'Failed to update notification' },\n { status: 500 }\n )\n }\n}\n","import { NextResponse } from 'next/server'\nimport { createImapClient } from '../lib/email/imap'\nimport { parseEmail } from '../lib/email/email-parser'\nimport { transporter } from '../lib/email/mailer'\nimport mailConfig from '../../config/mail.json'\nimport { saveAttachmentLocally } from '../lib/email/attachment-storage'\n\n/**\n * Handle GET /api/mail/inbox\n */\nexport async function handleMailInboxGet(request: Request) {\n const url = new URL(request.url)\n const page = Math.max(1, parseInt(url.searchParams.get('page') || '1', 10))\n const limit = Math.max(1, parseInt(url.searchParams.get('limit') || '20', 10))\n\n const client = createImapClient()\n let hasMore = false\n let totalMessages = 0\n\n try {\n await client.connect()\n const lock = await client.getMailboxLock('INBOX')\n const emailsList: any[] = []\n\n try {\n const status = await client.status('INBOX', { messages: true })\n totalMessages = status.messages || 0\n\n if (totalMessages > 0) {\n const offset = (page - 1) * limit\n const endSeq = Math.max(0, totalMessages - offset)\n const startSeq = Math.max(1, endSeq - limit + 1)\n\n if (endSeq >= 1) {\n const range = `${startSeq}:${endSeq}`\n hasMore = startSeq > 1\n\n for await (const message of client.fetch(range, { source: true, flags: true })) {\n const isRead = message.flags && message.flags.has('\\\\Seen')\n\n try {\n const parsed = await parseEmail(message.source as Buffer, message.seq, !!isRead)\n emailsList.push(parsed)\n } catch (parseErr) {\n console.error(`Failed to parse email sequence ${message.seq}:`, parseErr)\n }\n }\n\n emailsList.reverse()\n }\n }\n } finally {\n lock.release()\n }\n\n await client.logout()\n\n return NextResponse.json({\n success: true,\n emails: emailsList,\n hasMore,\n total: totalMessages,\n page,\n limit,\n })\n } catch (error: any) {\n console.error('Error reading mailbox via IMAP:', error)\n try {\n await client.logout()\n } catch (_) {}\n\n return NextResponse.json(\n {\n success: false,\n message: `Failed to load inbox emails: ${error.message || error}`,\n },\n { status: 500 }\n )\n }\n}\n\n/**\n * Handle GET /api/mail/sent\n */\nexport async function handleMailSentGet(request: Request) {\n const url = new URL(request.url)\n const page = Math.max(1, parseInt(url.searchParams.get('page') || '1', 10))\n const limit = Math.max(1, parseInt(url.searchParams.get('limit') || '20', 10))\n\n const client = createImapClient()\n let hasMore = false\n let totalMessages = 0\n\n try {\n await client.connect()\n\n let mailboxName = 'INBOX.Sent'\n let lock: any\n\n try {\n lock = await client.getMailboxLock(mailboxName)\n } catch (_) {\n mailboxName = 'Sent'\n lock = await client.getMailboxLock(mailboxName)\n }\n\n const emailsList: any[] = []\n\n try {\n const status = await client.status(mailboxName, { messages: true })\n totalMessages = status.messages || 0\n\n if (totalMessages > 0) {\n const offset = (page - 1) * limit\n const endSeq = Math.max(0, totalMessages - offset)\n const startSeq = Math.max(1, endSeq - limit + 1)\n\n if (endSeq >= 1) {\n const range = `${startSeq}:${endSeq}`\n hasMore = startSeq > 1\n\n for await (const message of client.fetch(range, { source: true, flags: true })) {\n try {\n const parsed = await parseEmail(message.source as Buffer, message.seq, true)\n emailsList.push({\n ...parsed,\n isSent: true,\n })\n } catch (parseErr) {\n console.error(`Failed to parse sent email sequence ${message.seq}:`, parseErr)\n }\n }\n\n emailsList.reverse()\n }\n }\n } finally {\n if (lock) {\n lock.release()\n }\n }\n\n await client.logout()\n\n return NextResponse.json({\n success: true,\n emails: emailsList,\n hasMore,\n total: totalMessages,\n page,\n limit,\n })\n } catch (error: any) {\n console.error('Error reading sent mailbox via IMAP:', error)\n try {\n await client.logout()\n } catch (_) {}\n\n return NextResponse.json(\n {\n success: false,\n message: `Failed to load sent emails: ${error.message || error}`,\n },\n { status: 500 }\n )\n }\n}\n\n/**\n * Handle POST /api/mail/send\n */\nexport async function handleMailSendPost(request: Request) {\n try {\n const body = await request.json()\n const { to, subject, html } = body\n\n if (!to || !subject || !html) {\n return NextResponse.json(\n {\n success: false,\n message: \"Validation failed: 'to', 'subject', and 'html' are required fields.\",\n },\n { status: 400 }\n )\n }\n\n const attachments = (body.attachments || []).map((att: any) => {\n let rawContent = att.content || att.url || ''\n const filename = att.filename || att.name || 'attachment'\n const contentType = att.contentType || att.type || 'application/octet-stream'\n\n const saved = saveAttachmentLocally(filename, rawContent)\n\n if (rawContent.includes(';base64,')) {\n rawContent = rawContent.split(';base64,').pop() || ''\n }\n\n return {\n filename,\n contentType,\n content: Buffer.from(rawContent, 'base64'),\n url: saved.url,\n size: saved.size,\n }\n })\n\n const mailOptions: any = {\n from: body.from || `\"${mailConfig.email.split('@')[0]}\" <${mailConfig.email}>`,\n to: Array.isArray(to) ? to.join(', ') : to,\n subject,\n html,\n }\n\n if (attachments.length > 0) {\n mailOptions.attachments = attachments\n }\n\n const info = await transporter.sendMail(mailOptions)\n\n try {\n const MailComposer = require('nodemailer/lib/mail-composer')\n const composer = new MailComposer(mailOptions)\n const rawMimeBuffer = await composer.compile().build()\n\n const client = createImapClient()\n await client.connect()\n await client.append('INBOX.Sent', rawMimeBuffer, ['\\\\Seen'])\n await client.logout()\n } catch (imapErr) {\n console.warn('Could not save to IMAP INBOX.Sent:', imapErr)\n }\n\n const returnedAttachments = attachments.map((att: any, idx: number) => ({\n id: `sent-att-${Date.now()}-${idx}`,\n name: att.filename,\n type: att.contentType,\n size: att.size,\n url: att.url,\n }))\n\n return NextResponse.json({\n success: true,\n message: 'Email sent successfully',\n messageId: info.messageId,\n attachments: returnedAttachments,\n })\n } catch (error: any) {\n console.error('Error sending email via Nodemailer:', error)\n return NextResponse.json(\n {\n success: false,\n message: `Failed to send email: ${error.message || error}`,\n },\n { status: 500 }\n )\n }\n}\n\n/**\n * Handle GET /api/mail/test\n */\nexport async function handleMailTestGet() {\n try {\n await transporter.verify()\n return NextResponse.json({\n success: true,\n message: 'SMTP connection successful',\n })\n } catch (error: any) {\n console.error('SMTP verification error:', error)\n return NextResponse.json(\n {\n success: false,\n message: `SMTP connection failed: ${error.message || error}`,\n },\n { status: 500 }\n )\n }\n}\n","import { ImapFlow } from \"imapflow\";\nimport mailConfig from \"../../../config/mail.json\";\n\n/**\n * IMAP (Internet Message Access Protocol) is used to access and read emails stored on a mail server.\n * Unlike SMTP, which is purely for sending emails, IMAP allows us to fetch, search, and manage messages in the inbox.\n *\n * ImapFlow is a modern, high-performance library for Node.js to communicate with IMAP servers.\n *\n * secure: true ensures that our connection to the Hostinger IMAP server on port 993 is encrypted\n * using SSL/TLS, keeping the email content and access credentials secure.\n *\n * SMTP = sending emails\n * IMAP = reading/receiving emails\n */\nexport function createImapClient() {\n return new ImapFlow({\n host: mailConfig.imap.host,\n port: mailConfig.imap.port,\n secure: mailConfig.imap.secure,\n auth: {\n user: mailConfig.email,\n pass: mailConfig.password,\n },\n // Hostinger and some other providers benefit from lower concurrency/logger configurations\n logger: false,\n });\n}\n","{\n \"email\": \"ask@morrai.com\",\n \"password\": \"0un:ZX3JOs&E\",\n \"smtp\": {\n \"host\": \"smtp.hostinger.com\",\n \"port\": 587,\n \"secure\": false,\n \"requireTLS\": true\n },\n \"imap\": {\n \"host\": \"imap.hostinger.com\",\n \"port\": 993,\n \"secure\": true\n }\n}","import { simpleParser } from \"mailparser\";\n\n/**\n * Parses a raw email buffer/stream using the mailparser library.\n * mailparser extracts headers, plain text, HTML, and other metadata from MIME messages.\n *\n * @param source Raw email MIME content (Buffer or Stream)\n * @param seq Sequence number or UID of the message on the IMAP server\n * @param isRead Flag indicating if the message has been marked as read (\\Seen flag)\n */\nexport async function parseEmail(source: Buffer, seq: number, isRead: boolean) {\n const parsed = await simpleParser(source);\n\n // Extract from address\n let fromAddress = \"\";\n const fromObj = parsed.from as any;\n if (fromObj && fromObj.value && fromObj.value.length > 0) {\n const fromVal = fromObj.value[0];\n fromAddress = fromVal.address || fromVal.name || \"\";\n }\n\n // Extract to address\n let toAddress = \"\";\n const toObj = parsed.to as any;\n if (toObj && toObj.value && toObj.value.length > 0) {\n const toVal = toObj.value[0];\n toAddress = toVal.address || toVal.name || \"\";\n }\n\n // Format file size\n const formatSize = (bytes: number) => {\n if (!bytes || bytes === 0) return \"0 B\";\n const k = 1024;\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + \" \" + sizes[i];\n };\n\n // Map attachments to frontend format and save to public/uploads/mail-attachments/\n const attachments = (parsed.attachments || []).map((att: any, idx: number) => {\n const mimeType = att.contentType || \"application/octet-stream\";\n let fileUrl = \"\";\n let formattedSize = formatSize(att.size || (att.content ? att.content.length : 0));\n\n if (att.content) {\n try {\n const buf = Buffer.isBuffer(att.content) ? att.content : Buffer.from(att.content);\n fileUrl = `data:${mimeType};base64,${buf.toString(\"base64\")}`;\n } catch (err) {\n console.error(\"Error processing attachment:\", err);\n }\n }\n\n return {\n id: `att-${seq}-${idx}`,\n name: att.filename || `attachment-${idx + 1}`,\n type: mimeType,\n size: formattedSize,\n url: fileUrl,\n };\n });\n\n return {\n id: String(seq),\n from: fromAddress,\n fromName: parsed.from?.text || fromAddress,\n to: toAddress,\n subject: parsed.subject || \"(No Subject)\",\n date: parsed.date ? parsed.date.toISOString() : new Date().toISOString(),\n text: parsed.text || \"\",\n html: parsed.html || parsed.textAsHtml || \"\",\n isRead: isRead,\n attachments: attachments,\n };\n}\n","import nodemailer from \"nodemailer\";\nimport mailConfig from \"../../../config/mail.json\";\n\n/**\n * Nodemailer is a popular Node.js library that allows server-side code to send emails.\n * It abstracts the complex network protocols and headers required to interact with email servers.\n *\n * SMTP (Simple Mail Transfer Protocol) is the standard protocol used for sending emails across the internet.\n * We connect to Hostinger's SMTP server to relay our emails to their destination.\n *\n * secure: true means we are establishing an encrypted SSL/TLS connection immediately on port 465.\n * This is crucial for protecting the email account credentials and content during transmission.\n *\n * Security: These credentials and SMTP connection logic must remain strictly on the server-side.\n * Exposing mailConfig or the transporter configuration to the client would allow malicious users\n * to extract the password and hijack our Hostinger email account to send spam.\n */\nexport const transporter = nodemailer.createTransport({\n host: mailConfig.smtp.host,\n port: mailConfig.smtp.port,\n secure: mailConfig.smtp.secure,\n requireTLS: mailConfig.smtp.requireTLS,\n\n auth: {\n user: mailConfig.email,\n pass: mailConfig.password,\n },\n});\n","import fs from \"fs\";\nimport path from \"path\";\n\nconst UPLOAD_DIR = path.join(process.cwd(), \"public\", \"uploads\", \"mail-attachments\");\n\nfunction ensureDirectoryExists() {\n if (!fs.existsSync(UPLOAD_DIR)) {\n fs.mkdirSync(UPLOAD_DIR, { recursive: true });\n }\n}\n\nfunction formatSize(bytes: number): string {\n if (!bytes || bytes === 0) return \"0 B\";\n const k = 1024;\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + \" \" + sizes[i];\n}\n\n/**\n * Saves attachment file buffer to public/uploads/mail-attachments/\n * and returns the relative public URL and formatted size.\n */\nexport function saveAttachmentLocally(\n originalFilename: string,\n content: Buffer | Uint8Array | string\n): { url: string; size: string } {\n try {\n ensureDirectoryExists();\n\n const sanitizeName = (originalFilename || \"attachment\")\n .replace(/[^a-zA-Z0-9._-]/g, \"_\")\n .substring(0, 100);\n\n const timestamp = Date.now();\n const safeFilename = `${timestamp}_${sanitizeName}`;\n const filePath = path.join(UPLOAD_DIR, safeFilename);\n\n let buffer: Buffer;\n if (Buffer.isBuffer(content)) {\n buffer = content;\n } else if (typeof content === \"string\") {\n let raw = content;\n if (raw.includes(\";base64,\")) {\n raw = raw.split(\";base64,\").pop() || \"\";\n }\n buffer = Buffer.from(raw, \"base64\");\n } else {\n buffer = Buffer.from(content);\n }\n\n fs.writeFileSync(filePath, buffer);\n\n const publicUrl = `/uploads/mail-attachments/${safeFilename}`;\n const formattedSize = formatSize(buffer.length);\n\n return {\n url: publicUrl,\n size: formattedSize,\n };\n } catch (err) {\n console.error(\"Failed to save attachment locally:\", err);\n return {\n url: \"\",\n size: \"0 B\",\n };\n }\n}\n"],"mappings":"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","names":["NextResponse","NextResponse","NextResponse","NextResponse","createClient","crypto","cookies","supabase","supabase","createClient","NextResponse","NextResponse","NextResponse","NextResponse","createClient","supabase","createClient","SUPABASE_URL","path","getConversationMessages","createMessage","getUserConversations","getOrCreateDirectConversation","NextResponse","getOrCreateDirectConversation","getConversationMessages","createMessage","NextResponse","supabase","createClient","supabase","createClient","createMessage","createContact","NextResponse","createContact","NextResponse","NextResponse","getUserConversations","NextResponse","saveGroup","createClient","NextResponse","supabaseUrl","supabaseAnonKey","supabase","saveGroup","NextResponse","getProfileByEmail","getAllProfiles","getProfileByEmail","NextResponse","getAllProfiles","NextResponse","NextResponse","NextResponse","formatSize","fs","path","NextResponse"]}
@@ -0,0 +1 @@
1
+ export * from './src/services';