chore: checkpoint admin editor and perf work

This commit is contained in:
2026-03-31 00:12:02 +08:00
parent 92a85eef20
commit 99b308e800
45 changed files with 7265 additions and 833 deletions

View File

@@ -1,15 +1,46 @@
use axum::http::HeaderMap;
use loco_rs::prelude::*;
use sea_orm::{ConnectionTrait, DatabaseBackend, DbBackend, FromQueryResult, Statement};
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use std::time::Instant;
use crate::models::_entities::posts;
use crate::services::content;
use crate::services::{analytics, content};
fn deserialize_boolish_option<'de, D>(
deserializer: D,
) -> std::result::Result<Option<bool>, D::Error>
where
D: Deserializer<'de>,
{
let raw = Option::<String>::deserialize(deserializer)?;
raw.map(|value| match value.trim().to_ascii_lowercase().as_str() {
"1" | "true" | "yes" | "on" => Ok(true),
"0" | "false" | "no" | "off" => Ok(false),
other => Err(serde::de::Error::custom(format!(
"invalid boolean value `{other}`"
))),
})
.transpose()
}
fn is_preview_search(query: &SearchQuery, headers: &HeaderMap) -> bool {
query.preview.unwrap_or(false)
|| headers
.get("x-termi-search-mode")
.and_then(|value| value.to_str().ok())
.map(|value| value.eq_ignore_ascii_case("preview"))
.unwrap_or(false)
}
#[derive(Clone, Debug, Default, Deserialize)]
pub struct SearchQuery {
pub q: Option<String>,
pub limit: Option<u64>,
#[serde(default, deserialize_with = "deserialize_boolish_option")]
pub preview: Option<bool>,
}
#[derive(Clone, Debug, Serialize, FromQueryResult)]
@@ -157,7 +188,10 @@ async fn fallback_search(ctx: &AppContext, q: &str, limit: u64) -> Result<Vec<Se
pub async fn search(
Query(query): Query<SearchQuery>,
State(ctx): State<AppContext>,
headers: HeaderMap,
) -> Result<Response> {
let started_at = Instant::now();
let preview_search = is_preview_search(&query, &headers);
content::sync_markdown_posts(&ctx).await?;
let q = query.q.unwrap_or_default().trim().to_string();
@@ -186,6 +220,17 @@ pub async fn search(
fallback_search(&ctx, &q, limit).await?
};
if !preview_search {
analytics::record_search_event(
&ctx,
&q,
results.len(),
&headers,
started_at.elapsed().as_millis() as i64,
)
.await;
}
format::json(results)
}