diff --git a/simple-crawler/smart-cloner.js b/simple-crawler/smart-cloner.js index 816fc08..a077a7e 100644 --- a/simple-crawler/smart-cloner.js +++ b/simple-crawler/smart-cloner.js @@ -71,7 +71,7 @@ class SmartCloner { } } - async cloneSite(url, maxPages = 20) { + async cloneSite(url, maxPages = 20, customFolderName = null) { console.log(`\n🚀 INICIANDO DEEP SMART CLONE: ${url}`); console.log(`📂 Limite de páginas: ${maxPages}\n`); @@ -82,7 +82,8 @@ class SmartCloner { const urlObj = new URL(url); const siteName = this.sanitizeFilename(urlObj.hostname); const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); - const siteDir = path.join(this.baseOutputDir, `${siteName}_${timestamp}`); + const finalFolderName = customFolderName ? this.sanitizeFilename(customFolderName) : `${siteName}_${timestamp}`; + const siteDir = path.join(this.baseOutputDir, finalFolderName); this.ensureDirectoryExists(siteDir); browser = await chromium.launch({ @@ -368,10 +369,12 @@ if (require.main === module) { const args = process.argv.slice(2); const urlArg = args.find(arg => arg.startsWith('--url=')); const url = urlArg ? urlArg.split('=')[1] : args[0]; + const folderArg = args.find(arg => arg.startsWith('--folderName=')); + const folderName = folderArg ? folderArg.split('=')[1] : null; if (url) { const cloner = new SmartCloner(); - cloner.cloneSite(url).then(res => { + cloner.cloneSite(url, 20, folderName).then(res => { console.log(JSON.stringify(res, null, 2)); process.exit(res.success ? 0 : 1); }); diff --git a/simple-crawler/wget-cloner.js b/simple-crawler/wget-cloner.js index e5953b2..81c8531 100644 --- a/simple-crawler/wget-cloner.js +++ b/simple-crawler/wget-cloner.js @@ -65,7 +65,7 @@ class WgetCloner { } } - async cloneWebsite(url) { + async cloneWebsite(url, customFolderName = null) { console.log(`\n${'='.repeat(60)}`); console.log(`🚀 CLONAGEM PROFISSIONAL COM WGET`); console.log(`${'='.repeat(60)}`); @@ -98,7 +98,8 @@ class WgetCloner { const urlObj = new URL(url); const siteName = this.sanitizeFilename(urlObj.hostname); const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); - const siteDir = path.join(this.baseOutputDir, `${siteName}_${timestamp}`); + const finalFolderName = customFolderName ? this.sanitizeFilename(customFolderName) : `${siteName}_${timestamp}`; + const siteDir = path.join(this.baseOutputDir, finalFolderName); this.ensureDirectoryExists(siteDir); console.log(`📁 Salvando em: ${siteDir}\n`); @@ -328,13 +329,13 @@ class WgetCloner { // API app.post('/wget-clone', async (req, res) => { try { - const { url } = req.body; + const { url, folderName } = req.body; if (!url) { return res.status(400).json({ error: 'URL obrigatória' }); } const cloner = new WgetCloner(); - const result = await cloner.cloneWebsite(url); + const result = await cloner.cloneWebsite(url, folderName); res.json({ success: true, data: result }); } catch (error) { diff --git a/update.sh b/update.sh index 4411ab5..d3f0582 100644 --- a/update.sh +++ b/update.sh @@ -17,4 +17,8 @@ git commit -m "$COMMIT_MESSAGE" || echo "Nenhuma mudança para comitar." # Se não houver origin ou for local, ele ignora falhas suavemente. git push origin main || git push || echo "Aviso: Push falhou ou repositório não tem remoto configurado." -echo "Atualização concluída!" +echo "Acionando o deploy no Coolify..." +curl -X POST -s -H "Authorization: Bearer 12|wbepNILQe24LAOjfEUmMROgU93F6uG1zuwPLwrRi786bca03" "http://localhost:8000/api/v1/deploy?uuid=at22zt4l2pv5at9cfkl2feh7" +echo "" + +echo "Atualização e Deploy concluídos!" diff --git a/web-interface/public/app-new.js b/web-interface/public/app-new.js index 3ff4601..5041e09 100644 --- a/web-interface/public/app-new.js +++ b/web-interface/public/app-new.js @@ -59,7 +59,8 @@ function navigateToPage(page) { 'dashboard': 'Dashboard', 'new-clone': 'Nova Clonagem', 'history': 'Histórico', - 'settings': 'Configurações' + 'settings': 'Configurações', + 'optimized-pages': 'Páginas Otimizadas (AI)' }; document.getElementById('pageTitle').textContent = titles[page] || page; @@ -70,6 +71,8 @@ function navigateToPage(page) { loadDashboard(); } else if (page === 'history') { loadHistory(); + } else if (page === 'optimized-pages') { + loadOptimizedPages(); } } @@ -206,6 +209,7 @@ async function handleCloneSubmit(e) { const includeCSSEl = document.getElementById('includeCSS'); const includeJSEl = document.getElementById('includeJS'); const convertLinksEl = document.getElementById('convertLinks'); + const customFolderEl = document.getElementById('customFolder'); if (!cloneUrlEl || !depthLevelEl || !includeImagesEl || !includeCSSEl || !includeJSEl || !convertLinksEl) { console.error('Form elements not found'); @@ -219,6 +223,7 @@ async function handleCloneSubmit(e) { const includeCSS = includeCSSEl.checked; const includeJS = includeJSEl.checked; const convertLinks = convertLinksEl.checked; + const folderName = customFolderEl ? customFolderEl.value.trim() : ''; const cloneMethod = 'wget'; if (!url) { @@ -240,6 +245,7 @@ async function handleCloneSubmit(e) { includeCSS, includeJS, convertLinks, + folderName, method: cloneMethod }); } @@ -306,7 +312,8 @@ async function startCloning(url, options) { headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url, - outputDir: settings.outputDir + outputDir: settings.outputDir, + folderName: options.folderName }) }); } else { @@ -317,7 +324,8 @@ async function startCloning(url, options) { body: JSON.stringify({ url, depth: options.depth, - outputDir: settings.outputDir + outputDir: settings.outputDir, + folderName: options.folderName }) }); } @@ -709,3 +717,61 @@ function formatRelativeTime(dateString) { if (minutes > 0) return minutes === 1 ? 'Há 1 minuto' : `Há ${minutes} minutos`; return 'Agora mesmo'; } + +// Optimized Pages +async function loadOptimizedPages() { + const listEl = document.getElementById('optimizedList'); + if (!listEl) return; + + listEl.innerHTML = ` +
+ +

Carregando...

+
+ `; + + try { + const response = await fetch('/api/optimized-pages'); + const data = await response.json(); + + if (data.success && data.pages.length > 0) { + listEl.innerHTML = data.pages.map(page => ` +
+
+
+ +
+
+

${page.name}

+

Otimizada pela IA • ${page.directory}

+
+
+
+ + +
+
+ `).join(''); + } else { + listEl.innerHTML = ` +
+ +

Nenhuma página otimizada encontrada

+

Use o Agente Designer para otimizar suas clonagens.

+
+ `; + } + } catch (e) { + listEl.innerHTML = ` +
+ +

Erro ao carregar

+

${e.message}

+
+ `; + } +} diff --git a/web-interface/public/index-new.html b/web-interface/public/index-new.html index 360706f..819792a 100644 --- a/web-interface/public/index-new.html +++ b/web-interface/public/index-new.html @@ -40,6 +40,11 @@ Storage + + + Otimizados (AI) + + Configurações @@ -150,6 +155,17 @@ required > +
+ + +
+ +
+
+

Gestão de Páginas Otimizadas pela IA

+
+ +
+
+ +
+
+ +

Carregando...

+
+
+
+
diff --git a/web-interface/server.js b/web-interface/server.js index 4f3ac19..e8f90cc 100644 --- a/web-interface/server.js +++ b/web-interface/server.js @@ -320,7 +320,7 @@ app.post('/api/wget-clone', async (req, res) => { // Smart Clone Endpoint (Local Spawn) app.post('/api/smart-clone', (req, res) => { - const { url } = req.body; + const { url, folderName } = req.body; if (!url) { return res.status(400).json({ error: 'URL is required' }); @@ -331,9 +331,12 @@ app.post('/api/smart-clone', (req, res) => { // Spawn the smart cloner process const scriptPath = path.join(__dirname, '..', 'simple-crawler', 'smart-cloner.js'); - console.log(`[SmartClone] Executing: node "${scriptPath}" --url="${url}"`); + let spawnArgs = [scriptPath, `--url=${url}`]; + if (folderName) spawnArgs.push(`--folderName=${folderName}`); - const child = spawn('node', [scriptPath, `--url=${url}`], { + console.log(`[SmartClone] Executing: node "${scriptPath}" --url="${url}" ${folderName ? `--folderName="${folderName}"` : ''}`); + + const child = spawn('node', spawnArgs, { cwd: path.join(__dirname, '..'), shell: true }); @@ -596,6 +599,32 @@ app.get('/index', (req, res) => { res.redirect('/'); }); +// List optimized pages +app.get('/api/optimized-pages', (req, res) => { + try { + const clonesDir = path.join(__dirname, '..', 'cloned-sites'); + if (!fs.existsSync(clonesDir)) { + return res.json({ success: true, pages: [] }); + } + + const items = fs.readdirSync(clonesDir); + const optimized = items + .filter(item => { + const fullPath = path.join(clonesDir, item); + return fs.statSync(fullPath).isDirectory() && item.endsWith('_designer'); + }) + .map(item => ({ + name: item, + directory: item + })); + + res.json({ success: true, pages: optimized }); + } catch (error) { + console.error('Error listing optimized pages:', error); + res.status(500).json({ success: false, error: error.message }); + } +}); + // AI Agent Route (MiniMax) app.post('/api/ai/agent', async (req, res) => { const { systemPrompt, userMessage } = req.body;