mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-09-30 05:02:06 +02:00
Runtime artifacts (agent workspaces, tool-output spill, C2 payloads, chat uploads, workflow checkpoints, diagnostic logs) previously accumulated without bound: most were only removed when a conversation or project was deleted, and tmp/c2 plus workflow checkpoints were never removed at all. Add a storage cleaner with named per-category tasks (Gitea-style), a settings page tab, and a background sweep that is off by default so upgrading never deletes existing data. Safety properties, since mis-deleting live task data costs far more than the disk saved: - dry-run is the default; a real cleanup requires dry_run=false together with confirm=true at the API layer, not just a frontend dialog - sessions active within active_grace_hours are always skipped, and a failed activity lookup skips conservatively (fail closed) - directories whose conversation/project no longer exists are reclaimed as orphans after orphan_grace_days - scanners never follow symlinks and every candidate path is confined to its category root; deletion renames to a .tmp-for-deletion marker first so a crash leaves recoverable residue instead of a half-deleted dir - storage:* permissions are admin-only; without the grantSystemRolePermissions skip the default branch would have given operators an irreversible file-deletion right Also fix two confirmed leaks: DeleteConversation left chat_uploads files on disk (their rows already vanished via ON DELETE CASCADE), and workflow checkpoints had no deletion path at all. Co-authored-by: Parallels <parallels@kali-linux-2025-2.localdomain>
This commit is contained in:
@@ -33,6 +33,7 @@ import (
|
||||
"cyberstrike-ai/internal/robot"
|
||||
"cyberstrike-ai/internal/security"
|
||||
"cyberstrike-ai/internal/skillpackage"
|
||||
"cyberstrike-ai/internal/storage"
|
||||
"cyberstrike-ai/internal/toolguard"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -72,6 +73,7 @@ type App struct {
|
||||
c2Watchdog *c2.SessionWatchdog // C2 会话看门狗
|
||||
c2WatchdogCancel context.CancelFunc // 看门狗取消函数
|
||||
c2Handler *handler.C2Handler // C2 REST(与 Manager 生命周期同步)
|
||||
storageHandler *handler.StorageHandler // 运行空间占用统计与垃圾清理
|
||||
auditSvc *audit.Service
|
||||
}
|
||||
|
||||
@@ -367,6 +369,48 @@ func New(cfg *config.Config, log *logger.Logger, configPath string) (*App, error
|
||||
reductionRoot := strings.TrimSpace(cfg.MultiAgent.EinoMiddleware.ReductionRootDir)
|
||||
workspaceRoot := strings.TrimSpace(cfg.Agent.WorkspaceRootDir)
|
||||
db.SetEinoConversationDirs(plantaskBase, checkpointBase, reductionRoot, workspaceRoot)
|
||||
|
||||
// 运行空间垃圾清理:根目录一律复用上面已解析好的同一批值,
|
||||
// 避免在 storage 包内重新推导导致「清理的目录」与「实际写入的目录」不一致。
|
||||
workspaceRootDir := strings.TrimSpace(workspaceRoot)
|
||||
if workspaceRootDir == "" {
|
||||
workspaceRootDir = filepath.Join("tmp", "workspace")
|
||||
}
|
||||
reductionRootDir := strings.TrimSpace(reductionRoot)
|
||||
if reductionRootDir == "" {
|
||||
reductionRootDir = filepath.Join("tmp", "reduction")
|
||||
}
|
||||
diagnosticLogDir := strings.TrimSpace(cfg.Log.DiagnosticDir)
|
||||
if diagnosticLogDir == "" {
|
||||
diagnosticLogDir = "log"
|
||||
}
|
||||
// chat_uploads 与 tmp/c2 目前均为相对进程工作目录的固定路径
|
||||
// (见 handler.chatUploadsRootDirName 与 app/c2_lifecycle.go 的 c2.NewManager)。
|
||||
chatUploadsRoot := "chat_uploads"
|
||||
c2Root := filepath.Join("tmp", "c2")
|
||||
// 让 DeleteConversation 一并删除上传附件:其 chat_upload_artifacts 行已由
|
||||
// ON DELETE CASCADE 清除,此前磁盘文件会永久残留。
|
||||
db.SetChatUploadsDir(chatUploadsRoot)
|
||||
storageCleaner := storage.NewCleaner(storage.Options{
|
||||
Config: cfg,
|
||||
Paths: storage.Paths{
|
||||
Workspace: workspaceRootDir,
|
||||
Reduction: reductionRootDir,
|
||||
ConversationArtifact: db.ConversationArtifactsBaseDir(),
|
||||
Plantask: plantaskBase,
|
||||
C2: c2Root,
|
||||
ChatUploads: chatUploadsRoot,
|
||||
WorkflowCheckpoints: filepath.Join(filepath.Dir(dbPath), "workflow-checkpoints"),
|
||||
DiagnosticLogs: diagnosticLogDir,
|
||||
},
|
||||
Activity: db,
|
||||
Logger: log.Logger,
|
||||
})
|
||||
storageService := storage.NewService(storageCleaner, cfg, log.Logger)
|
||||
storage.StartRetentionLoop(storageService, log.Logger)
|
||||
storageHandler := handler.NewStorageHandler(storageCleaner, cfg, log.Logger)
|
||||
storageHandler.SetAudit(auditSvc)
|
||||
|
||||
agent.SetPromptBaseDir(configDir)
|
||||
|
||||
agentsDir := cfg.AgentsDir
|
||||
@@ -479,6 +523,7 @@ func New(cfg *config.Config, log *logger.Logger, configPath string) (*App, error
|
||||
c2Watchdog: c2Watchdog,
|
||||
c2WatchdogCancel: watchdogCancel,
|
||||
c2Handler: c2Handler,
|
||||
storageHandler: storageHandler,
|
||||
auditSvc: auditSvc,
|
||||
}
|
||||
// 飞书/钉钉长连接(无需公网),启用时在后台启动;后续前端应用配置时会通过 RestartRobotConnections 重启
|
||||
@@ -1089,6 +1134,11 @@ func setupRoutes(
|
||||
protected.GET("/audit/logs/export", auditHandler.ExportLogs)
|
||||
protected.GET("/audit/logs/:id", auditHandler.GetLog)
|
||||
|
||||
// 运行空间占用与垃圾清理
|
||||
protected.GET("/storage/meta", app.storageHandler.Meta)
|
||||
protected.GET("/storage/status", app.storageHandler.Status)
|
||||
protected.POST("/storage/cleanup", app.storageHandler.Cleanup)
|
||||
|
||||
// 外部MCP管理
|
||||
protected.GET("/external-mcp", externalMCPHandler.GetExternalMCPs)
|
||||
protected.GET("/external-mcp/stats", externalMCPHandler.GetExternalMCPStats)
|
||||
|
||||
Reference in New Issue
Block a user