156 lines
5.0 KiB
TypeScript
156 lines
5.0 KiB
TypeScript
|
|
import "https://deno.land/x/xhr@0.1.0/mod.ts";
|
|
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
|
|
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.38.1";
|
|
|
|
const corsHeaders = {
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
|
|
};
|
|
|
|
const OPENAI_API_KEY = Deno.env.get('OPENAI_API_KEY');
|
|
const SUPABASE_URL = Deno.env.get('SUPABASE_URL') || "https://ffwcnetryatmpcnjkegg.supabase.co";
|
|
const SUPABASE_SERVICE_ROLE_KEY = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') || "";
|
|
|
|
serve(async (req) => {
|
|
// Handle CORS preflight requests
|
|
if (req.method === 'OPTIONS') {
|
|
return new Response(null, { headers: corsHeaders });
|
|
}
|
|
|
|
try {
|
|
const { uploadId, userId, filePath, fileType } = await req.json();
|
|
|
|
// Create Supabase client with service role key to bypass RLS
|
|
const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, {
|
|
auth: {
|
|
persistSession: false,
|
|
},
|
|
});
|
|
|
|
// Fetch the file content from Supabase Storage
|
|
const { data: fileData, error: fileError } = await supabase
|
|
.storage
|
|
.from('documents')
|
|
.download(filePath);
|
|
|
|
if (fileError) {
|
|
console.error('Error downloading file:', fileError);
|
|
return new Response(
|
|
JSON.stringify({ error: 'Error downloading file' }),
|
|
{ status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
);
|
|
}
|
|
|
|
// Convert file content to text
|
|
const text = await fileData.text();
|
|
|
|
// Extract tasks using OpenAI
|
|
const tasks = await extractTasksWithOpenAI(text, uploadId, userId);
|
|
|
|
// Save extracted tasks to the database
|
|
for (const task of tasks) {
|
|
const { error: taskError } = await supabase
|
|
.from('tasks')
|
|
.insert(task);
|
|
|
|
if (taskError) {
|
|
console.error('Error inserting task:', taskError);
|
|
}
|
|
}
|
|
|
|
// Update upload status to 'Completed'
|
|
const { error: updateError } = await supabase
|
|
.from('uploads')
|
|
.update({ status: 'Completed' })
|
|
.eq('id', uploadId);
|
|
|
|
if (updateError) {
|
|
console.error('Error updating upload status:', updateError);
|
|
return new Response(
|
|
JSON.stringify({ error: 'Error updating upload status' }),
|
|
{ status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
);
|
|
}
|
|
|
|
return new Response(
|
|
JSON.stringify({ success: true, tasksExtracted: tasks.length }),
|
|
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
);
|
|
|
|
} catch (error) {
|
|
console.error('Error processing document:', error);
|
|
return new Response(
|
|
JSON.stringify({ error: error.message }),
|
|
{ status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
);
|
|
}
|
|
});
|
|
|
|
async function extractTasksWithOpenAI(text: string, uploadId: string, userId: string) {
|
|
try {
|
|
// Call OpenAI API to extract tasks
|
|
const response = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': `Bearer ${OPENAI_API_KEY}`,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
model: 'gpt-4o-mini',
|
|
messages: [
|
|
{
|
|
role: 'system',
|
|
content: `
|
|
You are a specialized task extractor for IT security and compliance policy documents.
|
|
Your job is to identify specific actionable tasks from policy documents.
|
|
For each task, extract the following information:
|
|
1. Task name (short, clear description of what needs to be done)
|
|
2. Description (more detailed explanation)
|
|
3. Priority (High, Medium, Low)
|
|
4. Due date (if mentioned)
|
|
|
|
Format your response as a valid JSON array of task objects with these properties:
|
|
[
|
|
{
|
|
"task_name": "string",
|
|
"description": "string",
|
|
"priority": "string",
|
|
"due_date": "ISO date string or null"
|
|
}
|
|
]
|
|
|
|
If no due date is mentioned for a task, return null for that field.
|
|
Only return the JSON array, nothing else.
|
|
`
|
|
},
|
|
{
|
|
role: 'user',
|
|
content: text
|
|
}
|
|
]
|
|
}),
|
|
});
|
|
|
|
const data = await response.json();
|
|
const extractedTasksJson = data.choices[0].message.content;
|
|
|
|
// Parse the extracted tasks
|
|
const extractedTasks = JSON.parse(extractedTasksJson);
|
|
|
|
// Format tasks for database insertion
|
|
return extractedTasks.map((task: any) => ({
|
|
upload_id: uploadId,
|
|
user_id: userId,
|
|
task_name: task.task_name,
|
|
description: task.description,
|
|
priority: task.priority,
|
|
due_date: task.due_date,
|
|
status: 'Open'
|
|
}));
|
|
} catch (error) {
|
|
console.error('Error extracting tasks with OpenAI:', error);
|
|
throw new Error('Failed to extract tasks from document');
|
|
}
|
|
}
|