mirror of
https://github.com/Blah-IM/blahrs.git
synced 2025-05-01 00:31:09 +00:00
Make more server constants configurable
This commit is contained in:
parent
abdc32b51f
commit
6e7229e4ac
5 changed files with 57 additions and 16 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -300,6 +300,7 @@ dependencies = [
|
||||||
"sd-notify",
|
"sd-notify",
|
||||||
"serde",
|
"serde",
|
||||||
"serde-aux",
|
"serde-aux",
|
||||||
|
"serde-inline-default",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
|
@ -1455,6 +1456,17 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde-inline-default"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9980133dc534d02ab08df3b384295223a45090c40a4c46240e3eaa982b495910"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.209"
|
version = "1.0.209"
|
||||||
|
|
|
@ -26,3 +26,4 @@ uuid = { version = "1", features = ["v4"] }
|
||||||
|
|
||||||
blah = { path = "..", features = ["rusqlite"] }
|
blah = { path = "..", features = ["rusqlite"] }
|
||||||
basic-toml = "0.1.9"
|
basic-toml = "0.1.9"
|
||||||
|
serde-inline-default = "0.2.0"
|
||||||
|
|
|
@ -1,13 +1,29 @@
|
||||||
[database]
|
[database]
|
||||||
|
# (Required)
|
||||||
# The path to the main SQLite database.
|
# The path to the main SQLite database.
|
||||||
# It will be created and initialized if not exist.
|
# It will be created and initialized if not exist.
|
||||||
path = "/path/to/db.sqlite"
|
path = "/path/to/db.sqlite"
|
||||||
|
|
||||||
[server]
|
[server]
|
||||||
|
|
||||||
|
# (Required)
|
||||||
# The socket address to listen on.
|
# The socket address to listen on.
|
||||||
listen = "localhost:8080"
|
listen = "localhost:8080"
|
||||||
|
|
||||||
|
# (Required)
|
||||||
# The global absolute URL prefix where this service is hosted.
|
# The global absolute URL prefix where this service is hosted.
|
||||||
# It is for link generation and must not have trailing slash.
|
# It is for link generation and must not have trailing slash.
|
||||||
base_url = "http://localhost:8080"
|
base_url = "http://localhost:8080"
|
||||||
|
|
||||||
|
# Maximum number of items in a single response, eg. get chat items.
|
||||||
|
# More items will be paged.
|
||||||
|
max_page_len = 1024
|
||||||
|
|
||||||
|
# Maximum request body length in bytes.
|
||||||
|
max_request_len = 4096
|
||||||
|
|
||||||
|
# Maximum length of a single event queue.
|
||||||
|
event_queue_len = 1024
|
||||||
|
|
||||||
|
# The maximum timestamp tolerence in seconds for request validation.
|
||||||
|
timestamp_tolerence_secs = 90
|
||||||
|
|
|
@ -2,6 +2,7 @@ use std::path::PathBuf;
|
||||||
|
|
||||||
use anyhow::{ensure, Result};
|
use anyhow::{ensure, Result};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use serde_inline_default::serde_inline_default;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
|
@ -16,11 +17,22 @@ pub struct DatabaseConfig {
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[serde_inline_default]
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct ServerConfig {
|
pub struct ServerConfig {
|
||||||
pub listen: String,
|
pub listen: String,
|
||||||
pub base_url: String,
|
pub base_url: String,
|
||||||
|
|
||||||
|
#[serde_inline_default(1024)]
|
||||||
|
pub max_page_len: usize,
|
||||||
|
#[serde_inline_default(4096)] // 4KiB
|
||||||
|
pub max_request_len: usize,
|
||||||
|
#[serde_inline_default(1024)]
|
||||||
|
pub event_queue_len: usize,
|
||||||
|
|
||||||
|
#[serde_inline_default(90)]
|
||||||
|
pub timestamp_tolerence_secs: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
|
|
@ -26,11 +26,6 @@ use tokio_stream::StreamExt;
|
||||||
use utils::ExpiringSet;
|
use utils::ExpiringSet;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
const PAGE_LEN: usize = 64;
|
|
||||||
const EVENT_QUEUE_LEN: usize = 1024;
|
|
||||||
const MAX_BODY_LEN: usize = 4 << 10; // 4KiB
|
|
||||||
const TIMESTAMP_TOLERENCE: u64 = 90;
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod middleware;
|
mod middleware;
|
||||||
mod config;
|
mod config;
|
||||||
|
@ -105,7 +100,9 @@ impl AppState {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
conn: Mutex::new(conn),
|
conn: Mutex::new(conn),
|
||||||
room_listeners: Mutex::new(HashMap::new()),
|
room_listeners: Mutex::new(HashMap::new()),
|
||||||
used_nonces: Mutex::new(ExpiringSet::new(Duration::from_secs(TIMESTAMP_TOLERENCE))),
|
used_nonces: Mutex::new(ExpiringSet::new(Duration::from_secs(
|
||||||
|
config.server.timestamp_tolerence_secs,
|
||||||
|
))),
|
||||||
|
|
||||||
config,
|
config,
|
||||||
})
|
})
|
||||||
|
@ -124,7 +121,7 @@ impl AppState {
|
||||||
.expect("after UNIX epoch")
|
.expect("after UNIX epoch")
|
||||||
.as_secs()
|
.as_secs()
|
||||||
.abs_diff(data.signee.timestamp);
|
.abs_diff(data.signee.timestamp);
|
||||||
if timestamp_diff > TIMESTAMP_TOLERENCE {
|
if timestamp_diff > self.config.server.timestamp_tolerence_secs {
|
||||||
return Err(error_response!(
|
return Err(error_response!(
|
||||||
StatusCode::BAD_REQUEST,
|
StatusCode::BAD_REQUEST,
|
||||||
"invalid_timestamp",
|
"invalid_timestamp",
|
||||||
|
@ -161,7 +158,9 @@ async fn main_async(st: AppState) -> Result<()> {
|
||||||
.with_state(st.clone())
|
.with_state(st.clone())
|
||||||
// NB. This comes at last (outmost layer), so inner errors will still be wraped with
|
// NB. This comes at last (outmost layer), so inner errors will still be wraped with
|
||||||
// correct CORS headers.
|
// correct CORS headers.
|
||||||
.layer(tower_http::limit::RequestBodyLimitLayer::new(MAX_BODY_LEN))
|
.layer(tower_http::limit::RequestBodyLimitLayer::new(
|
||||||
|
st.config.server.max_request_len,
|
||||||
|
))
|
||||||
.layer(tower_http::cors::CorsLayer::permissive());
|
.layer(tower_http::cors::CorsLayer::permissive());
|
||||||
|
|
||||||
let listener = tokio::net::TcpListener::bind(&st.config.server.listen)
|
let listener = tokio::net::TcpListener::bind(&st.config.server.listen)
|
||||||
|
@ -276,8 +275,7 @@ async fn room_get_item(
|
||||||
WithRejection(params, _): WithRejection<Query<GetRoomItemParams>, ApiError>,
|
WithRejection(params, _): WithRejection<Query<GetRoomItemParams>, ApiError>,
|
||||||
OptionalAuth(user): OptionalAuth,
|
OptionalAuth(user): OptionalAuth,
|
||||||
) -> Result<impl IntoResponse, ApiError> {
|
) -> Result<impl IntoResponse, ApiError> {
|
||||||
let (room_meta, items) =
|
let (room_meta, items) = query_room_items(&st, ruuid, user.as_ref(), ¶ms)?;
|
||||||
query_room_items(&st.conn.lock().unwrap(), ruuid, user.as_ref(), ¶ms)?;
|
|
||||||
|
|
||||||
// TODO: This format is to-be-decided. Or do we even need this interface other than
|
// TODO: This format is to-be-decided. Or do we even need this interface other than
|
||||||
// `feed.json`?
|
// `feed.json`?
|
||||||
|
@ -289,7 +287,7 @@ async fn room_get_feed(
|
||||||
WithRejection(Path(ruuid), _): WithRejection<Path<Uuid>, ApiError>,
|
WithRejection(Path(ruuid), _): WithRejection<Path<Uuid>, ApiError>,
|
||||||
params: Query<GetRoomItemParams>,
|
params: Query<GetRoomItemParams>,
|
||||||
) -> Result<impl IntoResponse, ApiError> {
|
) -> Result<impl IntoResponse, ApiError> {
|
||||||
let (room_meta, items) = query_room_items(&st.conn.lock().unwrap(), ruuid, None, ¶ms)?;
|
let (room_meta, items) = query_room_items(&st, ruuid, None, ¶ms)?;
|
||||||
|
|
||||||
let items = items
|
let items = items
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -314,7 +312,7 @@ async fn room_get_feed(
|
||||||
|
|
||||||
let base_url = &st.config.server.base_url;
|
let base_url = &st.config.server.base_url;
|
||||||
let feed_url = format!("{base_url}/room/{ruuid}/feed.json");
|
let feed_url = format!("{base_url}/room/{ruuid}/feed.json");
|
||||||
let next_url = (items.len() == PAGE_LEN).then(|| {
|
let next_url = (items.len() == st.config.server.max_page_len).then(|| {
|
||||||
let last_id = &items.last().expect("page size is not 0").id;
|
let last_id = &items.last().expect("page size is not 0").id;
|
||||||
format!("{feed_url}?before_id={last_id}")
|
format!("{feed_url}?before_id={last_id}")
|
||||||
});
|
});
|
||||||
|
@ -401,12 +399,14 @@ fn get_room_if_readable<T>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn query_room_items(
|
fn query_room_items(
|
||||||
conn: &rusqlite::Connection,
|
st: &AppState,
|
||||||
ruuid: Uuid,
|
ruuid: Uuid,
|
||||||
user: Option<&UserKey>,
|
user: Option<&UserKey>,
|
||||||
params: &GetRoomItemParams,
|
params: &GetRoomItemParams,
|
||||||
) -> Result<(RoomMetadata, Vec<(u64, ChatItem)>), ApiError> {
|
) -> Result<(RoomMetadata, Vec<(u64, ChatItem)>), ApiError> {
|
||||||
let (rid, title, attrs) = get_room_if_readable(conn, ruuid, user, |row| {
|
let conn = st.conn.lock().unwrap();
|
||||||
|
|
||||||
|
let (rid, title, attrs) = get_room_if_readable(&conn, ruuid, user, |row| {
|
||||||
Ok((
|
Ok((
|
||||||
row.get::<_, u64>("rid")?,
|
row.get::<_, u64>("rid")?,
|
||||||
row.get::<_, String>("title")?,
|
row.get::<_, String>("title")?,
|
||||||
|
@ -432,7 +432,7 @@ fn query_room_items(
|
||||||
named_params! {
|
named_params! {
|
||||||
":rid": rid,
|
":rid": rid,
|
||||||
":before_cid": params.before_id,
|
":before_cid": params.before_id,
|
||||||
":limit": PAGE_LEN,
|
":limit": st.config.server.max_page_len,
|
||||||
},
|
},
|
||||||
|row| {
|
|row| {
|
||||||
let cid = row.get::<_, u64>("cid")?;
|
let cid = row.get::<_, u64>("cid")?;
|
||||||
|
@ -543,7 +543,7 @@ async fn room_event(
|
||||||
let rx = match st.room_listeners.lock().unwrap().entry(rid) {
|
let rx = match st.room_listeners.lock().unwrap().entry(rid) {
|
||||||
Entry::Occupied(ent) => ent.get().subscribe(),
|
Entry::Occupied(ent) => ent.get().subscribe(),
|
||||||
Entry::Vacant(ent) => {
|
Entry::Vacant(ent) => {
|
||||||
let (tx, rx) = broadcast::channel(EVENT_QUEUE_LEN);
|
let (tx, rx) = broadcast::channel(st.config.server.event_queue_len);
|
||||||
ent.insert(tx);
|
ent.insert(tx);
|
||||||
rx
|
rx
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue