fix: 低优先级收尾 — 图片上传/语言编辑/插件恢复/URL 编码
- P3-2: ArticleEditor 图片上传接入 /upload 端点 + 封面图上传按钮 - P4-3: recover_plugins 添加 tenant 日志 + 同 ID 去重保护 - P4-4: LanguageManager 编辑弹窗改为真实表单 (name 字段) + 后端 name 持久化 - P4-6: Settings API getSetting/updateSetting 添加 encodeURIComponent
This commit is contained in:
@@ -238,6 +238,7 @@ pub struct LanguageResp {
|
||||
#[derive(Debug, Deserialize, ToSchema)]
|
||||
pub struct UpdateLanguageReq {
|
||||
pub is_active: bool,
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -50,7 +50,12 @@ where
|
||||
.filter(|s| s.setting_key.starts_with("language."))
|
||||
.filter_map(|s| {
|
||||
let code = s.setting_key.strip_prefix("language.")?.to_string();
|
||||
let name = code.clone(); // 默认使用 code 作为名称
|
||||
let name = s
|
||||
.setting_value
|
||||
.get("name")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or(&code)
|
||||
.to_string();
|
||||
let is_active = s
|
||||
.setting_value
|
||||
.get("is_active")
|
||||
@@ -98,7 +103,10 @@ where
|
||||
require_permission(&ctx, "language.update")?;
|
||||
|
||||
let key = format!("language.{}", code);
|
||||
let value = serde_json::json!({"is_active": req.is_active});
|
||||
let mut value = serde_json::json!({"is_active": req.is_active});
|
||||
if let Some(ref name) = req.name {
|
||||
value["name"] = serde_json::Value::String(name.clone());
|
||||
}
|
||||
|
||||
SettingService::set(
|
||||
SetSettingParams {
|
||||
|
||||
@@ -455,14 +455,27 @@ impl PluginEngine {
|
||||
|
||||
let mut recovered = Vec::new();
|
||||
for model in running_plugins {
|
||||
let tenant_id = model.tenant_id;
|
||||
let manifest: PluginManifest = serde_json::from_value(model.manifest_json.clone())
|
||||
.map_err(|e| PluginError::InvalidManifest(e.to_string()))?;
|
||||
let plugin_id_str = &manifest.metadata.id;
|
||||
|
||||
// 跳过已被其他租户加载的同 ID 插件(WASM 二进制相同,数据隔离在 DB 层)
|
||||
if self.plugins.contains_key(plugin_id_str) {
|
||||
tracing::info!(
|
||||
plugin_id = %plugin_id_str,
|
||||
tenant_id = %tenant_id,
|
||||
"Plugin already loaded by another tenant, skipping duplicate load"
|
||||
);
|
||||
recovered.push(plugin_id_str.clone());
|
||||
continue;
|
||||
}
|
||||
|
||||
// 加载 WASM 到内存
|
||||
if let Err(e) = self.load(plugin_id_str, &model.wasm_binary, manifest.clone()).await {
|
||||
tracing::error!(
|
||||
plugin_id = %plugin_id_str,
|
||||
tenant_id = %tenant_id,
|
||||
error = %e,
|
||||
"Failed to recover plugin (load)"
|
||||
);
|
||||
@@ -473,6 +486,7 @@ impl PluginEngine {
|
||||
if let Err(e) = self.initialize(plugin_id_str).await {
|
||||
tracing::error!(
|
||||
plugin_id = %plugin_id_str,
|
||||
tenant_id = %tenant_id,
|
||||
error = %e,
|
||||
"Failed to recover plugin (initialize)"
|
||||
);
|
||||
@@ -483,13 +497,18 @@ impl PluginEngine {
|
||||
if let Err(e) = self.start_event_listener(plugin_id_str).await {
|
||||
tracing::error!(
|
||||
plugin_id = %plugin_id_str,
|
||||
tenant_id = %tenant_id,
|
||||
error = %e,
|
||||
"Failed to recover plugin (start_event_listener)"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
tracing::info!(plugin_id = %plugin_id_str, "Plugin recovered");
|
||||
tracing::info!(
|
||||
plugin_id = %plugin_id_str,
|
||||
tenant_id = %tenant_id,
|
||||
"Plugin recovered"
|
||||
);
|
||||
recovered.push(plugin_id_str.clone());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user