From 420d90c22193a77e705d370164e8cccf434f0a06 Mon Sep 17 00:00:00 2001 From: appflowy Date: Thu, 16 Sep 2021 13:41:07 +0800 Subject: [PATCH] [server]: fix expired duration --- .../welcome/presentation/splash_screen.dart | 2 +- backend/Cargo.toml | 15 +++++- backend/src/application.rs | 6 ++- backend/src/entities/token.rs | 3 +- backend/src/middleware/auth_middleware.rs | 14 ++++-- backend/src/service/log/mod.rs | 50 +++++++++++++++++++ backend/src/service/mod.rs | 1 + .../src/service/user_service/logged_user.rs | 29 +++++++---- backend/src/service/ws_service/router.rs | 2 +- rust-lib/flowy-document/src/module.rs | 25 +++++----- .../flowy-document/src/services/doc_cache.rs | 28 +++++++---- .../src/services/server/middleware.rs | 2 +- .../flowy-ot/src/client/document/document.rs | 2 + 13 files changed, 135 insertions(+), 44 deletions(-) create mode 100644 backend/src/service/log/mod.rs diff --git a/app_flowy/lib/welcome/presentation/splash_screen.dart b/app_flowy/lib/welcome/presentation/splash_screen.dart index 19b45d4d76..ff443c92c9 100644 --- a/app_flowy/lib/welcome/presentation/splash_screen.dart +++ b/app_flowy/lib/welcome/presentation/splash_screen.dart @@ -40,7 +40,7 @@ class SplashScreen extends StatelessWidget { (workspace) => getIt() .pushHomeScreen(context, userProfile, workspace.id), (error) async { - assert(error.code == workspace.ErrorCode.CurrentWorkspaceNotFound); + assert(error.code == workspace.ErrorCode.RecordNotFound); getIt().pushWelcomeScreen(context, userProfile); }, ); diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 0a7a2ac6ed..878779d574 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -24,10 +24,23 @@ bytes = "1" toml = "0.5.8" dashmap = "4.0" log = "0.4.14" + +# tracing +tracing = { version = "0.1", features = ["log"] } +tracing-futures = "0.2.4" +tracing-subscriber = { version = "0.2.12", features = ["registry", "env-filter", "ansi", "json"] } +tracing-bunyan-formatter = "0.2.2" +tracing-appender = "0.1" +tracing-core = "0.1" +tracing-log = { version = "0.1.1"} + +# serde serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } serde_repr = "0.1" serde-aux = "1.0.1" + + derive_more = {version = "0.99"} protobuf = {version = "2.20.0"} uuid = { version = "0.8", features = ["serde", "v4"] } @@ -41,8 +54,6 @@ sql-builder = "3.1.1" lazy_static = "1.4" tokio = { version = "1", features = ["full"] } - -flowy-log = { path = "../rust-lib/flowy-log" } flowy-user = { path = "../rust-lib/flowy-user" } flowy-workspace = { path = "../rust-lib/flowy-workspace" } flowy-document = { path = "../rust-lib/flowy-document" } diff --git a/backend/src/application.rs b/backend/src/application.rs index 3d416d8b65..77d9ac348c 100644 --- a/backend/src/application.rs +++ b/backend/src/application.rs @@ -82,7 +82,7 @@ async fn period_check(_pool: Data) { } } -fn ws_scope() -> Scope { web::scope("/ws").service(ws_service::router::start_connection) } +fn ws_scope() -> Scope { web::scope("/ws").service(ws_service::router::establish_ws_connection) } fn user_scope() -> Scope { // https://developer.mozilla.org/en-US/docs/Web/HTTP @@ -132,7 +132,9 @@ fn user_scope() -> Scope { } async fn init_app_context(configuration: &Settings) -> AppContext { - let _ = flowy_log::Builder::new("flowy").env_filter("Debug").build(); + let _ = crate::service::log::Builder::new("flowy") + .env_filter("Debug") + .build(); let pg_pool = get_connection_pool(&configuration.database) .await .expect(&format!( diff --git a/backend/src/entities/token.rs b/backend/src/entities/token.rs index 93030c6661..2d586fdc7a 100644 --- a/backend/src/entities/token.rs +++ b/backend/src/entities/token.rs @@ -28,7 +28,7 @@ impl Claim { sub: "auth".to_string(), user_id: user_id.to_string(), iat: Local::now().timestamp(), - exp: (Local::now() + Duration::hours(24)).timestamp(), + exp: (Local::now() + Duration::days(EXPIRED_DURATION_DAYS)).timestamp(), } } @@ -74,6 +74,7 @@ impl Token { } } +use crate::service::user_service::EXPIRED_DURATION_DAYS; use actix_web::{dev::Payload, FromRequest, HttpRequest}; use flowy_net::config::HEADER_TOKEN; use futures::future::{ready, Ready}; diff --git a/backend/src/middleware/auth_middleware.rs b/backend/src/middleware/auth_middleware.rs index 9e9fd8a593..eb8a78e454 100644 --- a/backend/src/middleware/auth_middleware.rs +++ b/backend/src/middleware/auth_middleware.rs @@ -65,11 +65,17 @@ where if !authenticate_pass { if let Some(header) = req.headers().get(HEADER_TOKEN) { let result: Result = header.try_into(); - if let Ok(logged_user) = result { - if AUTHORIZED_USERS.is_authorized(&logged_user) { - authenticate_pass = true; - } + match result { + Ok(logged_user) => { + authenticate_pass = AUTHORIZED_USERS.is_authorized(&logged_user); + + // Update user timestamp + AUTHORIZED_USERS.store_auth(logged_user, true); + }, + Err(e) => log::error!("{:?}", e), } + } else { + log::debug!("Can't find any token from request: {:?}", req); } } diff --git a/backend/src/service/log/mod.rs b/backend/src/service/log/mod.rs new file mode 100644 index 0000000000..d6f00fed04 --- /dev/null +++ b/backend/src/service/log/mod.rs @@ -0,0 +1,50 @@ +use log::LevelFilter; +use std::path::Path; +use tracing::subscriber::set_global_default; + +use tracing_bunyan_formatter::{BunyanFormattingLayer, JsonStorageLayer}; +use tracing_log::LogTracer; +use tracing_subscriber::{layer::SubscriberExt, EnvFilter}; + +pub struct Builder { + name: String, + env_filter: String, +} + +impl Builder { + pub fn new(name: &str) -> Self { + Builder { + name: name.to_owned(), + env_filter: "Info".to_owned(), + } + } + + pub fn env_filter(mut self, env_filter: &str) -> Self { + self.env_filter = env_filter.to_owned(); + self + } + + pub fn build(self) -> std::result::Result<(), String> { + let env_filter = EnvFilter::new(self.env_filter); + let subscriber = tracing_subscriber::fmt() + .with_target(true) + .with_max_level(tracing::Level::DEBUG) + .with_writer(std::io::stderr) + .with_thread_ids(false) + .compact() + .finish() + .with(env_filter); + + let formatting_layer = BunyanFormattingLayer::new(self.name.clone(), std::io::stdout); + let _ = set_global_default(subscriber.with(JsonStorageLayer).with(formatting_layer)) + .map_err(|e| format!("{:?}", e))?; + + let _ = LogTracer::builder() + .with_max_level(LevelFilter::Debug) + .init() + .map_err(|e| format!("{:?}", e)) + .unwrap(); + + Ok(()) + } +} diff --git a/backend/src/service/mod.rs b/backend/src/service/mod.rs index 2ff59b3d4a..2447faf567 100644 --- a/backend/src/service/mod.rs +++ b/backend/src/service/mod.rs @@ -1,4 +1,5 @@ pub mod doc_service; +pub(crate) mod log; pub mod user_service; pub(crate) mod util; pub mod workspace_service; diff --git a/backend/src/service/user_service/logged_user.rs b/backend/src/service/user_service/logged_user.rs index 91011f80ac..d39969be7a 100644 --- a/backend/src/service/user_service/logged_user.rs +++ b/backend/src/service/user_service/logged_user.rs @@ -33,10 +33,7 @@ impl LoggedUser { pub fn from_token(token: String) -> Result { let user: LoggedUser = Token::decode_token(&token.into())?.into(); - match AUTHORIZED_USERS.is_authorized(&user) { - true => Ok(user), - false => Err(ServerError::unauthorized()), - } + Ok(user) } pub fn get_user_id(&self) -> Result { @@ -68,7 +65,10 @@ impl std::convert::TryFrom<&HeaderValue> for LoggedUser { fn try_from(header: &HeaderValue) -> Result { match header.to_str() { Ok(val) => LoggedUser::from_token(val.to_owned()), - Err(_) => Err(ServerError::unauthorized()), + Err(e) => { + log::error!("Header to string failed: {:?}", e); + Err(ServerError::unauthorized()) + }, } } } @@ -79,27 +79,36 @@ enum AuthStatus { NotAuthorized, } +pub const EXPIRED_DURATION_DAYS: i64 = 5; + pub struct AuthorizedUsers(DashMap); impl AuthorizedUsers { pub fn new() -> Self { Self(DashMap::new()) } pub fn is_authorized(&self, user: &LoggedUser) -> bool { match self.0.get(user) { - None => false, + None => { + log::debug!("user not login yet or server was reboot"); + false + }, Some(status) => match *status { AuthStatus::Authorized(last_time) => { let current_time = Utc::now(); - (current_time - last_time).num_days() < 5 + let days = (current_time - last_time).num_days(); + log::debug!("user active {} from now", days); + days < EXPIRED_DURATION_DAYS + }, + AuthStatus::NotAuthorized => { + log::debug!("user logout already"); + false }, - AuthStatus::NotAuthorized => false, }, } } pub fn store_auth(&self, user: LoggedUser, is_auth: bool) -> Result<(), ServerError> { - let current_time = Utc::now(); let status = if is_auth { - AuthStatus::Authorized(current_time) + AuthStatus::Authorized(Utc::now()) } else { AuthStatus::NotAuthorized }; diff --git a/backend/src/service/ws_service/router.rs b/backend/src/service/ws_service/router.rs index f81d759ab9..810e2ac53b 100644 --- a/backend/src/service/ws_service/router.rs +++ b/backend/src/service/ws_service/router.rs @@ -11,7 +11,7 @@ use actix_web::{ use actix_web_actors::ws; #[get("/{token}")] -pub async fn start_connection( +pub async fn establish_ws_connection( request: HttpRequest, payload: Payload, path: Path, diff --git a/rust-lib/flowy-document/src/module.rs b/rust-lib/flowy-document/src/module.rs index 6c453ea281..5c4a38e643 100644 --- a/rust-lib/flowy-document/src/module.rs +++ b/rust-lib/flowy-document/src/module.rs @@ -44,8 +44,17 @@ impl FlowyDocument { } pub async fn open(&self, params: QueryDocParams, pool: Arc) -> Result { - let doc = self.controller.open(params, pool).await?; - let _ = self.cache.open(&doc.id, doc.data.clone())?; + let doc = match self.cache.is_opened(¶ms.doc_id) { + true => { + let data = self.cache.read_doc(¶ms.doc_id).await?; + Doc { id: params.doc_id, data } + }, + false => { + let doc = self.controller.open(params, pool).await?; + let _ = self.cache.open(&doc.id, doc.data.clone())?; + doc + }, + }; Ok(doc) } @@ -64,16 +73,8 @@ impl FlowyDocument { }) .await?; - let doc_str = match self.cache.read_doc(¶ms.id).await? { - None => "".to_owned(), - Some(doc_json) => doc_json, - }; - - let doc = Doc { - id: params.id, - data: doc_str.as_bytes().to_vec(), - }; - + let data = self.cache.read_doc(¶ms.id).await?; + let doc = Doc { id: params.id, data }; Ok(doc) } } diff --git a/rust-lib/flowy-document/src/services/doc_cache.rs b/rust-lib/flowy-document/src/services/doc_cache.rs index f6e1631863..10d0db83b9 100644 --- a/rust-lib/flowy-document/src/services/doc_cache.rs +++ b/rust-lib/flowy-document/src/services/doc_cache.rs @@ -38,6 +38,14 @@ impl DocCache { Ok(()) } + pub(crate) fn is_opened(&self, id: T) -> bool + where + T: Into, + { + let doc_id = id.into(); + self.inner.get(&doc_id).is_some() + } + pub(crate) async fn mut_doc(&self, id: T, f: F) -> Result<(), DocError> where T: Into, @@ -53,19 +61,19 @@ impl DocCache { } } - pub(crate) async fn read_doc(&self, id: T) -> Result, DocError> + pub(crate) async fn read_doc(&self, id: T) -> Result, DocError> where - T: Into, + T: Into + Clone, { - let doc_id = id.into(); - match self.inner.get(&doc_id) { - None => Err(doc_not_found()), - Some(doc_info) => { - let write_guard = doc_info.read().await; - let doc = &(*write_guard).document; - Ok(Some(doc.to_json())) - }, + if self.is_opened(id.clone()) { + return Err(doc_not_found()); } + + let doc_id = id.into(); + let doc_info = self.inner.get(&doc_id).unwrap(); + let write_guard = doc_info.read().await; + let doc = &(*write_guard).document; + Ok(doc.to_bytes()) } pub(crate) fn close(&self, id: T) -> Result<(), DocError> diff --git a/rust-lib/flowy-document/src/services/server/middleware.rs b/rust-lib/flowy-document/src/services/server/middleware.rs index 13ad7c3778..545cd8ddba 100644 --- a/rust-lib/flowy-document/src/services/server/middleware.rs +++ b/rust-lib/flowy-document/src/services/server/middleware.rs @@ -11,7 +11,7 @@ impl ResponseMiddleware for DocMiddleware { fn receive_response(&self, token: &Option, response: &FlowyResponse) { if let Some(error) = &response.error { if error.is_unauthorized() { - log::error!("workspace user is unauthorized"); + log::error!("doc user is unauthorized"); match token { None => {}, diff --git a/rust-lib/flowy-ot/src/client/document/document.rs b/rust-lib/flowy-ot/src/client/document/document.rs index f0a1a142b5..b83c518c22 100644 --- a/rust-lib/flowy-ot/src/client/document/document.rs +++ b/rust-lib/flowy-ot/src/client/document/document.rs @@ -51,6 +51,8 @@ impl Document { pub fn to_json(&self) -> String { self.delta.to_json() } + pub fn to_bytes(&self) -> Vec { self.delta.clone().into_bytes() } + pub fn to_string(&self) -> String { self.delta.apply("").unwrap() } pub fn apply_changeset(&mut self, changeset: T) -> Result<(), OTError>