1
use std::error::Error as StdError;
2

            
3
use chrono::{TimeZone, Utc};
4
use redis::{aio::MultiplexedConnection, AsyncCommands};
5
use serde::{Deserialize, Serialize};
6
use serde_json;
7

            
8
use super::{
9
    super::refresh_token::{QueryCond, RefreshToken, EXPIRES},
10
    conn::{self, Options},
11
};
12

            
13
/// Model instance.
14
pub struct Model {
15
    /// The associated database connection.
16
    _conn: MultiplexedConnection,
17
}
18

            
19
/// Redis schema. Use JSON string as the value.
20
#[derive(Deserialize, Serialize)]
21
struct RefreshTokenSchema {
22
    #[serde(rename = "refreshToken")]
23
    refresh_token: String,
24
    /// i64 as time tick from Epoch in milliseconds.
25
    #[serde(rename = "expiresAt")]
26
    expires_at: i64,
27
    #[serde(rename = "scope")]
28
    scope: Option<String>,
29
    #[serde(rename = "clientId")]
30
    client_id: String,
31
    #[serde(rename = "redirectUri")]
32
    redirect_uri: String,
33
    #[serde(rename = "userId")]
34
    user_id: String,
35
}
36

            
37
const PREFIX: &'static str = "auth:refreshToken:";
38

            
39
impl Model {
40
    /// To create the model instance with a database connection.
41
    pub async fn new(opts: &Options) -> Result<Self, Box<dyn StdError>> {
42
        Ok(Model {
43
            _conn: conn::connect(opts).await?,
44
        })
45
    }
46
}
47

            
48
pub async fn init(_conn: &MultiplexedConnection) -> Result<(), Box<dyn StdError>> {
49
    Ok(())
50
}
51

            
52
6
pub async fn get(
53
6
    conn: &mut MultiplexedConnection,
54
6
    refresh_token: &str,
55
6
) -> Result<Option<RefreshToken>, Box<dyn StdError>> {
56
6
    let result: Option<String> = conn.get(PREFIX.to_string() + refresh_token).await?;
57
6
    let token_str = match result {
58
2
        None => return Ok(None),
59
4
        Some(token) => token,
60
    };
61
4
    let token: RefreshTokenSchema = serde_json::from_str(token_str.as_str())?;
62
4
    Ok(Some(RefreshToken {
63
4
        refresh_token: token.refresh_token,
64
4
        expires_at: Utc.timestamp_nanos(token.expires_at * 1000000),
65
4
        scope: token.scope,
66
4
        client_id: token.client_id,
67
4
        redirect_uri: token.redirect_uri,
68
4
        user_id: token.user_id,
69
4
    }))
70
6
}
71

            
72
4
pub async fn add(
73
4
    conn: &mut MultiplexedConnection,
74
4
    token: &RefreshToken,
75
4
) -> Result<(), Box<dyn StdError>> {
76
4
    let token = RefreshTokenSchema {
77
4
        refresh_token: token.refresh_token.to_string(),
78
4
        expires_at: token.expires_at.timestamp_millis(),
79
4
        scope: match token.scope.as_deref() {
80
            None => None,
81
4
            Some(scope) => Some(scope.to_string()),
82
        },
83
4
        client_id: token.client_id.to_string(),
84
4
        redirect_uri: token.redirect_uri.to_string(),
85
4
        user_id: token.user_id.to_string(),
86
    };
87
4
    let item_str = serde_json::to_string(&token)?;
88
4
    let _: () = conn
89
4
        .set_ex(
90
4
            PREFIX.to_string() + token.refresh_token.as_str(),
91
4
            item_str,
92
4
            (EXPIRES + 60) as u64,
93
4
        )
94
4
        .await?;
95
4
    Ok(())
96
4
}
97

            
98
2
pub async fn del<'a>(
99
2
    conn: &mut MultiplexedConnection,
100
2
    cond: &QueryCond<'a>,
101
2
) -> Result<(), Box<dyn StdError>> {
102
2
    if cond.refresh_token.is_none() {
103
        return Ok(());
104
2
    }
105
2
    let _: () = conn
106
2
        .del(PREFIX.to_string() + cond.refresh_token.unwrap())
107
2
        .await?;
108
2
    Ok(())
109
2
}