refactor backend directory

This commit is contained in:
appflowy 2021-12-21 11:13:38 +08:00
parent b04111e5a3
commit d9421f70a0
93 changed files with 700 additions and 628 deletions

View File

@ -13,14 +13,11 @@ use crate::{
},
context::AppContext,
services::{
app::router as app,
doc::router as doc,
trash::router as trash,
core::{app::router as app, trash::router as trash, view::router as view, workspace::router as workspace},
document::router as doc,
user::router as user,
view::router as view,
workspace::router as workspace,
web_socket::WSServer,
},
web_socket::WsServer,
};
pub struct Application {
@ -58,8 +55,8 @@ pub fn run(listener: TcpListener, app_ctx: AppContext) -> Result<Server, std::io
.service(user_scope())
.app_data(app_ctx.ws_server.clone())
.app_data(app_ctx.pg_pool.clone())
.app_data(app_ctx.ws_bizs.clone())
.app_data(app_ctx.document_core.clone())
.app_data(app_ctx.ws_receivers.clone())
.app_data(app_ctx.document_mng.clone())
})
.listen(listener)?
.run();
@ -73,7 +70,7 @@ async fn period_check(_pool: Data<PgPool>) {
}
}
fn ws_scope() -> Scope { web::scope("/ws").service(crate::web_socket::router::establish_ws_connection) }
fn ws_scope() -> Scope { web::scope("/ws").service(crate::services::web_socket::router::establish_ws_connection) }
fn user_scope() -> Scope {
// https://developer.mozilla.org/en-US/docs/Web/HTTP
@ -112,7 +109,7 @@ fn user_scope() -> Scope {
.route(web::get().to(view::read_handler))
.route(web::patch().to(view::update_handler))
)
.service(web::resource("/doc")
.service(web::resource("/document")
.route(web::post().to(doc::create_handler))
.route(web::get().to(doc::read_handler))
.route(web::patch().to(doc::update_handler))
@ -132,14 +129,14 @@ fn user_scope() -> Scope {
}
pub async fn init_app_context(configuration: &Settings) -> AppContext {
let _ = crate::services::log::Builder::new("flowy-server")
let _ = crate::services::core::log::Builder::new("flowy-server")
.env_filter("Trace")
.build();
let pg_pool = get_connection_pool(&configuration.database)
.await
.unwrap_or_else(|_| panic!("Failed to connect to Postgres at {:?}.", configuration.database));
let ws_server = WsServer::new().start();
let ws_server = WSServer::new().start();
AppContext::new(ws_server, pg_pool)
}

View File

@ -1,6 +1,6 @@
use crate::{
services::doc::manager::DocumentCore,
web_socket::{WsBizHandlers, WsServer},
use crate::services::{
document::manager::DocumentManager,
web_socket::{WSServer, WebSocketReceivers},
};
use actix::Addr;
use actix_web::web::Data;
@ -10,26 +10,26 @@ use std::sync::Arc;
#[derive(Clone)]
pub struct AppContext {
pub ws_server: Data<Addr<WsServer>>,
pub ws_server: Data<Addr<WSServer>>,
pub pg_pool: Data<PgPool>,
pub ws_bizs: Data<WsBizHandlers>,
pub document_core: Data<Arc<DocumentCore>>,
pub ws_receivers: Data<WebSocketReceivers>,
pub document_mng: Data<Arc<DocumentManager>>,
}
impl AppContext {
pub fn new(ws_server: Addr<WsServer>, db_pool: PgPool) -> Self {
pub fn new(ws_server: Addr<WSServer>, db_pool: PgPool) -> Self {
let ws_server = Data::new(ws_server);
let pg_pool = Data::new(db_pool);
let mut ws_bizs = WsBizHandlers::new();
let document_core = Arc::new(DocumentCore::new(pg_pool.clone()));
ws_bizs.register(WSModule::Doc, document_core.clone());
let mut ws_receivers = WebSocketReceivers::new();
let document_mng = Arc::new(DocumentManager::new(pg_pool.clone()));
ws_receivers.set(WSModule::Doc, document_mng.clone());
AppContext {
ws_server,
pg_pool,
ws_bizs: Data::new(ws_bizs),
document_core: Data::new(document_core),
ws_receivers: Data::new(ws_receivers),
document_mng: Data::new(document_mng),
}
}
}

View File

@ -1,4 +1,4 @@
pub mod doc;
pub mod logged_user;
pub mod token;
pub mod user;
pub mod workspace;

View File

@ -1,7 +1,12 @@
use crate::config::env::{domain, jwt_secret};
use backend_service::errors::ServerError;
use crate::{
config::env::{domain, jwt_secret},
entities::logged_user::EXPIRED_DURATION_DAYS,
};
use actix_web::{dev::Payload, FromRequest, HttpRequest};
use backend_service::{configuration::HEADER_TOKEN, errors::ServerError};
use chrono::{Duration, Local};
use derive_more::{From, Into};
use futures::future::{ready, Ready};
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
@ -74,11 +79,6 @@ impl Token {
}
}
use crate::services::user::EXPIRED_DURATION_DAYS;
use actix_web::{dev::Payload, FromRequest, HttpRequest};
use backend_service::configuration::HEADER_TOKEN;
use futures::future::{ready, Ready};
impl FromRequest for Token {
type Error = ServerError;
type Future = Ready<Result<Self, Self::Error>>;

View File

@ -1,120 +0,0 @@
use chrono::Utc;
use flowy_core_data_model::protobuf::{App, RepeatedView, Trash, TrashType, View, ViewType, Workspace};
use protobuf::ProtobufEnum;
pub(crate) const WORKSPACE_TABLE: &str = "workspace_table";
pub(crate) const APP_TABLE: &str = "app_table";
pub(crate) const VIEW_TABLE: &str = "view_table";
pub(crate) const TRASH_TABLE: &str = "trash_table";
#[derive(Debug, Clone, sqlx::FromRow)]
pub struct WorkspaceTable {
pub(crate) id: uuid::Uuid,
pub(crate) name: String,
pub(crate) description: String,
pub(crate) modified_time: chrono::DateTime<Utc>,
pub(crate) create_time: chrono::DateTime<Utc>,
pub(crate) user_id: String,
}
impl std::convert::From<WorkspaceTable> for Workspace {
fn from(table: WorkspaceTable) -> Self {
let mut workspace = Workspace::default();
workspace.set_id(table.id.to_string());
workspace.set_name(table.name.clone());
workspace.set_desc(table.description.clone());
workspace.set_modified_time(table.modified_time.timestamp());
workspace.set_create_time(table.create_time.timestamp());
workspace
}
}
#[derive(Debug, Clone, sqlx::FromRow)]
pub struct AppTable {
pub(crate) id: uuid::Uuid,
pub(crate) workspace_id: String,
pub(crate) name: String,
pub(crate) description: String,
pub(crate) color_style: Vec<u8>,
pub(crate) last_view_id: String,
pub(crate) modified_time: chrono::DateTime<Utc>,
pub(crate) create_time: chrono::DateTime<Utc>,
pub(crate) user_id: String,
}
impl std::convert::From<AppTable> for App {
fn from(table: AppTable) -> Self {
let mut app = App::default();
app.set_id(table.id.to_string());
app.set_workspace_id(table.workspace_id.to_string());
app.set_name(table.name.clone());
app.set_desc(table.description.clone());
app.set_belongings(RepeatedView::default());
app.set_modified_time(table.modified_time.timestamp());
app.set_create_time(table.create_time.timestamp());
app
}
}
impl std::convert::From<AppTable> for Trash {
fn from(table: AppTable) -> Self {
Trash {
id: table.id.to_string(),
name: table.name,
modified_time: table.modified_time.timestamp(),
create_time: table.create_time.timestamp(),
ty: TrashType::App,
unknown_fields: Default::default(),
cached_size: Default::default(),
}
}
}
#[derive(Debug, Clone, sqlx::FromRow)]
pub struct ViewTable {
pub(crate) id: uuid::Uuid,
pub(crate) belong_to_id: String,
pub(crate) name: String,
pub(crate) description: String,
pub(crate) modified_time: chrono::DateTime<Utc>,
pub(crate) create_time: chrono::DateTime<Utc>,
pub(crate) thumbnail: String,
pub(crate) view_type: i32,
}
impl std::convert::From<ViewTable> for View {
fn from(table: ViewTable) -> Self {
let view_type = ViewType::from_i32(table.view_type).unwrap_or(ViewType::Doc);
let mut view = View::default();
view.set_id(table.id.to_string());
view.set_belong_to_id(table.belong_to_id);
view.set_name(table.name);
view.set_desc(table.description);
view.set_view_type(view_type);
view.set_belongings(RepeatedView::default());
view.set_create_time(table.create_time.timestamp());
view.set_modified_time(table.modified_time.timestamp());
view
}
}
impl std::convert::From<ViewTable> for Trash {
fn from(table: ViewTable) -> Self {
Trash {
id: table.id.to_string(),
name: table.name,
modified_time: table.modified_time.timestamp(),
create_time: table.create_time.timestamp(),
ty: TrashType::View,
unknown_fields: Default::default(),
cached_size: Default::default(),
}
}
}
#[derive(Debug, Clone, sqlx::FromRow)]
pub struct TrashTable {
pub(crate) id: uuid::Uuid,
pub(crate) user_id: String,
pub(crate) ty: i32,
}

View File

@ -2,7 +2,6 @@ pub mod application;
pub mod config;
pub mod context;
mod entities;
mod middleware;
pub mod middleware;
pub mod services;
mod sqlx_ext;
pub mod web_socket;
pub mod util;

View File

@ -1,4 +1,3 @@
use crate::services::user::{LoggedUser, AUTHORIZED_USERS};
use actix_service::{Service, Transform};
use actix_web::{
dev::{ServiceRequest, ServiceResponse},
@ -7,7 +6,10 @@ use actix_web::{
ResponseError,
};
use crate::config::IGNORE_ROUTES;
use crate::{
config::IGNORE_ROUTES,
entities::logged_user::{LoggedUser, AUTHORIZED_USERS},
};
use actix_web::{body::AnyBody, dev::MessageBody};
use backend_service::{configuration::HEADER_TOKEN, errors::ServerError};
use futures::future::{ok, LocalBoxFuture, Ready};

View File

@ -1,10 +1,10 @@
use crate::{
entities::workspace::{AppTable, APP_TABLE},
services::{app::sql_builder::*, user::LoggedUser, view::read_view_belong_to_id},
sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder},
};
use crate::services::core::view::read_view_belong_to_id;
use crate::services::trash::read_trash_ids;
use crate::{
entities::logged_user::LoggedUser,
services::core::{app::persistence::*, trash::read_trash_ids},
util::sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder},
};
use backend_service::errors::{invalid_params, ServerError};
use chrono::Utc;
use flowy_core_data_model::{

View File

@ -1,5 +1,5 @@
#![allow(clippy::module_inception)]
pub mod app;
pub mod controller;
pub mod router;
pub mod sql_builder;
pub mod persistence;

View File

@ -1,17 +1,16 @@
use crate::{
entities::workspace::{AppTable, APP_TABLE},
sqlx_ext::SqlBuilder,
};
use crate::util::sqlx_ext::SqlBuilder;
use backend_service::errors::{invalid_params, ServerError};
use chrono::{DateTime, NaiveDateTime, Utc};
use flowy_core_data_model::{
parser::app::AppId,
protobuf::{App, ColorStyle},
protobuf::{App, ColorStyle, RepeatedView},
};
use protobuf::Message;
use sqlx::postgres::PgArguments;
use uuid::Uuid;
pub(crate) const APP_TABLE: &str = "app_table";
pub struct NewAppSqlBuilder {
table: AppTable,
}
@ -111,3 +110,30 @@ pub(crate) fn check_app_id(id: String) -> Result<Uuid, ServerError> {
let app_id = Uuid::parse_str(app_id.as_ref())?;
Ok(app_id)
}
#[derive(Debug, Clone, sqlx::FromRow)]
pub struct AppTable {
pub(crate) id: uuid::Uuid,
pub(crate) workspace_id: String,
pub(crate) name: String,
pub(crate) description: String,
pub(crate) color_style: Vec<u8>,
pub(crate) last_view_id: String,
pub(crate) modified_time: chrono::DateTime<Utc>,
pub(crate) create_time: chrono::DateTime<Utc>,
pub(crate) user_id: String,
}
impl std::convert::From<AppTable> for App {
fn from(table: AppTable) -> Self {
let mut app = App::default();
app.set_id(table.id.to_string());
app.set_workspace_id(table.workspace_id.to_string());
app.set_name(table.name.clone());
app.set_desc(table.description.clone());
app.set_belongings(RepeatedView::default());
app.set_modified_time(table.modified_time.timestamp());
app.set_create_time(table.create_time.timestamp());
app
}
}

View File

@ -1,24 +1,27 @@
use crate::{
entities::logged_user::LoggedUser,
services::core::app::{
controller::{create_app, delete_app, read_app, update_app},
persistence::check_app_id,
},
util::serde_ext::parse_from_payload,
};
use actix_web::{
web::{Data, Payload},
HttpResponse,
};
use backend_service::errors::{invalid_params, ServerError};
use flowy_core_data_model::protobuf::{AppIdentifier, CreateAppParams, UpdateAppParams};
use anyhow::Context;
use backend_service::{
errors::{invalid_params, ServerError},
response::FlowyResponse,
};
use flowy_core_data_model::{
parser::app::{AppDesc, AppName},
protobuf::{AppIdentifier, CreateAppParams, UpdateAppParams},
};
use protobuf::Message;
use sqlx::PgPool;
use crate::services::{
app::{
app::{create_app, delete_app, read_app, update_app},
sql_builder::check_app_id,
},
user::LoggedUser,
util::parse_from_payload,
};
use anyhow::Context;
use backend_service::response::FlowyResponse;
use flowy_core_data_model::parser::app::{AppDesc, AppName};
pub async fn create_handler(
payload: Payload,
pool: Data<PgPool>,

View File

@ -0,0 +1,5 @@
pub mod app;
pub(crate) mod log;
pub mod trash;
pub mod view;
pub mod workspace;

View File

@ -1,4 +1,5 @@
#![allow(clippy::module_inception)]
mod persistence;
pub mod router;
mod trash;

View File

@ -0,0 +1,39 @@
use crate::services::core::{app::persistence::AppTable, view::persistence::ViewTable};
use flowy_core_data_model::protobuf::{Trash, TrashType};
pub(crate) const TRASH_TABLE: &str = "trash_table";
#[derive(Debug, Clone, sqlx::FromRow)]
pub struct TrashTable {
pub(crate) id: uuid::Uuid,
pub(crate) user_id: String,
pub(crate) ty: i32,
}
impl std::convert::From<AppTable> for Trash {
fn from(table: AppTable) -> Self {
Trash {
id: table.id.to_string(),
name: table.name,
modified_time: table.modified_time.timestamp(),
create_time: table.create_time.timestamp(),
ty: TrashType::App,
unknown_fields: Default::default(),
cached_size: Default::default(),
}
}
}
impl std::convert::From<ViewTable> for Trash {
fn from(table: ViewTable) -> Self {
Trash {
id: table.id.to_string(),
name: table.name,
modified_time: table.modified_time.timestamp(),
create_time: table.create_time.timestamp(),
ty: TrashType::View,
unknown_fields: Default::default(),
cached_size: Default::default(),
}
}
}

View File

@ -1,7 +1,7 @@
use crate::services::{
trash::{create_trash, delete_all_trash, delete_trash, read_trash},
user::LoggedUser,
util::parse_from_payload,
use crate::{
entities::logged_user::LoggedUser,
services::core::trash::{create_trash, delete_all_trash, delete_trash, read_trash},
util::serde_ext::parse_from_payload,
};
use ::protobuf::ProtobufEnum;
use actix_web::{

View File

@ -1,11 +1,11 @@
use crate::{
entities::workspace::{TrashTable, TRASH_TABLE},
services::{
app::app::{delete_app, read_app_table},
user::LoggedUser,
entities::logged_user::LoggedUser,
services::core::{
app::controller::{delete_app, read_app_table},
trash::persistence::{TrashTable, TRASH_TABLE},
view::{delete_view, read_view_table},
},
sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder},
util::sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder},
};
use ::protobuf::ProtobufEnum;
use backend_service::errors::ServerError;

View File

@ -1,12 +1,9 @@
use crate::services::document::{create_doc_with_transaction, delete_doc};
use crate::{
entities::workspace::{ViewTable, VIEW_TABLE},
services::{
doc::{create_doc_with_transaction, delete_doc},
trash::read_trash_ids,
user::LoggedUser,
view::sql_builder::*,
},
sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder},
entities::logged_user::LoggedUser,
services::core::{trash::read_trash_ids, view::persistence::*},
util::sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder},
};
use backend_service::errors::{invalid_params, ServerError};
use chrono::Utc;

View File

@ -0,0 +1,6 @@
#![allow(clippy::module_inception)]
mod controller;
pub mod persistence;
pub mod router;
pub(crate) use controller::*;

View File

@ -1,17 +1,16 @@
use crate::{
entities::workspace::{ViewTable, VIEW_TABLE},
sqlx_ext::SqlBuilder,
};
use crate::util::sqlx_ext::SqlBuilder;
use backend_service::errors::{invalid_params, ServerError};
use chrono::{DateTime, NaiveDateTime, Utc};
use flowy_core_data_model::{
parser::view::ViewId,
protobuf::{View, ViewType},
protobuf::{RepeatedView, View, ViewType},
};
use protobuf::ProtobufEnum;
use sqlx::postgres::PgArguments;
use uuid::Uuid;
pub(crate) const VIEW_TABLE: &str = "view_table";
pub struct NewViewSqlBuilder {
table: ViewTable,
}
@ -101,3 +100,32 @@ pub(crate) fn check_view_ids(ids: Vec<String>) -> Result<Vec<Uuid>, ServerError>
}
Ok(view_ids)
}
#[derive(Debug, Clone, sqlx::FromRow)]
pub struct ViewTable {
pub(crate) id: uuid::Uuid,
pub(crate) belong_to_id: String,
pub(crate) name: String,
pub(crate) description: String,
pub(crate) modified_time: chrono::DateTime<Utc>,
pub(crate) create_time: chrono::DateTime<Utc>,
pub(crate) thumbnail: String,
pub(crate) view_type: i32,
}
impl std::convert::From<ViewTable> for View {
fn from(table: ViewTable) -> Self {
let view_type = ViewType::from_i32(table.view_type).unwrap_or(ViewType::Doc);
let mut view = View::default();
view.set_id(table.id.to_string());
view.set_belong_to_id(table.belong_to_id);
view.set_name(table.name);
view.set_desc(table.description);
view.set_view_type(view_type);
view.set_belongings(RepeatedView::default());
view.set_create_time(table.create_time.timestamp());
view.set_modified_time(table.modified_time.timestamp());
view
}
}

View File

@ -1,8 +1,10 @@
use crate::services::{
doc::manager::DocumentCore,
user::LoggedUser,
util::parse_from_payload,
view::{create_view, delete_view, read_view, sql_builder::check_view_ids, update_view},
use crate::{
entities::logged_user::LoggedUser,
services::{
core::view::{create_view, delete_view, persistence::check_view_ids, read_view, update_view},
document::manager::DocumentManager,
},
util::serde_ext::parse_from_payload,
};
use actix_web::{
web::{Data, Payload},
@ -23,7 +25,7 @@ use std::sync::Arc;
pub async fn create_handler(
payload: Payload,
pool: Data<PgPool>,
_doc_biz: Data<Arc<DocumentCore>>,
_doc_biz: Data<Arc<DocumentManager>>,
) -> Result<HttpResponse, ServerError> {
let params: CreateViewParams = parse_from_payload(payload).await?;
let mut transaction = pool

View File

@ -1,8 +1,11 @@
use super::sql_builder::NewWorkspaceBuilder;
use super::persistence::NewWorkspaceBuilder;
use crate::{
entities::workspace::{AppTable, WorkspaceTable, WORKSPACE_TABLE},
services::{app::app::read_app, user::LoggedUser, workspace::sql_builder::*},
sqlx_ext::*,
entities::logged_user::LoggedUser,
services::core::{
app::{controller::read_app, persistence::AppTable},
workspace::persistence::*,
},
util::sqlx_ext::*,
};
use anyhow::Context;
use backend_service::errors::{invalid_params, ServerError};

View File

@ -0,0 +1,6 @@
#![allow(clippy::module_inception)]
mod controller;
pub mod persistence;
pub mod router;
pub use controller::*;

View File

@ -1,7 +1,4 @@
use crate::{
entities::workspace::{WorkspaceTable, WORKSPACE_TABLE},
sqlx_ext::SqlBuilder,
};
use crate::util::sqlx_ext::SqlBuilder;
use backend_service::errors::{invalid_params, ServerError};
use chrono::{DateTime, NaiveDateTime, Utc};
use flowy_core_data_model::{parser::workspace::WorkspaceId, protobuf::Workspace};
@ -76,3 +73,26 @@ pub(crate) fn check_workspace_id(id: String) -> Result<Uuid, ServerError> {
let workspace_id = Uuid::parse_str(workspace_id.as_ref())?;
Ok(workspace_id)
}
pub(crate) const WORKSPACE_TABLE: &str = "workspace_table";
#[derive(Debug, Clone, sqlx::FromRow)]
pub struct WorkspaceTable {
pub(crate) id: uuid::Uuid,
pub(crate) name: String,
pub(crate) description: String,
pub(crate) modified_time: chrono::DateTime<Utc>,
pub(crate) create_time: chrono::DateTime<Utc>,
pub(crate) user_id: String,
}
impl std::convert::From<WorkspaceTable> for Workspace {
fn from(table: WorkspaceTable) -> Self {
let mut workspace = Workspace::default();
workspace.set_id(table.id.to_string());
workspace.set_name(table.name.clone());
workspace.set_desc(table.description.clone());
workspace.set_modified_time(table.modified_time.timestamp());
workspace.set_create_time(table.create_time.timestamp());
workspace
}
}

View File

@ -1,13 +1,13 @@
use crate::services::{
user::LoggedUser,
util::parse_from_payload,
workspace::{
use crate::{
entities::logged_user::LoggedUser,
services::core::workspace::{
create_workspace,
delete_workspace,
persistence::check_workspace_id,
read_workspaces,
sql_builder::check_workspace_id,
update_workspace,
},
util::serde_ext::parse_from_payload,
};
use actix_web::{
web::{Data, Payload},

View File

@ -1,65 +0,0 @@
use crate::{
services::doc::update_doc,
web_socket::{entities::Socket, WsMessageAdaptor, WsUser},
};
use actix_web::web::Data;
use backend_service::errors::internal_error;
use flowy_collaboration::{
core::sync::{RevisionUser, SyncResponse},
protobuf::UpdateDocParams,
};
use sqlx::PgPool;
use std::sync::Arc;
#[derive(Clone, Debug)]
pub struct ServerDocUser {
pub user: Arc<WsUser>,
pub(crate) socket: Socket,
pub pg_pool: Data<PgPool>,
}
impl RevisionUser for ServerDocUser {
fn user_id(&self) -> String { self.user.id().to_string() }
fn receive(&self, resp: SyncResponse) {
let result = match resp {
SyncResponse::Pull(data) => {
let msg: WsMessageAdaptor = data.into();
self.socket.try_send(msg).map_err(internal_error)
},
SyncResponse::Push(data) => {
let msg: WsMessageAdaptor = data.into();
self.socket.try_send(msg).map_err(internal_error)
},
SyncResponse::Ack(data) => {
let msg: WsMessageAdaptor = data.into();
self.socket.try_send(msg).map_err(internal_error)
},
SyncResponse::NewRevision {
rev_id,
doc_id,
doc_json,
} => {
let pg_pool = self.pg_pool.clone();
tokio::task::spawn(async move {
let mut params = UpdateDocParams::new();
params.set_doc_id(doc_id);
params.set_data(doc_json);
params.set_rev_id(rev_id);
match update_doc(pg_pool.get_ref(), params).await {
Ok(_) => {},
Err(e) => log::error!("{}", e),
}
});
Ok(())
},
};
match result {
Ok(_) => {},
Err(e) => log::error!("[ServerDocUser]: {}", e),
}
}
}

View File

@ -1,6 +1,6 @@
use crate::{
entities::doc::{DocTable, DOC_TABLE},
sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder},
util::sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder},
};
use anyhow::Context;
use backend_service::errors::ServerError;
@ -27,14 +27,14 @@ pub(crate) async fn create_doc(pool: &PgPool, params: CreateDocParams) -> Result
let mut transaction = pool
.begin()
.await
.context("Failed to acquire a Postgres connection to create doc")?;
.context("Failed to acquire a Postgres connection to create document")?;
let _ = create_doc_with_transaction(&mut transaction, params).await?;
transaction
.commit()
.await
.context("Failed to commit SQL transaction to create doc.")?;
.context("Failed to commit SQL transaction to create document.")?;
Ok(())
}
@ -45,7 +45,7 @@ pub(crate) async fn read_doc(pool: &PgPool, params: DocIdentifier) -> Result<Doc
let mut transaction = pool
.begin()
.await
.context("Failed to acquire a Postgres connection to read doc")?;
.context("Failed to acquire a Postgres connection to read document")?;
let builder = SqlBuilder::select(DOC_TABLE).add_field("*").and_where_eq("id", &doc_id);
@ -60,7 +60,7 @@ pub(crate) async fn read_doc(pool: &PgPool, params: DocIdentifier) -> Result<Doc
transaction
.commit()
.await
.context("Failed to commit SQL transaction to read doc.")?;
.context("Failed to commit SQL transaction to read document.")?;
Ok(doc)
}
@ -71,7 +71,7 @@ pub async fn update_doc(pool: &PgPool, mut params: UpdateDocParams) -> Result<()
let mut transaction = pool
.begin()
.await
.context("Failed to acquire a Postgres connection to update doc")?;
.context("Failed to acquire a Postgres connection to update document")?;
let data = Some(params.take_data());
@ -91,7 +91,7 @@ pub async fn update_doc(pool: &PgPool, mut params: UpdateDocParams) -> Result<()
transaction
.commit()
.await
.context("Failed to commit SQL transaction to update doc.")?;
.context("Failed to commit SQL transaction to update document.")?;
Ok(())
}
@ -99,7 +99,6 @@ pub async fn update_doc(pool: &PgPool, mut params: UpdateDocParams) -> Result<()
#[tracing::instrument(level = "debug", skip(transaction), err)]
pub(crate) async fn delete_doc(transaction: &mut DBTransaction<'_>, doc_id: Uuid) -> Result<(), ServerError> {
let (sql, args) = SqlBuilder::delete(DOC_TABLE).and_where_eq("id", doc_id).build()?;
let _ = sqlx::query_with(&sql, args)
.execute(transaction)
.await

View File

@ -1,14 +1,13 @@
use crate::{
services::doc::{
use crate::services::{
document::{
create_doc,
read_doc,
update_doc,
ws_actor::{DocWsActor, DocWsMsg},
ws_actor::{DocumentWebSocketActor, WSActorMessage},
},
web_socket::{WsBizHandler, WsClientData},
web_socket::{WSClientData, WebSocketReceiver},
};
use actix_web::web::Data;
use crate::services::doc::create_doc;
use backend_service::errors::ServerError;
use flowy_collaboration::{
core::sync::{ServerDocManager, ServerDocPersistence},
@ -22,34 +21,29 @@ use sqlx::PgPool;
use std::{convert::TryInto, sync::Arc};
use tokio::sync::{mpsc, oneshot};
pub struct DocumentCore {
pub manager: Arc<ServerDocManager>,
ws_sender: mpsc::Sender<DocWsMsg>,
pub struct DocumentManager {
ws_sender: mpsc::Sender<WSActorMessage>,
pg_pool: Data<PgPool>,
}
impl DocumentCore {
impl DocumentManager {
pub fn new(pg_pool: Data<PgPool>) -> Self {
let manager = Arc::new(ServerDocManager::new(Arc::new(DocPersistenceImpl(pg_pool.clone()))));
let inner = Arc::new(ServerDocManager::new(Arc::new(DocPersistenceImpl(pg_pool.clone()))));
let (ws_sender, rx) = mpsc::channel(100);
let actor = DocWsActor::new(rx, manager.clone());
let actor = DocumentWebSocketActor::new(rx, inner);
tokio::task::spawn(actor.run());
Self {
manager,
ws_sender,
pg_pool,
}
Self { ws_sender, pg_pool }
}
}
impl WsBizHandler for DocumentCore {
fn receive(&self, data: WsClientData) {
impl WebSocketReceiver for DocumentManager {
fn receive(&self, data: WSClientData) {
let (ret, rx) = oneshot::channel();
let sender = self.ws_sender.clone();
let pool = self.pg_pool.clone();
actix_rt::spawn(async move {
let msg = DocWsMsg::ClientData {
let msg = WSActorMessage::ClientData {
client_data: data,
ret,
pool,

View File

@ -4,7 +4,6 @@ pub(crate) use crud::*;
pub use router::*;
pub mod crud;
mod editor;
pub mod manager;
pub mod router;
mod ws_actor;

View File

@ -1,12 +1,10 @@
use crate::services::{
doc::{create_doc, read_doc, update_doc},
util::parse_from_payload,
};
use crate::services::document::{create_doc, read_doc, update_doc};
use actix_web::{
web::{Data, Payload},
HttpResponse,
};
use crate::util::serde_ext::parse_from_payload;
use backend_service::{errors::ServerError, response::FlowyResponse};
use flowy_collaboration::protobuf::{CreateDocParams, DocIdentifier, UpdateDocParams};
use sqlx::PgPool;

View File

@ -1,9 +1,9 @@
use crate::{
services::{
doc::editor::ServerDocUser,
util::{md5, parse_from_bytes},
document::update_doc,
web_socket::{entities::Socket, WSClientData, WSMessageAdaptor, WSUser},
},
web_socket::WsClientData,
util::serde_ext::{md5, parse_from_bytes},
};
use actix_rt::task::spawn_blocking;
use actix_web::web::Data;
@ -11,32 +11,29 @@ use async_stream::stream;
use backend_service::errors::{internal_error, Result, ServerError};
use flowy_collaboration::{
core::sync::{RevisionUser, ServerDocManager, SyncResponse},
entities::ws::DocumentWSDataBuilder,
protobuf::{DocumentWSData, DocumentWSDataType},
protobuf::{DocumentWSData, DocumentWSDataType, NewDocumentUser, UpdateDocParams},
};
use flowy_collaboration::protobuf::NewDocumentUser;
use futures::stream::StreamExt;
use lib_ot::protobuf::Revision;
use sqlx::PgPool;
use std::{convert::TryInto, sync::Arc};
use tokio::sync::{mpsc, oneshot};
pub enum DocWsMsg {
pub enum WSActorMessage {
ClientData {
client_data: WsClientData,
client_data: WSClientData,
pool: Data<PgPool>,
ret: oneshot::Sender<Result<()>>,
},
}
pub struct DocWsActor {
receiver: Option<mpsc::Receiver<DocWsMsg>>,
pub struct DocumentWebSocketActor {
receiver: Option<mpsc::Receiver<WSActorMessage>>,
doc_manager: Arc<ServerDocManager>,
}
impl DocWsActor {
pub fn new(receiver: mpsc::Receiver<DocWsMsg>, manager: Arc<ServerDocManager>) -> Self {
impl DocumentWebSocketActor {
pub fn new(receiver: mpsc::Receiver<WSActorMessage>, manager: Arc<ServerDocManager>) -> Self {
Self {
receiver: Some(receiver),
doc_manager: manager,
@ -61,16 +58,16 @@ impl DocWsActor {
stream.for_each(|msg| self.handle_message(msg)).await;
}
async fn handle_message(&self, msg: DocWsMsg) {
async fn handle_message(&self, msg: WSActorMessage) {
match msg {
DocWsMsg::ClientData { client_data, pool, ret } => {
WSActorMessage::ClientData { client_data, pool, ret } => {
let _ = ret.send(self.handle_client_data(client_data, pool).await);
},
}
}
async fn handle_client_data(&self, client_data: WsClientData, pg_pool: Data<PgPool>) -> Result<()> {
let WsClientData { user, socket, data } = client_data;
async fn handle_client_data(&self, client_data: WSClientData, pg_pool: Data<PgPool>) -> Result<()> {
let WSClientData { user, socket, data } = client_data;
let document_data = spawn_blocking(move || {
let document_data: DocumentWSData = parse_from_bytes(&data)?;
Result::Ok(document_data)
@ -127,7 +124,7 @@ impl DocWsActor {
async fn handle_revision(&self, user: Arc<ServerDocUser>, mut revision: Revision) -> Result<()> {
let revision: lib_ot::revision::Revision = (&mut revision).try_into().map_err(internal_error)?;
// Create the doc if it doesn't exist
// Create the document if it doesn't exist
let handler = match self.doc_manager.get(&revision.doc_id).await {
None => self
.doc_manager
@ -149,3 +146,54 @@ fn verify_md5(revision: &Revision) -> Result<()> {
}
Ok(())
}
#[derive(Clone, Debug)]
pub struct ServerDocUser {
pub user: Arc<WSUser>,
pub(crate) socket: Socket,
pub pg_pool: Data<PgPool>,
}
impl RevisionUser for ServerDocUser {
fn user_id(&self) -> String { self.user.id().to_string() }
fn receive(&self, resp: SyncResponse) {
let result = match resp {
SyncResponse::Pull(data) => {
let msg: WSMessageAdaptor = data.into();
self.socket.try_send(msg).map_err(internal_error)
},
SyncResponse::Push(data) => {
let msg: WSMessageAdaptor = data.into();
self.socket.try_send(msg).map_err(internal_error)
},
SyncResponse::Ack(data) => {
let msg: WSMessageAdaptor = data.into();
self.socket.try_send(msg).map_err(internal_error)
},
SyncResponse::NewRevision {
rev_id,
doc_id,
doc_json,
} => {
let pg_pool = self.pg_pool.clone();
tokio::task::spawn(async move {
let mut params = UpdateDocParams::new();
params.set_doc_id(doc_id);
params.set_data(doc_json);
params.set_rev_id(rev_id);
match update_doc(pg_pool.get_ref(), params).await {
Ok(_) => {},
Err(e) => log::error!("{}", e),
}
});
Ok(())
},
};
match result {
Ok(_) => {},
Err(e) => log::error!("[ServerDocUser]: {}", e),
}
}
}

View File

@ -1,8 +1,4 @@
pub mod app;
pub mod doc;
pub(crate) mod log;
pub mod trash;
pub mod core;
pub mod document;
pub mod user;
pub(crate) mod util;
pub mod view;
pub mod workspace;
pub mod web_socket;

View File

@ -1,7 +1,13 @@
use crate::{
entities::{token::Token, user::UserTable},
services::user::{hash_password, verify_password, LoggedUser},
sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder},
entities::{
logged_user::{LoggedUser, AUTHORIZED_USERS},
token::Token,
user::UserTable,
},
util::{
sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder},
user_ext::{hash_password, verify_password},
},
};
use anyhow::Context;
use backend_service::{
@ -15,8 +21,6 @@ use flowy_user_data_model::{
};
use sqlx::{PgPool, Postgres};
use super::AUTHORIZED_USERS;
pub async fn sign_in(pool: &PgPool, params: SignInParams) -> Result<SignInResponse, ServerError> {
let email = UserEmail::parse(params.email).map_err(|e| ServerError::params_invalid().context(e))?;
let password = UserPassword::parse(params.password).map_err(|e| ServerError::params_invalid().context(e))?;

View File

@ -1,9 +1,4 @@
pub use auth::*;
pub use logged_user::*;
pub use utils::*;
pub use controller::*;
mod auth;
mod logged_user;
mod controller;
pub mod router;
pub mod user_default;
mod utils;

View File

@ -1,21 +1,17 @@
use crate::{
entities::{logged_user::LoggedUser, token::Token},
services::user::{get_user_profile, register_user, set_user_profile, sign_in, sign_out},
util::serde_ext::parse_from_payload,
};
use actix_identity::Identity;
use actix_web::{
web::{Data, Payload},
HttpRequest,
HttpResponse,
};
use sqlx::PgPool;
use backend_service::{errors::ServerError, response::FlowyResponse};
use flowy_user_data_model::protobuf::{SignInParams, SignUpParams, UpdateUserParams};
use crate::{
entities::token::Token,
services::{
user::{get_user_profile, register_user, set_user_profile, sign_in, sign_out, LoggedUser},
util::parse_from_payload,
},
};
use sqlx::PgPool;
pub async fn sign_in_handler(payload: Payload, id: Identity, pool: Data<PgPool>) -> Result<HttpResponse, ServerError> {
let params: SignInParams = parse_from_payload(payload).await?;

View File

@ -1,49 +0,0 @@
use crate::{
services::{
app::sql_builder::NewAppSqlBuilder as AppBuilder,
workspace::sql_builder::NewWorkspaceBuilder as WorkspaceBuilder,
},
sqlx_ext::{map_sqlx_error, DBTransaction},
};
use crate::services::view::{create_view_with_args, sql_builder::NewViewSqlBuilder};
use backend_service::errors::ServerError;
use chrono::Utc;
use flowy_collaboration::core::document::default::initial_string;
use flowy_core_data_model::protobuf::Workspace;
use std::convert::TryInto;
#[allow(dead_code)]
pub async fn create_default_workspace(
transaction: &mut DBTransaction<'_>,
user_id: &str,
) -> Result<Workspace, ServerError> {
let time = Utc::now();
let workspace: Workspace = flowy_core_data_model::user_default::create_default_workspace(time)
.try_into()
.unwrap();
let mut cloned_workspace = workspace.clone();
let mut apps = cloned_workspace.take_apps();
let (sql, args, _) = WorkspaceBuilder::from_workspace(user_id, cloned_workspace)?.build()?;
let _ = sqlx::query_with(&sql, args)
.execute(transaction as &mut DBTransaction<'_>)
.await
.map_err(map_sqlx_error)?;
for mut app in apps.take_items() {
let mut views = app.take_belongings();
let (sql, args, _) = AppBuilder::from_app(user_id, app)?.build()?;
let _ = sqlx::query_with(&sql, args)
.execute(transaction as &mut DBTransaction<'_>)
.await
.map_err(map_sqlx_error)?;
for view in views.take_items() {
let (sql, args, view) = NewViewSqlBuilder::from_view(view)?.build()?;
let _ = create_view_with_args(transaction, sql, args, view, initial_string()).await?;
}
}
Ok(workspace)
}

View File

@ -1,6 +0,0 @@
#![allow(clippy::module_inception)]
pub mod router;
pub mod sql_builder;
mod view;
pub(crate) use view::*;

View File

@ -1,10 +1,10 @@
use crate::web_socket::WsMessageAdaptor;
use crate::services::web_socket::WSMessageAdaptor;
use actix::{Message, Recipient};
use backend_service::errors::ServerError;
use serde::{Deserialize, Serialize};
use std::fmt::Formatter;
pub type Socket = Recipient<WsMessageAdaptor>;
pub type Socket = Recipient<WSMessageAdaptor>;
#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)]
pub struct SessionId(pub String);

View File

@ -6,15 +6,15 @@ use std::convert::TryInto;
#[derive(Debug, Message, Clone)]
#[rtype(result = "()")]
pub struct WsMessageAdaptor(pub Bytes);
pub struct WSMessageAdaptor(pub Bytes);
impl std::ops::Deref for WsMessageAdaptor {
impl std::ops::Deref for WSMessageAdaptor {
type Target = Bytes;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl std::convert::From<DocumentWSData> for WsMessageAdaptor {
impl std::convert::From<DocumentWSData> for WSMessageAdaptor {
fn from(data: DocumentWSData) -> Self {
let bytes: Bytes = data.try_into().unwrap();
let msg = WSMessage {
@ -23,6 +23,6 @@ impl std::convert::From<DocumentWSData> for WsMessageAdaptor {
};
let bytes: Bytes = msg.try_into().unwrap();
WsMessageAdaptor(bytes)
WSMessageAdaptor(bytes)
}
}

View File

@ -1,9 +1,7 @@
pub use biz_handler::*;
pub use entities::message::*;
pub use ws_client::*;
pub use ws_server::*;
mod biz_handler;
pub(crate) mod entities;
pub mod router;
mod ws_client;

View File

@ -1,6 +1,6 @@
use crate::{
services::user::LoggedUser,
web_socket::{WsBizHandlers, WsClient, WsServer, WsUser},
entities::logged_user::LoggedUser,
services::web_socket::{WSClient, WSServer, WSUser, WebSocketReceivers},
};
use actix::Addr;
use actix_web::{
@ -20,29 +20,29 @@ use actix_web_actors::ws;
// │ └────────┘ │ │
// └─────────────┘ │
// │
// │ ┌───────────────┐ ┌─────────────┐ ┌────────────────┐
// ├───▶│ WsBizHandlers │──▶│WsBizHandler │───▶│ WsClientData
// │ └───────────────┘ └─────────────┘ └────────────────┘
// WsClient │
// ┌─────────────┐ │
// │ ┌────────┐ │ │
// wss://xxx ─────▶│ │ WsUser │ │───┘ ┌───────────────┐
// │ └────────┘ │ │ DocumentCore │
// └─────────────┘ └───────────────┘
// │ ┌──────────────────┐ 1 n ┌──────────────────┐
// ├───▶│WebSocketReceivers│◆────│WebSocketReceiver
// │ └──────────────────┘ ──────────────────┘
// WsClient │
// ┌─────────────┐ │
// │ ┌────────┐ │ │
// wss://xxx ─────▶│ │ WsUser │ │───┘
// │ └────────┘ │ ┌───────────────┐
// └─────────────┘ │DocumentManager│
// └───────────────┘
#[get("/{token}")]
pub async fn establish_ws_connection(
request: HttpRequest,
payload: Payload,
token: Path<String>,
server: Data<Addr<WsServer>>,
biz_handlers: Data<WsBizHandlers>,
server: Data<Addr<WSServer>>,
ws_receivers: Data<WebSocketReceivers>,
) -> Result<HttpResponse, Error> {
tracing::info!("establish_ws_connection");
match LoggedUser::from_token(token.clone()) {
Ok(user) => {
let ws_user = WsUser::new(user);
let client = WsClient::new(ws_user, server.get_ref().clone(), biz_handlers);
let ws_user = WSUser::new(user);
let client = WSClient::new(ws_user, server.get_ref().clone(), ws_receivers);
let result = ws::start(client, &request, payload);
match result {
Ok(response) => Ok(response),

View File

@ -1,50 +1,69 @@
use crate::{
config::{HEARTBEAT_INTERVAL, PING_TIMEOUT},
services::user::LoggedUser,
web_socket::{
entities::logged_user::LoggedUser,
services::web_socket::{
entities::{Connect, Disconnect, Socket},
WsBizHandlers,
WsMessageAdaptor,
WsServer,
WSMessageAdaptor,
WSServer,
},
};
use actix::*;
use actix_web::web::Data;
use actix_web_actors::{ws, ws::Message::Text};
use bytes::Bytes;
use lib_ws::WSMessage;
use std::{convert::TryFrom, sync::Arc, time::Instant};
use lib_ws::{WSMessage, WSModule};
use std::{collections::HashMap, convert::TryFrom, sync::Arc, time::Instant};
pub trait WebSocketReceiver: Send + Sync {
fn receive(&self, data: WSClientData);
}
pub struct WebSocketReceivers {
inner: HashMap<WSModule, Arc<dyn WebSocketReceiver>>,
}
impl std::default::Default for WebSocketReceivers {
fn default() -> Self { Self { inner: HashMap::new() } }
}
impl WebSocketReceivers {
pub fn new() -> Self { WebSocketReceivers::default() }
pub fn set(&mut self, source: WSModule, handler: Arc<dyn WebSocketReceiver>) { self.inner.insert(source, handler); }
pub fn get(&self, source: &WSModule) -> Option<Arc<dyn WebSocketReceiver>> { self.inner.get(source).cloned() }
}
#[derive(Debug)]
pub struct WsUser {
pub struct WSUser {
inner: LoggedUser,
}
impl WsUser {
impl WSUser {
pub fn new(inner: LoggedUser) -> Self { Self { inner } }
pub fn id(&self) -> &str { &self.inner.user_id }
}
pub struct WsClientData {
pub(crate) user: Arc<WsUser>,
pub struct WSClientData {
pub(crate) user: Arc<WSUser>,
pub(crate) socket: Socket,
pub(crate) data: Bytes,
}
pub struct WsClient {
user: Arc<WsUser>,
server: Addr<WsServer>,
biz_handlers: Data<WsBizHandlers>,
pub struct WSClient {
user: Arc<WSUser>,
server: Addr<WSServer>,
ws_receivers: Data<WebSocketReceivers>,
hb: Instant,
}
impl WsClient {
pub fn new(user: WsUser, server: Addr<WsServer>, biz_handlers: Data<WsBizHandlers>) -> Self {
impl WSClient {
pub fn new(user: WSUser, server: Addr<WSServer>, ws_receivers: Data<WebSocketReceivers>) -> Self {
Self {
user: Arc::new(user),
server,
biz_handlers,
ws_receivers,
hb: Instant::now(),
}
}
@ -65,12 +84,12 @@ impl WsClient {
fn handle_binary_message(&self, bytes: Bytes, socket: Socket) {
// TODO: ok to unwrap?
let message: WSMessage = WSMessage::try_from(bytes).unwrap();
match self.biz_handlers.get(&message.module) {
match self.ws_receivers.get(&message.module) {
None => {
log::error!("Can't find the handler for {:?}", message.module);
log::error!("Can't find the receiver for {:?}", message.module);
},
Some(handler) => {
let client_data = WsClientData {
let client_data = WSClientData {
user: self.user.clone(),
socket,
data: Bytes::from(message.data),
@ -81,7 +100,7 @@ impl WsClient {
}
}
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WsClient {
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WSClient {
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
match msg {
Ok(ws::Message::Ping(msg)) => {
@ -113,13 +132,13 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WsClient {
}
}
impl Handler<WsMessageAdaptor> for WsClient {
impl Handler<WSMessageAdaptor> for WSClient {
type Result = ();
fn handle(&mut self, msg: WsMessageAdaptor, ctx: &mut Self::Context) { ctx.binary(msg.0); }
fn handle(&mut self, msg: WSMessageAdaptor, ctx: &mut Self::Context) { ctx.binary(msg.0); }
}
impl Actor for WsClient {
impl Actor for WSClient {
type Context = ws::WebsocketContext<Self>;
fn started(&mut self, ctx: &mut Self::Context) {

View File

@ -1,34 +1,34 @@
use crate::web_socket::{
use crate::services::web_socket::{
entities::{Connect, Disconnect, Session, SessionId},
WsMessageAdaptor,
WSMessageAdaptor,
};
use actix::{Actor, Context, Handler};
use backend_service::errors::ServerError;
use dashmap::DashMap;
pub struct WsServer {
pub struct WSServer {
sessions: DashMap<SessionId, Session>,
}
impl std::default::Default for WsServer {
impl std::default::Default for WSServer {
fn default() -> Self {
Self {
sessions: DashMap::new(),
}
}
}
impl WsServer {
pub fn new() -> Self { WsServer::default() }
impl WSServer {
pub fn new() -> Self { WSServer::default() }
pub fn send(&self, _msg: WsMessageAdaptor) { unimplemented!() }
pub fn send(&self, _msg: WSMessageAdaptor) { unimplemented!() }
}
impl Actor for WsServer {
impl Actor for WSServer {
type Context = Context<Self>;
fn started(&mut self, _ctx: &mut Self::Context) {}
}
impl Handler<Connect> for WsServer {
impl Handler<Connect> for WSServer {
type Result = Result<(), ServerError>;
fn handle(&mut self, msg: Connect, _ctx: &mut Context<Self>) -> Self::Result {
let session: Session = msg.into();
@ -38,7 +38,7 @@ impl Handler<Connect> for WsServer {
}
}
impl Handler<Disconnect> for WsServer {
impl Handler<Disconnect> for WSServer {
type Result = Result<(), ServerError>;
fn handle(&mut self, msg: Disconnect, _: &mut Context<Self>) -> Self::Result {
self.sessions.remove(&msg.sid);
@ -46,14 +46,14 @@ impl Handler<Disconnect> for WsServer {
}
}
impl Handler<WsMessageAdaptor> for WsServer {
impl Handler<WSMessageAdaptor> for WSServer {
type Result = ();
fn handle(&mut self, _msg: WsMessageAdaptor, _ctx: &mut Context<Self>) -> Self::Result { unimplemented!() }
fn handle(&mut self, _msg: WSMessageAdaptor, _ctx: &mut Context<Self>) -> Self::Result { unimplemented!() }
}
impl actix::Supervised for WsServer {
fn restarting(&mut self, _ctx: &mut Context<WsServer>) {
impl actix::Supervised for WSServer {
fn restarting(&mut self, _ctx: &mut Context<WSServer>) {
log::warn!("restarting");
}
}

View File

@ -1,6 +0,0 @@
#![allow(clippy::module_inception)]
pub mod router;
pub mod sql_builder;
mod workspace;
pub use workspace::*;

3
backend/src/util/mod.rs Normal file
View File

@ -0,0 +1,3 @@
pub mod serde_ext;
pub mod sqlx_ext;
pub mod user_ext;

View File

@ -1,24 +0,0 @@
use crate::web_socket::WsClientData;
use lib_ws::WSModule;
use std::{collections::HashMap, sync::Arc};
pub trait WsBizHandler: Send + Sync {
fn receive(&self, data: WsClientData);
}
pub type BizHandler = Arc<dyn WsBizHandler>;
pub struct WsBizHandlers {
inner: HashMap<WSModule, BizHandler>,
}
impl std::default::Default for WsBizHandlers {
fn default() -> Self { Self { inner: HashMap::new() } }
}
impl WsBizHandlers {
pub fn new() -> Self { WsBizHandlers::default() }
pub fn register(&mut self, source: WSModule, handler: BizHandler) { self.inner.insert(source, handler); }
pub fn get(&self, source: &WSModule) -> Option<BizHandler> { self.inner.get(source).cloned() }
}

View File

@ -70,7 +70,7 @@ async fn delta_sync_while_editing_with_attribute() {
// │ops: ["123", "456"] rev: 2│ │ │
// └──────────────────────────┘ │ │
// │ │
// ◀── http request ─┤ Open doc
// ◀── http request ─┤ Open document
// │ │
// │ │ ┌──────────────────────────┐
// ├──http response──┼─▶│ops: ["123", "456"] rev: 2│
@ -115,7 +115,7 @@ async fn delta_sync_with_server_push_delta() {
// └─────────┘ └─────────┘
// │ │
// │ │
// ◀── http request ─┤ Open doc
// ◀── http request ─┤ Open document
// │ │
// │ │ ┌───────────────┐
// ├──http response──┼─▶│ops: [] rev: 0 │
@ -165,7 +165,7 @@ async fn delta_sync_while_local_rev_less_than_server_rev() {
// ┌───────────────────┐ │ │
// │ops: ["123"] rev: 1│ │ │
// └───────────────────┘ │ │
// ◀── http request ─┤ Open doc
// ◀── http request ─┤ Open document
// │ │
// │ │ ┌───────────────┐
// ├──http response──┼──▶│ops: [123] rev:│

View File

@ -150,7 +150,7 @@ impl TestUserServer {
}
pub async fn read_doc(&self, params: DocIdentifier) -> Option<Doc> {
let url = format!("{}/api/doc", self.http_addr());
let url = format!("{}/api/document", self.http_addr());
let doc = read_doc_request(self.user_token(), params, &url).await.unwrap();
doc
}

View File

@ -0,0 +1,4 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod model;
pub use model::*;

View File

@ -0,0 +1,8 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod ffi_response;
pub use ffi_response::*;
mod ffi_request;
pub use ffi_request::*;

View File

@ -0,0 +1,4 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod model;
pub use model::*;

View File

@ -0,0 +1,5 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod subject;
pub use subject::*;

View File

@ -0,0 +1,4 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod model;
pub use model::*;

View File

@ -0,0 +1,8 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod observable;
pub use observable::*;
mod event;
pub use event::*;

View File

@ -0,0 +1,4 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod model;
pub use model::*;

View File

@ -0,0 +1,5 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod observable;
pub use observable::*;

View File

@ -56,7 +56,7 @@ impl DocController {
}
pub(crate) fn close(&self, doc_id: &str) -> Result<(), FlowyError> {
tracing::debug!("Close doc {}", doc_id);
tracing::debug!("Close document {}", doc_id);
self.open_cache.remove(doc_id);
self.ws_handlers.remove_handler(doc_id);
Ok(())
@ -114,7 +114,7 @@ impl DocController {
fn make_rev_manager(&self, doc_id: &str, pool: Arc<ConnectionPool>) -> Result<RevisionManager, FlowyError> {
// Opti: require upgradable_read lock and then upgrade to write lock using
// RwLockUpgradableReadGuard::upgrade(xx) of ws
// let doc = self.read_doc(doc_id, pool.clone()).await?;
// let document = self.read_doc(doc_id, pool.clone()).await?;
let user_id = self.user.user_id()?;
let cache = Arc::new(RevisionCache::new(&user_id, doc_id, pool));
Ok(RevisionManager::new(&user_id, doc_id, cache))

View File

@ -20,7 +20,7 @@ use flowy_collaboration::{
use flowy_error::{internal_error, FlowyError, FlowyResult};
use lib_infra::future::FutureResult;
use lib_ot::{
revision::{RevType, Revision, RevisionRange},
revision::{Revision, RevisionRange},
rich_text::RichTextDelta,
};
use lib_ws::WSConnectState;
@ -194,7 +194,7 @@ pub(crate) async fn handle_push_rev(
bytes: Bytes,
) -> FlowyResult<Option<Revision>> {
// Transform the revision
let (ret, rx) = oneshot::channel::<CollaborateResult<TransformDeltas>>();
let (_ret, _rx) = oneshot::channel::<CollaborateResult<TransformDeltas>>();
let revision = Revision::try_from(bytes)?;
let delta = RichTextDelta::from_bytes(&revision.delta_data)?;
let server_rev_id = revision.rev_id;
@ -217,10 +217,10 @@ pub(crate) async fn handle_push_rev(
ret,
};
let _ = edit_cmd_tx.send(msg);
let md5 = rx.await.map_err(internal_error)??;
let _md5 = rx.await.map_err(internal_error)??;
// update rev id
rev_manager.update_rev_id_counter_value(server_rev_id.clone().into());
rev_manager.update_rev_id_counter_value(server_rev_id);
// let (local_base_rev_id, local_rev_id) = rev_manager.next_rev_id();
// let delta_data = client_prime.to_bytes();
// // save the revision

View File

@ -64,7 +64,7 @@ impl FileManager {
pub(crate) fn create_file(&mut self, id: &str, dir: &str, text: &str) -> Result<PathBuf, FileError> {
let path = PathBuf::from(format!("{}/{}", dir, id));
let file_id: FileId = id.to_owned().into();
tracing::info!("Create doc at: {:?}", path);
tracing::info!("Create document at: {:?}", path);
let _ = self.save_new(&path, text, &file_id)?;
Ok(path)
}

View File

@ -11,7 +11,7 @@ impl ResponseMiddleware for DocMiddleware {
fn receive_response(&self, token: &Option<String>, response: &FlowyResponse) {
if let Some(error) = &response.error {
if error.is_unauthorized() {
log::error!("doc user is unauthorized");
log::error!("document user is unauthorized");
match token {
None => {},

View File

@ -0,0 +1,4 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod model;
pub use model::*;

View File

@ -0,0 +1,5 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod errors;
pub use errors::*;

View File

@ -0,0 +1,4 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod model;
pub use model::*;

View File

@ -0,0 +1,8 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod network_state;
pub use network_state::*;
mod event;
pub use event::*;

View File

@ -42,7 +42,7 @@ impl DocumentUser for DocumentUserImpl {
.user_dir()
.map_err(|e| FlowyError::unauthorized().context(e))?;
let doc_dir = format!("{}/doc", dir);
let doc_dir = format!("{}/document", dir);
if !Path::new(&doc_dir).exists() {
let _ = std::fs::create_dir_all(&doc_dir)?;
}

View File

@ -0,0 +1,4 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod model;
pub use model::*;

View File

@ -0,0 +1,8 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod observable;
pub use observable::*;
mod event;
pub use event::*;

View File

@ -16,7 +16,7 @@ impl CrateProtoInfo {
pub fn create_crate_mod_file(&self) {
// mod model;
// pub use model::*;
let mod_file_path = format!("{}/mod.rs", self.inner.protobuf_crate_name());
let mod_file_path = format!("{}/document", self.inner.protobuf_crate_name());
let mut content = "#![cfg_attr(rustfmt, rustfmt::skip)]\n".to_owned();
content.push_str("// Auto-generated, do not edit\n");
content.push_str("mod model;\npub use model::*;");
@ -81,7 +81,7 @@ impl ProtobufCrate {
}
pub fn proto_model_mod_file(&self) -> String {
format!("{}/mod.rs", self.proto_struct_output_dir())
format!("{}/document", self.proto_struct_output_dir())
}
}

View File

@ -54,7 +54,7 @@ impl ClientServerConfiguration {
pub fn view_url(&self) -> String { format!("{}/api/view", self.base_url()) }
pub fn doc_url(&self) -> String { format!("{}/api/doc", self.base_url()) }
pub fn doc_url(&self) -> String { format!("{}/api/document", self.base_url()) }
pub fn trash_url(&self) -> String { format!("{}/api/trash", self.base_url()) }

View File

@ -0,0 +1,4 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod model;
pub use model::*;

View File

@ -0,0 +1,5 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod error_code;
pub use error_code::*;

View File

@ -235,7 +235,7 @@ impl ServerDocEditor {
));
Ok(Self {
doc_id: doc.id.clone(),
doc_id: doc.id,
synchronizer,
users,
})
@ -247,10 +247,7 @@ impl ServerDocEditor {
Ok(())
}
pub fn document_json(&self) -> String {
let json = self.synchronizer.doc_json();
json
}
pub fn document_json(&self) -> String { self.synchronizer.doc_json() }
pub fn rev_id(&self) -> i64 { self.synchronizer.rev_id.load(SeqCst) }
}

View File

@ -47,9 +47,8 @@ impl std::convert::TryFrom<Revision> for Doc {
fn try_from(revision: Revision) -> Result<Self, Self::Error> {
if !revision.is_initial() {
return Err(
CollaborateError::revision_conflict().context("Revision's rev_id should be 0 when creating the doc")
);
return Err(CollaborateError::revision_conflict()
.context("Revision's rev_id should be 0 when creating the document"));
}
let delta = RichTextDelta::from_bytes(&revision.delta_data)?;

View File

@ -1,7 +1,7 @@
use crate::errors::CollaborateError;
use bytes::Bytes;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use lib_infra::uuid;
use lib_ot::revision::{Revision, RevisionRange};
use std::convert::{TryFrom, TryInto};
@ -62,7 +62,7 @@ pub struct DocumentWSDataBuilder();
impl DocumentWSDataBuilder {
// DocumentWSDataType::PushRev -> Revision
pub fn build_push_message(doc_id: &str, revision: Revision, id: &str) -> DocumentWSData {
let rev_id = revision.rev_id;
let _rev_id = revision.rev_id;
let bytes: Bytes = revision.try_into().unwrap();
DocumentWSData {
doc_id: doc_id.to_string(),

View File

@ -1340,56 +1340,56 @@ static file_descriptor_proto_data: &'static [u8] = b"\
\x17\n\x07user_id\x18\x01\x20\x01(\tR\x06userId\x12\x15\n\x06rev_id\x18\
\x02\x20\x01(\x03R\x05revId\x12\x15\n\x06doc_id\x18\x03\x20\x01(\tR\x05d\
ocId\"&\n\rDocIdentifier\x12\x15\n\x06doc_id\x18\x01\x20\x01(\tR\x05docI\
dJ\xdb\x07\n\x06\x12\x04\0\0\x1c\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\
\x17\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x12\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\r\n\
\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x10\x11\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\n\n\x02\x04\x01\x12\x04\x06\0\x0b\x01\n\n\
\n\x03\x04\x01\x01\x12\x03\x06\x08\x0b\n\x0b\n\x04\x04\x01\x02\0\x12\x03\
\x07\x04\x12\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\r\n\x0c\n\x05\x04\x01\x02\0\x03\x12\
\x03\x07\x10\x11\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\x08\x04\x14\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\x0f\n\x0c\n\x05\x04\x01\x02\x01\x03\x12\x03\x08\x12\
\x13\n\x0b\n\x04\x04\x01\x02\x02\x12\x03\t\x04\x15\n\x0c\n\x05\x04\x01\
\x02\x02\x05\x12\x03\t\x04\t\n\x0c\n\x05\x04\x01\x02\x02\x01\x12\x03\t\n\
\x10\n\x0c\n\x05\x04\x01\x02\x02\x03\x12\x03\t\x13\x14\n\x0b\n\x04\x04\
\x01\x02\x03\x12\x03\n\x04\x1a\n\x0c\n\x05\x04\x01\x02\x03\x05\x12\x03\n\
\x04\t\n\x0c\n\x05\x04\x01\x02\x03\x01\x12\x03\n\n\x15\n\x0c\n\x05\x04\
\x01\x02\x03\x03\x12\x03\n\x18\x19\n\n\n\x02\x04\x02\x12\x04\x0c\0\x10\
\x01\n\n\n\x03\x04\x02\x01\x12\x03\x0c\x08\x17\n\x0b\n\x04\x04\x02\x02\0\
\x12\x03\r\x04\x16\n\x0c\n\x05\x04\x02\x02\0\x05\x12\x03\r\x04\n\n\x0c\n\
\x05\x04\x02\x02\0\x01\x12\x03\r\x0b\x11\n\x0c\n\x05\x04\x02\x02\0\x03\
\x12\x03\r\x14\x15\n\x0b\n\x04\x04\x02\x02\x01\x12\x03\x0e\x04\x14\n\x0c\
\n\x05\x04\x02\x02\x01\x05\x12\x03\x0e\x04\n\n\x0c\n\x05\x04\x02\x02\x01\
\x01\x12\x03\x0e\x0b\x0f\n\x0c\n\x05\x04\x02\x02\x01\x03\x12\x03\x0e\x12\
\x13\n\x0b\n\x04\x04\x02\x02\x02\x12\x03\x0f\x04\x15\n\x0c\n\x05\x04\x02\
\x02\x02\x05\x12\x03\x0f\x04\t\n\x0c\n\x05\x04\x02\x02\x02\x01\x12\x03\
\x0f\n\x10\n\x0c\n\x05\x04\x02\x02\x02\x03\x12\x03\x0f\x13\x14\n\n\n\x02\
\x04\x03\x12\x04\x11\0\x14\x01\n\n\n\x03\x04\x03\x01\x12\x03\x11\x08\x10\
\n\x0b\n\x04\x04\x03\x02\0\x12\x03\x12\x04\x16\n\x0c\n\x05\x04\x03\x02\0\
\x05\x12\x03\x12\x04\n\n\x0c\n\x05\x04\x03\x02\0\x01\x12\x03\x12\x0b\x11\
\n\x0c\n\x05\x04\x03\x02\0\x03\x12\x03\x12\x14\x15\n\x0b\n\x04\x04\x03\
\x02\x01\x12\x03\x13\x04\x14\n\x0c\n\x05\x04\x03\x02\x01\x05\x12\x03\x13\
\x04\n\n\x0c\n\x05\x04\x03\x02\x01\x01\x12\x03\x13\x0b\x0f\n\x0c\n\x05\
\x04\x03\x02\x01\x03\x12\x03\x13\x12\x13\n\n\n\x02\x04\x04\x12\x04\x15\0\
\x19\x01\n\n\n\x03\x04\x04\x01\x12\x03\x15\x08\x12\n\x0b\n\x04\x04\x04\
\x02\0\x12\x03\x16\x04\x17\n\x0c\n\x05\x04\x04\x02\0\x05\x12\x03\x16\x04\
\n\n\x0c\n\x05\x04\x04\x02\0\x01\x12\x03\x16\x0b\x12\n\x0c\n\x05\x04\x04\
\x02\0\x03\x12\x03\x16\x15\x16\n\x0b\n\x04\x04\x04\x02\x01\x12\x03\x17\
\x04\x15\n\x0c\n\x05\x04\x04\x02\x01\x05\x12\x03\x17\x04\t\n\x0c\n\x05\
\x04\x04\x02\x01\x01\x12\x03\x17\n\x10\n\x0c\n\x05\x04\x04\x02\x01\x03\
\x12\x03\x17\x13\x14\n\x0b\n\x04\x04\x04\x02\x02\x12\x03\x18\x04\x16\n\
\x0c\n\x05\x04\x04\x02\x02\x05\x12\x03\x18\x04\n\n\x0c\n\x05\x04\x04\x02\
\x02\x01\x12\x03\x18\x0b\x11\n\x0c\n\x05\x04\x04\x02\x02\x03\x12\x03\x18\
\x14\x15\n\n\n\x02\x04\x05\x12\x04\x1a\0\x1c\x01\n\n\n\x03\x04\x05\x01\
\x12\x03\x1a\x08\x15\n\x0b\n\x04\x04\x05\x02\0\x12\x03\x1b\x04\x16\n\x0c\
\n\x05\x04\x05\x02\0\x05\x12\x03\x1b\x04\n\n\x0c\n\x05\x04\x05\x02\0\x01\
\x12\x03\x1b\x0b\x11\n\x0c\n\x05\x04\x05\x02\0\x03\x12\x03\x1b\x14\x15b\
\x06proto3\
dJ\xdb\x07\n\x06\x12\x04\0\0\x1b\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\
\n\x02\x04\0\x12\x04\x01\0\x04\x01\n\n\n\x03\x04\0\x01\x12\x03\x01\x08\
\x17\n\x0b\n\x04\x04\0\x02\0\x12\x03\x02\x04\x12\n\x0c\n\x05\x04\0\x02\0\
\x05\x12\x03\x02\x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x02\x0b\r\n\
\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x02\x10\x11\n\x0b\n\x04\x04\0\x02\x01\
\x12\x03\x03\x04\x14\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x03\x04\n\n\
\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x03\x0b\x0f\n\x0c\n\x05\x04\0\x02\
\x01\x03\x12\x03\x03\x12\x13\n\n\n\x02\x04\x01\x12\x04\x05\0\n\x01\n\n\n\
\x03\x04\x01\x01\x12\x03\x05\x08\x0b\n\x0b\n\x04\x04\x01\x02\0\x12\x03\
\x06\x04\x12\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\x06\x04\n\n\x0c\n\x05\
\x04\x01\x02\0\x01\x12\x03\x06\x0b\r\n\x0c\n\x05\x04\x01\x02\0\x03\x12\
\x03\x06\x10\x11\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\x07\x04\x14\n\x0c\n\
\x05\x04\x01\x02\x01\x05\x12\x03\x07\x04\n\n\x0c\n\x05\x04\x01\x02\x01\
\x01\x12\x03\x07\x0b\x0f\n\x0c\n\x05\x04\x01\x02\x01\x03\x12\x03\x07\x12\
\x13\n\x0b\n\x04\x04\x01\x02\x02\x12\x03\x08\x04\x15\n\x0c\n\x05\x04\x01\
\x02\x02\x05\x12\x03\x08\x04\t\n\x0c\n\x05\x04\x01\x02\x02\x01\x12\x03\
\x08\n\x10\n\x0c\n\x05\x04\x01\x02\x02\x03\x12\x03\x08\x13\x14\n\x0b\n\
\x04\x04\x01\x02\x03\x12\x03\t\x04\x1a\n\x0c\n\x05\x04\x01\x02\x03\x05\
\x12\x03\t\x04\t\n\x0c\n\x05\x04\x01\x02\x03\x01\x12\x03\t\n\x15\n\x0c\n\
\x05\x04\x01\x02\x03\x03\x12\x03\t\x18\x19\n\n\n\x02\x04\x02\x12\x04\x0b\
\0\x0f\x01\n\n\n\x03\x04\x02\x01\x12\x03\x0b\x08\x17\n\x0b\n\x04\x04\x02\
\x02\0\x12\x03\x0c\x04\x16\n\x0c\n\x05\x04\x02\x02\0\x05\x12\x03\x0c\x04\
\n\n\x0c\n\x05\x04\x02\x02\0\x01\x12\x03\x0c\x0b\x11\n\x0c\n\x05\x04\x02\
\x02\0\x03\x12\x03\x0c\x14\x15\n\x0b\n\x04\x04\x02\x02\x01\x12\x03\r\x04\
\x14\n\x0c\n\x05\x04\x02\x02\x01\x05\x12\x03\r\x04\n\n\x0c\n\x05\x04\x02\
\x02\x01\x01\x12\x03\r\x0b\x0f\n\x0c\n\x05\x04\x02\x02\x01\x03\x12\x03\r\
\x12\x13\n\x0b\n\x04\x04\x02\x02\x02\x12\x03\x0e\x04\x15\n\x0c\n\x05\x04\
\x02\x02\x02\x05\x12\x03\x0e\x04\t\n\x0c\n\x05\x04\x02\x02\x02\x01\x12\
\x03\x0e\n\x10\n\x0c\n\x05\x04\x02\x02\x02\x03\x12\x03\x0e\x13\x14\n\n\n\
\x02\x04\x03\x12\x04\x10\0\x13\x01\n\n\n\x03\x04\x03\x01\x12\x03\x10\x08\
\x10\n\x0b\n\x04\x04\x03\x02\0\x12\x03\x11\x04\x16\n\x0c\n\x05\x04\x03\
\x02\0\x05\x12\x03\x11\x04\n\n\x0c\n\x05\x04\x03\x02\0\x01\x12\x03\x11\
\x0b\x11\n\x0c\n\x05\x04\x03\x02\0\x03\x12\x03\x11\x14\x15\n\x0b\n\x04\
\x04\x03\x02\x01\x12\x03\x12\x04\x14\n\x0c\n\x05\x04\x03\x02\x01\x05\x12\
\x03\x12\x04\n\n\x0c\n\x05\x04\x03\x02\x01\x01\x12\x03\x12\x0b\x0f\n\x0c\
\n\x05\x04\x03\x02\x01\x03\x12\x03\x12\x12\x13\n\n\n\x02\x04\x04\x12\x04\
\x14\0\x18\x01\n\n\n\x03\x04\x04\x01\x12\x03\x14\x08\x12\n\x0b\n\x04\x04\
\x04\x02\0\x12\x03\x15\x04\x17\n\x0c\n\x05\x04\x04\x02\0\x05\x12\x03\x15\
\x04\n\n\x0c\n\x05\x04\x04\x02\0\x01\x12\x03\x15\x0b\x12\n\x0c\n\x05\x04\
\x04\x02\0\x03\x12\x03\x15\x15\x16\n\x0b\n\x04\x04\x04\x02\x01\x12\x03\
\x16\x04\x15\n\x0c\n\x05\x04\x04\x02\x01\x05\x12\x03\x16\x04\t\n\x0c\n\
\x05\x04\x04\x02\x01\x01\x12\x03\x16\n\x10\n\x0c\n\x05\x04\x04\x02\x01\
\x03\x12\x03\x16\x13\x14\n\x0b\n\x04\x04\x04\x02\x02\x12\x03\x17\x04\x16\
\n\x0c\n\x05\x04\x04\x02\x02\x05\x12\x03\x17\x04\n\n\x0c\n\x05\x04\x04\
\x02\x02\x01\x12\x03\x17\x0b\x11\n\x0c\n\x05\x04\x04\x02\x02\x03\x12\x03\
\x17\x14\x15\n\n\n\x02\x04\x05\x12\x04\x19\0\x1b\x01\n\n\n\x03\x04\x05\
\x01\x12\x03\x19\x08\x15\n\x0b\n\x04\x04\x05\x02\0\x12\x03\x1a\x04\x16\n\
\x0c\n\x05\x04\x05\x02\0\x05\x12\x03\x1a\x04\n\n\x0c\n\x05\x04\x05\x02\0\
\x01\x12\x03\x1a\x0b\x11\n\x0c\n\x05\x04\x05\x02\0\x03\x12\x03\x1a\x14\
\x15b\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -605,38 +605,38 @@ static file_descriptor_proto_data: &'static [u8] = b"\
\x05docId\x12#\n\rrevision_data\x18\x03\x20\x01(\x0cR\x0crevisionData*H\
\n\x12DocumentWSDataType\x12\x07\n\x03Ack\x10\0\x12\x0b\n\x07PushRev\x10\
\x01\x12\x0b\n\x07PullRev\x10\x02\x12\x0f\n\x0bUserConnect\x10\x03J\xff\
\x04\n\x06\x12\x04\0\0\x12\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\
\x04\0\x12\x04\x02\0\x07\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x16\n\
\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x16\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\x11\n\x0c\
\n\x05\x04\0\x02\0\x03\x12\x03\x03\x14\x15\n\x0b\n\x04\x04\0\x02\x01\x12\
\x03\x04\x04\x1e\n\x0c\n\x05\x04\0\x02\x01\x06\x12\x03\x04\x04\x16\n\x0c\
\n\x05\x04\0\x02\x01\x01\x12\x03\x04\x17\x19\n\x0c\n\x05\x04\0\x02\x01\
\x03\x12\x03\x04\x1c\x1d\n\x0b\n\x04\x04\0\x02\x02\x12\x03\x05\x04\x13\n\
\x0c\n\x05\x04\0\x02\x02\x05\x12\x03\x05\x04\t\n\x0c\n\x05\x04\0\x02\x02\
\x01\x12\x03\x05\n\x0e\n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\x05\x11\x12\
\n\x0b\n\x04\x04\0\x02\x03\x12\x03\x06\x04\x12\n\x0c\n\x05\x04\0\x02\x03\
\x05\x12\x03\x06\x04\n\n\x0c\n\x05\x04\0\x02\x03\x01\x12\x03\x06\x0b\r\n\
\x0c\n\x05\x04\0\x02\x03\x03\x12\x03\x06\x10\x11\n\n\n\x02\x04\x01\x12\
\x04\x08\0\x0c\x01\n\n\n\x03\x04\x01\x01\x12\x03\x08\x08\x17\n\x0b\n\x04\
\x04\x01\x02\0\x12\x03\t\x04\x17\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\t\
\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\t\x0b\x12\n\x0c\n\x05\x04\
\x01\x02\0\x03\x12\x03\t\x15\x16\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\n\
\x04\x16\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\n\x04\n\n\x0c\n\x05\x04\
\x01\x02\x01\x01\x12\x03\n\x0b\x11\n\x0c\n\x05\x04\x01\x02\x01\x03\x12\
\x03\n\x14\x15\n\x0b\n\x04\x04\x01\x02\x02\x12\x03\x0b\x04\x1c\n\x0c\n\
\x05\x04\x01\x02\x02\x05\x12\x03\x0b\x04\t\n\x0c\n\x05\x04\x01\x02\x02\
\x01\x12\x03\x0b\n\x17\n\x0c\n\x05\x04\x01\x02\x02\x03\x12\x03\x0b\x1a\
\x1b\n\n\n\x02\x05\0\x12\x04\r\0\x12\x01\n\n\n\x03\x05\0\x01\x12\x03\r\
\x05\x17\n\x0b\n\x04\x05\0\x02\0\x12\x03\x0e\x04\x0c\n\x0c\n\x05\x05\0\
\x02\0\x01\x12\x03\x0e\x04\x07\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x0e\n\
\x0b\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x0f\x04\x10\n\x0c\n\x05\x05\0\x02\
\x01\x01\x12\x03\x0f\x04\x0b\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x0f\
\x0e\x0f\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x10\x04\x10\n\x0c\n\x05\x05\0\
\x02\x02\x01\x12\x03\x10\x04\x0b\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\
\x10\x0e\x0f\n\x0b\n\x04\x05\0\x02\x03\x12\x03\x11\x04\x14\n\x0c\n\x05\
\x05\0\x02\x03\x01\x12\x03\x11\x04\x0f\n\x0c\n\x05\x05\0\x02\x03\x02\x12\
\x03\x11\x12\x13b\x06proto3\
\x04\n\x06\x12\x04\0\0\x11\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\
\x04\0\x12\x04\x01\0\x06\x01\n\n\n\x03\x04\0\x01\x12\x03\x01\x08\x16\n\
\x0b\n\x04\x04\0\x02\0\x12\x03\x02\x04\x16\n\x0c\n\x05\x04\0\x02\0\x05\
\x12\x03\x02\x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x02\x0b\x11\n\x0c\
\n\x05\x04\0\x02\0\x03\x12\x03\x02\x14\x15\n\x0b\n\x04\x04\0\x02\x01\x12\
\x03\x03\x04\x1e\n\x0c\n\x05\x04\0\x02\x01\x06\x12\x03\x03\x04\x16\n\x0c\
\n\x05\x04\0\x02\x01\x01\x12\x03\x03\x17\x19\n\x0c\n\x05\x04\0\x02\x01\
\x03\x12\x03\x03\x1c\x1d\n\x0b\n\x04\x04\0\x02\x02\x12\x03\x04\x04\x13\n\
\x0c\n\x05\x04\0\x02\x02\x05\x12\x03\x04\x04\t\n\x0c\n\x05\x04\0\x02\x02\
\x01\x12\x03\x04\n\x0e\n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\x04\x11\x12\
\n\x0b\n\x04\x04\0\x02\x03\x12\x03\x05\x04\x12\n\x0c\n\x05\x04\0\x02\x03\
\x05\x12\x03\x05\x04\n\n\x0c\n\x05\x04\0\x02\x03\x01\x12\x03\x05\x0b\r\n\
\x0c\n\x05\x04\0\x02\x03\x03\x12\x03\x05\x10\x11\n\n\n\x02\x04\x01\x12\
\x04\x07\0\x0b\x01\n\n\n\x03\x04\x01\x01\x12\x03\x07\x08\x17\n\x0b\n\x04\
\x04\x01\x02\0\x12\x03\x08\x04\x17\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\
\x08\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x08\x0b\x12\n\x0c\n\x05\
\x04\x01\x02\0\x03\x12\x03\x08\x15\x16\n\x0b\n\x04\x04\x01\x02\x01\x12\
\x03\t\x04\x16\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\t\x04\n\n\x0c\n\
\x05\x04\x01\x02\x01\x01\x12\x03\t\x0b\x11\n\x0c\n\x05\x04\x01\x02\x01\
\x03\x12\x03\t\x14\x15\n\x0b\n\x04\x04\x01\x02\x02\x12\x03\n\x04\x1c\n\
\x0c\n\x05\x04\x01\x02\x02\x05\x12\x03\n\x04\t\n\x0c\n\x05\x04\x01\x02\
\x02\x01\x12\x03\n\n\x17\n\x0c\n\x05\x04\x01\x02\x02\x03\x12\x03\n\x1a\
\x1b\n\n\n\x02\x05\0\x12\x04\x0c\0\x11\x01\n\n\n\x03\x05\0\x01\x12\x03\
\x0c\x05\x17\n\x0b\n\x04\x05\0\x02\0\x12\x03\r\x04\x0c\n\x0c\n\x05\x05\0\
\x02\0\x01\x12\x03\r\x04\x07\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\r\n\x0b\
\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x0e\x04\x10\n\x0c\n\x05\x05\0\x02\x01\
\x01\x12\x03\x0e\x04\x0b\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x0e\x0e\
\x0f\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x0f\x04\x10\n\x0c\n\x05\x05\0\x02\
\x02\x01\x12\x03\x0f\x04\x0b\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\x0f\
\x0e\x0f\n\x0b\n\x04\x05\0\x02\x03\x12\x03\x10\x04\x14\n\x0c\n\x05\x05\0\
\x02\x03\x01\x12\x03\x10\x04\x0f\n\x0c\n\x05\x05\0\x02\x03\x02\x12\x03\
\x10\x12\x13b\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -1,5 +1,4 @@
syntax = "proto3";
message CreateDocParams {
string id = 1;
string data = 2;

View File

@ -1,5 +1,4 @@
syntax = "proto3";
message DocumentWSData {
string doc_id = 1;
DocumentWSDataType ty = 2;

View File

@ -0,0 +1,4 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod model;
pub use model::*;

View File

@ -0,0 +1,41 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod view_update;
pub use view_update::*;
mod workspace_setting;
pub use workspace_setting::*;
mod app_query;
pub use app_query::*;
mod errors;
pub use errors::*;
mod workspace_update;
pub use workspace_update::*;
mod app_create;
pub use app_create::*;
mod workspace_query;
pub use workspace_query::*;
mod view_create;
pub use view_create::*;
mod workspace_create;
pub use workspace_create::*;
mod app_update;
pub use app_update::*;
mod view_query;
pub use view_query::*;
mod trash_create;
pub use trash_create::*;
mod export;
pub use export::*;

View File

@ -0,0 +1,4 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod model;
pub use model::*;

View File

@ -0,0 +1,11 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod errors;
pub use errors::*;
mod user_profile;
pub use user_profile::*;
mod auth;
pub use auth::*;

View File

@ -0,0 +1,4 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod model;
pub use model::*;

View File

@ -0,0 +1,5 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod model;
pub use model::*;

View File

@ -0,0 +1,4 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod model;
pub use model::*;

View File

@ -0,0 +1,8 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
// Auto-generated, do not edit
mod errors;
pub use errors::*;
mod msg;
pub use msg::*;