mirror of
https://github.com/telemt/telemt.git
synced 2026-04-29 00:14:11 +03:00
Atomically updates with Includes
Signed-off-by: Alexey <247128645+axkurcom@users.noreply.github.com>
This commit is contained in:
@@ -82,6 +82,7 @@ pub(super) async fn load_config_from_disk(config_path: &Path) -> Result<ProxyCon
|
|||||||
.map_err(|e| ApiFailure::internal(format!("failed to load config: {}", e)))
|
.map_err(|e| ApiFailure::internal(format!("failed to load config: {}", e)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(super) async fn save_config_to_disk(
|
pub(super) async fn save_config_to_disk(
|
||||||
config_path: &Path,
|
config_path: &Path,
|
||||||
cfg: &ProxyConfig,
|
cfg: &ProxyConfig,
|
||||||
@@ -106,6 +107,12 @@ pub(super) async fn save_access_sections_to_disk(
|
|||||||
if applied.contains(section) {
|
if applied.contains(section) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if find_toml_table_bounds(&content, section.table_name()).is_none()
|
||||||
|
&& access_section_is_empty(cfg, *section)
|
||||||
|
{
|
||||||
|
applied.push(*section);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let rendered = render_access_section(cfg, *section)?;
|
let rendered = render_access_section(cfg, *section)?;
|
||||||
content = upsert_toml_table(&content, section.table_name(), &rendered);
|
content = upsert_toml_table(&content, section.table_name(), &rendered);
|
||||||
applied.push(*section);
|
applied.push(*section);
|
||||||
@@ -183,6 +190,17 @@ fn render_access_section(cfg: &ProxyConfig, section: AccessSection) -> Result<St
|
|||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn access_section_is_empty(cfg: &ProxyConfig, section: AccessSection) -> bool {
|
||||||
|
match section {
|
||||||
|
AccessSection::Users => cfg.access.users.is_empty(),
|
||||||
|
AccessSection::UserAdTags => cfg.access.user_ad_tags.is_empty(),
|
||||||
|
AccessSection::UserMaxTcpConns => cfg.access.user_max_tcp_conns.is_empty(),
|
||||||
|
AccessSection::UserExpirations => cfg.access.user_expirations.is_empty(),
|
||||||
|
AccessSection::UserDataQuota => cfg.access.user_data_quota.is_empty(),
|
||||||
|
AccessSection::UserMaxUniqueIps => cfg.access.user_max_unique_ips.is_empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn serialize_table_body<T: Serialize>(value: &T) -> Result<String, ApiFailure> {
|
fn serialize_table_body<T: Serialize>(value: &T) -> Result<String, ApiFailure> {
|
||||||
toml::to_string(value)
|
toml::to_string(value)
|
||||||
.map_err(|e| ApiFailure::internal(format!("failed to serialize access section: {}", e)))
|
.map_err(|e| ApiFailure::internal(format!("failed to serialize access section: {}", e)))
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ use crate::stats::Stats;
|
|||||||
|
|
||||||
use super::ApiShared;
|
use super::ApiShared;
|
||||||
use super::config_store::{
|
use super::config_store::{
|
||||||
AccessSection, ensure_expected_revision, load_config_from_disk, save_access_sections_to_disk,
|
AccessSection, current_revision, ensure_expected_revision, load_config_from_disk,
|
||||||
save_config_to_disk,
|
save_access_sections_to_disk,
|
||||||
};
|
};
|
||||||
use super::model::{
|
use super::model::{
|
||||||
ApiFailure, CreateUserRequest, CreateUserResponse, PatchUserRequest, RotateSecretRequest,
|
ApiFailure, CreateUserRequest, CreateUserResponse, PatchUserRequest, RotateSecretRequest,
|
||||||
@@ -176,6 +176,13 @@ pub(super) async fn patch_user(
|
|||||||
expected_revision: Option<String>,
|
expected_revision: Option<String>,
|
||||||
shared: &ApiShared,
|
shared: &ApiShared,
|
||||||
) -> Result<(UserInfo, String), ApiFailure> {
|
) -> Result<(UserInfo, String), ApiFailure> {
|
||||||
|
let touches_users = body.secret.is_some();
|
||||||
|
let touches_user_ad_tags = !matches!(&body.user_ad_tag, Patch::Unchanged);
|
||||||
|
let touches_user_max_tcp_conns = !matches!(&body.max_tcp_conns, Patch::Unchanged);
|
||||||
|
let touches_user_expirations = !matches!(&body.expiration_rfc3339, Patch::Unchanged);
|
||||||
|
let touches_user_data_quota = !matches!(&body.data_quota_bytes, Patch::Unchanged);
|
||||||
|
let touches_user_max_unique_ips = !matches!(&body.max_unique_ips, Patch::Unchanged);
|
||||||
|
|
||||||
if let Some(secret) = body.secret.as_ref()
|
if let Some(secret) = body.secret.as_ref()
|
||||||
&& !is_valid_user_secret(secret)
|
&& !is_valid_user_secret(secret)
|
||||||
{
|
{
|
||||||
@@ -265,7 +272,31 @@ pub(super) async fn patch_user(
|
|||||||
cfg.validate()
|
cfg.validate()
|
||||||
.map_err(|e| ApiFailure::bad_request(format!("config validation failed: {}", e)))?;
|
.map_err(|e| ApiFailure::bad_request(format!("config validation failed: {}", e)))?;
|
||||||
|
|
||||||
let revision = save_config_to_disk(&shared.config_path, &cfg).await?;
|
let mut touched_sections = Vec::new();
|
||||||
|
if touches_users {
|
||||||
|
touched_sections.push(AccessSection::Users);
|
||||||
|
}
|
||||||
|
if touches_user_ad_tags {
|
||||||
|
touched_sections.push(AccessSection::UserAdTags);
|
||||||
|
}
|
||||||
|
if touches_user_max_tcp_conns {
|
||||||
|
touched_sections.push(AccessSection::UserMaxTcpConns);
|
||||||
|
}
|
||||||
|
if touches_user_expirations {
|
||||||
|
touched_sections.push(AccessSection::UserExpirations);
|
||||||
|
}
|
||||||
|
if touches_user_data_quota {
|
||||||
|
touched_sections.push(AccessSection::UserDataQuota);
|
||||||
|
}
|
||||||
|
if touches_user_max_unique_ips {
|
||||||
|
touched_sections.push(AccessSection::UserMaxUniqueIps);
|
||||||
|
}
|
||||||
|
|
||||||
|
let revision = if touched_sections.is_empty() {
|
||||||
|
current_revision(&shared.config_path).await?
|
||||||
|
} else {
|
||||||
|
save_access_sections_to_disk(&shared.config_path, &cfg, &touched_sections).await?
|
||||||
|
};
|
||||||
drop(_guard);
|
drop(_guard);
|
||||||
match max_unique_ips_change {
|
match max_unique_ips_change {
|
||||||
Some(Some(limit)) => shared.ip_tracker.set_user_limit(user, limit).await,
|
Some(Some(limit)) => shared.ip_tracker.set_user_limit(user, limit).await,
|
||||||
|
|||||||
Reference in New Issue
Block a user