AstraOS Ver. 1.10:
- Benachrichtigung aktualisiert - Logs hinzugefügt
This commit is contained in:
parent
9b5d3cdc58
commit
eec2a01618
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
{% block title %}AstraOS — Altersüberprüfung{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<header class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-4xl font-bold tracking-wide {% if maintenance_active %}text-yellow-300{% else %}text-purple-300{% endif %}">Altersüberprüfung</h2>
|
||||
|
|
@ -10,11 +14,35 @@
|
|||
<!-- FLASH MESSAGES -->
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
<div class="toast-container">
|
||||
{% for category, message in messages %}
|
||||
<div class="mb-4 rounded-md bg-{{ 'red' if category == 'danger' else ('green' if category == 'success' else 'blue') }}-500/20 p-4 text-sm text-{{ 'red' if category == 'danger' else ('green' if category == 'success' else 'blue') }}-300 border border-{{ 'red' if category == 'danger' else ('green' if category == 'success' else 'blue') }}-500/30">
|
||||
{{ message }}
|
||||
<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-sm 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 %}
|
||||
|
||||
|
|
@ -31,7 +59,7 @@
|
|||
</div>
|
||||
<div>
|
||||
<label for="steam_id" class="block text-sm font-medium text-gray-300">SteamID</label>
|
||||
<input type="text" name="steam_id" id="steam_id" placeholder="z.B. 76561199045670704@steam" required
|
||||
<input type="text" name="steam_id" id="steam_id" placeholder="z.B. 76561199045670704" required
|
||||
class="mt-1 w-full px-3 py-2 bg-gray-800 border border-gray-700 rounded-lg text-white focus:outline-none focus:ring-2 {% if maintenance_active %}focus:ring-yellow-500{% else %}focus:ring-purple-500{% endif %}">
|
||||
</div>
|
||||
<div>
|
||||
|
|
@ -116,4 +144,3 @@ function copyToClipboard(elementId) {
|
|||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,11 +37,35 @@
|
|||
<!-- FLASH MESSAGES -->
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
<div class="toast-container">
|
||||
{% for category, message in messages %}
|
||||
<div class="rounded-md bg-{{ 'red' if category == 'danger' else ('green' if category == 'success' else 'blue') }}-500/20 p-4 text-sm text-{{ 'red' if category == 'danger' else ('green' if category == 'success' else 'blue') }}-300 border border-{{ 'red' if category == 'danger' else ('green' if category == 'success' else 'blue') }}-500/30">
|
||||
{{ message }}
|
||||
<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-sm 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 %}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,40 @@
|
|||
|
||||
{% block content %}
|
||||
<div x-data="{ showUploadModal: false, showDeleteModal: false, botToDelete: null }">
|
||||
<!-- FLASH MESSAGES -->
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
<div class="toast-container">
|
||||
{% 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-sm 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 %}
|
||||
<!-- HEADER -->
|
||||
<header class="flex justify-between items-center mb-10">
|
||||
<h2 class="text-4xl font-bold tracking-wide {% if maintenance_active %}text-yellow-300{% else %}text-purple-300{% endif %}">Discord Bot Management</h2>
|
||||
|
|
|
|||
64
AstraOS/Frontend/HTML/logs.html
Normal file
64
AstraOS/Frontend/HTML/logs.html
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
{% extends "sidebar.html" %}
|
||||
|
||||
{% block title %}AstraOS — Logs{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<header class="flex justify-between items-center mb-10">
|
||||
<h2 class="text-4xl font-bold tracking-wide {% if maintenance_active %}text-yellow-300{% else %}text-purple-300{% endif %}">System-Logs</h2>
|
||||
</header>
|
||||
|
||||
<div class="bg-black/40 rounded-xl backdrop-blur-lg overflow-hidden
|
||||
{% if maintenance_active %}border border-yellow-500/30{% else %}border border-purple-500/20{% endif %}">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y {% if maintenance_active %}divide-yellow-500/30{% else %}divide-purple-500/20{% endif %}">
|
||||
<thead class="bg-black/20">
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Benutzer</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Aktion</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Details</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Zeitstempel (UTC)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y {% if maintenance_active %}divide-yellow-500/30{% else %}divide-purple-500/30{% endif %}">
|
||||
{% for log in logs %}
|
||||
<tr class="transition {% if maintenance_active %}hover:bg-yellow-500/5{% else %}hover:bg-purple-500/5{% endif %}">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-white">{{ log.username }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">{{ log.action }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-400">{{ log.details if log.details else '-' }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-400">{{ log.timestamp.strftime('%d.%m.%Y %H:%M:%S') }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="4" class="px-6 py-4 text-center text-gray-400">Keine Logs gefunden.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PAGINATION -->
|
||||
{% if total_pages > 1 %}
|
||||
<nav class="mt-6 flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-gray-400">
|
||||
Seite <span class="font-medium">{{ page }}</span> von <span class="font-medium">{{ total_pages }}</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex-1 flex justify-end gap-2">
|
||||
<a href="{{ url_for('logs', page=page-1) }}"
|
||||
class="relative inline-flex items-center px-4 py-2 border border-gray-700 text-sm font-medium rounded-md text-gray-300 bg-gray-800 hover:bg-gray-700
|
||||
{% if page <= 1 %}pointer-events-none opacity-50{% endif %}">
|
||||
Vorherige
|
||||
</a>
|
||||
<a href="{{ url_for('logs', page=page+1) }}"
|
||||
class="relative inline-flex items-center px-4 py-2 border border-gray-700 text-sm font-medium rounded-md text-gray-300 bg-gray-800 hover:bg-gray-700
|
||||
{% if page >= total_pages %}pointer-events-none opacity-50{% endif %}">
|
||||
Nächste
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
@ -9,6 +9,40 @@
|
|||
{% block content %}
|
||||
<div x-data="{ showAddRoleModal: false, showDeleteModal: false, roleToDelete: null, roleToEdit: null }">
|
||||
<main class="flex-1 p-8 space-y-10" id="mainContent">
|
||||
<!-- FLASH MESSAGES -->
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
<div class="toast-container">
|
||||
{% 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-sm 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 %}
|
||||
<!-- HEADER -->
|
||||
<header class="flex justify-between items-center mb-10">
|
||||
<h2 class="text-4xl font-bold tracking-wide {% if maintenance_active %}text-yellow-300{% else %}text-purple-300{% endif %}">Rollen verwalten</h2>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
{% block title %}AstraOS — Einstellungen{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="space-y-10">
|
||||
<header>
|
||||
|
|
@ -11,11 +15,35 @@
|
|||
<!-- FLASH MESSAGES -->
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
<div class="toast-container">
|
||||
{% for category, message in messages %}
|
||||
<div class="rounded-md bg-{{ 'red' if category == 'danger' else ('green' if category == 'success' else 'blue') }}-500/20 p-4 text-sm text-{{ 'red' if category == 'danger' else ('green' if category == 'success' else 'blue') }}-300 border border-{{ 'red' if category == 'danger' else ('green' if category == 'success' else 'blue') }}-500/30">
|
||||
{{ message }}
|
||||
<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-sm 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 %}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,13 +4,26 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}AstraOS{% endblock %}</title>
|
||||
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='media/AstraOS_Logo.ico') }}">
|
||||
|
||||
<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.12.2/gsap.min.js"></script>
|
||||
{% block head %}{% endblock %}
|
||||
|
||||
<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;
|
||||
|
|
@ -35,24 +48,21 @@
|
|||
justify-content: center;
|
||||
transition: background-color 0.2s, border-color 0.2s;
|
||||
margin-right: 0.75rem;
|
||||
border-color: #8b5cf6; /* purple-500 */
|
||||
}
|
||||
.custom-checkbox-box svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: white;
|
||||
transform: scale(0);
|
||||
transition: transform 0.2s ease-in-out;
|
||||
.custom-checkbox-label.maintenance .custom-checkbox-box {
|
||||
border-color: #f59e0b; /* yellow-600 */
|
||||
}
|
||||
.custom-checkbox-label input[type="checkbox"]:checked + .custom-checkbox-box {
|
||||
background-color: #8b5cf6;
|
||||
border-color: #8b5cf6;
|
||||
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: #ca8a04;
|
||||
border-color: #ca8a04;
|
||||
background-color: #f59e0b; /* yellow-600 */
|
||||
border-color: #f59e0b; /* yellow-600 */
|
||||
}
|
||||
.custom-checkbox-label input[type="checkbox"]:checked + .custom-checkbox-box svg {
|
||||
transform: scale(1);
|
||||
input[type="checkbox"]:checked + .custom-checkbox-box svg {
|
||||
display: block;
|
||||
}
|
||||
footer {
|
||||
text-align: center;
|
||||
|
|
@ -120,207 +130,215 @@
|
|||
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>
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
<body class="text-white flex flex-col min-h-screen">
|
||||
<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>
|
||||
|
||||
<div id="startup">
|
||||
<div id="star">
|
||||
<div class="blur"></div>
|
||||
<svg class="relative z-10" viewBox="0 0 100 100">
|
||||
<path d="M50 5 L63 40 L95 50 L63 60 L50 95 L37 60 L5 50 L37 40 Z" fill="#c084fc"/>
|
||||
<path d="M50 28 L58 45 L76 50 L58 55 L50 72 L42 55 L24 50 L42 45 Z" fill="#fff" opacity="0.9"/>
|
||||
<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">
|
||||
<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>
|
||||
</div>
|
||||
<p>AstraOS</p>
|
||||
</div>
|
||||
|
||||
<div class="animated-bg"></div>
|
||||
|
||||
<div class="flex">
|
||||
<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>
|
||||
<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">
|
||||
<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>
|
||||
<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 });
|
||||
}
|
||||
<script src="{{ url_for('static', filename='JAVASCRIPT/main.js') }}"></script>
|
||||
<script>
|
||||
gsap.to("#star", {
|
||||
rotation: 360,
|
||||
duration: 1.2,
|
||||
ease: "power1.inOut",
|
||||
repeat: -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');
|
||||
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);
|
||||
}
|
||||
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 %}
|
||||
</script>
|
||||
{% if maintenance_active %}
|
||||
<script>document.body.classList.add('maintenance-active');</script>
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -34,11 +34,35 @@
|
|||
<!-- FLASH MESSAGES -->
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
<div class="toast-container">
|
||||
{% for category, message in messages %}
|
||||
<div class="rounded-md bg-{{ 'red' if category == 'danger' else ('green' if category == 'success' else 'blue') }}-500/20 p-4 text-sm text-{{ 'red' if category == 'danger' else ('green' if category == 'success' else 'blue') }}-300 border border-{{ 'red' if category == 'danger' else ('green' if category == 'success' else 'blue') }}-500/30">
|
||||
{{ message }}
|
||||
<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-sm 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 %}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,40 @@
|
|||
|
||||
{% block content %}
|
||||
<div x-data="{ showDeleteModal: false }">
|
||||
<!-- FLASH MESSAGES -->
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
<div class="toast-container">
|
||||
{% 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-sm 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 %}
|
||||
<header class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-4xl font-bold tracking-wide {% if maintenance_active %}text-yellow-300{% else %}text-purple-300{% endif %}">Benutzerdetails</h2>
|
||||
<a href="{{ url_for('age_verification') }}" class="font-bold py-2 px-4 rounded-lg transition text-white {% if maintenance_active %}bg-yellow-600 hover:bg-yellow-700{% else %}bg-purple-600 hover:bg-purple-700{% endif %}">
|
||||
|
|
|
|||
|
|
@ -180,6 +180,17 @@ try:
|
|||
except Exception as e:
|
||||
tickets_collection = None
|
||||
print(f"FATAL: Verbindung zu Tickets-DB fehlgeschlagen: {e}")
|
||||
|
||||
def add_log(action, details=None):
|
||||
if 'username' in session and logs_collection is not None:
|
||||
log_entry = {
|
||||
'username': session['username'],
|
||||
'action': action,
|
||||
'details': details,
|
||||
'timestamp': datetime.utcnow()
|
||||
}
|
||||
logs_collection.insert_one(log_entry)
|
||||
|
||||
def get_user_permissions(user_id):
|
||||
user = accounts_collection.find_one({'_id': ObjectId(user_id)})
|
||||
if not user or 'role_id' not in user:
|
||||
|
|
@ -252,6 +263,7 @@ def login():
|
|||
session['user_id'] = str(user['_id'])
|
||||
session['username'] = user['username']
|
||||
session['permissions'] = get_user_permissions(str(user['_id']))
|
||||
add_log("User Login")
|
||||
|
||||
# Prüfen, ob Discord verknüpft ist
|
||||
if not user.get('discord_id'):
|
||||
|
|
@ -389,6 +401,7 @@ def dashboard():
|
|||
@app.route('/logout')
|
||||
@login_required
|
||||
def logout():
|
||||
add_log("User Logout")
|
||||
session.clear()
|
||||
flash("Sie wurden erfolgreich abgemeldet.", "info")
|
||||
return redirect(url_for('login'))
|
||||
|
|
@ -447,12 +460,13 @@ def add_user():
|
|||
'discord_username': None,
|
||||
'discord_avatar': None
|
||||
})
|
||||
add_log("User Created", f"Username: {username}")
|
||||
if dashboard_collection is not None:
|
||||
dashboard_collection.update_one(
|
||||
{"key": "last_user_created"},
|
||||
{"$set": {
|
||||
"time": datetime.utcnow(),
|
||||
"by": username
|
||||
"by": session.get('username')
|
||||
}},
|
||||
upsert=True
|
||||
)
|
||||
|
|
@ -491,6 +505,7 @@ def edit_user(user_id):
|
|||
{'_id': ObjectId(user_id)},
|
||||
{'$set': update_data}
|
||||
)
|
||||
add_log("User Edited", f"Username: {username}")
|
||||
flash(f"Benutzer '{username}' wurde erfolgreich aktualisiert.", "success")
|
||||
return redirect(url_for('user_management'))
|
||||
@app.route('/delete_user/<user_id>', methods=['POST'])
|
||||
|
|
@ -503,7 +518,9 @@ def delete_user(user_id):
|
|||
if session.get('user_id') == user_id:
|
||||
flash("Sie können sich nicht selbst löschen.", "danger")
|
||||
return redirect(url_for('user_management'))
|
||||
user = accounts_collection.find_one({'_id': ObjectId(user_id)})
|
||||
accounts_collection.delete_one({'_id': ObjectId(user_id)})
|
||||
add_log("User Deleted", f"Username: {user.get('username', 'N/A')}")
|
||||
flash("Benutzer wurde erfolgreich gelöscht.", "success")
|
||||
return redirect(url_for('user_management'))
|
||||
|
||||
|
|
@ -536,6 +553,7 @@ def update_password():
|
|||
|
||||
hashed_password = generate_password_hash(new_password)
|
||||
accounts_collection.update_one({'_id': ObjectId(user_id)}, {'$set': {'password': hashed_password}})
|
||||
add_log("Password Changed")
|
||||
flash("Dein Passwort wurde erfolgreich geändert.", "success")
|
||||
return redirect(url_for('settings'))
|
||||
|
||||
|
|
@ -547,6 +565,7 @@ def unlink_discord():
|
|||
{'_id': ObjectId(user_id)},
|
||||
{'$set': {'discord_id': None, 'discord_username': None, 'discord_avatar': None}}
|
||||
)
|
||||
add_log("Discord Unlinked")
|
||||
flash("Deine Discord-Verknüpfung wurde entfernt. Bitte verknüpfe dein Konto erneut.", "info")
|
||||
return redirect(url_for('link_discord'))
|
||||
|
||||
|
|
@ -621,6 +640,7 @@ def callback_discord():
|
|||
'discord_avatar': discord_avatar
|
||||
}}
|
||||
)
|
||||
add_log("Discord Linked")
|
||||
flash("Discord-Account erfolgreich verknüpft!", "success")
|
||||
return redirect(url_for('dashboard'))
|
||||
|
||||
|
|
@ -632,6 +652,7 @@ def callback_discord():
|
|||
session['user_id'] = str(user['_id'])
|
||||
session['username'] = user['username']
|
||||
session['permissions'] = get_user_permissions(str(user['_id']))
|
||||
add_log("User Login via Discord")
|
||||
flash("Erfolgreich mit Discord angemeldet!", "success")
|
||||
return redirect(url_for('dashboard'))
|
||||
else:
|
||||
|
|
@ -666,6 +687,7 @@ def add_role():
|
|||
"permissions": permissions,
|
||||
"is_deletable": True
|
||||
})
|
||||
add_log("Role Created", f"Role: {role_name}")
|
||||
flash(f"Rolle '{role_name}' wurde erstellt.", "success")
|
||||
return redirect(url_for('role_management'))
|
||||
@app.route('/edit_role/<role_id>', methods=['POST'])
|
||||
|
|
@ -681,6 +703,7 @@ def edit_role(role_id):
|
|||
{'_id': ObjectId(role_id)},
|
||||
{'$set': {'name': role_name, 'permissions': permissions}}
|
||||
)
|
||||
add_log("Role Edited", f"Role: {role_name}")
|
||||
flash(f"Rolle '{role_name}' wurde aktualisiert.", "success")
|
||||
return redirect(url_for('role_management'))
|
||||
@app.route('/delete_role/<role_id>', methods=['POST'])
|
||||
|
|
@ -692,6 +715,7 @@ def delete_role(role_id):
|
|||
flash("Diese Rolle kann nicht gelöscht werden.", "danger")
|
||||
return redirect(url_for('role_management'))
|
||||
roles_collection.delete_one({'_id': ObjectId(role_id)})
|
||||
add_log("Role Deleted", f"Role: {role.get('name', 'N/A')}")
|
||||
flash("Rolle wurde gelöscht.", "success")
|
||||
return redirect(url_for('role_management'))
|
||||
|
||||
|
|
@ -734,6 +758,7 @@ def age_verification():
|
|||
result['ban_duration_days'] = ban_duration_days
|
||||
result['ban_command'] = f"oban {steam_id} {ban_duration_days}d"
|
||||
|
||||
add_log("Age Verification Added", f"Ingame Name: {ingame_name}, SteamID: {steam_id}")
|
||||
flash("Altersüberprüfung erfolgreich durchgeführt und gespeichert.", "success")
|
||||
|
||||
if search_query:
|
||||
|
|
@ -803,6 +828,7 @@ def delete_age_verification(user_id):
|
|||
return redirect(url_for('view_user_age_details', user_id=user_id))
|
||||
|
||||
age_verification_collection.delete_one({'_id': ObjectId(user_id)})
|
||||
add_log("Age Verification Deleted", f"Ingame Name: {entry.get('ingame_name', 'N/A')}, SteamID: {entry.get('steam_id', 'N/A')}")
|
||||
flash("Eintrag zur Altersüberprüfung wurde gelöscht.", "success")
|
||||
return redirect(url_for('age_verification'))
|
||||
@app.route('/ip_analyzer', methods=['GET', 'POST'])
|
||||
|
|
@ -844,6 +870,7 @@ def ip_analyzer():
|
|||
"added_by": session["username"],
|
||||
"timestamp": datetime.utcnow()
|
||||
})
|
||||
add_log("IP Analyzed", f"IP: {ip_address}")
|
||||
flash("IP-Adresse erfolgreich analysiert und gespeichert.", "success")
|
||||
else:
|
||||
error = f"Fehler bei der API-Anfrage: Status {response.status_code}"
|
||||
|
|
@ -856,6 +883,25 @@ def ip_analyzer():
|
|||
ip_data=ip_data,
|
||||
error=error,
|
||||
similar_ips=similar_ips)
|
||||
|
||||
@app.route('/logs')
|
||||
@login_required
|
||||
@permission_required('view_logs')
|
||||
def logs():
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = 25
|
||||
skip = (page - 1) * per_page
|
||||
|
||||
log_entries = list(logs_collection.find().sort('timestamp', -1).skip(skip).limit(per_page))
|
||||
total_logs = logs_collection.count_documents({})
|
||||
total_pages = (total_logs + per_page - 1) // per_page
|
||||
|
||||
return render_template('logs.html',
|
||||
logs=log_entries,
|
||||
page=page,
|
||||
total_pages=total_pages,
|
||||
active_page='logs')
|
||||
|
||||
def stream_bot_output(bot_id, process):
|
||||
def stream_to_socket(pipe, log_type):
|
||||
try:
|
||||
|
|
@ -913,6 +959,7 @@ def upload_bot():
|
|||
'created_at': datetime.utcnow(),
|
||||
'status': 'stopped'
|
||||
})
|
||||
add_log("Bot Uploaded", f"Bot Name: {bot_name}")
|
||||
flash(f"Bot '{bot_name}' wurde erfolgreich hochgeladen.", "success")
|
||||
return redirect(url_for('discord_bots'))
|
||||
@app.route('/delete_bot/<bot_id>', methods=['POST'])
|
||||
|
|
@ -939,6 +986,7 @@ def delete_bot(bot_id):
|
|||
except Exception as e:
|
||||
flash(f"Fehler beim Löschen der Bot-Datei: {e}", "danger")
|
||||
discord_bots_collection.delete_one({'_id': ObjectId(bot_id)})
|
||||
add_log("Bot Deleted", f"Bot Name: {bot_info.get('name', 'N/A')}")
|
||||
flash(f"Bot '{bot_info['name']}' wurde erfolgreich gelöscht.", "success")
|
||||
return redirect(url_for('discord_bots'))
|
||||
@app.route('/bot_console/<bot_id>')
|
||||
|
|
@ -994,6 +1042,7 @@ def handle_toggle_bot(data):
|
|||
thread.daemon = True
|
||||
thread.start()
|
||||
discord_bots_collection.update_one({'_id': ObjectId(bot_id)}, {'$set': {'status': 'running'}})
|
||||
add_log("Bot Started", f"Bot Name: {bot_info.get('name', 'N/A')}")
|
||||
emit('status_update', {'bot_id': bot_id, 'status': 'running'}, room=bot_id)
|
||||
emit('console_output', {'type': 'system', 'data': f'--- Bot-Prozess gestartet (PID: {process.pid}) ---'}, room=bot_id)
|
||||
except Exception as e:
|
||||
|
|
@ -1008,6 +1057,7 @@ def handle_toggle_bot(data):
|
|||
if bot_id in running_bots:
|
||||
del running_bots[bot_id]
|
||||
discord_bots_collection.update_one({'_id': ObjectId(bot_id)}, {'$set': {'status': 'stopped'}})
|
||||
add_log("Bot Stopped", f"Bot Name: {bot_info.get('name', 'N/A')}")
|
||||
emit('status_update', {'bot_id': bot_id, 'status': 'stopped'}, room=bot_id)
|
||||
emit('console_output', {'type': 'system', 'data': '--- Bot-Prozess wird gestoppt... ---'}, room=bot_id)
|
||||
@app.route('/maintenance')
|
||||
|
|
@ -1083,6 +1133,7 @@ def update_maintenance():
|
|||
{"$set": update_data},
|
||||
upsert=True
|
||||
)
|
||||
add_log("Maintenance Settings Updated", f"Enabled: {is_enabled}, Message: {status_message}")
|
||||
flash("Wartungseinstellungen wurden aktualisiert.", "success")
|
||||
return redirect(url_for('maintenance_management'))
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
Loading…
Reference in a new issue