Files
app-installer/templates/config.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

393 lines
16 KiB
HTML

{% extends "base.html" %}
{% block title %}Konfiguration - {{ super() }}{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-8">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-cog me-2"></i>Systemkonfiguration
</h5>
</div>
<div class="card-body">
<form method="POST">
<div class="mb-4">
<label for="project_list_url" class="form-label">
<i class="fas fa-link me-1"></i>Projektliste URL
</label>
<input type="url"
class="form-control"
id="project_list_url"
name="project_list_url"
value="{{ config.project_list_url or '' }}"
placeholder="https://example.com/projects.json">
<div class="form-text">
URL zu einer JSON-Datei mit Projektinformationen oder einer Webseite mit Git-URLs.
<br>JSON Format: <code>[{"url": "https://git.example.com/repo", "name": "Project Name", "description": "..."}]</code>
</div>
</div>
<div class="mb-4">
<label for="auto_refresh_minutes" class="form-label">
<i class="fas fa-clock me-1"></i>Automatische Aktualisierung (Minuten)
</label>
<input type="number"
class="form-control"
id="auto_refresh_minutes"
name="auto_refresh_minutes"
value="{{ config.auto_refresh_minutes or 30 }}"
min="5"
max="1440">
<div class="form-text">
Wie oft soll die Projektliste automatisch aktualisiert werden? (5-1440 Minuten)
</div>
</div>
<div class="mb-4">
<label for="docker_registry" class="form-label">
<i class="fas fa-server me-1"></i>Docker Registry (Optional)
</label>
<input type="text"
class="form-control"
id="docker_registry"
name="docker_registry"
value="{{ config.docker_registry or '' }}"
placeholder="registry.example.com">
<div class="form-text">
Private Docker Registry für das Pushen von Images (optional).
</div>
</div>
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
<button type="button" class="btn btn-outline-secondary me-md-2" onclick="testConnection()">
<i class="fas fa-wifi"></i> Verbindung testen
</button>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save"></i> Speichern
</button>
</div>
</form>
</div>
</div>
</div>
<div class="col-lg-4">
<!-- System Status -->
<div class="card mb-4">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-info-circle me-2"></i>Systemstatus
</h5>
</div>
<div class="card-body">
<div class="status-item mb-3">
<div class="d-flex justify-content-between">
<span>Docker:</span>
<span id="dockerStatus" class="badge bg-secondary">Prüfung...</span>
</div>
</div>
<div class="status-item mb-3">
<div class="d-flex justify-content-between">
<span>Git:</span>
<span id="gitStatus" class="badge bg-secondary">Prüfung...</span>
</div>
</div>
<div class="status-item mb-3">
<div class="d-flex justify-content-between">
<span>Projektverzeichnis:</span>
<span id="projectDirStatus" class="badge bg-secondary">Prüfung...</span>
</div>
</div>
<div class="status-item">
<div class="d-flex justify-content-between">
<span>Festplattenspeicher:</span>
<span id="diskSpaceStatus" class="badge bg-secondary">Prüfung...</span>
</div>
</div>
</div>
</div>
<!-- Erweiterte Einstellungen -->
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-tools me-2"></i>Erweiterte Einstellungen
</h5>
</div>
<div class="card-body">
<div class="mb-3">
<button class="btn btn-outline-warning btn-sm w-100" onclick="clearCache()">
<i class="fas fa-broom"></i> Cache leeren
</button>
</div>
<div class="mb-3">
<button class="btn btn-outline-info btn-sm w-100" onclick="exportConfig()">
<i class="fas fa-download"></i> Konfiguration exportieren
</button>
</div>
<div class="mb-3">
<button class="btn btn-outline-success btn-sm w-100" onclick="document.getElementById('importFile').click()">
<i class="fas fa-upload"></i> Konfiguration importieren
</button>
<input type="file" id="importFile" style="display: none" accept=".json" onchange="importConfig(event)">
</div>
<div>
<button class="btn btn-outline-danger btn-sm w-100" onclick="resetConfig()">
<i class="fas fa-undo"></i> Auf Standard zurücksetzen
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Projektliste Vorschau -->
{% if config.projects %}
<div class="row mt-4">
<div class="col-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">
<i class="fas fa-list me-2"></i>Aktuelle Projektliste ({{ config.projects|length }} Projekte)
</h5>
<small class="text-muted">Letzte Aktualisierung vor {{ last_update or 'unbekannt' }}</small>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-sm table-striped">
<thead>
<tr>
<th>Name</th>
<th>URL</th>
<th>Beschreibung</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{% for project in config.projects[:10] %}
<tr>
<td><strong>{{ project.name or 'Unbekannt' }}</strong></td>
<td>
<a href="{{ project.url }}" target="_blank" class="text-decoration-none">
{{ project.url[:50] }}{% if project.url|length > 50 %}...{% endif %}
</a>
</td>
<td>{{ (project.description or 'Keine Beschreibung')[:60] }}{% if (project.description or '')|length > 60 %}...{% endif %}</td>
<td>
{% if project.name in installed_projects %}
<span class="badge bg-success">Installiert</span>
{% else %}
<span class="badge bg-secondary">Verfügbar</span>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if config.projects|length > 10 %}
<p class="text-muted text-center">... und {{ config.projects|length - 10 }} weitere Projekte</p>
{% endif %}
</div>
</div>
</div>
</div>
</div>
{% endif %}
<!-- Debug Informationen -->
<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-bug me-2"></i>Debug-Informationen
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<h6>Systeminfo:</h6>
<ul class="list-unstyled small">
<li><strong>Arbeitsverzeichnis:</strong> <code id="workingDir">Lade...</code></li>
<li><strong>Projektverzeichnis:</strong> <code>./projects/</code></li>
<li><strong>App-Verzeichnis:</strong> <code>./apps/</code></li>
<li><strong>Konfigurationsdatei:</strong> <code>./config.json</code></li>
</ul>
</div>
<div class="col-md-6">
<h6>Laufzeit-Info:</h6>
<ul class="list-unstyled small">
<li><strong>Flask Debug:</strong> <span id="flaskDebug">An</span></li>
<li><strong>Host:</strong> <code>0.0.0.0:5000</code></li>
<li><strong>Uptime:</strong> <span id="uptime">Lade...</span></li>
<li><strong>Speicherverbrauch:</strong> <span id="memUsage">Lade...</span></li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
// Systemstatus prüfen
function checkSystemStatus() {
// Docker Status
fetch('/api/system_status')
.then(response => response.json())
.then(data => {
updateStatusBadge('dockerStatus', data.docker, 'Docker');
updateStatusBadge('gitStatus', data.git, 'Git');
updateStatusBadge('projectDirStatus', data.project_dir, 'Projektverzeichnis');
updateStatusBadge('diskSpaceStatus', data.disk_space, 'Festplattenspeicher');
})
.catch(error => {
console.error('Fehler beim Prüfen des Systemstatus:', error);
});
}
function updateStatusBadge(elementId, status, name) {
const element = document.getElementById(elementId);
if (status.available) {
element.textContent = status.version || 'Verfügbar';
element.className = 'badge bg-success';
} else {
element.textContent = 'Nicht verfügbar';
element.className = 'badge bg-danger';
}
}
// Verbindung testen
function testConnection() {
const url = document.getElementById('project_list_url').value;
if (!url) {
alert('Bitte geben Sie eine URL ein.');
return;
}
const button = event.target;
const originalText = button.innerHTML;
button.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Teste...';
button.disabled = true;
fetch('/api/test_connection', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({url: url})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert(`Verbindung erfolgreich! ${data.projects_found} Projekte gefunden.`);
} else {
alert(`Verbindungsfehler: ${data.error}`);
}
})
.catch(error => {
alert(`Netzwerkfehler: ${error}`);
})
.finally(() => {
button.innerHTML = originalText;
button.disabled = false;
});
}
// Cache leeren
function clearCache() {
if (confirm('Möchten Sie wirklich den Cache leeren?')) {
fetch('/api/clear_cache', {method: 'POST'})
.then(response => response.json())
.then(data => alert(data.message))
.catch(error => alert('Fehler: ' + error));
}
}
// Konfiguration exportieren
function exportConfig() {
fetch('/api/export_config')
.then(response => response.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'app_installer_config.json';
a.click();
window.URL.revokeObjectURL(url);
})
.catch(error => alert('Fehler beim Export: ' + error));
}
// Konfiguration importieren
function importConfig(event) {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(e) {
try {
const config = JSON.parse(e.target.result);
if (confirm('Möchten Sie die aktuelle Konfiguration mit der importierten ersetzen?')) {
fetch('/api/import_config', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(config)
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Konfiguration erfolgreich importiert!');
location.reload();
} else {
alert('Fehler beim Import: ' + data.error);
}
})
.catch(error => alert('Netzwerkfehler: ' + error));
}
} catch (error) {
alert('Ungültige JSON-Datei: ' + error.message);
}
};
reader.readAsText(file);
}
// Konfiguration zurücksetzen
function resetConfig() {
if (confirm('Möchten Sie wirklich alle Einstellungen auf die Standardwerte zurücksetzen?')) {
fetch('/api/reset_config', {method: 'POST'})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Konfiguration zurückgesetzt!');
location.reload();
} else {
alert('Fehler beim Zurücksetzen: ' + data.error);
}
})
.catch(error => alert('Netzwerkfehler: ' + error));
}
}
// System-Info laden
function loadSystemInfo() {
document.getElementById('workingDir').textContent = window.location.origin;
document.getElementById('uptime').textContent = 'Läuft seit Start';
document.getElementById('memUsage').textContent = 'Nicht verfügbar';
}
// Beim Laden der Seite
document.addEventListener('DOMContentLoaded', function() {
checkSystemStatus();
loadSystemInfo();
});
</script>
{% endblock %}