Files
2025-02-21 22:12:13 +01:00

845 lines
30 KiB
Python

import sqlite3
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 = "fiudsh9uw4hefjsefnjdsdh"
DATABASE = 'clickcandit.db'
def get_db():
db = getattr(g, '_database', None)
if db is None:
db = g._database = sqlite3.connect(DATABASE)
db.row_factory = sqlite3.Row
return db
@app.teardown_appcontext
def close_connection(exception):
db = getattr(g, '_database', None)
if db is not None:
db.close()
def init_db():
with app.app_context():
db = get_db()
db.execute("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
email TEXT,
is_admin INTEGER DEFAULT 0
);
""")
db.execute("""
CREATE TABLE IF NOT EXISTS bookmarks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
url TEXT NOT NULL,
icon_class TEXT NOT NULL DEFAULT 'fas fa-bookmark',
bg_color TEXT NOT NULL DEFAULT 'fas fa-bookmark',
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
);
""")
db.execute("""
CREATE TABLE IF NOT EXISTS notifications (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER,
message TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
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
);
""")
db.execute("""
CREATE TABLE IF NOT EXISTS time_entries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
activity TEXT NOT NULL,
start_time DATETIME NOT NULL,
end_time DATETIME,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
);
""")
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.commit()
# ------------------------------------------------------------
# HILFSFUNKTIONEN
# ------------------------------------------------------------
def get_user_by_username_or_email(user_input):
db = get_db()
return db.execute("""
SELECT * FROM users
WHERE username = :val OR email = :val
""", {"val": user_input}).fetchone()
def get_user_by_username(username):
db = get_db()
return db.execute("SELECT * FROM users WHERE username = ?", (username,)).fetchone()
def get_user_by_id(user_id):
db = get_db()
return db.execute("SELECT * FROM users WHERE id = ?", (user_id,)).fetchone()
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: erster User wird Admin
# ------------------------------------------------------------
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST':
username = request.form.get('username')
email = request.form.get('email')
password = request.form.get('password')
if not username or not email or not password:
flash('Bitte alle Felder ausfüllen!', 'red')
return redirect(url_for('register'))
db = get_db()
# Prüfen, ob Benutzer/E-Mail existieren
existing = db.execute("SELECT * FROM users WHERE username=? OR email=?", (username, email)).fetchone()
if existing:
flash('Benutzername oder E-Mail ist bereits vergeben!', 'red')
return redirect(url_for('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
hashed_pw = generate_password_hash(password)
db.execute("""
INSERT INTO users (username, password, email, is_admin)
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
# ------------------------------------------------------------
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
user = get_user_by_username(username)
if user and check_password_hash(user['password'], password):
session['user_id'] = user['id']
session['username'] = user['username']
session['is_admin'] = user['is_admin']
flash("Login erfolgreich!", "green")
return redirect(url_for('dashboard'))
else:
flash("Benutzername oder Passwort falsch!", "red")
return render_template('login.html')
@app.route('/logout', methods=['GET', 'POST'])
def logout():
session.clear()
flash("Erfolgreich abgemeldet!", "green")
return redirect(url_for('login'))
# ------------------------------------------------------------
# PASSWORT VERGESSEN
# ------------------------------------------------------------
@app.route('/reset_password/<token>', 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 Benutzernamen oder E-Mail eingeben!", "red")
return redirect(url_for('forgot_password'))
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:
# 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()
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 Nutzer gefunden!", "red")
return redirect(url_for('forgot_password'))
return render_template('forgot_password.html')
# ------------------------------------------------------------
# ADMIN-BEREICH
# ------------------------------------------------------------
@app.route('/admin', methods=['GET', 'POST'])
def admin_panel():
if not is_admin():
flash("Zugriff verweigert!", "red")
return redirect(url_for('dashboard'))
db = get_db()
if request.method == 'POST':
new_username = request.form.get('new_username')
new_password = request.form.get('new_password')
new_is_admin = 1 if request.form.get('new_is_admin') == 'on' else 0
if new_username and new_password:
hashed_pw = generate_password_hash(new_password)
try:
db.execute("INSERT INTO users (username, password, is_admin) VALUES (?, ?, ?)",
(new_username, hashed_pw, new_is_admin))
db.commit()
flash("Neuer Benutzer erstellt!", "green")
except sqlite3.IntegrityError:
flash("Benutzername bereits vorhanden!", "red")
users = db.execute("SELECT * FROM users").fetchall()
return render_template('admin.html', users=users)
@app.route('/admin/delete_user/<int:user_id>', methods=['POST'])
def delete_user(user_id):
if not is_admin():
flash("Zugriff verweigert!", "red")
return redirect(url_for('dashboard'))
if user_id == session.get('user_id'):
flash("Du kannst dich nicht selbst löschen!", "red")
return redirect(url_for('admin_panel'))
db = get_db()
db.execute("DELETE FROM users WHERE id=?", (user_id,))
db.commit()
flash("Benutzer gelöscht!", "green")
return redirect(url_for('admin_panel'))
# ------------------------------------------------------------
# BENACHRICHTIGUNGEN
# ------------------------------------------------------------
@app.route('/admin/notifications', methods=['POST'])
def add_notification():
if not is_admin():
flash("Zugriff verweigert!", "red")
return redirect(url_for('dashboard'))
message = request.form.get('message')
user_id = request.form.get('user_id')
if message:
db = get_db()
if user_id == "all":
db.execute("INSERT INTO notifications (user_id, message) VALUES (NULL, ?)", (message,))
else:
db.execute("INSERT INTO notifications (user_id, message) VALUES (?, ?)", (user_id, message))
db.commit()
flash("Benachrichtigung erstellt!", "green")
return redirect(url_for('admin_panel'))
# ------------------------------------------------------------
# BOOKMARKS
# ------------------------------------------------------------
@app.route('/admin/bookmarks/<int:user_id>', methods=['GET', 'POST'])
def manage_bookmarks(user_id):
if not is_admin():
flash("Zugriff verweigert!", "red")
return redirect(url_for('dashboard'))
db = get_db()
user = get_user_by_id(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')
icon = request.form.get('icon_class', 'fas fa-bookmark')
if title and url_:
db.execute("INSERT INTO bookmarks (user_id, title, url, icon_class) VALUES (?, ?, ?, ?)",
(user_id, title, url_, icon))
db.commit()
flash("Neues Lesezeichen hinzugefügt!", "green")
bookmarks = db.execute("SELECT * FROM bookmarks WHERE user_id=?", (user_id,)).fetchall()
return render_template('admin.html', single_user=user, bookmarks=bookmarks)
@app.route('/bookmarks/delete/<int:bookmark_id>', methods=['POST'])
def delete_bookmark(bookmark_id):
"""Löscht ein Lesezeichen, wenn der Benutzer es besitzt oder Admin ist."""
if 'user_id' not in session:
flash("Bitte melde dich an!", "red")
return redirect(url_for('login'))
user_id = session['user_id']
db = get_db()
# Prüfen, ob der Benutzer das Lesezeichen besitzt
bookmark = db.execute("SELECT user_id FROM bookmarks WHERE id=?", (bookmark_id,)).fetchone()
if not bookmark:
flash("Lesezeichen nicht gefunden!", "red")
return redirect(url_for('dashboard'))
# Benutzer darf nur eigene Lesezeichen löschen, Admin kann alle löschen
if bookmark['user_id'] != user_id and not is_admin():
flash("Keine Berechtigung zum Löschen!", "red")
return redirect(url_for('dashboard'))
db.execute("DELETE FROM bookmarks WHERE id=?", (bookmark_id,))
db.commit()
flash("Lesezeichen gelöscht!", "green")
return redirect(url_for('dashboard'))
# ------------------------------------------------------------
# ZEITERFASSUNG
# ------------------------------------------------------------
@app.route('/time_tracking', methods=['POST'])
def time_tracking():
if 'user_id' not in session:
flash("Bitte erst einloggen!", "red")
return redirect(url_for('login'))
action = request.form.get('action')
activity = request.form.get('activity')
db = get_db()
if action == 'start':
if activity:
db.execute("""
INSERT INTO time_entries (user_id, activity, start_time)
VALUES (?, ?, ?)
""", (session['user_id'], activity, datetime.now()))
db.commit()
flash("Zeiterfassung gestartet!", "green")
else:
flash("Bitte einen Aktivitätsnamen angeben!", "red")
elif action == 'stop':
open_entry = db.execute("""
SELECT * FROM time_entries
WHERE user_id = ? AND end_time IS NULL
ORDER BY start_time DESC
LIMIT 1
""", (session['user_id'],)).fetchone()
if open_entry:
db.execute("UPDATE time_entries SET end_time=? WHERE id=?", (datetime.now(), open_entry['id']))
db.commit()
flash("Zeiterfassung gestoppt!", "green")
else:
flash("Keine laufende Zeiterfassung gefunden!", "red")
return redirect(url_for('dashboard'))
@app.route('/admin/time_tracking', methods=['GET'])
def admin_time_tracking():
if not is_admin():
flash("Zugriff verweigert!", "red")
return redirect(url_for('dashboard'))
db = get_db()
# Hole alle Zeiteinträge (JOIN mit users, damit wir den username sehen)
time_entries = db.execute("""
SELECT t.id, t.user_id, t.activity, t.start_time, t.end_time,
u.username
FROM time_entries t
JOIN users u ON t.user_id = u.id
ORDER BY t.start_time DESC
""").fetchall()
return render_template('admin_time_entries.html', time_entries=time_entries)
# ------------------------------------------------------------
# DASHBOARD
# ------------------------------------------------------------
@app.route('/dashboard')
def dashboard():
if 'user_id' not in session:
flash("Bitte melde dich an!", "red")
return redirect(url_for('login'))
user_id = session['user_id']
db = get_db()
# User und Settings ermitteln
user = db.execute("SELECT * FROM users WHERE id = ?", (user_id,)).fetchone()
if not user:
flash("Benutzer nicht gefunden.", "red")
return redirect(url_for('logout'))
settings = db.execute("SELECT * FROM user_settings WHERE user_id = ?", (user_id,)).fetchone()
# Standardwerte aus settings
if not settings:
wallpaper = '19.png'
city = 'Berlin'
show_forecast = True
# (Dieses 'bookmarks' ist optional, kannst du weglassen,
# wenn du ausschließlich die DB-Tabelle 'bookmarks' nutzt)
bookmarks_setting = []
else:
wallpaper = settings['wallpaper']
city = settings['city']
show_forecast = bool(settings['show_forecast'])
if settings['bookmarks']:
bookmarks_setting = settings['bookmarks'].split(",") # Nur als Fallback
else:
bookmarks_setting = []
# DB-Bookmarks für den eingeloggten User
user_bookmarks = db.execute("""
SELECT id, title, url, icon_class, bg_color
FROM bookmarks
WHERE user_id=?
ORDER BY id DESC
""", (user_id,)).fetchall()
# Notifications für diesen User (user_id) oder globale (user_id IS NULL)
notifications = db.execute("""
SELECT id, user_id, message, created_at
FROM notifications
WHERE user_id=? OR user_id IS NULL
ORDER BY created_at DESC
""", (user_id,)).fetchall()
# Ggf. Time-Entries (falls du sie zeigen willst)
time_entries = db.execute("""
SELECT *
FROM time_entries
WHERE user_id=?
ORDER BY start_time DESC
""", (user_id,)).fetchall()
# 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'
},
]
]
# Wetter (Dummy oder API)
current_temp, weather_icon, forecast = get_weather(city)
if current_temp is None:
current_temp = "N/A"
weather_icon = "fa-question"
forecast = []
domain = "clickcandit.com/login"
logo_path = url_for('static', filename='clickcandit.png')
return render_template(
'dashboard.html',
user=user['username'],
wallpaper=wallpaper,
city=city,
show_forecast=show_forecast,
bookmarks=bookmarks_setting, # falls noch gebraucht, sonst weglassen
current_temp=current_temp,
weather_icon=weather_icon,
forecast=forecast,
domain=domain,
logo_path=logo_path,
user_app_chunks=user_app_chunks,
# Hier die neuen Variablen:
user_bookmarks=user_bookmarks,
notifications=notifications,
time_entries=time_entries
)
# ------------------------------------------------------------
# SUPPORT & SETTINGS ROUTEN (Aus V1)
# ------------------------------------------------------------
@app.route('/delete_notification/<int:notif_id>', methods=['POST'])
def delete_notification(notif_id):
if 'user_id' not in session:
flash("Bitte melde dich an!", "red")
return redirect(url_for('login'))
user_id = session['user_id']
db = get_db()
# Prüfen, ob die Notification existiert
row = db.execute("""
SELECT user_id
FROM notifications
WHERE id=?
""", (notif_id,)).fetchone()
if not row:
flash("Benachrichtigung existiert nicht.", "red")
return redirect(url_for('dashboard'))
# Wenn user_id = NULL => globale Notification
# Du kannst selbst definieren, ob jeder sie löschen darf oder nur Admin
# Hier: Jeder kann globale Notification löschen, wenn er sie sieht.
# Oder du sagst: Nur Admin kann globale Notis löschen -> if row['user_id'] is None and not is_admin(): ...
if row['user_id'] is None:
# Optional: Nur Admin löschen
if not is_admin():
flash('Nicht erlaubt', 'red')
return redirect(url_for('dashboard'))
pass
else:
# Wenn es eine user-spezifische Notification ist: user_id muss übereinstimmen
if row['user_id'] != user_id:
flash("Keine Berechtigung, diese Benachrichtigung zu löschen.", "red")
return redirect(url_for('dashboard'))
# Benachrichtigung löschen
db.execute("DELETE FROM notifications WHERE id=?", (notif_id,))
db.commit()
flash("Benachrichtigung gelöscht!", "green")
return redirect(url_for('dashboard'))
@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": "24.png",
"city": "",
"show_forecast": False,
"bookmarks": []
})
@app.context_processor
def inject_wallpaper():
"""
Dieser Context Processor wird bei jedem Template-Aufruf ausgeführt.
Ermittelt das Wallpaper des eingeloggten Benutzers (falls eingeloggt)
und stellt es allen Templates als Variable 'WALLPAPER_URL' bereit.
"""
if 'user_id' in session:
db = get_db()
row = db.execute("SELECT wallpaper FROM user_settings WHERE user_id=?", (session['user_id'],)).fetchone()
if row and row['wallpaper']:
# Bsp: row['wallpaper'] könnte '19.png' o. ä. sein
return {
"WALLPAPER_URL": url_for('static', filename=row['wallpaper'])
}
# Fallback: Wenn User nicht eingeloggt oder kein Wallpaper gesetzt
return {
"WALLPAPER_URL": url_for('static', filename='24.png')
}
@app.route('/admin/notifications/multi', methods=['POST'])
def add_notification_multi():
if not is_admin():
flash("Zugriff verweigert!", "red")
return redirect(url_for('dashboard'))
message = request.form.get('message')
target_list = request.form.getlist('target_users') # ['all'] oder ['1','2'] etc.
if not message:
flash("Bitte eine Nachricht eingeben.", "red")
return redirect(url_for('admin_panel'))
db = get_db()
# Wenn 'all' in der Liste, Notification für alle
if 'all' in target_list:
db.execute("INSERT INTO notifications (user_id, message) VALUES (NULL, ?)", (message,))
else:
# Sonst für jede ausgewählte ID
for uid in target_list:
db.execute("INSERT INTO notifications (user_id, message) VALUES (?, ?)", (uid, message))
db.commit()
flash("Benachrichtigungen erstellt!", "green")
return redirect(url_for('admin_panel'))
# ------------------------------------------------------------
# BENUTZER-LESEZEICHEN (nur für sich selbst)
# ------------------------------------------------------------
@app.route('/bookmarks/add', methods=['POST'])
def add_bookmark():
if 'user_id' not in session:
flash("Bitte melde dich an!", "red")
return redirect(url_for('login'))
user_id = session['user_id']
title = request.form.get('title')
url_ = request.form.get('url')
icon_class = request.form.get('icon_class', 'fas fa-bookmark')
bg_color = request.form.get('bg_color', 'bg-blue-500') # Standardfarbe
if not title or not url_:
flash("Bitte Titel und URL angeben!", "red")
return redirect(url_for('dashboard'))
db = get_db()
db.execute(
"INSERT INTO bookmarks (user_id, title, url, icon_class, bg_color) VALUES (?, ?, ?, ?, ?)",
(user_id, title, url_, icon_class, bg_color)
)
db.commit()
flash("Lesezeichen erfolgreich hinzugefügt!", "green")
return redirect(url_for('dashboard'))
@app.route('/admin/bookmarks/multi', methods=['POST'])
def add_bookmarks_multi():
if not is_admin():
flash("Zugriff verweigert!", "red")
return redirect(url_for('dashboard'))
# Checkboxen im Template: name='target_users'
target_list = request.form.getlist('target_users') # Liste der IDs (Strings)
title = request.form.get('title')
url_ = request.form.get('url')
icon = request.form.get('icon_class', 'fas fa-bookmark')
# Ggf. Validierung
if not title or not url_:
flash("Bitte Titel und URL angeben!", "red")
return redirect(url_for('admin_panel'))
if not target_list:
flash("Bitte mindestens einen Benutzer auswählen!", "red")
return redirect(url_for('admin_panel'))
icon = request.form.get('icon_class')
db = get_db()
for uid in target_list:
db.execute(
"INSERT INTO bookmarks (user_id, title, url, icon_class) VALUES (?, ?, ?, ?)",
(uid, title, url_, icon)
)
db.commit()
flash("Neues Lesezeichen für mehrere Benutzer hinzugefügt!", "green")
return redirect(url_for('admin_panel'))
@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', '24.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, port=9559, host="0.0.0.0")