Browse Source

setup flowy sdk server configuration

appflowy 3 năm trước cách đây
mục cha
commit
5a89655982
36 tập tin đã thay đổi với 270 bổ sung123 xóa
  1. 2 0
      backend/Cargo.lock
  2. 3 0
      backend/Makefile
  3. 0 1
      backend/src/config/configuration.rs
  4. 1 1
      backend/src/entities/token.rs
  5. 1 1
      backend/src/middleware/auth_middleware.rs
  6. 4 7
      backend/tests/document/helper.rs
  7. 21 11
      backend/tests/util/helper.rs
  8. 7 1
      frontend/Makefile.toml
  9. 0 1
      frontend/app_flowy/lib/user/presentation/splash_screen.dart
  10. 2 3
      frontend/app_flowy/linux/flutter/dart_ffi/binding.h
  11. 2 2
      frontend/rust-lib/dart-ffi/src/lib.rs
  12. 2 2
      frontend/rust-lib/flowy-document/src/module.rs
  13. 4 2
      frontend/rust-lib/flowy-document/src/services/server/mod.rs
  14. 3 3
      frontend/rust-lib/flowy-document/src/services/server/server_api.rs
  15. 4 4
      frontend/rust-lib/flowy-sdk/src/lib.rs
  16. 5 2
      frontend/rust-lib/flowy-sdk/src/module.rs
  17. 3 3
      frontend/rust-lib/flowy-test/src/lib.rs
  18. 2 2
      frontend/rust-lib/flowy-user/src/services/server/mod.rs
  19. 3 3
      frontend/rust-lib/flowy-user/src/services/server/server_api.rs
  20. 3 3
      frontend/rust-lib/flowy-user/src/services/user/user_session.rs
  21. 2 2
      frontend/rust-lib/flowy-user/src/services/user/ws_manager.rs
  22. 2 2
      frontend/rust-lib/flowy-workspace/src/module.rs
  23. 4 2
      frontend/rust-lib/flowy-workspace/src/services/server/mod.rs
  24. 3 3
      frontend/rust-lib/flowy-workspace/src/services/server/server_api.rs
  25. 4 5
      frontend/scripts/makefile/desktop.toml
  26. 76 0
      shared-lib/Cargo.lock
  27. 2 0
      shared-lib/backend-service/Cargo.toml
  28. 5 0
      shared-lib/backend-service/configuration/base.yaml
  29. 3 0
      shared-lib/backend-service/configuration/local.yaml
  30. 2 0
      shared-lib/backend-service/configuration/production.yaml
  31. 0 53
      shared-lib/backend-service/src/config.rs
  32. 91 0
      shared-lib/backend-service/src/configuration.rs
  33. 1 1
      shared-lib/backend-service/src/lib.rs
  34. 1 1
      shared-lib/backend-service/src/request/request.rs
  35. 1 1
      shared-lib/backend-service/src/user_request.rs
  36. 1 1
      shared-lib/backend-service/src/workspace_request.rs

+ 2 - 0
backend/Cargo.lock

@@ -503,6 +503,7 @@ dependencies = [
  "actix-web",
  "anyhow",
  "bytes",
+ "config",
  "derive_more",
  "flowy-user-infra",
  "flowy-workspace-infra",
@@ -512,6 +513,7 @@ dependencies = [
  "protobuf",
  "reqwest",
  "serde",
+ "serde-aux",
  "serde_json",
  "serde_repr",
  "thiserror",

+ 3 - 0
backend/Makefile

@@ -10,6 +10,9 @@ docker_image:
 	source $(ROOT)/docker_env.sh && docker-compose up -d db
 	source $(ROOT)/docker_env.sh && docker-compose up -d backend
 
+local_server:
+	cargo run
+
 docker_test:
 	sh $(ROOT)/docker_test.sh
 

+ 0 - 1
backend/src/config/configuration.rs

@@ -56,7 +56,6 @@ pub fn get_configuration() -> Result<Settings, config::ConfigError> {
     let mut settings = config::Config::default();
     let base_path = std::env::current_dir().expect("Failed to determine the current directory");
     let configuration_dir = base_path.join("configuration");
-
     settings.merge(config::File::from(configuration_dir.join("base")).required(true))?;
 
     let environment: Environment = std::env::var("APP_ENVIRONMENT")

+ 1 - 1
backend/src/entities/token.rs

@@ -76,7 +76,7 @@ impl Token {
 
 use crate::service::user::EXPIRED_DURATION_DAYS;
 use actix_web::{dev::Payload, FromRequest, HttpRequest};
-use backend_service::config::HEADER_TOKEN;
+use backend_service::configuration::HEADER_TOKEN;
 use futures::future::{ready, Ready};
 
 impl FromRequest for Token {

+ 1 - 1
backend/src/middleware/auth_middleware.rs

@@ -9,7 +9,7 @@ use actix_web::{
 
 use crate::config::IGNORE_ROUTES;
 use actix_web::{body::AnyBody, dev::MessageBody};
-use backend_service::{config::HEADER_TOKEN, errors::ServerError};
+use backend_service::{configuration::HEADER_TOKEN, errors::ServerError};
 use futures::future::{ok, LocalBoxFuture, Ready};
 use std::{
     convert::TryInto,

+ 4 - 7
backend/tests/document/helper.rs

@@ -2,7 +2,6 @@
 #![cfg_attr(rustfmt, rustfmt::skip)]
 use actix_web::web::Data;
 use backend::service::doc::{crud::update_doc, manager::DocManager};
-use backend_service::config::ServerConfig;
 use flowy_document::services::doc::ClientDocEditor as ClientEditDocContext;
 use flowy_test::{workspace::ViewTest, FlowyTest};
 use flowy_user::services::user::UserSession;
@@ -29,14 +28,12 @@ pub enum DocScript {
     AssertClient(&'static str),
     AssertServer(&'static str, i64),
     ServerSaveDocument(String, i64), // delta_json, rev_id
-    Sleep(u64),
 }
 
 impl DocumentTest {
     pub async fn new() -> Self {
         let server = spawn_server().await;
-        let server_config = ServerConfig::new(&server.host, "http", "ws");
-        let flowy_test = FlowyTest::setup_with(server_config);
+        let flowy_test = FlowyTest::setup_with(server.client_server_config.clone());
         Self { server, flowy_test }
     }
 
@@ -136,9 +133,9 @@ async fn run_scripts(context: Arc<RwLock<ScriptContext>>, scripts: Vec<DocScript
                     let pg_pool = context.read().server_pg_pool.clone();
                     save_doc(&doc_id, json, rev_id, pg_pool).await;
                 },
-                DocScript::Sleep(sec) => {
-                    sleep(Duration::from_secs(sec)).await;
-                },
+                // DocScript::Sleep(sec) => {
+                //     sleep(Duration::from_secs(sec)).await;
+                // },
             }
         };
         fut_scripts.push(fut);

+ 21 - 11
backend/tests/util/helper.rs

@@ -3,7 +3,12 @@ use backend::{
     config::{get_configuration, DatabaseSettings},
     context::AppContext,
 };
-use backend_service::{errors::ServerError, user_request::*, workspace_request::*};
+use backend_service::{
+    configuration::{get_client_server_configuration, ClientServerConfiguration},
+    errors::ServerError,
+    user_request::*,
+    workspace_request::*,
+};
 use flowy_document::services::server::read_doc_request;
 use flowy_document_infra::entities::doc::{Doc, DocIdentifier};
 use flowy_user_infra::entities::*;
@@ -12,11 +17,10 @@ use sqlx::{Connection, Executor, PgConnection, PgPool};
 use uuid::Uuid;
 
 pub struct TestUserServer {
-    pub host: String,
-    pub port: u16,
     pub pg_pool: PgPool,
     pub user_token: Option<String>,
     pub user_id: Option<String>,
+    pub client_server_config: ClientServerConfiguration,
 }
 
 impl TestUserServer {
@@ -167,19 +171,24 @@ impl TestUserServer {
         response
     }
 
-    pub fn http_addr(&self) -> String { format!("http://{}", self.host) }
+    pub fn http_addr(&self) -> String { self.client_server_config.base_url() }
 
-    pub fn ws_addr(&self) -> String { format!("ws://{}/ws/{}", self.host, self.user_token.as_ref().unwrap()) }
+    pub fn ws_addr(&self) -> String {
+        format!(
+            "{}/{}",
+            self.client_server_config.ws_addr(),
+            self.user_token.as_ref().unwrap()
+        )
+    }
 }
 
 impl std::convert::From<TestServer> for TestUserServer {
     fn from(server: TestServer) -> Self {
         TestUserServer {
-            host: server.host,
-            port: server.port,
             pg_pool: server.pg_pool,
             user_token: None,
             user_id: None,
+            client_server_config: server.client_server_config.clone(),
         }
     }
 }
@@ -190,10 +199,9 @@ pub async fn spawn_user_server() -> TestUserServer {
 }
 
 pub struct TestServer {
-    pub host: String,
-    pub port: u16,
     pub pg_pool: PgPool,
     pub app_ctx: AppContext,
+    pub client_server_config: ClientServerConfiguration,
 }
 
 pub async fn spawn_server() -> TestServer {
@@ -218,13 +226,15 @@ pub async fn spawn_server() -> TestServer {
         // drop_test_database(database_name).await;
     });
 
+    let mut client_server_config = get_client_server_configuration().expect("Failed to read configuration.");
+    client_server_config.reset_host_with_port("localhost", application_port);
+
     TestServer {
-        host: format!("localhost:{}", application_port),
-        port: application_port,
         pg_pool: get_connection_pool(&configuration.database)
             .await
             .expect("Failed to connect to the database"),
         app_ctx,
+        client_server_config,
     }
 }
 

+ 7 - 1
frontend/Makefile.toml

@@ -19,6 +19,7 @@ PRODUCT_NAME = "AppFlowy"
 #CRATE_TYPE: https://doc.rust-lang.org/reference/linkage.html
 CRATE_TYPE = "staticlib"
 SDK_EXT = "a"
+APP_ENVIRONMENT = "local"
 
 [env.development-mac]
 TARGET_OS = "macos"
@@ -33,6 +34,7 @@ TARGET_OS = "macos"
 RUST_COMPILE_TARGET = "aarch64-apple-darwin"
 FLUTTER_OUTPUT_DIR = "Release"
 PRODUCT_EXT = "app"
+APP_ENVIRONMENT = "production"
 
 [env.production-mac-x86]
 BUILD_FLAG = "release"
@@ -40,7 +42,7 @@ TARGET_OS = "macos"
 RUST_COMPILE_TARGET = "x86_64-apple-darwin"
 FLUTTER_OUTPUT_DIR = "Release"
 PRODUCT_EXT = "app"
-
+APP_ENVIRONMENT = "production"
 
 [env.development-windows-x86]
 TARGET_OS = "windows"
@@ -59,6 +61,7 @@ FLUTTER_OUTPUT_DIR = "Release"
 PRODUCT_EXT = "exe"
 CRATE_TYPE = "cdylib"
 SDK_EXT = "dll"
+APP_ENVIRONMENT = "production"
 
 [env.development-linux-x86]
 TARGET_OS = "linux"
@@ -77,6 +80,7 @@ CRATE_TYPE = "cdylib"
 FLUTTER_OUTPUT_DIR = "Release"
 SDK_EXT = "so"
 LINUX_ARCH = "x64"
+APP_ENVIRONMENT = "production"
 
 [env.development-linux-aarch64]
 TARGET_OS = "linux"
@@ -95,6 +99,7 @@ CRATE_TYPE = "cdylib"
 FLUTTER_OUTPUT_DIR = "Release"
 SDK_EXT = "so"
 LINUX_ARCH = "arm64"
+APP_ENVIRONMENT = "production"
 
 [tasks.echo_env]
 script = [
@@ -106,6 +111,7 @@ script = [
     echo RUST_COMPILE_TARGET: ${RUST_COMPILE_TARGET}
     echo FEATURES: ${FEATURES}
     echo PRODUCT_EXT: ${PRODUCT_EXT}
+    echo APP_ENVIRONMENT: ${APP_ENVIRONMENT}
     echo ${platforms}
 
     echo "-------- Flutter --------"

+ 0 - 1
frontend/app_flowy/lib/user/presentation/splash_screen.dart

@@ -57,7 +57,6 @@ class SplashScreen extends StatelessWidget {
   }
 
   void _handleUnauthenticated(BuildContext context, Unauthenticated result) {
-    // Log.error(result.error);
     // getIt<ISplashRoute>().pushSignInScreen(context);
     getIt<ISplashRoute>().pushSkipLoginScreen(context);
   }

+ 2 - 3
frontend/app_flowy/linux/flutter/dart_ffi/binding.h

@@ -3,12 +3,11 @@
 #include <stdint.h>
 #include <stdlib.h>
 
-
 int64_t init_sdk(char *path);
 
-void async_command(int64_t port, const uint8_t *input, uintptr_t len);
+void async_event(int64_t port, const uint8_t *input, uintptr_t len);
 
-const uint8_t *sync_command(const uint8_t *input, uintptr_t len);
+const uint8_t *sync_event(const uint8_t *input, uintptr_t len);
 
 int32_t set_stream_port(int64_t port);
 

+ 2 - 2
frontend/rust-lib/dart-ffi/src/lib.rs

@@ -25,7 +25,7 @@ pub extern "C" fn init_sdk(path: *mut c_char) -> i64 {
     let c_str: &CStr = unsafe { CStr::from_ptr(path) };
     let path: &str = c_str.to_str().unwrap();
 
-    let server_config = ServerConfig::default();
+    let server_config = get_client_server_configuration().unwrap();
     let config = FlowySDKConfig::new(path, server_config, "appflowy").log_filter("debug");
     *FLOWY_SDK.write() = Some(Arc::new(FlowySDK::new(config)));
 
@@ -70,7 +70,7 @@ pub extern "C" fn set_stream_port(port: i64) -> i32 {
 #[no_mangle]
 pub extern "C" fn link_me_please() {}
 
-use backend_service::config::ServerConfig;
+use backend_service::configuration::get_client_server_configuration;
 use lib_dispatch::prelude::ToBytes;
 
 #[inline(always)]

+ 2 - 2
frontend/rust-lib/flowy-document/src/module.rs

@@ -6,7 +6,7 @@ use crate::{
         ws::WsDocumentManager,
     },
 };
-use backend_service::config::ServerConfig;
+use backend_service::configuration::ClientServerConfiguration;
 use flowy_database::ConnectionPool;
 use flowy_document_infra::entities::doc::{DocDelta, DocIdentifier};
 use std::sync::Arc;
@@ -27,7 +27,7 @@ impl FlowyDocument {
     pub fn new(
         user: Arc<dyn DocumentUser>,
         ws_manager: Arc<WsDocumentManager>,
-        server_config: &ServerConfig,
+        server_config: &ClientServerConfiguration,
     ) -> FlowyDocument {
         let server = construct_doc_server(server_config);
         let doc_ctrl = Arc::new(DocController::new(server, user.clone(), ws_manager));

+ 4 - 2
frontend/rust-lib/flowy-document/src/services/server/mod.rs

@@ -5,7 +5,7 @@ mod server_api_mock;
 pub use server_api::*;
 // TODO: ignore mock files in production
 use crate::errors::DocError;
-use backend_service::config::ServerConfig;
+use backend_service::configuration::ClientServerConfiguration;
 use flowy_document_infra::entities::doc::{CreateDocParams, Doc, DocIdentifier, UpdateDocParams};
 use lib_infra::future::ResultFuture;
 pub use server_api_mock::*;
@@ -20,7 +20,9 @@ pub trait DocumentServerAPI {
     fn update_doc(&self, token: &str, params: UpdateDocParams) -> ResultFuture<(), DocError>;
 }
 
-pub(crate) fn construct_doc_server(server_config: &ServerConfig) -> Arc<dyn DocumentServerAPI + Send + Sync> {
+pub(crate) fn construct_doc_server(
+    server_config: &ClientServerConfiguration,
+) -> Arc<dyn DocumentServerAPI + Send + Sync> {
     if cfg!(feature = "http_server") {
         Arc::new(DocServer::new(server_config.clone()))
     } else {

+ 3 - 3
frontend/rust-lib/flowy-document/src/services/server/server_api.rs

@@ -1,14 +1,14 @@
 use crate::{errors::DocError, services::server::DocumentServerAPI};
-use backend_service::{config::*, request::HttpRequestBuilder};
+use backend_service::{configuration::*, request::HttpRequestBuilder};
 use flowy_document_infra::entities::doc::{CreateDocParams, Doc, DocIdentifier, UpdateDocParams};
 use lib_infra::future::ResultFuture;
 
 pub struct DocServer {
-    config: ServerConfig,
+    config: ClientServerConfiguration,
 }
 
 impl DocServer {
-    pub fn new(config: ServerConfig) -> Self { Self { config } }
+    pub fn new(config: ClientServerConfiguration) -> Self { Self { config } }
 }
 
 impl DocumentServerAPI for DocServer {

+ 4 - 4
frontend/rust-lib/flowy-sdk/src/lib.rs

@@ -2,7 +2,7 @@ mod deps_resolve;
 // mod flowy_server;
 pub mod module;
 use crate::deps_resolve::WorkspaceDepsResolver;
-use backend_service::config::ServerConfig;
+use backend_service::configuration::ClientServerConfiguration;
 use flowy_document::module::FlowyDocument;
 use flowy_user::{
     prelude::UserStatus,
@@ -26,11 +26,11 @@ pub struct FlowySDKConfig {
     name: String,
     root: String,
     log_filter: String,
-    server_config: ServerConfig,
+    server_config: ClientServerConfiguration,
 }
 
 impl FlowySDKConfig {
-    pub fn new(root: &str, server_config: ServerConfig, name: &str) -> Self {
+    pub fn new(root: &str, server_config: ClientServerConfiguration, name: &str) -> Self {
         FlowySDKConfig {
             name: name.to_owned(),
             root: root.to_owned(),
@@ -173,7 +173,7 @@ fn init_log(config: &FlowySDKConfig) {
 fn mk_workspace_controller(
     user_session: Arc<UserSession>,
     flowy_document: Arc<FlowyDocument>,
-    server_config: &ServerConfig,
+    server_config: &ClientServerConfiguration,
 ) -> Arc<WorkspaceController> {
     let workspace_deps = WorkspaceDepsResolver::new(user_session);
     let (user, database) = workspace_deps.split_into();

+ 5 - 2
frontend/rust-lib/flowy-sdk/src/module.rs

@@ -1,5 +1,5 @@
 use crate::deps_resolve::DocumentDepsResolver;
-use backend_service::config::ServerConfig;
+use backend_service::configuration::ClientServerConfiguration;
 use flowy_document::module::FlowyDocument;
 use flowy_user::services::user::UserSession;
 use flowy_workspace::prelude::WorkspaceController;
@@ -18,7 +18,10 @@ fn mk_workspace_module(workspace_controller: Arc<WorkspaceController>) -> Module
     flowy_workspace::module::create(workspace_controller)
 }
 
-pub fn mk_document_module(user_session: Arc<UserSession>, server_config: &ServerConfig) -> Arc<FlowyDocument> {
+pub fn mk_document_module(
+    user_session: Arc<UserSession>,
+    server_config: &ClientServerConfiguration,
+) -> Arc<FlowyDocument> {
     let document_deps = DocumentDepsResolver::new(user_session);
     let (user, ws_manager) = document_deps.split_into();
     Arc::new(FlowyDocument::new(user, ws_manager, server_config))

+ 3 - 3
frontend/rust-lib/flowy-test/src/lib.rs

@@ -3,7 +3,7 @@ mod helper;
 pub mod workspace;
 
 use crate::helper::*;
-use backend_service::config::ServerConfig;
+use backend_service::configuration::{get_client_server_configuration, ClientServerConfiguration};
 use flowy_sdk::{FlowySDK, FlowySDKConfig};
 use flowy_user::entities::UserProfile;
 use lib_infra::uuid;
@@ -22,7 +22,7 @@ pub struct FlowyTest {
 
 impl FlowyTest {
     pub fn setup() -> Self {
-        let server_config = ServerConfig::default();
+        let server_config = get_client_server_configuration().unwrap();
         let test = Self::setup_with(server_config);
         std::mem::forget(test.sdk.dispatcher());
         test
@@ -38,7 +38,7 @@ impl FlowyTest {
         context.user_profile
     }
 
-    pub fn setup_with(server_config: ServerConfig) -> Self {
+    pub fn setup_with(server_config: ClientServerConfiguration) -> Self {
         let config = FlowySDKConfig::new(&root_dir(), server_config, &uuid()).log_filter("debug");
         let sdk = FlowySDK::new(config);
         Self { sdk }

+ 2 - 2
frontend/rust-lib/flowy-user/src/services/server/mod.rs

@@ -9,7 +9,7 @@ use crate::{
     entities::{SignInParams, SignInResponse, SignUpParams, SignUpResponse, UpdateUserParams, UserProfile},
     errors::UserError,
 };
-use backend_service::config::ServerConfig;
+use backend_service::configuration::ClientServerConfiguration;
 use lib_infra::future::ResultFuture;
 
 pub trait UserServerAPI {
@@ -21,7 +21,7 @@ pub trait UserServerAPI {
     fn ws_addr(&self) -> String;
 }
 
-pub(crate) fn construct_user_server(config: &ServerConfig) -> Arc<dyn UserServerAPI + Send + Sync> {
+pub(crate) fn construct_user_server(config: &ClientServerConfiguration) -> Arc<dyn UserServerAPI + Send + Sync> {
     if cfg!(feature = "http_server") {
         Arc::new(UserServer::new(config.clone()))
     } else {

+ 3 - 3
frontend/rust-lib/flowy-user/src/services/server/server_api.rs

@@ -3,14 +3,14 @@ use crate::{
     errors::UserError,
     services::server::UserServerAPI,
 };
-use backend_service::{config::*, user_request::*};
+use backend_service::{configuration::*, user_request::*};
 use lib_infra::future::ResultFuture;
 
 pub struct UserServer {
-    config: ServerConfig,
+    config: ClientServerConfiguration,
 }
 impl UserServer {
-    pub fn new(config: ServerConfig) -> Self { Self { config } }
+    pub fn new(config: ClientServerConfiguration) -> Self { Self { config } }
 }
 
 impl UserServerAPI for UserServer {

+ 3 - 3
frontend/rust-lib/flowy-user/src/services/user/user_session.rs

@@ -12,7 +12,7 @@ use crate::{
         user::{notifier::UserNotifier, ws_manager::WsManager},
     },
 };
-use backend_service::config::ServerConfig;
+use backend_service::configuration::ClientServerConfiguration;
 use flowy_database::{
     query_dsl::*,
     schema::{user_table, user_table::dsl},
@@ -30,12 +30,12 @@ use tokio::sync::{broadcast, mpsc};
 
 pub struct UserSessionConfig {
     root_dir: String,
-    server_config: ServerConfig,
+    server_config: ClientServerConfiguration,
     session_cache_key: String,
 }
 
 impl UserSessionConfig {
-    pub fn new(root_dir: &str, server_config: &ServerConfig, session_cache_key: &str) -> Self {
+    pub fn new(root_dir: &str, server_config: &ClientServerConfiguration, session_cache_key: &str) -> Self {
         Self {
             root_dir: root_dir.to_owned(),
             server_config: server_config.clone(),

+ 2 - 2
frontend/rust-lib/flowy-user/src/services/user/ws_manager.rs

@@ -21,12 +21,12 @@ impl WsManager {
 
     pub fn update_network_type(&self, new_type: &NetworkType) {
         let old_type = self.connect_type.read().clone();
-        if old_type != new_type {
+        if &old_type != new_type {
             log::debug!("Connect type switch from {:?} to {:?}", old_type, new_type);
             match (old_type.is_connect(), new_type.is_connect()) {
                 (false, true) => {
                     let ws_controller = self.inner.clone();
-                    tokio::spawn(async move { retry_connect(ws_controller, 3).await });
+                    tokio::spawn(async move { retry_connect(ws_controller, 100).await });
                 },
                 (true, false) => {
                     //

+ 2 - 2
frontend/rust-lib/flowy-workspace/src/module.rs

@@ -4,7 +4,7 @@ use crate::{
     handlers::*,
     services::{server::construct_workspace_server, AppController, TrashCan, ViewController, WorkspaceController},
 };
-use backend_service::config::ServerConfig;
+use backend_service::configuration::ClientServerConfiguration;
 use flowy_database::DBConnection;
 use flowy_document::module::FlowyDocument;
 use lib_dispatch::prelude::*;
@@ -32,7 +32,7 @@ pub fn init_workspace_controller(
     user: Arc<dyn WorkspaceUser>,
     database: Arc<dyn WorkspaceDatabase>,
     flowy_document: Arc<FlowyDocument>,
-    server_config: &ServerConfig,
+    server_config: &ClientServerConfiguration,
 ) -> Arc<WorkspaceController> {
     let server = construct_workspace_server(server_config);
 

+ 4 - 2
frontend/rust-lib/flowy-workspace/src/services/server/mod.rs

@@ -14,7 +14,7 @@ use crate::{
     },
     errors::WorkspaceError,
 };
-use backend_service::config::ServerConfig;
+use backend_service::configuration::ClientServerConfiguration;
 use lib_infra::future::ResultFuture;
 use std::sync::Arc;
 
@@ -62,7 +62,9 @@ pub trait WorkspaceServerAPI {
     fn read_trash(&self, token: &str) -> ResultFuture<RepeatedTrash, WorkspaceError>;
 }
 
-pub(crate) fn construct_workspace_server(config: &ServerConfig) -> Arc<dyn WorkspaceServerAPI + Send + Sync> {
+pub(crate) fn construct_workspace_server(
+    config: &ClientServerConfiguration,
+) -> Arc<dyn WorkspaceServerAPI + Send + Sync> {
     if cfg!(feature = "http_server") {
         Arc::new(WorkspaceServer::new(config.clone()))
     } else {

+ 3 - 3
frontend/rust-lib/flowy-workspace/src/services/server/server_api.rs

@@ -9,16 +9,16 @@ use crate::{
     notify::{send_dart_notification, WorkspaceNotification},
     services::server::WorkspaceServerAPI,
 };
-use backend_service::{config::ServerConfig, middleware::*, workspace_request::*};
+use backend_service::{configuration::ClientServerConfiguration, middleware::*, workspace_request::*};
 use flowy_workspace_infra::errors::ErrorCode;
 use lib_infra::future::ResultFuture;
 
 pub struct WorkspaceServer {
-    config: ServerConfig,
+    config: ClientServerConfiguration,
 }
 
 impl WorkspaceServer {
-    pub fn new(config: ServerConfig) -> WorkspaceServer { Self { config } }
+    pub fn new(config: ClientServerConfiguration) -> WorkspaceServer { Self { config } }
 }
 
 impl WorkspaceServerAPI for WorkspaceServer {

+ 4 - 5
frontend/scripts/makefile/desktop.toml

@@ -34,8 +34,8 @@ private = true
 script = [
   """
     cd rust-lib/
-    echo cargo build --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features=${FEATURES}
-    cargo build --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features=${FEATURES}
+    echo cargo build --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features "${FEATURES}"
+    cargo build --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features "${FEATURES}"
     cd ../
   """,
 ]
@@ -46,8 +46,7 @@ private = true
 script = [
   """
     cd rust-lib
-    echo cargo build --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features=${FEATURES}
-    exec cargo build --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features=${FEATURES}
+    exec cargo build --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features "${FEATURES}"
     cd ..
   """,
 ]
@@ -65,7 +64,7 @@ run_task = { name = ["setup-crate-type","sdk-release-build", "post-desktop", "re
 script = [
   """
     cd rust-lib/
-    cargo build --${BUILD_FLAG} --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features=${FEATURES}
+    cargo build --${BUILD_FLAG} --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features "${FEATURES}"
     cd ../
   """,
 ]

+ 76 - 0
shared-lib/Cargo.lock

@@ -226,6 +226,12 @@ version = "1.0.47"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "38d9ff5d688f1c13395289f67db01d4826b46dd694e7580accdc3e8430f2d98e"
 
+[[package]]
+name = "arrayvec"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
+
 [[package]]
 name = "atty"
 version = "0.2.14"
@@ -250,6 +256,7 @@ dependencies = [
  "actix-web",
  "anyhow",
  "bytes",
+ "config",
  "derive_more",
  "flowy-user-infra",
  "flowy-workspace-infra",
@@ -259,6 +266,7 @@ dependencies = [
  "protobuf",
  "reqwest",
  "serde",
+ "serde-aux",
  "serde_json",
  "serde_repr",
  "thiserror",
@@ -417,6 +425,18 @@ dependencies = [
  "autocfg",
 ]
 
+[[package]]
+name = "config"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3"
+dependencies = [
+ "lazy_static",
+ "nom",
+ "serde",
+ "yaml-rust",
+]
+
 [[package]]
 name = "const_fn"
 version = "0.4.8"
@@ -1068,6 +1088,19 @@ version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
+[[package]]
+name = "lexical-core"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
+dependencies = [
+ "arrayvec",
+ "bitflags",
+ "cfg-if",
+ "ryu",
+ "static_assertions",
+]
+
 [[package]]
 name = "lib-infra"
 version = "0.1.0"
@@ -1164,6 +1197,12 @@ dependencies = [
  "vcpkg",
 ]
 
+[[package]]
+name = "linked-hash-map"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
+
 [[package]]
 name = "local-channel"
 version = "0.1.2"
@@ -1295,6 +1334,17 @@ dependencies = [
  "tempfile",
 ]
 
+[[package]]
+name = "nom"
+version = "5.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
+dependencies = [
+ "lexical-core",
+ "memchr",
+ "version_check",
+]
+
 [[package]]
 name = "ntapi"
 version = "0.3.6"
@@ -1818,6 +1868,17 @@ dependencies = [
  "serde_derive",
 ]
 
+[[package]]
+name = "serde-aux"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "905f2fc9f3d1574e8b5923a58118240021f01d4e239673937ffb9f42707a4f22"
+dependencies = [
+ "chrono",
+ "serde",
+ "serde_json",
+]
+
 [[package]]
 name = "serde_derive"
 version = "1.0.130"
@@ -1944,6 +2005,12 @@ dependencies = [
  "version_check",
 ]
 
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
 [[package]]
 name = "stdweb"
 version = "0.4.20"
@@ -2533,6 +2600,15 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "yaml-rust"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
+dependencies = [
+ "linked-hash-map",
+]
+
 [[package]]
 name = "zstd"
 version = "0.7.0+zstd.1.4.9"

+ 2 - 0
shared-lib/backend-service/Cargo.toml

@@ -25,6 +25,8 @@ protobuf = {version = "2.18.0"}
 derive_more = {version = "0.99", features = ["display"]}
 tracing = { version = "0.1", features = ["log"] }
 actix-web = {version = "4.0.0-beta.8", optional = true}
+config = { version = "0.10.1", default-features = false, features = ["yaml"] }
+serde-aux = "1.0.1"
 
 [features]
 http_server = ["actix-web"]

+ 5 - 0
shared-lib/backend-service/configuration/base.yaml

@@ -0,0 +1,5 @@
+port: 8000
+host: 0.0.0.0
+http_scheme: http
+ws_scheme: ws
+

+ 3 - 0
shared-lib/backend-service/configuration/local.yaml

@@ -0,0 +1,3 @@
+host: 127.0.0.1
+http_scheme: http
+ws_scheme: ws

+ 2 - 0
shared-lib/backend-service/configuration/production.yaml

@@ -0,0 +1,2 @@
+host: 0.0.0.0
+

+ 0 - 53
shared-lib/backend-service/src/config.rs

@@ -1,53 +0,0 @@
-pub const HOST: &str = "localhost:8000";
-pub const HTTP_SCHEMA: &str = "http";
-pub const WS_SCHEMA: &str = "ws";
-pub const HEADER_TOKEN: &str = "token";
-
-#[derive(Debug, Clone)]
-pub struct ServerConfig {
-    http_schema: String,
-    host: String,
-    ws_schema: String,
-}
-
-impl std::default::Default for ServerConfig {
-    fn default() -> Self {
-        ServerConfig {
-            http_schema: HTTP_SCHEMA.to_string(),
-            host: HOST.to_string(),
-            ws_schema: WS_SCHEMA.to_string(),
-        }
-    }
-}
-
-impl ServerConfig {
-    pub fn new(host: &str, http_schema: &str, ws_schema: &str) -> Self {
-        Self {
-            http_schema: http_schema.to_owned(),
-            host: host.to_owned(),
-            ws_schema: ws_schema.to_owned(),
-        }
-    }
-
-    fn scheme(&self) -> String { format!("{}://", self.http_schema) }
-
-    pub fn sign_up_url(&self) -> String { format!("{}{}/api/register", self.scheme(), self.host) }
-
-    pub fn sign_in_url(&self) -> String { format!("{}{}/api/auth", self.scheme(), self.host) }
-
-    pub fn sign_out_url(&self) -> String { format!("{}{}/api/auth", self.scheme(), self.host) }
-
-    pub fn user_profile_url(&self) -> String { format!("{}{}/api/user", self.scheme(), self.host) }
-
-    pub fn workspace_url(&self) -> String { format!("{}{}/api/workspace", self.scheme(), self.host) }
-
-    pub fn app_url(&self) -> String { format!("{}{}/api/app", self.scheme(), self.host) }
-
-    pub fn view_url(&self) -> String { format!("{}{}/api/view", self.scheme(), self.host) }
-
-    pub fn doc_url(&self) -> String { format!("{}{}/api/doc", self.scheme(), self.host) }
-
-    pub fn trash_url(&self) -> String { format!("{}{}/api/trash", self.scheme(), self.host) }
-
-    pub fn ws_addr(&self) -> String { format!("{}://{}/ws", self.ws_schema, self.host) }
-}

+ 91 - 0
shared-lib/backend-service/src/configuration.rs

@@ -0,0 +1,91 @@
+use config::FileFormat;
+use serde_aux::field_attributes::deserialize_number_from_string;
+use std::convert::{TryFrom, TryInto};
+pub const HOST: &str = "localhost:8000";
+pub const HEADER_TOKEN: &str = "token";
+
+#[derive(serde::Deserialize, Clone, Debug)]
+pub struct ClientServerConfiguration {
+    #[serde(deserialize_with = "deserialize_number_from_string")]
+    pub port: u16,
+    pub host: String,
+    pub http_scheme: String,
+    pub ws_scheme: String,
+}
+
+pub fn get_client_server_configuration() -> Result<ClientServerConfiguration, config::ConfigError> {
+    let mut settings = config::Config::default();
+    let base = include_str!("../configuration/base.yaml");
+    settings.merge(config::File::from_str(base, FileFormat::Yaml).required(true))?;
+
+    let environment: Environment = std::env::var("APP_ENVIRONMENT")
+        .unwrap_or_else(|_| "local".into())
+        .try_into()
+        .expect("Failed to parse APP_ENVIRONMENT.");
+
+    let custom = match environment {
+        Environment::Local => include_str!("../configuration/local.yaml"),
+        Environment::Production => include_str!("../configuration/production.yaml"),
+    };
+
+    settings.merge(config::File::from_str(custom, FileFormat::Yaml).required(true))?;
+    settings.try_into()
+}
+
+impl ClientServerConfiguration {
+    pub fn reset_host_with_port(&mut self, host: &str, port: u16) {
+        self.host = host.to_owned();
+        self.port = port;
+    }
+
+    pub fn base_url(&self) -> String { format!("{}://{}:{}", self.http_scheme, self.host, self.port) }
+
+    pub fn sign_up_url(&self) -> String { format!("{}/api/register", self.base_url()) }
+
+    pub fn sign_in_url(&self) -> String { format!("{}/api/auth", self.base_url()) }
+
+    pub fn sign_out_url(&self) -> String { format!("{}/api/auth", self.base_url()) }
+
+    pub fn user_profile_url(&self) -> String { format!("{}/api/user", self.base_url()) }
+
+    pub fn workspace_url(&self) -> String { format!("{}/api/workspace", self.base_url()) }
+
+    pub fn app_url(&self) -> String { format!("{}/api/app", self.base_url()) }
+
+    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 trash_url(&self) -> String { format!("{}/api/trash", self.base_url()) }
+
+    pub fn ws_addr(&self) -> String { format!("{}://{}:{}/ws", self.ws_scheme, self.host, self.port) }
+}
+
+pub enum Environment {
+    Local,
+    Production,
+}
+
+impl Environment {
+    pub fn as_str(&self) -> &'static str {
+        match self {
+            Environment::Local => "local",
+            Environment::Production => "production",
+        }
+    }
+}
+
+impl TryFrom<String> for Environment {
+    type Error = String;
+
+    fn try_from(s: String) -> Result<Self, Self::Error> {
+        match s.to_lowercase().as_str() {
+            "local" => Ok(Self::Local),
+            "production" => Ok(Self::Production),
+            other => Err(format!(
+                "{} is not a supported environment. Use either `local` or `production`.",
+                other
+            )),
+        }
+    }
+}

+ 1 - 1
shared-lib/backend-service/src/lib.rs

@@ -1,4 +1,4 @@
-pub mod config;
+pub mod configuration;
 pub mod errors;
 pub mod middleware;
 pub mod request;

+ 1 - 1
shared-lib/backend-service/src/request/request.rs

@@ -1,4 +1,4 @@
-use crate::{config::HEADER_TOKEN, errors::ServerError, response::FlowyResponse};
+use crate::{configuration::HEADER_TOKEN, errors::ServerError, response::FlowyResponse};
 use bytes::Bytes;
 use hyper::http;
 use protobuf::ProtobufError;

+ 1 - 1
shared-lib/backend-service/src/user_request.rs

@@ -1,4 +1,4 @@
-use crate::{config::HEADER_TOKEN, errors::ServerError, request::HttpRequestBuilder};
+use crate::{configuration::HEADER_TOKEN, errors::ServerError, request::HttpRequestBuilder};
 use flowy_user_infra::entities::prelude::*;
 
 pub(crate) fn request_builder() -> HttpRequestBuilder {

+ 1 - 1
shared-lib/backend-service/src/workspace_request.rs

@@ -1,4 +1,4 @@
-use crate::{config::HEADER_TOKEN, errors::ServerError, request::HttpRequestBuilder};
+use crate::{configuration::HEADER_TOKEN, errors::ServerError, request::HttpRequestBuilder};
 use flowy_workspace_infra::entities::prelude::*;
 
 pub(crate) fn request_builder() -> HttpRequestBuilder {