salvo_captcha/storage/
mod.rs

1// Copyright (c) 2024-2025, Awiteb <a@4rs.nl>
2//     A captcha middleware for Salvo framework.
3//
4// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
8// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
9// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
10// THE SOFTWARE.
11
12use std::{sync::Arc, time::Duration};
13
14#[cfg(feature = "cacache-storage")]
15mod cacache_storage;
16mod memory_storage;
17
18#[cfg_attr(docsrs, doc(cfg(feature = "cacache-storage")))]
19#[cfg(feature = "cacache-storage")]
20pub use cacache_storage::*;
21pub use memory_storage::*;
22
23/// Trait to store the captcha token and answer. is also clear the expired captcha.
24///
25/// The trait will be implemented for `Arc<T>` if `T` implements the trait.
26///
27/// The trait is thread safe, so the storage can be shared between threads.
28pub trait CaptchaStorage: Send + Sync + 'static {
29    /// The error type of the storage.
30    type Error: std::error::Error + Send;
31
32    /// Store the captcha token and answer.
33    fn store_answer(
34        &self,
35        answer: String,
36    ) -> impl std::future::Future<Output = Result<String, Self::Error>> + Send;
37
38    /// Returns the answer of the captcha token. This method will return None if the token is not exist.
39    fn get_answer(
40        &self,
41        token: &str,
42    ) -> impl std::future::Future<Output = Result<Option<String>, Self::Error>> + Send;
43
44    /// Clear the expired captcha.
45    fn clear_expired(
46        &self,
47        expired_after: Duration,
48    ) -> impl std::future::Future<Output = Result<(), Self::Error>> + Send;
49
50    /// Clear the captcha by token.
51    fn clear_by_token(
52        &self,
53        token: &str,
54    ) -> impl std::future::Future<Output = Result<(), Self::Error>> + Send;
55
56    /// Create a new captcha image and return the answer and the image encoded as png.
57    ///
58    /// This method will store the answer in the storage.
59    fn new_captcha<G: crate::CaptchaGenerator>(
60        &self,
61        generator: G,
62    ) -> impl std::future::Future<
63        Output = Result<(String, Vec<u8>), either::Either<Self::Error, G::Error>>,
64    > + Send {
65        async move {
66            let (answer, image) = generator.new_captcha().await.map_err(either::Right)?;
67            Ok((
68                self.store_answer(answer).await.map_err(either::Left)?,
69                image,
70            ))
71        }
72    }
73}
74
75impl<T> CaptchaStorage for Arc<T>
76where
77    T: CaptchaStorage,
78{
79    type Error = T::Error;
80
81    fn store_answer(
82        &self,
83        answer: String,
84    ) -> impl std::future::Future<Output = Result<String, Self::Error>> + Send {
85        self.as_ref().store_answer(answer)
86    }
87
88    fn get_answer(
89        &self,
90        token: &str,
91    ) -> impl std::future::Future<Output = Result<Option<String>, Self::Error>> + Send {
92        self.as_ref().get_answer(token)
93    }
94
95    fn clear_expired(
96        &self,
97        expired_after: Duration,
98    ) -> impl std::future::Future<Output = Result<(), Self::Error>> + Send {
99        self.as_ref().clear_expired(expired_after)
100    }
101
102    fn clear_by_token(
103        &self,
104        token: &str,
105    ) -> impl std::future::Future<Output = Result<(), Self::Error>> + Send {
106        self.as_ref().clear_by_token(token)
107    }
108}