Files
qiming/qiming-rcoder/specs/docker-pod-info/0001-docker-pod-info.md
2026-06-01 13:54:52 +08:00

960 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Docker Pod 信息接口设计文档
> **文档版本**: v1.0.1
> **创建日期**: 2024-12-16
> **更新日期**: 2024-12-16
> **状态**: 草案
> **相关模块**: `crates/rcoder/src/router.rs`, `crates/docker_manager`, `crates/shared_types`
---
## 1. 需求概述
### 1.1 背景
RCoder 系统采用动态容器管理模式,可以根据 `user_id``project_id` 创建和管理 Docker 容器(特别是 `ComputerAgentRunner` 类型的容器)。为了便于系统监控和前端用户使用 noVNC 远程虚拟桌面,需要新增三个接口:
1. **获取当前容器数量接口** - 用于监控系统中已创建的容器总数
2. **启动容器接口** - 根据 `user_id``project_id` 启动(或获取已存在的)容器,**仅启动容器本身,不启动 Agent 服务**,以便用户可以通过 noVNC 访问远程虚拟桌面
3. **容器保活接口** - 刷新容器的最后活动时间,防止容器被定时清理任务销毁
> [!IMPORTANT]
> 所有接口响应统一使用 `shared_types::HttpResult<T>` 结构进行包装,保持与系统其他 API 的一致性。
### 1.2 目标
| 目标 | 描述 |
|------|------|
| **监控能力** | 提供容器数量统计,便于系统运维监控 |
| **用户体验** | 简化容器启动流程,自动化处理容器存在性检查 |
| **简洁响应** | 响应只包含容器基本信息VNC 访问通过独立接口获取 |
| **轻量启动** | 仅启动容器,不启动 Agent 服务,减少资源占用 |
| **容器保活** | 支持刷新容器活动时间,防止被自动清理 |
---
## 2. HttpResult 响应包装
### 2.1 HttpResult 结构 (来自 `shared_types`)
所有接口响应均使用 `HttpResult<T>` 包装:
```rust
// 来自 crates/shared_types/src/model/http_result.rs
#[derive(Debug, Deserialize, ToSchema)]
pub struct HttpResult<T> {
/// 响应码: "0000" 表示成功
pub code: String,
/// 响应消息
pub message: String,
/// 响应数据 (成功时有值)
pub data: Option<T>,
/// OpenTelemetry Trace ID (用于链路追踪)
pub tid: Option<String>,
/// 是否成功 (code == "0000")
#[serde(skip)]
pub success: bool,
}
impl<T> HttpResult<T> {
/// 创建成功响应
pub fn success(data: T) -> Self;
/// 创建错误响应
pub fn error(code: &str, message: &str) -> Self;
/// 创建内部错误响应 (code: "5000")
pub fn internal_error(message: &str) -> Self;
}
```
### 2.2 响应格式示例
**成功响应:**
```json
{
"code": "0000",
"message": "成功",
"data": { ... },
"tid": "abc123def456...",
"success": true
}
```
**错误响应:**
```json
{
"code": "5000",
"message": "获取 DockerManager 失败",
"data": null,
"tid": "abc123def456...",
"success": false
}
```
---
## 3. 接口设计
### 3.1 接口一:获取当前容器数量
#### 3.1.1 接口规格
| 属性 | 值 |
|------|-----|
| **路径** | `GET /computer/pod/count` |
| **方法** | GET |
| **入参** | 无 |
| **响应类型** | `HttpResult<PodCountResponse>` |
| **标签** | `pod` (Pod 容器管理接口) |
#### 3.1.2 请求示例
```bash
curl -X GET http://localhost:8087/computer/pod/count
```
#### 3.1.3 响应数据结构 (data 字段)
```rust
/// 容器数量响应结构
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct PodCountResponse {
/// 当前运行的容器总数
#[schema(example = 5)]
pub total_count: u32,
/// 按服务类型分类的容器数量
pub by_service_type: PodCountByServiceType,
/// 统计时间戳 (Unix 毫秒)
#[schema(example = 1702700000000)]
pub timestamp: u64,
}
/// 按服务类型分类的容器数量
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct PodCountByServiceType {
/// RCoder 类型容器数量
#[schema(example = 2)]
pub rcoder: u32,
/// ComputerAgentRunner 类型容器数量
#[schema(example = 3)]
pub computer_agent_runner: u32,
}
```
#### 3.1.4 完整响应示例
```json
{
"code": "0000",
"message": "成功",
"data": {
"total_count": 5,
"by_service_type": {
"rcoder": 2,
"computer_agent_runner": 3
},
"timestamp": 1702700000000
},
"tid": "4bf92f3577b34da6a3ce929d0e0e4736",
"success": true
}
```
---
### 3.2 接口二:启动容器 (Ensure Container)
#### 3.2.1 接口规格
| 属性 | 值 |
|------|-----|
| **路径** | `POST /computer/pod/ensure` |
| **方法** | POST |
| **入参** | `user_id` (必填), `project_id` (必填) |
| **响应类型** | `HttpResult<EnsurePodResponse>` |
| **标签** | `pod` (Pod 容器管理接口) |
> [!NOTE]
> 此接口采用 **幂等设计**:如果对应的容器已经存在且处于运行状态,则直接返回现有容器信息,不会创建新容器。
> [!IMPORTANT]
> **此接口仅启动容器,不启动 Agent 服务。** 容器启动后,用户可直接通过 noVNC 访问虚拟桌面,无需等待 Agent 服务初始化。如需使用 AI Agent 功能,请调用 `/computer/chat` 接口。
#### 3.2.2 请求结构
```rust
/// 启动容器请求结构
#[derive(Debug, Clone, Deserialize, ToSchema)]
pub struct EnsurePodRequest {
/// 用户唯一标识符
#[schema(example = "user_123")]
pub user_id: String,
/// 项目唯一标识符
#[schema(example = "proj_456")]
pub project_id: String,
/// 可选的资源限制配置
#[serde(skip_serializing_if = "Option::is_none")]
pub resource_limits: Option<PodResourceLimits>,
}
/// Pod 资源限制配置
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct PodResourceLimits {
/// 内存限制 (bytes), 例如 4GB = 4294967296
#[schema(example = 4294967296)]
pub memory: Option<u64>,
/// CPU 份额 (1024 = 1 核)
#[schema(example = 2048)]
pub cpu_shares: Option<u64>,
}
```
#### 3.2.3 请求示例
```bash
curl -X POST http://localhost:8087/computer/pod/ensure \
-H "Content-Type: application/json" \
-d '{
"user_id": "user_123",
"project_id": "proj_456"
}'
```
#### 3.2.4 响应数据结构 (data 字段)
```rust
/// 启动容器响应结构
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct EnsurePodResponse {
/// 容器是否为新创建 (false 表示已存在)
pub created: bool,
/// 容器基本信息
pub container_info: PodContainerInfo,
/// 提示消息
#[schema(example = "容器已就绪")]
pub message: String,
}
/// 容器基本信息
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct PodContainerInfo {
/// 容器 ID
#[schema(example = "abc123def456")]
pub container_id: String,
/// 容器名称
#[schema(example = "computer-agent-runner-user_123")]
pub container_name: String,
/// 容器 IP 地址 (内部网络)
#[schema(example = "172.17.0.5")]
pub container_ip: String,
/// 服务 URL
#[schema(example = "http://172.17.0.5:8086")]
pub service_url: String,
/// 容器状态
#[schema(example = "running")]
pub status: String,
}
```
#### 3.2.5 完整响应示例
**成功响应(新创建容器):**
```json
{
"code": "0000",
"message": "成功",
"data": {
"created": true,
"container_info": {
"container_id": "abc123def456...",
"container_name": "computer-agent-runner-user_123",
"container_ip": "172.17.0.5",
"service_url": "http://172.17.0.5:8086",
"status": "running"
},
"message": "容器创建成功Agent 服务未启动)"
},
"tid": "4bf92f3577b34da6a3ce929d0e0e4736",
"success": true
}
```
**成功响应(容器已存在):**
```json
{
"code": "0000",
"message": "成功",
"data": {
"created": false,
"container_info": {
"container_id": "abc123def456...",
"container_name": "computer-agent-runner-user_123",
"container_ip": "172.17.0.5",
"service_url": "http://172.17.0.5:8086",
"status": "running"
},
"message": "容器已存在"
},
"tid": "4bf92f3577b34da6a3ce929d0e0e4736",
"success": true
}
```
**错误响应:**
```json
{
"code": "5000",
"message": "启动容器失败: Docker 连接超时",
"data": null,
"tid": "4bf92f3577b34da6a3ce929d0e0e4736",
"success": false
}
```
---
### 3.3 接口三:容器保活 (Keepalive)
#### 3.3.1 接口规格
| 属性 | 值 |
|------|-----|
| **路径** | `POST /computer/pod/keepalive` |
| **方法** | POST |
| **入参** | `user_id` (必填), `project_id` (必填) |
| **响应类型** | `HttpResult<KeepalivePodResponse>` |
| **标签** | `pod` (Pod 容器管理接口) |
> [!NOTE]
> 此接口用于防止容器被定时清理任务销毁。系统会定期(默认每 5 分钟)检查容器的最后活动时间,闲置超过 30 分钟的容器将被自动清理。
> [!TIP]
> **使用场景**:前端在用户使用 noVNC 远程桌面时,应定期调用此接口(建议每 5-10 分钟一次)来保持容器活跃状态。
#### 3.3.2 请求结构
```rust
/// 容器保活请求结构
#[derive(Debug, Clone, Deserialize, ToSchema)]
pub struct KeepalivePodRequest {
/// 用户唯一标识符
#[schema(example = "user_123")]
pub user_id: String,
/// 项目唯一标识符
#[schema(example = "proj_456")]
pub project_id: String,
}
```
#### 3.3.3 请求示例
```bash
curl -X POST http://localhost:8087/computer/pod/keepalive \
-H "Content-Type: application/json" \
-d '{
"user_id": "user_123",
"project_id": "proj_456"
}'
```
#### 3.3.4 响应数据结构 (data 字段)
```rust
/// 容器保活响应结构
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct KeepalivePodResponse {
/// 容器是否存在 (true=已存在, false=新创建)
pub existed: bool,
/// 容器是否为新创建 (当 existed=false 时为 true)
pub created: bool,
/// 容器基本信息
pub container_info: PodContainerInfo,
/// 上次活动时间 (Unix 毫秒时间戳, 更新前)
#[schema(example = 1702700000000)]
pub previous_activity_time: u64,
/// 当前活动时间 (Unix 毫秒时间戳, 更新后)
#[schema(example = 1702700600000)]
pub current_activity_time: u64,
/// 距离下次清理的剩余时间 (秒)
/// 基于默认 30 分钟闲置超时计算
#[schema(example = 1800)]
pub time_until_cleanup: u64,
/// 提示消息
#[schema(example = "容器活动时间已刷新")]
pub message: String,
}
```
#### 3.3.5 完整响应示例
**成功响应(容器已存在,刷新活动时间):**
```json
{
"code": "0000",
"message": "成功",
"data": {
"existed": true,
"created": false,
"container_info": {
"container_id": "abc123def456...",
"container_name": "computer-agent-runner-user_123",
"container_ip": "172.17.0.5",
"service_url": "http://172.17.0.5:8086",
"status": "running"
},
"previous_activity_time": 1702700000000,
"current_activity_time": 1702700600000,
"time_until_cleanup": 1800,
"message": "容器活动时间已刷新,距离自动清理还有 30 分钟"
},
"tid": "4bf92f3577b34da6a3ce929d0e0e4736",
"success": true
}
```
**成功响应(容器不存在,自动创建):**
```json
{
"code": "0000",
"message": "成功",
"data": {
"existed": false,
"created": true,
"container_info": {
"container_id": "xyz789...",
"container_name": "computer-agent-runner-user_123",
"container_ip": "172.17.0.6",
"service_url": "http://172.17.0.6:8086",
"status": "running"
},
"previous_activity_time": 0,
"current_activity_time": 1702700600000,
"time_until_cleanup": 1800,
"message": "容器已自动创建,距离自动清理还有 30 分钟"
},
"tid": "4bf92f3577b34da6a3ce929d0e0e4736",
"success": true
}
```
**错误响应:**
```json
{
"code": "5000",
"message": "刷新活动时间失败: 容器状态异常",
"data": null,
"tid": "4bf92f3577b34da6a3ce929d0e0e4736",
"success": false
}
```
---
## 4. 架构设计
### 4.1 整体架构
```
┌─────────────────────────────────────────────────────────────────┐
│ 外部客户端 │
└────────────────────────────┬────────────────────────────────────┘
│ HTTP
┌─────────────────────────────────────────────────────────────────┐
│ rcoder (主服务) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ router.rs │ │
│ │ /computer/pod/count ─────▶ pod_count_handler │ │
│ │ /computer/pod/ensure ─────▶ pod_ensure_handler │ │
│ │ /computer/pod/keepalive─────▶ pod_keepalive_handler │ │
│ └───────────────────────────┬─────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────▼─────────────────────────────┐ │
│ │ service/pod_service.rs │ │
│ │ ┌─────────────────┐ ┌──────────────────────────────┐ │ │
│ │ │ PodService │ │ PodServiceTrait (trait) │ │ │
│ │ │ - get_count() │ │ - get_pod_count() │ │ │
│ │ │ - ensure_pod() │ │ - ensure_pod() │ │ │
│ │ └─────────────────┘ └──────────────────────────────┘ │ │
│ └───────────────────────────┬─────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────▼─────────────────────────────┐ │
│ │ docker_manager (crate) │ │
│ │ DockerManager │ │
│ │ - list_containers() │ │
│ │ - get_agent_info() │ │
│ │ - start_agent_container() │ │
│ └───────────────────────────┬─────────────────────────────┘ │
└──────────────────────────────┼──────────────────────────────────┘
│ Docker API
┌─────────────────────────────────────────────────────────────────┐
│ Docker Daemon │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ container-1 │ │ container-2 │ │ container-3 │ │
│ │ (user_123) │ │ (user_456) │ │ (project_xyz) │ │
│ └────────────────┘ └────────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### 4.2 模块职责
| 模块 | 职责 |
|------|------|
| `handler/pod_handler.rs` | 处理 HTTP 请求,参数解析与验证 |
| `service/pod_service.rs` | 核心业务逻辑,调用 DockerManager |
| `docker_manager` | Docker API 交互,容器生命周期管理 |
---
## 5. Trait 与结构体定义
### 5.1 Service Trait
```rust
// crates/rcoder/src/service/pod_service.rs
use async_trait::async_trait;
use crate::AppError;
/// Pod 服务 Trait
///
/// 定义 Pod 容器管理的核心能力接口
#[async_trait]
pub trait PodServiceTrait: Send + Sync {
/// 获取当前容器数量统计
///
/// # 返回
/// 容器数量统计信息
async fn get_pod_count(&self) -> Result<PodCountResponse, AppError>;
/// 确保容器存在(幂等操作)
///
/// 如果容器已存在则返回现有容器信息,否则创建新容器
/// 仅启动容器,不启动 Agent 服务
///
/// # 参数
/// - `request`: 启动容器请求
///
/// # 返回
/// 容器信息和 VNC 访问信息
async fn ensure_pod(&self, request: EnsurePodRequest) -> Result<EnsurePodResponse, AppError>;
/// 容器保活(刷新活动时间)
///
/// 如果容器已存在则刷新其最后活动时间,防止被清理任务销毁
/// 如果容器不存在则自动创建
///
/// # 参数
/// - `request`: 保活请求(包含 user_id 和 project_id
///
/// # 返回
/// 保活结果,包含活动时间信息和距离清理的剩余时间
async fn keepalive_pod(&self, request: KeepalivePodRequest) -> Result<KeepalivePodResponse, AppError>;
}
```
### 5.2 请求/响应结构体
```rust
// crates/rcoder/src/handler/pod_handler.rs
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
// ============================================================================
// 接口一:获取容器数量
// ============================================================================
/// 容器数量响应
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct PodCountResponse {
/// 当前运行的容器总数
pub total_count: u32,
/// 按服务类型分类的容器数量
pub by_service_type: PodCountByServiceType,
/// 统计时间戳 (Unix 毫秒)
pub timestamp: u64,
}
/// 按服务类型分类的容器数量
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct PodCountByServiceType {
/// RCoder 类型容器数量
pub rcoder: u32,
/// ComputerAgentRunner 类型容器数量
pub computer_agent_runner: u32,
}
// ============================================================================
// 接口二:启动容器
// ============================================================================
/// 启动容器请求
#[derive(Debug, Clone, Deserialize, ToSchema)]
pub struct EnsurePodRequest {
/// 用户唯一标识符 (必填)
#[schema(example = "user_123")]
pub user_id: String,
/// 项目唯一标识符 (必填)
#[schema(example = "proj_456")]
pub project_id: String,
/// 可选的资源限制配置
#[serde(skip_serializing_if = "Option::is_none")]
pub resource_limits: Option<PodResourceLimits>,
}
/// Pod 资源限制配置
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct PodResourceLimits {
/// 内存限制 (bytes)
pub memory: Option<u64>,
/// CPU 份额
pub cpu_shares: Option<u64>,
}
/// 启动容器响应
///
/// 注意: 此结构体作为 HttpResult<EnsurePodResponse> 的 data 字段返回
/// 成功状态由外层 HttpResult 的 code 字段表示
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct EnsurePodResponse {
/// 容器是否为新创建 (false 表示已存在)
pub created: bool,
/// 容器基本信息
pub container_info: PodContainerInfo,
/// 提示消息
pub message: String,
}
/// 容器基本信息
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct PodContainerInfo {
/// 容器 ID
pub container_id: String,
/// 容器名称
pub container_name: String,
/// 容器 IP 地址
pub container_ip: String,
/// 服务 URL
pub service_url: String,
/// 容器状态
pub status: String,
}
// ============================================================================
// 接口三:容器保活
// ============================================================================
/// 容器保活请求
#[derive(Debug, Clone, Deserialize, ToSchema)]
pub struct KeepalivePodRequest {
/// 用户唯一标识符
#[schema(example = "user_123")]
pub user_id: String,
/// 项目唯一标识符
#[schema(example = "proj_456")]
pub project_id: String,
}
/// 容器保活响应
///
/// 注意: 此结构体作为 HttpResult<KeepalivePodResponse> 的 data 字段返回
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct KeepalivePodResponse {
/// 容器是否已存在
pub existed: bool,
/// 容器是否为新创建 (当 existed=false 时为 true)
pub created: bool,
/// 容器基本信息
pub container_info: PodContainerInfo,
/// 上次活动时间 (Unix 毫秒时间戳, 更新前)
pub previous_activity_time: u64,
/// 当前活动时间 (Unix 毫秒时间戳, 更新后)
pub current_activity_time: u64,
/// 距离下次清理的剩余时间 (秒)
pub time_until_cleanup: u64,
/// 提示消息
pub message: String,
}
```
### 5.3 Service 实现结构
```rust
// crates/rcoder/src/service/pod_service.rs
use std::sync::Arc;
/// Pod 服务实现
///
/// 封装 Docker 容器管理的业务逻辑
pub struct PodService {
/// Docker 管理器
docker_manager: Arc<docker_manager::DockerManager>,
}
impl PodService {
/// 创建 PodService 实例
pub fn new(docker_manager: Arc<docker_manager::DockerManager>) -> Self {
Self { docker_manager }
}
}
#[async_trait]
impl PodServiceTrait for PodService {
async fn get_pod_count(&self) -> Result<PodCountResponse, AppError> {
// 具体实现...
todo!()
}
async fn ensure_pod(&self, request: EnsurePodRequest) -> Result<EnsurePodResponse, AppError> {
// 具体实现...
todo!()
}
async fn keepalive_pod(&self, request: KeepalivePodRequest) -> Result<KeepalivePodResponse, AppError> {
// 具体实现...
// 1. 查询容器是否存在
// 2. 不存在则创建容器
// 3. 存在则刷新 last_activity 时间
// 4. 计算距离清理的剩余时间
todo!()
}
}
```
---
## 6. 路由配置
### 6.1 新增路由
`crates/rcoder/src/router.rs` 中添加以下路由:
```rust
// 在 create_router 函数中添加
// Pod 容器管理路由 (统一使用 /computer 前缀)
.route("/computer/pod/count", get(handler::pod_count))
.route("/computer/pod/ensure", post(handler::pod_ensure))
.route("/computer/pod/keepalive", post(handler::pod_keepalive))
```
### 6.2 OpenAPI 配置更新
```rust
// 在 ApiDoc 结构体中添加
#[openapi(
paths(
// ... 现有路径
handler::pod_count,
handler::pod_ensure,
handler::pod_keepalive,
),
components(
schemas(
// ... 现有 schemas
handler::PodCountResponse,
handler::PodCountByServiceType,
handler::EnsurePodRequest,
handler::PodResourceLimits,
handler::EnsurePodResponse,
handler::PodContainerInfo,
handler::KeepalivePodRequest,
handler::KeepalivePodResponse,
)
),
tags(
// ... 现有 tags
(name = "pod", description = "Pod 容器管理接口,支持容器监控和启动"),
),
)]
```
---
## 7. 错误处理
### 7.1 错误类型
| 错误码 | HTTP 状态码 | 描述 |
|--------|-------------|------|
| `DOCKER_MANAGER_ERROR` | 500 | Docker Manager 获取失败 |
| `CONTAINER_CREATE_FAILED` | 500 | 容器创建失败 |
| `CONTAINER_NOT_FOUND` | 404 | 容器未找到(仅查询场景) |
| `INVALID_REQUEST` | 400 | 请求参数无效 |
### 7.2 错误响应结构HttpResult 统一格式)
错误响应统一使用 `HttpResult<T>` 格式,`data` 字段为 `null`
```json
{
"code": "5000",
"message": "启动容器失败: Docker 连接超时",
"data": null,
"tid": "4bf92f3577b34da6a3ce929d0e0e4736",
"success": false
}
```
---
## 8. 与现有功能的关系
### 8.1 复用现有组件
| 组件 | 复用方式 |
|------|----------|
| `docker_manager::global::get_global_docker_manager()` | 获取全局 Docker 管理器实例 |
| `ComputerContainerManager` | 复用用户容器创建逻辑 |
| `ContainerBasicInfo` | 复用容器信息结构 |
### 8.2 与 noVNC 集成
VNC 访问信息通过独立接口获取:
- **VNC 桌面访问接口**: `GET /computer/desktop/{user_id}/{project_id}`
Pingora 已配置路由规则 `/computer/vnc/{user_id}/{project_id}/{*path}`,会自动代理到对应容器的 6080 端口。
---
## 9. 实现清单
### 9.1 新增文件
| 文件路径 | 描述 |
|----------|------|
| `crates/rcoder/src/handler/pod_handler.rs` | Pod 相关 HTTP 处理器 |
| `crates/rcoder/src/service/pod_service.rs` | Pod 服务业务逻辑 |
### 9.2 修改文件
| 文件路径 | 修改内容 |
|----------|----------|
| `crates/rcoder/src/router.rs` | 添加 `/computer/pod/*` 路由 |
| `crates/rcoder/src/handler/mod.rs` | 导出 `pod_handler` 模块 |
| `crates/rcoder/src/service/mod.rs` | 导出 `pod_service` 模块 |
---
## 10. 验证计划
### 10.1 自动化测试
```bash
# 1. 编译检查
cargo build --workspace
# 2. 单元测试
cargo test --workspace
# 3. Clippy 检查
cargo clippy --workspace --all-targets
```
### 10.2 手动验证
```bash
# 1. 启动服务
make dev-restart
# 2. 测试获取容器数量
curl -X GET http://localhost:8087/computer/pod/count | jq
# 3. 测试启动容器
curl -X POST http://localhost:8087/computer/pod/ensure \
-H "Content-Type: application/json" \
-d '{"user_id": "test_user", "project_id": "test_project"}' | jq
# 4. 验证 VNC 访问
# 使用返回的 proxy_vnc_url 在浏览器中访问
```
### 10.3 OpenAPI 文档验证
启动服务后访问 Swagger UI`http://localhost:8087/swagger-ui/` 确认新接口已正确注册。
---
## 11. 附录
### 11.1 参考文件
- [computer_container_manager.rs](file:///Volumes/soddygo/git_work/rcoder/crates/rcoder/src/service/computer_container_manager.rs) - 用户容器管理逻辑
- [computer_desktop_handler.rs](file:///Volumes/soddygo/git_work/rcoder/crates/rcoder/src/handler/computer_desktop_handler.rs) - VNC 桌面处理器
- [docker_manager/manager.rs](file:///Volumes/soddygo/git_work/rcoder/crates/docker_manager/src/manager.rs) - Docker 容器管理核心
### 11.2 相关接口
| 接口 | 描述 |
|------|------|
| `GET /computer/desktop/{user_id}/{project_id}` | 获取 VNC 桌面访问信息 |
| `POST /computer/chat` | Computer Agent 聊天接口 |
| `/computer/vnc/{user_id}/{project_id}/*` | Pingora VNC 代理路由 |
---
## 12. 变更记录
| 日期 | 版本 | 变更内容 | 作者 |
|------|------|----------|------|
| 2024-12-16 | v1.0.0 | 初始版本 | AI Assistant |
| 2024-12-16 | v1.0.1 | 使用 HttpResult 包装响应;明确不启动 Agent 服务 | AI Assistant |
| 2024-12-16 | v1.1.0 | 新增容器保活接口 (keepalive),支持刷新容器活动时间防止被自动清理 | AI Assistant |