Files
app-installer/templates/index.html
SimolZimol 0f83f15588 new file: README.md
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
2025-07-04 23:50:04 +02:00

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 %}