Login Funktioniert. Dark Mode. Match Formular Seite erstellt.

This commit is contained in:
Daniel Nagel 2026-02-25 14:26:24 +00:00
parent 296d170442
commit 01256cba0d
6 changed files with 263 additions and 0 deletions

19
.gitignore vendored Normal file
View File

@ -0,0 +1,19 @@
# 1. Datenbanken
*.db
*.sqlite3
# 2. Python Caches
__pycache__/
*.py[cod]
# 3. Virtuelle Umgebungen
venv/
.venv/
env/
# 4. Geheime Konfigurationen (Umgebungsvariablen)
.env
# 5. IDE / Editor Einstellungen
.vscode/
.idea/

View File

@ -0,0 +1 @@
{"authenticated":true,"discord_id":"277898241750859776","discord_name":"mrteels","db_id":1}

91
database.py Normal file
View File

@ -0,0 +1,91 @@
import sqlite3
def init_db():
# Neue englische Datenbank-Datei
connection = sqlite3.connect("warhammer_league.db")
cursor = connection.cursor()
# Fremdschlüssel (Foreign Keys) in SQLite aktivieren
cursor.execute('PRAGMA foreign_keys = ON;')
# Tabelle: players (ehemals spieler)
cursor.execute('''
CREATE TABLE IF NOT EXISTS players (
id INTEGER PRIMARY KEY AUTOINCREMENT,
discord_id TEXT UNIQUE,
name TEXT NOT NULL,
mmr INTEGER DEFAULT 1000,
points INTEGER DEFAULT 0,
games INTEGER DEFAULT 0
)
''')
# Tabelle: matches (ehemals spiele)
cursor.execute('''
CREATE TABLE IF NOT EXISTS matches (
id INTEGER PRIMARY KEY AUTOINCREMENT,
player1_id INTEGER,
score_player1 INTEGER,
player2_id INTEGER,
score_player2 INTEGER,
FOREIGN KEY (player1_id) REFERENCES players (id),
FOREIGN KEY (player2_id) REFERENCES players (id)
)
''')
connection.commit()
connection.close()
print("Warhammer database successfully initialized!")
def get_or_create_player(discord_id, discord_name):
# 1. Verbindung öffnen
connection = sqlite3.connect("warhammer_league.db")
cursor = connection.cursor()
# 2. Suchen, ob der Spieler schon existiert
cursor.execute("SELECT id, name, mmr, points, games FROM players WHERE discord_id = ?", (discord_id,))
player = cursor.fetchone()
# 3. Wenn der Spieler nicht gefunden wurde (ist "None")
if player is None:
# Neu anlegen
cursor.execute("INSERT INTO players (discord_id, name) VALUES (?, ?)", (discord_id, discord_name))
connection.commit()
# Den frisch angelegten Spieler direkt wieder auslesen, um seine neue ID zu bekommen
cursor.execute("SELECT id, name, mmr, points, games FROM players WHERE discord_id = ?", (discord_id,))
player = cursor.fetchone()
# 4. Verbindung schließen
connection.close()
# 5. Daten zurückgeben
return player
if __name__ == "__main__":
init_db()
def get_all_players():
connection = sqlite3.connect("warhammer_league.db")
cursor = connection.cursor()
# Alle Spieler laden, absteigend sortiert nach MMR
cursor.execute("SELECT id, name, mmr, points, games FROM players ORDER BY mmr DESC")
players = cursor.fetchall()
connection.close()
# Die Daten für das Web-GUI in eine lesbare Form umwandeln (Liste von Dictionaries)
result = []
for player in players:
result.append({
'id': player[0],
'name': player[1],
'mmr': player[2],
'points': player[3],
'games': player[4]
})
return result

View File

@ -0,0 +1,110 @@
import os
import urllib.parse
import requests
from nicegui import ui, app
# NEU: Wir importieren unsere eigene Datenbank-Datei
import database
def setup_routes():
client_id = os.getenv("DISCORD_CLIENT_ID")
client_secret = os.getenv("DISCORD_CLIENT_SECRET")
app_url = os.getenv("APP_URL")
redirect_uri = f"{app_url}/login/discord"
encoded_redirect_uri = urllib.parse.quote(redirect_uri, safe="")
discord_auth_url = f"https://discord.com/api/oauth2/authorize?client_id={client_id}&redirect_uri={encoded_redirect_uri}&response_type=code&scope=identify"
# --- HOME PAGE ---
@ui.page('/')
def home_page():
ui.dark_mode(True)
with ui.card().classes('w-full items-center mt-10'):
ui.label('Warhammer League').classes('text-2xl font-bold')
# --- LOGIN BEREICH ---
if app.storage.user.get('authenticated', False):
discord_name = app.storage.user.get('discord_name')
db_id = app.storage.user.get('db_id')
ui.label(f'Welcome back, {discord_name}!').classes('text-green-500 font-bold')
ui.button('Enter Match', on_click=lambda: ui.navigate.to('/add-match')).classes('mt-4 bg-blue-500 text-white')
def logout():
app.storage.user.clear()
ui.navigate.to('/')
ui.button('Logout', on_click=logout).classes('mt-4 bg-red-500 text-white')
else:
ui.button('Login with Discord', on_click=lambda: ui.navigate.to(discord_auth_url))
# --- LEADERBOARD BEREICH ---
with ui.card().classes('w-full items-center mt-6'):
ui.label('Leaderboard').classes('text-xl font-bold mb-4')
# 1. Spalten (Columns) für die Tabelle definieren
table_columns = [
{'name': 'name', 'label': 'Player', 'field': 'name', 'align': 'left'},
{'name': 'mmr', 'label': 'MMR', 'field': 'mmr', 'sortable': True},
{'name': 'points', 'label': 'Points', 'field': 'points', 'sortable': True},
{'name': 'games', 'label': 'Games Played', 'field': 'games', 'sortable': True},
]
# 2. Daten (Rows) aus der Datenbank holen
player_data = database.get_all_players()
# 3. Tabelle zeichnen
ui.table(columns=table_columns, rows=player_data, row_key='name').classes('w-full max-w-4xl')
# --- DISCORD CALLBACK PAGE ---
@ui.page('/login/discord')
def discord_callback(code: str = None):
if not code:
ui.label('Fehler: Kein Code erhalten.').classes('text-red-500')
return
token_data = {
'client_id': client_id,
'client_secret': client_secret,
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': redirect_uri
}
token_response = requests.post('https://discord.com/api/oauth2/token', data=token_data)
token_json = token_response.json()
if 'access_token' in token_json:
access_token = token_json['access_token']
user_headers = {
'Authorization': f"Bearer {access_token}"
}
user_response = requests.get('https://discord.com/api/users/@me', headers=user_headers)
user_json = user_response.json()
# --- NEU: DATENBANK INTEGRATION ---
discord_id = user_json['id']
discord_name = user_json['username']
# 1. Spieler in der DB suchen oder neu anlegen
player = database.get_or_create_player(discord_id, discord_name)
# 2. Session-Daten aktualisieren
app.storage.user['authenticated'] = True
app.storage.user['discord_id'] = discord_id
app.storage.user['discord_name'] = discord_name
# player ist ein Tuple aus der DB, z.B.: (1, "123456", "Daniel", 1000, 0, 0)
# player[0] ist die interne Datenbank-ID. Diese merken wir uns in der Session!
app.storage.user['db_id'] = player[0]
ui.navigate.to('/')
else:
ui.label('Fehler beim Login!').classes('text-red-500')
ui.label(str(token_json))

23
gui/match_gui.py Normal file
View File

@ -0,0 +1,23 @@
from nicegui import ui, app
def setup_match_routes():
# Unsere neue Unterseite für das Eintragen der Spiele
@ui.page('/add-match')
def add_match_page():
with ui.card().classes('w-full items-center mt-10'):
ui.label('Enter New Match').classes('text-2xl font-bold')
# --- SICHERHEITS-CHECK ---
# Prüfen, ob der User wirklich eingeloggt ist.
# Wenn nicht, brechen wir hier sofort ab!
if not app.storage.user.get('authenticated', False):
ui.label('Access Denied. Please log in first.').classes('text-red-500')
ui.button('Back to Home', on_click=lambda: ui.navigate.to('/'))
return
# --- PLATZHALTER ---
ui.label('Hier kommt im nächsten Schritt das Eingabe-Formular hin!').classes('text-gray-500 my-4')
# Ein Button, um wieder zurück zur Startseite zu kommen
ui.button('Cancel', on_click=lambda: ui.navigate.to('/')).classes('bg-gray-500 text-white')

19
main.py
View File

@ -0,0 +1,19 @@
import os
from dotenv import load_dotenv
from nicegui import ui
from gui import main_gui
from gui import match_gui
# 1. Lade die geheimen Variablen aus der .env Datei in den Speicher
load_dotenv()
# 2. Variablen abrufen
client_id = os.getenv("DISCORD_CLIENT_ID")
client_secret = os.getenv("DISCORD_CLIENT_SECRET")
# 3. Wir rufen unsere Funktion aus der main_gui.py auf
main_gui.setup_routes()
match_gui.setup_match_routes()
# 4. Wir starten die NiceGUI App
ui.run(title="Warhammer Liga", port=9000, storage_secret="ein_sehr_geheimes_passwort_fuer_die_cookies")