🚀 Auto-deploy: Camila atualizado em 06/06/2026 21:07:19
This commit is contained in:
@@ -1335,6 +1335,207 @@ app.delete('/api/poesias/:id', requireAuth, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// ROTAS DO CRIAR CARTAZES
|
||||
// ============================================================
|
||||
app.post('/api/cartazes/generate', requireAuth, async (req, res) => {
|
||||
const { titulo, tema, corFundo, corTexto, layout, promptImagem } = req.body;
|
||||
const usuarioId = req.user?.id || '00000000-0000-0000-0000-000000000000';
|
||||
|
||||
if (!titulo || !tema || !layout) {
|
||||
return res.status(400).json({ error: 'Título, tema e layout são obrigatórios.' });
|
||||
}
|
||||
|
||||
try {
|
||||
const agentConfig = readAgentConfig();
|
||||
const agentName = agentConfig.agentName || "Kemily";
|
||||
|
||||
// 1. Gerar Conteúdo do Cartaz
|
||||
const posterPrompt = `Você é a ${agentName}, assistente pedagógica da professora Camila Martella Gasparini Reifonas.
|
||||
A professora Camila quer criar um Cartaz para sala de aula da Educação Infantil.
|
||||
|
||||
Detalhes do Cartaz:
|
||||
- Título: "${titulo}"
|
||||
- Tema / Foco Pedagógico: "${tema}"
|
||||
- Layout Selecionado: "${layout}" (Opções: regras, mural, calendario, livre)
|
||||
|
||||
Por favor, gere o CONTEÚDO textual estruturado do cartaz.
|
||||
REGRAS:
|
||||
- Deve ser acolhedor, lúdico e muito bem estruturado.
|
||||
- Use emojis apropriados para crianças.
|
||||
- Se for layout "regras", crie uma lista de combinados da sala (max 5) de forma positiva (ex: "Cuidar dos nossos brinquedos" ao invés de "Não quebrar os brinquedos").
|
||||
- Se for "calendario", estruture áreas para o dia da semana, ajudante do dia e tempo (ensolarado, chuvoso).
|
||||
- Se for "mural", crie seções de recados e novidades da semana.
|
||||
- Se for "livre", crie um texto poético ou explicativo baseado no tema.
|
||||
- Mantenha os textos curtos, visuais e fáceis de ler para crianças ou pais.
|
||||
- Retorne apenas o conteúdo em formato HTML simples (use tags como <h1>, <p>, <ul>, <li>, <strong>, <span> e estilos inline simples se necessário) para renderizarmos no preview do cartaz. Não adicione tags <html> ou <body>, apenas o bloco de conteúdo interno.`;
|
||||
|
||||
let conteudoHtml = '';
|
||||
|
||||
// Tentar MiniMax M3
|
||||
try {
|
||||
console.log('[Cartazes] Gerando conteúdo com MiniMax...');
|
||||
const result = await callMinimax({
|
||||
messages: [{ role: 'user', content: posterPrompt }],
|
||||
temperature: 0.7,
|
||||
max_tokens: 1500
|
||||
});
|
||||
conteudoHtml = result.text;
|
||||
} catch (err) {
|
||||
console.error('[Cartazes] Erro MiniMax:', err.message);
|
||||
}
|
||||
|
||||
// Fallback OpenRouter
|
||||
if (!conteudoHtml && process.env.OPENROUTER_API_KEY) {
|
||||
try {
|
||||
console.log('[Cartazes] Fallback OpenRouter...');
|
||||
const r = await fetch('https://openrouter.ai/api/v1/chat/completions', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: process.env.OPENROUTER_MODEL || 'openai/gpt-4.1-nano',
|
||||
messages: [{ role: 'user', content: posterPrompt }],
|
||||
temperature: 0.7,
|
||||
max_tokens: 1500
|
||||
})
|
||||
});
|
||||
if (r.ok) {
|
||||
const data = await r.json();
|
||||
conteudoHtml = data.choices?.[0]?.message?.content || '';
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Cartazes] Erro OpenRouter:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (!conteudoHtml) {
|
||||
conteudoHtml = `<h2>${titulo}</h2><p>Tema: ${tema}</p><p>Não foi possível gerar com IA. Edite o conteúdo livremente.</p>`;
|
||||
}
|
||||
|
||||
// Clean HTML from backticks
|
||||
conteudoHtml = conteudoHtml.replace(/```html|```/g, '').trim();
|
||||
|
||||
// 2. Gerar Imagem Ilustrativa se solicitado
|
||||
let generatedImageUrl = null;
|
||||
if (promptImagem && promptImagem.trim().length > 3) {
|
||||
try {
|
||||
console.log('[Cartazes] Gerando ilustração para o cartaz...');
|
||||
const minmBase = (process.env.MINIMAX_API_BASE || 'https://api.minimax.io/v1').replace(/\/v1\/?$/, '');
|
||||
const genResp = await fetch(`${minmBase}/v1/image_generation`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${process.env.MINIMAX_API_KEY}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: 'image-01',
|
||||
prompt: `${promptImagem}. Estilo ilustração de livro infantil de alta qualidade, 3D Pixar, cores suaves, traço limpo, sem texto.`,
|
||||
n: 1
|
||||
})
|
||||
});
|
||||
|
||||
if (genResp.ok) {
|
||||
const genData = await genResp.json();
|
||||
const rawImgUrl = genData.data?.image_urls?.[0];
|
||||
if (rawImgUrl) {
|
||||
const mediaDir = path.join(__dirname, 'public', 'generated-media');
|
||||
if (!fs.existsSync(mediaDir)) fs.mkdirSync(mediaDir, { recursive: true });
|
||||
|
||||
const imgFetch = await fetch(rawImgUrl);
|
||||
if (imgFetch.ok) {
|
||||
const imgBuffer = Buffer.from(await imgFetch.arrayBuffer());
|
||||
const fileName = `cartaz_${Date.now()}_${Math.floor(Math.random() * 1000)}.jpg`;
|
||||
fs.writeFileSync(path.join(mediaDir, fileName), imgBuffer);
|
||||
generatedImageUrl = `/generated-media/${fileName}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (imgErr) {
|
||||
console.error('[Cartazes] Erro na geração de imagem:', imgErr);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Salvar no Banco
|
||||
const dbRes = await dbPool.query(
|
||||
`INSERT INTO escola.cartazes (usuario_id, titulo, tema, cor_fundo, cor_texto, layout, conteudo, imagem_url)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id, created_at`,
|
||||
[usuarioId, titulo, tema, corFundo || '#ffffff', corTexto || '#1e293b', layout, conteudoHtml, generatedImageUrl]
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
id: dbRes.rows[0].id,
|
||||
titulo,
|
||||
tema,
|
||||
corFundo,
|
||||
corTexto,
|
||||
layout,
|
||||
conteudo: conteudoHtml,
|
||||
imagemUrl: generatedImageUrl,
|
||||
createdAt: dbRes.rows[0].created_at
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erro ao criar cartaz:', err);
|
||||
res.status(500).json({ error: 'Erro ao criar cartaz: ' + err.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/cartazes/list', requireAuth, async (req, res) => {
|
||||
const usuarioId = req.user?.id || '00000000-0000-0000-0000-000000000000';
|
||||
try {
|
||||
const dbRes = await dbPool.query(
|
||||
`SELECT id, titulo, tema, cor_fundo as "corFundo", cor_texto as "corTexto", layout, imagem_url as "imagemUrl", created_at as "createdAt"
|
||||
FROM escola.cartazes WHERE usuario_id = $1 ORDER BY created_at DESC`,
|
||||
[usuarioId]
|
||||
);
|
||||
res.json(dbRes.rows);
|
||||
} catch (err) {
|
||||
console.error('Erro ao listar cartazes:', err);
|
||||
res.status(500).json({ error: 'Erro ao listar cartazes.' });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/cartazes/:id', requireAuth, async (req, res) => {
|
||||
const { id } = req.params;
|
||||
const usuarioId = req.user?.id || '00000000-0000-0000-0000-000000000000';
|
||||
try {
|
||||
const dbRes = await dbPool.query(
|
||||
`SELECT id, titulo, tema, cor_fundo as "corFundo", cor_texto as "corTexto", layout, conteudo, imagem_url as "imagemUrl", created_at as "createdAt"
|
||||
FROM escola.cartazes WHERE id = $1 AND usuario_id = $2`,
|
||||
[id, usuarioId]
|
||||
);
|
||||
if (dbRes.rows.length === 0) {
|
||||
return res.status(404).json({ error: 'Cartaz não encontrado.' });
|
||||
}
|
||||
res.json(dbRes.rows[0]);
|
||||
} catch (err) {
|
||||
console.error('Erro ao buscar cartaz:', err);
|
||||
res.status(500).json({ error: 'Erro ao buscar cartaz.' });
|
||||
}
|
||||
});
|
||||
|
||||
app.delete('/api/cartazes/:id', requireAuth, async (req, res) => {
|
||||
const { id } = req.params;
|
||||
const usuarioId = req.user?.id || '00000000-0000-0000-0000-000000000000';
|
||||
try {
|
||||
const dbRes = await dbPool.query(
|
||||
`DELETE FROM escola.cartazes WHERE id = $1 AND usuario_id = $2`,
|
||||
[id, usuarioId]
|
||||
);
|
||||
if (dbRes.rowCount === 0) {
|
||||
return res.status(404).json({ error: 'Cartaz não encontrado ou não pertence ao usuário.' });
|
||||
}
|
||||
res.json({ success: true, message: 'Cartaz excluído com sucesso.' });
|
||||
} catch (err) {
|
||||
console.error('Erro ao deletar cartaz:', err);
|
||||
res.status(500).json({ error: 'Erro ao deletar cartaz.' });
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// ROTAS DO MUSICANDO IDEIAS
|
||||
// ============================================================
|
||||
@@ -1848,6 +2049,20 @@ async function initDatabase() {
|
||||
historia TEXT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Cartazes (Criar Cartazes)
|
||||
CREATE TABLE IF NOT EXISTS escola.cartazes (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
usuario_id UUID NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000',
|
||||
titulo VARCHAR(255) NOT NULL,
|
||||
tema VARCHAR(255) NOT NULL,
|
||||
cor_fundo VARCHAR(50) NOT NULL,
|
||||
cor_texto VARCHAR(50) NOT NULL,
|
||||
layout VARCHAR(50) NOT NULL, -- 'mural', 'regras', 'calendario', 'livre'
|
||||
conteudo TEXT NOT NULL,
|
||||
imagem_url TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
`);
|
||||
|
||||
// Garantir que a coluna character_description_english exista na tabela projetos_comics
|
||||
|
||||
Reference in New Issue
Block a user