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:
169
config.json
169
config.json
@@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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()
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -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>
|
||||
|
||||
166
test_icons.html
166
test_icons.html
@@ -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>
|
||||
Reference in New Issue
Block a user