Astra OS Ver. 1.2:
- Vorbereitung für Prod HTTPS Usage
This commit is contained in:
parent
dce283d2aa
commit
1d6ad149b4
7
AstraOS/.env
Normal file
7
AstraOS/.env
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
SECRET_KEY='super-secret-key-that-you-should-change'
|
||||||
|
MONGO_URI='mongodb+srv://MrSniff:5169Galaxy@chainsaw.d0shx.mongodb.net/'
|
||||||
|
SSH_HOST='89.144.42.175'
|
||||||
|
SSH_USER='root'
|
||||||
|
SSH_PASS='iwkms@1812:('
|
||||||
|
VPNAPI_KEY='015ada9472664329affb2f26d7036d47'
|
||||||
|
|
||||||
|
|
@ -17,25 +17,18 @@ import requests
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import socket
|
import socket
|
||||||
import ssl
|
import ssl
|
||||||
|
from dotenv import load_dotenv
|
||||||
# Verwenden Sie eventlet für die Produktion
|
load_dotenv()
|
||||||
async_mode = "threading"
|
async_mode = "eventlet"
|
||||||
app = Flask(__name__,
|
app = Flask(__name__,
|
||||||
template_folder='Frontend/HTML',
|
template_folder='Frontend/HTML',
|
||||||
static_folder='Frontend')
|
static_folder='Frontend')
|
||||||
socketio = SocketIO(app, async_mode=async_mode)
|
socketio = SocketIO(app, async_mode=async_mode)
|
||||||
|
|
||||||
# Verzeichnis für hochgeladene Bots
|
|
||||||
BOT_UPLOAD_FOLDER = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Bots')
|
BOT_UPLOAD_FOLDER = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Bots')
|
||||||
if not os.path.exists(BOT_UPLOAD_FOLDER):
|
if not os.path.exists(BOT_UPLOAD_FOLDER):
|
||||||
os.makedirs(BOT_UPLOAD_FOLDER)
|
os.makedirs(BOT_UPLOAD_FOLDER)
|
||||||
|
|
||||||
app.config['BOT_UPLOAD_FOLDER'] = BOT_UPLOAD_FOLDER
|
app.config['BOT_UPLOAD_FOLDER'] = BOT_UPLOAD_FOLDER
|
||||||
|
|
||||||
# Dictionary zum Speichern laufender Bot-Prozesse
|
|
||||||
running_bots = {}
|
running_bots = {}
|
||||||
|
|
||||||
# Filter, um die GET /api/server_load und GET /api/system_status Anfragen aus den Logs auszublenden
|
|
||||||
class SuppressServerLoadFilter(logging.Filter):
|
class SuppressServerLoadFilter(logging.Filter):
|
||||||
def filter(self, record):
|
def filter(self, record):
|
||||||
msg = record.getMessage()
|
msg = record.getMessage()
|
||||||
|
|
@ -43,20 +36,14 @@ class SuppressServerLoadFilter(logging.Filter):
|
||||||
"GET /api/server_load" not in msg
|
"GET /api/server_load" not in msg
|
||||||
and "GET /api/system_status" not in msg
|
and "GET /api/system_status" not in msg
|
||||||
)
|
)
|
||||||
|
|
||||||
# Den Filter zum Werkzeug-Logger hinzufügen
|
|
||||||
log = logging.getLogger('werkzeug')
|
log = logging.getLogger('werkzeug')
|
||||||
log.addFilter(SuppressServerLoadFilter())
|
log.addFilter(SuppressServerLoadFilter())
|
||||||
|
app.secret_key = os.environ.get('SECRET_KEY', os.urandom(32))
|
||||||
|
|
||||||
app.secret_key = os.urandom(32)
|
|
||||||
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=8)
|
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=8)
|
||||||
app.config['SESSION_COOKIE_SECURE'] = True
|
app.config['SESSION_COOKIE_SECURE'] = True
|
||||||
app.config['SESSION_COOKIE_HTTPONLY'] = True
|
app.config['SESSION_COOKIE_HTTPONLY'] = True
|
||||||
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
|
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
|
||||||
|
|
||||||
csrf = CSRFProtect(app)
|
csrf = CSRFProtect(app)
|
||||||
|
|
||||||
@app.context_processor
|
@app.context_processor
|
||||||
def inject_utility_processor():
|
def inject_utility_processor():
|
||||||
maintenance_status = dashboard_collection.find_one({"key": "scheduled_maintenance"}) if dashboard_collection is not None else None
|
maintenance_status = dashboard_collection.find_one({"key": "scheduled_maintenance"}) if dashboard_collection is not None else None
|
||||||
|
|
@ -65,9 +52,8 @@ def inject_utility_processor():
|
||||||
timedelta=timedelta,
|
timedelta=timedelta,
|
||||||
maintenance_active=is_maintenance_active
|
maintenance_active=is_maintenance_active
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
client = MongoClient("mongodb+srv://MrSniff:5169Galaxy@chainsaw.d0shx.mongodb.net/")
|
client = MongoClient(os.environ.get("MONGO_URI"))
|
||||||
db = client["astra_os"]
|
db = client["astra_os"]
|
||||||
accounts_collection = db["accounts"]
|
accounts_collection = db["accounts"]
|
||||||
logs_collection = db["logs"]
|
logs_collection = db["logs"]
|
||||||
|
|
@ -76,7 +62,6 @@ try:
|
||||||
discord_bots_collection = db["discord_bots_data"]
|
discord_bots_collection = db["discord_bots_data"]
|
||||||
ip_analyzer_results_collection = db["ip_analyzer_results"]
|
ip_analyzer_results_collection = db["ip_analyzer_results"]
|
||||||
print("INFO: MongoDB-Verbindung erfolgreich hergestellt.")
|
print("INFO: MongoDB-Verbindung erfolgreich hergestellt.")
|
||||||
|
|
||||||
AVAILABLE_PERMISSIONS = [
|
AVAILABLE_PERMISSIONS = [
|
||||||
"view_dashboard",
|
"view_dashboard",
|
||||||
"view_user_management",
|
"view_user_management",
|
||||||
|
|
@ -89,7 +74,6 @@ try:
|
||||||
"manage_discord_bots",
|
"manage_discord_bots",
|
||||||
"view_ip_analyzer"
|
"view_ip_analyzer"
|
||||||
]
|
]
|
||||||
|
|
||||||
if roles_collection.count_documents({}) == 0:
|
if roles_collection.count_documents({}) == 0:
|
||||||
print("WARNUNG: Keine Rollen in der Datenbank gefunden. Erstelle Standard-Rollen...")
|
print("WARNUNG: Keine Rollen in der Datenbank gefunden. Erstelle Standard-Rollen...")
|
||||||
roles_collection.insert_many([
|
roles_collection.insert_many([
|
||||||
|
|
@ -105,7 +89,6 @@ try:
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
print("INFO: Standard-Rollen ('Owner', 'Benutzer') wurden erstellt.")
|
print("INFO: Standard-Rollen ('Owner', 'Benutzer') wurden erstellt.")
|
||||||
|
|
||||||
if accounts_collection.count_documents({}) == 0:
|
if accounts_collection.count_documents({}) == 0:
|
||||||
print("WARNUNG: Keine Benutzer in der Datenbank gefunden. Erstelle Standard-Admin-Benutzer...")
|
print("WARNUNG: Keine Benutzer in der Datenbank gefunden. Erstelle Standard-Admin-Benutzer...")
|
||||||
owner_role = roles_collection.find_one({"name": "Owner"})
|
owner_role = roles_collection.find_one({"name": "Owner"})
|
||||||
|
|
@ -117,8 +100,6 @@ try:
|
||||||
'creation_date': datetime.utcnow()
|
'creation_date': datetime.utcnow()
|
||||||
})
|
})
|
||||||
print("INFO: Standard-Admin-Benutzer ('admin', Passwort: '1234') mit Rolle 'Owner' wurde erstellt.")
|
print("INFO: Standard-Admin-Benutzer ('admin', Passwort: '1234') mit Rolle 'Owner' wurde erstellt.")
|
||||||
|
|
||||||
# Standard-Dashboard-Einträge anlegen, falls Collection leer
|
|
||||||
if dashboard_collection.count_documents({}) == 0:
|
if dashboard_collection.count_documents({}) == 0:
|
||||||
print("INFO: Leere dashboard_stats Collection - lege Standardstatistiken an.")
|
print("INFO: Leere dashboard_stats Collection - lege Standardstatistiken an.")
|
||||||
dashboard_collection.insert_many([
|
dashboard_collection.insert_many([
|
||||||
|
|
@ -130,7 +111,6 @@ try:
|
||||||
{"key": "last_user_created", "time": None, "by": None}
|
{"key": "last_user_created", "time": None, "by": None}
|
||||||
])
|
])
|
||||||
print("INFO: Standard Dashboard-Statistiken wurden erstellt.")
|
print("INFO: Standard Dashboard-Statistiken wurden erstellt.")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"FATAL: Verbindung zur MongoDB fehlgeschlagen: {e}")
|
print(f"FATAL: Verbindung zur MongoDB fehlgeschlagen: {e}")
|
||||||
db = None
|
db = None
|
||||||
|
|
@ -144,16 +124,13 @@ except Exception as e:
|
||||||
db = None
|
db = None
|
||||||
accounts_collection = None
|
accounts_collection = None
|
||||||
logs_collection = None
|
logs_collection = None
|
||||||
|
|
||||||
# MongoDB für Tickets (chainsaw_dc_data)
|
|
||||||
try:
|
try:
|
||||||
tickets_client = MongoClient("mongodb+srv://MrSniff:5169Galaxy@chainsaw.d0shx.mongodb.net/")
|
tickets_client = MongoClient(os.environ.get("MONGO_URI"))
|
||||||
tickets_db = tickets_client["chainsaw_dc_data"]
|
tickets_db = tickets_client["chainsaw_dc_data"]
|
||||||
tickets_collection = tickets_db["tickets"]
|
tickets_collection = tickets_db["tickets"]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
tickets_collection = None
|
tickets_collection = None
|
||||||
print(f"FATAL: Verbindung zu Tickets-DB fehlgeschlagen: {e}")
|
print(f"FATAL: Verbindung zu Tickets-DB fehlgeschlagen: {e}")
|
||||||
|
|
||||||
def get_user_permissions(user_id):
|
def get_user_permissions(user_id):
|
||||||
user = accounts_collection.find_one({'_id': ObjectId(user_id)})
|
user = accounts_collection.find_one({'_id': ObjectId(user_id)})
|
||||||
if not user or 'role_id' not in user:
|
if not user or 'role_id' not in user:
|
||||||
|
|
@ -162,26 +139,19 @@ def get_user_permissions(user_id):
|
||||||
if not role:
|
if not role:
|
||||||
return []
|
return []
|
||||||
return role.get('permissions', [])
|
return role.get('permissions', [])
|
||||||
|
|
||||||
def login_required(f):
|
def login_required(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
if 'user_id' not in session:
|
if 'user_id' not in session:
|
||||||
flash("Bitte melden Sie sich an, um diese Seite zu sehen.", "warning")
|
flash("Bitte melden Sie sich an, um diese Seite zu sehen.", "warning")
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
# Berechtigungen bei jeder Anfrage aktualisieren, um Änderungen sofort zu übernehmen
|
|
||||||
session['permissions'] = get_user_permissions(session['user_id'])
|
session['permissions'] = get_user_permissions(session['user_id'])
|
||||||
|
|
||||||
# Wartungsmodus-Check
|
|
||||||
maintenance_status = dashboard_collection.find_one({"key": "scheduled_maintenance"})
|
maintenance_status = dashboard_collection.find_one({"key": "scheduled_maintenance"})
|
||||||
if maintenance_status and maintenance_status.get('enabled', False):
|
if maintenance_status and maintenance_status.get('enabled', False):
|
||||||
if 'bypass_maintenance' not in session['permissions']:
|
if 'bypass_maintenance' not in session['permissions']:
|
||||||
return redirect(url_for('maintenance'))
|
return redirect(url_for('maintenance'))
|
||||||
|
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
def permission_required(permission):
|
def permission_required(permission):
|
||||||
def decorator(f):
|
def decorator(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
|
|
@ -195,37 +165,28 @@ def permission_required(permission):
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
return decorated_function
|
return decorated_function
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def home():
|
def home():
|
||||||
if 'user_id' in session:
|
if 'user_id' in session:
|
||||||
return redirect(url_for('dashboard'))
|
return redirect(url_for('dashboard'))
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
@app.route('/login', methods=['GET', 'POST'])
|
@app.route('/login', methods=['GET', 'POST'])
|
||||||
def login():
|
def login():
|
||||||
if 'user_id' in session:
|
if 'user_id' in session:
|
||||||
return redirect(url_for('dashboard'))
|
return redirect(url_for('dashboard'))
|
||||||
|
|
||||||
# Wartungsmodus-Check vor dem Login-Versuch
|
|
||||||
maintenance_status = dashboard_collection.find_one({"key": "scheduled_maintenance"}) if dashboard_collection is not None else None
|
maintenance_status = dashboard_collection.find_one({"key": "scheduled_maintenance"}) if dashboard_collection is not None else None
|
||||||
if maintenance_status and maintenance_status.get('enabled', False):
|
if maintenance_status and maintenance_status.get('enabled', False):
|
||||||
return redirect(url_for('maintenance'))
|
return redirect(url_for('maintenance'))
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
username = request.form.get('username')
|
username = request.form.get('username')
|
||||||
password = request.form.get('password')
|
password = request.form.get('password')
|
||||||
|
|
||||||
if not username or not password:
|
if not username or not password:
|
||||||
flash("Benutzername und Passwort sind erforderlich.", "danger")
|
flash("Benutzername und Passwort sind erforderlich.", "danger")
|
||||||
return render_template('login.html')
|
return render_template('login.html')
|
||||||
|
|
||||||
if accounts_collection is None:
|
if accounts_collection is None:
|
||||||
flash("Datenbankverbindung nicht verfügbar. Bitte versuchen Sie es später erneut.", "danger")
|
flash("Datenbankverbindung nicht verfügbar. Bitte versuchen Sie es später erneut.", "danger")
|
||||||
return render_template('login.html'), 500
|
return render_template('login.html'), 500
|
||||||
|
|
||||||
user = accounts_collection.find_one({'username': username})
|
user = accounts_collection.find_one({'username': username})
|
||||||
|
|
||||||
if user and check_password_hash(user['password'], password):
|
if user and check_password_hash(user['password'], password):
|
||||||
session.permanent = True
|
session.permanent = True
|
||||||
session['user_id'] = str(user['_id'])
|
session['user_id'] = str(user['_id'])
|
||||||
|
|
@ -236,22 +197,17 @@ def login():
|
||||||
else:
|
else:
|
||||||
flash("Ungültiger Benutzername oder Passwort.", "danger")
|
flash("Ungültiger Benutzername oder Passwort.", "danger")
|
||||||
return render_template('login.html')
|
return render_template('login.html')
|
||||||
|
|
||||||
return render_template('login.html')
|
return render_template('login.html')
|
||||||
|
|
||||||
@app.route('/api/server_load')
|
@app.route('/api/server_load')
|
||||||
@login_required
|
@login_required
|
||||||
def api_server_load():
|
def api_server_load():
|
||||||
# SSH zu 89.144.42.175, user root, pw iwkms@1812:(
|
|
||||||
try:
|
try:
|
||||||
ssh = paramiko.SSHClient()
|
ssh = paramiko.SSHClient()
|
||||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
# Timeout zur Verbindung hinzugefügt, um Hängenbleiben zu verhindern
|
ssh.connect(os.environ.get('SSH_HOST'), username=os.environ.get('SSH_USER'), password=os.environ.get('SSH_PASS'), timeout=10)
|
||||||
ssh.connect("89.144.42.175", username="root", password="iwkms@1812:(", timeout=10)
|
|
||||||
stdin, stdout, stderr = ssh.exec_command("top -bn1 | grep 'Cpu(s)'")
|
stdin, stdout, stderr = ssh.exec_command("top -bn1 | grep 'Cpu(s)'")
|
||||||
output = stdout.read().decode()
|
output = stdout.read().decode()
|
||||||
ssh.close()
|
ssh.close()
|
||||||
# Parse CPU usage
|
|
||||||
import re
|
import re
|
||||||
match = re.search(r"(\d+\.\d+)\s*id", output)
|
match = re.search(r"(\d+\.\d+)\s*id", output)
|
||||||
if match:
|
if match:
|
||||||
|
|
@ -269,22 +225,17 @@ def api_server_load():
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Allgemeiner Fehler bei Server-Auslastung: {e}")
|
print(f"Allgemeiner Fehler bei Server-Auslastung: {e}")
|
||||||
return jsonify({"percent": 0, "status": "Nicht erreichbar"})
|
return jsonify({"percent": 0, "status": "Nicht erreichbar"})
|
||||||
|
|
||||||
@app.route('/api/system_status')
|
@app.route('/api/system_status')
|
||||||
@login_required
|
@login_required
|
||||||
def api_system_status():
|
def api_system_status():
|
||||||
# Wartungsstatus prüfen
|
|
||||||
maintenance_status = dashboard_collection.find_one({"key": "scheduled_maintenance"}) if dashboard_collection is not None else None
|
maintenance_status = dashboard_collection.find_one({"key": "scheduled_maintenance"}) if dashboard_collection is not None else None
|
||||||
maintenance_active = maintenance_status and maintenance_status.get('enabled', False)
|
maintenance_active = maintenance_status and maintenance_status.get('enabled', False)
|
||||||
discord_bots_maintenance = maintenance_status.get('discord_bots_maintenance', False) if maintenance_status else False
|
discord_bots_maintenance = maintenance_status.get('discord_bots_maintenance', False) if maintenance_status else False
|
||||||
|
|
||||||
# SCP:SL Server Status (UDP-Ping statt TCP)
|
|
||||||
scpsl_status = "offline"
|
scpsl_status = "offline"
|
||||||
try:
|
try:
|
||||||
udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
udp_sock.settimeout(2)
|
udp_sock.settimeout(2)
|
||||||
udp_sock.sendto(b"\x00", ("89.144.42.175", 7101))
|
udp_sock.sendto(b"\x00", ("89.144.42.175", 7101))
|
||||||
# Wenn keine Exception, dann ist der Port erreichbar (Server antwortet nicht, aber Port offen)
|
|
||||||
scpsl_status = "online"
|
scpsl_status = "online"
|
||||||
udp_sock.close()
|
udp_sock.close()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -292,8 +243,6 @@ def api_system_status():
|
||||||
scpsl_status = "offline"
|
scpsl_status = "offline"
|
||||||
if maintenance_active:
|
if maintenance_active:
|
||||||
scpsl_status = "wartung"
|
scpsl_status = "wartung"
|
||||||
|
|
||||||
# Gamepanel Status
|
|
||||||
gamepanel_status = "offline"
|
gamepanel_status = "offline"
|
||||||
try:
|
try:
|
||||||
resp = requests.get("https://gamepanel.mrsniff.de", timeout=3)
|
resp = requests.get("https://gamepanel.mrsniff.de", timeout=3)
|
||||||
|
|
@ -303,15 +252,10 @@ def api_system_status():
|
||||||
gamepanel_status = "offline"
|
gamepanel_status = "offline"
|
||||||
if maintenance_active:
|
if maintenance_active:
|
||||||
gamepanel_status = "wartung"
|
gamepanel_status = "wartung"
|
||||||
|
|
||||||
# Discord Bots Status (online/wartung)
|
|
||||||
discord_bots_status = "wartung" if discord_bots_maintenance or maintenance_active else "online"
|
discord_bots_status = "wartung" if discord_bots_maintenance or maintenance_active else "online"
|
||||||
|
|
||||||
# Datenbank Status
|
|
||||||
db_status = "online" if db is not None else "offline"
|
db_status = "online" if db is not None else "offline"
|
||||||
if maintenance_active:
|
if maintenance_active:
|
||||||
db_status = "wartung"
|
db_status = "wartung"
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"scpsl": scpsl_status,
|
"scpsl": scpsl_status,
|
||||||
"gamepanel": gamepanel_status,
|
"gamepanel": gamepanel_status,
|
||||||
|
|
@ -319,41 +263,28 @@ def api_system_status():
|
||||||
"database": db_status,
|
"database": db_status,
|
||||||
"maintenance": maintenance_active
|
"maintenance": maintenance_active
|
||||||
})
|
})
|
||||||
|
|
||||||
@app.route('/status')
|
@app.route('/status')
|
||||||
@login_required
|
@login_required
|
||||||
def status_page():
|
def status_page():
|
||||||
# Wartungsstatus prüfen
|
|
||||||
maintenance_status = dashboard_collection.find_one({"key": "scheduled_maintenance"}) if dashboard_collection is not None else None
|
maintenance_status = dashboard_collection.find_one({"key": "scheduled_maintenance"}) if dashboard_collection is not None else None
|
||||||
maintenance_active = maintenance_status and maintenance_status.get('enabled', False)
|
maintenance_active = maintenance_status and maintenance_status.get('enabled', False)
|
||||||
return render_template('status_page.html', active_page='status_page', maintenance_active=maintenance_active)
|
return render_template('status_page.html', active_page='status_page', maintenance_active=maintenance_active)
|
||||||
|
|
||||||
@app.route('/dashboard')
|
@app.route('/dashboard')
|
||||||
@login_required
|
@login_required
|
||||||
def dashboard():
|
def dashboard():
|
||||||
# Aktive Benutzer
|
|
||||||
user_count = accounts_collection.count_documents({}) if accounts_collection is not None else 1
|
user_count = accounts_collection.count_documents({}) if accounts_collection is not None else 1
|
||||||
|
|
||||||
# Letzte Benutzererstellung
|
|
||||||
last_user = accounts_collection.find_one(sort=[("creation_date", -1)]) if accounts_collection is not None else None
|
last_user = accounts_collection.find_one(sort=[("creation_date", -1)]) if accounts_collection is not None else None
|
||||||
last_user_time = last_user["creation_date"] if last_user and "creation_date" in last_user else None
|
last_user_time = last_user["creation_date"] if last_user and "creation_date" in last_user else None
|
||||||
last_user_by = last_user["username"] if last_user and "username" in last_user else None
|
last_user_by = last_user["username"] if last_user and "username" in last_user else None
|
||||||
|
|
||||||
# Offene Tickets
|
|
||||||
open_tickets = 0
|
open_tickets = 0
|
||||||
if tickets_collection is not None:
|
if tickets_collection is not None:
|
||||||
open_tickets = tickets_collection.count_documents({"status": "open"})
|
open_tickets = tickets_collection.count_documents({"status": "open"})
|
||||||
|
|
||||||
# Server-Auslastung (wird per AJAX geladen, hier nur Platzhalter)
|
|
||||||
server_load = {"percent": 0, "status": "Lädt..."}
|
server_load = {"percent": 0, "status": "Lädt..."}
|
||||||
|
|
||||||
# Systemstatus per API holen (wie Status-Seite)
|
|
||||||
system_status = {"status": "Online"}
|
system_status = {"status": "Online"}
|
||||||
try:
|
try:
|
||||||
resp = requests.get(request.url_root.rstrip('/') + '/api/system_status', cookies=request.cookies, timeout=2)
|
resp = requests.get(request.url_root.rstrip('/') + '/api/system_status', cookies=request.cookies, timeout=2)
|
||||||
if resp.status_code == 200:
|
if resp.status_code == 200:
|
||||||
data = resp.json()
|
data = resp.json()
|
||||||
# Priorität: Wartung > Wartungsmodus > Offline > Online
|
|
||||||
if data.get("maintenance"):
|
if data.get("maintenance"):
|
||||||
system_status = {"status": "Online (Wartungsarbeiten)"}
|
system_status = {"status": "Online (Wartungsarbeiten)"}
|
||||||
elif any(data.get(k) == "wartung" for k in ["scpsl", "gamepanel", "discord_bots", "database"]):
|
elif any(data.get(k) == "wartung" for k in ["scpsl", "gamepanel", "discord_bots", "database"]):
|
||||||
|
|
@ -366,8 +297,6 @@ def dashboard():
|
||||||
system_status = {"status": "Offline"}
|
system_status = {"status": "Offline"}
|
||||||
except Exception:
|
except Exception:
|
||||||
system_status = {"status": "Offline"}
|
system_status = {"status": "Offline"}
|
||||||
|
|
||||||
# Geplante Wartung (aus dashboard_stats falls vorhanden)
|
|
||||||
scheduled_maintenance = dashboard_collection.find_one({"key": "scheduled_maintenance"}) if dashboard_collection is not None else None
|
scheduled_maintenance = dashboard_collection.find_one({"key": "scheduled_maintenance"}) if dashboard_collection is not None else None
|
||||||
def _dt_to_str(v):
|
def _dt_to_str(v):
|
||||||
if v is None:
|
if v is None:
|
||||||
|
|
@ -378,7 +307,6 @@ def dashboard():
|
||||||
return str(v)
|
return str(v)
|
||||||
maint = scheduled_maintenance or {"time": None, "status": "Keine", "duration_minutes": None}
|
maint = scheduled_maintenance or {"time": None, "status": "Keine", "duration_minutes": None}
|
||||||
maint["time_str"] = _dt_to_str(maint.get("time"))
|
maint["time_str"] = _dt_to_str(maint.get("time"))
|
||||||
|
|
||||||
stats = {
|
stats = {
|
||||||
"active_users": {"value": user_count, "percent": 0},
|
"active_users": {"value": user_count, "percent": 0},
|
||||||
"server_load": server_load,
|
"server_load": server_load,
|
||||||
|
|
@ -391,18 +319,13 @@ def dashboard():
|
||||||
"time_str": _dt_to_str(last_user_time)
|
"time_str": _dt_to_str(last_user_time)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return render_template('dashboard.html', stats=stats, active_page='dashboard')
|
return render_template('dashboard.html', stats=stats, active_page='dashboard')
|
||||||
|
|
||||||
@app.route('/logout')
|
@app.route('/logout')
|
||||||
@login_required
|
@login_required
|
||||||
def logout():
|
def logout():
|
||||||
session.clear()
|
session.clear()
|
||||||
flash("Sie wurden erfolgreich abgemeldet.", "info")
|
flash("Sie wurden erfolgreich abgemeldet.", "info")
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
# --- User Management ---
|
|
||||||
|
|
||||||
@app.route('/user_management')
|
@app.route('/user_management')
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('view_user_management')
|
@permission_required('view_user_management')
|
||||||
|
|
@ -410,17 +333,11 @@ def user_management():
|
||||||
if accounts_collection is None or roles_collection is None:
|
if accounts_collection is None or roles_collection is None:
|
||||||
flash("Datenbankverbindung nicht verfügbar.", "danger")
|
flash("Datenbankverbindung nicht verfügbar.", "danger")
|
||||||
return redirect(url_for('dashboard'))
|
return redirect(url_for('dashboard'))
|
||||||
|
|
||||||
users_list = list(accounts_collection.find())
|
users_list = list(accounts_collection.find())
|
||||||
roles_list = list(roles_collection.find())
|
roles_list = list(roles_collection.find())
|
||||||
|
|
||||||
# Konvertiere ObjectId in String für die JSON-Serialisierung in roles_list
|
|
||||||
for role in roles_list:
|
for role in roles_list:
|
||||||
role['_id'] = str(role['_id'])
|
role['_id'] = str(role['_id'])
|
||||||
|
|
||||||
role_map = {role['_id']: role['name'] for role in roles_list}
|
role_map = {role['_id']: role['name'] for role in roles_list}
|
||||||
|
|
||||||
# Daten für das Alpine.js Modal vorbereiten (ohne Datum)
|
|
||||||
users_for_modal = {}
|
users_for_modal = {}
|
||||||
for user in users_list:
|
for user in users_list:
|
||||||
user_id_str = str(user['_id'])
|
user_id_str = str(user['_id'])
|
||||||
|
|
@ -429,18 +346,14 @@ def user_management():
|
||||||
'username': user['username'],
|
'username': user['username'],
|
||||||
'role_id': str(user.get('role_id')) if user.get('role_id') else None
|
'role_id': str(user.get('role_id')) if user.get('role_id') else None
|
||||||
}
|
}
|
||||||
|
|
||||||
# Erstelle eine separate Liste für die Anzeige in der Tabelle, die das Datum enthält
|
|
||||||
users_for_table = list(accounts_collection.find())
|
users_for_table = list(accounts_collection.find())
|
||||||
for user in users_for_table:
|
for user in users_for_table:
|
||||||
user['role_name'] = role_map.get(str(user.get('role_id')), 'Unbekannt')
|
user['role_name'] = role_map.get(str(user.get('role_id')), 'Unbekannt')
|
||||||
|
|
||||||
return render_template('user_management.html',
|
return render_template('user_management.html',
|
||||||
users_for_modal=users_for_modal,
|
users_for_modal=users_for_modal,
|
||||||
users_for_table=users_for_table,
|
users_for_table=users_for_table,
|
||||||
roles=roles_list,
|
roles=roles_list,
|
||||||
active_page='user_management')
|
active_page='user_management')
|
||||||
|
|
||||||
@app.route('/add_user', methods=['POST'])
|
@app.route('/add_user', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('manage_users')
|
@permission_required('manage_users')
|
||||||
|
|
@ -448,19 +361,15 @@ def add_user():
|
||||||
if accounts_collection is None:
|
if accounts_collection is None:
|
||||||
flash("Datenbankverbindung nicht verfügbar.", "danger")
|
flash("Datenbankverbindung nicht verfügbar.", "danger")
|
||||||
return redirect(url_for('user_management'))
|
return redirect(url_for('user_management'))
|
||||||
|
|
||||||
username = request.form.get('username')
|
username = request.form.get('username')
|
||||||
password = request.form.get('password')
|
password = request.form.get('password')
|
||||||
role_id = request.form.get('role')
|
role_id = request.form.get('role')
|
||||||
|
|
||||||
if not username or not password or not role_id:
|
if not username or not password or not role_id:
|
||||||
flash("Alle Felder sind erforderlich.", "danger")
|
flash("Alle Felder sind erforderlich.", "danger")
|
||||||
return redirect(url_for('user_management'))
|
return redirect(url_for('user_management'))
|
||||||
|
|
||||||
if accounts_collection.find_one({'username': username}):
|
if accounts_collection.find_one({'username': username}):
|
||||||
flash("Ein Benutzer mit diesem Namen existiert bereits.", "danger")
|
flash("Ein Benutzer mit diesem Namen existiert bereits.", "danger")
|
||||||
return redirect(url_for('user_management'))
|
return redirect(url_for('user_management'))
|
||||||
|
|
||||||
hashed_password = generate_password_hash(password)
|
hashed_password = generate_password_hash(password)
|
||||||
accounts_collection.insert_one({
|
accounts_collection.insert_one({
|
||||||
'username': username,
|
'username': username,
|
||||||
|
|
@ -468,7 +377,6 @@ def add_user():
|
||||||
'role_id': ObjectId(role_id),
|
'role_id': ObjectId(role_id),
|
||||||
'creation_date': datetime.utcnow()
|
'creation_date': datetime.utcnow()
|
||||||
})
|
})
|
||||||
# Dashboard-Statistik aktualisieren
|
|
||||||
if dashboard_collection is not None:
|
if dashboard_collection is not None:
|
||||||
dashboard_collection.update_one(
|
dashboard_collection.update_one(
|
||||||
{"key": "last_user_created"},
|
{"key": "last_user_created"},
|
||||||
|
|
@ -478,7 +386,6 @@ def add_user():
|
||||||
}},
|
}},
|
||||||
upsert=True
|
upsert=True
|
||||||
)
|
)
|
||||||
# Aktive Benutzer aktualisieren
|
|
||||||
user_count = accounts_collection.count_documents({})
|
user_count = accounts_collection.count_documents({})
|
||||||
dashboard_collection.update_one(
|
dashboard_collection.update_one(
|
||||||
{"key": "active_users"},
|
{"key": "active_users"},
|
||||||
|
|
@ -487,7 +394,6 @@ def add_user():
|
||||||
)
|
)
|
||||||
flash(f"Benutzer '{username}' wurde erfolgreich erstellt.", "success")
|
flash(f"Benutzer '{username}' wurde erfolgreich erstellt.", "success")
|
||||||
return redirect(url_for('user_management'))
|
return redirect(url_for('user_management'))
|
||||||
|
|
||||||
@app.route('/edit_user/<user_id>', methods=['POST'])
|
@app.route('/edit_user/<user_id>', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('manage_users')
|
@permission_required('manage_users')
|
||||||
|
|
@ -495,38 +401,28 @@ def edit_user(user_id):
|
||||||
if accounts_collection is None:
|
if accounts_collection is None:
|
||||||
flash("Datenbankverbindung nicht verfügbar.", "danger")
|
flash("Datenbankverbindung nicht verfügbar.", "danger")
|
||||||
return redirect(url_for('user_management'))
|
return redirect(url_for('user_management'))
|
||||||
|
|
||||||
username = request.form.get('username')
|
username = request.form.get('username')
|
||||||
password = request.form.get('password')
|
password = request.form.get('password')
|
||||||
role_id = request.form.get('role')
|
role_id = request.form.get('role')
|
||||||
|
|
||||||
if not username or not role_id:
|
if not username or not role_id:
|
||||||
flash("Benutzername und Rolle sind erforderlich.", "danger")
|
flash("Benutzername und Rolle sind erforderlich.", "danger")
|
||||||
return redirect(url_for('user_management'))
|
return redirect(url_for('user_management'))
|
||||||
|
|
||||||
# Überprüfen, ob der neue Benutzername bereits von einem anderen Benutzer verwendet wird
|
|
||||||
existing_user = accounts_collection.find_one({'username': username})
|
existing_user = accounts_collection.find_one({'username': username})
|
||||||
if existing_user and str(existing_user['_id']) != user_id:
|
if existing_user and str(existing_user['_id']) != user_id:
|
||||||
flash("Ein anderer Benutzer mit diesem Namen existiert bereits.", "danger")
|
flash("Ein anderer Benutzer mit diesem Namen existiert bereits.", "danger")
|
||||||
return redirect(url_for('user_management'))
|
return redirect(url_for('user_management'))
|
||||||
|
|
||||||
update_data = {
|
update_data = {
|
||||||
'username': username,
|
'username': username,
|
||||||
'role_id': ObjectId(role_id)
|
'role_id': ObjectId(role_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
# Passwort nur aktualisieren, wenn ein neues eingegeben wurde
|
|
||||||
if password:
|
if password:
|
||||||
update_data['password'] = generate_password_hash(password)
|
update_data['password'] = generate_password_hash(password)
|
||||||
|
|
||||||
accounts_collection.update_one(
|
accounts_collection.update_one(
|
||||||
{'_id': ObjectId(user_id)},
|
{'_id': ObjectId(user_id)},
|
||||||
{'$set': update_data}
|
{'$set': update_data}
|
||||||
)
|
)
|
||||||
|
|
||||||
flash(f"Benutzer '{username}' wurde erfolgreich aktualisiert.", "success")
|
flash(f"Benutzer '{username}' wurde erfolgreich aktualisiert.", "success")
|
||||||
return redirect(url_for('user_management'))
|
return redirect(url_for('user_management'))
|
||||||
|
|
||||||
@app.route('/delete_user/<user_id>', methods=['POST'])
|
@app.route('/delete_user/<user_id>', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('manage_users')
|
@permission_required('manage_users')
|
||||||
|
|
@ -534,17 +430,12 @@ def delete_user(user_id):
|
||||||
if accounts_collection is None:
|
if accounts_collection is None:
|
||||||
flash("Datenbankverbindung nicht verfügbar.", "danger")
|
flash("Datenbankverbindung nicht verfügbar.", "danger")
|
||||||
return redirect(url_for('user_management'))
|
return redirect(url_for('user_management'))
|
||||||
|
|
||||||
if session.get('user_id') == user_id:
|
if session.get('user_id') == user_id:
|
||||||
flash("Sie können sich nicht selbst löschen.", "danger")
|
flash("Sie können sich nicht selbst löschen.", "danger")
|
||||||
return redirect(url_for('user_management'))
|
return redirect(url_for('user_management'))
|
||||||
|
|
||||||
accounts_collection.delete_one({'_id': ObjectId(user_id)})
|
accounts_collection.delete_one({'_id': ObjectId(user_id)})
|
||||||
flash("Benutzer wurde erfolgreich gelöscht.", "success")
|
flash("Benutzer wurde erfolgreich gelöscht.", "success")
|
||||||
return redirect(url_for('user_management'))
|
return redirect(url_for('user_management'))
|
||||||
|
|
||||||
# --- Role Management ---
|
|
||||||
|
|
||||||
@app.route('/role_management')
|
@app.route('/role_management')
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('view_role_management')
|
@permission_required('view_role_management')
|
||||||
|
|
@ -552,29 +443,22 @@ def role_management():
|
||||||
if roles_collection is None:
|
if roles_collection is None:
|
||||||
flash("Datenbankverbindung nicht verfügbar.", "danger")
|
flash("Datenbankverbindung nicht verfügbar.", "danger")
|
||||||
return redirect(url_for('dashboard'))
|
return redirect(url_for('dashboard'))
|
||||||
|
|
||||||
roles = list(roles_collection.find())
|
roles = list(roles_collection.find())
|
||||||
# Konvertiere ObjectId in String für die JSON-Serialisierung im Template
|
|
||||||
for role in roles:
|
for role in roles:
|
||||||
role['_id'] = str(role['_id'])
|
role['_id'] = str(role['_id'])
|
||||||
|
|
||||||
return render_template('role_management.html', roles=roles, available_permissions=AVAILABLE_PERMISSIONS, active_page='role_management')
|
return render_template('role_management.html', roles=roles, available_permissions=AVAILABLE_PERMISSIONS, active_page='role_management')
|
||||||
|
|
||||||
@app.route('/add_role', methods=['POST'])
|
@app.route('/add_role', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('manage_roles')
|
@permission_required('manage_roles')
|
||||||
def add_role():
|
def add_role():
|
||||||
role_name = request.form.get('role_name')
|
role_name = request.form.get('role_name')
|
||||||
permissions = request.form.getlist('permissions')
|
permissions = request.form.getlist('permissions')
|
||||||
|
|
||||||
if not role_name:
|
if not role_name:
|
||||||
flash("Rollenname ist erforderlich.", "danger")
|
flash("Rollenname ist erforderlich.", "danger")
|
||||||
return redirect(url_for('role_management'))
|
return redirect(url_for('role_management'))
|
||||||
|
|
||||||
if roles_collection.find_one({'name': role_name}):
|
if roles_collection.find_one({'name': role_name}):
|
||||||
flash("Eine Rolle mit diesem Namen existiert bereits.", "danger")
|
flash("Eine Rolle mit diesem Namen existiert bereits.", "danger")
|
||||||
return redirect(url_for('role_management'))
|
return redirect(url_for('role_management'))
|
||||||
|
|
||||||
roles_collection.insert_one({
|
roles_collection.insert_one({
|
||||||
"name": role_name,
|
"name": role_name,
|
||||||
"permissions": permissions,
|
"permissions": permissions,
|
||||||
|
|
@ -582,25 +466,21 @@ def add_role():
|
||||||
})
|
})
|
||||||
flash(f"Rolle '{role_name}' wurde erstellt.", "success")
|
flash(f"Rolle '{role_name}' wurde erstellt.", "success")
|
||||||
return redirect(url_for('role_management'))
|
return redirect(url_for('role_management'))
|
||||||
|
|
||||||
@app.route('/edit_role/<role_id>', methods=['POST'])
|
@app.route('/edit_role/<role_id>', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('manage_roles')
|
@permission_required('manage_roles')
|
||||||
def edit_role(role_id):
|
def edit_role(role_id):
|
||||||
role_name = request.form.get('role_name')
|
role_name = request.form.get('role_name')
|
||||||
permissions = request.form.getlist('permissions')
|
permissions = request.form.getlist('permissions')
|
||||||
|
|
||||||
if not role_name:
|
if not role_name:
|
||||||
flash("Rollenname ist erforderlich.", "danger")
|
flash("Rollenname ist erforderlich.", "danger")
|
||||||
return redirect(url_for('role_management'))
|
return redirect(url_for('role_management'))
|
||||||
|
|
||||||
roles_collection.update_one(
|
roles_collection.update_one(
|
||||||
{'_id': ObjectId(role_id)},
|
{'_id': ObjectId(role_id)},
|
||||||
{'$set': {'name': role_name, 'permissions': permissions}}
|
{'$set': {'name': role_name, 'permissions': permissions}}
|
||||||
)
|
)
|
||||||
flash(f"Rolle '{role_name}' wurde aktualisiert.", "success")
|
flash(f"Rolle '{role_name}' wurde aktualisiert.", "success")
|
||||||
return redirect(url_for('role_management'))
|
return redirect(url_for('role_management'))
|
||||||
|
|
||||||
@app.route('/delete_role/<role_id>', methods=['POST'])
|
@app.route('/delete_role/<role_id>', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('manage_roles')
|
@permission_required('manage_roles')
|
||||||
|
|
@ -609,12 +489,9 @@ def delete_role(role_id):
|
||||||
if role and not role.get('is_deletable', True):
|
if role and not role.get('is_deletable', True):
|
||||||
flash("Diese Rolle kann nicht gelöscht werden.", "danger")
|
flash("Diese Rolle kann nicht gelöscht werden.", "danger")
|
||||||
return redirect(url_for('role_management'))
|
return redirect(url_for('role_management'))
|
||||||
|
|
||||||
roles_collection.delete_one({'_id': ObjectId(role_id)})
|
roles_collection.delete_one({'_id': ObjectId(role_id)})
|
||||||
flash("Rolle wurde gelöscht.", "success")
|
flash("Rolle wurde gelöscht.", "success")
|
||||||
return redirect(url_for('role_management'))
|
return redirect(url_for('role_management'))
|
||||||
|
|
||||||
# --- IP Analyzer ---
|
|
||||||
@app.route('/ip_analyzer', methods=['GET', 'POST'])
|
@app.route('/ip_analyzer', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('view_ip_analyzer')
|
@permission_required('view_ip_analyzer')
|
||||||
|
|
@ -622,29 +499,23 @@ def ip_analyzer():
|
||||||
ip_data = None
|
ip_data = None
|
||||||
error = None
|
error = None
|
||||||
similar_ips = []
|
similar_ips = []
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
ip_address = request.form.get("ip_address")
|
ip_address = request.form.get("ip_address")
|
||||||
is_cheater = request.form.get("is_cheater") == "on"
|
is_cheater = request.form.get("is_cheater") == "on"
|
||||||
steam_id = request.form.get("steam_id") if is_cheater else None
|
steam_id = request.form.get("steam_id") if is_cheater else None
|
||||||
|
|
||||||
if not ip_address:
|
if not ip_address:
|
||||||
error = "Bitte eine IP-Adresse eingeben."
|
error = "Bitte eine IP-Adresse eingeben."
|
||||||
elif is_cheater and not steam_id:
|
elif is_cheater and not steam_id:
|
||||||
error = "SteamID ist erforderlich, wenn die IP als Cheater markiert wird."
|
error = "SteamID ist erforderlich, wenn die IP als Cheater markiert wird."
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
api_key = "015ada9472664329affb2f26d7036d47" # API-Schlüssel aus altem Code
|
api_key = os.environ.get('VPNAPI_KEY')
|
||||||
response = requests.get(f"https://vpnapi.io/api/{ip_address}?key={api_key}", timeout=5)
|
response = requests.get(f"https://vpnapi.io/api/{ip_address}?key={api_key}", timeout=5)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
ip_data = response.json()
|
ip_data = response.json()
|
||||||
|
|
||||||
# Sicherstellen, dass verschachtelte Objekte existieren
|
|
||||||
ip_data.setdefault('security', {})
|
ip_data.setdefault('security', {})
|
||||||
ip_data.setdefault('location', {})
|
ip_data.setdefault('location', {})
|
||||||
ip_data.setdefault('network', {})
|
ip_data.setdefault('network', {})
|
||||||
|
|
||||||
# Ähnliche IPs finden
|
|
||||||
similar_ips = list(ip_analyzer_results_collection.find({
|
similar_ips = list(ip_analyzer_results_collection.find({
|
||||||
"$or": [
|
"$or": [
|
||||||
{"ip": ip_address},
|
{"ip": ip_address},
|
||||||
|
|
@ -652,8 +523,6 @@ def ip_analyzer():
|
||||||
{"data.network.autonomous_system_number": ip_data.get('network', {}).get('autonomous_system_number')}
|
{"data.network.autonomous_system_number": ip_data.get('network', {}).get('autonomous_system_number')}
|
||||||
]
|
]
|
||||||
}))
|
}))
|
||||||
|
|
||||||
# Neuen Eintrag speichern
|
|
||||||
ip_analyzer_results_collection.insert_one({
|
ip_analyzer_results_collection.insert_one({
|
||||||
"ip": ip_address,
|
"ip": ip_address,
|
||||||
"is_cheater": is_cheater,
|
"is_cheater": is_cheater,
|
||||||
|
|
@ -669,51 +538,36 @@ def ip_analyzer():
|
||||||
error = f"Fehler bei der Verbindung zur API: {e}"
|
error = f"Fehler bei der Verbindung zur API: {e}"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error = f"Ein unerwarteter Fehler ist aufgetreten: {e}"
|
error = f"Ein unerwarteter Fehler ist aufgetreten: {e}"
|
||||||
|
|
||||||
return render_template('ip_analyzer.html',
|
return render_template('ip_analyzer.html',
|
||||||
active_page='ip_analyzer',
|
active_page='ip_analyzer',
|
||||||
ip_data=ip_data,
|
ip_data=ip_data,
|
||||||
error=error,
|
error=error,
|
||||||
similar_ips=similar_ips)
|
similar_ips=similar_ips)
|
||||||
|
|
||||||
|
|
||||||
# --- Discord Bot Management ---
|
|
||||||
|
|
||||||
def stream_bot_output(bot_id, process):
|
def stream_bot_output(bot_id, process):
|
||||||
"""Liest stdout/stderr eines Prozesses, speichert den Verlauf und sendet es via SocketIO."""
|
|
||||||
def stream_to_socket(pipe, log_type):
|
def stream_to_socket(pipe, log_type):
|
||||||
try:
|
try:
|
||||||
for line in iter(pipe.readline, ''):
|
for line in iter(pipe.readline, ''):
|
||||||
log_entry = {'type': log_type, 'data': line}
|
log_entry = {'type': log_type, 'data': line}
|
||||||
# Verlauf speichern, wenn der Bot noch läuft
|
|
||||||
if bot_id in running_bots:
|
if bot_id in running_bots:
|
||||||
running_bots[bot_id]['history'].append(log_entry)
|
running_bots[bot_id]['history'].append(log_entry)
|
||||||
# Optional: Begrenzen Sie die Größe des Verlaufs, um Speicher zu sparen
|
|
||||||
max_history_lines = 1000
|
max_history_lines = 1000
|
||||||
if len(running_bots[bot_id]['history']) > max_history_lines:
|
if len(running_bots[bot_id]['history']) > max_history_lines:
|
||||||
running_bots[bot_id]['history'].pop(0)
|
running_bots[bot_id]['history'].pop(0)
|
||||||
|
|
||||||
socketio.emit('console_output', log_entry, room=bot_id)
|
socketio.emit('console_output', log_entry, room=bot_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Stream-Fehler für Bot {bot_id}: {e}")
|
print(f"Stream-Fehler für Bot {bot_id}: {e}")
|
||||||
finally:
|
finally:
|
||||||
if pipe:
|
if pipe:
|
||||||
pipe.close()
|
pipe.close()
|
||||||
|
|
||||||
stdout_thread = threading.Thread(target=stream_to_socket, args=(process.stdout, 'stdout'))
|
stdout_thread = threading.Thread(target=stream_to_socket, args=(process.stdout, 'stdout'))
|
||||||
stderr_thread = threading.Thread(target=stream_to_socket, args=(process.stderr, 'stderr'))
|
stderr_thread = threading.Thread(target=stream_to_socket, args=(process.stderr, 'stderr'))
|
||||||
stdout_thread.start()
|
stdout_thread.start()
|
||||||
stderr_thread.start()
|
stderr_thread.start()
|
||||||
|
process.wait()
|
||||||
process.wait() # Warten, bis der Prozess endet
|
|
||||||
|
|
||||||
# Prozess beendet, Status aktualisieren
|
|
||||||
if bot_id in running_bots:
|
if bot_id in running_bots:
|
||||||
del running_bots[bot_id]
|
del running_bots[bot_id]
|
||||||
socketio.emit('status_update', {'bot_id': bot_id, 'status': 'stopped'}, room=bot_id)
|
socketio.emit('status_update', {'bot_id': bot_id, 'status': 'stopped'}, room=bot_id)
|
||||||
socketio.emit('console_output', {'bot_id': bot_id, 'type': 'system', 'data': '--- Bot-Prozess beendet ---'}, room=bot_id)
|
socketio.emit('console_output', {'bot_id': bot_id, 'type': 'system', 'data': '--- Bot-Prozess beendet ---'}, room=bot_id)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/discord_bots')
|
@app.route('/discord_bots')
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('manage_discord_bots')
|
@permission_required('manage_discord_bots')
|
||||||
|
|
@ -722,36 +576,29 @@ def discord_bots():
|
||||||
for bot in bots:
|
for bot in bots:
|
||||||
bot['status'] = 'running' if str(bot['_id']) in running_bots else 'stopped'
|
bot['status'] = 'running' if str(bot['_id']) in running_bots else 'stopped'
|
||||||
return render_template('discord_bots.html', active_page='discord_bots', bots=bots)
|
return render_template('discord_bots.html', active_page='discord_bots', bots=bots)
|
||||||
|
|
||||||
@app.route('/upload_bot', methods=['POST'])
|
@app.route('/upload_bot', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('manage_discord_bots')
|
@permission_required('manage_discord_bots')
|
||||||
def upload_bot():
|
def upload_bot():
|
||||||
bot_name = request.form.get('bot_name')
|
bot_name = request.form.get('bot_name')
|
||||||
bot_file = request.files.get('bot_file')
|
bot_file = request.files.get('bot_file')
|
||||||
|
|
||||||
if not bot_name or not bot_file or bot_file.filename == '':
|
if not bot_name or not bot_file or bot_file.filename == '':
|
||||||
flash("Bot-Name und Datei sind erforderlich.", "danger")
|
flash("Bot-Name und Datei sind erforderlich.", "danger")
|
||||||
return redirect(url_for('discord_bots'))
|
return redirect(url_for('discord_bots'))
|
||||||
|
|
||||||
if discord_bots_collection.find_one({'name': bot_name}):
|
if discord_bots_collection.find_one({'name': bot_name}):
|
||||||
flash("Ein Bot mit diesem Namen existiert bereits.", "danger")
|
flash("Ein Bot mit diesem Namen existiert bereits.", "danger")
|
||||||
return redirect(url_for('discord_bots'))
|
return redirect(url_for('discord_bots'))
|
||||||
|
|
||||||
filename = secure_filename(bot_file.filename)
|
filename = secure_filename(bot_file.filename)
|
||||||
filepath = os.path.join(app.config['BOT_UPLOAD_FOLDER'], filename)
|
filepath = os.path.join(app.config['BOT_UPLOAD_FOLDER'], filename)
|
||||||
bot_file.save(filepath)
|
bot_file.save(filepath)
|
||||||
|
|
||||||
discord_bots_collection.insert_one({
|
discord_bots_collection.insert_one({
|
||||||
'name': bot_name,
|
'name': bot_name,
|
||||||
'filename': filename,
|
'filename': filename,
|
||||||
'filepath': filepath,
|
'filepath': filepath,
|
||||||
'created_at': datetime.utcnow()
|
'created_at': datetime.utcnow()
|
||||||
})
|
})
|
||||||
|
|
||||||
flash(f"Bot '{bot_name}' wurde erfolgreich hochgeladen.", "success")
|
flash(f"Bot '{bot_name}' wurde erfolgreich hochgeladen.", "success")
|
||||||
return redirect(url_for('discord_bots'))
|
return redirect(url_for('discord_bots'))
|
||||||
|
|
||||||
@app.route('/delete_bot/<bot_id>', methods=['POST'])
|
@app.route('/delete_bot/<bot_id>', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('manage_discord_bots')
|
@permission_required('manage_discord_bots')
|
||||||
|
|
@ -760,8 +607,6 @@ def delete_bot(bot_id):
|
||||||
if not bot_info:
|
if not bot_info:
|
||||||
flash("Bot nicht gefunden.", "danger")
|
flash("Bot nicht gefunden.", "danger")
|
||||||
return redirect(url_for('discord_bots'))
|
return redirect(url_for('discord_bots'))
|
||||||
|
|
||||||
# Bot stoppen, falls er läuft
|
|
||||||
if bot_id in running_bots:
|
if bot_id in running_bots:
|
||||||
process = running_bots[bot_id]['process']
|
process = running_bots[bot_id]['process']
|
||||||
try:
|
try:
|
||||||
|
|
@ -771,22 +616,15 @@ def delete_bot(bot_id):
|
||||||
process.kill()
|
process.kill()
|
||||||
del running_bots[bot_id]
|
del running_bots[bot_id]
|
||||||
socketio.emit('status_update', {'bot_id': bot_id, 'status': 'deleted'}, room=bot_id)
|
socketio.emit('status_update', {'bot_id': bot_id, 'status': 'deleted'}, room=bot_id)
|
||||||
|
|
||||||
# Bot-Datei löschen
|
|
||||||
try:
|
try:
|
||||||
filepath = bot_info.get('filepath')
|
filepath = bot_info.get('filepath')
|
||||||
if filepath and os.path.exists(filepath):
|
if filepath and os.path.exists(filepath):
|
||||||
os.remove(filepath)
|
os.remove(filepath)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
flash(f"Fehler beim Löschen der Bot-Datei: {e}", "danger")
|
flash(f"Fehler beim Löschen der Bot-Datei: {e}", "danger")
|
||||||
# Fortfahren, um den DB-Eintrag trotzdem zu löschen
|
|
||||||
|
|
||||||
# Eintrag aus der Datenbank löschen
|
|
||||||
discord_bots_collection.delete_one({'_id': ObjectId(bot_id)})
|
discord_bots_collection.delete_one({'_id': ObjectId(bot_id)})
|
||||||
|
|
||||||
flash(f"Bot '{bot_info['name']}' wurde erfolgreich gelöscht.", "success")
|
flash(f"Bot '{bot_info['name']}' wurde erfolgreich gelöscht.", "success")
|
||||||
return redirect(url_for('discord_bots'))
|
return redirect(url_for('discord_bots'))
|
||||||
|
|
||||||
@app.route('/bot_console/<bot_id>')
|
@app.route('/bot_console/<bot_id>')
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('manage_discord_bots')
|
@permission_required('manage_discord_bots')
|
||||||
|
|
@ -795,48 +633,38 @@ def bot_console(bot_id):
|
||||||
if not bot:
|
if not bot:
|
||||||
flash("Bot nicht gefunden.", "danger")
|
flash("Bot nicht gefunden.", "danger")
|
||||||
return redirect(url_for('discord_bots'))
|
return redirect(url_for('discord_bots'))
|
||||||
|
|
||||||
bot['status'] = 'running' if bot_id in running_bots else 'stopped'
|
bot['status'] = 'running' if bot_id in running_bots else 'stopped'
|
||||||
return render_template('bot_console.html', bot=bot, active_page='discord_bots')
|
return render_template('bot_console.html', bot=bot, active_page='discord_bots')
|
||||||
|
|
||||||
@socketio.on('connect')
|
@socketio.on('connect')
|
||||||
def handle_connect():
|
def handle_connect():
|
||||||
print('Client verbunden')
|
print('Client verbunden')
|
||||||
|
|
||||||
@socketio.on('join_console')
|
@socketio.on('join_console')
|
||||||
def handle_join_console(data):
|
def handle_join_console(data):
|
||||||
bot_id = data['bot_id']
|
bot_id = data['bot_id']
|
||||||
join_room(bot_id)
|
join_room(bot_id)
|
||||||
emit('console_output', {'type': 'system', 'data': f'Verbunden mit Konsole für Bot {bot_id}.'})
|
emit('console_output', {'type': 'system', 'data': f'Verbunden mit Konsole für Bot {bot_id}.'})
|
||||||
# Gespeicherten Verlauf an den neuen Client senden
|
|
||||||
if bot_id in running_bots and 'history' in running_bots[bot_id]:
|
if bot_id in running_bots and 'history' in running_bots[bot_id]:
|
||||||
for log in running_bots[bot_id]['history']:
|
for log in running_bots[bot_id]['history']:
|
||||||
emit('console_output', log)
|
emit('console_output', log)
|
||||||
|
|
||||||
@socketio.on('leave_console')
|
@socketio.on('leave_console')
|
||||||
def handle_leave_console(data):
|
def handle_leave_console(data):
|
||||||
bot_id = data['bot_id']
|
bot_id = data['bot_id']
|
||||||
leave_room(bot_id)
|
leave_room(bot_id)
|
||||||
print(f'Client hat Konsole für Bot {bot_id} verlassen.')
|
print(f'Client hat Konsole für Bot {bot_id} verlassen.')
|
||||||
|
|
||||||
@socketio.on('toggle_bot')
|
@socketio.on('toggle_bot')
|
||||||
def handle_toggle_bot(data):
|
def handle_toggle_bot(data):
|
||||||
bot_id = data['bot_id']
|
bot_id = data['bot_id']
|
||||||
action = data['action'] # 'start' or 'stop'
|
action = data['action']
|
||||||
|
|
||||||
bot_info = discord_bots_collection.find_one({'_id': ObjectId(bot_id)})
|
bot_info = discord_bots_collection.find_one({'_id': ObjectId(bot_id)})
|
||||||
if not bot_info:
|
if not bot_info:
|
||||||
emit('console_output', {'type': 'error', 'data': 'Bot nicht in der Datenbank gefunden.'}, room=bot_id)
|
emit('console_output', {'type': 'error', 'data': 'Bot nicht in der Datenbank gefunden.'}, room=bot_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
if action == 'start' and bot_id not in running_bots:
|
if action == 'start' and bot_id not in running_bots:
|
||||||
filepath = bot_info.get('filepath')
|
filepath = bot_info.get('filepath')
|
||||||
if not os.path.exists(filepath):
|
if not os.path.exists(filepath):
|
||||||
emit('console_output', {'type': 'error', 'data': f'Bot-Datei nicht gefunden: {filepath}'}, room=bot_id)
|
emit('console_output', {'type': 'error', 'data': f'Bot-Datei nicht gefunden: {filepath}'}, room=bot_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Start process
|
|
||||||
process = subprocess.Popen(
|
process = subprocess.Popen(
|
||||||
['python', '-u', filepath],
|
['python', '-u', filepath],
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
|
|
@ -846,65 +674,48 @@ def handle_toggle_bot(data):
|
||||||
errors='replace'
|
errors='replace'
|
||||||
)
|
)
|
||||||
running_bots[bot_id] = {'process': process, 'history': []}
|
running_bots[bot_id] = {'process': process, 'history': []}
|
||||||
|
|
||||||
# Start thread to stream output
|
|
||||||
thread = threading.Thread(target=stream_bot_output, args=(bot_id, process))
|
thread = threading.Thread(target=stream_bot_output, args=(bot_id, process))
|
||||||
thread.daemon = True
|
thread.daemon = True
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
emit('status_update', {'bot_id': bot_id, 'status': 'running'}, room=bot_id)
|
emit('status_update', {'bot_id': bot_id, 'status': 'running'}, room=bot_id)
|
||||||
emit('console_output', {'type': 'system', 'data': f'--- Bot-Prozess gestartet (PID: {process.pid}) ---'}, room=bot_id)
|
emit('console_output', {'type': 'system', 'data': f'--- Bot-Prozess gestartet (PID: {process.pid}) ---'}, room=bot_id)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
emit('console_output', {'type': 'error', 'data': f'Fehler beim Starten des Bots: {e}'}, room=bot_id)
|
emit('console_output', {'type': 'error', 'data': f'Fehler beim Starten des Bots: {e}'}, room=bot_id)
|
||||||
|
|
||||||
elif action == 'stop' and bot_id in running_bots:
|
elif action == 'stop' and bot_id in running_bots:
|
||||||
process = running_bots[bot_id]['process']
|
process = running_bots[bot_id]['process']
|
||||||
try:
|
try:
|
||||||
process.terminate() # Graceful shutdown
|
process.terminate()
|
||||||
process.wait(timeout=5)
|
process.wait(timeout=5)
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
process.kill() # Force kill
|
process.kill()
|
||||||
|
|
||||||
if bot_id in running_bots:
|
if bot_id in running_bots:
|
||||||
del running_bots[bot_id]
|
del running_bots[bot_id]
|
||||||
|
|
||||||
emit('status_update', {'bot_id': bot_id, 'status': 'stopped'}, room=bot_id)
|
emit('status_update', {'bot_id': bot_id, 'status': 'stopped'}, room=bot_id)
|
||||||
emit('console_output', {'type': 'system', 'data': '--- Bot-Prozess wird gestoppt... ---'}, room=bot_id)
|
emit('console_output', {'type': 'system', 'data': '--- Bot-Prozess wird gestoppt... ---'}, room=bot_id)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/maintenance')
|
@app.route('/maintenance')
|
||||||
def maintenance():
|
def maintenance():
|
||||||
maint_info = dashboard_collection.find_one({"key": "scheduled_maintenance"}) if dashboard_collection is not None else {}
|
maint_info = dashboard_collection.find_one({"key": "scheduled_maintenance"}) if dashboard_collection is not None else {}
|
||||||
if not maint_info or not maint_info.get('enabled', False):
|
if not maint_info or not maint_info.get('enabled', False):
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
return render_template('maintenance.html', maint_info=maint_info)
|
return render_template('maintenance.html', maint_info=maint_info)
|
||||||
|
|
||||||
@app.route('/maintenance_login', methods=['GET', 'POST'])
|
@app.route('/maintenance_login', methods=['GET', 'POST'])
|
||||||
def maintenance_login():
|
def maintenance_login():
|
||||||
if 'user_id' in session:
|
if 'user_id' in session:
|
||||||
return redirect(url_for('dashboard'))
|
return redirect(url_for('dashboard'))
|
||||||
|
|
||||||
maintenance_status = dashboard_collection.find_one({"key": "scheduled_maintenance"}) if dashboard_collection is not None else None
|
maintenance_status = dashboard_collection.find_one({"key": "scheduled_maintenance"}) if dashboard_collection is not None else None
|
||||||
if not maintenance_status or not maintenance_status.get('enabled', False):
|
if not maintenance_status or not maintenance_status.get('enabled', False):
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
username = request.form.get('username')
|
username = request.form.get('username')
|
||||||
password = request.form.get('password')
|
password = request.form.get('password')
|
||||||
|
|
||||||
if not username or not password:
|
if not username or not password:
|
||||||
flash("Benutzername und Passwort sind erforderlich.", "danger")
|
flash("Benutzername und Passwort sind erforderlich.", "danger")
|
||||||
return render_template('maintenance_login.html')
|
return render_template('maintenance_login.html')
|
||||||
|
|
||||||
if accounts_collection is None:
|
if accounts_collection is None:
|
||||||
flash("Datenbankverbindung nicht verfügbar.", "danger")
|
flash("Datenbankverbindung nicht verfügbar.", "danger")
|
||||||
return render_template('maintenance_login.html'), 500
|
return render_template('maintenance_login.html'), 500
|
||||||
|
|
||||||
user = accounts_collection.find_one({'username': username})
|
user = accounts_collection.find_one({'username': username})
|
||||||
|
|
||||||
if user and check_password_hash(user['password'], password):
|
if user and check_password_hash(user['password'], password):
|
||||||
# Überprüfen, ob der Benutzer die Bypass-Berechtigung hat
|
|
||||||
user_perms = get_user_permissions(str(user['_id']))
|
user_perms = get_user_permissions(str(user['_id']))
|
||||||
if 'bypass_maintenance' in user_perms:
|
if 'bypass_maintenance' in user_perms:
|
||||||
session.permanent = True
|
session.permanent = True
|
||||||
|
|
@ -919,9 +730,7 @@ def maintenance_login():
|
||||||
else:
|
else:
|
||||||
flash("Ungültiger Benutzername oder Passwort.", "danger")
|
flash("Ungültiger Benutzername oder Passwort.", "danger")
|
||||||
return render_template('maintenance_login.html')
|
return render_template('maintenance_login.html')
|
||||||
|
|
||||||
return render_template('maintenance_login.html')
|
return render_template('maintenance_login.html')
|
||||||
|
|
||||||
@app.route('/maintenance_management')
|
@app.route('/maintenance_management')
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('maintenance_management')
|
@permission_required('maintenance_management')
|
||||||
|
|
@ -929,11 +738,9 @@ def maintenance_management():
|
||||||
maint_info = dashboard_collection.find_one({"key": "scheduled_maintenance"}) if dashboard_collection is not None else {}
|
maint_info = dashboard_collection.find_one({"key": "scheduled_maintenance"}) if dashboard_collection is not None else {}
|
||||||
if maint_info and maint_info.get('time'):
|
if maint_info and maint_info.get('time'):
|
||||||
maint_info['time_local_str'] = maint_info['time'].strftime('%Y-%m-%dT%H:%M')
|
maint_info['time_local_str'] = maint_info['time'].strftime('%Y-%m-%dT%H:%M')
|
||||||
# Discord Bots Wartungsstatus für das Template bereitstellen
|
|
||||||
if 'discord_bots_maintenance' not in maint_info:
|
if 'discord_bots_maintenance' not in maint_info:
|
||||||
maint_info['discord_bots_maintenance'] = False
|
maint_info['discord_bots_maintenance'] = False
|
||||||
return render_template('maintenance_management.html', active_page='maintenance_management', maint_info=maint_info)
|
return render_template('maintenance_management.html', active_page='maintenance_management', maint_info=maint_info)
|
||||||
|
|
||||||
@app.route('/update_maintenance', methods=['POST'])
|
@app.route('/update_maintenance', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('maintenance_management')
|
@permission_required('maintenance_management')
|
||||||
|
|
@ -941,13 +748,11 @@ def update_maintenance():
|
||||||
if dashboard_collection is None:
|
if dashboard_collection is None:
|
||||||
flash("Datenbankverbindung nicht verfügbar.", "danger")
|
flash("Datenbankverbindung nicht verfügbar.", "danger")
|
||||||
return redirect(url_for('maintenance_management'))
|
return redirect(url_for('maintenance_management'))
|
||||||
|
|
||||||
is_enabled = request.form.get('maintenance_enabled') == 'on'
|
is_enabled = request.form.get('maintenance_enabled') == 'on'
|
||||||
status_message = request.form.get('status_message', 'Keine')
|
status_message = request.form.get('status_message', 'Keine')
|
||||||
duration = request.form.get('duration_minutes')
|
duration = request.form.get('duration_minutes')
|
||||||
time_str = request.form.get('maintenance_time')
|
time_str = request.form.get('maintenance_time')
|
||||||
discord_bots_maintenance = request.form.get('discord_bots_maintenance') == 'on'
|
discord_bots_maintenance = request.form.get('discord_bots_maintenance') == 'on'
|
||||||
|
|
||||||
update_data = {
|
update_data = {
|
||||||
"enabled": is_enabled,
|
"enabled": is_enabled,
|
||||||
"status": status_message,
|
"status": status_message,
|
||||||
|
|
@ -955,37 +760,14 @@ def update_maintenance():
|
||||||
"time": datetime.fromisoformat(time_str) if time_str else None,
|
"time": datetime.fromisoformat(time_str) if time_str else None,
|
||||||
"discord_bots_maintenance": discord_bots_maintenance
|
"discord_bots_maintenance": discord_bots_maintenance
|
||||||
}
|
}
|
||||||
|
|
||||||
dashboard_collection.update_one(
|
dashboard_collection.update_one(
|
||||||
{"key": "scheduled_maintenance"},
|
{"key": "scheduled_maintenance"},
|
||||||
{"$set": update_data},
|
{"$set": update_data},
|
||||||
upsert=True
|
upsert=True
|
||||||
)
|
)
|
||||||
|
|
||||||
flash("Wartungseinstellungen wurden aktualisiert.", "success")
|
flash("Wartungseinstellungen wurden aktualisiert.", "success")
|
||||||
return redirect(url_for('maintenance_management'))
|
return redirect(url_for('maintenance_management'))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# Optionale SSL-Unterstützung:
|
app.config['SESSION_COOKIE_SECURE'] = False
|
||||||
# Legen Sie entweder die Umgebungsvariablen SSL_CERT_FILE und SSL_KEY_FILE fest
|
print("INFO: Starte den Server im Entwicklungsmodus ohne HTTPS.")
|
||||||
# oder legen Sie die Dateien certs/fullchain.pem und certs/privkey.pem im Projektordner ab.
|
|
||||||
cert_file = os.environ.get('SSL_CERT_FILE', os.path.join(os.path.dirname(os.path.abspath(__file__)), 'certs', 'fullchain.pem'))
|
|
||||||
key_file = os.environ.get('SSL_KEY_FILE', os.path.join(os.path.dirname(os.path.abspath(__file__)), 'certs', 'privkey.pem'))
|
|
||||||
|
|
||||||
use_ssl = os.path.exists(cert_file) and os.path.exists(key_file)
|
|
||||||
|
|
||||||
# Sitzungscookies nur als Secure markieren, wenn tatsächlich HTTPS verwendet wird
|
|
||||||
app.config['SESSION_COOKIE_SECURE'] = bool(use_ssl)
|
|
||||||
|
|
||||||
if use_ssl:
|
|
||||||
print(f"INFO: Starte mit SSL, cert={cert_file}, key={key_file}")
|
|
||||||
# Für bessere Kontrolle können Sie auch ein SSLContext erstellen:
|
|
||||||
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
|
||||||
context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
|
|
||||||
context.load_cert_chain(certfile=cert_file, keyfile=key_file)
|
|
||||||
# In Produktion debug=False setzen und Port 443 verwenden (oder Reverse Proxy)
|
|
||||||
socketio.run(app, host='0.0.0.0', port=443, debug=False, ssl_context=context)
|
|
||||||
else:
|
|
||||||
print("WARNUNG: SSL-Zertifikat nicht gefunden. Starte ohne HTTPS (nur für Entwicklung).")
|
|
||||||
# allow_unsafe_werkzeug bleibt hier, da dies Entwicklungsmodus ist
|
|
||||||
socketio.run(app, host='0.0.0.0', port=5000, debug=True, allow_unsafe_werkzeug=True)
|
socketio.run(app, host='0.0.0.0', port=5000, debug=True, allow_unsafe_werkzeug=True)
|
||||||
|
|
|
||||||
12
AstraOS/requirements.txt
Normal file
12
AstraOS/requirements.txt
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
Flask
|
||||||
|
pymongo
|
||||||
|
werkzeug
|
||||||
|
Flask-WTF
|
||||||
|
paramiko
|
||||||
|
requests
|
||||||
|
Flask-SocketIO
|
||||||
|
gunicorn
|
||||||
|
eventlet
|
||||||
|
ipaddress
|
||||||
|
python-dotenv
|
||||||
|
|
||||||
35
AstraOS/start_prod_server.py
Normal file
35
AstraOS/start_prod_server.py
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def start_server():
|
||||||
|
project_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
command = [
|
||||||
|
"gunicorn",
|
||||||
|
"--worker-class", "eventlet",
|
||||||
|
"-w", "1",
|
||||||
|
"--bind", "0.0.0.0:5000",
|
||||||
|
"wsgi:app"
|
||||||
|
]
|
||||||
|
print("=============================================")
|
||||||
|
print("=== AstraOS Produktionsserver wird gestartet ===")
|
||||||
|
print(f"=== Arbeitsverzeichnis: {project_dir}")
|
||||||
|
print(f"=== Befehl: {' '.join(command)}")
|
||||||
|
print("=============================================")
|
||||||
|
print("Drücken Sie STRG+C, um den Server zu beenden.")
|
||||||
|
try:
|
||||||
|
subprocess.run(command, cwd=project_dir, check=True)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print("\n[FEHLER] 'gunicorn' wurde nicht gefunden.")
|
||||||
|
print("Stellen Sie sicher, dass Gunicorn installiert ist ('pip install gunicorn').")
|
||||||
|
sys.exit(1)
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(f"\n[FEHLER] Der Server wurde unerwartet beendet: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\nServer wurde vom Benutzer gestoppt.")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
start_server()
|
||||||
|
|
||||||
4
AstraOS/wsgi.py
Normal file
4
AstraOS/wsgi.py
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
from astra_os import app, socketio
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
socketio.run(app)
|
||||||
Loading…
Reference in a new issue