modified: config.json

deleted:    docker_diagnose.py
	modified:   projects_list.json
	modified:   templates/available_projects.html
	deleted:    test_icons.html
This commit is contained in:
SimolZimol
2025-07-06 12:14:26 +02:00
parent 5109de2d19
commit f6bcfb298a
5 changed files with 257 additions and 544 deletions

View File

@@ -1,25 +1,75 @@
{
"project_list_url": "http://localhost:5000/api/projects_list.json",
"project_list_url": "https://simolzimol.eu/projects_list.json",
"auto_refresh_minutes": 30,
"docker_registry": "",
"projects": [
{
"category": "Web Application",
"config_hints": {
"backup": "Automatische Datenbank-Backups konfigurierbar",
"docker_compose": "docker-compose.yml für Development und Testing",
"dockerfile": "Multi-stage Build für optimierte Container-Größe",
"env_example": ".env.example vorhanden mit allen nötigen Variablen"
},
"demo_url": "https://demo.simolzimol.net/quizify",
"url": "https://gitea.simolzimol.net/Simon/quizify",
"name": "quizify",
"description": "Ein interaktives Quiz-System mit modernem Web-Interface für Echtzeit-Quizzes und Wettbewerbe",
"language": "JavaScript",
"tags": [
"quiz",
"web",
"javascript",
"node.js",
"interactive",
"realtime",
"multiplayer"
],
"category": "Web Application",
"docker_port": 3000,
"environment_vars": {
"ADMIN_PASSWORD": "",
"NODE_ENV": "production",
"PORT": "3000",
"DATABASE_URL": "sqlite:///data/quiz.db",
"JWT_SECRET": "",
"NODE_ENV": "production",
"PORT": "3000"
"ADMIN_PASSWORD": ""
},
"requirements": {
"docker": true,
"min_memory": "512MB",
"min_cpu": "1 Core",
"min_disk": "500MB",
"ports": [
3000,
8080
]
},
"installation": {
"methods": {
"clone": {
"available": true,
"url": "https://gitea.simolzimol.net/Simon/quizify",
"type": "git",
"description": "Quellcode klonen und selbst bauen"
},
"image": {
"available": true,
"url": "docker.io/simolzimol/quizify:1.3.0",
"type": "docker",
"description": "Vorgefertigtes Docker-Image herunterladen"
}
},
"preferred": "clone"
},
"metadata": {
"created": "2024-01-15",
"last_updated": "2025-07-06",
"version": "1.3.0",
"author": "Simon",
"license": "MIT",
"homepage": "https://gitea.simolzimol.net/Simon/quizify",
"documentation": {
"available": true,
"url": "https://gitea.simolzimol.net/Simon/quizify/wiki"
},
"issues": {
"available": true,
"url": "https://gitea.simolzimol.net/Simon/quizify/issues"
},
"downloads": 245,
"stars": 150
},
"features": [
"Interaktive Quiz-Erstellung mit Drag & Drop",
@@ -32,75 +82,38 @@
"Zeitbasierte Challenges",
"Statistiken und Analytics"
],
"install_time": "2-3 Minuten",
"installation": {
"methods": {
"clone": {
"available": true,
"description": "Quellcode klonen und selbst bauen",
"type": "git",
"url": "https://gitea.simolzimol.net/Simon/quizify"
},
"image": {
"available": false,
"description": "Vorgefertigtes Docker-Image herunterladen",
"type": "docker",
"url": "docker.io/simolzimol/quizify:1.3.0"
}
},
"preferred": "clone"
},
"installation_notes": "Benötigt Node.js 18+ und SQLite. Automatische Datenbank-Migration beim ersten Start. Admin-Account wird automatisch erstellt.",
"language": "JavaScript",
"metadata": {
"author": "Simon",
"created": "2024-01-15",
"documentation": "https://gitea.simolzimol.net/Simon/quizify/wiki",
"downloads": 245,
"homepage": "https://gitea.simolzimol.net/Simon/quizify",
"issues": "https://gitea.simolzimol.net/Simon/quizify/issues",
"last_updated": "2025-07-05",
"license": "MIT",
"stars": 150,
"version": "1.3.0"
},
"min_resources": {
"cpu_cores": 1,
"disk_mb": 500,
"ram_mb": 512
},
"name": "quizify",
"rating": {
"downloads": 245,
"reviews": 12,
"score": 4.5
},
"requirements": {
"docker": true,
"min_cpu": "1 Core",
"min_disk": "500MB",
"min_memory": "512MB",
"ports": [
3000,
8080
"screenshots": {
"available": true,
"urls": [
"https://gitea.simolzimol.net/Simon/quizify/raw/branch/main/docs/screenshot1.png",
"https://gitea.simolzimol.net/Simon/quizify/raw/branch/main/docs/screenshot2.png",
"https://gitea.simolzimol.net/Simon/quizify/raw/branch/main/docs/screenshot3.png"
]
},
"screenshots": [
"https://gitea.simolzimol.net/Simon/quizify/raw/branch/main/docs/screenshot1.png",
"https://gitea.simolzimol.net/Simon/quizify/raw/branch/main/docs/screenshot2.png",
"https://gitea.simolzimol.net/Simon/quizify/raw/branch/main/docs/screenshot3.png"
],
"demo": {
"available": true,
"url": "https://demo.simolzimol.net/quizify",
"description": "Live Demo des Quiz-Systems"
},
"installation_notes": "Benötigt Node.js 18+ und SQLite. Automatische Datenbank-Migration beim ersten Start. Admin-Account wird automatisch erstellt.",
"config_hints": {
"env_example": ".env.example vorhanden mit allen nötigen Variablen",
"dockerfile": "Multi-stage Build für optimierte Container-Größe",
"docker_compose": "docker-compose.yml für Development und Testing",
"backup": "Automatische Datenbank-Backups konfigurierbar"
},
"rating": {
"score": 4.5,
"reviews": 12,
"downloads": 245
},
"size_mb": 45,
"tags": [
"quiz",
"web",
"javascript",
"node.js",
"interactive",
"realtime",
"multiplayer"
],
"url": "https://gitea.simolzimol.net/Simon/quizify"
"install_time": "2-3 Minuten",
"min_resources": {
"ram_mb": 512,
"cpu_cores": 1,
"disk_mb": 500
}
}
]
}

View File

@@ -1,237 +0,0 @@
#!/usr/bin/env python3
"""
Docker Diagnose Tool für App Installer
Hilft bei der Diagnose von Docker-Problemen
"""
import subprocess
import sys
import os
import json
from datetime import datetime
def run_command(cmd, timeout=10):
"""Führe Kommando aus und gib Ergebnis zurück"""
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=timeout,
shell=True
)
return {
'success': result.returncode == 0,
'stdout': result.stdout.strip(),
'stderr': result.stderr.strip(),
'returncode': result.returncode
}
except subprocess.TimeoutExpired:
return {
'success': False,
'stdout': '',
'stderr': f'Timeout nach {timeout} Sekunden',
'returncode': -1
}
except Exception as e:
return {
'success': False,
'stdout': '',
'stderr': str(e),
'returncode': -1
}
def check_docker_installation():
"""Prüfe Docker Installation"""
print("🔍 Prüfe Docker Installation...")
result = run_command("docker --version")
if result['success']:
print(f"✅ Docker installiert: {result['stdout']}")
return True
else:
print(f"❌ Docker nicht gefunden: {result['stderr']}")
print(" 💡 Installiere Docker Desktop von https://docker.com")
return False
def check_docker_daemon():
"""Prüfe Docker Daemon Status"""
print("\n🔍 Prüfe Docker Daemon...")
result = run_command("docker info")
if result['success']:
print("✅ Docker Daemon läuft")
return True
else:
print(f"❌ Docker Daemon nicht erreichbar: {result['stderr']}")
print(" 💡 Starte Docker Desktop")
return False
def check_docker_permissions():
"""Prüfe Docker Berechtigungen"""
print("\n🔍 Prüfe Docker Berechtigungen...")
result = run_command("docker ps")
if result['success']:
print("✅ Docker Berechtigungen OK")
return True
else:
print(f"❌ Docker Berechtigungsfehler: {result['stderr']}")
if "permission denied" in result['stderr'].lower():
print(" 💡 Starte als Administrator oder füge User zur docker Gruppe hinzu")
return False
def check_docker_images():
"""Prüfe vorhandene Docker Images"""
print("\n🔍 Prüfe Docker Images...")
result = run_command("docker images --format json")
if result['success']:
lines = result['stdout'].split('\n') if result['stdout'] else []
images = []
for line in lines:
if line.strip():
try:
img = json.loads(line)
images.append(img)
except:
pass
print(f"{len(images)} Docker Images gefunden")
for img in images[:5]: # Zeige ersten 5
print(f" - {img.get('Repository', 'unknown')}:{img.get('Tag', 'unknown')}")
if len(images) > 5:
print(f" ... und {len(images) - 5} weitere")
return True
else:
print(f"❌ Kann Images nicht abrufen: {result['stderr']}")
return False
def check_docker_containers():
"""Prüfe laufende Container"""
print("\n🔍 Prüfe Docker Container...")
result = run_command("docker ps -a --format json")
if result['success']:
lines = result['stdout'].split('\n') if result['stdout'] else []
containers = []
for line in lines:
if line.strip():
try:
container = json.loads(line)
containers.append(container)
except:
pass
running = [c for c in containers if c.get('State') == 'running']
print(f"{len(containers)} Container total, {len(running)} laufend")
for container in containers[:3]: # Zeige ersten 3
name = container.get('Names', 'unknown')
state = container.get('State', 'unknown')
print(f" - {name}: {state}")
return True
else:
print(f"❌ Kann Container nicht abrufen: {result['stderr']}")
return False
def check_docker_compose():
"""Prüfe Docker Compose"""
print("\n🔍 Prüfe Docker Compose...")
result = run_command("docker-compose --version")
if result['success']:
print(f"✅ Docker Compose verfügbar: {result['stdout']}")
return True
else:
# Versuche neue docker compose Syntax
result = run_command("docker compose version")
if result['success']:
print(f"✅ Docker Compose (v2) verfügbar: {result['stdout']}")
return True
else:
print("❌ Docker Compose nicht verfügbar")
print(" 💡 Docker Compose ist in Docker Desktop enthalten")
return False
def check_network_connectivity():
"""Prüfe Netzwerk-Konnektivität"""
print("\n🔍 Prüfe Netzwerk-Konnektivität...")
# Prüfe Docker Hub Verbindung
result = run_command("docker run --rm hello-world", timeout=30)
if result['success']:
print("✅ Docker Hub Verbindung OK")
return True
else:
print(f"❌ Docker Hub nicht erreichbar: {result['stderr']}")
print(" 💡 Prüfe Internetverbindung und Firewall")
return False
def check_system_resources():
"""Prüfe System-Ressourcen"""
print("\n🔍 Prüfe System-Ressourcen...")
result = run_command("docker system df")
if result['success']:
print("✅ Docker System-Info:")
for line in result['stdout'].split('\n'):
if line.strip():
print(f" {line}")
return True
else:
print(f"❌ Kann System-Info nicht abrufen: {result['stderr']}")
return False
def suggest_solutions():
"""Schlage Lösungen vor"""
print("\n🛠️ Lösungsvorschläge:")
print("1. Starte Docker Desktop neu")
print("2. Prüfe Windows-Dienste: Docker Desktop Service")
print("3. Prüfe Hyper-V/WSL2 Einstellungen")
print("4. Neustart des Computers")
print("5. Docker Desktop Neuinstallation")
print("\n📚 Weitere Hilfe:")
print("- Docker Docs: https://docs.docker.com/")
print("- Docker Desktop Troubleshooting: https://docs.docker.com/desktop/troubleshoot/")
def main():
"""Hauptfunktion"""
print("🐳 Docker Diagnose Tool")
print("=" * 50)
print(f"Zeitpunkt: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"System: {os.name}")
checks = [
check_docker_installation,
check_docker_daemon,
check_docker_permissions,
check_docker_images,
check_docker_containers,
check_docker_compose,
check_network_connectivity,
check_system_resources
]
results = []
for check in checks:
try:
result = check()
results.append(result)
except Exception as e:
print(f"❌ Fehler bei {check.__name__}: {e}")
results.append(False)
print("\n" + "=" * 50)
success_count = sum(1 for r in results if r)
print(f"📊 Ergebnis: {success_count}/{len(results)} Checks erfolgreich")
if success_count < len(results):
suggest_solutions()
else:
print("🎉 Alle Docker-Checks erfolgreich! Docker ist bereit.")
print("\n" + "=" * 50)
if __name__ == "__main__":
main()

View File

@@ -30,7 +30,7 @@
"description": "Quellcode klonen und selbst bauen"
},
"image": {
"available": false,
"available": true,
"url": "docker.io/simolzimol/quizify:1.3.0",
"type": "docker",
"description": "Vorgefertigtes Docker-Image herunterladen"
@@ -40,13 +40,19 @@
},
"metadata": {
"created": "2024-01-15",
"last_updated": "2025-07-05",
"last_updated": "2025-07-06",
"version": "1.3.0",
"author": "Simon",
"license": "MIT",
"homepage": "https://gitea.simolzimol.net/Simon/quizify",
"documentation": "https://gitea.simolzimol.net/Simon/quizify/wiki",
"issues": "https://gitea.simolzimol.net/Simon/quizify/issues",
"documentation": {
"available": true,
"url": "https://gitea.simolzimol.net/Simon/quizify/wiki"
},
"issues": {
"available": true,
"url": "https://gitea.simolzimol.net/Simon/quizify/issues"
},
"downloads": 245,
"stars": 150
},
@@ -61,11 +67,19 @@
"Zeitbasierte Challenges",
"Statistiken und Analytics"
],
"screenshots": [
"https://gitea.simolzimol.net/Simon/quizify/raw/branch/main/docs/screenshot1.png",
"https://gitea.simolzimol.net/Simon/quizify/raw/branch/main/docs/screenshot2.png",
"https://gitea.simolzimol.net/Simon/quizify/raw/branch/main/docs/screenshot3.png"
],
"screenshots": {
"available": true,
"urls": [
"https://gitea.simolzimol.net/Simon/quizify/raw/branch/main/docs/screenshot1.png",
"https://gitea.simolzimol.net/Simon/quizify/raw/branch/main/docs/screenshot2.png",
"https://gitea.simolzimol.net/Simon/quizify/raw/branch/main/docs/screenshot3.png"
]
},
"demo": {
"available": true,
"url": "https://demo.simolzimol.net/quizify",
"description": "Live Demo des Quiz-Systems"
},
"installation_notes": "Benötigt Node.js 18+ und SQLite. Automatische Datenbank-Migration beim ersten Start. Admin-Account wird automatisch erstellt.",
"config_hints": {
"env_example": ".env.example vorhanden mit allen nötigen Variablen",
@@ -80,11 +94,80 @@
},
"size_mb": 45,
"install_time": "2-3 Minuten",
"demo_url": "https://demo.simolzimol.net/quizify",
"min_resources": {
"ram_mb": 512,
"cpu_cores": 1,
"disk_mb": 500
}
},
{
"url": "https://github.com/example/simple-app",
"name": "simple-app",
"description": "Eine einfache Test-App ohne alle Features",
"language": "Python",
"tags": ["test", "simple", "python"],
"category": "Utility",
"docker_port": 8080,
"installation": {
"methods": {
"clone": {
"available": true,
"url": "https://github.com/example/simple-app",
"type": "git",
"description": "Nur Git Clone verfügbar"
},
"image": {
"available": false,
"url": "",
"type": "docker",
"description": "Docker Image nicht verfügbar"
}
},
"preferred": "clone"
},
"metadata": {
"created": "2024-06-01",
"last_updated": "2025-07-01",
"version": "1.0.0",
"author": "Test Author",
"license": "MIT",
"homepage": "https://github.com/example/simple-app",
"documentation": {
"available": false,
"url": ""
},
"issues": {
"available": false,
"url": ""
},
"downloads": 10,
"stars": 2
},
"features": [
"Einfache Funktionalität",
"Leichtgewichtig"
],
"screenshots": {
"available": false,
"urls": []
},
"demo": {
"available": false,
"url": "",
"description": ""
},
"installation_notes": "Einfache Python-App mit minimalen Anforderungen.",
"rating": {
"score": 3.0,
"reviews": 2,
"downloads": 10
},
"size_mb": 5,
"install_time": "1 Minute",
"min_resources": {
"ram_mb": 128,
"cpu_cores": 1,
"disk_mb": 100
}
}
]

View File

@@ -234,85 +234,105 @@
<!-- Action Buttons -->
<div class="mt-auto">
<div class="d-grid gap-2">
<!-- Hauptinstallation (bevorzugte Methode) -->
<!-- Installation Buttons mit Dropdown -->
{% if project.installation and project.installation.methods %}
{% set preferred_method = project.installation.preferred or 'clone' %}
{% set methods = project.installation.methods %}
{% set preferred = methods[preferred_method] if preferred_method in methods else methods.values()|list|first %}
{% set available_methods = [] %}
{% for method_type, method_info in methods.items() %}
{% if method_info.get('available', True) %}
{% set _ = available_methods.append((method_type, method_info)) %}
{% endif %}
{% endfor %}
<div class="btn-group" role="group">
<button class="btn btn-success install-btn"
{% if available_methods|length > 1 %}
<!-- Dropdown wenn mehrere Methoden verfügbar -->
<div class="btn-group w-100" role="group">
<button class="btn btn-success install-btn flex-grow-1"
data-url="{{ preferred.url }}"
data-name="{{ project.name }}"
data-method="{{ preferred_method }}"
onclick="installProject('{{ preferred.url }}', '{{ project.name }}', '{{ preferred_method }}')">
<i class="fas fa-download"></i>
<i class="fas fa-download me-1"></i>
{% if preferred_method == 'image' %}
<i class="fab fa-docker"></i> Docker installieren
<i class="fab fa-docker me-1"></i>Docker
{% else %}
<i class="fab fa-git-alt"></i> Code klonen
<i class="fab fa-git-alt me-1"></i>Code
{% endif %}
</button>
{% if project.demo_url %}
<a href="{{ project.demo_url }}" target="_blank" class="btn btn-outline-info">
<i class="fas fa-external-link-alt"></i> Demo
</a>
<button type="button" class="btn btn-success dropdown-toggle dropdown-toggle-split"
data-bs-toggle="dropdown" aria-expanded="false" style="flex: 0 0 auto;">
<span class="visually-hidden">Weitere Optionen</span>
</button>
<ul class="dropdown-menu">
{% for method_type, method_info in available_methods %}
<li>
<a class="dropdown-item" href="#"
onclick="installProject('{{ method_info.url }}', '{{ project.name }}', '{{ method_type }}'); return false;">
{% if method_type == 'image' %}
<i class="fab fa-docker me-2 text-primary"></i>Docker Image herunterladen
{% else %}
<i class="fab fa-git-alt me-2 text-success"></i>Quellcode klonen
{% endif %}
<small class="text-muted d-block">{{ method_info.description }}</small>
</a>
</li>
{% endfor %}
<li><hr class="dropdown-divider"></li>
<li>
<a class="dropdown-item" href="#" onclick="showProjectDetails('{{ project.name }}'); return false;">
<i class="fas fa-info-circle me-2 text-info"></i>Projektdetails anzeigen
</a>
</li>
</ul>
</div>
{% else %}
<!-- Einzelner Button wenn nur eine Methode verfügbar -->
<button class="btn btn-success install-btn w-100"
data-url="{{ preferred.url }}"
data-name="{{ project.name }}"
data-method="{{ preferred_method }}"
onclick="installProject('{{ preferred.url }}', '{{ project.name }}', '{{ preferred_method }}')">
<i class="fas fa-download me-1"></i>
{% if preferred_method == 'image' %}
<i class="fab fa-docker me-1"></i>Docker installieren
{% else %}
<i class="fab fa-git-alt me-1"></i>Code installieren
{% endif %}
</div>
<!-- Alternative Installationsmethode (falls verfügbar) -->
{% if methods|length > 1 %}
<div class="btn-group btn-group-sm" role="group">
{% for method_type, method_info in methods.items() %}
{% if method_type != preferred_method and method_info.get('available', True) %}
<button class="btn btn-outline-secondary"
data-url="{{ method_info.url }}"
data-name="{{ project.name }}"
data-method="{{ method_type }}"
onclick="installProject('{{ method_info.url }}', '{{ project.name }}', '{{ method_type }}')">
{% if method_type == 'image' %}
<i class="fab fa-docker"></i> Docker
{% else %}
<i class="fab fa-git-alt"></i> Code
{% endif %}
</button>
{% endif %}
{% endfor %}
</div>
</button>
{% endif %}
{% else %}
<!-- Fallback für alte Projekte ohne neue Struktur -->
<div class="btn-group" role="group">
<button class="btn btn-success install-btn"
data-url="{{ project.url }}"
data-name="{{ project.name }}"
data-method="clone"
onclick="installProject('{{ project.url }}', '{{ project.name }}', 'clone')">
<i class="fas fa-download"></i> <i class="fab fa-git-alt"></i> Installieren
</button>
{% if project.demo_url %}
<a href="{{ project.demo_url }}" target="_blank" class="btn btn-outline-info">
<i class="fas fa-external-link-alt"></i> Demo
</a>
{% endif %}
</div>
<button class="btn btn-success install-btn w-100"
data-url="{{ project.url }}"
data-name="{{ project.name }}"
data-method="clone"
onclick="installProject('{{ project.url }}', '{{ project.name }}', 'clone')">
<i class="fas fa-download me-1"></i><i class="fab fa-git-alt me-1"></i>Installieren
</button>
{% endif %}
<!-- Zusätzliche Buttons -->
<div class="btn-group btn-group-sm" role="group">
{% if project.metadata and project.metadata.documentation %}
<a href="{{ project.metadata.documentation }}" target="_blank" class="btn btn-outline-secondary">
<i class="fas fa-book"></i> Docs
<!-- Zusätzliche Action-Buttons -->
<div class="btn-group btn-group-sm w-100" role="group">
{% if project.demo and project.demo.available %}
<a href="{{ project.demo.url }}" target="_blank" class="btn btn-outline-primary">
<i class="fas fa-play me-1"></i>Demo
</a>
{% endif %}
{% if project.metadata and project.metadata.documentation and project.metadata.documentation.available %}
<a href="{{ project.metadata.documentation.url }}" target="_blank" class="btn btn-outline-secondary">
<i class="fas fa-book me-1"></i>Docs
</a>
{% endif %}
{% if project.metadata and project.metadata.homepage %}
<a href="{{ project.metadata.homepage }}" target="_blank" class="btn btn-outline-secondary">
<i class="fas fa-home"></i> Home
<i class="fas fa-home me-1"></i>Home
</a>
{% endif %}
<button class="btn btn-outline-secondary" onclick="showProjectDetails('{{ project.name }}')">
<i class="fas fa-info-circle"></i> Details
<button class="btn btn-outline-info" onclick="showProjectDetails('{{ project.name }}')">
<i class="fas fa-info-circle me-1"></i>Details
</button>
</div>
</div>

View File

@@ -1,166 +0,0 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Icon Test</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<!-- FontAwesome Icons -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"
integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw=="
crossorigin="anonymous" referrerpolicy="no-referrer">
<!-- Bootstrap Icons Fallback -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
</head>
<body class="p-4">
<div class="container">
<h1>Icon Test</h1>
<div class="row">
<div class="col-md-6">
<h3>FontAwesome Icons</h3>
<div class="d-flex flex-wrap gap-3 mb-4">
<i class="fas fa-home fa-2x text-primary" title="Home"></i>
<i class="fas fa-cube fa-2x text-info" title="Docker (Cube)"></i>
<i class="fas fa-server fa-2x text-success" title="Server"></i>
<i class="fas fa-box fa-2x text-warning" title="Container"></i>
<i class="fas fa-layer-group fa-2x text-secondary" title="Images"></i>
<i class="fas fa-cog fa-2x text-muted" title="Settings"></i>
<i class="fas fa-play fa-2x text-success" title="Start"></i>
<i class="fas fa-stop fa-2x text-danger" title="Stop"></i>
<i class="fas fa-sync-alt fa-2x text-primary" title="Refresh"></i>
<i class="fas fa-stethoscope fa-2x text-info" title="Diagnose"></i>
</div>
</div>
<div class="col-md-6">
<h3>Bootstrap Icons</h3>
<div class="d-flex flex-wrap gap-3 mb-4">
<i class="bi bi-house-fill fs-1 text-primary" title="Home"></i>
<i class="bi bi-app-indicator fs-1 text-info" title="Docker"></i>
<i class="bi bi-server fs-1 text-success" title="Server"></i>
<i class="bi bi-box fs-1 text-warning" title="Container"></i>
<i class="bi bi-layers fs-1 text-secondary" title="Images"></i>
<i class="bi bi-gear-fill fs-1 text-muted" title="Settings"></i>
<i class="bi bi-play-fill fs-1 text-success" title="Start"></i>
<i class="bi bi-stop-fill fs-1 text-danger" title="Stop"></i>
<i class="bi bi-arrow-repeat fs-1 text-primary" title="Refresh"></i>
<i class="bi bi-heart-pulse fs-1 text-info" title="Diagnose"></i>
</div>
</div>
</div>
<div class="alert alert-info">
<h5>Icon Test Status:</h5>
<p id="fontAwesomeStatus">FontAwesome: <span class="text-muted">Wird geprüft...</span></p>
<p id="bootstrapIconStatus">Bootstrap Icons: <span class="text-muted">Wird geprüft...</span></p>
</div>
<button class="btn btn-primary" onclick="testIconFallback()">
<i class="fas fa-test"></i> Test Icon Fallback
</button>
<button class="btn btn-secondary" onclick="showIconMapping()">
<i class="fas fa-list"></i> Zeige Icon-Mapping
</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
function checkIconLibraries() {
// FontAwesome Test
const faTest = document.createElement('i');
faTest.className = 'fas fa-home';
faTest.style.visibility = 'hidden';
faTest.style.position = 'absolute';
document.body.appendChild(faTest);
setTimeout(() => {
const faComputed = window.getComputedStyle(faTest);
const faFamily = faComputed.getPropertyValue('font-family');
const faLoaded = faFamily.includes('Font Awesome');
document.getElementById('fontAwesomeStatus').innerHTML =
`FontAwesome: <span class="${faLoaded ? 'text-success' : 'text-danger'}">${faLoaded ? 'Geladen' : 'Nicht verfügbar'}</span>`;
document.body.removeChild(faTest);
// Bootstrap Icons Test
const biTest = document.createElement('i');
biTest.className = 'bi bi-house';
biTest.style.visibility = 'hidden';
biTest.style.position = 'absolute';
document.body.appendChild(biTest);
setTimeout(() => {
const biComputed = window.getComputedStyle(biTest);
const biFamily = biComputed.getPropertyValue('font-family');
const biLoaded = biFamily.includes('bootstrap-icons') || biComputed.content !== 'none';
document.getElementById('bootstrapIconStatus').innerHTML =
`Bootstrap Icons: <span class="${biLoaded ? 'text-success' : 'text-danger'}">${biLoaded ? 'Geladen' : 'Nicht verfügbar'}</span>`;
document.body.removeChild(biTest);
}, 100);
}, 100);
}
function testIconFallback() {
// Ersetze alle FontAwesome Icons mit Bootstrap Icons
const iconMap = {
'fas fa-home': 'bi bi-house-fill',
'fas fa-cube': 'bi bi-cube',
'fas fa-server': 'bi bi-server',
'fas fa-box': 'bi bi-box',
'fas fa-layer-group': 'bi bi-layers',
'fas fa-cog': 'bi bi-gear-fill',
'fas fa-play': 'bi bi-play-fill',
'fas fa-stop': 'bi bi-stop-fill',
'fas fa-sync-alt': 'bi bi-arrow-repeat',
'fas fa-stethoscope': 'bi bi-heart-pulse'
};
Object.keys(iconMap).forEach(faClass => {
const elements = document.querySelectorAll(`i.${faClass.replace(/\s+/g, '.')}`);
elements.forEach(el => {
el.className = el.className.replace(faClass, iconMap[faClass]);
});
});
alert('Icon Fallback angewendet!');
}
function showIconMapping() {
const mapping = {
'fas fa-home': 'bi bi-house-fill',
'fas fa-docker': 'bi bi-app-indicator',
'fas fa-server': 'bi bi-server',
'fas fa-box': 'bi bi-box',
'fas fa-layer-group': 'bi bi-layers',
'fas fa-cog': 'bi bi-gear-fill',
'fas fa-play': 'bi bi-play-fill',
'fas fa-stop': 'bi bi-stop-fill',
'fas fa-sync-alt': 'bi bi-arrow-repeat',
'fas fa-stethoscope': 'bi bi-heart-pulse'
};
let mappingText = 'Icon Mapping:\n\n';
Object.entries(mapping).forEach(([fa, bi]) => {
mappingText += `${fa}${bi}\n`;
});
alert(mappingText);
}
// Test beim Laden der Seite
document.addEventListener('DOMContentLoaded', function() {
checkIconLibraries();
});
</script>
</body>
</html>