d489716c9b
This commit introduces the foundation for web-based authentication and user interface. It includes: - **Session Management**: Securely handling user sessions using cryptographically generated tokens, cookies with appropriate security flags, and server-side storage. - **Login/Logout Functionality**: Endpoints for users to log in with their credentials and log out, clearing their session. - **User Interface**: Basic templates for the login page, a list of cases, and a detailed view of a single case, allowing users to navigate and view their data. - **Authorization**: An `AuthenticatedWebUser` extractor to ensure only logged-in users can access protected web routes. - **Configuration**: Added a `cookie_secure` option to the configuration to control the `Secure` flag on session cookies, useful for local development. - **Dependencies**: Added necessary dependencies for password hashing, time handling, and TOML file manipulation. - **Helper Binary**: Included a `hash-password` binary to simplify generating bcrypt hashes for user passwords.
62 lines
2.5 KiB
HTML
62 lines
2.5 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Doctate — Fall {{ case_id_short }}</title>
|
|
<style>
|
|
body { font-family: sans-serif; max-width: 900px; margin: 2em auto; padding: 0 1em; }
|
|
header { display: flex; justify-content: space-between; align-items: center; }
|
|
header form { margin: 0; }
|
|
.back { color: #4a90e2; text-decoration: none; }
|
|
.oneliner { font-size: 1.1em; color: #222; margin: 0.6em 0 1em; }
|
|
.meta { color: #666; font-family: monospace; font-size: 0.9em; }
|
|
.recording { margin: 1em 0; padding: 0.7em; border: 1px solid #ddd; border-radius: 4px; }
|
|
.recording.failed-row { background: #fff7f7; border-left: 3px solid #e24a4a; }
|
|
.recording-head { display: flex; align-items: center; gap: 1em; flex-wrap: wrap; }
|
|
.recording-head span { font-family: monospace; font-size: 0.9em; min-width: 20em; }
|
|
.transcript { margin: 0.6em 0 0; padding: 0.6em; background: #f6f6f6; border-radius: 4px; white-space: pre-wrap; font-family: serif; }
|
|
.pending { color: #888; font-style: italic; margin-top: 0.5em; }
|
|
.failed { color: #b00; font-weight: bold; margin-top: 0.5em; }
|
|
.status-badge { display: inline-block; padding: 0.1em 0.6em; border-radius: 999px; font-size: 0.8em; font-weight: bold; color: white; }
|
|
.status-badge.open { background: #4a90e2; }
|
|
.status-badge.done { background: #7ed321; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<div><a class="back" href="/web/cases">← Zurück</a></div>
|
|
<form method="post" action="/web/logout"><button type="submit">Logout</button></form>
|
|
</header>
|
|
<h1>Fall {{ case_id_short }} <span class="status-badge {{ status }}">{{ status }}</span></h1>
|
|
{% match oneliner %}
|
|
{% when Some with (t) %}
|
|
<div class="oneliner">{{ t }}</div>
|
|
{% when None %}
|
|
<div class="oneliner" style="color:#888;font-style:italic">Noch kein Oneliner.</div>
|
|
{% endmatch %}
|
|
<div class="meta">{{ case_id }}</div>
|
|
|
|
<h2>Aufnahmen ({{ recordings.len() }})</h2>
|
|
{% for rec in recordings %}
|
|
<div class="recording{% if rec.failed %} failed-row{% endif %}">
|
|
<div class="recording-head">
|
|
<span>{{ rec.filename }}</span>
|
|
<audio controls preload="none">
|
|
<source src="/web/audio/{{ slug }}/{{ case_id }}/{{ rec.filename }}" type="audio/mp4">
|
|
</audio>
|
|
</div>
|
|
{% if rec.failed %}
|
|
<div class="failed">Transkription fehlgeschlagen — .failed-Suffix entfernen zum Erneut-Versuchen.</div>
|
|
{% else %}
|
|
{% match rec.transcript %}
|
|
{% when Some with (t) %}
|
|
<div class="transcript">{{ t }}</div>
|
|
{% when None %}
|
|
<div class="pending">Transkription läuft…</div>
|
|
{% endmatch %}
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
</body>
|
|
</html>
|