improved launch process using cloudflare

This commit is contained in:
2025-06-20 19:34:37 +02:00
parent bb139a2b95
commit e85d91d1f4
9 changed files with 878 additions and 69 deletions

View File

@@ -16,6 +16,30 @@ document.addEventListener('DOMContentLoaded', function() {
function initializeSteps() {
const stepsContainer = document.getElementById('stepsContainer');
// Add Cloudflare connection check step
const cloudflareStep = document.createElement('div');
cloudflareStep.className = 'step-item';
cloudflareStep.innerHTML = `
<div class="step-icon"><i class="fas fa-cloud"></i></div>
<div class="step-content">
<h5>Checking Cloudflare Connection</h5>
<p class="step-status">Verifying Cloudflare API connection...</p>
</div>
`;
stepsContainer.appendChild(cloudflareStep);
// Add DNS record creation step
const dnsCreateStep = document.createElement('div');
dnsCreateStep.className = 'step-item';
dnsCreateStep.innerHTML = `
<div class="step-icon"><i class="fas fa-plus-circle"></i></div>
<div class="step-content">
<h5>Creating DNS Records</h5>
<p class="step-status">Setting up domain DNS records in Cloudflare...</p>
</div>
`;
stepsContainer.appendChild(dnsCreateStep);
// Add DNS check step
const dnsStep = document.createElement('div');
dnsStep.className = 'step-item';
@@ -199,8 +223,72 @@ function initializeSteps() {
async function startLaunch(data) {
try {
// Step 1: Check DNS records
await updateStep(1, 'Checking DNS Records', 'Verifying domain configurations...');
// Step 1: Check Cloudflare connection
await updateStep(1, 'Checking Cloudflare Connection', 'Verifying Cloudflare API connection...');
const cloudflareResult = await checkCloudflareConnection();
if (!cloudflareResult.success) {
throw new Error(cloudflareResult.error || 'Failed to connect to Cloudflare');
}
// Update the step to show success
const cloudflareStep = document.querySelectorAll('.step-item')[0];
cloudflareStep.classList.remove('active');
cloudflareStep.classList.add('completed');
cloudflareStep.querySelector('.step-status').textContent = `Successfully connected to Cloudflare (${cloudflareResult.zone_name})`;
// Step 2: Create DNS records
await updateStep(2, 'Creating DNS Records', 'Setting up domain DNS records in Cloudflare...');
const dnsCreateResult = await createDNSRecords(data.webAddresses);
if (!dnsCreateResult.success) {
throw new Error(dnsCreateResult.error || 'Failed to create DNS records');
}
// Update the step to show success
const dnsCreateStep = document.querySelectorAll('.step-item')[1];
dnsCreateStep.classList.remove('active');
dnsCreateStep.classList.add('completed');
dnsCreateStep.querySelector('.step-status').textContent = 'DNS records created successfully';
// Add DNS creation details
const dnsCreateDetails = document.createElement('div');
dnsCreateDetails.className = 'mt-3';
dnsCreateDetails.innerHTML = `
<div class="card">
<div class="card-body">
<h6 class="card-title mb-3">DNS Record Creation Results</h6>
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th>Domain</th>
<th>Status</th>
<th>Message</th>
</tr>
</thead>
<tbody>
${Object.entries(dnsCreateResult.results).map(([domain, result]) => `
<tr>
<td>${domain}</td>
<td>
<span class="badge bg-${result.status === 'created' || result.status === 'updated' ? 'success' : 'danger'}">
${result.status}
</span>
</td>
<td>${result.message}</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
</div>
</div>
`;
dnsCreateStep.querySelector('.step-content').appendChild(dnsCreateDetails);
// Step 3: Check DNS records
await updateStep(3, 'Checking DNS Records', 'Verifying domain configurations...');
const dnsResult = await checkDNSRecords(data.webAddresses);
// Check if any domains failed to resolve
@@ -213,7 +301,7 @@ async function startLaunch(data) {
}
// Update the step to show success
const dnsStep = document.querySelectorAll('.step-item')[0];
const dnsStep = document.querySelectorAll('.step-item')[2];
dnsStep.classList.remove('active');
dnsStep.classList.add('completed');
@@ -259,8 +347,8 @@ async function startLaunch(data) {
statusText.textContent = 'DNS records verified successfully';
statusText.after(detailsSection);
// Step 2: Check NGINX connection
await updateStep(2, 'Checking NGINX Connection', 'Verifying connection to NGINX Proxy Manager...');
// Step 4: Check NGINX connection
await updateStep(4, 'Checking NGINX Connection', 'Verifying connection to NGINX Proxy Manager...');
const nginxResult = await checkNginxConnection();
if (!nginxResult.success) {
@@ -268,29 +356,29 @@ async function startLaunch(data) {
}
// Update the step to show success
const nginxStep = document.querySelectorAll('.step-item')[1];
const nginxStep = document.querySelectorAll('.step-item')[3];
nginxStep.classList.remove('active');
nginxStep.classList.add('completed');
nginxStep.querySelector('.step-status').textContent = 'Successfully connected to NGINX Proxy Manager';
// Step 3: Generate SSL Certificate
await updateStep(3, 'Generating SSL Certificate', 'Setting up secure HTTPS connection...');
// Step 5: Generate SSL Certificate
await updateStep(5, 'Generating SSL Certificate', 'Setting up secure HTTPS connection...');
const sslResult = await generateSSLCertificate(data.webAddresses);
if (!sslResult.success) {
throw new Error(sslResult.error || 'Failed to generate SSL certificate');
}
// Step 4: Create Proxy Host
await updateStep(4, 'Creating Proxy Host', 'Setting up NGINX proxy host configuration...');
// Step 6: Create Proxy Host
await updateStep(6, 'Creating Proxy Host', 'Setting up NGINX proxy host configuration...');
const proxyResult = await createProxyHost(data.webAddresses, data.port, sslResult.data.certificate.id);
if (!proxyResult.success) {
throw new Error(proxyResult.error || 'Failed to create proxy host');
}
// Step 5: Check Portainer connection
await updateStep(5, 'Checking Portainer Connection', 'Verifying connection to Portainer...');
// Step 7: Check Portainer connection
await updateStep(7, 'Checking Portainer Connection', 'Verifying connection to Portainer...');
const portainerResult = await checkPortainerConnection();
if (!portainerResult.success) {
@@ -298,13 +386,13 @@ async function startLaunch(data) {
}
// Update the step to show success
const portainerStep = document.querySelectorAll('.step-item')[4];
const portainerStep = document.querySelectorAll('.step-item')[6];
portainerStep.classList.remove('active');
portainerStep.classList.add('completed');
portainerStep.querySelector('.step-status').textContent = portainerResult.message;
// Step 6: Download Docker Compose
await updateStep(6, 'Downloading Docker Compose', 'Fetching docker-compose.yml from repository...');
// Step 8: Download Docker Compose
await updateStep(8, 'Downloading Docker Compose', 'Fetching docker-compose.yml from repository...');
const dockerComposeResult = await downloadDockerCompose(data.repository, data.branch);
if (!dockerComposeResult.success) {
@@ -312,7 +400,7 @@ async function startLaunch(data) {
}
// Update the step to show success
const dockerComposeStep = document.querySelectorAll('.step-item')[5];
const dockerComposeStep = document.querySelectorAll('.step-item')[7];
dockerComposeStep.classList.remove('active');
dockerComposeStep.classList.add('completed');
dockerComposeStep.querySelector('.step-status').textContent = 'Successfully downloaded docker-compose.yml';
@@ -334,8 +422,8 @@ async function startLaunch(data) {
};
dockerComposeStep.querySelector('.step-content').appendChild(downloadButton);
// Step 7: Deploy Stack
await updateStep(7, 'Deploying Stack', 'Launching your application stack...');
// Step 9: Deploy Stack
await updateStep(9, 'Deploying Stack', 'Launching your application stack...');
const stackResult = await deployStack(dockerComposeResult.content, data.instanceName, data.port);
if (!stackResult.success) {
@@ -343,7 +431,7 @@ async function startLaunch(data) {
}
// Update the step to show success
const stackDeployStep = document.querySelectorAll('.step-item')[6];
const stackDeployStep = document.querySelectorAll('.step-item')[8];
stackDeployStep.classList.remove('active');
stackDeployStep.classList.add('completed');
stackDeployStep.querySelector('.step-status').textContent =
@@ -392,7 +480,7 @@ async function startLaunch(data) {
stackDeployStep.querySelector('.step-content').appendChild(stackDetails);
// Save instance data
await updateStep(8, 'Saving Instance Data', 'Storing instance information...');
await updateStep(10, 'Saving Instance Data', 'Storing instance information...');
try {
const instanceData = {
name: data.instanceName,
@@ -407,15 +495,15 @@ async function startLaunch(data) {
console.log('Saving instance data:', instanceData);
const saveResult = await saveInstanceData(instanceData);
console.log('Save result:', saveResult);
await updateStep(8, 'Saving Instance Data', 'Instance data saved successfully');
await updateStep(10, 'Saving Instance Data', 'Instance data saved successfully');
} catch (error) {
console.error('Error saving instance data:', error);
await updateStep(8, 'Saving Instance Data', `Error: ${error.message}`);
await updateStep(10, 'Saving Instance Data', `Error: ${error.message}`);
throw error;
}
// Update the step to show success
const saveDataStep = document.querySelectorAll('.step-item')[7];
const saveDataStep = document.querySelectorAll('.step-item')[9];
saveDataStep.classList.remove('active');
saveDataStep.classList.add('completed');
saveDataStep.querySelector('.step-status').textContent = 'Successfully saved instance data';
@@ -453,7 +541,7 @@ async function startLaunch(data) {
saveDataStep.querySelector('.step-content').appendChild(instanceDetails);
// After saving instance data, add the health check step
await updateStep(9, 'Health Check', 'Verifying instance health...');
await updateStep(11, 'Health Check', 'Verifying instance health...');
const healthResult = await checkInstanceHealth(`https://${data.webAddresses[0]}`);
if (!healthResult.success) {
@@ -461,7 +549,7 @@ async function startLaunch(data) {
}
// Add a retry button if health check fails
const healthStep = document.querySelectorAll('.step-item')[8];
const healthStep = document.querySelectorAll('.step-item')[10];
if (!healthResult.success) {
const retryButton = document.createElement('button');
retryButton.className = 'btn btn-sm btn-warning mt-2';
@@ -483,7 +571,7 @@ async function startLaunch(data) {
}
// After health check, add authentication step
await updateStep(10, 'Instance Authentication', 'Setting up instance authentication...');
await updateStep(12, 'Instance Authentication', 'Setting up instance authentication...');
const authResult = await authenticateInstance(`https://${data.webAddresses[0]}`, data.instanceId);
if (!authResult.success) {
@@ -491,7 +579,7 @@ async function startLaunch(data) {
}
// Update the auth step to show success
const authStep = document.querySelectorAll('.step-item')[9];
const authStep = document.querySelectorAll('.step-item')[11];
authStep.classList.remove('active');
authStep.classList.add('completed');
authStep.querySelector('.step-status').textContent = authResult.alreadyAuthenticated ?
@@ -538,8 +626,8 @@ async function startLaunch(data) {
`;
authStep.querySelector('.step-content').appendChild(authDetails);
// Step 11: Apply Company Information
await updateStep(11, 'Apply Company Information', 'Configuring company details...');
// Step 13: Apply Company Information
await updateStep(13, 'Apply Company Information', 'Configuring company details...');
const companyResult = await applyCompanyInformation(`https://${data.webAddresses[0]}`, data.company);
if (!companyResult.success) {
@@ -547,7 +635,7 @@ async function startLaunch(data) {
}
// Update the company step to show success
const companyStep = document.querySelectorAll('.step-item')[10];
const companyStep = document.querySelectorAll('.step-item')[12];
companyStep.classList.remove('active');
companyStep.classList.add('completed');
companyStep.querySelector('.step-status').textContent = 'Successfully applied company information';
@@ -596,8 +684,8 @@ async function startLaunch(data) {
`;
companyStep.querySelector('.step-content').appendChild(companyDetails);
// Step 12: Apply Colors
await updateStep(12, 'Apply Colors', 'Configuring color scheme...');
// Step 14: Apply Colors
await updateStep(14, 'Apply Colors', 'Configuring color scheme...');
const colorsResult = await applyColors(`https://${data.webAddresses[0]}`, data.colors);
if (!colorsResult.success) {
@@ -605,7 +693,7 @@ async function startLaunch(data) {
}
// Update the colors step to show success
const colorsStep = document.querySelectorAll('.step-item')[11];
const colorsStep = document.querySelectorAll('.step-item')[13];
colorsStep.classList.remove('active');
colorsStep.classList.add('completed');
colorsStep.querySelector('.step-status').textContent = 'Successfully applied color scheme';
@@ -645,8 +733,8 @@ async function startLaunch(data) {
`;
colorsStep.querySelector('.step-content').appendChild(colorsDetails);
// Step 13: Update Admin Credentials
await updateStep(13, 'Update Admin Credentials', 'Setting up admin account...');
// Step 15: Update Admin Credentials
await updateStep(15, 'Update Admin Credentials', 'Setting up admin account...');
const credentialsResult = await updateAdminCredentials(`https://${data.webAddresses[0]}`, data.company.email);
if (!credentialsResult.success) {
@@ -654,7 +742,7 @@ async function startLaunch(data) {
}
// Update the credentials step to show success
const credentialsStep = document.querySelectorAll('.step-item')[12];
const credentialsStep = document.querySelectorAll('.step-item')[14];
credentialsStep.classList.remove('active');
credentialsStep.classList.add('completed');
@@ -719,8 +807,8 @@ async function startLaunch(data) {
`;
credentialsStep.querySelector('.step-content').appendChild(credentialsDetails);
// Step 14: Copy SMTP Settings
await updateStep(14, 'Copy SMTP Settings', 'Configuring email settings...');
// Step 16: Copy SMTP Settings
await updateStep(16, 'Copy SMTP Settings', 'Configuring email settings...');
const smtpResult = await copySmtpSettings(`https://${data.webAddresses[0]}`);
if (!smtpResult.success) {
@@ -728,7 +816,7 @@ async function startLaunch(data) {
}
// Update the SMTP step to show success
const smtpStep = document.querySelectorAll('.step-item')[13];
const smtpStep = document.querySelectorAll('.step-item')[15];
smtpStep.classList.remove('active');
smtpStep.classList.add('completed');
smtpStep.querySelector('.step-status').textContent = 'Successfully copied SMTP settings';
@@ -789,8 +877,8 @@ async function startLaunch(data) {
`;
smtpStep.querySelector('.step-content').appendChild(smtpDetails);
// Step 15: Send Completion Email
await updateStep(15, 'Send Completion Email', 'Sending notification to client...');
// Step 17: Send Completion Email
await updateStep(17, 'Send Completion Email', 'Sending notification to client...');
const emailResult = await sendCompletionEmail(`https://${data.webAddresses[0]}`, data.company, credentialsResult.data);
if (!emailResult.success) {
@@ -798,7 +886,7 @@ async function startLaunch(data) {
}
// Update the email step to show success
const emailStep = document.querySelectorAll('.step-item')[14];
const emailStep = document.querySelectorAll('.step-item')[16];
emailStep.classList.remove('active');
emailStep.classList.add('completed');
emailStep.querySelector('.step-status').textContent = 'Successfully sent completion email';
@@ -983,31 +1071,128 @@ Thank you for choosing DocuPulse!
} catch (error) {
console.error('Launch failed:', error);
await updateStep(15, 'Send Completion Email', `Error: ${error.message}`);
await updateStep(17, 'Send Completion Email', `Error: ${error.message}`);
showError(error.message);
}
}
async function checkDNSRecords(domains) {
const maxRetries = 30; // 30 retries * 10 seconds = 5 minutes
const baseDelay = 10000; // 10 seconds base delay
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch('/api/check-dns', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': window.csrfToken
},
body: JSON.stringify({ domains: domains })
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to check DNS records');
}
const result = await response.json();
// Check if all domains are resolved
const allResolved = Object.values(result.results).every(result => result.resolved);
if (allResolved) {
console.log(`DNS records resolved successfully on attempt ${attempt}`);
return result;
}
// If not all domains are resolved and this isn't the last attempt, wait and retry
if (attempt < maxRetries) {
const delay = baseDelay * Math.pow(1.2, attempt - 1); // Exponential backoff
const failedDomains = Object.entries(result.results)
.filter(([_, result]) => !result.resolved)
.map(([domain]) => domain);
console.log(`Attempt ${attempt}/${maxRetries}: DNS not yet propagated for ${failedDomains.join(', ')}. Waiting ${Math.round(delay/1000)}s before retry...`);
// Update the step description to show retry progress
const currentStep = document.querySelector('.step-item.active');
if (currentStep) {
const statusElement = currentStep.querySelector('.step-status');
statusElement.textContent = `Waiting for DNS propagation... (Attempt ${attempt}/${maxRetries})`;
}
await new Promise(resolve => setTimeout(resolve, delay));
} else {
// Last attempt failed
console.log(`DNS records failed to resolve after ${maxRetries} attempts`);
return result;
}
} catch (error) {
console.error(`Error checking DNS records (attempt ${attempt}):`, error);
if (attempt === maxRetries) {
throw error;
}
// Wait before retrying on error
const delay = baseDelay * Math.pow(1.2, attempt - 1);
console.log(`DNS check failed, retrying in ${Math.round(delay/1000)}s...`);
// Update the step description to show retry progress
const currentStep = document.querySelector('.step-item.active');
if (currentStep) {
const statusElement = currentStep.querySelector('.step-status');
statusElement.textContent = `DNS check failed, retrying... (Attempt ${attempt}/${maxRetries})`;
}
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
async function checkCloudflareConnection() {
try {
const response = await fetch('/api/check-dns', {
const response = await fetch('/api/check-cloudflare-connection', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({ domains })
'X-CSRF-Token': window.csrfToken
}
});
if (!response.ok) {
throw new Error('Failed to check DNS records');
const error = await response.json();
throw new Error(error.error || 'Failed to check Cloudflare connection');
}
const result = await response.json();
console.log('DNS check result:', result);
return result;
return await response.json();
} catch (error) {
console.error('Error checking DNS records:', error);
console.error('Error checking Cloudflare connection:', error);
throw error;
}
}
async function createDNSRecords(domains) {
try {
const response = await fetch('/api/create-dns-records', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': window.csrfToken
},
body: JSON.stringify({ domains: domains })
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to create DNS records');
}
return await response.json();
} catch (error) {
console.error('Error creating DNS records:', error);
throw error;
}
}
@@ -1390,7 +1575,7 @@ async function createProxyHost(domains, port, sslCertificateId) {
`;
// Update the proxy step to show success and add the results
const proxyStep = document.querySelectorAll('.step-item')[3];
const proxyStep = document.querySelectorAll('.step-item')[5];
proxyStep.classList.remove('active');
proxyStep.classList.add('completed');
const statusText = proxyStep.querySelector('.step-status');
@@ -1509,7 +1694,7 @@ async function generateSSLCertificate(domains) {
}
// Update the SSL step to show success
const sslStep = document.querySelectorAll('.step-item')[2];
const sslStep = document.querySelectorAll('.step-item')[4];
sslStep.classList.remove('active');
sslStep.classList.add('completed');
const sslStatusText = sslStep.querySelector('.step-status');
@@ -1589,8 +1774,8 @@ function updateStep(stepNumber, title, description) {
document.getElementById('currentStep').textContent = title;
document.getElementById('stepDescription').textContent = description;
// Calculate progress based on total number of steps (14 steps total)
const totalSteps = 14;
// Calculate progress based on total number of steps (17 steps total)
const totalSteps = 17;
const progress = ((stepNumber - 1) / (totalSteps - 1)) * 100;
const progressBar = document.getElementById('launchProgress');
progressBar.style.width = `${progress}%`;
@@ -1674,7 +1859,11 @@ async function downloadDockerCompose(repo, branch) {
// Add new function to deploy stack
async function deployStack(dockerComposeContent, stackName, port) {
const maxRetries = 30; // 30 retries * 10 seconds = 5 minutes
const baseDelay = 10000; // 10 seconds base delay
try {
// First, attempt to deploy the stack
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10 * 60 * 1000); // 10 minutes timeout
@@ -1709,10 +1898,135 @@ async function deployStack(dockerComposeContent, stackName, port) {
}
const result = await response.json();
// If deployment was successful, wait for stack to come online
if (result.success || result.data) {
console.log('Stack deployment initiated, waiting for stack to come online...');
// Update status to show we're waiting for stack to come online
const currentStep = document.querySelector('.step-item.active');
if (currentStep) {
const statusElement = currentStep.querySelector('.step-status');
statusElement.textContent = 'Stack deployed, waiting for services to start...';
}
// Wait and retry to check if stack is online
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
// Check stack status via Portainer API
const stackCheckResponse = await fetch('/api/admin/check-stack-status', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({
stack_name: `docupulse_${port}`
})
});
if (stackCheckResponse.ok) {
const stackStatus = await stackCheckResponse.json();
if (stackStatus.success && stackStatus.data.status === 'active') {
console.log(`Stack came online successfully on attempt ${attempt}`);
// Update status to show success
if (currentStep) {
const statusElement = currentStep.querySelector('.step-status');
statusElement.textContent = 'Stack deployed and online successfully';
}
return {
success: true,
data: {
...result.data || result,
status: 'active',
attempt: attempt
}
};
}
}
// If not online yet and this isn't the last attempt, wait and retry
if (attempt < maxRetries) {
const delay = baseDelay * Math.pow(1.2, attempt - 1); // Exponential backoff
console.log(`Attempt ${attempt}/${maxRetries}: Stack not yet online. Waiting ${Math.round(delay/1000)}s before retry...`);
// Update the step description to show retry progress
if (currentStep) {
const statusElement = currentStep.querySelector('.step-status');
statusElement.textContent = `Waiting for stack to come online... (Attempt ${attempt}/${maxRetries})`;
}
await new Promise(resolve => setTimeout(resolve, delay));
} else {
// Last attempt failed - stack might be online but API check failed
console.log(`Stack status check failed after ${maxRetries} attempts, but deployment was successful`);
// Update status to show partial success
if (currentStep) {
const statusElement = currentStep.querySelector('.step-status');
statusElement.textContent = 'Stack deployed (status check timeout)';
}
return {
success: true,
data: {
...result.data || result,
status: 'deployed',
note: 'Status check timeout - stack may be online'
}
};
}
} catch (error) {
console.error(`Stack status check attempt ${attempt} failed:`, error);
if (attempt === maxRetries) {
// Last attempt failed, but deployment was successful
console.log('Stack status check failed after all attempts, but deployment was successful');
if (currentStep) {
const statusElement = currentStep.querySelector('.step-status');
statusElement.textContent = 'Stack deployed (status check failed)';
}
return {
success: true,
data: {
...result.data || result,
status: 'deployed',
note: 'Status check failed - stack may be online'
}
};
}
// Wait before retrying on error
const delay = baseDelay * Math.pow(1.2, attempt - 1);
console.log(`Stack check failed, retrying in ${Math.round(delay/1000)}s...`);
if (currentStep) {
const statusElement = currentStep.querySelector('.step-status');
statusElement.textContent = `Stack check failed, retrying... (Attempt ${attempt}/${maxRetries})`;
}
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
// If we get here, deployment was successful but we couldn't verify status
return {
success: true,
data: result
data: {
...result.data || result,
status: 'deployed',
note: 'Deployment successful, status unknown'
}
};
} catch (error) {
console.error('Error deploying stack:', error);
return {

View File

@@ -435,6 +435,100 @@ async function saveGitConnection(event, provider) {
saveModal.show();
}
// Test Cloudflare Connection
async function testCloudflareConnection() {
const saveModal = new bootstrap.Modal(document.getElementById('saveConnectionModal'));
const messageElement = document.getElementById('saveConnectionMessage');
messageElement.textContent = 'Testing connection...';
messageElement.className = '';
saveModal.show();
try {
const email = document.getElementById('cloudflareEmail').value;
const apiKey = document.getElementById('cloudflareApiKey').value;
const zoneId = document.getElementById('cloudflareZone').value;
const serverIp = document.getElementById('cloudflareServerIp').value;
if (!email || !apiKey || !zoneId || !serverIp) {
throw new Error('Please fill in all required fields');
}
const data = {
email: email,
api_key: apiKey,
zone_id: zoneId,
server_ip: serverIp
};
const response = await fetch('/settings/test-cloudflare-connection', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCsrfToken()
},
body: JSON.stringify(data)
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Connection test failed');
}
messageElement.textContent = 'Connection test successful!';
messageElement.className = 'text-success';
} catch (error) {
messageElement.textContent = error.message || 'Connection test failed';
messageElement.className = 'text-danger';
}
}
// Save Cloudflare Connection
async function saveCloudflareConnection(event) {
event.preventDefault();
const saveModal = new bootstrap.Modal(document.getElementById('saveConnectionModal'));
const messageElement = document.getElementById('saveConnectionMessage');
messageElement.textContent = '';
messageElement.className = '';
try {
const email = document.getElementById('cloudflareEmail').value;
const apiKey = document.getElementById('cloudflareApiKey').value;
const zoneId = document.getElementById('cloudflareZone').value;
const serverIp = document.getElementById('cloudflareServerIp').value;
if (!email || !apiKey || !zoneId || !serverIp) {
throw new Error('Please fill in all required fields');
}
const response = await fetch('/settings/save-cloudflare-connection', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCsrfToken()
},
body: JSON.stringify({
email: email,
api_key: apiKey,
zone_id: zoneId,
server_ip: serverIp
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to save settings');
}
messageElement.textContent = 'Settings saved successfully!';
messageElement.className = 'text-success';
} catch (error) {
messageElement.textContent = error.message || 'Failed to save settings';
messageElement.className = 'text-danger';
}
saveModal.show();
}
// Initialize on page load
document.addEventListener('DOMContentLoaded', function() {
const gitSettings = JSON.parse(document.querySelector('meta[name="git-settings"]').getAttribute('content'));