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

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

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