2021-09-10 16:22:38 +08:00
|
|
|
use std::{net::TcpListener, time::Duration};
|
|
|
|
|
|
|
|
use actix::Actor;
|
|
|
|
use actix_identity::{CookieIdentityPolicy, IdentityService};
|
|
|
|
use actix_web::{dev::Server, middleware, web, web::Data, App, HttpServer, Scope};
|
|
|
|
use sqlx::{postgres::PgPoolOptions, PgPool};
|
|
|
|
use tokio::time::interval;
|
|
|
|
|
2021-08-21 22:02:05 +08:00
|
|
|
use crate::{
|
2021-08-23 18:39:10 +08:00
|
|
|
config::{
|
|
|
|
env::{domain, secret, use_https},
|
|
|
|
DatabaseSettings,
|
|
|
|
Settings,
|
|
|
|
},
|
2021-08-21 22:02:05 +08:00
|
|
|
context::AppContext,
|
2021-09-10 16:22:38 +08:00
|
|
|
service::{
|
|
|
|
doc_service::router as doc,
|
|
|
|
user_service::router as user,
|
|
|
|
workspace_service::{
|
|
|
|
app::router as app,
|
|
|
|
view::router as view,
|
|
|
|
workspace::router as workspace,
|
|
|
|
},
|
|
|
|
ws_service,
|
|
|
|
ws_service::WSServer,
|
|
|
|
},
|
2021-08-21 22:02:05 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
pub struct Application {
|
|
|
|
port: u16,
|
|
|
|
server: Server,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Application {
|
|
|
|
pub async fn build(configuration: Settings) -> Result<Self, std::io::Error> {
|
|
|
|
let address = format!(
|
|
|
|
"{}:{}",
|
|
|
|
configuration.application.host, configuration.application.port
|
|
|
|
);
|
|
|
|
let listener = TcpListener::bind(&address)?;
|
|
|
|
let port = listener.local_addr().unwrap().port();
|
2021-08-22 22:16:03 +08:00
|
|
|
let app_ctx = init_app_context(&configuration).await;
|
|
|
|
let server = run(listener, app_ctx)?;
|
|
|
|
Ok(Self { port, server })
|
2021-08-21 22:02:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn run_until_stopped(self) -> Result<(), std::io::Error> { self.server.await }
|
2021-08-23 22:10:36 +08:00
|
|
|
|
|
|
|
pub fn port(&self) -> u16 { self.port }
|
2021-08-21 22:02:05 +08:00
|
|
|
}
|
|
|
|
|
2021-08-22 22:16:03 +08:00
|
|
|
pub fn run(listener: TcpListener, app_ctx: AppContext) -> Result<Server, std::io::Error> {
|
|
|
|
let AppContext { ws_server, pg_pool } = app_ctx;
|
|
|
|
let ws_server = Data::new(ws_server);
|
|
|
|
let pg_pool = Data::new(pg_pool);
|
2021-08-23 18:39:10 +08:00
|
|
|
let domain = domain();
|
|
|
|
let secret: String = secret();
|
2021-08-22 22:16:03 +08:00
|
|
|
|
2021-08-31 17:25:08 +08:00
|
|
|
actix_rt::spawn(period_check(pg_pool.clone()));
|
|
|
|
|
2021-08-21 22:02:05 +08:00
|
|
|
let server = HttpServer::new(move || {
|
|
|
|
App::new()
|
|
|
|
.wrap(middleware::Logger::default())
|
2021-08-23 18:39:10 +08:00
|
|
|
.wrap(identify_service(&domain, &secret))
|
2021-09-04 16:32:34 +08:00
|
|
|
.wrap(crate::middleware::default_cors())
|
2021-09-02 17:19:23 +08:00
|
|
|
.wrap(crate::middleware::AuthenticationService)
|
2021-08-21 22:02:05 +08:00
|
|
|
.app_data(web::JsonConfig::default().limit(4096))
|
|
|
|
.service(ws_scope())
|
|
|
|
.service(user_scope())
|
2021-08-22 22:16:03 +08:00
|
|
|
.app_data(ws_server.clone())
|
|
|
|
.app_data(pg_pool.clone())
|
2021-08-21 22:02:05 +08:00
|
|
|
})
|
|
|
|
.listen(listener)?
|
|
|
|
.run();
|
|
|
|
Ok(server)
|
|
|
|
}
|
|
|
|
|
2021-08-31 17:25:08 +08:00
|
|
|
async fn period_check(_pool: Data<PgPool>) {
|
|
|
|
let mut i = interval(Duration::from_secs(60));
|
|
|
|
loop {
|
|
|
|
i.tick().await;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-16 13:41:07 +08:00
|
|
|
fn ws_scope() -> Scope { web::scope("/ws").service(ws_service::router::establish_ws_connection) }
|
2021-08-21 22:02:05 +08:00
|
|
|
|
|
|
|
fn user_scope() -> Scope {
|
2021-08-27 23:53:53 +08:00
|
|
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP
|
|
|
|
// TODO: replace GET body with query params
|
2021-08-23 18:39:10 +08:00
|
|
|
web::scope("/api")
|
|
|
|
// authentication
|
|
|
|
.service(web::resource("/auth")
|
2021-08-24 13:10:53 +08:00
|
|
|
.route(web::post().to(user::sign_in_handler))
|
|
|
|
.route(web::delete().to(user::sign_out_handler))
|
2021-09-01 16:08:32 +08:00
|
|
|
)
|
|
|
|
.service(web::resource("/user")
|
2021-09-04 16:53:58 +08:00
|
|
|
.route(web::patch().to(user::set_user_profile_handler))
|
|
|
|
.route(web::get().to(user::get_user_profile_handler))
|
2021-08-24 13:10:53 +08:00
|
|
|
)
|
2021-08-31 23:01:46 +08:00
|
|
|
.service(web::resource("/register")
|
|
|
|
.route(web::post().to(user::register_handler))
|
|
|
|
)
|
2021-08-24 13:10:53 +08:00
|
|
|
.service(web::resource("/workspace")
|
2021-08-24 21:38:53 +08:00
|
|
|
.route(web::post().to(workspace::create_handler))
|
|
|
|
.route(web::delete().to(workspace::delete_handler))
|
|
|
|
.route(web::get().to(workspace::read_handler))
|
|
|
|
.route(web::patch().to(workspace::update_handler))
|
2021-08-24 13:10:53 +08:00
|
|
|
)
|
2021-08-26 17:58:59 +08:00
|
|
|
.service(web::resource("/workspace_list/{user_id}")
|
|
|
|
.route(web::get().to(workspace::workspace_list))
|
|
|
|
)
|
2021-08-24 13:10:53 +08:00
|
|
|
.service(web::resource("/app")
|
2021-08-25 17:34:20 +08:00
|
|
|
.route(web::post().to(app::create_handler))
|
|
|
|
.route(web::delete().to(app::delete_handler))
|
|
|
|
.route(web::get().to(app::read_handler))
|
|
|
|
.route(web::patch().to(app::update_handler))
|
2021-08-24 13:10:53 +08:00
|
|
|
)
|
|
|
|
.service(web::resource("/view")
|
2021-08-25 21:33:29 +08:00
|
|
|
.route(web::post().to(view::create_handler))
|
|
|
|
.route(web::delete().to(view::delete_handler))
|
|
|
|
.route(web::get().to(view::read_handler))
|
|
|
|
.route(web::patch().to(view::update_handler))
|
2021-08-23 18:39:10 +08:00
|
|
|
)
|
2021-09-10 15:53:24 +08:00
|
|
|
.service(web::resource("/doc")
|
|
|
|
.route(web::get().to(doc::read_handler))
|
|
|
|
.route(web::patch().to(doc::update_handler))
|
|
|
|
)
|
2021-08-23 18:39:10 +08:00
|
|
|
// password
|
|
|
|
.service(web::resource("/password_change")
|
2021-08-24 13:10:53 +08:00
|
|
|
.route(web::post().to(user::change_password))
|
2021-08-23 18:39:10 +08:00
|
|
|
)
|
2021-08-21 22:02:05 +08:00
|
|
|
}
|
|
|
|
|
2021-08-22 22:16:03 +08:00
|
|
|
async fn init_app_context(configuration: &Settings) -> AppContext {
|
2021-09-16 13:41:07 +08:00
|
|
|
let _ = crate::service::log::Builder::new("flowy")
|
|
|
|
.env_filter("Debug")
|
|
|
|
.build();
|
2021-08-22 22:16:03 +08:00
|
|
|
let pg_pool = get_connection_pool(&configuration.database)
|
|
|
|
.await
|
|
|
|
.expect(&format!(
|
|
|
|
"Failed to connect to Postgres at {:?}.",
|
|
|
|
configuration.database
|
|
|
|
));
|
2021-08-21 22:02:05 +08:00
|
|
|
|
|
|
|
let ws_server = WSServer::new().start();
|
|
|
|
|
2021-08-22 22:16:03 +08:00
|
|
|
AppContext::new(ws_server, pg_pool)
|
2021-08-21 22:02:05 +08:00
|
|
|
}
|
|
|
|
|
2021-08-23 18:39:10 +08:00
|
|
|
pub fn identify_service(domain: &str, secret: &str) -> IdentityService<CookieIdentityPolicy> {
|
|
|
|
IdentityService::new(
|
|
|
|
CookieIdentityPolicy::new(secret.as_bytes())
|
|
|
|
.name("auth")
|
|
|
|
.path("/")
|
|
|
|
.domain(domain)
|
|
|
|
.max_age_secs(24 * 3600)
|
|
|
|
.secure(use_https()),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-08-21 22:02:05 +08:00
|
|
|
pub async fn get_connection_pool(configuration: &DatabaseSettings) -> Result<PgPool, sqlx::Error> {
|
|
|
|
PgPoolOptions::new()
|
2021-08-22 15:32:48 +08:00
|
|
|
.connect_timeout(std::time::Duration::from_secs(5))
|
2021-08-21 22:02:05 +08:00
|
|
|
.connect_with(configuration.with_db())
|
|
|
|
.await
|
|
|
|
}
|