feat: migrate vanilla js to react vite spa
continuous-integration/webhook Falha no deploy (VPS4)

This commit is contained in:
VPS 4 Deploy Agent
2026-05-30 18:26:56 +02:00
parent 3bc1161d95
commit d6ae290e56
47 changed files with 5374 additions and 24 deletions
+441
View File
@@ -0,0 +1,441 @@
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Instalação - Dental Image Server</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.install-container {
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
max-width: 600px;
width: 100%;
padding: 40px;
}
h1 {
color: #333;
margin-bottom: 10px;
font-size: 28px;
}
.subtitle {
color: #666;
margin-bottom: 30px;
font-size: 14px;
}
.step {
margin-bottom: 25px;
}
.step-title {
font-weight: 600;
color: #667eea;
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 10px;
}
.step-number {
background: #667eea;
color: white;
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: bold;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
color: #333;
font-weight: 500;
font-size: 14px;
}
input, select {
width: 100%;
padding: 12px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 14px;
transition: border-color 0.3s;
}
input:focus, select:focus {
outline: none;
border-color: #667eea;
}
.form-help {
font-size: 12px;
color: #999;
margin-top: 5px;
}
.btn {
width: 100%;
padding: 14px;
background: #667eea;
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: background 0.3s;
margin-top: 10px;
}
.btn:hover {
background: #5568d3;
}
.btn:disabled {
background: #ccc;
cursor: not-allowed;
}
.alert {
padding: 12px;
border-radius: 8px;
margin-bottom: 20px;
font-size: 14px;
}
.alert-success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.alert-info {
background: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
.loading {
display: none;
text-align: center;
padding: 20px;
}
.spinner {
border: 3px solid #f3f3f3;
border-top: 3px solid #667eea;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 0 auto 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.success-message {
text-align: center;
padding: 20px;
display: none;
}
.success-icon {
font-size: 64px;
margin-bottom: 20px;
}
.check-status {
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
margin-bottom: 20px;
}
.status-item {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #e0e0e0;
}
.status-item:last-child {
border-bottom: none;
}
.status-ok {
color: #28a745;
font-weight: 600;
}
.status-fail {
color: #dc3545;
font-weight: 600;
}
</style>
</head>
<body>
<div class="install-container">
<h1>🦷 Instalação do Sistema</h1>
<p class="subtitle">Configure o banco de dados e crie o usuário administrador</p>
<div id="statusCheck" class="check-status">
<div class="status-item">
<span>Verificando status da instalação...</span>
<span id="statusLoading" class="spinner" style="width: 16px; height: 16px; display: inline-block;"></span>
</div>
</div>
<div id="alertContainer"></div>
<div id="installForm">
<div class="step">
<div class="step-title">
<span class="step-number">1</span>
<span>Configuração do Banco de Dados</span>
</div>
<div class="form-group">
<label>Host MySQL</label>
<input type="text" id="dbHost" value="localhost" placeholder="localhost">
</div>
<div class="form-group">
<label>Porta MySQL</label>
<input type="number" id="dbPort" value="3306" placeholder="3306">
</div>
<div class="form-group">
<label>Nome do Banco</label>
<input type="text" id="dbName" value="dental_images" placeholder="dental_images">
<div class="form-help">O banco será criado automaticamente se não existir</div>
</div>
<div class="form-group">
<label>Usuário MySQL</label>
<input type="text" id="dbUser" value="root" placeholder="root">
</div>
<div class="form-group">
<label>Senha MySQL</label>
<input type="password" id="dbPassword" placeholder="Digite a senha">
</div>
</div>
<div class="step">
<div class="step-title">
<span class="step-number">2</span>
<span>Usuário Administrador</span>
</div>
<div class="form-group">
<label>Nome de Usuário</label>
<input type="text" id="adminUsername" value="rcesar" placeholder="rcesar">
</div>
<div class="form-group">
<label>Email</label>
<input type="email" id="adminEmail" value="rcesar@rcesar.com" placeholder="admin@example.com">
</div>
<div class="form-group">
<label>Senha</label>
<input type="password" id="adminPassword" value="Rc362514" placeholder="Digite uma senha segura">
<div class="form-help">Mínimo de 8 caracteres</div>
</div>
</div>
<button id="installBtn" class="btn" onclick="runInstallation()">
▶ Iniciar Instalação
</button>
</div>
<div id="loading" class="loading">
<div class="spinner"></div>
<p>Instalando... Por favor aguarde.</p>
</div>
<div id="successMessage" class="success-message">
<div class="success-icon"></div>
<h2>Instalação Concluída!</h2>
<p>O sistema foi configurado com sucesso.</p>
<button class="btn" onclick="window.location.href='/login'" style="margin-top: 20px;">
Ir para Login
</button>
</div>
</div>
<script>
// Verificar status ao carregar
async function checkStatus() {
try {
const response = await fetch('/api/install/status');
const data = await response.json();
const statusHtml = `
<div class="status-item">
<span>Arquivo de instalação (.installed)</span>
<span class="${data.installed ? 'status-ok' : 'status-fail'}">
${data.installed ? '✓ OK' : '✗ Não instalado'}
</span>
</div>
<div class="status-item">
<span>Arquivo de configuração (.env)</span>
<span class="${data.hasEnvFile ? 'status-ok' : 'status-fail'}">
${data.hasEnvFile ? '✓ OK' : '✗ Não configurado'}
</span>
</div>
<div class="status-item">
<span>Banco de dados</span>
<span class="${data.databaseExists ? 'status-ok' : 'status-fail'}">
${data.databaseExists ? '✓ Conectado' : '✗ Não configurado'}
</span>
</div>
<div class="status-item">
<span>Usuário admin</span>
<span class="${data.userExists ? 'status-ok' : 'status-fail'}">
${data.userExists ? '✓ Existe' : '✗ Não existe'}
</span>
</div>
`;
document.getElementById('statusCheck').innerHTML = statusHtml;
if (data.installed && data.databaseExists && data.userExists) {
showAlert('Sistema já está instalado!', 'success');
document.getElementById('installForm').style.display = 'none';
document.getElementById('successMessage').style.display = 'block';
}
} catch (error) {
console.error('Erro ao verificar status:', error);
}
}
async function runInstallation() {
const btn = document.getElementById('installBtn');
const form = document.getElementById('installForm');
const loading = document.getElementById('loading');
const success = document.getElementById('successMessage');
// Coletar dados
const installData = {
db: {
host: document.getElementById('dbHost').value.trim(),
port: parseInt(document.getElementById('dbPort').value) || 3306,
name: document.getElementById('dbName').value.trim(),
user: document.getElementById('dbUser').value.trim(),
password: document.getElementById('dbPassword').value
},
admin: {
username: document.getElementById('adminUsername').value.trim(),
email: document.getElementById('adminEmail').value.trim(),
password: document.getElementById('adminPassword').value
}
};
// Validações
if (!installData.db.host || !installData.db.name || !installData.db.user) {
showAlert('Preencha todos os campos do banco de dados!', 'error');
return;
}
if (!installData.admin.username || !installData.admin.email || !installData.admin.password) {
showAlert('Preencha todos os campos do administrador!', 'error');
return;
}
if (installData.admin.password.length < 8) {
showAlert('A senha deve ter no mínimo 8 caracteres!', 'error');
return;
}
if (!installData.admin.email.includes('@')) {
showAlert('Email inválido!', 'error');
return;
}
// Desabilitar botão
btn.disabled = true;
btn.textContent = 'Instalando...';
// Mostrar loading
form.style.display = 'none';
loading.style.display = 'block';
try {
const response = await fetch('/api/install', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(installData)
});
const result = await response.json();
if (result.success) {
loading.style.display = 'none';
success.style.display = 'block';
showAlert('Instalação concluída com sucesso! Você será redirecionado...', 'success');
// Aguardar e redirecionar
setTimeout(() => {
window.location.href = '/login';
}, 2000);
} else {
loading.style.display = 'none';
form.style.display = 'block';
btn.disabled = false;
btn.textContent = '▶ Iniciar Instalação';
showAlert(result.error || 'Erro na instalação. Verifique os dados e tente novamente.', 'error');
}
} catch (error) {
loading.style.display = 'none';
form.style.display = 'block';
btn.disabled = false;
btn.textContent = '▶ Iniciar Instalação';
showAlert('Erro ao conectar com o servidor: ' + error.message, 'error');
}
}
function showAlert(message, type) {
const container = document.getElementById('alertContainer');
container.innerHTML = `<div class="alert alert-${type}">${message}</div>`;
setTimeout(() => {
container.innerHTML = '';
}, 5000);
}
// Iniciar verificação
checkStatus();
</script>
</body>
</html>