new file: app.py new file: config.example.json new file: config.json new file: docker_diagnose.py new file: requirements.txt new file: start.bat new file: templates/available_projects.html new file: templates/base.html new file: templates/config.html new file: templates/docker_status.html new file: templates/index.html new file: templates/project_details.html new file: templates/project_details_fixed.html new file: templates/project_details_new.html new file: templates/project_details_old.html .gitignore
221 lines
9.0 KiB
HTML
221 lines
9.0 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block content %}
|
|
<div class="header-stats">
|
|
<div class="row">
|
|
<div class="col-md-3">
|
|
<div class="stat-item">
|
|
<span class="stat-number">{{ projects|length }}</span>
|
|
<span>Installierte Apps</span>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="stat-item">
|
|
<span class="stat-number">{{ projects|selectattr('status', 'equalto', 'running')|list|length }}</span>
|
|
<span>Laufende Apps</span>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="stat-item">
|
|
<span class="stat-number">{{ config.projects|length if config.projects else 0 }}</span>
|
|
<span>Verfügbare Apps</span>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="stat-item">
|
|
<span class="stat-number">{{ projects|selectattr('has_dockerfile', 'equalto', true)|list|length }}</span>
|
|
<span>Docker Apps</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if not projects %}
|
|
<div class="text-center py-5">
|
|
<i class="fas fa-rocket fa-3x text-muted mb-3"></i>
|
|
<h3 class="text-muted">Noch keine Apps installiert</h3>
|
|
<p class="text-muted mb-4">Beginnen Sie mit der Installation Ihrer ersten App!</p>
|
|
<a href="{{ url_for('available_projects') }}" class="btn btn-primary btn-lg">
|
|
<i class="fas fa-download"></i> Verfügbare Apps anzeigen
|
|
</a>
|
|
</div>
|
|
{% else %}
|
|
<div class="row">
|
|
{% for project in projects %}
|
|
<div class="col-lg-6 col-xl-4">
|
|
<div class="card project-card">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-cube me-2"></i>{{ project.name }}
|
|
</h5>
|
|
<span class="status-badge status-{{ 'running' if project.status == 'running' else 'stopped' if project.status in ['exited', 'stopped'] else 'unknown' }}">
|
|
<i class="fas fa-circle me-1"></i>
|
|
{% if project.status == 'running' %}Läuft{% elif project.status in ['exited', 'stopped'] %}Gestoppt{% else %}Unbekannt{% endif %}
|
|
</span>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row mb-3">
|
|
<div class="col-6">
|
|
<small class="text-muted">Docker:</small><br>
|
|
{% if project.has_dockerfile %}
|
|
<i class="fas fa-check text-success"></i> Verfügbar
|
|
{% else %}
|
|
<i class="fas fa-times text-danger"></i> Nicht verfügbar
|
|
{% endif %}
|
|
</div>
|
|
<div class="col-6">
|
|
<small class="text-muted">Konfiguration:</small><br>
|
|
{% if project.has_env_example %}
|
|
<i class="fas fa-check text-success"></i> .env vorhanden
|
|
{% else %}
|
|
<i class="fas fa-minus text-warning"></i> Keine .env
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
{% if project.readme %}
|
|
<div class="mb-3">
|
|
<small class="text-muted">Beschreibung:</small>
|
|
<p class="card-text small">{{ project.readme[:150] }}{% if project.readme|length > 150 %}...{% endif %}</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<small class="text-muted">Installiert: {{ project.created }}</small>
|
|
</div>
|
|
<div class="card-footer">
|
|
<div class="action-buttons">
|
|
{% if project.status == 'running' %}
|
|
<a href="{{ url_for('stop_project', project_name=project.name) }}" class="btn btn-warning btn-sm">
|
|
<i class="fas fa-stop"></i> Stoppen
|
|
</a>
|
|
{% else %}
|
|
<a href="{{ url_for('start_project', project_name=project.name) }}" class="btn btn-success btn-sm">
|
|
<i class="fas fa-play"></i> Starten
|
|
</a>
|
|
{% endif %}
|
|
|
|
<a href="{{ url_for('build_project', project_name=project.name) }}" class="btn btn-info btn-sm">
|
|
<i class="fas fa-hammer"></i> Build
|
|
</a>
|
|
|
|
<a href="{{ url_for('project_details', project_name=project.name) }}" class="btn btn-primary btn-sm">
|
|
<i class="fas fa-cog"></i> Config
|
|
</a>
|
|
|
|
<a href="{{ url_for('remove_project', project_name=project.name) }}"
|
|
class="btn btn-danger btn-sm"
|
|
onclick="return confirmAction('remove', '{{ project.name }}')">
|
|
<i class="fas fa-trash"></i> Entfernen
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="row mt-4">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-bolt me-2"></i>Schnellaktionen
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-3 mb-2">
|
|
<a href="{{ url_for('available_projects') }}" class="btn btn-outline-primary w-100">
|
|
<i class="fas fa-download"></i> Neue App installieren
|
|
</a>
|
|
</div>
|
|
<div class="col-md-3 mb-2">
|
|
<a href="{{ url_for('refresh_projects') }}" class="btn btn-outline-info w-100">
|
|
<i class="fas fa-sync-alt"></i> Projektliste aktualisieren
|
|
</a>
|
|
</div>
|
|
<div class="col-md-3 mb-2">
|
|
<a href="{{ url_for('config') }}" class="btn btn-outline-secondary w-100">
|
|
<i class="fas fa-cog"></i> Einstellungen
|
|
</a>
|
|
</div>
|
|
<div class="col-md-3 mb-2">
|
|
<button class="btn btn-outline-warning w-100" onclick="startAllApps()">
|
|
<i class="fas fa-rocket"></i> Alle starten
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Port Management Modal -->
|
|
<div class="modal fade" id="portModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Port auswählen</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="portForm">
|
|
<div class="mb-3">
|
|
<label for="portInput" class="form-label">Port (Standard: 8080)</label>
|
|
<input type="number" class="form-control" id="portInput" value="8080" min="1" max="65535">
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
|
<button type="button" class="btn btn-primary" onclick="startWithPort()">Starten</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>
|
|
let currentProject = '';
|
|
|
|
function startWithCustomPort(projectName) {
|
|
currentProject = projectName;
|
|
const modal = new bootstrap.Modal(document.getElementById('portModal'));
|
|
modal.show();
|
|
}
|
|
|
|
function startWithPort() {
|
|
const port = document.getElementById('portInput').value;
|
|
window.location.href = `/start_project/${currentProject}?port=${port}`;
|
|
}
|
|
|
|
function startAllApps() {
|
|
if (confirm('Möchten Sie alle gestoppten Apps starten?')) {
|
|
const stoppedApps = document.querySelectorAll('.status-stopped').length;
|
|
if (stoppedApps > 0) {
|
|
// Hier könnte eine Batch-Start-Funktion implementiert werden
|
|
alert(`${stoppedApps} Apps werden gestartet. Diese Funktion wird in einer zukünftigen Version implementiert.`);
|
|
} else {
|
|
alert('Alle Apps laufen bereits oder es sind keine Apps installiert.');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Auto-update status badges
|
|
function updateStatus() {
|
|
fetch('/api/status')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
// Status updates würden hier implementiert
|
|
})
|
|
.catch(error => console.log('Status update failed:', error));
|
|
}
|
|
|
|
// Update status every 30 seconds
|
|
setInterval(updateStatus, 30000);
|
|
</script>
|
|
{% endblock %}
|