Merge branch 'main' into gotrue-2.159.0
This commit is contained in:
commit
44a8bf35c2
|
|
@ -2080,7 +2080,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "collab"
|
name = "collab"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=6f98f054a8306a8791b65bf1825091ab3fe60166#6f98f054a8306a8791b65bf1825091ab3fe60166"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ec4f92941942f20c84dce142cd2eeafd44ca7362#ec4f92941942f20c84dce142cd2eeafd44ca7362"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
|
|
@ -2105,7 +2105,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-document"
|
name = "collab-document"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=6f98f054a8306a8791b65bf1825091ab3fe60166#6f98f054a8306a8791b65bf1825091ab3fe60166"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ec4f92941942f20c84dce142cd2eeafd44ca7362#ec4f92941942f20c84dce142cd2eeafd44ca7362"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
|
|
@ -2125,7 +2125,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-entity"
|
name = "collab-entity"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=6f98f054a8306a8791b65bf1825091ab3fe60166#6f98f054a8306a8791b65bf1825091ab3fe60166"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ec4f92941942f20c84dce142cd2eeafd44ca7362#ec4f92941942f20c84dce142cd2eeafd44ca7362"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
|
@ -2144,7 +2144,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-folder"
|
name = "collab-folder"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=6f98f054a8306a8791b65bf1825091ab3fe60166#6f98f054a8306a8791b65bf1825091ab3fe60166"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ec4f92941942f20c84dce142cd2eeafd44ca7362#ec4f92941942f20c84dce142cd2eeafd44ca7362"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
|
|
@ -2228,7 +2228,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-user"
|
name = "collab-user"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=6f98f054a8306a8791b65bf1825091ab3fe60166#6f98f054a8306a8791b65bf1825091ab3fe60166"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ec4f92941942f20c84dce142cd2eeafd44ca7362#ec4f92941942f20c84dce142cd2eeafd44ca7362"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
|
|
|
||||||
10
Cargo.toml
10
Cargo.toml
|
|
@ -283,11 +283,11 @@ debug = true
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
# It's diffcult to resovle different version with the same crate used in AppFlowy Frontend and the Client-API crate.
|
# It's diffcult to resovle different version with the same crate used in AppFlowy Frontend and the Client-API crate.
|
||||||
# So using patch to workaround this issue.
|
# So using patch to workaround this issue.
|
||||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6f98f054a8306a8791b65bf1825091ab3fe60166" }
|
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ec4f92941942f20c84dce142cd2eeafd44ca7362" }
|
||||||
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6f98f054a8306a8791b65bf1825091ab3fe60166" }
|
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ec4f92941942f20c84dce142cd2eeafd44ca7362" }
|
||||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6f98f054a8306a8791b65bf1825091ab3fe60166" }
|
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ec4f92941942f20c84dce142cd2eeafd44ca7362" }
|
||||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6f98f054a8306a8791b65bf1825091ab3fe60166" }
|
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ec4f92941942f20c84dce142cd2eeafd44ca7362" }
|
||||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6f98f054a8306a8791b65bf1825091ab3fe60166" }
|
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ec4f92941942f20c84dce142cd2eeafd44ca7362" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
history = []
|
history = []
|
||||||
|
|
|
||||||
|
|
@ -316,6 +316,7 @@ pub enum ErrorCode {
|
||||||
#[cfg(feature = "sqlx_error")]
|
#[cfg(feature = "sqlx_error")]
|
||||||
SqlxArgEncodingError = 1035,
|
SqlxArgEncodingError = 1035,
|
||||||
InvalidContentType = 1036,
|
InvalidContentType = 1036,
|
||||||
|
SingleUploadLimitExceeded = 1037,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ErrorCode {
|
impl ErrorCode {
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,11 @@ use std::future::Future;
|
||||||
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio_retry::strategy::{ExponentialBackoff, FixedInterval};
|
use tokio_retry::strategy::{ExponentialBackoff, FixedInterval};
|
||||||
use tokio_retry::{Retry, RetryIf};
|
use tokio_retry::{Condition, RetryIf};
|
||||||
use tracing::{event, info, instrument, trace};
|
use tracing::{event, info, instrument, trace};
|
||||||
|
|
||||||
pub use infra::file_util::ChunkedBytes;
|
pub use infra::file_util::ChunkedBytes;
|
||||||
|
|
@ -138,7 +139,7 @@ impl Client {
|
||||||
// 2 seconds, 4 seconds, 8 seconds
|
// 2 seconds, 4 seconds, 8 seconds
|
||||||
let retry_strategy = ExponentialBackoff::from_millis(2).factor(1000).take(3);
|
let retry_strategy = ExponentialBackoff::from_millis(2).factor(1000).take(3);
|
||||||
let action = GetCollabAction::new(self.clone(), params);
|
let action = GetCollabAction::new(self.clone(), params);
|
||||||
Retry::spawn(retry_strategy, action).await
|
RetryIf::spawn(retry_strategy, action, RetryGetCollabCondition).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip_all, err)]
|
#[instrument(level = "debug", skip_all, err)]
|
||||||
|
|
@ -391,3 +392,10 @@ where
|
||||||
|
|
||||||
Ok(Bytes::from(chunk))
|
Ok(Bytes::from(chunk))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct RetryGetCollabCondition;
|
||||||
|
impl Condition<AppResponseError> for RetryGetCollabCondition {
|
||||||
|
fn should_retry(&mut self, error: &AppResponseError) -> bool {
|
||||||
|
!error.is_record_not_found()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ use tokio_stream::StreamExt;
|
||||||
use tracing::{debug, error, event, instrument, trace};
|
use tracing::{debug, error, event, instrument, trace};
|
||||||
|
|
||||||
use app_error::AppError;
|
use app_error::AppError;
|
||||||
|
use authentication::jwt::{authorization_from_token, UserUuid};
|
||||||
use collab_rt_entity::user::{AFUserChange, RealtimeUser, UserMessage};
|
use collab_rt_entity::user::{AFUserChange, RealtimeUser, UserMessage};
|
||||||
use collab_rt_entity::{HttpRealtimeMessage, RealtimeMessage};
|
use collab_rt_entity::{HttpRealtimeMessage, RealtimeMessage};
|
||||||
use shared_entity::response::{AppResponse, AppResponseError};
|
use shared_entity::response::{AppResponse, AppResponseError};
|
||||||
|
|
@ -31,7 +32,6 @@ use crate::compression::{
|
||||||
decompress, CompressionType, X_COMPRESSION_BUFFER_SIZE, X_COMPRESSION_TYPE,
|
decompress, CompressionType, X_COMPRESSION_BUFFER_SIZE, X_COMPRESSION_TYPE,
|
||||||
};
|
};
|
||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
use authentication::jwt::{authorization_from_token, UserUuid};
|
|
||||||
|
|
||||||
pub fn ws_scope() -> Scope {
|
pub fn ws_scope() -> Scope {
|
||||||
web::scope("/ws").service(web::resource("/v1").route(web::get().to(establish_ws_connection_v1)))
|
web::scope("/ws").service(web::resource("/v1").route(web::get().to(establish_ws_connection_v1)))
|
||||||
|
|
@ -76,6 +76,10 @@ pub async fn establish_ws_connection_v1(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if client_version < state.config.websocket.min_client_version {
|
||||||
|
return Err(AppError::Connect("Client version is too low".to_string()).into());
|
||||||
|
}
|
||||||
|
|
||||||
start_connect(
|
start_connect(
|
||||||
&request,
|
&request,
|
||||||
payload,
|
payload,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use std::time::Duration;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use collab::entity::EncodedCollab;
|
use collab::entity::EncodedCollab;
|
||||||
use collab_entity::CollabType;
|
use collab_entity::CollabType;
|
||||||
use sqlx::{PgPool, Transaction};
|
use sqlx::{Error, PgPool, Transaction};
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
use tracing::{event, instrument, Level};
|
use tracing::{event, instrument, Level};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
@ -100,19 +100,21 @@ impl CollabDiskCache {
|
||||||
.await?;
|
.await?;
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// Handle non-retryable errors immediately
|
match e {
|
||||||
if matches!(e, sqlx::Error::RowNotFound) {
|
Error::RowNotFound => {
|
||||||
let msg = format!("Can't find the row for query: {:?}", query);
|
let msg = format!("Can't find the row for query: {:?}", query);
|
||||||
return Err(AppError::RecordNotFound(msg));
|
return Err(AppError::RecordNotFound(msg));
|
||||||
}
|
},
|
||||||
|
_ => {
|
||||||
// Increment attempts and retry if below MAX_ATTEMPTS and the error is retryable
|
// Increment attempts and retry if below MAX_ATTEMPTS and the error is retryable
|
||||||
if attempts < MAX_ATTEMPTS - 1 && matches!(e, sqlx::Error::PoolTimedOut) {
|
if attempts < MAX_ATTEMPTS - 1 && matches!(e, sqlx::Error::PoolTimedOut) {
|
||||||
attempts += 1;
|
attempts += 1;
|
||||||
sleep(Duration::from_millis(500 * attempts as u64)).await;
|
sleep(Duration::from_millis(500 * attempts as u64)).await;
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
return Err(e.into());
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use secrecy::Secret;
|
use secrecy::Secret;
|
||||||
|
use semver::Version;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlx::postgres::{PgConnectOptions, PgSslMode};
|
use sqlx::postgres::{PgConnectOptions, PgSslMode};
|
||||||
|
|
||||||
|
|
@ -70,6 +71,7 @@ impl AISettings {
|
||||||
pub struct WebsocketSetting {
|
pub struct WebsocketSetting {
|
||||||
pub heartbeat_interval: u8,
|
pub heartbeat_interval: u8,
|
||||||
pub client_timeout: u8,
|
pub client_timeout: u8,
|
||||||
|
pub min_client_version: Version,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -140,6 +142,7 @@ pub fn get_configuration() -> Result<Config, anyhow::Error> {
|
||||||
websocket: WebsocketSetting {
|
websocket: WebsocketSetting {
|
||||||
heartbeat_interval: get_env_var("APPFLOWY_WEBSOCKET_HEARTBEAT_INTERVAL", "6").parse()?,
|
heartbeat_interval: get_env_var("APPFLOWY_WEBSOCKET_HEARTBEAT_INTERVAL", "6").parse()?,
|
||||||
client_timeout: get_env_var("APPFLOWY_WEBSOCKET_CLIENT_TIMEOUT", "60").parse()?,
|
client_timeout: get_env_var("APPFLOWY_WEBSOCKET_CLIENT_TIMEOUT", "60").parse()?,
|
||||||
|
min_client_version: get_env_var("APPFLOWY_WEBSOCKET_CLIENT_MIN_VERSION", "0.5.0").parse()?,
|
||||||
},
|
},
|
||||||
db_settings: DatabaseSetting {
|
db_settings: DatabaseSetting {
|
||||||
pg_conn_opts: PgConnectOptions::from_str(&get_env_var(
|
pg_conn_opts: PgConnectOptions::from_str(&get_env_var(
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use tracing::{instrument, trace, warn};
|
||||||
|
|
||||||
use access_control::collab::RealtimeAccessControl;
|
use access_control::collab::RealtimeAccessControl;
|
||||||
use collab_rt_entity::user::RealtimeUser;
|
use collab_rt_entity::user::RealtimeUser;
|
||||||
use collab_rt_entity::{ClientCollabMessage, ServerCollabMessage, SinkMessage};
|
use collab_rt_entity::{AckCode, ClientCollabMessage, ServerCollabMessage, SinkMessage};
|
||||||
use collab_rt_entity::{CollabAck, RealtimeMessage};
|
use collab_rt_entity::{CollabAck, RealtimeMessage};
|
||||||
use database::collab::CollabStorage;
|
use database::collab::CollabStorage;
|
||||||
|
|
||||||
|
|
@ -155,7 +155,11 @@ where
|
||||||
let origin = first_message.origin().clone();
|
let origin = first_message.origin().clone();
|
||||||
let msg_id = first_message.msg_id();
|
let msg_id = first_message.msg_id();
|
||||||
let object_id = first_message.object_id().to_string();
|
let object_id = first_message.object_id().to_string();
|
||||||
let ack = CollabAck::new(origin, object_id, msg_id, 0);
|
|
||||||
|
// when the group with given id is not found and the the first message is not init sync.
|
||||||
|
// Return AckCode::CannotApplyUpdate to the client and then client will send the init sync message.
|
||||||
|
let ack =
|
||||||
|
CollabAck::new(origin, object_id, msg_id, 0).with_code(AckCode::CannotApplyUpdate);
|
||||||
entry
|
entry
|
||||||
.value()
|
.value()
|
||||||
.send_message(ServerCollabMessage::ClientAck(ack).into())
|
.send_message(ServerCollabMessage::ClientAck(ack).into())
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use collab_entity::CollabType;
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use futures_util::{SinkExt, StreamExt};
|
use futures_util::{SinkExt, StreamExt};
|
||||||
use tokio::sync::{mpsc, RwLock};
|
use tokio::sync::{mpsc, RwLock};
|
||||||
use tracing::{error, event, trace};
|
use tracing::{error, event, info, trace};
|
||||||
use yrs::updates::decoder::Decode;
|
use yrs::updates::decoder::Decode;
|
||||||
use yrs::updates::encoder::Encode;
|
use yrs::updates::encoder::Encode;
|
||||||
use yrs::Update;
|
use yrs::Update;
|
||||||
|
|
@ -200,7 +200,26 @@ impl CollabGroup {
|
||||||
modified_at.elapsed().as_secs() > 60 * 60
|
modified_at.elapsed().as_secs() > 60 * 60
|
||||||
} else {
|
} else {
|
||||||
let elapsed_secs = modified_at.elapsed().as_secs();
|
let elapsed_secs = modified_at.elapsed().as_secs();
|
||||||
elapsed_secs > self.timeout_secs() && self.subscribers.is_empty()
|
if elapsed_secs > self.timeout_secs() {
|
||||||
|
// Mark the group as inactive if it has been inactive for more than 3 hours, regardless of the number of subscribers.
|
||||||
|
// Otherwise, return `true` only if there are no subscribers remaining in the group.
|
||||||
|
// If a client modifies a group that has already been marked as inactive (removed),
|
||||||
|
// the client will automatically send an initialization sync to reinitialize the group.
|
||||||
|
const MAXIMUM_SECS: u64 = 3 * 60 * 60;
|
||||||
|
if elapsed_secs > MAXIMUM_SECS {
|
||||||
|
info!(
|
||||||
|
"Group:{} is inactive for {} seconds, subscribers: {}",
|
||||||
|
self.object_id,
|
||||||
|
modified_at.elapsed().as_secs(),
|
||||||
|
self.subscribers.len()
|
||||||
|
);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
self.subscribers.is_empty()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ impl IndexerProvider {
|
||||||
|
|
||||||
fn get_unindexed_collabs(
|
fn get_unindexed_collabs(
|
||||||
&self,
|
&self,
|
||||||
) -> Pin<Box<dyn Stream<Item = Result<UnindexedCollab, anyhow::Error>>>> {
|
) -> Pin<Box<dyn Stream<Item = Result<UnindexedCollab, anyhow::Error>> + Send>> {
|
||||||
let db = self.db.clone();
|
let db = self.db.clone();
|
||||||
Box::pin(try_stream! {
|
Box::pin(try_stream! {
|
||||||
let collabs = get_collabs_without_embeddings(&db).await?;
|
let collabs = get_collabs_without_embeddings(&db).await?;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use dashmap::mapref::entry::Entry;
|
||||||
|
use dashmap::DashMap;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use dashmap::mapref::entry::Entry;
|
|
||||||
use dashmap::DashMap;
|
|
||||||
use tokio::sync::Notify;
|
use tokio::sync::Notify;
|
||||||
use tokio::time::interval;
|
use tokio::time::interval;
|
||||||
use tracing::{error, info, trace};
|
use tracing::{error, info, trace};
|
||||||
|
|
@ -254,7 +253,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_handle_unindexed_collabs(indexer_provider: Arc<IndexerProvider>) {
|
fn spawn_handle_unindexed_collabs(indexer_provider: Arc<IndexerProvider>) {
|
||||||
tokio::task::spawn_local(IndexerProvider::handle_unindexed_collabs(indexer_provider));
|
tokio::spawn(IndexerProvider::handle_unindexed_collabs(indexer_provider));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_period_check_inactive_group<S, AC>(
|
fn spawn_period_check_inactive_group<S, AC>(
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ use crate::state::AppState;
|
||||||
|
|
||||||
pub fn ws_scope() -> Scope {
|
pub fn ws_scope() -> Scope {
|
||||||
web::scope("/ws")
|
web::scope("/ws")
|
||||||
.service(establish_ws_connection)
|
//.service(establish_ws_connection)
|
||||||
.service(web::resource("/v1").route(web::get().to(establish_ws_connection_v1)))
|
.service(web::resource("/v1").route(web::get().to(establish_ws_connection_v1)))
|
||||||
}
|
}
|
||||||
const MAX_FRAME_SIZE: usize = 65_536; // 64 KiB
|
const MAX_FRAME_SIZE: usize = 65_536; // 64 KiB
|
||||||
|
|
@ -86,6 +86,10 @@ pub async fn establish_ws_connection_v1(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if client_version < state.config.websocket.min_client_version {
|
||||||
|
return Err(AppError::Connect("Client version is too low".to_string()).into());
|
||||||
|
}
|
||||||
|
|
||||||
start_connect(
|
start_connect(
|
||||||
&request,
|
&request,
|
||||||
payload,
|
payload,
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
use anyhow::Context;
|
|
||||||
use infra::env_util::get_env_var;
|
|
||||||
use secrecy::{ExposeSecret, Secret};
|
|
||||||
use serde::Deserialize;
|
|
||||||
use sqlx::postgres::{PgConnectOptions, PgSslMode};
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
use secrecy::{ExposeSecret, Secret};
|
||||||
|
use semver::Version;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use sqlx::postgres::{PgConnectOptions, PgSslMode};
|
||||||
|
|
||||||
|
use infra::env_util::get_env_var;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub app_env: Environment,
|
pub app_env: Environment,
|
||||||
|
|
@ -166,6 +169,7 @@ pub fn get_configuration() -> Result<Config, anyhow::Error> {
|
||||||
websocket: WebsocketSetting {
|
websocket: WebsocketSetting {
|
||||||
heartbeat_interval: get_env_var("APPFLOWY_WEBSOCKET_HEARTBEAT_INTERVAL", "6").parse()?,
|
heartbeat_interval: get_env_var("APPFLOWY_WEBSOCKET_HEARTBEAT_INTERVAL", "6").parse()?,
|
||||||
client_timeout: get_env_var("APPFLOWY_WEBSOCKET_CLIENT_TIMEOUT", "60").parse()?,
|
client_timeout: get_env_var("APPFLOWY_WEBSOCKET_CLIENT_TIMEOUT", "60").parse()?,
|
||||||
|
min_client_version: get_env_var("APPFLOWY_WEBSOCKET_CLIENT_MIN_VERSION", "0.5.0").parse()?,
|
||||||
},
|
},
|
||||||
redis_uri: get_env_var("APPFLOWY_REDIS_URI", "redis://localhost:6379").into(),
|
redis_uri: get_env_var("APPFLOWY_REDIS_URI", "redis://localhost:6379").into(),
|
||||||
s3: S3Setting {
|
s3: S3Setting {
|
||||||
|
|
@ -239,4 +243,5 @@ impl FromStr for Environment {
|
||||||
pub struct WebsocketSetting {
|
pub struct WebsocketSetting {
|
||||||
pub heartbeat_interval: u8,
|
pub heartbeat_interval: u8,
|
||||||
pub client_timeout: u8,
|
pub client_timeout: u8,
|
||||||
|
pub min_client_version: Version,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue