Browse Source

update user profile api

appflowy 3 năm trước cách đây
mục cha
commit
77a5e84979
26 tập tin đã thay đổi với 553 bổ sung344 xóa
  1. 2 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/errors.pbenum.dart
  2. 2 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/errors.pbjson.dart
  3. 0 14
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/user_detail.pb.dart
  4. 1 2
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/user_detail.pbjson.dart
  5. 4 1
      backend/src/application.rs
  6. 6 6
      backend/src/entities/token.rs
  7. 97 24
      backend/src/user_service/auth.rs
  8. 44 21
      backend/src/user_service/logged_user.rs
  9. 22 6
      backend/src/user_service/router.rs
  10. 5 0
      backend/src/user_service/utils.rs
  11. 87 25
      backend/tests/api/auth.rs
  12. 15 7
      backend/tests/api/helper.rs
  13. 3 1
      rust-lib/flowy-net/src/config.rs
  14. 20 9
      rust-lib/flowy-net/src/request/request.rs
  15. 1 1
      rust-lib/flowy-sdk/src/deps_resolve/workspace_deps_impl.rs
  16. 25 4
      rust-lib/flowy-user/src/entities/user_detail.rs
  17. 13 3
      rust-lib/flowy-user/src/errors.rs
  18. 73 67
      rust-lib/flowy-user/src/protobuf/model/errors.rs
  19. 65 110
      rust-lib/flowy-user/src/protobuf/model/user_detail.rs
  20. 1 0
      rust-lib/flowy-user/src/protobuf/proto/errors.proto
  21. 0 1
      rust-lib/flowy-user/src/protobuf/proto/user_detail.proto
  22. 26 7
      rust-lib/flowy-user/src/services/server/server_api.rs
  23. 5 1
      rust-lib/flowy-user/src/services/server/server_api_mock.rs
  24. 27 26
      rust-lib/flowy-user/src/services/user/user_session.rs
  25. 4 3
      rust-lib/flowy-workspace/src/services/app_controller.rs
  26. 5 4
      rust-lib/flowy-workspace/src/services/view_controller.rs

+ 2 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/errors.pbenum.dart

@@ -28,6 +28,7 @@ class ErrorCode extends $pb.ProtobufEnum {
   static const ErrorCode PasswordTooLong = ErrorCode._(31, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'PasswordTooLong');
   static const ErrorCode PasswordContainsForbidCharacters = ErrorCode._(32, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'PasswordContainsForbidCharacters');
   static const ErrorCode PasswordFormatInvalid = ErrorCode._(33, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'PasswordFormatInvalid');
+  static const ErrorCode PasswordNotMatch = ErrorCode._(34, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'PasswordNotMatch');
   static const ErrorCode UserNameTooLong = ErrorCode._(40, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserNameTooLong');
   static const ErrorCode UserNameContainsForbiddenCharacters = ErrorCode._(41, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserNameContainsForbiddenCharacters');
   static const ErrorCode UserNameIsEmpty = ErrorCode._(42, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserNameIsEmpty');
@@ -56,6 +57,7 @@ class ErrorCode extends $pb.ProtobufEnum {
     PasswordTooLong,
     PasswordContainsForbidCharacters,
     PasswordFormatInvalid,
+    PasswordNotMatch,
     UserNameTooLong,
     UserNameContainsForbiddenCharacters,
     UserNameIsEmpty,

+ 2 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/errors.pbjson.dart

@@ -30,6 +30,7 @@ const ErrorCode$json = const {
     const {'1': 'PasswordTooLong', '2': 31},
     const {'1': 'PasswordContainsForbidCharacters', '2': 32},
     const {'1': 'PasswordFormatInvalid', '2': 33},
+    const {'1': 'PasswordNotMatch', '2': 34},
     const {'1': 'UserNameTooLong', '2': 40},
     const {'1': 'UserNameContainsForbiddenCharacters', '2': 41},
     const {'1': 'UserNameIsEmpty', '2': 42},
@@ -42,7 +43,7 @@ const ErrorCode$json = const {
 };
 
 /// Descriptor for `ErrorCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode('CglFcnJvckNvZGUSCwoHVW5rbm93bhAAEhoKFlVzZXJEYXRhYmFzZUluaXRGYWlsZWQQARIbChdVc2VyRGF0YWJhc2VXcml0ZUxvY2tlZBACEhoKFlVzZXJEYXRhYmFzZVJlYWRMb2NrZWQQAxIbChdVc2VyRGF0YWJhc2VEaWROb3RNYXRjaBAEEh0KGVVzZXJEYXRhYmFzZUludGVybmFsRXJyb3IQBRIUChBTcWxJbnRlcm5hbEVycm9yEAYSGAoURGF0YWJhc2VDb25uZWN0RXJyb3IQBxITCg9Vc2VyTm90TG9naW5ZZXQQChIXChNSZWFkQ3VycmVudElkRmFpbGVkEAsSGAoUV3JpdGVDdXJyZW50SWRGYWlsZWQQDBIQCgxFbWFpbElzRW1wdHkQFBIWChJFbWFpbEZvcm1hdEludmFsaWQQFRIWChJFbWFpbEFscmVhZHlFeGlzdHMQFhITCg9QYXNzd29yZElzRW1wdHkQHhITCg9QYXNzd29yZFRvb0xvbmcQHxIkCiBQYXNzd29yZENvbnRhaW5zRm9yYmlkQ2hhcmFjdGVycxAgEhkKFVBhc3N3b3JkRm9ybWF0SW52YWxpZBAhEhMKD1VzZXJOYW1lVG9vTG9uZxAoEicKI1VzZXJOYW1lQ29udGFpbnNGb3JiaWRkZW5DaGFyYWN0ZXJzECkSEwoPVXNlck5hbWVJc0VtcHR5ECoSGAoUVXNlcldvcmtzcGFjZUludmFsaWQQMhIRCg1Vc2VySWRJbnZhbGlkEDMSIAocQ3JlYXRlRGVmYXVsdFdvcmtzcGFjZUZhaWxlZBA0EiAKHERlZmF1bHRXb3Jrc3BhY2VBbHJlYWR5RXhpc3QQNRIPCgtTZXJ2ZXJFcnJvchBk');
+final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode('CglFcnJvckNvZGUSCwoHVW5rbm93bhAAEhoKFlVzZXJEYXRhYmFzZUluaXRGYWlsZWQQARIbChdVc2VyRGF0YWJhc2VXcml0ZUxvY2tlZBACEhoKFlVzZXJEYXRhYmFzZVJlYWRMb2NrZWQQAxIbChdVc2VyRGF0YWJhc2VEaWROb3RNYXRjaBAEEh0KGVVzZXJEYXRhYmFzZUludGVybmFsRXJyb3IQBRIUChBTcWxJbnRlcm5hbEVycm9yEAYSGAoURGF0YWJhc2VDb25uZWN0RXJyb3IQBxITCg9Vc2VyTm90TG9naW5ZZXQQChIXChNSZWFkQ3VycmVudElkRmFpbGVkEAsSGAoUV3JpdGVDdXJyZW50SWRGYWlsZWQQDBIQCgxFbWFpbElzRW1wdHkQFBIWChJFbWFpbEZvcm1hdEludmFsaWQQFRIWChJFbWFpbEFscmVhZHlFeGlzdHMQFhITCg9QYXNzd29yZElzRW1wdHkQHhITCg9QYXNzd29yZFRvb0xvbmcQHxIkCiBQYXNzd29yZENvbnRhaW5zRm9yYmlkQ2hhcmFjdGVycxAgEhkKFVBhc3N3b3JkRm9ybWF0SW52YWxpZBAhEhQKEFBhc3N3b3JkTm90TWF0Y2gQIhITCg9Vc2VyTmFtZVRvb0xvbmcQKBInCiNVc2VyTmFtZUNvbnRhaW5zRm9yYmlkZGVuQ2hhcmFjdGVycxApEhMKD1VzZXJOYW1lSXNFbXB0eRAqEhgKFFVzZXJXb3Jrc3BhY2VJbnZhbGlkEDISEQoNVXNlcklkSW52YWxpZBAzEiAKHENyZWF0ZURlZmF1bHRXb3Jrc3BhY2VGYWlsZWQQNBIgChxEZWZhdWx0V29ya3NwYWNlQWxyZWFkeUV4aXN0EDUSDwoLU2VydmVyRXJyb3IQZA==');
 @$core.Deprecated('Use userErrorDescriptor instead')
 const UserError$json = const {
   '1': 'UserError',

+ 0 - 14
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/user_detail.pb.dart

@@ -63,7 +63,6 @@ class UserDetail extends $pb.GeneratedMessage {
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
     ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'email')
     ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
-    ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'token')
     ..hasRequiredFields = false
   ;
 
@@ -72,7 +71,6 @@ class UserDetail extends $pb.GeneratedMessage {
     $core.String? id,
     $core.String? email,
     $core.String? name,
-    $core.String? token,
   }) {
     final _result = create();
     if (id != null) {
@@ -84,9 +82,6 @@ class UserDetail extends $pb.GeneratedMessage {
     if (name != null) {
       _result.name = name;
     }
-    if (token != null) {
-      _result.token = token;
-    }
     return _result;
   }
   factory UserDetail.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
@@ -136,15 +131,6 @@ class UserDetail extends $pb.GeneratedMessage {
   $core.bool hasName() => $_has(2);
   @$pb.TagNumber(3)
   void clearName() => clearField(3);
-
-  @$pb.TagNumber(4)
-  $core.String get token => $_getSZ(3);
-  @$pb.TagNumber(4)
-  set token($core.String v) { $_setString(3, v); }
-  @$pb.TagNumber(4)
-  $core.bool hasToken() => $_has(3);
-  @$pb.TagNumber(4)
-  void clearToken() => clearField(4);
 }
 
 enum UpdateUserRequest_OneOfName {

+ 1 - 2
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/user_detail.pbjson.dart

@@ -37,12 +37,11 @@ const UserDetail$json = const {
     const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
     const {'1': 'email', '3': 2, '4': 1, '5': 9, '10': 'email'},
     const {'1': 'name', '3': 3, '4': 1, '5': 9, '10': 'name'},
-    const {'1': 'token', '3': 4, '4': 1, '5': 9, '10': 'token'},
   ],
 };
 
 /// Descriptor for `UserDetail`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List userDetailDescriptor = $convert.base64Decode('CgpVc2VyRGV0YWlsEg4KAmlkGAEgASgJUgJpZBIUCgVlbWFpbBgCIAEoCVIFZW1haWwSEgoEbmFtZRgDIAEoCVIEbmFtZRIUCgV0b2tlbhgEIAEoCVIFdG9rZW4=');
+final $typed_data.Uint8List userDetailDescriptor = $convert.base64Decode('CgpVc2VyRGV0YWlsEg4KAmlkGAEgASgJUgJpZBIUCgVlbWFpbBgCIAEoCVIFZW1haWwSEgoEbmFtZRgDIAEoCVIEbmFtZQ==');
 @$core.Deprecated('Use updateUserRequestDescriptor instead')
 const UpdateUserRequest$json = const {
   '1': 'UpdateUserRequest',

+ 4 - 1
backend/src/application.rs

@@ -81,7 +81,10 @@ fn user_scope() -> Scope {
         .service(web::resource("/auth")
             .route(web::post().to(user::sign_in_handler))
             .route(web::delete().to(user::sign_out_handler))
-            .route(web::get().to(user::user_detail_handler))
+        )
+        .service(web::resource("/user")
+            .route(web::patch().to(user::set_user_detail_handler))
+            .route(web::get().to(user::get_user_detail_handler))
         )
         .service(web::resource("/register")
             .route(web::post().to(user::register_handler))

+ 6 - 6
backend/src/entities/token.rs

@@ -20,22 +20,22 @@ pub struct Claim {
     iat: i64,
     // expiry
     exp: i64,
-    email: String,
+    user_id: String,
 }
 
 impl Claim {
-    pub fn with_email(email: &str) -> Self {
+    pub fn with_user_id(user_id: &str) -> Self {
         let domain = domain();
         Self {
             iss: domain,
             sub: "auth".to_string(),
-            email: email.to_string(),
+            user_id: user_id.to_string(),
             iat: Local::now().timestamp(),
             exp: (Local::now() + Duration::hours(24)).timestamp(),
         }
     }
 
-    pub fn get_email(self) -> String { self.email }
+    pub fn get_user_id(self) -> String { self.user_id }
 }
 
 // impl From<Claim> for User {
@@ -45,8 +45,8 @@ impl Claim {
 #[derive(From, Into, Clone)]
 pub struct Token(String);
 impl Token {
-    pub fn create_token(data: &str) -> Result<Self, ServerError> {
-        let claims = Claim::with_email(&data);
+    pub fn create_token(user_id: &str) -> Result<Self, ServerError> {
+        let claims = Claim::with_user_id(&user_id);
         encode(
             &Header::new(DEFAULT_ALGORITHM),
             &claims,

+ 97 - 24
backend/src/user_service/auth.rs

@@ -6,15 +6,24 @@ use crate::{
     workspace_service::user_default::create_default_workspace,
 };
 
+use crate::sqlx_ext::{map_sqlx_error, SqlBuilder};
 use anyhow::Context;
 use chrono::Utc;
 use flowy_net::{
-    errors::{ErrorCode, ServerError},
+    errors::{invalid_params, ErrorCode, ServerError},
     response::FlowyResponse,
 };
 use flowy_user::{
     entities::parser::{UserEmail, UserName, UserPassword},
-    protobuf::{SignInParams, SignInResponse, SignUpParams, SignUpResponse, UserDetail},
+    protobuf::{
+        SignInParams,
+        SignInResponse,
+        SignUpParams,
+        SignUpResponse,
+        UpdateUserParams,
+        UserDetail,
+        UserToken,
+    },
 };
 use sqlx::{PgPool, Postgres};
 
@@ -29,27 +38,23 @@ pub async fn sign_in(pool: &PgPool, params: SignInParams) -> Result<SignInRespon
         .await
         .context("Failed to acquire a Postgres connection to sign in")?;
 
-    let user = read_user(&mut transaction, &email.0).await?;
+    let user = check_user_password(&mut transaction, email.as_ref(), password.as_ref()).await?;
     transaction
         .commit()
         .await
         .context("Failed to commit SQL transaction to sign in.")?;
 
-    match verify_password(&password.0, &user.password) {
-        Ok(true) => {
-            let token = Token::create_token(&user.email)?;
-            let _ = AUTHORIZED_USERS.store_auth(user.email.clone().into(), true)?;
+    let token = Token::create_token(&user.id.to_string())?;
+    let logged_user = LoggedUser::new(&user.id.to_string());
 
-            let mut response_data = SignInResponse::default();
-            response_data.set_uid(user.id.to_string());
-            response_data.set_name(user.name);
-            response_data.set_email(user.email);
-            response_data.set_token(token.clone().into());
+    let _ = AUTHORIZED_USERS.store_auth(logged_user, true)?;
+    let mut response_data = SignInResponse::default();
+    response_data.set_uid(user.id.to_string());
+    response_data.set_name(user.name);
+    response_data.set_email(user.email);
+    response_data.set_token(token.clone().into());
 
-            Ok(response_data)
-        },
-        _ => Err(ServerError::password_not_match()),
-    }
+    Ok(response_data)
 }
 
 pub async fn sign_out(params: UserToken) -> Result<FlowyResponse, ServerError> {
@@ -83,7 +88,8 @@ pub async fn register_user(
     .await
     .context("Failed to insert user")?;
 
-    let _ = AUTHORIZED_USERS.store_auth(email.as_ref().to_string().into(), true)?;
+    let logged_user = LoggedUser::new(&response_data.uid);
+    let _ = AUTHORIZED_USERS.store_auth(logged_user, true)?;
     let _ = create_default_workspace(&mut transaction, response_data.get_uid()).await?;
 
     transaction
@@ -96,15 +102,20 @@ pub async fn register_user(
 
 pub(crate) async fn get_user_details(
     pool: &PgPool,
-    token: &str,
+    logged_user: LoggedUser,
 ) -> Result<FlowyResponse, ServerError> {
-    let logged_user = LoggedUser::from_token(token.to_owned().into())?;
     let mut transaction = pool
         .begin()
         .await
         .context("Failed to acquire a Postgres connection to get user detail")?;
 
-    let user_table = read_user(&mut transaction, &logged_user.email).await?;
+    let id = logged_user.get_user_id()?;
+    let user_table =
+        sqlx::query_as::<Postgres, UserTable>("SELECT * FROM user_table WHERE id = $1")
+            .bind(id)
+            .fetch_one(&mut transaction)
+            .await
+            .map_err(|err| ServerError::internal().context(err))?;
 
     transaction
         .commit()
@@ -115,12 +126,70 @@ pub(crate) async fn get_user_details(
     let _ = AUTHORIZED_USERS.store_auth(logged_user, true)?;
 
     let mut user_detail = UserDetail::default();
+    user_detail.set_id(user_table.id.to_string());
     user_detail.set_email(user_table.email);
     user_detail.set_name(user_table.name);
-    user_detail.set_token(token.to_owned());
     FlowyResponse::success().pb(user_detail)
 }
 
+pub(crate) async fn set_user_detail(
+    pool: &PgPool,
+    logged_user: LoggedUser,
+    params: UpdateUserParams,
+) -> Result<FlowyResponse, ServerError> {
+    let mut transaction = pool
+        .begin()
+        .await
+        .context("Failed to acquire a Postgres connection to update user profile")?;
+
+    let name = match params.has_name() {
+        false => None,
+        true => Some(
+            UserName::parse(params.get_name().to_owned())
+                .map_err(invalid_params)?
+                .0,
+        ),
+    };
+
+    let email = match params.has_email() {
+        false => None,
+        true => Some(
+            UserEmail::parse(params.get_email().to_owned())
+                .map_err(invalid_params)?
+                .0,
+        ),
+    };
+
+    let password = match params.has_password() {
+        false => None,
+        true => {
+            let password =
+                UserPassword::parse(params.get_password().to_owned()).map_err(invalid_params)?;
+            let password = hash_password(password.as_ref())?;
+            Some(password)
+        },
+    };
+
+    let (sql, args) = SqlBuilder::update("user_table")
+        .add_some_arg("name", name)
+        .add_some_arg("email", email)
+        .add_some_arg("password", password)
+        .and_where_eq("id", &logged_user.get_user_id()?)
+        .build()?;
+
+    sqlx::query_with(&sql, args)
+        .execute(&mut transaction)
+        .await
+        .map_err(map_sqlx_error)?;
+
+    transaction
+        .commit()
+        .await
+        .context("Failed to commit SQL transaction to update user profile.")?;
+
+    Ok(FlowyResponse::success())
+}
+
 async fn is_email_exist(
     transaction: &mut DBTransaction<'_>,
     email: &str,
@@ -140,9 +209,10 @@ async fn is_email_exist(
     }
 }
 
-async fn read_user(
+async fn check_user_password(
     transaction: &mut DBTransaction<'_>,
     email: &str,
+    password: &str,
 ) -> Result<UserTable, ServerError> {
     let user = sqlx::query_as::<Postgres, UserTable>("SELECT * FROM user_table WHERE email = $1")
         .bind(email)
@@ -150,7 +220,10 @@ async fn read_user(
         .await
         .map_err(|err| ServerError::internal().context(err))?;
 
-    Ok(user)
+    match verify_password(&password, &user.password) {
+        Ok(true) => Ok(user),
+        _ => Err(ServerError::password_not_match()),
+    }
 }
 
 async fn insert_new_user(
@@ -159,8 +232,8 @@ async fn insert_new_user(
     email: &str,
     password: &str,
 ) -> Result<SignUpResponse, ServerError> {
-    let token = Token::create_token(email)?;
     let uuid = uuid::Uuid::new_v4();
+    let token = Token::create_token(&uuid.to_string())?;
     let password = hash_password(password)?;
     let _ = sqlx::query!(
         r#"

+ 44 - 21
backend/src/user_service/logged_user.rs

@@ -4,6 +4,8 @@ use chrono::{DateTime, Utc};
 use dashmap::DashMap;
 use flowy_net::errors::ServerError;
 
+use actix_http::header::ToStrError;
+use actix_web::http::HeaderValue;
 use lazy_static::lazy_static;
 
 lazy_static! {
@@ -12,22 +14,35 @@ lazy_static! {
 
 #[derive(Debug, PartialEq, Eq, Hash, Clone)]
 pub struct LoggedUser {
-    pub email: String,
+    user_id: String,
 }
 
 impl std::convert::From<Claim> for LoggedUser {
     fn from(c: Claim) -> Self {
         Self {
-            email: c.get_email(),
+            user_id: c.get_user_id(),
         }
     }
 }
 
-impl std::convert::From<String> for LoggedUser {
-    fn from(email: String) -> Self { Self { email } }
+impl std::convert::TryFrom<&HeaderValue> for LoggedUser {
+    type Error = ServerError;
+
+    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()),
+        }
+    }
 }
 
 impl LoggedUser {
+    pub fn new(user_id: &str) -> Self {
+        Self {
+            user_id: user_id.to_owned(),
+        }
+    }
+
     pub fn from_token(token: String) -> Result<Self, ServerError> {
         let user: LoggedUser = Token::decode_token(&token.into())?.into();
         match AUTHORIZED_USERS.is_authorized(&user) {
@@ -35,25 +50,33 @@ impl LoggedUser {
             false => Err(ServerError::unauthorized()),
         }
     }
+
+    pub fn get_user_id(&self) -> Result<uuid::Uuid, ServerError> {
+        let id = uuid::Uuid::parse_str(&self.user_id)?;
+        Ok(id)
+    }
 }
 
-// use futures::{
-//     executor::block_on,
-//     future::{ready, Ready},
-// };
-// impl FromRequest for LoggedUser {
-//     type Config = ();
-//     type Error = ServerError;
-//     type Future = Ready<Result<Self, Self::Error>>;
-//
-//     fn from_request(_req: &HttpRequest, payload: &mut Payload) ->
-// Self::Future {         let result: Result<SignOutParams, ServerError> =
-// block_on(parse_from_dev_payload(payload));         match result {
-//             Ok(params) => ready(LoggedUser::from_token(params.token)),
-//             Err(e) => ready(Err(e)),
-//         }
-//     }
-// }
+use actix_web::{dev::Payload, FromRequest, HttpRequest};
+use flowy_net::config::HEADER_TOKEN;
+use futures::{
+    executor::block_on,
+    future::{ready, Ready},
+};
+use std::convert::TryInto;
+
+impl FromRequest for LoggedUser {
+    type Config = ();
+    type Error = ServerError;
+    type Future = Ready<Result<Self, Self::Error>>;
+
+    fn from_request(request: &HttpRequest, _payload: &mut Payload) -> Self::Future {
+        match request.headers().get(HEADER_TOKEN) {
+            Some(header) => ready(header.try_into()),
+            None => ready(Err(ServerError::unauthorized())),
+        }
+    }
+}
 
 #[derive(Clone, Debug, Copy)]
 enum AuthStatus {

+ 22 - 6
backend/src/user_service/router.rs

@@ -5,10 +5,17 @@ use actix_web::{
     HttpResponse,
 };
 
-use crate::user_service::{get_user_details, register_user, sign_in, sign_out};
+use crate::user_service::{
+    get_user_details,
+    register_user,
+    set_user_detail,
+    sign_in,
+    sign_out,
+    LoggedUser,
+};
 use actix_identity::Identity;
 use flowy_net::{errors::ServerError, response::FlowyResponse};
-use flowy_user::protobuf::{SignInParams, SignUpParams, UserToken};
+use flowy_user::protobuf::{SignInParams, SignUpParams, UpdateUserParams, UserToken};
 use sqlx::PgPool;
 
 pub async fn sign_in_handler(
@@ -31,12 +38,21 @@ pub async fn sign_out_handler(payload: Payload, id: Identity) -> Result<HttpResp
     Ok(response.into())
 }
 
-pub async fn user_detail_handler(
-    payload: Payload,
+pub async fn get_user_detail_handler(
+    logged_user: LoggedUser,
+    pool: Data<PgPool>,
+) -> Result<HttpResponse, ServerError> {
+    let response = get_user_details(pool.get_ref(), logged_user).await?;
+    Ok(response.into())
+}
+
+pub async fn set_user_detail_handler(
+    logged_user: LoggedUser,
     pool: Data<PgPool>,
+    payload: Payload,
 ) -> Result<HttpResponse, ServerError> {
-    let params: QueryUserDetailParams = parse_from_payload(payload).await?;
-    let response = get_user_details(pool.get_ref(), &params.token).await?;
+    let params: UpdateUserParams = parse_from_payload(payload).await?;
+    let response = set_user_detail(pool.get_ref(), logged_user, params).await?;
     Ok(response.into())
 }
 

+ 5 - 0
backend/src/user_service/utils.rs

@@ -12,6 +12,11 @@ pub fn hash_password(plain: &str) -> Result<String, ServerError> {
     hash(plain, hashing_cost).map_err(|e| ServerError::internal().context(e))
 }
 
+// The Source is the password user enter. The hash is the source after hashing.
+// let source = "123";
+// let hash = hash_password(source).unwrap();
+//
+// verify_password(source, hash)
 pub fn verify_password(source: &str, hash: &str) -> Result<bool, ServerError> {
     match verify(source, hash) {
         Ok(true) => Ok(true),

+ 87 - 25
backend/tests/api/auth.rs

@@ -1,10 +1,20 @@
 use crate::helper::{spawn_app, TestApp};
-use flowy_user::entities::{SignInParams, SignUpParams, SignUpResponse, UserToken};
+use flowy_user::{
+    entities::{
+        SignInParams,
+        SignInResponse,
+        SignUpParams,
+        SignUpResponse,
+        UpdateUserParams,
+        UserToken,
+    },
+    errors::UserError,
+};
 
 #[actix_rt::test]
 async fn user_register() {
     let app = spawn_app().await;
-    let response = register_user(&app, "[email protected]", "HelloWork123!").await;
+    let response = register_user(&app, "[email protected]", "HelloWorld123!").await;
     log::info!("{:?}", response);
 }
 
@@ -22,7 +32,7 @@ async fn user_sign_in_with_invalid_password() {
 async fn user_sign_in_with_invalid_email() {
     let app = spawn_app().await;
     let email = "annie@gmail@";
-    let password = "HelloWork123!";
+    let password = "HelloWorld123!";
     let _ = register_user(&app, email, password).await;
 }
 
@@ -30,51 +40,103 @@ async fn user_sign_in_with_invalid_email() {
 async fn user_sign_in() {
     let app = spawn_app().await;
     let email = "[email protected]";
-    let password = "HelloWork123!";
+    let password = "HelloWorld123!";
     let _ = register_user(&app, email, password).await;
-    let response = app
-        .sign_in(SignInParams {
-            email: email.to_string(),
-            password: password.to_string(),
-        })
-        .await;
-
-    log::info!("{:?}", response);
+    let params = SignInParams {
+        email: email.to_string(),
+        password: password.to_string(),
+    };
+    let _ = app.sign_in(params).await.unwrap();
 }
 
 #[actix_rt::test]
+#[should_panic]
 async fn user_sign_out() {
     let app = spawn_app().await;
     let email = "[email protected]";
-    let password = "HelloWork123!";
+    let password = "HelloWorld123!";
     let _ = register_user(&app, email, password).await;
-    let sign_in_resp = app
-        .sign_in(SignInParams {
-            email: email.to_string(),
-            password: password.to_string(),
-        })
-        .await;
 
+    let params = SignInParams {
+        email: email.to_string(),
+        password: password.to_string(),
+    };
+    let sign_in_resp = app.sign_in(params).await.unwrap();
     let token = sign_in_resp.token.clone();
     let user_token = UserToken {
         token: token.clone(),
     };
     app.sign_out(user_token).await;
 
-    let query_user_params = UserToken { token };
-    app.get_user_detail(query_user_params).await;
+    // user_detail will be empty because use was sign out.
+    app.get_user_detail(&token).await;
 }
 
 #[actix_rt::test]
 async fn user_get_detail() {
+    let app = spawn_app().await;
+    let sign_up_resp = sign_up_user(&app).await;
+    log::info!("{:?}", app.get_user_detail(&sign_up_resp.token).await);
+}
+
+#[actix_rt::test]
+async fn user_update_password() {
     let app = spawn_app().await;
     let email = "[email protected]";
-    let password = "HelloWork123!";
+    let password = "HelloWorld123!";
     let sign_up_resp = register_user(&app, email, password).await;
-    let query_user_params = UserToken {
-        token: sign_up_resp.token,
+
+    let params = UpdateUserParams::new(&sign_up_resp.uid).password("Hello123!");
+    app.update_user_detail(&sign_up_resp.token, params)
+        .await
+        .unwrap();
+
+    let sign_in_params = SignInParams {
+        email: email.to_string(),
+        password: password.to_string(),
     };
-    log::info!("{:?}", app.get_user_detail(query_user_params).await);
+
+    match app.sign_in(sign_in_params).await {
+        Ok(_) => {},
+        Err(e) => {
+            assert_eq!(e.code, flowy_user::errors::ErrorCode::PasswordNotMatch);
+        },
+    }
+}
+
+#[actix_rt::test]
+async fn user_update_name() {
+    let app = spawn_app().await;
+    let sign_up_resp = sign_up_user(&app).await;
+    let name = "tom".to_string();
+    let params = UpdateUserParams::new(&sign_up_resp.uid).name(&name);
+    app.update_user_detail(&sign_up_resp.token, params)
+        .await
+        .unwrap();
+
+    let user = app.get_user_detail(&sign_up_resp.token).await;
+    assert_eq!(user.name, name);
+}
+
+#[actix_rt::test]
+async fn user_update_email() {
+    let app = spawn_app().await;
+    let sign_up_resp = sign_up_user(&app).await;
+    let email = "[email protected]".to_string();
+    let params = UpdateUserParams::new(&sign_up_resp.uid).email(&email);
+    app.update_user_detail(&sign_up_resp.token, params)
+        .await
+        .unwrap();
+
+    let user = app.get_user_detail(&sign_up_resp.token).await;
+    assert_eq!(user.email, email);
+}
+
+async fn sign_up_user(app: &TestApp) -> SignUpResponse {
+    let email = "[email protected]";
+    let password = "HelloWorld123!";
+    let response = register_user(&app, email, password).await;
+    response
 }
 
 async fn register_user(app: &TestApp, email: &str, password: &str) -> SignUpResponse {

+ 15 - 7
backend/tests/api/helper.rs

@@ -3,7 +3,7 @@ use backend::{
     config::{get_configuration, DatabaseSettings},
 };
 
-use flowy_user::prelude::*;
+use flowy_user::{errors::UserError, prelude::*};
 use flowy_workspace::prelude::*;
 use sqlx::{Connection, Executor, PgConnection, PgPool};
 use uuid::Uuid;
@@ -21,10 +21,9 @@ impl TestApp {
         resp
     }
 
-    pub async fn sign_in(&self, params: SignInParams) -> SignInResponse {
+    pub async fn sign_in(&self, params: SignInParams) -> Result<SignInResponse, UserError> {
         let url = format!("{}/api/auth", self.address);
-        let resp = user_sign_in(params, &url).await.unwrap();
-        resp
+        user_sign_in(params, &url).await
     }
 
     pub async fn sign_out(&self, params: UserToken) {
@@ -32,12 +31,21 @@ impl TestApp {
         let _ = user_sign_out(params, &url).await.unwrap();
     }
 
-    pub async fn get_user_detail(&self, params: UserToken) -> UserDetail {
-        let url = format!("{}/api/auth", self.address);
-        let user_detail = get_user_detail(params, &url).await.unwrap();
+    pub async fn get_user_detail(&self, token: &str) -> UserDetail {
+        let url = format!("{}/api/user", self.address);
+        let user_detail = get_user_detail(token, &url).await.unwrap();
         user_detail
     }
 
+    pub async fn update_user_detail(
+        &self,
+        token: &str,
+        params: UpdateUserParams,
+    ) -> Result<(), UserError> {
+        let url = format!("{}/api/user", self.address);
+        update_user_detail(token, params, &url).await
+    }
+
     pub async fn create_workspace(&self, params: CreateWorkspaceParams) -> Workspace {
         let url = format!("{}/api/workspace", self.address);
         let workspace = create_workspace_request(params, &url).await.unwrap();

+ 3 - 1
rust-lib/flowy-net/src/config.rs

@@ -2,9 +2,11 @@ use lazy_static::lazy_static;
 
 pub const HOST: &'static str = "http://localhost:8000";
 
+pub const HEADER_TOKEN: &'static str = "token";
+
 lazy_static! {
     pub static ref SIGN_UP_URL: String = format!("{}/api/register", HOST);
     pub static ref SIGN_IN_URL: String = format!("{}/api/auth", HOST);
-    pub static ref USER_DETAIL_URL: String = format!("{}/api/auth", HOST);
     pub static ref SIGN_OUT_URL: String = format!("{}/api/auth", HOST);
+    pub static ref USER_DETAIL_URL: String = format!("{}/api/user", HOST);
 }

+ 20 - 9
rust-lib/flowy-net/src/request/request.rs

@@ -2,7 +2,7 @@ use crate::{errors::ServerError, response::FlowyResponse};
 use bytes::Bytes;
 use hyper::http;
 use protobuf::ProtobufError;
-use reqwest::{Client, Method, Response};
+use reqwest::{header::HeaderMap, Client, Method, Response};
 use std::{
     convert::{TryFrom, TryInto},
     time::Duration,
@@ -12,7 +12,8 @@ use tokio::sync::oneshot;
 pub struct HttpRequestBuilder {
     url: String,
     body: Option<Bytes>,
-    response: Option<Response>,
+    response: Option<Bytes>,
+    headers: HeaderMap,
     method: Method,
 }
 
@@ -22,6 +23,7 @@ impl HttpRequestBuilder {
             url: url.to_owned(),
             body: None,
             response: None,
+            headers: HeaderMap::new(),
             method: Method::GET,
         }
     }
@@ -50,6 +52,11 @@ impl HttpRequestBuilder {
         builder
     }
 
+    pub fn header(mut self, key: &'static str, value: &str) -> Self {
+        self.headers.insert(key, value.parse().unwrap());
+        self
+    }
+
     pub fn protobuf<T1>(self, body: T1) -> Result<Self, ServerError>
     where
         T1: TryInto<Bytes, Error = ProtobufError>,
@@ -68,11 +75,12 @@ impl HttpRequestBuilder {
         let url = self.url.clone();
         let body = self.body.take();
         let method = self.method.clone();
+        let headers = self.headers.clone();
 
         // reqwest client is not 'Sync' by channel is.
         tokio::spawn(async move {
             let client = default_client();
-            let mut builder = client.request(method, url);
+            let mut builder = client.request(method, url).headers(headers);
 
             if let Some(body) = body {
                 builder = builder.body(body);
@@ -88,8 +96,14 @@ impl HttpRequestBuilder {
         });
 
         let response = rx.await??;
-        self.response = Some(response);
-        Ok(self)
+
+        match get_response_data(response).await {
+            Ok(bytes) => {
+                self.response = Some(bytes);
+                Ok(self)
+            },
+            Err(error) => Err(error),
+        }
     }
 
     pub async fn response<T2>(self) -> Result<T2, ServerError>
@@ -101,10 +115,7 @@ impl HttpRequestBuilder {
                 let msg = format!("Request: {} receives unexpected empty body", self.url);
                 Err(ServerError::payload_none().context(msg))
             },
-            Some(response) => {
-                let data = get_response_data(response).await?;
-                Ok(T2::try_from(data)?)
-            },
+            Some(data) => Ok(T2::try_from(data)?),
         }
     }
 }

+ 1 - 1
rust-lib/flowy-sdk/src/deps_resolve/workspace_deps_impl.rs

@@ -13,7 +13,7 @@ pub struct WorkspaceUserImpl {
 
 impl WorkspaceUser for WorkspaceUserImpl {
     fn user_id(&self) -> Result<String, WorkspaceError> {
-        self.user_session.current_session().map_err(|e| {
+        self.user_session.user_id().map_err(|e| {
             ErrorBuilder::new(ErrorCode::UserInternalError)
                 .error(e)
                 .build()

+ 25 - 4
rust-lib/flowy-user/src/entities/user_detail.rs

@@ -27,9 +27,6 @@ pub struct UserDetail {
 
     #[pb(index = 3)]
     pub name: String,
-
-    #[pb(index = 4)]
-    pub token: String,
 }
 
 use crate::{
@@ -45,7 +42,6 @@ impl std::convert::From<UserTable> for UserDetail {
             id: user.id,
             email: user.email,
             name: user.name,
-            token: user.token,
         }
     }
 }
@@ -91,6 +87,7 @@ impl UpdateUserRequest {
 
 #[derive(ProtoBuf, Default)]
 pub struct UpdateUserParams {
+    // TODO: remove user id
     #[pb(index = 1)]
     pub id: String,
 
@@ -104,6 +101,30 @@ pub struct UpdateUserParams {
     pub password: Option<String>,
 }
 
+impl UpdateUserParams {
+    pub fn new(user_id: &str) -> Self {
+        Self {
+            id: user_id.to_owned(),
+            ..Default::default()
+        }
+    }
+
+    pub fn name(mut self, name: &str) -> Self {
+        self.name = Some(name.to_owned());
+        self
+    }
+
+    pub fn email(mut self, email: &str) -> Self {
+        self.email = Some(email.to_owned());
+        self
+    }
+
+    pub fn password(mut self, password: &str) -> Self {
+        self.password = Some(password.to_owned());
+        self
+    }
+}
+
 impl TryInto<UpdateUserParams> for UpdateUserRequest {
     type Error = UserError;
 

+ 13 - 3
rust-lib/flowy-user/src/errors.rs

@@ -2,6 +2,7 @@ use bytes::Bytes;
 use derive_more::Display;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_dispatch::prelude::{EventResponse, ResponseBuilder};
+use flowy_net::errors::ServerError;
 use std::{
     convert::TryInto,
     fmt::{Debug, Formatter},
@@ -69,6 +70,8 @@ pub enum ErrorCode {
         fmt = "Password should contain a minimum of 6 characters with 1 special 1 letter and 1 numeric"
     )]
     PasswordFormatInvalid = 33,
+    #[display(fmt = "Password not match")]
+    PasswordNotMatch     = 34,
 
     #[display(fmt = "User name is too long")]
     UserNameTooLong      = 40,
@@ -158,9 +161,16 @@ impl std::convert::From<flowy_sqlite::Error> for UserError {
 
 impl std::convert::From<flowy_net::errors::ServerError> for UserError {
     fn from(error: flowy_net::errors::ServerError) -> Self {
-        ErrorBuilder::new(ErrorCode::ServerError)
-            .error(error.msg)
-            .build()
+        match error.code {
+            flowy_net::errors::ErrorCode::PasswordNotMatch => {
+                ErrorBuilder::new(ErrorCode::PasswordNotMatch)
+                    .error(error.msg)
+                    .build()
+            },
+            _ => ErrorBuilder::new(ErrorCode::ServerError)
+                .error(error.msg)
+                .build(),
+        }
     }
 }
 

+ 73 - 67
rust-lib/flowy-user/src/protobuf/model/errors.rs

@@ -233,6 +233,7 @@ pub enum ErrorCode {
     PasswordTooLong = 31,
     PasswordContainsForbidCharacters = 32,
     PasswordFormatInvalid = 33,
+    PasswordNotMatch = 34,
     UserNameTooLong = 40,
     UserNameContainsForbiddenCharacters = 41,
     UserNameIsEmpty = 42,
@@ -268,6 +269,7 @@ impl ::protobuf::ProtobufEnum for ErrorCode {
             31 => ::std::option::Option::Some(ErrorCode::PasswordTooLong),
             32 => ::std::option::Option::Some(ErrorCode::PasswordContainsForbidCharacters),
             33 => ::std::option::Option::Some(ErrorCode::PasswordFormatInvalid),
+            34 => ::std::option::Option::Some(ErrorCode::PasswordNotMatch),
             40 => ::std::option::Option::Some(ErrorCode::UserNameTooLong),
             41 => ::std::option::Option::Some(ErrorCode::UserNameContainsForbiddenCharacters),
             42 => ::std::option::Option::Some(ErrorCode::UserNameIsEmpty),
@@ -300,6 +302,7 @@ impl ::protobuf::ProtobufEnum for ErrorCode {
             ErrorCode::PasswordTooLong,
             ErrorCode::PasswordContainsForbidCharacters,
             ErrorCode::PasswordFormatInvalid,
+            ErrorCode::PasswordNotMatch,
             ErrorCode::UserNameTooLong,
             ErrorCode::UserNameContainsForbiddenCharacters,
             ErrorCode::UserNameIsEmpty,
@@ -338,7 +341,7 @@ impl ::protobuf::reflect::ProtobufValue for ErrorCode {
 static file_descriptor_proto_data: &'static [u8] = b"\
     \n\x0cerrors.proto\"=\n\tUserError\x12\x1e\n\x04code\x18\x01\x20\x01(\
     \x0e2\n.ErrorCodeR\x04code\x12\x10\n\x03msg\x18\x02\x20\x01(\tR\x03msg*\
-    \xa3\x05\n\tErrorCode\x12\x0b\n\x07Unknown\x10\0\x12\x1a\n\x16UserDataba\
+    \xb9\x05\n\tErrorCode\x12\x0b\n\x07Unknown\x10\0\x12\x1a\n\x16UserDataba\
     seInitFailed\x10\x01\x12\x1b\n\x17UserDatabaseWriteLocked\x10\x02\x12\
     \x1a\n\x16UserDatabaseReadLocked\x10\x03\x12\x1b\n\x17UserDatabaseDidNot\
     Match\x10\x04\x12\x1d\n\x19UserDatabaseInternalError\x10\x05\x12\x14\n\
@@ -348,72 +351,75 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     \x10\x14\x12\x16\n\x12EmailFormatInvalid\x10\x15\x12\x16\n\x12EmailAlrea\
     dyExists\x10\x16\x12\x13\n\x0fPasswordIsEmpty\x10\x1e\x12\x13\n\x0fPassw\
     ordTooLong\x10\x1f\x12$\n\x20PasswordContainsForbidCharacters\x10\x20\
-    \x12\x19\n\x15PasswordFormatInvalid\x10!\x12\x13\n\x0fUserNameTooLong\
-    \x10(\x12'\n#UserNameContainsForbiddenCharacters\x10)\x12\x13\n\x0fUserN\
-    ameIsEmpty\x10*\x12\x18\n\x14UserWorkspaceInvalid\x102\x12\x11\n\rUserId\
-    Invalid\x103\x12\x20\n\x1cCreateDefaultWorkspaceFailed\x104\x12\x20\n\
-    \x1cDefaultWorkspaceAlreadyExist\x105\x12\x0f\n\x0bServerError\x10dJ\xda\
-    \t\n\x06\x12\x04\0\0!\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\
-    \0\x12\x04\x02\0\x05\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x11\n\x0b\n\
-    \x04\x04\0\x02\0\x12\x03\x03\x04\x17\n\x0c\n\x05\x04\0\x02\0\x06\x12\x03\
-    \x03\x04\r\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x0e\x12\n\x0c\n\x05\
-    \x04\0\x02\0\x03\x12\x03\x03\x15\x16\n\x0b\n\x04\x04\0\x02\x01\x12\x03\
-    \x04\x04\x13\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x04\x04\n\n\x0c\n\x05\
-    \x04\0\x02\x01\x01\x12\x03\x04\x0b\x0e\n\x0c\n\x05\x04\0\x02\x01\x03\x12\
-    \x03\x04\x11\x12\n\n\n\x02\x05\0\x12\x04\x06\0!\x01\n\n\n\x03\x05\0\x01\
-    \x12\x03\x06\x05\x0e\n\x0b\n\x04\x05\0\x02\0\x12\x03\x07\x04\x10\n\x0c\n\
-    \x05\x05\0\x02\0\x01\x12\x03\x07\x04\x0b\n\x0c\n\x05\x05\0\x02\0\x02\x12\
-    \x03\x07\x0e\x0f\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x08\x04\x1f\n\x0c\n\
-    \x05\x05\0\x02\x01\x01\x12\x03\x08\x04\x1a\n\x0c\n\x05\x05\0\x02\x01\x02\
-    \x12\x03\x08\x1d\x1e\n\x0b\n\x04\x05\0\x02\x02\x12\x03\t\x04\x20\n\x0c\n\
-    \x05\x05\0\x02\x02\x01\x12\x03\t\x04\x1b\n\x0c\n\x05\x05\0\x02\x02\x02\
-    \x12\x03\t\x1e\x1f\n\x0b\n\x04\x05\0\x02\x03\x12\x03\n\x04\x1f\n\x0c\n\
-    \x05\x05\0\x02\x03\x01\x12\x03\n\x04\x1a\n\x0c\n\x05\x05\0\x02\x03\x02\
-    \x12\x03\n\x1d\x1e\n\x0b\n\x04\x05\0\x02\x04\x12\x03\x0b\x04\x20\n\x0c\n\
-    \x05\x05\0\x02\x04\x01\x12\x03\x0b\x04\x1b\n\x0c\n\x05\x05\0\x02\x04\x02\
-    \x12\x03\x0b\x1e\x1f\n\x0b\n\x04\x05\0\x02\x05\x12\x03\x0c\x04\"\n\x0c\n\
-    \x05\x05\0\x02\x05\x01\x12\x03\x0c\x04\x1d\n\x0c\n\x05\x05\0\x02\x05\x02\
-    \x12\x03\x0c\x20!\n\x0b\n\x04\x05\0\x02\x06\x12\x03\r\x04\x19\n\x0c\n\
-    \x05\x05\0\x02\x06\x01\x12\x03\r\x04\x14\n\x0c\n\x05\x05\0\x02\x06\x02\
-    \x12\x03\r\x17\x18\n\x0b\n\x04\x05\0\x02\x07\x12\x03\x0e\x04\x1d\n\x0c\n\
-    \x05\x05\0\x02\x07\x01\x12\x03\x0e\x04\x18\n\x0c\n\x05\x05\0\x02\x07\x02\
-    \x12\x03\x0e\x1b\x1c\n\x0b\n\x04\x05\0\x02\x08\x12\x03\x0f\x04\x19\n\x0c\
-    \n\x05\x05\0\x02\x08\x01\x12\x03\x0f\x04\x13\n\x0c\n\x05\x05\0\x02\x08\
-    \x02\x12\x03\x0f\x16\x18\n\x0b\n\x04\x05\0\x02\t\x12\x03\x10\x04\x1d\n\
-    \x0c\n\x05\x05\0\x02\t\x01\x12\x03\x10\x04\x17\n\x0c\n\x05\x05\0\x02\t\
-    \x02\x12\x03\x10\x1a\x1c\n\x0b\n\x04\x05\0\x02\n\x12\x03\x11\x04\x1e\n\
-    \x0c\n\x05\x05\0\x02\n\x01\x12\x03\x11\x04\x18\n\x0c\n\x05\x05\0\x02\n\
-    \x02\x12\x03\x11\x1b\x1d\n\x0b\n\x04\x05\0\x02\x0b\x12\x03\x12\x04\x16\n\
-    \x0c\n\x05\x05\0\x02\x0b\x01\x12\x03\x12\x04\x10\n\x0c\n\x05\x05\0\x02\
-    \x0b\x02\x12\x03\x12\x13\x15\n\x0b\n\x04\x05\0\x02\x0c\x12\x03\x13\x04\
-    \x1c\n\x0c\n\x05\x05\0\x02\x0c\x01\x12\x03\x13\x04\x16\n\x0c\n\x05\x05\0\
-    \x02\x0c\x02\x12\x03\x13\x19\x1b\n\x0b\n\x04\x05\0\x02\r\x12\x03\x14\x04\
-    \x1c\n\x0c\n\x05\x05\0\x02\r\x01\x12\x03\x14\x04\x16\n\x0c\n\x05\x05\0\
-    \x02\r\x02\x12\x03\x14\x19\x1b\n\x0b\n\x04\x05\0\x02\x0e\x12\x03\x15\x04\
-    \x19\n\x0c\n\x05\x05\0\x02\x0e\x01\x12\x03\x15\x04\x13\n\x0c\n\x05\x05\0\
-    \x02\x0e\x02\x12\x03\x15\x16\x18\n\x0b\n\x04\x05\0\x02\x0f\x12\x03\x16\
-    \x04\x19\n\x0c\n\x05\x05\0\x02\x0f\x01\x12\x03\x16\x04\x13\n\x0c\n\x05\
-    \x05\0\x02\x0f\x02\x12\x03\x16\x16\x18\n\x0b\n\x04\x05\0\x02\x10\x12\x03\
-    \x17\x04*\n\x0c\n\x05\x05\0\x02\x10\x01\x12\x03\x17\x04$\n\x0c\n\x05\x05\
-    \0\x02\x10\x02\x12\x03\x17')\n\x0b\n\x04\x05\0\x02\x11\x12\x03\x18\x04\
-    \x1f\n\x0c\n\x05\x05\0\x02\x11\x01\x12\x03\x18\x04\x19\n\x0c\n\x05\x05\0\
-    \x02\x11\x02\x12\x03\x18\x1c\x1e\n\x0b\n\x04\x05\0\x02\x12\x12\x03\x19\
-    \x04\x19\n\x0c\n\x05\x05\0\x02\x12\x01\x12\x03\x19\x04\x13\n\x0c\n\x05\
-    \x05\0\x02\x12\x02\x12\x03\x19\x16\x18\n\x0b\n\x04\x05\0\x02\x13\x12\x03\
-    \x1a\x04-\n\x0c\n\x05\x05\0\x02\x13\x01\x12\x03\x1a\x04'\n\x0c\n\x05\x05\
-    \0\x02\x13\x02\x12\x03\x1a*,\n\x0b\n\x04\x05\0\x02\x14\x12\x03\x1b\x04\
-    \x19\n\x0c\n\x05\x05\0\x02\x14\x01\x12\x03\x1b\x04\x13\n\x0c\n\x05\x05\0\
-    \x02\x14\x02\x12\x03\x1b\x16\x18\n\x0b\n\x04\x05\0\x02\x15\x12\x03\x1c\
-    \x04\x1e\n\x0c\n\x05\x05\0\x02\x15\x01\x12\x03\x1c\x04\x18\n\x0c\n\x05\
-    \x05\0\x02\x15\x02\x12\x03\x1c\x1b\x1d\n\x0b\n\x04\x05\0\x02\x16\x12\x03\
-    \x1d\x04\x17\n\x0c\n\x05\x05\0\x02\x16\x01\x12\x03\x1d\x04\x11\n\x0c\n\
-    \x05\x05\0\x02\x16\x02\x12\x03\x1d\x14\x16\n\x0b\n\x04\x05\0\x02\x17\x12\
-    \x03\x1e\x04&\n\x0c\n\x05\x05\0\x02\x17\x01\x12\x03\x1e\x04\x20\n\x0c\n\
-    \x05\x05\0\x02\x17\x02\x12\x03\x1e#%\n\x0b\n\x04\x05\0\x02\x18\x12\x03\
-    \x1f\x04&\n\x0c\n\x05\x05\0\x02\x18\x01\x12\x03\x1f\x04\x20\n\x0c\n\x05\
-    \x05\0\x02\x18\x02\x12\x03\x1f#%\n\x0b\n\x04\x05\0\x02\x19\x12\x03\x20\
-    \x04\x16\n\x0c\n\x05\x05\0\x02\x19\x01\x12\x03\x20\x04\x0f\n\x0c\n\x05\
-    \x05\0\x02\x19\x02\x12\x03\x20\x12\x15b\x06proto3\
+    \x12\x19\n\x15PasswordFormatInvalid\x10!\x12\x14\n\x10PasswordNotMatch\
+    \x10\"\x12\x13\n\x0fUserNameTooLong\x10(\x12'\n#UserNameContainsForbidde\
+    nCharacters\x10)\x12\x13\n\x0fUserNameIsEmpty\x10*\x12\x18\n\x14UserWork\
+    spaceInvalid\x102\x12\x11\n\rUserIdInvalid\x103\x12\x20\n\x1cCreateDefau\
+    ltWorkspaceFailed\x104\x12\x20\n\x1cDefaultWorkspaceAlreadyExist\x105\
+    \x12\x0f\n\x0bServerError\x10dJ\x83\n\n\x06\x12\x04\0\0\"\x01\n\x08\n\
+    \x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x05\x01\n\n\n\x03\
+    \x04\0\x01\x12\x03\x02\x08\x11\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\
+    \x17\n\x0c\n\x05\x04\0\x02\0\x06\x12\x03\x03\x04\r\n\x0c\n\x05\x04\0\x02\
+    \0\x01\x12\x03\x03\x0e\x12\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x15\
+    \x16\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\x13\n\x0c\n\x05\x04\0\x02\
+    \x01\x05\x12\x03\x04\x04\n\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\x0b\
+    \x0e\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x11\x12\n\n\n\x02\x05\0\
+    \x12\x04\x06\0\"\x01\n\n\n\x03\x05\0\x01\x12\x03\x06\x05\x0e\n\x0b\n\x04\
+    \x05\0\x02\0\x12\x03\x07\x04\x10\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x07\
+    \x04\x0b\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x07\x0e\x0f\n\x0b\n\x04\x05\
+    \0\x02\x01\x12\x03\x08\x04\x1f\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x08\
+    \x04\x1a\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x08\x1d\x1e\n\x0b\n\x04\
+    \x05\0\x02\x02\x12\x03\t\x04\x20\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\t\
+    \x04\x1b\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\t\x1e\x1f\n\x0b\n\x04\x05\
+    \0\x02\x03\x12\x03\n\x04\x1f\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\n\x04\
+    \x1a\n\x0c\n\x05\x05\0\x02\x03\x02\x12\x03\n\x1d\x1e\n\x0b\n\x04\x05\0\
+    \x02\x04\x12\x03\x0b\x04\x20\n\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x0b\
+    \x04\x1b\n\x0c\n\x05\x05\0\x02\x04\x02\x12\x03\x0b\x1e\x1f\n\x0b\n\x04\
+    \x05\0\x02\x05\x12\x03\x0c\x04\"\n\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\
+    \x0c\x04\x1d\n\x0c\n\x05\x05\0\x02\x05\x02\x12\x03\x0c\x20!\n\x0b\n\x04\
+    \x05\0\x02\x06\x12\x03\r\x04\x19\n\x0c\n\x05\x05\0\x02\x06\x01\x12\x03\r\
+    \x04\x14\n\x0c\n\x05\x05\0\x02\x06\x02\x12\x03\r\x17\x18\n\x0b\n\x04\x05\
+    \0\x02\x07\x12\x03\x0e\x04\x1d\n\x0c\n\x05\x05\0\x02\x07\x01\x12\x03\x0e\
+    \x04\x18\n\x0c\n\x05\x05\0\x02\x07\x02\x12\x03\x0e\x1b\x1c\n\x0b\n\x04\
+    \x05\0\x02\x08\x12\x03\x0f\x04\x19\n\x0c\n\x05\x05\0\x02\x08\x01\x12\x03\
+    \x0f\x04\x13\n\x0c\n\x05\x05\0\x02\x08\x02\x12\x03\x0f\x16\x18\n\x0b\n\
+    \x04\x05\0\x02\t\x12\x03\x10\x04\x1d\n\x0c\n\x05\x05\0\x02\t\x01\x12\x03\
+    \x10\x04\x17\n\x0c\n\x05\x05\0\x02\t\x02\x12\x03\x10\x1a\x1c\n\x0b\n\x04\
+    \x05\0\x02\n\x12\x03\x11\x04\x1e\n\x0c\n\x05\x05\0\x02\n\x01\x12\x03\x11\
+    \x04\x18\n\x0c\n\x05\x05\0\x02\n\x02\x12\x03\x11\x1b\x1d\n\x0b\n\x04\x05\
+    \0\x02\x0b\x12\x03\x12\x04\x16\n\x0c\n\x05\x05\0\x02\x0b\x01\x12\x03\x12\
+    \x04\x10\n\x0c\n\x05\x05\0\x02\x0b\x02\x12\x03\x12\x13\x15\n\x0b\n\x04\
+    \x05\0\x02\x0c\x12\x03\x13\x04\x1c\n\x0c\n\x05\x05\0\x02\x0c\x01\x12\x03\
+    \x13\x04\x16\n\x0c\n\x05\x05\0\x02\x0c\x02\x12\x03\x13\x19\x1b\n\x0b\n\
+    \x04\x05\0\x02\r\x12\x03\x14\x04\x1c\n\x0c\n\x05\x05\0\x02\r\x01\x12\x03\
+    \x14\x04\x16\n\x0c\n\x05\x05\0\x02\r\x02\x12\x03\x14\x19\x1b\n\x0b\n\x04\
+    \x05\0\x02\x0e\x12\x03\x15\x04\x19\n\x0c\n\x05\x05\0\x02\x0e\x01\x12\x03\
+    \x15\x04\x13\n\x0c\n\x05\x05\0\x02\x0e\x02\x12\x03\x15\x16\x18\n\x0b\n\
+    \x04\x05\0\x02\x0f\x12\x03\x16\x04\x19\n\x0c\n\x05\x05\0\x02\x0f\x01\x12\
+    \x03\x16\x04\x13\n\x0c\n\x05\x05\0\x02\x0f\x02\x12\x03\x16\x16\x18\n\x0b\
+    \n\x04\x05\0\x02\x10\x12\x03\x17\x04*\n\x0c\n\x05\x05\0\x02\x10\x01\x12\
+    \x03\x17\x04$\n\x0c\n\x05\x05\0\x02\x10\x02\x12\x03\x17')\n\x0b\n\x04\
+    \x05\0\x02\x11\x12\x03\x18\x04\x1f\n\x0c\n\x05\x05\0\x02\x11\x01\x12\x03\
+    \x18\x04\x19\n\x0c\n\x05\x05\0\x02\x11\x02\x12\x03\x18\x1c\x1e\n\x0b\n\
+    \x04\x05\0\x02\x12\x12\x03\x19\x04\x1a\n\x0c\n\x05\x05\0\x02\x12\x01\x12\
+    \x03\x19\x04\x14\n\x0c\n\x05\x05\0\x02\x12\x02\x12\x03\x19\x17\x19\n\x0b\
+    \n\x04\x05\0\x02\x13\x12\x03\x1a\x04\x19\n\x0c\n\x05\x05\0\x02\x13\x01\
+    \x12\x03\x1a\x04\x13\n\x0c\n\x05\x05\0\x02\x13\x02\x12\x03\x1a\x16\x18\n\
+    \x0b\n\x04\x05\0\x02\x14\x12\x03\x1b\x04-\n\x0c\n\x05\x05\0\x02\x14\x01\
+    \x12\x03\x1b\x04'\n\x0c\n\x05\x05\0\x02\x14\x02\x12\x03\x1b*,\n\x0b\n\
+    \x04\x05\0\x02\x15\x12\x03\x1c\x04\x19\n\x0c\n\x05\x05\0\x02\x15\x01\x12\
+    \x03\x1c\x04\x13\n\x0c\n\x05\x05\0\x02\x15\x02\x12\x03\x1c\x16\x18\n\x0b\
+    \n\x04\x05\0\x02\x16\x12\x03\x1d\x04\x1e\n\x0c\n\x05\x05\0\x02\x16\x01\
+    \x12\x03\x1d\x04\x18\n\x0c\n\x05\x05\0\x02\x16\x02\x12\x03\x1d\x1b\x1d\n\
+    \x0b\n\x04\x05\0\x02\x17\x12\x03\x1e\x04\x17\n\x0c\n\x05\x05\0\x02\x17\
+    \x01\x12\x03\x1e\x04\x11\n\x0c\n\x05\x05\0\x02\x17\x02\x12\x03\x1e\x14\
+    \x16\n\x0b\n\x04\x05\0\x02\x18\x12\x03\x1f\x04&\n\x0c\n\x05\x05\0\x02\
+    \x18\x01\x12\x03\x1f\x04\x20\n\x0c\n\x05\x05\0\x02\x18\x02\x12\x03\x1f#%\
+    \n\x0b\n\x04\x05\0\x02\x19\x12\x03\x20\x04&\n\x0c\n\x05\x05\0\x02\x19\
+    \x01\x12\x03\x20\x04\x20\n\x0c\n\x05\x05\0\x02\x19\x02\x12\x03\x20#%\n\
+    \x0b\n\x04\x05\0\x02\x1a\x12\x03!\x04\x16\n\x0c\n\x05\x05\0\x02\x1a\x01\
+    \x12\x03!\x04\x0f\n\x0c\n\x05\x05\0\x02\x1a\x02\x12\x03!\x12\x15b\x06pro\
+    to3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 65 - 110
rust-lib/flowy-user/src/protobuf/model/user_detail.rs

@@ -188,7 +188,6 @@ pub struct UserDetail {
     pub id: ::std::string::String,
     pub email: ::std::string::String,
     pub name: ::std::string::String,
-    pub token: ::std::string::String,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -282,32 +281,6 @@ impl UserDetail {
     pub fn take_name(&mut self) -> ::std::string::String {
         ::std::mem::replace(&mut self.name, ::std::string::String::new())
     }
-
-    // string token = 4;
-
-
-    pub fn get_token(&self) -> &str {
-        &self.token
-    }
-    pub fn clear_token(&mut self) {
-        self.token.clear();
-    }
-
-    // Param is passed by value, moved
-    pub fn set_token(&mut self, v: ::std::string::String) {
-        self.token = v;
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_token(&mut self) -> &mut ::std::string::String {
-        &mut self.token
-    }
-
-    // Take field
-    pub fn take_token(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.token, ::std::string::String::new())
-    }
 }
 
 impl ::protobuf::Message for UserDetail {
@@ -328,9 +301,6 @@ impl ::protobuf::Message for UserDetail {
                 3 => {
                     ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.name)?;
                 },
-                4 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.token)?;
-                },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
                 },
@@ -352,9 +322,6 @@ impl ::protobuf::Message for UserDetail {
         if !self.name.is_empty() {
             my_size += ::protobuf::rt::string_size(3, &self.name);
         }
-        if !self.token.is_empty() {
-            my_size += ::protobuf::rt::string_size(4, &self.token);
-        }
         my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
         self.cached_size.set(my_size);
         my_size
@@ -370,9 +337,6 @@ impl ::protobuf::Message for UserDetail {
         if !self.name.is_empty() {
             os.write_string(3, &self.name)?;
         }
-        if !self.token.is_empty() {
-            os.write_string(4, &self.token)?;
-        }
         os.write_unknown_fields(self.get_unknown_fields())?;
         ::std::result::Result::Ok(())
     }
@@ -426,11 +390,6 @@ impl ::protobuf::Message for UserDetail {
                 |m: &UserDetail| { &m.name },
                 |m: &mut UserDetail| { &mut m.name },
             ));
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
-                "token",
-                |m: &UserDetail| { &m.token },
-                |m: &mut UserDetail| { &mut m.token },
-            ));
             ::protobuf::reflect::MessageDescriptor::new_pb_name::<UserDetail>(
                 "UserDetail",
                 fields,
@@ -450,7 +409,6 @@ impl ::protobuf::Clear for UserDetail {
         self.id.clear();
         self.email.clear();
         self.name.clear();
-        self.token.clear();
         self.unknown_fields.clear();
     }
 }
@@ -1328,75 +1286,72 @@ impl ::protobuf::reflect::ProtobufValue for UserStatus {
 
 static file_descriptor_proto_data: &'static [u8] = b"\
     \n\x11user_detail.proto\"!\n\tUserToken\x12\x14\n\x05token\x18\x01\x20\
-    \x01(\tR\x05token\"\\\n\nUserDetail\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\
+    \x01(\tR\x05token\"F\n\nUserDetail\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\
     \x02id\x12\x14\n\x05email\x18\x02\x20\x01(\tR\x05email\x12\x12\n\x04name\
-    \x18\x03\x20\x01(\tR\x04name\x12\x14\n\x05token\x18\x04\x20\x01(\tR\x05t\
-    oken\"\xa1\x01\n\x11UpdateUserRequest\x12\x0e\n\x02id\x18\x01\x20\x01(\t\
-    R\x02id\x12\x14\n\x04name\x18\x02\x20\x01(\tH\0R\x04name\x12\x16\n\x05em\
-    ail\x18\x03\x20\x01(\tH\x01R\x05email\x12\x1c\n\x08password\x18\x04\x20\
-    \x01(\tH\x02R\x08passwordB\r\n\x0bone_of_nameB\x0e\n\x0cone_of_emailB\
-    \x11\n\x0fone_of_password\"\xa0\x01\n\x10UpdateUserParams\x12\x0e\n\x02i\
-    d\x18\x01\x20\x01(\tR\x02id\x12\x14\n\x04name\x18\x02\x20\x01(\tH\0R\x04\
-    name\x12\x16\n\x05email\x18\x03\x20\x01(\tH\x01R\x05email\x12\x1c\n\x08p\
-    assword\x18\x04\x20\x01(\tH\x02R\x08passwordB\r\n\x0bone_of_nameB\x0e\n\
-    \x0cone_of_emailB\x11\n\x0fone_of_password*1\n\nUserStatus\x12\x0b\n\x07\
-    Unknown\x10\0\x12\t\n\x05Login\x10\x01\x12\x0b\n\x07Expired\x10\x02J\xf2\
-    \x08\n\x06\x12\x04\0\0\x1b\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\
-    \x04\0\x12\x04\x02\0\x04\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x11\n\
-    \x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x15\n\x0c\n\x05\x04\0\x02\0\x05\
-    \x12\x03\x03\x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x0b\x10\n\x0c\
-    \n\x05\x04\0\x02\0\x03\x12\x03\x03\x13\x14\n\n\n\x02\x04\x01\x12\x04\x05\
-    \0\n\x01\n\n\n\x03\x04\x01\x01\x12\x03\x05\x08\x12\n\x0b\n\x04\x04\x01\
-    \x02\0\x12\x03\x06\x04\x12\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\x06\x04\
-    \n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x06\x0b\r\n\x0c\n\x05\x04\x01\
-    \x02\0\x03\x12\x03\x06\x10\x11\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\x07\
-    \x04\x15\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\x07\x04\n\n\x0c\n\x05\
-    \x04\x01\x02\x01\x01\x12\x03\x07\x0b\x10\n\x0c\n\x05\x04\x01\x02\x01\x03\
-    \x12\x03\x07\x13\x14\n\x0b\n\x04\x04\x01\x02\x02\x12\x03\x08\x04\x14\n\
-    \x0c\n\x05\x04\x01\x02\x02\x05\x12\x03\x08\x04\n\n\x0c\n\x05\x04\x01\x02\
-    \x02\x01\x12\x03\x08\x0b\x0f\n\x0c\n\x05\x04\x01\x02\x02\x03\x12\x03\x08\
-    \x12\x13\n\x0b\n\x04\x04\x01\x02\x03\x12\x03\t\x04\x15\n\x0c\n\x05\x04\
-    \x01\x02\x03\x05\x12\x03\t\x04\n\n\x0c\n\x05\x04\x01\x02\x03\x01\x12\x03\
-    \t\x0b\x10\n\x0c\n\x05\x04\x01\x02\x03\x03\x12\x03\t\x13\x14\n\n\n\x02\
-    \x04\x02\x12\x04\x0b\0\x10\x01\n\n\n\x03\x04\x02\x01\x12\x03\x0b\x08\x19\
-    \n\x0b\n\x04\x04\x02\x02\0\x12\x03\x0c\x04\x12\n\x0c\n\x05\x04\x02\x02\0\
-    \x05\x12\x03\x0c\x04\n\n\x0c\n\x05\x04\x02\x02\0\x01\x12\x03\x0c\x0b\r\n\
-    \x0c\n\x05\x04\x02\x02\0\x03\x12\x03\x0c\x10\x11\n\x0b\n\x04\x04\x02\x08\
-    \0\x12\x03\r\x04*\n\x0c\n\x05\x04\x02\x08\0\x01\x12\x03\r\n\x15\n\x0b\n\
-    \x04\x04\x02\x02\x01\x12\x03\r\x18(\n\x0c\n\x05\x04\x02\x02\x01\x05\x12\
-    \x03\r\x18\x1e\n\x0c\n\x05\x04\x02\x02\x01\x01\x12\x03\r\x1f#\n\x0c\n\
-    \x05\x04\x02\x02\x01\x03\x12\x03\r&'\n\x0b\n\x04\x04\x02\x08\x01\x12\x03\
-    \x0e\x04,\n\x0c\n\x05\x04\x02\x08\x01\x01\x12\x03\x0e\n\x16\n\x0b\n\x04\
-    \x04\x02\x02\x02\x12\x03\x0e\x19*\n\x0c\n\x05\x04\x02\x02\x02\x05\x12\
-    \x03\x0e\x19\x1f\n\x0c\n\x05\x04\x02\x02\x02\x01\x12\x03\x0e\x20%\n\x0c\
-    \n\x05\x04\x02\x02\x02\x03\x12\x03\x0e()\n\x0b\n\x04\x04\x02\x08\x02\x12\
-    \x03\x0f\x042\n\x0c\n\x05\x04\x02\x08\x02\x01\x12\x03\x0f\n\x19\n\x0b\n\
-    \x04\x04\x02\x02\x03\x12\x03\x0f\x1c0\n\x0c\n\x05\x04\x02\x02\x03\x05\
-    \x12\x03\x0f\x1c\"\n\x0c\n\x05\x04\x02\x02\x03\x01\x12\x03\x0f#+\n\x0c\n\
-    \x05\x04\x02\x02\x03\x03\x12\x03\x0f./\n\n\n\x02\x04\x03\x12\x04\x11\0\
-    \x16\x01\n\n\n\x03\x04\x03\x01\x12\x03\x11\x08\x18\n\x0b\n\x04\x04\x03\
-    \x02\0\x12\x03\x12\x04\x12\n\x0c\n\x05\x04\x03\x02\0\x05\x12\x03\x12\x04\
-    \n\n\x0c\n\x05\x04\x03\x02\0\x01\x12\x03\x12\x0b\r\n\x0c\n\x05\x04\x03\
-    \x02\0\x03\x12\x03\x12\x10\x11\n\x0b\n\x04\x04\x03\x08\0\x12\x03\x13\x04\
-    *\n\x0c\n\x05\x04\x03\x08\0\x01\x12\x03\x13\n\x15\n\x0b\n\x04\x04\x03\
-    \x02\x01\x12\x03\x13\x18(\n\x0c\n\x05\x04\x03\x02\x01\x05\x12\x03\x13\
-    \x18\x1e\n\x0c\n\x05\x04\x03\x02\x01\x01\x12\x03\x13\x1f#\n\x0c\n\x05\
-    \x04\x03\x02\x01\x03\x12\x03\x13&'\n\x0b\n\x04\x04\x03\x08\x01\x12\x03\
-    \x14\x04,\n\x0c\n\x05\x04\x03\x08\x01\x01\x12\x03\x14\n\x16\n\x0b\n\x04\
-    \x04\x03\x02\x02\x12\x03\x14\x19*\n\x0c\n\x05\x04\x03\x02\x02\x05\x12\
-    \x03\x14\x19\x1f\n\x0c\n\x05\x04\x03\x02\x02\x01\x12\x03\x14\x20%\n\x0c\
-    \n\x05\x04\x03\x02\x02\x03\x12\x03\x14()\n\x0b\n\x04\x04\x03\x08\x02\x12\
-    \x03\x15\x042\n\x0c\n\x05\x04\x03\x08\x02\x01\x12\x03\x15\n\x19\n\x0b\n\
-    \x04\x04\x03\x02\x03\x12\x03\x15\x1c0\n\x0c\n\x05\x04\x03\x02\x03\x05\
-    \x12\x03\x15\x1c\"\n\x0c\n\x05\x04\x03\x02\x03\x01\x12\x03\x15#+\n\x0c\n\
-    \x05\x04\x03\x02\x03\x03\x12\x03\x15./\n\n\n\x02\x05\0\x12\x04\x17\0\x1b\
-    \x01\n\n\n\x03\x05\0\x01\x12\x03\x17\x05\x0f\n\x0b\n\x04\x05\0\x02\0\x12\
-    \x03\x18\x04\x10\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x18\x04\x0b\n\x0c\n\
-    \x05\x05\0\x02\0\x02\x12\x03\x18\x0e\x0f\n\x0b\n\x04\x05\0\x02\x01\x12\
-    \x03\x19\x04\x0e\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x19\x04\t\n\x0c\n\
-    \x05\x05\0\x02\x01\x02\x12\x03\x19\x0c\r\n\x0b\n\x04\x05\0\x02\x02\x12\
-    \x03\x1a\x04\x10\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\x1a\x04\x0b\n\x0c\
-    \n\x05\x05\0\x02\x02\x02\x12\x03\x1a\x0e\x0fb\x06proto3\
+    \x18\x03\x20\x01(\tR\x04name\"\xa1\x01\n\x11UpdateUserRequest\x12\x0e\n\
+    \x02id\x18\x01\x20\x01(\tR\x02id\x12\x14\n\x04name\x18\x02\x20\x01(\tH\0\
+    R\x04name\x12\x16\n\x05email\x18\x03\x20\x01(\tH\x01R\x05email\x12\x1c\n\
+    \x08password\x18\x04\x20\x01(\tH\x02R\x08passwordB\r\n\x0bone_of_nameB\
+    \x0e\n\x0cone_of_emailB\x11\n\x0fone_of_password\"\xa0\x01\n\x10UpdateUs\
+    erParams\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x14\n\x04name\x18\
+    \x02\x20\x01(\tH\0R\x04name\x12\x16\n\x05email\x18\x03\x20\x01(\tH\x01R\
+    \x05email\x12\x1c\n\x08password\x18\x04\x20\x01(\tH\x02R\x08passwordB\r\
+    \n\x0bone_of_nameB\x0e\n\x0cone_of_emailB\x11\n\x0fone_of_password*1\n\n\
+    UserStatus\x12\x0b\n\x07Unknown\x10\0\x12\t\n\x05Login\x10\x01\x12\x0b\n\
+    \x07Expired\x10\x02J\xbb\x08\n\x06\x12\x04\0\0\x1a\x01\n\x08\n\x01\x0c\
+    \x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x04\x01\n\n\n\x03\x04\0\
+    \x01\x12\x03\x02\x08\x11\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x15\n\
+    \x0c\n\x05\x04\0\x02\0\x05\x12\x03\x03\x04\n\n\x0c\n\x05\x04\0\x02\0\x01\
+    \x12\x03\x03\x0b\x10\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x13\x14\n\n\
+    \n\x02\x04\x01\x12\x04\x05\0\t\x01\n\n\n\x03\x04\x01\x01\x12\x03\x05\x08\
+    \x12\n\x0b\n\x04\x04\x01\x02\0\x12\x03\x06\x04\x12\n\x0c\n\x05\x04\x01\
+    \x02\0\x05\x12\x03\x06\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x06\
+    \x0b\r\n\x0c\n\x05\x04\x01\x02\0\x03\x12\x03\x06\x10\x11\n\x0b\n\x04\x04\
+    \x01\x02\x01\x12\x03\x07\x04\x15\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\
+    \x07\x04\n\n\x0c\n\x05\x04\x01\x02\x01\x01\x12\x03\x07\x0b\x10\n\x0c\n\
+    \x05\x04\x01\x02\x01\x03\x12\x03\x07\x13\x14\n\x0b\n\x04\x04\x01\x02\x02\
+    \x12\x03\x08\x04\x14\n\x0c\n\x05\x04\x01\x02\x02\x05\x12\x03\x08\x04\n\n\
+    \x0c\n\x05\x04\x01\x02\x02\x01\x12\x03\x08\x0b\x0f\n\x0c\n\x05\x04\x01\
+    \x02\x02\x03\x12\x03\x08\x12\x13\n\n\n\x02\x04\x02\x12\x04\n\0\x0f\x01\n\
+    \n\n\x03\x04\x02\x01\x12\x03\n\x08\x19\n\x0b\n\x04\x04\x02\x02\0\x12\x03\
+    \x0b\x04\x12\n\x0c\n\x05\x04\x02\x02\0\x05\x12\x03\x0b\x04\n\n\x0c\n\x05\
+    \x04\x02\x02\0\x01\x12\x03\x0b\x0b\r\n\x0c\n\x05\x04\x02\x02\0\x03\x12\
+    \x03\x0b\x10\x11\n\x0b\n\x04\x04\x02\x08\0\x12\x03\x0c\x04*\n\x0c\n\x05\
+    \x04\x02\x08\0\x01\x12\x03\x0c\n\x15\n\x0b\n\x04\x04\x02\x02\x01\x12\x03\
+    \x0c\x18(\n\x0c\n\x05\x04\x02\x02\x01\x05\x12\x03\x0c\x18\x1e\n\x0c\n\
+    \x05\x04\x02\x02\x01\x01\x12\x03\x0c\x1f#\n\x0c\n\x05\x04\x02\x02\x01\
+    \x03\x12\x03\x0c&'\n\x0b\n\x04\x04\x02\x08\x01\x12\x03\r\x04,\n\x0c\n\
+    \x05\x04\x02\x08\x01\x01\x12\x03\r\n\x16\n\x0b\n\x04\x04\x02\x02\x02\x12\
+    \x03\r\x19*\n\x0c\n\x05\x04\x02\x02\x02\x05\x12\x03\r\x19\x1f\n\x0c\n\
+    \x05\x04\x02\x02\x02\x01\x12\x03\r\x20%\n\x0c\n\x05\x04\x02\x02\x02\x03\
+    \x12\x03\r()\n\x0b\n\x04\x04\x02\x08\x02\x12\x03\x0e\x042\n\x0c\n\x05\
+    \x04\x02\x08\x02\x01\x12\x03\x0e\n\x19\n\x0b\n\x04\x04\x02\x02\x03\x12\
+    \x03\x0e\x1c0\n\x0c\n\x05\x04\x02\x02\x03\x05\x12\x03\x0e\x1c\"\n\x0c\n\
+    \x05\x04\x02\x02\x03\x01\x12\x03\x0e#+\n\x0c\n\x05\x04\x02\x02\x03\x03\
+    \x12\x03\x0e./\n\n\n\x02\x04\x03\x12\x04\x10\0\x15\x01\n\n\n\x03\x04\x03\
+    \x01\x12\x03\x10\x08\x18\n\x0b\n\x04\x04\x03\x02\0\x12\x03\x11\x04\x12\n\
+    \x0c\n\x05\x04\x03\x02\0\x05\x12\x03\x11\x04\n\n\x0c\n\x05\x04\x03\x02\0\
+    \x01\x12\x03\x11\x0b\r\n\x0c\n\x05\x04\x03\x02\0\x03\x12\x03\x11\x10\x11\
+    \n\x0b\n\x04\x04\x03\x08\0\x12\x03\x12\x04*\n\x0c\n\x05\x04\x03\x08\0\
+    \x01\x12\x03\x12\n\x15\n\x0b\n\x04\x04\x03\x02\x01\x12\x03\x12\x18(\n\
+    \x0c\n\x05\x04\x03\x02\x01\x05\x12\x03\x12\x18\x1e\n\x0c\n\x05\x04\x03\
+    \x02\x01\x01\x12\x03\x12\x1f#\n\x0c\n\x05\x04\x03\x02\x01\x03\x12\x03\
+    \x12&'\n\x0b\n\x04\x04\x03\x08\x01\x12\x03\x13\x04,\n\x0c\n\x05\x04\x03\
+    \x08\x01\x01\x12\x03\x13\n\x16\n\x0b\n\x04\x04\x03\x02\x02\x12\x03\x13\
+    \x19*\n\x0c\n\x05\x04\x03\x02\x02\x05\x12\x03\x13\x19\x1f\n\x0c\n\x05\
+    \x04\x03\x02\x02\x01\x12\x03\x13\x20%\n\x0c\n\x05\x04\x03\x02\x02\x03\
+    \x12\x03\x13()\n\x0b\n\x04\x04\x03\x08\x02\x12\x03\x14\x042\n\x0c\n\x05\
+    \x04\x03\x08\x02\x01\x12\x03\x14\n\x19\n\x0b\n\x04\x04\x03\x02\x03\x12\
+    \x03\x14\x1c0\n\x0c\n\x05\x04\x03\x02\x03\x05\x12\x03\x14\x1c\"\n\x0c\n\
+    \x05\x04\x03\x02\x03\x01\x12\x03\x14#+\n\x0c\n\x05\x04\x03\x02\x03\x03\
+    \x12\x03\x14./\n\n\n\x02\x05\0\x12\x04\x16\0\x1a\x01\n\n\n\x03\x05\0\x01\
+    \x12\x03\x16\x05\x0f\n\x0b\n\x04\x05\0\x02\0\x12\x03\x17\x04\x10\n\x0c\n\
+    \x05\x05\0\x02\0\x01\x12\x03\x17\x04\x0b\n\x0c\n\x05\x05\0\x02\0\x02\x12\
+    \x03\x17\x0e\x0f\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x18\x04\x0e\n\x0c\n\
+    \x05\x05\0\x02\x01\x01\x12\x03\x18\x04\t\n\x0c\n\x05\x05\0\x02\x01\x02\
+    \x12\x03\x18\x0c\r\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x19\x04\x10\n\x0c\n\
+    \x05\x05\0\x02\x02\x01\x12\x03\x19\x04\x0b\n\x0c\n\x05\x05\0\x02\x02\x02\
+    \x12\x03\x19\x0e\x0fb\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 1 - 0
rust-lib/flowy-user/src/protobuf/proto/errors.proto

@@ -23,6 +23,7 @@ enum ErrorCode {
     PasswordTooLong = 31;
     PasswordContainsForbidCharacters = 32;
     PasswordFormatInvalid = 33;
+    PasswordNotMatch = 34;
     UserNameTooLong = 40;
     UserNameContainsForbiddenCharacters = 41;
     UserNameIsEmpty = 42;

+ 0 - 1
rust-lib/flowy-user/src/protobuf/proto/user_detail.proto

@@ -7,7 +7,6 @@ message UserDetail {
     string id = 1;
     string email = 2;
     string name = 3;
-    string token = 4;
 }
 message UpdateUserRequest {
     string id = 1;

+ 26 - 7
rust-lib/flowy-user/src/services/server/server_api.rs

@@ -3,14 +3,18 @@ use crate::{
     errors::{ErrorBuilder, ErrorCode, UserError},
 };
 
-use crate::entities::UserToken;
+use crate::entities::{UpdateUserParams, UserToken};
 use flowy_infra::future::ResultFuture;
 use flowy_net::{config::*, request::HttpRequestBuilder};
+use std::sync::Arc;
+
+pub type Server = Arc<dyn UserServerAPI + Send + Sync>;
 
 pub trait UserServerAPI {
     fn sign_up(&self, params: SignUpParams) -> ResultFuture<SignUpResponse, UserError>;
     fn sign_in(&self, params: SignInParams) -> ResultFuture<SignInResponse, UserError>;
     fn sign_out(&self, token: &str) -> ResultFuture<(), UserError>;
+    fn update_user(&self, params: UpdateUserParams) -> ResultFuture<(), UserError>;
     fn get_user_detail(&self, token: &str) -> ResultFuture<UserDetail, UserError>;
 }
 
@@ -38,11 +42,13 @@ impl UserServerAPI for UserServer {
         })
     }
 
+    fn update_user(&self, params: UpdateUserParams) -> ResultFuture<(), UserError> {
+        unimplemented!();
+    }
+
     fn get_user_detail(&self, token: &str) -> ResultFuture<UserDetail, UserError> {
-        let params = UserToken {
-            token: token.to_string(),
-        };
-        ResultFuture::new(async { get_user_detail(params, USER_DETAIL_URL.as_ref()).await })
+        let token = token.to_owned();
+        ResultFuture::new(async move { get_user_detail(&token, USER_DETAIL_URL.as_ref()).await })
     }
 }
 
@@ -74,12 +80,25 @@ pub async fn user_sign_out(params: UserToken, url: &str) -> Result<(), UserError
     Ok(())
 }
 
-pub async fn get_user_detail(params: UserToken, url: &str) -> Result<UserDetail, UserError> {
+pub async fn get_user_detail(token: &str, url: &str) -> Result<UserDetail, UserError> {
     let user_detail = HttpRequestBuilder::get(&url.to_owned())
-        .protobuf(params)?
+        .header(HEADER_TOKEN, token)
         .send()
         .await?
         .response()
         .await?;
     Ok(user_detail)
 }
+
+pub async fn update_user_detail(
+    token: &str,
+    params: UpdateUserParams,
+    url: &str,
+) -> Result<(), UserError> {
+    let _ = HttpRequestBuilder::patch(&url.to_owned())
+        .header(HEADER_TOKEN, token)
+        .protobuf(params)?
+        .send()
+        .await?;
+    Ok(())
+}

+ 5 - 1
rust-lib/flowy-user/src/services/server/server_api_mock.rs

@@ -4,7 +4,7 @@ use crate::{
     services::user::UserServerAPI,
 };
 
-use crate::entities::UserToken;
+use crate::entities::{UpdateUserParams, UserToken};
 
 use flowy_infra::future::ResultFuture;
 use std::sync::Arc;
@@ -42,6 +42,10 @@ impl UserServerAPI for UserServerMock {
         ResultFuture::new(async { Ok(()) })
     }
 
+    fn update_user(&self, _params: UpdateUserParams) -> ResultFuture<(), UserError> {
+        ResultFuture::new(async { Ok(()) })
+    }
+
     fn get_user_detail(&self, _token: &str) -> ResultFuture<UserDetail, UserError> {
         ResultFuture::new(async { Err(ErrorBuilder::new(ErrorCode::Unknown).build()) })
     }

+ 27 - 26
rust-lib/flowy-user/src/services/user/user_session.rs

@@ -13,7 +13,7 @@ use flowy_database::{
     UserDatabaseConnection,
 };
 
-use crate::entities::UserToken;
+use crate::{entities::UserToken, services::server::Server};
 use flowy_infra::kv::KVStore;
 use flowy_sqlite::ConnectionPool;
 use parking_lot::RwLock;
@@ -33,13 +33,11 @@ impl UserSessionConfig {
     }
 }
 
-type Server = Arc<dyn UserServerAPI + Send + Sync>;
-
 pub struct UserSession {
     database: UserDB,
     config: UserSessionConfig,
     #[allow(dead_code)]
-    pub(crate) server: Server,
+    server: Server,
     session: RwLock<Option<Session>>,
 }
 
@@ -90,32 +88,22 @@ impl UserSession {
     }
 
     pub async fn sign_out(&self) -> Result<(), UserError> {
-        let user_detail = self.user_detail().await?;
-
-        match self.server.sign_out(&user_detail.token).await {
+        let session = self.get_session()?;
+        match self.server.sign_out(&session.token).await {
             Ok(_) => {},
             Err(e) => log::error!("Sign out failed: {:?}", e),
         }
 
         let conn = self.get_db_connection()?;
         let _ =
-            diesel::delete(dsl::user_table.filter(dsl::id.eq(&user_detail.id))).execute(&*conn)?;
-        let _ = self.server.sign_out(&user_detail.id);
-        let _ = self.database.close_user_db(&user_detail.id)?;
+            diesel::delete(dsl::user_table.filter(dsl::id.eq(&session.user_id))).execute(&*conn)?;
+        let _ = self.server.sign_out(&session.token);
+        let _ = self.database.close_user_db(&session.user_id)?;
         let _ = self.set_session(None)?;
 
         Ok(())
     }
 
-    async fn save_user(&self, user: UserTable) -> Result<UserTable, UserError> {
-        let conn = self.get_db_connection()?;
-        let _ = diesel::insert_into(user_table::table)
-            .values(user.clone())
-            .execute(&*conn)?;
-
-        Ok(user)
-    }
-
     pub async fn update_user(&self, params: UpdateUserParams) -> Result<(), UserError> {
         let changeset = UserTableChangeset::new(params);
         let conn = self.get_db_connection()?;
@@ -153,6 +141,24 @@ impl UserSession {
         Ok(format!("{}/{}", self.config.root_dir, session.user_id))
     }
 
+    pub fn user_id(&self) -> Result<String, UserError> { Ok(self.get_session()?.user_id) }
+
+    // pub fn user_token(&self) -> Result<String, UserError> {
+    //     let user_detail = self.user_detail()?;
+    //     Ok(user_detail.token)
+    // }
+}
+
+impl UserSession {
+    async fn save_user(&self, user: UserTable) -> Result<UserTable, UserError> {
+        let conn = self.get_db_connection()?;
+        let _ = diesel::insert_into(user_table::table)
+            .values(user.clone())
+            .execute(&*conn)?;
+
+        Ok(user)
+    }
+
     fn set_session(&self, session: Option<Session>) -> Result<(), UserError> {
         log::trace!("Update user session: {:?}", session);
         match &session {
@@ -185,11 +191,6 @@ impl UserSession {
             Some(session) => Ok(session),
         }
     }
-
-    // pub fn user_token(&self) -> Result<String, UserError> {
-    //     let user_detail = self.user_detail()?;
-    //     Ok(user_detail.token)
-    // }
 }
 
 pub async fn update_user(
@@ -238,7 +239,7 @@ impl std::convert::From<String> for Session {
         match serde_json::from_str(&s) {
             Ok(s) => s,
             Err(e) => {
-                log::error!("{Deserialize string to Session failed: {:?}", e"}");
+                log::error!("Deserialize string to Session failed: {:?}", e);
                 Session::default()
             },
         }
@@ -250,7 +251,7 @@ impl std::convert::Into<String> for Session {
         match serde_json::to_string(&self) {
             Ok(s) => s,
             Err(e) => {
-                log::error!("{Serialize session to string failed: {:?}", e"}");
+                log::error!("Serialize session to string failed: {:?}", e);
                 "".to_string()
             },
         }

+ 4 - 3
rust-lib/flowy-workspace/src/services/app_controller.rs

@@ -98,12 +98,13 @@ pub async fn read_app_request(
     let result = HttpRequestBuilder::get(&url.to_owned())
         .protobuf(params)?
         .send()
-        .await?
-        .response::<App>()
         .await;
 
     match result {
-        Ok(app) => Ok(Some(app)),
+        Ok(builder) => {
+            let app = builder.response::<App>().await?;
+            Ok(Some(app))
+        },
         Err(e) => {
             if e.is_not_found() {
                 Ok(None)

+ 5 - 4
rust-lib/flowy-workspace/src/services/view_controller.rs

@@ -5,7 +5,7 @@ use crate::{
     observable::{send_observable, WorkspaceObservable},
     sql_tables::view::{ViewTable, ViewTableChangeset, ViewTableSql},
 };
-use flowy_net::request::HttpRequestBuilder;
+use flowy_net::{errors::ServerError, request::HttpRequestBuilder};
 use std::sync::Arc;
 
 pub struct ViewController {
@@ -83,12 +83,13 @@ pub async fn read_view_request(
     let result = HttpRequestBuilder::get(&url.to_owned())
         .protobuf(params)?
         .send()
-        .await?
-        .response::<View>()
         .await;
 
     match result {
-        Ok(view) => Ok(Some(view)),
+        Ok(builder) => {
+            let view = builder.response::<View>().await?;
+            Ok(Some(view))
+        },
         Err(e) => {
             if e.is_not_found() {
                 Ok(None)