ws connect to server

This commit is contained in:
appflowy 2021-09-17 19:03:46 +08:00
parent 7e9d7c0304
commit 1fd460f3e5
42 changed files with 299 additions and 343 deletions

View File

@ -18,7 +18,7 @@ export 'package:app_flowy/welcome/domain/i_splash.dart';
class SplashUserImpl implements ISplashUser { class SplashUserImpl implements ISplashUser {
@override @override
Future<AuthState> currentUserProfile() { Future<AuthState> currentUserProfile() {
final result = UserEventGetUserProfile().send(); final result = UserEventInitUser().send();
return result.then((result) { return result.then((result) {
return result.fold( return result.fold(
(userProfile) { (userProfile) {
@ -32,7 +32,6 @@ class SplashUserImpl implements ISplashUser {
} }
} }
class SplashRoute implements ISplashRoute { class SplashRoute implements ISplashRoute {
@override @override
Future<void> pushWelcomeScreen(BuildContext context, UserProfile user) async { Future<void> pushWelcomeScreen(BuildContext context, UserProfile user) async {

View File

@ -288,12 +288,12 @@ class WorkspaceEventApplyChangeset {
} }
} }
class UserEventGetUserProfile { class UserEventInitUser {
UserEventGetUserProfile(); UserEventInitUser();
Future<Either<UserProfile, UserError>> send() { Future<Either<UserProfile, UserError>> send() {
final request = FFIRequest.create() final request = FFIRequest.create()
..event = UserEvent.GetUserProfile.toString(); ..event = UserEvent.InitUser.toString();
return Dispatch.asyncRequest(request).then((bytesResult) => bytesResult.fold( return Dispatch.asyncRequest(request).then((bytesResult) => bytesResult.fold(
(okBytes) => left(UserProfile.fromBuffer(okBytes)), (okBytes) => left(UserProfile.fromBuffer(okBytes)),
@ -367,3 +367,17 @@ class UserEventUpdateUser {
} }
} }
class UserEventGetUserProfile {
UserEventGetUserProfile();
Future<Either<UserProfile, UserError>> send() {
final request = FFIRequest.create()
..event = UserEvent.GetUserProfile.toString();
return Dispatch.asyncRequest(request).then((bytesResult) => bytesResult.fold(
(okBytes) => left(UserProfile.fromBuffer(okBytes)),
(errBytes) => right(UserError.fromBuffer(errBytes)),
));
}
}

View File

@ -10,18 +10,20 @@ import 'dart:core' as $core;
import 'package:protobuf/protobuf.dart' as $pb; import 'package:protobuf/protobuf.dart' as $pb;
class UserEvent extends $pb.ProtobufEnum { class UserEvent extends $pb.ProtobufEnum {
static const UserEvent GetUserProfile = UserEvent._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetUserProfile'); static const UserEvent InitUser = UserEvent._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'InitUser');
static const UserEvent SignIn = UserEvent._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SignIn'); 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 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 UserEvent SignOut = UserEvent._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SignOut');
static const UserEvent UpdateUser = UserEvent._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateUser'); static const UserEvent UpdateUser = UserEvent._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateUser');
static const UserEvent GetUserProfile = UserEvent._(5, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetUserProfile');
static const $core.List<UserEvent> values = <UserEvent> [ static const $core.List<UserEvent> values = <UserEvent> [
GetUserProfile, InitUser,
SignIn, SignIn,
SignUp, SignUp,
SignOut, SignOut,
UpdateUser, UpdateUser,
GetUserProfile,
]; ];
static final $core.Map<$core.int, UserEvent> _byValue = $pb.ProtobufEnum.initByValue(values); static final $core.Map<$core.int, UserEvent> _byValue = $pb.ProtobufEnum.initByValue(values);

View File

@ -12,13 +12,14 @@ import 'dart:typed_data' as $typed_data;
const UserEvent$json = const { const UserEvent$json = const {
'1': 'UserEvent', '1': 'UserEvent',
'2': const [ '2': const [
const {'1': 'GetUserProfile', '2': 0}, const {'1': 'InitUser', '2': 0},
const {'1': 'SignIn', '2': 1}, const {'1': 'SignIn', '2': 1},
const {'1': 'SignUp', '2': 2}, const {'1': 'SignUp', '2': 2},
const {'1': 'SignOut', '2': 3}, const {'1': 'SignOut', '2': 3},
const {'1': 'UpdateUser', '2': 4}, const {'1': 'UpdateUser', '2': 4},
const {'1': 'GetUserProfile', '2': 5},
], ],
}; };
/// Descriptor for `UserEvent`. Decode as a `google.protobuf.EnumDescriptorProto`. /// Descriptor for `UserEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List userEventDescriptor = $convert.base64Decode('CglVc2VyRXZlbnQSEgoOR2V0VXNlclByb2ZpbGUQABIKCgZTaWduSW4QARIKCgZTaWduVXAQAhILCgdTaWduT3V0EAMSDgoKVXBkYXRlVXNlchAE'); final $typed_data.Uint8List userEventDescriptor = $convert.base64Decode('CglVc2VyRXZlbnQSDAoISW5pdFVzZXIQABIKCgZTaWduSW4QARIKCgZTaWduVXAQAhILCgdTaWduT3V0EAMSDgoKVXBkYXRlVXNlchAEEhIKDkdldFVzZXJQcm9maWxlEAU=');

View File

@ -9,8 +9,6 @@ import 'dart:core' as $core;
import 'package:protobuf/protobuf.dart' as $pb; import 'package:protobuf/protobuf.dart' as $pb;
export 'user_profile.pbenum.dart';
class UserToken extends $pb.GeneratedMessage { class UserToken extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'UserToken', createEmptyInstance: create) static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'UserToken', createEmptyInstance: create)
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'token') ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'token')

View File

@ -5,24 +5,3 @@
// @dart = 2.12 // @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: 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);
}

View File

@ -8,18 +8,6 @@
import 'dart:core' as $core; import 'dart:core' as $core;
import 'dart:convert' as $convert; import 'dart:convert' as $convert;
import 'dart:typed_data' as $typed_data; import 'dart:typed_data' as $typed_data;
@$core.Deprecated('Use userStatusDescriptor instead')
const UserStatus$json = const {
'1': 'UserStatus',
'2': const [
const {'1': 'Unknown', '2': 0},
const {'1': 'Login', '2': 1},
const {'1': 'Expired', '2': 2},
],
};
/// Descriptor for `UserStatus`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List userStatusDescriptor = $convert.base64Decode('CgpVc2VyU3RhdHVzEgsKB1Vua25vd24QABIJCgVMb2dpbhABEgsKB0V4cGlyZWQQAg==');
@$core.Deprecated('Use userTokenDescriptor instead') @$core.Deprecated('Use userTokenDescriptor instead')
const UserToken$json = const { const UserToken$json = const {
'1': 'UserToken', '1': 'UserToken',

View File

@ -86,4 +86,5 @@ path = "src/main.rs"
once_cell = "1.7.2" once_cell = "1.7.2"
linkify = "0.5.0" linkify = "0.5.0"
flowy-user = { path = "../rust-lib/flowy-user" } flowy-user = { path = "../rust-lib/flowy-user" }
flowy-workspace = { path = "../rust-lib/flowy-workspace" } flowy-workspace = { path = "../rust-lib/flowy-workspace" }
flowy-ws = { path = "../rust-lib/flowy-ws" }

View File

@ -4,4 +4,4 @@ pub const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(8);
pub const PING_TIMEOUT: Duration = Duration::from_secs(60); pub const PING_TIMEOUT: Duration = Duration::from_secs(60);
pub const MAX_PAYLOAD_SIZE: usize = 262_144; // max payload size is 256k pub const MAX_PAYLOAD_SIZE: usize = 262_144; // max payload size is 256k
pub const IGNORE_ROUTES: [&str; 2] = ["/api/register", "/api/auth"]; pub const IGNORE_ROUTES: [&str; 3] = ["/api/register", "/api/auth", "/ws"];

View File

@ -32,7 +32,7 @@ impl Claim {
} }
} }
pub fn get_user_id(self) -> String { self.user_id } pub fn user_id(self) -> String { self.user_id }
} }
// impl From<Claim> for User { // impl From<Claim> for User {

View File

@ -56,6 +56,7 @@ where
fn call(&self, req: ServiceRequest) -> Self::Future { fn call(&self, req: ServiceRequest) -> Self::Future {
let mut authenticate_pass: bool = false; let mut authenticate_pass: bool = false;
for ignore_route in IGNORE_ROUTES.iter() { for ignore_route in IGNORE_ROUTES.iter() {
log::info!("ignore: {}, path: {}", ignore_route, req.path());
if req.path().starts_with(ignore_route) { if req.path().starts_with(ignore_route) {
authenticate_pass = true; authenticate_pass = true;
break; break;
@ -68,7 +69,6 @@ where
match result { match result {
Ok(logged_user) => { Ok(logged_user) => {
authenticate_pass = AUTHORIZED_USERS.is_authorized(&logged_user); authenticate_pass = AUTHORIZED_USERS.is_authorized(&logged_user);
// Update user timestamp // Update user timestamp
AUTHORIZED_USERS.store_auth(logged_user, true); AUTHORIZED_USERS.store_auth(logged_user, true);
}, },

View File

@ -49,7 +49,7 @@ pub async fn sign_in(pool: &PgPool, params: SignInParams) -> Result<SignInRespon
let token = Token::create_token(&user.id.to_string())?; let token = Token::create_token(&user.id.to_string())?;
let logged_user = LoggedUser::new(&user.id.to_string()); let logged_user = LoggedUser::new(&user.id.to_string());
let _ = AUTHORIZED_USERS.store_auth(logged_user, true)?; AUTHORIZED_USERS.store_auth(logged_user, true);
let mut response_data = SignInResponse::default(); let mut response_data = SignInResponse::default();
response_data.set_user_id(user.id.to_string()); response_data.set_user_id(user.id.to_string());
response_data.set_name(user.name); response_data.set_name(user.name);
@ -60,7 +60,7 @@ pub async fn sign_in(pool: &PgPool, params: SignInParams) -> Result<SignInRespon
} }
pub async fn sign_out(logged_user: LoggedUser) -> Result<FlowyResponse, ServerError> { pub async fn sign_out(logged_user: LoggedUser) -> Result<FlowyResponse, ServerError> {
let _ = AUTHORIZED_USERS.store_auth(logged_user, false)?; AUTHORIZED_USERS.store_auth(logged_user, false);
Ok(FlowyResponse::success()) Ok(FlowyResponse::success())
} }
@ -91,7 +91,7 @@ pub async fn register_user(
.context("Failed to insert user")?; .context("Failed to insert user")?;
let logged_user = LoggedUser::new(&response_data.user_id); let logged_user = LoggedUser::new(&response_data.user_id);
let _ = AUTHORIZED_USERS.store_auth(logged_user, true)?; AUTHORIZED_USERS.store_auth(logged_user, true);
let _ = create_default_workspace(&mut transaction, response_data.get_user_id()).await?; let _ = create_default_workspace(&mut transaction, response_data.get_user_id()).await?;
transaction transaction
@ -112,7 +112,7 @@ pub(crate) async fn get_user_profile(
.await .await
.context("Failed to acquire a Postgres connection to get user detail")?; .context("Failed to acquire a Postgres connection to get user detail")?;
let id = logged_user.get_user_id()?; let id = logged_user.as_uuid()?;
let user_table = let user_table =
sqlx::query_as::<Postgres, UserTable>("SELECT * FROM user_table WHERE id = $1") sqlx::query_as::<Postgres, UserTable>("SELECT * FROM user_table WHERE id = $1")
.bind(id) .bind(id)
@ -126,7 +126,7 @@ pub(crate) async fn get_user_profile(
.context("Failed to commit SQL transaction to get user detail.")?; .context("Failed to commit SQL transaction to get user detail.")?;
// update the user active time // update the user active time
let _ = AUTHORIZED_USERS.store_auth(logged_user, true)?; AUTHORIZED_USERS.store_auth(logged_user, true);
let mut user_profile = UserProfile::default(); let mut user_profile = UserProfile::default();
user_profile.set_id(user_table.id.to_string()); user_profile.set_id(user_table.id.to_string());
@ -178,7 +178,7 @@ pub(crate) async fn set_user_profile(
.add_some_arg("name", name) .add_some_arg("name", name)
.add_some_arg("email", email) .add_some_arg("email", email)
.add_some_arg("password", password) .add_some_arg("password", password)
.and_where_eq("id", &logged_user.get_user_id()?) .and_where_eq("id", &logged_user.as_uuid()?)
.build()?; .build()?;
sqlx::query_with(&sql, args) sqlx::query_with(&sql, args)

View File

@ -13,13 +13,13 @@ lazy_static! {
#[derive(Debug, PartialEq, Eq, Hash, Clone)] #[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct LoggedUser { pub struct LoggedUser {
user_id: String, pub user_id: String,
} }
impl std::convert::From<Claim> for LoggedUser { impl std::convert::From<Claim> for LoggedUser {
fn from(c: Claim) -> Self { fn from(c: Claim) -> Self {
Self { Self {
user_id: c.get_user_id(), user_id: c.user_id(),
} }
} }
} }
@ -36,7 +36,7 @@ impl LoggedUser {
Ok(user) Ok(user)
} }
pub fn get_user_id(&self) -> Result<uuid::Uuid, ServerError> { pub fn as_uuid(&self) -> Result<uuid::Uuid, ServerError> {
let id = uuid::Uuid::parse_str(&self.user_id)?; let id = uuid::Uuid::parse_str(&self.user_id)?;
Ok(id) Ok(id)
} }
@ -106,13 +106,12 @@ impl AuthorizedUsers {
} }
} }
pub fn store_auth(&self, user: LoggedUser, is_auth: bool) -> Result<(), ServerError> { pub fn store_auth(&self, user: LoggedUser, is_auth: bool) {
let status = if is_auth { let status = if is_auth {
AuthStatus::Authorized(Utc::now()) AuthStatus::Authorized(Utc::now())
} else { } else {
AuthStatus::NotAuthorized AuthStatus::NotAuthorized
}; };
self.0.insert(user, status); self.0.insert(user, status);
Ok(())
} }
} }

View File

@ -31,7 +31,7 @@ pub(crate) async fn create_app(
) -> Result<FlowyResponse, ServerError> { ) -> Result<FlowyResponse, ServerError> {
let name = AppName::parse(params.take_name()).map_err(invalid_params)?; let name = AppName::parse(params.take_name()).map_err(invalid_params)?;
let workspace_id = WorkspaceId::parse(params.take_workspace_id()).map_err(invalid_params)?; let workspace_id = WorkspaceId::parse(params.take_workspace_id()).map_err(invalid_params)?;
let user_id = logged_user.get_user_id()?.to_string(); let user_id = logged_user.as_uuid()?.to_string();
let desc = AppDesc::parse(params.take_desc()).map_err(invalid_params)?; let desc = AppDesc::parse(params.take_desc()).map_err(invalid_params)?;
let mut transaction = pool let mut transaction = pool
.begin() .begin()

View File

@ -28,7 +28,7 @@ pub(crate) async fn create_workspace(
) -> Result<FlowyResponse, ServerError> { ) -> Result<FlowyResponse, ServerError> {
let name = WorkspaceName::parse(params.get_name().to_owned()).map_err(invalid_params)?; let name = WorkspaceName::parse(params.get_name().to_owned()).map_err(invalid_params)?;
let desc = WorkspaceDesc::parse(params.get_desc().to_owned()).map_err(invalid_params)?; let desc = WorkspaceDesc::parse(params.get_desc().to_owned()).map_err(invalid_params)?;
let user_id = logged_user.get_user_id()?.to_string(); let user_id = logged_user.as_uuid()?.to_string();
let mut transaction = pool let mut transaction = pool
.begin() .begin()
@ -134,7 +134,7 @@ pub async fn read_workspaces(
workspace_id: Option<String>, workspace_id: Option<String>,
logged_user: LoggedUser, logged_user: LoggedUser,
) -> Result<FlowyResponse, ServerError> { ) -> Result<FlowyResponse, ServerError> {
let user_id = logged_user.get_user_id()?.to_string(); let user_id = logged_user.as_uuid()?.to_string();
let mut transaction = pool let mut transaction = pool
.begin() .begin()
.await .await

View File

@ -1,6 +1,7 @@
use crate::service::ws_service::{entities::SessionId, WSClient, WSServer}; use crate::service::ws_service::{entities::SessionId, WSClient, WSServer};
use actix::Addr; use actix::Addr;
use crate::service::user_service::LoggedUser;
use actix_web::{ use actix_web::{
get, get,
web::{Data, Path, Payload}, web::{Data, Path, Payload},
@ -14,17 +15,30 @@ use actix_web_actors::ws;
pub async fn establish_ws_connection( pub async fn establish_ws_connection(
request: HttpRequest, request: HttpRequest,
payload: Payload, payload: Payload,
path: Path<String>, token: Path<String>,
server: Data<Addr<WSServer>>, server: Data<Addr<WSServer>>,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
let client = WSClient::new(SessionId::new(path.clone()), server.get_ref().clone()); match LoggedUser::from_token(token.clone()) {
let result = ws::start(client, &request, payload); Ok(user) => {
let client = WSClient::new(
match result { SessionId::new(user.user_id.clone()),
Ok(response) => Ok(response.into()), server.get_ref().clone(),
);
let result = ws::start(client, &request, payload);
match result {
Ok(response) => Ok(response.into()),
Err(e) => {
log::error!("ws connection error: {:?}", e);
Err(e)
},
}
},
Err(e) => { Err(e) => {
log::error!("ws connection error: {:?}", e); if e.is_unauthorized() {
Err(e) Ok(HttpResponse::Unauthorized().json(e))
} else {
Ok(HttpResponse::BadRequest().json(e))
}
}, },
} }
} }

View File

@ -179,6 +179,10 @@ impl TestServer {
let response = user_sign_up_request(params, &url).await.unwrap(); let response = user_sign_up_request(params, &url).await.unwrap();
response response
} }
pub(crate) fn ws_addr(&self) -> String {
format!("{}/ws/{}", self.address, self.user_token.as_ref().unwrap())
}
} }
pub async fn spawn_server() -> TestServer { pub async fn spawn_server() -> TestServer {
let database_name = format!("{}", Uuid::new_v4().to_string()); let database_name = format!("{}", Uuid::new_v4().to_string());

View File

@ -2,3 +2,4 @@ mod auth;
mod doc; mod doc;
mod helper; mod helper;
mod workspace; mod workspace;
mod ws;

10
backend/tests/api/ws.rs Normal file
View File

@ -0,0 +1,10 @@
use crate::helper::TestServer;
use flowy_ws::WsController;
#[actix_rt::test]
async fn ws_connect() {
let server = TestServer::new().await;
let mut controller = WsController::new();
let addr = server.ws_addr();
let _ = controller.connect(addr).await.unwrap();
}

View File

@ -20,8 +20,7 @@ byteorder = {version = "1.3.4"}
ffi-support = {version = "0.4.2"} ffi-support = {version = "0.4.2"}
protobuf = {version = "2.20.0"} protobuf = {version = "2.20.0"}
lazy_static = {version = "1.4.0"} lazy_static = {version = "1.4.0"}
#tokio = { version = "1", features = ["rt", "rt-multi-thread"] } tokio = { version = "1", features = ["rt", "rt-multi-thread"] }
tokio = { version = "1", features = ["full"] }
log = "0.4.14" log = "0.4.14"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = {version = "1.0"} serde_json = {version = "1.0"}

View File

@ -80,7 +80,6 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
| "WorkspaceObservable" | "WorkspaceObservable"
| "DocObservable" | "DocObservable"
| "FFIStatusCode" | "FFIStatusCode"
| "UserStatus"
| "UserEvent" | "UserEvent"
| "UserObservable" | "UserObservable"
=> TypeCategory::Enum, => TypeCategory::Enum,

View File

@ -4,7 +4,7 @@ use tokio::runtime;
pub mod ready; pub mod ready;
pub(crate) fn tokio_default_runtime() -> io::Result<tokio::runtime::Runtime> { pub fn tokio_default_runtime() -> io::Result<tokio::runtime::Runtime> {
runtime::Builder::new_multi_thread() runtime::Builder::new_multi_thread()
.thread_name("flowy-rt") .thread_name("flowy-rt")
.enable_io() .enable_io()

View File

@ -1,16 +0,0 @@
#[rustfmt::skip]
use flowy_dispatch::prelude::*;
use std::sync::Once;
#[allow(dead_code)]
pub fn setup_env() {
static INIT: Once = Once::new();
(|| env_logger::init());
}
pub fn init_dispatch<F>(module_factory: F) -> EventDispatch
where
F: FnOnce() -> Vec<Module>,
{
EventDispatch::construct(module_factory)
}

View File

@ -1,2 +1 @@
mod helper;
mod module; mod module;

View File

@ -6,9 +6,10 @@ pub async fn hello() -> String { "say hello".to_string() }
#[tokio::test] #[tokio::test]
async fn test() { async fn test() {
setup_env(); env_logger::init();
let event = "1"; let event = "1";
let dispatch = Arc::new(init_dispatch(|| vec![Module::new().event(event, hello)])); let dispatch = Arc::new(EventDispatch::construct(|| vec![Module::new().event(event, hello)]));
let request = ModuleRequest::new(event); let request = ModuleRequest::new(event);
let _ = EventDispatch::async_send_with_callback(dispatch.clone(), request, |resp| { let _ = EventDispatch::async_send_with_callback(dispatch.clone(), request, |resp| {
Box::pin(async move { Box::pin(async move {

View File

@ -13,12 +13,12 @@ flowy-infra = { path = "../flowy-infra" }
flowy-workspace = { path = "../flowy-workspace" } flowy-workspace = { path = "../flowy-workspace" }
flowy-database = { path = "../flowy-database" } flowy-database = { path = "../flowy-database" }
flowy-document = { path = "../flowy-document" } flowy-document = { path = "../flowy-document" }
flowy-ws = { path = "../flowy-ws" }
tracing = { version = "0.1" } tracing = { version = "0.1" }
log = "0.4.14" log = "0.4.14"
futures-core = { version = "0.3", default-features = false } futures-core = { version = "0.3", default-features = false }
color-eyre = { version = "0.5", default-features = false } color-eyre = { version = "0.5", default-features = false }
bytes = "1.0" bytes = "1.0"
tokio = { version = "1", features = ["rt"] }
[dev-dependencies] [dev-dependencies]
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }

View File

@ -3,7 +3,6 @@ mod deps_resolve;
pub mod module; pub mod module;
use flowy_dispatch::prelude::*; use flowy_dispatch::prelude::*;
use flowy_ws::start_ws_connection;
use module::build_modules; use module::build_modules;
pub use module::*; pub use module::*;
use std::sync::{ use std::sync::{
@ -88,9 +87,5 @@ fn init_log(config: &FlowySDKConfig) {
fn init_dispatch(root: &str) -> EventDispatch { fn init_dispatch(root: &str) -> EventDispatch {
let config = ModuleConfig { root: root.to_owned() }; let config = ModuleConfig { root: root.to_owned() };
let dispatch = EventDispatch::construct(|| build_modules(config)); let dispatch = EventDispatch::construct(|| build_modules(config));
dispatch.spawn(async {
start_ws_connection();
});
dispatch dispatch
} }

View File

@ -10,6 +10,10 @@ pub struct ModuleConfig {
} }
pub fn build_modules(config: ModuleConfig) -> Vec<Module> { pub fn build_modules(config: ModuleConfig) -> Vec<Module> {
// runtime.spawn(async move {
// start_ws_connection("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
// eyJpc3MiOiJsb2NhbGhvc3QiLCJzdWIiOiJhdXRoIiwiaWF0IjoxNjMxNzcwODQ2LCJleHAiOjE2MzIyMDI4NDYsInVzZXJfaWQiOiI5ZmFiN2I4MS1mZDAyLTRhN2EtYjA4Zi05NDM3NTdmZmE5MDcifQ.
// UzV01tHnWEZWBp3nJPTmFi7ypxBoCe56AjEPb9bnsFE") });
let user_session = Arc::new(UserSessionBuilder::new().root_dir(&config.root).build()); let user_session = Arc::new(UserSessionBuilder::new().root_dir(&config.root).build());
let workspace_user_impl = Arc::new(WorkspaceUserImpl { let workspace_user_impl = Arc::new(WorkspaceUserImpl {

View File

@ -13,6 +13,7 @@ flowy-database = { path = "../flowy-database" }
flowy-sqlite = { path = "../flowy-sqlite" } flowy-sqlite = { path = "../flowy-sqlite" }
flowy-infra = { path = "../flowy-infra" } flowy-infra = { path = "../flowy-infra" }
flowy-net = { path = "../flowy-net", features = ["flowy_request"] } flowy-net = { path = "../flowy-net", features = ["flowy_request"] }
flowy-ws = { path = "../flowy-ws"}
flowy-observable = { path = "../flowy-observable" } flowy-observable = { path = "../flowy-observable" }
tracing = { version = "0.1", features = ["log"] } tracing = { version = "0.1", features = ["log"] }

View File

@ -1,4 +1,4 @@
use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_derive::ProtoBuf;
#[derive(Default, ProtoBuf)] #[derive(Default, ProtoBuf)]
pub struct UserToken { pub struct UserToken {
@ -6,17 +6,6 @@ pub struct UserToken {
pub token: String, pub token: String,
} }
#[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, PartialEq, Eq, Clone)] #[derive(ProtoBuf, Default, Debug, PartialEq, Eq, Clone)]
pub struct UserProfile { pub struct UserProfile {
#[pb(index = 1)] #[pb(index = 1)]

View File

@ -5,7 +5,7 @@ use strum_macros::Display;
#[event_err = "UserError"] #[event_err = "UserError"]
pub enum UserEvent { pub enum UserEvent {
#[event(output = "UserProfile")] #[event(output = "UserProfile")]
GetUserProfile = 0, InitUser = 0,
#[event(input = "SignInRequest", output = "UserProfile")] #[event(input = "SignInRequest", output = "UserProfile")]
SignIn = 1, SignIn = 1,
@ -18,4 +18,7 @@ pub enum UserEvent {
#[event(input = "UpdateUserRequest")] #[event(input = "UpdateUserRequest")]
UpdateUser = 4, UpdateUser = 4,
#[event(output = "UserProfile")]
GetUserProfile = 5,
} }

View File

@ -3,8 +3,14 @@ use flowy_dispatch::prelude::*;
use std::{convert::TryInto, sync::Arc}; use std::{convert::TryInto, sync::Arc};
#[tracing::instrument(name = "get_profile", skip(session))] #[tracing::instrument(skip(session))]
pub async fn user_profile_handler(session: Unit<Arc<UserSession>>) -> DataResult<UserProfile, UserError> { pub async fn init_user_handler(session: Unit<Arc<UserSession>>) -> DataResult<UserProfile, UserError> {
let user_profile = session.init_user().await?;
data_result(user_profile)
}
#[tracing::instrument(skip(session))]
pub async fn get_user_profile_handler(session: Unit<Arc<UserSession>>) -> DataResult<UserProfile, UserError> {
let user_profile = session.user_profile().await?; let user_profile = session.user_profile().await?;
data_result(user_profile) data_result(user_profile)
} }

View File

@ -9,7 +9,8 @@ pub fn create(user_session: Arc<UserSession>) -> Module {
.data(user_session) .data(user_session)
.event(UserEvent::SignIn, sign_in) .event(UserEvent::SignIn, sign_in)
.event(UserEvent::SignUp, sign_up) .event(UserEvent::SignUp, sign_up)
.event(UserEvent::GetUserProfile, user_profile_handler) .event(UserEvent::InitUser, init_user_handler)
.event(UserEvent::GetUserProfile, get_user_profile_handler)
.event(UserEvent::SignOut, sign_out) .event(UserEvent::SignOut, sign_out)
.event(UserEvent::UpdateUser, update_user_handler) .event(UserEvent::UpdateUser, update_user_handler)
} }

View File

@ -25,11 +25,12 @@
#[derive(Clone,PartialEq,Eq,Debug,Hash)] #[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum UserEvent { pub enum UserEvent {
GetUserProfile = 0, InitUser = 0,
SignIn = 1, SignIn = 1,
SignUp = 2, SignUp = 2,
SignOut = 3, SignOut = 3,
UpdateUser = 4, UpdateUser = 4,
GetUserProfile = 5,
} }
impl ::protobuf::ProtobufEnum for UserEvent { impl ::protobuf::ProtobufEnum for UserEvent {
@ -39,22 +40,24 @@ impl ::protobuf::ProtobufEnum for UserEvent {
fn from_i32(value: i32) -> ::std::option::Option<UserEvent> { fn from_i32(value: i32) -> ::std::option::Option<UserEvent> {
match value { match value {
0 => ::std::option::Option::Some(UserEvent::GetUserProfile), 0 => ::std::option::Option::Some(UserEvent::InitUser),
1 => ::std::option::Option::Some(UserEvent::SignIn), 1 => ::std::option::Option::Some(UserEvent::SignIn),
2 => ::std::option::Option::Some(UserEvent::SignUp), 2 => ::std::option::Option::Some(UserEvent::SignUp),
3 => ::std::option::Option::Some(UserEvent::SignOut), 3 => ::std::option::Option::Some(UserEvent::SignOut),
4 => ::std::option::Option::Some(UserEvent::UpdateUser), 4 => ::std::option::Option::Some(UserEvent::UpdateUser),
5 => ::std::option::Option::Some(UserEvent::GetUserProfile),
_ => ::std::option::Option::None _ => ::std::option::Option::None
} }
} }
fn values() -> &'static [Self] { fn values() -> &'static [Self] {
static values: &'static [UserEvent] = &[ static values: &'static [UserEvent] = &[
UserEvent::GetUserProfile, UserEvent::InitUser,
UserEvent::SignIn, UserEvent::SignIn,
UserEvent::SignUp, UserEvent::SignUp,
UserEvent::SignOut, UserEvent::SignOut,
UserEvent::UpdateUser, UserEvent::UpdateUser,
UserEvent::GetUserProfile,
]; ];
values values
} }
@ -72,7 +75,7 @@ impl ::std::marker::Copy for UserEvent {
impl ::std::default::Default for UserEvent { impl ::std::default::Default for UserEvent {
fn default() -> Self { fn default() -> Self {
UserEvent::GetUserProfile UserEvent::InitUser
} }
} }
@ -83,21 +86,24 @@ impl ::protobuf::reflect::ProtobufValue for UserEvent {
} }
static file_descriptor_proto_data: &'static [u8] = b"\ static file_descriptor_proto_data: &'static [u8] = b"\
\n\x0bevent.proto*T\n\tUserEvent\x12\x12\n\x0eGetUserProfile\x10\0\x12\n\ \n\x0bevent.proto*b\n\tUserEvent\x12\x0c\n\x08InitUser\x10\0\x12\n\n\x06\
\n\x06SignIn\x10\x01\x12\n\n\x06SignUp\x10\x02\x12\x0b\n\x07SignOut\x10\ SignIn\x10\x01\x12\n\n\x06SignUp\x10\x02\x12\x0b\n\x07SignOut\x10\x03\
\x03\x12\x0e\n\nUpdateUser\x10\x04J\xf7\x01\n\x06\x12\x04\0\0\x08\x01\n\ \x12\x0e\n\nUpdateUser\x10\x04\x12\x12\n\x0eGetUserProfile\x10\x05J\xa0\
\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x05\0\x12\x04\x02\0\x08\x01\n\n\ \x02\n\x06\x12\x04\0\0\t\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\
\n\x03\x05\0\x01\x12\x03\x02\x05\x0e\n\x0b\n\x04\x05\0\x02\0\x12\x03\x03\ \x05\0\x12\x04\x02\0\t\x01\n\n\n\x03\x05\0\x01\x12\x03\x02\x05\x0e\n\x0b\
\x04\x17\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x03\x04\x12\n\x0c\n\x05\x05\ \n\x04\x05\0\x02\0\x12\x03\x03\x04\x11\n\x0c\n\x05\x05\0\x02\0\x01\x12\
\0\x02\0\x02\x12\x03\x03\x15\x16\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x04\ \x03\x03\x04\x0c\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x03\x0f\x10\n\x0b\n\
\x04\x0f\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x04\x04\n\n\x0c\n\x05\x05\ \x04\x05\0\x02\x01\x12\x03\x04\x04\x0f\n\x0c\n\x05\x05\0\x02\x01\x01\x12\
\0\x02\x01\x02\x12\x03\x04\r\x0e\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x05\ \x03\x04\x04\n\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x04\r\x0e\n\x0b\n\
\x04\x0f\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\x05\x04\n\n\x0c\n\x05\x05\ \x04\x05\0\x02\x02\x12\x03\x05\x04\x0f\n\x0c\n\x05\x05\0\x02\x02\x01\x12\
\0\x02\x02\x02\x12\x03\x05\r\x0e\n\x0b\n\x04\x05\0\x02\x03\x12\x03\x06\ \x03\x05\x04\n\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\x05\r\x0e\n\x0b\n\
\x04\x10\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\x06\x04\x0b\n\x0c\n\x05\ \x04\x05\0\x02\x03\x12\x03\x06\x04\x10\n\x0c\n\x05\x05\0\x02\x03\x01\x12\
\x05\0\x02\x03\x02\x12\x03\x06\x0e\x0f\n\x0b\n\x04\x05\0\x02\x04\x12\x03\ \x03\x06\x04\x0b\n\x0c\n\x05\x05\0\x02\x03\x02\x12\x03\x06\x0e\x0f\n\x0b\
\x07\x04\x13\n\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x07\x04\x0e\n\x0c\n\ \n\x04\x05\0\x02\x04\x12\x03\x07\x04\x13\n\x0c\n\x05\x05\0\x02\x04\x01\
\x05\x05\0\x02\x04\x02\x12\x03\x07\x11\x12b\x06proto3\ \x12\x03\x07\x04\x0e\n\x0c\n\x05\x05\0\x02\x04\x02\x12\x03\x07\x11\x12\n\
\x0b\n\x04\x05\0\x02\x05\x12\x03\x08\x04\x17\n\x0c\n\x05\x05\0\x02\x05\
\x01\x12\x03\x08\x04\x12\n\x0c\n\x05\x05\0\x02\x05\x02\x12\x03\x08\x15\
\x16b\x06proto3\
"; ";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -1273,59 +1273,6 @@ impl ::protobuf::reflect::ProtobufValue for UpdateUserParams {
} }
} }
#[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"\ static file_descriptor_proto_data: &'static [u8] = b"\
\n\x12user_profile.proto\"!\n\tUserToken\x12\x14\n\x05token\x18\x01\x20\ \n\x12user_profile.proto\"!\n\tUserToken\x12\x14\n\x05token\x18\x01\x20\
\x01(\tR\x05token\"]\n\x0bUserProfile\x12\x0e\n\x02id\x18\x01\x20\x01(\t\ \x01(\tR\x05token\"]\n\x0bUserProfile\x12\x0e\n\x02id\x18\x01\x20\x01(\t\
@ -1339,64 +1286,56 @@ static file_descriptor_proto_data: &'static [u8] = b"\
id\x18\x01\x20\x01(\tR\x02id\x12\x14\n\x04name\x18\x02\x20\x01(\tH\0R\ id\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\ \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\ \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\nUserStatus\x12\x0b\ \x0e\n\x0cone_of_emailB\x11\n\x0fone_of_passwordJ\xdf\x07\n\x06\x12\x04\
\n\x07Unknown\x10\0\x12\t\n\x05Login\x10\x01\x12\x0b\n\x07Expired\x10\ \0\0\x16\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\
\x02J\xf2\x08\n\x06\x12\x04\0\0\x1b\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\ \0\x04\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x11\n\x0b\n\x04\x04\0\x02\
\n\n\n\x02\x04\0\x12\x04\x02\0\x04\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\ \0\x12\x03\x03\x04\x15\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\x03\x04\n\n\
\x08\x11\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x15\n\x0c\n\x05\x04\0\ \x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x0b\x10\n\x0c\n\x05\x04\0\x02\0\
\x02\0\x05\x12\x03\x03\x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x0b\ \x03\x12\x03\x03\x13\x14\n\n\n\x02\x04\x01\x12\x04\x05\0\n\x01\n\n\n\x03\
\x10\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x13\x14\n\n\n\x02\x04\x01\ \x04\x01\x01\x12\x03\x05\x08\x13\n\x0b\n\x04\x04\x01\x02\0\x12\x03\x06\
\x12\x04\x05\0\n\x01\n\n\n\x03\x04\x01\x01\x12\x03\x05\x08\x13\n\x0b\n\ \x04\x12\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\x06\x04\n\n\x0c\n\x05\x04\
\x04\x04\x01\x02\0\x12\x03\x06\x04\x12\n\x0c\n\x05\x04\x01\x02\0\x05\x12\ \x01\x02\0\x01\x12\x03\x06\x0b\r\n\x0c\n\x05\x04\x01\x02\0\x03\x12\x03\
\x03\x06\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x06\x0b\r\n\x0c\n\ \x06\x10\x11\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\x07\x04\x15\n\x0c\n\x05\
\x05\x04\x01\x02\0\x03\x12\x03\x06\x10\x11\n\x0b\n\x04\x04\x01\x02\x01\ \x04\x01\x02\x01\x05\x12\x03\x07\x04\n\n\x0c\n\x05\x04\x01\x02\x01\x01\
\x12\x03\x07\x04\x15\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\x07\x04\n\n\ \x12\x03\x07\x0b\x10\n\x0c\n\x05\x04\x01\x02\x01\x03\x12\x03\x07\x13\x14\
\x0c\n\x05\x04\x01\x02\x01\x01\x12\x03\x07\x0b\x10\n\x0c\n\x05\x04\x01\ \n\x0b\n\x04\x04\x01\x02\x02\x12\x03\x08\x04\x14\n\x0c\n\x05\x04\x01\x02\
\x02\x01\x03\x12\x03\x07\x13\x14\n\x0b\n\x04\x04\x01\x02\x02\x12\x03\x08\ \x02\x05\x12\x03\x08\x04\n\n\x0c\n\x05\x04\x01\x02\x02\x01\x12\x03\x08\
\x04\x14\n\x0c\n\x05\x04\x01\x02\x02\x05\x12\x03\x08\x04\n\n\x0c\n\x05\ \x0b\x0f\n\x0c\n\x05\x04\x01\x02\x02\x03\x12\x03\x08\x12\x13\n\x0b\n\x04\
\x04\x01\x02\x02\x01\x12\x03\x08\x0b\x0f\n\x0c\n\x05\x04\x01\x02\x02\x03\ \x04\x01\x02\x03\x12\x03\t\x04\x15\n\x0c\n\x05\x04\x01\x02\x03\x05\x12\
\x12\x03\x08\x12\x13\n\x0b\n\x04\x04\x01\x02\x03\x12\x03\t\x04\x15\n\x0c\ \x03\t\x04\n\n\x0c\n\x05\x04\x01\x02\x03\x01\x12\x03\t\x0b\x10\n\x0c\n\
\n\x05\x04\x01\x02\x03\x05\x12\x03\t\x04\n\n\x0c\n\x05\x04\x01\x02\x03\ \x05\x04\x01\x02\x03\x03\x12\x03\t\x13\x14\n\n\n\x02\x04\x02\x12\x04\x0b\
\x01\x12\x03\t\x0b\x10\n\x0c\n\x05\x04\x01\x02\x03\x03\x12\x03\t\x13\x14\ \0\x10\x01\n\n\n\x03\x04\x02\x01\x12\x03\x0b\x08\x19\n\x0b\n\x04\x04\x02\
\n\n\n\x02\x04\x02\x12\x04\x0b\0\x10\x01\n\n\n\x03\x04\x02\x01\x12\x03\ \x02\0\x12\x03\x0c\x04\x12\n\x0c\n\x05\x04\x02\x02\0\x05\x12\x03\x0c\x04\
\x0b\x08\x19\n\x0b\n\x04\x04\x02\x02\0\x12\x03\x0c\x04\x12\n\x0c\n\x05\ \n\n\x0c\n\x05\x04\x02\x02\0\x01\x12\x03\x0c\x0b\r\n\x0c\n\x05\x04\x02\
\x04\x02\x02\0\x05\x12\x03\x0c\x04\n\n\x0c\n\x05\x04\x02\x02\0\x01\x12\ \x02\0\x03\x12\x03\x0c\x10\x11\n\x0b\n\x04\x04\x02\x08\0\x12\x03\r\x04*\
\x03\x0c\x0b\r\n\x0c\n\x05\x04\x02\x02\0\x03\x12\x03\x0c\x10\x11\n\x0b\n\ \n\x0c\n\x05\x04\x02\x08\0\x01\x12\x03\r\n\x15\n\x0b\n\x04\x04\x02\x02\
\x04\x04\x02\x08\0\x12\x03\r\x04*\n\x0c\n\x05\x04\x02\x08\0\x01\x12\x03\ \x01\x12\x03\r\x18(\n\x0c\n\x05\x04\x02\x02\x01\x05\x12\x03\r\x18\x1e\n\
\r\n\x15\n\x0b\n\x04\x04\x02\x02\x01\x12\x03\r\x18(\n\x0c\n\x05\x04\x02\ \x0c\n\x05\x04\x02\x02\x01\x01\x12\x03\r\x1f#\n\x0c\n\x05\x04\x02\x02\
\x02\x01\x05\x12\x03\r\x18\x1e\n\x0c\n\x05\x04\x02\x02\x01\x01\x12\x03\r\ \x01\x03\x12\x03\r&'\n\x0b\n\x04\x04\x02\x08\x01\x12\x03\x0e\x04,\n\x0c\
\x1f#\n\x0c\n\x05\x04\x02\x02\x01\x03\x12\x03\r&'\n\x0b\n\x04\x04\x02\ \n\x05\x04\x02\x08\x01\x01\x12\x03\x0e\n\x16\n\x0b\n\x04\x04\x02\x02\x02\
\x08\x01\x12\x03\x0e\x04,\n\x0c\n\x05\x04\x02\x08\x01\x01\x12\x03\x0e\n\ \x12\x03\x0e\x19*\n\x0c\n\x05\x04\x02\x02\x02\x05\x12\x03\x0e\x19\x1f\n\
\x16\n\x0b\n\x04\x04\x02\x02\x02\x12\x03\x0e\x19*\n\x0c\n\x05\x04\x02\ \x0c\n\x05\x04\x02\x02\x02\x01\x12\x03\x0e\x20%\n\x0c\n\x05\x04\x02\x02\
\x02\x02\x05\x12\x03\x0e\x19\x1f\n\x0c\n\x05\x04\x02\x02\x02\x01\x12\x03\ \x02\x03\x12\x03\x0e()\n\x0b\n\x04\x04\x02\x08\x02\x12\x03\x0f\x042\n\
\x0e\x20%\n\x0c\n\x05\x04\x02\x02\x02\x03\x12\x03\x0e()\n\x0b\n\x04\x04\ \x0c\n\x05\x04\x02\x08\x02\x01\x12\x03\x0f\n\x19\n\x0b\n\x04\x04\x02\x02\
\x02\x08\x02\x12\x03\x0f\x042\n\x0c\n\x05\x04\x02\x08\x02\x01\x12\x03\ \x03\x12\x03\x0f\x1c0\n\x0c\n\x05\x04\x02\x02\x03\x05\x12\x03\x0f\x1c\"\
\x0f\n\x19\n\x0b\n\x04\x04\x02\x02\x03\x12\x03\x0f\x1c0\n\x0c\n\x05\x04\ \n\x0c\n\x05\x04\x02\x02\x03\x01\x12\x03\x0f#+\n\x0c\n\x05\x04\x02\x02\
\x02\x02\x03\x05\x12\x03\x0f\x1c\"\n\x0c\n\x05\x04\x02\x02\x03\x01\x12\ \x03\x03\x12\x03\x0f./\n\n\n\x02\x04\x03\x12\x04\x11\0\x16\x01\n\n\n\x03\
\x03\x0f#+\n\x0c\n\x05\x04\x02\x02\x03\x03\x12\x03\x0f./\n\n\n\x02\x04\ \x04\x03\x01\x12\x03\x11\x08\x18\n\x0b\n\x04\x04\x03\x02\0\x12\x03\x12\
\x03\x12\x04\x11\0\x16\x01\n\n\n\x03\x04\x03\x01\x12\x03\x11\x08\x18\n\ \x04\x12\n\x0c\n\x05\x04\x03\x02\0\x05\x12\x03\x12\x04\n\n\x0c\n\x05\x04\
\x0b\n\x04\x04\x03\x02\0\x12\x03\x12\x04\x12\n\x0c\n\x05\x04\x03\x02\0\ \x03\x02\0\x01\x12\x03\x12\x0b\r\n\x0c\n\x05\x04\x03\x02\0\x03\x12\x03\
\x05\x12\x03\x12\x04\n\n\x0c\n\x05\x04\x03\x02\0\x01\x12\x03\x12\x0b\r\n\ \x12\x10\x11\n\x0b\n\x04\x04\x03\x08\0\x12\x03\x13\x04*\n\x0c\n\x05\x04\
\x0c\n\x05\x04\x03\x02\0\x03\x12\x03\x12\x10\x11\n\x0b\n\x04\x04\x03\x08\ \x03\x08\0\x01\x12\x03\x13\n\x15\n\x0b\n\x04\x04\x03\x02\x01\x12\x03\x13\
\0\x12\x03\x13\x04*\n\x0c\n\x05\x04\x03\x08\0\x01\x12\x03\x13\n\x15\n\ \x18(\n\x0c\n\x05\x04\x03\x02\x01\x05\x12\x03\x13\x18\x1e\n\x0c\n\x05\
\x0b\n\x04\x04\x03\x02\x01\x12\x03\x13\x18(\n\x0c\n\x05\x04\x03\x02\x01\ \x04\x03\x02\x01\x01\x12\x03\x13\x1f#\n\x0c\n\x05\x04\x03\x02\x01\x03\
\x05\x12\x03\x13\x18\x1e\n\x0c\n\x05\x04\x03\x02\x01\x01\x12\x03\x13\x1f\ \x12\x03\x13&'\n\x0b\n\x04\x04\x03\x08\x01\x12\x03\x14\x04,\n\x0c\n\x05\
#\n\x0c\n\x05\x04\x03\x02\x01\x03\x12\x03\x13&'\n\x0b\n\x04\x04\x03\x08\ \x04\x03\x08\x01\x01\x12\x03\x14\n\x16\n\x0b\n\x04\x04\x03\x02\x02\x12\
\x01\x12\x03\x14\x04,\n\x0c\n\x05\x04\x03\x08\x01\x01\x12\x03\x14\n\x16\ \x03\x14\x19*\n\x0c\n\x05\x04\x03\x02\x02\x05\x12\x03\x14\x19\x1f\n\x0c\
\n\x0b\n\x04\x04\x03\x02\x02\x12\x03\x14\x19*\n\x0c\n\x05\x04\x03\x02\ \n\x05\x04\x03\x02\x02\x01\x12\x03\x14\x20%\n\x0c\n\x05\x04\x03\x02\x02\
\x02\x05\x12\x03\x14\x19\x1f\n\x0c\n\x05\x04\x03\x02\x02\x01\x12\x03\x14\ \x03\x12\x03\x14()\n\x0b\n\x04\x04\x03\x08\x02\x12\x03\x15\x042\n\x0c\n\
\x20%\n\x0c\n\x05\x04\x03\x02\x02\x03\x12\x03\x14()\n\x0b\n\x04\x04\x03\ \x05\x04\x03\x08\x02\x01\x12\x03\x15\n\x19\n\x0b\n\x04\x04\x03\x02\x03\
\x08\x02\x12\x03\x15\x042\n\x0c\n\x05\x04\x03\x08\x02\x01\x12\x03\x15\n\ \x12\x03\x15\x1c0\n\x0c\n\x05\x04\x03\x02\x03\x05\x12\x03\x15\x1c\"\n\
\x19\n\x0b\n\x04\x04\x03\x02\x03\x12\x03\x15\x1c0\n\x0c\n\x05\x04\x03\ \x0c\n\x05\x04\x03\x02\x03\x01\x12\x03\x15#+\n\x0c\n\x05\x04\x03\x02\x03\
\x02\x03\x05\x12\x03\x15\x1c\"\n\x0c\n\x05\x04\x03\x02\x03\x01\x12\x03\ \x03\x12\x03\x15./b\x06proto3\
\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\
"; ";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -1,9 +1,10 @@
syntax = "proto3"; syntax = "proto3";
enum UserEvent { enum UserEvent {
GetUserProfile = 0; InitUser = 0;
SignIn = 1; SignIn = 1;
SignUp = 2; SignUp = 2;
SignOut = 3; SignOut = 3;
UpdateUser = 4; UpdateUser = 4;
GetUserProfile = 5;
} }

View File

@ -21,8 +21,3 @@ message UpdateUserParams {
oneof one_of_email { string email = 3; }; oneof one_of_email { string email = 3; };
oneof one_of_password { string password = 4; }; oneof one_of_password { string password = 4; };
} }
enum UserStatus {
Unknown = 0;
Login = 1;
Expired = 2;
}

View File

@ -1,20 +1,31 @@
use crate::services::user::{UserSession, UserSessionConfig}; use crate::services::user::{SessionStatusCallback, UserSession, UserSessionConfig};
use std::sync::Arc;
pub struct UserSessionBuilder { pub struct UserSessionBuilder {
config: Option<UserSessionConfig>, config: Option<UserSessionConfig>,
callback: SessionStatusCallback,
} }
impl UserSessionBuilder { impl UserSessionBuilder {
pub fn new() -> Self { Self { config: None } } pub fn new() -> Self {
Self {
config: None,
callback: Arc::new(|_| {}),
}
}
pub fn root_dir(mut self, dir: &str) -> Self { pub fn root_dir(mut self, dir: &str) -> Self {
self.config = Some(UserSessionConfig::new(dir)); self.config = Some(UserSessionConfig::new(dir));
self self
} }
pub fn status_callback(mut self, callback: SessionStatusCallback) -> Self {
self.callback = callback;
self
}
pub fn build(mut self) -> UserSession { pub fn build(mut self) -> UserSession {
let config = self.config.take().unwrap(); let config = self.config.take().unwrap();
UserSession::new(config, self.callback.clone())
UserSession::new(config)
} }
} }

View File

@ -18,6 +18,7 @@ use flowy_database::{
}; };
use flowy_infra::kv::KV; use flowy_infra::kv::KV;
use flowy_sqlite::ConnectionPool; use flowy_sqlite::ConnectionPool;
use flowy_ws::WsController;
use parking_lot::RwLock; use parking_lot::RwLock;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::Arc; use std::sync::Arc;
@ -34,24 +35,36 @@ impl UserSessionConfig {
} }
} }
pub enum SessionStatus {
Login { token: String },
Expired { token: String },
}
pub type SessionStatusCallback = Arc<dyn Fn(SessionStatus) + Send + Sync>;
pub struct UserSession { pub struct UserSession {
database: UserDB, database: UserDB,
config: UserSessionConfig, config: UserSessionConfig,
#[allow(dead_code)] #[allow(dead_code)]
server: Server, server: Server,
session: RwLock<Option<Session>>, session: RwLock<Option<Session>>,
ws: RwLock<WsController>,
status_callback: SessionStatusCallback,
} }
impl UserSession { impl UserSession {
pub fn new(config: UserSessionConfig) -> Self { pub fn new(config: UserSessionConfig, status_callback: SessionStatusCallback) -> Self {
let db = UserDB::new(&config.root_dir); let db = UserDB::new(&config.root_dir);
let server = construct_user_server(); let server = construct_user_server();
Self { let ws = RwLock::new(WsController::new());
let user_session = Self {
database: db, database: db,
config, config,
server, server,
session: RwLock::new(None), session: RwLock::new(None),
} ws,
status_callback,
};
user_session
} }
pub fn db_connection(&self) -> Result<DBConnection, UserError> { pub fn db_connection(&self) -> Result<DBConnection, UserError> {
@ -80,6 +93,9 @@ impl UserSession {
let _ = self.set_session(Some(session))?; let _ = self.set_session(Some(session))?;
let user_table = self.save_user(resp.into()).await?; let user_table = self.save_user(resp.into()).await?;
let user_profile = UserProfile::from(user_table); let user_profile = UserProfile::from(user_table);
(self.status_callback)(SessionStatus::Login {
token: user_profile.token.clone(),
});
Ok(user_profile) Ok(user_profile)
} }
} }
@ -94,6 +110,9 @@ impl UserSession {
let _ = self.set_session(Some(session))?; let _ = self.set_session(Some(session))?;
let user_table = self.save_user(resp.into()).await?; let user_table = self.save_user(resp.into()).await?;
let user_profile = UserProfile::from(user_table); let user_profile = UserProfile::from(user_table);
(self.status_callback)(SessionStatus::Login {
token: user_profile.token.clone(),
});
Ok(user_profile) Ok(user_profile)
} }
} }
@ -104,6 +123,9 @@ impl UserSession {
let _ = diesel::delete(dsl::user_table.filter(dsl::id.eq(&session.user_id))).execute(&*(self.db_connection()?))?; let _ = diesel::delete(dsl::user_table.filter(dsl::id.eq(&session.user_id))).execute(&*(self.db_connection()?))?;
let _ = self.database.close_user_db(&session.user_id)?; let _ = self.database.close_user_db(&session.user_id)?;
let _ = self.set_session(None)?; let _ = self.set_session(None)?;
(self.status_callback)(SessionStatus::Expired {
token: session.token.clone(),
});
let _ = self.sign_out_on_server(&session.token).await?; let _ = self.sign_out_on_server(&session.token).await?;
Ok(()) Ok(())
@ -119,13 +141,26 @@ impl UserSession {
Ok(()) Ok(())
} }
pub async fn init_user(&self) -> Result<UserProfile, UserError> {
let (user_id, token) = self.get_session()?.into_part();
let user = dsl::user_table
.filter(user_table::id.eq(&user_id))
.first::<UserTable>(&*(self.db_connection()?))?;
let _ = self.read_user_profile_on_server(&token)?;
let _ = self.start_ws_connection(&token)?;
Ok(UserProfile::from(user))
}
pub async fn user_profile(&self) -> Result<UserProfile, UserError> { pub async fn user_profile(&self) -> Result<UserProfile, UserError> {
let (user_id, token) = self.get_session()?.into_part(); let (user_id, token) = self.get_session()?.into_part();
let user = dsl::user_table let user = dsl::user_table
.filter(user_table::id.eq(&user_id)) .filter(user_table::id.eq(&user_id))
.first::<UserTable>(&*(self.db_connection()?))?; .first::<UserTable>(&*(self.db_connection()?))?;
let _ = self.read_user_profile_on_server(&token).await?; let _ = self.read_user_profile_on_server(&token)?;
Ok(UserProfile::from(user)) Ok(UserProfile::from(user))
} }
@ -140,7 +175,7 @@ impl UserSession {
} }
impl UserSession { impl UserSession {
async fn read_user_profile_on_server(&self, token: &str) -> Result<(), UserError> { fn read_user_profile_on_server(&self, token: &str) -> Result<(), UserError> {
let server = self.server.clone(); let server = self.server.clone();
let token = token.to_owned(); let token = token.to_owned();
tokio::spawn(async move { tokio::spawn(async move {
@ -225,6 +260,25 @@ impl UserSession {
Err(_) => false, Err(_) => false,
} }
} }
fn start_ws_connection(&self, token: &str) -> Result<(), UserError> {
let addr = format!("{}/{}", flowy_net::config::WS_ADDR.as_str(), token);
log::debug!("🐴 Try to connect: {}", &addr);
let (conn, handlers) = self.ws.write().make_connect(addr);
tokio::spawn(async {
match conn.await {
Ok(_) => {
log::debug!("🐴 ws connect success");
let _ = handlers.await;
},
Err(e) => {
// TODO: retry?
log::error!("ws connect failed: {}", e);
},
}
});
Ok(())
}
} }
pub async fn update_user(_server: Server, pool: Arc<ConnectionPool>, params: UpdateUserParams) -> Result<(), UserError> { pub async fn update_user(_server: Server, pool: Arc<ConnectionPool>, params: UpdateUserParams) -> Result<(), UserError> {

View File

@ -14,8 +14,6 @@ futures-util = "0.3.17"
futures-channel = "0.3.17" futures-channel = "0.3.17"
tokio = {version = "1", features = ["full"]} tokio = {version = "1", features = ["full"]}
futures = "0.3.17" futures = "0.3.17"
lazy_static = "1.4"
parking_lot = "0.11"
bytes = "0.5" bytes = "0.5"
pin-project = "1.0.0" pin-project = "1.0.0"
futures-core = { version = "0.3", default-features = false } futures-core = { version = "0.3", default-features = false }
@ -27,4 +25,5 @@ strum = "0.21"
strum_macros = "0.21" strum_macros = "0.21"
[dev-dependencies] [dev-dependencies]
tokio = {version = "1", features = ["full"]} tokio = {version = "1", features = ["full"]}
env_logger = "0.8.2"

View File

@ -27,6 +27,7 @@ macro_rules! static_user_error {
} }
impl WsError { impl WsError {
#[allow(dead_code)]
pub(crate) fn new(code: ErrorCode) -> WsError { WsError { code, msg: "".to_string() } } pub(crate) fn new(code: ErrorCode) -> WsError { WsError { code, msg: "".to_string() } }
pub fn context<T: Debug>(mut self, error: T) -> Self { pub fn context<T: Debug>(mut self, error: T) -> Self {

View File

@ -1,9 +1,8 @@
use crate::errors::WsError; use crate::errors::WsError;
use flowy_net::{errors::ServerError, response::FlowyResponse};
use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
use futures_core::{future::BoxFuture, ready, Stream}; use futures_core::{future::BoxFuture, ready, Stream};
use futures_util::{pin_mut, FutureExt, StreamExt}; use futures_util::{pin_mut, FutureExt, StreamExt};
use lazy_static::lazy_static;
use parking_lot::RwLock;
use pin_project::pin_project; use pin_project::pin_project;
use std::{ use std::{
future::Future, future::Future,
@ -14,20 +13,17 @@ use std::{
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tokio_tungstenite::{ use tokio_tungstenite::{
connect_async, connect_async,
tungstenite::{handshake::client::Response, Error, Message}, tungstenite::{handshake::client::Response, http::StatusCode, Error, Message},
MaybeTlsStream, MaybeTlsStream,
WebSocketStream, WebSocketStream,
}; };
lazy_static! {
pub static ref WS: RwLock<WsController> = RwLock::new(WsController::new());
}
pub fn start_ws_connection() { WS.write().connect(flowy_net::config::WS_ADDR.as_ref()); }
pub type MsgReceiver = UnboundedReceiver<Message>; pub type MsgReceiver = UnboundedReceiver<Message>;
pub type MsgSender = UnboundedSender<Message>; pub type MsgSender = UnboundedSender<Message>;
pub trait WsMessageHandler: Sync + Send + 'static { pub trait WsMessageHandler: Sync + Send + 'static {
fn handler_message(&self, msg: &Message); fn can_handle(&self) -> bool;
fn receive_message(&self, msg: &Message);
fn send_message(&self, sender: Arc<WsSender>);
} }
pub struct WsController { pub struct WsController {
@ -47,13 +43,15 @@ impl WsController {
pub fn add_handlers(&mut self, handler: Arc<dyn WsMessageHandler>) { self.handlers.push(handler); } pub fn add_handlers(&mut self, handler: Arc<dyn WsMessageHandler>) { self.handlers.push(handler); }
pub fn connect(&mut self, addr: &str) { #[allow(dead_code)]
let (ws, handlers) = self.make_connect(&addr); pub async fn connect(&mut self, addr: String) -> Result<(), ServerError> {
let _ = tokio::spawn(ws); let (conn, handlers) = self.make_connect(addr);
let _ = conn.await?;
let _ = tokio::spawn(handlers); let _ = tokio::spawn(handlers);
Ok(())
} }
fn make_connect(&mut self, addr: &str) -> (WsRaw, WsHandlers) { pub fn make_connect(&mut self, addr: String) -> (WsConnection, WsHandlers) {
// Stream User // Stream User
// ┌───────────────┐ ┌──────────────┐ // ┌───────────────┐ ┌──────────────┐
// ┌──────┐ │ ┌─────────┐ │ ┌────────┐ │ ┌────────┐ │ // ┌──────┐ │ ┌─────────┐ │ ┌────────┐ │ ┌────────┐ │
@ -64,15 +62,12 @@ impl WsController {
// └─────────┼──│ws_write │◀─┼────│ ws_rx │◀──┼──│ ws_tx │ │ // └─────────┼──│ws_write │◀─┼────│ ws_rx │◀──┼──│ ws_tx │ │
// │ └─────────┘ │ └────────┘ │ └────────┘ │ // │ └─────────┘ │ └────────┘ │ └────────┘ │
// └───────────────┘ └──────────────┘ // └───────────────┘ └──────────────┘
let addr = addr.to_string();
let (msg_tx, msg_rx) = futures_channel::mpsc::unbounded(); let (msg_tx, msg_rx) = futures_channel::mpsc::unbounded();
let (ws_tx, ws_rx) = futures_channel::mpsc::unbounded(); let (ws_tx, ws_rx) = futures_channel::mpsc::unbounded();
let sender = Arc::new(WsSender::new(ws_tx)); let sender = Arc::new(WsSender::new(ws_tx));
let handlers = self.handlers.clone(); let handlers = self.handlers.clone();
self.sender = Some(sender.clone()); self.sender = Some(sender.clone());
log::debug!("🐴ws prepare connection"); (WsConnection::new(msg_tx, ws_rx, addr), WsHandlers::new(handlers, msg_rx))
(WsRaw::new(msg_tx, ws_rx, addr), WsHandlers::new(handlers, msg_rx))
} }
pub fn send_message(&self, msg: Message) -> Result<(), WsError> { pub fn send_message(&self, msg: Message) -> Result<(), WsError> {
@ -84,7 +79,7 @@ impl WsController {
} }
#[pin_project] #[pin_project]
struct WsHandlers { pub struct WsHandlers {
#[pin] #[pin]
msg_rx: MsgReceiver, msg_rx: MsgReceiver,
handlers: Vec<Arc<dyn WsMessageHandler>>, handlers: Vec<Arc<dyn WsMessageHandler>>,
@ -101,7 +96,7 @@ impl Future for WsHandlers {
match ready!(self.as_mut().project().msg_rx.poll_next(cx)) { match ready!(self.as_mut().project().msg_rx.poll_next(cx)) {
None => return Poll::Ready(()), None => return Poll::Ready(()),
Some(message) => self.handlers.iter().for_each(|handler| { Some(message) => self.handlers.iter().for_each(|handler| {
handler.handler_message(&message); handler.receive_message(&message);
}), }),
} }
} }
@ -109,16 +104,16 @@ impl Future for WsHandlers {
} }
#[pin_project] #[pin_project]
pub struct WsRaw { pub struct WsConnection {
msg_tx: Option<MsgSender>, msg_tx: Option<MsgSender>,
ws_rx: Option<MsgReceiver>, ws_rx: Option<MsgReceiver>,
#[pin] #[pin]
fut: BoxFuture<'static, Result<(WebSocketStream<MaybeTlsStream<TcpStream>>, Response), Error>>, fut: BoxFuture<'static, Result<(WebSocketStream<MaybeTlsStream<TcpStream>>, Response), Error>>,
} }
impl WsRaw { impl WsConnection {
pub fn new(msg_tx: MsgSender, ws_rx: MsgReceiver, addr: String) -> Self { pub fn new(msg_tx: MsgSender, ws_rx: MsgReceiver, addr: String) -> Self {
WsRaw { WsConnection {
msg_tx: Some(msg_tx), msg_tx: Some(msg_tx),
ws_rx: Some(ws_rx), ws_rx: Some(ws_rx),
fut: Box::pin(async move { connect_async(&addr).await }), fut: Box::pin(async move { connect_async(&addr).await }),
@ -126,8 +121,8 @@ impl WsRaw {
} }
} }
impl Future for WsRaw { impl Future for WsConnection {
type Output = (); type Output = Result<(), ServerError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// [[pin]] // [[pin]]
// poll async function. The following methods not work. // poll async function. The following methods not work.
@ -147,50 +142,35 @@ impl Future for WsRaw {
loop { loop {
return match ready!(self.as_mut().project().fut.poll(cx)) { return match ready!(self.as_mut().project().fut.poll(cx)) {
Ok((stream, _)) => { Ok((stream, _)) => {
log::debug!("🐴 ws connect success");
let mut ws_stream = WsStream { let mut ws_stream = WsStream {
msg_tx: self.msg_tx.take(), msg_tx: self.msg_tx.take(),
ws_rx: self.ws_rx.take(), ws_rx: self.ws_rx.take(),
stream: Some(stream), stream: Some(stream),
}; };
match Pin::new(&mut ws_stream).poll(cx) { match Pin::new(&mut ws_stream).poll(cx) {
Poll::Ready(_a) => Poll::Ready(()), Poll::Ready(_) => Poll::Ready(Ok(())),
Poll::Pending => Poll::Pending, Poll::Pending => Poll::Pending,
} }
}, },
Err(e) => { Err(error) => Poll::Ready(Err(error_to_flowy_response(error))),
log::error!("🐴 ws connect failed: {:?}", e);
Poll::Ready(())
},
}; };
} }
} }
} }
#[pin_project] fn error_to_flowy_response(error: tokio_tungstenite::tungstenite::Error) -> ServerError {
struct WsConn { let error = match error {
#[pin] Error::Http(response) => {
fut: BoxFuture<'static, Result<(WebSocketStream<MaybeTlsStream<TcpStream>>, Response), Error>>, if response.status() == StatusCode::UNAUTHORIZED {
} ServerError::unauthorized()
} else {
ServerError::internal().context(response)
}
},
_ => ServerError::internal().context(error),
};
impl WsConn { error
fn new(addr: String) -> Self {
Self {
fut: Box::pin(async move { connect_async(&addr).await }),
}
}
}
impl Future for WsConn {
type Output = Result<(WebSocketStream<MaybeTlsStream<TcpStream>>, Response), Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {
return match ready!(self.as_mut().project().fut.poll(cx)) {
Ok(o) => Poll::Ready(Ok(o)),
Err(e) => Poll::Ready(Err(e)),
};
}
}
} }
struct WsStream { struct WsStream {
@ -218,7 +198,7 @@ impl Future for WsStream {
}); });
pin_mut!(to_ws, from_ws); pin_mut!(to_ws, from_ws);
log::debug!("🐴 ws start poll stream"); log::trace!("🐴 ws start poll stream");
match to_ws.poll_unpin(cx) { match to_ws.poll_unpin(cx) {
Poll::Ready(_) => Poll::Ready(()), Poll::Ready(_) => Poll::Ready(()),
Poll::Pending => match from_ws.poll_unpin(cx) { Poll::Pending => match from_ws.poll_unpin(cx) {
@ -245,39 +225,18 @@ impl WsSender {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::WsController; use super::WsController;
use futures_util::{pin_mut, StreamExt};
use tokio_tungstenite::connect_async;
#[tokio::test] #[tokio::test]
async fn connect() { async fn connect() {
std::env::set_var("RUST_LOG", "Debug");
env_logger::init();
let mut controller = WsController::new(); let mut controller = WsController::new();
let addr = format!("{}/123", flowy_net::config::WS_ADDR.as_str()); let addr = format!("{}/123", flowy_net::config::WS_ADDR.as_str());
let (a, b) = controller.make_connect(&addr); let (a, b) = controller.make_connect(addr);
tokio::select! { tokio::select! {
_ = a => println!("write completed"), r = a => println!("write completed {:?}", r),
_ = b => println!("read completed"), _ = b => println!("read completed"),
}; };
} }
#[tokio::test]
async fn connect_raw() {
let _controller = WsController::new();
let addr = format!("{}/123", flowy_net::config::WS_ADDR.as_str());
let (tx, rx) = futures_channel::mpsc::unbounded();
let (ws_write, ws_read) = connect_async(&addr).await.unwrap().0.split();
let to_ws = rx.map(Ok).forward(ws_write);
let from_ws = ws_read.for_each(|message| async {
tx.unbounded_send(message.unwrap()).unwrap();
});
pin_mut!(to_ws, from_ws);
tokio::select! {
_ = to_ws => {
log::debug!("ws write completed")
}
_ = from_ws => {
log::debug!("ws read completed")
}
};
}
} }