KWDB Playground 新增代码执行功能,支持在课程中编写和运行 Python 代码。
代码执行功能允许用户在课程页面中:
- 阅读包含 Python 代码块的课程内容
- 点击「Run」按钮直接执行代码
- 查看代码执行结果(输出、错误、退出码)
在课程列表中选择标记为「Python」的课程,点击进入学习页面。
点击「启动容器」按钮,等待容器状态变为「运行中」。
页面中的 Python 代码块会显示「Run」按钮,点击即可执行代码,结果将显示在右侧的代码终端面板中。
注意:代码编辑器在容器启动前即可查看,但「运行」按钮需要容器处于「运行中」状态才能使用。
在课程 YAML 文件中添加 codeTerminal: true 字段:
title: Python 入门
description: 学习 Python 基础语法
details:
intro:
text: intro.md
steps:
- title: Hello World
text: step1.md
finish:
text: finish.md
backend:
imageid: python:3.11-slim
codeTerminal: true # 启用代码执行功能
estimatedMinutes: 10
difficulty: beginner
tags:
- Python在 Markdown 中使用 ```python 标记代码块:
```python
print("Hello, World!")
name = "小明"
age = 18
print(f"姓名: {name}, 年龄: {age}")
```
## 界面布局
代码课程采用上下分离布局:
```
+-------------------------------------+
| 编辑器 [Python▼] [清空][运行] | <- 上半部分:代码编辑器 (60%)
+-------------------------------------+
| 输出 | <- 下半部分:终端输出 (40%)
| Hello World! |
+-------------------------------------+
```
**布局说明**:
- **上半部分 (60%)**:代码编辑器 - 编写 Python 代码,包含语言选择、清空、运行按钮
- **下半部分 (40%)**:终端输出 - 查看执行结果、错误信息、退出码和耗时
**状态显示**:
- 顶部状态栏显示编辑器/语言选择器,以及 WebSocket 连接状态(已连接/未连接)
- 「运行」按钮在容器未启动或代码为空时禁用
- 代码编辑器在容器启动前即可查看和使用
代码课程采用上下分离布局:
```
+-------------------------------------+
| 编辑器 [运行] | <- 上半部分:代码编辑器
+-------------------------------------+
| 输出 | <- 下半部分:终端输出
| |
+-------------------------------------+
```
**布局说明**:
- **上半部分 (60%)**:代码编辑器 - 编写 Python 代码,包含语言选择、清空、运行按钮
- **下半部分 (40%)**:终端输出 - 查看执行结果、错误信息、退出码和耗时
**状态显示**:
- 顶部状态栏显示编辑器/语言选择器,以及 WebSocket 连接状态(已连接/未连接)
- 「运行」按钮在容器未启动或代码为空时禁用
- 代码编辑器在容器启动前即可查看和使用
## 技术架构
- **上半部分**:代码编辑器 - 编写 Python 代码
- **下半部分**:终端输出 - 查看执行结果
## 技术架构
### 前端组件
| 组件 | 路径 | 说明 |
|------|------|------|
| CodeEditor | `src/components/business/CodeEditor.tsx` | 基于 CodeMirror 的代码编辑器 |
| CodeExecutionResult | `src/components/business/CodeExecutionResult.tsx` | 执行结果展示 |
RY|| CodeTerminal | `src/components/business/CodeTerminal.tsx` | WebSocket 客户端,负责代码执行 |
**CodeTerminalRef 接口** (用于父组件调用):
```typescript
interface CodeTerminalRef {
executeCode: (code: string, language?: string) => void // 执行代码
cancelExecution: () => void // 取消执行
getCode: () => string // 获取当前代码
setCode: (code: string) => void // 设置代码到编辑器
}
```
### 后端实现
### 后端实现
| 模块 | 路径 | 说明 |
|------|------|------|
| WebSocket Handler | `internal/websocket/code.go` | 处理代码执行 WebSocket 连接 |
| Docker Controller | `internal/docker/controller.go` | ExecCode 方法执行容器内代码 |
| API Routes | `internal/api/routes.go` | `/ws/code` 路由注册 |
### WebSocket 协议
**连接地址**: `ws://host:port/ws/code`
**客户端发送 (ExecuteRequest)**:
```json
{
"type": "execute",
"data": {
"containerId": "容器ID",
"language": "python",
"code": "print('Hello')",
"timeout": 30
}
}
```
**服务端响应**:
```json
{
"type": "done",
"executionId": "exec_1234567890",
"output": "Hello\n",
"error": "",
"exitCode": 0,
"duration": 150
}
```
### 执行流程
```
用户点击 Run
↓
Learn.tsx 提取 Python 代码
↓
调用 CodeTerminal.executeCode()
↓
WebSocket 发送 execute 消息到 /ws/code
↓
CodeSession.handleExecute()
↓
DockerController.ExecCode()
↓
1. 将代码写入容器文件 /tmp/user_code.py
2. 执行 python3 /tmp/user_code.py
↓
返回执行结果
↓
CodeTerminal 显示结果
```
## 文件执行模式
代码执行采用文件执行模式,优势:
1. **多行代码支持** - 不受 shell 引号转义限制
2. **友好错误信息** - Python 显示正确的行号
3. **支持文件操作** - 可以 import、读写文件
4. **真实开发体验** - 用户编辑的是实际文件
执行过程:
- 代码保存到:`/tmp/user_code.py`
- 执行命令:`python3 /tmp/user_code.py`
## 安全限制
| 限制 | 值 | 说明 |
|------|-----|------|
| 超时时间 | 30 秒 | 代码执行最大时长 |
| 内存限制 | 512 MB | 容器内存上限 |
| 支持语言 | python, bash, node | 当前支持的语言 |
## 现有课程
已创建的示例课程:
- **Python 入门** (`courses/python-kwdb/`) - 基础 Python 语法与 KWDB 数据库操作学习
## 常见问题
### Q: 代码执行失败怎么办?
A: 检查以下几点:
1. 容器是否处于「运行中」状态
2. 代码是否有语法错误
3. 是否超过 30 秒超时限制
### Q: 如何添加新的编程语言?
A: 需要修改:
1. `internal/docker/controller.go` - 添加新语言的执行方法
2. `src/components/business/CodeTerminal.tsx` - 添加语言选项
### Q: 代码执行是否安全?
A: 代码在隔离的 Docker 容器中执行,有 30 秒超时和 512MB 内存限制。
## 开发指南
### 添加新语言支持
1. **后端**: 在 `docker/controller.go` 的 `ExecCode` 方法中添加语言分支
2. **前端**: 在 `CodeTerminal.tsx` 的语言选择器中添加选项
### 修改超时限制
- 前端: `CodeTerminal.tsx` 中的 `timeout: 30`
- 后端: `websocket/code.go` 中的 `defaultCodeTimeout`
### 测试
运行开发服务器后,访问 Python 课程即可测试功能。
```bash
make dev
# 访问 http://localhost:3006
# 选择 "Python 入门" 课程
```