feat: complete Epic 1 production foundation

This commit is contained in:
2026-08-25 01:24:11 +03:00
parent 767428436d
commit 182bde8ac0
298 changed files with 35719 additions and 5299 deletions
+53 -2
View File
@@ -5,18 +5,52 @@ use serde_json::json;
use crate::{
auth::{AuthenticatedSession, cleared_session_cookie, extract_session_token, session_cookie},
error::ApiError,
rate_limit::ClientIdentityBucket,
service::{
ChangePasswordPayload, LoginPayload, UpdateCurrentWorkspacePayload, UpdateProfilePayload,
ChangePasswordPayload, CompleteBootstrapPayload, LoginPayload,
UpdateCurrentWorkspacePayload, UpdateProfilePayload,
},
state::AppState,
};
pub async fn bootstrap_status(
State(state): State<AppState>,
) -> Result<Json<serde_json::Value>, ApiError> {
Ok(Json(serde_json::json!(
state.service.bootstrap_status().await?
)))
}
pub async fn complete_bootstrap(
State(state): State<AppState>,
jar: CookieJar,
Json(payload): Json<CompleteBootstrapPayload>,
) -> Result<impl IntoResponse, ApiError> {
let (session_data, session) = state.service.complete_bootstrap(payload).await?;
let cookie_value = format!(
"{}.{}",
session_data.session_id.as_str(),
session_data.value
);
let jar = jar.add(session_cookie(state.service.auth_settings(), &cookie_value));
Ok((jar, Json(serde_json::json!(session))))
}
pub async fn login(
State(state): State<AppState>,
jar: CookieJar,
client_bucket: Option<Extension<ClientIdentityBucket>>,
Json(payload): Json<LoginPayload>,
) -> Result<impl IntoResponse, ApiError> {
let (session_data, session) = state.service.login(payload).await?;
let client_bucket = client_bucket
.as_ref()
.map(|Extension(bucket)| bucket.0.as_str())
.unwrap_or("anonymous:/api/auth/login");
let (session_data, session) = state
.service
.login_with_client_bucket(payload, client_bucket)
.await?;
let cookie_value = format!(
"{}.{}",
session_data.session_id.as_str(),
@@ -54,6 +88,22 @@ pub async fn get_session(
Ok(Json(serde_json::json!(session)))
}
pub async fn refresh_session_csrf(
State(state): State<AppState>,
jar: CookieJar,
) -> Result<Json<serde_json::Value>, ApiError> {
let (session_id, session_value) = extract_session_token(&jar)
.ok_or_else(|| ApiError::unauthorized("authentication required"))?;
state
.service
.get_session(&session_id, &session_value)
.await?
.ok_or_else(|| ApiError::unauthorized("session is invalid or expired"))?;
let csrf_token = state.service.rotate_session_csrf_token(&session_id).await?;
Ok(Json(json!({ "csrf_token": csrf_token })))
}
pub async fn get_profile(
Extension(session): Extension<AuthenticatedSession>,
) -> Result<Json<serde_json::Value>, ApiError> {
@@ -73,6 +123,7 @@ pub async fn update_profile(
.service
.update_profile(
&session.user.id,
&session.session_id,
session.current_workspace_id.as_ref(),
payload,
)