A modern web interface for Luanti (Minetest) server management with ContentDB integration. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
305 lines
8.8 KiB
Plaintext
305 lines
8.8 KiB
Plaintext
<%
|
|
const body = `
|
|
<div class="page-header">
|
|
<h2>🔄 Package Updates</h2>
|
|
<p>Check and install updates for your packages</p>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3>📊 Update Status</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="stat-item">
|
|
<strong>${installedCount || 0}</strong>
|
|
<span>Total Packages</span>
|
|
</div>
|
|
<div class="stat-item">
|
|
<strong>${updateCount || 0}</strong>
|
|
<span>Updates Available</span>
|
|
</div>
|
|
<div class="stat-item">
|
|
<strong>${installedCount - updateCount || 0}</strong>
|
|
<span>Up to Date</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3>⚡ Quick Actions</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
${updateCount > 0 ? `
|
|
<button class="btn btn-success btn-block" onclick="updateAllPackages()">
|
|
📦 Update All (${updateCount})
|
|
</button>
|
|
<button class="btn btn-outline-primary btn-block" onclick="window.location.reload()">
|
|
🔄 Refresh Check
|
|
</button>
|
|
` : `
|
|
<button class="btn btn-outline-primary btn-block" onclick="window.location.reload()">
|
|
🔄 Check Again
|
|
</button>
|
|
`}
|
|
<a href="/contentdb/installed" class="btn btn-outline-secondary btn-block">
|
|
📦 View All Installed
|
|
</a>
|
|
<a href="/contentdb" class="btn btn-outline-secondary btn-block">
|
|
🌐 Browse ContentDB
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-8">
|
|
${updateCount === 0 ? `
|
|
<div class="card">
|
|
<div class="card-body text-center">
|
|
<h3>✅ All Packages Up to Date!</h3>
|
|
<p>All your installed packages are running the latest versions.</p>
|
|
<div class="emoji-large">🎉</div>
|
|
<p class="text-muted">
|
|
${installedCount === 0 ?
|
|
'You haven\\'t installed any packages yet.' :
|
|
\`Checked \${installedCount} package\${installedCount !== 1 ? 's' : ''}.\`
|
|
}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
` : `
|
|
<div class="updates-list">
|
|
${updates.map(update => `
|
|
<div class="card update-card">
|
|
<div class="card-header">
|
|
<div class="update-title">
|
|
<h4>${update.latest.package.title || update.installed.name}</h4>
|
|
<small class="text-muted">by ${update.installed.author}</small>
|
|
</div>
|
|
<div class="update-badge">
|
|
<span class="badge badge-warning">Update Available</span>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="version-comparison">
|
|
<div class="version-item current">
|
|
<div class="version-label">Current Version</div>
|
|
<div class="version-value">${update.installed.version}</div>
|
|
<div class="version-date">
|
|
Installed: ${new Date(update.installed.installed_at).toLocaleDateString()}
|
|
</div>
|
|
</div>
|
|
<div class="version-arrow">➜</div>
|
|
<div class="version-item latest">
|
|
<div class="version-label">Latest Version</div>
|
|
<div class="version-value">${update.latest.release.title}</div>
|
|
<div class="version-date">
|
|
Released: ${new Date(update.latest.release.created_at).toLocaleDateString()}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="package-location">
|
|
<strong>Location:</strong>
|
|
<span class="location-badge ${update.installed.install_location === 'global' ? 'global' : 'world'}">
|
|
${update.installed.install_location === 'global' ? 'Global' : update.installed.install_location.replace('world:', '')}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="update-actions">
|
|
<button class="btn btn-success"
|
|
onclick="updatePackage('${update.installed.author}', '${update.installed.name}', '${update.installed.install_location}')">
|
|
📦 Update Now
|
|
</button>
|
|
<a href="https://content.luanti.org/packages/${update.installed.author}/${update.installed.name}/"
|
|
target="_blank"
|
|
class="btn btn-outline-primary">
|
|
View on ContentDB
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
`}
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.stat-item {
|
|
text-align: center;
|
|
padding: 0.75rem 0;
|
|
border-bottom: 1px solid var(--border-color);
|
|
}
|
|
|
|
.stat-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.stat-item strong {
|
|
display: block;
|
|
font-size: 1.5rem;
|
|
color: var(--primary-color);
|
|
}
|
|
|
|
.stat-item span {
|
|
font-size: 0.875rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.emoji-large {
|
|
font-size: 3rem;
|
|
margin: 1rem 0;
|
|
}
|
|
|
|
.updates-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1.5rem;
|
|
}
|
|
|
|
.update-card {
|
|
transition: transform 0.2s ease;
|
|
}
|
|
|
|
.update-card:hover {
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.update-title {
|
|
flex: 1;
|
|
}
|
|
|
|
.update-title h4 {
|
|
margin: 0;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.version-comparison {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
margin-bottom: 1rem;
|
|
padding: 1rem;
|
|
background: var(--bg-accent);
|
|
border-radius: var(--border-radius);
|
|
}
|
|
|
|
.version-item {
|
|
flex: 1;
|
|
text-align: center;
|
|
}
|
|
|
|
.version-label {
|
|
font-size: 0.875rem;
|
|
color: var(--text-muted);
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.version-value {
|
|
font-size: 1.1rem;
|
|
font-weight: bold;
|
|
color: var(--text-primary);
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.version-date {
|
|
font-size: 0.75rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.version-arrow {
|
|
font-size: 1.5rem;
|
|
color: var(--primary-color);
|
|
}
|
|
|
|
.current .version-value {
|
|
color: var(--warning-color);
|
|
}
|
|
|
|
.latest .version-value {
|
|
color: var(--success-color);
|
|
}
|
|
|
|
.package-location {
|
|
margin-bottom: 1rem;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.location-badge {
|
|
padding: 0.25rem 0.5rem;
|
|
border-radius: 4px;
|
|
font-size: 0.75rem;
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.location-badge.global {
|
|
background: var(--success-color);
|
|
color: white;
|
|
}
|
|
|
|
.location-badge.world {
|
|
background: var(--primary-color);
|
|
color: white;
|
|
}
|
|
|
|
.update-actions {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.badge {
|
|
padding: 0.25rem 0.5rem;
|
|
border-radius: 4px;
|
|
font-size: 0.75rem;
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.badge-warning {
|
|
background: var(--warning-color);
|
|
color: white;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.version-comparison {
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.version-arrow {
|
|
transform: rotate(90deg);
|
|
}
|
|
|
|
.update-actions {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.update-actions .btn {
|
|
width: 100%;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
function updatePackage(author, name, location) {
|
|
alert('Update functionality coming soon!');
|
|
// TODO: Implement individual package update
|
|
}
|
|
|
|
function updateAllPackages() {
|
|
if (!confirm('Update all packages? This may take a while.')) {
|
|
return;
|
|
}
|
|
alert('Bulk update functionality coming soon!');
|
|
// TODO: Implement bulk package update
|
|
}
|
|
</script>
|
|
`;
|
|
%>
|
|
|
|
<%- include('../layout', { body: body, currentPage: 'contentdb', title: title }) %> |