Atualização automática: 2026-05-18 17:10:08
This commit is contained in:
@@ -610,14 +610,18 @@ function openClonedSite(directory) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openCloneFolder(directory) {
|
function openCloneFolder(directory) {
|
||||||
showToast('Abrindo pasta no sistema da VPS: ' + directory, 'info');
|
showToast('Abrindo gerenciador de arquivos web...', 'info');
|
||||||
fetch('/api/open-folder', {
|
fetch('/api/open-folder', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ path: directory })
|
body: JSON.stringify({ path: directory })
|
||||||
}).then(res => res.json())
|
}).then(res => res.json())
|
||||||
.then(data => {
|
.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));
|
.catch(err => console.error('Erro ao chamar open-folder:', err));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,12 @@ document.getElementById('btnOpenFolder').addEventListener('click', () => {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ path: clonedData.data.path })
|
body: JSON.stringify({ path: clonedData.data.path })
|
||||||
});
|
}).then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success && data.url) {
|
||||||
|
window.open(data.url, '_blank');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -418,20 +418,22 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
// Funções para gerenciar sites clonados
|
// Funções para gerenciar sites clonados
|
||||||
function openCloneFolder(directory) {
|
function openCloneFolder(directory) {
|
||||||
// Tentar abrir a pasta no explorador do Windows
|
|
||||||
fetch('/api/open-folder', {
|
fetch('/api/open-folder', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ path: directory })
|
body: JSON.stringify({ path: directory })
|
||||||
}).then(response => {
|
}).then(response => response.json())
|
||||||
if (!response.ok) {
|
.then(data => {
|
||||||
alert('Não foi possível abrir a pasta automaticamente.\nLocalização: ' + directory);
|
if (data.success && data.url) {
|
||||||
}
|
window.open(data.url, '_blank');
|
||||||
}).catch(error => {
|
} else {
|
||||||
alert('Não foi possível abrir a pasta automaticamente.\nLocalização: ' + directory);
|
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) {
|
function openClonedSite(directory) {
|
||||||
|
|||||||
+133
-23
@@ -38,6 +38,11 @@ app.use(express.static(path.join(__dirname, 'public'), {
|
|||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
if (!req.path.startsWith('/api') && req.path !== '/' && req.path !== '/old') {
|
if (!req.path.startsWith('/api') && req.path !== '/' && req.path !== '/old') {
|
||||||
let urlPath = req.path;
|
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.match(/\.[a-zA-Z0-9]+$/)) {
|
||||||
if (!urlPath.endsWith('/')) urlPath += '/';
|
if (!urlPath.endsWith('/')) urlPath += '/';
|
||||||
urlPath += 'index.html';
|
urlPath += 'index.html';
|
||||||
@@ -54,10 +59,12 @@ app.use((req, res, next) => {
|
|||||||
(function() {
|
(function() {
|
||||||
try {
|
try {
|
||||||
const originalPathname = Object.getOwnPropertyDescriptor(Location.prototype, 'pathname').get;
|
const originalPathname = Object.getOwnPropertyDescriptor(Location.prototype, 'pathname').get;
|
||||||
|
const originalHref = Object.getOwnPropertyDescriptor(Location.prototype, 'href').get;
|
||||||
|
|
||||||
Object.defineProperty(Location.prototype, 'pathname', {
|
Object.defineProperty(Location.prototype, 'pathname', {
|
||||||
get: function() {
|
get: function() {
|
||||||
const path = originalPathname.call(this);
|
const path = originalPathname.call(this);
|
||||||
if (path !== '/' && path !== '/index.html') {
|
if (path !== '/' && path !== '/index.html' && (path.includes('_202') || path.includes('/cloned-sites/'))) {
|
||||||
return '/';
|
return '/';
|
||||||
}
|
}
|
||||||
return path;
|
return path;
|
||||||
@@ -66,7 +73,29 @@ app.use((req, res, next) => {
|
|||||||
window.location.assign(val);
|
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) {
|
} catch(e) {
|
||||||
console.error('❌ [CloneWeb] Failed to initialize router fix:', 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('<h1>Pasta não encontrada</h1><p>A pasta solicitada não existe no servidor.</p>');
|
||||||
|
}
|
||||||
|
|
||||||
|
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 += `
|
||||||
|
<tr>
|
||||||
|
<td><i class="fas ${icon}" style="color: #4f46e5; margin-right: 10px;"></i> <a href="${link}" target="_blank" style="color: #1f2937; text-decoration: none; font-weight: 500;">${item}</a></td>
|
||||||
|
<td style="color: #6b7280;">${size}</td>
|
||||||
|
<td style="color: #6b7280;">${date}</td>
|
||||||
|
<td>
|
||||||
|
${!isDir ? `<a href="${link}" target="_blank" class="btn btn-secondary btn-sm" style="background: #e0e7ff; color: #4f46e5; padding: 5px 10px; border-radius: 6px; text-decoration: none; font-size: 14px;">Visualizar</a>` : ''}
|
||||||
|
${!isDir ? `<a href="${link}" download="${item}" class="btn btn-secondary btn-sm" style="background: #fee2e2; color: #ef4444; padding: 5px 10px; border-radius: 6px; text-decoration: none; font-size: 14px; margin-left: 5px;">Baixar</a>` : ''}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const html = `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="pt-BR">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Arquivos Clonados - ${folderName}</title>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body { font-family: 'Inter', sans-serif; background: #f3f4f6; margin: 0; padding: 40px 20px; color: #1f2937; }
|
||||||
|
.container { max-width: 1000px; margin: 0 auto; background: white; border-radius: 16px; box-shadow: 0 10px 25px rgba(0,0,0,0.05); overflow: hidden; padding: 30px; }
|
||||||
|
.header { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #e5e7eb; padding-bottom: 20px; margin-bottom: 25px; }
|
||||||
|
.header h1 { font-size: 24px; margin: 0; color: #111827; display: flex; align-items: center; gap: 12px; }
|
||||||
|
.actions { display: flex; gap: 12px; }
|
||||||
|
.btn { padding: 10px 20px; border-radius: 8px; font-weight: 600; text-decoration: none; display: inline-flex; align-items: center; gap: 8px; font-size: 14px; transition: all 0.2s; }
|
||||||
|
.btn-primary { background: #4f46e5; color: white; }
|
||||||
|
.btn-primary:hover { background: #4338ca; }
|
||||||
|
.btn-secondary { background: #f3f4f6; color: #374151; }
|
||||||
|
.btn-secondary:hover { background: #e5e7eb; }
|
||||||
|
table { width: 100%; border-collapse: collapse; text-align: left; }
|
||||||
|
th { padding: 16px; background: #f9fafb; font-weight: 600; color: #374151; border-bottom: 1px solid #e5e7eb; font-size: 14px; }
|
||||||
|
td { padding: 16px; border-bottom: 1px solid #e5e7eb; font-size: 15px; }
|
||||||
|
tr:hover { background: #f9fafb; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h1><i class="fas fa-folder-open" style="color: #4f46e5;"></i> ${folderName}</h1>
|
||||||
|
<div class="actions">
|
||||||
|
<a href="/${folderName}/index.html" target="_blank" class="btn btn-primary"><i class="fas fa-external-link-alt"></i> Abrir Site Clonado</a>
|
||||||
|
<a href="/" class="btn btn-secondary"><i class="fas fa-arrow-left"></i> Voltar ao Dashboard</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Nome do Ficheiro</th>
|
||||||
|
<th>Tamanho</th>
|
||||||
|
<th>Modificado em</th>
|
||||||
|
<th>Ações</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
${filesHtml}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`;
|
||||||
|
|
||||||
|
res.send(html);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erro ao listar pasta:', error);
|
||||||
|
res.status(500).send('<h1>Erro interno do servidor</h1>');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Abrir pasta do clone (mantém compatibilidade legada e retorna a URL do Web Browse)
|
||||||
app.post('/api/open-folder', (req, res) => {
|
app.post('/api/open-folder', (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { path: folderPath } = req.body;
|
const { path: folderPath } = req.body;
|
||||||
@@ -265,14 +389,10 @@ app.post('/api/open-folder', (req, res) => {
|
|||||||
return res.status(400).json({ error: 'Caminho da pasta é obrigatório' });
|
return res.status(400).json({ error: 'Caminho da pasta é obrigatório' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Abrir pasta no gerenciador de ficheiros
|
const folderName = path.basename(folderPath);
|
||||||
exec(`xdg-open "${folderPath}"`, (error) => {
|
const browseUrl = `/api/browse/${folderName}`;
|
||||||
if (error) {
|
|
||||||
console.error('Erro ao abrir pasta:', error);
|
res.json({ success: true, message: 'Pasta acessível via Web', url: browseUrl });
|
||||||
return res.status(500).json({ error: 'Não foi possível abrir a pasta' });
|
|
||||||
}
|
|
||||||
res.json({ success: true, message: 'Pasta aberta com sucesso' });
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erro ao abrir pasta:', error);
|
console.error('Erro ao abrir pasta:', error);
|
||||||
res.status(500).json({ error: 'Erro interno do servidor' });
|
res.status(500).json({ error: 'Erro interno do servidor' });
|
||||||
@@ -287,20 +407,10 @@ app.post('/api/open-site', (req, res) => {
|
|||||||
return res.status(400).json({ error: 'Caminho do site é obrigatório' });
|
return res.status(400).json({ error: 'Caminho do site é obrigatório' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extrair apenas o nome da pasta (último segmento do caminho)
|
|
||||||
const folderName = path.basename(sitePath);
|
const folderName = path.basename(sitePath);
|
||||||
const siteUrl = `http://localhost:${PORT}/${folderName}/index.html`;
|
const siteUrl = `/${folderName}/index.html`;
|
||||||
|
|
||||||
console.log(`🌍 Opening site via HTTP: ${siteUrl}`);
|
res.json({ success: true, message: 'Site aberto com sucesso', url: siteUrl });
|
||||||
|
|
||||||
// Abrir URL no navegador padrão (Linux)
|
|
||||||
exec(`xdg-open "${siteUrl}"`, (error) => {
|
|
||||||
if (error) {
|
|
||||||
console.error('Erro ao abrir site:', error);
|
|
||||||
return res.status(500).json({ error: 'Não foi possível abrir o site via HTTP' });
|
|
||||||
}
|
|
||||||
res.json({ success: true, message: 'Site aberto via HTTP com sucesso', url: siteUrl });
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erro ao abrir site:', error);
|
console.error('Erro ao abrir site:', error);
|
||||||
res.status(500).json({ error: 'Erro interno do servidor' });
|
res.status(500).json({ error: 'Erro interno do servidor' });
|
||||||
|
|||||||
Reference in New Issue
Block a user