Atualização automática: 2026-05-18 20:12:35

This commit is contained in:
Hermes
2026-05-18 20:12:35 +00:00
parent 37612a7b81
commit e596342950
5 changed files with 92 additions and 23 deletions
+2 -14
View File
@@ -593,20 +593,8 @@ function saveSettings() {
// Utility Functions
function openClonedSite(directory) {
// Try to open via server endpoint
fetch('/api/health', {
method: 'GET'
}).then(response => {
if (response.ok) {
// Server is running, open the site via same origin
const siteName = directory.replace(/\\/g, '/').split('/').pop();
window.open(`/${siteName}/index.html`, '_blank');
} else {
showToast('Servidor não está respondendo.', 'error');
}
}).catch(error => {
showToast('Servidor não está respondendo.', 'error');
});
const siteName = directory.replace(/\\/g, '/').split('/').pop();
window.open(`/api/preview/${siteName}`, '_blank');
}
function openCloneFolder(directory) {
+1 -5
View File
@@ -15,12 +15,8 @@ urlInput.addEventListener('keypress', (e) => {
document.getElementById('btnOpenSite').addEventListener('click', () => {
if (clonedData && clonedData.data && clonedData.data.path) {
// Construct local URL. Server serves /<folderName>/index.html
// We know server serves static files from cloned-sites directly if configured?
// Wait, server.js serves ../cloned-sites as static.
// so path is just /FolderName/index.html
const dirName = clonedData.data.path.split('\\').pop().split('/').pop();
window.open(`/${dirName}/index.html`, '_blank');
window.open(`/api/preview/${dirName}`, '_blank');
}
});
+1 -3
View File
@@ -437,8 +437,6 @@ function openCloneFolder(directory) {
}
function openClonedSite(directory) {
// Extract folder name and open site in browser via window.open
const folderName = directory.split('/').pop();
const siteUrl = window.location.protocol + '//' + window.location.host + '/' + folderName + '/index.html';
window.open(siteUrl, '_blank');
window.open('/api/preview/' + folderName, '_blank');
}
+1 -1
View File
@@ -373,7 +373,7 @@ function confirmDeleteClone(cloneId) {
}
function openClone(cloneId) {
window.open(`/${cloneId}/index.html`, '_blank');
window.open(`/api/preview/${cloneId}`, '_blank');
}
function cloneSiteAgain(baseUrl) {
+87
View File
@@ -25,6 +25,93 @@ app.use((req, res, next) => {
app.use(cors());
app.use(express.json());
// Helper para parsear cookies
function parseCookies(cookieHeader) {
const list = {};
if (!cookieHeader) return list;
cookieHeader.split(';').forEach(cookie => {
let [name, ...rest] = cookie.split('=');
name = name?.trim();
if (!name) return;
const value = rest.join('=').trim();
if (!value) return;
list[name] = decodeURIComponent(value);
});
return list;
}
// Rota para ativar o Virtual Root Preview (define o cookie e redireciona para a raiz)
app.get('/api/preview/:folder', (req, res) => {
const folderName = req.params.folder;
res.setHeader('Set-Cookie', `clone_preview_target=${encodeURIComponent(folderName)}; Path=/; HttpOnly; SameSite=Lax`);
res.redirect('/');
});
// Rota para sair do Virtual Root Preview (limpa o cookie e volta ao dashboard)
app.get('/api/exit-preview', (req, res) => {
res.setHeader('Set-Cookie', 'clone_preview_target=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT');
res.redirect('/');
});
// Middleware de Virtual Root Proxy (serve o site clonado diretamente na raiz / quando o cookie está ativo)
app.use((req, res, next) => {
const cookies = parseCookies(req.headers.cookie);
if (cookies.clone_preview_target && !req.path.startsWith('/api') && req.path !== '/old') {
const folderName = cookies.clone_preview_target;
const folderPath = path.join(__dirname, '..', 'cloned-sites', folderName);
if (fs.existsSync(folderPath)) {
let targetFile = req.path;
if (targetFile === '/') targetFile = '/index.html';
// Se o arquivo solicitado existir dentro da pasta do clone
const fullPath = path.join(folderPath, targetFile);
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
// Se for HTML, injeta a barra de preview flutuante
if (targetFile.endsWith('.html')) {
let html = fs.readFileSync(fullPath, 'utf8');
const previewBar = `
<div id="__cloneweb_preview_bar" style="position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: #1f2937; color: white; padding: 12px 24px; border-radius: 50px; box-shadow: 0 10px 25px rgba(0,0,0,0.3); display: flex; align-items: center; gap: 16px; z-index: 2147483647; font-family: 'Inter', sans-serif; font-size: 14px;">
<span style="display: flex; align-items: center; gap: 8px;"><i class="fas fa-eye" style="color: #10b981;"></i> Live Preview: <strong style="color: #60a5fa;">${folderName.split('_')[0]}</strong></span>
<a href="/api/exit-preview" style="background: #ef4444; color: white; padding: 6px 16px; border-radius: 30px; text-decoration: none; font-weight: 600; transition: all 0.2s; box-shadow: 0 4px 12px rgba(239,68,68,0.3); font-size: 13px;">Sair do Preview</a>
</div>
`;
if (html.includes('</body>')) {
html = html.replace('</body>', previewBar + '</body>');
} else {
html += previewBar;
}
res.setHeader('Content-Type', 'text/html');
return res.send(html);
}
// Para outros arquivos (CSS, JS, imagens), serve diretamente com o MIME type correto
if (targetFile.endsWith('.css')) res.setHeader('Content-Type', 'text/css');
if (targetFile.endsWith('.js')) res.setHeader('Content-Type', 'application/javascript');
return res.sendFile(fullPath);
} else {
// Fallback de SPA: se a rota virtual não existir como arquivo no disco, serve o index.html do clone!!!
const indexPath = path.join(folderPath, 'index.html');
if (fs.existsSync(indexPath)) {
let html = fs.readFileSync(indexPath, 'utf8');
const previewBar = `
<div id="__cloneweb_preview_bar" style="position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: #1f2937; color: white; padding: 12px 24px; border-radius: 50px; box-shadow: 0 10px 25px rgba(0,0,0,0.3); display: flex; align-items: center; gap: 16px; z-index: 2147483647; font-family: 'Inter', sans-serif; font-size: 14px;">
<span style="display: flex; align-items: center; gap: 8px;"><i class="fas fa-eye" style="color: #10b981;"></i> Live Preview: <strong style="color: #60a5fa;">${folderName.split('_')[0]}</strong></span>
<a href="/api/exit-preview" style="background: #ef4444; color: white; padding: 6px 16px; border-radius: 30px; text-decoration: none; font-weight: 600; transition: all 0.2s; box-shadow: 0 4px 12px rgba(239,68,68,0.3); font-size: 13px;">Sair do Preview</a>
</div>
`;
if (html.includes('</body>')) html = html.replace('</body>', previewBar + '</body>');
else html += previewBar;
res.setHeader('Content-Type', 'text/html');
return res.send(html);
}
}
}
}
next();
});
// Serve static files with proper MIME types
app.use(express.static(path.join(__dirname, 'public'), {
setHeaders: (res, path) => {