agnes-the-ai-analyst/app/web/templates/error.html
Vojtech 38f6b639d2
feat(observability): request_id end-to-end + dev debug toolbar + centralized logging (#136)
Cuts release 0.20.0.

## Highlights
- X-Request-ID header on every response + sanitized to [A-Za-z0-9_-] (CRLF log-forging mitigation)
- Error pages (HTML + JSON 500) surface request_id for support tickets
- Dev debug toolbar gated by DEBUG=1 — fastapi-debug-toolbar with custom DuckDBPanel
- Centralized app.logging_config.setup_logging() replaces 23 scattered basicConfig calls
- Telegram bot drops bot.log file — stdout only (BREAKING)

## Devin findings addressed
- BUG_0001: .env.template no longer claims FastAPI debug=True
- BUG_0002: subprocess extractor logs INFO to stderr again
- ANALYSIS_0003: _wants_html no longer matches Accept: */* (curl gets JSON as before)
- BUG on b1c6ee9: HTML 500 page no longer leaks str(exc) in production
- BUG on b13d2fe: 2 CLAUDE.md compliance flags (transform.py + ws_gateway) accepted as scope-limited logging refactor — follow-up to update CLAUDE.md if needed

See CHANGELOG [0.20.0] for full notes.
2026-04-29 22:54:21 +02:00

134 lines
3.3 KiB
HTML

{% extends "base.html" %}
{% block title %}Error {{ code }} — Data Analyst Portal{% endblock %}
{% block content %}
<div class="error-container">
<div class="error-card">
<div class="error-code">{{ code }}</div>
<h2 class="error-title">{{ title }}</h2>
{% if message %}
<p class="error-message">{{ message }}</p>
{% endif %}
{% if path %}
<p class="error-path">
<span class="error-path-label">Path:</span>
<code>{{ path }}</code>
</p>
{% endif %}
{% if traceback %}
<details class="error-traceback">
<summary>Traceback (DEBUG=1)</summary>
<pre>{{ traceback }}</pre>
</details>
{% endif %}
{% if request_id %}
<p class="error-request-id">
<span class="error-request-id-label">Reference:</span>
<code>{{ request_id }}</code>
</p>
{% endif %}
<div class="error-actions">
<a href="{{ url_for('index') }}" class="btn btn-primary">Go home</a>
<a href="javascript:history.back()" class="btn btn-secondary">Back</a>
</div>
</div>
</div>
<style>
.error-container {
display: flex;
align-items: center;
justify-content: center;
min-height: 60vh;
padding: 2rem 1rem;
}
.error-card {
max-width: 36rem;
width: 100%;
padding: 2.5rem 2rem;
background: var(--card-bg, #fff);
border: 1px solid var(--border-color, #e0e0e0);
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,.05);
text-align: center;
}
.error-code {
font-size: 4rem;
font-weight: 700;
line-height: 1;
color: var(--accent-color, #d04a4a);
margin-bottom: .5rem;
letter-spacing: -.02em;
}
.error-title {
margin: 0 0 1rem;
font-size: 1.5rem;
font-weight: 600;
}
.error-message {
color: var(--text-muted, #555);
margin: 0 0 1.25rem;
line-height: 1.5;
}
.error-path {
margin: 0 0 1.5rem;
font-size: .9rem;
}
.error-path-label {
color: var(--text-muted, #777);
margin-right: .25rem;
}
.error-path code {
background: var(--code-bg, #f5f5f5);
padding: .15rem .4rem;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: .85em;
}
.error-traceback {
text-align: left;
margin: 0 0 1.5rem;
padding: .75rem 1rem;
background: var(--code-bg, #f5f5f5);
border-radius: 6px;
border: 1px solid var(--border-color, #e0e0e0);
}
.error-traceback summary {
cursor: pointer;
font-size: .85rem;
color: var(--text-muted, #555);
user-select: none;
}
.error-traceback pre {
margin: .75rem 0 0;
font-size: .75rem;
line-height: 1.4;
overflow-x: auto;
white-space: pre-wrap;
word-break: break-word;
}
.error-request-id {
margin: 0 0 1.25rem;
font-size: .8rem;
color: var(--text-muted, #777);
}
.error-request-id-label {
margin-right: .25rem;
}
.error-request-id code {
background: var(--code-bg, #f5f5f5);
padding: .15rem .4rem;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: .9em;
user-select: all;
}
.error-actions {
display: flex;
gap: .75rem;
justify-content: center;
flex-wrap: wrap;
}
</style>
{% endblock %}