AstraOS Ver. 1.4:
- Settings hinzugefügt - Discord Authentication hinzugefügt
This commit is contained in:
parent
72672195c6
commit
29644cf4f9
|
|
@ -5,3 +5,8 @@ SSH_USER='root'
|
||||||
SSH_PASS='iwkms@1812:('
|
SSH_PASS='iwkms@1812:('
|
||||||
VPNAPI_KEY='015ada9472664329affb2f26d7036d47'
|
VPNAPI_KEY='015ada9472664329affb2f26d7036d47'
|
||||||
|
|
||||||
|
# Discord OAuth2 Credentials
|
||||||
|
DISCORD_CLIENT_ID='1395018660267036712'
|
||||||
|
DISCORD_CLIENT_SECRET='QVGmwtTDsuE8mh2YlBMqeHndeRRKW9-a'
|
||||||
|
DISCORD_BOT_TOKEN='MTM5NTAxODY2MDI2NzAzNjcxMg.GJE-k4.W0o09yJZfG3onzyzHLbXu4vBrKPXFS1IVuzhMA'
|
||||||
|
DISCORD_REDIRECT_URI='http://127.0.0.1:5000/callback/discord'
|
||||||
|
|
|
||||||
75
AstraOS/Frontend/HTML/link_discord.html
Normal file
75
AstraOS/Frontend/HTML/link_discord.html
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>AstraOS — Discord verknüpfen</title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='media/AstraOS_Logo.ico') }}">
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background: radial-gradient(circle at top, #1b1b26, #0a0a0f 70%);
|
||||||
|
}
|
||||||
|
.animated-bg {
|
||||||
|
position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
|
||||||
|
z-index: -1; 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;
|
||||||
|
}
|
||||||
|
@keyframes float {
|
||||||
|
0% { transform: translateY(100vh) scale(1); }
|
||||||
|
100% { transform: translateY(-10vh) scale(1.5); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="text-white">
|
||||||
|
<div class="animated-bg"></div>
|
||||||
|
|
||||||
|
<div class="min-h-screen flex flex-col items-center justify-center px-4 text-center">
|
||||||
|
<div class="w-full max-w-lg bg-black/40 border border-purple-500/20 rounded-2xl p-8 backdrop-blur-lg shadow-xl">
|
||||||
|
<h1 class="text-3xl font-bold mb-4 tracking-wider text-purple-300">Discord-Konto verknüpfen</h1>
|
||||||
|
<p class="text-gray-300 mb-8">
|
||||||
|
Um die Sicherheit deines Kontos zu gewährleisten und alle Funktionen von AstraOS nutzen zu können,
|
||||||
|
ist eine Verknüpfung mit deinem Discord-Konto erforderlich.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
|
{% if messages %}
|
||||||
|
{% for category, message in messages %}
|
||||||
|
<div class="mb-4 rounded-md bg-{{ 'red' if category == 'danger' else 'blue' }}-500/20 p-4 text-sm text-{{ 'red' if category == 'danger' else 'blue' }}-300 border border-{{ 'red' if category == 'danger' else 'blue' }}-500/30">
|
||||||
|
{{ message }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
<a href="{{ url_for('login_discord') }}"
|
||||||
|
class="inline-flex items-center justify-center w-full max-w-xs bg-[#5865F2] hover:bg-[#4752C4] text-white py-3 rounded-lg font-semibold transition text-lg">
|
||||||
|
<svg class="w-6 h-6 mr-3" aria-hidden="true" role="img" viewBox="0 0 24 24"><path fill="currentColor" d="M20.317 4.36981C18.2767 3.20426 16.1224 2.27659 13.8468 1.61774C13.679 1.57584 13.5041 1.68116 13.4343 1.84475C12.8151 3.24952 12.3413 4.96228 12.0009 6.53635C10.6866 6.37181 9.36521 6.37181 8.01525 6.53635C7.67488 4.96228 7.20103 3.24952 6.58181 1.84475C6.51205 1.68116 6.33712 1.57584 6.16931 1.61774C3.89372 2.27659 1.73942 3.20426 -0.300884 4.36981C-0.535332 4.49339 -0.620213 4.7833 -0.499842 5.01329C0.0107496 5.92333 0.406433 6.97593 0.683583 8.0343C0.715011 8.14443 0.801901 8.23132 0.912038 8.26275C2.43731 8.73725 3.93953 9.1111 5.42371 9.34818C5.60563 9.3789 5.75754 9.24538 5.71964 9.06346C5.54471 8.25822 5.3928 7.43716 5.24089 6.62322C5.22194 6.52066 5.28274 6.4181 5.3853 6.39233C9.41501 5.69637 14.5858 5.69637 18.6155 6.39233C18.7181 6.4181 18.7789 6.52066 18.7599 6.62322C18.608 7.43716 18.4561 8.25822 18.2812 9.06346C18.2433 9.24538 18.3952 9.3789 18.5771 9.34818C20.0613 9.1111 21.5635 8.73725 23.0888 8.26275C23.1989 8.23132 23.2858 8.14443 23.3172 8.0343C23.5944 6.97593 23.9901 5.92333 24.4998 5.01329C24.6202 4.7833 24.5353 4.49339 24.3009 4.36981L20.317 4.36981Z"></path><path fill="currentColor" d="M12.0009 16.4373C14.2131 16.4373 16.0013 14.834 16.0013 12.8337C16.0013 10.8334 14.2202 9.23004 12.0009 9.23004C9.78161 9.23004 8.00049 10.8334 8.00049 12.8337C8.00049 14.834 9.78161 16.4373 12.0009 16.4373ZM18.0016 16.4373C20.2138 16.4373 22.002 14.834 22.002 12.8337C22.002 10.8334 20.2209 9.23004 18.0016 9.23004C15.7823 9.23004 14.0012 10.8334 14.0012 12.8337C14.0012 14.834 15.7823 16.4373 18.0016 16.4373Z"></path></svg>
|
||||||
|
Mit Discord verknüpfen
|
||||||
|
</a>
|
||||||
|
<div class="mt-8">
|
||||||
|
<a href="{{ url_for('logout') }}" class="text-sm text-gray-400 hover:text-white transition">Abmelden</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const bg = document.querySelector('.animated-bg');
|
||||||
|
if (bg) {
|
||||||
|
for (let i = 0; i < 50; i++) {
|
||||||
|
const dot = document.createElement('div');
|
||||||
|
dot.classList.add('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>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
@ -111,6 +111,18 @@
|
||||||
Login
|
Login
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<div class="flex items-center my-1">
|
||||||
|
<div class="flex-grow border-t border-gray-600"></div>
|
||||||
|
<span class="flex-shrink mx-4 text-gray-400 text-sm">oder</span>
|
||||||
|
<div class="flex-grow border-t border-gray-600"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a href="{{ url_for('login_discord') }}"
|
||||||
|
class="flex items-center justify-center bg-[#5865F2] hover:bg-[#4752C4] text-white py-2 rounded-lg font-semibold transition">
|
||||||
|
<svg class="w-6 h-6 mr-2" aria-hidden="true" role="img" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M20.317 4.36981C18.2767 3.20426 16.1224 2.27659 13.8468 1.61774C13.679 1.57584 13.5041 1.68116 13.4343 1.84475C12.8151 3.24952 12.3413 4.96228 12.0009 6.53635C10.6866 6.37181 9.36521 6.37181 8.01525 6.53635C7.67488 4.96228 7.20103 3.24952 6.58181 1.84475C6.51205 1.68116 6.33712 1.57584 6.16931 1.61774C3.89372 2.27659 1.73942 3.20426 -0.300884 4.36981C-0.535332 4.49339 -0.620213 4.7833 -0.499842 5.01329C0.0107496 5.92333 0.406433 6.97593 0.683583 8.0343C0.715011 8.14443 0.801901 8.23132 0.912038 8.26275C2.43731 8.73725 3.93953 9.1111 5.42371 9.34818C5.60563 9.3789 5.75754 9.24538 5.71964 9.06346C5.54471 8.25822 5.3928 7.43716 5.24089 6.62322C5.22194 6.52066 5.28274 6.4181 5.3853 6.39233C9.41501 5.69637 14.5858 5.69637 18.6155 6.39233C18.7181 6.4181 18.7789 6.52066 18.7599 6.62322C18.608 7.43716 18.4561 8.25822 18.2812 9.06346C18.2433 9.24538 18.3952 9.3789 18.5771 9.34818C20.0613 9.1111 21.5635 8.73725 23.0888 8.26275C23.1989 8.23132 23.2858 8.14443 23.3172 8.0343C23.5944 6.97593 23.9901 5.92333 24.4998 5.01329C24.6202 4.7833 24.5353 4.49339 24.3009 4.36981L20.317 4.36981Z"></path><path fill="currentColor" d="M12.0009 16.4373C14.2131 16.4373 16.0013 14.834 16.0013 12.8337C16.0013 10.8334 14.2202 9.23004 12.0009 9.23004C9.78161 9.23004 8.00049 10.8334 8.00049 12.8337C8.00049 14.834 9.78161 16.4373 12.0009 16.4373ZM18.0016 16.4373C20.2138 16.4373 22.002 14.834 22.002 12.8337C22.002 10.8334 20.2209 9.23004 18.0016 9.23004C15.7823 9.23004 14.0012 10.8334 14.0012 12.8337C14.0012 14.834 15.7823 16.4373 18.0016 16.4373Z"></path></svg>
|
||||||
|
Mit Discord anmelden
|
||||||
|
</a>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
88
AstraOS/Frontend/HTML/settings.html
Normal file
88
AstraOS/Frontend/HTML/settings.html
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
{% extends "sidebar.html" %}
|
||||||
|
|
||||||
|
{% block title %}AstraOS — Einstellungen{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="space-y-10">
|
||||||
|
<header>
|
||||||
|
<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 %}
|
||||||
|
{% 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>
|
||||||
|
{% endfor %}
|
||||||
|
{% 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>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-400">Benutzername</label>
|
||||||
|
<p class="text-lg text-white">{{ user.username }}</p>
|
||||||
|
<p class="text-xs text-gray-500 mt-1">Um deinen Benutzernamen zu ändern, kontaktiere bitte einen Administrator.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- PASSWORT ÄNDERN -->
|
||||||
|
<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 %}">Passwort ändern</h3>
|
||||||
|
<form action="{{ url_for('update_password') }}" method="POST" class="space-y-4 max-w-md">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||||
|
<div>
|
||||||
|
<label for="current_password" class="block text-sm font-medium text-gray-300">Aktuelles Passwort</label>
|
||||||
|
<input type="password" name="current_password" id="current_password" 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>
|
||||||
|
<label for="new_password" class="block text-sm font-medium text-gray-300">Neues Passwort</label>
|
||||||
|
<input type="password" name="new_password" id="new_password" 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>
|
||||||
|
<label for="confirm_password" class="block text-sm font-medium text-gray-300">Neues Passwort bestätigen</label>
|
||||||
|
<input type="password" name="confirm_password" id="confirm_password" 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 class="pt-2">
|
||||||
|
<button type="submit" 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 %}">
|
||||||
|
Passwort speichern
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- DISCORD-VERKNÜPFUNG -->
|
||||||
|
<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 %}">Discord-Verknüpfung</h3>
|
||||||
|
{% if user.discord_id %}
|
||||||
|
<div class="flex items-center space-x-4">
|
||||||
|
{% if user.discord_avatar %}
|
||||||
|
<img src="https://cdn.discordapp.com/avatars/{{ user.discord_id }}/{{ user.discord_avatar }}.png" alt="Discord Avatar" class="w-16 h-16 rounded-full">
|
||||||
|
{% endif %}
|
||||||
|
<div>
|
||||||
|
<p class="text-lg font-semibold text-white">{{ user.discord_username }}</p>
|
||||||
|
<p class="text-sm text-gray-400">Erfolgreich verknüpft.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<form action="{{ url_for('unlink_discord') }}" method="POST" class="mt-6">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||||
|
<button type="submit" class="font-bold py-2 px-4 rounded-lg transition text-white bg-red-600 hover:bg-red-700">
|
||||||
|
Verknüpfung aufheben
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
{% else %}
|
||||||
|
<p class="text-gray-300 mb-4">Dein Account ist nicht mit Discord verknüpft.</p>
|
||||||
|
<a href="{{ url_for('login_discord') }}" class="inline-flex items-center justify-center bg-[#5865F2] hover:bg-[#4752C4] text-white py-2 px-4 rounded-lg font-semibold transition">
|
||||||
|
<svg class="w-6 h-6 mr-2" aria-hidden="true" role="img" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M20.317 4.36981C18.2767 3.20426 16.1224 2.27659 13.8468 1.61774C13.679 1.57584 13.5041 1.68116 13.4343 1.84475C12.8151 3.24952 12.3413 4.96228 12.0009 6.53635C10.6866 6.37181 9.36521 6.37181 8.01525 6.53635C7.67488 4.96228 7.20103 3.24952 6.58181 1.84475C6.51205 1.68116 6.33712 1.57584 6.16931 1.61774C3.89372 2.27659 1.73942 3.20426 -0.300884 4.36981C-0.535332 4.49339 -0.620213 4.7833 -0.499842 5.01329C0.0107496 5.92333 0.406433 6.97593 0.683583 8.0343C0.715011 8.14443 0.801901 8.23132 0.912038 8.26275C2.43731 8.73725 3.93953 9.1111 5.42371 9.34818C5.60563 9.3789 5.75754 9.24538 5.71964 9.06346C5.54471 8.25822 5.3928 7.43716 5.24089 6.62322C5.22194 6.52066 5.28274 6.4181 5.3853 6.39233C9.41501 5.69637 14.5858 5.69637 18.6155 6.39233C18.7181 6.4181 18.7789 6.52066 18.7599 6.62322C18.608 7.43716 18.4561 8.25822 18.2812 9.06346C18.2433 9.24538 18.3952 9.3789 18.5771 9.34818C20.0613 9.1111 21.5635 8.73725 23.0888 8.26275C23.1989 8.23132 23.2858 8.14443 23.3172 8.0343C23.5944 6.97593 23.9901 5.92333 24.4998 5.01329C24.6202 4.7833 24.5353 4.49339 24.3009 4.36981L20.317 4.36981Z"></path><path fill="currentColor" d="M12.0009 16.4373C14.2131 16.4373 16.0013 14.834 16.0013 12.8337C16.0013 10.8334 14.2202 9.23004 12.0009 9.23004C9.78161 9.23004 8.00049 10.8334 8.00049 12.8337C8.00049 14.834 9.78161 16.4373 12.0009 16.4373ZM18.0016 16.4373C20.2138 16.4373 22.002 14.834 22.002 12.8337C22.002 10.8334 20.2209 9.23004 18.0016 9.23004C15.7823 9.23004 14.0012 10.8334 14.0012 12.8337C14.0012 14.834 15.7823 16.4373 18.0016 16.4373Z"></path></svg>
|
||||||
|
Mit Discord verknüpfen
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
@ -159,6 +159,13 @@
|
||||||
Status Page
|
Status Page
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<a href="{{ url_for('settings') }}"
|
||||||
|
class="px-3 py-2 rounded-md transition
|
||||||
|
{% 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>
|
<div>
|
||||||
<button class="w-full text-left px-3 py-2 rounded-md transition flex justify-between items-center collapsible
|
<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 %}"
|
{% if maintenance_active %}hover:bg-yellow-500/10 hover:text-yellow-300{% else %}hover:bg-purple-500/10 hover:text-purple-300{% endif %}"
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Benutzername</th>
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Benutzername</th>
|
||||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Rolle</th>
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Rolle</th>
|
||||||
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Discord</th>
|
||||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Erstellt am</th>
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Erstellt am</th>
|
||||||
<th scope="col" class="relative px-6 py-3">
|
<th scope="col" class="relative px-6 py-3">
|
||||||
<span class="sr-only">Aktionen</span>
|
<span class="sr-only">Aktionen</span>
|
||||||
|
|
@ -49,6 +50,13 @@
|
||||||
<tr class="transition {% if maintenance_active %}hover:bg-yellow-500/5{% else %}hover:bg-purple-500/5{% endif %}">
|
<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">{{ user_table_item.username }}</td>
|
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-white">{{ user_table_item.username }}</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">{{ user_table_item.role_name }}</td>
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">{{ user_table_item.role_name }}</td>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">
|
||||||
|
{% if user_table_item.discord_linked %}
|
||||||
|
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">Verknüpft</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">Nicht verknüpft</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">{{ user_table_item.creation_date.strftime('%d.%m.%Y') if user_table_item.creation_date else 'N/A' }}</td>
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">{{ user_table_item.creation_date.strftime('%d.%m.%Y') if user_table_item.creation_date else 'N/A' }}</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||||
<button @click="openEditModal('{{ user_table_item._id | string }}')" class="mr-4 {% if maintenance_active %}text-yellow-400 hover:text-yellow-300{% else %}text-indigo-400 hover:text-indigo-300{% endif %}">Bearbeiten</button>
|
<button @click="openEditModal('{{ user_table_item._id | string }}')" class="mr-4 {% if maintenance_active %}text-yellow-400 hover:text-yellow-300{% else %}text-indigo-400 hover:text-indigo-300{% endif %}">Bearbeiten</button>
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,10 @@ try:
|
||||||
'username': 'admin',
|
'username': 'admin',
|
||||||
'password': hashed_password,
|
'password': hashed_password,
|
||||||
'role_id': owner_role['_id'],
|
'role_id': owner_role['_id'],
|
||||||
'creation_date': datetime.utcnow()
|
'creation_date': datetime.utcnow(),
|
||||||
|
'discord_id': None,
|
||||||
|
'discord_username': None,
|
||||||
|
'discord_avatar': None
|
||||||
})
|
})
|
||||||
print("INFO: Standard-Admin-Benutzer ('admin', Passwort: '1234') mit Rolle 'Owner' wurde erstellt.")
|
print("INFO: Standard-Admin-Benutzer ('admin', Passwort: '1234') mit Rolle 'Owner' wurde erstellt.")
|
||||||
if dashboard_collection.count_documents({}) == 0:
|
if dashboard_collection.count_documents({}) == 0:
|
||||||
|
|
@ -145,6 +148,17 @@ def login_required(f):
|
||||||
if 'user_id' not in session:
|
if 'user_id' not in session:
|
||||||
flash("Bitte melden Sie sich an, um diese Seite zu sehen.", "warning")
|
flash("Bitte melden Sie sich an, um diese Seite zu sehen.", "warning")
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
user = accounts_collection.find_one({'_id': ObjectId(session['user_id'])})
|
||||||
|
if not user:
|
||||||
|
session.clear()
|
||||||
|
flash("Benutzer nicht gefunden.", "danger")
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
# Discord-Verknüpfung prüfen
|
||||||
|
if not user.get('discord_id') and request.endpoint not in ['link_discord', 'login_discord', 'callback_discord', 'logout', 'static']:
|
||||||
|
return redirect(url_for('link_discord'))
|
||||||
|
|
||||||
session['permissions'] = get_user_permissions(session['user_id'])
|
session['permissions'] = get_user_permissions(session['user_id'])
|
||||||
maintenance_status = dashboard_collection.find_one({"key": "scheduled_maintenance"})
|
maintenance_status = dashboard_collection.find_one({"key": "scheduled_maintenance"})
|
||||||
if maintenance_status and maintenance_status.get('enabled', False):
|
if maintenance_status and maintenance_status.get('enabled', False):
|
||||||
|
|
@ -192,6 +206,12 @@ def login():
|
||||||
session['user_id'] = str(user['_id'])
|
session['user_id'] = str(user['_id'])
|
||||||
session['username'] = user['username']
|
session['username'] = user['username']
|
||||||
session['permissions'] = get_user_permissions(str(user['_id']))
|
session['permissions'] = get_user_permissions(str(user['_id']))
|
||||||
|
|
||||||
|
# Prüfen, ob Discord verknüpft ist
|
||||||
|
if not user.get('discord_id'):
|
||||||
|
flash("Bitte verknüpfe deinen Discord-Account, um fortzufahren.", "info")
|
||||||
|
return redirect(url_for('link_discord'))
|
||||||
|
|
||||||
flash("Erfolgreich angemeldet!", "success")
|
flash("Erfolgreich angemeldet!", "success")
|
||||||
return redirect(url_for('dashboard'))
|
return redirect(url_for('dashboard'))
|
||||||
else:
|
else:
|
||||||
|
|
@ -349,6 +369,7 @@ def user_management():
|
||||||
users_for_table = list(accounts_collection.find())
|
users_for_table = list(accounts_collection.find())
|
||||||
for user in users_for_table:
|
for user in users_for_table:
|
||||||
user['role_name'] = role_map.get(str(user.get('role_id')), 'Unbekannt')
|
user['role_name'] = role_map.get(str(user.get('role_id')), 'Unbekannt')
|
||||||
|
user['discord_linked'] = bool(user.get('discord_id'))
|
||||||
return render_template('user_management.html',
|
return render_template('user_management.html',
|
||||||
users_for_modal=users_for_modal,
|
users_for_modal=users_for_modal,
|
||||||
users_for_table=users_for_table,
|
users_for_table=users_for_table,
|
||||||
|
|
@ -375,7 +396,10 @@ def add_user():
|
||||||
'username': username,
|
'username': username,
|
||||||
'password': hashed_password,
|
'password': hashed_password,
|
||||||
'role_id': ObjectId(role_id),
|
'role_id': ObjectId(role_id),
|
||||||
'creation_date': datetime.utcnow()
|
'creation_date': datetime.utcnow(),
|
||||||
|
'discord_id': None,
|
||||||
|
'discord_username': None,
|
||||||
|
'discord_avatar': None
|
||||||
})
|
})
|
||||||
if dashboard_collection is not None:
|
if dashboard_collection is not None:
|
||||||
dashboard_collection.update_one(
|
dashboard_collection.update_one(
|
||||||
|
|
@ -436,6 +460,138 @@ def delete_user(user_id):
|
||||||
accounts_collection.delete_one({'_id': ObjectId(user_id)})
|
accounts_collection.delete_one({'_id': ObjectId(user_id)})
|
||||||
flash("Benutzer wurde erfolgreich gelöscht.", "success")
|
flash("Benutzer wurde erfolgreich gelöscht.", "success")
|
||||||
return redirect(url_for('user_management'))
|
return redirect(url_for('user_management'))
|
||||||
|
|
||||||
|
@app.route('/settings', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def settings():
|
||||||
|
user = accounts_collection.find_one({'_id': ObjectId(session['user_id'])})
|
||||||
|
return render_template('settings.html', user=user, active_page='settings')
|
||||||
|
|
||||||
|
@app.route('/update_password', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def update_password():
|
||||||
|
user_id = session['user_id']
|
||||||
|
current_password = request.form.get('current_password')
|
||||||
|
new_password = request.form.get('new_password')
|
||||||
|
confirm_password = request.form.get('confirm_password')
|
||||||
|
|
||||||
|
if not all([current_password, new_password, confirm_password]):
|
||||||
|
flash("Alle Passwortfelder sind erforderlich.", "danger")
|
||||||
|
return redirect(url_for('settings'))
|
||||||
|
|
||||||
|
if new_password != confirm_password:
|
||||||
|
flash("Die neuen Passwörter stimmen nicht überein.", "danger")
|
||||||
|
return redirect(url_for('settings'))
|
||||||
|
|
||||||
|
user = accounts_collection.find_one({'_id': ObjectId(user_id)})
|
||||||
|
if not user or not check_password_hash(user['password'], current_password):
|
||||||
|
flash("Das aktuelle Passwort ist nicht korrekt.", "danger")
|
||||||
|
return redirect(url_for('settings'))
|
||||||
|
|
||||||
|
hashed_password = generate_password_hash(new_password)
|
||||||
|
accounts_collection.update_one({'_id': ObjectId(user_id)}, {'$set': {'password': hashed_password}})
|
||||||
|
flash("Dein Passwort wurde erfolgreich geändert.", "success")
|
||||||
|
return redirect(url_for('settings'))
|
||||||
|
|
||||||
|
@app.route('/unlink_discord', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def unlink_discord():
|
||||||
|
user_id = session['user_id']
|
||||||
|
accounts_collection.update_one(
|
||||||
|
{'_id': ObjectId(user_id)},
|
||||||
|
{'$set': {'discord_id': None, 'discord_username': None, 'discord_avatar': None}}
|
||||||
|
)
|
||||||
|
flash("Deine Discord-Verknüpfung wurde entfernt. Bitte verknüpfe dein Konto erneut.", "info")
|
||||||
|
return redirect(url_for('link_discord'))
|
||||||
|
|
||||||
|
@app.route('/link_discord')
|
||||||
|
@login_required
|
||||||
|
def link_discord():
|
||||||
|
return render_template('link_discord.html')
|
||||||
|
|
||||||
|
@app.route('/login/discord')
|
||||||
|
def login_discord():
|
||||||
|
client_id = os.environ.get('DISCORD_CLIENT_ID')
|
||||||
|
redirect_uri = os.environ.get('DISCORD_REDIRECT_URI')
|
||||||
|
scope = 'identify'
|
||||||
|
discord_auth_url = (
|
||||||
|
f'https://discord.com/api/oauth2/authorize?client_id={client_id}&'
|
||||||
|
f'redirect_uri={redirect_uri}&response_type=code&scope={scope}'
|
||||||
|
)
|
||||||
|
return redirect(discord_auth_url)
|
||||||
|
|
||||||
|
@app.route('/callback/discord')
|
||||||
|
def callback_discord():
|
||||||
|
code = request.args.get('code')
|
||||||
|
if not code:
|
||||||
|
flash("Discord-Authentifizierung fehlgeschlagen.", "danger")
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
client_id = os.environ.get('DISCORD_CLIENT_ID')
|
||||||
|
client_secret = os.environ.get('DISCORD_CLIENT_SECRET')
|
||||||
|
redirect_uri = os.environ.get('DISCORD_REDIRECT_URI')
|
||||||
|
|
||||||
|
token_url = 'https://discord.com/api/oauth2/token'
|
||||||
|
token_data = {
|
||||||
|
'client_id': client_id,
|
||||||
|
'client_secret': client_secret,
|
||||||
|
'grant_type': 'authorization_code',
|
||||||
|
'code': code,
|
||||||
|
'redirect_uri': redirect_uri,
|
||||||
|
'scope': 'identify'
|
||||||
|
}
|
||||||
|
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
|
||||||
|
token_r = requests.post(token_url, data=token_data, headers=headers)
|
||||||
|
if token_r.status_code != 200:
|
||||||
|
flash("Fehler beim Abrufen des Tokens von Discord.", "danger")
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
access_token = token_r.json()['access_token']
|
||||||
|
user_info_url = 'https://discord.com/api/users/@me'
|
||||||
|
user_info_r = requests.get(user_info_url, headers={'Authorization': f'Bearer {access_token}'})
|
||||||
|
if user_info_r.status_code != 200:
|
||||||
|
flash("Fehler beim Abrufen der Benutzerinformationen von Discord.", "danger")
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
discord_user = user_info_r.json()
|
||||||
|
discord_id = discord_user['id']
|
||||||
|
discord_username = f"{discord_user['username']}"
|
||||||
|
discord_avatar = discord_user.get('avatar')
|
||||||
|
|
||||||
|
# Fall 1: Benutzer ist bereits eingeloggt und möchte verknüpfen
|
||||||
|
if 'user_id' in session:
|
||||||
|
user_id = session['user_id']
|
||||||
|
# Prüfen, ob dieser Discord-Account bereits von einem anderen Benutzer verwendet wird
|
||||||
|
existing_link = accounts_collection.find_one({'discord_id': discord_id})
|
||||||
|
if existing_link and str(existing_link['_id']) != user_id:
|
||||||
|
flash("Dieser Discord-Account ist bereits mit einem anderen Benutzer verknüpft.", "danger")
|
||||||
|
return redirect(url_for('settings'))
|
||||||
|
|
||||||
|
accounts_collection.update_one(
|
||||||
|
{'_id': ObjectId(user_id)},
|
||||||
|
{'$set': {
|
||||||
|
'discord_id': discord_id,
|
||||||
|
'discord_username': discord_username,
|
||||||
|
'discord_avatar': discord_avatar
|
||||||
|
}}
|
||||||
|
)
|
||||||
|
flash("Discord-Account erfolgreich verknüpft!", "success")
|
||||||
|
return redirect(url_for('dashboard'))
|
||||||
|
|
||||||
|
# Fall 2: Benutzer meldet sich direkt über Discord an
|
||||||
|
else:
|
||||||
|
user = accounts_collection.find_one({'discord_id': discord_id})
|
||||||
|
if user:
|
||||||
|
session.permanent = True
|
||||||
|
session['user_id'] = str(user['_id'])
|
||||||
|
session['username'] = user['username']
|
||||||
|
session['permissions'] = get_user_permissions(str(user['_id']))
|
||||||
|
flash("Erfolgreich mit Discord angemeldet!", "success")
|
||||||
|
return redirect(url_for('dashboard'))
|
||||||
|
else:
|
||||||
|
flash("Kein AstraOS-Benutzer mit diesem Discord-Konto gefunden. Bitte melde dich zuerst normal an und verknüpfe deinen Account.", "warning")
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
@app.route('/role_management')
|
@app.route('/role_management')
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('view_role_management')
|
@permission_required('view_role_management')
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue