fix: address Phase 1-2 audit findings

- CORS: replace permissive() with configurable whitelist (default.toml)
- Auth store: synchronously restore state at creation to eliminate
  flash-of-login-page on refresh
- MainLayout: menu highlight now tracks current route via useLocation
- Add extractErrorMessage() utility to reduce repeated error parsing
- Fix all clippy warnings across 4 crates (erp-auth, erp-config,
  erp-workflow, erp-message): remove unnecessary casts, use div_ceil,
  collapse nested ifs, reduce function arguments with DTOs
This commit is contained in:
iven
2026-04-11 12:36:34 +08:00
parent 5c899e6f4a
commit 3a05523d23
35 changed files with 283 additions and 187 deletions

View File

@@ -71,10 +71,7 @@ impl SettingService {
/// If a record with the same (scope, scope_id, key) exists and is not
/// soft-deleted, it will be updated. Otherwise a new record is inserted.
pub async fn set(
key: &str,
scope: &str,
scope_id: &Option<Uuid>,
value: serde_json::Value,
params: crate::dto::SetSettingParams,
tenant_id: Uuid,
operator_id: Uuid,
db: &sea_orm::DatabaseConnection,
@@ -83,9 +80,9 @@ impl SettingService {
// Look for an existing non-deleted record
let existing = setting::Entity::find()
.filter(setting::Column::TenantId.eq(tenant_id))
.filter(setting::Column::Scope.eq(scope))
.filter(setting::Column::ScopeId.eq(*scope_id))
.filter(setting::Column::SettingKey.eq(key))
.filter(setting::Column::Scope.eq(&params.scope))
.filter(setting::Column::ScopeId.eq(params.scope_id))
.filter(setting::Column::SettingKey.eq(&params.key))
.filter(setting::Column::DeletedAt.is_null())
.one(db)
.await
@@ -94,7 +91,7 @@ impl SettingService {
if let Some(model) = existing {
// Update existing record
let mut active: setting::ActiveModel = model.into();
active.setting_value = Set(value.clone());
active.setting_value = Set(params.value.clone());
active.updated_at = Set(Utc::now());
active.updated_by = Set(operator_id);
@@ -108,8 +105,8 @@ impl SettingService {
tenant_id,
serde_json::json!({
"setting_id": updated.id,
"key": key,
"scope": scope,
"key": params.key,
"scope": params.scope,
}),
));
@@ -121,10 +118,10 @@ impl SettingService {
let model = setting::ActiveModel {
id: Set(id),
tenant_id: Set(tenant_id),
scope: Set(scope.to_string()),
scope_id: Set(*scope_id),
setting_key: Set(key.to_string()),
setting_value: Set(value),
scope: Set(params.scope.clone()),
scope_id: Set(params.scope_id),
setting_key: Set(params.key.clone()),
setting_value: Set(params.value),
created_at: Set(now),
updated_at: Set(now),
created_by: Set(operator_id),
@@ -142,8 +139,8 @@ impl SettingService {
tenant_id,
serde_json::json!({
"setting_id": id,
"key": key,
"scope": scope,
"key": params.key,
"scope": params.scope,
}),
));
@@ -171,7 +168,7 @@ impl SettingService {
.await
.map_err(|e| ConfigError::Validation(e.to_string()))?;
let page_index = pagination.page.unwrap_or(1).saturating_sub(1) as u64;
let page_index = pagination.page.unwrap_or(1).saturating_sub(1);
let models = paginator
.fetch_page(page_index)
.await
@@ -248,7 +245,7 @@ impl SettingService {
/// Returns a list of (scope, scope_id) tuples to try in order.
fn fallback_chain(
scope: &str,
scope_id: &Option<Uuid>,
_scope_id: &Option<Uuid>,
tenant_id: Uuid,
) -> ConfigResult<Vec<(String, Option<Uuid>)>> {
match scope {