380 lines
20 KiB
HTML
380 lines
20 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{% block title %}AstraOS{% endblock %}</title>
|
|
<link rel="icon" href="{{ url_for('static', filename='logo.png') }}" type="image/png">
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
|
|
<style>
|
|
/* Custom scrollbar */
|
|
::-webkit-scrollbar {
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
::-webkit-scrollbar-track {
|
|
background: #111827;
|
|
}
|
|
::-webkit-scrollbar-thumb {
|
|
background-color: #4b5563;
|
|
border-radius: 4px;
|
|
}
|
|
::-webkit-scrollbar-thumb:hover {
|
|
background-color: #6b7280;
|
|
}
|
|
|
|
body {
|
|
background: radial-gradient(circle at top, #1b1b26, #0a0a0f 70%);
|
|
overflow-x: hidden;
|
|
min-height: 100vh;
|
|
}
|
|
.custom-checkbox-label {
|
|
display: flex;
|
|
align-items: center;
|
|
cursor: pointer;
|
|
user-select: none;
|
|
}
|
|
.custom-checkbox-label input[type="checkbox"] {
|
|
display: none;
|
|
}
|
|
.custom-checkbox-box {
|
|
width: 20px;
|
|
height: 20px;
|
|
border: 2px solid #4a5568;
|
|
border-radius: 4px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
transition: background-color 0.2s, border-color 0.2s;
|
|
margin-right: 0.75rem;
|
|
border-color: #8b5cf6; /* purple-500 */
|
|
}
|
|
.custom-checkbox-label.maintenance .custom-checkbox-box {
|
|
border-color: #f59e0b; /* yellow-600 */
|
|
}
|
|
input[type="checkbox"]:checked + .custom-checkbox-box {
|
|
background-color: #8b5cf6; /* purple-500 */
|
|
border-color: #8b5cf6; /* purple-500 */
|
|
}
|
|
.custom-checkbox-label.maintenance input[type="checkbox"]:checked + .custom-checkbox-box {
|
|
background-color: #f59e0b; /* yellow-600 */
|
|
border-color: #f59e0b; /* yellow-600 */
|
|
}
|
|
input[type="checkbox"]:checked + .custom-checkbox-box svg {
|
|
display: block;
|
|
}
|
|
footer {
|
|
text-align: center;
|
|
padding: 1rem;
|
|
font-size: 0.875rem;
|
|
color: #888;
|
|
background: rgba(0,0,0,0.3);
|
|
}
|
|
#startup {
|
|
position: fixed;
|
|
top: 0; left: 0; right: 0; bottom: 0;
|
|
width: 100vw !important;
|
|
height: 100vh !important;
|
|
margin: 0 !important;
|
|
padding: 0 !important;
|
|
background: black !important;
|
|
z-index: 9999 !important;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
#star {
|
|
width: 64px;
|
|
height: 64px;
|
|
position: relative;
|
|
}
|
|
#star .blur {
|
|
position: absolute;
|
|
inset: 0;
|
|
border-radius: 50%;
|
|
filter: blur(12px);
|
|
opacity: 0.7;
|
|
background: rgba(192,132,252,0.7);
|
|
}
|
|
#startup p {
|
|
margin-top: 1rem;
|
|
font-size: 1.25rem;
|
|
letter-spacing: 0.05em;
|
|
opacity: 0.8;
|
|
color: #fff;
|
|
}
|
|
.animated-bg {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100vw;
|
|
height: 100vh;
|
|
z-index: 0;
|
|
pointer-events: none;
|
|
overflow: hidden;
|
|
}
|
|
.dot {
|
|
position: absolute;
|
|
width: 4px;
|
|
height: 4px;
|
|
border-radius: 50%;
|
|
background: rgba(192, 132, 252, 0.2);
|
|
animation: float 20s infinite linear;
|
|
}
|
|
.dot.maintenance {
|
|
background: rgba(243, 156, 18, 0.2);
|
|
}
|
|
@keyframes float {
|
|
0% { transform: translateY(100vh) scale(1); }
|
|
100% { transform: translateY(-10vh) scale(1.5); }
|
|
}
|
|
.toast-container {
|
|
position: fixed;
|
|
top: 1.5rem;
|
|
right: 1.5rem;
|
|
z-index: 9999;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.75rem;
|
|
}
|
|
.toast {
|
|
transform-origin: top right;
|
|
}
|
|
</style>
|
|
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
|
{% block head %}{% endblock %}
|
|
</head>
|
|
<body class="bg-gray-900 text-gray-200 font-sans antialiased">
|
|
<div class="flex h-screen overflow-hidden">
|
|
<!-- Sidebar -->
|
|
<aside class="fixed top-0 left-0 w-64 h-screen bg-black/40 border-r p-6 backdrop-blur-lg flex flex-col flex-shrink-0 z-20
|
|
{% if maintenance_active %}border-yellow-500/30{% else %}border-purple-500/20{% endif %}">
|
|
<h1 class="text-3xl font-bold mb-8 tracking-wide
|
|
{% if maintenance_active %}text-yellow-300{% else %}text-purple-300{% endif %}">AstraOS</h1>
|
|
|
|
<nav class="flex flex-col gap-2 text-gray-300 overflow-y-auto">
|
|
|
|
<a href="{{ url_for('dashboard') }}"
|
|
class="px-3 py-2 rounded-md transition flex-shrink-0
|
|
{% if active_page == 'dashboard' %}{% if maintenance_active %}bg-yellow-500/10 text-yellow-300{% else %}bg-purple-500/10 text-purple-300{% endif %}{% else %}
|
|
{% if maintenance_active %}hover:bg-yellow-500/10 hover:text-yellow-300{% else %}hover:bg-purple-500/10 hover:text-purple-300{% endif %}{% endif %}">
|
|
Dashboard
|
|
</a>
|
|
|
|
<a href="{{ url_for('status_page') }}"
|
|
class="px-3 py-2 rounded-md transition flex-shrink-0
|
|
{% if active_page == 'status_page' %}{% if maintenance_active %}bg-yellow-500/10 text-yellow-300{% else %}bg-purple-500/10 text-purple-300{% endif %}{% else %}
|
|
{% if maintenance_active %}hover:bg-yellow-500/10 hover:text-yellow-300{% else %}hover:bg-purple-500/10 hover:text-purple-300{% endif %}{% endif %}">
|
|
Status Page
|
|
</a>
|
|
|
|
<a href="{{ url_for('settings') }}"
|
|
class="px-3 py-2 rounded-md transition flex-shrink-0
|
|
{% if active_page == 'settings' %}{% if maintenance_active %}bg-yellow-500/10 text-yellow-300{% else %}bg-purple-500/10 text-purple-300{% endif %}{% else %}
|
|
{% if maintenance_active %}hover:bg-yellow-500/10 hover:text-yellow-300{% else %}hover:bg-purple-500/10 hover:text-purple-300{% endif %}{% endif %}">
|
|
Einstellungen
|
|
</a>
|
|
|
|
<div>
|
|
<button class="w-full text-left px-3 py-2 rounded-md transition flex justify-between items-center collapsible flex-shrink-0
|
|
{% if maintenance_active %}hover:bg-yellow-500/10 hover:text-yellow-300{% else %}hover:bg-purple-500/10 hover:text-purple-300{% endif %}"
|
|
data-open="{% if active_page in ['user_management', 'role_management', 'maintenance_management'] %}true{% else %}false{% endif %}">
|
|
<span>Administration</span>
|
|
<svg class="w-4 h-4 transform transition-transform"
|
|
fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M19 9l-7 7-7-7"></path>
|
|
</svg>
|
|
</button>
|
|
|
|
<div class="pl-4 mt-1 space-y-1 content hidden">
|
|
<a href="{{ url_for('user_management') }}"
|
|
class="block px-3 py-1 rounded-md transition
|
|
{% if active_page == 'user_management' %}{% if maintenance_active %}bg-yellow-500/10 text-yellow-300{% else %}bg-purple-500/10 text-purple-300{% endif %}{% else %}
|
|
{% if maintenance_active %}hover:bg-yellow-500/10 hover:text-yellow-300{% else %}hover:bg-purple-500/10 hover:text-purple-300{% endif %}{% endif %}">
|
|
👤 User Management
|
|
</a>
|
|
|
|
<a href="{{ url_for('role_management') }}"
|
|
class="block px-3 py-1 rounded-md transition
|
|
{% if active_page == 'role_management' %}{% if maintenance_active %}bg-yellow-500/10 text-yellow-300{% else %}bg-purple-500/10 text-purple-300{% endif %}{% else %}
|
|
{% if maintenance_active %}hover:bg-yellow-500/10 hover:text-yellow-300{% else %}hover:bg-purple-500/10 hover:text-purple-300{% endif %}{% endif %}">
|
|
🛡️ Role Management
|
|
</a>
|
|
{% if 'view_logs' in session.get('permissions', []) %}
|
|
<a href="{{ url_for('logs') }}"
|
|
class="block px-3 py-1 rounded-md transition
|
|
{% if active_page == 'logs' %}{% if maintenance_active %}bg-yellow-500/10 text-yellow-300{% else %}bg-purple-500/10 text-purple-300{% endif %}{% else %}
|
|
{% if maintenance_active %}hover:bg-yellow-500/10 hover:text-yellow-300{% else %}hover:bg-purple-500/10 hover:text-purple-300{% endif %}{% endif %}">
|
|
📜 Logs
|
|
</a>
|
|
{% endif %}
|
|
<a href="{{ url_for('maintenance_management') }}"
|
|
class="block px-3 py-1 rounded-md transition
|
|
{% if active_page == 'maintenance_management' %}{% if maintenance_active %}bg-yellow-500/10 text-yellow-300{% else %}bg-purple-500/10 text-purple-300{% endif %}{% else %}
|
|
{% if maintenance_active %}hover:bg-yellow-500/10 hover:text-yellow-300{% else %}hover:bg-purple-500/10 hover:text-purple-300{% endif %}{% endif %}">
|
|
🔧 Maintenance
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<button class="w-full text-left px-3 py-2 rounded-md transition flex justify-between items-center collapsible
|
|
{% if maintenance_active %}hover:bg-yellow-500/10 hover:text-yellow-300{% else %}hover:bg-purple-500/10 hover:text-purple-300{% endif %}"
|
|
data-open="{% if active_page in ['discord_bots'] %}true{% else %}false{% endif %}">
|
|
<span>Private Tools</span>
|
|
<svg class="w-4 h-4 transform transition-transform"
|
|
fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M19 9l-7 7-7-7"></path>
|
|
</svg>
|
|
</button>
|
|
|
|
<div class="pl-4 mt-1 space-y-1 content hidden">
|
|
{% if 'manage_discord_bots' in session.get('permissions', []) %}
|
|
<a href="{{ url_for('discord_bots') }}"
|
|
class="block px-3 py-1 rounded-md transition
|
|
{% if active_page == 'discord_bots' %}{% if maintenance_active %}bg-yellow-500/10 text-yellow-300{% else %}bg-purple-500/10 text-purple-300{% endif %}{% else %}
|
|
{% if maintenance_active %}hover:bg-yellow-500/10 hover:text-yellow-300{% else %}hover:bg-purple-500/10 hover:text-purple-300{% endif %}{% endif %}">
|
|
🤖 Discord Bots
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<button class="w-full text-left px-3 py-2 rounded-md transition flex justify-between items-center collapsible
|
|
{% if maintenance_active %}hover:bg-yellow-500/10 hover:text-yellow-300{% else %}hover:bg-purple-500/10 hover:text-purple-300{% endif %}"
|
|
data-open="{% if active_page in ['ip_analyzer'] %}true{% else %}false{% endif %}">
|
|
<span>Moonlab Tools</span>
|
|
<svg class="w-4 h-4 transform transition-transform"
|
|
fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M19 9l-7 7-7-7"></path>
|
|
</svg>
|
|
</button>
|
|
|
|
<div class="pl-4 mt-1 space-y-1 content hidden">
|
|
{% if 'view_ip_analyzer' in session.get('permissions', []) %}
|
|
<a href="{{ url_for('ip_analyzer') }}"
|
|
class="block px-3 py-1 rounded-md transition
|
|
{% if active_page == 'ip_analyzer' %}{% if maintenance_active %}bg-yellow-500/10 text-yellow-300{% else %}bg-purple-500/10 text-purple-300{% endif %}{% else %}
|
|
{% if maintenance_active %}hover:bg-yellow-500/10 hover:text-yellow-300{% else %}hover:bg-purple-500/10 hover:text-purple-300{% endif %}{% endif %}">
|
|
🌐 IP Analyzer
|
|
</a>
|
|
{% endif %}
|
|
<a href="https://intra.moon-lab.de" target="_blank" rel="noopener noreferrer" class="block px-3 py-1 rounded-md {% if maintenance_active %}hover:bg-yellow-500/10 hover:text-yellow-300{% else %}hover:bg-purple-500/10 hover:text-purple-300{% endif %}">🌐 Moonlab-Intranet</a>
|
|
{% if 'view_age_verification' in session.get('permissions', []) %}
|
|
<a href="{{ url_for('age_verification') }}" class="block px-3 py-1 rounded-md transition
|
|
{% if active_page == 'age_verification' %}{% if maintenance_active %}bg-yellow-500/10 text-yellow-300{% else %}bg-purple-500/10 text-purple-300{% endif %}{% else %}
|
|
{% if maintenance_active %}hover:bg-yellow-500/10 hover:text-yellow-300{% else %}hover:bg-purple-500/10 hover:text-purple-300{% endif %}{% endif %}">
|
|
🎂 Altersüberprüfung
|
|
</a>
|
|
{% endif %}
|
|
<a href="javascript:void(0);" onclick="alert('Diese Funktion wird in Kürze verfügbar sein.')" class="block px-3 py-1 rounded-md {% if maintenance_active %}hover:bg-yellow-500/10 hover:text-yellow-300{% else %}hover:bg-purple-500/10 hover:text-purple-300{% endif %}">🎉 Events</a>
|
|
</div>
|
|
</div>
|
|
|
|
</nav>
|
|
</aside>
|
|
|
|
<div class="ml-64 flex-1 flex flex-col min-h-screen">
|
|
<!-- FLASH MESSAGES -->
|
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
|
{% if messages %}
|
|
<div class="toast-container" x-data>
|
|
{% for category, message in messages %}
|
|
<div x-data="{ show: true }" x-init="setTimeout(() => show = false, 5000)" x-show="show"
|
|
x-transition:enter="toast transition ease-out duration-300"
|
|
x-transition:enter-start="opacity-0 translate-x-8"
|
|
x-transition:enter-end="opacity-100 translate-x-0"
|
|
x-transition:leave="toast transition ease-in duration-300"
|
|
x-transition:leave-start="opacity-100"
|
|
x-transition:leave-end="opacity-0"
|
|
class="max-w-md w-full bg-gray-800 shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden border border-{{ 'red' if category == 'danger' else ('green' if category == 'success' else ('blue' if category == 'info' else 'gray')) }}-500/40">
|
|
<div class="p-4">
|
|
<div class="flex items-start">
|
|
<div class="flex-shrink-0">
|
|
{% if category == 'success' %}
|
|
<svg class="h-6 w-6 text-green-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
|
|
{% elif category == 'danger' %}
|
|
<svg class="h-6 w-6 text-red-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
|
|
{% else %}
|
|
<svg class="h-6 w-6 text-blue-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
|
|
{% endif %}
|
|
</div>
|
|
<div class="ml-3 w-0 flex-1 pt-0.5">
|
|
<p class="text-sm font-medium text-gray-100">{{ message }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
{% endwith %}
|
|
<main class="flex-1 p-8 space-y-10" id="mainContent">
|
|
{% block content %}{% endblock %}
|
|
</main>
|
|
|
|
<footer>
|
|
© 2025 AstraOS
|
|
</footer>
|
|
</div>
|
|
</div>
|
|
|
|
<a href="{{ url_for('logout') }}" class="fixed bottom-6 left-6 text-white font-bold py-3 px-5 rounded-full shadow-lg transition-all backdrop-blur-sm flex items-center gap-2 z-30
|
|
{% if maintenance_active %}
|
|
bg-yellow-600/50 hover:bg-yellow-500/80 hover:shadow-yellow-500/40 border border-yellow-500/20
|
|
{% else %}
|
|
bg-purple-600/50 hover:bg-purple-500/80 hover:shadow-purple-500/40 border border-purple-500/20
|
|
{% endif %}">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15m3 0l3-3m0 0l-3-3m3 3H9" />
|
|
</svg>
|
|
<span>Logout</span>
|
|
</a>
|
|
|
|
|
|
<script src="{{ url_for('static', filename='JAVASCRIPT/main.js') }}"></script>
|
|
<script>
|
|
gsap.to("#star", {
|
|
rotation: 360,
|
|
duration: 1.2,
|
|
ease: "power1.inOut",
|
|
repeat: -1
|
|
});
|
|
gsap.to("#star", { scale: 1.2, duration: 0.8, yoyo: true, repeat: -1, ease: "power1.inOut" });
|
|
setTimeout(() => {
|
|
gsap.to("#startup", {
|
|
opacity: 0,
|
|
duration: 0.3,
|
|
onComplete: () => {
|
|
document.getElementById("startup").style.display = "none";
|
|
gsap.to("#mainContent", { opacity: 1, duration: 0.1 });
|
|
}
|
|
});
|
|
}, 400);
|
|
const bg = document.querySelector('.animated-bg');
|
|
if (bg) {
|
|
for (let i = 0; i < 50; i++) {
|
|
const dot = document.createElement('div');
|
|
dot.classList.add('dot');
|
|
if (document.body.classList.contains('maintenance-active')) {
|
|
dot.classList.add('maintenance');
|
|
}
|
|
dot.style.left = `${Math.random() * 100}vw`;
|
|
dot.style.animationDelay = `${Math.random() * -20}s`;
|
|
dot.style.animationDuration = `${20 + Math.random() * 10}s`;
|
|
bg.appendChild(dot);
|
|
}
|
|
}
|
|
</script>
|
|
{% if maintenance_active %}
|
|
<script>document.body.classList.add('maintenance-active');</script>
|
|
{% endif %}
|
|
</body>
|
|
</html>
|