init tools repo

This commit is contained in:
2025-11-23 19:57:05 +01:00
commit d778206940
35 changed files with 6197 additions and 0 deletions

View File

@@ -0,0 +1,208 @@
<%- include('layout', { body: `
<div class="d-flex justify-content-between align-items-center mb-4">
<h2><i class="fas fa-tachometer-alt"></i> My DevBenches</h2>
<button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#createDevbenchModal">
<i class="fas fa-plus"></i> Create DevBench
</button>
</div>
<div class="row">
${devbenches.map(db => `
<div class="col-md-6 col-lg-4 mb-4">
<div class="card h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="mb-0">${db.name}</h6>
<span id="status-${db.id}" class="badge bg-${db.status === 'active' ? 'success' : db.status === 'creating' ? 'warning' : 'danger'}">
${db.status}
</span>
</div>
<div class="card-body">
${db.actual_name ? `
<p><strong>VM Name:</strong> ${db.actual_name}</p>
${db.ssh_info || db.vnc_info ? `
<div class="connection-info">
<h6><i class="fas fa-plug"></i> Connection Info:</h6>
${db.ssh_info ? `
<div class="mb-3">
<strong><i class="fas fa-terminal"></i> SSH Port:</strong><br>
<code class="fs-5">${db.ssh_info}</code>
<button class="btn btn-sm btn-outline-primary ms-2" onclick="copyToClipboard('${db.ssh_info}')">
<i class="fas fa-copy"></i>
</button>
</div>
` : ''}
${db.vnc_info ? `
<div class="mb-3">
<strong><i class="fas fa-desktop"></i> VNC Port:</strong><br>
<code class="fs-5">${db.vnc_info}</code>
<button class="btn btn-sm btn-outline-primary ms-2" onclick="copyToClipboard('${db.vnc_info}')">
<i class="fas fa-copy"></i>
</button>
</div>
` : ''}
<div class="alert alert-info mt-3">
<i class="fas fa-info-circle"></i> Need help connecting?
<a href="/help" class="alert-link">View setup guide</a>
</div>
</div>
` : ''}
` : `
<p class="text-muted">DevBench is being created...</p>
<div class="log-output" id="log-${db.id}"></div>
`}
<p class="text-muted mt-2">
<small>Created: ${new Date(db.created_at).toLocaleString()}</small>
</p>
</div>
<div class="card-footer">
<div class="btn-group w-100" role="group">
${db.status !== 'creating' ? `
<button class="btn btn-sm btn-primary" onclick="activateDevbench(${db.id})"
${db.status === 'active' ? 'disabled' : ''}>
<i class="fas fa-play"></i> Activate
</button>
<button class="btn btn-sm btn-info" onclick="checkStatus(${db.id})">
<i class="fas fa-sync"></i> Check Status
</button>
` : ''}
<button class="btn btn-sm btn-danger" onclick="deleteDevbench(${db.id})">
<i class="fas fa-trash"></i> Delete
</button>
</div>
</div>
</div>
</div>
`).join('')}
${devbenches.length === 0 ? `
<div class="col-12">
<div class="text-center py-5">
<i class="fas fa-server fa-3x text-muted mb-3"></i>
<h4 class="text-muted">No DevBenches Yet</h4>
<p class="text-muted">Create your first DevBench to get started!</p>
</div>
</div>
` : ''}
</div>
<!-- Create DevBench Modal -->
<div class="modal fade" id="createDevbenchModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Create New DevBench</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form id="createDevbenchForm">
<div class="modal-body">
<div class="mb-3">
<label for="devbenchName" class="form-label">DevBench Name</label>
<input type="text" class="form-control" id="devbenchName" name="name" required
pattern="[a-zA-Z0-9_-]+" title="Only letters, numbers, hyphens, and underscores allowed">
<div class="form-text">
Only letters, numbers, hyphens (-), and underscores (_) are allowed.<br>
Full name will be: <strong>${username}_[your-name]</strong>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-success">Create DevBench</button>
</div>
</form>
</div>
</div>
</div>
<script>
document.getElementById('createDevbenchForm').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
try {
const response = await fetch('/create-devbench', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(Object.fromEntries(formData))
});
const result = await response.json();
if (result.success) {
location.reload();
} else {
alert(result.error);
}
} catch (error) {
alert('Error creating DevBench');
}
});
async function deleteDevbench(devbenchId) {
if (confirm('Are you sure you want to delete this DevBench?')) {
try {
const response = await fetch(\`/delete-devbench/\${devbenchId}\`, { method: 'POST' });
const result = await response.json();
if (result.success) {
location.reload();
} else {
alert(result.error);
}
} catch (error) {
alert('Error deleting DevBench');
}
}
}
async function activateDevbench(devbenchId) {
try {
const response = await fetch(\`/activate-devbench/\${devbenchId}\`, { method: 'POST' });
const result = await response.json();
if (result.success) {
alert('DevBench activation started');
} else {
alert(result.error);
}
} catch (error) {
alert('Error activating DevBench');
}
}
async function checkStatus(devbenchId) {
try {
const response = await fetch(\`/check-status/\${devbenchId}\`);
const result = await response.json();
const statusElement = document.getElementById(\`status-\${devbenchId}\`);
if (statusElement) {
statusElement.className = \`badge bg-\${result.status === 'active' ? 'success' : 'danger'}\`;
statusElement.textContent = result.status;
}
} catch (error) {
alert('Error checking status');
}
}
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(() => {
// Show a temporary success message
const toast = document.createElement('div');
toast.className = 'alert alert-success position-fixed';
toast.style.cssText = 'top: 20px; right: 20px; z-index: 9999; opacity: 0.9;';
toast.innerHTML = '<i class="fas fa-check"></i> Copied to clipboard!';
document.body.appendChild(toast);
setTimeout(() => {
document.body.removeChild(toast);
}, 2000);
}).catch(err => {
console.error('Failed to copy: ', err);
alert('Failed to copy to clipboard');
});
}
</script>
`, username }) %>