Private
Public Access
1
0

fix: resolve settings save failure - boolean type mismatch and improve error messages
All checks were successful
CI Pipeline / Rust Format Check (push) Successful in 4s
CI Pipeline / Clippy Lints (push) Successful in 57s
CI Pipeline / Rust Unit Tests (push) Successful in 1m19s
CI Pipeline / Security Audit (push) Successful in 4s
CI Pipeline / Frontend Lint & Type Check (push) Successful in 15s
CI Pipeline / Build .deb & Release (push) Has been skipped

This commit is contained in:
2026-05-13 00:07:46 +00:00
parent c8b1feee19
commit c8b6b01dbc
2 changed files with 56 additions and 56 deletions

View File

@ -293,62 +293,57 @@ async fn update_settings(
admin_only(&auth)?; admin_only(&auth)?;
// Update Azure SSO config // Update Azure SSO config
if let Some(azure) = &req.azure_sso { // Use static queries with proper typed bindings to avoid boolean→string mismatch
// Build dynamic UPDATE query — only set fields that are Some if let Some(azure) = req.azure_sso {
let mut sets = Vec::new(); let update_secret = azure.client_secret.as_ref().is_some_and(|s| s != MASKED);
let mut vals: Vec<String> = Vec::new();
let mut idx = 1;
if let Some(v) = azure.enabled { let result = if update_secret {
sets.push(format!("enabled = ${}", idx)); sqlx::query(
vals.push(v.to_string()); "UPDATE azure_sso_config SET \
idx += 1; enabled = COALESCE($1, enabled), \
} tenant_id = COALESCE($2, tenant_id), \
if let Some(ref v) = azure.tenant_id { client_id = COALESCE($3, client_id), \
sets.push(format!("tenant_id = ${}", idx)); client_secret = $4, \
vals.push(v.clone()); redirect_uri = COALESCE($5, redirect_uri), \
idx += 1; scopes = COALESCE($6, scopes), \
} updated_at = NOW() \
if let Some(ref v) = azure.client_id { WHERE id = 1",
sets.push(format!("client_id = ${}", idx)); )
vals.push(v.clone()); .bind(azure.enabled)
idx += 1; .bind(&azure.tenant_id)
} .bind(&azure.client_id)
if let Some(ref v) = azure.client_secret { .bind(azure.client_secret.as_deref().unwrap_or(""))
if v != MASKED { .bind(&azure.redirect_uri)
sets.push(format!("client_secret = ${}", idx)); .bind(&azure.scopes)
vals.push(v.clone()); .execute(&state.db)
idx += 1; .await
} } else {
} sqlx::query(
if let Some(ref v) = azure.redirect_uri { "UPDATE azure_sso_config SET \
sets.push(format!("redirect_uri = ${}", idx)); enabled = COALESCE($1, enabled), \
vals.push(v.clone()); tenant_id = COALESCE($2, tenant_id), \
idx += 1; client_id = COALESCE($3, client_id), \
} redirect_uri = COALESCE($4, redirect_uri), \
if let Some(ref v) = azure.scopes { scopes = COALESCE($5, scopes), \
sets.push(format!("scopes = ${}", idx)); updated_at = NOW() \
vals.push(v.clone()); WHERE id = 1",
idx += 1; )
} .bind(azure.enabled)
.bind(&azure.tenant_id)
.bind(&azure.client_id)
.bind(&azure.redirect_uri)
.bind(&azure.scopes)
.execute(&state.db)
.await
};
if !sets.is_empty() { result.map_err(|e| {
let sql = format!( tracing::error!(error = %e, "Failed to update azure_sso_config");
"UPDATE azure_sso_config SET {}, updated_at = NOW() WHERE id = 1", (
sets.join(", ") StatusCode::INTERNAL_SERVER_ERROR,
); Json(json!({ "error": { "code": "internal_error", "message": format!("Failed to update Azure SSO config: {}", e) } })),
let mut q = sqlx::query(&sql); )
for val in &vals { })?;
q = q.bind(val);
}
q.execute(&state.db).await.map_err(|e| {
tracing::error!(error = %e, "Failed to update azure_sso_config");
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({ "error": { "code": "internal_error", "message": "Database error" } })),
)
})?;
}
log_event( log_event(
&state.db, &state.db,

View File

@ -5,6 +5,7 @@ import {
IconButton, InputLabel, MenuItem, Select, Snackbar, Switch, TextField, IconButton, InputLabel, MenuItem, Select, Snackbar, Switch, TextField,
Toolbar, Typography, Toolbar, Typography,
} from '@mui/material' } from '@mui/material'
import type { AxiosError } from 'axios'
import ExpandMoreIcon from '@mui/icons-material/ExpandMore' import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
import SaveIcon from '@mui/icons-material/Save' import SaveIcon from '@mui/icons-material/Save'
import DeleteIcon from '@mui/icons-material/Delete' import DeleteIcon from '@mui/icons-material/Delete'
@ -78,8 +79,12 @@ export default function SettingsPage() {
notification, notification,
}) })
setSuccess('Settings saved successfully') setSuccess('Settings saved successfully')
} catch { } catch (err: unknown) {
setError('Failed to save settings') const axiosErr = err as AxiosError<{ error?: { message?: string } }>
const msg =
axiosErr.response?.data?.error?.message ??
(err instanceof Error ? err.message : 'Failed to save settings')
setError(msg)
} finally { } finally {
setSaving(false) setSaving(false)
} }