Files
clube67_newwhats.local/newwhats.clube67.com/newwhats.local/plugins/nanobana/backend/routes.js
T
VPS 4 Deploy Agent 2f8c04a0a7
continuous-integration/webhook Falha no deploy de clube67_newwhats.local (VPS 4)
chore(ops): restore all source files in newwhats.clube67.com
2026-05-18 03:28:29 +02:00

164 lines
8.0 KiB
JavaScript

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createRoutes = createRoutes;
const express_1 = require("express");
const openai_1 = __importDefault(require("openai"));
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const https_1 = __importDefault(require("https"));
const uuid_1 = require("uuid");
function createRoutes(ctx) {
const router = (0, express_1.Router)();
const uploadDir = process.env.UPLOAD_DIR || path_1.default.join(process.cwd(), 'uploads');
router.get('/', (req, res) => {
res.json({
message: 'Nanobana plugin active',
timestamp: new Date().toISOString(),
service: 'Clube67 System'
});
});
router.post('/generate-image', async (req, res) => {
try {
const { type, prompt, partnerName, city, description, partnerId } = req.body;
if (!partnerId) {
return res.status(400).json({ error: 'Partner ID is required' });
}
// Check Usage Limit (1 per type)
// FUTURE UPDATE: Add monetization logic here to allow more generations
// Check for Unlimited User (ruibto@gmail.com)
const user = await ctx.db('users').where({ partner_id: partnerId }).first();
const isUnlimited = user && (user.email === 'ruibto@gmail.com');
if (!isUnlimited) {
const usageCount = await ctx.db('nanobana_usage')
.where({ partner_id: partnerId, type })
.count('id as count')
.first();
const count = usageCount ? Number(usageCount.count) : 0;
if (count >= 1) {
return res.status(403).json({ error: `Limite gratuito atingido para ${type === 'logo' ? 'Logo' : 'Banner'}. (Máximo: 1)` });
}
}
// Get Config
const nanobanaConfig = ctx.config.get('nanobana');
const apiKey = nanobanaConfig?.apiKey;
if (!apiKey) {
return res.status(400).json({ error: 'OpenAI API Key não configurada no plugin Nanobana.' });
}
const openai = new openai_1.default({ apiKey });
// Vision Analysis (if enabled)
let visionDescription = '';
if (req.body.useImageRef && req.body.referenceImageUrl) {
try {
const refUrl = req.body.referenceImageUrl;
let imagePath = '';
// Resolve local path
if (refUrl.includes('/uploads/')) {
const filename = path_1.default.basename(refUrl);
imagePath = path_1.default.join(uploadDir, filename);
}
if (imagePath && fs_1.default.existsSync(imagePath)) {
const imageBuffer = fs_1.default.readFileSync(imagePath);
const base64Image = imageBuffer.toString('base64');
const mimeType = imagePath.endsWith('.png') ? 'image/png' : 'image/jpeg';
const dataUrl = `data:${mimeType};base64,${base64Image}`;
const visionResponse = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{
role: "user",
content: [
{ type: "text", text: "Describe this image in detail. Focus on the main subject, colors, style, artistic technique, and composition. The description will be used to recreate a similar image." },
{ type: "image_url", image_url: { url: dataUrl } }
],
},
],
max_tokens: 300,
});
visionDescription = visionResponse.choices[0].message.content || '';
console.log('[Nanobana] Vision Description:', visionDescription);
}
else {
console.warn('[Nanobana] Reference image not found locally:', refUrl);
}
}
catch (visionErr) {
console.error('[Nanobana] Vision Error:', visionErr);
// Continue without vision
}
}
// Construct Prompt
let finalPrompt = '';
if (visionDescription) {
finalPrompt = `Create an image based on this visual description: "${visionDescription}".\n\nCONTEXT/MODIFICATIONS REQUESTED: ${prompt || 'Keep the style similar.'}.\n\nIdentity: ${partnerName} (${city}).`;
}
else if (prompt) {
finalPrompt = prompt;
}
else {
if (type === 'logo') {
finalPrompt = `A professional, minimalist, and modern logo for a company named "${partnerName}". Context: ${description}. Location style: ${city}. High quality, vector style, white background.`;
}
else {
finalPrompt = `A stunning, high-quality banner image for a company named "${partnerName}". Context: ${description}. The image should be wide (landscape), professional photography style, inviting, and related to the business topic.`;
}
}
// Generate Image
const response = await openai.images.generate({
model: "dall-e-3",
prompt: finalPrompt,
n: 1,
size: "1024x1024",
quality: "standard",
response_format: "url",
});
const imageUrl = response.data[0].url;
if (!imageUrl)
throw new Error('Falha ao gerar imagem na OpenAI');
// Download Image to Local Storage
const ext = '.png'; // DALL-E 3 usually png
const filename = `${(0, uuid_1.v4)()}${ext}`;
const localPath = path_1.default.join(uploadDir, filename);
const publicUrl = `/uploads/${filename}`;
const file = fs_1.default.createWriteStream(localPath);
await new Promise((resolve, reject) => {
https_1.default.get(imageUrl, function (response) {
response.pipe(file);
file.on('finish', () => {
file.close();
resolve(true);
});
}).on('error', function (err) {
fs_1.default.unlink(localPath, () => { });
reject(err);
});
});
// Record Usage
await ctx.db('nanobana_usage').insert({
partner_id: partnerId,
type
});
res.json({
success: true,
url: publicUrl,
originalPrompt: finalPrompt
});
}
catch (error) {
console.error('Nanobana Error:', error);
let errorMessage = error.message || 'Erro ao gerar imagem';
// Translate Safety Error
if (errorMessage.includes('safety system') || errorMessage.includes('rejected')) {
errorMessage = '⚠️ A imagem não pôde ser gerada pois a descrição ou o contexto viola as políticas de segurança da IA (conteúdo impróprio, marcas protegidas ou termos sensíveis). Tente ajustar o texto para ser mais genérico.';
}
else if (errorMessage.includes('billing') || errorMessage.includes('quota')) {
errorMessage = 'Erro de faturamento na IA. Contate o suporte.';
}
res.status(500).json({ error: errorMessage });
}
});
return router;
}
//# sourceMappingURL=routes.js.map