1use crate::prelude::*;
2use rand::Rng;
3use deadpool_redis::redis::AsyncCommands;
4
5#[derive(thiserror::Error, Debug)]
9pub enum LinkVError {
10 #[error("Redis manager error: {0}")]
11 RedisManager(#[from] RedisManagerError),
12
13 #[error("Redis error: {0}")]
14 Redis(#[from] deadpool_redis::redis::RedisError),
15
16 #[error("Redis pool error: {0}")]
17 RedisPool(#[from] deadpool_redis::PoolError),
18}
19
20
21pub type Result<T> = std::result::Result<T, LinkVError>;
22
23#[derive(Clone)]
26pub struct LinkVConfig {
27 pub secret: String,
28 pub crypto: CryptoService,
29 pub redis: RedisManager,
30 pub ttl_secs: u64,
31}
32
33#[derive(Clone)]
34pub struct LinkV {
35 config: LinkVConfig,
36}
37
38impl LinkV {
39 pub fn new(config: LinkVConfig) -> Self {
40 Self { config }
41 }
42
43 pub async fn generate_token(&self, user_id: &str) -> Result<String> {
44 let mut token_bytes = vec![0u8; 256];
45 rand::rng().fill_bytes(&mut token_bytes);
46 let token = hex::encode(token_bytes);
47 let redis_key = format!("linkv:verify:{}:{}", user_id, token);
48
49 let mut con = self.config.redis.get_connection().await?;
50
51 let _res: () = con.set_ex(&redis_key, "1", self.config.ttl_secs).await?;
53
54 Ok(token)
55 }
56
57 pub async fn verify_token(&self, user_id: &str, token: &str) -> Result<bool> {
58 let redis_key = format!("linkv:verify:{}:{}", user_id, token);
59
60 let mut con = self.config.redis.get_connection().await?;
61 let is_valid: bool = con.exists(&redis_key).await?;
62
63 if is_valid {
64 let _res: () = con.del(&redis_key).await?;
66 Ok(true)
67 } else {
68 Ok(false)
69 }
70 }
71}