refactor(hermes): share provider-source marker constants and write guard

After /simplify review of the P1-3 second wave, two small cleanups:

- Lift the `_cc_source` / `providers_dict` magic strings out of
  ProviderCard into a shared helper (`isHermesReadOnlyProvider`) and
  named constants in hermesProviderPresets.ts. Front-end and back-end
  now document the same marker contract in two mirrored places
  instead of drifting strings.

- Replace the duplicate `is_dict_only_provider` + `format!` branches
  at the top of `set_provider` / `remove_provider` with a single
  `ensure_provider_writable(config, name, verb)` guard. Future error
  copy tweaks only have to happen once.

No behaviour change; all 52 hermes_config tests stay green.
This commit is contained in:
Jason
2026-04-20 10:43:23 +08:00
parent abb305a82f
commit f57edfd697
3 changed files with 44 additions and 13 deletions
+19 -10
View File
@@ -635,6 +635,23 @@ pub fn get_providers() -> Result<serde_json::Map<String, serde_json::Value>, App
Ok(map)
}
/// Reject writes that would target a dict-only overlay entry.
///
/// `verb` is inlined into the user-facing error so both "edit" and "remove"
/// callers can share one implementation.
fn ensure_provider_writable(
config: &serde_yaml::Value,
name: &str,
verb: &str,
) -> Result<(), AppError> {
if is_dict_only_provider(config, name) {
return Err(AppError::Config(format!(
"Provider '{name}' is managed by Hermes' 'providers:' dict — {verb} via Hermes Web UI"
)));
}
Ok(())
}
/// True when `name` appears in `providers:` dict but not in `custom_providers:`
/// list — i.e. it is a read-only overlay CC Switch must not touch.
fn is_dict_only_provider(config: &serde_yaml::Value, name: &str) -> bool {
@@ -691,11 +708,7 @@ pub fn set_provider(
let _guard = hermes_write_lock().lock()?;
let config = read_hermes_config()?;
if is_dict_only_provider(&config, name) {
return Err(AppError::Config(format!(
"Provider '{name}' is managed by Hermes' 'providers:' dict — edit via Hermes Web UI"
)));
}
ensure_provider_writable(&config, name, "edit")?;
let mut providers: Vec<serde_yaml::Value> = config
.get("custom_providers")
.and_then(|v| v.as_sequence())
@@ -768,11 +781,7 @@ pub fn remove_provider(name: &str) -> Result<HermesWriteOutcome, AppError> {
let _guard = hermes_write_lock().lock()?;
let config = read_hermes_config()?;
if is_dict_only_provider(&config, name) {
return Err(AppError::Config(format!(
"Provider '{name}' is managed by Hermes' 'providers:' dict — remove via Hermes Web UI"
)));
}
ensure_provider_writable(&config, name, "remove")?;
let mut providers: Vec<serde_yaml::Value> = config
.get("custom_providers")