AstraOS Ver. 1.9:

- Age Verification hinzugefügt (für SkyFlo)
This commit is contained in:
MrWhiff 2025-12-11 16:26:22 +01:00
parent da97db7553
commit 1b7a12f8e2
5 changed files with 280 additions and 55 deletions

View file

@ -0,0 +1,119 @@
{% extends "sidebar.html" %}
{% block title %}AstraOS — Altersüberprüfung{% 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>
</header>
<!-- FLASH MESSAGES -->
{% 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 ('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 %}
<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 %}">
<h3 class="text-2xl font-semibold mb-4">Neue Überprüfung</h3>
<form method="POST" class="space-y-4">
<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
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="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. STEAM_0:1:23456789" 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="ingame_name" class="block text-sm font-medium text-gray-300">Ingame Name</label>
<input type="text" name="ingame_name" id="ingame_name" placeholder="z.B. Max Mustermann" 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="flex justify-end">
<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 %}">
Überprüfen
</button>
</div>
</form>
{% if result %}
<div class="mt-6 border-t {% if maintenance_active %}border-yellow-500/30{% else %}border-purple-500/20{% endif %} pt-4">
<h4 class="text-xl font-semibold mb-2">Ergebnis</h4>
<p class="text-lg">Die Person ist <span class="font-bold {% if result.age < 13 %}text-red-400{% else %}text-green-400{% endif %}">{{ result.age }}</span> Jahre alt.</p>
{% if result.age < 13 %}
<p class="text-lg mt-2">Diese Person muss für <span class="font-bold text-yellow-400">{{ result.ban_duration_days }}</span> Tage gesperrt werden.</p>
<div class="mt-4">
<label for="ban-command" class="block text-sm font-medium text-gray-300">Bann-Befehl</label>
<div class="flex items-center gap-2 mt-1">
<input type="text" id="ban-command" readonly value="{{ result.ban_command }}"
class="w-full px-3 py-2 bg-gray-800 border border-gray-700 rounded-lg text-white">
<button onclick="copyToClipboard('ban-command')" class="flex-shrink-0 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 %}">
Kopieren
</button>
</div>
</div>
{% else %}
<p class="text-lg mt-2 text-green-400">Keine Sperre erforderlich.</p>
{% endif %}
</div>
{% endif %}
</div>
<!-- User Search -->
<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 %}">
<h3 class="text-2xl font-semibold mb-4">Benutzer suchen</h3>
<form method="GET" class="flex gap-2 mb-4">
<input type="text" name="search" placeholder="Suche nach Name oder SteamID..." value="{{ search_query }}"
class="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 %}">
<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 %}">
Suchen
</button>
</form>
<div class="space-y-2 max-h-96 overflow-y-auto">
{% for user in users %}
<a href="{{ url_for('view_user_age_details', user_id=user._id) }}" class="block p-3 rounded-lg transition {% if maintenance_active %}hover:bg-yellow-500/10{% else %}hover:bg-purple-500/10{% endif %}">
<div class="flex justify-between items-center">
<div>
<p class="font-semibold {% if user.age < 13 %}text-red-400{% else %}text-white{% endif %}">{{ user.ingame_name }}</p>
<p class="text-sm text-gray-400">{{ user.steam_id }}</p>
</div>
<div class="text-right">
<p class="text-lg font-bold">{{ user.age }} Jahre</p>
<p class="text-xs text-gray-500">{{ user.timestamp.strftime('%d.%m.%Y') }}</p>
</div>
</div>
</a>
{% else %}
<p class="text-gray-400">Keine Benutzer gefunden.</p>
{% endfor %}
</div>
</div>
</div>
<script>
function copyToClipboard(elementId) {
const input = document.getElementById(elementId);
input.select();
input.setSelectionRange(0, 99999); // For mobile devices
document.execCommand('copy');
// Optional: Visuelles Feedback geben
const originalButtonText = event.target.innerText;
event.target.innerText = 'Kopiert!';
setTimeout(() => {
event.target.innerText = originalButtonText;
}, 1500);
}
</script>
{% endblock %}

View file

@ -247,7 +247,13 @@
</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>
<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 %}">🎂 Altersüberprüfung</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>

View file

@ -0,0 +1,74 @@
{% extends "sidebar.html" %}
{% block title %}AstraOS — Benutzerdetails{% 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 %}">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 %}">
&larr; Zurück
</a>
</header>
<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 %}">
<div class="space-y-3">
<div>
<p class="text-sm text-gray-400">Ingame Name</p>
<p class="text-xl font-semibold">{{ user.ingame_name }}</p>
</div>
<div>
<p class="text-sm text-gray-400">SteamID</p>
<p class="text-xl font-semibold">{{ user.steam_id }}</p>
</div>
<div>
<p class="text-sm text-gray-400">Geburtsdatum</p>
<p class="text-xl font-semibold">{{ user.birthdate }}</p>
</div>
<div>
<p class="text-sm text-gray-400">Alter bei Überprüfung</p>
<p class="text-xl font-bold {% if user.age < 13 %}text-red-400{% else %}text-green-400{% endif %}">{{ user.age }} Jahre</p>
</div>
<div>
<p class="text-sm text-gray-400">Überprüft von</p>
<p class="text-xl font-semibold">{{ user.checked_by }} am {{ user.timestamp.strftime('%d.%m.%Y um %H:%M') }}</p>
</div>
</div>
{% if result %}
<div class="mt-6 border-t {% if maintenance_active %}border-yellow-500/30{% else %}border-purple-500/20{% endif %} pt-4">
<h4 class="text-xl font-semibold mb-2">Aktion erforderlich</h4>
<p class="text-lg mt-2">Diese Person muss für <span class="font-bold text-yellow-400">{{ result.ban_duration_days }}</span> Tage gesperrt werden.</p>
<div class="mt-4">
<label for="ban-command" class="block text-sm font-medium text-gray-300">Bann-Befehl</label>
<div class="flex items-center gap-2 mt-1">
<input type="text" id="ban-command" readonly value="{{ result.ban_command }}"
class="w-full px-3 py-2 bg-gray-800 border border-gray-700 rounded-lg text-white">
<button onclick="copyToClipboard('ban-command')" class="flex-shrink-0 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 %}">
Kopieren
</button>
</div>
</div>
</div>
{% elif user.age >= 13 %}
<div class="mt-6 border-t {% if maintenance_active %}border-yellow-500/30{% else %}border-purple-500/20{% endif %} pt-4">
<p class="text-lg text-green-400">Keine Sperre erforderlich.</p>
</div>
{% endif %}
</div>
<script>
function copyToClipboard(elementId) {
const input = document.getElementById(elementId);
input.select();
input.setSelectionRange(0, 99999); // For mobile devices
document.execCommand('copy');
// Optional: Visuelles Feedback geben
const originalButtonText = event.target.innerText;
event.target.innerText = 'Kopiert!';
setTimeout(() => {
event.target.innerText = originalButtonText;
}, 1500);
}
</script>
{% endblock %}

View file

@ -61,6 +61,7 @@ try:
dashboard_collection = db["dashboard_stats"]
discord_bots_collection = db["discord_bots_data"]
ip_analyzer_results_collection = db["ip_analyzer_results"]
age_verification_collection = db["age_verification"]
print("INFO: MongoDB-Verbindung erfolgreich hergestellt.")
AVAILABLE_PERMISSIONS = [
"view_dashboard",
@ -72,7 +73,8 @@ try:
"maintenance_management",
"bypass_maintenance",
"manage_discord_bots",
"view_ip_analyzer"
"view_ip_analyzer",
"view_age_verification"
]
if roles_collection.count_documents({}) == 0:
print("WARNUNG: Keine Rollen in der Datenbank gefunden. Erstelle Standard-Rollen...")
@ -691,6 +693,83 @@ def delete_role(role_id):
roles_collection.delete_one({'_id': ObjectId(role_id)})
flash("Rolle wurde gelöscht.", "success")
return redirect(url_for('role_management'))
@app.route('/age_verification', methods=['GET', 'POST'])
@login_required
@permission_required('view_age_verification')
def age_verification():
result = None
search_query = request.args.get('search', '')
if request.method == 'POST':
birthdate_str = request.form.get('birthdate')
steam_id = request.form.get('steam_id')
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')
today = datetime.today()
age = today.year - birthdate_dt.year - ((today.month, today.day) < (birthdate_dt.month, birthdate_dt.day))
user_data = {
'birthdate': birthdate_str,
'steam_id': steam_id,
'ingame_name': ingame_name,
'age': age,
'checked_by': session.get('username'),
'timestamp': datetime.utcnow()
}
age_verification_collection.insert_one(user_data)
result = {'age': age, 'steam_id': steam_id}
if age < 13:
thirteenth_birthday = birthdate_dt.replace(year=birthdate_dt.year + 13)
ban_duration_days = (thirteenth_birthday - today).days + 1
result['ban_duration_days'] = ban_duration_days
result['ban_command'] = f"oban {steam_id} {ban_duration_days}d"
flash("Altersüberprüfung erfolgreich durchgeführt und gespeichert.", "success")
if search_query:
users = list(age_verification_collection.find({
"$or": [
{"ingame_name": {"$regex": search_query, "$options": "i"}},
{"steam_id": {"$regex": search_query, "$options": "i"}}
]
}).sort("timestamp", -1))
else:
users = list(age_verification_collection.find().sort("timestamp", -1).limit(10))
return render_template('age_verification.html',
active_page='age_verification',
result=result,
users=users,
search_query=search_query)
@app.route('/age_verification/<user_id>')
@login_required
@permission_required('view_age_verification')
def view_user_age_details(user_id):
user = age_verification_collection.find_one({'_id': ObjectId(user_id)})
if not user:
flash("Benutzer nicht gefunden.", "danger")
return redirect(url_for('age_verification'))
result = None
if user.get('age') < 13:
today = datetime.today()
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
if ban_duration_days > 0:
result = {
'ban_duration_days': ban_duration_days,
'ban_command': f"oban {user['steam_id']} {ban_duration_days}d"
}
return render_template('view_user_age_details.html',
active_page='age_verification',
user=user,
result=result)
@app.route('/ip_analyzer', methods=['GET', 'POST'])
@login_required
@permission_required('view_ip_analyzer')

View file

@ -885,59 +885,6 @@ def start_bots_on_server_start():
start_bots_on_server_start()
@app.route("/verify_age", methods=["GET", "POST"])
@csrf.exempt
def verify_age():
if "username" not in session:
return redirect(url_for("login"))
age = None
birthdate_dt = None
today = datetime.today()
if request.method == "POST":
birthdate = request.form["birthdate"]
steam_id = request.form["steam_id"]
ingame_name = request.form["ingame_name"]
birthdate_dt = datetime.strptime(birthdate, "%Y-%m-%d")
age = today.year - birthdate_dt.year - ((today.month, today.day) < (birthdate_dt.month, birthdate_dt.day))
user_ages_collection.insert_one({
"username": session["username"],
"birthdate": birthdate,
"age": age,
"steam_id": steam_id,
"ingame_name": ingame_name,
"timestamp": datetime.now()
})
search_query = request.args.get('search')
if search_query:
users = user_ages_collection.find({
"$or": [
{"ingame_name": {"$regex": search_query, "$options": "i"}},
{"steam_id": {"$regex": search_query, "$options": "i"}}
]
})
else:
users = user_ages_collection.find().limit(10)
return render_template("verify_age.html", age=age, birthdate_dt=birthdate_dt, today=today, users=users)
@app.route("/verify_age/<user_id>", methods=["GET"])
def view_user_age(user_id):
if "username" not in session:
return redirect(url_for("login"))
user = user_ages_collection.find_one({"_id": ObjectId(user_id)})
if user:
user['_id'] = str(user['_id'])
return render_template("view_user_age.html", user=user)
else:
return "Benutzer nicht gefunden", 404
@app.route('/logout')
def logout():
add_log(f"{session['username']} hat sich ausgeloggt")