Add missing views/worlds/index.ejs and fix gitignore patterns
- Add views/worlds/index.ejs template file to repository - Update .gitignore to use /worlds/ instead of worlds/ to only exclude root-level worlds directory - This ensures application view templates are tracked while site-specific Luanti data remains ignored 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -59,7 +59,7 @@ temp/
|
|||||||
*.tmp
|
*.tmp
|
||||||
|
|
||||||
# Luanti/Minetest specific files (if running locally)
|
# Luanti/Minetest specific files (if running locally)
|
||||||
worlds/
|
/worlds/
|
||||||
mods/
|
mods/
|
||||||
games/
|
games/
|
||||||
textures/
|
textures/
|
||||||
|
138
views/worlds/index.ejs
Normal file
138
views/worlds/index.ejs
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
<%
|
||||||
|
const body = `
|
||||||
|
<div class="page-header">
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 8px;">
|
||||||
|
<div>
|
||||||
|
<h2>Worlds Management</h2>
|
||||||
|
<p class="text-secondary">Manage your Luanti worlds</p>
|
||||||
|
</div>
|
||||||
|
<a href="/worlds/new" class="btn btn-primary">Create New World</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="creatingAlert" class="alert alert-info" style="display: none;">
|
||||||
|
<strong>Creating world...</strong><br>
|
||||||
|
<span id="creatingWorldName"></span> is being created.
|
||||||
|
<div style="margin-top: 8px;">
|
||||||
|
<div style="width: 100%; height: 6px; background: rgba(255,255,255,0.3); border-radius: 3px;">
|
||||||
|
<div id="progressBar" style="height: 100%; background: var(--text-light); border-radius: 3px; width: 0%; transition: width 0.3s;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${typeof worlds !== 'undefined' && worlds.length > 0 ? `
|
||||||
|
<div class="grid">
|
||||||
|
${worlds.map(world => `
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3>${world.displayName}</h3>
|
||||||
|
<span class="badge ${world.creativeMode ? 'badge-info' : 'badge-success'}">
|
||||||
|
${world.creativeMode ? 'Creative' : 'Survival'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
${world.description ? `<p class="text-secondary">${world.description}</p>` : ''}
|
||||||
|
<div class="details">
|
||||||
|
<div class="detail-item">
|
||||||
|
<strong>Game:</strong> ${world.gameTitle || world.gameid}
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<strong>Players:</strong> ${world.playerCount || 0}
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<strong>PvP:</strong> ${world.enablePvp ? 'Enabled' : 'Disabled'}
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<strong>Damage:</strong> ${world.enableDamage ? 'Enabled' : 'Disabled'}
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<strong>Last Modified:</strong> ${new Date(world.lastModified).toLocaleDateString()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer">
|
||||||
|
<a href="/worlds/${world.name}" class="btn btn-primary btn-sm">View Details</a>
|
||||||
|
<form method="POST" action="/worlds/${world.name}/delete"
|
||||||
|
style="display: inline;"
|
||||||
|
onsubmit="return confirmDelete('world', '${world.displayName}')">
|
||||||
|
${typeof csrfToken !== 'undefined' && csrfToken ? `<input type="hidden" name="_csrf" value="${csrfToken}">` : ''}
|
||||||
|
<button type="submit" class="btn btn-danger btn-sm">Delete</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`).join('')}
|
||||||
|
</div>
|
||||||
|
` : `
|
||||||
|
<div class="empty-state">
|
||||||
|
<h3>No worlds created yet</h3>
|
||||||
|
<p>Create your first world to get started with hosting Luanti servers.</p>
|
||||||
|
<a href="/worlds/new" class="btn btn-primary">Create First World</a>
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
|
||||||
|
`;
|
||||||
|
%>
|
||||||
|
|
||||||
|
<%- include('../layout', {
|
||||||
|
body: body,
|
||||||
|
currentPage: 'worlds',
|
||||||
|
title: title,
|
||||||
|
inlineScript: `
|
||||||
|
// Handle world creation progress
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const creatingWorldName = urlParams.get('creating');
|
||||||
|
|
||||||
|
if (creatingWorldName) {
|
||||||
|
// Show creating alert
|
||||||
|
const creatingAlert = document.getElementById('creatingAlert');
|
||||||
|
const creatingWorldNameSpan = document.getElementById('creatingWorldName');
|
||||||
|
const progressBar = document.getElementById('progressBar');
|
||||||
|
|
||||||
|
if (creatingAlert && creatingWorldNameSpan) {
|
||||||
|
creatingWorldNameSpan.textContent = creatingWorldName;
|
||||||
|
creatingAlert.style.display = 'block';
|
||||||
|
|
||||||
|
// Quick progress animation (since creation is fast but not instant)
|
||||||
|
let progress = 0;
|
||||||
|
const progressInterval = setInterval(() => {
|
||||||
|
progress += 15;
|
||||||
|
if (progress > 85) progress = 85; // Don't complete until we get websocket confirmation
|
||||||
|
progressBar.style.width = progress + '%';
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
// Listen for websocket events
|
||||||
|
if (typeof socket !== 'undefined') {
|
||||||
|
socket.on('worldCreated', function(data) {
|
||||||
|
if (data.worldName === creatingWorldName) {
|
||||||
|
clearInterval(progressInterval);
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
progressBar.style.width = '100%';
|
||||||
|
setTimeout(() => {
|
||||||
|
// Remove the creating parameter and reload
|
||||||
|
const newUrl = new URL(window.location);
|
||||||
|
newUrl.searchParams.delete('creating');
|
||||||
|
window.location.href = newUrl.toString();
|
||||||
|
}, 500);
|
||||||
|
} else {
|
||||||
|
creatingAlert.className = 'alert alert-danger';
|
||||||
|
creatingAlert.innerHTML = '<strong>World creation failed:</strong><br>' +
|
||||||
|
(data.error || 'Unknown error occurred');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: reload after 5 seconds if no websocket response
|
||||||
|
setTimeout(() => {
|
||||||
|
clearInterval(progressInterval);
|
||||||
|
const newUrl = new URL(window.location);
|
||||||
|
newUrl.searchParams.delete('creating');
|
||||||
|
window.location.href = newUrl.toString();
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
`
|
||||||
|
}) %>
|
Reference in New Issue
Block a user