Prechádzať zdrojové kódy

refine control docker

welin 3 mesiacov pred
rodič
commit
64b8e58f24

+ 1 - 1
.env

@@ -1,4 +1,4 @@
-ENV=PROD
+ENV=DEV
 DATABASE_URL=mysql+asyncmy://root:GqLLL7Bofj0WaaOpp.0@visafly.top:3306/book_user_info?charset=utf8mb4
 DATABASE_URL=mysql+asyncmy://root:GqLLL7Bofj0WaaOpp.0@visafly.top:3306/book_user_info?charset=utf8mb4
 REDIS_URL=redis://:STEs2x6ML0U1HlpE9SojM6YU7QPhqzY8@45.137.220.138:6379/0
 REDIS_URL=redis://:STEs2x6ML0U1HlpE9SojM6YU7QPhqzY8@45.137.220.138:6379/0
 OPENAI_API_KEY=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
 OPENAI_API_KEY=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

+ 11 - 11
app/api/router.py

@@ -111,43 +111,43 @@ async def list_remote_servers(db: AsyncSession = Depends(get_db)):
 
 
 @admin_required_router.post("/remote/server/docker/status", summary="获取预配置服务器容器状态", tags=["Docker远程控制"], response_model=ApiResponse[DockerStatusOut])
 @admin_required_router.post("/remote/server/docker/status", summary="获取预配置服务器容器状态", tags=["Docker远程控制"], response_model=ApiResponse[DockerStatusOut])
 async def server_docker_status(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
 async def server_docker_status(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
-    config = await DockerRemoteService.get_server_config(db, request.server_id)
+    config = await DockerRemoteService.get_server_config(db, request.server_id, request.project_path)
     res = await DockerRemoteService.get_container_status(config)
     res = await DockerRemoteService.get_container_status(config)
     return success(data=DockerStatusOut(containers=res))
     return success(data=DockerStatusOut(containers=res))
 
 
 @admin_required_router.post("/remote/server/docker/up", summary="启动预配置服务器Docker Compose", tags=["Docker远程控制"], response_model=ApiResponse[bool])
 @admin_required_router.post("/remote/server/docker/up", summary="启动预配置服务器Docker Compose", tags=["Docker远程控制"], response_model=ApiResponse[bool])
 async def server_docker_up(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
 async def server_docker_up(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
-    config = await DockerRemoteService.get_server_config(db, request.server_id)
+    config = await DockerRemoteService.get_server_config(db, request.server_id, request.project_path)
     res = await DockerRemoteService.docker_compose_up(config, request.services)
     res = await DockerRemoteService.docker_compose_up(config, request.services)
     return success(data=res)
     return success(data=res)
 
 
 @admin_required_router.post("/remote/server/docker/down", summary="停止预配置服务器Docker Compose", tags=["Docker远程控制"], response_model=ApiResponse[bool])
 @admin_required_router.post("/remote/server/docker/down", summary="停止预配置服务器Docker Compose", tags=["Docker远程控制"], response_model=ApiResponse[bool])
 async def server_docker_down(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
 async def server_docker_down(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
-    config = await DockerRemoteService.get_server_config(db, request.server_id)
+    config = await DockerRemoteService.get_server_config(db, request.server_id, request.project_path)
     res = await DockerRemoteService.docker_compose_down(config, request.services)
     res = await DockerRemoteService.docker_compose_down(config, request.services)
     return success(data=res)
     return success(data=res)
 
 
 @admin_required_router.post("/remote/server/docker/restart", summary="重启预配置服务器容器", tags=["Docker远程控制"], response_model=ApiResponse[bool])
 @admin_required_router.post("/remote/server/docker/restart", summary="重启预配置服务器容器", tags=["Docker远程控制"], response_model=ApiResponse[bool])
 async def server_docker_restart(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
 async def server_docker_restart(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
-    config = await DockerRemoteService.get_server_config(db, request.server_id)
+    config = await DockerRemoteService.get_server_config(db, request.server_id, request.project_path)
     res = await DockerRemoteService.docker_restart(config, request.container_name)
     res = await DockerRemoteService.docker_restart(config, request.container_name)
     return success(data=res)
     return success(data=res)
 
 
 @admin_required_router.post("/remote/server/docker/start", summary="启动预配置服务器容器", tags=["Docker远程控制"], response_model=ApiResponse[bool])
 @admin_required_router.post("/remote/server/docker/start", summary="启动预配置服务器容器", tags=["Docker远程控制"], response_model=ApiResponse[bool])
 async def server_docker_start(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
 async def server_docker_start(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
-    config = await DockerRemoteService.get_server_config(db, request.server_id)
+    config = await DockerRemoteService.get_server_config(db, request.server_id, request.project_path)
     res = await DockerRemoteService.docker_start(config, request.container_name)
     res = await DockerRemoteService.docker_start(config, request.container_name)
     return success(data=res)
     return success(data=res)
 
 
 @admin_required_router.post("/remote/server/docker/stop", summary="停止预配置服务器容器", tags=["Docker远程控制"], response_model=ApiResponse[bool])
 @admin_required_router.post("/remote/server/docker/stop", summary="停止预配置服务器容器", tags=["Docker远程控制"], response_model=ApiResponse[bool])
 async def server_docker_stop(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
 async def server_docker_stop(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
-    config = await DockerRemoteService.get_server_config(db, request.server_id)
+    config = await DockerRemoteService.get_server_config(db, request.server_id, request.project_path)
     res = await DockerRemoteService.docker_stop(config, request.container_name)
     res = await DockerRemoteService.docker_stop(config, request.container_name)
     return success(data=res)
     return success(data=res)
 
 
 @admin_required_router.post("/remote/server/docker/logs", summary="查看预配置服务器容器日志", tags=["Docker远程控制"], response_model=ApiResponse[DockerLogsOut])
 @admin_required_router.post("/remote/server/docker/logs", summary="查看预配置服务器容器日志", tags=["Docker远程控制"], response_model=ApiResponse[DockerLogsOut])
 async def server_docker_logs(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
 async def server_docker_logs(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
-    config = await DockerRemoteService.get_server_config(db, request.server_id)
+    config = await DockerRemoteService.get_server_config(db, request.server_id, request.project_path)
     # 构造 DockerLogsRequest
     # 构造 DockerLogsRequest
     log_req = DockerLogsRequest(
     log_req = DockerLogsRequest(
         **config.model_dump(),
         **config.model_dump(),
@@ -160,13 +160,13 @@ async def server_docker_logs(request: RemoteActionRequest, db: AsyncSession = De
 
 
 @admin_required_router.post("/remote/server/config/read", summary="读取预配置服务器配置文件", tags=["Docker远程控制"], response_model=ApiResponse[Dict[str, Any]])
 @admin_required_router.post("/remote/server/config/read", summary="读取预配置服务器配置文件", tags=["Docker远程控制"], response_model=ApiResponse[Dict[str, Any]])
 async def server_docker_config_read(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
 async def server_docker_config_read(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
-    config = await DockerRemoteService.get_server_config(db, request.server_id)
+    config = await DockerRemoteService.get_server_config(db, request.server_id, request.project_path)
     res = await DockerRemoteService.read_config(config, request.config_file)
     res = await DockerRemoteService.read_config(config, request.config_file)
     return success(data={"config": res})
     return success(data={"config": res})
 
 
 @admin_required_router.post("/remote/server/config/update", summary="更新预配置服务器配置文件", tags=["Docker远程控制"], response_model=ApiResponse[bool])
 @admin_required_router.post("/remote/server/config/update", summary="更新预配置服务器配置文件", tags=["Docker远程控制"], response_model=ApiResponse[bool])
 async def server_docker_config_update(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
 async def server_docker_config_update(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
-    config = await DockerRemoteService.get_server_config(db, request.server_id)
+    config = await DockerRemoteService.get_server_config(db, request.server_id, request.project_path)
     # 构造 ConfigUpdateRequest
     # 构造 ConfigUpdateRequest
     update_req = ConfigUpdateRequest(
     update_req = ConfigUpdateRequest(
         **config.model_dump(),
         **config.model_dump(),
@@ -179,13 +179,13 @@ async def server_docker_config_update(request: RemoteActionRequest, db: AsyncSes
 
 
 @admin_required_router.post("/remote/server/log/list", summary="列出预配置服务器日志文件", tags=["Docker远程控制"], response_model=ApiResponse[LogListOut])
 @admin_required_router.post("/remote/server/log/list", summary="列出预配置服务器日志文件", tags=["Docker远程控制"], response_model=ApiResponse[LogListOut])
 async def server_docker_logs_list(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
 async def server_docker_logs_list(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
-    config = await DockerRemoteService.get_server_config(db, request.server_id)
+    config = await DockerRemoteService.get_server_config(db, request.server_id, request.project_path)
     res = await DockerRemoteService.list_logs(config)
     res = await DockerRemoteService.list_logs(config)
     return success(data=LogListOut(log_files=res))
     return success(data=LogListOut(log_files=res))
 
 
 @admin_required_router.post("/remote/server/log/read", summary="读取预配置服务器日志文件内容", tags=["Docker远程控制"], response_model=ApiResponse[LogReadOut])
 @admin_required_router.post("/remote/server/log/read", summary="读取预配置服务器日志文件内容", tags=["Docker远程控制"], response_model=ApiResponse[LogReadOut])
 async def server_docker_logs_read(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
 async def server_docker_logs_read(request: RemoteActionRequest, db: AsyncSession = Depends(get_db)):
-    config = await DockerRemoteService.get_server_config(db, request.server_id)
+    config = await DockerRemoteService.get_server_config(db, request.server_id, request.project_path)
     # 构造 LogReadRequest
     # 构造 LogReadRequest
     read_req = LogReadRequest(
     read_req = LogReadRequest(
         **config.model_dump(),
         **config.model_dump(),

+ 1 - 0
app/schemas/docker_remote.py

@@ -92,6 +92,7 @@ class ServerListOut(BaseModel):
 class RemoteActionRequest(BaseModel):
 class RemoteActionRequest(BaseModel):
     """基于服务器ID的远程操作请求"""
     """基于服务器ID的远程操作请求"""
     server_id: str
     server_id: str
+    project_path: Optional[str] = Field(None, description="项目路径(如果不传则使用数据库默认路径)")
     container_name: Optional[str] = None
     container_name: Optional[str] = None
     services: Optional[List[str]] = None
     services: Optional[List[str]] = None
     config_file: Optional[str] = None
     config_file: Optional[str] = None

+ 2 - 2
app/services/docker_remote_service.py

@@ -21,7 +21,7 @@ class DockerRemoteService:
     """Docker远程控制服务"""
     """Docker远程控制服务"""
     
     
     @staticmethod
     @staticmethod
-    async def get_server_config(db: AsyncSession, server_id: str) -> RemoteServerConfig:
+    async def get_server_config(db: AsyncSession, server_id: str, override_path: Optional[str] = None) -> RemoteServerConfig:
         """从数据库中获取服务器连接信息"""
         """从数据库中获取服务器连接信息"""
         result = await db.execute(select(RemoteServer).filter(RemoteServer.server_id == server_id))
         result = await db.execute(select(RemoteServer).filter(RemoteServer.server_id == server_id))
         server = result.scalar_one_or_none()
         server = result.scalar_one_or_none()
@@ -34,7 +34,7 @@ class DockerRemoteService:
             username=server.username,
             username=server.username,
             password=server.password,
             password=server.password,
             key_file=server.key_file,
             key_file=server.key_file,
-            project_path=server.project_path
+            project_path=override_path or server.project_path
         )
         )
     
     
     @staticmethod
     @staticmethod

+ 19 - 3
app/utils/docker_remote_control.py

@@ -188,9 +188,25 @@ class DockerRemoteController:
             return False
             return False
     
     
     def docker_status(self) -> Dict[str, Any]:
     def docker_status(self) -> Dict[str, Any]:
-        """查看所有容器状态"""
+        """查看指定项目路径下的容器状态"""
+        # 1. 获取该项目下的所有容器 ID
+        # 兼容 docker-compose v1 和 v2 (docker compose)
+        id_cmd = f"cd {self.project_path} && (docker-compose ps -aq 2>/dev/null || docker compose ps -aq)"
+        stdout_ids, stderr_ids, exit_code_ids = self.execute_command(id_cmd)
+        
+        container_ids = [cid.strip() for cid in stdout_ids.strip().split('\n') if cid.strip()]
+        
+        if not container_ids or exit_code_ids != 0:
+            # 如果没找到容器或出错,返回空(或者可以保留原有逻辑作为回退,但用户要求只看对应的)
+            if exit_code_ids != 0 and stderr_ids:
+                print(f"✗ 获取项目容器列表失败: {stderr_ids}")
+            return {}
+
+        # 2. 根据 ID 获取这些容器的详细状态
+        # 为了防止 ID 列表过长导致命令超长,我们可以分批或者使用过滤
+        filters = " ".join([f"--filter id={cid}" for cid in container_ids])
         stdout, stderr, exit_code = self.execute_command(
         stdout, stderr, exit_code = self.execute_command(
-            "docker ps -a --format '{{.Names}}\t{{.Status}}\t{{.Image}}'"
+            f"docker ps -a {filters} --format '{{{{.Names}}}}\t{{{{.Status}}}}\t{{{{.Image}}}}'"
         )
         )
         
         
         if exit_code == 0:
         if exit_code == 0:
@@ -208,7 +224,7 @@ class DockerRemoteController:
                         }
                         }
             return containers
             return containers
         else:
         else:
-            print(f"✗ 获取容器状态失败: {stderr}")
+            print(f"✗ 获取容器详细状态失败: {stderr}")
             return {}
             return {}
     
     
     def docker_logs(self, container_name: str, lines: int = 100, follow: bool = False) -> str:
     def docker_logs(self, container_name: str, lines: int = 100, follow: bool = False) -> str: