Compare commits
No commits in common. "a59a58b41d975b8c5b5174032d81d8ed0bbf6f07" and "44eba241211ff48b3fd4ea650958b0a791468788" have entirely different histories.
a59a58b41d
...
44eba24121
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,5 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
use super::helpers::{clean_tags, get_current_admin_user, sync_tags};
|
||||
use super::types::CreatePostResponse;
|
||||
#[cfg(feature = "server")]
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
use super::helpers::get_current_admin_user;
|
||||
use super::types::CreatePostResponse;
|
||||
#[cfg(feature = "server")]
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
use super::helpers::{get_current_admin_user, row_to_post_list};
|
||||
use super::types::PostListResponse;
|
||||
#[cfg(feature = "server")]
|
||||
@ -21,21 +20,15 @@ pub async fn list_published_posts(
|
||||
|
||||
let client = get_conn().await.map_err(AppError::db_conn)?;
|
||||
|
||||
// Get total count from cache or query
|
||||
let total = if let Some(cached_total) = crate::cache::get_total_published_posts().await {
|
||||
cached_total
|
||||
} else {
|
||||
let count_row = client
|
||||
.query_one(
|
||||
"SELECT COUNT(*) FROM posts WHERE status = 'published' AND deleted_at IS NULL",
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
.map_err(AppError::query)?;
|
||||
let total: i64 = count_row.get(0);
|
||||
crate::cache::set_total_published_posts(total).await;
|
||||
total
|
||||
};
|
||||
// Get total count
|
||||
let count_row = client
|
||||
.query_one(
|
||||
"SELECT COUNT(*) FROM posts WHERE status = 'published' AND deleted_at IS NULL",
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
.map_err(AppError::query)?;
|
||||
let total: i64 = count_row.get(0);
|
||||
|
||||
let offset = ((page - 1).max(0) as i64) * (per_page as i64);
|
||||
let limit = per_page as i64;
|
||||
@ -146,9 +139,6 @@ pub async fn get_posts_by_tag(tag_name: String) -> Result<PostListResponse, Serv
|
||||
posts.push(row_to_post_list(&client, row).await);
|
||||
}
|
||||
|
||||
// NOTE: total = posts.len() is correct because get_posts_by_tag
|
||||
// currently fetches ALL matching posts (no LIMIT/OFFSET).
|
||||
// If pagination is added later, switch to a proper COUNT(*) query.
|
||||
let total = posts.len() as i64;
|
||||
crate::cache::set_posts_by_tag(&tag_name, posts.clone(), total).await;
|
||||
Ok(PostListResponse { posts, total })
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
use super::helpers::{get_current_admin_user, row_to_post_full, row_to_post_list};
|
||||
use super::types::SinglePostResponse;
|
||||
#[cfg(feature = "server")]
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
use super::helpers::row_to_post_list;
|
||||
use super::types::PostListResponse;
|
||||
#[cfg(feature = "server")]
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
use super::helpers::get_current_admin_user;
|
||||
use super::types::PostStatsResponse;
|
||||
#[cfg(feature = "server")]
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
use super::helpers::{clean_tags, get_current_admin_user, sync_tags};
|
||||
use super::types::CreatePostResponse;
|
||||
#[cfg(feature = "server")]
|
||||
|
||||
11
src/cache.rs
11
src/cache.rs
@ -31,7 +31,6 @@ const TTL_TAG_POSTS: Duration = Duration::from_secs(120);
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||
pub enum CacheKey {
|
||||
PublishedPosts { page: i32, per_page: i32 },
|
||||
TotalPublishedPosts,
|
||||
AllTags,
|
||||
PostBySlug(String),
|
||||
PostsByTag(String),
|
||||
@ -110,16 +109,6 @@ pub async fn set_post_list(key: &CacheKey, posts: Vec<Post>, total: i64) {
|
||||
let _ = POST_LIST_CACHE.insert(key.clone(), (posts, total)).await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
pub async fn get_total_published_posts() -> Option<i64> {
|
||||
POST_LIST_CACHE.get(&CacheKey::TotalPublishedPosts).await.map(|(_, total)| total)
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
pub async fn set_total_published_posts(total: i64) {
|
||||
let _ = POST_LIST_CACHE.insert(CacheKey::TotalPublishedPosts, (vec![], total)).await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
pub async fn get_tag_list() -> Option<Vec<Tag>> {
|
||||
TAG_LIST_CACHE.get(&CacheKey::AllTags).await
|
||||
|
||||
@ -55,7 +55,7 @@ pub fn Admin() -> Element {
|
||||
"最近文章"
|
||||
}
|
||||
match &*posts_res.read() {
|
||||
Some(Ok(PostListResponse { posts, total: _ })) => {
|
||||
Some(Ok(PostListResponse { posts })) => {
|
||||
rsx! {
|
||||
div { class: "space-y-0",
|
||||
for post in posts.iter().take(5) {
|
||||
@ -64,14 +64,7 @@ pub fn Admin() -> Element {
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(Err(_e)) => {
|
||||
rsx! {
|
||||
div { class: "text-center text-red-500 dark:text-red-400 py-20",
|
||||
"加载失败"
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
_ => {
|
||||
rsx! {
|
||||
div { class: if show_posts_skeleton() { "space-y-4 animate-pulse" } else { "space-y-4 opacity-0" },
|
||||
for _ in 0..5 {
|
||||
|
||||
@ -27,7 +27,7 @@ pub fn Posts() -> Element {
|
||||
}
|
||||
|
||||
match &*posts_res.read() {
|
||||
Some(Ok(PostListResponse { posts, total: _ })) => {
|
||||
Some(Ok(PostListResponse { posts })) => {
|
||||
if posts.is_empty() {
|
||||
rsx! {
|
||||
div { class: "text-center py-20 text-gray-500 dark:text-[#9b9c9d]",
|
||||
|
||||
@ -95,12 +95,12 @@ fn ArchivesContent() -> Element {
|
||||
|
||||
let posts_data = posts_res.read();
|
||||
match &*posts_data {
|
||||
Some(Ok(PostListResponse { posts, total })) => {
|
||||
Some(Ok(PostListResponse { posts })) => {
|
||||
let grouped = group_posts(posts);
|
||||
rsx! {
|
||||
div { class: "mt-2 text-base text-gray-500 dark:text-[#9b9c9d]",
|
||||
"共 "
|
||||
span { class: "font-medium text-gray-700 dark:text-[#dadadb]", "{total}" }
|
||||
span { class: "font-medium text-gray-700 dark:text-[#dadadb]", "{posts.len()}" }
|
||||
" 篇文章"
|
||||
}
|
||||
for year_group in grouped.iter() {
|
||||
|
||||
@ -29,12 +29,12 @@ fn HomePosts(current_page: i32) -> Element {
|
||||
let posts_res = use_server_future(move || list_published_posts(current_page, POSTS_PER_PAGE))?;
|
||||
|
||||
let posts_data = posts_res.read().as_ref().map(|r| match r {
|
||||
Ok(PostListResponse { posts, total }) => Ok((posts.clone(), *total)),
|
||||
Ok(PostListResponse { posts }) => Ok(posts.clone()),
|
||||
Err(e) => Err(e.to_string()),
|
||||
});
|
||||
|
||||
match posts_data {
|
||||
Some(Ok((posts, total))) => {
|
||||
Some(Ok(posts)) => {
|
||||
rsx! {
|
||||
for post in posts.iter() {
|
||||
PostCard { post: post.clone() }
|
||||
@ -44,7 +44,7 @@ fn HomePosts(current_page: i32) -> Element {
|
||||
"暂无文章"
|
||||
}
|
||||
}
|
||||
Pagination { current_page, total }
|
||||
Pagination { current_page, posts: posts.clone() }
|
||||
}
|
||||
}
|
||||
Some(Err(e)) => {
|
||||
@ -77,10 +77,9 @@ fn HomeInfo() -> Element {
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Pagination(current_page: i32, total: i64) -> Element {
|
||||
fn Pagination(current_page: i32, posts: Vec<crate::models::post::Post>) -> Element {
|
||||
let has_prev = current_page > 1;
|
||||
let total_pages = ((total + POSTS_PER_PAGE as i64 - 1) / POSTS_PER_PAGE as i64).max(1) as i32;
|
||||
let has_next = current_page < total_pages;
|
||||
let has_next = posts.len() >= POSTS_PER_PAGE as usize;
|
||||
let prev = current_page - 1;
|
||||
let prev_route = if prev <= 1 {
|
||||
Route::Home {}
|
||||
|
||||
@ -49,7 +49,7 @@ pub fn Search() -> Element {
|
||||
}
|
||||
if is_searching() {
|
||||
DelayedSkeleton { SearchSkeleton {} }
|
||||
} else if let Some(Ok(PostListResponse { posts, total: _ })) = search_res() {
|
||||
} else if let Some(Ok(PostListResponse { posts })) = search_res() {
|
||||
if posts.is_empty() {
|
||||
div { class: "text-center text-gray-500 dark:text-[#9b9c9d] py-20",
|
||||
"未找到相关文章"
|
||||
|
||||
@ -85,16 +85,16 @@ fn TagDetailContent(tag: String) -> Element {
|
||||
let posts_res = use_server_future(move || get_posts_by_tag(tag.clone()))?;
|
||||
|
||||
let posts_data = posts_res.read().as_ref().map(|r| match r {
|
||||
Ok(PostListResponse { posts, total }) => Ok((posts.clone(), *total)),
|
||||
Ok(PostListResponse { posts }) => Ok(posts.clone()),
|
||||
Err(e) => Err(e.to_string()),
|
||||
});
|
||||
|
||||
match posts_data {
|
||||
Some(Ok((posts, total))) => {
|
||||
Some(Ok(posts)) => {
|
||||
rsx! {
|
||||
div { class: "mt-2 text-base text-gray-500 dark:text-[#9b9c9d]",
|
||||
"共 "
|
||||
span { class: "font-medium text-gray-700 dark:text-[#dadadb]", "{total}" }
|
||||
span { class: "font-medium text-gray-700 dark:text-[#dadadb]", "{posts.len()}" }
|
||||
" 篇文章"
|
||||
}
|
||||
for post in posts.iter() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user