mirror of
https://github.com/Lumither/nocturne.git
synced 2026-10-17 10:26:21 +00:00
feat(blog::api) fix apis, frontend
This commit is contained in:
14 files changed
+82
-226
No files matched your search
@@ -1,3 +1,5 @@
|
||||
use std::error::Error;
|
||||
|
||||
use constants::blog::PAGE_SIZE;
|
||||
|
||||
use axum::{extract::Query, extract::State, http::StatusCode, Json};
|
||||
@@ -14,6 +16,7 @@ pub struct Params {
|
||||
page: Option<u32>,
|
||||
}
|
||||
|
||||
#[axum::debug_handler]
|
||||
pub async fn get_post_list(
|
||||
State(db_connection): State<PgPool>,
|
||||
Query(params): Query<Params>,
|
||||
@@ -47,33 +50,72 @@ pub async fn get_post_list(
|
||||
}
|
||||
};
|
||||
|
||||
let mut res = Map::new();
|
||||
let mut res = Vec::new();
|
||||
let posts: Vec<_> = post_list
|
||||
.into_iter()
|
||||
.map(|post| analyze_post(&db_connection, post))
|
||||
.map(|post| async { analyze_post(&db_connection, post).await })
|
||||
.collect();
|
||||
|
||||
let posts = join_all(posts).await;
|
||||
for post in posts {
|
||||
match post {
|
||||
Ok(p) => res.push(p),
|
||||
Err(e) => {
|
||||
return Err((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json::from(json!(("error", e.to_string()))),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.insert("posts".to_string(), Value::from(posts));
|
||||
// res.insert("posts".to_string(), Value::from(posts));
|
||||
Ok(Json::from(Value::from(res)))
|
||||
}
|
||||
|
||||
async fn analyze_post(db_connection: &PgPool, post: PgRow) -> Value {
|
||||
async fn analyze_post(db_connection: &PgPool, post: PgRow) -> Result<Value, Box<dyn Error + Send>> {
|
||||
let post_id: Uuid = post.get("post_id");
|
||||
let tags = fetch_post_tags(db_connection, post_id).await;
|
||||
let meta = fetch_post_meta(db_connection, post_id).await.unwrap();
|
||||
let header_img = meta.get("header_img");
|
||||
let last_update = meta.get("last_update");
|
||||
|
||||
json!({
|
||||
Ok(json!({
|
||||
"post_id": post_id.to_string(),
|
||||
"title": post.get::<String, _>("title"),
|
||||
"category": post.get::<String, _>("category"),
|
||||
"header_img": post.get::<String, _>("header_img"),
|
||||
"header_img": header_img,
|
||||
"sub_title": post.get::<String, _>("sub_title"),
|
||||
"summary": post.get::<String, _>("summary"),
|
||||
"last_update": post.get::<DateTime<Utc>, _>("last_update"),
|
||||
"last_update": last_update,
|
||||
"first_update": post.get::<DateTime<Utc>, _>("first_update"),
|
||||
"tags": tags,
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
async fn fetch_post_meta(db_connection: &PgPool, post_id: Uuid) -> Option<Value> {
|
||||
match query(
|
||||
r#"
|
||||
SELECT jsonb_strip_nulls(to_jsonb(t.*)) - 'post_id' AS meta
|
||||
FROM meta t
|
||||
WHERE post_id = $1;
|
||||
"#,
|
||||
)
|
||||
.bind(post_id)
|
||||
.fetch_one(db_connection)
|
||||
.await
|
||||
{
|
||||
Ok(value) => Some(value.get::<Value, _>("meta")),
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Database error on Reading `{}` from table `{}`: <{}>: {}",
|
||||
"post meta data",
|
||||
"meta",
|
||||
post_id.to_string(),
|
||||
e.to_string()
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_post_tags(db_connection: &PgPool, post_id: Uuid) -> Vec<String> {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
pub mod get_page_count;
|
||||
pub mod get_post;
|
||||
pub mod get_post_list;
|
||||
pub mod refresh;
|
||||
@@ -1,190 +0,0 @@
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use axum::extract::{Query, State};
|
||||
use axum::http::StatusCode;
|
||||
use axum::Json;
|
||||
use serde::Deserialize;
|
||||
use serde_json::{Map, Value};
|
||||
use sqlx::PgPool;
|
||||
|
||||
// use crate::api::post::errors::refresh::PostIdxError;
|
||||
// use crate::api::post::utils::{build_post_idx, post};
|
||||
// use crate::model::blog::{POST_BASE_MODEL, POST_HASH_MODEL, POST_META_MODEL, TAG_MODEL};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Params {
|
||||
/// refresh level
|
||||
level: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn refresh(
|
||||
State(db_connection): State<PgPool>,
|
||||
Query(params): Query<Params>,
|
||||
) -> Result<StatusCode, (StatusCode, Json<Value>)> {
|
||||
// let is_force_refresh = params.level.unwrap_or("".to_string()) == "force";
|
||||
//
|
||||
// // search post
|
||||
// let data_repo_dir = match env::var("GIT_WORK_DIR") {
|
||||
// Ok(value) => value,
|
||||
// Err(_) => {
|
||||
// return Err((
|
||||
// StatusCode::INTERNAL_SERVER_ERROR,
|
||||
// Json::from(json!((
|
||||
// "error",
|
||||
// "error at parsing environment variable: GIT_WORK_DIR"
|
||||
// ))),
|
||||
// ));
|
||||
// }
|
||||
// };
|
||||
// let post_dir = Path::new(&data_repo_dir).join("posts");
|
||||
//
|
||||
// if is_force_refresh {
|
||||
// if let Err(e) = query("DROP TABLE IF EXISTS hash, meta, post, tag;")
|
||||
// .execute(&db_connection)
|
||||
// .await
|
||||
// {
|
||||
// error!(
|
||||
// "DB query error when deleting table: `hash`, `meta`, `post`, `tag`: {}",
|
||||
// e.to_string()
|
||||
// );
|
||||
// return Err((
|
||||
// StatusCode::INTERNAL_SERVER_ERROR,
|
||||
// Json::from(json!(("error", e.to_string()))),
|
||||
// ));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // generate post idx
|
||||
// let post_path = search_md(&post_dir);
|
||||
//
|
||||
// let post_list_with_hash: Vec<(Map<_, _>, Vec<_>)> = post_path
|
||||
// .into_iter()
|
||||
// .filter_map(|post_path| match post::from_path(&post_path) {
|
||||
// Ok(post) => match fs::read(&post_path) {
|
||||
// Ok(bytes) => {
|
||||
// let mut hasher = Sha256::new();
|
||||
// hasher.update(bytes);
|
||||
// let hash = hasher.finalize().to_vec();
|
||||
// Some((post, hash))
|
||||
// }
|
||||
// Err(e) => {
|
||||
// error!(
|
||||
// "Failed to read file while hashing the post <{}>, skipping: {}",
|
||||
// post_path,
|
||||
// e.to_string()
|
||||
// );
|
||||
// None
|
||||
// }
|
||||
// },
|
||||
// Err(e) => {
|
||||
// error!(
|
||||
// "Failed to parse post <{}>, skipping: {}",
|
||||
// post_path,
|
||||
// e.to_string()
|
||||
// );
|
||||
// None
|
||||
// }
|
||||
// })
|
||||
// .collect();
|
||||
//
|
||||
// // db create
|
||||
// query(POST_BASE_MODEL)
|
||||
// .execute(&db_connection)
|
||||
// .await
|
||||
// .unwrap();
|
||||
// query(POST_META_MODEL)
|
||||
// .execute(&db_connection)
|
||||
// .await
|
||||
// .unwrap();
|
||||
// query(TAG_MODEL).execute(&db_connection).await.unwrap();
|
||||
// query(POST_HASH_MODEL)
|
||||
// .execute(&db_connection)
|
||||
// .await
|
||||
// .unwrap();
|
||||
//
|
||||
// let res: Vec<_> = stream::iter(post_list_with_hash)
|
||||
// .map(|(post, hash)| {
|
||||
// let db_clone = db_connection.clone();
|
||||
// async move { build_post_idx(&db_clone, post, hash).await }
|
||||
// })
|
||||
// .buffer_unordered(usize::MAX)
|
||||
// .collect()
|
||||
// .await;
|
||||
//
|
||||
// let errs: Vec<_> = res
|
||||
// .into_iter()
|
||||
// .filter_map(|res| res.err())
|
||||
// .map(|e| e.to_string())
|
||||
// .collect();
|
||||
//
|
||||
// if !errs.is_empty() {
|
||||
// Err((
|
||||
// StatusCode::INTERNAL_SERVER_ERROR,
|
||||
// Json::from(json!({"error": errs})),
|
||||
// ))
|
||||
// } else {
|
||||
// Ok(StatusCode::OK)
|
||||
// }
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Search a dir, return a list of markdown files
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `entry`: dir path
|
||||
///
|
||||
/// # Returns
|
||||
/// Vec<String, Global>
|
||||
/// * `String`: path of a markdown file
|
||||
///
|
||||
fn search_md(entry: &Path) -> Vec<String> {
|
||||
if entry.ends_with(".git") {
|
||||
return vec![];
|
||||
}
|
||||
if entry.is_dir() {
|
||||
if let Ok(files) = entry.read_dir() {
|
||||
files
|
||||
.flat_map(|file| {
|
||||
let file_path = file.unwrap().path();
|
||||
search_md(file_path.as_path())
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
} else if entry.extension().is_some_and(|ext| ext == "md") {
|
||||
vec![entry.to_str().unwrap().to_string()]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
async fn build_post_idx(
|
||||
db_connection: &PgPool,
|
||||
post: Map<String, Value>,
|
||||
hash: Vec<u8>,
|
||||
// ) -> Result<(), PostIdxError> {
|
||||
) -> Result<(), ()> {
|
||||
todo!()
|
||||
// let meta = &post["meta"];
|
||||
//
|
||||
// // extract post id
|
||||
// let post_id = match Uuid::from_str(post["post_id"].as_str().unwrap()) {
|
||||
// Ok(id) => id,
|
||||
// Err(e) => {
|
||||
// return Err(PostIdxError::InvalidUUID {
|
||||
// id: post["post_id"].as_str().unwrap().to_string(),
|
||||
// err_msg: e.to_string(),
|
||||
// })
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// if build_post_idx::hash(db_connection, post_id, hash).await? {
|
||||
// build_post_idx::base(db_connection, &post, post_id, meta).await?;
|
||||
// build_post_idx::tag(db_connection, post_id, meta).await?;
|
||||
// build_post_idx::meta(db_connection, post_id, meta).await?;
|
||||
// }
|
||||
//
|
||||
// Ok(())
|
||||
}
|
||||
@@ -5,14 +5,12 @@ use crate::blog::api::{
|
||||
get_page_count::get_page_count, get_post::get_post, get_post_list::get_post_list,
|
||||
};
|
||||
|
||||
use crate::blog::api::refresh::refresh;
|
||||
use axum::routing::{get, post};
|
||||
use axum::routing::get;
|
||||
use axum::Router;
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
pub fn get_router() -> Router<Pool<Postgres>> {
|
||||
Router::new()
|
||||
.route("/refresh_posts", post(refresh))
|
||||
.route("/get_post_list", get(get_post_list))
|
||||
.route("/get_page_count", get(get_page_count))
|
||||
.route("/get_post/:post_id", get(get_post))
|
||||
|
||||
@@ -11,7 +11,10 @@ pub async fn init() -> Result<Pool<Postgres>, Box<dyn Error>> {
|
||||
let db_connect_option = PgConnectOptions::from_str(&parse_db_uri())
|
||||
.unwrap()
|
||||
.disable_statement_logging();
|
||||
Ok(PgPoolOptions::new().connect_with(db_connect_option).await?)
|
||||
Ok(PgPoolOptions::new()
|
||||
.max_connections(1) // todo: temporal solution, to be fixed
|
||||
.connect_with(db_connect_option)
|
||||
.await?)
|
||||
}
|
||||
|
||||
fn parse_db_uri() -> String {
|
||||
|
||||
Reference in new issue
Block a user