Feat: Implement basic web authentication and UI

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.
This commit is contained in:
2026-04-14 15:09:23 +02:00
parent 61b5c8e125
commit d489716c9b
24 changed files with 930 additions and 16 deletions
+5 -5
View File
@@ -11,12 +11,12 @@ use tracing::warn;
use crate::config::Config;
use crate::error::AppError;
struct RecordingView {
filename: String,
transcript: Option<String>,
pub(crate) struct RecordingView {
pub(crate) filename: String,
pub(crate) transcript: Option<String>,
/// True if the file has been renamed to `<ts>.m4a.failed` by the worker
/// after a non-recoverable ffmpeg or whisper error.
failed: bool,
pub(crate) failed: bool,
}
struct CaseView {
@@ -170,7 +170,7 @@ async fn scan_cases(data_path: &Path) -> Vec<CaseView> {
cases
}
async fn scan_recordings(case_dir: &Path) -> Vec<RecordingView> {
pub(crate) async fn scan_recordings(case_dir: &Path) -> Vec<RecordingView> {
let mut recordings = Vec::new();
let mut entries = match tokio::fs::read_dir(case_dir).await {
Ok(r) => r,