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

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

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