summaryrefslogtreecommitdiffstats
path: root/src/main.rs
blob: 0f3962278d8bb1506af4c85bc97073d7c89cc2eb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_migrations;

use actix::prelude::*;
use actix_web::middleware::{errhandlers::ErrorHandlers, Logger};

use actix_identity::{CookieIdentityPolicy, IdentityService};
use actix_web::{http, web, App, HttpServer};
use argonautica::{Hasher, Verifier};

use chrono::Duration;
use diesel::mysql::MysqlConnection;
use dotenv::dotenv;
use r2d2_diesel::ConnectionManager;

mod admin;
mod error;
mod error_handlers;
mod model;
mod schema;
mod user;
mod util;

use crate::admin::user_creation::PassHashExecutor;
use crate::model::DbExecutor;
use crate::user::login::HashVerifyExecutor;

embed_migrations!();

pub struct Secret(String);

#[actix_rt::main]
async fn main() -> std::io::Result<()> {
    dotenv().ok();

    std::env::set_var(
        "RUST_LOG",
        "joklerpoint=debug,actix_web=trace,actix_server=debug",
    );
    env_logger::init();

    let bind_host: String =
        std::env::var("BIND_HOST").unwrap_or_else(|_| "127.0.0.1:8000".to_string());

    let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
    let manager = ConnectionManager::<MysqlConnection>::new(database_url);
    let pool = r2d2::Pool::builder()
        .build(manager)
        .expect("Can to build connection pool");
    embedded_migrations::run(&*pool.get().expect("Can connect to mysql"))
        .expect("Can run migrations");

    let db_addr: Addr<DbExecutor> = SyncArbiter::start(4, move || DbExecutor(pool.clone()));
    let verifier_addr: Addr<HashVerifyExecutor> =
        SyncArbiter::start(4, move || HashVerifyExecutor(Verifier::default()));
    let hasher_addr: Addr<PassHashExecutor> =
        SyncArbiter::start(4, move || PassHashExecutor(Hasher::default()));

    HttpServer::new(move || {
        let secret: String = std::env::var("SECRET_KEY").expect("SECRET_KEY env var should be set");
        let domain: String = std::env::var("DOMAIN").unwrap_or_else(|_| "localhost".to_string());
        let secure_cookies = &domain != "localhost";

        let error_handlers = ErrorHandlers::new()
            .handler(
                http::StatusCode::INTERNAL_SERVER_ERROR,
                error_handlers::internal_server_error,
            )
            .handler(http::StatusCode::BAD_REQUEST, error_handlers::bad_request)
            .handler(http::StatusCode::NOT_FOUND, error_handlers::not_found);

        App::new()
            .data(db_addr.clone())
            .data(verifier_addr.clone())
            .data(hasher_addr.clone())
            .data(Secret(secret.clone()))
            .wrap(error_handlers)
            .wrap(IdentityService::new(
                CookieIdentityPolicy::new(secret.as_bytes())
                    .name("jkp-session")
                    .domain(domain.as_str())
                    .max_age_time(Duration::days(30))
                    .secure(secure_cookies), // this has to reflect the usage of https
            ))
            .wrap(Logger::default())
            .route("/", web::get().to(user::index))
            .service(
                web::resource("/login")
                    .route(web::get().to(user::login))
                    .route(web::post().to(user::confirm_login)),
            )
            .route("/transactions", web::get().to(user::transactions))
            .service(
                web::resource("/transfer")
                    .route(web::get().to(user::transfer))
                    .route(web::post().to(user::confirm_transfer)),
            )
            .route("/settings", web::get().to(user::settings))
            .route("/reset-password", web::post().to(user::reset_password))
            .route("/logout", web::get().to(user::logout))
            .service(
                web::resource("/admin")
                    .route(web::get().to(admin::index))
                    .route(web::post().to(admin::create_user)),
            )
            .service(actix_files::Files::new("/static", "static/"))
    })
    .bind(&bind_host)?
    .run()
    .await?;

    Ok(())
}