diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..21f44c2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +# CloneWeb - Web Interface + Crawler Dockerfile +FROM node:20-alpine + +WORKDIR /app + +# Install wget for crawler + bash for scripts +RUN apk add --no-cache wget bash && ln -s /usr/bin/wget /bin/wget + +# --- Crawler service (port 3002) --- +COPY simple-crawler/package*.json /app/simple-crawler/ +WORKDIR /app/simple-crawler +RUN npm install --production + +COPY simple-crawler/ /app/simple-crawler/ + +# --- Web interface service (port 3000) --- +WORKDIR /app +COPY web-interface/package*.json /app/web-interface/ +WORKDIR /app/web-interface +RUN npm install --production + +COPY web-interface/ /app/web-interface/ + +# --- Storage dir for clones --- +RUN mkdir -p /app/cloned-sites + +# Expose port +EXPOSE 3000 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:3000/ || exit 1 + +# Start crawler in background (port 3002), then web server (port 3000) +CMD bash -c "node /app/simple-crawler/wget-cloner.js > /tmp/crawler.log 2>&1 & node server.js" \ No newline at end of file diff --git a/web-interface/public/app-new.js b/web-interface/public/app-new.js index c48c381..db695f6 100644 --- a/web-interface/public/app-new.js +++ b/web-interface/public/app-new.js @@ -100,8 +100,8 @@ async function checkServiceStatus() { const statusIndicator = document.getElementById('serviceStatus'); try { - // Verificar o serviço WGET na porta 3002 (não 3001) - const response = await fetch('http://localhost:3002/health', { + // Verificar o serviço WGET via proxy do servidor (mesma origem) + const response = await fetch('/api/health', { method: 'GET' }); @@ -200,13 +200,26 @@ function setupForms() { async function handleCloneSubmit(e) { e.preventDefault(); - const url = document.getElementById('cloneUrl').value.trim(); - const depth = parseInt(document.getElementById('depthLevel').value); - const includeImages = document.getElementById('includeImages').checked; - const includeCSS = document.getElementById('includeCSS').checked; - const includeJS = document.getElementById('includeJS').checked; - const convertLinks = document.getElementById('convertLinks').checked; - const cloneMethod = document.getElementById('cloneMethod').value; + const cloneUrlEl = document.getElementById('cloneUrl'); + const depthLevelEl = document.getElementById('depthLevel'); + const includeImagesEl = document.getElementById('includeImages'); + const includeCSSEl = document.getElementById('includeCSS'); + const includeJSEl = document.getElementById('includeJS'); + const convertLinksEl = document.getElementById('convertLinks'); + + if (!cloneUrlEl || !depthLevelEl || !includeImagesEl || !includeCSSEl || !includeJSEl || !convertLinksEl) { + console.error('Form elements not found'); + showToast('Erro: Elementos do formulário não encontrados', 'error'); + return; + } + + const url = cloneUrlEl.value.trim(); + const depth = parseInt(depthLevelEl.value); + const includeImages = includeImagesEl.checked; + const includeCSS = includeCSSEl.checked; + const includeJS = includeJSEl.checked; + const convertLinks = convertLinksEl.checked; + const cloneMethod = 'wget'; if (!url) { showToast('Por favor, insira uma URL válida', 'error'); @@ -582,18 +595,18 @@ function saveSettings() { // Utility Functions function openClonedSite(directory) { // Try to open via server endpoint - fetch('http://localhost:8080', { + fetch('/api/health', { method: 'GET' }).then(response => { if (response.ok) { - // Server is running, open the site - const siteName = directory.split('\\').pop(); - window.open(`http://localhost:8080/${siteName}/`, '_blank'); + // Server is running, open the site via same origin + const siteName = directory.split('/').pop(); + window.open(`/${siteName}/index.html`, '_blank'); } else { - showToast('Servidor HTTP não está rodando. Inicie o servidor na porta 8080.', 'error'); + showToast('Servidor não está respondendo.', 'error'); } }).catch(error => { - showToast('Servidor HTTP não está rodando. Inicie o servidor na porta 8080.', 'error'); + showToast('Servidor não está respondendo.', 'error'); }); } diff --git a/web-interface/public/index-new.html b/web-interface/public/index-new.html index 988f13d..b4611e3 100644 --- a/web-interface/public/index-new.html +++ b/web-interface/public/index-new.html @@ -5,7 +5,9 @@ CloneWeb Pro - Clonagem Profissional de Sites - + + + @@ -28,6 +30,16 @@ Histórico + + + Agentes AI + + + + + Storage + + Configurações @@ -290,6 +302,154 @@ + + + +
+
+

💾 Gestão de Storage

+

Gerencie todos os sites clonados, versões e metadata

+
+ +
+ +
+ +
+ + +
+ + + +
+ +
+
+ + + +
+
+

🤖 Agentes BrainSteel Web

+

Pipeline completo: Scout → Designer → Outreach → Orchestrator

+
+ + +
+ + + + +
+ + +
+
+

Scout - Prospecção

+

Identifica websites de empresas locais que precisam de redesign

+ +
+ + + +
+ +
Aguardando pesquisa...
+ +
+ +
+
+
+ + + + + + + + + +
+ diff --git a/web-interface/public/styles-new.css b/web-interface/public/styles-new.css index 8abdfc6..2611098 100644 --- a/web-interface/public/styles-new.css +++ b/web-interface/public/styles-new.css @@ -738,3 +738,334 @@ input:checked + .slider:before { font-size: 1.25rem; margin-bottom: 0.5rem; } + + +/* ============================================ + BRAINSTEEL WEB - AGENTS PANEL STYLES + ============================================ */ + +.agents-header { + text-align: center; + margin-bottom: 30px; +} + +.agents-header h2 { + font-size: 28px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.agents-header p { + color: var(--text-secondary); + margin-top: 8px; +} + +/* Agent Tabs */ +.agent-tabs { + display: flex; + gap: 10px; + margin-bottom: 30px; + border-bottom: 2px solid var(--border-color); + padding-bottom: 10px; +} + +.agent-tab-btn { + padding: 12px 24px; + border: none; + background: transparent; + color: var(--text-secondary); + font-size: 14px; + font-weight: 600; + cursor: pointer; + border-radius: 8px 8px 0 0; + transition: var(--transition); + display: flex; + align-items: center; + gap: 8px; +} + +.agent-tab-btn:hover { + background: var(--bg-primary); + color: var(--text-primary); +} + +.agent-tab-btn.active { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; +} + +.agent-tab-btn i { + font-size: 16px; +} + +/* Agent Cards */ +.agent-section .agent-card { + background: var(--bg-secondary); + border-radius: 16px; + padding: 30px; + box-shadow: var(--shadow-md); + margin-bottom: 20px; +} + +.agent-card h3 { + font-size: 20px; + color: var(--text-primary); + margin-bottom: 10px; + display: flex; + align-items: center; + gap: 10px; +} + +.agent-card h3 i { + color: var(--primary-color); +} + +.agent-description { + color: var(--text-secondary); + margin-bottom: 25px; + font-size: 14px; +} + +/* Agent Inputs */ +.agent-inputs { + display: flex; + gap: 10px; + margin-bottom: 20px; + flex-wrap: wrap; +} + +.agent-inputs .form-control { + flex: 1; + min-width: 200px; + padding: 12px 16px; + border: 2px solid var(--border-color); + border-radius: 10px; + font-size: 14px; + transition: var(--transition); +} + +.agent-inputs .form-control:focus { + border-color: var(--primary-color); + outline: none; +} + +.agent-inputs .btn { + padding: 12px 24px; + border-radius: 10px; + font-weight: 600; + display: flex; + align-items: center; + gap: 8px; +} + +/* Agent Status */ +.agent-status { + padding: 12px 16px; + background: var(--bg-primary); + border-radius: 8px; + font-size: 14px; + color: var(--text-secondary); + margin-bottom: 20px; + border-left: 4px solid var(--primary-color); +} + +/* Scout Results */ +.scout-results { + display: grid; + gap: 15px; + max-height: 500px; + overflow-y: auto; +} + +.scout-target-card { + background: var(--bg-primary); + border-radius: 12px; + padding: 20px; + border: 1px solid var(--border-color); + transition: var(--transition); +} + +.scout-target-card:hover { + border-color: var(--primary-color); + transform: translateY(-2px); + box-shadow: var(--shadow-md); +} + +.target-header { + display: flex; + align-items: center; + gap: 15px; + margin-bottom: 12px; +} + +.target-score { + width: 50px; + height: 50px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-weight: 700; + font-size: 16px; + color: white; +} + +.target-score.score-high { background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); } +.target-score.score-medium { background: linear-gradient(135deg, #ffa726 0%, #ff9800 100%); } +.target-score.score-low { background: linear-gradient(135deg, #718096 0%, #546e7a 100%); } + +.target-info h4 { + font-size: 16px; + color: var(--text-primary); + margin-bottom: 4px; +} + +.target-url { + font-size: 12px; + color: var(--primary-color); + text-decoration: none; +} + +.target-body { + margin-bottom: 15px; +} + +.target-reason { + font-size: 13px; + color: var(--text-secondary); + margin-bottom: 10px; +} + +.target-meta { + display: flex; + gap: 15px; + font-size: 12px; + color: var(--text-light); +} + +.target-meta i { + margin-right: 4px; +} + +.target-niche { + background: var(--bg-secondary); + padding: 2px 8px; + border-radius: 4px; +} + +.target-actions { + display: flex; + gap: 10px; +} + +.target-actions .btn { + padding: 8px 16px; + font-size: 13px; +} + +/* Design Preview */ +.design-preview { + background: var(--bg-primary); + border-radius: 12px; + padding: 15px; + margin-top: 20px; +} + +.design-preview iframe { + border-radius: 8px; + border: 2px solid var(--border-color); +} + +/* Outreach Stats */ +.outreach-stats { + display: flex; + gap: 30px; + margin-top: 20px; + padding: 15px; + background: var(--bg-primary); + border-radius: 8px; +} + +.outreach-stats span { + display: flex; + align-items: center; + gap: 8px; + font-size: 14px; +} + +.outreach-stats i { + font-size: 16px; +} + +.outreach-stats strong { + font-size: 18px; + color: var(--text-primary); +} + +/* Orchestrator */ +.orchestrator-info { + background: var(--bg-primary); + padding: 15px; + border-radius: 8px; + margin-bottom: 20px; + font-size: 14px; + color: var(--text-secondary); +} + +.orchestrator-info i { + color: var(--info-color); + margin-right: 8px; +} + +.btn-lg { + padding: 16px 32px; + font-size: 16px; +} + +.progress-bar-container { + height: 8px; + background: var(--bg-primary); + border-radius: 4px; + margin-top: 20px; + overflow: hidden; +} + +.progress-bar { + height: 100%; + background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); + border-radius: 4px; + transition: width 0.5s ease; +} + +/* Responsive */ +@media (max-width: 768px) { + .agent-tabs { + flex-wrap: wrap; + } + + .agent-tab-btn { + flex: 1; + justify-content: center; + min-width: 100px; + } + + .agent-inputs { + flex-direction: column; + } + + .agent-inputs .form-control, + .agent-inputs .btn { + width: 100%; + } + + .target-actions { + flex-direction: column; + } + + .outreach-stats { + flex-direction: column; + gap: 15px; + } +}