diff --git a/agent/logger.go b/agent/logger.go index d8237ff..228fb53 100644 --- a/agent/logger.go +++ b/agent/logger.go @@ -65,5 +65,10 @@ func initLogger(logFile string) { Compress: false, } - log.SetOutput(io.MultiWriter(os.Stdout, lumberjackLogger)) + // daemon 模式下只输出到文件,避免重复日志 + if isDaemon { + log.SetOutput(lumberjackLogger) + } else { + log.SetOutput(io.MultiWriter(os.Stdout, lumberjackLogger)) + } } diff --git a/internal/controllers/task_controller.go b/internal/controllers/task_controller.go index 7480313..e4e5764 100644 --- a/internal/controllers/task_controller.go +++ b/internal/controllers/task_controller.go @@ -78,8 +78,11 @@ func (tc *TaskController) CreateTask(c *gin.Context) { return } - // 转换为绝对路径 - workDir := resolveWorkDir(req.WorkDir) + // 转换为绝对路径(Agent 任务保持原样) + workDir := req.WorkDir + if req.AgentID == nil || *req.AgentID == 0 { + workDir = resolveWorkDir(req.WorkDir) + } task := tc.taskService.CreateTask(req.Name, req.Command, req.Schedule, req.Timeout, workDir, req.CleanConfig, req.Envs, req.Type, req.Config, req.AgentID) @@ -166,7 +169,13 @@ func (tc *TaskController) UpdateTask(c *gin.Context) { } } - task := tc.taskService.UpdateTask(id, req.Name, req.Command, req.Schedule, req.Timeout, resolveWorkDir(req.WorkDir), req.CleanConfig, req.Envs, req.Enabled, req.Type, req.Config, req.AgentID) + // 转换为绝对路径(Agent 任务保持原样) + workDir := req.WorkDir + if req.AgentID == nil || *req.AgentID == 0 { + workDir = resolveWorkDir(req.WorkDir) + } + + task := tc.taskService.UpdateTask(id, req.Name, req.Command, req.Schedule, req.Timeout, workDir, req.CleanConfig, req.Envs, req.Enabled, req.Type, req.Config, req.AgentID) if task == nil { utils.NotFound(c, "任务不存在") return diff --git a/web/src/views/agents/Agents.vue b/web/src/views/agents/Agents.vue index 8c88d6c..984cc4b 100644 --- a/web/src/views/agents/Agents.vue +++ b/web/src/views/agents/Agents.vue @@ -6,11 +6,10 @@ import { Label } from '@/components/ui/label' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from '@/components/ui/dialog' import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from '@/components/ui/alert-dialog' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' -import { RefreshCw, Trash2, Edit, Copy, Server, Search, Download, RotateCw, Plus, Ticket, Power, PowerOff, ListTodo } from 'lucide-vue-next' +import { RefreshCw, Trash2, Edit, Copy, Server, Search, Download, RotateCw, Plus, Ticket, Power, PowerOff, ListTodo, Eye } from 'lucide-vue-next' import { api, type Agent, type AgentRegCode } from '@/api' import { toast } from 'vue-sonner' import { useRouter } from 'vue-router' -import TextOverflow from '@/components/TextOverflow.vue' const router = useRouter() @@ -25,10 +24,12 @@ const showEditDialog = ref(false) const showDeleteDialog = ref(false) const showDownloadDialog = ref(false) const showRegCodeDialog = ref(false) +const showDetailDialog = ref(false) const formData = ref({ name: '', description: '' }) const regCodeForm = ref({ remark: '', max_uses: 0, expires_at: '' }) const editingAgent = ref(null) const deletingAgent = ref(null) +const viewingAgent = ref(null) let refreshTimer: ReturnType | null = null const filteredAgents = computed(() => { @@ -41,13 +42,12 @@ const filteredAgents = computed(() => { ) }) -// 判断 Agent 是否在线(last_seen 在 2 分钟内) function isOnline(agent: Agent): boolean { if (!agent.last_seen) return false const lastSeen = new Date(agent.last_seen) const now = new Date() const diffMs = now.getTime() - lastSeen.getTime() - return diffMs < 2 * 60 * 1000 // 2 分钟 + return diffMs < 2 * 60 * 1000 } async function loadAgents() { @@ -69,6 +69,11 @@ async function loadAgents() { } } +function viewDetail(agent: Agent) { + viewingAgent.value = agent + showDetailDialog.value = true +} + function openEditDialog(agent: Agent) { editingAgent.value = agent formData.value = { name: agent.name, description: agent.description } @@ -220,57 +225,55 @@ onUnmounted(() => {
-
- - 名称 - IP - 主机名 - 版本 - 构建时间 - 心跳时间 - 描述 - 操作 +
+ ID + + 名称 + + + + + + 操作
-
+
{{ searchQuery ? '无匹配结果' : '暂无 Agent' }}
-
- - - - +
+ #{{ agent.id }} + + + + + - - {{ agent.name }} - {{ agent.ip || '-' }} - {{ agent.hostname || '-' }} - {{ agent.version || '-' }} - {{ agent.build_time || '-' }} - {{ agent.last_seen || '-' }} - - - - - - - - - +
@@ -280,38 +283,38 @@ onUnmounted(() => {
-
- - 令牌 - 备注 - 使用次数 - 过期时间 - +
+ + 令牌 + 备注 + 使用次数 + +
-
+
暂无令牌
-
- +
+ - {{ code.code }} - {{ code.remark || '-' }} - + {{ code.code }} + {{ code.remark || '-' }} + {{ code.used_count }}/{{ code.max_uses === 0 ? '∞' : code.max_uses }} - + - + @@ -325,20 +328,91 @@ onUnmounted(() => { + + + + + Agent 详情 + +
+
+
+ +
#{{ viewingAgent.id }}
+
+
+ +
{{ viewingAgent.name }}
+
+
+ +
{{ viewingAgent.ip || '-' }}
+
+
+ +
{{ viewingAgent.hostname || '-' }}
+
+
+ +
{{ viewingAgent.os || '-' }}
+
+
+ +
{{ viewingAgent.arch || '-' }}
+
+
+ +
{{ viewingAgent.version || '-' }}
+
+
+ +
+ + + + + {{ isOnline(viewingAgent) ? '在线' : '离线' }} +
+
+
+ +
{{ viewingAgent.enabled ? '已启用' : '已禁用' }}
+
+
+ +
{{ viewingAgent.last_seen || '-' }}
+
+
+ +
{{ viewingAgent.created_at || '-' }}
+
+
+ +
{{ viewingAgent.updated_at || '-' }}
+
+
+
+ +
{{ viewingAgent.description }}
+
+
+
+
+ - + 编辑 Agent -
-
- - +
+
+ +
-
- - +
+ +
@@ -348,12 +422,14 @@ onUnmounted(() => {
- + 确认删除 - 确定要删除 "{{ deletingAgent?.name }}" 吗?此操作不可恢复。 + + 确定要删除 Agent "{{ deletingAgent?.name }}" 吗?此操作无法撤销。 + 取消 @@ -364,56 +440,40 @@ onUnmounted(() => { - + 下载 Agent - 当前版本: {{ agentVersion }} + 当前版本: {{ agentVersion }} -
-
- 暂无可用的 Agent 程序 -
-
-
-
-

使用说明

-
-

1. 下载对应平台的 Agent 压缩包并解压

-

2. 修改 config.example.ini 为 config.ini,设置 server_url

-

3. 在"令牌"标签页生成令牌,填入 config.ini 的 token

-

4. 运行 ./baihu-agent start 启动

-

5. 可选: ./baihu-agent install 设置开机自启

-
-
- - -
- + - 生成令牌 - Agent 使用令牌可直接注册,无需审核 + 生成注册令牌 -
-
- - +
+
+ +
-
- - +
+ +
-
- - +
+ +