mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
add workspace/app/view router
This commit is contained in:
parent
d6c761917b
commit
74800278f9
@ -34,7 +34,7 @@ class UserErrCode extends $pb.ProtobufEnum {
|
||||
static const UserErrCode UserIdInvalid = UserErrCode._(51, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserIdInvalid');
|
||||
static const UserErrCode CreateDefaultWorkspaceFailed = UserErrCode._(52, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateDefaultWorkspaceFailed');
|
||||
static const UserErrCode DefaultWorkspaceAlreadyExist = UserErrCode._(53, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DefaultWorkspaceAlreadyExist');
|
||||
static const UserErrCode NetworkError = UserErrCode._(100, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'NetworkError');
|
||||
static const UserErrCode ServerError = UserErrCode._(100, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ServerError');
|
||||
|
||||
static const $core.List<UserErrCode> values = <UserErrCode> [
|
||||
Unknown,
|
||||
@ -61,7 +61,7 @@ class UserErrCode extends $pb.ProtobufEnum {
|
||||
UserIdInvalid,
|
||||
CreateDefaultWorkspaceFailed,
|
||||
DefaultWorkspaceAlreadyExist,
|
||||
NetworkError,
|
||||
ServerError,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, UserErrCode> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
|
@ -36,12 +36,12 @@ const UserErrCode$json = const {
|
||||
const {'1': 'UserIdInvalid', '2': 51},
|
||||
const {'1': 'CreateDefaultWorkspaceFailed', '2': 52},
|
||||
const {'1': 'DefaultWorkspaceAlreadyExist', '2': 53},
|
||||
const {'1': 'NetworkError', '2': 100},
|
||||
const {'1': 'ServerError', '2': 100},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `UserErrCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||
final $typed_data.Uint8List userErrCodeDescriptor = $convert.base64Decode('CgtVc2VyRXJyQ29kZRILCgdVbmtub3duEAASGgoWVXNlckRhdGFiYXNlSW5pdEZhaWxlZBABEhsKF1VzZXJEYXRhYmFzZVdyaXRlTG9ja2VkEAISGgoWVXNlckRhdGFiYXNlUmVhZExvY2tlZBADEhsKF1VzZXJEYXRhYmFzZURpZE5vdE1hdGNoEAQSHQoZVXNlckRhdGFiYXNlSW50ZXJuYWxFcnJvchAFEhQKEFNxbEludGVybmFsRXJyb3IQBhITCg9Vc2VyTm90TG9naW5ZZXQQChIXChNSZWFkQ3VycmVudElkRmFpbGVkEAsSGAoUV3JpdGVDdXJyZW50SWRGYWlsZWQQDBIQCgxFbWFpbElzRW1wdHkQFBIWChJFbWFpbEZvcm1hdEludmFsaWQQFRIWChJFbWFpbEFscmVhZHlFeGlzdHMQFhITCg9QYXNzd29yZElzRW1wdHkQHhITCg9QYXNzd29yZFRvb0xvbmcQHxIkCiBQYXNzd29yZENvbnRhaW5zRm9yYmlkQ2hhcmFjdGVycxAgEhkKFVBhc3N3b3JkRm9ybWF0SW52YWxpZBAhEhMKD1VzZXJOYW1lVG9vTG9uZxAoEicKI1VzZXJOYW1lQ29udGFpbnNGb3JiaWRkZW5DaGFyYWN0ZXJzECkSEwoPVXNlck5hbWVJc0VtcHR5ECoSGAoUVXNlcldvcmtzcGFjZUludmFsaWQQMhIRCg1Vc2VySWRJbnZhbGlkEDMSIAocQ3JlYXRlRGVmYXVsdFdvcmtzcGFjZUZhaWxlZBA0EiAKHERlZmF1bHRXb3Jrc3BhY2VBbHJlYWR5RXhpc3QQNRIQCgxOZXR3b3JrRXJyb3IQZA==');
|
||||
final $typed_data.Uint8List userErrCodeDescriptor = $convert.base64Decode('CgtVc2VyRXJyQ29kZRILCgdVbmtub3duEAASGgoWVXNlckRhdGFiYXNlSW5pdEZhaWxlZBABEhsKF1VzZXJEYXRhYmFzZVdyaXRlTG9ja2VkEAISGgoWVXNlckRhdGFiYXNlUmVhZExvY2tlZBADEhsKF1VzZXJEYXRhYmFzZURpZE5vdE1hdGNoEAQSHQoZVXNlckRhdGFiYXNlSW50ZXJuYWxFcnJvchAFEhQKEFNxbEludGVybmFsRXJyb3IQBhITCg9Vc2VyTm90TG9naW5ZZXQQChIXChNSZWFkQ3VycmVudElkRmFpbGVkEAsSGAoUV3JpdGVDdXJyZW50SWRGYWlsZWQQDBIQCgxFbWFpbElzRW1wdHkQFBIWChJFbWFpbEZvcm1hdEludmFsaWQQFRIWChJFbWFpbEFscmVhZHlFeGlzdHMQFhITCg9QYXNzd29yZElzRW1wdHkQHhITCg9QYXNzd29yZFRvb0xvbmcQHxIkCiBQYXNzd29yZENvbnRhaW5zRm9yYmlkQ2hhcmFjdGVycxAgEhkKFVBhc3N3b3JkRm9ybWF0SW52YWxpZBAhEhMKD1VzZXJOYW1lVG9vTG9uZxAoEicKI1VzZXJOYW1lQ29udGFpbnNGb3JiaWRkZW5DaGFyYWN0ZXJzECkSEwoPVXNlck5hbWVJc0VtcHR5ECoSGAoUVXNlcldvcmtzcGFjZUludmFsaWQQMhIRCg1Vc2VySWRJbnZhbGlkEDMSIAocQ3JlYXRlRGVmYXVsdFdvcmtzcGFjZUZhaWxlZBA0EiAKHERlZmF1bHRXb3Jrc3BhY2VBbHJlYWR5RXhpc3QQNRIPCgtTZXJ2ZXJFcnJvchBk');
|
||||
@$core.Deprecated('Use userErrorDescriptor instead')
|
||||
const UserError$json = const {
|
||||
'1': 'UserError',
|
||||
|
@ -31,4 +31,24 @@ export DB_PORT=5433
|
||||
|
||||
### Run
|
||||
By default, Docker images do not expose their ports to the underlying host machine. We need to do it explicitly using the -p flag.
|
||||
`docker run -p 8000:8000 backend`
|
||||
`docker run -p 8000:8000 backend`
|
||||
|
||||
|
||||
### Sqlx
|
||||
|
||||
**Sqlx and Diesel commands**
|
||||
* create migration
|
||||
* sqlx: sqlx migrate add $(table)
|
||||
* diesel: diesel migration generation $(table)
|
||||
|
||||
* run migration
|
||||
* sqlx: sqlx migrate run
|
||||
* diesel: diesel migration run
|
||||
|
||||
* reset database
|
||||
* sqlx: sqlx database reset
|
||||
* diesel: diesel database reset
|
||||
|
||||
**Type mapping**
|
||||
* [postgres type map](https://docs.rs/sqlx/0.5.7/sqlx/postgres/types/index.html)
|
||||
* [postgres and diesel type map](https://kotiri.com/2018/01/31/postgresql-diesel-rust-types.html)
|
10
backend/migrations/20210824033032_workspace.sql
Normal file
10
backend/migrations/20210824033032_workspace.sql
Normal file
@ -0,0 +1,10 @@
|
||||
-- Add migration script here
|
||||
CREATE TABLE workspace_table(
|
||||
id uuid NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
name TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
modified_time timestamptz NOT NULL,
|
||||
create_time timestamptz NOT NULL,
|
||||
user_id TEXT NOT NULL
|
||||
);
|
14
backend/migrations/20210824033742_app.sql
Normal file
14
backend/migrations/20210824033742_app.sql
Normal file
@ -0,0 +1,14 @@
|
||||
-- Add migration script here
|
||||
CREATE TABLE app_table(
|
||||
id uuid NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
workspace_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
color_style BYTEA NOT NULL,
|
||||
last_view_id TEXT DEFAULT '',
|
||||
modified_time timestamptz NOT NULL,
|
||||
create_time timestamptz NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
is_trash BOOL NOT NULL DEFAULT false
|
||||
);
|
13
backend/migrations/20210824033748_view.sql
Normal file
13
backend/migrations/20210824033748_view.sql
Normal file
@ -0,0 +1,13 @@
|
||||
-- Add migration script here
|
||||
CREATE TABLE view_table(
|
||||
id uuid NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
belong_to_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
modified_time timestamptz NOT NULL,
|
||||
create_time timestamptz NOT NULL,
|
||||
thumbnail TEXT NOT NULL,
|
||||
view_type INTEGER NOT NULL,
|
||||
is_trash BOOL NOT NULL DEFAULT false
|
||||
);
|
@ -13,13 +13,16 @@ init_database:
|
||||
${ROOT}/db_init.sh
|
||||
|
||||
reset_db:
|
||||
#diesel database reset
|
||||
sqlx database reset
|
||||
|
||||
add_migrations:
|
||||
#make table="the name of your table" add_migrations
|
||||
# diesel migration generation $(table)
|
||||
sqlx migrate add $(table)
|
||||
|
||||
run_migrations:
|
||||
# diesel migration run
|
||||
sqlx migrate run
|
||||
|
||||
echo_db_url:
|
||||
|
@ -7,6 +7,9 @@ use crate::{
|
||||
},
|
||||
context::AppContext,
|
||||
routers::*,
|
||||
user_service::router as user,
|
||||
workspace_service::{app::router as app, view::router as view, workspace::router as workspace},
|
||||
ws_service,
|
||||
ws_service::WSServer,
|
||||
};
|
||||
use actix::Actor;
|
||||
@ -60,23 +63,41 @@ pub fn run(listener: TcpListener, app_ctx: AppContext) -> Result<Server, std::io
|
||||
Ok(server)
|
||||
}
|
||||
|
||||
fn ws_scope() -> Scope { web::scope("/ws").service(ws_router::start_connection) }
|
||||
fn ws_scope() -> Scope { web::scope("/ws").service(ws_service::router::start_connection) }
|
||||
|
||||
fn user_scope() -> Scope {
|
||||
web::scope("/api")
|
||||
// authentication
|
||||
.service(web::resource("/auth")
|
||||
.route(web::post().to(user_router::sign_in_handler))
|
||||
.route(web::delete().to(user_router::sign_out_handler))
|
||||
.route(web::get().to(user_router::user_profile))
|
||||
.route(web::post().to(user::sign_in_handler))
|
||||
.route(web::delete().to(user::sign_out_handler))
|
||||
.route(web::get().to(user::user_profile))
|
||||
)
|
||||
.service(web::resource("/workspace")
|
||||
.route(web::post().to(workspace::create_workspace))
|
||||
.route(web::delete().to(workspace::delete_workspace))
|
||||
.route(web::get().to(workspace::read_workspace))
|
||||
.route(web::patch().to(workspace::update_workspace))
|
||||
)
|
||||
.service(web::resource("/app")
|
||||
.route(web::post().to(app::create_app))
|
||||
.route(web::delete().to(app::delete_app))
|
||||
.route(web::get().to(app::read_app))
|
||||
.route(web::patch().to(app::update_app))
|
||||
)
|
||||
.service(web::resource("/view")
|
||||
.route(web::post().to(view::create_view))
|
||||
.route(web::delete().to(view::delete_view))
|
||||
.route(web::get().to(view::read_view))
|
||||
.route(web::patch().to(view::update_view))
|
||||
)
|
||||
// password
|
||||
.service(web::resource("/password_change")
|
||||
.route(web::post().to(user_router::change_password))
|
||||
.route(web::post().to(user::change_password))
|
||||
)
|
||||
// register
|
||||
.service(web::resource("/register")
|
||||
.route(web::post().to(user_router::register_user_handler))
|
||||
.route(web::post().to(user::register_user_handler))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -4,4 +4,5 @@ mod context;
|
||||
mod entities;
|
||||
mod routers;
|
||||
pub mod user_service;
|
||||
pub mod workspace_service;
|
||||
pub mod ws_service;
|
||||
|
@ -1,6 +1 @@
|
||||
pub(crate) mod user_router;
|
||||
mod utils;
|
||||
pub(crate) mod ws_router;
|
||||
|
||||
pub use user_router::*;
|
||||
pub use ws_router::*;
|
||||
pub(crate) mod utils;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::config::MAX_PAYLOAD_SIZE;
|
||||
use actix_web::web;
|
||||
use flowy_net::{
|
||||
errors::{ErrorCode, ServerError},
|
||||
errors::{ErrorCode, Kind, ServerError},
|
||||
response::*,
|
||||
};
|
||||
use futures::StreamExt;
|
||||
@ -26,10 +26,11 @@ pub async fn poll_payload(mut payload: web::Payload) -> Result<web::BytesMut, Se
|
||||
let chunk = chunk.map_err(|err| ServerError::internal().context(err))?;
|
||||
|
||||
if (body.len() + chunk.len()) > MAX_PAYLOAD_SIZE {
|
||||
return Err(ServerError {
|
||||
code: ErrorCode::PayloadOverflow,
|
||||
msg: "Payload overflow".to_string(),
|
||||
});
|
||||
return Err(ServerError::new(
|
||||
"Payload overflow".to_string(),
|
||||
ErrorCode::PayloadOverflow,
|
||||
Kind::Other,
|
||||
));
|
||||
}
|
||||
body.extend_from_slice(&chunk);
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
use crate::{
|
||||
entities::{token::Token, user::User},
|
||||
user_service::utils::{hash_password, verify_password},
|
||||
user_service::{hash_password, verify_password},
|
||||
};
|
||||
use actix_identity::Identity;
|
||||
use anyhow::Context;
|
||||
use chrono::Utc;
|
||||
use flowy_net::{
|
||||
errors::{ErrorCode, ServerError},
|
||||
errors::{ErrorCode, Kind, ServerError},
|
||||
response::FlowyResponse,
|
||||
};
|
||||
use flowy_user::{
|
||||
entities::{SignInResponse, SignUpResponse},
|
||||
entities::{parser::UserName, SignInResponse, SignUpResponse},
|
||||
prelude::parser::{UserEmail, UserPassword},
|
||||
protobuf::{SignInParams, SignUpParams},
|
||||
};
|
||||
@ -58,15 +58,27 @@ pub async fn register_user(
|
||||
pool: &PgPool,
|
||||
params: SignUpParams,
|
||||
) -> Result<FlowyResponse, ServerError> {
|
||||
let name =
|
||||
UserName::parse(params.name).map_err(|e| ServerError::params_invalid().context(e))?;
|
||||
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))?;
|
||||
|
||||
let mut transaction = pool
|
||||
.begin()
|
||||
.await
|
||||
.context("Failed to acquire a Postgres connection to register user")?;
|
||||
|
||||
let _ = is_email_exist(&mut transaction, ¶ms.email).await?;
|
||||
let data = insert_user(&mut transaction, params)
|
||||
.await
|
||||
.context("Failed to insert user")?;
|
||||
let _ = is_email_exist(&mut transaction, email.as_ref()).await?;
|
||||
let data = insert_user(
|
||||
&mut transaction,
|
||||
name.as_ref(),
|
||||
email.as_ref(),
|
||||
password.as_ref(),
|
||||
)
|
||||
.await
|
||||
.context("Failed to insert user")?;
|
||||
|
||||
transaction
|
||||
.commit()
|
||||
@ -90,6 +102,7 @@ async fn is_email_exist(
|
||||
Some(_) => Err(ServerError {
|
||||
code: ErrorCode::EmailAlreadyExists,
|
||||
msg: format!("{} already exists", email),
|
||||
kind: Kind::User,
|
||||
}),
|
||||
None => Ok(()),
|
||||
}
|
||||
@ -110,18 +123,20 @@ async fn read_user(
|
||||
|
||||
async fn insert_user(
|
||||
transaction: &mut Transaction<'_, Postgres>,
|
||||
params: SignUpParams,
|
||||
name: &str,
|
||||
email: &str,
|
||||
password: &str,
|
||||
) -> Result<SignUpResponse, ServerError> {
|
||||
let uuid = uuid::Uuid::new_v4();
|
||||
let password = hash_password(¶ms.password)?;
|
||||
let password = hash_password(password)?;
|
||||
let _ = sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO user_table (id, email, name, create_time, password)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
"#,
|
||||
uuid,
|
||||
params.email,
|
||||
params.name,
|
||||
email,
|
||||
name,
|
||||
Utc::now(),
|
||||
password,
|
||||
)
|
||||
@ -131,8 +146,8 @@ async fn insert_user(
|
||||
|
||||
let data = SignUpResponse {
|
||||
uid: uuid.to_string(),
|
||||
name: params.name,
|
||||
email: params.email,
|
||||
name: name.to_string(),
|
||||
email: email.to_string(),
|
||||
};
|
||||
|
||||
Ok(data)
|
@ -1,2 +1,8 @@
|
||||
pub mod auth_service;
|
||||
pub mod utils;
|
||||
mod auth;
|
||||
mod profile;
|
||||
pub mod router;
|
||||
mod utils;
|
||||
|
||||
pub use auth::*;
|
||||
pub use profile::*;
|
||||
pub use utils::*;
|
||||
|
0
backend/src/user_service/profile.rs
Normal file
0
backend/src/user_service/profile.rs
Normal file
@ -8,7 +8,7 @@ use actix_web::{
|
||||
use flowy_net::response::*;
|
||||
use flowy_user::protobuf::{SignInParams, SignUpParams};
|
||||
|
||||
use crate::user_service::auth_service::{register_user, sign_in};
|
||||
use crate::user_service::{register_user, sign_in};
|
||||
use actix_identity::Identity;
|
||||
use flowy_net::errors::ServerError;
|
||||
use sqlx::PgPool;
|
@ -1,5 +1,5 @@
|
||||
use bcrypt::{hash, verify, BcryptError, DEFAULT_COST};
|
||||
use flowy_net::errors::{ErrorCode, ServerError};
|
||||
use flowy_net::errors::{ErrorCode, Kind, ServerError};
|
||||
use jsonwebtoken::Algorithm;
|
||||
|
||||
pub fn uuid() -> String { uuid::Uuid::new_v4().to_string() }
|
||||
@ -16,9 +16,10 @@ pub fn hash_password(plain: &str) -> Result<String, ServerError> {
|
||||
pub fn verify_password(source: &str, hash: &str) -> Result<bool, ServerError> {
|
||||
match verify(source, hash) {
|
||||
Ok(true) => Ok(true),
|
||||
_ => Err(ServerError {
|
||||
code: ErrorCode::PasswordNotMatch,
|
||||
msg: "Username and password don't match".to_string(),
|
||||
}),
|
||||
_ => Err(ServerError::new(
|
||||
"Username and password don't match".to_string(),
|
||||
ErrorCode::PasswordNotMatch,
|
||||
Kind::User,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
1
backend/src/workspace_service/app/mod.rs
Normal file
1
backend/src/workspace_service/app/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod router;
|
41
backend/src/workspace_service/app/router.rs
Normal file
41
backend/src/workspace_service/app/router.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use actix_identity::Identity;
|
||||
use actix_web::{
|
||||
web::{Data, Payload},
|
||||
Error,
|
||||
HttpRequest,
|
||||
HttpResponse,
|
||||
};
|
||||
use flowy_net::errors::ServerError;
|
||||
use sqlx::PgPool;
|
||||
|
||||
pub async fn create_app(
|
||||
payload: Payload,
|
||||
id: Identity,
|
||||
pool: Data<PgPool>,
|
||||
) -> Result<HttpResponse, ServerError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub async fn read_app(
|
||||
payload: Payload,
|
||||
id: Identity,
|
||||
pool: Data<PgPool>,
|
||||
) -> Result<HttpResponse, ServerError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub async fn update_app(
|
||||
payload: Payload,
|
||||
id: Identity,
|
||||
pool: Data<PgPool>,
|
||||
) -> Result<HttpResponse, ServerError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub async fn delete_app(
|
||||
payload: Payload,
|
||||
id: Identity,
|
||||
pool: Data<PgPool>,
|
||||
) -> Result<HttpResponse, ServerError> {
|
||||
unimplemented!()
|
||||
}
|
3
backend/src/workspace_service/mod.rs
Normal file
3
backend/src/workspace_service/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod app;
|
||||
pub mod view;
|
||||
pub mod workspace;
|
1
backend/src/workspace_service/view/mod.rs
Normal file
1
backend/src/workspace_service/view/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod router;
|
41
backend/src/workspace_service/view/router.rs
Normal file
41
backend/src/workspace_service/view/router.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use actix_identity::Identity;
|
||||
use actix_web::{
|
||||
web::{Data, Payload},
|
||||
Error,
|
||||
HttpRequest,
|
||||
HttpResponse,
|
||||
};
|
||||
use flowy_net::errors::ServerError;
|
||||
use sqlx::PgPool;
|
||||
|
||||
pub async fn create_view(
|
||||
payload: Payload,
|
||||
id: Identity,
|
||||
pool: Data<PgPool>,
|
||||
) -> Result<HttpResponse, ServerError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub async fn read_view(
|
||||
payload: Payload,
|
||||
id: Identity,
|
||||
pool: Data<PgPool>,
|
||||
) -> Result<HttpResponse, ServerError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub async fn update_view(
|
||||
payload: Payload,
|
||||
id: Identity,
|
||||
pool: Data<PgPool>,
|
||||
) -> Result<HttpResponse, ServerError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub async fn delete_view(
|
||||
payload: Payload,
|
||||
id: Identity,
|
||||
pool: Data<PgPool>,
|
||||
) -> Result<HttpResponse, ServerError> {
|
||||
unimplemented!()
|
||||
}
|
1
backend/src/workspace_service/workspace/mod.rs
Normal file
1
backend/src/workspace_service/workspace/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod router;
|
41
backend/src/workspace_service/workspace/router.rs
Normal file
41
backend/src/workspace_service/workspace/router.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use actix_identity::Identity;
|
||||
use actix_web::{
|
||||
web::{Data, Payload},
|
||||
Error,
|
||||
HttpRequest,
|
||||
HttpResponse,
|
||||
};
|
||||
use flowy_net::errors::ServerError;
|
||||
use sqlx::PgPool;
|
||||
|
||||
pub async fn create_workspace(
|
||||
payload: Payload,
|
||||
id: Identity,
|
||||
pool: Data<PgPool>,
|
||||
) -> Result<HttpResponse, ServerError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub async fn read_workspace(
|
||||
payload: Payload,
|
||||
id: Identity,
|
||||
pool: Data<PgPool>,
|
||||
) -> Result<HttpResponse, ServerError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub async fn delete_workspace(
|
||||
payload: Payload,
|
||||
id: Identity,
|
||||
pool: Data<PgPool>,
|
||||
) -> Result<HttpResponse, ServerError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub async fn update_workspace(
|
||||
payload: Payload,
|
||||
id: Identity,
|
||||
pool: Data<PgPool>,
|
||||
) -> Result<HttpResponse, ServerError> {
|
||||
unimplemented!()
|
||||
}
|
@ -3,5 +3,6 @@ pub use ws_client::*;
|
||||
pub use ws_server::*;
|
||||
|
||||
pub(crate) mod entities;
|
||||
pub mod router;
|
||||
mod ws_client;
|
||||
mod ws_server;
|
||||
|
@ -1,33 +1,37 @@
|
||||
use crate::helper::spawn_app;
|
||||
use flowy_user::entities::{SignInParams, SignInResponse, SignUpParams};
|
||||
use crate::helper::{spawn_app, TestApp};
|
||||
use flowy_user::entities::{SignInParams, SignInResponse, SignUpParams, SignUpResponse};
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn user_register() {
|
||||
let app = spawn_app().await;
|
||||
let params = SignUpParams {
|
||||
email: "annie@appflowy.io".to_string(),
|
||||
name: "annie".to_string(),
|
||||
password: "123".to_string(),
|
||||
};
|
||||
|
||||
let response = app.register_user(params).await;
|
||||
let response = register_user(&app, "annie@appflowy.io", "HelloWork123!").await;
|
||||
log::info!("{:?}", response);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
#[should_panic]
|
||||
async fn user_sign_in_with_invalid_password() {
|
||||
let app = spawn_app().await;
|
||||
let email = "annie@appflowy.io";
|
||||
let password = "123";
|
||||
let _ = register_user(&app, email, password).await;
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
#[should_panic]
|
||||
async fn user_sign_in_with_invalid_email() {
|
||||
let app = spawn_app().await;
|
||||
let email = "annie@gmail@";
|
||||
let password = "HelloWork123!";
|
||||
let _ = register_user(&app, email, password).await;
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn user_sign_in() {
|
||||
let app = spawn_app().await;
|
||||
let email = "annie@appflowy.io";
|
||||
let password = "123";
|
||||
|
||||
let _ = app
|
||||
.register_user(SignUpParams {
|
||||
email: email.to_string(),
|
||||
name: "annie".to_string(),
|
||||
password: password.to_string(),
|
||||
})
|
||||
.await;
|
||||
|
||||
let password = "HelloWork123!";
|
||||
let _ = register_user(&app, email, password).await;
|
||||
let response = app
|
||||
.sign_in(SignInParams {
|
||||
email: email.to_string(),
|
||||
@ -37,3 +41,14 @@ async fn user_sign_in() {
|
||||
|
||||
log::info!("{:?}", response);
|
||||
}
|
||||
|
||||
async fn register_user(app: &TestApp, email: &str, password: &str) -> SignUpResponse {
|
||||
let params = SignUpParams {
|
||||
email: email.to_string(),
|
||||
name: "annie".to_string(),
|
||||
password: password.to_string(),
|
||||
};
|
||||
|
||||
let response = app.register_user(params).await;
|
||||
response
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ use crate::response::FlowyResponse;
|
||||
pub struct ServerError {
|
||||
pub code: ErrorCode,
|
||||
pub msg: String,
|
||||
pub kind: Kind,
|
||||
}
|
||||
|
||||
macro_rules! static_error {
|
||||
@ -18,6 +19,7 @@ macro_rules! static_error {
|
||||
ServerError {
|
||||
code: $status,
|
||||
msg: format!("{}", $status),
|
||||
kind: Kind::Other,
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -30,6 +32,17 @@ impl ServerError {
|
||||
static_error!(unauthorized, ErrorCode::Unauthorized);
|
||||
static_error!(password_not_match, ErrorCode::PasswordNotMatch);
|
||||
static_error!(params_invalid, ErrorCode::ParamsInvalid);
|
||||
static_error!(connect_timeout, ErrorCode::ConnectTimeout);
|
||||
static_error!(connect_close, ErrorCode::ConnectClose);
|
||||
static_error!(connect_cancel, ErrorCode::ConnectCancel);
|
||||
static_error!(connect_refused, ErrorCode::ConnectRefused);
|
||||
|
||||
pub fn new(msg: String, code: ErrorCode, kind: Kind) -> Self { Self { code, msg, kind } }
|
||||
|
||||
pub fn kind(mut self, kind: Kind) -> Self {
|
||||
self.kind = kind;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn context<T: Debug>(mut self, error: T) -> Self {
|
||||
self.msg = format!("{:?}", error);
|
||||
@ -53,6 +66,12 @@ impl std::convert::From<&ServerError> for FlowyResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum Kind {
|
||||
User,
|
||||
Other,
|
||||
}
|
||||
|
||||
#[derive(Serialize_repr, Deserialize_repr, PartialEq, Debug, Clone, derive_more::Display)]
|
||||
#[repr(u16)]
|
||||
pub enum ErrorCode {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::errors::{ErrorCode, ServerError};
|
||||
use crate::errors::{ErrorCode, Kind, ServerError};
|
||||
use bytes::Bytes;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{convert::TryInto, error::Error, fmt::Debug};
|
||||
@ -23,12 +23,7 @@ impl FlowyResponse {
|
||||
}
|
||||
|
||||
impl std::convert::From<protobuf::ProtobufError> for ServerError {
|
||||
fn from(err: protobuf::ProtobufError) -> Self {
|
||||
ServerError {
|
||||
code: ErrorCode::ProtobufError,
|
||||
msg: format!("{}", err),
|
||||
}
|
||||
}
|
||||
fn from(e: protobuf::ProtobufError) -> Self { ServerError::internal().context(e) }
|
||||
}
|
||||
|
||||
impl std::convert::From<RecvError> for ServerError {
|
||||
@ -36,13 +31,7 @@ impl std::convert::From<RecvError> for ServerError {
|
||||
}
|
||||
|
||||
impl std::convert::From<serde_json::Error> for ServerError {
|
||||
fn from(e: serde_json::Error) -> Self {
|
||||
let msg = format!("Serial error: {:?}", e);
|
||||
ServerError {
|
||||
code: ErrorCode::SerdeError,
|
||||
msg,
|
||||
}
|
||||
}
|
||||
fn from(e: serde_json::Error) -> Self { ServerError::internal().context(e) }
|
||||
}
|
||||
|
||||
impl std::convert::From<anyhow::Error> for ServerError {
|
||||
@ -52,19 +41,13 @@ impl std::convert::From<anyhow::Error> for ServerError {
|
||||
impl std::convert::From<reqwest::Error> for ServerError {
|
||||
fn from(error: reqwest::Error) -> Self {
|
||||
if error.is_timeout() {
|
||||
return ServerError {
|
||||
code: ErrorCode::ConnectTimeout,
|
||||
msg: format!("{}", error),
|
||||
};
|
||||
return ServerError::connect_timeout().context(error);
|
||||
}
|
||||
|
||||
if error.is_request() {
|
||||
let hyper_error: Option<&hyper::Error> = error.source().unwrap().downcast_ref();
|
||||
return match hyper_error {
|
||||
None => ServerError {
|
||||
code: ErrorCode::ConnectRefused,
|
||||
msg: format!("{:?}", error),
|
||||
},
|
||||
None => ServerError::connect_refused().context(error),
|
||||
Some(hyper_error) => {
|
||||
let mut code = ErrorCode::InternalError;
|
||||
let msg = format!("{}", error);
|
||||
@ -82,15 +65,15 @@ impl std::convert::From<reqwest::Error> for ServerError {
|
||||
|
||||
if hyper_error.is_timeout() {}
|
||||
|
||||
ServerError { code, msg }
|
||||
ServerError {
|
||||
code,
|
||||
msg,
|
||||
kind: Kind::Other,
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let msg = format!("{:?}", error);
|
||||
ServerError {
|
||||
code: ErrorCode::ProtobufError,
|
||||
msg,
|
||||
}
|
||||
ServerError::internal().context(error)
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,10 @@ impl UserPassword {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for UserPassword {
|
||||
fn as_ref(&self) -> &str { &self.0 }
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
// Test it in https://regex101.com/
|
||||
// https://stackoverflow.com/questions/2370015/regular-expression-for-password-validation/2370045
|
||||
|
@ -2,7 +2,11 @@ use bytes::Bytes;
|
||||
use derive_more::Display;
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use flowy_dispatch::prelude::{EventResponse, ResponseBuilder};
|
||||
use std::convert::TryInto;
|
||||
use flowy_net::errors::Kind;
|
||||
use std::{
|
||||
convert::TryInto,
|
||||
fmt::{Debug, Formatter},
|
||||
};
|
||||
|
||||
#[derive(Debug, Default, Clone, ProtoBuf)]
|
||||
pub struct UserError {
|
||||
@ -22,7 +26,7 @@ impl UserError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, ProtoBuf_Enum, Display, PartialEq, Eq)]
|
||||
#[derive(Clone, ProtoBuf_Enum, Display, PartialEq, Eq)]
|
||||
pub enum UserErrCode {
|
||||
#[display(fmt = "Unknown")]
|
||||
Unknown = 0,
|
||||
@ -80,8 +84,12 @@ pub enum UserErrCode {
|
||||
#[display(fmt = "User default workspace already exists")]
|
||||
DefaultWorkspaceAlreadyExist = 53,
|
||||
|
||||
#[display(fmt = "Network error")]
|
||||
NetworkError = 100,
|
||||
#[display(fmt = "Server error")]
|
||||
ServerError = 100,
|
||||
}
|
||||
|
||||
impl Debug for UserErrCode {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str(&format!("{}", self)) }
|
||||
}
|
||||
|
||||
impl UserErrCode {
|
||||
@ -139,8 +147,8 @@ impl std::convert::From<flowy_sqlite::Error> for UserError {
|
||||
|
||||
impl std::convert::From<flowy_net::errors::ServerError> for UserError {
|
||||
fn from(error: flowy_net::errors::ServerError) -> Self {
|
||||
ErrorBuilder::new(UserErrCode::NetworkError)
|
||||
.error(error)
|
||||
ErrorBuilder::new(UserErrCode::ServerError)
|
||||
.error(error.msg)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
mod event;
|
||||
mod handlers;
|
||||
mod sql_tables;
|
||||
|
||||
pub mod entities;
|
||||
pub mod errors;
|
||||
pub mod event;
|
||||
pub mod module;
|
||||
pub mod protobuf;
|
||||
pub mod services;
|
||||
|
@ -239,7 +239,7 @@ pub enum UserErrCode {
|
||||
UserIdInvalid = 51,
|
||||
CreateDefaultWorkspaceFailed = 52,
|
||||
DefaultWorkspaceAlreadyExist = 53,
|
||||
NetworkError = 100,
|
||||
ServerError = 100,
|
||||
}
|
||||
|
||||
impl ::protobuf::ProtobufEnum for UserErrCode {
|
||||
@ -273,7 +273,7 @@ impl ::protobuf::ProtobufEnum for UserErrCode {
|
||||
51 => ::std::option::Option::Some(UserErrCode::UserIdInvalid),
|
||||
52 => ::std::option::Option::Some(UserErrCode::CreateDefaultWorkspaceFailed),
|
||||
53 => ::std::option::Option::Some(UserErrCode::DefaultWorkspaceAlreadyExist),
|
||||
100 => ::std::option::Option::Some(UserErrCode::NetworkError),
|
||||
100 => ::std::option::Option::Some(UserErrCode::ServerError),
|
||||
_ => ::std::option::Option::None
|
||||
}
|
||||
}
|
||||
@ -304,7 +304,7 @@ impl ::protobuf::ProtobufEnum for UserErrCode {
|
||||
UserErrCode::UserIdInvalid,
|
||||
UserErrCode::CreateDefaultWorkspaceFailed,
|
||||
UserErrCode::DefaultWorkspaceAlreadyExist,
|
||||
UserErrCode::NetworkError,
|
||||
UserErrCode::ServerError,
|
||||
];
|
||||
values
|
||||
}
|
||||
@ -335,7 +335,7 @@ impl ::protobuf::reflect::ProtobufValue for UserErrCode {
|
||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\n\x0cerrors.proto\"?\n\tUserError\x12\x20\n\x04code\x18\x01\x20\x01(\
|
||||
\x0e2\x0c.UserErrCodeR\x04code\x12\x10\n\x03msg\x18\x02\x20\x01(\tR\x03m\
|
||||
sg*\x8c\x05\n\x0bUserErrCode\x12\x0b\n\x07Unknown\x10\0\x12\x1a\n\x16Use\
|
||||
sg*\x8b\x05\n\x0bUserErrCode\x12\x0b\n\x07Unknown\x10\0\x12\x1a\n\x16Use\
|
||||
rDatabaseInitFailed\x10\x01\x12\x1b\n\x17UserDatabaseWriteLocked\x10\x02\
|
||||
\x12\x1a\n\x16UserDatabaseReadLocked\x10\x03\x12\x1b\n\x17UserDatabaseDi\
|
||||
dNotMatch\x10\x04\x12\x1d\n\x19UserDatabaseInternalError\x10\x05\x12\x14\
|
||||
@ -348,8 +348,8 @@ static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\x12\x13\n\x0fUserNameTooLong\x10(\x12'\n#UserNameContainsForbiddenChara\
|
||||
cters\x10)\x12\x13\n\x0fUserNameIsEmpty\x10*\x12\x18\n\x14UserWorkspaceI\
|
||||
nvalid\x102\x12\x11\n\rUserIdInvalid\x103\x12\x20\n\x1cCreateDefaultWork\
|
||||
spaceFailed\x104\x12\x20\n\x1cDefaultWorkspaceAlreadyExist\x105\x12\x10\
|
||||
\n\x0cNetworkError\x10dJ\xb1\t\n\x06\x12\x04\0\0\x20\x01\n\x08\n\x01\x0c\
|
||||
spaceFailed\x104\x12\x20\n\x1cDefaultWorkspaceAlreadyExist\x105\x12\x0f\
|
||||
\n\x0bServerError\x10dJ\xb1\t\n\x06\x12\x04\0\0\x20\x01\n\x08\n\x01\x0c\
|
||||
\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x05\x01\n\n\n\x03\x04\0\
|
||||
\x01\x12\x03\x02\x08\x11\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x19\n\
|
||||
\x0c\n\x05\x04\0\x02\0\x06\x12\x03\x03\x04\x0f\n\x0c\n\x05\x04\0\x02\0\
|
||||
@ -406,8 +406,8 @@ static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\x12\x03\x1d\x04\x20\n\x0c\n\x05\x05\0\x02\x16\x02\x12\x03\x1d#%\n\x0b\n\
|
||||
\x04\x05\0\x02\x17\x12\x03\x1e\x04&\n\x0c\n\x05\x05\0\x02\x17\x01\x12\
|
||||
\x03\x1e\x04\x20\n\x0c\n\x05\x05\0\x02\x17\x02\x12\x03\x1e#%\n\x0b\n\x04\
|
||||
\x05\0\x02\x18\x12\x03\x1f\x04\x17\n\x0c\n\x05\x05\0\x02\x18\x01\x12\x03\
|
||||
\x1f\x04\x10\n\x0c\n\x05\x05\0\x02\x18\x02\x12\x03\x1f\x13\x16b\x06proto\
|
||||
\x05\0\x02\x18\x12\x03\x1f\x04\x16\n\x0c\n\x05\x05\0\x02\x18\x01\x12\x03\
|
||||
\x1f\x04\x0f\n\x0c\n\x05\x05\0\x02\x18\x02\x12\x03\x1f\x12\x15b\x06proto\
|
||||
3\
|
||||
";
|
||||
|
||||
|
@ -29,5 +29,5 @@ enum UserErrCode {
|
||||
UserIdInvalid = 51;
|
||||
CreateDefaultWorkspaceFailed = 52;
|
||||
DefaultWorkspaceAlreadyExist = 53;
|
||||
NetworkError = 100;
|
||||
ServerError = 100;
|
||||
}
|
||||
|
@ -49,14 +49,10 @@ fn sign_in_with_invalid_password() {
|
||||
password,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
UserTestBuilder::new()
|
||||
.event(SignIn)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.error()
|
||||
.code,
|
||||
UserErrCode::PasswordFormatInvalid
|
||||
);
|
||||
UserTestBuilder::new()
|
||||
.event(SignIn)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.assert_error();
|
||||
}
|
||||
}
|
||||
|
@ -49,14 +49,10 @@ fn sign_up_with_invalid_password() {
|
||||
password,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
UserTestBuilder::new()
|
||||
.event(SignUp)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.error()
|
||||
.code,
|
||||
UserErrCode::PasswordFormatInvalid
|
||||
);
|
||||
UserTestBuilder::new()
|
||||
.event(SignUp)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.assert_error();
|
||||
}
|
||||
}
|
||||
|
@ -104,15 +104,11 @@ fn user_update_with_invalid_password() {
|
||||
password: Some(password),
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
UserTestBuilder::new()
|
||||
.event(UpdateUser)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.error()
|
||||
.code,
|
||||
UserErrCode::PasswordFormatInvalid
|
||||
);
|
||||
UserTestBuilder::new()
|
||||
.event(UpdateUser)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.assert_error();
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,13 +124,9 @@ fn user_update_with_invalid_name() {
|
||||
password: None,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
UserTestBuilder::new()
|
||||
.event(UpdateUser)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.error()
|
||||
.code,
|
||||
UserErrCode::UserIdInvalid
|
||||
);
|
||||
UserTestBuilder::new()
|
||||
.event(UpdateUser)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.assert_error();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user