mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Refactor: delete unused crates (#2543)
* refactor: delete user model * refactor: delete user model crate * refactor: rm flowy-server-sync crate * refactor: rm flowy-database and flowy-folder * refactor: rm folder-model * refactor: rm database model * refactor: rm flowy-sync * refactor: rm document-model * refactor: rm flowy-document * refactor: rm flowy-client-sync * refactor: rm ws-model * refactor: rm flowy-revisoin * refactor: rm revision-model * refactor: rm flowy-folder * refactor: rm flowy-client-ws * refactor: move crates * chore: move configuration file * ci: fix tauri build' * ci: fix flutter build * ci: rust test script * ci: tauri pnpm version conflict * ci: tauri build
This commit is contained in:
@ -6,10 +6,9 @@ edition = "2018"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
flowy-derive = { path = "../flowy-derive" }
|
||||
flowy-derive = { path = "../../../shared-lib/flowy-derive" }
|
||||
flowy-sqlite = { path = "../flowy-sqlite", optional = true }
|
||||
flowy-error = { path = "../flowy-error", features = ["adaptor_database", "adaptor_dispatch", "adaptor_user"] }
|
||||
user-model = { path = "../../../shared-lib/user-model" }
|
||||
flowy-error = { path = "../flowy-error", features = ["adaptor_database", "adaptor_dispatch"] }
|
||||
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
||||
flowy-notification = { path = "../flowy-notification" }
|
||||
lib-dispatch = { path = "../lib-dispatch" }
|
||||
@ -29,10 +28,18 @@ parking_lot = "0.12.1"
|
||||
strum = "0.21"
|
||||
strum_macros = "0.21"
|
||||
tokio = { version = "1.26", features = ["rt"] }
|
||||
validator = "0.16.0"
|
||||
unicode-segmentation = "1.10"
|
||||
fancy-regex = "0.11.0"
|
||||
|
||||
[dev-dependencies]
|
||||
flowy-test = { path = "../flowy-test" }
|
||||
nanoid = "0.4.0"
|
||||
fake = "2.0.0"
|
||||
rand = "0.8.4"
|
||||
quickcheck = "1.0.3"
|
||||
rand_core = "0.6.2"
|
||||
quickcheck_macros = "1.0"
|
||||
|
||||
[features]
|
||||
default = ["rev-sqlite"]
|
||||
@ -41,4 +48,4 @@ dart = ["flowy-codegen/dart", "flowy-notification/dart"]
|
||||
ts = ["flowy-codegen/ts", "flowy-notification/ts"]
|
||||
|
||||
[build-dependencies]
|
||||
flowy-codegen = { path = "../flowy-codegen"}
|
||||
flowy-codegen = { path = "../../../shared-lib/flowy-codegen"}
|
||||
|
@ -1,7 +1,11 @@
|
||||
use crate::errors::ErrorCode;
|
||||
use flowy_derive::ProtoBuf;
|
||||
use std::convert::TryInto;
|
||||
use user_model::{SignInParams, SignUpParams, UserEmail, UserName, UserPassword};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use flowy_derive::ProtoBuf;
|
||||
|
||||
use crate::entities::parser::*;
|
||||
use crate::errors::ErrorCode;
|
||||
|
||||
#[derive(ProtoBuf, Default)]
|
||||
pub struct SignInPayloadPB {
|
||||
@ -56,3 +60,91 @@ impl TryInto<SignUpParams> for SignUpPayloadPB {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize, Debug)]
|
||||
pub struct SignInParams {
|
||||
pub email: String,
|
||||
pub password: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||
pub struct SignInResponse {
|
||||
pub user_id: i64,
|
||||
pub name: String,
|
||||
pub email: String,
|
||||
pub token: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, Debug)]
|
||||
pub struct SignUpParams {
|
||||
pub email: String,
|
||||
pub name: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||
pub struct SignUpResponse {
|
||||
pub user_id: i64,
|
||||
pub name: String,
|
||||
pub email: String,
|
||||
pub token: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, Debug, Clone)]
|
||||
pub struct UserProfile {
|
||||
pub id: i64,
|
||||
pub email: String,
|
||||
pub name: String,
|
||||
pub token: String,
|
||||
pub icon_url: String,
|
||||
pub openai_key: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, Clone, Debug)]
|
||||
pub struct UpdateUserProfileParams {
|
||||
pub id: i64,
|
||||
pub name: Option<String>,
|
||||
pub email: Option<String>,
|
||||
pub password: Option<String>,
|
||||
pub icon_url: Option<String>,
|
||||
pub openai_key: Option<String>,
|
||||
}
|
||||
|
||||
impl UpdateUserProfileParams {
|
||||
pub fn new(id: i64) -> Self {
|
||||
Self {
|
||||
id,
|
||||
name: None,
|
||||
email: None,
|
||||
password: None,
|
||||
icon_url: None,
|
||||
openai_key: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(mut self, name: &str) -> Self {
|
||||
self.name = Some(name.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn email(mut self, email: &str) -> Self {
|
||||
self.email = Some(email.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn password(mut self, password: &str) -> Self {
|
||||
self.password = Some(password.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn icon_url(mut self, icon_url: &str) -> Self {
|
||||
self.icon_url = Some(icon_url.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn openai_key(mut self, openai_key: &str) -> Self {
|
||||
self.openai_key = Some(openai_key.to_owned());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -3,5 +3,6 @@ pub use user_profile::*;
|
||||
pub use user_setting::*;
|
||||
|
||||
pub mod auth;
|
||||
pub mod parser;
|
||||
mod user_profile;
|
||||
mod user_setting;
|
||||
|
16
frontend/rust-lib/flowy-user/src/entities/parser/mod.rs
Normal file
16
frontend/rust-lib/flowy-user/src/entities/parser/mod.rs
Normal file
@ -0,0 +1,16 @@
|
||||
// https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
|
||||
mod user_email;
|
||||
mod user_icon;
|
||||
mod user_id;
|
||||
mod user_name;
|
||||
mod user_openai_key;
|
||||
mod user_password;
|
||||
mod user_workspace;
|
||||
|
||||
pub use user_email::*;
|
||||
pub use user_icon::*;
|
||||
pub use user_id::*;
|
||||
pub use user_name::*;
|
||||
pub use user_openai_key::*;
|
||||
pub use user_password::*;
|
||||
pub use user_workspace::*;
|
@ -0,0 +1,73 @@
|
||||
use flowy_error::ErrorCode;
|
||||
use validator::validate_email;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UserEmail(pub String);
|
||||
|
||||
impl UserEmail {
|
||||
pub fn parse(s: String) -> Result<UserEmail, ErrorCode> {
|
||||
if s.trim().is_empty() {
|
||||
return Err(ErrorCode::EmailIsEmpty);
|
||||
}
|
||||
|
||||
if validate_email(&s) {
|
||||
Ok(Self(s))
|
||||
} else {
|
||||
Err(ErrorCode::EmailFormatInvalid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for UserEmail {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use fake::{faker::internet::en::SafeEmail, Fake};
|
||||
use rand::prelude::StdRng;
|
||||
use rand_core::SeedableRng;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn empty_string_is_rejected() {
|
||||
let email = "".to_string();
|
||||
assert!(UserEmail::parse(email).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn email_missing_at_symbol_is_rejected() {
|
||||
let email = "helloworld.com".to_string();
|
||||
assert!(UserEmail::parse(email).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn email_missing_subject_is_rejected() {
|
||||
let email = "@domain.com".to_string();
|
||||
assert!(UserEmail::parse(email).is_err());
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct ValidEmailFixture(pub String);
|
||||
|
||||
impl quickcheck::Arbitrary for ValidEmailFixture {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let mut rand_slice: [u8; 32] = [0; 32];
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for i in 0..32 {
|
||||
rand_slice[i] = u8::arbitrary(g);
|
||||
}
|
||||
let mut seed = StdRng::from_seed(rand_slice);
|
||||
let email = SafeEmail().fake_with_rng(&mut seed);
|
||||
Self(email)
|
||||
}
|
||||
}
|
||||
|
||||
#[quickcheck_macros::quickcheck]
|
||||
fn valid_emails_are_parsed_successfully(valid_email: ValidEmailFixture) -> bool {
|
||||
UserEmail::parse(valid_email.0).is_ok()
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
use flowy_error::ErrorCode;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UserIcon(pub String);
|
||||
|
||||
impl UserIcon {
|
||||
pub fn parse(s: String) -> Result<UserIcon, ErrorCode> {
|
||||
Ok(Self(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for UserIcon {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
20
frontend/rust-lib/flowy-user/src/entities/parser/user_id.rs
Normal file
20
frontend/rust-lib/flowy-user/src/entities/parser/user_id.rs
Normal file
@ -0,0 +1,20 @@
|
||||
use flowy_error::ErrorCode;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UserId(pub String);
|
||||
|
||||
impl UserId {
|
||||
pub fn parse(s: String) -> Result<UserId, ErrorCode> {
|
||||
let is_empty_or_whitespace = s.trim().is_empty();
|
||||
if is_empty_or_whitespace {
|
||||
return Err(ErrorCode::UserIdInvalid);
|
||||
}
|
||||
Ok(Self(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for UserId {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use flowy_error::ErrorCode;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UserName(pub String);
|
||||
|
||||
impl UserName {
|
||||
pub fn parse(s: String) -> Result<UserName, ErrorCode> {
|
||||
let is_empty_or_whitespace = s.trim().is_empty();
|
||||
if is_empty_or_whitespace {
|
||||
return Err(ErrorCode::UserNameIsEmpty);
|
||||
}
|
||||
// A grapheme is defined by the Unicode standard as a "user-perceived"
|
||||
// character: `å` is a single grapheme, but it is composed of two characters
|
||||
// (`a` and `̊`).
|
||||
//
|
||||
// `graphemes` returns an iterator over the graphemes in the input `s`.
|
||||
// `true` specifies that we want to use the extended grapheme definition set,
|
||||
// the recommended one.
|
||||
let is_too_long = s.graphemes(true).count() > 256;
|
||||
if is_too_long {
|
||||
return Err(ErrorCode::UserNameTooLong);
|
||||
}
|
||||
|
||||
let forbidden_characters = ['/', '(', ')', '"', '<', '>', '\\', '{', '}'];
|
||||
let contains_forbidden_characters = s.chars().any(|g| forbidden_characters.contains(&g));
|
||||
|
||||
if contains_forbidden_characters {
|
||||
return Err(ErrorCode::UserNameContainForbiddenCharacters);
|
||||
}
|
||||
|
||||
Ok(Self(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for UserName {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::UserName;
|
||||
|
||||
#[test]
|
||||
fn a_256_grapheme_long_name_is_valid() {
|
||||
let name = "a̐".repeat(256);
|
||||
assert!(UserName::parse(name).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_name_longer_than_256_graphemes_is_rejected() {
|
||||
let name = "a".repeat(257);
|
||||
assert!(UserName::parse(name).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn whitespace_only_names_are_rejected() {
|
||||
let name = " ".to_string();
|
||||
assert!(UserName::parse(name).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_string_is_rejected() {
|
||||
let name = "".to_string();
|
||||
assert!(UserName::parse(name).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn names_containing_an_invalid_character_are_rejected() {
|
||||
for name in &['/', '(', ')', '"', '<', '>', '\\', '{', '}'] {
|
||||
let name = name.to_string();
|
||||
assert!(UserName::parse(name).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_valid_name_is_parsed_successfully() {
|
||||
let name = "nathan".to_string();
|
||||
assert!(UserName::parse(name).is_ok());
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
use flowy_error::ErrorCode;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UserOpenaiKey(pub String);
|
||||
|
||||
impl UserOpenaiKey {
|
||||
pub fn parse(s: String) -> Result<UserOpenaiKey, ErrorCode> {
|
||||
Ok(Self(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for UserOpenaiKey {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
use lazy_static::lazy_static;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use fancy_regex::Regex;
|
||||
use flowy_error::ErrorCode;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UserPassword(pub String);
|
||||
|
||||
impl UserPassword {
|
||||
pub fn parse(s: String) -> Result<UserPassword, ErrorCode> {
|
||||
if s.trim().is_empty() {
|
||||
return Err(ErrorCode::PasswordIsEmpty);
|
||||
}
|
||||
|
||||
if s.graphemes(true).count() > 100 {
|
||||
return Err(ErrorCode::PasswordTooLong);
|
||||
}
|
||||
|
||||
let forbidden_characters = ['/', '(', ')', '"', '<', '>', '\\', '{', '}'];
|
||||
let contains_forbidden_characters = s.chars().any(|g| forbidden_characters.contains(&g));
|
||||
if contains_forbidden_characters {
|
||||
return Err(ErrorCode::PasswordContainsForbidCharacters);
|
||||
}
|
||||
|
||||
if !validate_password(&s) {
|
||||
return Err(ErrorCode::PasswordFormatInvalid);
|
||||
}
|
||||
|
||||
Ok(Self(s))
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// Hell1!
|
||||
// [invalid, greater or equal to 6]
|
||||
// Hel1!
|
||||
//
|
||||
// Hello1!
|
||||
// [invalid, must include number]
|
||||
// Hello!
|
||||
//
|
||||
// Hello12!
|
||||
// [invalid must include upper case]
|
||||
// hello12!
|
||||
static ref PASSWORD: Regex = Regex::new("((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\\W]).{6,20})").unwrap();
|
||||
}
|
||||
|
||||
pub fn validate_password(password: &str) -> bool {
|
||||
match PASSWORD.is_match(password) {
|
||||
Ok(is_match) => is_match,
|
||||
Err(e) => {
|
||||
tracing::error!("validate_password fail: {:?}", e);
|
||||
false
|
||||
},
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
use flowy_error::ErrorCode;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UserWorkspace(pub String);
|
||||
|
||||
impl UserWorkspace {
|
||||
pub fn parse(s: String) -> Result<UserWorkspace, ErrorCode> {
|
||||
let is_empty_or_whitespace = s.trim().is_empty();
|
||||
if is_empty_or_whitespace {
|
||||
return Err(ErrorCode::WorkspaceIdInvalid);
|
||||
}
|
||||
Ok(Self(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for UserWorkspace {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
use crate::errors::ErrorCode;
|
||||
use flowy_derive::ProtoBuf;
|
||||
use std::convert::TryInto;
|
||||
use user_model::{
|
||||
UpdateUserProfileParams, UserEmail, UserIcon, UserName, UserOpenaiKey, UserPassword, UserProfile,
|
||||
};
|
||||
|
||||
use flowy_derive::ProtoBuf;
|
||||
|
||||
use crate::entities::parser::{UserEmail, UserIcon, UserName, UserOpenaiKey, UserPassword};
|
||||
use crate::entities::{UpdateUserProfileParams, UserProfile};
|
||||
use crate::errors::ErrorCode;
|
||||
|
||||
#[derive(Default, ProtoBuf)]
|
||||
pub struct UserTokenPB {
|
||||
|
@ -1,10 +1,12 @@
|
||||
use crate::entities::*;
|
||||
use crate::services::UserSession;
|
||||
use std::{convert::TryInto, sync::Arc};
|
||||
|
||||
use flowy_error::FlowyError;
|
||||
use flowy_sqlite::kv::KV;
|
||||
use lib_dispatch::prelude::*;
|
||||
use std::{convert::TryInto, sync::Arc};
|
||||
use user_model::{SignInParams, SignUpParams, UpdateUserProfileParams};
|
||||
|
||||
use crate::entities::*;
|
||||
use crate::entities::{SignInParams, SignUpParams, UpdateUserProfileParams};
|
||||
use crate::services::UserSession;
|
||||
|
||||
// tracing instrument 👉🏻 https://docs.rs/tracing/0.1.26/tracing/attr.instrument.html
|
||||
#[tracing::instrument(level = "debug", name = "sign_in", skip(data, session), fields(email = %data.email), err)]
|
||||
|
@ -1,15 +1,18 @@
|
||||
use crate::entities::UserProfilePB;
|
||||
use crate::event_handler::*;
|
||||
use crate::{errors::FlowyError, services::UserSession};
|
||||
use std::sync::Arc;
|
||||
|
||||
use strum_macros::Display;
|
||||
|
||||
use flowy_derive::{Flowy_Event, ProtoBuf_Enum};
|
||||
use flowy_error::FlowyResult;
|
||||
use lib_dispatch::prelude::*;
|
||||
use lib_infra::future::{Fut, FutureResult};
|
||||
use std::sync::Arc;
|
||||
use strum_macros::Display;
|
||||
use user_model::{
|
||||
|
||||
use crate::entities::UserProfilePB;
|
||||
use crate::entities::{
|
||||
SignInParams, SignInResponse, SignUpParams, SignUpResponse, UpdateUserProfileParams, UserProfile,
|
||||
};
|
||||
use crate::event_handler::*;
|
||||
use crate::{errors::FlowyError, services::UserSession};
|
||||
|
||||
pub fn init(user_session: Arc<UserSession>) -> AFPlugin {
|
||||
AFPlugin::new()
|
||||
|
@ -8,7 +8,8 @@ use parking_lot::RwLock;
|
||||
use flowy_error::FlowyError;
|
||||
use flowy_sqlite::ConnectionPool;
|
||||
use flowy_sqlite::{schema::user_table, DBConnection, Database};
|
||||
use user_model::{SignInResponse, SignUpResponse, UpdateUserProfileParams, UserProfile};
|
||||
|
||||
use crate::entities::{SignInResponse, SignUpResponse, UpdateUserProfileParams, UserProfile};
|
||||
|
||||
pub struct UserDB {
|
||||
db_dir: String,
|
||||
|
@ -11,10 +11,10 @@ use flowy_sqlite::{
|
||||
schema::{user_table, user_table::dsl},
|
||||
DBConnection, ExpressionMethods, UserDatabaseConnection,
|
||||
};
|
||||
use user_model::{
|
||||
|
||||
use crate::entities::{
|
||||
SignInParams, SignInResponse, SignUpParams, SignUpResponse, UpdateUserProfileParams, UserProfile,
|
||||
};
|
||||
|
||||
use crate::entities::{UserProfilePB, UserSettingPB};
|
||||
use crate::event_map::UserStatusCallback;
|
||||
use crate::{
|
||||
@ -24,9 +24,6 @@ use crate::{
|
||||
services::database::{UserDB, UserTable, UserTableChangeset},
|
||||
};
|
||||
|
||||
// lazy_static! {
|
||||
// static ref ID_GEN: Mutex<UserIDGenerator> = Mutex::new(UserIDGenerator::new(1));
|
||||
// }
|
||||
pub struct UserSessionConfig {
|
||||
root_dir: String,
|
||||
|
||||
|
Reference in New Issue
Block a user