瀏覽代碼

[server]: fix expired duration

appflowy 3 年之前
父節點
當前提交
420d90c221

+ 1 - 1
app_flowy/lib/welcome/presentation/splash_screen.dart

@@ -40,7 +40,7 @@ class SplashScreen extends StatelessWidget {
           (workspace) => getIt<ISplashRoute>()
               .pushHomeScreen(context, userProfile, workspace.id),
           (error) async {
-            assert(error.code == workspace.ErrorCode.CurrentWorkspaceNotFound);
+            assert(error.code == workspace.ErrorCode.RecordNotFound);
             getIt<ISplashRoute>().pushWelcomeScreen(context, userProfile);
           },
         );

+ 13 - 2
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" }

+ 4 - 2
backend/src/application.rs

@@ -82,7 +82,7 @@ async fn period_check(_pool: Data<PgPool>) {
     }
 }
 
-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!(

+ 2 - 1
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};

+ 10 - 4
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<LoggedUser, ServerError> = 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);
             }
         }
 

+ 50 - 0
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(())
+    }
+}

+ 1 - 0
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;

+ 19 - 10
backend/src/service/user_service/logged_user.rs

@@ -33,10 +33,7 @@ impl LoggedUser {
 
     pub fn from_token(token: String) -> Result<Self, ServerError> {
         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<uuid::Uuid, ServerError> {
@@ -68,7 +65,10 @@ impl std::convert::TryFrom<&HeaderValue> for LoggedUser {
     fn try_from(header: &HeaderValue) -> Result<Self, Self::Error> {
         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<LoggedUser, AuthStatus>);
 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
         };

+ 1 - 1
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<String>,

+ 13 - 12
rust-lib/flowy-document/src/module.rs

@@ -44,8 +44,17 @@ impl FlowyDocument {
     }
 
     pub async fn open(&self, params: QueryDocParams, pool: Arc<ConnectionPool>) -> Result<Doc, DocError> {
-        let doc = self.controller.open(params, pool).await?;
-        let _ = self.cache.open(&doc.id, doc.data.clone())?;
+        let doc = match self.cache.is_opened(&params.doc_id) {
+            true => {
+                let data = self.cache.read_doc(&params.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(&params.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(&params.id).await?;
+        let doc = Doc { id: params.id, data };
         Ok(doc)
     }
 }

+ 18 - 10
rust-lib/flowy-document/src/services/doc_cache.rs

@@ -38,6 +38,14 @@ impl DocCache {
         Ok(())
     }
 
+    pub(crate) fn is_opened<T>(&self, id: T) -> bool
+    where
+        T: Into<DocId>,
+    {
+        let doc_id = id.into();
+        self.inner.get(&doc_id).is_some()
+    }
+
     pub(crate) async fn mut_doc<T, F>(&self, id: T, f: F) -> Result<(), DocError>
     where
         T: Into<DocId>,
@@ -53,19 +61,19 @@ impl DocCache {
         }
     }
 
-    pub(crate) async fn read_doc<T>(&self, id: T) -> Result<Option<String>, DocError>
+    pub(crate) async fn read_doc<T>(&self, id: T) -> Result<Vec<u8>, DocError>
     where
-        T: Into<DocId>,
+        T: Into<DocId> + 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<T>(&self, id: T) -> Result<(), DocError>

+ 1 - 1
rust-lib/flowy-document/src/services/server/middleware.rs

@@ -11,7 +11,7 @@ impl ResponseMiddleware for DocMiddleware {
     fn receive_response(&self, token: &Option<String>, 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 => {},

+ 2 - 0
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<u8> { self.delta.clone().into_bytes() }
+
     pub fn to_string(&self) -> String { self.delta.apply("").unwrap() }
 
     pub fn apply_changeset<T>(&mut self, changeset: T) -> Result<(), OTError>