From 2b53b407786cf5e0024fc8f2cf9cd74153c1efdf Mon Sep 17 00:00:00 2001 From: Jason Hirsch Date: Wed, 19 Feb 2025 17:27:01 +0100 Subject: [PATCH] Dashboard V2 --- Dashboard/clickcandit.db | Bin 28672 -> 28672 bytes Dashboard_V2/app.py | 394 ++++++++++++++++++--- Dashboard_V2/clickcandit.db | Bin 28672 -> 40960 bytes Dashboard_V2/templates/admin.html | 250 ++++++------- Dashboard_V2/templates/base.html | 17 +- Dashboard_V2/templates/dashboard.html | 184 +++++++++- Dashboard_V2/templates/reset_password.html | 33 ++ 7 files changed, 684 insertions(+), 194 deletions(-) create mode 100644 Dashboard_V2/templates/reset_password.html diff --git a/Dashboard/clickcandit.db b/Dashboard/clickcandit.db index 935acd1becff5e847f06428ac48ff8ccf432dfbe..6de2eb76bfe2e58468f82f5bbe224d5ec8fc89ae 100644 GIT binary patch delta 335 zcmZp8z}WDBae_1>??f4AM&6AHOZa(M_;MNeyZH6^F7f4V78Ho(t50U(WKdUcWD#Xx z$Vn~Aw=i``&#ll)Nk!qP7$vC~8mah~`ItMGTZZd9<@tvC_&7N_xq0QL=IDhxI%TDs zh6ZI-h85+RmxUOb=%ss<<|moz`TMw+7N-_PF>^AAtAZ`bNX*H}2NPh6HouXRVB}%p z-^{@Ooc|jCQU1-F1r-+a3kfo-Gom=R_H2M$U~1OZd5%`7SZ=ck%1-UD_5C1p*51R!O9`SGf5HBDA0PYefi2wiq diff --git a/Dashboard_V2/app.py b/Dashboard_V2/app.py index 3442c9a..35bf530 100644 --- a/Dashboard_V2/app.py +++ b/Dashboard_V2/app.py @@ -1,7 +1,15 @@ import sqlite3 -from flask import Flask, render_template, request, redirect, url_for, session, g, flash +from flask import Flask, render_template, request, redirect, url_for, session, g, flash, jsonify from datetime import datetime from werkzeug.security import generate_password_hash, check_password_hash +import smtplib, ssl, secrets +from datetime import datetime, timedelta +import json + +SMTP_SERVER = "smtp.gmail.com" +SMTP_PORT = 465 +EMAIL_SENDER = "clickcandit@gmail.com" +EMAIL_PASSWORD = "iuxexntistlwilhl" app = Flask(__name__) app.secret_key = "SUPER_SECRET_KEY" # Bitte anpassen @@ -55,6 +63,14 @@ def init_db(): is_read INTEGER DEFAULT 0 ); """) + db.execute(""" + CREATE TABLE IF NOT EXISTS reset_tokens ( + user_id INTEGER NOT NULL, + token TEXT NOT NULL, + expires_at DATETIME NOT NULL, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + ); + """) # Time Tracking db.execute(""" CREATE TABLE IF NOT EXISTS time_entries ( @@ -66,15 +82,41 @@ def init_db(): FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE ); """) + # Settings (OPTIONAL, falls du globale User-Einstellungen abspeichern willst) + db.execute(""" + CREATE TABLE IF NOT EXISTS user_settings ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + wallpaper TEXT DEFAULT '1.png', + city TEXT DEFAULT '', + show_forecast INTEGER DEFAULT 0, + bookmarks TEXT DEFAULT '', + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + ); + """) + db.execute(""" + CREATE TABLE IF NOT EXISTS settings ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + wallpaper TEXT DEFAULT '19.png', + city TEXT DEFAULT 'Berlin', + show_forecast INTEGER DEFAULT 1, + bookmarks TEXT DEFAULT '[]', + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + ); + """) db.commit() +# ------------------------------------------------------------ +# HILFSFUNKTIONEN +# ------------------------------------------------------------ + def get_user_by_username_or_email(user_input): db = get_db() - user = db.execute(""" + return db.execute(""" SELECT * FROM users WHERE username = :val OR email = :val """, {"val": user_input}).fetchone() - return user def get_user_by_username(username): db = get_db() @@ -87,13 +129,18 @@ def get_user_by_id(user_id): def is_admin(): return session.get('is_admin', 0) == 1 +# ------------------------------------------------------------ +# STARTSEITE +# ------------------------------------------------------------ @app.route('/') def index(): if 'user_id' in session: return redirect(url_for('dashboard')) return redirect(url_for('login')) -# --------------------- REGISTRIEREN ------------------------- +# ------------------------------------------------------------ +# REGISTRIEREN: erster User wird Admin +# ------------------------------------------------------------ @app.route('/register', methods=['GET', 'POST']) def register(): if request.method == 'POST': @@ -114,7 +161,7 @@ def register(): # Erster registrierter User wird Admin count = db.execute("SELECT COUNT(*) as cnt FROM users").fetchone()['cnt'] - is_admin_val = 1 if count == 0 else 0 # Falls noch niemand existiert, ist das der Admin + is_admin_val = 1 if count == 0 else 0 hashed_pw = generate_password_hash(password) db.execute(""" @@ -122,11 +169,25 @@ def register(): VALUES (?, ?, ?, ?) """, (username, hashed_pw, email, is_admin_val)) db.commit() + + # OPTIONAL: initialer Eintrag in user_settings + if is_admin_val == 1: + # Falls du globale Settings pro Benutzer willst + user_id = db.execute("SELECT id FROM users WHERE username=?",(username,)).fetchone()['id'] + db.execute(""" + INSERT INTO user_settings (user_id, wallpaper, city, show_forecast, bookmarks) + VALUES (?, '1.png', '', 0, '') + """, (user_id,)) + db.commit() + flash('Registrierung erfolgreich! Bitte melde dich an.', 'green') return redirect(url_for('login')) + return render_template('register.html') -# --------------------- LOGIN/LOGOUT ------------------------- +# ------------------------------------------------------------ +# LOGIN / LOGOUT +# ------------------------------------------------------------ @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': @@ -149,33 +210,107 @@ def logout(): flash("Erfolgreich abgemeldet!", "green") return redirect(url_for('login')) -# --------------------- PASSWORT VERGESSEN ------------------- +# ------------------------------------------------------------ +# PASSWORT VERGESSEN +# ------------------------------------------------------------ +@app.route('/reset_password/', methods=['GET', 'POST']) +def reset_password(token): + db = get_db() + row = db.execute(""" + SELECT * FROM reset_tokens + WHERE token = ? + """, (token,)).fetchone() + if not row: + flash("Ungültiges oder bereits benutztes Token!", "red") + return redirect(url_for('forgot_password')) + + # Ablaufdatum prüfen + expires_at = datetime.fromisoformat(row['expires_at']) + if datetime.now() > expires_at: + flash("Token abgelaufen! Bitte erneut anfordern.", "red") + db.execute("DELETE FROM reset_tokens WHERE token=?", (token,)) + db.commit() + return redirect(url_for('forgot_password')) + + if request.method == 'POST': + pw1 = request.form.get('pw1') + pw2 = request.form.get('pw2') + if not pw1 or not pw2: + flash("Bitte beide Felder ausfüllen!", "red") + return redirect(url_for('reset_password', token=token)) + + if pw1 != pw2: + flash("Passwörter stimmen nicht überein!", "red") + return redirect(url_for('reset_password', token=token)) + + hashed_pw = generate_password_hash(pw1) + # Passwort setzen + db.execute("UPDATE users SET password=? WHERE id=?", (hashed_pw, row['user_id'])) + # Token direkt löschen + db.execute("DELETE FROM reset_tokens WHERE token=?", (token,)) + db.commit() + + flash("Passwort erfolgreich zurückgesetzt! Du kannst dich jetzt einloggen.", "green") + return redirect(url_for('login')) + + # GET: Formular + return render_template('reset_password.html', token=token) + @app.route('/forgot_password', methods=['GET', 'POST']) def forgot_password(): if request.method == 'POST': user_input = request.form.get('username_or_email') if not user_input: - flash("Bitte einen Benutzernamen oder eine E-Mail angeben!", "red") + flash("Bitte Benutzernamen oder E-Mail eingeben!", "red") return redirect(url_for('forgot_password')) - user = get_user_by_username_or_email(user_input) - # Hier könnte man z.B. eine E-Mail mit Link senden oder im Notfall ein neues PW setzen + db = get_db() + # Schaue nach Username oder Email + user = db.execute(""" + SELECT * FROM users + WHERE username=? OR email=? + """, (user_input, user_input)).fetchone() + if user: - # Demo: Wir setzen einfach das Passwort auf "reset123" (nicht sicher!) - # Besser: generiere Token, sende E-Mail etc. - new_pw_hashed = generate_password_hash("reset123") - db = get_db() - db.execute("UPDATE users SET password=? WHERE id=?", (new_pw_hashed, user['id'])) + # Token generieren + token = secrets.token_urlsafe(32) + expires = datetime.now() + timedelta(hours=1) + + db.execute(""" + INSERT INTO reset_tokens (user_id, token, expires_at) + VALUES (?, ?, ?) + """, (user['id'], token, expires)) db.commit() - flash("Dein Passwort wurde zurückgesetzt auf 'reset123'. Bitte ändere es nach dem Login!", "green") + + reset_link = f"{request.url_root}reset_password/{token}" + subject = "Passwort zurücksetzen" + body = f"""Hallo {user['username']}, + +klicke auf folgenden Link, um dein Passwort zurückzusetzen (gültig 1 Stunde): +{reset_link} + +Wenn du das nicht angefordert hast, ignoriere diese Nachricht. +""" + message = f"Subject: {subject}\n\n{body}" + context = ssl.create_default_context() + + try: + with smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT, context=context) as server: + server.login(EMAIL_SENDER, EMAIL_PASSWORD) + server.sendmail(EMAIL_SENDER, user['email'], message) + flash("Eine E-Mail zum Zurücksetzen deines Passworts wurde versendet!", "green") + except Exception as e: + flash(f"Fehler beim Senden der E-Mail: {e}", "red") + return redirect(url_for('login')) else: - flash("Kein passender Benutzer gefunden. Versuche es erneut!", "red") + flash("Kein Nutzer gefunden!", "red") return redirect(url_for('forgot_password')) return render_template('forgot_password.html') - -# --------------------- ADMIN-BEREICH ------------------------ +# ------------------------------------------------------------ +# ADMIN-BEREICH +# ------------------------------------------------------------ @app.route('/admin', methods=['GET', 'POST']) def admin_panel(): if not is_admin(): @@ -214,7 +349,9 @@ def delete_user(user_id): flash("Benutzer gelöscht!", "green") return redirect(url_for('admin_panel')) -# --------------------- NOTIFICATIONS ------------------------ +# ------------------------------------------------------------ +# BENACHRICHTIGUNGEN +# ------------------------------------------------------------ @app.route('/admin/notifications', methods=['POST']) def add_notification(): if not is_admin(): @@ -233,7 +370,9 @@ def add_notification(): flash("Benachrichtigung erstellt!", "green") return redirect(url_for('admin_panel')) -# --------------------- BOOKMARKS (nur Admin pflegbar) ------- +# ------------------------------------------------------------ +# BOOKMARKS +# ------------------------------------------------------------ @app.route('/admin/bookmarks/', methods=['GET', 'POST']) def manage_bookmarks(user_id): if not is_admin(): @@ -244,13 +383,14 @@ def manage_bookmarks(user_id): if not user: flash("Benutzer nicht gefunden!", "red") return redirect(url_for('admin_panel')) + if request.method == 'POST': title = request.form.get('title') - url = request.form.get('url') + url_ = request.form.get('url') icon = request.form.get('icon_class', 'fas fa-bookmark') - if title and url: + if title and url_: db.execute("INSERT INTO bookmarks (user_id, title, url, icon_class) VALUES (?, ?, ?, ?)", - (user_id, title, url, icon)) + (user_id, title, url_, icon)) db.commit() flash("Neues Lesezeichen hinzugefügt!", "green") @@ -268,7 +408,9 @@ def delete_bookmark(bookmark_id, user_id): flash("Lesezeichen gelöscht!", "green") return redirect(url_for('manage_bookmarks', user_id=user_id)) -# --------------------- ZEITERFASSUNG ------------------------ +# ------------------------------------------------------------ +# ZEITERFASSUNG +# ------------------------------------------------------------ @app.route('/time_tracking', methods=['POST']) def time_tracking(): if 'user_id' not in session: @@ -306,43 +448,193 @@ def time_tracking(): return redirect(url_for('dashboard')) -# --------------------- DASHBOARD ---------------------------- +# ------------------------------------------------------------ +# DASHBOARD +# ------------------------------------------------------------ @app.route('/dashboard') def dashboard(): if 'user_id' not in session: - flash("Bitte erst einloggen!", "red") + flash("Bitte melde dich an!", "red") return redirect(url_for('login')) - db = get_db() user_id = session['user_id'] - user = get_user_by_id(user_id) + db = get_db() - # Notifications (global oder speziell für diesen User) - notifications = db.execute(""" - SELECT * FROM notifications - WHERE user_id = ? OR user_id IS NULL - ORDER BY created_at DESC - """, (user_id,)).fetchall() + # User abfragen + user = db.execute("SELECT * FROM users WHERE id = ?", (user_id,)).fetchone() + if not user: + flash("Benutzer nicht gefunden.", "red") + return redirect(url_for('logout')) - # Bookmarks für diesen Benutzer - user_bookmarks = db.execute("SELECT * FROM bookmarks WHERE user_id = ?", (user_id,)).fetchall() + # Settings aus user_settings + settings = db.execute("SELECT * FROM user_settings WHERE user_id = ?", (user_id,)).fetchone() - # Time-Entries - time_entries = db.execute(""" - SELECT * FROM time_entries - WHERE user_id = ? - ORDER BY start_time DESC - """, (user_id,)).fetchall() + # Standardwerte + if not settings: + wallpaper = '19.png' + city = 'Berlin' + show_forecast = True + bookmarks = [] + else: + wallpaper = settings['wallpaper'] + city = settings['city'] + show_forecast = bool(settings['show_forecast']) + if settings['bookmarks']: + bookmarks = settings['bookmarks'].split(",") + else: + bookmarks = [] - return render_template('dashboard.html', - user=user['username'], - notifications=notifications, - user_bookmarks=user_bookmarks, - time_entries=time_entries, - domain="meinedomain.de", - logo_path="static/logo.png") + # Wetter holen (wenn gewünscht) + current_temp, weather_icon, forecast = get_weather(city) + if current_temp is None: + current_temp = "N/A" + weather_icon = "fa-question" + forecast = [] -# --------------------- STARTUP ------------------------------ + # Domain, Logo usw. + 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['username'], + 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 + ) + +# ------------------------------------------------------------ +# SUPPORT & SETTINGS ROUTEN (Aus V1) +# ------------------------------------------------------------ + +@app.route('/send_support_message', methods=['POST']) +def send_support_message(): + """ + Beispiel-Endpunkt, den dein Support-Modal per Fetch aufruft. + Erwartet JSON-Daten: { "email": ..., "problemType": ..., "message": ... } + """ + data = request.get_json() + email = data.get('email') + problem_type = data.get('problemType') + message = data.get('message') + + if not email or not message: + return jsonify({"success": False, "error": "Fehlende Felder"}), 400 + + # Hier könntest du z.B. eine E-Mail verschicken oder einen Eintrag in der DB anlegen + # Demo: wir legen einfach einen Notification-Eintrag an + db = get_db() + db.execute(""" + INSERT INTO notifications (user_id, message) + VALUES (NULL, ?) + """, (f"Support-Anfrage ({problem_type}) von {email}: {message}",)) + db.commit() + + return jsonify({"success": True}) + +@app.route('/get_settings', methods=['GET']) +def get_settings(): + """ + Liefert das aktuell gespeicherte Setting zurück (V1-Modal). + Du brauchst user_settings oder ein eigenes Modell, wo du die Bookmarks/Stadt/etc. speicherst + """ + if 'user_id' not in session: + return jsonify({"error": "not logged in"}), 403 + + db = get_db() + settings_row = db.execute("SELECT * FROM user_settings WHERE user_id=?", (session['user_id'],)).fetchone() + if settings_row: + return jsonify({ + "wallpaper": settings_row['wallpaper'], + "city": settings_row['city'], + "show_forecast": bool(settings_row['show_forecast']), + "bookmarks": settings_row['bookmarks'].split(",") if settings_row['bookmarks'] else [] + }) + else: + # Falls noch kein Datensatz existiert + return jsonify({ + "wallpaper": "1.png", + "city": "", + "show_forecast": False, + "bookmarks": [] + }) + +@app.route('/save_settings', methods=['POST']) +def save_settings(): + """ + Speichert die Einstellungen, die vom Settings-Modal per Fetch geschickt werden + JSON body: { "wallpaper": ..., "city": ..., "show_forecast": ..., "bookmarks": ... } + """ + if 'user_id' not in session: + return jsonify({"success": False, "error": "not logged in"}), 403 + + data = request.get_json() + wallpaper = data.get('wallpaper', '1.png') + city = data.get('city', '') + show_forecast = data.get('show_forecast', False) + bookmarks_list = data.get('bookmarks', []) + + db = get_db() + # Check if row exists + existing = db.execute("SELECT id FROM user_settings WHERE user_id=?", (session['user_id'],)).fetchone() + if existing: + db.execute(""" + UPDATE user_settings + SET wallpaper=?, city=?, show_forecast=?, bookmarks=? + WHERE user_id=? + """, (wallpaper, city, int(show_forecast), ",".join(bookmarks_list), session['user_id'])) + else: + db.execute(""" + INSERT INTO user_settings (user_id, wallpaper, city, show_forecast, bookmarks) + VALUES (?, ?, ?, ?, ?) + """, (session['user_id'], wallpaper, city, int(show_forecast), ",".join(bookmarks_list))) + db.commit() + + return jsonify({"success": True}) + +def get_weather(city): + """ + Gibt (current_temp, weather_icon, forecast) zurück + """ + # Hier kannst du z. B. eine Wetter-API anfragen + # oder Dummy-Werte für den Anfang setzen + + # DUMMY Beispiel: + current_temp = 24 + weather_icon = "fa-cloud" + forecast = [] + return (current_temp, weather_icon, forecast) + +# ------------------------------------------------------------ +# STARTUP +# ------------------------------------------------------------ if __name__ == '__main__': init_db() app.run(debug=True) diff --git a/Dashboard_V2/clickcandit.db b/Dashboard_V2/clickcandit.db index 45499997cecc08efbe9476d3ffd506abf541a82a..8a653139cd2a09b7a7a079fc7a799cf50f6b35e8 100644 GIT binary patch delta 1009 zcmb7@&rcIU6vuaIaoMe9Xb5QtqDulPO-S8eot@7MQcs>n!8eNSJ)-*GgHQdX%O{Z++%_9#R(p@$>`nG62T7KRkB-uZ z6CO6p#3i0rtQ6eyk(*o9XAPY0;B zma0_>i7AfnYF5$P>e6P2Q!ZA1qjeuu>p8pX5DU8=zqoAZH}utuJH)_wvdg{zfJ!0o2LQ~x{kf8z?j#*+UNZWA`N z?=t=vH%|987P#AVCVq*DPfXAU8!dFF`Nui0S|Sy`%w_Es0e+&%ywP+&TJmr6g z@`GfvRtz*b?D6wKei)0lbpK^^q;WIyE*wS_K#drm0y}#;%}~kxl_Xm(6Q_#OGllid zRGzq&vsLq^Wl4o-8cmDiiyIjOt8iPWWLD;v*XrXc#+qv9RYg!yS`r`;L;+#jA_$r` zk)QxY5o)5QKm{QcnOG1-%M#NvQB|0ip(<(6yaTZ!q|;D=Y0I=Mj7%9}#log#%9y}B z$y=IW%bJOTb0|-2*~XS4Nl=AS=YyNIMT1icoHkPCtNiXL!GfirO2h|PQk zR|a}Q3AR0h+&(%KZ|%5mv-H4gI(yOCmj4q9oCN;Af}z16ZtU@%{QG$nQirhfba zDq!IU@B{FS#K^+h0SSqTBC&KrjMzv_rLXi%&&T_`@8QSB;Wy{y%CG|fXjk|*&69O< zqXYb%l^*;JTAfwU{{TPtv!$Qi*PYdJIC$r|$9d~j+Z#LWa@~9AlyADvo6R)<*D7`d z500KVZ6J;x2``$aQ5NalI4vg6W;&hv(R`Liix+!ENU-2Clpz{V(bK)+zPiUVB=(c| z@mOn16-ok0g|*xm#Y|u^ z0X#2ID; - - - - Admin-Bereich - - - -

Admin-Bereich

+ +{% extends "base.html" %} +{% block title %}Admin-Bereich{% endblock %} + +{% block content %} +
+

Admin-Bereich

{% with messages = get_flashed_messages(with_categories=true) %} {% if messages %} @@ -18,122 +16,134 @@ {% endif %} {% endwith %} - -
-

Neuen Benutzer anlegen

-
-
- - -
-
- - -
-
- -
- -
+ +
+

Neuen Benutzer anlegen

+
+
+ + +
+
+ + +
+
+ +
+ +
- - {% if users is not none %} -
-

Benutzerverwaltung

- - - - - - - - - - - {% for user in users %} - - - - - - - {% endfor %} - -
IDBenutzernameAdmin?Aktion
{{ user.id }}{{ user.username }}{{ 'Ja' if user.is_admin else 'Nein' }} -
- -
- Lesezeichen -
-
- {% endif %} - - {% if single_user is defined and single_user %} - -
-

Lesezeichen für {{ single_user.username }}

-
-
- - -
-
- - -
-
- - -
- -
- - -
    - {% for bm in bookmarks %} -
  • - -
    - + +
    +

    Benutzerverwaltung

    + + + + + + + + + + + {% for u in users %} + + + + + + {% endfor %} - - - {% endif %} - - -
    -

    Benachrichtigung erstellen

    -
    -
    - - -
    -
    - - -
    - - +
    +
    IDUsernameAdmin?
    {{ u.id }}{{ u.username }}{{ 'Ja' if u.is_admin else 'Nein' }} + {% if u.id != session.user_id %} + + - + {% else %} + [Eigener Account] + {% endif %} + + Lesezeichen + +
    + +
    +

    Benachrichtigung erstellen

    +
    +
    + + +
    +
    + + +
    + +
    +
    + + + {% if single_user is defined and single_user %} +
    +

    + Lesezeichen für {{ single_user.username }} +

    +
    +
    + + +
    +
    + + +
    +
    + + +
    + +
    + + +
      + {% for bm in bookmarks %} +
    • + +
      + +
      +
    • + {% endfor %} +
    +
    + {% endif %} + - Zurück zum Dashboard + Zurück zum Dashboard - - +
+{% endblock content %} diff --git a/Dashboard_V2/templates/base.html b/Dashboard_V2/templates/base.html index 2e5238a..c95036b 100644 --- a/Dashboard_V2/templates/base.html +++ b/Dashboard_V2/templates/base.html @@ -11,7 +11,7 @@ - + {% endblock content %} diff --git a/Dashboard_V2/templates/reset_password.html b/Dashboard_V2/templates/reset_password.html new file mode 100644 index 0000000..7c652f2 --- /dev/null +++ b/Dashboard_V2/templates/reset_password.html @@ -0,0 +1,33 @@ +{% extends "base.html" %} +{% block title %}Passwort zurücksetzen{% endblock %} + +{% block content %} +
+

Neues Passwort setzen

+ + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, msg in messages %} +
+ {{ msg }} +
+ {% endfor %} + {% endif %} + {% endwith %} + +
+ + + + + + + +
+
+{% endblock content %}