Atualização automática: 2026-05-18 20:12:35
This commit is contained in:
@@ -593,20 +593,8 @@ function saveSettings() {
|
|||||||
|
|
||||||
// Utility Functions
|
// Utility Functions
|
||||||
function openClonedSite(directory) {
|
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();
|
const siteName = directory.replace(/\\/g, '/').split('/').pop();
|
||||||
window.open(`/${siteName}/index.html`, '_blank');
|
window.open(`/api/preview/${siteName}`, '_blank');
|
||||||
} else {
|
|
||||||
showToast('Servidor não está respondendo.', 'error');
|
|
||||||
}
|
|
||||||
}).catch(error => {
|
|
||||||
showToast('Servidor não está respondendo.', 'error');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function openCloneFolder(directory) {
|
function openCloneFolder(directory) {
|
||||||
|
|||||||
@@ -15,12 +15,8 @@ urlInput.addEventListener('keypress', (e) => {
|
|||||||
|
|
||||||
document.getElementById('btnOpenSite').addEventListener('click', () => {
|
document.getElementById('btnOpenSite').addEventListener('click', () => {
|
||||||
if (clonedData && clonedData.data && clonedData.data.path) {
|
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();
|
const dirName = clonedData.data.path.split('\\').pop().split('/').pop();
|
||||||
window.open(`/${dirName}/index.html`, '_blank');
|
window.open(`/api/preview/${dirName}`, '_blank');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -437,8 +437,6 @@ function openCloneFolder(directory) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openClonedSite(directory) {
|
function openClonedSite(directory) {
|
||||||
// Extract folder name and open site in browser via window.open
|
|
||||||
const folderName = directory.split('/').pop();
|
const folderName = directory.split('/').pop();
|
||||||
const siteUrl = window.location.protocol + '//' + window.location.host + '/' + folderName + '/index.html';
|
window.open('/api/preview/' + folderName, '_blank');
|
||||||
window.open(siteUrl, '_blank');
|
|
||||||
}
|
}
|
||||||
@@ -373,7 +373,7 @@ function confirmDeleteClone(cloneId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openClone(cloneId) {
|
function openClone(cloneId) {
|
||||||
window.open(`/${cloneId}/index.html`, '_blank');
|
window.open(`/api/preview/${cloneId}`, '_blank');
|
||||||
}
|
}
|
||||||
|
|
||||||
function cloneSiteAgain(baseUrl) {
|
function cloneSiteAgain(baseUrl) {
|
||||||
|
|||||||
@@ -25,6 +25,93 @@ app.use((req, res, next) => {
|
|||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.use(express.json());
|
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
|
// Serve static files with proper MIME types
|
||||||
app.use(express.static(path.join(__dirname, 'public'), {
|
app.use(express.static(path.join(__dirname, 'public'), {
|
||||||
setHeaders: (res, path) => {
|
setHeaders: (res, path) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user