User-Panel gebaut
This commit is contained in:
629
Dashboard/app.py
Normal file
629
Dashboard/app.py
Normal file
@@ -0,0 +1,629 @@
|
||||
from flask import Flask, render_template, request, redirect, url_for, session, flash, jsonify, send_file
|
||||
import sqlite3
|
||||
import bcrypt
|
||||
import secrets
|
||||
import smtplib
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
import os
|
||||
import requests
|
||||
from email.mime.text import MIMEText
|
||||
|
||||
app = Flask(__name__, static_folder="static")
|
||||
app.secret_key = 'supersecretkey'
|
||||
|
||||
DATABASE = "clickcandit.db"
|
||||
SMTP_SERVER = "smtp.gmail.com"
|
||||
SMTP_PORT = 465
|
||||
EMAIL_SENDER = "clickcandit@gmail.com"
|
||||
EMAIL_PASSWORD = 'iuxexntistlwilhl'
|
||||
|
||||
WEATHER_API_KEY = '640935f82530489f8c7105323241409'
|
||||
WEATHER_API_URL = 'http://api.openweathermap.org/data/2.5/forecast'
|
||||
|
||||
EXPORT_FOLDER = "exports"
|
||||
os.makedirs(EXPORT_FOLDER, exist_ok=True)
|
||||
|
||||
def get_db_connection():
|
||||
conn = sqlite3.connect(DATABASE)
|
||||
conn.row_factory = sqlite3.Row
|
||||
return conn
|
||||
|
||||
def init_db():
|
||||
conn = get_db_connection()
|
||||
c = conn.cursor()
|
||||
# Benutzer (User & Admin)
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT UNIQUE NOT NULL,
|
||||
password TEXT NOT NULL,
|
||||
role TEXT DEFAULT 'user',
|
||||
reset_token TEXT DEFAULT NULL,
|
||||
reset_token_expiry TEXT DEFAULT NULL
|
||||
)
|
||||
""")
|
||||
# Einstellungen
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL UNIQUE,
|
||||
wallpaper TEXT DEFAULT '7.png',
|
||||
city TEXT DEFAULT 'Berlin',
|
||||
bookmarks TEXT DEFAULT '[]',
|
||||
show_forecast BOOLEAN DEFAULT 1,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id)
|
||||
)
|
||||
""")
|
||||
# Download Links (Nutzerdatenexport)
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS download_links (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
token TEXT NOT NULL,
|
||||
expiration_time TEXT NOT NULL,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id)
|
||||
)
|
||||
""")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
init_db()
|
||||
|
||||
######################################
|
||||
# Hilfsfunktionen
|
||||
######################################
|
||||
|
||||
def admin_exists():
|
||||
conn = get_db_connection()
|
||||
result = conn.execute("SELECT 1 FROM users WHERE role = 'admin' LIMIT 1").fetchone()
|
||||
conn.close()
|
||||
return result is not None
|
||||
|
||||
def get_weather(city):
|
||||
params = {
|
||||
'q': city,
|
||||
'appid': WEATHER_API_KEY,
|
||||
'units': 'metric',
|
||||
'lang': 'de'
|
||||
}
|
||||
response = requests.get(WEATHER_API_URL, params=params)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
current_temp = data['list'][0]['main']['temp']
|
||||
weather_icon = map_weather_icon(data['list'][0]['weather'][0]['icon'])
|
||||
# Einfaches Forecast-Beispiel
|
||||
forecast = []
|
||||
for i in range(0, len(data['list']), 8):
|
||||
day = data['list'][i]
|
||||
forecast.append({
|
||||
'date': day['dt_txt'].split()[0],
|
||||
'day': {
|
||||
'avgtemp_c': day['main']['temp']
|
||||
},
|
||||
'weather_icon': map_weather_icon(day['weather'][0]['icon'])
|
||||
})
|
||||
return current_temp, weather_icon, forecast
|
||||
else:
|
||||
return None, None, []
|
||||
|
||||
def map_weather_icon(icon_code):
|
||||
mapping = {
|
||||
'01d': 'fa-sun',
|
||||
'01n': 'fa-moon',
|
||||
'02d': 'fa-cloud-sun',
|
||||
'02n': 'fa-cloud-moon',
|
||||
'03d': 'fa-cloud',
|
||||
'03n': 'fa-cloud',
|
||||
'04d': 'fa-cloud-meatball',
|
||||
'04n': 'fa-cloud-meatball',
|
||||
'09d': 'fa-cloud-showers-heavy',
|
||||
'09n': 'fa-cloud-showers-heavy',
|
||||
'10d': 'fa-cloud-sun-rain',
|
||||
'10n': 'fa-cloud-moon-rain',
|
||||
'11d': 'fa-poo-storm',
|
||||
'11n': 'fa-poo-storm',
|
||||
'13d': 'fa-snowflake',
|
||||
'13n': 'fa-snowflake',
|
||||
'50d': 'fa-smog',
|
||||
'50n': 'fa-smog'
|
||||
}
|
||||
return mapping.get(icon_code, 'fa-cloud')
|
||||
|
||||
def send_reset_email(to_email, reset_url):
|
||||
subject = "Passwort zurücksetzen"
|
||||
body = f"""
|
||||
Hallo,
|
||||
|
||||
Sie haben eine Anfrage zum Zurücksetzen Ihres Passworts erhalten. Klicken Sie auf den folgenden Link, um Ihr Passwort zurückzusetzen:
|
||||
|
||||
{reset_url}
|
||||
|
||||
Der Link ist 48 Stunden gültig. Falls Sie diese Anfrage nicht gestellt haben, ignorieren Sie bitte diese E-Mail.
|
||||
|
||||
Mit freundlichen Grüßen,
|
||||
Ihr ClickCandit Team
|
||||
"""
|
||||
msg = MIMEText(body, "plain")
|
||||
msg["Subject"] = subject
|
||||
msg["From"] = EMAIL_SENDER
|
||||
msg["To"] = to_email
|
||||
|
||||
try:
|
||||
with smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT) as server:
|
||||
server.login(EMAIL_SENDER, EMAIL_PASSWORD)
|
||||
server.sendmail(EMAIL_SENDER, to_email, msg.as_string())
|
||||
print(f"Passwort-Reset-E-Mail erfolgreich an {to_email} gesendet.")
|
||||
except Exception as e:
|
||||
print(f"Fehler beim Senden der E-Mail: {e}")
|
||||
|
||||
def send_export_email(to_email, download_link):
|
||||
subject = "Ihre Nutzerdaten zum Download"
|
||||
body = f"""
|
||||
Hallo {to_email},
|
||||
|
||||
Sie haben den Export Ihrer Nutzerdaten angefordert. Klicken Sie auf den folgenden Link, um Ihre Daten herunterzuladen:
|
||||
|
||||
{download_link}
|
||||
|
||||
Dieser Link ist 48 Stunden gültig.
|
||||
|
||||
Mit freundlichen Grüßen,
|
||||
Ihr ClickCandit Team
|
||||
"""
|
||||
msg = MIMEText(body, "plain")
|
||||
msg["Subject"] = subject
|
||||
msg["From"] = EMAIL_SENDER
|
||||
msg["To"] = to_email
|
||||
|
||||
try:
|
||||
with smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT) as server:
|
||||
server.login(EMAIL_SENDER, EMAIL_PASSWORD)
|
||||
server.sendmail(EMAIL_SENDER, to_email, msg.as_string())
|
||||
print(f"Download-Link an {to_email} gesendet.")
|
||||
except Exception as e:
|
||||
print(f"Fehler beim Senden der E-Mail: {e}")
|
||||
|
||||
######################################
|
||||
# Routen
|
||||
######################################
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
# Wenn kein Admin existiert, zur Register-Seite (erster Admin)
|
||||
if not admin_exists():
|
||||
return redirect(url_for('register'))
|
||||
# Wenn schon Admin da, aber keiner eingeloggt -> Login
|
||||
if 'user_id' not in session:
|
||||
return redirect(url_for('login'))
|
||||
# Ansonsten: Dashboard
|
||||
return redirect(url_for('dashboard'))
|
||||
|
||||
@app.route('/register', methods=['GET', 'POST'])
|
||||
def register():
|
||||
already_admin = admin_exists()
|
||||
if request.method == 'GET':
|
||||
# Wenn Admin existiert, nur eingeloggter Admin darf registrieren
|
||||
if already_admin:
|
||||
if 'role' not in session or session['role'] != 'admin':
|
||||
flash("Nur Admins können weitere Benutzer hinzufügen!", "danger")
|
||||
return redirect(url_for('login'))
|
||||
return render_template('register.html')
|
||||
|
||||
# POST: Registrieren
|
||||
name = request.form['name'].strip()
|
||||
email = request.form['email'].strip()
|
||||
password = request.form['password'].strip()
|
||||
|
||||
if not name or not email or not password:
|
||||
flash("Bitte alle Felder ausfüllen.", "danger")
|
||||
return redirect(url_for('register'))
|
||||
|
||||
conn = get_db_connection()
|
||||
try:
|
||||
# Wenn noch kein Admin da -> erster wird Admin
|
||||
role = 'admin' if not already_admin else request.form.get('role', 'user')
|
||||
hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
|
||||
conn.execute("INSERT INTO users (name, email, password, role) VALUES (?, ?, ?, ?)",
|
||||
(name, email, hashed_password, role))
|
||||
conn.commit()
|
||||
flash("Benutzer erfolgreich registriert!", "success")
|
||||
except sqlite3.IntegrityError:
|
||||
flash("Diese E-Mail existiert bereits!", "danger")
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
if not already_admin:
|
||||
# Erster Admin angelegt -> nun einloggen
|
||||
return redirect(url_for('login'))
|
||||
else:
|
||||
return redirect(url_for('admin'))
|
||||
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
if request.method == 'POST':
|
||||
email = request.form['email'].strip()
|
||||
password = request.form['password'].strip()
|
||||
|
||||
if not email or not password:
|
||||
flash("Bitte E-Mail und Passwort angeben!", "danger")
|
||||
return redirect(url_for('login'))
|
||||
|
||||
conn = get_db_connection()
|
||||
user = conn.execute("SELECT * FROM users WHERE email = ?", (email,)).fetchone()
|
||||
conn.close()
|
||||
|
||||
if user and bcrypt.checkpw(password.encode('utf-8'), user['password']):
|
||||
session['user_id'] = user['id']
|
||||
session['user_name'] = user['name']
|
||||
session['role'] = user['role']
|
||||
flash("Login erfolgreich!", "success")
|
||||
return redirect(url_for('dashboard'))
|
||||
else:
|
||||
flash("Falsche E-Mail oder Passwort!", "danger")
|
||||
return redirect(url_for('login'))
|
||||
else:
|
||||
return render_template('login.html')
|
||||
|
||||
@app.route('/logout')
|
||||
def logout():
|
||||
session.clear()
|
||||
flash("Du wurdest ausgeloggt.", "info")
|
||||
return redirect(url_for('login'))
|
||||
|
||||
@app.route('/dashboard')
|
||||
def dashboard():
|
||||
if 'user_id' not in session:
|
||||
flash("Bitte melde dich an!", "danger")
|
||||
return redirect(url_for('login'))
|
||||
|
||||
user_id = session['user_id']
|
||||
conn = get_db_connection()
|
||||
user = conn.execute("SELECT * FROM users WHERE id = ?", (user_id,)).fetchone()
|
||||
settings = conn.execute("SELECT * FROM settings WHERE user_id = ?", (user_id,)).fetchone()
|
||||
conn.close()
|
||||
|
||||
if not user:
|
||||
flash("Benutzer nicht gefunden.", "danger")
|
||||
return redirect(url_for('logout'))
|
||||
|
||||
# Default-Werte
|
||||
if settings is None:
|
||||
wallpaper = '19.png'
|
||||
city = 'Berlin'
|
||||
show_forecast = True
|
||||
bookmarks = []
|
||||
else:
|
||||
wallpaper = settings['wallpaper']
|
||||
city = settings['city']
|
||||
show_forecast = bool(settings['show_forecast'])
|
||||
bookmarks = json.loads(settings['bookmarks'])
|
||||
|
||||
current_temp, weather_icon, forecast = get_weather(city)
|
||||
if current_temp is None:
|
||||
current_temp = "N/A"
|
||||
weather_icon = "fa-question"
|
||||
forecast = []
|
||||
|
||||
domain = "example.com"
|
||||
logo_path = url_for('static', filename='clickcandit.png')
|
||||
# Beispiel Apps
|
||||
user_app_chunks = [
|
||||
[
|
||||
{
|
||||
'name': 'Mail',
|
||||
'subdomain': 'mail',
|
||||
'appkey': 'mailapp',
|
||||
'icon_class': 'fa fa-envelope',
|
||||
'bg_color': 'bg-blue-500'
|
||||
},
|
||||
{
|
||||
'name': 'Calendar',
|
||||
'subdomain': 'calendar',
|
||||
'appkey': 'calendarapp',
|
||||
'icon_class': 'fa fa-calendar',
|
||||
'bg_color': 'bg-green-500'
|
||||
},
|
||||
]
|
||||
]
|
||||
|
||||
return render_template(
|
||||
'dashboard.html',
|
||||
user=user['name'],
|
||||
role=user['role'], # Für Admin-Abfrage im Template
|
||||
wallpaper=wallpaper,
|
||||
city=city,
|
||||
show_forecast=show_forecast,
|
||||
bookmarks=bookmarks,
|
||||
current_temp=current_temp,
|
||||
weather_icon=weather_icon,
|
||||
forecast=forecast,
|
||||
domain=domain,
|
||||
logo_path=logo_path,
|
||||
user_app_chunks=user_app_chunks
|
||||
)
|
||||
|
||||
######################################
|
||||
# Admin-Bereich
|
||||
######################################
|
||||
|
||||
@app.route('/admin')
|
||||
def admin():
|
||||
if 'role' not in session or session['role'] != 'admin':
|
||||
flash("Kein Zugriff!", "danger")
|
||||
return redirect(url_for('login'))
|
||||
|
||||
conn = get_db_connection()
|
||||
users = conn.execute("SELECT id, name, email, role FROM users").fetchall()
|
||||
conn.close()
|
||||
return render_template('admin.html', users=users)
|
||||
|
||||
@app.route('/admin/delete/<int:user_id>', methods=['POST'])
|
||||
def delete_user(user_id):
|
||||
if 'role' not in session or session['role'] != 'admin':
|
||||
flash("Kein Zugriff!", "danger")
|
||||
return redirect(url_for('login'))
|
||||
|
||||
conn = get_db_connection()
|
||||
conn.execute("DELETE FROM users WHERE id = ?", (user_id,))
|
||||
conn.execute("DELETE FROM settings WHERE user_id = ?", (user_id,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
flash("Benutzer gelöscht!", "success")
|
||||
return redirect(url_for('admin'))
|
||||
|
||||
@app.route('/admin/edit_bookmarks/<int:user_id>', methods=['GET', 'POST'])
|
||||
def edit_bookmarks(user_id):
|
||||
if 'role' not in session or session['role'] != 'admin':
|
||||
flash("Kein Zugriff", "danger")
|
||||
return redirect(url_for('login'))
|
||||
|
||||
conn = get_db_connection()
|
||||
settings = conn.execute("SELECT * FROM settings WHERE user_id = ?", (user_id,)).fetchone()
|
||||
|
||||
if request.method == 'POST':
|
||||
bookmarks_raw = request.form.get('bookmarks', '')
|
||||
bookmarks_list = [b.strip() for b in bookmarks_raw.split(',') if b.strip()]
|
||||
bookmarks_json = json.dumps(bookmarks_list)
|
||||
|
||||
if settings:
|
||||
conn.execute("UPDATE settings SET bookmarks = ? WHERE user_id = ?", (bookmarks_json, user_id))
|
||||
else:
|
||||
conn.execute("INSERT INTO settings (user_id, bookmarks) VALUES (?, ?)", (user_id, bookmarks_json))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
flash("Lesezeichen aktualisiert", "success")
|
||||
return redirect(url_for('admin'))
|
||||
else:
|
||||
current_bookmarks = json.loads(settings['bookmarks']) if settings else []
|
||||
conn.close()
|
||||
return render_template('edit_bookmarks.html', user_id=user_id, bookmarks=current_bookmarks)
|
||||
|
||||
######################################
|
||||
# Settings / API
|
||||
######################################
|
||||
|
||||
@app.route('/save_settings', methods=['POST'])
|
||||
def save_settings():
|
||||
if 'user_id' not in session:
|
||||
return jsonify({'success': False, 'message': 'Nicht autorisiert'}), 401
|
||||
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({'success': False, 'message': 'Keine Daten übergeben'}), 400
|
||||
|
||||
wallpaper = data.get('wallpaper', '7.png')
|
||||
city = data.get('city', 'Berlin')
|
||||
bookmarks = json.dumps(data.get('bookmarks', []))
|
||||
show_forecast = data.get('show_forecast', True)
|
||||
|
||||
conn = get_db_connection()
|
||||
conn.execute("""
|
||||
INSERT INTO settings (user_id, wallpaper, city, bookmarks, show_forecast)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
ON CONFLICT(user_id) DO UPDATE SET
|
||||
wallpaper=excluded.wallpaper,
|
||||
city=excluded.city,
|
||||
bookmarks=excluded.bookmarks,
|
||||
show_forecast=excluded.show_forecast
|
||||
""", (session['user_id'], wallpaper, city, bookmarks, show_forecast))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
return jsonify({'success': True})
|
||||
|
||||
@app.route('/get_settings', methods=['GET'])
|
||||
def get_settings():
|
||||
if 'user_id' not in session:
|
||||
return jsonify({'success': False, 'message': 'Nicht autorisiert'}), 401
|
||||
|
||||
conn = get_db_connection()
|
||||
settings = conn.execute("SELECT * FROM settings WHERE user_id = ?", (session['user_id'],)).fetchone()
|
||||
conn.close()
|
||||
|
||||
if settings:
|
||||
return jsonify({
|
||||
'wallpaper_url': url_for('static', filename=settings['wallpaper']),
|
||||
'city': settings['city'],
|
||||
'bookmarks': json.loads(settings['bookmarks']),
|
||||
'show_forecast': bool(settings['show_forecast'])
|
||||
})
|
||||
else:
|
||||
# Falls noch keine Settings da
|
||||
return jsonify({
|
||||
'wallpaper_url': url_for('static', filename='7.png'),
|
||||
'city': 'Berlin',
|
||||
'bookmarks': [],
|
||||
'show_forecast': True
|
||||
})
|
||||
|
||||
######################################
|
||||
# Passwort / Account
|
||||
######################################
|
||||
|
||||
@app.route('/forgot-password', methods=['GET', 'POST'])
|
||||
def forgot_password():
|
||||
if request.method == 'POST':
|
||||
email = request.form['email'].strip()
|
||||
conn = get_db_connection()
|
||||
user = conn.execute("SELECT * FROM users WHERE email = ?", (email,)).fetchone()
|
||||
if not user:
|
||||
flash("Diese E-Mail ist nicht registriert.", "danger")
|
||||
conn.close()
|
||||
return redirect(url_for('forgot_password'))
|
||||
|
||||
reset_token = secrets.token_hex(16)
|
||||
expiry_time = (datetime.utcnow() + timedelta(hours=48)).isoformat()
|
||||
conn.execute("UPDATE users SET reset_token=?, reset_token_expiry=? WHERE email=?",
|
||||
(reset_token, expiry_time, email))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
reset_url = url_for('reset_password', token=reset_token, _external=True)
|
||||
send_reset_email(email, reset_url)
|
||||
flash("Eine E-Mail zum Zurücksetzen des Passworts wurde gesendet.", "success")
|
||||
return redirect(url_for('login'))
|
||||
|
||||
return render_template('forgot_password.html')
|
||||
|
||||
@app.route('/reset-password/<token>', methods=['GET', 'POST'])
|
||||
def reset_password(token):
|
||||
conn = get_db_connection()
|
||||
user = conn.execute("SELECT * FROM users WHERE reset_token = ?", (token,)).fetchone()
|
||||
if not user:
|
||||
conn.close()
|
||||
flash("Ungültiger Reset-Token", "danger")
|
||||
return redirect(url_for('forgot_password'))
|
||||
|
||||
expiry = datetime.fromisoformat(user['reset_token_expiry']) if user['reset_token_expiry'] else None
|
||||
if not expiry or expiry < datetime.utcnow():
|
||||
conn.close()
|
||||
flash("Reset-Link ist abgelaufen.", "danger")
|
||||
return redirect(url_for('forgot_password'))
|
||||
|
||||
if request.method == 'POST':
|
||||
pw = request.form['password'].strip()
|
||||
cpw = request.form['confirm_password'].strip()
|
||||
if pw != cpw:
|
||||
flash("Passwörter stimmen nicht überein.", "danger")
|
||||
return render_template('reset_password.html', token=token)
|
||||
|
||||
hashed_pw = bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt())
|
||||
conn.execute("UPDATE users SET password=?, reset_token=NULL, reset_token_expiry=NULL WHERE id=?",
|
||||
(hashed_pw, user['id']))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
flash("Ihr Passwort wurde zurückgesetzt.", "success")
|
||||
return redirect(url_for('login'))
|
||||
|
||||
conn.close()
|
||||
return render_template('reset_password.html', token=token)
|
||||
|
||||
@app.route('/delete_account', methods=['POST'])
|
||||
def delete_account():
|
||||
if 'user_id' not in session:
|
||||
flash("Bitte melde dich an!", "danger")
|
||||
return redirect(url_for('login'))
|
||||
|
||||
user_id = session['user_id']
|
||||
try:
|
||||
conn = get_db_connection()
|
||||
user = conn.execute("SELECT * FROM users WHERE id = ?", (user_id,)).fetchone()
|
||||
if not user:
|
||||
flash("Benutzerkonto nicht gefunden.", "danger")
|
||||
conn.close()
|
||||
return redirect(url_for('dashboard'))
|
||||
|
||||
email = user["email"]
|
||||
conn.execute("DELETE FROM settings WHERE user_id=?", (user_id,))
|
||||
conn.execute("DELETE FROM download_links WHERE user_id=?", (user_id,))
|
||||
conn.execute("DELETE FROM users WHERE id=?", (user_id,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
export_file = os.path.join(EXPORT_FOLDER, f"{email}_data.json")
|
||||
if os.path.exists(export_file):
|
||||
os.remove(export_file)
|
||||
|
||||
session.clear()
|
||||
flash("Dein Konto wurde erfolgreich gelöscht.", "success")
|
||||
return redirect(url_for('login'))
|
||||
except Exception as e:
|
||||
print("Fehler beim Löschen des Kontos:", e)
|
||||
flash("Fehler beim Löschen des Kontos.", "danger")
|
||||
return redirect(url_for('dashboard'))
|
||||
|
||||
######################################
|
||||
# Nutzerdaten-Export
|
||||
######################################
|
||||
|
||||
@app.route('/request_data_export', methods=['GET'])
|
||||
def request_data_export():
|
||||
if 'user_id' not in session:
|
||||
flash("Bitte melde dich an!", "danger")
|
||||
return redirect(url_for('login'))
|
||||
|
||||
user_id = session['user_id']
|
||||
conn = get_db_connection()
|
||||
user = conn.execute("SELECT * FROM users WHERE id = ?", (user_id,)).fetchone()
|
||||
if not user:
|
||||
flash("Benutzerkonto nicht gefunden.", "danger")
|
||||
conn.close()
|
||||
return redirect(url_for('dashboard'))
|
||||
|
||||
download_token = secrets.token_hex(16)
|
||||
expiry = (datetime.utcnow() + timedelta(hours=48)).isoformat()
|
||||
|
||||
conn.execute("INSERT INTO download_links (user_id, token, expiration_time) VALUES (?, ?, ?)",
|
||||
(user_id, download_token, expiry))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
export_file = os.path.join(EXPORT_FOLDER, f"{user['email']}_data.json")
|
||||
user_data = {
|
||||
"name": user["name"],
|
||||
"email": user["email"],
|
||||
"role": user["role"]
|
||||
}
|
||||
|
||||
with open(export_file, "w", encoding="utf-8") as f:
|
||||
json.dump(user_data, f, indent=4)
|
||||
|
||||
download_url = url_for('download_user_data', token=download_token, _external=True)
|
||||
send_export_email(user["email"], download_url)
|
||||
|
||||
flash("Eine E-Mail mit dem Download-Link wurde gesendet.", "success")
|
||||
return redirect(url_for('dashboard'))
|
||||
|
||||
@app.route('/download_user_data/<token>', methods=['GET'])
|
||||
def download_user_data(token):
|
||||
conn = get_db_connection()
|
||||
link_data = conn.execute("SELECT * FROM download_links WHERE token=?", (token,)).fetchone()
|
||||
if not link_data:
|
||||
conn.close()
|
||||
flash("Ungültiger oder abgelaufener Download-Link.", "danger")
|
||||
return redirect(url_for('dashboard'))
|
||||
|
||||
expiry_time = datetime.fromisoformat(link_data["expiration_time"])
|
||||
if expiry_time < datetime.utcnow():
|
||||
conn.close()
|
||||
flash("Der Download-Link ist abgelaufen.", "danger")
|
||||
return redirect(url_for('dashboard'))
|
||||
|
||||
user = conn.execute("SELECT * FROM users WHERE id=?", (link_data["user_id"],)).fetchone()
|
||||
conn.close()
|
||||
if not user:
|
||||
flash("Benutzerdaten nicht gefunden.", "danger")
|
||||
return redirect(url_for('dashboard'))
|
||||
|
||||
export_file = os.path.join(EXPORT_FOLDER, f"{user['email']}_data.json")
|
||||
if not os.path.exists(export_file):
|
||||
flash("Exportdatei nicht gefunden.", "danger")
|
||||
return redirect(url_for('dashboard'))
|
||||
|
||||
return send_file(export_file, as_attachment=True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True)
|
||||
Reference in New Issue
Block a user