Skip to main content

pebble_cms/services/
settings.rs

1use crate::db::Database;
2use anyhow::Result;
3use rusqlite::OptionalExtension;
4
5/// Get a setting value by key
6pub fn get_setting(db: &Database, key: &str) -> Result<Option<String>> {
7    let conn = db.get()?;
8    let mut stmt = conn.prepare("SELECT value FROM settings WHERE key = ?")?;
9    let result = stmt.query_row([key], |row| row.get(0)).optional()?;
10    Ok(result)
11}
12
13/// Set a setting value (insert or update)
14pub fn set_setting(db: &Database, key: &str, value: &str) -> Result<()> {
15    let conn = db.get()?;
16    conn.execute(
17        "INSERT INTO settings (key, value, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP)
18         ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = CURRENT_TIMESTAMP",
19        [key, value],
20    )?;
21    Ok(())
22}
23
24fn escape_like_pattern(s: &str) -> String {
25    s.replace('\\', "\\\\")
26        .replace('%', "\\%")
27        .replace('_', "\\_")
28}
29
30/// Get multiple settings by prefix
31pub fn get_settings_by_prefix(db: &Database, prefix: &str) -> Result<Vec<(String, String)>> {
32    let conn = db.get()?;
33    let mut stmt = conn.prepare("SELECT key, value FROM settings WHERE key LIKE ? ESCAPE '\\'")?;
34    let pattern = format!("{}%", escape_like_pattern(prefix));
35    let rows = stmt.query_map([pattern], |row| {
36        Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?))
37    })?;
38    let mut result = Vec::new();
39    for row in rows {
40        result.push(row?);
41    }
42    Ok(result)
43}
44
45/// Delete a setting
46pub fn delete_setting(db: &Database, key: &str) -> Result<()> {
47    let conn = db.get()?;
48    conn.execute("DELETE FROM settings WHERE key = ?", [key])?;
49    Ok(())
50}
51
52// Homepage setting keys
53pub const HOMEPAGE_TITLE: &str = "homepage_title";
54pub const HOMEPAGE_SUBTITLE: &str = "homepage_subtitle";
55pub const HOMEPAGE_SHOW_PAGES: &str = "homepage_show_pages";
56pub const HOMEPAGE_SHOW_POSTS: &str = "homepage_show_posts";
57pub const HOMEPAGE_CUSTOM_CONTENT: &str = "homepage_custom_content";
58
59/// Get homepage settings with defaults
60pub fn get_homepage_settings(db: &Database) -> Result<HomepageSettings> {
61    let title = get_setting(db, HOMEPAGE_TITLE)?.unwrap_or_default();
62    let subtitle = get_setting(db, HOMEPAGE_SUBTITLE)?.unwrap_or_default();
63    let show_pages = get_setting(db, HOMEPAGE_SHOW_PAGES)?
64        .map(|v| v == "true")
65        .unwrap_or(true);
66    let show_posts = get_setting(db, HOMEPAGE_SHOW_POSTS)?
67        .map(|v| v == "true")
68        .unwrap_or(true);
69    let custom_content = get_setting(db, HOMEPAGE_CUSTOM_CONTENT)?.unwrap_or_default();
70
71    Ok(HomepageSettings {
72        title,
73        subtitle,
74        show_pages,
75        show_posts,
76        custom_content,
77    })
78}
79
80/// Save homepage settings
81pub fn save_homepage_settings(db: &Database, settings: &HomepageSettings) -> Result<()> {
82    set_setting(db, HOMEPAGE_TITLE, &settings.title)?;
83    set_setting(db, HOMEPAGE_SUBTITLE, &settings.subtitle)?;
84    set_setting(
85        db,
86        HOMEPAGE_SHOW_PAGES,
87        if settings.show_pages { "true" } else { "false" },
88    )?;
89    set_setting(
90        db,
91        HOMEPAGE_SHOW_POSTS,
92        if settings.show_posts { "true" } else { "false" },
93    )?;
94    set_setting(db, HOMEPAGE_CUSTOM_CONTENT, &settings.custom_content)?;
95    Ok(())
96}
97
98#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
99pub struct HomepageSettings {
100    pub title: String,
101    pub subtitle: String,
102    #[serde(default = "default_true")]
103    pub show_pages: bool,
104    #[serde(default = "default_true")]
105    pub show_posts: bool,
106    pub custom_content: String,
107}
108
109fn default_true() -> bool {
110    true
111}