better-staridc-MNBT
1---
2title: MNBT Docker 集成 PRD
3description: MNBT Docker 容器管理能力的产品需求文档(历史 PRD,实现见 Docker API 文档与 Docker 使用指南)
4---
5
6# MNBT Docker 集成 PRD(产品需求文档)
7
8> 历史 PRD,实现见 [Docker API 文档](../api/docker.md) 与 [Docker 使用指南](../guide/docker.md)。
9
10> 版本:v0.2(完全独立方案,待评审)
11> 日期:2026-08-04
12> 状态:待评审
13> 关联文档:[API.md](../api/overview.md)、[README.md](../guide/intro.md)、[app_plugins/PLUGIN_DEV.md](../development/plugin/guide.md)
14
15## 1. 背景与目标
16
17### 1.1 背景
18
19MNBT 目前是一个基于宝塔面板 API 的虚拟主机分销系统,核心链路(管理员 → 宝塔节点 → 虚拟主机 `MN_zj`)成熟但**与站点强耦合**(`bt_api` 建站、`MN_zj` 存站点/FTP/SQL 字段、`MN_bs` 套餐绑定一键部署)。
20
21现需新增 **Docker 容器管理**能力。经评审,**彻底放弃复用虚拟主机逻辑**——虚拟主机与 Docker 业务模型差异大(站点 vs 容器、域名/FTP/SQL vs 镜像/Compose/应用商店),强行复用会互相污染。
22
23### 1.2 决策:完全独立,只复用管理员后台
24
25| 组件 | 策略 |
26|------|------|
27| Docker 用户 | **独立表** `MN_docker_user`,独立认证 cookie,不碰 `MN_zj` |
28| Docker 套餐 | **独立表** `MN_docker_plan`,不碰 `MN_bs` |
29| Docker 订单 | **独立表** `MN_docker_order`,不碰 `MN_dd` |
30| 用户端 UI | **独立 scope** `templates/docker/` + 独立 `docker/` 控制器,不碰 `user/` |
31| 宝塔对接 | **独立类** `bt_docker`,不碰 `bt_api` |
32| 对外 API | **独立入口** `api/docker.php` |
33| **管理员后台** | ✅ **复用**:admin 登录、`admin/` 框架、admin scope 主题、操作日志 |
34
35> 原则:**两个产品线互不可见、互不依赖**。未来任一方的改动不影响另一方。
36
37### 1.3 范围
38
39**核心产品模型:每个 Docker 账户仅对应一个容器(单容器交付)**。用户账户 = 一个容器实例,用户登录后在应用商店/镜像页自助选择并创建,创建走异步任务 + 轮询。
40
41**本期(P0)**
42- `bt_docker` 类:容器/镜像/存储卷/网络/Compose/应用商店查询与管理。
43- `MN_docker_user / MN_docker_plan / MN_docker_order` 三张独立表 + 独立认证。
44- 独立 Docker 用户控制台(登录/自助选择镜像/应用商店创建/我的容器)。
45- **`create_app` P0 直接实现,异步任务 + 前端轮询拉取安装结果**。
46- **容器权限隔离 P0 实现:用户仅能查看/操作自己的单容器**。
47- **到期软删:到期后软删(停用)7 天,7 天后删除容器数据**。
48- 管理员端:Docker 用户管理、节点容器总览、套餐管理。
49- `api/docker.php` 对外对接 API(仅开通,校验节点 Docker 可达,不通则拒绝)。
50
51**下期(P1,本期不做)**
52- Docker 用户自助注册/购买下单 + 支付闭环。
53- 容器实时用量采集与配额告警。
54- 多容器账户扩展(当前锁定单容器)。
55
56---
57
58## 2. 架构总览
59
60```
61 Docker用户(独立控制台) 第三方系统
62 │ │
63┌───────▼────────┐ ┌───────▼──────┐
64│ docker/ 控制器 │ │ api/docker.php│
65│ 独立cookie认证 │ │ (对外API) │
66│ 独立scope模板 │ └───────┬──────┘
67└───────┬────────┘ │ 独立鉴权
68 │ 统一调用 ▼
69 └──────────────┬──────────────────────────────────┐
70 ▼ ▼
71 ┌─────────────────────────┐ ┌───────────────────────┐
72 │ MPHX/bt_docker.php(类) │ │ MN_docker_user/plan/ │
73 │ 容器/镜像/卷/网络/Compose │ │ order (独立表) │
74 └──────────┬──────────────┘ └──────────┬────────────┘
75 │ GET /btdocker/* POST /mod/docker/com/*/stype
76 ▼
77 宝塔面板 Docker API
78```
79
80管理员侧:复用 `admin/` 框架(`admin/login.php` 登录、admin scope 主题)新增 Docker 管理页,走 `bt_docker` + 独立三表。
81
82---
83
84## 3. 数据库设计(全新,不碰现有表结构)
85
86### 3.1 `MN_docker_user` — Docker 用户(单容器模型)
87
88> 每个账户 = 一个容器。`qk` 记录生命周期状态,`datae` 到期后进入 7 天软删。
89
90```sql
91CREATE TABLE IF NOT EXISTS `MN_docker_user` (
92 `id` int(11) NOT NULL AUTO_INCREMENT,
93 `username` varchar(64) NOT NULL, -- 登录名(唯一),同时作为容器命名前缀
94 `password_hash` varchar(255) NOT NULL, -- password_hash/bcrypt
95 `email` varchar(128) DEFAULT NULL,
96 `ssbt` varchar(250) NOT NULL, -- 所属宝塔 MN_bt.btdh
97 `data` varchar(50) NOT NULL, -- 开通时间
98 `datae` varchar(50) NOT NULL, -- 到期时间(0000-00-00=永久)
99 `qk` varchar(20) NOT NULL DEFAULT 'active',-- 状态:active/expired/paused
100 `plan_id` int(11) DEFAULT NULL, -- 套餐 MN_docker_plan.id
101 `container_id` varchar(64) DEFAULT NULL, -- 已创建容器 ID(单容器)
102 `service_name` varchar(64) DEFAULT NULL, -- create_app 的 service_name(唯一)
103 `app_name` varchar(64) DEFAULT NULL, -- 应用名(源自 get_apps)
104 `container_spec` text, -- 用户选择的容器规格 JSON(镜像/版本/cpus/mem/appenv)
105 `container_status` varchar(20) DEFAULT 'none', -- none/creating/running/stopped/failed
106 `disk_usage` bigint(20) NOT NULL DEFAULT '0', -- 最近磁盘用量(字节,由 get_path_size 采集)
107 `disk_usage_at` varchar(50) DEFAULT NULL, -- 磁盘用量采集时间
108 `expired_at` varchar(50) DEFAULT NULL, -- 软删开始时间(到期时间)
109 `prune_due` varchar(50) DEFAULT NULL, -- 7 天物理删除到期时间(空=未排程)
110 `extra` text, -- JSON 扩展(compose_dir 等)
111 `created_at` varchar(50) NOT NULL,
112 PRIMARY KEY (`id`),
113 UNIQUE KEY `uk_username` (`username`),
114 KEY `idx_ssbt` (`ssbt`),
115 KEY `idx_qk` (`qk`)
116) ENGINE=MyISAM DEFAULT CHARSET=utf8;
117```
118
119### 3.2 `MN_docker_plan` — Docker 套餐(单容器配额)
120
121```sql
122CREATE TABLE IF NOT EXISTS `MN_docker_plan` (
123 `id` int(11) NOT NULL AUTO_INCREMENT,
124 `name` varchar(64) NOT NULL, -- 套餐名
125 `jc` text, -- 介绍
126 `cpu_max` varchar(20) NOT NULL DEFAULT '1',-- CPU 核上限(create_app.cpus)
127 `mem_max` varchar(20) NOT NULL DEFAULT '512',-- 内存 MB 上限(create_app.memory_limit)
128 `disk_max` varchar(20) NOT NULL DEFAULT '0',-- 磁盘配额 MB 上限(0=不限制,通过 get_installed_apps.path + get_path_size 采集比对)
129 `jg` varchar(50) NOT NULL, -- 价格
130 `qk` varchar(10) NOT NULL DEFAULT 'true', -- 上架/下架
131 `date` varchar(50) NOT NULL,
132 PRIMARY KEY (`id`)
133) ENGINE=MyISAM DEFAULT CHARSET=utf8;
134```
135> 单容器模型下无 `container_max` 字段(每账户固定 1 容器)。
136
137### 3.3 `MN_docker_order` — Docker 订单(预留,P0 可只建表不接支付)
138
139```sql
140CREATE TABLE IF NOT EXISTS `MN_docker_order` (
141 `id` int(11) NOT NULL AUTO_INCREMENT,
142 `username` varchar(64) NOT NULL, -- MN_docker_user.username
143 `plan_id` int(11) NOT NULL,
144 `rmb` varchar(50) NOT NULL,
145 `qk` varchar(10) NOT NULL DEFAULT 'false', -- 支付状态
146 `date` varchar(50) NOT NULL,
147 PRIMARY KEY (`id`)
148) ENGINE=MyISAM DEFAULT CHARSET=utf8;
149```
150
151### 3.4 升级 SQL
152
153- `install/install.sql` 追加三张表建表语句。
154- 新增 `update/update_v183_docker.sql` 增量脚本(`CREATE TABLE IF NOT EXISTS`)。
155
156---
157
158## 4. Docker 用户认证(独立于 member.php)
159
160### 4.1 方案
161
162仿照 `user_info` 插件的思路但落在核心层,**不改** `MPHX/member.php`:
163
164- Cookie 名:`docker_token`(与 `admin_token`/`user_token` 完全独立)。
165- 加密:`authcode($user_id . "\t" . $session_hash, 'ENCODE', SYS_KEY)`。
166- `session_hash = md5($user_id . $password_hash . SYS_KEY)`(改密后旧 cookie 自动失效)。
167- 密码:`password_hash` / `password_verify`(bcrypt)。
168
169### 4.2 认证函数(新增 `MPHX/docker.member.php`)
170
171```php
172function docker_auth_login($user_id, $password_hash);
173function docker_auth_logout();
174function docker_auth_current(); // 查 MN_docker_user,校验状态/到期
175function docker_auth_require(); // 未登录跳转 docker/login.php
176```
177
178### 4.3 登录流程
179
180- `docker/login.php`:登录表单 → `POST docker/ajax.php`(`gn=login`)→ 校验 → 写 cookie → 跳 `docker/console.php`。
181- 到期检查:登录时 `datae` 已过且非永久 → 拒绝并提示(沿用现有到期语义)。
182- 暂停检查:`qk=expired` → 提示到期;`qk=paused` → 提示已暂停。
183
184---
185
186## 5. `MPHX/bt_docker.php`(宝塔 Docker API 封装)
187
188### 5.1 定位
189
190独立类,**不得**修改 `MPHX/bt_api.php`。两者共享签名算法但不继承。
191
192### 5.2 类原型
193
194```php
195class bt_docker
196{
197 public $BT_PANEL;
198 public $BT_KEY;
199
200 public function __construct($bt_panel = null, $bt_key = null);
201
202 private function GetKeyData(); // md5(time.md5(key)) + time(自持,不改 bt_api)
203 private function HttpGet($path, $params = [], $timeout = 30); // 签名入 query + cookie
204 private function HttpPost($path, $params = [], $timeout = 60); // 签名入 body + cookie
205
206 // —— 安装与配置(GET /btdocker/setup/)——
207 public function get_config();
208 public function install_docker_program();
209 public function get_registry_mirrors();
210 public function set_registry_mirrors($mirrors);
211
212 // —— 容器(GET /btdocker/container/)——
213 public function container_list();
214 public function container_start($id, $name);
215 public function container_stop($id, $name);
216 public function container_restart($id, $name);
217 public function container_prune();
218 public function container_log($id, $name, $lines = 500);
219
220 // —— 镜像(GET /btdocker/image/)——
221 public function image_list();
222 public function image_search($keyword);
223 public function image_prune();
224
225 // —— 存储卷(GET /btdocker/volume/)——
226 public function volume_list();
227 public function volume_add($name, $driver = 'local');
228 public function volume_prune();
229
230 // —— 网络(GET /btdocker/network/)——
231 public function network_list();
232 public function network_create($name, $driver = 'bridge');
233 public function network_prune();
234
235 // —— 仓库(GET /btdocker/registry/)——
236 public function registry_list();
237
238 // —— Compose/项目(GET /btdocker/compose|project/)——
239 public function template_list();
240 public function project_list();
241
242 // —— 应用商店(POST /mod/docker/com/)——
243 public function app_list(); // get_apps:应用列表及参数定义
244 public function app_create($params); // create_app:P0 直接实现(异步任务封装)
245 public function app_dependence($app); // get_dependence_apps
246 public function get_cmd_log(); // 容器执行日志(轮询安装进度用)
247 public function get_path_size($path); // 获取指定路径磁盘占用大小(字节)
248}
249```
250
251### 5.3 签名与请求方式(关键风险点)
252
253宝塔官方文档仅给路由,**未标注签名/cookie**,且注明接口不稳定。假设沿用面板统一签名:
254
255- `GET /btdocker/*`:签名拼入 query string + cookie。
256- `POST /mod/docker/com/*/stype`:签名入 body + cookie。
257
258> ⚠️ **M1 首要任务:真机联调验证**(先 `get_config`、`container_list`)。如有出入,仅改 `bt_docker` 内部 transport,不影响上层。
259
260---
261
262## 6. 前端:独立 Docker scope
263
264### 6.1 `theme.php` 增加 `docker` scope
265
266`theme.php` 当前只认 `user|admin`,新增 `docker` scope(**唯一核心文件改动**,不影响现有 5 主题):
267
268- `mnbt_theme_resolve($view, 'docker')` → `templates/{theme}/docker/{view}.php`,回退 `templates/default/docker/`。
269- `mnbt_theme_name('docker')` → 读 `active_docker_theme` / `conf['docker_theme']`。
270- `mnbt_render($view, $vars, $exit, 'docker')` 与 `mnbt_theme_url('/assets/..','docker')` 支持 docker scope。
271
272### 6.2 目录约定
273
274```
275templates/
276├── default/docker/ # 默认 Docker scope 视图(回退基准)
277│ ├── login.php
278│ ├── console.php # 容器控制台
279│ ├── image.php # 镜像管理
280│ ├── volume.php # 存储卷
281│ ├── compose.php # Compose/项目
282│ ├── appstore.php # 应用商店
283│ └── assets/
284├── active_docker_theme # 当前 Docker 主题名
285└── layui|jqueryui|bootstrapui|tdesign # —— 无需改动 ——
286```
287
288### 6.3 Docker 控制器
289
290```
291docker/ # 新顶层控制器
292├── login.php # 登录(独立 docker_token 认证)
293├── index.php # scope 外壳
294├── console.php # 我的容器
295├── image.php
296├── volume.php
297├── compose.php
298├── appstore.php
299└── ajax.php # gn 分发(docker_* 操作)
300```
301
302### 6.4 Docker 账号生命周期(到期软删)
303
304`MN_docker_user.qk` 状态机 + 7 天软删(决策 6):
305
306| 状态 | 触发 | 行为 |
307|------|------|------|
308| `active` | 开通 | 用户可登录、可管理/安装容器 |
309| `expired` | `datae` 到期 | `container_stop` 停容器;写 `expired_at`;进入 7 天软删期,`qk=expired`,禁止登录/新建 |
310| 7 天后 | cron 扫描 `prune_due` 到期 | `container_prune` 物理清理容器数据 + `MN_docker_user` 置 `qk=pruned` |
311
312- 7 天软删期内管理员可**续期恢复**:`datae` 延后 + `qk=active`。
313- `prune_due = expired_at + 7 天`,由定时任务(cron)扫描执行。
314- 永久账号(`datae=0000-00-00`)不进入软删流程。
315
316---
317
318## 7. 管理员端(复用 admin 框架)
319
320管理员侧栏硬编码新增分组 **Docker 管理**(系统级,不属于插件菜单):
321
322| 页面 | 说明 |
323|------|------|
324| `admin/docker.php` | Docker 管理入口页(Tab:用户 / 套餐 / 节点容器总览) |
325| 用户 Tab | `MN_docker_user` 列表:添加/编辑/暂停/删除/重置密码 |
326| 套餐 Tab | `MN_docker_plan` 列表:增删改、上架/下架 |
327| 节点 Tab | 选节点 → `bt_docker::container_list` 容器总览 |
328
329复用点:admin 登录(`admin/login.php`)、admin scope 主题(`mnbt_admin_render`)、`MN_log` 操作日志、`admin/api/` 模块分发。
330
331> 侧栏"Docker 管理"是**系统级硬编码项**,本期仅加 default 主题的 `admin/index.php`;layui/jqueryui/bootstrapui/tdesign 下期补(见决策 1)。
332
333---
334
335## 8. 对外对接 API:`api/docker.php`
336
337### 8.1 定位
338
339`api/docker.php` **仅提供开通(provision)逻辑**,与 `api/api.php` 的 `gn=kt`(开通主机)对称。开通只创建账号记录(`MN_docker_user`),**不创建任何容器**;容器创建由用户登录 Docker 控制台后在应用商店 / 镜像界面引导完成(内部走 `bt_docker::app_create`,用量限制经 create_app 的 `cpus` / `memory_limit` 生效)。
340
341### 8.2 鉴权(独立于 api/api.php)
342
343| 参数 | 来源 |
344|------|------|
345| `mn_bh` | `MN_bt.btdh` |
346| `mn_key` | = `MN_config.api`(系统密钥) |
347| `mn_keye` | = `md5(MN_bt.ktmy . MN_bt.qmk)`(宝塔调用密钥) |
348| `mn_vs` | 协议版本号 >= 15 |
349| `username` | 待开通的 Docker 用户名 |
350
351> 不复用 `MN_zj`,只认 `MN_docker_user`。
352
353### 8.3 接口:`gn=kt` 开通 Docker 用户
354
355传入/传出结构参考 `api/api.php` 的 `gn=kt`。
356
357**请求(POST /api/docker.php?gn=kt):**
358
359| 参数 | 必填 | 说明 |
360|------|------|------|
361| `mn_bh` | 是 | 宝塔节点编号 |
362| `mn_key` | 是 | 系统密钥 |
363| `mn_keye` | 是 | 宝塔调用密钥 = `md5(MN_bt.ktmy . MN_bt.qmk)` |
364| `mn_vs` | 是 | 协议版本号 >= 15 |
365| `username` | 是 | Docker 用户名(>= 6 位,唯一) |
366| `password` | 是 | 密码(>= 6 位) |
367| `plan_id` | 否 | 套餐 ID(来自 `MN_docker_plan`);不传则用默认配额 |
368| `dqtime` | 否 | 到期日期 `Y-m-d`,`0` = 永久 |
369
370**处理逻辑(对照 api.php 的 kt):**
371
3721. `MN_config.apiqk` 关闭则拒绝。
3732. 校验参数完整 + `mn_key` 匹配 + 节点存在且启用 + `mn_keye` 匹配。
3743. 查重 `MN_docker_user.username`,重复则拒绝。
3754. 账号/密码长度 >= 6。
3765. 按 `plan_id` 解析配额(`cpu_max` / `mem_max`)落库 `MN_docker_user`(单容器模型,无 `container_max`)。
3776. **校验节点 Docker 可达**(`bt_docker->get_config()`);不通则**拒绝开通**(`code=100`,"节点 Docker 不可用")。
3787. 写 `MN_log`,触发 `host.created` 钩子。
3798. **不创建容器**(交给用户登录后引导创建)。
380
381**响应(成功):**
382
383```json
384{ "success": true, "code": 200, "msg": "Docker 账号开通成功!" }
385```
386
387**响应(失败,对齐 api/api.php 风格):**
388
389```json
390{ "success": false, "code": 100, "msg": "错误!该账号已存在!" }
391```
392
393### 8.4 不包含(P0)
394
395容器/镜像/存储卷/网络/Compose 的管理与查询、应用商店列表与安装、用户登录令牌等,**均不在 `api/docker.php` 内**,归属用户控制台 `docker/` + `bt_docker`。`api/docker.php` 只做开通一件事。
396
397---
398
399> 后续章节(用户侧用量限制、目录结构汇总、已确认决策点、里程碑与验收、风险与开放问题)见 [docker-appendix.md](./docker-appendix.md)。