Add YAML editor with real-time linting
- Added editable YAML textarea - Real-time YAML validation with js-yaml library - Visual feedback for valid/invalid YAML - Ability to create server from edited YAML - Download edited YAML functionality
This commit is contained in:
parent
c31e48e2b1
commit
1406f9f120
1 changed files with 164 additions and 15 deletions
179
index.html
179
index.html
|
|
@ -4,6 +4,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Stronghold - Minecraft Server Generator</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/js-yaml@4.1.0/dist/js-yaml.min.js"></script>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
|
|
@ -526,8 +527,20 @@
|
|||
|
||||
<div id="preview" class="preview" style="display: none;">
|
||||
<h3>Generated docker-compose.yml</h3>
|
||||
<pre id="yamlPreview"></pre>
|
||||
<button class="download-btn" id="downloadBtn">Download docker-compose.yml</button>
|
||||
<div class="yaml-tab">
|
||||
<div class="yaml-tab-header">
|
||||
<span class="yaml-status" id="yamlStatus">Valid YAML</span>
|
||||
</div>
|
||||
<textarea id="yamlEditor" class="yaml-editor" placeholder="YAML will appear here..."></textarea>
|
||||
<div id="yamlError" class="yaml-error"></div>
|
||||
<div id="yamlSuccess" class="yaml-success">✓ Valid YAML</div>
|
||||
<div class="yaml-actions">
|
||||
<button class="btn btn-primary btn-sm" id="validateYamlBtn">Validate YAML</button>
|
||||
<button class="btn btn-success btn-sm" id="downloadYamlBtn">Download YAML</button>
|
||||
<button class="btn btn-primary btn-sm" id="createFromYamlBtn">Create Server from YAML</button>
|
||||
</div>
|
||||
</div>
|
||||
<pre id="yamlPreview" style="display: none;"></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -583,26 +596,162 @@
|
|||
|
||||
const API_BASE = window.location.origin;
|
||||
|
||||
const yamlEditor = document.getElementById('yamlEditor');
|
||||
const yamlError = document.getElementById('yamlError');
|
||||
const yamlSuccess = document.getElementById('yamlSuccess');
|
||||
const yamlStatus = document.getElementById('yamlStatus');
|
||||
let currentYaml = '';
|
||||
|
||||
// YAML validation function
|
||||
function validateYAML(yamlText) {
|
||||
yamlError.classList.remove('show');
|
||||
yamlSuccess.classList.remove('show');
|
||||
yamlEditor.classList.remove('error', 'valid');
|
||||
yamlStatus.textContent = '';
|
||||
yamlStatus.classList.remove('valid', 'invalid');
|
||||
|
||||
if (!yamlText || yamlText.trim() === '') {
|
||||
return { valid: false, error: 'YAML is empty' };
|
||||
}
|
||||
|
||||
try {
|
||||
jsyaml.load(yamlText);
|
||||
yamlEditor.classList.add('valid');
|
||||
yamlError.classList.remove('show');
|
||||
yamlSuccess.classList.add('show');
|
||||
yamlStatus.textContent = '✓ Valid YAML';
|
||||
yamlStatus.classList.add('valid');
|
||||
return { valid: true };
|
||||
} catch (error) {
|
||||
yamlEditor.classList.add('error');
|
||||
yamlEditor.classList.remove('valid');
|
||||
yamlError.textContent = `YAML Error: ${error.message}`;
|
||||
yamlError.classList.add('show');
|
||||
yamlSuccess.classList.remove('show');
|
||||
yamlStatus.textContent = '✗ Invalid YAML';
|
||||
yamlStatus.classList.add('invalid');
|
||||
return { valid: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
// Real-time YAML linting with debounce
|
||||
let lintTimeout;
|
||||
yamlEditor.addEventListener('input', () => {
|
||||
clearTimeout(lintTimeout);
|
||||
lintTimeout = setTimeout(() => {
|
||||
validateYAML(yamlEditor.value);
|
||||
}, 500); // Wait 500ms after user stops typing
|
||||
});
|
||||
|
||||
// Manual validate button
|
||||
document.getElementById('validateYamlBtn').addEventListener('click', () => {
|
||||
validateYAML(yamlEditor.value);
|
||||
});
|
||||
|
||||
// Download YAML button
|
||||
document.getElementById('downloadYamlBtn').addEventListener('click', () => {
|
||||
const yaml = yamlEditor.value;
|
||||
const blob = new Blob([yaml], { type: 'text/yaml' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'docker-compose.yml';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
});
|
||||
|
||||
// Create server from YAML button
|
||||
document.getElementById('createFromYamlBtn').addEventListener('click', async () => {
|
||||
const yaml = yamlEditor.value;
|
||||
const validation = validateYAML(yaml);
|
||||
|
||||
if (!validation.valid) {
|
||||
alert('Please fix YAML errors before creating server.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse YAML to extract server name and config
|
||||
try {
|
||||
const config = jsyaml.load(yaml);
|
||||
const serviceName = Object.keys(config.services || {})[0];
|
||||
const service = config.services[serviceName];
|
||||
|
||||
if (!serviceName || !service) {
|
||||
throw new Error('No service found in YAML');
|
||||
}
|
||||
|
||||
// Convert docker-compose YAML to our config format
|
||||
const serverConfig = {
|
||||
serverName: serviceName,
|
||||
serverType: service.environment?.TYPE || 'PAPER',
|
||||
version: service.environment?.VERSION || 'LATEST',
|
||||
memory: service.environment?.MEMORY || '2G',
|
||||
port: Object.keys(service.ports || {})[0]?.split(':')[0] || '25565',
|
||||
difficulty: service.environment?.DIFFICULTY || '',
|
||||
gamemode: service.environment?.MODE || '',
|
||||
levelType: service.environment?.LEVEL_TYPE || '',
|
||||
motd: service.environment?.MOTD || '',
|
||||
maxPlayers: parseInt(service.environment?.MAX_PLAYERS) || 20,
|
||||
viewDistance: parseInt(service.environment?.VIEW_DISTANCE) || 10,
|
||||
acceptEULA: service.environment?.EULA === 'TRUE',
|
||||
enableRCON: service.environment?.ENABLE_RCON === 'true',
|
||||
rconPort: service.environment?.RCON_PORT || '25575',
|
||||
rconPassword: service.environment?.RCON_PASSWORD || '',
|
||||
pvpEnabled: service.environment?.PVP !== 'false',
|
||||
allowFlight: service.environment?.ALLOW_FLIGHT === 'true',
|
||||
restartPolicy: service.restart || 'unless-stopped'
|
||||
};
|
||||
|
||||
// Submit to API
|
||||
const submitBtn = document.getElementById('submitBtn');
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.textContent = 'Creating Server...';
|
||||
|
||||
const response = await fetch(`${API_BASE}/api/containers`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(serverConfig),
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
preview.innerHTML = `
|
||||
<h3 style="color: #28a745;">✓ Server Created Successfully!</h3>
|
||||
<p><strong>Name:</strong> ${result.name}</p>
|
||||
<p><strong>Status:</strong> ${result.status}</p>
|
||||
<br>
|
||||
<a href="manage.html" class="btn btn-primary" style="text-decoration: none; display: inline-block; padding: 10px 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 6px;">Go to Manage Page</a>
|
||||
`;
|
||||
} else {
|
||||
throw new Error(result.detail || 'Failed to create server');
|
||||
}
|
||||
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = 'Create Server';
|
||||
} catch (error) {
|
||||
alert('Error creating server: ' + error.message);
|
||||
}
|
||||
});
|
||||
|
||||
// Generate YAML only (existing functionality)
|
||||
document.getElementById('generateYamlBtn').addEventListener('click', () => {
|
||||
const config = gatherFormData();
|
||||
const yaml = generateDockerCompose(config);
|
||||
|
||||
yamlPreview.textContent = yaml;
|
||||
currentYaml = yaml;
|
||||
yamlEditor.value = yaml;
|
||||
preview.style.display = 'block';
|
||||
downloadBtn.disabled = false;
|
||||
|
||||
downloadBtn.onclick = () => {
|
||||
const blob = new Blob([yaml], { type: 'text/yaml' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'docker-compose.yml';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
// Hide old preview, show editor
|
||||
yamlPreview.style.display = 'none';
|
||||
|
||||
// Validate the generated YAML
|
||||
validateYAML(yaml);
|
||||
});
|
||||
|
||||
// Create server (new functionality)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue