modified: app.py
modified: config.json modified: projects_list.json modified: templates/available_projects.html
This commit is contained in:
120
app.py
120
app.py
@@ -6,6 +6,9 @@ import json
|
||||
import re
|
||||
import time
|
||||
import shutil
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import tempfile
|
||||
from urllib.parse import urlparse
|
||||
import docker
|
||||
import yaml
|
||||
@@ -493,6 +496,66 @@ class ProjectManager:
|
||||
except Exception as e:
|
||||
return False, f"Fehler beim Herunterladen des Images: {str(e)}"
|
||||
|
||||
def load_docker_image_from_url(self, image_url, project_name):
|
||||
"""Lade Docker-Image von einer URL herunter (z.B. .tar Datei)"""
|
||||
if not self.docker_available:
|
||||
return False, "Docker ist nicht verfügbar"
|
||||
|
||||
try:
|
||||
print(f"Lade Docker-Image von URL herunter: {image_url}")
|
||||
|
||||
# Temporärer Dateiname für Download
|
||||
with tempfile.NamedTemporaryFile(suffix='.tar', delete=False) as temp_file:
|
||||
temp_path = temp_file.name
|
||||
|
||||
# Image von URL herunterladen
|
||||
print(f"Lade Datei herunter: {image_url}")
|
||||
urllib.request.urlretrieve(image_url, temp_path)
|
||||
|
||||
# Docker-Image aus Datei laden
|
||||
print(f"Importiere Docker-Image aus Datei...")
|
||||
with open(temp_path, 'rb') as f:
|
||||
images = self.docker_client.images.load(f)
|
||||
|
||||
# Versuche das Image mit Projektname zu taggen
|
||||
if images:
|
||||
image = images[0] if isinstance(images, list) else images
|
||||
image.tag(f"{project_name}:latest")
|
||||
print(f"✓ Docker-Image von URL erfolgreich geladen und getaggt als {project_name}:latest")
|
||||
else:
|
||||
return False, "Kein Image aus der geladenen Datei gefunden"
|
||||
|
||||
# Temporäre Datei löschen
|
||||
os.unlink(temp_path)
|
||||
|
||||
# Erstelle minimales Projektverzeichnis für Konfiguration
|
||||
project_path = os.path.join(PROJECTS_DIR, project_name)
|
||||
os.makedirs(project_path, exist_ok=True)
|
||||
|
||||
# Erstelle .dockerurl Datei um zu markieren, dass es ein URL-Download ist
|
||||
with open(os.path.join(project_path, '.dockerurl'), 'w', encoding='utf-8') as f:
|
||||
json.dump({
|
||||
'image_url': image_url,
|
||||
'loaded_at': datetime.now().isoformat(),
|
||||
'installation_method': 'docker_url'
|
||||
}, f, indent=2)
|
||||
|
||||
return True, f"Docker-Image {project_name} erfolgreich von URL geladen"
|
||||
|
||||
except urllib.error.URLError as e:
|
||||
return False, f"URL-Fehler beim Herunterladen: {str(e)}"
|
||||
except docker.errors.APIError as e:
|
||||
return False, f"Docker-API-Fehler beim Laden: {str(e)}"
|
||||
except Exception as e:
|
||||
return False, f"Fehler beim Laden des Images von URL: {str(e)}"
|
||||
finally:
|
||||
# Stelle sicher, dass temporäre Datei gelöscht wird
|
||||
try:
|
||||
if 'temp_path' in locals() and os.path.exists(temp_path):
|
||||
os.unlink(temp_path)
|
||||
except:
|
||||
pass
|
||||
|
||||
def get_installation_methods(self, project):
|
||||
"""Ermittle verfügbare Installationsmethoden für ein Projekt"""
|
||||
methods = []
|
||||
@@ -1210,25 +1273,73 @@ def install_project():
|
||||
})
|
||||
|
||||
# Neue Installation basierend auf Methode
|
||||
if installation_method == 'image':
|
||||
# Docker-Image herunterladen
|
||||
if installation_method in ['image', 'docker_image', 'docker_registry']:
|
||||
# Docker-Image herunterladen (aus Registry)
|
||||
success, message = project_manager.pull_docker_image(project_url, project_name)
|
||||
|
||||
if success:
|
||||
# Nach erfolgreichem Download die Version speichern
|
||||
try:
|
||||
project_manager.save_project_version(project_name, project_url, installation_method='image')
|
||||
project_manager.save_project_version(project_name, project_url, installation_method=installation_method)
|
||||
except Exception as e:
|
||||
print(f"Warnung: Konnte Version nach Image-Download nicht speichern: {e}")
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': f'{message}. Bereit zum Starten.',
|
||||
'method': 'image'
|
||||
'method': installation_method
|
||||
})
|
||||
else:
|
||||
return jsonify({'success': False, 'message': message})
|
||||
|
||||
elif installation_method in ['docker_url', 'docker_file']:
|
||||
# Docker-Image von URL herunterladen (z.B. .tar Datei)
|
||||
success, message = project_manager.load_docker_image_from_url(project_url, project_name)
|
||||
|
||||
if success:
|
||||
try:
|
||||
project_manager.save_project_version(project_name, project_url, installation_method=installation_method)
|
||||
except Exception as e:
|
||||
print(f"Warnung: Konnte Version nach URL-Download nicht speichern: {e}")
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': f'{message}. Bereit zum Starten.',
|
||||
'method': installation_method
|
||||
})
|
||||
else:
|
||||
return jsonify({'success': False, 'message': message})
|
||||
|
||||
elif installation_method in ['dockerfile', 'docker_build']:
|
||||
# Dockerfile-basierte Installation: Klone Repository und baue Docker-Image
|
||||
print(f"🐳 Dockerfile-Installation für {project_name} von {project_url}")
|
||||
|
||||
# Erst klonen, dann Docker-Image bauen
|
||||
success, message = project_manager.clone_project(project_url, project_name)
|
||||
|
||||
if success:
|
||||
# Versuche Docker-Image aus Dockerfile zu bauen
|
||||
build_success, build_message = project_manager.build_project(project_name)
|
||||
if build_success:
|
||||
# Nach erfolgreichem Build die Version speichern
|
||||
try:
|
||||
project_manager.save_project_version(project_name, project_url, installation_method='dockerfile')
|
||||
except Exception as e:
|
||||
print(f"Warnung: Konnte Version nach Docker-Build nicht speichern: {e}")
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': f'Projekt geklont und Docker-Image gebaut: {build_message}',
|
||||
'method': 'dockerfile'
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': f'Projekt geklont, aber Docker-Build fehlgeschlagen: {build_message}'
|
||||
})
|
||||
else:
|
||||
return jsonify({'success': False, 'message': message})
|
||||
|
||||
else:
|
||||
# Standard: Git Clone
|
||||
success, message = project_manager.clone_project(project_url, project_name)
|
||||
@@ -1792,6 +1903,7 @@ def api_check_port(port):
|
||||
'available': available,
|
||||
'message': f'Port {port} ist {"verfügbar" if available else "belegt"}'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"description": "Quellcode klonen und selbst bauen"
|
||||
},
|
||||
"docker_registry": {
|
||||
"available": false,
|
||||
"available": true,
|
||||
"url": "docker.io/simolzimol/quizify:1.3.0",
|
||||
"type": "docker_registry",
|
||||
"description": "Offizielles Docker-Image von Registry"
|
||||
@@ -57,7 +57,7 @@
|
||||
"description": "Docker-Image-Datei von Webserver"
|
||||
},
|
||||
"docker_url": {
|
||||
"available": false,
|
||||
"available": true,
|
||||
"url": "https://simolzimol.eu/images/quizify-1.3.0.tar",
|
||||
"type": "docker_url",
|
||||
"description": "Docker-Image direkt von URL laden"
|
||||
@@ -73,7 +73,7 @@
|
||||
"license": "MIT",
|
||||
"homepage": "https://gitea.simolzimol.net/Simon/quizify",
|
||||
"documentation": {
|
||||
"available": true,
|
||||
"available": false,
|
||||
"url": "https://gitea.simolzimol.net/Simon/quizify/wiki"
|
||||
},
|
||||
"issues": {
|
||||
|
||||
@@ -34,9 +34,21 @@
|
||||
"url": "docker.io/simolzimol/quizify:1.3.0",
|
||||
"type": "docker",
|
||||
"description": "Vorgefertigtes Docker-Image herunterladen"
|
||||
},
|
||||
"docker_registry": {
|
||||
"available": true,
|
||||
"url": "simolzimol/quizify:latest",
|
||||
"type": "docker",
|
||||
"description": "Von Docker Registry herunterladen"
|
||||
},
|
||||
"docker_file": {
|
||||
"available": true,
|
||||
"url": "https://simolzimol.eu/images/quizify-1.3.0.tar",
|
||||
"type": "docker_file",
|
||||
"description": "Docker-Image-Datei von Webserver herunterladen"
|
||||
}
|
||||
},
|
||||
"preferred": "clone"
|
||||
"preferred": "image"
|
||||
},
|
||||
"metadata": {
|
||||
"created": "2024-01-15",
|
||||
@@ -205,6 +217,24 @@
|
||||
"url": "docker.io/example/media-server:2.1.0",
|
||||
"type": "docker",
|
||||
"description": "Fertiges Docker-Image"
|
||||
},
|
||||
"docker_registry": {
|
||||
"available": true,
|
||||
"url": "example/media-server:2.1.0",
|
||||
"type": "docker",
|
||||
"description": "Von Docker Hub Registry"
|
||||
},
|
||||
"docker_url": {
|
||||
"available": true,
|
||||
"url": "https://github.com/example/media-server/releases/download/v2.1.0/media-server-docker.tar",
|
||||
"type": "docker",
|
||||
"description": "Docker-Image als .tar Datei herunterladen"
|
||||
},
|
||||
"docker_file": {
|
||||
"available": true,
|
||||
"url": "https://github.com/example/media-server",
|
||||
"type": "dockerfile",
|
||||
"description": "Mit Dockerfile selbst bauen"
|
||||
}
|
||||
},
|
||||
"preferred": "image"
|
||||
@@ -265,5 +295,108 @@
|
||||
"cpu_cores": 2,
|
||||
"disk_mb": 1000
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nginx/nginx",
|
||||
"name": "nginx-server",
|
||||
"description": "High-performance HTTP server und reverse proxy",
|
||||
"language": "C",
|
||||
"tags": ["webserver", "proxy", "http", "nginx", "performance"],
|
||||
"category": "Web Server",
|
||||
"docker_port": 80,
|
||||
"environment_vars": {
|
||||
"NGINX_HOST": "localhost",
|
||||
"NGINX_PORT": "80"
|
||||
},
|
||||
"installation": {
|
||||
"methods": {
|
||||
"clone": {
|
||||
"available": true,
|
||||
"url": "https://github.com/nginx/nginx",
|
||||
"type": "git",
|
||||
"description": "Nginx Quellcode klonen und kompilieren"
|
||||
},
|
||||
"image": {
|
||||
"available": true,
|
||||
"url": "nginx:latest",
|
||||
"type": "docker",
|
||||
"description": "Offizielles Nginx Docker-Image"
|
||||
},
|
||||
"docker_registry": {
|
||||
"available": true,
|
||||
"url": "nginx:alpine",
|
||||
"type": "docker",
|
||||
"description": "Lightweight Alpine Nginx von Registry"
|
||||
},
|
||||
"docker_file": {
|
||||
"available": true,
|
||||
"url": "https://hub.docker.com/v2/repositories/nginx/tags/alpine/images",
|
||||
"type": "docker_file",
|
||||
"description": "Nginx Docker-Image als Archiv herunterladen"
|
||||
},
|
||||
"docker_url": {
|
||||
"available": true,
|
||||
"url": "https://github.com/nginxinc/docker-nginx/archive/refs/heads/master.tar.gz",
|
||||
"type": "docker_url",
|
||||
"description": "Nginx Docker-Build von GitHub"
|
||||
}
|
||||
},
|
||||
"preferred": "image"
|
||||
},
|
||||
"metadata": {
|
||||
"created": "1994-01-01",
|
||||
"last_updated": "2025-07-06",
|
||||
"version": "1.24.0",
|
||||
"author": "Igor Sysoev",
|
||||
"license": "BSD-2-Clause",
|
||||
"homepage": "https://nginx.org",
|
||||
"documentation": {
|
||||
"available": true,
|
||||
"url": "https://nginx.org/en/docs/"
|
||||
},
|
||||
"issues": {
|
||||
"available": true,
|
||||
"url": "https://github.com/nginx/nginx/issues"
|
||||
},
|
||||
"downloads": 50000,
|
||||
"stars": 18500
|
||||
},
|
||||
"features": [
|
||||
"High-performance HTTP server",
|
||||
"Reverse proxy server",
|
||||
"Load balancer",
|
||||
"Mail proxy server",
|
||||
"HTTP cache",
|
||||
"SSL/TLS termination",
|
||||
"WebSocket support",
|
||||
"HTTP/2 support"
|
||||
],
|
||||
"screenshots": {
|
||||
"available": false,
|
||||
"urls": []
|
||||
},
|
||||
"demo": {
|
||||
"available": false,
|
||||
"url": "",
|
||||
"description": ""
|
||||
},
|
||||
"installation_notes": "Standard Nginx-Server. Kann als Webserver oder Reverse Proxy verwendet werden.",
|
||||
"config_hints": {
|
||||
"env_example": "Standard Nginx-Konfiguration",
|
||||
"dockerfile": "Multi-stage Build verfügbar",
|
||||
"docker_compose": "Einfache Nginx-Konfiguration"
|
||||
},
|
||||
"rating": {
|
||||
"score": 4.8,
|
||||
"reviews": 850,
|
||||
"downloads": 50000
|
||||
},
|
||||
"size_mb": 25,
|
||||
"install_time": "30 Sekunden",
|
||||
"min_resources": {
|
||||
"ram_mb": 64,
|
||||
"cpu_cores": 1,
|
||||
"disk_mb": 100
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -255,8 +255,10 @@
|
||||
data-method="{{ preferred_method }}"
|
||||
onclick="installProject('{{ preferred.url }}', '{{ project.name }}', '{{ preferred_method }}')">
|
||||
<i class="fas fa-download me-1"></i>
|
||||
{% if preferred_method == 'image' %}
|
||||
{% if preferred_method in ['image', 'docker_registry', 'docker_url', 'docker_file'] %}
|
||||
<i class="fab fa-docker me-1"></i>Docker
|
||||
{% elif preferred_method in ['docker_build', 'dockerfile'] %}
|
||||
<i class="fas fa-cog me-1"></i>Build
|
||||
{% else %}
|
||||
<i class="fab fa-git-alt me-1"></i>Code
|
||||
{% endif %}
|
||||
@@ -272,8 +274,18 @@
|
||||
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 %}
|
||||
{% elif method_type == 'docker_registry' %}
|
||||
<i class="fab fa-docker me-2 text-info"></i>Docker Registry Pull
|
||||
{% elif method_type == 'docker_url' %}
|
||||
<i class="fas fa-cloud-download-alt me-2 text-warning"></i>Docker .tar von URL laden
|
||||
{% elif method_type == 'docker_file' %}
|
||||
<i class="fas fa-file-archive me-2 text-warning"></i>Docker-Image-Datei herunterladen
|
||||
{% elif method_type == 'docker_build' or method_type == 'dockerfile' %}
|
||||
<i class="fas fa-cog me-2 text-secondary"></i>Mit Dockerfile bauen
|
||||
{% elif method_type == 'clone' %}
|
||||
<i class="fab fa-git-alt me-2 text-success"></i>Quellcode klonen
|
||||
{% else %}
|
||||
<i class="fas fa-download me-2"></i>{{ method_type|title }}
|
||||
{% endif %}
|
||||
<small class="text-muted d-block">{{ method_info.description }}</small>
|
||||
</a>
|
||||
|
||||
Reference in New Issue
Block a user