Refactor: Email statt Username als Anzeige-Identifier
- Header zeigt volle Email statt Username - Lagen-Karten/Sidebar zeigen Email-Prefix (vor dem @) als Ersteller - Feedback-Emails nutzen Email-Prefix statt Username - Login/Notification-Emails nutzen Email-Prefix als Anrede - DB-Queries holen email statt username für Ersteller-Anzeige Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -294,7 +294,7 @@ async def _send_email_notifications_for_incident(
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
subject, html = incident_notification_email(
|
subject, html = incident_notification_email(
|
||||||
username=prefs["username"],
|
username=prefs["email"].split("@")[0],
|
||||||
incident_title=incident_title,
|
incident_title=incident_title,
|
||||||
notifications=filtered_notifications,
|
notifications=filtered_notifications,
|
||||||
dashboard_url=dashboard_url,
|
dashboard_url=dashboard_url,
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ async def request_magic_link(
|
|||||||
|
|
||||||
# E-Mail senden
|
# E-Mail senden
|
||||||
link = f"{MAGIC_LINK_BASE_URL}/?token={token}"
|
link = f"{MAGIC_LINK_BASE_URL}/?token={token}"
|
||||||
subject, html = magic_link_login_email(user["username"], code, link)
|
subject, html = magic_link_login_email(user["email"].split("@")[0], code, link)
|
||||||
await send_email(email, subject, html)
|
await send_email(email, subject, html)
|
||||||
|
|
||||||
magic_link_limiter.record(email, ip)
|
magic_link_limiter.record(email, ip)
|
||||||
|
|||||||
@@ -63,12 +63,12 @@ async def send_feedback(
|
|||||||
detail="E-Mail-Versand nicht verfuegbar.",
|
detail="E-Mail-Versand nicht verfuegbar.",
|
||||||
)
|
)
|
||||||
|
|
||||||
username = current_user["username"]
|
|
||||||
email = current_user.get("email", "")
|
email = current_user.get("email", "")
|
||||||
|
display_name = email.split("@")[0] if email else "Unbekannt"
|
||||||
category_label = CATEGORY_LABELS.get(data.category, data.category)
|
category_label = CATEGORY_LABELS.get(data.category, data.category)
|
||||||
message_escaped = html.escape(data.message)
|
message_escaped = html.escape(data.message)
|
||||||
|
|
||||||
subject = f"[AegisSight Feedback] {category_label} von {username}"
|
subject = f"[AegisSight Feedback] {category_label} von {display_name}"
|
||||||
html_body = f"""\
|
html_body = f"""\
|
||||||
<div style="font-family:Arial,sans-serif;max-width:600px;margin:0 auto;">
|
<div style="font-family:Arial,sans-serif;max-width:600px;margin:0 auto;">
|
||||||
<div style="background:#151D2E;color:#E8ECF4;padding:20px;border-radius:8px 8px 0 0;">
|
<div style="background:#151D2E;color:#E8ECF4;padding:20px;border-radius:8px 8px 0 0;">
|
||||||
@@ -77,7 +77,7 @@ async def send_feedback(
|
|||||||
<div style="background:#1A2440;color:#E8ECF4;padding:20px;border-radius:0 0 8px 8px;">
|
<div style="background:#1A2440;color:#E8ECF4;padding:20px;border-radius:0 0 8px 8px;">
|
||||||
<table style="border-collapse:collapse;">
|
<table style="border-collapse:collapse;">
|
||||||
<tr><td style="color:#8896AB;padding:4px 16px 4px 0;">Kategorie:</td><td><strong>{category_label}</strong></td></tr>
|
<tr><td style="color:#8896AB;padding:4px 16px 4px 0;">Kategorie:</td><td><strong>{category_label}</strong></td></tr>
|
||||||
<tr><td style="color:#8896AB;padding:4px 16px 4px 0;">Nutzer:</td><td>{html.escape(username)}</td></tr>
|
<tr><td style="color:#8896AB;padding:4px 16px 4px 0;">Nutzer:</td><td>{html.escape(display_name)}</td></tr>
|
||||||
<tr><td style="color:#8896AB;padding:4px 16px 4px 0;">E-Mail:</td><td>{html.escape(email) if email else "nicht hinterlegt"}</td></tr>
|
<tr><td style="color:#8896AB;padding:4px 16px 4px 0;">E-Mail:</td><td>{html.escape(email) if email else "nicht hinterlegt"}</td></tr>
|
||||||
</table>
|
</table>
|
||||||
<hr style="border:none;border-top:1px solid #1E2D45;margin:16px 0;">
|
<hr style="border:none;border-top:1px solid #1E2D45;margin:16px 0;">
|
||||||
@@ -92,7 +92,7 @@ async def send_feedback(
|
|||||||
if email:
|
if email:
|
||||||
msg["Reply-To"] = email
|
msg["Reply-To"] = email
|
||||||
|
|
||||||
text_fallback = f"Feedback von {username} ({category_label}):\n\n{data.message}"
|
text_fallback = f"Feedback von {display_name} ({category_label}):\n\n{data.message}"
|
||||||
msg.attach(MIMEText(text_fallback, "plain", "utf-8"))
|
msg.attach(MIMEText(text_fallback, "plain", "utf-8"))
|
||||||
msg.attach(MIMEText(html_body, "html", "utf-8"))
|
msg.attach(MIMEText(html_body, "html", "utf-8"))
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ async def send_feedback(
|
|||||||
start_tls=SMTP_USE_TLS,
|
start_tls=SMTP_USE_TLS,
|
||||||
)
|
)
|
||||||
_user_timestamps[user_id].append(now)
|
_user_timestamps[user_id].append(now)
|
||||||
logger.info(f"Feedback von {username} ({category_label}) gesendet")
|
logger.info(f"Feedback von {display_name} ({category_label}) gesendet")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Feedback-E-Mail fehlgeschlagen: {e}")
|
logger.error(f"Feedback-E-Mail fehlgeschlagen: {e}")
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|||||||
@@ -55,14 +55,14 @@ async def _enrich_incident(db: aiosqlite.Connection, row: aiosqlite.Row) -> dict
|
|||||||
source_count = (await cursor.fetchone())["cnt"]
|
source_count = (await cursor.fetchone())["cnt"]
|
||||||
|
|
||||||
cursor = await db.execute(
|
cursor = await db.execute(
|
||||||
"SELECT username FROM users WHERE id = ?",
|
"SELECT email FROM users WHERE id = ?",
|
||||||
(incident["created_by"],),
|
(incident["created_by"],),
|
||||||
)
|
)
|
||||||
user_row = await cursor.fetchone()
|
user_row = await cursor.fetchone()
|
||||||
|
|
||||||
incident["article_count"] = article_count
|
incident["article_count"] = article_count
|
||||||
incident["source_count"] = source_count
|
incident["source_count"] = source_count
|
||||||
incident["created_by_username"] = user_row["username"] if user_row else "Unbekannt"
|
incident["created_by_username"] = user_row["email"] if user_row else "Unbekannt"
|
||||||
return incident
|
return incident
|
||||||
|
|
||||||
|
|
||||||
@@ -744,9 +744,9 @@ async def export_incident(
|
|||||||
incident = dict(row)
|
incident = dict(row)
|
||||||
|
|
||||||
# Ersteller-Name
|
# Ersteller-Name
|
||||||
cursor = await db.execute("SELECT username FROM users WHERE id = ?", (incident["created_by"],))
|
cursor = await db.execute("SELECT email FROM users WHERE id = ?", (incident["created_by"],))
|
||||||
user_row = await cursor.fetchone()
|
user_row = await cursor.fetchone()
|
||||||
creator = user_row["username"] if user_row else "Unbekannt"
|
creator = user_row["email"] if user_row else "Unbekannt"
|
||||||
|
|
||||||
# Artikel
|
# Artikel
|
||||||
cursor = await db.execute(
|
cursor = await db.execute(
|
||||||
|
|||||||
@@ -430,8 +430,8 @@ const App = {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const user = await API.getMe();
|
const user = await API.getMe();
|
||||||
this._currentUsername = user.username;
|
this._currentUsername = user.email;
|
||||||
document.getElementById('header-user').textContent = user.username;
|
document.getElementById('header-user').textContent = user.email;
|
||||||
|
|
||||||
// Org-Name anzeigen
|
// Org-Name anzeigen
|
||||||
const orgNameEl = document.getElementById('header-org-name');
|
const orgNameEl = document.getElementById('header-org-name');
|
||||||
@@ -668,14 +668,14 @@ const App = {
|
|||||||
// Ersteller anzeigen
|
// Ersteller anzeigen
|
||||||
const creatorEl = document.getElementById('incident-creator');
|
const creatorEl = document.getElementById('incident-creator');
|
||||||
if (creatorEl) {
|
if (creatorEl) {
|
||||||
creatorEl.textContent = incident.created_by_username || '';
|
creatorEl.textContent = (incident.created_by_username || '').split('@')[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete-Button: nur Ersteller darf löschen
|
// Delete-Button: nur Ersteller darf löschen
|
||||||
const deleteBtn = document.getElementById('delete-incident-btn');
|
const deleteBtn = document.getElementById('delete-incident-btn');
|
||||||
const isCreator = incident.created_by_username === this._currentUsername;
|
const isCreator = incident.created_by_username === this._currentUsername;
|
||||||
deleteBtn.disabled = !isCreator;
|
deleteBtn.disabled = !isCreator;
|
||||||
deleteBtn.title = isCreator ? '' : `Nur ${incident.created_by_username} kann diese Lage löschen`;
|
deleteBtn.title = isCreator ? '' : `Nur ${(incident.created_by_username || '').split('@')[0]} kann diese Lage löschen`;
|
||||||
|
|
||||||
// Zusammenfassung mit Quellenverzeichnis
|
// Zusammenfassung mit Quellenverzeichnis
|
||||||
const summaryText = document.getElementById('summary-text');
|
const summaryText = document.getElementById('summary-text');
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const UI = {
|
|||||||
const isRefreshing = App._refreshingIncidents && App._refreshingIncidents.has(incident.id);
|
const isRefreshing = App._refreshingIncidents && App._refreshingIncidents.has(incident.id);
|
||||||
const dotClass = isRefreshing ? 'refreshing' : (incident.status === 'active' ? 'active' : 'archived');
|
const dotClass = isRefreshing ? 'refreshing' : (incident.status === 'active' ? 'active' : 'archived');
|
||||||
const activeClass = isActive ? 'active' : '';
|
const activeClass = isActive ? 'active' : '';
|
||||||
const creator = incident.created_by_username || '';
|
const creator = (incident.created_by_username || '').split('@')[0];
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="incident-item ${activeClass}" data-id="${incident.id}" onclick="App.selectIncident(${incident.id})" role="button" tabindex="0">
|
<div class="incident-item ${activeClass}" data-id="${incident.id}" onclick="App.selectIncident(${incident.id})" role="button" tabindex="0">
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren