modified: config.json modified: templates/available_projects.html modified: templates/base.html new file: templates/custom_install.html
397 lines
19 KiB
HTML
397 lines
19 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}{{ project.name }} - Installation - {{ super() }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<!-- Header -->
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<div>
|
|
<h2>
|
|
<i class="fas fa-download me-2"></i>{{ project.name }} Installation
|
|
</h2>
|
|
<p class="text-muted mb-0">{{ project.description }}</p>
|
|
</div>
|
|
<div>
|
|
<a href="{{ url_for('available_projects') }}" class="btn btn-outline-secondary">
|
|
<i class="fas fa-arrow-left"></i> Zurück zur Liste
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<!-- Linke Spalte: Projektinfo -->
|
|
<div class="col-lg-4">
|
|
<!-- Projekt-Übersicht -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-info-circle me-2"></i>Projekt-Übersicht
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if project.language %}
|
|
<div class="mb-2">
|
|
<strong>Sprache:</strong>
|
|
<span class="badge bg-primary">{{ project.language }}</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if project.category %}
|
|
<div class="mb-2">
|
|
<strong>Kategorie:</strong>
|
|
<span class="badge bg-info">{{ project.category }}</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if project.tags %}
|
|
<div class="mb-2">
|
|
<strong>Tags:</strong><br>
|
|
{% for tag in project.tags %}
|
|
<span class="badge bg-secondary me-1">{{ tag }}</span>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if project.requirements %}
|
|
<div class="mb-2">
|
|
<strong>Anforderungen:</strong>
|
|
<ul class="small mt-1 mb-0">
|
|
{% if project.requirements.min_memory %}
|
|
<li>RAM: {{ project.requirements.min_memory }}</li>
|
|
{% endif %}
|
|
{% if project.requirements.min_disk %}
|
|
<li>Speicher: {{ project.requirements.min_disk }}</li>
|
|
{% endif %}
|
|
{% if project.requirements.ports %}
|
|
<li>Ports: {{ project.requirements.ports|join(', ') }}</li>
|
|
{% endif %}
|
|
</ul>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Empfohlene Installation -->
|
|
{% if preferred_method %}
|
|
<div class="card mb-4">
|
|
<div class="card-header bg-success text-white">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-star me-2"></i>Empfohlene Installation
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<div>
|
|
<strong>{{ preferred_method[0]|title }}</strong>
|
|
<p class="text-muted small mb-0">{{ preferred_method[1].description }}</p>
|
|
</div>
|
|
<div>
|
|
{% if preferred_method[0] in ['image', 'docker_registry', 'docker_url', 'docker_file'] %}
|
|
<i class="fab fa-docker fa-2x text-primary"></i>
|
|
{% elif preferred_method[0] in ['docker_build', 'dockerfile'] %}
|
|
<i class="fas fa-cog fa-2x text-secondary"></i>
|
|
{% else %}
|
|
<i class="fab fa-git-alt fa-2x text-success"></i>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<button class="btn btn-success w-100"
|
|
onclick="installProject('{{ preferred_method[1].url }}', '{{ project.name }}', '{{ preferred_method[0] }}')">
|
|
<i class="fas fa-download me-2"></i>Jetzt installieren
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Schnellinfo -->
|
|
{% if project.metadata %}
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-chart-line me-2"></i>Projekt-Info
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if project.metadata.author %}
|
|
<div class="d-flex justify-content-between mb-2">
|
|
<span>Autor:</span>
|
|
<span>{{ project.metadata.author }}</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if project.metadata.version %}
|
|
<div class="d-flex justify-content-between mb-2">
|
|
<span>Version:</span>
|
|
<span class="badge bg-primary">{{ project.metadata.version }}</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if project.metadata.last_updated %}
|
|
<div class="d-flex justify-content-between mb-2">
|
|
<span>Letzte Aktualisierung:</span>
|
|
<span>{{ project.metadata.last_updated }}</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if project.metadata.downloads %}
|
|
<div class="d-flex justify-content-between mb-2">
|
|
<span>Downloads:</span>
|
|
<span class="badge bg-info">{{ project.metadata.downloads }}</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if project.metadata.stars %}
|
|
<div class="d-flex justify-content-between mb-2">
|
|
<span>Stars:</span>
|
|
<span class="badge bg-warning">{{ project.metadata.stars }}</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Rechte Spalte: Installationsmethoden -->
|
|
<div class="col-lg-8">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-tools me-2"></i>Verfügbare Installationsmethoden
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if available_methods %}
|
|
<div class="row">
|
|
{% for method_type, method_info in available_methods %}
|
|
<div class="col-md-6 mb-4">
|
|
<div class="card h-100 {% if preferred_method and method_type == preferred_method[0] %}border-success{% endif %}">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<div class="d-flex align-items-center">
|
|
{% if method_type == 'image' %}
|
|
<i class="fab fa-docker fa-lg text-primary me-2"></i>
|
|
<strong>Docker Image</strong>
|
|
{% elif method_type == 'docker_registry' %}
|
|
<i class="fab fa-docker fa-lg text-info me-2"></i>
|
|
<strong>Docker Registry</strong>
|
|
{% elif method_type == 'docker_url' %}
|
|
<i class="fas fa-cloud-download-alt fa-lg text-warning me-2"></i>
|
|
<strong>Docker URL</strong>
|
|
{% elif method_type == 'docker_file' %}
|
|
<i class="fas fa-file-archive fa-lg text-warning me-2"></i>
|
|
<strong>Docker File</strong>
|
|
{% elif method_type == 'docker_build' or method_type == 'dockerfile' %}
|
|
<i class="fas fa-cog fa-lg text-secondary me-2"></i>
|
|
<strong>Docker Build</strong>
|
|
{% elif method_type == 'clone' %}
|
|
<i class="fab fa-git-alt fa-lg text-success me-2"></i>
|
|
<strong>Git Clone</strong>
|
|
{% elif method_type == 'native_python' %}
|
|
<i class="fab fa-python fa-lg text-success me-2"></i>
|
|
<strong>Python Native</strong>
|
|
{% elif method_type == 'native_nodejs' %}
|
|
<i class="fab fa-node-js fa-lg text-success me-2"></i>
|
|
<strong>Node.js Native</strong>
|
|
{% elif method_type == 'native_batch' %}
|
|
<i class="fas fa-terminal fa-lg text-info me-2"></i>
|
|
<strong>Windows Batch</strong>
|
|
{% elif method_type == 'native_shell' %}
|
|
<i class="fas fa-terminal fa-lg text-success me-2"></i>
|
|
<strong>Linux/macOS Shell</strong>
|
|
{% else %}
|
|
<i class="fas fa-download fa-lg me-2"></i>
|
|
<strong>{{ method_type|title }}</strong>
|
|
{% endif %}
|
|
</div>
|
|
{% if preferred_method and method_type == preferred_method[0] %}
|
|
<span class="badge bg-success">Empfohlen</span>
|
|
{% endif %}
|
|
</div>
|
|
<div class="card-body d-flex flex-column">
|
|
<p class="text-muted mb-3">{{ method_info.description }}</p>
|
|
|
|
<!-- Zusätzliche Infos je nach Typ -->
|
|
{% if method_type in ['image', 'docker_registry', 'docker_url', 'docker_file'] %}
|
|
<div class="mb-3">
|
|
<small class="text-info">
|
|
<i class="fas fa-info-circle me-1"></i>
|
|
Fertige Container-Installation - Schnell und einfach
|
|
</small>
|
|
</div>
|
|
{% elif method_type in ['docker_build', 'dockerfile'] %}
|
|
<div class="mb-3">
|
|
<small class="text-warning">
|
|
<i class="fas fa-clock me-1"></i>
|
|
Erstellt Container aus Quellcode - Dauert länger
|
|
</small>
|
|
</div>
|
|
{% elif method_type == 'clone' %}
|
|
<div class="mb-3">
|
|
<small class="text-success">
|
|
<i class="fas fa-code me-1"></i>
|
|
Vollständiger Quellcode - Maximale Kontrolle
|
|
</small>
|
|
</div>
|
|
{% elif method_type.startswith('native_') %}
|
|
<div class="mb-3">
|
|
<small class="text-primary">
|
|
<i class="fas fa-desktop me-1"></i>
|
|
Native Ausführung - Ohne Container
|
|
</small>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if method_info.url %}
|
|
<div class="mb-3">
|
|
<small class="text-muted">
|
|
<strong>URL:</strong>
|
|
<code class="small">{{ method_info.url|truncate(50) }}</code>
|
|
</small>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="mt-auto">
|
|
<button class="btn {% if preferred_method and method_type == preferred_method[0] %}btn-success{% else %}btn-outline-primary{% endif %} w-100"
|
|
onclick="installProject('{{ method_info.url }}', '{{ project.name }}', '{{ method_type }}')">
|
|
<i class="fas fa-download me-2"></i>
|
|
{% if preferred_method and method_type == preferred_method[0] %}
|
|
Empfohlene Installation
|
|
{% else %}
|
|
Mit dieser Methode installieren
|
|
{% endif %}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="fas fa-exclamation-triangle fa-3x text-warning mb-3"></i>
|
|
<h5 class="text-muted">Keine Installationsmethoden verfügbar</h5>
|
|
<p class="text-muted">Für dieses Projekt sind keine Installationsmethoden konfiguriert.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Progress Modal -->
|
|
<div class="modal fade" id="installProgressModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">
|
|
<i class="fas fa-download me-2"></i>Installation läuft...
|
|
</h5>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="text-center">
|
|
<div class="spinner-border text-primary mb-3" role="status">
|
|
<span class="visually-hidden">Loading...</span>
|
|
</div>
|
|
<h5 id="installStatus">{{ project.name }} wird installiert...</h5>
|
|
<p class="text-muted mb-0" id="installMethod">Bitte warten Sie einen Moment.</p>
|
|
</div>
|
|
<div class="progress mt-3">
|
|
<div class="progress-bar progress-bar-striped progress-bar-animated"
|
|
role="progressbar" style="width: 100%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>
|
|
// Installation mit Progress Modal
|
|
function installProject(url, name, method) {
|
|
const button = event.target;
|
|
const originalText = button.innerHTML;
|
|
|
|
// Show progress modal
|
|
const modal = new bootstrap.Modal(document.getElementById('installProgressModal'));
|
|
document.getElementById('installStatus').textContent = `${name} wird installiert...`;
|
|
document.getElementById('installMethod').textContent = `Methode: ${method}`;
|
|
modal.show();
|
|
|
|
// Disable all install buttons
|
|
document.querySelectorAll('.btn').forEach(btn => {
|
|
if (btn.textContent.includes('installieren')) {
|
|
btn.disabled = true;
|
|
}
|
|
});
|
|
|
|
console.log(`🚀 Installiere ${name} via ${method} von ${url}`);
|
|
|
|
fetch('/install_project', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
},
|
|
body: `project_url=${encodeURIComponent(url)}&project_name=${encodeURIComponent(name)}&installation_method=${encodeURIComponent(method)}`
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
modal.hide();
|
|
|
|
if (data.success) {
|
|
// Success notification
|
|
showAlert('success', `${name} wurde erfolgreich installiert!`);
|
|
|
|
// Redirect to project details or dashboard after 2 seconds
|
|
setTimeout(() => {
|
|
window.location.href = '/project_details/' + encodeURIComponent(name);
|
|
}, 2000);
|
|
} else {
|
|
showAlert('danger', `Installation fehlgeschlagen: ${data.message}`);
|
|
|
|
// Re-enable buttons
|
|
document.querySelectorAll('.btn').forEach(btn => {
|
|
if (btn.textContent.includes('installieren')) {
|
|
btn.disabled = false;
|
|
}
|
|
});
|
|
}
|
|
})
|
|
.catch(error => {
|
|
modal.hide();
|
|
showAlert('danger', `Netzwerkfehler bei Installation von ${name}: ${error}`);
|
|
|
|
// Re-enable buttons
|
|
document.querySelectorAll('.btn').forEach(btn => {
|
|
if (btn.textContent.includes('installieren')) {
|
|
btn.disabled = false;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function showAlert(type, message) {
|
|
const alertHtml = `
|
|
<div class="alert alert-${type} alert-dismissible fade show" role="alert">
|
|
<i class="fas fa-${type === 'success' ? 'check-circle' : 'exclamation-triangle'} me-2"></i>
|
|
${message}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
</div>
|
|
`;
|
|
|
|
// Insert alert at top of page
|
|
const container = document.querySelector('.container-fluid');
|
|
container.insertAdjacentHTML('afterbegin', alertHtml);
|
|
|
|
// Auto-remove after 5 seconds
|
|
setTimeout(() => {
|
|
const alert = container.querySelector('.alert');
|
|
if (alert) {
|
|
alert.remove();
|
|
}
|
|
}, 5000);
|
|
}
|
|
</script>
|
|
{% endblock %}
|