Login Page hinzugefügt

This commit is contained in:
MrWhiff 2025-12-06 21:12:08 +01:00
parent d4497ea17b
commit 7c838ee732
52 changed files with 7384 additions and 0 deletions

View file

@ -0,0 +1,162 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AstraOS — Login</title>
<script src="https://cdn.tailwindcss.com"></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/TextPlugin.min.js"></script>
<style>
body {
background: radial-gradient(circle at top, #1b1b26, #0a0a0f 70%);
}
</style>
</head>
<body class="text-white">
<div id="startup" class="fixed inset-0 bg-black flex flex-col items-center justify-center z-50">
<div id="star" class="w-16 h-16 relative">
<div class="absolute inset-0 rounded-full blur-2xl opacity-70"
style="background: rgba(192, 132, 252, 0.7)"></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="#ffffff"
opacity="0.9"
/>
</svg>
</div>
<p class="mt-4 text-xl tracking-wide opacity-80">AstraOS</p>
</div>
<div id="loginContent" class="min-h-screen flex items-center justify-center px-4">
<div id="loginBox" class="w-full max-w-md bg-black/40 border border-purple-500/20 rounded-2xl p-8 backdrop-blur-lg shadow-xl opacity-0">
<h1 id="loginTitle" class="text-3xl font-bold text-center mb-6 tracking-wider text-purple-300">
</h1>
<form id="loginForm" action="/login" method="POST" class="flex flex-col gap-5 opacity-0">
<div>
<label class="text-gray-300 text-sm">Username</label>
<input type="text" name="username" required placeholder="Username"
class="mt-1 w-full px-4 py-2 rounded-lg bg-gray-800/60 text-white
focus:outline-none focus:ring-2 focus:ring-purple-600">
</div>
<div x-data="{ showPassword: false }">
<label class="text-gray-300 text-sm">Passwort</label>
<div class="relative">
<input :type="showPassword ? 'text' : 'password'" name="password" required placeholder="Passwort"
class="mt-1 w-full px-4 py-2 rounded-lg bg-gray-800/60 text-white
focus:outline-none focus:ring-2 focus:ring-purple-600 pr-10">
<button type="button" @click="showPassword = !showPassword" class="absolute inset-y-0 right-0 top-0 pr-3 flex items-center text-gray-400 hover:text-gray-200">
<svg x-show="!showPassword" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.432 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<svg x-show="showPassword" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5" style="display: none;">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.98 8.223A10.477 10.477 0 001.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.45 10.45 0 0112 4.5c4.756 0 8.773 3.162 10.065 7.498a10.523 10.523 0 01-4.293 5.774M6.228 6.228L3 3m3.228 3.228l3.65 3.65m7.894 7.894L21 21m-3.228-3.228l-3.65-3.65m0 0a3 3 0 10-4.243-4.243m4.243 4.243l-4.243-4.243" />
</svg>
</button>
</div>
</div>
<button type="submit"
class="mt-2 bg-purple-600 hover:bg-purple-700 text-white py-2 rounded-lg
font-semibold transition">
Login
</button>
</form>
</div>
</div>
<script>
gsap.to("#star", {
rotation: 360,
duration: 1.6,
ease: "power1.inOut",
repeat: -1
});
gsap.to("#star", {
scale: 1.22,
duration: 1,
yoyo: true,
repeat: -1,
ease: "power1.inOut"
});
gsap.registerPlugin(TextPlugin);
setTimeout(() => {
gsap.to("#startup", {
opacity: 0,
duration: 0.6,
onComplete: () => {
document.getElementById("startup").style.display = "none";
const tl = gsap.timeline();
tl.fromTo("#loginBox",
{ opacity: 0, scale: 0.1 },
{
opacity: 1,
scale: 1,
duration: 0.8,
ease: "power2.out"
}
);
tl.to("#loginTitle", {
duration: 1,
text: "AstraOS Login",
ease: "none"
}, "-=0.2");
tl.to("#loginForm", {
opacity: 1,
duration: 0.5
}, "-=0.5");
}
});
}, 1500);
</script>
</body>
</html>

0
AstraOS/astra_os.py Normal file
View file

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -0,0 +1,234 @@
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
background-color: #f4f4f9;
color: #333;
}
.top-bar {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #42028b;
color: white;
padding: 10px 20px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
.logo img {
height: 40px;
vertical-align: middle;
}
.title {
font-size: 24px;
font-weight: bold;
}
.username {
display: flex;
align-items: center;
position: relative;
}
.username span {
margin-right: 10px;
}
.username img {
height: 30px;
width: 30px;
border-radius: 50%;
cursor: pointer;
}
.username .dropdown {
display: none;
position: absolute;
top: 40px;
right: 0;
background-color: white;
border: 1px solid #ccc;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
border-radius: 5px;
z-index: 10;
opacity: 0;
transition: opacity 0.3s ease-out;
}
.username .dropdown a {
display: block;
padding: 10px 20px;
color: #333;
text-decoration: none;
}
.username .dropdown a:hover {
background-color: #f4f4f9;
}
.username:hover .dropdown {
display: block;
opacity: 1;
}
.username .dropdown.hide {
opacity: 0;
transition: opacity 0.3s ease-out 0.5s;
}
.settings-button, .logout-button, .black-button {
background-color: #007bff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
margin: 10px 0;
text-decoration: none;
}
.settings-button:hover, .logout-button:hover, .black-button:hover {
background-color: #0056b3;
}
.black-button {
background-color: #000;
}
.black-button:hover {
background-color: #333;
}
.content {
padding: 20px;
}
h1 {
font-size: 28px;
}
p {
font-size: 16px;
line-height: 1.5;
}
button {
background-color: #007bff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #0056b3;
}
input {
width: 100%;
padding: 10px;
margin-bottom: 20px;
border: 1px solid #ddd;
border-radius: 4px;
}
@media (max-width: 768px) {
.top-bar {
flex-direction: column;
align-items: flex-start;
}
.username .dropdown {
right: auto;
left: 0;
}
}
#popup {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.7);
z-index: 1000;
justify-content: center;
align-items: center;
}
#popup .popup-content {
background: white;
padding: 20px;
border-radius: 8px;
width: 80%;
max-width: 600px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
text-align: center;
position: relative;
}
#popup .popup-header {
font-size: 24px;
font-weight: bold;
margin-bottom: 20px;
}
#popup .popup-close {
position: absolute;
top: 10px;
right: 10px;
cursor: pointer;
font-size: 20px;
font-weight: bold;
}
.bar-container {
display: flex;
justify-content: space-around;
margin-top: 30px;
height: 300px;
align-items: flex-end;
position: relative;
}
.bar {
width: 30%;
background-color: #007bff;
transition: height 2s ease-out;
position: relative;
}
.bar span {
position: absolute;
bottom: -30px;
left: 50%;
transform: translateX(-50%);
font-size: 16px;
font-weight: bold;
color: #333;
}
#criteria-text {
margin-bottom: 20px;
font-size: 18px;
color: #333;
text-align: left;
}
@media (max-width: 768px) {
#popup .popup-content {
width: 90%;
padding: 15px;
}
.bar-container {
flex-direction: column;
height: auto;
}
}

View file

@ -0,0 +1,211 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #0D1117;
color: #c9d1d9;
line-height: 1.6;
}
header {
text-align: center;
padding: 20px;
background-color: #161b22;
border-bottom: 2px solid #30363d;
}
h1, h2, h3 {
color: #58a6ff;
}
main {
max-width: 1200px;
margin: 40px auto;
padding: 20px;
}
section {
margin-bottom: 40px;
}
h2, h3 {
color: #c9d1d9;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #30363d;
}
th {
background-color: #161b22;
}
a {
color: #58a6ff;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
button {
background-color: #238636;
color: #fff;
border: none;
padding: 10px 20px;
border-radius: 8px;
cursor: pointer;
}
button:disabled {
background-color: #30363d;
cursor: not-allowed;
}
button:hover:enabled {
background-color: #2ea043;
}
.upload-btn {
background-color: #238636;
color: #fff;
padding: 10px 20px;
border-radius: 8px;
display: inline-block;
margin-top: 20px;
}
.upload-btn:hover {
background-color: #2ea043;
}
/* Custom Datei-Upload */
.custom-file-upload {
background-color: #21262d;
border: 2px dashed #58a6ff;
padding: 20px;
text-align: center;
border-radius: 8px;
cursor: pointer;
margin-bottom: 20px;
}
.custom-file-upload label {
cursor: pointer;
color: #58a6ff;
font-weight: bold;
}
#fileNameDisplay {
display: block;
margin-top: 10px;
color: #c9d1d9;
}
form {
background-color: #161b22;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
max-width: 600px;
margin: 20px auto;
}
.form-group {
margin-bottom: 20px;
}
label {
color: #c9d1d9;
font-size: 16px;
display: block;
margin-bottom: 8px;
}
input[type="text"], input[type="file"] {
width: 100%;
padding: 10px;
border-radius: 5px;
border: 1px solid #30363d;
background-color: #0d1117;
color: #c9d1d9;
}
input[type="text"]:focus, input[type="file"]:focus {
outline: none;
border-color: #58a6ff;
}
.upload-icon {
width: 24px;
height: 24px;
vertical-align: middle;
margin-left: 10px;
}
footer {
text-align: center;
margin-top: 40px;
}
footer a {
color: #58a6ff;
text-decoration: none;
}
footer a:hover {
text-decoration: underline;
}
.btn {
display: inline-block;
background-color: #238636;
color: #ffffff;
border: none;
padding: 8px 20px;
border-radius: 6px;
text-align: center;
cursor: pointer;
text-decoration: none;
font-weight: bold;
transition: background 0.3s ease;
}
.btn:hover {
background-color: #2ea043;
}
.btn-danger {
background-color: #da3633;
}
.btn-danger:hover {
background-color: #f85149;
}
.btn-primary {
background-color: #0d1117;
border: 1px solid #30363d;
}
.btn-primary:hover {
background-color: #161b22;
}
.inline-form {
display: inline;
margin-left: 5px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7 KiB

View file

@ -0,0 +1,18 @@
{% if session['theme'] == 'darkmode' %}
{% extends "base_dark.html" %}
{% else %}
{% extends "base.html" %}
{% endif %}
{% block title %}Add Changelog{% endblock %}
{% block content %}
<div class="form-container" style="max-width: 600px; margin: 30px auto;">
<h2>Add a New Changelog</h2>
<form action="{{ url_for('add_changelog') }}" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<textarea name="changelog" rows="5" style="width: 100%; padding: 10px; border-radius: 8px; border: 1px solid #ccc;" placeholder="Enter the changelog details here..." required></textarea>
<button type="submit" style="margin-top: 10px; background-color: #007BFF; color: white; padding: 10px 20px; border: none; border-radius: 8px; cursor: pointer;">Submit</button>
</form>
</div>
{% endblock %}

View file

@ -0,0 +1,79 @@
{% if session['theme'] == 'darkmode' %}
{% extends "base_dark.html" %}
{% else %}
{% extends "base.html" %}
{% endif %}
{% block title %}Termin Planer{% endblock %}
{% block content %}
<div class="content-wrapper">
<div class="form-container" style="max-width: 1000px; padding: 30px;">
<h2>Termin Planer</h2>
<div id="calendar" style="margin-top: 20px;">
<table style="width: 100%; border-collapse: collapse; text-align: center;">
<thead>
<tr>
<th style="padding: 10px; border: 1px solid #ddd;">Montag</th>
<th style="padding: 10px; border: 1px solid #ddd;">Dienstag</th>
<th style="padding: 10px; border: 1px solid #ddd;">Mittwoch</th>
<th style="padding: 10px; border: 1px solid #ddd;">Donnerstag</th>
<th style="padding: 10px; border: 1px solid #ddd;">Freitag</th>
<th style="padding: 10px; border: 1px solid #ddd;">Samstag</th>
<th style="padding: 10px; border: 1px solid #ddd;">Sonntag</th>
</tr>
</thead>
<tbody>
{% for week in calendar %}
<tr>
{% for day in week %}
<td style="padding: 10px; border: 1px solid #ddd; vertical-align: top;">
<strong>{{ day.date }}</strong>
<ul style="list-style: none; padding: 0; margin: 10px 0;">
{% for event in day.events %}
<li>{{ event.time }} - {{ event.player }}: {{ event.description }} (Für: {{ event.assigned_to }})</li>
{% endfor %}
</ul>
<button onclick="openEventModal('{{ day.date }}')" style="margin-top: 10px;">+ Termin</button>
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div id="eventModal" style="display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); z-index: 1000;">
<h3>Neuen Termin hinzufügen</h3>
<form method="POST" action="{{ url_for('appointment_scheduler') }}">
<input type="hidden" name="date" id="eventDate">
<label for="time">Uhrzeit:</label>
<input type="time" name="time" id="time" required>
<br>
<label for="player">Spieler:</label>
<input type="text" name="player" id="player" required>
<br>
<label for="description">Beschreibung:</label>
<input type="text" name="description" id="description" required>
<br>
<label for="assigned_to">Für:</label>
<input type="text" name="assigned_to" id="assigned_to" required>
<br>
<button type="submit" style="margin-top: 10px;">Speichern</button>
<button type="button" onclick="closeEventModal()" style="margin-top: 10px;">Abbrechen</button>
</form>
</div>
<script>
function openEventModal(date) {
document.getElementById('eventDate').value = date;
document.getElementById('eventModal').style.display = 'block';
}
function closeEventModal() {
document.getElementById('eventModal').style.display = 'none';
}
</script>
{% endblock %}

View file

@ -0,0 +1,159 @@
{% extends "base.html" if session["theme"] == "lightmode" else "base_dark.html" %}
{% block title %}Termin Planer{% endblock %}
{% block content %}
<div class="calendar-wrapper" style="max-width: 1200px; margin: 30px auto; display: flex; gap: 20px;">
<div class="calendar-container" style="flex: 1; background: {% if session['theme'] == 'lightmode' %}#f0f0f0{% else %}#323232{% endif %}; padding: 20px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);">
<h2 style="margin-bottom: 20px;">📅 Termin Planer</h2>
<div class="calendar-navigation" style="margin: 20px 0;">
<button onclick="navigateMonth(-1)" class="neon-button neon1"><span>⬅️ Vorheriger Monat</span></button>
<span id="currentMonth" style="margin: 0 20px; font-size: 18px; font-weight: bold;"></span>
<button onclick="navigateMonth(1)" class="neon-button neon1"><span>➡️ Nächster Monat</span></button>
</div>
<div id="calendar" style="display: grid; grid-template-columns: repeat(7, 1fr); gap: 10px; text-align: center;">
</div>
<div style="margin-top: 20px;">
<a href="{{ url_for('dashboard') }}" class="neon-button neon2"><span>🔙 Zurück zum Dashboard</span></a>
</div>
</div>
<div class="appointments-container" style="flex: 1; background: {% if session['theme'] == 'lightmode' %}#ffffff{% else %}#2c2c2c{% endif %}; padding: 20px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);">
<h2 style="margin-bottom: 20px;">📋 Anstehende Termine</h2>
{% if appointments %}
<ul style="list-style: none; padding: 0;">
{% for appointment in appointments %}
<li style="margin-bottom: 15px; padding: 10px; border: 1px solid #ccc; border-radius: 8px; background: {% if session['theme'] == 'lightmode' %}#f9f9f9{% else %}#3a3a3a{% endif %};">
<strong>{{ appointment.title }}</strong><br>
<small>{{ appointment.start_date }} - {{ appointment.end_date }}</small><br>
<span>{{ appointment.description }}</span>
</li>
{% endfor %}
</ul>
{% else %}
<p>Keine anstehenden Termine.</p>
{% endif %}
</div>
</div>
<<div id="appointmentPopup" style="display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: {% if session['theme'] == 'lightmode' %}#ffffff{% else %}#2c2c2c{% endif %}; padding: 20px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); z-index: 1000; width: 300px;">
<h3>Termin hinzufügen</h3>
<form id="appointmentForm" method="POST" action="{{ url_for('appointments') }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<label for="title">Titel:</label>
<input type="text" id="title" name="title" style="width: 100%; margin-bottom: 10px; padding: 8px; border-radius: 6px; border: 1px solid #ccc;" required>
<label for="start_date">Startdatum:</label>
<input type="date" id="start_date" name="start_date" style="width: 100%; margin-bottom: 10px; padding: 8px; border-radius: 6px; border: 1px solid #ccc;" required>
<label for="end_date">Enddatum:</label>
<input type="date" id="end_date" name="end_date" style="width: 100%; margin-bottom: 10px; padding: 8px; border-radius: 6px; border: 1px solid #ccc;" required>
<label for="description">Beschreibung:</label>
<textarea id="description" name="description" style="width: 100%; margin-bottom: 10px; padding: 8px; border-radius: 6px; border: 1px solid #ccc;"></textarea>
<button type="submit" class="neon-button neon1" style="margin-top: 10px;">Speichern</button>
<button type="button" onclick="closePopup()" class="neon-button neon3" style="margin-top: 10px;">Abbrechen</button>
</form>
</div>
<div id="popupOverlay" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); z-index: 999;" onclick="closePopup()"></div>
<script>
const calendar = document.getElementById('calendar');
const currentMonth = document.getElementById('currentMonth');
const popup = document.getElementById('appointmentPopup');
const overlay = document.getElementById('popupOverlay');
let date = new Date();
const today = new Date();
function renderCalendar() {
calendar.innerHTML = '';
const year = date.getFullYear();
const month = date.getMonth();
currentMonth.textContent = date.toLocaleString('default', { month: 'long', year: 'numeric' });
const firstDay = new Date(year, month, 1).getDay();
const daysInMonth = new Date(year, month + 1, 0).getDate();
for (let i = 0; i < firstDay; i++) {
calendar.innerHTML += '<div></div>';
}
for (let day = 1; day <= daysInMonth; day++) {
const isToday = today.getFullYear() === year && today.getMonth() === month && today.getDate() === day;
const backgroundColor = isToday
? (sessionStorage.getItem('theme') === 'darkmode' ? '#1e3a8a' : '#ffeb3b')
: 'transparent';
calendar.innerHTML += `<div style="padding: 10px; border: 1px solid #555; border-radius: 4px; cursor: pointer; background-color: ${backgroundColor};" onclick="openPopup(${day})">${day}</div>`;
}
}
function navigateMonth(offset) {
date.setMonth(date.getMonth() + offset);
renderCalendar();
}
function openPopup(day) {
const selectedDate = new Date(date.getFullYear(), date.getMonth(), day);
document.getElementById('start_date').value = selectedDate.toISOString().split('T')[0];
document.getElementById('end_date').value = selectedDate.toISOString().split('T')[0];
popup.style.display = 'block';
overlay.style.display = 'block';
}
function closePopup() {
popup.style.display = 'none';
overlay.style.display = 'none';
}
renderCalendar();
</script>
<style>
.neon-button {
width: auto;
min-width: 150px;
padding: 14px 20px;
font-size: 15px;
font-weight: bold;
border: 2px solid;
color: inherit;
background: transparent;
cursor: pointer;
position: relative;
overflow: hidden;
transition: 0.3s;
box-shadow: 0 0 8px currentColor, 0 0 14px currentColor inset;
margin: 10px 0;
border-radius: 6px;
text-align: center;
}
.neon1 { color: #00fff7; border-color: #00fff7; }
.neon2 { color: #ff00ff; border-color: #ff00ff; }
.neon3 { color: #39ff14; border-color: #39ff14; }
.neon-button::before {
content: '';
position: absolute;
top: 0; left: 0;
width: 100%; height: 100%;
background: currentColor;
z-index: 0;
transform: scaleX(0);
transform-origin: left;
transition: 0.3s ease-in-out;
}
.neon-button:hover::before {
transform: scaleX(1);
}
.neon-button span {
position: relative;
z-index: 1;
transition: color 0.3s;
}
.neon-button:hover span {
color: #000;
}
</style>
{% endblock %}

View file

@ -0,0 +1,154 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Moonlab{% endblock %}</title>
<link rel="stylesheet" href="/static/style.css">
<link rel="icon" href="/static/logo.png" type="image/png">
<style>
html, body {
height: 100%;
overflow: hidden;
}
body {
background-image: url('/static/background.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
height: 100vh;
margin: 0;
display: flex;
flex-direction: column;
}
.content-wrapper {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
height: 100vh;
overflow: hidden;
}
.form-container {
background: rgba(255, 255, 255, 0.6);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
max-width: 400px;
width: 100%;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
box-sizing: border-box;
}
.form-container input[type="text"],
.form-container input[type="password"] {
width: 100%;
max-width: 350px;
box-sizing: border-box;
padding: 10px;
margin: 8px 0 16px 0;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 15px;
background: #fff;
}
.theme-toggle {
position: fixed;
bottom: 20px;
right: 20px;
width: 60px;
height: 60px;
border-radius: 16px;
background-color: gray;
background-size: 70%;
background-position: center;
background-repeat: no-repeat;
cursor: pointer;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
transition: background-image 0.3s ease;
}
.neon-button {
width: 100%;
padding: 14px 20px;
font-size: 15px;
font-weight: bold;
border: 2px solid;
color: inherit;
background: transparent;
cursor: pointer;
position: relative;
overflow: hidden;
transition: 0.3s;
box-shadow: 0 0 8px currentColor, 0 0 14px currentColor inset;
margin: 10px 0;
border-radius: 6px;
}
.neon1 { color: #00fff7; border-color: #00fff7; }
.neon2 { color: #ff00ff; border-color: #ff00ff; }
.neon3 { color: #39ff14; border-color: #39ff14; }
.neon-button::before {
content: '';
position: absolute;
top: 0; left: 0;
width: 100%; height: 100%;
background: currentColor;
z-index: 0;
transform: scaleX(0);
transform-origin: left;
transition: 0.3s ease-in-out;
}
.neon-button:hover::before {
transform: scaleX(1);
}
.neon-button span {
position: relative;
z-index: 1;
transition: color 0.3s;
}
.neon-button:hover span {
color: #000;
}
</style>
</head>
<body>
{% include "top_bar.html" %}
<div class="content-wrapper">
{% block content %}
{% endblock %}
</div>
<div class="theme-toggle" id="themeToggle" style="background-image: url('/static/{{ 'night-mode.png' if session['theme'] == 'lightmode' else 'light-mode.png' }}');"></div>
<script>
const themeToggle = document.getElementById('themeToggle');
themeToggle.addEventListener('click', () => {
fetch('/toggle-theme', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'same-origin'
}).then(response => {
if (response.ok) {
location.reload();
} else {
console.error('Failed to toggle theme:', response.statusText);
}
});
});
</script>
</body>
</html>

View file

@ -0,0 +1,288 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Moonlab{% endblock %}</title>
<link rel="stylesheet" href="/static/style.css">
<link rel="icon" href="/static/logo.png" type="image/png">
<style>
@import url('https://fonts.googleapis.com/css?family=Exo:400,700');
html, body {
height: 100%;
overflow: hidden;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Exo', sans-serif;
margin: 0;
color: white;
overflow: hidden;
}
.area {
background: #000;
width: 100%;
height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: -1;
}
.circles {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
.circles li {
position: absolute;
display: block;
list-style: none;
width: 20px;
height: 20px;
background: rgba(255, 255, 255, 0.2);
animation: animate 25s linear infinite;
bottom: -150px;
}
.circles li:nth-child(1) {
left: 25%;
width: 80px;
height: 80px;
animation-delay: 0s;
}
.circles li:nth-child(2) {
left: 10%;
width: 20px;
height: 20px;
animation-delay: 2s;
animation-duration: 12s;
}
.circles li:nth-child(3) {
left: 70%;
width: 20px;
height: 20px;
animation-delay: 4s;
}
.circles li:nth-child(4) {
left: 40%;
width: 60px;
height: 60px;
animation-delay: 0s;
animation-duration: 18s;
}
.circles li:nth-child(5) {
left: 65%;
width: 20px;
height: 20px;
animation-delay: 0s;
}
.circles li:nth-child(6) {
left: 75%;
width: 110px;
height: 110px;
animation-delay: 3s;
}
.circles li:nth-child(7) {
left: 35%;
width: 150px;
height: 150px;
animation-delay: 7s;
}
.circles li:nth-child(8) {
left: 50%;
width: 25px;
height: 25px;
animation-delay: 15s;
animation-duration: 45s;
}
.circles li:nth-child(9) {
left: 20%;
width: 15px;
height: 15px;
animation-delay: 2s;
animation-duration: 35s;
}
.circles li:nth-child(10) {
left: 85%;
width: 150px;
height: 150px;
animation-delay: 0s;
animation-duration: 11s;
}
@keyframes animate {
0% {
transform: translateY(0) rotate(0deg);
opacity: 1;
border-radius: 0;
}
100% {
transform: translateY(-1000px) rotate(720deg);
opacity: 0;
border-radius: 50%;
}
}
.content-wrapper {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.form-container {
position: relative;
z-index: 1;
background: rgba(30, 30, 30, 0.85);
padding: 30px;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.6);
width: 100%;
max-width: 400px;
text-align: center;
backdrop-filter: blur(4px);
display: flex;
flex-direction: column;
align-items: center;
}
.theme-toggle {
position: fixed;
bottom: 20px;
right: 20px;
width: 60px;
height: 60px;
border-radius: 16px;
background-color: gray;
background-size: 70%;
background-position: center;
background-repeat: no-repeat;
cursor: pointer;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
transition: background-image 0.3s ease;
}
.neon-button {
width: 100%;
padding: 14px 20px;
font-size: 15px;
font-weight: bold;
border: 2px solid;
color: inherit;
background: transparent;
cursor: pointer;
position: relative;
overflow: hidden;
transition: 0.3s;
box-shadow: 0 0 8px currentColor, 0 0 14px currentColor inset;
margin: 10px 0;
border-radius: 6px;
}
.neon1 { color: #00fff7; border-color: #00fff7; }
.neon2 { color: #ff00ff; border-color: #ff00ff; }
.neon3 { color: #39ff14; border-color: #39ff14; }
.neon-button::before {
content: '';
position: absolute;
top: 0; left: 0;
width: 100%; height: 100%;
background: currentColor;
z-index: 0;
transform: scaleX(0);
transform-origin: left;
transition: 0.3s ease-in-out;
}
.neon-button:hover::before {
transform: scaleX(1);
}
.neon-button span {
position: relative;
z-index: 1;
transition: color 0.3s;
}
.neon-button:hover span {
color: #111;
}
.logout-button {
color: #ff4d4d;
border-color: #ff4d4d;
}
.logout-button:hover span {
color: #111;
}
</style>
</head>
<body>
<div class="area">
<ul class="circles">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
{% include "top_bar.html" %}
<div class="content-wrapper">
{% block content %}
{% endblock %}
</div>
<div class="theme-toggle" id="themeToggle" style="background-image: url('/static/{{ 'night-mode.png' if session['theme'] == 'lightmode' else 'light-mode.png' }}');"></div>
<script>
const themeToggle = document.getElementById('themeToggle');
themeToggle.addEventListener('click', () => {
fetch('/toggle-theme', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'same-origin'
}).then(response => {
if (response.ok) {
location.reload();
} else {
console.error('Failed to toggle theme:', response.statusText);
}
});
});
</script>
</body>
</html>

View file

@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bot Logs</title>
<link rel="stylesheet" href="/static/style.css">
<link rel="icon" href="/static/logo.png" type="image/png">
<style>
.logs-container {
background: rgba(255, 255, 255, 0.6);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
max-width: 600px;
width: 100%;
margin: auto;
text-align: left;
}
.logs-container h2 {
text-align: center;
margin-bottom: 20px;
}
.log-entry {
background: #f9f9f9;
padding: 10px;
border-radius: 4px;
margin-bottom: 10px;
}
</style>
</head>
<body>
{% include "top_bar.html" %}
<div class="logs-container">
<h2>Logs für {{ bot_name }}</h2>
{% for log in logs %}
<div class="log-entry">{{ log }}</div>
{% endfor %}
<a href="{{ url_for('dc_bots') }}" class="black-button">Zurück</a>
</div>
</body>
</html>

View file

@ -0,0 +1,133 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>License Key erstellen</title>
<link rel="stylesheet" href="/static/style.css">
<link rel="icon" href="/static/logo.png" type="image/png">
<style>
body {
background-image: url('/static/background.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
height: 100vh;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
.content {
background: rgba(255, 255, 255, 0.8);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
width: 80%;
max-width: 800px;
max-height: 90vh;
overflow-y: auto;
text-align: center;
}
.content h2 {
text-align: center;
}
.content .license-key {
display: flex;
align-items: center;
margin: 20px 0;
}
.content .license-key input {
width: 300px;
padding: 10px;
margin-right: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
.content .license-key button {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
}
.content .license-key button:hover {
background-color: #0056b3;
}
.content .message {
display: none;
color: green;
margin-top: 10px;
}
.content form {
display: flex;
flex-direction: column;
align-items: center;
}
.content form label {
margin-top: 10px;
width: 100%;
text-align: left;
}
.content form input,
.content form textarea,
.content form select {
width: 100%;
padding: 10px;
margin-top: 5px;
border: 1px solid #ccc;
border-radius: 4px;
}
.content form button {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
margin-top: 20px;
}
.content form button:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
{% include "top_bar.html" %}
<div class="content">
<h2>License Key erstellen</h2>
{% if license_key %}
<div class="license-key">
<input type="text" id="licenseKey" value="{{ license_key }}" readonly>
<button onclick="copyToClipboard()">Kopieren</button>
</div>
<div class="message" id="message">
<img src="/static/green_check.png" alt="Check" style="width: 20px; vertical-align: middle;">
License key wurde kopiert
</div>
{% elif error %}
<p>{{ error }}</p>
{% endif %}
<form action="{{ url_for('create_license_key') }}" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit">Neuen License Key generieren</button>
</form>
<a href="/license_keys" class="black-button">Zurück</a>
</div>
<script>
function copyToClipboard() {
var copyText = document.getElementById("licenseKey");
copyText.select();
copyText.setSelectionRange(0, 99999);
document.execCommand("copy");
var message = document.getElementById("message");
message.style.display = "block";
setTimeout(function() {
message.style.display = "none";
}, 2000);
}
</script>
</body>
</html>

View file

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Neues Projekt erstellen</title>
<style>
body {
background-color: gray;
color: white;
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.container {
background-color: #333;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
max-width: 400px;
width: 100%;
text-align: center;
}
h1 {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 10px;
}
input[type="text"] {
width: 100%;
padding: 10px;
margin-bottom: 20px;
border: none;
border-radius: 4px;
}
button {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 20px;
cursor: pointer;
border-radius: 4px;
}
button:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<div class="container">
<h1>Neues Projekt erstellen</h1>
<form method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<label for="project_name">Projektname:</label>
<input type="text" id="project_name" name="project_name" required>
<button type="submit">Erstellen</button>
</form>
</div>
</body>
</html>

View file

@ -0,0 +1,123 @@
{% extends "base_dark.html" %}
{% block title %}Dashboard (Dark Mode){% endblock %}
{% block content %}
<div class="dashboard-wrapper" style="max-width: 1200px; margin: 80px auto 30px auto;">
<div class="dashboard-container" style="display: flex; justify-content: space-between; gap: 20px;">
<style>
.dashboard-box {
background: rgba(30, 30, 30, 0.85);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.6);
width: 100%;
max-width: 300px;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
.dashboard-box h3 {
margin-bottom: 15px;
color: #00fff7;
}
.dashboard-box form, .dashboard-box a {
margin-bottom: 10px;
width: 100%;
}
.dashboard-box button, .dashboard-box a {
width: 100%;
text-align: center;
}
</style>
<div class="dashboard-box">
<h3>Administration</h3>
{% if 'user_management' in session['permissions'] %}
<form action="{{ url_for('user_management') }}" method="get">
<button type="submit" class="neon-button neon1"><span>👤 User Management</span></button>
</form>
{% endif %}
{% if 'delete_requests' in session['permissions'] %}
<form action="{{ url_for('delete_requests') }}" method="get">
<button type="submit" class="neon-button neon3"><span>🗑️ Delete-Requests</span></button>
</form>
{% endif %}
{% if 'logs' in session['permissions'] %}
<form action="{{ url_for('logs') }}" method="get">
<button type="submit" class="neon-button"><span>📜 Logs</span></button>
</form>
{% endif %}
{% if 'changelogs' in session['permissions'] %}
<form action="{{ url_for('add_changelog') }}" method="get">
<button type="submit" class="neon-button neon1"><span>📝 Add-Changelog</span></button>
</form>
{% endif %}
<form action="{{ url_for('view_changelogs') }}" method="get">
<button type="submit" class="neon-button neon2"><span>📋 Changelogs</span></button>
</form>
</div>
<div class="dashboard-box">
<h3>Private Tools</h3>
{% if 'discord_bots' in session['permissions'] %}
<form action="{{ url_for('dc_bots') }}" method="get">
<button type="submit" class="neon-button neon2"><span>🤖 Discord Bots</span></button>
</form>
{% endif %}
{% if 'appointments' in session['permissions'] %}
<form action="{{ url_for('appointments') }}" method="get">
<button type="submit" class="neon-button neon1"><span>📅 Termin Planer</span></button>
</form>
{% endif %}
{% if 'view_license_keys' in session['permissions'] %}
<form action="{{ url_for('license_keys') }}" method="get">
<button type="submit" class="neon-button neon1"><span>🛡️ ML Cheat Scanner</span></button>
</form>
{% endif %}
</div>
<div class="dashboard-box">
<h3>Moonlab Tools</h3>
{% if 'intranet' in session['permissions'] %}
<form action="https://intra.moon-lab.de" method="get" target="_blank">
<button type="submit" class="neon-button neon2"><span>🌐 Moonlab-Intranet</span></button>
</form>
{% endif %}
{% if 'create_event_feedback' in session['permissions'] %}
<form action="{{ url_for('event_feedback') }}" method="get">
<button type="submit" class="neon-button"><span>📝 Event Feedback</span></button>
</form>
{% endif %}
{% if 'view_event_feedback' in session['permissions'] %}
<form action="{{ url_for('view_event_feedbacks') }}" method="get">
<button type="submit" class="neon-button"><span>📊 Event Feedback ansehen</span></button>
</form>
{% endif %}
{% if "verify_age" in session["permissions"] %}
<form action="{{ url_for('verify_age') }}" method="get">
<button type="submit" class="neon-button neon3"><span>🎂 Altersüberprüfung</span></button>
</form>
{% endif %}
{% if 'events' in session['permissions'] %}
<form action="{{ url_for('events') }}" method="get">
<button type="submit" class="neon-button neon2"><span>🎉 Events</span></button>
</form>
{% endif %}
{% if 'ip_checker' in session['permissions'] %}
<form action="{{ url_for('ip_checker') }}" method="get">
<button type="submit" class="neon-button neon3"><span>🌍 IP Checker</span></button>
</form>
{% endif %}
</div>
</div>
<div style="display: flex; justify-content: center; gap: 20px; margin-top: 20px;">
<form action="{{ url_for('logout') }}" method="get">
<button type="submit" class="neon-button neon3"><span>🚪 Logout</span></button>
</form>
<form action="{{ url_for('settings') }}" method="get">
<button type="submit" class="neon-button neon2"><span>⚙️ Einstellungen</span></button>
</form>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,124 @@
{% extends "base.html" %}
{% block title %}Dashboard{% endblock %}
{% block content %}
<div class="dashboard-wrapper" style="max-width: 1200px; margin: 30px auto;">
<div class="dashboard-container" style="display: flex; justify-content: space-between; gap: 20px;">
<style>
.dashboard-box {
background: rgba(255, 255, 255, 0.6);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 300px;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
.dashboard-box h3 {
margin-bottom: 15px;
}
.dashboard-box form, .dashboard-box a {
margin-bottom: 10px;
width: 100%;
}
.dashboard-box button, .dashboard-box a {
width: 100%;
text-align: center;
}
</style>
<div class="dashboard-box">
<h3>Administration</h3>
{% if 'user_management' in session['permissions'] %}
<form action="{{ url_for('user_management') }}" method="get">
<button type="submit" style="background-color: #1ac583;">👤 User Management</button>
</form>
{% endif %}
{% if 'delete_requests' in session['permissions'] %}
<form action="{{ url_for('delete_requests') }}" method="get">
<button type="submit" style="background-color: #ff0000;">🗑️ Delete-Requests</button>
</form>
{% endif %}
{% if 'logs' in session['permissions'] %}
<form action="{{ url_for('logs') }}" method="get">
<button type="submit" style="background-color: #007BFF;">📜 Logs</button>
</form>
{% endif %}
{% if 'changelogs' in session['permissions'] %}
<form action="{{ url_for('add_changelog') }}" method="get">
<button type="submit" style="background-color: #FFD700;">📝 Add-Changelog</button>
</form>
{% endif %}
<form action="{{ url_for('view_changelogs') }}" method="get">
<button type="submit" style="background-color: #00bfff;">📋 Changelogs</button>
</form>
</div>
<div class="dashboard-box">
<h3>Private Tools</h3>
{% if 'discord_bots' in session['permissions'] %}
<form action="{{ url_for('dc_bots') }}" method="get">
<button type="submit" style="background-color: #0b55c4;">🤖 Discord Bots</button>
</form>
{% endif %}
{% if 'appointments' in session['permissions'] %}
<form action="{{ url_for('appointments') }}" method="get">
<button type="submit" style="background-color: #1ac583;">📅 Termin Planer</button>
</form>
{% endif %}
{% if 'view_license_keys' in session['permissions'] %}
<form action="{{ url_for('license_keys') }}" method="get">
<button type="submit" style="background-color: #4d0c97;">🛡️ ML Cheat Scanner</button>
</form>
{% endif %}
</div>
<div class="dashboard-box">
<h3>Moonlab Tools</h3>
{% if 'intranet' in session['permissions'] %}
<form action="https://intra.moon-lab.de" method="get" target="_blank">
<button type="submit" style="background-color: #4b0082;">🌐 Moonlab-Intranet</button>
</form>
{% endif %}
{% if 'create_event_feedback' in session['permissions'] %}
<form action="{{ url_for('event_feedback') }}" method="get">
<button type="submit" style="background-color: #007BFF;">📝 Event Feedback</button>
</form>
{% endif %}
{% if 'view_event_feedback' in session['permissions'] %}
<form action="{{ url_for('view_event_feedbacks') }}" method="get">
<button type="submit" style="background-color: #007BFF;">📊 Event Feedback ansehen</button>
</form>
{% endif %}
{% if "verify_age" in session["permissions"] %}
<form action="{{ url_for('verify_age') }}" method="get">
<button type="submit" style="background-color: #5de700;">🎂 Altersüberprüfung</button>
</form>
{% endif %}
{% if 'events' in session['permissions'] %}
<form action="{{ url_for('events') }}" method="get">
<button type="submit" style="background-color: #ff00ff;">🎉 Events</button>
</form>
{% endif %}
{% if 'ip_checker' in session['permissions'] %}
<form action="{{ url_for('ip_checker') }}" method="get">
<button type="submit" style="background-color: #ff8c00;">🌍 IP Checker</button>
</form>
{% endif %}
</div>
</div>
<div style="display: flex; justify-content: center; gap: 20px; margin-top: 20px;">
<form action="{{ url_for('logout') }}" method="get">
<button type="submit" style="background-color: #ff4d4d;">🚪 Logout</button>
</form>
<form action="{{ url_for('settings') }}" method="get">
<button type="submit" style="background-color: #4b0082;">⚙️ Einstellungen</button>
</form>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,527 @@
{% if session['theme'] == 'darkmode' %}
{% extends "base_dark.html" %}
{% else %}
{% extends "base.html" %}
{% endif %}
{% block title %}Discord Bots{% endblock %}
{% block content %}
{% if session['theme'] == 'darkmode' %}
<style>
.bots-outer {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
.bots-container {
background: rgba(20, 20, 20, 0.95);
padding: 30px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.7);
max-width: 1100px;
width: 100%;
min-height: 300px;
max-height: 90vh;
overflow-y: auto;
text-align: center;
}
.button-container {
display: flex;
justify-content: center;
width: 100%;
margin-bottom: 20px;
gap: 16px;
}
.button-container form {
margin: 0;
}
.button-container .neon-button {
min-width: 150px;
margin: 0 6px;
}
.add-bot-button {
min-width: 170px;
margin: 0 6px;
}
.bot-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 20px;
padding: 20px 0;
}
.bot-box {
background: rgba(40, 40, 40, 0.9);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.4);
width: 340px;
text-align: center;
position: relative;
margin-bottom: 16px;
display: flex;
flex-direction: column;
align-items: center;
}
.bot-box h3 {
margin-bottom: 18px;
color: #00fff7;
}
.bot-box form {
margin: 0 0 8px 0;
width: 100%;
}
.bot-box button {
width: 100%;
margin: 4px 0;
padding: 10px 0;
border-radius: 4px;
font-weight: bold;
}
.bot-box .turn-on {
background: none;
color: #39ff14;
border: 2px solid #39ff14;
box-shadow: 0 0 8px #39ff14, 0 0 14px #39ff14 inset;
transition: 0.3s;
}
.bot-box .turn-on:hover {
background: #39ff14;
color: #111;
}
.bot-box .turn-off {
background: none;
color: #ff4d4d;
border: 2px solid #ff4d4d;
box-shadow: 0 0 8px #ff4d4d, 0 0 14px #ff4d4d inset;
transition: 0.3s;
}
.bot-box .turn-off:hover {
background: #ff4d4d;
color: #111;
}
.bot-box .logs {
background: none;
color: #00fff7;
border: 2px solid #00fff7;
box-shadow: 0 0 8px #00fff7, 0 0 14px #00fff7 inset;
transition: 0.3s;
}
.bot-box .logs:hover {
background: #00fff7;
color: #111;
}
.add-bot-button {
background: none;
color: #ff00ff;
border: 2px solid #ff00ff;
box-shadow: 0 0 8px #ff00ff, 0 0 14px #ff00ff inset;
font-weight: bold;
padding: 14px 20px;
border-radius: 6px;
cursor: pointer;
transition: 0.3s;
}
.add-bot-button:hover {
background: #ff00ff;
color: #111;
}
.modal {
display: none;
position: fixed;
z-index: 10;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.4);
}
.modal-content {
background-color: #222;
color: #fff;
margin: 10% auto;
padding: 20px;
border: 1px solid #555;
width: 90%;
max-width: 400px;
border-radius: 8px;
text-align: center;
}
.close {
color: #ccc;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: #fff;
text-decoration: none;
cursor: pointer;
}
.status-icon {
position: absolute;
top: 10px;
right: 10px;
width: 30px;
height: 30px;
}
.bots-back-btn-row {
display: flex;
justify-content: center;
margin-top: 18px;
}
.bots-back-btn {
display: inline-block;
text-align: center;
width: auto;
padding: 14px 28px;
margin-top: 10px;
}
</style>
<div class="bots-outer">
<div class="bots-container">
<div class="button-container">
<form action="{{ url_for('dashboard') }}" method="get">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="neon-button neon3"><span>Zurück zum Dashboard</span></button>
</form>
<form action="{{ url_for('add_dc_bot') }}" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="neon-button neon1"><span>Bot hinzufügen</span></button>
</form>
</div>
<div class="button-container">
<button class="add-bot-button" onclick="document.getElementById('addBotModal').style.display='block'">Add Discord Bot</button>
<form action="{{ url_for('turn_on_all_bots') }}" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="neon-button neon3"><span>Turn On All</span></button>
</form>
<form action="{{ url_for('turn_off_all_bots') }}" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="neon-button neon2"><span>Turn Off All</span></button>
</form>
</div>
<h2 style="color:#00fff7;">Discord Bots</h2>
<div id="addBotModal" class="modal">
<div class="modal-content">
<span class="close" onclick="document.getElementById('addBotModal').style.display='none'">&times;</span>
<h2>Add Discord Bot</h2>
<form action="{{ url_for('add_dc_bot') }}" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<label for="bot_name">Bot Name:</label>
<input type="text" id="bot_name" name="bot_name" required>
<label for="bot_directory">Bot Directory:</label>
<input type="text" id="bot_directory" name="bot_directory" required>
<button type="submit" class="neon-button neon1"><span>Add Bot</span></button>
</form>
</div>
</div>
<div class="bot-container">
{% for bot in bots %}
<div class="bot-box">
<h3>{{ bot.name }}</h3>
<img src="/static/{{ 'circle_green_checkmark.png' if 'pid' in bot else 'offline.png' }}" alt="{{ 'Online' if 'pid' in bot else 'Offline' }}" class="status-icon">
<form action="{{ url_for('turn_on_bot', bot_name=bot.name) }}" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="turn-on">Turn On</button>
</form>
<form action="{{ url_for('turn_off_bot', bot_name=bot.name) }}" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="turn-off">Turn Off</button>
</form>
<form action="{{ url_for('bot_logs', bot_name=bot.name) }}" method="get">
<button type="submit" class="logs">Logs</button>
</form>
</div>
{% endfor %}
</div>
<div class="bots-back-btn-row">
<a href="{{ url_for('dashboard') }}" class="neon-button neon3 bots-back-btn"><span>Zurück</span></a>
</div>
</div>
</div>
<div id="statusModal" class="modal">
<div class="modal-content">
<span class="close" onclick="document.getElementById('statusModal').style.display='none'">&times;</span>
<p id="statusMessage"></p>
</div>
</div>
<script>
function checkBotStatus(botName, isRunning, action) {
if ((action === 'on' && isRunning) || (action === 'off' && !isRunning)) {
document.getElementById('statusMessage').innerText = `Bot ${botName} kann nicht ${action === 'on' ? 'eingeschaltet' : 'ausgeschaltet'} werden, da er bereits ${action === 'on' ? 'läuft' : 'nicht läuft'}.`;
document.getElementById('statusModal').style.display = 'block';
return false;
}
return true;
}
window.onclick = function(event) {
if (event.target == document.getElementById('addBotModal')) {
document.getElementById('addBotModal').style.display = "none";
}
if (event.target == document.getElementById('statusModal')) {
document.getElementById('statusModal').style.display = "none";
}
}
</script>
{% else %}
<style>
.bots-outer {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
.bots-container {
background: rgba(255,255,255,0.9);
padding: 30px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
max-width: 1100px;
width: 100%;
min-height: 300px;
max-height: 90vh;
overflow-y: auto;
text-align: center;
}
.button-container {
display: flex;
justify-content: center;
width: 100%;
margin-bottom: 20px;
gap: 16px;
}
.button-container form {
margin: 0;
}
.add-bot-button {
background-color: #007BFF;
color: white;
border: none;
padding: 14px 20px;
border-radius: 6px;
font-weight: bold;
cursor: pointer;
min-width: 170px;
margin: 0 6px;
transition: background 0.3s;
}
.add-bot-button:hover {
background-color: #0056b3;
}
.bot-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 20px;
padding: 20px 0;
}
.bot-box {
background: rgba(180, 180, 180, 0.9);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
width: 340px;
text-align: center;
position: relative;
margin-bottom: 16px;
display: flex;
flex-direction: column;
align-items: center;
}
.bot-box h3 {
margin-bottom: 18px;
color: #007BFF;
}
.bot-box form {
margin: 0 0 8px 0;
width: 100%;
}
.bot-box button {
width: 100%;
margin: 4px 0;
padding: 10px 0;
border-radius: 4px;
font-weight: bold;
border: none;
cursor: pointer;
transition: background 0.3s;
}
.bot-box .turn-on {
background-color: #28a745;
color: white;
}
.bot-box .turn-on:hover {
background-color: #218838;
}
.bot-box .turn-off {
background-color: #dc3545;
color: white;
}
.bot-box .turn-off:hover {
background-color: #b52a37;
}
.bot-box .logs {
background-color: #fd7e14;
color: white;
}
.bot-box .logs:hover {
background-color: #c76a10;
}
.modal {
display: none;
position: fixed;
z-index: 10;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.4);
}
.modal-content {
background-color: #fefefe;
color: #000;
margin: 10% auto;
padding: 20px;
border: 1px solid #888;
width: 90%;
max-width: 400px;
border-radius: 8px;
text-align: center;
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
.status-icon {
position: absolute;
top: 10px;
right: 10px;
width: 30px;
height: 30px;
}
.bots-back-btn-row {
display: flex;
justify-content: center;
margin-top: 18px;
}
.bots-back-btn {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
width: auto;
text-align: center;
display: inline-block;
text-decoration: none;
margin-top: 10px;
}
.bots-back-btn:hover {
background-color: #0056b3;
}
</style>
<div class="bots-outer">
<div class="bots-container">
<div class="button-container">
<form action="{{ url_for('dashboard') }}" method="get">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="bots-back-btn">Zurück zum Dashboard</button>
</form>
<form action="{{ url_for('add_dc_bot') }}" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="add-bot-button">Bot hinzufügen</button>
</form>
</div>
<div class="button-container">
<button class="add-bot-button" onclick="document.getElementById('addBotModal').style.display='block'">Add Discord Bot</button>
<form action="{{ url_for('turn_on_all_bots') }}" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="add-bot-button">Turn On All</button>
</form>
<form action="{{ url_for('turn_off_all_bots') }}" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="add-bot-button">Turn Off All</button>
</form>
</div>
<h2 style="color:#007BFF;">Discord Bots</h2>
<div id="addBotModal" class="modal">
<div class="modal-content">
<span class="close" onclick="document.getElementById('addBotModal').style.display='none'">&times;</span>
<h2>Add Discord Bot</h2>
<form action="{{ url_for('add_dc_bot') }}" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<label for="bot_name">Bot Name:</label>
<input type="text" id="bot_name" name="bot_name" required>
<label for="bot_directory">Bot Directory:</label>
<input type="text" id="bot_directory" name="bot_directory" required>
<button type="submit" class="add-bot-button">Add Bot</button>
</form>
</div>
</div>
<div class="bot-container">
{% for bot in bots %}
<div class="bot-box">
<h3>{{ bot.name }}</h3>
<img src="/static/{{ 'circle_green_checkmark.png' if 'pid' in bot else 'offline.png' }}" alt="{{ 'Online' if 'pid' in bot else 'Offline' }}" class="status-icon">
<form action="{{ url_for('turn_on_bot', bot_name=bot.name) }}" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="turn-on">Turn On</button>
</form>
<form action="{{ url_for('turn_off_bot', bot_name=bot.name) }}" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="turn-off">Turn Off</button>
</form>
<form action="{{ url_for('bot_logs', bot_name=bot.name) }}" method="get">
<button type="submit" class="logs">Logs</button>
</form>
</div>
{% endfor %}
</div>
<div class="bots-back-btn-row">
<a href="{{ url_for('dashboard') }}" class="bots-back-btn">Zurück</a>
</div>
</div>
</div>
<div id="statusModal" class="modal">
<div class="modal-content">
<span class="close" onclick="document.getElementById('statusModal').style.display='none'">&times;</span>
<p id="statusMessage"></p>
</div>
</div>
<script>
function checkBotStatus(botName, isRunning, action) {
if ((action === 'on' && isRunning) || (action === 'off' && !isRunning)) {
document.getElementById('statusMessage').innerText = `Bot ${botName} kann nicht ${action === 'on' ? 'eingeschaltet' : 'ausgeschaltet'} werden, da er bereits ${action === 'on' ? 'läuft' : 'nicht läuft'}.`;
document.getElementById('statusModal').style.display = 'block';
return false;
}
return true;
}
window.onclick = function(event) {
if (event.target == document.getElementById('addBotModal')) {
document.getElementById('addBotModal').style.display = "none";
}
if (event.target == document.getElementById('statusModal')) {
document.getElementById('statusModal').style.display = "none";
}
}
</script>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,237 @@
{% if session['theme'] == 'darkmode' %}
{% extends "base_dark.html" %}
{% else %}
{% extends "base.html" %}
{% endif %}
{% block title %}Lösch Anfragen{% endblock %}
{% block content %}
{% if session['theme'] == 'darkmode' %}
<style>
.delete-container {
background: rgba(20, 20, 20, 0.95);
padding: 30px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.7);
max-width: 800px;
width: 100%;
min-height: 300px;
max-height: 90vh;
overflow-y: auto;
text-align: center;
}
.delete-container h2 {
text-align: center;
color: #00fff7;
}
.delete-container table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
color: #fff;
}
.delete-container th, .delete-container td {
padding: 10px;
border: 1px solid #333;
text-align: left;
}
.delete-container th {
background-color: #00fff7;
color: #111;
}
.delete-container .message {
color: #ff4d4d;
margin-top: 10px;
}
.approve-button {
background-color: #39ff14;
color: #111;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
font-weight: bold;
}
.approve-button:hover {
background-color: #28a745;
color: #fff;
}
.deny-button {
background-color: #FF0000;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
font-weight: bold;
}
.deny-button:hover {
background-color: #CC0000;
}
.delete-back-btn-row {
display: flex;
justify-content: center;
margin-top: 18px;
}
.delete-back-btn {
display: inline-block;
text-align: center;
width: auto;
padding: 14px 28px;
margin-top: 10px;
}
</style>
<div style="display:flex; flex-direction:column; align-items:center; width:100%;">
<div class="delete-container">
<h2>Lösch Anfragen</h2>
{% if delete_requests|length == 0 %}
<p class="message">Zurzeit keine aktive Lösch-Anfragen</p>
{% else %}
<table>
<tr>
<th>Lizenzschlüssel</th>
<th>Benutzername</th>
<th>Aktionen</th>
</tr>
{% for request in delete_requests %}
<tr>
<td>{{ request['license_key'] }}</td>
<td>{{ request['username'] }}</td>
<td>
<form action="{{ url_for('approve_delete_request', request_id=request['_id']) }}" method="post" style="display:inline;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="approve-button">Genehmigen</button>
</form>
<form action="{{ url_for('deny_delete_request', request_id=request['_id']) }}" method="post" style="display:inline;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="deny-button">Ablehnen</button>
</form>
</td>
</tr>
{% endfor %}
</table>
{% endif %}
<div class="delete-back-btn-row">
<a href="{{ url_for('dashboard') }}" class="neon-button neon3 delete-back-btn"><span>Zurück</span></a>
</div>
</div>
</div>
{% else %}
<style>
.delete-container {
background: rgba(255,255,255,0.9);
padding: 30px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
max-width: 800px;
width: 100%;
min-height: 300px;
max-height: 90vh;
overflow-y: auto;
text-align: center;
}
.delete-container h2 {
text-align: center;
color: #007BFF;
}
.delete-container table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
color: #333;
}
.delete-container th, .delete-container td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.delete-container th {
background-color: #007BFF;
color: white;
}
.delete-container .message {
color: red;
margin-top: 10px;
}
.approve-button {
background-color: #28a745;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
}
.approve-button:hover {
background-color: #218838;
}
.deny-button {
background-color: #FF0000;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
}
.deny-button:hover {
background-color: #CC0000;
}
.delete-back-btn-row {
display: flex;
justify-content: center;
margin-top: 18px;
}
.delete-back-btn {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
width: auto;
text-align: center;
display: inline-block;
text-decoration: none;
margin-top: 10px;
}
.delete-back-btn:hover {
background-color: #0056b3;
}
</style>
<div style="display:flex; flex-direction:column; align-items:center; width:100%;">
<div class="delete-container">
<h2>Lösch Anfragen</h2>
{% if delete_requests|length == 0 %}
<p class="message">Zurzeit keine aktive Lösch-Anfragen</p>
{% else %}
<table>
<tr>
<th>Lizenzschlüssel</th>
<th>Benutzername</th>
<th>Aktionen</th>
</tr>
{% for request in delete_requests %}
<tr>
<td>{{ request['license_key'] }}</td>
<td>{{ request['username'] }}</td>
<td>
<form action="{{ url_for('approve_delete_request', request_id=request['_id']) }}" method="post" style="display:inline;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="approve-button">Genehmigen</button>
</form>
<form action="{{ url_for('deny_delete_request', request_id=request['_id']) }}" method="post" style="display:inline;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="deny-button">Ablehnen</button>
</form>
</td>
</tr>
{% endfor %}
</table>
{% endif %}
<div class="delete-back-btn-row">
<a href="{{ url_for('dashboard') }}" class="delete-back-btn">Zurück</a>
</div>
</div>
</div>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,162 @@
{% if session['theme'] == 'darkmode' %}
{% extends "base_dark.html" %}
{% else %}
{% extends "base.html" %}
{% endif %}
{% block title %}Benutzer bearbeiten{% endblock %}
{% block content %}
<style>
.profile-pic-hover-effect {
transition: box-shadow 0.3s ease-in-out;
}
.profile-pic-hover-effect:hover {
box-shadow: 0 0 15px 5px rgba(0, 255, 247, 0.7);
}
.success-message {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background-color: rgba(0, 200, 0, 0.8);
color: white;
padding: 10px 20px;
border-radius: 5px;
z-index: 1000;
opacity: 0;
transition: opacity 0.5s ease-in-out;
}
.success-message.show {
opacity: 1;
}
</style>
<div class="content-wrapper">
<div class="form-container">
<label for="pfp_upload" style="cursor: pointer;">
<img id="profile_pic_preview"
src="/static/pfps/{{ user['_id'] }}.png?t={{ user.get('website_accounts', {}).get('pfp_last_updated', 0) }}"
alt="User Profile Picture"
onerror="this.onerror=null; this.src='/static/user.png';"
class="profile-pic-hover-effect"
style="width: 150px; height: 150px; margin-bottom: 20px; border-radius: 50%; object-fit: cover;">
</label>
<form id="edit_user_form" method="POST" action="{{ url_for('edit_user', user_id=user['_id']) }}" enctype="multipart/form-data" style="width: 100%;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="file" id="pfp_upload" name="pfp" accept="image/png, image/jpeg" style="display: none;">
<h2>Benutzer bearbeiten</h2>
<label for="display_name">Anzeigename:</label>
<input type="text" id="display_name" name="display_name" value="{{ user['display_name'] }}" required>
<label for="password">Neues Passwort:</label>
<input type="password" id="password" name="password">
<p><strong>Berechtigungen:</strong> {{ user['permissions'] | join(', ') }}</p>
<div style="display: flex; flex-direction: column; gap: 10px; width: 100%;">
{% if session['theme'] == 'lightmode' %}
<button type="submit" class="blue-button"><span>Speichern</span></button>
<a href="/dashboard" class="black-button"><span>Zurück</span></a>
{% else %}
<button type="submit" class="neon-button neon1" style="width: 100%;"><span>Speichern</span></button>
<a href="/dashboard" class="neon-button neon3" style="width: 100%; text-align: center;"><span>Zurück</span></a>
{% endif %}
</div>
</form>
</div>
</div>
<div id="success_message" class="success-message"></div>
<script>
const pfpUploadInput = document.getElementById('pfp_upload');
const profilePicPreview = document.getElementById('profile_pic_preview');
const editUserForm = document.getElementById('edit_user_form');
const successMessage = document.getElementById('success_message');
let imageJustUploaded = false; // Flag, um zu verhindern, dass der Submit mehrfach ausgelöst wird
// Trigger file input when image is clicked
profilePicPreview.addEventListener('click', () => {
pfpUploadInput.click();
});
// Display selected image immediately and submit the form
pfpUploadInput.addEventListener('change', (event) => {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
profilePicPreview.src = e.target.result;
// Automatisches Absenden des Formulars, sobald ein Bild ausgewählt wurde
if (!imageJustUploaded) {
imageJustUploaded = true;
editUserForm.requestSubmit();
}
};
reader.readAsDataURL(file);
}
});
// Handle form submission with Fetch API for instant update
editUserForm.addEventListener('submit', async (event) => {
event.preventDefault(); // Prevent default form submission (wird jetzt immer aufgerufen)
const formData = new FormData(editUserForm);
const userId = "{{ user['_id'] }}"; // Get user ID from Jinja context
try {
const response = await fetch(`/edit_user/${userId}`, {
method: 'POST',
body: formData
});
if (response.ok) {
const result = await response.json(); // Assuming server returns JSON
if (result.success) {
// Show success message
successMessage.textContent = "Änderungen gespeichert!";
successMessage.classList.add('show');
setTimeout(() => {
successMessage.classList.remove('show');
imageJustUploaded = false; // Reset Flag nach erfolgreicher Speicherung
// Optional: Hier könntest du auch zur Dashboard-Seite weiterleiten
// window.location.href = "/dashboard";
}, 3000); // Meldung für 3 Sekunden anzeigen
// Update profile picture on success (sollte bereits durch die Vorschau geschehen sein)
if (result.pfp_url) {
// Append a timestamp to bust cache (wird auch beim ersten Laden gemacht)
profilePicPreview.src = result.pfp_url + '?t=' + new Date().getTime();
}
console.log('Profilbild erfolgreich gespeichert!');
} else {
console.error('Fehler beim Speichern des Profilbildes:', result.message);
successMessage.textContent = 'Fehler beim Speichern!';
successMessage.classList.add('show');
setTimeout(() => {
successMessage.classList.remove('show');
imageJustUploaded = false; // Reset Flag bei Fehler
}, 3000);
}
} else {
console.error('Serverfehler:', response.status, response.statusText);
successMessage.textContent = 'Serverfehler!';
successMessage.classList.add('show');
setTimeout(() => {
successMessage.classList.remove('show');
imageJustUploaded = false; // Reset Flag bei Fehler
}, 3000);
}
} catch (error) {
console.error('Netzwerkfehler:', error);
successMessage.textContent = 'Netzwerkfehler!';
successMessage.classList.add('show');
setTimeout(() => {
successMessage.classList.remove('show');
imageJustUploaded = false; // Reset Flag bei Fehler
}, 3000);
}
});
</script>
{% endblock %}

View file

@ -0,0 +1,120 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Event Feedback</title>
<link rel="stylesheet" href="/static/style.css">
<link rel="icon" href="/static/logo.png" type="image/png">
<style>
body {
background-image: url('/static/background.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
height: 100vh;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
.content {
background: rgba(255, 255, 255, 0.8);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
width: 80%;
max-width: 600px;
max-height: 90vh;
overflow-y: auto;
text-align: center;
}
.content form {
display: flex;
flex-direction: column;
align-items: center;
}
.content form label {
margin-top: 10px;
width: 100%;
text-align: left;
}
.content form input,
.content form textarea,
.content form select {
width: 100%;
padding: 10px;
margin-top: 5px;
border: 1px solid #ccc;
border-radius: 4px;
}
.content form button {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
margin-top: 20px;
}
.content form button:hover {
background-color: #0056b3;
}
</style>
<script>
function toggleOtherInput(selectId, inputId) {
var selectElement = document.getElementById(selectId);
var inputElement = document.getElementById(inputId);
if (selectElement.value === "Anderes") {
inputElement.style.display = "block";
} else {
inputElement.style.display = "none";
}
}
</script>
</head>
<body>
{% include "top_bar.html" %}
<div class="content">
<h1>Event Feedback</h1>
<form method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<label for="event_name">Event:</label>
<select id="event_name" name="event_name" onchange="toggleOtherInput('event_name', 'other_event_name')" required>
<option value="Simon Says">Simon Says</option>
<option value="Speedevent">Speedevent</option>
<option value="SCP999">SCP999</option>
<option value="Hide and Seek mit SCP939">Hide and Seek mit SCP939</option>
<option value="Hide and Seek mit Scaling">Hide and Seek mit Scaling</option>
<option value="Team Deathmatch auf der Surface">Team Deathmatch auf der Surface</option>
<option value="Team Deathmatch in der Heavy Zone">Team Deathmatch in der Heavy Zone</option>
<option value="Team Deathmatch in der Light">Team Deathmatch in der Light</option>
<option value="Anderes">Anderes</option>
</select>
<input type="text" id="other_event_name" name="other_event_name" placeholder="Event Name eingeben" style="display:none;">
<label for="event_progress">Verlauf des Events:</label>
<textarea id="event_progress" name="event_progress" required></textarea>
<input type="text" id="other_event_progress" name="other_event_progress" placeholder="Verlauf des Events eingeben" style="display:none;">
<label for="event_end">Ende des Events:</label>
<textarea id="event_end" name="event_end" required></textarea>
<input type="text" id="other_event_end" name="other_event_end" placeholder="Ende des Events eingeben" style="display:none;">
<label for="community_feedback">Feedback der Community:</label>
<textarea id="community_feedback" name="community_feedback" required></textarea>
<input type="text" id="other_community_feedback" name="other_community_feedback" placeholder="Feedback der Community eingeben" style="display:none;">
<label for="improvement_suggestions">Verbesserungsvorschläge:</label>
<textarea id="improvement_suggestions" name="improvement_suggestions" required></textarea>
<button type="submit">Abschicken</button>
</form>
<form action="{{ url_for('dashboard') }}" method="get">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit">Zurück</button>
</form>
</div>
</body>
</html>

View file

@ -0,0 +1,332 @@
{% if session['theme'] == 'darkmode' %}
{% extends "base_dark.html" %}
{% else %}
{% extends "base.html" %}
{% endif %}
{% block title %}Events{% endblock %}
{% block content %}
<style>
.events-flex-row {
display: flex;
justify-content: center;
align-items: flex-start;
gap: 30px;
margin-top: 40px;
flex-wrap: nowrap;
}
.events-flex-row .form-container {
margin: 0;
min-width: 320px;
max-width: 400px;
flex: 1 1 320px;
}
.events-back-btn-row {
display: flex;
justify-content: center;
margin-top: 40px;
}
.events-form-light button[type="submit"] {
width: 100%;
padding: 14px 20px;
font-size: 15px;
font-weight: bold;
border: 2px solid #007BFF;
color: #007BFF;
background: transparent;
cursor: pointer;
border-radius: 6px;
margin-top: 16px;
margin-bottom: 10px;
transition: background 0.2s, color 0.2s;
}
.events-form-light button[type="submit"]:hover {
background: #007BFF;
color: #fff;
}
.events-back-btn-light {
min-width: 150px;
text-align: center;
padding: 14px 20px;
font-size: 15px;
font-weight: bold;
border: 2px solid #007BFF;
color: #007BFF;
background: transparent;
border-radius: 6px;
margin-top: 10px;
transition: background 0.2s, color 0.2s;
display: inline-block;
}
.events-back-btn-light:hover {
background: #007BFF;
color: #fff;
}
.events-back-btn-dark {
width: auto;
min-width: unset;
max-width: unset;
padding: 14px 28px;
display: inline-block;
text-align: center;
}
</style>
{% if session['theme'] == 'darkmode' %}
<div>
<div class="events-flex-row">
<div class="form-container">
<h2>Event hinzufügen</h2>
<form action="{{ url_for('events') }}" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<label for="event_name">Event Name:</label>
<input type="text" id="event_name" name="event_name" required>
<label for="community_feedback">Hat es der Community gefallen?</label>
<select id="community_feedback" name="community_feedback" required>
<option value="top">Ja</option>
<option value="flop">Nein</option>
<option value="etwas">Teilweise</option>
</select>
<label for="players">Spieleranzahl:</label>
<input type="number" id="players" name="players" required>
<label for="time">Uhrzeit:</label>
<input type="time" id="time" name="time" required>
<label for="weekday">Wochentag:</label>
<select id="weekday" name="weekday" required>
<option value="Montag">Montag</option>
<option value="Dienstag">Dienstag</option>
<option value="Mittwoch">Mittwoch</option>
<option value="Donnerstag">Donnerstag</option>
<option value="Freitag">Freitag</option>
<option value="Samstag">Samstag</option>
<option value="Sonntag">Sonntag</option>
</select>
<button type="submit" class="neon-button neon1"><span>Event hinzufügen</span></button>
</form>
</div>
<div class="form-container">
<h2>Bestes Event berechnen</h2>
<form action="{{ url_for('events') }}" method="get">
<label for="current_players">Aktuelle Spieler:</label>
<input type="number" id="current_players" name="current_players" required>
<label for="current_time">Aktuelle Uhrzeit:</label>
<input type="time" id="current_time" name="current_time" required>
<label for="current_weekday">Aktueller Wochentag:</label>
<select id="current_weekday" name="current_weekday" required>
<option value="Montag">Montag</option>
<option value="Dienstag">Dienstag</option>
<option value="Mittwoch">Mittwoch</option>
<option value="Donnerstag">Donnerstag</option>
<option value="Freitag">Freitag</option>
<option value="Samstag">Samstag</option>
<option value="Sonntag">Sonntag</option>
</select>
<button type="submit" name="calculate_best" value="1" id="calculateBestEvent" class="neon-button neon2"><span>Bestes Event berechnen</span></button>
</form>
{% if best_events %}
<h2>Top 5 Events</h2>
<div id="barChartContainer" style="width:100%;height:260px;display:flex;align-items:end;justify-content:space-around;gap:10px;margin-top:30px;">
</div>
<script>
const bestEvents = {{ best_events|tojson }};
const colors = ["#00fff7", "#ff00ff", "#39ff14", "#ffb300", "#ff4d4d"];
function animateBars() {
const maxRating = Math.max(...bestEvents.map(e => e.rating));
const container = document.getElementById('barChartContainer');
container.innerHTML = '';
bestEvents.forEach((event, idx) => {
const bar = document.createElement('div');
bar.style.height = '0px';
bar.style.width = '60px';
bar.style.background = colors[idx % colors.length];
bar.style.borderRadius = '8px 8px 0 0';
bar.style.display = 'flex';
bar.style.flexDirection = 'column';
bar.style.alignItems = 'center';
bar.style.justifyContent = 'flex-end';
bar.style.transition = 'height 1s cubic-bezier(.68,-0.55,.27,1.55)';
bar.style.position = 'relative';
const label = document.createElement('div');
label.innerText = event.name;
label.style.position = 'absolute';
label.style.bottom = '-32px';
label.style.width = '100%';
label.style.textAlign = 'center';
label.style.fontSize = '13px';
label.style.color = '#fff';
label.style.wordBreak = 'break-word';
const value = document.createElement('div');
value.innerText = event.rating;
value.style.position = 'absolute';
value.style.top = '-28px';
value.style.width = '100%';
value.style.textAlign = 'center';
value.style.fontWeight = 'bold';
value.style.fontSize = '15px';
value.style.color = '#fff';
bar.appendChild(label);
bar.appendChild(value);
container.appendChild(bar);
setTimeout(() => {
bar.style.height = (event.rating / (maxRating || 1) * 180 + 30) + 'px';
}, 100 + idx * 120);
});
}
window.addEventListener('DOMContentLoaded', animateBars);
</script>
{% elif best_event %}
<h2>Bestes Event</h2>
<p>{{ best_event.name }} - Rating: {{ best_event.rating }} (Top: {{ best_event.top }}, Flop: {{ best_event.flop }})</p>
{% endif %}
</div>
<div class="form-container">
<h2>Protokollierte Events</h2>
<ul style="list-style-type: none; padding: 0;">
{% set event_names = [] %}
{% for event in events %}
{% if event.name not in event_names %}
<li style="background: rgba(255,255,255,0.35); margin: 5px 0; padding: 10px; border-radius: 4px;">{{ event.name }}</li>
{% set event_names = event_names.append(event.name) %}
{% endif %}
{% endfor %}
</ul>
</div>
</div>
<div class="events-back-btn-row">
<a href="javascript:window.history.back()" class="neon-button neon3 events-back-btn-dark"><span>Zurück</span></a>
</div>
</div>
{% else %}
<div>
<div class="events-flex-row">
<div class="form-container">
<h2>Event hinzufügen</h2>
<form action="{{ url_for('events') }}" method="post" class="events-form-light">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<label for="event_name">Event Name:</label>
<input type="text" id="event_name" name="event_name" required>
<label for="community_feedback">Hat es der Community gefallen?</label>
<select id="community_feedback" name="community_feedback" required>
<option value="top">Ja</option>
<option value="flop">Nein</option>
<option value="etwas">Teilweise</option>
</select>
<label for="players">Spieleranzahl:</label>
<input type="number" id="players" name="players" required>
<label for="time">Uhrzeit:</label>
<input type="time" id="time" name="time" required>
<label for="weekday">Wochentag:</label>
<select id="weekday" name="weekday" required>
<option value="Montag">Montag</option>
<option value="Dienstag">Dienstag</option>
<option value="Mittwoch">Mittwoch</option>
<option value="Donnerstag">Donnerstag</option>
<option value="Freitag">Freitag</option>
<option value="Samstag">Samstag</option>
<option value="Sonntag">Sonntag</option>
</select>
<button type="submit">Event hinzufügen</button>
</form>
</div>
<div class="form-container">
<h2>Bestes Event berechnen</h2>
<form action="{{ url_for('events') }}" method="get" class="events-form-light">
<label for="current_players">Aktuelle Spieler:</label>
<input type="number" id="current_players" name="current_players" required>
<label for="current_time">Aktuelle Uhrzeit:</label>
<input type="time" id="current_time" name="current_time" required>
<label for="current_weekday">Aktueller Wochentag:</label>
<select id="current_weekday" name="current_weekday" required>
<option value="Montag">Montag</option>
<option value="Dienstag">Dienstag</option>
<option value="Mittwoch">Mittwoch</option>
<option value="Donnerstag">Donnerstag</option>
<option value="Freitag">Freitag</option>
<option value="Samstag">Samstag</option>
<option value="Sonntag">Sonntag</option>
</select>
<button type="submit" name="calculate_best" value="1" id="calculateBestEvent">Bestes Event berechnen</button>
</form>
{% if best_events %}
<h2>Top 5 Events</h2>
<div id="barChartContainer" style="width:100%;height:260px;display:flex;align-items:end;justify-content:space-around;gap:10px;margin-top:30px;">
</div>
<script>
const bestEvents = {{ best_events|tojson }};
const colors = ["#00fff7", "#ff00ff", "#39ff14", "#ffb300", "#ff4d4d"];
function animateBars() {
const maxRating = Math.max(...bestEvents.map(e => e.rating));
const container = document.getElementById('barChartContainer');
container.innerHTML = '';
bestEvents.forEach((event, idx) => {
const bar = document.createElement('div');
bar.style.height = '0px';
bar.style.width = '60px';
bar.style.background = colors[idx % colors.length];
bar.style.borderRadius = '8px 8px 0 0';
bar.style.display = 'flex';
bar.style.flexDirection = 'column';
bar.style.alignItems = 'center';
bar.style.justifyContent = 'flex-end';
bar.style.transition = 'height 1s cubic-bezier(.68,-0.55,.27,1.55)';
bar.style.position = 'relative';
const label = document.createElement('div');
label.innerText = event.name;
label.style.position = 'absolute';
label.style.bottom = '-32px';
label.style.width = '100%';
label.style.textAlign = 'center';
label.style.fontSize = '13px';
label.style.color = '#fff';
label.style.wordBreak = 'break-word';
const value = document.createElement('div');
value.innerText = event.rating;
value.style.position = 'absolute';
value.style.top = '-28px';
value.style.width = '100%';
value.style.textAlign = 'center';
value.style.fontWeight = 'bold';
value.style.fontSize = '15px';
value.style.color = '#fff';
bar.appendChild(label);
bar.appendChild(value);
container.appendChild(bar);
setTimeout(() => {
bar.style.height = (event.rating / (maxRating || 1) * 180 + 30) + 'px';
}, 100 + idx * 120);
});
}
window.addEventListener('DOMContentLoaded', animateBars);
</script>
{% elif best_event %}
<h2>Bestes Event</h2>
<p>{{ best_event.name }} - Rating: {{ best_event.rating }} (Top: {{ best_event.top }}, Flop: {{ best_event.flop }})</p>
{% endif %}
</div>
<div class="form-container">
<h2>Protokollierte Events</h2>
<ul style="list-style-type: none; padding: 0;">
{% set event_names = [] %}
{% for event in events %}
{% if event.name not in event_names %}
<li style="background: rgba(255,255,255,0.5); margin: 5px 0; padding: 10px; border-radius: 4px;">{{ event.name }}</li>
{% set event_names = event_names.append(event.name) %}
{% endif %}
{% endfor %}
</ul>
</div>
</div>
<div class="events-back-btn-row">
<a href="javascript:window.history.back()" class="events-back-btn-light">Zurück</a>
</div>
</div>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,134 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Daten anzeigen</title>
<link rel="stylesheet" href="/static/style.css">
<link rel="icon" href="/static/logo.png" type="image/png">
<style>
body {
background-image: url('/static/background.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
height: 100vh;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
.content {
background: rgba(255, 255, 255, 0.8);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
width: 80%;
max-width: 800px;
max-height: 90vh;
overflow-y: auto;
text-align: center;
}
.content h2 {
text-align: center;
}
.content form {
display: flex;
flex-direction: column;
align-items: center;
}
.content form label {
margin-top: 10px;
width: 100%;
text-align: left;
}
.content form input,
.content form textarea,
.content form select {
width: 100%;
padding: 10px;
margin-top: 5px;
border: 1px solid #ccc;
border-radius: 4px;
}
.content form button {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
margin-top: 20px;
}
.content form button:hover {
background-color: #0056b3;
}
.search-bar {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.search-bar input {
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
margin-right: 10px;
}
.search-bar button {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
}
.search-bar button:hover {
background-color: #0056b3;
}
.message {
display: none;
color: red;
margin-top: 10px;
}
</style>
</head>
<body>
{% include "top_bar.html" %}
<div class="content">
<h2>Daten aus der MongoDB</h2>
<div class="search-bar">
<input type="text" id="licenseKeyInput" placeholder="License Key einfügen">
<button onclick="searchLicenseKey()">Suchen</button>
</div>
<div class="message" id="message">
<img src="/static/red_cross.png" alt="Cross" style="width: 20px; vertical-align: middle;">
Der License Key wurde nicht gefunden
</div>
{% for doc in data %}
<form action="{{ url_for('view_license', license_key=doc['license_key']) }}" method="get">
<button type="submit">{{ doc['ingame_name'] }}</button>
</form>
{% endfor %}
<a href="/license_keys" class="black-button" style="margin-top: 20px;">Zurück</a>
</div>
<script>
function searchLicenseKey() {
var licenseKey = document.getElementById("licenseKeyInput").value;
fetch(`/search_license_key?license_key=${licenseKey}`)
.then(response => response.json())
.then(data => {
if (data.found) {
window.location.href = `/license=${licenseKey}`;
} else {
var message = document.getElementById("message");
message.style.display = "block";
setTimeout(function() {
message.style.display = "none";
}, 2000);
}
});
}
</script>
</body>
</html>

View file

@ -0,0 +1,100 @@
{% if session['theme'] == 'darkmode' %}
{% extends "base_dark.html" %}
{% else %}
{% extends "base.html" %}
{% endif %}
{% block title %}IP Checker{% endblock %}
{% block content %}
<div class="content-wrapper">
<div class="form-container" style="max-width: {% if session['theme'] == 'darkmode' %}1000px{% else %}800px{% endif %}; padding: 30px; max-height: 90vh; overflow-y: auto;">
<h2>IP Checker</h2>
<form method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="text" name="ip_address" placeholder="IP-Adresse eingeben" required>
<br>
<label for="is_cheater">Ist diese IP eines Cheaters?</label>
<div class="styled-select">
<select name="is_cheater" id="is_cheater" required>
<option value="no">Nein</option>
<option value="yes">Ja</option>
</select>
</div>
<br>
<input type="text" name="steam_id" id="steam_id" placeholder="SteamID (erforderlich für Cheater)">
<br>
<div style="display: flex; gap: 20px">
{% if session['theme'] == 'darkmode' %}
<button type="submit" class="neon-button neon1" style="flex: 1;"><span>Prüfen</span></button>
<a href="/dashboard" class="neon-button neon3" style="flex: 1; text-align: center;"><span>Zurück</span></a>
{% else %}
<button type="submit" style="flex: 1; padding: 10px;">Prüfen</button>
<a href="/dashboard" class="black-button" style="flex: 1; padding: 10px; text-align: center;">Zurück</a>
{% endif %}
</div>
</form>
{% if error %}
<p style="color: red; margin-top: 20px;">{{ error }}</p>
{% endif %}
{% if ip_data %}
<h3 style="margin-top: 30px;">IP-Daten</h3>
<table style="width: 100%; border-collapse: collapse; margin-top: 20px;">
<tr>
<th style="padding: 10px; border: 1px solid #ddd; background-color: #007BFF; color: white;">Feld</th>
<th style="padding: 10px; border: 1px solid #ddd; background-color: #007BFF; color: white;">Wert</th>
</tr>
<tr><td style="padding: 10px; border: 1px solid #ddd;">IP-Adresse</td><td style="padding: 10px; border: 1px solid #ddd;">{{ ip_data.get('ip', 'N/A') }}</td></tr>
<tr><td style="padding: 10px; border: 1px solid #ddd;">VPN</td><td style="padding: 10px; border: 1px solid #ddd;">{{ 'Ja' if ip_data['security'].get('vpn', False) else 'Nein' }}</td></tr>
<tr><td style="padding: 10px; border: 1px solid #ddd;">Proxy</td><td style="padding: 10px; border: 1px solid #ddd;">{{ 'Ja' if ip_data['security'].get('proxy', False) else 'Nein' }}</td></tr>
<tr><td style="padding: 10px; border: 1px solid #ddd;">Tor</td><td style="padding: 10px; border: 1px solid #ddd;">{{ 'Ja' if ip_data['security'].get('tor', False) else 'Nein' }}</td></tr>
<tr><td style="padding: 10px; border: 1px solid #ddd;">Land</td><td style="padding: 10px; border: 1px solid #ddd;">{{ ip_data['location'].get('country', 'N/A') }}</td></tr>
<tr><td style="padding: 10px; border: 1px solid #ddd;">Stadt</td><td style="padding: 10px; border: 1px solid #ddd;">{{ ip_data['location'].get('city', 'N/A') }}</td></tr>
<tr><td style="padding: 10px; border: 1px solid #ddd;">ASN</td><td style="padding: 10px; border: 1px solid #ddd;">{{ ip_data['network'].get('autonomous_system_number', 'N/A') }}</td></tr>
<tr><td style="padding: 10px; border: 1px solid #ddd;">Netzwerk</td><td style="padding: 10px; border: 1px solid #ddd;">{{ ip_data['network'].get('network', 'N/A') }}</td></tr>
</table>
{% endif %}
{% if similar_cheater_ips %}
<h3 style="margin-top: 30px;">Ähnliche Cheater-IPs</h3>
<table style="width: 100%; border-collapse: collapse; margin-top: 20px;">
<tr>
<th style="padding: 10px; border: 1px solid #ddd; background-color: #007BFF; color: white;">IP-Adresse</th>
<th style="padding: 10px; border: 1px solid #ddd; background-color: #007BFF; color: white;">Stadt</th>
<th style="padding: 10px; border: 1px solid #ddd; background-color: #007BFF; color: white;">ASN</th>
<th style="padding: 10px; border: 1px solid #ddd; background-color: #007BFF; color: white;">SteamID</th>
</tr>
{% for ip in similar_cheater_ips %}
<tr>
<td style="padding: 10px; border: 1px solid #ddd;">{{ ip['ip'] }}</td>
<td style="padding: 10px; border: 1px solid #ddd;">{{ ip['data']['location'].get('city', 'N/A') }}</td>
<td style="padding: 10px; border: 1px solid #ddd;">{{ ip['data']['network'].get('autonomous_system_number', 'N/A') }}</td>
<td style="padding: 10px; border: 1px solid #ddd;">{{ ip.get('steam_id', 'N/A') }}</td>
</tr>
{% endfor %}
</table>
{% endif %}
{% if similar_ips|selectattr('is_cheater', 'equalto', True)|list|length > 0 %}
<h3 style="margin-top: 30px;">Ähnliche Cheater-IPs</h3>
<table style="width: 100%; border-collapse: collapse; margin-top: 20px;">
<tr>
<th style="padding: 10px; border: 1px solid #ddd; background-color: #007BFF; color: white;">IP-Adresse</th>
<th style="padding: 10px; border: 1px solid #ddd; background-color: #007BFF; color: white;">Stadt</th>
<th style="padding: 10px; border: 1px solid #ddd; background-color: #007BFF; color: white;">ASN</th>
<th style="padding: 10px; border: 1px solid #ddd; background-color: #007BFF; color: white;">SteamID</th>
</tr>
{% for ip in similar_ips %}
{% if ip['is_cheater'] %}
<tr>
<td style="padding: 10px; border: 1px solid #ddd;">{{ ip['ip'] }}</td>
<td style="padding: 10px; border: 1px solid #ddd;">{{ ip['data']['location'].get('city', 'N/A') }}</td>
<td style="padding: 10px; border: 1px solid #ddd;">{{ ip['data']['network'].get('autonomous_system_number', 'N/A') }}</td>
<td style="padding: 10px; border: 1px solid #ddd;">{{ ip.get('steam_id', 'N/A') }}</td>
</tr>
{% endif %}
{% endfor %}
</table>
{% endif %}
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,148 @@
{% if session['theme'] == 'darkmode' %}
{% extends "base_dark.html" %}
{% else %}
{% extends "base.html" %}
{% endif %}
{% block title %}License Keys{% endblock %}
{% block content %}
{% if session['theme'] == 'darkmode' %}
<style>
.license-container {
background: rgba(20, 20, 20, 0.95);
padding: 30px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.7);
max-width: 800px;
width: 100%;
min-height: 300px;
max-height: 90vh;
overflow-y: auto;
text-align: center;
}
.license-container form {
display: flex;
flex-direction: column;
align-items: center;
}
.license-container form label {
margin-top: 10px;
width: 100%;
text-align: left;
color: #00fff7;
}
.license-container form input,
.license-container form textarea,
.license-container form select {
width: 100%;
padding: 10px;
margin-top: 5px;
border: 1px solid #333;
border-radius: 4px;
background: #222;
color: #fff;
}
.license-btn-row {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 20px;
align-items: center;
}
.license-btn-row .neon-button {
width: 220px;
min-width: 120px;
max-width: 100%;
}
</style>
<div style="display:flex; flex-direction:column; align-items:center; width:100%;">
<div class="license-container">
<h2>License Keys</h2>
<div class="license-btn-row">
{% if 'create_license_key' in session['permissions'] %}
<form action="{{ url_for('create_license_key') }}" method="get">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="neon-button neon1"><span>License Key erstellen</span></button>
</form>
{% endif %}
<form action="{{ url_for('get_data') }}" method="get">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="neon-button neon2"><span>Datenbank Einträge</span></button>
</form>
<form action="{{ url_for('dashboard') }}" method="get">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="neon-button neon3"><span>Zurück</span></button>
</form>
</div>
</div>
</div>
{% else %}
<style>
.license-container {
background: rgba(255,255,255,0.9);
padding: 30px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
max-width: 800px;
width: 100%;
min-height: 300px;
max-height: 90vh;
overflow-y: auto;
text-align: center;
}
.license-container form {
display: flex;
flex-direction: column;
align-items: center;
}
.license-container form label {
margin-top: 10px;
width: 100%;
text-align: left;
}
.license-container form input,
.license-container form textarea,
.license-container form select {
width: 100%;
padding: 10px;
margin-top: 5px;
border: 1px solid #ccc;
border-radius: 4px;
}
.license-btn-row {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 20px;
align-items: center;
}
.license-btn-row button {
width: 220px;
min-width: 120px;
max-width: 100%;
}
</style>
<div style="display:flex; flex-direction:column; align-items:center; width:100%;">
<div class="license-container">
<h2>License Keys</h2>
<div class="license-btn-row">
{% if 'create_license_key' in session['permissions'] %}
<form action="{{ url_for('create_license_key') }}" method="get">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit">License Key erstellen</button>
</form>
{% endif %}
<form action="{{ url_for('get_data') }}" method="get">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit">Datenbank Einträge</button>
</form>
<form action="{{ url_for('dashboard') }}" method="get">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit">Zurück</button>
</form>
</div>
</div>
</div>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,96 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login</title>
<link rel="stylesheet" href="/static/style.css">
<link rel="icon" href="/static/logo.png" type="image/png">
<style>
body {
background-image: url('/static/background.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
height: 100vh;
margin: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.login-container {
background: rgba(255, 255, 255, 0.6);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
max-width: 400px;
width: 100%;
text-align: center;
}
.login-container h1 {
margin-bottom: 20px;
}
.login-container form {
margin: 5px 0;
width: 100%;
}
.login-container form button {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
width: 100%;
margin-bottom: 5px;
}
.login-container form button:hover {
background-color: #0056b3;
}
.login-container input {
width: 100%;
box-sizing: border-box;
padding: 10px;
margin: 0 0 10px 0;
border-radius: 4px;
border: 1px solid #ccc;
display: block;
}
.error-message {
color: red;
margin-top: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
}
.error-message img {
margin-right: 5px;
width: 14px;
height: 14px;
}
</style>
</head>
<body>
<div class="login-container">
<h1>Login</h1>
<form method="POST" action="{{ url_for('login') }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<label for="username">Benutzername:</label>
<input type="text" id="username" name="username" value="{{ request.form.username }}" required>
<br>
<label for="password">Passwort:</label>
<input type="password" id="password" name="password" required>
<br>
<button type="submit">Login</button>
</form>
{% if error %}
<div class="error-message">
<img src="/static/red_cross.png" alt="Error">
<span>Du hast ein falsches Passwort bzw einen falschen Benutzernamen eingegeben!</span>
</div>
{% endif %}
</div>
</body>
</html>

View file

@ -0,0 +1,120 @@
{% if session['theme'] == 'darkmode' %}
{% extends "base_dark.html" %}
{% else %}
{% extends "base.html" %}
{% endif %}
{% block title %}Logs{% endblock %}
{% block content %}
{% if session['theme'] == 'darkmode' %}
<style>
.logs-container {
background: rgba(20, 20, 20, 0.95);
padding: 30px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.7);
max-width: 800px;
width: 100%;
min-height: 300px;
max-height: 80vh;
overflow-y: auto;
margin: 0 auto;
text-align: center;
}
.log-entry {
width: 100%;
padding: 12px;
margin: 8px 0;
border-radius: 8px;
background: rgba(40, 40, 40, 0.9);
color: #fff;
text-align: left;
}
.logs-back-btn-row {
display: flex;
justify-content: center;
margin-bottom: 18px;
}
.logs-back-btn {
display: inline-block;
text-align: center;
width: auto;
padding: 14px 28px;
}
</style>
<div style="display:flex; flex-direction:column; align-items:center; width:100%;">
<div class="logs-back-btn-row">
<a href="{{ url_for('dashboard') }}" class="neon-button neon3 logs-back-btn"><span>Zurück</span></a>
</div>
<div class="logs-container">
<h2>Logs</h2>
{% for log in logs %}
<div class="log-entry">
<p><strong>Datum:</strong> {{ log.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}</p>
<p><strong>Nachricht:</strong> {{ log.message }}</p>
</div>
{% endfor %}
</div>
</div>
{% else %}
<style>
.logs-container {
background: rgba(255,255,255,0.9);
padding: 30px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
max-width: 800px;
width: 100%;
min-height: 300px;
max-height: 80vh;
overflow-y: auto;
margin: 0 auto;
text-align: center;
}
.log-entry {
width: 100%;
padding: 12px;
margin: 8px 0;
border-radius: 8px;
background: rgba(180, 180, 180, 0.9);
color: #333;
text-align: left;
}
.logs-back-btn-row {
display: flex;
justify-content: center;
margin-bottom: 18px;
}
.logs-back-btn {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
width: auto;
text-align: center;
display: inline-block;
text-decoration: none;
}
.logs-back-btn:hover {
background-color: #0056b3;
}
</style>
<div style="display:flex; flex-direction:column; align-items:center; width:100%;">
<div class="logs-back-btn-row">
<a href="{{ url_for('dashboard') }}" class="logs-back-btn">Zurück</a>
</div>
<div class="logs-container">
<h2>Logs</h2>
{% for log in logs %}
<div class="log-entry">
<p><strong>Datum:</strong> {{ log.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}</p>
<p><strong>Nachricht:</strong> {{ log.message }}</p>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,29 @@
User-agent: MSNBot
Disallow: /
User-agent: Slurp
Disallow: /
User-agent: Teoma
Disallow: /
User-agent: Gigabot
Disallow: /
User-agent: Robozilla
Disallow: /
User-agent: Nutch
Disallow: /
User-agent: ia_archiver
Disallow: /
User-agent: baiduspider
Disallow: /
User-agent: naverbot
Disallow: /
User-agent: yeti
Disallow: /
User-agent: yahoo-mmcrawler
Disallow: /
User-agent: psbot
Disallow: /
User-agent: yahoo-blogs/v3.9
Disallow: /
User-agent: *
Disallow:
Disallow: /static

View file

@ -0,0 +1,91 @@
:root {
--clr-one: rgb(255, 20, 147);
--clr-two: rgb(127, 255, 0);
--clr-three: rgb(138, 43, 226);
--clr-bg: #1d1e22;
}
* {
box-sizing: border-box;
margin: 0;
}
body {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
background: var(--clr-bg);
font-family: "Balsamiq Sans", cursive;
padding: 2rem;
}
h1, p {
color: white;
text-align: center;
margin-bottom: 1rem;
}
.container {
display: flex;
flex-direction: column;
align-items: center;
}
.buttons {
display: flex;
flex-wrap: wrap;
gap: 1rem;
justify-content: center;
}
.neon-button {
font-size: 1rem;
text-decoration: none;
padding: 0.75em 1.5em;
border-radius: 0.25em;
position: relative;
transition: 0.3s ease;
background: transparent;
color: inherit;
font-weight: bold;
text-shadow: 0 0 0.5em currentColor;
border: 2px solid currentColor;
}
/* Farben */
.neon-button__1 {
color: var(--clr-one);
box-shadow: 0 0 0.6em var(--clr-one);
}
.neon-button__2 {
color: var(--clr-two);
box-shadow: 0 0 0.6em var(--clr-two);
}
.neon-button__3 {
color: var(--clr-three);
box-shadow: 0 0 0.6em var(--clr-three);
}
.neon-button:hover,
.neon-button:focus {
background-color: currentColor;
color: #000;
text-shadow: none;
box-shadow:
0 0 0.7em currentColor,
0 0 1.5em currentColor,
inset 0 0 1em currentColor;
transform: scale(1.05);
}
.neon-button:active {
animation: bounce 0.4s ease;
}
@keyframes bounce {
0% { transform: translateY(0); }
50% { transform: translateY(-8px); }
100% { transform: translateY(0); }
}

View file

@ -0,0 +1,78 @@
<div class="top-bar">
<div class="logo">
<img src="/static/logo.png" alt="Logo">
</div>
<div class="title">MrSniff's Admin-Tools Dashboard</div>
<div class="user-info">
<div class="username">
<span>{{ session['username'] }}</span>
</div>
<div class="profile-picture">
<img src="/static/pfps/{{ session['user_id'] }}.png?t={{ session.get('pfp_last_updated', 0) }}"
alt="Profilbild"
onerror="this.onerror=null; this.src='/static/user_small.png';"
style="width: 40px; height: 40px; border-radius: 50%; object-fit: cover;">
</div>
</div>
</div>
<style>
.top-bar {
width: 100%;
background-color: #42028B;
color: white;
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
position: fixed;
top: 0;
left: 0;
z-index: 1000;
}
.top-bar .logo img {
height: 60px;
}
.top-bar .title {
flex-grow: 1;
text-align: center;
font-size: 24px;
font-weight: bold;
}
.top-bar .user-info {
display: flex;
align-items: center;
margin-right: 5%;
}
.top-bar .user-info .username {
font-size: 18px;
white-space: nowrap;
overflow: visible;
text-overflow: clip;
max-width: none;
margin-right: 10px; /* Abstand zwischen Name und Bild */
}
.top-bar .user-info .profile-picture img {
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
}
@media (min-width: 768px) {
.top-bar .user-info {
max-width: 25%;
justify-content: flex-end; /* Benutzerinfo rechts ausrichten */
}
.top-bar .user-info .username {
text-align: right;
margin-right: 10px;
}
}
</style>

View file

@ -0,0 +1,178 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ScytheNet</title>
<style>
body {
background-color: #000;
color: #fff;
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
.container {
display: flex;
height: 100vh;
}
.logo {
font-size: 24px;
font-weight: bold;
color: #fff;
padding: 10px;
background-color: #333;
display: inline-block;
cursor: pointer;
margin-bottom: 20px;
text-align: center;
}
.logo span {
color: red;
}
.sidebar {
width: 200px;
background-color: #1a1a1a;
display: flex;
flex-direction: column;
padding-top: 20px;
align-items: center;
}
.sidebar button {
background-color: #333;
color: #fff;
border: none;
padding: 15px;
font-size: 18px;
cursor: pointer;
margin-bottom: 10px;
text-align: left;
width: 100%;
}
.sidebar button:hover {
background-color: #9c149c;
}
.sidebar button.active {
background-color: #9c149c;
}
.content {
flex: 1;
padding: 20px;
background-color: #222;
}
.button {
background-color: #9c149c;
color: white;
border: none;
padding: 15px 20px;
font-size: 18px;
cursor: pointer;
margin-top: 20px;
width: 100%;
}
.button:hover {
background-color: #9c149c;
}
input, label {
width: 100%;
padding: 10px;
margin: 10px 0;
font-size: 16px;
border: 1px solid #fff;
background-color: #1a1a1a;
color: #fff;
}
h1 {
color: #ecf0f1;
font-size: 24px;
}
.emoji {
font-size: 24px;
margin-right: 10px;
}
</style>
</head>
<body>
<div class="container">
<div class="sidebar">
<div class="logo" onclick="showDashboard()">Scythe<span>NET</span></div>
<button onclick="showDashboard()" id="dashboardBtn">🖥️ Dashboard</button>
<button onclick="showAPI()" id="apiBtn">🔑 API</button>
<button onclick="showAttack()" id="attackBtn">💥 Attack</button>
</div>
<div class="content" id="content">
<h1>Willkommen bei ScytheNet</h1>
<p>Wählen Sie eine Option aus der Seitenleiste</p>
</div>
</div>
<script>
function resetActiveButtons() {
document.querySelectorAll('.sidebar button').forEach(button => {
button.classList.remove('active');
});
}
function showDashboard() {
resetActiveButtons();
document.getElementById('dashboardBtn').classList.add('active');
document.getElementById('content').innerHTML = `
<h1>Dashboard</h1>
<p>Online Nutzer: <span id="online_count">-</span></p>
<p>Weltkarte: 🌍</p>
<p>Pakete gesendet: 0</p>
<p>Pakete empfangen: 0</p>
<button class="button" onclick="fetchOnlineCount()">Online abrufen</button>
`;
}
function showAPI() {
resetActiveButtons();
document.getElementById('apiBtn').classList.add('active');
document.getElementById('content').innerHTML = `
<h1>API Einstellungen</h1>
<label for="api_key">API Key:</label>
<input type="text" id="api_key" placeholder="Geben Sie Ihren API Key ein">
<button class="button" onclick="saveAPIKey()">Speichern</button>
`;
}
function showAttack() {
resetActiveButtons();
document.getElementById('attackBtn').classList.add('active');
document.getElementById('content').innerHTML = `
<h1>Angriff</h1>
<label for="ip">IP-Adresse:</label>
<input type="text" id="ip" placeholder="Geben Sie die IP-Adresse ein">
<label for="port">Port:</label>
<input type="text" id="port" placeholder="Geben Sie den Port ein">
<label for="duration">Dauer:</label>
<input type="text" id="duration" placeholder="Geben Sie die Dauer ein">
<button class="button" onclick="startAttack()">Angriff starten</button>
`;
}
function fetchOnlineCount() {
fetch('http://localhost:8081/online')
.then(response => response.text())
.then(data => {
document.getElementById('online_count').innerText = data;
});
}
function saveAPIKey() {
const apiKey = document.getElementById('api_key').value;
// Hier würde dein C-Backend aufgerufen, um den API-Schlüssel zu speichern.
alert('API Key gespeichert: ' + apiKey);
}
function startAttack() {
const ip = document.getElementById('ip').value;
const port = document.getElementById('port').value;
const duration = document.getElementById('duration').value;
// Hier würde dein C-Backend aufgerufen, um den Angriff zu starten.
alert('Angriff auf ' + ip + ':' + port + ' für ' + duration + ' Sekunden gestartet!');
}
</script>
</body>
</html>

View file

@ -0,0 +1,490 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% if session['theme'] == 'darkmode' %}
{% extends "base_dark.html" %}
{% else %}
{% extends "base.html" %}
{% endif %}
{% block title %}User Management{% endblock %}
{% block content %}
<div class="form-container" style="max-width: 1100px; max-height: 80vh; overflow-y: auto; margin-top: 30px;">
<h2>User Management</h2>
<style>
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th, td {
padding: 10px;
text-align: left;
border: 1px solid #ddd;
}
th:first-child {
width: 60px;
}
td:first-child {
text-align: center;
}
.profile-pic-small {
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
margin: 5px;
}
.permission-box {
display: inline-flex;
align-items: center;
justify-content: space-between;
background: rgba(255, 255, 255, 0.8);
border-radius: 12px;
padding: 5px 10px;
margin: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
font-size: 14px;
{% if session['theme'] == 'darkmode' %}
color: #000;
{% endif %}
}
.permission-box img {
width: 16px;
height: 16px;
cursor: pointer;
margin-left: 8px;
}
.dropdown-container {
position: relative;
display: inline-block;
margin-left: 5px;
}
.add-permission {
background-color: #28a745;
color: white;
border: none;
padding: 5px 10px;
cursor: pointer;
border-radius: 4px;
font-size: 18px;
font-weight: bold;
line-height: 1;
transition: background-color 0.2s ease;
height: 38px;
display: flex;
align-items: center;
justify-content: center;
}
.add-permission:hover {
background-color: #218838;
}
.dropdown {
position: absolute;
min-width: 180px;
box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.2);
z-index: 1000;
border-radius: 8px;
overflow: hidden;
margin-top: 5px;
left: 0;
background-color: #f9f9f9;
border: 1px solid #ddd;
max-height: 0;
opacity: 0;
transition: max-height 0.3s ease-out, opacity 0.3s ease-out;
overflow-y: hidden;
}
{% if session['theme'] == 'darkmode' %}
.dropdown {
background-color: #333;
border: 1px solid #555;
}
.dropdown a {
color: #f9f9f9;
}
.dropdown a:hover {
background-color: #555;
box-shadow: 0 0 5px #00fff7, 0 0 10px #00fff7;
}
{% endif %}
.dropdown a {
color: black;
padding: 10px 15px;
text-decoration: none;
display: block;
transition: background-color 0.2s ease, color 0.2s ease;
}
.dropdown-container:hover .dropdown {
max-height: 300px;
opacity: 1;
overflow-y: auto;
}
.dropdown a:hover {
background-color: #f1f1f1;
}
.neon-button {
width: auto;
min-width: 150px;
padding: 14px 20px;
font-size: 15px;
font-weight: bold;
border: 2px solid;
color: inherit;
background: transparent;
cursor: pointer;
position: relative;
overflow: hidden;
transition: 0.3s;
box-shadow: 0 0 8px currentColor, 0 0 14px currentColor inset;
margin: 10px 0;
border-radius: 6px;
text-align: center;
height: 38px;
display: flex;
align-items: center;
justify-content: center;
}
.neon1 { color: #00fff7; border-color: #00fff7; }
.neon2 { color: #ff00ff; border-color: #ff00ff; }
.neon3 { color: #39ff14; border-color: #39ff14; }
.neon-button::before {
content: '';
position: absolute;
top: 0; left: 0;
width: 100%; height: 100%;
background: currentColor;
z-index: 0;
transform: scaleX(0);
transform-origin: left;
transition: 0.3s ease-in-out;
}
.neon-button:hover::before {
transform: scaleX(1);
}
.neon-button span {
position: relative;
z-index: 1;
transition: color 0.3s;
}
.neon-button:hover span {
color: #000;
}
.standard-button {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
font-size: 15px;
font-weight: bold;
margin: 10px 0;
min-width: 150px;
text-align: center;
transition: background 0.2s;
height: 38px;
display: flex;
align-items: center;
justify-content: center;
}
.standard-button:hover {
background-color: #0056b3;
}
.add-user-form {
display: flex;
flex-direction: column;
align-items: stretch;
background: rgba(255,255,255,0.7);
border-radius: 10px;
padding: 20px 24px 10px 24px;
margin-top: 20px;
max-width: 400px;
margin-left: auto;
margin-right: auto;
box-shadow: 0 2px 8px rgba(0,0,0,0.07);
}
{% if session['theme'] == 'darkmode' %}
.add-user-form {
background: rgba(40,40,40,0.85);
}
{% endif %}
.add-user-form label,
.add-user-form input {
display: block;
margin-bottom: 10px;
}
.add-user-form input[type="text"],
.add-user-form input[type="password"] {
width: 100%;
padding: 10px 12px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
.add-user-form button {
margin-top: 10px;
margin-bottom: 10px;
align-self: center;
min-width: 150px;
}
.popup-overlay {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.45);
z-index: 9999;
display: none;
align-items: center;
justify-content: center;
}
.popup-content {
background: #fff;
border-radius: 12px;
padding: 32px 28px 22px 28px;
box-shadow: 0 4px 24px rgba(0,0,0,0.18);
text-align: center;
max-width: 340px;
min-width: 260px;
margin: 0 10px;
}
.popup-content h3 {
margin-top: 0;
margin-bottom: 18px;
font-size: 1.3em;
color: #dc3545;
}
.popup-content p {
margin-bottom: 24px;
color: #333;
}
.popup-btn-row {
display: flex;
justify-content: center;
gap: 18px;
}
.popup-btn-cancel, .popup-btn-confirm {
min-width: 90px;
padding: 8px 0;
border-radius: 5px;
font-size: 15px;
font-weight: bold;
border: none;
cursor: pointer;
transition: background 0.2s;
}
.popup-btn-cancel {
background: #e0e0e0;
color: #333;
}
.popup-btn-cancel:hover {
background: #bdbdbd;
}
.popup-btn-confirm {
background: #dc3545;
color: #fff;
}
.popup-btn-confirm:hover {
background: #b52a37;
}
{% if session['theme'] == 'darkmode' %}
.popup-content {
background: #232323;
color: #fff;
}
.popup-content p {
color: #eee;
}
.popup-btn-cancel {
background: #444;
color: #eee;
}
.popup-btn-cancel:hover {
background: #222;
}
.popup-btn-confirm {
background: #b52a37;
color: #fff;
}
.popup-btn-confirm:hover {
background: #dc3545;
}
{% endif %}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('form[data-confirm="delete-user"]').forEach(function(form) {
form.addEventListener('submit', function(e) {
e.preventDefault();
const username = form.getAttribute('data-username');
showDeletePopup(form, username);
});
});
});
function showDeletePopup(form, username) {
let overlay = document.getElementById('delete-popup-overlay');
if (!overlay) {
overlay = document.createElement('div');
overlay.className = 'popup-overlay';
overlay.id = 'delete-popup-overlay';
overlay.innerHTML = `
<div class="popup-content">
<h3>Account löschen?</h3>
<p>Bist du dir sicher, dass der Account <b id="popup-username"></b> gelöscht werden soll?<br>Diese Aktion kann nicht rückgängig gemacht werden.</p>
<div class="popup-btn-row">
<button class="popup-btn-cancel" type="button">Abbrechen</button>
<button class="popup-btn-confirm" type="button">Löschen</button>
</div>
</div>
`;
document.body.appendChild(overlay);
}
overlay.style.display = 'flex';
overlay.querySelector('#popup-username').textContent = username;
const cancelBtn = overlay.querySelector('.popup-btn-cancel');
const confirmBtn = overlay.querySelector('.popup-btn-confirm');
// Remove previous listeners
cancelBtn.onclick = function() {
overlay.style.display = 'none';
};
confirmBtn.onclick = function() {
overlay.style.display = 'none';
form.submit();
};
}
</script>
<table>
<thead>
<tr>
<th>PFPs</th> <th>Username</th>
<th>Display Name</th>
<th>Perms</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>
<img src="/static/pfps/{{ user['_id'] }}.png?t={{ user.get('website_accounts', {}).get('pfp_last_updated', 0) }}"
alt="Profilbild"
onerror="this.onerror=null; this.src='/static/user_small.png';"
class="profile-pic-small">
</td>
<td>{{ user['username'] }}</td>
<td>{{ user['display_name'] }}</td>
<td>
<div style="display: flex; flex-wrap: wrap;">
{% for permission in user['permissions'] %}
<div class="permission-box">
{{ permission }}
{% if user['username'] != session['username'] and permission in session.get('permissions', []) %}
<form action="{{ url_for('change_permissions', user_id=user['_id']) }}" method="post" style="display:inline;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="remove_permission" value="{{ permission }}">
<img src="/static/red_cross.png" alt="Remove" onclick="this.parentElement.submit()">
</form>
{% endif %}
</div>
{% endfor %}
{% if user['username'] != session['username'] %}
<div class="dropdown-container">
<button class="add-permission">+</button>
<div class="dropdown">
{% for permission_option in ['user_management', 'permission_management', 'delete_requests', 'logs', 'create_license_key', 'view_license_keys', 'create_event_feedback', 'view_event_feedback', 'discord_bots', 'verify_age', 'events', 'project_manager', 'intranet', 'ip_checker', 'changelogs', 'appointments'] %}
{% if permission_option not in user['permissions'] and permission_option in session.get('permissions', []) %}
{% if permission_option in ['user_management', 'permission_management'] %}
{% if 'System' in session.get('permissions', []) %}
<a href="#" onclick="event.preventDefault(); document.getElementById('add-permission-{{ user['_id'] }}-{{ permission_option }}').submit();">{{ permission_option }}</a>
<form id="add-permission-{{ user['_id'] }}-{{ permission_option }}" action="{{ url_for('change_permissions', user_id=user['_id']) }}" method="post" style="display:none;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="add_permission" value="{{ permission_option }}">
</form>
{% endif %}
{% else %}
<a href="#" onclick="event.preventDefault(); document.getElementById('add-permission-{{ user['_id'] }}-{{ permission_option }}').submit();">{{ permission_option }}</a>
<form id="add-permission-{{ user['_id'] }}-{{ permission_option }}" action="{{ url_for('change_permissions', user_id=user['_id']) }}" method="post" style="display:none;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="add_permission" value="{{ permission_option }}">
</form>
{% endif %}
{% endif %}
{% endfor %}
</div>
</div>
{% endif %}
</div>
</td>
<td>
{% if user['username'] != session['username'] %}
{% set current_user_permissions = session.get('permissions', []) %}
{% if 'permission_management' in current_user_permissions and 'permission_management' in user['permissions'] and session['username'] != 'MrSniff' %}
Keine Aktionen verfügbar
{% else %}
<form action="{{ url_for('edit_user', user_id=user['_id']) }}" method="get" style="display:inline;">
{% if session['theme'] == 'darkmode' %}
<button type="submit" class="neon-button neon1" style="min-width: 80px; padding: 8px 12px; font-size: 14px;"><span>Edit</span></button>
{% else %}
<button type="submit" class="standard-button" style="min-width: 80px; padding: 8px 12px; font-size: 14px;">Edit</button>
{% endif %}
</form>
<form action="{{ url_for('delete_user', user_id=user['_id']) }}" method="post" style="display:inline;" data-confirm="delete-user" data-username="{{ user['username'] }}">
{% if session['theme'] == 'darkmode' %}
<button type="submit" class="neon-button neon3" style="min-width: 80px; padding: 8px 12px; font-size: 14px;"><span>Delete</span></button>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
{% else %}
<button type="submit" class="standard-button" style="min-width: 80px; padding: 8px 12px; font-size: 14px; background-color: #dc3545; border-color: #dc3545;">Delete</button>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
{% endif %}
</form>
{% endif %}
{% else %}
Keine Aktionen verfügbar
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<form action="{{ url_for('add_user') }}" method="post" class="add-user-form">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<label for="new_username">Username:</label>
<input type="text" id="new_username" name="username" required>
<label for="new_display_name">Display Name:</label>
<input type="text" id="new_display_name" name="display_name" required>
<label for="new_password">Passwort:</label>
<input type="password" id="new_password" name="password" required>
{% if session['theme'] == 'darkmode' %}
<button type="submit" class="neon-button neon1"><span>Hinzufügen</span></button>
{% else %}
<button type="submit" class="standard-button">Hinzufügen</button>
{% endif %}
</form>
{% if session['theme'] == 'darkmode' %}
<a href="/dashboard" class="neon-button neon3" style="display:inline-block; margin-top: 20px; margin-left: 0; padding: 12px 20px; line-height: 1.5;"><span>Zurück</span></a>
{% else %}
<a href="/dashboard" class="standard-button" style="display:inline-block; margin-top: 20px; margin-left: 0; padding: 12px 15px;">Zurück</a>
{% endif %}
</div>
{% endblock %}
</body>
</html>

View file

@ -0,0 +1,285 @@
{% if session['theme'] == 'darkmode' %}
{% extends "base_dark.html" %}
{% else %}
{% extends "base.html" %}
{% endif %}
{% block title %}Altersüberprüfung{% endblock %}
{% block content %}
{% if session['theme'] == 'darkmode' %}
<style>
.container {
display: flex;
justify-content: center;
align-items: flex-start;
gap: 40px;
margin: auto;
margin-top: 100px;
}
.form-container, .search-container {
background: rgba(30, 30, 30, 0.95);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.5);
max-width: 500px;
width: 100%;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
.form-container h1, .search-container h1 {
margin-bottom: 20px;
color: #fff;
}
.form-container form, .search-container form {
margin: 5px 0;
width: 100%;
}
.form-container form button, .search-container form button {
margin-bottom: 5px;
}
.form-container input, .search-container input {
width: 100%;
padding: 10px;
margin: 5px 0;
border-radius: 4px;
border: 1px solid #444;
background: #222;
color: #fff;
}
.result {
font-size: 1.5em;
color: #0ff;
margin-top: 20px;
}
.user-list {
margin-top: 20px;
width: 100%;
max-height: 400px;
overflow-y: auto;
}
.user-list button {
width: 100%;
margin-bottom: 5px;
}
.back-btn-row {
display: flex;
justify-content: center;
margin-top: 40px;
}
.back-button-dark {
display: inline-block;
text-align: center;
width: auto;
padding: 14px 28px;
}
label {
color: #fff;
}
</style>
<div>
<div class="container">
<div class="form-container">
<h1>Altersüberprüfung</h1>
<form method="POST">
<label for="birthdate">Geburtsdatum:</label>
<input type="date" id="birthdate" name="birthdate" value="{{ request.form.birthdate }}" required>
<br>
<label for="steam_id">SteamID:</label>
<input type="text" id="steam_id" name="steam_id" value="{{ request.form.steam_id }}" required>
<br>
<label for="ingame_name">Ingame Name:</label>
<input type="text" id="ingame_name" name="ingame_name" value="{{ request.form.ingame_name }}" required>
<br>
<button type="submit" class="neon-button neon1"><span>Überprüfen</span></button>
</form>
{% if age is not none %}
<p class="result">Die Person ist {{ age }} Jahre alt.</p>
{% if age < 13 %}
<p class="result">Diese Person muss für {{ (birthdate_dt.replace(year=birthdate_dt.year + 13) - today).days }} Tage gebannt werden.</p>
{% else %}
<p class="result">Diese Person muss nicht gebannt werden.</p>
{% endif %}
{% endif %}
</div>
<div class="search-container">
<h1>Benutzer suchen</h1>
<form method="GET">
<input type="text" name="search" placeholder="Suche nach Ingame Name oder SteamID" value="{{ request.args.search }}">
<button type="submit" class="neon-button neon2"><span>Suchen</span></button>
</form>
<div class="user-list">
{% for user in users %}
<form action="{{ url_for('view_user_age', user_id=user._id) }}" method="get">
<button type="submit" class="neon-button neon1"><span>{{ user.ingame_name }}</span></button>
</form>
{% endfor %}
</div>
</div>
</div>
<div class="back-btn-row">
<a href="{{ url_for('dashboard') }}" class="neon-button neon3 back-button-dark"><span>Zurück</span></a>
</div>
</div>
{% else %}
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Altersüberprüfung</title>
<link rel="stylesheet" href="/static/style.css">
<link rel="icon" href="/static/logo.png" type="image/png">
<style>
body {
background-image: url('/static/background.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
height: 100vh;
margin: 0;
display: flex;
flex-direction: column;
}
.container {
display: flex;
justify-content: center;
align-items: flex-start;
gap: 40px;
margin: auto;
margin-top: 150px;
}
.form-container, .search-container {
background: rgba(255, 255, 255, 0.6);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
max-width: 500px;
width: 100%;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
.form-container h1, .search-container h1 {
margin-bottom: 20px;
}
.form-container form, .search-container form {
margin: 5px 0;
width: 100%;
}
.form-container form button, .search-container form button {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
width: 100%;
margin-bottom: 5px;
}
.form-container form button:hover, .search-container form button:hover {
background-color: #0056b3;
}
.form-container input, .search-container input {
width: 100%;
padding: 10px;
margin: 5px 0;
border-radius: 4px;
border: 1px solid #ccc;
}
.result {
font-size: 1.5em;
color: #333;
margin-top: 20px;
}
.user-list {
margin-top: 20px;
width: 100%;
max-height: 400px;
overflow-y: auto;
}
.user-list button {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
width: 100%;
margin-bottom: 5px;
}
.user-list button:hover {
background-color: #0056b3;
}
.back-btn-row {
display: flex;
justify-content: center;
margin-top: 40px;
}
.back-button {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
width: auto;
margin-top: 0;
text-align: center;
display: inline-block;
}
.back-button:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
{% include "top_bar.html" %}
<div class="container">
<div class="form-container">
<h1>Altersüberprüfung</h1>
<form method="POST">
<label for="birthdate">Geburtsdatum:</label>
<input type="date" id="birthdate" name="birthdate" value="{{ request.form.birthdate }}" required>
<br>
<label for="steam_id">SteamID:</label>
<input type="text" id="steam_id" name="steam_id" value="{{ request.form.steam_id }}" required>
<br>
<label for="ingame_name">Ingame Name:</label>
<input type="text" id="ingame_name" name="ingame_name" value="{{ request.form.ingame_name }}" required>
<br>
<button type="submit">Überprüfen</button>
</form>
{% if age is not none %}
<p class="result">Die Person ist {{ age }} Jahre alt.</p>
{% if age < 13 %}
<p class="result">Diese Person muss für {{ (birthdate_dt.replace(year=birthdate_dt.year + 13) - today).days }} Tage gebannt werden.</p>
{% else %}
<p class="result">Diese Person muss nicht gebannt werden.</p>
{% endif %}
{% endif %}
</div>
<div class="search-container">
<h1>Benutzer suchen</h1>
<form method="GET">
<input type="text" name="search" placeholder="Suche nach Ingame Name oder SteamID" value="{{ request.args.search }}">
<button type="submit">Suchen</button>
</form>
<div class="user-list">
{% for user in users %}
<form action="{{ url_for('view_user_age', user_id=user._id) }}" method="get">
<button type="submit">{{ user.ingame_name }}</button>
</form>
{% endfor %}
</div>
</div>
</div>
<div class="back-btn-row">
<a href="{{ url_for('dashboard') }}" class="back-button">Zurück</a>
</div>
</body>
</html>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,169 @@
{% if session['theme'] == 'darkmode' %}
{% extends "base_dark.html" %}
{% else %}
{% extends "base.html" %}
{% endif %}
{% block title %}Changelogs{% endblock %}
{% block content %}
{% if session['theme'] == 'darkmode' %}
<style>
.changelog-list-outer {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 60px;
}
.changelog-list-container {
background: rgba(20, 20, 20, 0.95);
padding: 30px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.7);
max-width: 700px;
width: 100%;
min-height: 300px;
max-height: 500px;
overflow-y: auto;
}
.changelog-list-container h2 {
color: #00fff7;
margin-bottom: 20px;
text-align: center;
}
.changelog-item {
background: rgba(40, 40, 40, 0.9);
margin-bottom: 16px;
padding: 14px 18px;
border-radius: 8px;
text-align: left;
white-space: pre-wrap;
color: white;
display: flex;
flex-direction: column;
gap: 6px;
}
.changelog-item .timestamp {
font-size: 12px;
color: #aaa;
align-self: flex-end;
margin-bottom: -4px;
}
.changelog-item .message {
font-size: 15px;
color: #ddd;
margin: 0;
line-height: 1.4;
}
.back-btn-row {
display: flex;
justify-content: center;
width: 100%;
margin-bottom: 18px;
}
.back-button-dark {
display: inline-block;
text-align: center;
width: auto;
padding: 14px 28px;
}
</style>
<div class="changelog-list-outer">
<div class="back-btn-row">
<a href="{{ url_for('dashboard') }}" class="neon-button neon3 back-button-dark"><span>Zurück</span></a>
</div>
<div class="changelog-list-container">
<h2>Website Changelogs</h2>
{% for changelog in changelogs %}
<div class="changelog-item">
<span class="timestamp">{{ changelog.timestamp }}</span>
<div class="message">{{ changelog.text }}</div>
</div>
{% endfor %}
</div>
</div>
{% else %}
<style>
.changelog-list-outer {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 60px;
}
.changelog-list-container {
background: rgba(255,255,255,0.9);
padding: 30px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
max-width: 700px;
width: 100%;
min-height: 300px;
max-height: 500px;
overflow-y: auto;
}
.changelog-list-container h2 {
color: #007BFF;
margin-bottom: 20px;
text-align: center;
}
.changelog-item {
background: rgba(180, 180, 180, 0.9);
margin-bottom: 16px;
padding: 14px 18px;
border-radius: 8px;
text-align: left;
white-space: pre-wrap;
color: #333;
display: flex;
flex-direction: column;
gap: 6px;
}
.changelog-item .timestamp {
font-size: 12px;
color: #555;
align-self: flex-end;
margin-bottom: -4px;
}
.changelog-item .message {
font-size: 15px;
color: #333;
margin: 0;
line-height: 1.4;
}
.back-btn-row {
display: flex;
justify-content: center;
width: 100%;
margin-bottom: 18px;
}
.back-button {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
width: auto;
text-align: center;
display: inline-block;
}
.back-button:hover {
background-color: #0056b3;
}
</style>
<div class="changelog-list-outer">
<div class="back-btn-row">
<a href="{{ url_for('dashboard') }}" class="back-button">Zurück</a>
</div>
<div class="changelog-list-container">
<h2>Website Changelogs</h2>
{% for changelog in changelogs %}
<div class="changelog-item">
<span class="timestamp">{{ changelog.timestamp }}</span>
<div class="message">{{ changelog.text }}</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,82 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Event Feedback</title>
<link rel="stylesheet" href="/static/style.css">
<link rel="icon" href="/static/logo.png" type="image/png">
<style>
body {
background-image: url('/static/background.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
height: 100vh;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
}
.content {
background: rgba(255, 255, 255, 0.6);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
max-width: 350px;
width: 100%;
text-align: center;
}
.content h1 {
margin-bottom: 20px;
}
.content form {
margin: 5px 0; /* Verringere den Abstand */
width: 100%;
}
.content form button {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
width: 100%;
margin-bottom: 5px; /* Verringere den Abstand */
}
.content form button:hover {
background-color: #0056b3;
}
.content a {
margin: 5px 0; /* Verringere den Abstand */
text-decoration: none;
color: #007BFF;
display: block;
width: 100%;
text-align: center;
padding: 10px 15px;
border-radius: 4px;
background-color: #007BFF;
color: white;
}
.content a:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
{% include "top_bar.html" %}
<div class="content">
<h1>Event Feedback</h1>
<p><strong>Event:</strong> {{ feedback.event_name }}</p>
<p><strong>Verlauf des Events:</strong> {{ feedback.event_progress }}</p>
<p><strong>Ende des Events:</strong> {{ feedback.event_end }}</p>
<p><strong>Feedback der Community:</strong> {{ feedback.community_feedback }}</p>
<p><strong>Abgegeben von:</strong> {{ feedback.username }}</p>
<p><strong>Datum:</strong> {{ feedback.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}</p>
<form action="{{ url_for('view_event_feedbacks') }}" method="get">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit">Zurück</button>
</form>
</div>
</body>
</html>

View file

@ -0,0 +1,80 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Event Feedbacks</title>
<link rel="stylesheet" href="/static/style.css">
<link rel="icon" href="/static/logo.png" type="image/png">
<style>
body {
background-image: url('/static/background.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
height: 100vh;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
.content {
background: rgba(255, 255, 255, 0.8);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
width: 80%;
max-width: 800px;
max-height: 90vh;
overflow-y: auto;
text-align: center;
}
.feedback-entry {
background: rgba(255, 255, 255, 0.6);
padding: 10px;
border-radius: 8px;
margin-bottom: 10px;
text-align: left;
}
.feedback-entry h3 {
margin-top: 0;
}
.feedback-entry p {
margin: 5px 0;
}
.content form button {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
margin-top: 20px;
}
.content form button:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
{% include "top_bar.html" %}
<div class="content">
<h2>Event Feedbacks</h2>
{% for feedback in feedbacks %}
<div class="feedback-entry">
<h3>{{ feedback.event_name }}</h3>
<p><strong>Verlauf des Events:</strong> {{ feedback.event_progress }}</p>
<p><strong>Ende des Events:</strong> {{ feedback.event_end }}</p>
<p><strong>Feedback der Community:</strong> {{ feedback.community_feedback }}</p>
<p><strong>Verbesserungsvorschläge:</strong> {{ feedback.improvement_suggestions }}</p>
<p><strong>Abgegeben von:</strong> {{ feedback.username }} am {{ feedback.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}</p>
</div>
{% endfor %}
<form action="{{ url_for('dashboard') }}" method="get">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit">Zurück</button>
</form>
</div>
</body>
</html>

View file

@ -0,0 +1,105 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lizenzdaten anzeigen</title>
<link rel="stylesheet" href="/static/style.css">
<link rel="icon" href="/static/logo.png" type="image/png">
<style>
body {
background-image: url('/static/background.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
height: 100vh;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
.content {
background: rgba(255, 255, 255, 0.8);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
width: 80%;
max-width: 800px;
max-height: 90vh;
overflow-y: auto;
text-align: center;
}
.content form {
display: flex;
flex-direction: column;
align-items: center;
}
.content form label {
margin-top: 10px;
width: 100%;
text-align: left;
}
.content form input,
.content form textarea,
.content form select {
width: 100%;
padding: 10px;
margin-top: 5px;
border: 1px solid #ccc;
border-radius: 4px;
}
.content form button {
background-color: #007BFF;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
margin-top: 20px;
}
.content form button:hover {
background-color: #0056b3;
}
.delete-button {
background-color: #FF0000;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
margin-top: 20px;
}
.delete-button:hover {
background-color: #CC0000;
}
</style>
</head>
<body>
{% include "top_bar.html" %}
<div class="content">
<h2>Lizenzdaten für {{ license_data['ingame_name'] }}</h2>
<p><strong>Lizenzschlüssel:</strong> {{ license_data['license_key'] }}</p>
<p><strong>Status:</strong> {{ license_data['status'] }}</p>
<p><strong>Name:</strong> {{ license_data['ingame_name'] }}</p>
<p><strong>Gefundene Ordner:</strong></p>
<ul>
{% for folder in license_data["found_folders"] %}
<li>{{ folder }}</li>
{% endfor %}
</ul>
{% if 'delete_requests' in session['permissions'] %}
<form action="{{ url_for('delete_license', license_key=license_data['license_key']) }}" method="post" style="display:inline;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="delete-button">Löschen</button>
</form>
{% else %}
<form action="{{ url_for('request_delete_license', license_key=license_data['license_key']) }}" method="post" style="display:inline;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="delete-button">Löschanfrage stellen</button>
</form>
{% endif %}
<a href="/get_data" class="black-button">Zurück</a>
</div>
</body>
</html>

View file

@ -0,0 +1,118 @@
{% if session['theme'] == 'darkmode' %}
{% extends "base_dark.html" %}
{% else %}
{% extends "base.html" %}
{% endif %}
{% block title %}Benutzerdetails{% endblock %}
{% block content %}
{% if session['theme'] == 'darkmode' %}
<style>
.details-container {
background: rgba(30, 30, 30, 0.95);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.5);
max-width: 500px;
width: 100%;
text-align: center;
margin: auto;
display: flex;
flex-direction: column;
align-items: center;
}
.details-container h1 {
margin-bottom: 20px;
color: #fff;
}
.details-container p {
font-size: 1.2em;
color: #0ff;
margin: 10px 0;
}
.back-button-dark {
margin-top: 20px;
display: inline-block;
width: auto;
padding: 14px 28px;
text-align: center;
}
.details-container strong {
color: #fff;
}
</style>
<div class="details-container">
<h1>Benutzerdetails</h1>
<p><strong>Ingame Name:</strong> {{ user.ingame_name }}</p>
<p><strong>SteamID:</strong> {{ user.steam_id }}</p>
<p><strong>Geburtsdatum:</strong> {{ user.birthdate }}</p>
<p><strong>Alter:</strong> {{ user.age }} Jahre</p>
<a href="/verify_age" class="neon-button neon3 back-button-dark"><span>Zurück</span></a>
</div>
{% else %}
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Benutzerdetails</title>
<link rel="stylesheet" href="/static/style.css">
<link rel="icon" href="/static/logo.png" type="image/png">
<style>
body {
background-image: url('/static/background.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
height: 100vh;
margin: 0;
display: flex;
flex-direction: column;
}
.details-container {
background: rgba(255, 255, 255, 0.6);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
max-width: 500px;
width: 100%;
text-align: center;
margin: auto;
display: flex;
flex-direction: column;
align-items: center;
}
.details-container h1 {
margin-bottom: 20px;
}
.details-container p {
font-size: 1.2em;
color: #333;
margin: 10px 0;
}
.back-button {
margin-top: 20px;
background-color: #007BFF;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
text-decoration: none;
}
</style>
</head>
<body>
{% include "top_bar.html" %}
<div class="details-container">
<h1>Benutzerdetails</h1>
<p><strong>Ingame Name:</strong> {{ user.ingame_name }}</p>
<p><strong>SteamID:</strong> {{ user.steam_id }}</p>
<p><strong>Geburtsdatum:</strong> {{ user.birthdate }}</p>
<p><strong>Alter:</strong> {{ user.age }} Jahre</p>
<a href="/verify_age" class="back-button">Zurück</a>
</div>
</body>
</html>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,377 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SCP:SL Server Status</title>
<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap" rel="stylesheet">
<style>
:root {
--primary-bg-color: rgba(0, 0, 0, 0.3);
--text-color: #e0e0e0;
--highlight-color: #00ff00;
--error-color: #ff4444;
--border-color: rgba(255, 255, 255, 0.08);
--shadow-color: rgba(0, 0, 0, 0.6);
--angle: 90deg;
--warning-color: #ffcc00;
}
body {
width: 100vw;
height: 100vh;
margin: 0;
padding: 0;
overflow: hidden;
background: linear-gradient(180deg, #07131c, #305472);
font-family: 'Roboto', sans-serif;
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.server-info-card {
position: relative;
z-index: 10;
text-align: center;
background-color: var(--primary-bg-color);
padding: 40px 70px;
border-radius: 16px;
box-shadow: 0 10px 32px var(--shadow-color);
max-width: 98%;
width: 1000px;
border: 1px solid var(--border-color);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
transform: translateY(0);
opacity: 1;
transition: transform 0.5s ease-out, opacity 0.5s ease-out;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.server-info-card.hidden {
transform: translateY(20px);
opacity: 0;
pointer-events: none;
}
#server-name {
font-size: 3.125em;
margin-bottom: 18px;
font-weight: 700;
line-height: 1.2;
letter-spacing: 0.5px;
text-transform: none;
animation: fadeIn 1.5s ease-in-out forwards;
text-shadow: 1px 1px 3px var(--shadow-color);
word-break: break-word;
}
#server-name span {
display: inline-block;
}
#player-count {
font-size: 2.75em;
margin-top: 18px;
font-weight: 700;
color: var(--highlight-color);
transition: color 0.3s ease-in-out;
text-shadow: 1px 1px 3px var(--shadow-color);
margin-bottom: 25px;
}
.loading-message {
font-size: 1.75em;
color: var(--text-color);
animation: pulse 1.5s infinite alternate;
}
.error-message {
font-size: 1.75em;
color: var(--error-color);
font-weight: 700;
}
.warning-message {
font-size: 1.75em;
color: var(--warning-color);
font-weight: 700;
}
.rain {
position: absolute;
width: 100vw;
height: 100vh;
z-index: 0;
top: 0;
left: 0;
pointer-events: none;
overflow: hidden;
}
.drop {
border: 0.25vmin solid transparent;
border-bottom-color: #abc2e9;
position: absolute;
top: -5vmin;
width: 0;
height: 0;
animation: fall var(--drop-duration) var(--drop-delay) linear infinite;
left: var(--drop-left);
border-left-width: var(--drop-width);
opacity: var(--drop-opacity);
transform: rotate(var(--angle));
transform-origin: 50% 100%;
}
@keyframes fall {
from {
transform: translateY(0) rotate(var(--angle));
}
to {
transform: translateY(calc(100vh + 10vmin)) rotate(var(--angle));
}
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes pulse {
from { opacity: 0.7; }
to { opacity: 1; }
}
.graph-image-container {
width: 100%;
max-width: 900px;
height: auto;
background-color: rgba(0, 0, 0, 0.2);
border-radius: 22px;
padding: 32px;
box-sizing: border-box;
margin-top: 36px;
display: flex;
justify-content: center;
align-items: center;
}
.graph-image-container img {
max-width: 100%;
height: auto;
display: block;
border-radius: 14px;
}
@media (max-width: 1100px) {
.server-info-card {
width: 98%;
padding: 25px 10px;
}
.graph-image-container {
max-width: 98vw;
padding: 10px;
}
}
@media (max-width: 768px) {
.server-info-card {
padding: 18px 2vw;
width: 99vw;
}
#server-name {
font-size: 2.25em;
}
#player-count {
font-size: 2.0em;
}
.loading-message, .error-message, .warning-message {
font-size: 1.25em;
}
.graph-image-container {
max-width: 100vw;
height: auto;
padding: 4vw;
}
}
</style>
</head>
<body>
<div class="rain"></div>
<div class="server-info-card">
<div id="server-name" class="loading-message">Lade Servernamen...</div>
<div id="player-count" class="loading-message">Lade Spieleranzahl...</div>
<div id="global-error-message" class="error-message" style="display:none; margin-top: 10px;"></div>
<div class="graph-image-container">
<img id="playerHistoryGraph" src="/player_history_graph_img" alt="Spielerstatistik der letzten 24h">
</div>
</div>
<script>
const numDrops = 500;
const rainContainer = document.querySelector('.rain');
const defaultAngle = 90;
let initialLoadDone = false;
for (let i = 0; i < numDrops; i++) {
const drop = document.createElement('div');
drop.classList.add('drop');
drop.style.setProperty('--drop-opacity', (Math.random() * 0.7) + 0.1);
drop.style.setProperty('--drop-left', (Math.random() * 100) + 'vw');
drop.style.setProperty('--drop-width', (Math.random() * 0.4) + 0.05 + 'vmin');
drop.style.setProperty('--drop-duration', (Math.random() * 1.5) + 1.5 + 's');
drop.style.setProperty('--drop-delay', (Math.random() * -3) + 's');
drop.style.setProperty('--angle', `${defaultAngle}deg`);
rainContainer.appendChild(drop);
}
function extractShortServerName(rawInfo) {
const nameMatch = rawInfo.match(/(<span style="color:#[a-fA-F0-9]{6}">\[G<\/span><span style="color:#[a-fA-F0-9]{6}">E<\/span><span style="color:#[a-fA-F0-9]{6}">R\]<\/span>)?\s*<b>(.*?)<\/b>(\s*<span style="color:#[a-fA-F0-9]{6}">\[#\d+\]<\/span>)?/);
if (nameMatch) {
let finalName = '';
if (nameMatch[1]) finalName += nameMatch[1] + ' ';
if (nameMatch[2]) finalName += nameMatch[2];
if (nameMatch[3]) finalName += nameMatch[3];
return finalName.split('<br')[0].trim();
}
return rawInfo.split('<br')[0].replace(/<[^>]*>/g, '').trim();
}
function updatePlayerHistoryGraph() {
const graphImage = document.getElementById('playerHistoryGraph');
graphImage.src = `/player_history_graph_img?t=${new Date().getTime()}`;
}
async function fetchServerData(isInitialCall = false) {
const apiUrl = '/api/scpsl_proxy';
const serverNameElement = document.getElementById('server-name');
const playerCountElement = document.getElementById('player-count');
const cardElement = document.querySelector('.server-info-card');
const globalErrorMessageElement = document.getElementById('global-error-message');
if (isInitialCall || !initialLoadDone || serverNameElement.classList.contains('error-message') || globalErrorMessageElement.style.display === 'block') {
serverNameElement.className = 'loading-message';
serverNameElement.innerHTML = 'Lade Servernamen...';
playerCountElement.className = 'loading-message';
playerCountElement.textContent = 'Lade Spieleranzahl...';
globalErrorMessageElement.style.display = 'none';
cardElement.classList.remove('hidden');
} else {
if (!playerCountElement.classList.contains('error-message')) {
playerCountElement.classList.add('loading-message');
}
}
const timeoutPromise = new Promise((resolve, reject) => {
const id = setTimeout(() => {
clearTimeout(id);
reject(new Error('Server response timed out (mehr als 5 Sekunden).'));
}, 5000);
});
try {
const response = await Promise.race([
fetch(apiUrl),
timeoutPromise
]);
if (!response.ok) {
let errorDetails = 'Unbekannter Fehler';
try {
const errorData = await response.json();
errorDetails = errorData.error || errorDetails;
} catch (e) {
errorDetails = response.statusText || errorDetails;
}
throw new Error(`Serverfehler: ${response.status} - ${errorDetails}`);
}
const data = await response.json();
let rawServerInfo = data.info || "Unbekannter Server";
const currentServerNameHTML = serverNameElement.innerHTML;
const newServerNameHTML = extractShortServerName(rawServerInfo);
if (isInitialCall || !initialLoadDone || serverNameElement.classList.contains('error-message') || currentServerNameHTML !== newServerNameHTML) {
serverNameElement.innerHTML = newServerNameHTML;
}
if (data.online) {
playerCountElement.textContent = `Spieler: ${data.players}`;
playerCountElement.style.color = 'var(--highlight-color)';
serverNameElement.classList.remove('loading-message', 'error-message', 'warning-message');
playerCountElement.classList.remove('loading-message', 'error-message', 'warning-message');
globalErrorMessageElement.style.display = 'none';
cardElement.classList.remove('hidden');
} else {
if (!serverNameElement.innerHTML.includes('[OFFLINE]')) {
serverNameElement.innerHTML = newServerNameHTML + '<br><span style="font-size:0.6em; color:var(--error-color);">[OFFLINE]</span>';
}
playerCountElement.textContent = 'Server ist offline.';
playerCountElement.style.color = 'var(--error-color)';
serverNameElement.classList.remove('loading-message', 'warning-message');
serverNameElement.classList.add('error-message');
playerCountElement.classList.remove('loading-message', 'warning-message');
playerCountElement.classList.add('error-message');
globalErrorMessageElement.style.display = 'none';
cardElement.classList.remove('hidden');
}
playerCountElement.classList.remove('loading-message');
initialLoadDone = true;
} catch (error) {
console.error('Fehler beim Abrufen der Serverdaten:', error);
let errorMessage = 'Server konnte nicht erreicht werden.';
let errorSubtext = '<i>(Allgemeiner Fehler)</i>';
let errorClass = 'error-message';
if (error.message.includes('timed out')) {
errorMessage = 'Server-Antwort Timeout.';
errorSubtext = '<i>(Ladeversuch läuft im Hintergrund)</i>';
errorClass = 'warning-message';
} else if (error.message.includes('Serverfehler:')) {
errorMessage = 'Serverfehler.';
errorSubtext = `<i>(${error.message.split('Serverfehler: ')[1]})</i>`;
errorClass = 'error-message';
}
serverNameElement.className = errorClass;
serverNameElement.innerHTML = errorMessage + '<br><small>' + errorSubtext + '</small>';
playerCountElement.className = errorClass;
playerCountElement.textContent = 'Daten nicht verfügbar.';
globalErrorMessageElement.textContent = 'Aktualisierung fehlgeschlagen. Bitte versuche es später erneut.';
globalErrorMessageElement.style.display = 'block';
if (errorClass === 'error-message') {
cardElement.classList.add('hidden');
} else {
cardElement.classList.remove('hidden');
}
initialLoadDone = false;
}
}
document.addEventListener('DOMContentLoaded', () => {
fetchServerData(true);
updatePlayerHistoryGraph();
setInterval(fetchServerData, 5000);
setInterval(updatePlayerHistoryGraph, 60000);
});
</script>
</body>
</html>

1535
Outdated Website/website.py Normal file

File diff suppressed because it is too large Load diff