feat(pixelle): 连接凭证 service(settings 读写 + config.yaml 同步)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -14,3 +14,4 @@ pub mod runtime_logs;
|
||||
pub mod shared;
|
||||
pub mod workspace;
|
||||
pub mod comfy_cloud;
|
||||
pub mod pixelle_connection;
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
use crate::error::{AppError, AppResult};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PixelleConnection {
|
||||
pub runninghub_api_key: Option<String>,
|
||||
pub comfyui_url: Option<String>,
|
||||
pub comfyui_api_key: Option<String>,
|
||||
}
|
||||
|
||||
const KEY_RH: &str = "pixelle.runninghub_api_key";
|
||||
const KEY_URL: &str = "pixelle.comfyui_url";
|
||||
const KEY_CK: &str = "pixelle.comfyui_api_key";
|
||||
|
||||
pub async fn read_connection(db: &SqlitePool) -> AppResult<PixelleConnection> {
|
||||
async fn get(db: &SqlitePool, k: &str) -> AppResult<Option<String>> {
|
||||
Ok(sqlx::query_scalar("SELECT value FROM settings WHERE key = ?")
|
||||
.bind(k).fetch_optional(db).await?)
|
||||
}
|
||||
Ok(PixelleConnection {
|
||||
runninghub_api_key: get(db, KEY_RH).await?,
|
||||
comfyui_url: get(db, KEY_URL).await?,
|
||||
comfyui_api_key: get(db, KEY_CK).await?,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn save_connection(db: &SqlitePool, conn: &PixelleConnection) -> AppResult<()> {
|
||||
async fn put(db: &SqlitePool, k: &str, v: &Option<String>) -> AppResult<()> {
|
||||
if let Some(val) = v {
|
||||
sqlx::query("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)")
|
||||
.bind(k).bind(val).execute(db).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
put(db, KEY_RH, &conn.runninghub_api_key).await?;
|
||||
put(db, KEY_URL, &conn.comfyui_url).await?;
|
||||
put(db, KEY_CK, &conn.comfyui_api_key).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 改 config.yaml 的 comfyui.{runninghub_api_key,comfyui_url,comfyui_api_key} 三键,其余保留。
|
||||
/// 改前备份到 <path>.bak-<ts>。返回备份路径。
|
||||
pub fn sync_to_pixelle(config_path: &str, conn: &PixelleConnection) -> AppResult<String> {
|
||||
let content = std::fs::read_to_string(config_path)
|
||||
.map_err(|e| AppError::ServiceUnavailable(format!("读取 Pixelle config 失败: {e}")))?;
|
||||
let mut doc: serde_yaml::Value = serde_yaml::from_str(&content)
|
||||
.map_err(|e| AppError::ServiceUnavailable(format!("解析 config.yaml 失败: {e}")))?;
|
||||
let comfyui = doc.get_mut("comfyui")
|
||||
.ok_or_else(|| AppError::ServiceUnavailable("config.yaml 缺少 comfyui 段".into()))?;
|
||||
let map = comfyui.as_mapping_mut()
|
||||
.ok_or_else(|| AppError::ServiceUnavailable("comfyui 段不是映射".into()))?;
|
||||
if let Some(v) = &conn.runninghub_api_key {
|
||||
map.insert("runninghub_api_key".into(), serde_yaml::Value::String(v.clone()));
|
||||
}
|
||||
if let Some(v) = &conn.comfyui_url {
|
||||
map.insert("comfyui_url".into(), serde_yaml::Value::String(v.clone()));
|
||||
}
|
||||
if let Some(v) = &conn.comfyui_api_key {
|
||||
map.insert("comfyui_api_key".into(), serde_yaml::Value::String(v.clone()));
|
||||
}
|
||||
let ts = chrono::Utc::now().format("%Y%m%d%H%M%S").to_string();
|
||||
let backup = format!("{config_path}.bak-{ts}");
|
||||
std::fs::copy(config_path, &backup)
|
||||
.map_err(|e| AppError::ServiceUnavailable(format!("备份失败: {e}")))?;
|
||||
let out = serde_yaml::to_string(&doc)
|
||||
.map_err(|e| AppError::Internal(anyhow::anyhow!(e)))?;
|
||||
std::fs::write(config_path, out)
|
||||
.map_err(|e| AppError::ServiceUnavailable(format!("写入 config 失败: {e}")))?;
|
||||
Ok(backup)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn sync_updates_three_keys_preserves_rest() {
|
||||
let dir = std::env::temp_dir().join(format!("pxtest_{}", uuid::Uuid::new_v4().simple()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let path = dir.join("config.yaml");
|
||||
std::fs::write(&path,
|
||||
"project_name: Pixelle-Video\ncomfyui:\n comfyui_url: https://old\n runninghub_api_key: oldkey\n comfyui_api_key: oldck\n runninghub_concurrent_limit: 1\n").unwrap();
|
||||
let conn = PixelleConnection {
|
||||
runninghub_api_key: Some("newkey".into()),
|
||||
comfyui_url: Some("http://new".into()),
|
||||
comfyui_api_key: Some("newck".into()),
|
||||
};
|
||||
let backup = sync_to_pixelle(path.to_str().unwrap(), &conn).unwrap();
|
||||
assert!(std::path::Path::new(&backup).exists());
|
||||
let after = std::fs::read_to_string(&path).unwrap();
|
||||
assert!(after.contains("newkey"));
|
||||
assert!(after.contains("http://new"));
|
||||
assert!(after.contains("newck"));
|
||||
assert!(after.contains("runninghub_concurrent_limit"));
|
||||
assert!(after.contains("project_name"));
|
||||
std::fs::remove_dir_all(&dir).ok();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user