yggdrasil/api/settings/
site.rs1#![allow(clippy::unused_unit, deprecated, clippy::too_many_arguments)]
4
5use crate::api::error::ApiError;
6use leptos::prelude::*;
7
8#[cfg(feature = "server")]
9use crate::api::auth::get_current_admin_user;
10#[cfg(feature = "server")]
11use crate::api::error::AppError;
12#[cfg(feature = "server")]
13use crate::db::pool::get_conn;
14use crate::models::settings::SiteSettings;
15
16#[server(GetSiteSettings, endpoint = "get_site_settings", input = leptos::server_fn::codec::Json)]
25pub async fn get_site_settings() -> Result<SiteSettings, ApiError> {
26 #[cfg(feature = "server")]
27 {
28 if let Some(cached) = crate::cache::get_site_settings().await {
29 return Ok(cached);
30 }
31
32 let client = get_conn().await.map_err(AppError::db_conn)?;
33
34 let rows = client
35 .query(
36 "SELECT key, value FROM settings WHERE key = ANY($1)",
37 &[&SITE_KEYS.as_slice()],
38 )
39 .await
40 .map_err(AppError::query)?;
41 let mut settings = SiteSettings::default();
42 for row in rows {
43 let value: String = row.get("value");
44 match row.get::<_, &str>("key") {
45 "site_github_url" => settings.github_url = value,
46 "site_home_title" => settings.home_title = value,
47 "site_home_subtitle" => settings.home_subtitle = value,
48 "site_home_intro" => settings.home_intro = value,
49 "site_description" => settings.site_description = value,
50 _ => {}
51 }
52 }
53 crate::cache::set_site_settings(settings.clone()).await;
54 Ok(settings)
55 }
56
57 #[cfg(not(feature = "server"))]
58 {
59 Ok(SiteSettings::default())
60 }
61}
62
63#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
65pub struct SiteCard {
66 pub name: String,
67 pub url: String,
69 pub description: String,
70}
71
72impl SiteCard {
73 pub fn feed_url(&self) -> String {
75 format!("{}/feed.xml", self.url)
76 }
77}
78
79#[server(GetSiteCard, endpoint = "get_site_card", input = leptos::server_fn::codec::Json)]
83pub async fn get_site_card() -> Result<SiteCard, ApiError> {
84 let settings = get_site_settings().await?;
85 #[cfg(feature = "server")]
86 let url = crate::api::settings::runtime_security_settings()
87 .await
88 .app_base_url
89 .trim()
90 .trim_end_matches('/')
91 .to_string();
92 #[cfg(not(feature = "server"))]
93 let url = String::new();
94 Ok(SiteCard {
95 name: "Yggdrasil".to_string(),
96 url,
97 description: settings.description().to_string(),
98 })
99}
100
101#[cfg(feature = "server")]
103const SITE_KEYS: [&str; 5] = [
104 "site_github_url",
105 "site_home_title",
106 "site_home_subtitle",
107 "site_home_intro",
108 "site_description",
109];
110
111#[cfg(feature = "server")]
112fn site_values(settings: &SiteSettings) -> [&str; 5] {
113 [
114 &settings.github_url,
115 &settings.home_title,
116 &settings.home_subtitle,
117 &settings.home_intro,
118 &settings.site_description,
119 ]
120}
121
122#[server(UpdateSiteSettings, endpoint = "update_site_settings", input = leptos::server_fn::codec::Json)]
127pub async fn update_site_settings(settings: SiteSettings) -> Result<SiteSettings, ApiError> {
128 let _user = get_current_admin_user().await?;
129
130 let settings = settings.normalized();
131
132 #[cfg(feature = "server")]
133 {
134 let mut client = get_conn().await.map_err(AppError::db_conn)?;
135 let tx = client.transaction().await.map_err(AppError::tx)?;
136 for (key, value) in SITE_KEYS.iter().zip(site_values(&settings)) {
137 super::upsert_setting(&*tx, key, value).await?;
138 }
139 tx.commit().await.map_err(AppError::tx)?;
140
141 crate::cache::invalidate_site_settings();
142 crate::ssr_cache::invalidate_ssr_all_public();
143 tracing::info!("Site settings updated: github_url={}", settings.github_url);
144 }
145
146 Ok(settings)
147}