Atualização automática: 2026-05-23 22:02:28
This commit is contained in:
@@ -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);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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!"
|
||||||
|
|||||||
@@ -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' : `Há ${minutes} minutos`;
|
if (minutes > 0) return minutes === 1 ? 'Há 1 minuto' : `Há ${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>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user