config user db and user session

This commit is contained in:
appflowy 2021-07-09 23:31:44 +08:00
parent 13e52ae350
commit b9d7902acb
33 changed files with 255 additions and 122 deletions

View File

@ -16,8 +16,8 @@
<sourceFolder url="file://$MODULE_DIR$/scripts/flowy-tool/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/rust-lib/flowy-test/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/rust-lib/flowy-user/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/rust-lib/flowy-db/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/rust-lib/flowy-infra/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/rust-lib/flowy-database/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/rust-lib/flowy-sqlite/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/app_flowy/packages/af_protobuf/.pub" />
<excludeFolder url="file://$MODULE_DIR$/app_flowy/packages/af_protobuf/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/app_flowy/packages/af_protobuf/build" />

View File

@ -1,5 +1,5 @@
PODS:
- flowy_infra (0.0.1):
- flowy_sqlite (0.0.1):
- FlutterMacOS
- flowy_sdk (0.0.1):
- FlutterMacOS
@ -9,15 +9,15 @@ PODS:
- FlutterMacOS
DEPENDENCIES:
- flowy_infra (from `Flutter/ephemeral/.symlinks/plugins/flowy_infra/macos`)
- flowy_sqlite (from `Flutter/ephemeral/.symlinks/plugins/flowy_sqlite/macos`)
- flowy_sdk (from `Flutter/ephemeral/.symlinks/plugins/flowy_sdk/macos`)
- FlutterMacOS (from `Flutter/ephemeral/.symlinks/flutter/darwin-x64`)
- path_provider (from `Flutter/ephemeral/.symlinks/plugins/path_provider/macos`)
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
EXTERNAL SOURCES:
flowy_infra:
:path: Flutter/ephemeral/.symlinks/plugins/flowy_infra/macos
flowy_sqlite:
:path: Flutter/ephemeral/.symlinks/plugins/flowy_sqlite/macos
flowy_sdk:
:path: Flutter/ephemeral/.symlinks/plugins/flowy_sdk/macos
FlutterMacOS:
@ -28,7 +28,7 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
SPEC CHECKSUMS:
flowy_infra: 6638deea1ca0baeef75156d16236123d4703161d
flowy_sqlite: 6638deea1ca0baeef75156d16236123d4703161d
flowy_sdk: 12d2c047ed260a0aa8788a0b9616da46e2312025
FlutterMacOS: 15bea8a44d2fa024068daa0140371c020b4b6ff9
path_provider: e0848572d1d38b9a7dd099e79cf83f5b7e2cde9f

View File

@ -8,8 +8,8 @@ members = [
"flowy-ast",
"flowy-derive",
"flowy-test",
"flowy-infra",
"flowy-db",
"flowy-sqlite",
"flowy-database",
]
[profile.dev]

View File

@ -1,5 +1,5 @@
[package]
name = "flowy-db"
name = "flowy-database"
version = "0.1.0"
edition = "2018"
@ -9,4 +9,4 @@ edition = "2018"
diesel = {version = "1.4.7", features = ["sqlite"]}
diesel_derives = {version = "1.4.1", features = ["sqlite"]}
diesel_migrations = {version = "1.4.0", features = ["sqlite"]}
flowy-infra = {path = "../flowy-infra"}
flowy-sqlite = {path = "../flowy-sqlite" }

View File

@ -0,0 +1,16 @@
use flowy_sqlite::Error;
use std::io;
#[derive(Debug)]
pub enum DataBaseError {
InitError(String),
IOError(String),
}
impl std::convert::From<flowy_sqlite::Error> for DataBaseError {
fn from(error: flowy_sqlite::Error) -> Self { DataBaseError::InitError(format!("{:?}", error)) }
}
impl std::convert::From<io::Error> for DataBaseError {
fn from(error: io::Error) -> Self { DataBaseError::IOError(format!("{:?}", error)) }
}

View File

@ -0,0 +1,29 @@
mod errors;
mod schema;
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_derives;
#[macro_use]
extern crate diesel_migrations;
pub use errors::*;
pub use flowy_sqlite::{DBConnection, DataBase};
use diesel_migrations::*;
use flowy_sqlite::PoolConfig;
use std::path::Path;
embed_migrations!("../flowy-database/migrations/");
pub const DB_NAME: &str = "flowy-database.db";
pub fn init(storage_path: &str) -> Result<DataBase, DataBaseError> {
if !Path::new(storage_path).exists() {
std::fs::create_dir_all(storage_path)?;
}
let pool_config = PoolConfig::default();
let database = DataBase::new(storage_path, DB_NAME, pool_config)?;
Ok(database)
}

View File

@ -1,17 +0,0 @@
use crate::errors::FlowyDBError;
use diesel_migrations::*;
use flowy_infra::sqlite::*;
use std::path::Path;
embed_migrations!("../flowy-db/migrations/");
pub const DB_NAME: &str = "flowy-database.db";
pub fn init(storage_path: &str) -> Result<DataBase, FlowyDBError> {
if !Path::new(storage_path).exists() {
std::fs::create_dir_all(storage_path)?;
}
let pool_config = PoolConfig::default();
let database = DataBase::new(storage_path, DB_NAME, pool_config)?;
Ok(database)
}

View File

@ -1,16 +0,0 @@
use flowy_infra::Error;
use std::io;
#[derive(Debug)]
pub enum FlowyDBError {
InitError(String),
IOError(String),
}
impl std::convert::From<flowy_infra::Error> for FlowyDBError {
fn from(error: flowy_infra::Error) -> Self { FlowyDBError::InitError(format!("{:?}", error)) }
}
impl std::convert::From<io::Error> for FlowyDBError {
fn from(error: io::Error) -> Self { FlowyDBError::IOError(format!("{:?}", error)) }
}

View File

@ -1,15 +0,0 @@
mod database;
mod errors;
mod schema;
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_derives;
#[macro_use]
extern crate diesel_migrations;
pub use flowy_infra::sqlite::DataBase;
pub use database::init;
pub use errors::*;

View File

@ -1,13 +0,0 @@
#[allow(deprecated, clippy::large_enum_variant)]
mod errors;
pub mod sqlite;
pub use errors::{Error, ErrorKind, Result};
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

View File

@ -1,5 +0,0 @@
mod database;
mod pool;
pub use database::*;
pub use pool::*;

View File

@ -3,6 +3,7 @@ pub use module::*;
use flowy_dispatch::prelude::*;
use module::build_modules;
pub struct FlowySDK {}
impl FlowySDK {
@ -10,6 +11,11 @@ impl FlowySDK {
pub fn init(path: &str) {
tracing::trace!("🔥 Root path: {}", path);
EventDispatch::construct(|| build_modules());
let config = ModuleConfig {
root: path.to_string(),
};
EventDispatch::construct(|| build_modules(config));
}
}

View File

@ -1,3 +1,14 @@
use flowy_dispatch::prelude::Module;
use flowy_user::prelude::user_session::{UserSession, UserSessionConfig};
use std::sync::Arc;
pub fn build_modules() -> Vec<Module> { vec![flowy_user::module::create()] }
pub struct ModuleConfig {
pub root: String,
}
pub fn build_modules(config: ModuleConfig) -> Vec<Module> {
let user_config = UserSessionConfig::new(&config.root);
let user_session = Arc::new(UserSession::new(user_config));
vec![flowy_user::module::create(user_session)]
}

View File

@ -1,5 +1,5 @@
[package]
name = "flowy-infra"
name = "flowy-sqlite"
version = "0.1.0"
edition = "2018"

View File

@ -1,6 +1,6 @@
use crate::{
errors::*,
sqlite::pool::{ConnectionManager, ConnectionPool, PoolConfig},
pool::{ConnectionManager, ConnectionPool, PoolConfig},
};
use r2d2::PooledConnection;
@ -9,6 +9,8 @@ pub struct DataBase {
pool: ConnectionPool,
}
pub type DBConnection = PooledConnection<ConnectionManager>;
impl DataBase {
pub fn new(dir: &str, name: &str, pool_config: PoolConfig) -> Result<Self> {
let uri = db_file_uri(dir, name);
@ -18,7 +20,7 @@ impl DataBase {
pub fn get_uri(&self) -> &str { &self.uri }
pub fn get_conn(&self) -> Result<PooledConnection<ConnectionManager>> {
pub fn get_connection(&self) -> Result<DBConnection> {
let conn = self.pool.get()?;
Ok(conn)
}

View File

@ -0,0 +1,9 @@
mod database;
#[allow(deprecated, clippy::large_enum_variant)]
mod errors;
mod pool;
pub use database::*;
pub use pool::*;
pub use errors::{Error, ErrorKind, Result};

View File

@ -0,0 +1 @@

View File

@ -10,7 +10,8 @@ derive_more = {version = "0.99", features = ["display"]}
flowy-dispatch = { path = "../flowy-dispatch" }
flowy-log = { path = "../flowy-log" }
flowy-derive = { path = "../flowy-derive" }
flowy-db = { path = "../flowy-db" }
flowy-database = { path = "../flowy-database" }
flowy-sqlite = { path = "../flowy-sqlite" }
tracing = { version = "0.1", features = ["log"] }
bytes = "1.0"

View File

@ -1,28 +0,0 @@
use crate::errors::UserError;
use flowy_db::DataBase;
use lazy_static::lazy_static;
use std::sync::{
atomic::{AtomicBool, Ordering},
RwLock,
};
lazy_static! {
pub static ref DB: RwLock<Option<DataBase>> = RwLock::new(None);
}
static DB_INIT: AtomicBool = AtomicBool::new(false);
pub fn init_user_db(dir: &str) -> Result<(), UserError> {
let database = flowy_db::init(dir)?;
*(DB.write()?) = Some(database);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn init_db_test() {
// init_user_db(".").unwrap();
}
}

View File

@ -1,4 +1,4 @@
pub use user::*;
mod database;
pub mod user;
pub mod user_db;
pub mod user_session;

View File

@ -0,0 +1,87 @@
use crate::errors::UserError;
use flowy_database::{DBConnection, DataBase};
use lazy_static::lazy_static;
use std::{
cell::RefCell,
sync::{
atomic::{AtomicBool, Ordering},
RwLock,
},
};
thread_local! {
static USER_ID: RefCell<Option<String>> = RefCell::new(None);
}
fn set_user_id(user_id: Option<String>) {
USER_ID.with(|id| {
*id.borrow_mut() = user_id;
});
}
fn get_user_id() -> Option<String> { USER_ID.with(|id| id.borrow().clone()) }
static IS_USER_DB_INIT: AtomicBool = AtomicBool::new(false);
lazy_static! {
static ref USER_DB_INNER: RwLock<Option<DataBase>> = RwLock::new(None);
}
pub(crate) struct UserDB {
db_dir: String,
}
impl UserDB {
pub(crate) fn new(db_dir: &str) -> Self {
Self {
db_dir: db_dir.to_owned(),
}
}
fn open_user_db(&self, user_id: &str) -> Result<(), UserError> {
let user_dir = format!("{}/{}", self.db_dir, user_id);
let database = flowy_database::init(&user_dir)?;
let mut write_guard = USER_DB_INNER.write()?;
set_user_id(Some(user_id.to_owned()));
*(write_guard) = Some(database);
IS_USER_DB_INIT.store(true, Ordering::SeqCst);
Ok(())
}
pub(crate) fn close_user_db(&mut self) -> Result<(), UserError> {
let mut write_guard = USER_DB_INNER.write()?;
*write_guard = None;
set_user_id(None);
IS_USER_DB_INIT.store(false, Ordering::SeqCst);
Ok(())
}
pub(crate) fn get_connection(&self, user_id: &str) -> Result<DBConnection, UserError> {
if !IS_USER_DB_INIT.load(Ordering::SeqCst) {
let _ = self.open_user_db(user_id);
}
let thread_user_id = get_user_id();
if thread_user_id != Some(user_id.to_owned()) {
let msg = format!(
"DataBase owner does not match. origin: {:?}, current: {}",
thread_user_id, user_id
);
log::error!("{}", msg);
return Err(UserError::DBConnection(msg));
}
let read_guard = USER_DB_INNER.read()?;
match read_guard.as_ref() {
None => Err(UserError::DBNotInit),
Some(database) => Ok(database.get_connection()?),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn init_db_test() {
// init_user_db(".").unwrap();
}
}

View File

@ -0,0 +1,48 @@
use crate::{domain::user_db::UserDB, errors::UserError};
use flowy_sqlite::DBConnection;
use lazy_static::lazy_static;
use std::sync::RwLock;
lazy_static! {
pub static ref CURRENT_USER_ID: RwLock<Option<String>> = RwLock::new(None);
}
fn get_current_user_id() -> Result<Option<String>, UserError> {
match CURRENT_USER_ID.read() {
Ok(read_guard) => Ok((*read_guard).clone()),
Err(e) => {
log::error!("Get current user id failed: {:?}", e);
Err(e.into())
},
}
}
pub struct UserSessionConfig {
root_dir: String,
}
impl UserSessionConfig {
pub fn new(root_dir: &str) -> Self {
Self {
root_dir: root_dir.to_owned(),
}
}
}
pub struct UserSession {
db: UserDB,
config: UserSessionConfig,
}
impl UserSession {
pub fn new(config: UserSessionConfig) -> Self {
let db = UserDB::new(&config.root_dir);
Self { db, config }
}
pub fn get_db_connection(&self) -> Result<DBConnection, UserError> {
match get_current_user_id()? {
None => Err(UserError::UserNotLogin),
Some(user_id) => self.db.get_connection(&user_id),
}
}
}

View File

@ -1,15 +1,23 @@
use flowy_database::DataBaseError;
use std::sync::PoisonError;
#[derive(Debug)]
pub enum UserError {
DBInitFail(String),
DBInit(String),
DBNotInit,
UserNotLogin,
DBConnection(String),
PoisonError(String),
}
impl std::convert::From<flowy_db::FlowyDBError> for UserError {
fn from(error: flowy_db::FlowyDBError) -> Self { UserError::DBInitFail(format!("{:?}", error)) }
impl std::convert::From<DataBaseError> for UserError {
fn from(error: DataBaseError) -> Self { UserError::DBInit(format!("{:?}", error)) }
}
impl<T> std::convert::From<PoisonError<T>> for UserError {
fn from(error: PoisonError<T>) -> Self { UserError::PoisonError(format!("{:?}", error)) }
}
impl std::convert::From<flowy_sqlite::Error> for UserError {
fn from(e: flowy_sqlite::Error) -> Self { UserError::DBConnection(format!("{:?}", e)) }
}

View File

@ -1,6 +1,6 @@
use crate::domain::user::*;
use crate::domain::{user::*, user_session::UserSession};
use flowy_dispatch::prelude::*;
use std::convert::TryInto;
use std::{convert::TryInto, sync::Arc};
// tracing instrument 👉🏻 https://docs.rs/tracing/0.1.26/tracing/attr.instrument.html
#[tracing::instrument(
@ -19,12 +19,15 @@ pub async fn user_sign_in(data: Data<SignInRequest>) -> ResponseResult<SignInRes
#[tracing::instrument(
name = "user_sign_up",
skip(data),
skip(data, session),
fields(
email = %data.email,
)
)]
pub async fn user_sign_up(data: Data<SignUpRequest>) -> ResponseResult<SignUpResponse, String> {
pub async fn user_sign_up(
data: Data<SignUpRequest>,
session: ModuleData<Arc<UserSession>>,
) -> ResponseResult<SignUpResponse, String> {
let _params: SignUpParams = data.into_inner().try_into()?;
// TODO: user sign up

View File

@ -1,10 +1,16 @@
use flowy_dispatch::prelude::*;
use crate::{event::UserEvent, handlers::*};
use crate::{
domain::{user_db::*, user_session::UserSession},
event::UserEvent,
handlers::*,
};
use std::sync::Arc;
pub fn create() -> Module {
pub fn create(user_session: Arc<UserSession>) -> Module {
Module::new()
.name("Flowy-User")
.data(user_session)
.event(UserEvent::SignIn, user_sign_in)
.event(UserEvent::SignUp, user_sign_up)
}