Atualização automática: 2026-05-23 22:02:28

This commit is contained in:
Hermes
2026-05-23 22:02:28 +00:00
parent 7cfc1724d3
commit bfa0ab4394
6 changed files with 152 additions and 14 deletions
+6 -3
View File
@@ -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(`\n🚀 INICIANDO DEEP SMART CLONE: ${url}`);
console.log(`📂 Limite de páginas: ${maxPages}\n`); console.log(`📂 Limite de páginas: ${maxPages}\n`);
@@ -82,7 +82,8 @@ class SmartCloner {
const urlObj = new URL(url); const urlObj = new URL(url);
const siteName = this.sanitizeFilename(urlObj.hostname); const siteName = this.sanitizeFilename(urlObj.hostname);
const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); 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); this.ensureDirectoryExists(siteDir);
browser = await chromium.launch({ browser = await chromium.launch({
@@ -368,10 +369,12 @@ if (require.main === module) {
const args = process.argv.slice(2); const args = process.argv.slice(2);
const urlArg = args.find(arg => arg.startsWith('--url=')); const urlArg = args.find(arg => arg.startsWith('--url='));
const url = urlArg ? urlArg.split('=')[1] : args[0]; 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) { if (url) {
const cloner = new SmartCloner(); const cloner = new SmartCloner();
cloner.cloneSite(url).then(res => { cloner.cloneSite(url, 20, folderName).then(res => {
console.log(JSON.stringify(res, null, 2)); console.log(JSON.stringify(res, null, 2));
process.exit(res.success ? 0 : 1); process.exit(res.success ? 0 : 1);
}); });
+5 -4
View File
@@ -65,7 +65,7 @@ class WgetCloner {
} }
} }
async cloneWebsite(url) { async cloneWebsite(url, customFolderName = null) {
console.log(`\n${'='.repeat(60)}`); console.log(`\n${'='.repeat(60)}`);
console.log(`🚀 CLONAGEM PROFISSIONAL COM WGET`); console.log(`🚀 CLONAGEM PROFISSIONAL COM WGET`);
console.log(`${'='.repeat(60)}`); console.log(`${'='.repeat(60)}`);
@@ -98,7 +98,8 @@ class WgetCloner {
const urlObj = new URL(url); const urlObj = new URL(url);
const siteName = this.sanitizeFilename(urlObj.hostname); const siteName = this.sanitizeFilename(urlObj.hostname);
const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); 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); this.ensureDirectoryExists(siteDir);
console.log(`📁 Salvando em: ${siteDir}\n`); console.log(`📁 Salvando em: ${siteDir}\n`);
@@ -328,13 +329,13 @@ class WgetCloner {
// API // API
app.post('/wget-clone', async (req, res) => { app.post('/wget-clone', async (req, res) => {
try { try {
const { url } = req.body; const { url, folderName } = req.body;
if (!url) { if (!url) {
return res.status(400).json({ error: 'URL obrigatória' }); return res.status(400).json({ error: 'URL obrigatória' });
} }
const cloner = new WgetCloner(); const cloner = new WgetCloner();
const result = await cloner.cloneWebsite(url); const result = await cloner.cloneWebsite(url, folderName);
res.json({ success: true, data: result }); res.json({ success: true, data: result });
} catch (error) { } catch (error) {
+5 -1
View File
@@ -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. # 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." 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!"
+69 -3
View File
@@ -59,7 +59,8 @@ function navigateToPage(page) {
'dashboard': 'Dashboard', 'dashboard': 'Dashboard',
'new-clone': 'Nova Clonagem', 'new-clone': 'Nova Clonagem',
'history': 'Histórico', 'history': 'Histórico',
'settings': 'Configurações' 'settings': 'Configurações',
'optimized-pages': 'Páginas Otimizadas (AI)'
}; };
document.getElementById('pageTitle').textContent = titles[page] || page; document.getElementById('pageTitle').textContent = titles[page] || page;
@@ -70,6 +71,8 @@ function navigateToPage(page) {
loadDashboard(); loadDashboard();
} else if (page === 'history') { } else if (page === 'history') {
loadHistory(); loadHistory();
} else if (page === 'optimized-pages') {
loadOptimizedPages();
} }
} }
@@ -206,6 +209,7 @@ async function handleCloneSubmit(e) {
const includeCSSEl = document.getElementById('includeCSS'); const includeCSSEl = document.getElementById('includeCSS');
const includeJSEl = document.getElementById('includeJS'); const includeJSEl = document.getElementById('includeJS');
const convertLinksEl = document.getElementById('convertLinks'); const convertLinksEl = document.getElementById('convertLinks');
const customFolderEl = document.getElementById('customFolder');
if (!cloneUrlEl || !depthLevelEl || !includeImagesEl || !includeCSSEl || !includeJSEl || !convertLinksEl) { if (!cloneUrlEl || !depthLevelEl || !includeImagesEl || !includeCSSEl || !includeJSEl || !convertLinksEl) {
console.error('Form elements not found'); console.error('Form elements not found');
@@ -219,6 +223,7 @@ async function handleCloneSubmit(e) {
const includeCSS = includeCSSEl.checked; const includeCSS = includeCSSEl.checked;
const includeJS = includeJSEl.checked; const includeJS = includeJSEl.checked;
const convertLinks = convertLinksEl.checked; const convertLinks = convertLinksEl.checked;
const folderName = customFolderEl ? customFolderEl.value.trim() : '';
const cloneMethod = 'wget'; const cloneMethod = 'wget';
if (!url) { if (!url) {
@@ -240,6 +245,7 @@ async function handleCloneSubmit(e) {
includeCSS, includeCSS,
includeJS, includeJS,
convertLinks, convertLinks,
folderName,
method: cloneMethod method: cloneMethod
}); });
} }
@@ -306,7 +312,8 @@ async function startCloning(url, options) {
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
url, url,
outputDir: settings.outputDir outputDir: settings.outputDir,
folderName: options.folderName
}) })
}); });
} else { } else {
@@ -317,7 +324,8 @@ async function startCloning(url, options) {
body: JSON.stringify({ body: JSON.stringify({
url, url,
depth: options.depth, 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' : `${minutes} minutos`; if (minutes > 0) return minutes === 1 ? 'Há 1 minuto' : `${minutes} minutos`;
return 'Agora mesmo'; return 'Agora mesmo';
} }
// Optimized Pages
async function loadOptimizedPages() {
const listEl = document.getElementById('optimizedList');
if (!listEl) return;
listEl.innerHTML = `
<div class="empty-state">
<i class="fas fa-spinner fa-spin"></i>
<h3>Carregando...</h3>
</div>
`;
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 => `
<div class="clone-item">
<div class="clone-info">
<div class="clone-icon" style="background: linear-gradient(135deg, #ec4899 0%, #8b5cf6 100%);">
<i class="fas fa-magic" style="color: white;"></i>
</div>
<div class="clone-details">
<h4>${page.name}</h4>
<p>Otimizada pela IA • ${page.directory}</p>
</div>
</div>
<div class="clone-actions">
<button class="btn btn-primary btn-sm" onclick="openClonedSite('${page.directory}')">
<i class="fas fa-external-link-alt"></i> Visualizar
</button>
<button class="btn btn-secondary btn-sm" onclick="openCloneFolder('${page.directory}')">
<i class="fas fa-folder-open"></i> Pasta
</button>
</div>
</div>
`).join('');
} else {
listEl.innerHTML = `
<div class="empty-state">
<i class="fas fa-paint-brush"></i>
<h3>Nenhuma página otimizada encontrada</h3>
<p>Use o Agente Designer para otimizar suas clonagens.</p>
</div>
`;
}
} catch (e) {
listEl.innerHTML = `
<div class="empty-state">
<i class="fas fa-exclamation-triangle"></i>
<h3>Erro ao carregar</h3>
<p>${e.message}</p>
</div>
`;
}
}
+35
View File
@@ -40,6 +40,11 @@
<span>Storage</span> <span>Storage</span>
</a> </a>
<a href="#" class="nav-item" data-page="optimized-pages">
<i class="fas fa-paint-brush"></i>
<span>Otimizados (AI)</span>
</a>
<a href="#" class="nav-item" data-page="settings"> <a href="#" class="nav-item" data-page="settings">
<i class="fas fa-cog"></i> <i class="fas fa-cog"></i>
<span>Configurações</span> <span>Configurações</span>
@@ -150,6 +155,17 @@
required required
> >
</div> </div>
<div class="form-group">
<label for="customFolder">
<i class="fas fa-folder"></i> Nome Específico da Pasta (Opcional)
</label>
<input
type="text"
id="customFolder"
class="form-control"
placeholder="ex: meu_projeto"
>
</div>
<div class="form-group"> <div class="form-group">
<label> <label>
@@ -238,6 +254,25 @@
</div> </div>
</div> </div>
<!-- Optimized Pages Page -->
<div class="page" id="optimized-pages">
<div class="section-header">
<h3><i class="fas fa-paint-brush"></i> Gestão de Páginas Otimizadas pela IA</h3>
<div class="header-actions">
<button class="btn btn-secondary btn-sm" onclick="loadOptimizedPages()">
<i class="fas fa-sync-alt"></i> Atualizar
</button>
</div>
</div>
<div class="clones-list" id="optimizedList">
<div class="empty-state">
<i class="fas fa-spinner fa-spin"></i>
<h3>Carregando...</h3>
</div>
</div>
</div>
<!-- Settings Page --> <!-- Settings Page -->
<div class="page" id="settings"> <div class="page" id="settings">
<div class="settings-container"> <div class="settings-container">
+32 -3
View File
@@ -320,7 +320,7 @@ app.post('/api/wget-clone', async (req, res) => {
// Smart Clone Endpoint (Local Spawn) // Smart Clone Endpoint (Local Spawn)
app.post('/api/smart-clone', (req, res) => { app.post('/api/smart-clone', (req, res) => {
const { url } = req.body; const { url, folderName } = req.body;
if (!url) { if (!url) {
return res.status(400).json({ error: 'URL is required' }); 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 // Spawn the smart cloner process
const scriptPath = path.join(__dirname, '..', 'simple-crawler', 'smart-cloner.js'); 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, '..'), cwd: path.join(__dirname, '..'),
shell: true shell: true
}); });
@@ -596,6 +599,32 @@ app.get('/index', (req, res) => {
res.redirect('/'); 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) // AI Agent Route (MiniMax)
app.post('/api/ai/agent', async (req, res) => { app.post('/api/ai/agent', async (req, res) => {
const { systemPrompt, userMessage } = req.body; const { systemPrompt, userMessage } = req.body;