feat: 新增管理后台前端项目及安全加固
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

refactor(saas): 重构认证中间件与限流策略
- 登录限流调整为5次/分钟/IP
- 注册限流调整为3次/小时/IP
- GET请求不计入限流

fix(saas): 修复调度器时间戳处理
- 使用NOW()替代文本时间戳
- 兼容TEXT和TIMESTAMPTZ列类型

feat(saas): 实现环境变量插值
- 支持${ENV_VAR}语法解析
- 数据库密码支持环境变量注入

chore: 新增前端管理界面
- 基于React+Ant Design Pro
- 包含路由守卫/错误边界
- 对接58个API端点

docs: 更新安全加固文档
- 新增密钥管理规范
- 记录P0安全项审计结果
- 补充TLS终止说明

test: 完善配置解析单元测试
- 新增环境变量插值测试用例
This commit is contained in:
iven
2026-03-31 00:11:33 +08:00
parent 6821df5f44
commit eb956d0dce
129 changed files with 11913 additions and 863 deletions

View File

@@ -428,9 +428,8 @@ static LAST_INTERACTION: OnceLock<RwLock<StdHashMap<String, String>>> = OnceLock
pub struct MemoryStatsCache {
pub task_count: usize,
pub total_entries: usize,
#[allow(dead_code)] // Reserved for UI display
pub storage_size_bytes: usize,
#[allow(dead_code)] // Reserved for UI display
#[allow(dead_code)] // Reserved for UI display; will be exposed via heartbeat_get_memory_stats
pub last_updated: Option<String>,
}

View File

@@ -539,7 +539,7 @@ pub type IdentityManagerState = Arc<Mutex<AgentIdentityManager>>;
/// Initialize identity manager
#[tauri::command]
#[allow(dead_code)] // Registered via invoke_handler! at runtime
#[allow(dead_code)] // NOT registered in invoke_handler — identity state is initialized lazily via identity_get
pub async fn identity_init() -> Result<IdentityManagerState, String> {
Ok(Arc::new(Mutex::new(AgentIdentityManager::new())))
}

View File

@@ -16,29 +16,20 @@ use zclaw_runtime::driver::LlmDriver;
/// Run pre-conversation intelligence hooks
///
/// 1. Build memory context from VikingStorage (FTS5 + TF-IDF + Embedding)
/// 2. Build identity-enhanced system prompt (SOUL.md + instructions)
/// Builds identity-enhanced system prompt (SOUL.md + instructions).
///
/// Returns the enhanced system prompt that should be passed to the kernel.
/// NOTE: Memory context injection is NOT done here — it is handled by
/// `MemoryMiddleware.before_completion()` in the Kernel's middleware chain.
/// Previously, both paths injected memories, causing duplicate injection.
pub async fn pre_conversation_hook(
agent_id: &str,
user_message: &str,
_user_message: &str,
identity_state: &IdentityManagerState,
) -> Result<String, String> {
// Step 1: Build memory context from Viking storage
let memory_context = match build_memory_context(agent_id, user_message).await {
Ok(ctx) => ctx,
Err(e) => {
warn!(
"[intelligence_hooks] Failed to build memory context for agent {}: {}",
agent_id, e
);
String::new()
}
};
// Step 2: Build identity-enhanced system prompt
let enhanced_prompt = match build_identity_prompt(agent_id, &memory_context, identity_state).await {
// Build identity-enhanced system prompt (SOUL.md + instructions)
// Memory context is injected by MemoryMiddleware in the kernel middleware chain,
// not here, to avoid duplicate injection.
let enhanced_prompt = match build_identity_prompt(agent_id, "", identity_state).await {
Ok(prompt) => prompt,
Err(e) => {
warn!(
@@ -117,6 +108,10 @@ pub async fn post_conversation_hook(
}
/// Build memory context by searching VikingStorage for relevant memories
///
/// NOTE: Memory injection is now handled by MemoryMiddleware in the Kernel
/// middleware chain. This function is kept as a utility for ad-hoc queries.
#[allow(dead_code)]
async fn build_memory_context(
agent_id: &str,
user_message: &str,

View File

@@ -16,6 +16,9 @@ use crate::intelligence::validation::{validate_identifier, validate_string_lengt
/// Kernel state wrapper for Tauri
pub type KernelState = Arc<Mutex<Option<Kernel>>>;
/// Scheduler state — holds a reference to the SchedulerService so it can be stopped on shutdown
pub type SchedulerState = Arc<Mutex<Option<zclaw_kernel::scheduler::SchedulerService>>>;
/// Session-level stream concurrency guard.
/// Prevents two concurrent `agent_chat_stream` calls from interleaving events
/// for the same session_id.
@@ -146,6 +149,7 @@ fn default_kernel_model() -> String { "gpt-4o-mini".to_string() }
#[tauri::command]
pub async fn kernel_init(
state: State<'_, KernelState>,
scheduler_state: State<'_, SchedulerState>,
config_request: Option<KernelConfigRequest>,
) -> Result<KernelStatusResponse, String> {
let mut kernel_lock = state.lock().await;
@@ -267,6 +271,22 @@ pub async fn kernel_init(
*kernel_lock = Some(kernel);
// Start SchedulerService — periodically checks and fires scheduled triggers
{
let mut sched_lock = scheduler_state.lock().await;
// Stop old scheduler if any
if let Some(ref old) = *sched_lock {
old.stop();
}
let scheduler = zclaw_kernel::scheduler::SchedulerService::new(
state.inner().clone(),
60, // check every 60 seconds
);
scheduler.start();
tracing::info!("[kernel_init] SchedulerService started (60s interval)");
*sched_lock = Some(scheduler);
}
Ok(KernelStatusResponse {
initialized: true,
agent_count,
@@ -305,7 +325,17 @@ pub async fn kernel_status(
#[tauri::command]
pub async fn kernel_shutdown(
state: State<'_, KernelState>,
scheduler_state: State<'_, SchedulerState>,
) -> Result<(), String> {
// Stop scheduler first
{
let mut sched_lock = scheduler_state.lock().await;
if let Some(scheduler) = sched_lock.take() {
scheduler.stop();
tracing::info!("[kernel_shutdown] SchedulerService stopped");
}
}
let mut kernel_lock = state.lock().await;
if let Some(kernel) = kernel_lock.take() {
@@ -806,6 +836,11 @@ pub fn create_kernel_state() -> KernelState {
Arc::new(Mutex::new(None))
}
/// Create the scheduler state for Tauri
pub fn create_scheduler_state() -> SchedulerState {
Arc::new(Mutex::new(None))
}
// ============================================================================
// Skills Commands - Dynamic Discovery
// ============================================================================
@@ -1964,10 +1999,8 @@ pub struct ScheduledTaskResponse {
/// Create a scheduled task (backed by kernel TriggerManager)
///
/// ⚠️ PLANNNED: Tasks are stored in the kernel's trigger system, but automatic
/// execution requires a scheduler loop that is not yet implemented in embedded
/// kernel mode. Created tasks will be persisted but not auto-executed until
/// the scheduler loop is implemented.
/// Tasks are automatically executed by the SchedulerService which checks
/// every 60 seconds for due triggers.
#[tauri::command]
pub async fn scheduled_task_create(
state: State<'_, KernelState>,

View File

@@ -212,7 +212,6 @@ fn get_platform_binary_names() -> Vec<String> {
}
/// Legacy: Build staged runtime using Node.js (for backward compatibility)
#[allow(dead_code)]
fn build_staged_runtime_legacy(source: &str, root_dir: PathBuf) -> Option<ZclawRuntime> {
let node_executable = root_dir.join(if cfg!(target_os = "windows") {
"node.exe"
@@ -973,11 +972,9 @@ fn zclaw_version(app: AppHandle) -> Result<VersionResponse, String> {
/// Health status enum
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "lowercase")]
#[allow(dead_code)] // Reserved for future health check expansion
enum HealthStatus {
Healthy,
Unhealthy,
Unknown,
}
/// Port check result
@@ -1309,6 +1306,9 @@ pub fn run() {
// Initialize internal ZCLAW Kernel state
let kernel_state = kernel_commands::create_kernel_state();
// Initialize Scheduler state (for automatic trigger execution)
let scheduler_state = kernel_commands::create_scheduler_state();
// Initialize Pipeline state (DSL-based workflows)
let pipeline_state = pipeline_commands::create_pipeline_state();
@@ -1320,6 +1320,7 @@ pub fn run() {
.manage(reflection_state)
.manage(identity_state)
.manage(kernel_state)
.manage(scheduler_state)
.manage(kernel_commands::SessionStreamGuard::default())
.manage(pipeline_state)
.invoke_handler(tauri::generate_handler![