yggdrasil/api/posts/
tags.rs1use crate::api::error::ApiError;
8use leptos::prelude::*;
9
10use super::types::TagListResponse;
11#[cfg(feature = "server")]
12use crate::api::error::AppError;
13#[cfg(feature = "server")]
14use crate::db::pool::get_conn;
15#[cfg(feature = "server")]
16use crate::models::post::Tag;
17
18#[server(ListTags, endpoint = "list_tags", input = leptos::server_fn::codec::Json)]
22pub async fn list_tags() -> Result<TagListResponse, ApiError> {
23 #[cfg(feature = "server")]
24 {
25 if let Some(cached) = crate::cache::get_tag_list().await {
26 return Ok(TagListResponse { tags: cached });
27 }
28
29 let client = get_conn().await.map_err(AppError::db_conn)?;
30
31 let rows = client
33 .query(
34 "SELECT t.id, t.name, COUNT(p.id) as post_count
35 FROM tags t
36 LEFT JOIN post_tags pt ON t.id = pt.tag_id
37 LEFT JOIN posts p ON pt.post_id = p.id AND p.deleted_at IS NULL AND p.status = 'published'
38 GROUP BY t.id, t.name
39 ORDER BY t.name",
40 &[],
41 )
42 .await
43 .map_err(AppError::query)?;
44
45 let tags: Vec<Tag> = rows
46 .iter()
47 .map(|r| Tag {
48 id: r.get("id"),
49 name: r.get("name"),
50 post_count: r.get("post_count"),
51 })
52 .collect();
53
54 crate::cache::set_tag_list(tags.clone()).await;
55 Ok(TagListResponse { tags })
56 }
57
58 #[cfg(not(feature = "server"))]
59 {
60 Ok(TagListResponse { tags: Vec::new() })
61 }
62}