application.rs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. use std::{net::TcpListener, time::Duration};
  2. use actix::Actor;
  3. use actix_identity::{CookieIdentityPolicy, IdentityService};
  4. use actix_web::{dev::Server, middleware, web, web::Data, App, HttpServer, Scope};
  5. use sqlx::{postgres::PgPoolOptions, PgPool};
  6. use tokio::time::interval;
  7. use crate::{
  8. config::{
  9. env::{domain, secret, use_https},
  10. DatabaseSettings,
  11. Settings,
  12. },
  13. context::AppContext,
  14. service::{
  15. doc_service::router as doc,
  16. user_service::router as user,
  17. workspace_service::{
  18. app::router as app,
  19. view::router as view,
  20. workspace::router as workspace,
  21. },
  22. ws_service,
  23. ws_service::WSServer,
  24. },
  25. };
  26. pub struct Application {
  27. port: u16,
  28. server: Server,
  29. }
  30. impl Application {
  31. pub async fn build(configuration: Settings) -> Result<Self, std::io::Error> {
  32. let address = format!(
  33. "{}:{}",
  34. configuration.application.host, configuration.application.port
  35. );
  36. let listener = TcpListener::bind(&address)?;
  37. let port = listener.local_addr().unwrap().port();
  38. let app_ctx = init_app_context(&configuration).await;
  39. let server = run(listener, app_ctx)?;
  40. Ok(Self { port, server })
  41. }
  42. pub async fn run_until_stopped(self) -> Result<(), std::io::Error> { self.server.await }
  43. pub fn port(&self) -> u16 { self.port }
  44. }
  45. pub fn run(listener: TcpListener, app_ctx: AppContext) -> Result<Server, std::io::Error> {
  46. let AppContext { ws_server, pg_pool } = app_ctx;
  47. let ws_server = Data::new(ws_server);
  48. let pg_pool = Data::new(pg_pool);
  49. let domain = domain();
  50. let secret: String = secret();
  51. actix_rt::spawn(period_check(pg_pool.clone()));
  52. let server = HttpServer::new(move || {
  53. App::new()
  54. .wrap(middleware::Logger::default())
  55. .wrap(identify_service(&domain, &secret))
  56. .wrap(crate::middleware::default_cors())
  57. .wrap(crate::middleware::AuthenticationService)
  58. .app_data(web::JsonConfig::default().limit(4096))
  59. .service(ws_scope())
  60. .service(user_scope())
  61. .app_data(ws_server.clone())
  62. .app_data(pg_pool.clone())
  63. })
  64. .listen(listener)?
  65. .run();
  66. Ok(server)
  67. }
  68. async fn period_check(_pool: Data<PgPool>) {
  69. let mut i = interval(Duration::from_secs(60));
  70. loop {
  71. i.tick().await;
  72. }
  73. }
  74. fn ws_scope() -> Scope { web::scope("/ws").service(ws_service::router::start_connection) }
  75. fn user_scope() -> Scope {
  76. // https://developer.mozilla.org/en-US/docs/Web/HTTP
  77. // TODO: replace GET body with query params
  78. web::scope("/api")
  79. // authentication
  80. .service(web::resource("/auth")
  81. .route(web::post().to(user::sign_in_handler))
  82. .route(web::delete().to(user::sign_out_handler))
  83. )
  84. .service(web::resource("/user")
  85. .route(web::patch().to(user::set_user_profile_handler))
  86. .route(web::get().to(user::get_user_profile_handler))
  87. )
  88. .service(web::resource("/register")
  89. .route(web::post().to(user::register_handler))
  90. )
  91. .service(web::resource("/workspace")
  92. .route(web::post().to(workspace::create_handler))
  93. .route(web::delete().to(workspace::delete_handler))
  94. .route(web::get().to(workspace::read_handler))
  95. .route(web::patch().to(workspace::update_handler))
  96. )
  97. .service(web::resource("/workspace_list/{user_id}")
  98. .route(web::get().to(workspace::workspace_list))
  99. )
  100. .service(web::resource("/app")
  101. .route(web::post().to(app::create_handler))
  102. .route(web::delete().to(app::delete_handler))
  103. .route(web::get().to(app::read_handler))
  104. .route(web::patch().to(app::update_handler))
  105. )
  106. .service(web::resource("/view")
  107. .route(web::post().to(view::create_handler))
  108. .route(web::delete().to(view::delete_handler))
  109. .route(web::get().to(view::read_handler))
  110. .route(web::patch().to(view::update_handler))
  111. )
  112. .service(web::resource("/doc")
  113. .route(web::post().to(doc::create_handler))
  114. .route(web::delete().to(doc::delete_handler))
  115. .route(web::get().to(doc::read_handler))
  116. .route(web::patch().to(doc::update_handler))
  117. )
  118. // password
  119. .service(web::resource("/password_change")
  120. .route(web::post().to(user::change_password))
  121. )
  122. }
  123. async fn init_app_context(configuration: &Settings) -> AppContext {
  124. let _ = flowy_log::Builder::new("flowy").env_filter("Debug").build();
  125. let pg_pool = get_connection_pool(&configuration.database)
  126. .await
  127. .expect(&format!(
  128. "Failed to connect to Postgres at {:?}.",
  129. configuration.database
  130. ));
  131. let ws_server = WSServer::new().start();
  132. AppContext::new(ws_server, pg_pool)
  133. }
  134. pub fn identify_service(domain: &str, secret: &str) -> IdentityService<CookieIdentityPolicy> {
  135. IdentityService::new(
  136. CookieIdentityPolicy::new(secret.as_bytes())
  137. .name("auth")
  138. .path("/")
  139. .domain(domain)
  140. .max_age_secs(24 * 3600)
  141. .secure(use_https()),
  142. )
  143. }
  144. pub async fn get_connection_pool(configuration: &DatabaseSettings) -> Result<PgPool, sqlx::Error> {
  145. PgPoolOptions::new()
  146. .connect_timeout(std::time::Duration::from_secs(5))
  147. .connect_with(configuration.with_db())
  148. .await
  149. }