salvo_flash/
cookie_store.rs

1use salvo_core::http::cookie::time::Duration;
2use salvo_core::http::cookie::{Cookie, SameSite};
3use salvo_core::{Depot, Request, Response};
4
5use super::{Flash, FlashHandler, FlashStore};
6
7/// CookieStore is a `FlashStore` implementation that stores the flash messages in a cookie.
8#[derive(Debug)]
9#[non_exhaustive]
10pub struct CookieStore {
11    /// The cookie max age.
12    pub max_age: Duration,
13    /// The cookie same site.
14    pub same_site: SameSite,
15    /// The cookie http only.
16    pub http_only: bool,
17    /// The cookie path.
18    pub path: String,
19    /// The cookie name.
20    pub name: String,
21}
22impl Default for CookieStore {
23    fn default() -> Self {
24        Self::new()
25    }
26}
27
28impl CookieStore {
29    /// Create a new `CookieStore`.
30    pub fn new() -> Self {
31        Self {
32            max_age: Duration::seconds(60),
33            same_site: SameSite::Lax,
34            http_only: true,
35            path: "/".into(),
36            name: "salvo.flash".into(),
37        }
38    }
39
40    /// Sets cookie name.
41    pub fn name(mut self, name: impl Into<String>) -> Self {
42        self.name = name.into();
43        self
44    }
45
46    /// Sets cookie max_age.
47    pub fn max_age(mut self, max_age: Duration) -> Self {
48        self.max_age = max_age;
49        self
50    }
51
52    /// Sets cookie same site.
53    pub fn same_site(mut self, same_site: SameSite) -> Self {
54        self.same_site = same_site;
55        self
56    }
57
58    /// Sets cookie http only.
59    pub fn http_only(mut self, http_only: bool) -> Self {
60        self.http_only = http_only;
61        self
62    }
63
64    /// Sets cookie path.
65    pub fn path(mut self, path: impl Into<String>) -> Self {
66        self.path = path.into();
67        self
68    }
69
70    /// Into `FlashHandler`.
71    pub fn into_handler(self) -> FlashHandler<CookieStore> {
72        FlashHandler::new(self)
73    }
74}
75impl FlashStore for CookieStore {
76    async fn load_flash(&self, req: &mut Request, _depot: &mut Depot) -> Option<Flash> {
77        match req.cookie(&self.name) {
78            None => None,
79            Some(cookie) => match serde_json::from_str(cookie.value()) {
80                Ok(flash) => Some(flash),
81                Err(e) => {
82                    tracing::error!(error = ?e, "deserialize flash cookie failed");
83                    None
84                }
85            },
86        }
87    }
88    async fn save_flash(&self, _req: &mut Request, _depot: &mut Depot, res: &mut Response, flash: Flash) {
89        res.add_cookie(
90            Cookie::build((self.name.clone(), serde_json::to_string(&flash).unwrap_or_default()))
91                .max_age(self.max_age)
92                .path(self.path.clone())
93                .same_site(self.same_site)
94                .http_only(self.http_only)
95                .build(),
96        );
97    }
98    async fn clear_flash(&self, _depot: &mut Depot, res: &mut Response) {
99        res.add_cookie(
100            Cookie::build((self.name.clone(), ""))
101                .max_age(Duration::seconds(0))
102                .same_site(self.same_site)
103                .http_only(self.http_only)
104                .path(self.path.clone())
105                .build(),
106        );
107    }
108}