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::access_token::{AccessToken, QueryCond, 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
16
#[derive(Deserialize, Serialize)]
21
struct Schema {
22
    #[serde(rename = "accessToken")]
23
    access_token: String,
24
    #[serde(rename = "refreshToken")]
25
    refresh_token: Option<String>,
26
    /// i64 as time tick from Epoch in milliseconds.
27
    #[serde(rename = "expiresAt")]
28
    expires_at: i64,
29
    #[serde(rename = "scope")]
30
    scope: Option<String>,
31
    #[serde(rename = "clientId")]
32
    client_id: String,
33
    #[serde(rename = "redirectUri")]
34
    redirect_uri: String,
35
    #[serde(rename = "userId")]
36
    user_id: String,
37
}
38

            
39
const PREFIX: &'static str = "auth:accessToken:";
40

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

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

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

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

            
105
1
pub async fn del<'a>(
106
1
    conn: &mut MultiplexedConnection,
107
1
    cond: &QueryCond<'a>,
108
1
) -> Result<(), Box<dyn StdError>> {
109
1
    if cond.access_token.is_none() {
110
        return Ok(());
111
1
    }
112
1
    let _: () = conn
113
1
        .del(PREFIX.to_string() + cond.access_token.unwrap())
114
1
        .await?;
115
1
    Ok(())
116
1
}