diff --git a/web-interface/public/app-new.js b/web-interface/public/app-new.js index b6fe0c5..dccf2c3 100644 --- a/web-interface/public/app-new.js +++ b/web-interface/public/app-new.js @@ -610,14 +610,18 @@ function openClonedSite(directory) { } function openCloneFolder(directory) { - showToast('Abrindo pasta no sistema da VPS: ' + directory, 'info'); + showToast('Abrindo gerenciador de arquivos web...', 'info'); fetch('/api/open-folder', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ path: directory }) }).then(res => res.json()) .then(data => { - if (!data.success) showToast('Erro ao abrir pasta: ' + (data.error || ''), 'error'); + if (!data.success) { + showToast('Erro ao abrir pasta: ' + (data.error || ''), 'error'); + } else if (data.url) { + window.open(data.url, '_blank'); + } }) .catch(err => console.error('Erro ao chamar open-folder:', err)); } diff --git a/web-interface/public/app-premium.js b/web-interface/public/app-premium.js index 37c5f8f..46379dc 100644 --- a/web-interface/public/app-premium.js +++ b/web-interface/public/app-premium.js @@ -30,7 +30,12 @@ document.getElementById('btnOpenFolder').addEventListener('click', () => { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ path: clonedData.data.path }) - }); + }).then(res => res.json()) + .then(data => { + if (data.success && data.url) { + window.open(data.url, '_blank'); + } + }); } }); diff --git a/web-interface/public/app.js b/web-interface/public/app.js index bb0c2ff..64bb8ea 100644 --- a/web-interface/public/app.js +++ b/web-interface/public/app.js @@ -418,20 +418,22 @@ document.addEventListener('DOMContentLoaded', function() { // Funções para gerenciar sites clonados function openCloneFolder(directory) { - // Tentar abrir a pasta no explorador do Windows fetch('/api/open-folder', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ path: directory }) - }).then(response => { - if (!response.ok) { - alert('Não foi possível abrir a pasta automaticamente.\nLocalização: ' + directory); - } - }).catch(error => { - alert('Não foi possível abrir a pasta automaticamente.\nLocalização: ' + directory); - }); + }).then(response => response.json()) + .then(data => { + if (data.success && data.url) { + window.open(data.url, '_blank'); + } else { + alert('Não foi possível abrir a pasta.\nLocalização: ' + directory); + } + }).catch(error => { + alert('Não foi possível abrir a pasta.\nLocalização: ' + directory); + }); } function openClonedSite(directory) { diff --git a/web-interface/server.js b/web-interface/server.js index 21ffa26..fc6b51f 100644 --- a/web-interface/server.js +++ b/web-interface/server.js @@ -38,6 +38,11 @@ app.use(express.static(path.join(__dirname, 'public'), { app.use((req, res, next) => { if (!req.path.startsWith('/api') && req.path !== '/' && req.path !== '/old') { let urlPath = req.path; + // Remover prefixo /cloned-sites se existir para buscar corretamente no disco + if (urlPath.startsWith('/cloned-sites/')) { + urlPath = urlPath.replace('/cloned-sites/', '/'); + } + if (!urlPath.match(/\.[a-zA-Z0-9]+$/)) { if (!urlPath.endsWith('/')) urlPath += '/'; urlPath += 'index.html'; @@ -54,10 +59,12 @@ app.use((req, res, next) => { (function() { try { const originalPathname = Object.getOwnPropertyDescriptor(Location.prototype, 'pathname').get; + const originalHref = Object.getOwnPropertyDescriptor(Location.prototype, 'href').get; + Object.defineProperty(Location.prototype, 'pathname', { get: function() { const path = originalPathname.call(this); - if (path !== '/' && path !== '/index.html') { + if (path !== '/' && path !== '/index.html' && (path.includes('_202') || path.includes('/cloned-sites/'))) { return '/'; } return path; @@ -66,7 +73,29 @@ app.use((req, res, next) => { window.location.assign(val); } }); - console.log('🚀 [CloneWeb] SPA Router fix initialized. Simulating root pathname /'); + + Object.defineProperty(Location.prototype, 'href', { + get: function() { + const href = originalHref.call(this); + if (href.includes('_202') || href.includes('/cloned-sites/')) { + const url = new URL(href); + return url.protocol + '//' + url.host + '/'; + } + return href; + } + }); + + const OriginalURL = window.URL; + window.URL = function(url, base) { + if (typeof url === 'string' && (url.includes('_202') || url.includes('/cloned-sites/'))) { + url = url.replace(/\\/cloned-sites\\/[^/]+/gi, '').replace(/\\/[^/]+_202[0-9-_T:.]+(\\/index\\.html)?/gi, '/'); + } + return new OriginalURL(url, base); + }; + window.URL.prototype = OriginalURL.prototype; + Object.assign(window.URL, OriginalURL); + + console.log('🚀 [CloneWeb] Ultimate SPA Router fix initialized. Simulating root pathname /'); } catch(e) { console.error('❌ [CloneWeb] Failed to initialize router fix:', e); } @@ -257,7 +286,102 @@ app.get('/api/health', async (req, res) => { } }); -// Abrir pasta do clone +// Rota de visualização de arquivos da pasta clonada (Gerenciador de Arquivos Web Embutido) +app.get('/api/browse/:folder', (req, res) => { + try { + const folderName = req.params.folder; + const folderPath = path.join(__dirname, '..', 'cloned-sites', folderName); + + if (!fs.existsSync(folderPath)) { + return res.status(404).send('
A pasta solicitada não existe no servidor.
'); + } + + const items = fs.readdirSync(folderPath); + let filesHtml = ''; + + items.forEach(item => { + const itemPath = path.join(folderPath, item); + const stat = fs.statSync(itemPath); + const isDir = stat.isDirectory(); + const size = isDir ? '-' : (stat.size / 1024).toFixed(2) + ' KB'; + const date = new Date(stat.mtime).toLocaleString('pt-BR'); + const icon = isDir ? 'fa-folder' : (item.endsWith('.html') ? 'fa-file-code' : (item.endsWith('.json') ? 'fa-file-alt' : 'fa-file')); + const link = isDir ? `#` : `/${folderName}/${item}`; + + filesHtml += ` +| Nome do Ficheiro | +Tamanho | +Modificado em | +Ações | +
|---|