Skip to content

Commit 036e2e3

Browse files
DAdjadjclaude
andcommitted
Fix copy logs clipboard fallback and simplify SMTP error messages
- Copy logs: fall back to a selectable textarea when clipboard API is unavailable instead of showing a generic error - SMTP errors: show human-readable messages like "Wrong password" instead of raw error codes like (535, b'5.7.8 Error: ...') Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e21c23a commit 036e2e3

File tree

2 files changed

+35
-11
lines changed

2 files changed

+35
-11
lines changed

app/email_notify.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,23 @@ def send(subject: str, body: str, raise_on_error: bool = False):
4848
except Exception as e:
4949
logger.warning("Failed to send email: %s", e)
5050
if raise_on_error:
51-
raise
51+
raise RuntimeError(_friendly_smtp_error(e)) from e
52+
53+
54+
def _friendly_smtp_error(e):
55+
"""Convert SMTP exceptions to human-readable messages."""
56+
msg = str(e).lower()
57+
if "authentication failed" in msg or "535" in msg:
58+
return "Wrong password. Check your app-specific password and try again."
59+
if "username and password not accepted" in msg:
60+
return "Wrong email or password. Check your credentials and try again."
61+
if "connection refused" in msg or "errno 111" in msg:
62+
return "Could not connect to the email server. Check your email address."
63+
if "timed out" in msg or "timeout" in msg:
64+
return "Email server did not respond. Try again in a moment."
65+
if "relay" in msg or "sender" in msg:
66+
return "Your email provider rejected the message. Make sure the 'send from' address is correct."
67+
return f"Email failed: {e}"
5268

5369

5470
def send_success(tx_count: int):

app/web/templates/status.html

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -123,22 +123,30 @@
123123
var status = document.getElementById('logs-status');
124124
btn.textContent = 'Fetching...';
125125
btn.disabled = true;
126+
status.style.display = 'block';
126127
try {
127128
var resp = await fetch('/api/logs?lines=200');
128129
var data = await resp.json();
129-
if (data.logs) {
130-
await navigator.clipboard.writeText(data.logs);
131-
status.style.display = 'block';
132-
status.textContent = 'Logs copied to clipboard. Paste them in your support email.';
133-
status.style.color = '#10b981';
134-
setTimeout(function() { status.style.display = 'none'; }, 5000);
135-
} else {
136-
status.style.display = 'block';
137-
status.textContent = data.error || 'Could not fetch logs.';
130+
if (!data.logs) {
131+
status.textContent = data.error || 'No logs available.';
138132
status.style.color = '#ef4444';
133+
} else {
134+
try {
135+
await navigator.clipboard.writeText(data.logs);
136+
status.textContent = 'Logs copied to clipboard.';
137+
status.style.color = '#10b981';
138+
} catch(clipErr) {
139+
// Clipboard API may not be available — show logs in a textarea instead
140+
var ta = document.createElement('textarea');
141+
ta.value = data.logs;
142+
ta.style.cssText = 'width:100%;height:120px;margin-top:8px;background:#080b12;color:#94a3b8;border:1px solid #253450;border-radius:6px;font-size:11px;font-family:monospace;padding:8px;';
143+
status.textContent = 'Select all and copy:';
144+
status.style.color = '#94a3b8';
145+
status.parentNode.insertBefore(ta, status.nextSibling);
146+
ta.select();
147+
}
139148
}
140149
} catch(e) {
141-
status.style.display = 'block';
142150
status.textContent = 'Could not fetch logs.';
143151
status.style.color = '#ef4444';
144152
}

0 commit comments

Comments
 (0)