advance 1-8 post-mvp execution batches

This commit is contained in:
lix-2026
2026-05-21 23:53:39 +08:00
parent 3ebcbff728
commit fdb20300e9
67 changed files with 4378 additions and 275 deletions
+62 -2
View File
@@ -4,9 +4,13 @@ use axum::extract::{Extension, State};
use axum::http::{HeaderMap, HeaderName, HeaderValue, StatusCode};
use axum::response::{IntoResponse, Response};
use axum::Json;
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _};
use serde::Serialize;
const HEADER_MNOTE_WEB_OWNER: &str = "x-mnote-web-owner";
const COOKIE_CONVEX_AUTH_JWT: &str = "__convexAuthJWT";
const COOKIE_ACTOR_EMAIL: &str = "mnote_actor_email";
const COOKIE_ACTOR_NAME: &str = "mnote_actor_name";
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
@@ -63,6 +67,18 @@ pub async fn refresh_session(
fn build_session_response(state: &AppState, context: RequestContext) -> SessionResponse {
let actor_id = context.auth.actor_id.trim();
let has_forwarded_actor = !actor_id.is_empty() && actor_id != "anonymous";
let actor_email = context
.auth
.cookie_header
.as_deref()
.and_then(|cookies| encoded_cookie_value(cookies, COOKIE_ACTOR_EMAIL))
.or_else(|| jwt_cookie_claim(&context, &["email", "preferred_username"]));
let actor_name = context
.auth
.cookie_header
.as_deref()
.and_then(|cookies| encoded_cookie_value(cookies, COOKIE_ACTOR_NAME))
.or_else(|| jwt_cookie_claim(&context, &["name", "username"]));
let user_id = if has_forwarded_actor {
actor_id.to_string()
} else {
@@ -79,12 +95,12 @@ fn build_session_response(state: &AppState, context: RequestContext) -> SessionR
owner: "mnote-web",
user_id,
email: if has_forwarded_actor {
String::new()
actor_email.unwrap_or_default()
} else {
state.config().dev_user_email.clone()
},
name: if has_forwarded_actor {
actor_id.to_string()
actor_name.unwrap_or_else(|| actor_id.to_string())
} else {
state.config().dev_user_name.clone()
},
@@ -99,6 +115,50 @@ fn build_session_response(state: &AppState, context: RequestContext) -> SessionR
}
}
fn jwt_cookie_claim(context: &RequestContext, keys: &[&str]) -> Option<String> {
let token = context
.auth
.cookie_header
.as_deref()
.and_then(|cookies| raw_cookie_value(cookies, COOKIE_CONVEX_AUTH_JWT))?;
let payload_segment = token.split('.').nth(1)?;
let decoded = URL_SAFE_NO_PAD.decode(payload_segment.as_bytes()).ok()?;
let payload: serde_json::Value = serde_json::from_slice(&decoded).ok()?;
keys.iter().find_map(|key| {
payload
.get(key)
.and_then(serde_json::Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty())
.map(ToOwned::to_owned)
})
}
fn encoded_cookie_value(cookie_header: &str, name: &str) -> Option<String> {
let encoded = raw_cookie_value(cookie_header, name)?;
let decoded = URL_SAFE_NO_PAD.decode(encoded.as_bytes()).ok()?;
String::from_utf8(decoded)
.ok()
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty())
}
fn raw_cookie_value(cookie_header: &str, name: &str) -> Option<String> {
cookie_header.split(';').find_map(|part| {
let (cookie_name, cookie_value) = part.trim().split_once('=')?;
if cookie_name.trim() == name {
let value = cookie_value.trim();
if value.is_empty() {
None
} else {
Some(value.to_string())
}
} else {
None
}
})
}
fn owner_json<T>(payload: Json<T>) -> Response
where
T: Serialize,