diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..5e51750 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,47 @@ +# Changelog + +Todas as mudanças notáveis neste projeto serão documentadas neste arquivo. + +O formato é baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.0.0/), +e este projeto adere ao [Semantic Versioning](https://semver.org/lang/pt-BR/). + +## [0.1.0] - 2024-03-23 + +### Adicionado +- Edge function `generate-story` para geração de histórias com IA + - Integração com OpenAI GPT para criação de texto + - Integração com DALL-E para geração de imagens + - Sistema de logs estruturados para monitoramento + - Tratamento robusto de erros e validações + +- Componente `StoryGenerator` para interface de criação + - Fluxo de seleção de categorias (tema, disciplina, personagem, cenário) + - Feedback visual do processo de geração + - Validações de campos obrigatórios + - Navegação automática entre etapas + - Tratamento de erros com feedback visual + +### Modificado +- Atualização do schema do banco para suportar novas categorias + - Adição de tabelas para temas, disciplinas, personagens e cenários + - Relacionamentos entre histórias e categorias + - Índices para otimização de consultas + +### Técnico +- Implementação de logs estruturados com prefixos por contexto +- Validações de dados em múltiplas camadas +- Tratamento de respostas da IA com fallbacks +- Otimização de queries no banco de dados +- Feedback em tempo real do processo de geração + +### Segurança +- Validação de dados de entrada na edge function +- Verificação de permissões do usuário +- Sanitização de prompts para a IA +- Proteção contra dados sensíveis nos logs + +### Próximos Passos +- [ ] Implementar cache de respostas da IA +- [ ] Adicionar retry policy para falhas de geração +- [ ] Melhorar prompts para histórias mais educativas +- [ ] Adicionar métricas de uso e performance \ No newline at end of file diff --git a/supabase/functions/generate-story/index.ts b/supabase/functions/generate-story/index.ts index 330b512..b4e1acf 100644 --- a/supabase/functions/generate-story/index.ts +++ b/supabase/functions/generate-story/index.ts @@ -14,7 +14,26 @@ interface StoryPrompt { context?: string; } +const ALLOWED_ORIGINS = [ + 'http://localhost:5173', // Vite dev server + 'http://localhost:3000', // Caso use outro port + 'https://historiasmagicas.netlify.app' // Produção +]; + serve(async (req) => { + const origin = req.headers.get('origin') || ''; + const corsHeaders = { + 'Access-Control-Allow-Origin': ALLOWED_ORIGINS.includes(origin) ? origin : ALLOWED_ORIGINS[0], + 'Access-Control-Allow-Methods': 'POST, OPTIONS', + 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', + 'Access-Control-Max-Age': '86400', // 24 horas + }; + + // Preflight request + if (req.method === 'OPTIONS') { + return new Response('ok', { headers: corsHeaders }) + } + const { record } = await req.json() console.log('[Request]', record) @@ -111,6 +130,11 @@ serve(async (req) => { console.log('[GPT] História gerada:', completion.choices[0].message) const storyContent = JSON.parse(completion.choices[0].message.content || '{}') + // Validar estrutura do retorno da IA + if (!storyContent.title || !Array.isArray(storyContent.pages)) { + throw new Error('Formato inválido retornado pela IA'); + } + console.log('[DALL-E] Iniciando geração de imagens...') const pages = await Promise.all( storyContent.pages.map(async (page: any, index: number) => { @@ -131,33 +155,53 @@ serve(async (req) => { console.log('[DALL-E] Todas as imagens geradas com sucesso') - console.log('[DB] Salvando história...') - await supabase - .from('stories') - .update({ + // Preparar dados para salvar + const storyData = { + title: storyContent.title, + content: { title: storyContent.title, - content: { - title: storyContent.title, - pages: pages, - theme: theme.title, - subject: subject.title, - character: character.title, - setting: setting.title, - context: record.context - }, - status: 'published' - }) - .eq('id', record.id) + pages: pages, + theme: theme.title, + subject: subject.title, + character: character.title, + setting: setting.title, + context: record.context, + original_prompt: prompt, + ai_response: completion.choices[0].message.content + }, + status: 'published', + theme_id: theme.id, + subject_id: subject.id, + character_id: character.id, + setting_id: setting.id, + updated_at: new Date().toISOString() + } - console.log('[DB] História salva com sucesso') + console.log('[DB] Dados para salvar:', storyData) + + // Atualizar história no Supabase + console.log('[DB] Salvando história...') + const { data: savedStory, error: updateError } = await supabase + .from('stories') + .update(storyData) + .eq('id', record.id) + .select() + .single() + + if (updateError) { + throw new Error(`Erro ao salvar história: ${updateError.message}`); + } + + console.log('[DB] História salva com sucesso:', savedStory) return new Response( JSON.stringify({ success: true, message: 'História gerada e salva com sucesso', - storyId: record.id + storyId: record.id, + story: savedStory }), - { headers: { 'Content-Type': 'application/json' } } + { headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ) } catch (error) { @@ -171,9 +215,9 @@ serve(async (req) => { timestamp: new Date().toISOString() }), { - headers: { 'Content-Type': 'application/json' }, + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, status: 500 } ) } -}) \ No newline at end of file +}) \ No newline at end of file