2026-03-04 18:25:09 +00:00

106 lines
3.1 KiB
TypeScript

import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.39.3';
import { requireAuth, checkRateLimit } from '../_shared/auth.ts';
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
};
Deno.serve(async (req) => {
if (req.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
try {
// Auth check
const auth = await requireAuth(req);
if (auth.error) return auth.error;
// Rate limit check (20 AI suggestions per hour)
const supabaseService = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
);
const rateLimitResponse = await checkRateLimit(auth.userId, 'ai_suggest', supabaseService);
if (rateLimitResponse) return rateLimitResponse;
const apiKey = Deno.env.get('INTEGRATIONS_API_KEY');
if (!apiKey) {
throw new Error('INTEGRATIONS_API_KEY not configured');
}
const { query, conversationHistory = [] } = await req.json();
if (!query) {
return new Response(
JSON.stringify({ error: 'Query is required' }),
{ status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
}
// Build conversation contents
const contents = [
...conversationHistory,
{
role: 'user',
parts: [{ text: query }]
}
];
// Call Gemini AI Search API
const response = await fetch(
'https://app-9w9pd00g5j41-api-zYm4ze3j7XvL.gateway.appmedo.com/v1beta/models/gemini-2.5-flash:streamGenerateContent?alt=sse',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Gateway-Authorization': `Bearer ${apiKey}`,
},
body: JSON.stringify({ contents }),
}
);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`AI Search API error: ${response.status} - ${errorText}`);
}
// Log search to database
const authHeader = req.headers.get('Authorization');
if (authHeader) {
const supabaseUrl = Deno.env.get('SUPABASE_URL')!;
const supabaseKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!;
const supabase = createClient(supabaseUrl, supabaseKey);
const token = authHeader.replace('Bearer ', '');
const { data: { user } } = await supabase.auth.getUser(token);
if (user) {
await supabase.from('admin_search_history').insert({
admin_id: user.id,
search_type: 'ai_search',
query: query,
metadata: { has_history: conversationHistory.length > 0 }
});
}
}
// Return streaming response
return new Response(response.body, {
headers: {
...corsHeaders,
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
},
});
} catch (error) {
console.error('AI Search error:', error);
return new Response(
JSON.stringify({ error: error.message }),
{ status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
}
});