AstraOS Ver. 1.10.3:

- Benachrichtigung gefixt
- Sachen übersetzt
This commit is contained in:
MrWhiff 2025-12-11 17:11:56 +01:00
parent c9d30cfbdd
commit 21279f455d
6 changed files with 54 additions and 182 deletions

View file

@ -10,41 +10,6 @@
<h2 class="text-4xl font-bold tracking-wide {% if maintenance_active %}text-yellow-300{% else %}text-purple-300{% endif %}">Altersüberprüfung</h2>
</header>
<!-- 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-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 %}
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<!-- Verification Form -->
<div class="bg-black/40 p-6 rounded-xl backdrop-blur border {% if maintenance_active %}border-yellow-500/30{% else %}border-purple-500/20{% endif %}">
@ -53,7 +18,7 @@
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div>
<label for="birthdate" class="block text-sm font-medium text-gray-300">Geburtsdatum</label>
<input type="date" name="birthdate" id="birthdate" required
<input type="text" name="birthdate" id="birthdate" placeholder="DD.MM.YYYY" 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>

View file

@ -34,41 +34,6 @@
<h2 class="text-4xl font-bold tracking-wide {% if maintenance_active %}text-yellow-300{% else %}text-purple-300{% endif %}">Dashboard</h2>
</header>
<!-- 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-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 %}
<div class="grid grid-cols-1 gap-8">
<div class="glow-card opacity-0 translate-y-8 lg:col-span-2 bg-black/40 p-6 rounded-xl backdrop-blur
{% if maintenance_active %}border border-yellow-500/30{% else %}border border-purple-500/20{% endif %}">

View file

@ -8,40 +8,6 @@
{% 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-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 %}
<!-- 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>

View file

@ -11,41 +11,6 @@
<h2 class="text-4xl font-bold tracking-wide {% if maintenance_active %}text-yellow-300{% else %}text-purple-300{% endif %}">Einstellungen</h2>
</header>
<!-- 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-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 %}
<!-- ACCOUNT-INFORMATIONEN -->
<div class="bg-black/40 rounded-xl backdrop-blur-lg p-8 {% if maintenance_active %}border border-yellow-500/30{% else %}border border-purple-500/20{% endif %}">
<h3 class="text-2xl font-semibold mb-6 {% if maintenance_active %}text-yellow-300{% else %}text-purple-300{% endif %}">Account-Informationen</h3>

View file

@ -293,7 +293,7 @@
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">
class="max-w-lg 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">

View file

@ -263,7 +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")
add_log("Benutzer-Login")
# Prüfen, ob Discord verknüpft ist
if not user.get('discord_id'):
@ -401,7 +401,7 @@ def dashboard():
@app.route('/logout')
@login_required
def logout():
add_log("User Logout")
add_log("Benutzer-Logout")
session.clear()
flash("Sie wurden erfolgreich abgemeldet.", "info")
return redirect(url_for('login'))
@ -460,7 +460,7 @@ def add_user():
'discord_username': None,
'discord_avatar': None
})
add_log("User Created", f"Username: {username}")
add_log("Benutzer erstellt", f"Benutzername: {username}")
if dashboard_collection is not None:
dashboard_collection.update_one(
{"key": "last_user_created"},
@ -505,7 +505,7 @@ def edit_user(user_id):
{'_id': ObjectId(user_id)},
{'$set': update_data}
)
add_log("User Edited", f"Username: {username}")
add_log("Benutzer bearbeitet", f"Benutzername: {username}")
flash(f"Benutzer '{username}' wurde erfolgreich aktualisiert.", "success")
return redirect(url_for('user_management'))
@app.route('/delete_user/<user_id>', methods=['POST'])
@ -520,7 +520,7 @@ def delete_user(user_id):
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')}")
add_log("Benutzer gelöscht", f"Benutzername: {user.get('username', 'N/A')}")
flash("Benutzer wurde erfolgreich gelöscht.", "success")
return redirect(url_for('user_management'))
@ -553,7 +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")
add_log("Passwort geändert")
flash("Dein Passwort wurde erfolgreich geändert.", "success")
return redirect(url_for('settings'))
@ -565,7 +565,7 @@ def unlink_discord():
{'_id': ObjectId(user_id)},
{'$set': {'discord_id': None, 'discord_username': None, 'discord_avatar': None}}
)
add_log("Discord Unlinked")
add_log("Discord-Verknüpfung aufgehoben")
flash("Deine Discord-Verknüpfung wurde entfernt. Bitte verknüpfe dein Konto erneut.", "info")
return redirect(url_for('link_discord'))
@ -640,7 +640,7 @@ def callback_discord():
'discord_avatar': discord_avatar
}}
)
add_log("Discord Linked")
add_log("Discord verknüpft")
flash("Discord-Account erfolgreich verknüpft!", "success")
return redirect(url_for('dashboard'))
@ -652,7 +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")
add_log("Benutzer-Login via Discord")
flash("Erfolgreich mit Discord angemeldet!", "success")
return redirect(url_for('dashboard'))
else:
@ -687,7 +687,7 @@ def add_role():
"permissions": permissions,
"is_deletable": True
})
add_log("Role Created", f"Role: {role_name}")
add_log("Rolle erstellt", f"Rolle: {role_name}")
flash(f"Rolle '{role_name}' wurde erstellt.", "success")
return redirect(url_for('role_management'))
@app.route('/edit_role/<role_id>', methods=['POST'])
@ -703,7 +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}")
add_log("Rolle bearbeitet", f"Rolle: {role_name}")
flash(f"Rolle '{role_name}' wurde aktualisiert.", "success")
return redirect(url_for('role_management'))
@app.route('/delete_role/<role_id>', methods=['POST'])
@ -715,11 +715,16 @@ 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')}")
add_log("Rolle gelöscht", f"Rolle: {role.get('name', 'N/A')}")
flash("Rolle wurde gelöscht.", "success")
return redirect(url_for('role_management'))
def calculate_current_age(birthdate_str):
try:
# Versuche zuerst das DD.MM.YYYY Format
birthdate_dt = datetime.strptime(birthdate_str, '%d.%m.%Y')
except ValueError:
# Wenn das fehlschlägt, versuche das YYYY-MM-DD Format (für alte Einträge)
birthdate_dt = datetime.strptime(birthdate_str, '%Y-%m-%d')
today = datetime.today()
return today.year - birthdate_dt.year - ((today.month, today.day) < (birthdate_dt.month, birthdate_dt.day))
@ -737,7 +742,8 @@ def age_verification():
ingame_name = request.form.get('ingame_name')
if birthdate_str and steam_id and ingame_name:
birthdate_dt = datetime.strptime(birthdate_str, '%Y-%m-%d')
try:
birthdate_dt = datetime.strptime(birthdate_str, '%d.%m.%Y')
today = datetime.today()
age = today.year - birthdate_dt.year - ((today.month, today.day) < (birthdate_dt.month, birthdate_dt.day))
@ -758,8 +764,10 @@ 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}")
add_log("Altersüberprüfung hinzugefügt", f"Ingame Name: {ingame_name}, SteamID: {steam_id}")
flash("Altersüberprüfung erfolgreich durchgeführt und gespeichert.", "success")
except ValueError:
flash("Ungültiges Datumsformat. Bitte DD.MM.YYYY verwenden.", "danger")
if search_query:
users = list(age_verification_collection.find({
@ -793,6 +801,9 @@ def view_user_age_details(user_id):
result = None
if user.get('age') < 13:
today = datetime.today()
try:
birthdate_dt = datetime.strptime(user['birthdate'], '%d.%m.%Y')
except ValueError:
birthdate_dt = datetime.strptime(user['birthdate'], '%Y-%m-%d')
thirteenth_birthday = birthdate_dt.replace(year=birthdate_dt.year + 13)
ban_duration_days = (thirteenth_birthday - today).days + 1
@ -828,7 +839,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')}")
add_log("Altersüberprüfung gelöscht", 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'])
@ -870,7 +881,7 @@ def ip_analyzer():
"added_by": session["username"],
"timestamp": datetime.utcnow()
})
add_log("IP Analyzed", f"IP: {ip_address}")
add_log("IP analysiert", f"IP: {ip_address}")
flash("IP-Adresse erfolgreich analysiert und gespeichert.", "success")
else:
error = f"Fehler bei der API-Anfrage: Status {response.status_code}"
@ -959,7 +970,7 @@ def upload_bot():
'created_at': datetime.utcnow(),
'status': 'stopped'
})
add_log("Bot Uploaded", f"Bot Name: {bot_name}")
add_log("Bot hochgeladen", 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'])
@ -986,7 +997,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')}")
add_log("Bot gelöscht", 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>')
@ -1042,7 +1053,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')}")
add_log("Bot gestartet", 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:
@ -1057,7 +1068,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')}")
add_log("Bot gestoppt", 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')
@ -1133,7 +1144,7 @@ def update_maintenance():
{"$set": update_data},
upsert=True
)
add_log("Maintenance Settings Updated", f"Enabled: {is_enabled}, Message: {status_message}")
add_log("Wartungseinstellungen aktualisiert", f"Aktiviert: {is_enabled}, Nachricht: {status_message}")
flash("Wartungseinstellungen wurden aktualisiert.", "success")
return redirect(url_for('maintenance_management'))
if __name__ == '__main__':