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
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:
@@ -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>,
|
||||
}
|
||||
|
||||
|
||||
@@ -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())))
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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![
|
||||
|
||||
Reference in New Issue
Block a user