소스 검색

add get user detail event, extract remote serviceinto UserServer

appflowy 3 년 전
부모
커밋
1d74fbdd7b
65개의 변경된 파일1162개의 추가작업 그리고 805개의 파일을 삭제
  1. 2 1
      .gitignore
  2. 3 0
      .idea/appflowy_client.iml
  3. 2 0
      app_flowy/packages/flowy_sdk/lib/protobuf.dart
  4. 2 2
      app_flowy/packages/flowy_sdk/lib/protobuf/event.pbenum.dart
  5. 1 1
      app_flowy/packages/flowy_sdk/lib/protobuf/event.pbjson.dart
  6. 0 47
      app_flowy/packages/flowy_sdk/lib/protobuf/kv.pb.dart
  7. 0 7
      app_flowy/packages/flowy_sdk/lib/protobuf/kv.pbjson.dart
  8. 0 47
      app_flowy/packages/flowy_sdk/lib/protobuf/sign_in.pb.dart
  9. 0 7
      app_flowy/packages/flowy_sdk/lib/protobuf/sign_in.pbjson.dart
  10. 100 0
      app_flowy/packages/flowy_sdk/lib/protobuf/user.pb.dart
  11. 7 0
      app_flowy/packages/flowy_sdk/lib/protobuf/user.pbenum.dart
  12. 18 0
      app_flowy/packages/flowy_sdk/lib/protobuf/user.pbjson.dart
  13. 9 0
      app_flowy/packages/flowy_sdk/lib/protobuf/user.pbserver.dart
  14. 90 0
      app_flowy/packages/flowy_sdk/lib/protobuf/user_status.pb.dart
  15. 28 0
      app_flowy/packages/flowy_sdk/lib/protobuf/user_status.pbenum.dart
  16. 26 0
      app_flowy/packages/flowy_sdk/lib/protobuf/user_status.pbjson.dart
  17. 9 0
      app_flowy/packages/flowy_sdk/lib/protobuf/user_status.pbserver.dart
  18. 3 3
      rust-lib/flowy-derive/src/derive_cache/derive_cache.rs
  19. 61 55
      rust-lib/flowy-infra/src/kv/kv.rs
  20. 1 1
      rust-lib/flowy-infra/src/lib.rs
  21. 21 184
      rust-lib/flowy-infra/src/protobuf/model/kv.rs
  22. 0 3
      rust-lib/flowy-infra/src/protobuf/proto/kv.proto
  23. 1 0
      rust-lib/flowy-sdk/Cargo.toml
  24. 3 1
      rust-lib/flowy-sdk/src/lib.rs
  25. 0 1
      rust-lib/flowy-sqlite/src/lib.rs
  26. 0 1
      rust-lib/flowy-sqlite/src/mod.rs
  27. 3 0
      rust-lib/flowy-test/src/lib.rs
  28. 1 1
      rust-lib/flowy-user/Flowy.toml
  29. 0 3
      rust-lib/flowy-user/src/domain/mod.rs
  30. 4 0
      rust-lib/flowy-user/src/entities/mod.rs
  31. 1 11
      rust-lib/flowy-user/src/entities/sign_in.rs
  32. 1 1
      rust-lib/flowy-user/src/entities/sign_up.rs
  33. 0 0
      rust-lib/flowy-user/src/entities/user_email.rs
  34. 16 0
      rust-lib/flowy-user/src/entities/user_id.rs
  35. 0 0
      rust-lib/flowy-user/src/entities/user_name.rs
  36. 0 0
      rust-lib/flowy-user/src/entities/user_password.rs
  37. 35 0
      rust-lib/flowy-user/src/entities/user_status.rs
  38. 4 4
      rust-lib/flowy-user/src/event.rs
  39. 21 13
      rust-lib/flowy-user/src/handlers/auth.rs
  40. 3 2
      rust-lib/flowy-user/src/lib.rs
  41. 1 0
      rust-lib/flowy-user/src/module.rs
  42. 5 5
      rust-lib/flowy-user/src/protobuf/model/event.rs
  43. 3 0
      rust-lib/flowy-user/src/protobuf/model/mod.rs
  44. 15 172
      rust-lib/flowy-user/src/protobuf/model/sign_in.rs
  45. 346 0
      rust-lib/flowy-user/src/protobuf/model/user_status.rs
  46. 1 1
      rust-lib/flowy-user/src/protobuf/proto/event.proto
  47. 0 3
      rust-lib/flowy-user/src/protobuf/proto/sign_in.proto
  48. 12 0
      rust-lib/flowy-user/src/protobuf/proto/user_status.proto
  49. 0 2
      rust-lib/flowy-user/src/services/mod.rs
  50. 0 22
      rust-lib/flowy-user/src/services/register.rs
  51. 2 5
      rust-lib/flowy-user/src/services/user_session/builder.rs
  52. 28 23
      rust-lib/flowy-user/src/services/user_session/database.rs
  53. 5 3
      rust-lib/flowy-user/src/services/user_session/mod.rs
  54. 67 0
      rust-lib/flowy-user/src/services/user_session/register.rs
  55. 53 40
      rust-lib/flowy-user/src/services/user_session/user_session.rs
  56. 0 0
      rust-lib/flowy-user/src/sql_tables/mod.rs
  57. 2 2
      rust-lib/flowy-user/src/sql_tables/user_table.rs
  58. 0 130
      rust-lib/flowy-user/tests/auth_test.rs
  59. 40 0
      rust-lib/flowy-user/tests/event/helper.rs
  60. 4 0
      rust-lib/flowy-user/tests/event/main.rs
  61. 46 0
      rust-lib/flowy-user/tests/event/sign_in_test.rs
  62. 47 0
      rust-lib/flowy-user/tests/event/sign_up_test.rs
  63. 5 0
      rust-lib/flowy-user/tests/event/user_status_test.rs
  64. 0 1
      rust-lib/flowy-user/tests/main.rs
  65. 4 0
      scripts/install_tool.sh

+ 2 - 1
.gitignore

@@ -8,4 +8,5 @@ Cargo.lock
 
 # These are backup files generated by rustfmt
 **/*.rs.bk
-**/target/
+**/target/
+**/*.db

+ 3 - 0
.idea/appflowy_client.iml

@@ -63,6 +63,9 @@
       <excludeFolder url="file://$MODULE_DIR$/app_flowy/packages/flowy_editor/.dart_tool" />
       <excludeFolder url="file://$MODULE_DIR$/app_flowy/packages/flowy_editor/build" />
       <excludeFolder url="file://$MODULE_DIR$/app_flowy/packages/flowy_editor/.pub" />
+      <excludeFolder url="file://$MODULE_DIR$/app_flowy/packages/flowy_editor/example/.dart_tool" />
+      <excludeFolder url="file://$MODULE_DIR$/app_flowy/packages/flowy_editor/example/.pub" />
+      <excludeFolder url="file://$MODULE_DIR$/app_flowy/packages/flowy_editor/example/build" />
     </content>
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />

+ 2 - 0
app_flowy/packages/flowy_sdk/lib/protobuf.dart

@@ -2,7 +2,9 @@
 export 'protobuf/kv.pb.dart';
 export 'protobuf/ffi_response.pb.dart';
 export 'protobuf/ffi_request.pb.dart';
+export 'protobuf/user_status.pb.dart';
 export 'protobuf/sign_up.pb.dart';
 export 'protobuf/sign_in.pb.dart';
 export 'protobuf/user_table.pb.dart';
 export 'protobuf/event.pb.dart';
+export 'protobuf/user.pb.dart';

+ 2 - 2
app_flowy/packages/flowy_sdk/lib/protobuf/event.pbenum.dart

@@ -10,13 +10,13 @@ import 'dart:core' as $core;
 import 'package:protobuf/protobuf.dart' as $pb;
 
 class UserEvent extends $pb.ProtobufEnum {
-  static const UserEvent AuthCheck = UserEvent._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AuthCheck');
+  static const UserEvent GetStatus = UserEvent._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetStatus');
   static const UserEvent SignIn = UserEvent._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SignIn');
   static const UserEvent SignUp = UserEvent._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SignUp');
   static const UserEvent SignOut = UserEvent._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SignOut');
 
   static const $core.List<UserEvent> values = <UserEvent> [
-    AuthCheck,
+    GetStatus,
     SignIn,
     SignUp,
     SignOut,

+ 1 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/event.pbjson.dart

@@ -9,7 +9,7 @@ import 'dart:core' as $core;
 const UserEvent$json = const {
   '1': 'UserEvent',
   '2': const [
-    const {'1': 'AuthCheck', '2': 0},
+    const {'1': 'GetStatus', '2': 0},
     const {'1': 'SignIn', '2': 1},
     const {'1': 'SignUp', '2': 2},
     const {'1': 'SignOut', '2': 3},

+ 0 - 47
app_flowy/packages/flowy_sdk/lib/protobuf/kv.pb.dart

@@ -165,50 +165,3 @@ class KeyValue extends $pb.GeneratedMessage {
   void clearBoolValue() => clearField(5);
 }
 
-class KeyValueQuery extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'KeyValueQuery', createEmptyInstance: create)
-    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'key')
-    ..hasRequiredFields = false
-  ;
-
-  KeyValueQuery._() : super();
-  factory KeyValueQuery({
-    $core.String? key,
-  }) {
-    final _result = create();
-    if (key != null) {
-      _result.key = key;
-    }
-    return _result;
-  }
-  factory KeyValueQuery.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory KeyValueQuery.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
-  @$core.Deprecated(
-  'Using this can add significant overhead to your binary. '
-  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
-  'Will be removed in next major version')
-  KeyValueQuery clone() => KeyValueQuery()..mergeFromMessage(this);
-  @$core.Deprecated(
-  'Using this can add significant overhead to your binary. '
-  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
-  'Will be removed in next major version')
-  KeyValueQuery copyWith(void Function(KeyValueQuery) updates) => super.copyWith((message) => updates(message as KeyValueQuery)) as KeyValueQuery; // ignore: deprecated_member_use
-  $pb.BuilderInfo get info_ => _i;
-  @$core.pragma('dart2js:noInline')
-  static KeyValueQuery create() => KeyValueQuery._();
-  KeyValueQuery createEmptyInstance() => create();
-  static $pb.PbList<KeyValueQuery> createRepeated() => $pb.PbList<KeyValueQuery>();
-  @$core.pragma('dart2js:noInline')
-  static KeyValueQuery getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<KeyValueQuery>(create);
-  static KeyValueQuery? _defaultInstance;
-
-  @$pb.TagNumber(1)
-  $core.String get key => $_getSZ(0);
-  @$pb.TagNumber(1)
-  set key($core.String v) { $_setString(0, v); }
-  @$pb.TagNumber(1)
-  $core.bool hasKey() => $_has(0);
-  @$pb.TagNumber(1)
-  void clearKey() => clearField(1);
-}
-

+ 0 - 7
app_flowy/packages/flowy_sdk/lib/protobuf/kv.pbjson.dart

@@ -23,10 +23,3 @@ const KeyValue$json = const {
   ],
 };
 
-const KeyValueQuery$json = const {
-  '1': 'KeyValueQuery',
-  '2': const [
-    const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'},
-  ],
-};
-

+ 0 - 47
app_flowy/packages/flowy_sdk/lib/protobuf/sign_in.pb.dart

@@ -131,50 +131,3 @@ class SignInParams extends $pb.GeneratedMessage {
   void clearPassword() => clearField(2);
 }
 
-class SignInResponse extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SignInResponse', createEmptyInstance: create)
-    ..aOB(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isSuccess')
-    ..hasRequiredFields = false
-  ;
-
-  SignInResponse._() : super();
-  factory SignInResponse({
-    $core.bool? isSuccess,
-  }) {
-    final _result = create();
-    if (isSuccess != null) {
-      _result.isSuccess = isSuccess;
-    }
-    return _result;
-  }
-  factory SignInResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory SignInResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
-  @$core.Deprecated(
-  'Using this can add significant overhead to your binary. '
-  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
-  'Will be removed in next major version')
-  SignInResponse clone() => SignInResponse()..mergeFromMessage(this);
-  @$core.Deprecated(
-  'Using this can add significant overhead to your binary. '
-  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
-  'Will be removed in next major version')
-  SignInResponse copyWith(void Function(SignInResponse) updates) => super.copyWith((message) => updates(message as SignInResponse)) as SignInResponse; // ignore: deprecated_member_use
-  $pb.BuilderInfo get info_ => _i;
-  @$core.pragma('dart2js:noInline')
-  static SignInResponse create() => SignInResponse._();
-  SignInResponse createEmptyInstance() => create();
-  static $pb.PbList<SignInResponse> createRepeated() => $pb.PbList<SignInResponse>();
-  @$core.pragma('dart2js:noInline')
-  static SignInResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SignInResponse>(create);
-  static SignInResponse? _defaultInstance;
-
-  @$pb.TagNumber(1)
-  $core.bool get isSuccess => $_getBF(0);
-  @$pb.TagNumber(1)
-  set isSuccess($core.bool v) { $_setBool(0, v); }
-  @$pb.TagNumber(1)
-  $core.bool hasIsSuccess() => $_has(0);
-  @$pb.TagNumber(1)
-  void clearIsSuccess() => clearField(1);
-}
-

+ 0 - 7
app_flowy/packages/flowy_sdk/lib/protobuf/sign_in.pbjson.dart

@@ -22,10 +22,3 @@ const SignInParams$json = const {
   ],
 };
 
-const SignInResponse$json = const {
-  '1': 'SignInResponse',
-  '2': const [
-    const {'1': 'is_success', '3': 1, '4': 1, '5': 8, '10': 'isSuccess'},
-  ],
-};
-

+ 100 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/user.pb.dart

@@ -0,0 +1,100 @@
+///
+//  Generated code. Do not modify.
+//  source: user.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+import 'dart:core' as $core;
+
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class User extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'User', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
+    ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'email')
+    ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'password')
+    ..hasRequiredFields = false
+  ;
+
+  User._() : super();
+  factory User({
+    $core.String? id,
+    $core.String? name,
+    $core.String? email,
+    $core.String? password,
+  }) {
+    final _result = create();
+    if (id != null) {
+      _result.id = id;
+    }
+    if (name != null) {
+      _result.name = name;
+    }
+    if (email != null) {
+      _result.email = email;
+    }
+    if (password != null) {
+      _result.password = password;
+    }
+    return _result;
+  }
+  factory User.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory User.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  User clone() => User()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  User copyWith(void Function(User) updates) => super.copyWith((message) => updates(message as User)) as User; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static User create() => User._();
+  User createEmptyInstance() => create();
+  static $pb.PbList<User> createRepeated() => $pb.PbList<User>();
+  @$core.pragma('dart2js:noInline')
+  static User getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<User>(create);
+  static User? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get id => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set id($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearId() => clearField(1);
+
+  @$pb.TagNumber(2)
+  $core.String get name => $_getSZ(1);
+  @$pb.TagNumber(2)
+  set name($core.String v) { $_setString(1, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasName() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearName() => clearField(2);
+
+  @$pb.TagNumber(3)
+  $core.String get email => $_getSZ(2);
+  @$pb.TagNumber(3)
+  set email($core.String v) { $_setString(2, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasEmail() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearEmail() => clearField(3);
+
+  @$pb.TagNumber(4)
+  $core.String get password => $_getSZ(3);
+  @$pb.TagNumber(4)
+  set password($core.String v) { $_setString(3, v); }
+  @$pb.TagNumber(4)
+  $core.bool hasPassword() => $_has(3);
+  @$pb.TagNumber(4)
+  void clearPassword() => clearField(4);
+}
+

+ 7 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/user.pbenum.dart

@@ -0,0 +1,7 @@
+///
+//  Generated code. Do not modify.
+//  source: user.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+

+ 18 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/user.pbjson.dart

@@ -0,0 +1,18 @@
+///
+//  Generated code. Do not modify.
+//  source: user.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+import 'dart:core' as $core;
+const User$json = const {
+  '1': 'User',
+  '2': const [
+    const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
+    const {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'},
+    const {'1': 'email', '3': 3, '4': 1, '5': 9, '10': 'email'},
+    const {'1': 'password', '3': 4, '4': 1, '5': 9, '10': 'password'},
+  ],
+};
+

+ 9 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/user.pbserver.dart

@@ -0,0 +1,9 @@
+///
+//  Generated code. Do not modify.
+//  source: user.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+export 'user.pb.dart';
+

+ 90 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/user_status.pb.dart

@@ -0,0 +1,90 @@
+///
+//  Generated code. Do not modify.
+//  source: user_status.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+import 'dart:core' as $core;
+
+import 'package:protobuf/protobuf.dart' as $pb;
+
+import 'user_status.pbenum.dart';
+
+export 'user_status.pbenum.dart';
+
+class UserDetail extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'UserDetail', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'email')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
+    ..e<UserStatus>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'status', $pb.PbFieldType.OE, defaultOrMaker: UserStatus.Unknown, valueOf: UserStatus.valueOf, enumValues: UserStatus.values)
+    ..hasRequiredFields = false
+  ;
+
+  UserDetail._() : super();
+  factory UserDetail({
+    $core.String? email,
+    $core.String? name,
+    UserStatus? status,
+  }) {
+    final _result = create();
+    if (email != null) {
+      _result.email = email;
+    }
+    if (name != null) {
+      _result.name = name;
+    }
+    if (status != null) {
+      _result.status = status;
+    }
+    return _result;
+  }
+  factory UserDetail.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory UserDetail.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  UserDetail clone() => UserDetail()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  UserDetail copyWith(void Function(UserDetail) updates) => super.copyWith((message) => updates(message as UserDetail)) as UserDetail; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static UserDetail create() => UserDetail._();
+  UserDetail createEmptyInstance() => create();
+  static $pb.PbList<UserDetail> createRepeated() => $pb.PbList<UserDetail>();
+  @$core.pragma('dart2js:noInline')
+  static UserDetail getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<UserDetail>(create);
+  static UserDetail? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get email => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set email($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasEmail() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearEmail() => clearField(1);
+
+  @$pb.TagNumber(2)
+  $core.String get name => $_getSZ(1);
+  @$pb.TagNumber(2)
+  set name($core.String v) { $_setString(1, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasName() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearName() => clearField(2);
+
+  @$pb.TagNumber(3)
+  UserStatus get status => $_getN(2);
+  @$pb.TagNumber(3)
+  set status(UserStatus v) { setField(3, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasStatus() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearStatus() => clearField(3);
+}
+

+ 28 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/user_status.pbenum.dart

@@ -0,0 +1,28 @@
+///
+//  Generated code. Do not modify.
+//  source: user_status.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+// ignore_for_file: UNDEFINED_SHOWN_NAME
+import 'dart:core' as $core;
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class UserStatus extends $pb.ProtobufEnum {
+  static const UserStatus Unknown = UserStatus._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Unknown');
+  static const UserStatus Login = UserStatus._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Login');
+  static const UserStatus Expired = UserStatus._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Expired');
+
+  static const $core.List<UserStatus> values = <UserStatus> [
+    Unknown,
+    Login,
+    Expired,
+  ];
+
+  static final $core.Map<$core.int, UserStatus> _byValue = $pb.ProtobufEnum.initByValue(values);
+  static UserStatus? valueOf($core.int value) => _byValue[value];
+
+  const UserStatus._($core.int v, $core.String n) : super(v, n);
+}
+

+ 26 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/user_status.pbjson.dart

@@ -0,0 +1,26 @@
+///
+//  Generated code. Do not modify.
+//  source: user_status.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+import 'dart:core' as $core;
+const UserStatus$json = const {
+  '1': 'UserStatus',
+  '2': const [
+    const {'1': 'Unknown', '2': 0},
+    const {'1': 'Login', '2': 1},
+    const {'1': 'Expired', '2': 2},
+  ],
+};
+
+const UserDetail$json = const {
+  '1': 'UserDetail',
+  '2': const [
+    const {'1': 'email', '3': 1, '4': 1, '5': 9, '10': 'email'},
+    const {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'},
+    const {'1': 'status', '3': 3, '4': 1, '5': 14, '6': '.UserStatus', '10': 'status'},
+  ],
+};
+

+ 9 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/user_status.pbserver.dart

@@ -0,0 +1,9 @@
+///
+//  Generated code. Do not modify.
+//  source: user_status.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+export 'user_status.pb.dart';
+

+ 3 - 3
rust-lib/flowy-derive/src/derive_cache/derive_cache.rs

@@ -16,18 +16,18 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
         "u8" => TypeCategory::Bytes,
         "String" => TypeCategory::Str,
         "KeyValue"
-        | "KeyValueQuery"
         | "FFIRequest"
         | "FFIResponse"
-        | "User"
+        | "UserDetail"
         | "SignUpRequest"
         | "SignUpParams"
         | "SignUpResponse"
         | "SignInRequest"
         | "SignInParams"
-        | "SignInResponse"
         => TypeCategory::Protobuf,
         "FFIStatusCode"
+        | "UserStatus"
+        | "UserEvent"
         => TypeCategory::Enum,
 
         "Option" => TypeCategory::Opt,

+ 61 - 55
rust-lib/flowy-infra/src/kv/kv.rs

@@ -4,7 +4,11 @@ use diesel::{Connection, SqliteConnection};
 use flowy_derive::ProtoBuf;
 use flowy_sqlite::{DBConnection, Database, PoolConfig};
 use lazy_static::lazy_static;
-use std::{path::Path, sync::RwLock};
+use std::{
+    path::Path,
+    sync::{PoisonError, RwLock, RwLockWriteGuard},
+};
+
 const DB_NAME: &str = "kv.db";
 lazy_static! {
     pub static ref KV_HOLDER: RwLock<KVStore> = RwLock::new(KVStore::new());
@@ -17,58 +21,51 @@ pub struct KVStore {
 impl KVStore {
     fn new() -> Self { KVStore { database: None } }
 
-    pub fn init(&mut self, root: &str) -> Result<(), String> {
-        if !Path::new(root).exists() {
-            return Err(format!("{} not exists", root));
-        }
-
-        let pool_config = PoolConfig::default();
-        let database = Database::new(root, DB_NAME, pool_config).unwrap();
-        let conn = database.get_connection().unwrap();
-        SqliteConnection::execute(&*conn, KV_SQL).unwrap();
-        self.database = Some(database);
+    pub fn set(item: KeyValue) -> Result<(), String> {
+        let conn = get_connection()?;
+        let _ = diesel::replace_into(kv_table::table)
+            .values(&item)
+            .execute(&*conn)
+            .map_err(|e| format!("{:?}", e))?;
 
         Ok(())
     }
 
-    pub fn set(item: KeyValue) -> Result<(), String> {
-        match get_connection() {
-            Ok(conn) => {
-                let _ = diesel::insert_into(kv_table::table)
-                    .values(&item)
-                    .execute(&*conn)
-                    .map_err(|e| format!("{:?}", e))?;
-
-                Ok(())
-            },
-            Err(e) => Err(e),
-        }
+    pub fn get(key: &str) -> Result<KeyValue, String> {
+        let conn = get_connection()?;
+        let item = dsl::kv_table
+            .filter(kv_table::key.eq(key))
+            .first::<KeyValue>(&*conn)
+            .map_err(|e| format!("{:?}", e))?;
+        Ok(item)
     }
 
     #[allow(dead_code)]
     pub fn remove(key: &str) -> Result<(), String> {
-        match get_connection() {
-            Ok(conn) => {
-                let _ = diesel::delete(dsl::kv_table.filter(kv_table::key.eq(key)))
-                    .execute(&*conn)
-                    .map_err(|e| format!("{:?}", e))?;
-                Ok(())
-            },
-            Err(e) => Err(e),
-        }
+        let conn = get_connection()?;
+        let sql = dsl::kv_table.filter(kv_table::key.eq(key));
+        let _ = diesel::delete(sql)
+            .execute(&*conn)
+            .map_err(|e| format!("{:?}", e))?;
+        Ok(())
     }
 
-    pub fn get(key: &str) -> Result<KeyValue, String> {
-        match get_connection() {
-            Ok(conn) => {
-                let item = dsl::kv_table
-                    .filter(kv_table::key.eq(key))
-                    .first::<KeyValue>(&*conn)
-                    .map_err(|e| format!("{:?}", e))?;
-                Ok(item)
-            },
-            Err(e) => Err(e),
+    pub fn init(root: &str) -> Result<(), String> {
+        if !Path::new(root).exists() {
+            return Err(format!("Init KVStore failed. {} not exists", root));
         }
+
+        let pool_config = PoolConfig::default();
+        let database = Database::new(root, DB_NAME, pool_config).unwrap();
+        let conn = database.get_connection().unwrap();
+        SqliteConnection::execute(&*conn, KV_SQL).unwrap();
+
+        let mut store = KV_HOLDER
+            .write()
+            .map_err(|e| format!("KVStore write failed: {:?}", e))?;
+        store.database = Some(database);
+
+        Ok(())
     }
 }
 
@@ -142,9 +139,7 @@ fn get_connection() -> Result<DBConnection, String> {
     }
 }
 
-#[derive(
-    PartialEq, Clone, Debug, ProtoBuf, Default, Queryable, Identifiable, Insertable, AsChangeset,
-)]
+#[derive(Clone, Debug, ProtoBuf, Default, Queryable, Identifiable, Insertable, AsChangeset)]
 #[table_name = "kv_table"]
 #[primary_key(key)]
 pub struct KeyValue {
@@ -173,16 +168,27 @@ impl KeyValue {
     }
 }
 
-#[derive(ProtoBuf, Default, Debug)]
-pub struct KeyValueQuery {
-    #[pb(index = 1)]
-    key: String,
-}
+#[cfg(test)]
+mod tests {
+    use crate::kv::KVStore;
 
-impl KeyValueQuery {
-    pub fn new(key: &str) -> Self {
-        let mut query = KeyValueQuery::default();
-        query.key = key.to_string();
-        query
+    #[test]
+    fn kv_store_test() {
+        let dir = "./temp/";
+        if !std::path::Path::new(dir).exists() {
+            std::fs::create_dir_all(dir).unwrap();
+        }
+
+        KVStore::init(dir);
+
+        KVStore::set_str("1", "hello".to_string());
+        assert_eq!(KVStore::get_str("1").unwrap(), "hello");
+
+        assert_eq!(KVStore::get_str("2"), None);
+
+        KVStore::set_bool("1", true);
+        assert_eq!(KVStore::get_bool("1").unwrap(), true);
+
+        assert_eq!(KVStore::get_bool("2"), None);
     }
 }

+ 1 - 1
rust-lib/flowy-infra/src/lib.rs

@@ -7,7 +7,7 @@ extern crate diesel_derives;
 #[macro_use]
 extern crate diesel_migrations;
 
-mod kv;
+pub mod kv;
 mod protobuf;
 
 pub fn uuid() -> String { uuid::Uuid::new_v4().to_string() }

+ 21 - 184
rust-lib/flowy-infra/src/protobuf/model/kv.rs

@@ -435,165 +435,6 @@ impl ::protobuf::reflect::ProtobufValue for KeyValue {
     }
 }
 
-#[derive(PartialEq,Clone,Default)]
-pub struct KeyValueQuery {
-    // message fields
-    pub key: ::std::string::String,
-    // special fields
-    pub unknown_fields: ::protobuf::UnknownFields,
-    pub cached_size: ::protobuf::CachedSize,
-}
-
-impl<'a> ::std::default::Default for &'a KeyValueQuery {
-    fn default() -> &'a KeyValueQuery {
-        <KeyValueQuery as ::protobuf::Message>::default_instance()
-    }
-}
-
-impl KeyValueQuery {
-    pub fn new() -> KeyValueQuery {
-        ::std::default::Default::default()
-    }
-
-    // string key = 1;
-
-
-    pub fn get_key(&self) -> &str {
-        &self.key
-    }
-    pub fn clear_key(&mut self) {
-        self.key.clear();
-    }
-
-    // Param is passed by value, moved
-    pub fn set_key(&mut self, v: ::std::string::String) {
-        self.key = v;
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_key(&mut self) -> &mut ::std::string::String {
-        &mut self.key
-    }
-
-    // Take field
-    pub fn take_key(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.key, ::std::string::String::new())
-    }
-}
-
-impl ::protobuf::Message for KeyValueQuery {
-    fn is_initialized(&self) -> bool {
-        true
-    }
-
-    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
-        while !is.eof()? {
-            let (field_number, wire_type) = is.read_tag_unpack()?;
-            match field_number {
-                1 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.key)?;
-                },
-                _ => {
-                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
-                },
-            };
-        }
-        ::std::result::Result::Ok(())
-    }
-
-    // Compute sizes of nested messages
-    #[allow(unused_variables)]
-    fn compute_size(&self) -> u32 {
-        let mut my_size = 0;
-        if !self.key.is_empty() {
-            my_size += ::protobuf::rt::string_size(1, &self.key);
-        }
-        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
-        self.cached_size.set(my_size);
-        my_size
-    }
-
-    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
-        if !self.key.is_empty() {
-            os.write_string(1, &self.key)?;
-        }
-        os.write_unknown_fields(self.get_unknown_fields())?;
-        ::std::result::Result::Ok(())
-    }
-
-    fn get_cached_size(&self) -> u32 {
-        self.cached_size.get()
-    }
-
-    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
-        &self.unknown_fields
-    }
-
-    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
-        &mut self.unknown_fields
-    }
-
-    fn as_any(&self) -> &dyn (::std::any::Any) {
-        self as &dyn (::std::any::Any)
-    }
-    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
-        self as &mut dyn (::std::any::Any)
-    }
-    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
-        self
-    }
-
-    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
-        Self::descriptor_static()
-    }
-
-    fn new() -> KeyValueQuery {
-        KeyValueQuery::new()
-    }
-
-    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
-        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
-        descriptor.get(|| {
-            let mut fields = ::std::vec::Vec::new();
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
-                "key",
-                |m: &KeyValueQuery| { &m.key },
-                |m: &mut KeyValueQuery| { &mut m.key },
-            ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<KeyValueQuery>(
-                "KeyValueQuery",
-                fields,
-                file_descriptor_proto()
-            )
-        })
-    }
-
-    fn default_instance() -> &'static KeyValueQuery {
-        static instance: ::protobuf::rt::LazyV2<KeyValueQuery> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(KeyValueQuery::new)
-    }
-}
-
-impl ::protobuf::Clear for KeyValueQuery {
-    fn clear(&mut self) {
-        self.key.clear();
-        self.unknown_fields.clear();
-    }
-}
-
-impl ::std::fmt::Debug for KeyValueQuery {
-    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
-        ::protobuf::text_format::fmt(self, f)
-    }
-}
-
-impl ::protobuf::reflect::ProtobufValue for KeyValueQuery {
-    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
-        ::protobuf::reflect::ReflectValueRef::Message(self)
-    }
-}
-
 static file_descriptor_proto_data: &'static [u8] = b"\
     \n\x08kv.proto\"\xf1\x01\n\x08KeyValue\x12\x10\n\x03key\x18\x01\x20\x01(\
     \tR\x03key\x12\x1d\n\tstr_value\x18\x02\x20\x01(\tH\0R\x08strValue\x12\
@@ -601,31 +442,27 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     _value\x18\x04\x20\x01(\x01H\x02R\nfloatValue\x12\x1f\n\nbool_value\x18\
     \x05\x20\x01(\x08H\x03R\tboolValueB\x12\n\x10one_of_str_valueB\x12\n\x10\
     one_of_int_valueB\x14\n\x12one_of_float_valueB\x13\n\x11one_of_bool_valu\
-    e\"!\n\rKeyValueQuery\x12\x10\n\x03key\x18\x01\x20\x01(\tR\x03keyJ\xf8\
-    \x03\n\x06\x12\x04\0\0\x0b\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\
-    \x04\0\x12\x04\x02\0\x08\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x10\n\
-    \x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x13\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\x0e\n\x0c\
-    \n\x05\x04\0\x02\0\x03\x12\x03\x03\x11\x12\n\x0b\n\x04\x04\0\x08\0\x12\
-    \x03\x04\x044\n\x0c\n\x05\x04\0\x08\0\x01\x12\x03\x04\n\x1a\n\x0b\n\x04\
-    \x04\0\x02\x01\x12\x03\x04\x1d2\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\
-    \x04\x1d#\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04$-\n\x0c\n\x05\x04\0\
-    \x02\x01\x03\x12\x03\x0401\n\x0b\n\x04\x04\0\x08\x01\x12\x03\x05\x043\n\
-    \x0c\n\x05\x04\0\x08\x01\x01\x12\x03\x05\n\x1a\n\x0b\n\x04\x04\0\x02\x02\
-    \x12\x03\x05\x1d1\n\x0c\n\x05\x04\0\x02\x02\x05\x12\x03\x05\x1d\"\n\x0c\
-    \n\x05\x04\0\x02\x02\x01\x12\x03\x05#,\n\x0c\n\x05\x04\0\x02\x02\x03\x12\
-    \x03\x05/0\n\x0b\n\x04\x04\0\x08\x02\x12\x03\x06\x048\n\x0c\n\x05\x04\0\
-    \x08\x02\x01\x12\x03\x06\n\x1c\n\x0b\n\x04\x04\0\x02\x03\x12\x03\x06\x1f\
-    6\n\x0c\n\x05\x04\0\x02\x03\x05\x12\x03\x06\x1f%\n\x0c\n\x05\x04\0\x02\
-    \x03\x01\x12\x03\x06&1\n\x0c\n\x05\x04\0\x02\x03\x03\x12\x03\x0645\n\x0b\
-    \n\x04\x04\0\x08\x03\x12\x03\x07\x044\n\x0c\n\x05\x04\0\x08\x03\x01\x12\
-    \x03\x07\n\x1b\n\x0b\n\x04\x04\0\x02\x04\x12\x03\x07\x1e2\n\x0c\n\x05\
-    \x04\0\x02\x04\x05\x12\x03\x07\x1e\"\n\x0c\n\x05\x04\0\x02\x04\x01\x12\
-    \x03\x07#-\n\x0c\n\x05\x04\0\x02\x04\x03\x12\x03\x0701\n\n\n\x02\x04\x01\
-    \x12\x04\t\0\x0b\x01\n\n\n\x03\x04\x01\x01\x12\x03\t\x08\x15\n\x0b\n\x04\
-    \x04\x01\x02\0\x12\x03\n\x04\x13\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\n\
-    \x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\n\x0b\x0e\n\x0c\n\x05\x04\
-    \x01\x02\0\x03\x12\x03\n\x11\x12b\x06proto3\
+    eJ\xa9\x03\n\x06\x12\x04\0\0\x08\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\
+    \n\x02\x04\0\x12\x04\x02\0\x08\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\
+    \x10\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x13\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\x0e\n\
+    \x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x11\x12\n\x0b\n\x04\x04\0\x08\0\
+    \x12\x03\x04\x044\n\x0c\n\x05\x04\0\x08\0\x01\x12\x03\x04\n\x1a\n\x0b\n\
+    \x04\x04\0\x02\x01\x12\x03\x04\x1d2\n\x0c\n\x05\x04\0\x02\x01\x05\x12\
+    \x03\x04\x1d#\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04$-\n\x0c\n\x05\
+    \x04\0\x02\x01\x03\x12\x03\x0401\n\x0b\n\x04\x04\0\x08\x01\x12\x03\x05\
+    \x043\n\x0c\n\x05\x04\0\x08\x01\x01\x12\x03\x05\n\x1a\n\x0b\n\x04\x04\0\
+    \x02\x02\x12\x03\x05\x1d1\n\x0c\n\x05\x04\0\x02\x02\x05\x12\x03\x05\x1d\
+    \"\n\x0c\n\x05\x04\0\x02\x02\x01\x12\x03\x05#,\n\x0c\n\x05\x04\0\x02\x02\
+    \x03\x12\x03\x05/0\n\x0b\n\x04\x04\0\x08\x02\x12\x03\x06\x048\n\x0c\n\
+    \x05\x04\0\x08\x02\x01\x12\x03\x06\n\x1c\n\x0b\n\x04\x04\0\x02\x03\x12\
+    \x03\x06\x1f6\n\x0c\n\x05\x04\0\x02\x03\x05\x12\x03\x06\x1f%\n\x0c\n\x05\
+    \x04\0\x02\x03\x01\x12\x03\x06&1\n\x0c\n\x05\x04\0\x02\x03\x03\x12\x03\
+    \x0645\n\x0b\n\x04\x04\0\x08\x03\x12\x03\x07\x044\n\x0c\n\x05\x04\0\x08\
+    \x03\x01\x12\x03\x07\n\x1b\n\x0b\n\x04\x04\0\x02\x04\x12\x03\x07\x1e2\n\
+    \x0c\n\x05\x04\0\x02\x04\x05\x12\x03\x07\x1e\"\n\x0c\n\x05\x04\0\x02\x04\
+    \x01\x12\x03\x07#-\n\x0c\n\x05\x04\0\x02\x04\x03\x12\x03\x0701b\x06proto\
+    3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 0 - 3
rust-lib/flowy-infra/src/protobuf/proto/kv.proto

@@ -7,6 +7,3 @@ message KeyValue {
     oneof one_of_float_value { double float_value = 4; };
     oneof one_of_bool_value { bool bool_value = 5; };
 }
-message KeyValueQuery {
-    string key = 1;
-}

+ 1 - 0
rust-lib/flowy-sdk/Cargo.toml

@@ -10,6 +10,7 @@ flowy-dispatch = { path = "../flowy-dispatch", features = ["use_tracing"]}
 flowy-log = { path = "../flowy-log" }
 #flowy-log = { path = "../flowy-log", features = ["use_bunyan"] }
 flowy-user = { path = "../flowy-user" }
+flowy-infra = { path = "../flowy-infra" }
 tracing = { version = "0.1" }
 log = "0.4.14"
 

+ 3 - 1
rust-lib/flowy-sdk/src/lib.rs

@@ -10,7 +10,9 @@ impl FlowySDK {
     pub fn init_log(directory: &str) { flowy_log::init_log("flowy", directory, "Debug").unwrap(); }
 
     pub fn init(path: &str) {
-        tracing::trace!("🔥 Root path: {}", path);
+        tracing::info!("🔥 Root path: {}", path);
+
+        flowy_infra::kv::KVStore::init(path);
 
         let config = ModuleConfig {
             root: path.to_string(),

+ 0 - 1
rust-lib/flowy-sqlite/src/lib.rs

@@ -2,7 +2,6 @@ mod database;
 #[allow(deprecated, clippy::large_enum_variant)]
 mod errors;
 mod pool;
-
 pub use database::*;
 pub use pool::*;
 

+ 0 - 1
rust-lib/flowy-sqlite/src/mod.rs

@@ -1 +0,0 @@
-

+ 3 - 0
rust-lib/flowy-test/src/lib.rs

@@ -110,6 +110,9 @@ impl EventTester {
         R: FromBytes,
     {
         let response = self.response.unwrap();
+        if response.status_code == StatusCode::Err {
+            dbg!(&response);
+        }
         <Data<R>>::try_from(response.payload).unwrap().into_inner()
     }
 }

+ 1 - 1
rust-lib/flowy-user/Flowy.toml

@@ -1,3 +1,3 @@
 
-proto_crates = ["src/domain"]
+proto_crates = ["src/entities", "src/event.rs"]
 event_files = ["src/event.rs"]

+ 0 - 3
rust-lib/flowy-user/src/domain/mod.rs

@@ -1,3 +0,0 @@
-pub use user::*;
-pub mod tables;
-pub mod user;

+ 4 - 0
rust-lib/flowy-user/src/domain/user/mod.rs → rust-lib/flowy-user/src/entities/mod.rs

@@ -1,11 +1,15 @@
 mod sign_in;
 mod sign_up;
 mod user_email;
+mod user_id;
 mod user_name;
 mod user_password;
+mod user_status;
 
 pub use sign_in::*;
 pub use sign_up::*;
 pub use user_email::*;
+pub use user_id::*;
 pub use user_name::*;
 pub use user_password::*;
+pub use user_status::*;

+ 1 - 11
rust-lib/flowy-user/src/domain/user/sign_in.rs → rust-lib/flowy-user/src/entities/sign_in.rs

@@ -1,4 +1,4 @@
-use crate::domain::{UserEmail, UserPassword};
+use crate::entities::{UserEmail, UserPassword};
 use flowy_derive::ProtoBuf;
 use std::convert::TryInto;
 
@@ -33,13 +33,3 @@ impl TryInto<SignInParams> for SignInRequest {
         })
     }
 }
-
-#[derive(ProtoBuf, Default, Debug)]
-pub struct SignInResponse {
-    #[pb(index = 1)]
-    pub is_success: bool,
-}
-
-impl SignInResponse {
-    pub fn new(is_success: bool) -> Self { Self { is_success } }
-}

+ 1 - 1
rust-lib/flowy-user/src/domain/user/sign_up.rs → rust-lib/flowy-user/src/entities/sign_up.rs

@@ -1,4 +1,4 @@
-use crate::domain::{UserEmail, UserName, UserPassword};
+use crate::entities::{UserEmail, UserName, UserPassword};
 use flowy_derive::ProtoBuf;
 use std::convert::TryInto;
 

+ 0 - 0
rust-lib/flowy-user/src/domain/user/user_email.rs → rust-lib/flowy-user/src/entities/user_email.rs


+ 16 - 0
rust-lib/flowy-user/src/entities/user_id.rs

@@ -0,0 +1,16 @@
+#[derive(Debug)]
+pub struct UserId(pub String);
+
+impl UserId {
+    pub fn parse(s: String) -> Result<UserId, String> {
+        let is_empty_or_whitespace = s.trim().is_empty();
+        if is_empty_or_whitespace {
+            return Err(format!("user id is empty or whitespace"));
+        }
+        Ok(Self(s))
+    }
+}
+
+impl AsRef<str> for UserId {
+    fn as_ref(&self) -> &str { &self.0 }
+}

+ 0 - 0
rust-lib/flowy-user/src/domain/user/user_name.rs → rust-lib/flowy-user/src/entities/user_name.rs


+ 0 - 0
rust-lib/flowy-user/src/domain/user/user_password.rs → rust-lib/flowy-user/src/entities/user_password.rs


+ 35 - 0
rust-lib/flowy-user/src/entities/user_status.rs

@@ -0,0 +1,35 @@
+use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
+
+#[derive(Debug, ProtoBuf_Enum)]
+pub enum UserStatus {
+    Unknown = 0,
+    Login   = 1,
+    Expired = 2,
+}
+
+impl std::default::Default for UserStatus {
+    fn default() -> Self { UserStatus::Unknown }
+}
+
+#[derive(ProtoBuf, Default, Debug)]
+pub struct UserDetail {
+    #[pb(index = 1)]
+    pub email: String,
+
+    #[pb(index = 2)]
+    pub name: String,
+
+    #[pb(index = 3)]
+    pub status: UserStatus,
+}
+
+use crate::sql_tables::User;
+impl std::convert::From<User> for UserDetail {
+    fn from(user: User) -> Self {
+        UserDetail {
+            email: user.email,
+            name: user.name,
+            status: UserStatus::Login,
+        }
+    }
+}

+ 4 - 4
rust-lib/flowy-user/src/event.rs

@@ -3,13 +3,13 @@ use flowy_derive::{Flowy_Event, ProtoBuf_Enum};
 
 #[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
 pub enum UserEvent {
-    #[display(fmt = "AuthCheck")]
-    AuthCheck = 0,
+    #[display(fmt = "GetStatus")]
+    GetStatus = 0,
     #[display(fmt = "SignIn")]
-    #[event(input = "SignInRequest", output = "SignInResponse")]
+    #[event(input = "SignInRequest", output = "UserDetail")]
     SignIn    = 1,
     #[display(fmt = "SignUp")]
-    #[event(input = "SignUpRequest", output = "SignUpResponse")]
+    #[event(input = "SignUpRequest", output = "UserDetail")]
     SignUp    = 2,
     #[display(fmt = "SignOut")]
     SignOut   = 3,

+ 21 - 13
rust-lib/flowy-user/src/handlers/auth.rs

@@ -1,20 +1,23 @@
-use crate::{domain::user::*, services::user_session::UserSession};
+use crate::{entities::*, services::user_session::UserSession};
 use flowy_dispatch::prelude::*;
 use std::{convert::TryInto, sync::Arc};
 
 // tracing instrument 👉🏻 https://docs.rs/tracing/0.1.26/tracing/attr.instrument.html
 #[tracing::instrument(
     name = "user_sign_in",
-    skip(data),
+    skip(data, session),
     fields(
         email = %data.email,
     )
 )]
-pub async fn user_sign_in(data: Data<SignInRequest>) -> ResponseResult<SignInResponse, String> {
-    let _params: SignInParams = data.into_inner().try_into()?;
-    // TODO: user sign in
-    let response = SignInResponse::new(true);
-    response_ok(response)
+pub async fn user_sign_in(
+    data: Data<SignInRequest>,
+    session: ModuleData<Arc<UserSession>>,
+) -> ResponseResult<UserDetail, String> {
+    let params: SignInParams = data.into_inner().try_into()?;
+    let user = session.sign_in(params).await?;
+    let user_detail = UserDetail::from(user);
+    response_ok(user_detail)
 }
 
 #[tracing::instrument(
@@ -28,12 +31,17 @@ pub async fn user_sign_in(data: Data<SignInRequest>) -> ResponseResult<SignInRes
 pub async fn user_sign_up(
     data: Data<SignUpRequest>,
     session: ModuleData<Arc<UserSession>>,
-) -> ResponseResult<SignUpResponse, String> {
+) -> ResponseResult<UserDetail, String> {
     let params: SignUpParams = data.into_inner().try_into()?;
-    // TODO: user sign up
-
-    let _user = session.sign_up(params)?;
+    let user = session.sign_up(params).await?;
+    let user_detail = UserDetail::from(user);
+    response_ok(user_detail)
+}
 
-    let fake_resp = SignUpResponse::new(true);
-    response_ok(fake_resp)
+pub async fn user_get_status(
+    user_id: String,
+    session: ModuleData<Arc<UserSession>>,
+) -> ResponseResult<UserDetail, String> {
+    let user_detail = session.get_user_status(&user_id).await?;
+    response_ok(user_detail)
 }

+ 3 - 2
rust-lib/flowy-user/src/lib.rs

@@ -1,14 +1,15 @@
-mod domain;
+pub mod entities;
 mod errors;
 pub mod event;
 mod handlers;
 pub mod module;
 mod protobuf;
 mod services;
+pub mod sql_tables;
 
 #[macro_use]
 extern crate flowy_database;
 
 pub mod prelude {
-    pub use crate::{domain::*, handlers::auth::*, services::user_session::*};
+    pub use crate::{entities::*, handlers::auth::*, services::user_session::*};
 }

+ 1 - 0
rust-lib/flowy-user/src/module.rs

@@ -9,4 +9,5 @@ pub fn create(user_session: Arc<UserSession>) -> Module {
         .data(user_session)
         .event(UserEvent::SignIn, user_sign_in)
         .event(UserEvent::SignUp, user_sign_up)
+        .event(UserEvent::GetStatus, user_get_status)
 }

+ 5 - 5
rust-lib/flowy-user/src/protobuf/model/event.rs

@@ -25,7 +25,7 @@
 
 #[derive(Clone,PartialEq,Eq,Debug,Hash)]
 pub enum UserEvent {
-    AuthCheck = 0,
+    GetStatus = 0,
     SignIn = 1,
     SignUp = 2,
     SignOut = 3,
@@ -38,7 +38,7 @@ impl ::protobuf::ProtobufEnum for UserEvent {
 
     fn from_i32(value: i32) -> ::std::option::Option<UserEvent> {
         match value {
-            0 => ::std::option::Option::Some(UserEvent::AuthCheck),
+            0 => ::std::option::Option::Some(UserEvent::GetStatus),
             1 => ::std::option::Option::Some(UserEvent::SignIn),
             2 => ::std::option::Option::Some(UserEvent::SignUp),
             3 => ::std::option::Option::Some(UserEvent::SignOut),
@@ -48,7 +48,7 @@ impl ::protobuf::ProtobufEnum for UserEvent {
 
     fn values() -> &'static [Self] {
         static values: &'static [UserEvent] = &[
-            UserEvent::AuthCheck,
+            UserEvent::GetStatus,
             UserEvent::SignIn,
             UserEvent::SignUp,
             UserEvent::SignOut,
@@ -69,7 +69,7 @@ impl ::std::marker::Copy for UserEvent {
 
 impl ::std::default::Default for UserEvent {
     fn default() -> Self {
-        UserEvent::AuthCheck
+        UserEvent::GetStatus
     }
 }
 
@@ -80,7 +80,7 @@ impl ::protobuf::reflect::ProtobufValue for UserEvent {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x0bevent.proto*?\n\tUserEvent\x12\r\n\tAuthCheck\x10\0\x12\n\n\x06Sig\
+    \n\x0bevent.proto*?\n\tUserEvent\x12\r\n\tGetStatus\x10\0\x12\n\n\x06Sig\
     nIn\x10\x01\x12\n\n\x06SignUp\x10\x02\x12\x0b\n\x07SignOut\x10\x03J\xce\
     \x01\n\x06\x12\x04\0\0\x07\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\
     \x05\0\x12\x04\x02\0\x07\x01\n\n\n\x03\x05\0\x01\x12\x03\x02\x05\x0e\n\

+ 3 - 0
rust-lib/flowy-user/src/protobuf/model/mod.rs

@@ -1,5 +1,8 @@
 // Auto-generated, do not edit 
 
+mod user_status; 
+pub use user_status::*; 
+
 mod sign_up; 
 pub use sign_up::*; 
 

+ 15 - 172
rust-lib/flowy-user/src/protobuf/model/sign_in.rs

@@ -425,182 +425,25 @@ impl ::protobuf::reflect::ProtobufValue for SignInParams {
     }
 }
 
-#[derive(PartialEq,Clone,Default)]
-pub struct SignInResponse {
-    // message fields
-    pub is_success: bool,
-    // special fields
-    pub unknown_fields: ::protobuf::UnknownFields,
-    pub cached_size: ::protobuf::CachedSize,
-}
-
-impl<'a> ::std::default::Default for &'a SignInResponse {
-    fn default() -> &'a SignInResponse {
-        <SignInResponse as ::protobuf::Message>::default_instance()
-    }
-}
-
-impl SignInResponse {
-    pub fn new() -> SignInResponse {
-        ::std::default::Default::default()
-    }
-
-    // bool is_success = 1;
-
-
-    pub fn get_is_success(&self) -> bool {
-        self.is_success
-    }
-    pub fn clear_is_success(&mut self) {
-        self.is_success = false;
-    }
-
-    // Param is passed by value, moved
-    pub fn set_is_success(&mut self, v: bool) {
-        self.is_success = v;
-    }
-}
-
-impl ::protobuf::Message for SignInResponse {
-    fn is_initialized(&self) -> bool {
-        true
-    }
-
-    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
-        while !is.eof()? {
-            let (field_number, wire_type) = is.read_tag_unpack()?;
-            match field_number {
-                1 => {
-                    if wire_type != ::protobuf::wire_format::WireTypeVarint {
-                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
-                    }
-                    let tmp = is.read_bool()?;
-                    self.is_success = tmp;
-                },
-                _ => {
-                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
-                },
-            };
-        }
-        ::std::result::Result::Ok(())
-    }
-
-    // Compute sizes of nested messages
-    #[allow(unused_variables)]
-    fn compute_size(&self) -> u32 {
-        let mut my_size = 0;
-        if self.is_success != false {
-            my_size += 2;
-        }
-        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
-        self.cached_size.set(my_size);
-        my_size
-    }
-
-    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
-        if self.is_success != false {
-            os.write_bool(1, self.is_success)?;
-        }
-        os.write_unknown_fields(self.get_unknown_fields())?;
-        ::std::result::Result::Ok(())
-    }
-
-    fn get_cached_size(&self) -> u32 {
-        self.cached_size.get()
-    }
-
-    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
-        &self.unknown_fields
-    }
-
-    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
-        &mut self.unknown_fields
-    }
-
-    fn as_any(&self) -> &dyn (::std::any::Any) {
-        self as &dyn (::std::any::Any)
-    }
-    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
-        self as &mut dyn (::std::any::Any)
-    }
-    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
-        self
-    }
-
-    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
-        Self::descriptor_static()
-    }
-
-    fn new() -> SignInResponse {
-        SignInResponse::new()
-    }
-
-    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
-        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
-        descriptor.get(|| {
-            let mut fields = ::std::vec::Vec::new();
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBool>(
-                "is_success",
-                |m: &SignInResponse| { &m.is_success },
-                |m: &mut SignInResponse| { &mut m.is_success },
-            ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<SignInResponse>(
-                "SignInResponse",
-                fields,
-                file_descriptor_proto()
-            )
-        })
-    }
-
-    fn default_instance() -> &'static SignInResponse {
-        static instance: ::protobuf::rt::LazyV2<SignInResponse> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(SignInResponse::new)
-    }
-}
-
-impl ::protobuf::Clear for SignInResponse {
-    fn clear(&mut self) {
-        self.is_success = false;
-        self.unknown_fields.clear();
-    }
-}
-
-impl ::std::fmt::Debug for SignInResponse {
-    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
-        ::protobuf::text_format::fmt(self, f)
-    }
-}
-
-impl ::protobuf::reflect::ProtobufValue for SignInResponse {
-    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
-        ::protobuf::reflect::ReflectValueRef::Message(self)
-    }
-}
-
 static file_descriptor_proto_data: &'static [u8] = b"\
     \n\rsign_in.proto\"A\n\rSignInRequest\x12\x14\n\x05email\x18\x01\x20\x01\
     (\tR\x05email\x12\x1a\n\x08password\x18\x02\x20\x01(\tR\x08password\"@\n\
     \x0cSignInParams\x12\x14\n\x05email\x18\x01\x20\x01(\tR\x05email\x12\x1a\
-    \n\x08password\x18\x02\x20\x01(\tR\x08password\"/\n\x0eSignInResponse\
-    \x12\x1d\n\nis_success\x18\x01\x20\x01(\x08R\tisSuccessJ\xed\x02\n\x06\
-    \x12\x04\0\0\x0c\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\x15\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\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\
-    \x18\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\x13\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\
-    \x04\x16\x17\n\n\n\x02\x04\x01\x12\x04\x06\0\t\x01\n\n\n\x03\x04\x01\x01\
-    \x12\x03\x06\x08\x14\n\x0b\n\x04\x04\x01\x02\0\x12\x03\x07\x04\x15\n\x0c\
-    \n\x05\x04\x01\x02\0\x05\x12\x03\x07\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\
-    \x12\x03\x07\x0b\x10\n\x0c\n\x05\x04\x01\x02\0\x03\x12\x03\x07\x13\x14\n\
-    \x0b\n\x04\x04\x01\x02\x01\x12\x03\x08\x04\x18\n\x0c\n\x05\x04\x01\x02\
-    \x01\x05\x12\x03\x08\x04\n\n\x0c\n\x05\x04\x01\x02\x01\x01\x12\x03\x08\
-    \x0b\x13\n\x0c\n\x05\x04\x01\x02\x01\x03\x12\x03\x08\x16\x17\n\n\n\x02\
-    \x04\x02\x12\x04\n\0\x0c\x01\n\n\n\x03\x04\x02\x01\x12\x03\n\x08\x16\n\
-    \x0b\n\x04\x04\x02\x02\0\x12\x03\x0b\x04\x18\n\x0c\n\x05\x04\x02\x02\0\
-    \x05\x12\x03\x0b\x04\x08\n\x0c\n\x05\x04\x02\x02\0\x01\x12\x03\x0b\t\x13\
-    \n\x0c\n\x05\x04\x02\x02\0\x03\x12\x03\x0b\x16\x17b\x06proto3\
+    \n\x08password\x18\x02\x20\x01(\tR\x08passwordJ\x9e\x02\n\x06\x12\x04\0\
+    \0\t\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\x15\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\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\x18\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\x13\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x16\x17\n\
+    \n\n\x02\x04\x01\x12\x04\x06\0\t\x01\n\n\n\x03\x04\x01\x01\x12\x03\x06\
+    \x08\x14\n\x0b\n\x04\x04\x01\x02\0\x12\x03\x07\x04\x15\n\x0c\n\x05\x04\
+    \x01\x02\0\x05\x12\x03\x07\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\
+    \x07\x0b\x10\n\x0c\n\x05\x04\x01\x02\0\x03\x12\x03\x07\x13\x14\n\x0b\n\
+    \x04\x04\x01\x02\x01\x12\x03\x08\x04\x18\n\x0c\n\x05\x04\x01\x02\x01\x05\
+    \x12\x03\x08\x04\n\n\x0c\n\x05\x04\x01\x02\x01\x01\x12\x03\x08\x0b\x13\n\
+    \x0c\n\x05\x04\x01\x02\x01\x03\x12\x03\x08\x16\x17b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 346 - 0
rust-lib/flowy-user/src/protobuf/model/user_status.rs

@@ -0,0 +1,346 @@
+// This file is generated by rust-protobuf 2.22.1. Do not edit
+// @generated
+
+// https://github.com/rust-lang/rust-clippy/issues/702
+#![allow(unknown_lints)]
+#![allow(clippy::all)]
+
+#![allow(unused_attributes)]
+#![cfg_attr(rustfmt, rustfmt::skip)]
+
+#![allow(box_pointers)]
+#![allow(dead_code)]
+#![allow(missing_docs)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+#![allow(non_upper_case_globals)]
+#![allow(trivial_casts)]
+#![allow(unused_imports)]
+#![allow(unused_results)]
+//! Generated file from `user_status.proto`
+
+/// Generated files are compatible only with the same version
+/// of protobuf runtime.
+// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_22_1;
+
+#[derive(PartialEq,Clone,Default)]
+pub struct UserDetail {
+    // message fields
+    pub email: ::std::string::String,
+    pub name: ::std::string::String,
+    pub status: UserStatus,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a UserDetail {
+    fn default() -> &'a UserDetail {
+        <UserDetail as ::protobuf::Message>::default_instance()
+    }
+}
+
+impl UserDetail {
+    pub fn new() -> UserDetail {
+        ::std::default::Default::default()
+    }
+
+    // string email = 1;
+
+
+    pub fn get_email(&self) -> &str {
+        &self.email
+    }
+    pub fn clear_email(&mut self) {
+        self.email.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_email(&mut self, v: ::std::string::String) {
+        self.email = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_email(&mut self) -> &mut ::std::string::String {
+        &mut self.email
+    }
+
+    // Take field
+    pub fn take_email(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.email, ::std::string::String::new())
+    }
+
+    // string name = 2;
+
+
+    pub fn get_name(&self) -> &str {
+        &self.name
+    }
+    pub fn clear_name(&mut self) {
+        self.name.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_name(&mut self, v: ::std::string::String) {
+        self.name = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_name(&mut self) -> &mut ::std::string::String {
+        &mut self.name
+    }
+
+    // Take field
+    pub fn take_name(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.name, ::std::string::String::new())
+    }
+
+    // .UserStatus status = 3;
+
+
+    pub fn get_status(&self) -> UserStatus {
+        self.status
+    }
+    pub fn clear_status(&mut self) {
+        self.status = UserStatus::Unknown;
+    }
+
+    // Param is passed by value, moved
+    pub fn set_status(&mut self, v: UserStatus) {
+        self.status = v;
+    }
+}
+
+impl ::protobuf::Message for UserDetail {
+    fn is_initialized(&self) -> bool {
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            let (field_number, wire_type) = is.read_tag_unpack()?;
+            match field_number {
+                1 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.email)?;
+                },
+                2 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.name)?;
+                },
+                3 => {
+                    ::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.status, 3, &mut self.unknown_fields)?
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        if !self.email.is_empty() {
+            my_size += ::protobuf::rt::string_size(1, &self.email);
+        }
+        if !self.name.is_empty() {
+            my_size += ::protobuf::rt::string_size(2, &self.name);
+        }
+        if self.status != UserStatus::Unknown {
+            my_size += ::protobuf::rt::enum_size(3, self.status);
+        }
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        if !self.email.is_empty() {
+            os.write_string(1, &self.email)?;
+        }
+        if !self.name.is_empty() {
+            os.write_string(2, &self.name)?;
+        }
+        if self.status != UserStatus::Unknown {
+            os.write_enum(3, ::protobuf::ProtobufEnum::value(&self.status))?;
+        }
+        os.write_unknown_fields(self.get_unknown_fields())?;
+        ::std::result::Result::Ok(())
+    }
+
+    fn get_cached_size(&self) -> u32 {
+        self.cached_size.get()
+    }
+
+    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
+        &self.unknown_fields
+    }
+
+    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
+        &mut self.unknown_fields
+    }
+
+    fn as_any(&self) -> &dyn (::std::any::Any) {
+        self as &dyn (::std::any::Any)
+    }
+    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
+        self as &mut dyn (::std::any::Any)
+    }
+    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
+        self
+    }
+
+    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
+        Self::descriptor_static()
+    }
+
+    fn new() -> UserDetail {
+        UserDetail::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            let mut fields = ::std::vec::Vec::new();
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "email",
+                |m: &UserDetail| { &m.email },
+                |m: &mut UserDetail| { &mut m.email },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "name",
+                |m: &UserDetail| { &m.name },
+                |m: &mut UserDetail| { &mut m.name },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<UserStatus>>(
+                "status",
+                |m: &UserDetail| { &m.status },
+                |m: &mut UserDetail| { &mut m.status },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<UserDetail>(
+                "UserDetail",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static UserDetail {
+        static instance: ::protobuf::rt::LazyV2<UserDetail> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(UserDetail::new)
+    }
+}
+
+impl ::protobuf::Clear for UserDetail {
+    fn clear(&mut self) {
+        self.email.clear();
+        self.name.clear();
+        self.status = UserStatus::Unknown;
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for UserDetail {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for UserDetail {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
+#[derive(Clone,PartialEq,Eq,Debug,Hash)]
+pub enum UserStatus {
+    Unknown = 0,
+    Login = 1,
+    Expired = 2,
+}
+
+impl ::protobuf::ProtobufEnum for UserStatus {
+    fn value(&self) -> i32 {
+        *self as i32
+    }
+
+    fn from_i32(value: i32) -> ::std::option::Option<UserStatus> {
+        match value {
+            0 => ::std::option::Option::Some(UserStatus::Unknown),
+            1 => ::std::option::Option::Some(UserStatus::Login),
+            2 => ::std::option::Option::Some(UserStatus::Expired),
+            _ => ::std::option::Option::None
+        }
+    }
+
+    fn values() -> &'static [Self] {
+        static values: &'static [UserStatus] = &[
+            UserStatus::Unknown,
+            UserStatus::Login,
+            UserStatus::Expired,
+        ];
+        values
+    }
+
+    fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            ::protobuf::reflect::EnumDescriptor::new_pb_name::<UserStatus>("UserStatus", file_descriptor_proto())
+        })
+    }
+}
+
+impl ::std::marker::Copy for UserStatus {
+}
+
+impl ::std::default::Default for UserStatus {
+    fn default() -> Self {
+        UserStatus::Unknown
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for UserStatus {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
+    }
+}
+
+static file_descriptor_proto_data: &'static [u8] = b"\
+    \n\x11user_status.proto\"[\n\nUserDetail\x12\x14\n\x05email\x18\x01\x20\
+    \x01(\tR\x05email\x12\x12\n\x04name\x18\x02\x20\x01(\tR\x04name\x12#\n\
+    \x06status\x18\x03\x20\x01(\x0e2\x0b.UserStatusR\x06status*1\n\nUserStat\
+    us\x12\x0b\n\x07Unknown\x10\0\x12\t\n\x05Login\x10\x01\x12\x0b\n\x07Expi\
+    red\x10\x02J\xe2\x02\n\x06\x12\x04\0\0\x0b\x01\n\x08\n\x01\x0c\x12\x03\0\
+    \0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x06\x01\n\n\n\x03\x04\0\x01\x12\x03\
+    \x02\x08\x12\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\x0b\n\x04\x04\
+    \0\x02\x01\x12\x03\x04\x04\x14\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\x0f\n\x0c\n\x05\x04\
+    \0\x02\x01\x03\x12\x03\x04\x12\x13\n\x0b\n\x04\x04\0\x02\x02\x12\x03\x05\
+    \x04\x1a\n\x0c\n\x05\x04\0\x02\x02\x06\x12\x03\x05\x04\x0e\n\x0c\n\x05\
+    \x04\0\x02\x02\x01\x12\x03\x05\x0f\x15\n\x0c\n\x05\x04\0\x02\x02\x03\x12\
+    \x03\x05\x18\x19\n\n\n\x02\x05\0\x12\x04\x07\0\x0b\x01\n\n\n\x03\x05\0\
+    \x01\x12\x03\x07\x05\x0f\n\x0b\n\x04\x05\0\x02\0\x12\x03\x08\x04\x10\n\
+    \x0c\n\x05\x05\0\x02\0\x01\x12\x03\x08\x04\x0b\n\x0c\n\x05\x05\0\x02\0\
+    \x02\x12\x03\x08\x0e\x0f\n\x0b\n\x04\x05\0\x02\x01\x12\x03\t\x04\x0e\n\
+    \x0c\n\x05\x05\0\x02\x01\x01\x12\x03\t\x04\t\n\x0c\n\x05\x05\0\x02\x01\
+    \x02\x12\x03\t\x0c\r\n\x0b\n\x04\x05\0\x02\x02\x12\x03\n\x04\x10\n\x0c\n\
+    \x05\x05\0\x02\x02\x01\x12\x03\n\x04\x0b\n\x0c\n\x05\x05\0\x02\x02\x02\
+    \x12\x03\n\x0e\x0fb\x06proto3\
+";
+
+static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
+
+fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
+    ::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
+}
+
+pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
+    file_descriptor_proto_lazy.get(|| {
+        parse_descriptor_proto()
+    })
+}

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

@@ -1,7 +1,7 @@
 syntax = "proto3";
 
 enum UserEvent {
-    AuthCheck = 0;
+    GetStatus = 0;
     SignIn = 1;
     SignUp = 2;
     SignOut = 3;

+ 0 - 3
rust-lib/flowy-user/src/protobuf/proto/sign_in.proto

@@ -8,6 +8,3 @@ message SignInParams {
     string email = 1;
     string password = 2;
 }
-message SignInResponse {
-    bool is_success = 1;
-}

+ 12 - 0
rust-lib/flowy-user/src/protobuf/proto/user_status.proto

@@ -0,0 +1,12 @@
+syntax = "proto3";
+
+message UserDetail {
+    string email = 1;
+    string name = 2;
+    UserStatus status = 3;
+}
+enum UserStatus {
+    Unknown = 0;
+    Login = 1;
+    Expired = 2;
+}

+ 0 - 2
rust-lib/flowy-user/src/services/mod.rs

@@ -1,3 +1 @@
-pub mod database;
-mod register;
 pub mod user_session;

+ 0 - 22
rust-lib/flowy-user/src/services/register.rs

@@ -1,22 +0,0 @@
-use crate::{
-    domain::{tables::User, SignUpParams},
-    errors::UserError,
-};
-
-pub trait UserRegister {
-    fn register_user(&self, params: SignUpParams) -> Result<User, UserError>;
-}
-
-pub struct MockUserRegister {}
-
-impl UserRegister for MockUserRegister {
-    fn register_user(&self, params: SignUpParams) -> Result<User, UserError> {
-        let user_id = "9527".to_owned();
-        Ok(User::new(
-            user_id,
-            params.name,
-            params.email,
-            params.password,
-        ))
-    }
-}

+ 2 - 5
rust-lib/flowy-user/src/services/user_session/builder.rs

@@ -1,7 +1,4 @@
-use crate::services::{
-    register::MockUserRegister,
-    user_session::{UserSession, UserSessionConfig},
-};
+use crate::services::user_session::{register::MockUserServer, UserSession, UserSessionConfig};
 
 pub struct UserSessionBuilder {
     config: Option<UserSessionConfig>,
@@ -17,7 +14,7 @@ impl UserSessionBuilder {
 
     pub fn build(mut self) -> UserSession {
         let config = self.config.take().unwrap();
-        let register = MockUserRegister {};
+        let register = MockUserServer {};
         UserSession::new(config, register)
     }
 }

+ 28 - 23
rust-lib/flowy-user/src/services/database.rs → rust-lib/flowy-user/src/services/user_session/database.rs

@@ -9,18 +9,6 @@ use std::{
     },
 };
 
-thread_local! {
-    static USER_ID: RefCell<Option<String>> = RefCell::new(None);
-}
-fn set_user_id(user_id: Option<String>) {
-    USER_ID.with(|id| {
-        *id.borrow_mut() = user_id;
-    });
-}
-fn get_user_id() -> Option<String> { USER_ID.with(|id| id.borrow().clone()) }
-
-static IS_USER_DB_INIT: AtomicBool = AtomicBool::new(false);
-
 lazy_static! {
     static ref DB: RwLock<Option<Database>> = RwLock::new(None);
 }
@@ -37,8 +25,10 @@ impl UserDB {
     }
 
     fn open_user_db(&self, user_id: &str) -> Result<(), UserError> {
+        INIT_FLAG.store(true, Ordering::SeqCst);
         let dir = format!("{}/{}", self.db_dir, user_id);
-        let db = flowy_database::init(&dir).map_err(|e| UserError::Database(format!("{:?}", e)))?;
+        let db =
+            flowy_database::init(&dir).map_err(|e| UserError::Database(format!("😁{:?}", e)))?;
 
         let mut user_db = DB
             .write()
@@ -46,33 +36,36 @@ impl UserDB {
         *(user_db) = Some(db);
 
         set_user_id(Some(user_id.to_owned()));
-        IS_USER_DB_INIT.store(true, Ordering::SeqCst);
         Ok(())
     }
 
     pub(crate) fn close_user_db(&mut self) -> Result<(), UserError> {
+        INIT_FLAG.store(false, Ordering::SeqCst);
+
         let mut write_guard = DB
             .write()
             .map_err(|e| UserError::Database(format!("Close user db failed. {:?}", e)))?;
         *write_guard = None;
         set_user_id(None);
-        IS_USER_DB_INIT.store(false, Ordering::SeqCst);
+
         Ok(())
     }
 
     pub(crate) fn get_connection(&self, user_id: &str) -> Result<DBConnection, UserError> {
-        if !IS_USER_DB_INIT.load(Ordering::SeqCst) {
+        if !INIT_FLAG.load(Ordering::SeqCst) {
             let _ = self.open_user_db(user_id);
         }
 
         let thread_user_id = get_user_id();
-        if thread_user_id != Some(user_id.to_owned()) {
-            let msg = format!(
-                "Database owner does not match. origin: {:?}, current: {}",
-                thread_user_id, user_id
-            );
-            log::error!("{}", msg);
-            return Err(UserError::Database(msg));
+        if thread_user_id.is_some() {
+            if thread_user_id != Some(user_id.to_owned()) {
+                let msg = format!(
+                    "Database owner does not match. origin: {:?}, current: {}",
+                    thread_user_id, user_id
+                );
+                log::error!("{}", msg);
+                return Err(UserError::Database(msg));
+            }
         }
 
         let read_guard = DB
@@ -87,6 +80,18 @@ impl UserDB {
     }
 }
 
+thread_local! {
+    static USER_ID: RefCell<Option<String>> = RefCell::new(None);
+}
+fn set_user_id(user_id: Option<String>) {
+    USER_ID.with(|id| {
+        *id.borrow_mut() = user_id;
+    });
+}
+fn get_user_id() -> Option<String> { USER_ID.with(|id| id.borrow().clone()) }
+
+static INIT_FLAG: AtomicBool = AtomicBool::new(false);
+
 #[cfg(test)]
 mod tests {
 

+ 5 - 3
rust-lib/flowy-user/src/services/user_session/mod.rs

@@ -1,5 +1,7 @@
-mod builder;
-mod user_session;
-
 pub use builder::*;
 pub use user_session::*;
+
+mod builder;
+pub mod database;
+mod register;
+mod user_session;

+ 67 - 0
rust-lib/flowy-user/src/services/user_session/register.rs

@@ -0,0 +1,67 @@
+use std::sync::RwLock;
+
+use lazy_static::lazy_static;
+
+use flowy_infra::kv::KVStore;
+
+use crate::{
+    entities::{SignInParams, SignUpParams},
+    errors::UserError,
+    sql_tables::User,
+};
+
+lazy_static! {
+    pub static ref CURRENT_USER_ID: RwLock<Option<String>> = RwLock::new(None);
+}
+const USER_ID: &str = "user_id";
+pub(crate) fn get_current_user_id() -> Result<Option<String>, UserError> {
+    let read_guard = CURRENT_USER_ID
+        .read()
+        .map_err(|e| UserError::Auth(format!("Read current user id failed. {:?}", e)))?;
+
+    let mut current_user_id = (*read_guard).clone();
+    if current_user_id.is_none() {
+        current_user_id = KVStore::get_str(USER_ID);
+    }
+
+    Ok(current_user_id)
+}
+
+pub(crate) fn set_current_user_id(user_id: Option<String>) -> Result<(), UserError> {
+    KVStore::set_str(USER_ID, user_id.clone().unwrap_or("".to_owned()));
+
+    let mut current_user_id = CURRENT_USER_ID
+        .write()
+        .map_err(|e| UserError::Auth(format!("Write current user id failed. {:?}", e)))?;
+    *current_user_id = user_id;
+    Ok(())
+}
+
+pub trait UserServer {
+    fn sign_up(&self, params: SignUpParams) -> Result<User, UserError>;
+    fn sign_in(&self, params: SignInParams) -> Result<User, UserError>;
+}
+
+pub struct MockUserServer {}
+
+impl UserServer for MockUserServer {
+    fn sign_up(&self, params: SignUpParams) -> Result<User, UserError> {
+        let user_id = "9527".to_owned();
+        Ok(User::new(
+            user_id,
+            params.name,
+            params.email,
+            params.password,
+        ))
+    }
+
+    fn sign_in(&self, params: SignInParams) -> Result<User, UserError> {
+        let user_id = "9527".to_owned();
+        Ok(User::new(
+            user_id,
+            "".to_owned(),
+            params.email,
+            params.password,
+        ))
+    }
+}

+ 53 - 40
rust-lib/flowy-user/src/services/user_session/user_session.rs

@@ -1,14 +1,23 @@
-use crate::{
-    domain::{tables::User, SignUpParams},
-    errors::UserError,
-    services::{database::UserDB, register::UserRegister},
+use flowy_database::{
+    query_dsl::*,
+    schema::{user_table, user_table::dsl},
+    DBConnection,
+    ExpressionMethods,
 };
-use ::diesel::query_dsl::*;
-use flowy_database::schema::user_table;
-use flowy_sqlite::DBConnection;
+use flowy_infra::kv::KVStore;
 use lazy_static::lazy_static;
 use std::sync::RwLock;
 
+use crate::{
+    entities::{SignInParams, SignUpParams, UserDetail, UserId},
+    errors::UserError,
+    services::user_session::{
+        database::UserDB,
+        register::{UserServer, *},
+    },
+    sql_tables::User,
+};
+
 pub struct UserSessionConfig {
     root_dir: String,
 }
@@ -24,63 +33,67 @@ impl UserSessionConfig {
 pub struct UserSession {
     database: UserDB,
     config: UserSessionConfig,
-    register: Box<dyn UserRegister + Send + Sync>,
+    server: Box<dyn UserServer + Send + Sync>,
 }
 
 impl UserSession {
     pub fn new<R>(config: UserSessionConfig, register: R) -> Self
     where
-        R: 'static + UserRegister + Send + Sync,
+        R: 'static + UserServer + Send + Sync,
     {
         let db = UserDB::new(&config.root_dir);
         Self {
             database: db,
             config,
-            register: Box::new(register),
+            server: Box::new(register),
         }
     }
 
+    pub async fn sign_in(&self, params: SignInParams) -> Result<User, UserError> {
+        let user = self.server.sign_in(params)?;
+        let _ = set_current_user_id(Some(user.id.clone()))?;
+        self.save_user(user)
+    }
+
+    pub async fn sign_up(&self, params: SignUpParams) -> Result<User, UserError> {
+        let user = self.server.sign_up(params)?;
+        let _ = set_current_user_id(Some(user.id.clone()))?;
+        self.save_user(user)
+    }
+
+    pub fn sign_out(&self) -> Result<(), UserError> {
+        let _ = set_current_user_id(None)?;
+        // TODO: close the db
+        unimplemented!()
+    }
+
+    pub async fn get_user_status(&self, user_id: &str) -> Result<UserDetail, UserError> {
+        let user_id = UserId::parse(user_id.to_owned()).map_err(|e| UserError::Auth(e))?;
+        let conn = self.get_db_connection()?;
+
+        let user = dsl::user_table
+            .filter(user_table::id.eq(user_id.as_ref()))
+            .first::<User>(&*conn)?;
+
+        // TODO: getting user detail from remote
+        Ok(UserDetail::from(user))
+    }
+
     pub fn get_db_connection(&self) -> Result<DBConnection, UserError> {
         match get_current_user_id()? {
             None => Err(UserError::Auth("User is not login yet".to_owned())),
             Some(user_id) => self.database.get_connection(&user_id),
         }
     }
+}
 
-    pub fn sign_up(&self, params: SignUpParams) -> Result<User, UserError> {
-        let user = self.register.register_user(params)?;
-        set_current_user_id(Some(user.id.clone()));
-
+impl UserSession {
+    fn save_user(&self, user: User) -> Result<User, UserError> {
         let conn = self.get_db_connection()?;
-        let _ = diesel::insert_into(user_table::table)
+        let result = diesel::insert_into(user_table::table)
             .values(user.clone())
             .execute(&*conn)?;
 
         Ok(user)
     }
-
-    pub fn sign_out(&self) -> Result<(), UserError> {
-        set_current_user_id(None);
-        // TODO: close the db
-        unimplemented!()
-    }
-}
-
-lazy_static! {
-    pub static ref CURRENT_USER_ID: RwLock<Option<String>> = RwLock::new(None);
-}
-fn get_current_user_id() -> Result<Option<String>, UserError> {
-    let current_user_id = CURRENT_USER_ID
-        .read()
-        .map_err(|e| UserError::Auth(format!("Read current user id failed. {:?}", e)))?;
-
-    Ok((*current_user_id).clone())
-}
-
-pub fn set_current_user_id(user_id: Option<String>) -> Result<(), UserError> {
-    let mut current_user_id = CURRENT_USER_ID
-        .write()
-        .map_err(|e| UserError::Auth(format!("Write current user id failed. {:?}", e)))?;
-    *current_user_id = user_id;
-    Ok(())
 }

+ 0 - 0
rust-lib/flowy-user/src/domain/tables/mod.rs → rust-lib/flowy-user/src/sql_tables/mod.rs


+ 2 - 2
rust-lib/flowy-user/src/domain/tables/user_table.rs → rust-lib/flowy-user/src/sql_tables/user_table.rs

@@ -8,10 +8,10 @@ pub struct User {
     pub(crate) id: String,
 
     #[pb(index = 2)]
-    name: String,
+    pub(crate) name: String,
 
     #[pb(index = 3)]
-    email: String,
+    pub(crate) email: String,
 
     #[pb(index = 4)]
     password: String,

+ 0 - 130
rust-lib/flowy-user/tests/auth_test.rs

@@ -1,130 +0,0 @@
-use flowy_test::prelude::*;
-use flowy_user::{event::UserEvent::*, prelude::*};
-
-#[test]
-fn sign_up_success() {
-    let request = SignUpRequest {
-        email: valid_email(),
-        name: valid_name(),
-        password: valid_password(),
-    };
-
-    let _response = EventTester::new(SignUp).request(request).sync_send();
-    // .parse::<SignUpResponse>();
-    // dbg!(&response);
-}
-
-#[test]
-fn sign_in_success() {
-    let request = SignInRequest {
-        email: valid_email(),
-        password: valid_password(),
-    };
-
-    let response = EventTester::new(SignIn)
-        .request(request)
-        .sync_send()
-        .parse::<SignInResponse>();
-    dbg!(&response);
-}
-
-#[test]
-fn sign_up_with_invalid_email() {
-    for email in invalid_email_test_case() {
-        let request = SignUpRequest {
-            email: email.to_string(),
-            name: valid_name(),
-            password: valid_password(),
-        };
-
-        let _ = EventTester::new(SignUp)
-            .request(request)
-            .assert_error()
-            .sync_send();
-    }
-}
-#[test]
-fn sign_up_with_invalid_password() {
-    for password in invalid_password_test_case() {
-        let request = SignUpRequest {
-            email: valid_email(),
-            name: valid_name(),
-            password,
-        };
-
-        let _ = EventTester::new(SignUp)
-            .request(request)
-            .assert_error()
-            .sync_send();
-    }
-}
-#[test]
-fn sign_in_with_invalid_email() {
-    for email in invalid_email_test_case() {
-        let request = SignInRequest {
-            email: email.to_string(),
-            password: valid_password(),
-        };
-
-        let _ = EventTester::new(SignIn)
-            .request(request)
-            .assert_error()
-            .sync_send();
-    }
-}
-
-#[test]
-fn sign_in_with_invalid_password() {
-    for password in invalid_password_test_case() {
-        let request = SignInRequest {
-            email: valid_email(),
-            password,
-        };
-
-        let _ = EventTester::new(SignIn)
-            .request(request)
-            .assert_error()
-            .sync_send();
-    }
-}
-
-fn invalid_email_test_case() -> Vec<String> {
-    // https://gist.github.com/cjaoude/fd9910626629b53c4d25
-    vec![
-        "",
-        "annie@",
-        "annie@gmail@",
-        "#@%^%#$@#$@#.com",
-        "@example.com",
-        "Joe Smith <[email protected]>",
-        "email.example.com",
-        "email@[email protected]",
-        "[email protected]",
-        "[email protected]",
-        "あいうえお@example.com",
-        /* The following email is valid according to the validate_email function return
-         * "[email protected]",
-         * "[email protected]",
-         * "[email protected]",
-         * "email@example",
-         * "[email protected]",
-         * "[email protected]",
-         * "[email protected]", */
-    ]
-    .iter()
-    .map(|s| s.to_string())
-    .collect::<Vec<_>>()
-}
-
-fn invalid_password_test_case() -> Vec<String> {
-    vec!["", "123456", "1234".repeat(100).as_str()]
-        .iter()
-        .map(|s| s.to_string())
-        .collect::<Vec<_>>()
-}
-
-fn valid_email() -> String { "[email protected]".to_string() }
-
-fn valid_password() -> String { "HelloWorld!123".to_string() }
-
-fn valid_name() -> String { "AppFlowy".to_string() }

+ 40 - 0
rust-lib/flowy-user/tests/event/helper.rs

@@ -0,0 +1,40 @@
+pub(crate) fn invalid_email_test_case() -> Vec<String> {
+    // https://gist.github.com/cjaoude/fd9910626629b53c4d25
+    vec![
+        "",
+        "annie@",
+        "annie@gmail@",
+        "#@%^%#$@#$@#.com",
+        "@example.com",
+        "Joe Smith <[email protected]>",
+        "email.example.com",
+        "email@[email protected]",
+        "[email protected]",
+        "[email protected]",
+        "あいうえお@example.com",
+        /* The following email is valid according to the validate_email function return
+         * "[email protected]",
+         * "[email protected]",
+         * "[email protected]",
+         * "email@example",
+         * "[email protected]",
+         * "[email protected]",
+         * "[email protected]", */
+    ]
+    .iter()
+    .map(|s| s.to_string())
+    .collect::<Vec<_>>()
+}
+
+pub(crate) fn invalid_password_test_case() -> Vec<String> {
+    vec!["", "123456", "1234".repeat(100).as_str()]
+        .iter()
+        .map(|s| s.to_string())
+        .collect::<Vec<_>>()
+}
+
+pub(crate) fn valid_email() -> String { "[email protected]".to_string() }
+
+pub(crate) fn valid_password() -> String { "HelloWorld!123".to_string() }
+
+pub(crate) fn valid_name() -> String { "AppFlowy".to_string() }

+ 4 - 0
rust-lib/flowy-user/tests/event/main.rs

@@ -0,0 +1,4 @@
+mod helper;
+mod sign_in_test;
+mod sign_up_test;
+mod user_status_test;

+ 46 - 0
rust-lib/flowy-user/tests/event/sign_in_test.rs

@@ -0,0 +1,46 @@
+use crate::helper::*;
+use flowy_test::prelude::*;
+use flowy_user::{event::UserEvent::*, prelude::*};
+#[test]
+fn sign_in_success() {
+    let request = SignInRequest {
+        email: valid_email(),
+        password: valid_password(),
+    };
+
+    let response = EventTester::new(SignIn)
+        .request(request)
+        .sync_send()
+        .parse::<UserDetail>();
+    dbg!(&response);
+}
+
+#[test]
+fn sign_in_with_invalid_email() {
+    for email in invalid_email_test_case() {
+        let request = SignInRequest {
+            email: email.to_string(),
+            password: valid_password(),
+        };
+
+        let _ = EventTester::new(SignIn)
+            .request(request)
+            .assert_error()
+            .sync_send();
+    }
+}
+
+#[test]
+fn sign_in_with_invalid_password() {
+    for password in invalid_password_test_case() {
+        let request = SignInRequest {
+            email: valid_email(),
+            password,
+        };
+
+        let _ = EventTester::new(SignIn)
+            .request(request)
+            .assert_error()
+            .sync_send();
+    }
+}

+ 47 - 0
rust-lib/flowy-user/tests/event/sign_up_test.rs

@@ -0,0 +1,47 @@
+use crate::helper::*;
+use flowy_test::prelude::*;
+use flowy_user::{event::UserEvent::*, prelude::*};
+
+#[test]
+fn sign_up_success() {
+    let request = SignUpRequest {
+        email: valid_email(),
+        name: valid_name(),
+        password: valid_password(),
+    };
+
+    let _response = EventTester::new(SignUp).request(request).sync_send();
+    // .parse::<SignUpResponse>();
+    // dbg!(&response);
+}
+
+#[test]
+fn sign_up_with_invalid_email() {
+    for email in invalid_email_test_case() {
+        let request = SignUpRequest {
+            email: email.to_string(),
+            name: valid_name(),
+            password: valid_password(),
+        };
+
+        let _ = EventTester::new(SignUp)
+            .request(request)
+            .assert_error()
+            .sync_send();
+    }
+}
+#[test]
+fn sign_up_with_invalid_password() {
+    for password in invalid_password_test_case() {
+        let request = SignUpRequest {
+            email: valid_email(),
+            name: valid_name(),
+            password,
+        };
+
+        let _ = EventTester::new(SignUp)
+            .request(request)
+            .assert_error()
+            .sync_send();
+    }
+}

+ 5 - 0
rust-lib/flowy-user/tests/event/user_status_test.rs

@@ -0,0 +1,5 @@
+use crate::helper::*;
+use flowy_test::prelude::*;
+use flowy_user::{event::UserEvent::*, prelude::*};
+#[test]
+fn user_status_get_after_login() {}

+ 0 - 1
rust-lib/flowy-user/tests/main.rs

@@ -1 +0,0 @@
-mod auth_test;

+ 4 - 0
scripts/install_tool.sh

@@ -12,4 +12,8 @@ cargo install cargo-cache
 
 #protobuf code gen env
 brew install [email protected]
+brew tap dart-lang/dart
+brew install dart
+pub global activate protoc_plugin
+
 cargo install --version 2.20.0 protobuf-codegen