use askama::Template; use chrono::SecondsFormat; use std::net::SocketAddr; use axum::{ Json, Router, body::Body, extract::{ConnectInfo, Path, Query, State}, http::{HeaderMap, StatusCode, header}, response::{Html, IntoResponse, Redirect, Response}, routing::{get, post}, }; use serde::{Deserialize, Serialize}; use tower_sessions::Session; use uuid::Uuid; use secrets_core::audit::log_login; use secrets_core::crypto::hex; use secrets_core::service::{ api_key::{ensure_api_key, regenerate_api_key}, audit_log::list_for_user, user::{ OAuthProfile, bind_oauth_account, find_or_create_user, get_user_by_id, unbind_oauth_account, update_user_key_setup, }, }; use crate::AppState; use crate::oauth::{OAuthConfig, OAuthUserInfo, google_auth_url, random_state}; const SESSION_USER_ID: &str = "user_id"; const SESSION_OAUTH_STATE: &str = "oauth_state"; const SESSION_OAUTH_BIND_MODE: &str = "oauth_bind_mode"; const SESSION_LOGIN_PROVIDER: &str = "login_provider"; // ── Template types ──────────────────────────────────────────────────────────── #[derive(Template)] #[template(path = "login.html")] struct LoginTemplate { has_google: bool, base_url: String, version: &'static str, } #[derive(Template)] #[template(path = "home.html")] struct HomeTemplate { is_logged_in: bool, base_url: String, version: &'static str, } #[derive(Template)] #[template(path = "dashboard.html")] struct DashboardTemplate { user_name: String, user_email: String, has_passphrase: bool, base_url: String, version: &'static str, } #[derive(Template)] #[template(path = "audit.html")] struct AuditPageTemplate { user_name: String, user_email: String, entries: Vec, version: &'static str, } struct AuditEntryView { /// RFC3339 UTC for `