1
use std::{collections::HashMap, error::Error as StdError, sync::Arc};
2

            
3
use axum::Router;
4

            
5
use sylvia_iot_corelib::constants::DbEngine;
6

            
7
use crate::{
8
    libs::config::{self, Config},
9
    models::{self, ConnOptions, Model, MongoDbOptions, SqliteOptions},
10
};
11

            
12
pub mod oauth2;
13
mod v1;
14

            
15
/// The resources used by this service.
16
#[derive(Clone)]
17
pub struct State {
18
    /// The scope root path for the service.
19
    ///
20
    /// For example `/auth`, the APIs are
21
    /// - `http://host:port/auth/oauth2/xxx`
22
    /// - `http://host:port/auth/api/v1/user/xxx`
23
    /// - `http://host:port/auth/api/v1/client/xxx`
24
    pub scope_path: &'static str,
25
    /// The scopes for accessing APIs.
26
    pub api_scopes: HashMap<String, Vec<String>>,
27
    /// Jinja2 templates.
28
    pub templates: HashMap<String, String>,
29
    /// The database model.
30
    pub model: Arc<dyn Model>,
31
}
32

            
33
/// The sylvia-iot module specific error codes in addition to standard
34
/// [`sylvia_iot_corelib::err::ErrResp`].
35
pub struct ErrReq;
36

            
37
impl ErrReq {
38
    pub const USER_EXIST: (u16, &'static str) = (400, "err_auth_user_exist");
39
    pub const USER_NOT_EXIST: (u16, &'static str) = (400, "err_auth_user_not_exist");
40
}
41

            
42
/// To create resources for the service.
43
16
pub async fn new_state(
44
16
    scope_path: &'static str,
45
16
    conf: &Config,
46
16
) -> Result<State, Box<dyn StdError>> {
47
16
    let conf = config::apply_default(conf);
48
16
    let db_opts = match conf.db.as_ref().unwrap().engine.as_ref().unwrap().as_str() {
49
16
        DbEngine::MONGODB => {
50
6
            let conf = conf.db.as_ref().unwrap().mongodb.as_ref().unwrap();
51
6
            ConnOptions::MongoDB(MongoDbOptions {
52
6
                url: conf.url.as_ref().unwrap().to_string(),
53
6
                db: conf.database.as_ref().unwrap().to_string(),
54
6
                pool_size: conf.pool_size,
55
6
            })
56
        }
57
        _ => {
58
10
            let conf = conf.db.as_ref().unwrap().sqlite.as_ref().unwrap();
59
10
            ConnOptions::Sqlite(SqliteOptions {
60
10
                path: conf.path.as_ref().unwrap().to_string(),
61
10
            })
62
        }
63
    };
64
16
    let model = models::new(&db_opts).await?;
65
    Ok(State {
66
16
        scope_path: match scope_path.len() {
67
            0 => "/",
68
16
            _ => scope_path,
69
        },
70
16
        api_scopes: conf.api_scopes.as_ref().unwrap().clone(),
71
16
        templates: conf.templates.as_ref().unwrap().clone(),
72
16
        model,
73
    })
74
16
}
75

            
76
/// To register service URIs in the specified root path.
77
1948
pub fn new_service(state: &State) -> Router {
78
1948
    Router::new().nest(
79
1948
        &state.scope_path,
80
1948
        Router::new()
81
1948
            .nest("/oauth2", oauth2::new_service(state))
82
1948
            .merge(v1::auth::new_service("/api/v1/auth", state))
83
1948
            .merge(v1::user::new_service("/api/v1/user", state))
84
1948
            .merge(v1::client::new_service("/api/v1/client", state)),
85
    )
86
1948
}