AstraOS-Panel/Outdated Website/website.py
MrWhiff 1b7a12f8e2 AstraOS Ver. 1.9:
- Age Verification hinzugefügt (für SkyFlo)
2025-12-11 16:26:22 +01:00

1483 lines
54 KiB
Python

from PIL import Image
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from io import BytesIO
import matplotlib.dates as mdates
from flask import Flask, request, render_template, redirect, url_for, session, jsonify, Response, send_file
from flask_socketio import SocketIO, join_room, leave_room, emit
from pymongo import MongoClient
from bson.objectid import ObjectId
import uuid
import datetime
from flask_wtf.csrf import CSRFProtect
from werkzeug.security import generate_password_hash, check_password_hash
import subprocess
import signal
import threading
import psutil
import time
import json
import requests
import os
from datetime import datetime, timedelta
app = Flask(__name__, static_folder='/var/www/admin_website/static', template_folder='/var/www/admin_website/templates')
app.secret_key = os.urandom(24)
socketio = SocketIO(app, cors_allowed_origins="*", async_mode="threading")
csrf = CSRFProtect(app)
client = MongoClient("mongodb+srv://MrSniff:5169Galaxy@chainsaw.d0shx.mongodb.net/")
MONGO_URI = "mongodb+srv://MrSniff:5169Galaxy@chainsaw.d0shx.mongodb.net/"
db = client["moonlab"]
collection = db["anti_cheat"]
accounts_collection = db["website_accounts"]
delete_requests_collection = db["delete_requests"]
logs_collection = db["logs"]
user_ages_collection = db["user_ages"]
projects_collection = db["projects"]
changelogs_collection = db["changelogs"]
appointments_collection = db["appointments"]
scpsl_playerdata_collection = db["scpsl-playerdata"]
ETS2_DB_NAME = "ets2_license_db"
ETS2_COLLECTION_NAME = "licenses"
ets2_db = client[ETS2_DB_NAME]
ets2_licenses = ets2_db[ETS2_COLLECTION_NAME]
SCP_SL_API_URL = "https://api.scplist.kr/api/servers/85734"
app.config.update(
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_SAMESITE='Lax',
REMEMBER_COOKIE_HTTPONLY=True,
REMEMBER_COOKIE_SECURE=True,
REMEMBER_COOKIE_SAMESITE='Lax'
)
if accounts_collection.count_documents({}) == 0:
accounts_collection.insert_one({
"username": "Admin",
"display_name": "Admin",
"password": generate_password_hash("1812"),
"permissions": ["user_management", "permission_management", "delete_requests", "logs", "create_license_key",
"view_license_keys", "create_event_feedback", "view_event_feedback", "verify_age",
"project_manager", "ip_checker", "changelogs"]
})
def generate_license_key():
return str(uuid.uuid4())
def add_license_key_to_db(license_key):
if collection.find_one({"license_key": license_key}):
return False
else:
collection.insert_one({"license_key": license_key, "status": "unused"})
return True
def add_log(message):
logs_collection.insert_one({"timestamp": datetime.now(), "message": message})
def stream_logs(process):
def generate():
for line in iter(process.stdout.readline, b''):
yield line.decode('utf-8')
return Response(generate(), mimetype='text/plain')
def load_bot_config():
try:
with open("bot_config.json", "r") as file:
return json.load(file)
except FileNotFoundError:
return {}
def save_bot_config(config):
with open("bot_config.json", "w") as file:
json.dump(config, file, indent=4)
bot_config = load_bot_config()
@app.route("/website.py", methods=["GET"])
def deny_access():
return "Access denied", 403
@app.route("/", methods=["GET", "POST"])
def login():
error = None
if request.method == "POST":
username = request.form["username"]
password = request.form["password"]
remember_me = "remember_me" in request.form
user = accounts_collection.find_one({"username": username})
if user and check_password_hash(user["password"], password):
session["user_id"] = str(user["_id"])
session["username"] = user["username"]
session["display_name"] = user.get("display_name", user["username"])
session["permissions"] = user.get("permissions", [])
add_log(f"{username} hat sich eingeloggt")
if remember_me:
session.permanent = True
return redirect(url_for("dashboard"))
else:
error = "Du hast ein(en) falsches Passwort/Benutzernamen eingegeben"
return render_template("login.html", error=error)
@app.route("/dashboard")
def dashboard():
if "username" not in session:
return redirect(url_for("login"))
user = accounts_collection.find_one({"_id": ObjectId(session["user_id"])})
if not user:
return redirect(url_for("login"))
session["permissions"] = user.get("permissions", [])
if "Style" not in user:
accounts_collection.update_one({"_id": ObjectId(session["user_id"])}, {"$set": {"Style": "lightmode"}})
session["theme"] = "lightmode"
else:
session["theme"] = user["Style"]
if session["theme"] == "darkmode":
return render_template("dashboard_dark.html")
else:
return render_template("dashboard_light.html")
@app.route('/view_changelogs', methods=['GET'])
def view_changelogs():
if "username" not in session:
return redirect(url_for("login"))
changelogs = [
{"text": changelog["text"], "timestamp": changelog["timestamp"]}
for changelog in changelogs_collection.find().sort("timestamp", -1)
]
return render_template("view_changelogs.html", changelogs=changelogs)
@app.route("/user_management", methods=["GET", "POST"])
def user_management():
if "username" not in session or "user_management" not in session["permissions"]:
return redirect(url_for("login"))
users = accounts_collection.find()
return render_template("user_management.html", users=users)
@app.route("/add_user", methods=["POST"])
def add_user():
if "username" not in session or "user_management" not in session["permissions"]:
return redirect(url_for("login"))
new_username = request.form["username"]
new_display_name = request.form["display_name"]
new_password = request.form["password"]
new_permissions = request.form.getlist("permissions")
if accounts_collection.find_one({"username": new_username}):
return "Benutzername existiert bereits", 400
accounts_collection.insert_one({
"username": new_username,
"display_name": new_display_name,
"password": generate_password_hash(new_password),
"permissions": new_permissions
})
add_log(f"{session['username']} created or modified user {new_username}")
return redirect(url_for("user_management"))
@app.route("/change_permissions/<user_id>", methods=["POST"])
def change_permissions(user_id):
if "username" not in session or "permission_management" not in session["permissions"]:
return redirect(url_for("login"))
user = accounts_collection.find_one({"_id": ObjectId(user_id)})
if not user:
return "User not found", 404
if "remove_permission" in request.form:
permission_to_remove = request.form["remove_permission"]
accounts_collection.update_one({"_id": ObjectId(user_id)}, {"$pull": {"permissions": permission_to_remove}})
add_log(f"{session['username']} removed permission {permission_to_remove} from user {user['username']}")
if "add_permission" in request.form:
permission_to_add = request.form["add_permission"]
accounts_collection.update_one({"_id": ObjectId(user_id)}, {"$addToSet": {"permissions": permission_to_add}})
add_log(f"{session['username']} added permission {permission_to_add} to user {user['username']}")
if str(user["_id"]) == session.get("user_id"):
updated_user = accounts_collection.find_one({"_id": ObjectId(user_id)})
session["permissions"] = updated_user.get("permissions", [])
return redirect(url_for("user_management"))
@app.route("/delete_user/<user_id>", methods=["POST"])
def delete_user(user_id):
if "username" not in session or "user_management" not in session["permissions"]:
return redirect(url_for("login"))
user = accounts_collection.find_one({"_id": ObjectId(user_id)})
if user:
accounts_collection.delete_one({"_id": ObjectId(user_id)})
add_log(f"{session['username']} deleted user {user['username']}")
return redirect(url_for("user_management"))
UPLOAD_FOLDER = "/var/www/admin_website/static/pfps"
ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg"}
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route("/edit_user/<user_id>", methods=["GET", "POST"])
def edit_user(user_id):
if "username" not in session:
return redirect(url_for("login"))
user = accounts_collection.find_one({"_id": ObjectId(user_id)})
if "permission_management" not in session["permissions"] and session["user_id"] != str(user["_id"]):
return "Access denied", 403
if request.method == "POST":
new_display_name = request.form.get("display_name")
new_password = request.form.get("password")
profile_pic = request.files.get("pfp")
update_data = {"display_name": new_display_name}
pfp_uploaded = False
pfp_url = None
if new_password:
update_data["password"] = generate_password_hash(new_password, method='scrypt')
if profile_pic and profile_pic.filename != "":
ext = profile_pic.filename.rsplit('.', 1)[-1].lower()
if ext in ["png", "jpg", "jpeg"]:
filename = f"{user_id}.png"
upload_folder = "/var/www/admin_website/static/pfps"
os.makedirs(upload_folder, exist_ok=True)
save_path = os.path.join(upload_folder, filename)
try:
img = Image.open(profile_pic.stream)
if img.mode == 'RGBA' or (img.mode == 'P' and 'transparency' in img.info):
img.save(save_path, "PNG")
else:
img = img.convert("RGB")
img.save(save_path, "PNG")
website_data = user.get("website_accounts", {})
website_data["pfp_last_updated"] = int(time.time())
website_data["pfp_filename"] = filename
update_data["website_accounts"] = website_data
pfp_uploaded = True
pfp_url = f"/static/pfps/{filename}"
except Exception as e:
print(f"Fehler beim Speichern oder Konvertieren des Profilbildes: {e}")
else:
print(f"Unerlaubter Dateityp für Profilbild: {ext}")
accounts_collection.update_one({"_id": ObjectId(user_id)}, {"$set": update_data})
if user_id == session.get("user_id"):
session["display_name"] = new_display_name
if pfp_uploaded:
session["pfp_last_updated"] = update_data["website_accounts"]["pfp_last_updated"]
if pfp_uploaded:
return jsonify({"success": True, "message": "Profilbild erfolgreich aktualisiert!", "pfp_url": pfp_url})
else:
return jsonify({"success": True, "message": "Profil erfolgreich aktualisiert!", "pfp_url": None})
return render_template("edit_user.html", user=user)
@app.route('/get_data', methods=['GET'])
def get_data():
if "username" not in session:
return redirect(url_for("login"))
data = collection.find()
result = []
for doc in data:
if 'ingame_name' in doc and doc['ingame_name']:
doc['_id'] = str(doc['_id'])
result.append(doc)
return render_template("get_data.html", data=result)
@app.route('/license=<license_key>', methods=['GET'])
def view_license(license_key):
if "username" not in session:
return redirect(url_for("login"))
license_data = collection.find_one({"license_key": license_key})
if license_data:
license_data['_id'] = str(license_data['_id'])
return render_template("view_license.html", license_data=license_data)
else:
return "Lizenzschlüssel nicht gefunden", 404
@app.route('/delete_license/<license_key>', methods=['POST'])
def delete_license(license_key):
if "username" not in session:
return redirect(url_for("login"))
collection.delete_one({"license_key": license_key})
add_log(f"{session['username']} hat Lizenzschlüssel {license_key} gelöscht")
return redirect(url_for("get_data"))
@app.route('/request_delete_license/<license_key>', methods=['POST'])
def request_delete_license(license_key):
if "username" not in session:
return redirect(url_for("login"))
delete_requests_collection.insert_one({"license_key": license_key, "username": session["username"]})
add_log(f"{session['username']} hat eine Delete-Request für Lizenzschlüssel {license_key} gestellt")
return redirect(url_for("get_data"))
@app.route('/delete_requests', methods=['GET'])
def delete_requests():
if "username" not in session or "delete_requests" not in session["permissions"]:
return redirect(url_for("login"))
delete_requests = list(delete_requests_collection.find())
return render_template("delete_request.html", delete_requests=delete_requests)
@app.route('/approve_delete_request/<request_id>', methods=['POST'])
def approve_delete_request(request_id):
if "username" not in session or "delete_requests" not in session["permissions"]:
return redirect(url_for("login"))
request = delete_requests_collection.find_one({"_id": ObjectId(request_id)})
if request:
collection.delete_one({"license_key": request["license_key"]})
delete_requests_collection.delete_one({"_id": ObjectId(request_id)})
add_log(f"{session['username']} hat die Delete-Request für Lizenzschlüssel {request['license_key']} genehmigt")
return redirect(url_for("delete_requests"))
@app.route('/deny_delete_request/<request_id>', methods=['POST'])
def deny_delete_request(request_id):
if "username" not in session or "delete_requests" not in session["permissions"]:
return redirect(url_for("login"))
delete_requests_collection.delete_one({"_id": ObjectId(request_id)})
add_log(f"{session['username']} hat die Delete-Request für Lizenzschlüssel {request_id} abgelehnt")
return redirect(url_for("delete_requests"))
@app.route('/add_data', methods=['POST'])
def add_data():
if "username" not in session:
return redirect(url_for("login"))
new_data = request.get_json()
result = collection.insert_one(new_data)
return jsonify({"message": "Daten erfolgreich hinzugefügt", "inserted_id": str(result.inserted_id)})
@app.route('/create_license_key', methods=['GET', 'POST'])
def create_license_key():
if "username" not in session or "create_license_key" not in session["permissions"]:
return redirect(url_for("login"))
if request.method == "POST":
license_key = generate_license_key()
if add_license_key_to_db(license_key):
add_log(f"{session['username']} hat Lizenzschlüssel {license_key} erstellt")
return render_template("create_license_key.html", license_key=license_key)
else:
return render_template("create_license_key.html", error="Fehler beim Erstellen des Lizenzschlüssels")
return render_template("create_license_key.html")
@app.route('/database_entries', methods=['GET'])
def database_entries():
if "username" not in session:
return redirect(url_for("login"))
data = collection.find()
result = []
for doc in data:
doc['_id'] = str(doc['_id'])
result.append(doc)
return render_template("view_license.html", data=result)
@app.route('/license_keys', methods=['GET'])
def license_keys():
if "username" not in session:
return redirect(url_for("login"))
return render_template("license_keys.html")
@app.route('/search_license_key', methods=['GET'])
def search_license_key():
license_key = request.args.get('license_key')
license_data = collection.find_one({"license_key": license_key})
if license_data:
return jsonify({"found": True})
else:
return jsonify({"found": False})
@app.route('/logs', methods=['GET'])
def logs():
if "username" not in session or "logs" not in session["permissions"]:
return redirect(url_for("login"))
logs = list(logs_collection.find().sort("timestamp", -1))
return render_template("logs.html", logs=logs)
@app.route('/event_feedback', methods=['GET', 'POST'])
def event_feedback():
if "username" not in session or "create_event_feedback" not in session["permissions"]:
return redirect(url_for("login"))
if request.method == "POST":
event_name = request.form["event_name"]
if event_name == "Anderes":
event_name = request.form["other_event_name"]
event_progress = request.form["event_progress"]
if event_progress == "Anderes":
event_progress = request.form["other_event_progress"]
event_end = request.form["event_end"]
if event_end == "Anderes":
event_end = request.form["other_event_end"]
community_feedback = request.form["community_feedback"]
if community_feedback == "Anderes":
community_feedback = request.form["other_community_feedback"]
improvement_suggestions = request.form["improvement_suggestions"]
feedback_data = {
"event_name": event_name,
"event_progress": event_progress,
"event_end": event_end,
"community_feedback": community_feedback,
"improvement_suggestions": improvement_suggestions,
"timestamp": datetime.now(),
"username": session["username"]
}
db.event_feedback.insert_one(feedback_data)
add_log(f"{session['username']} hat Feedback für das Event {event_name} abgegeben")
return redirect(url_for("dashboard"))
return render_template("event_feedback.html")
@app.route('/event=<event_id>', methods=['GET'])
def view_event_feedback(event_id):
if "username" not in session:
return redirect(url_for("login"))
feedback = db.event_feedback.find_one({"_id": ObjectId(event_id)})
if feedback:
feedback['_id'] = str(feedback['_id'])
return render_template("view_event_feedback.html", feedback=feedback)
else:
return "Event-Feedback nicht gefunden", 404
@app.route('/view_event_feedbacks', methods=['GET'])
def view_event_feedbacks():
if "username" not in session or "view_event_feedback" not in session["permissions"]:
return redirect(url_for("login"))
feedbacks = list(db.event_feedback.find().sort("timestamp", -1))
return render_template("view_event_feedbacks.html", feedbacks=feedbacks)
@app.route("/events", methods=["GET", "POST"])
@csrf.exempt
def events():
if "username" not in session:
return redirect(url_for("login"))
if request.method == "POST":
event_name = request.form["event_name"]
community_feedback = request.form["community_feedback"]
players = int(request.form["players"])
time = request.form["time"]
weekday = request.form["weekday"]
top = 1 if community_feedback == "top" else 0
flop = 1 if community_feedback == "flop" else 0
etwas = 1 if community_feedback == "etwas" else 0
rating = top - flop + etwas
event = db.events.find_one({"name": event_name, "time": time, "weekday": weekday})
if event:
db.events.update_one(
{"_id": event["_id"]},
{"$inc": {"top": top, "flop": flop, "etwas": etwas, "rating": rating}}
)
add_log(f"{session['username']} updated event {event_name} with rating {rating}")
else:
db.events.insert_one({
"name": event_name,
"players": players,
"time": time,
"weekday": weekday,
"top": top,
"flop": flop,
"etwas": etwas,
"rating": rating,
"timestamp": datetime.now()
})
add_log(f"{session['username']} added event {event_name} with rating {rating}")
events = list(db.events.find().sort("timestamp", -1))
best_event = None
best_events = None
if request.args.get("calculate_best"):
current_players = int(request.args.get("current_players"))
current_time = request.args.get("current_time")
current_weekday = request.args.get("current_weekday")
event_type = request.args.get("event_type", None)
best_events = calculate_best_events(current_players, current_time, current_weekday, event_type)
if best_events:
best_event = best_events[0]
return render_template("events.html", events=events, best_event=best_event)
def calculate_best_events(current_players, current_time, current_weekday, event_type=None):
events = list(db.events.find())
scored_events = []
for event in events:
similarity_score = 0
similarity_score -= abs(event['players'] - current_players) * 0.5
if event['weekday'] != current_weekday:
similarity_score -= 1
similarity_score -= abs(int(event['time'].split(':')[0]) - int(current_time.split(':')[0])) * 0.2
similarity_score += (event['rating'] * 1.0)
if event_type and event.get('type') == event_type:
similarity_score += 0.1
scored_events.append((similarity_score, event))
scored_events.sort(reverse=True, key=lambda x: x[0])
return [e for _, e in scored_events[:5]]
@app.route("/dc_bots", methods=["GET"])
def dc_bots():
if "username" not in session or "discord_bots" not in session["permissions"]:
return redirect(url_for("login"))
bots = list(db.dc_bots.find())
return render_template("dc_bots.html", bots=bots)
@app.route("/add_dc_bot", methods=["POST"])
def add_dc_bot():
if "username" not in session or "discord_bots" not in session["permissions"]:
return redirect(url_for("login"))
bot_name = request.form["bot_name"]
bot_directory = request.form["bot_directory"]
db.dc_bots.insert_one({"name": bot_name, "directory": bot_directory})
add_log(f"{session['username']} added bot {bot_name}")
return redirect(url_for("dc_bots"))
@app.route("/upload_bot_version/<bot_name>", methods=["POST"])
def upload_bot_version(bot_name):
if "username" not in session or "discord_bots" not in session["permissions"]:
return redirect(url_for("login"))
bot = db.dc_bots.find_one({"name": bot_name})
if not bot:
return f"Bot {bot_name} nicht gefunden", 404
if "bot_file" not in request.files:
return "Keine Datei hochgeladen", 400
bot_file = request.files["bot_file"]
if bot_file.filename == "":
return "Keine Datei ausgewählt", 400
version = bot.get("version", "1.0")
major, minor = map(int, version.split("."))
new_version = f"{major}.{minor + 1}"
base_directory_name = bot['directory'].rsplit('_v', 1)[0] if '_v' in bot['directory'] else bot['directory']
bot_directory_with_version = f"{base_directory_name}_v{new_version}"
os.makedirs(bot_directory_with_version, exist_ok=True)
bot_file.save(os.path.join(bot_directory_with_version, bot_file.filename))
db.dc_bots.update_one({"name": bot_name},
{"$set": {"directory": bot_directory_with_version, "version": new_version}})
add_log(f"{session['username']} uploaded new version {new_version} for bot {bot_name}")
return redirect(url_for("dc_bots"))
@app.route("/revert_bot_version/<bot_name>/<version>", methods=["POST"])
def revert_bot_version(bot_name, version):
if "username" not in session or "discord_bots" not in session["permissions"]:
return redirect(url_for("login"))
bot = db.dc_bots.find_one({"name": bot_name})
if not bot:
return f"Bot {bot_name} nicht gefunden", 404
base_directory_name = bot['directory'].rsplit('_v', 1)[0] if '_v' in bot['directory'] else bot['directory']
bot_directory = f"{base_directory_name}_v{version}"
if not os.path.exists(bot_directory):
return f"Version {version} für Bot {bot_name} nicht gefunden", 404
db.dc_bots.update_one({"name": bot_name}, {"$set": {"directory": bot_directory, "version": version}})
add_log(f"{session['username']} reverted bot {bot_name} to version {version}")
return redirect(url_for("dc_bots"))
def initialize_bot_versions():
bots = db.dc_bots.find({"version": {"$exists": False}})
for bot in bots:
db.dc_bots.update_one({"_id": bot["_id"]}, {"$set": {"version": "1.0"}})
add_log(f"Initialized version 1.0 for bot {bot['name']}")
initialize_bot_versions()
if db.events.count_documents({}) == 0:
db.events.insert_one({
"name": "Sample Event",
"top": 0,
"flop": 0,
"rating": 0,
"timestamp": datetime.now()
})
def save_logs_to_file(process, log_file_path):
try:
with open(log_file_path, "w") as log_file:
while True:
output = process.stdout.readline()
error = process.stderr.readline()
if output:
log_file.write(repr(output) + "\n")
log_file.flush()
print(output, end="")
if error:
log_file.write("[ERROR] " + repr(error) + "\n")
log_file.flush()
print("[ERROR] " + error, end="")
if process.poll() is not None:
for line in process.stdout.readlines():
log_file.write(repr(line) + "\n")
for line in process.stderr.readlines():
log_file.write("[ERROR] " + repr(line) + "\n")
break
time.sleep(0.5)
except Exception as e:
print(f"Fehler beim Speichern der Logs: {str(e)}")
@app.route("/turn_on_bot/<bot_name>", methods=["POST"])
def turn_on_bot(bot_name):
if "username" not in session or "discord_bots" not in session["permissions"]:
return redirect(url_for("login"))
bot = db.dc_bots.find_one({"name": bot_name})
if bot:
if "pid" in bot and psutil.pid_exists(bot["pid"]):
add_log(f"Bot {bot_name} is already running with PID {bot['pid']}")
return f"Bot {bot_name} läuft bereits.", 400
bot_directory = bot["directory"]
try:
command = ["python", bot_directory]
process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
log_file_path = f"/tmp/{bot_name}_log.txt"
db.dc_bots.update_one({"name": bot_name}, {
"$set": {
"pid": process.pid,
"log_file": log_file_path
}
})
threading.Thread(target=save_logs_to_file, args=(process, log_file_path)).start()
bot_config[bot_name] = {"status": "on", "pid": process.pid}
save_bot_config(bot_config)
add_log(f"{session['username']} turned on {bot_name}")
except Exception as e:
add_log(f"Error starting bot {bot_name}: {str(e)}")
return f"Fehler beim Starten des Bots {bot_name}: {str(e)}", 500
else:
return f"Bot {bot_name} nicht gefunden", 404
return redirect(url_for("dc_bots"))
@app.route("/turn_off_bot/<bot_name>", methods=["POST"])
def turn_off_bot(bot_name):
if "username" not in session or "discord_bots" not in session["permissions"]:
return redirect(url_for("login"))
bot = db.dc_bots.find_one({"name": bot_name})
if bot and "pid" in bot:
try:
pid = bot["pid"]
if psutil.pid_exists(pid):
os.kill(pid, signal.SIGTERM)
db.dc_bots.update_one({"name": bot_name}, {"$unset": {"pid": ""}})
bot_config[bot_name] = {"status": "off"}
save_bot_config(bot_config)
add_log(f"{session['username']} turned off {bot_name}")
else:
add_log(f"Process for bot {bot_name} does not exist")
db.dc_bots.update_one({"name": bot_name}, {"$unset": {"pid": ""}})
except Exception as e:
add_log(f"Error stopping bot {bot_name}: {str(e)}")
return f"Fehler beim Stoppen des Bots {bot_name}: {str(e)}", 500
else:
return f"Bot {bot_name} nicht gefunden oder kein aktiver Prozess vorhanden", 404
return redirect(url_for("dc_bots"))
@app.route("/turn_on_all_bots", methods=["POST"])
def turn_on_all_bots():
if "username" not in session or "discord_bots" not in session["permissions"]:
return redirect(url_for("login"))
bots = list(db.dc_bots.find())
for bot in bots:
if "pid" not in bot or not psutil.pid_exists(bot["pid"]):
bot_directory = bot["directory"]
try:
command = ["python", bot_directory]
process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
log_file_path = f"/tmp/{bot['name']}_log.txt"
db.dc_bots.update_one({"name": bot["name"]}, {
"$set": {
"pid": process.pid,
"log_file": log_file_path
}
})
threading.Thread(target=save_logs_to_file, args=(process, log_file_path)).start()
bot_config[bot["name"]] = {"status": "on", "pid": process.pid}
save_bot_config(bot_config)
add_log(f"{session['username']} turned on {bot['name']}")
except Exception as e:
add_log(f"Error starting bot {bot['name']}: {str(e)}")
return redirect(url_for("dc_bots"))
@app.route("/turn_off_all_bots", methods=["POST"])
def turn_off_all_bots():
if "username" not in session or "discord_bots" not in session["permissions"]:
return redirect(url_for("login"))
bots = list(db.dc_bots.find())
for bot in bots:
if "pid" in bot:
try:
pid = bot["pid"]
if psutil.pid_exists(pid):
os.kill(pid, signal.SIGTERM)
db.dc_bots.update_one({"name": bot["name"]}, {"$unset": {"pid": ""}})
bot_config[bot["name"]] = {"status": "off"}
save_bot_config(bot_config)
add_log(f"{session['username']} turned off {bot['name']}")
else:
add_log(f"Process for bot {bot['name']} does not exist")
db.dc_bots.update_one({"name": bot["name"]}, {"$unset": {"pid": ""}})
except Exception as e:
add_log(f"Error stopping bot {bot['name']}: {str(e)}")
return redirect(url_for("dc_bots"))
@app.route("/bot_logs/<bot_name>", methods=["GET"])
def bot_logs(bot_name):
if "username" not in session or "discord_bots" not in session["permissions"]:
return redirect(url_for("login"))
bot = db.dc_bots.find_one({"name": bot_name})
if bot and "log_file" in bot:
log_file_path = bot["log_file"]
try:
with open(log_file_path, "r") as log_file:
logs = log_file.readlines()
except FileNotFoundError:
logs = ["Keine Logs gefunden."]
return render_template("bot_logs.html", bot_name=bot_name, logs=logs)
else:
return f"Logs für Bot {bot_name} nicht gefunden", 404
def start_bots_on_server_start():
for bot_name, config in bot_config.items():
if config["status"] == "on":
bot = db.dc_bots.find_one({"name": bot_name})
if bot:
if "pid" in bot and psutil.pid_exists(bot["pid"]):
add_log(f"Bot {bot_name} is already running with PID {bot['pid']}")
continue
bot_directory = bot["directory"]
try:
command = ["python", bot_directory]
process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
log_file_path = f"/tmp/{bot_name}_log.txt"
db.dc_bots.update_one({"name": bot_name}, {
"$set": {
"pid": process.pid,
"log_file": log_file_path
}
})
threading.Thread(target=save_logs_to_file, args=(process, log_file_path)).start()
bot_config[bot_name] = {"status": "on", "pid": process.pid}
save_bot_config(bot_config)
add_log(f"Bot {bot_name} automatically started on server start")
except Exception as e:
add_log(f"Error starting bot {bot_name} on server start: {str(e)}")
start_bots_on_server_start()
@app.route('/logout')
def logout():
add_log(f"{session['username']} hat sich ausgeloggt")
session.pop("username", None)
session.pop("permissions", None)
session.pop("user_id", None)
return redirect(url_for("login"))
@app.route('/toggle-theme', methods=['POST'])
@csrf.exempt
def toggle_theme():
if "username" not in session:
return "Unauthorized", 401
user = accounts_collection.find_one({"_id": ObjectId(session["user_id"])})
if not user:
return "User not found", 404
current_theme = user.get("Style", "lightmode")
new_theme = "darkmode" if current_theme == "lightmode" else "lightmode"
accounts_collection.update_one({"_id": ObjectId(session["user_id"])}, {"$set": {"Style": new_theme}})
session["theme"] = new_theme
return "", 204
@app.route("/ip_checker", methods=["GET", "POST"])
def ip_checker():
if "username" not in session or "ip_checker" not in session["permissions"]:
return redirect(url_for("login"))
import ipaddress
ip_data = None
error = None
similar_ips = []
is_cheater_network = False
similar_cheater_ips = []
if request.method == "POST":
ip_address = request.form.get("ip_address")
is_cheater = request.form.get("is_cheater") == "yes"
steam_id = request.form.get("steam_id") if is_cheater else None
if not ip_address:
error = "Bitte eine IP-Adresse eingeben."
elif is_cheater and not steam_id:
error = "SteamID ist erforderlich, wenn die IP als Cheater markiert wird."
else:
try:
api_key = "015ada9472664329affb2f26d7036d47"
response = requests.get(f"https://vpnapi.io/api/{ip_address}?key={api_key}")
if response.status_code == 200:
ip_data = response.json()
if "security" not in ip_data or not isinstance(ip_data["security"], dict):
ip_data["security"] = {}
if "location" not in ip_data or not isinstance(ip_data["location"], dict):
ip_data["location"] = {}
if "network" not in ip_data or not isinstance(ip_data["network"], dict):
ip_data["network"] = {}
checked_network = None
if "network" in ip_data and "network" in ip_data["network"]:
cidr = str(ip_data["network"]["network"]).replace(" ", "")
try:
checked_network = ipaddress.ip_network(cidr, strict=False)
except Exception:
checked_network = None
cheater_ips = list(db.ip_infos.find({"is_cheater": True}))
for cheater in cheater_ips:
cheater_data = cheater.get("data", {})
cheater_network = None
if "network" in cheater_data and "network" in cheater_data["network"]:
cheater_cidr = str(cheater_data["network"]["network"]).replace(" ", "")
try:
cheater_network = ipaddress.ip_network(cheater_cidr, strict=False)
except Exception:
cheater_network = None
cheater_ip = cheater.get("ip")
if cheater_network and checked_network:
if checked_network.overlaps(cheater_network):
is_cheater_network = True
similar_cheater_ips.append(cheater)
if cheater_ip and checked_network:
try:
if ipaddress.ip_address(cheater_ip) in checked_network:
is_cheater_network = True
similar_cheater_ips.append(cheater)
except Exception:
pass
similar_ips = list(db.ip_infos.find({
"$or": [
{"ip": ip_address},
{"data.location.city": ip_data["location"].get("city")},
{"data.network.autonomous_system_number": ip_data["network"].get(
"autonomous_system_number")}
]
}))
db.ip_infos.insert_one({
"ip": ip_address,
"is_cheater": is_cheater,
"steam_id": steam_id,
"data": ip_data,
"added_by": session["username"],
"timestamp": datetime.now()
})
else:
error = "Fehler beim Abrufen der IP-Daten."
except Exception as e:
error = f"Fehler: {str(e)}"
return render_template(
"ip_checker.html",
ip_data=ip_data,
error=error,
similar_ips=similar_ips,
is_cheater_network=is_cheater_network,
similar_cheater_ips=similar_cheater_ips
)
@app.route('/add-changelog', methods=['GET', 'POST'])
def add_changelog():
if 'changelogs' not in session.get('permissions', []):
return "Unauthorized", 403
if request.method == 'POST':
changelog_text = request.form.get('changelog')
if changelog_text:
changelogs_collection.insert_one({
'text': changelog_text,
'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
})
return redirect(url_for('dashboard'))
return render_template('add_changelog.html')
@app.route("/appointments", methods=["GET", "POST"])
def appointments():
if "username" not in session or "appointments" not in session["permissions"]:
return redirect(url_for("login"))
if request.method == "POST":
title = request.form["title"]
start_date = request.form["start_date"]
end_date = request.form["end_date"]
description = request.form.get("description", "")
appointments_collection.insert_one({
"username": session["username"],
"title": title,
"start_date": start_date,
"end_date": end_date,
"description": description,
"created_at": datetime.now()
})
add_log(f"{session['username']} hat einen neuen Termin erstellt: {title}")
return redirect(url_for("appointments"))
user_appointments = list(appointments_collection.find({"username": session["username"]}).sort("start_date", 1))
return render_template("appointments.html", appointments=user_appointments)
@app.route("/delete_appointment/<appointment_id>", methods=["POST"])
def delete_appointment(appointment_id):
if "username" not in session or "appointments" not in session["permissions"]:
return redirect(url_for("login"))
appointment = appointments_collection.find_one({"_id": ObjectId(appointment_id), "username": session["username"]})
if appointment:
appointments_collection.delete_one({"_id": ObjectId(appointment_id)})
add_log(f"{session['username']} hat den Termin '{appointment['title']}' gelöscht")
return redirect(url_for("appointments"))
@app.route("/edit_appointment/<appointment_id>", methods=["GET", "POST"])
def edit_appointment(appointment_id):
if "username" not in session or "appointments" not in session["permissions"]:
return redirect(url_for("login"))
appointment = appointments_collection.find_one({"_id": ObjectId(appointment_id), "username": session["username"]})
if not appointment:
return "Termin nicht gefunden", 404
if request.method == "POST":
title = request.form["title"]
start_date = request.form["start_date"]
end_date = request.form["end_date"]
description = request.form.get("description", "")
appointments_collection.update_one(
{"_id": ObjectId(appointment_id)},
{"$set": {
"title": title,
"start_date": start_date,
"end_date": end_date,
"description": description
}}
)
add_log(f"{session['username']} hat den Termin '{title}' bearbeitet")
return redirect(url_for("appointments"))
return render_template("edit_appointment.html", appointment=appointment)
@app.route("/pcmachwas", methods=["GET", "POST"])
def pcmachwas():
return render_template("stinker.html")
@app.route("/settings", methods=["GET"])
def settings():
if "username" not in session:
return redirect(url_for("login"))
return redirect(url_for("edit_user", user_id=session["user_id"]))
@app.route("/ets2_map_cracked")
def ets2_map_cracked():
scs_dir = "/var/www/admin_website/static/ets2_map_cracked"
if not os.path.isdir(scs_dir):
return f"Verzeichnis nicht gefunden oder keine Rechte: {scs_dir}", 404
try:
files = [
f for f in os.listdir(scs_dir)
if f.endswith(".scs") and os.path.isfile(os.path.join(scs_dir, f))
]
except Exception as e:
return f"Fehler beim Lesen des Verzeichnisses: {e}", 500
if not files:
return "Keine .scs Dateien gefunden.", 404
links = "".join(
f'<a href="/download_ets2_map_cracked/{fname}" download id="dl_{i}" style="display:none"></a>'
for i, fname in enumerate(files)
)
js = f"""
<script>
const files = {files!r};
let idx = 0;
function downloadNext() {{
if(idx >= files.length) return;
document.getElementById('dl_' + idx).click();
idx++;
setTimeout(downloadNext, 1000);
}}
window.onload = downloadNext;
</script>
"""
return f"<h2>ETS2 Maps Cracked Downloads</h2>{links}{js}<p>Downloads werden automatisch gestartet...</p>"
@app.route("/download_ets2_map_cracked/<filename>")
def download_ets2_map_cracked(filename):
scs_dir = "/var/www/admin_website/static/ets2_map_cracked"
file_path = os.path.join(scs_dir, filename)
if os.path.isfile(file_path) and filename.endswith(".scs"):
return send_file(file_path, as_attachment=True)
return "Datei nicht gefunden.", 404
# wallpaper
@app.route("/wallpaper")
def wallpaper():
return render_template("wallpaper.html")
# NEUE PROXY-ROUTE für die SCP:SL API
@app.route('/api/scpsl_proxy')
def scpsl_proxy():
try:
response = requests.get(SCP_SL_API_URL, timeout=5)
response.raise_for_status()
return jsonify(response.json())
except requests.exceptions.Timeout:
return jsonify({"error": "Request timed out"}), 504
except requests.exceptions.ConnectionError:
return jsonify({"error": "Connection error"}), 503
except requests.exceptions.RequestException as e:
return jsonify({"error": f"Failed to fetch data: {e}"}), 500
def fetch_and_store_player_count():
with app.app_context():
try:
response = requests.get(SCP_SL_API_URL, timeout=5)
response.raise_for_status()
data = response.json()
players_str_full = data.get('players', '0/0')
current_players = 0
if '/' in players_str_full:
try:
current_players = int(players_str_full.split('/')[0])
except ValueError:
current_players = 0
else:
try:
current_players = int(players_str_full)
except ValueError:
current_players = 0
scpsl_playerdata_collection.insert_one({
"timestamp": datetime.utcnow(),
"players": current_players
})
scpsl_playerdata_collection.create_index("timestamp")
except requests.exceptions.RequestException as e:
print(f"Error fetching player count: {e}")
except Exception as e:
print(f"Unexpected error fetching/storing to MongoDB: {e}")
def background_task():
while True:
fetch_and_store_player_count()
time.sleep(60)
threading.Thread(target=background_task, daemon=True).start()
@app.route('/api/player_history')
def player_history_api():
graph_data = []
one_day_ago = datetime.utcnow() - timedelta(hours=24)
history_cursor = scpsl_playerdata_collection.find(
{"timestamp": {"$gte": one_day_ago}},
{"_id": 0, "timestamp": 1, "players": 1}
).sort("timestamp", 1)
zero_start = None
for doc in history_cursor:
t = doc["timestamp"]
p = doc["players"]
if p == 0:
if zero_start is None:
zero_start = t
continue
else:
if zero_start is not None:
duration = (t - zero_start).total_seconds()
if duration > 20:
graph_data.append({
"time": zero_start.isoformat() + "Z",
"players": 0
})
graph_data.append({
"time": t.isoformat() + "Z",
"players": p
})
zero_start = None
graph_data.append({
"time": t.isoformat() + "Z",
"players": p
})
if zero_start is not None:
last_time = datetime.utcnow()
duration = (last_time - zero_start).total_seconds()
if duration > 20:
graph_data.append({
"time": zero_start.isoformat() + "Z",
"players": 0
})
graph_data.append({
"time": last_time.isoformat() + "Z",
"players": 0
})
return jsonify(graph_data)
@app.route('/player_history_graph')
def player_history_graph():
html = """
<html>
<head><title>SCP:SL Player Count (Letzte 24h)</title></head>
<body style="background:#1a1a1a;">
<img id="player_graph" src="/player_history_graph_img" style="width:90vw;max-width:1200px;border:2px solid #444;">
<script>
setInterval(function() {
var img = document.getElementById('player_graph');
img.src = '/player_history_graph_img?ts=' + new Date().getTime();
}, 10000);
</script>
</body>
</html>
"""
return html
@app.route('/player_history_graph_img')
def player_history_graph_img():
one_day_ago = datetime.utcnow() - timedelta(hours=24)
history_cursor = scpsl_playerdata_collection.find(
{"timestamp": {"$gte": one_day_ago}},
{"_id": 0, "timestamp": 1, "players": 1}
).sort("timestamp", 1)
zero_start = None
filtered_times = []
filtered_players = []
for doc in history_cursor:
t = doc["timestamp"]
p = doc["players"]
if p == 0:
if zero_start is None:
zero_start = t
continue
else:
if zero_start is not None:
duration = (t - zero_start).total_seconds()
if duration > 20:
filtered_times.append(zero_start)
filtered_players.append(0)
filtered_times.append(t)
filtered_players.append(p)
zero_start = None
filtered_times.append(t)
filtered_players.append(p)
if zero_start is not None:
last_time = datetime.utcnow()
duration = (last_time - zero_start).total_seconds()
if duration > 20:
filtered_times.append(zero_start)
filtered_players.append(0)
filtered_times.append(last_time)
filtered_players.append(0)
fig, ax = plt.subplots(figsize=(10, 6))
if not filtered_times:
ax.text(0.5, 0.5, 'No player data available for the last 24 hours.',
horizontalalignment='center', verticalalignment='center', transform=ax.transAxes, fontsize=12, color='red')
ax.set_ylim(bottom=0, top=36)
else:
ax.plot(filtered_times, filtered_players, color='lime', linewidth=2)
max_players_in_history = max(filtered_players) if filtered_players else 0
if max_players_in_history > 0:
ax.set_ylim(bottom=0, top=max_players_in_history + 5)
else:
ax.set_ylim(bottom=0, top=36)
ax.set_title('SCP:SL Player Count (Letzte 24h)', color='white')
ax.set_xlabel('Zeit', color='white')
ax.set_ylabel('Spieleranzahl', color='white')
ax.set_facecolor('#1a1a1a')
fig.patch.set_facecolor('#1a1a1a')
ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
ax.xaxis.set_major_locator(mdates.HourLocator(interval=3))
fig.autofmt_xdate()
ax.tick_params(axis='x', colors='white')
ax.tick_params(axis='y', colors='white')
ax.yaxis.set_major_locator(plt.MaxNLocator(integer=True))
ax.grid(True, linestyle='--', alpha=0.5, color='#444444')
ax.spines['bottom'].set_color('white')
ax.spines['top'].set_color('white')
ax.spines['left'].set_color('white')
ax.spines['right'].set_color('white')
buffer = BytesIO()
plt.savefig(buffer, format='png', bbox_inches='tight', dpi=100)
buffer.seek(0)
plt.close(fig)
return send_file(buffer, mimetype='image/png')
# Voice-Channel Übersicht
@app.route("/vcs")
def vcs_overview():
# Beispiel: Channels können dynamisch sein, hier statisch
channels = [
{"id": "talk1", "name": "Teamtalk"},
{"id": "talk2", "name": "Privat"},
{"id": "talk3", "name": "Privat 2"}
]
return render_template("schisscord.html", channels=channels)
# Voice-Channel Join
@app.route("/join/<channel_id>")
def join_vc(channel_id):
if "username" not in session:
return redirect(url_for("login"))
return render_template("vc_room.html", channel_id=channel_id, username=session["username"])
# SocketIO: Nutzer tritt Channel bei
@socketio.on("join_channel")
def handle_join_channel(data):
channel = data.get("channel")
username = data.get("username")
join_room(channel)
emit("user_joined", {"username": username}, room=channel)
@socketio.on("leave_channel")
def handle_leave_channel(data):
channel = data.get("channel")
username = data.get("username")
leave_room(channel)
emit("user_left", {"username": username}, room=channel)
@socketio.on("signal")
def handle_signal(data):
channel = data.get("channel")
signal_data = data.get("signal")
sender = data.get("username")
emit("signal", {"signal": signal_data, "username": sender}, room=channel, include_self=False)
MONGO_URI = "mongodb+srv://MrSniff:5169Galaxy@chainsaw.d0shx.mongodb.net/"
client = MongoClient(MONGO_URI)
ETS2_DB_NAME = "ets2_license_db"
ETS2_COLLECTION_NAME = "licenses"
ets2_db = client[ETS2_DB_NAME]
ets2_licenses = ets2_db[ETS2_COLLECTION_NAME]
print(f"[DEBUG] Using DB: {ETS2_DB_NAME}, Collection: {ETS2_COLLECTION_NAME}")
# --- DLC Folder ---
DLC_FOLDER = "/var/www/admin_website/static/ets2_dlcs"
# --- License Check Endpoint ---
@app.route("/checklicense", methods=["GET"])
def check_license():
key = request.args.get("key")
user = request.args.get("user")
print(f"[DEBUG] /checklicense called with key='{key}' user='{user}'")
if not key or not user:
print("[DEBUG] Missing key or user")
return "invalid", 400
entry = ets2_licenses.find_one({"license_key": key})
print(f"[DEBUG] MongoDB entry found: {entry}")
if entry:
if not entry.get("windows_user"):
ets2_licenses.update_one({"license_key": key}, {"$set": {"windows_user": user}})
print(f"[DEBUG] Key '{key}' registered to user '{user}'")
return "valid"
elif entry["windows_user"] == user:
print(f"[DEBUG] Key '{key}' valid for user '{user}'")
return "valid"
else:
print(f"[DEBUG] Key '{key}' already used by another user '{entry['windows_user']}'")
return "invalid_user"
else:
print(f"[DEBUG] Key '{key}' not found in database")
return "invalid"
# --- DLC Download Endpoint ---
@app.route("/download", methods=["GET"])
def download_dlc():
key = request.headers.get("License-Key")
user = request.headers.get("Windows-User")
dlc_type = request.args.get("type", "all") # "all_map_dlcs" oder "all_dlcs"
print(f"[DEBUG] /download called with key='{key}', user='{user}', type='{dlc_type}'")
if not key or not user:
print("[DEBUG] Missing headers")
return "missing_headers", 400
entry = ets2_licenses.find_one({"license_key": key})
print(f"[DEBUG] MongoDB entry for download: {entry}")
if not entry or entry.get("windows_user") != user:
print(f"[DEBUG] Unauthorized access for key '{key}' user '{user}'")
return "unauthorized", 403
# Bestimme die Datei basierend auf DLC Typ
if dlc_type == "all_map_dlcs":
file_name = "all_map_dlcs.zip"
else:
file_name = "all_dlcs.zip"
file_path = os.path.join(DLC_FOLDER, file_name)
if not os.path.exists(file_path):
print(f"[DEBUG] File not found at {file_path}")
return "not_found", 404
print(f"[DEBUG] Sending file {file_path} to user '{user}'")
return send_file(file_path, as_attachment=True)
if __name__ == "__main__":
print("[DEBUG] Starting ETS2 DLC Server")
print(f"[DEBUG] Using DB: {ETS2_DB_NAME}, Collection: {ETS2_COLLECTION_NAME}")
socketio.run(app, debug=True, host='0.0.0.0', port=5000)