Files
LuHost/views/auth/register.ejs
Nathan Schneider 2d3b1166fe Fix server management issues and improve overall stability
Major server management fixes:
- Replace Flatpak-specific pkill with universal process tree termination using pstree + process.kill()
- Fix signal format errors (SIGTERM/SIGKILL instead of TERM/KILL strings)
- Add 5-second cooldown after server stop to prevent race conditions with external detection
- Enable Stop Server button for external servers in UI
- Implement proper timeout handling with process tree killing

ContentDB improvements:
- Fix download retry logic and "closed" error by preventing concurrent zip extraction
- Implement smart root directory detection and stripping during package extraction
- Add game-specific timeout handling (8s for VoxeLibre vs 3s for simple games)

World creation fixes:
- Make world creation asynchronous to prevent browser hangs
- Add WebSocket notifications for world creation completion status

Other improvements:
- Remove excessive debug logging
- Improve error handling and user feedback throughout the application
- Clean up temporary files and unnecessary logging

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-24 19:17:38 -06:00

181 lines
8.2 KiB
Plaintext

<%
const body = `
<div style="max-width: 500px; margin: 2rem auto;">
<div class="card">
<div class="card-header" style="text-align: center;">
<h2>${isFirstUser ? 'Setup Administrator Account' : 'Create Account'}</h2>
<p style="color: var(--text-secondary); margin: 0;">
${isFirstUser ?
'Create the first administrator account for this Luanti server' :
'Join this Luanti server management team'
}
</p>
</div>
${typeof error !== 'undefined' ? `
<div class="alert alert-danger">
<strong>Error:</strong> ${error}
</div>
` : ''}
${isFirstUser ? `
<div class="alert alert-info">
<strong>First User Setup:</strong> You are creating the first administrator account for this server. All users have full admin privileges.
</div>
` : ''}
<form method="POST" action="/register">
${typeof csrfToken !== 'undefined' && csrfToken ? `<input type="hidden" name="_csrf" value="${csrfToken}">` : ''}
<div class="form-group">
<label for="username">Username*</label>
<input type="text"
id="username"
name="username"
class="form-control"
value="${typeof formData !== 'undefined' ? formData.username || '' : ''}"
required
pattern="[a-zA-Z0-9_-]{3,20}"
title="3-20 characters, letters, numbers, underscore, or hyphen only"
data-validate-name
autofocus
autocomplete="username">
<small style="color: var(--text-secondary);">3-20 characters, letters, numbers, underscore, or hyphen only</small>
</div>
<div class="form-row">
<div class="form-group">
<label for="password">Password*</label>
<input type="password"
id="password"
name="password"
class="form-control"
required
minlength="8"
autocomplete="new-password">
<small style="color: var(--text-secondary);">At least 8 characters long</small>
</div>
<div class="form-group">
<label for="confirmPassword">Confirm Password*</label>
<input type="password"
id="confirmPassword"
name="confirmPassword"
class="form-control"
required
minlength="8"
autocomplete="new-password">
</div>
</div>
${isFirstUser ? `
<div class="form-group" style="margin-top: 2rem;">
<label for="dataDirectory">Luanti Data Directory*</label>
<select id="dataDirectory" name="dataDirectory" class="form-control" required>
${detectedDirectories.length > 0 ?
detectedDirectories.map(dir => `
<option value="${dir.path}"
${dir.path === defaultDataDir ? 'selected' : ''}
data-confidence="${dir.confidence}">
${dir.type}: ${dir.path}
${dir.hasConfig ? ' ✓ Config' : ''}
${dir.hasWorlds ? ' ✓ Worlds' : ''}
${dir.hasDebug ? ' ✓ Active' : ''}
</option>
`).join('') :
`<option value="${defaultDataDir}">${defaultDataDir} (Default)</option>`
}
<option value="custom">Custom directory...</option>
</select>
<small style="color: var(--text-secondary);">
Choose the correct Luanti data directory based on your installation method.
Directories with ✓ marks have existing Luanti files.
</small>
<div id="customDirGroup" style="display: none; margin-top: 1rem;">
<label for="customDataDirectory">Custom Directory Path</label>
<input type="text"
id="customDataDirectory"
name="customDataDirectory"
class="form-control"
placeholder="/path/to/luanti/data/directory">
<small style="color: var(--text-secondary);">Enter the full path to your Luanti data directory</small>
</div>
</div>
<div class="alert alert-warning">
<h4 style="margin-top: 0;">⚠️ Important: Data Directory Selection</h4>
<p>The data directory must match where your Luanti installation stores its data:</p>
<ul style="margin: 0.5rem 0;">
<li><strong>Flatpak:</strong> ~/.var/app/org.luanti.luanti/.minetest</li>
<li><strong>System Package:</strong> ~/.minetest or ~/.luanti</li>
<li><strong>Snap:</strong> ~/snap/luanti/current/.local/share/minetest</li>
</ul>
<p style="margin-bottom: 0;">Choosing the wrong directory will prevent the web interface from managing your worlds and server properly.</p>
</div>
` : ''}
<div style="display: flex; justify-content: space-between; align-items: center; margin-top: 2rem;">
<a href="/login" class="btn btn-outline">
Already have an account?
</a>
<button type="submit" class="btn btn-success">
${isFirstUser ? 'Setup Account' : 'Create Account'}
</button>
</div>
</form>
</div>
<div style="text-align: center; margin-top: 1rem; color: var(--text-secondary); font-size: 0.875rem;">
<p>
${isFirstUser ?
'This will be the primary administrator account.' :
'All accounts have full server administration privileges.'
}
</p>
</div>
</div>
<script>
// Client-side password confirmation validation
document.getElementById('confirmPassword').addEventListener('input', function() {
const password = document.getElementById('password').value;
const confirmPassword = this.value;
if (password && confirmPassword) {
if (password !== confirmPassword) {
this.setCustomValidity('Passwords do not match');
} else {
this.setCustomValidity('');
}
}
});
document.getElementById('password').addEventListener('input', function() {
const confirmPassword = document.getElementById('confirmPassword');
if (confirmPassword.value) {
confirmPassword.dispatchEvent(new Event('input'));
}
});
// Handle custom data directory selection
const dataDirectorySelect = document.getElementById('dataDirectory');
const customDirGroup = document.getElementById('customDirGroup');
const customDirInput = document.getElementById('customDataDirectory');
if (dataDirectorySelect && customDirGroup) {
dataDirectorySelect.addEventListener('change', function() {
if (this.value === 'custom') {
customDirGroup.style.display = 'block';
customDirInput.required = true;
} else {
customDirGroup.style.display = 'none';
customDirInput.required = false;
customDirInput.value = '';
}
});
}
</script>
`;
%>
<%- include('../layout', { body: body, currentPage: 'register', title: title }) %>