- New timeline page showing all groups and active events in parallel - Group order API endpoints with persistence (GET/POST /api/groups/order) - Customizable group ordering with visual controls - Fix CSS and TypeScript lint errors - Update documentation and bump version to 2026.1.0-alpha.14
89 lines
4.3 KiB
Python
89 lines
4.3 KiB
Python
from sqlalchemy import create_engine, text
|
||
import os
|
||
from dotenv import load_dotenv
|
||
import bcrypt
|
||
|
||
# .env laden (nur in Dev)
|
||
if os.getenv("ENV", "development") == "development":
|
||
load_dotenv()
|
||
|
||
# Use same logic as database.py: prefer DB_CONN, fallback to individual vars
|
||
DB_URL = os.getenv("DB_CONN")
|
||
if not DB_URL:
|
||
DB_USER = os.getenv("DB_USER", "infoscreen_admin")
|
||
DB_PASSWORD = os.getenv("DB_PASSWORD")
|
||
# In Docker Compose: DB_HOST will be 'db' from env
|
||
# In dev container: will be 'localhost' from .env
|
||
DB_HOST = os.getenv("DB_HOST", "db") # Default to 'db' for Docker Compose
|
||
DB_NAME = os.getenv("DB_NAME", "infoscreen_by_taa")
|
||
DB_URL = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:3306/{DB_NAME}"
|
||
|
||
print(f"init_defaults.py connecting to: {DB_URL.split('@')[1] if '@' in DB_URL else DB_URL}")
|
||
engine = create_engine(DB_URL, isolation_level="AUTOCOMMIT")
|
||
|
||
with engine.connect() as conn:
|
||
# Default-Gruppe mit id=1 anlegen, falls nicht vorhanden
|
||
result = conn.execute(
|
||
text("SELECT COUNT(*) FROM client_groups WHERE id=1"))
|
||
if result.scalar() == 0:
|
||
conn.execute(
|
||
text(
|
||
"INSERT INTO client_groups (id, name, is_active) VALUES (1, 'Nicht zugeordnet', 1)")
|
||
)
|
||
print("✅ Default-Gruppe mit id=1 angelegt.")
|
||
|
||
# Superadmin-Benutzer anlegen, falls nicht vorhanden
|
||
admin_user = os.getenv("DEFAULT_SUPERADMIN_USERNAME", "superadmin")
|
||
admin_pw = os.getenv("DEFAULT_SUPERADMIN_PASSWORD")
|
||
|
||
if not admin_pw:
|
||
print("⚠️ DEFAULT_SUPERADMIN_PASSWORD nicht gesetzt. Superadmin wird nicht erstellt.")
|
||
else:
|
||
# Passwort hashen mit bcrypt
|
||
hashed_pw = bcrypt.hashpw(admin_pw.encode(
|
||
'utf-8'), bcrypt.gensalt()).decode('utf-8')
|
||
# Prüfen, ob User existiert
|
||
result = conn.execute(text(
|
||
"SELECT COUNT(*) FROM users WHERE username=:username"), {"username": admin_user})
|
||
if result.scalar() == 0:
|
||
# Rolle: 'superadmin' gemäß UserRole enum
|
||
conn.execute(
|
||
text("INSERT INTO users (username, password_hash, role, is_active) VALUES (:username, :password_hash, 'superadmin', 1)"),
|
||
{"username": admin_user, "password_hash": hashed_pw}
|
||
)
|
||
print(f"✅ Superadmin-Benutzer '{admin_user}' angelegt.")
|
||
else:
|
||
print(f"ℹ️ Superadmin-Benutzer '{admin_user}' existiert bereits.")
|
||
|
||
# Default System Settings anlegen
|
||
default_settings = [
|
||
('supplement_table_url', '', 'URL für Vertretungsplan / WebUntis (Stundenplan-Änderungstabelle)'),
|
||
('supplement_table_enabled', 'false', 'Ob Vertretungsplan aktiviert ist'),
|
||
('presentation_interval', '10', 'Standard Intervall für Präsentationen (Sekunden)'),
|
||
('presentation_page_progress', 'true', 'Seitenfortschrift anzeigen (Page-Progress) für Präsentationen'),
|
||
('presentation_auto_progress', 'true', 'Automatischer Fortschritt (Auto-Progress) für Präsentationen'),
|
||
('video_autoplay', 'true', 'Autoplay (automatisches Abspielen) für Videos'),
|
||
('video_loop', 'true', 'Loop (Wiederholung) für Videos'),
|
||
('video_volume', '0.8', 'Standard Lautstärke für Videos (0.0 - 1.0)'),
|
||
('holiday_banner_enabled', 'true', 'Ferienstatus-Banner auf Dashboard anzeigen'),
|
||
('organization_name', '', 'Name der Organisation (wird im Header angezeigt)'),
|
||
('refresh_seconds', '0', 'Scheduler Republish-Intervall (Sekunden; 0 deaktiviert)'),
|
||
('group_order', '[]', 'Benutzerdefinierte Reihenfolge der Raumgruppen (JSON-Array mit Group-IDs)'),
|
||
]
|
||
|
||
for key, value, description in default_settings:
|
||
result = conn.execute(
|
||
text("SELECT COUNT(*) FROM system_settings WHERE `key`=:key"),
|
||
{"key": key}
|
||
)
|
||
if result.scalar() == 0:
|
||
conn.execute(
|
||
text("INSERT INTO system_settings (`key`, value, description) VALUES (:key, :value, :description)"),
|
||
{"key": key, "value": value, "description": description}
|
||
)
|
||
print(f"✅ System-Einstellung '{key}' angelegt.")
|
||
else:
|
||
print(f"ℹ️ System-Einstellung '{key}' existiert bereits.")
|
||
|
||
print("✅ Initialisierung abgeschlossen.")
|