This commit is contained in:
Mateusz Gruszczyński committed 2026-10-06 10:16:16 +02:00
1 parent 32e6875980
commit cf25423dde
68 files changed
+2314 -422

No files matched your search

+165
View File
@@ -30,10 +30,51 @@ const BACKEND_TABLE_COLUMNS: &[&str] = &[
"lastFailure",
"lastRecovery",
];
const DASHBOARD_WIDGETS: &[&str] = &[
"requests",
"active",
"bandwidth",
"errors",
"cache",
"memory",
"websocket",
"http2",
"http3",
"tls",
"http_protocol",
"websocket_protocol",
];
fn default_admin_theme() -> String {
"dark".to_owned()
}
fn default_admin_language() -> String {
"auto".to_owned()
}
fn default_admin_ui_scale() -> u16 {
100
}
fn default_admin_time_format() -> String {
"24h".to_owned()
}
fn default_admin_chart_style() -> String {
"default".to_owned()
}
fn default_admin_button_style() -> String {
"normal".to_owned()
}
fn default_admin_table_density() -> String {
"normal".to_owned()
}
fn default_admin_dashboard_density() -> String {
"compact".to_owned()
}
fn default_admin_dashboard_widgets() -> Vec<String> {
DASHBOARD_WIDGETS
.iter()
.map(|value| (*value).to_owned())
.collect()
}
fn default_file_log_columns() -> Vec<String> {
[
"time",
@@ -64,6 +105,24 @@ struct AdminPreferences {
live_log_columns: Vec<String>,
#[serde(default = "default_admin_theme")]
theme: String,
#[serde(default = "default_admin_language")]
language: String,
#[serde(default = "default_admin_ui_scale")]
ui_scale: u16,
#[serde(default = "default_admin_time_format")]
time_format: String,
#[serde(default = "default_admin_chart_style")]
chart_style: String,
#[serde(default = "default_admin_button_style")]
button_style: String,
#[serde(default = "default_admin_table_density")]
table_density: String,
#[serde(default = "default_admin_dashboard_density")]
dashboard_density: String,
#[serde(default = "default_admin_dashboard_widgets")]
dashboard_widgets: Vec<String>,
#[serde(default)]
dashboard_hidden_widgets: Vec<String>,
#[serde(default = "default_file_log_columns")]
file_log_columns: Vec<String>,
#[serde(default = "default_backend_table_columns")]
@@ -82,6 +141,15 @@ impl Default for AdminPreferences {
.map(|value| (*value).to_owned())
.collect(),
theme: default_admin_theme(),
language: default_admin_language(),
ui_scale: default_admin_ui_scale(),
time_format: default_admin_time_format(),
chart_style: default_admin_chart_style(),
button_style: default_admin_button_style(),
table_density: default_admin_table_density(),
dashboard_density: default_admin_dashboard_density(),
dashboard_widgets: default_admin_dashboard_widgets(),
dashboard_hidden_widgets: Vec::new(),
file_log_columns: default_file_log_columns(),
backend_table_columns: default_backend_table_columns(),
backend_table_sort: default_backend_table_sort(),
@@ -135,6 +203,103 @@ fn validate_admin_preferences(preferences: &AdminPreferences) -> Result<(), ApiE
"invalid administration theme preference".into(),
));
}
if preferences.language != "auto"
&& (preferences.language.is_empty()
|| preferences.language.len() > 64
|| !preferences
.language
.chars()
.all(|value| value.is_ascii_alphanumeric() || matches!(value, '_' | '-')))
{
return Err(ApiError(
StatusCode::UNPROCESSABLE_ENTITY,
"invalid administration language preference".into(),
));
}
if !(70..=125).contains(&preferences.ui_scale) {
return Err(ApiError(
StatusCode::UNPROCESSABLE_ENTITY,
"invalid administration interface scale preference".into(),
));
}
if !matches!(preferences.time_format.as_str(), "24h" | "12h") {
return Err(ApiError(
StatusCode::UNPROCESSABLE_ENTITY,
"invalid administration time format preference".into(),
));
}
if !matches!(
preferences.chart_style.as_str(),
"default"
| "soft"
| "technical"
| "high_contrast"
| "neon"
| "monochrome"
| "warm"
| "cool"
) {
return Err(ApiError(
StatusCode::UNPROCESSABLE_ENTITY,
"invalid administration chart style preference".into(),
));
}
if !matches!(preferences.button_style.as_str(), "normal" | "compact") {
return Err(ApiError(
StatusCode::UNPROCESSABLE_ENTITY,
"invalid administration button style preference".into(),
));
}
if !matches!(
preferences.table_density.as_str(),
"normal" | "compact" | "ultra"
) {
return Err(ApiError(
StatusCode::UNPROCESSABLE_ENTITY,
"invalid administration table density preference".into(),
));
}
if !matches!(
preferences.dashboard_density.as_str(),
"normal" | "compact" | "ultra"
) {
return Err(ApiError(
StatusCode::UNPROCESSABLE_ENTITY,
"invalid administration dashboard density preference".into(),
));
}
if preferences.dashboard_widgets.is_empty()
|| preferences.dashboard_widgets.len() > DASHBOARD_WIDGETS.len()
{
return Err(ApiError(
StatusCode::UNPROCESSABLE_ENTITY,
"invalid administration dashboard widget order".into(),
));
}
let mut seen = std::collections::HashSet::new();
for widget in &preferences.dashboard_widgets {
if !DASHBOARD_WIDGETS.contains(&widget.as_str()) || !seen.insert(widget.as_str()) {
return Err(ApiError(
StatusCode::UNPROCESSABLE_ENTITY,
"invalid administration dashboard widget order".into(),
));
}
}
if preferences.dashboard_hidden_widgets.len() > DASHBOARD_WIDGETS.len() {
return Err(ApiError(
StatusCode::UNPROCESSABLE_ENTITY,
"invalid administration hidden dashboard widgets".into(),
));
}
let mut seen = std::collections::HashSet::new();
for widget in &preferences.dashboard_hidden_widgets {
if !DASHBOARD_WIDGETS.contains(&widget.as_str()) || !seen.insert(widget.as_str()) {
return Err(ApiError(
StatusCode::UNPROCESSABLE_ENTITY,
"invalid administration hidden dashboard widgets".into(),
));
}
}
if preferences.backend_table_columns.is_empty()
|| preferences.backend_table_columns.len() > BACKEND_TABLE_COLUMNS.len()
{