nodebbs 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -111
- package/bin/dev.js +6 -1
- package/bin/run.js +6 -1
- package/dist/commands/clean/index.d.ts +11 -0
- package/dist/commands/clean/index.js +93 -0
- package/dist/commands/db/backup.d.ts +9 -0
- package/dist/commands/db/backup.js +78 -0
- package/dist/commands/db/generate.d.ts +3 -0
- package/dist/commands/db/generate.js +9 -3
- package/dist/commands/db/migrate.d.ts +3 -0
- package/dist/commands/db/migrate.js +8 -2
- package/dist/commands/db/push.d.ts +3 -0
- package/dist/commands/db/push.js +8 -2
- package/dist/commands/db/reset.d.ts +3 -0
- package/dist/commands/db/reset.js +10 -4
- package/dist/commands/db/seed.d.ts +3 -0
- package/dist/commands/db/seed.js +9 -3
- package/dist/commands/logs/api.d.ts +3 -0
- package/dist/commands/logs/api.js +8 -2
- package/dist/commands/logs/db.d.ts +3 -0
- package/dist/commands/logs/db.js +8 -2
- package/dist/commands/logs/index.d.ts +3 -0
- package/dist/commands/logs/index.js +8 -2
- package/dist/commands/logs/redis.d.ts +3 -0
- package/dist/commands/logs/redis.js +8 -2
- package/dist/commands/logs/web.d.ts +3 -0
- package/dist/commands/logs/web.js +9 -3
- package/dist/commands/{db → rebuild}/index.d.ts +2 -1
- package/dist/commands/rebuild/index.js +11 -0
- package/dist/commands/restart/index.d.ts +3 -0
- package/dist/commands/restart/index.js +10 -2
- package/dist/commands/shell/api.d.ts +3 -0
- package/dist/commands/shell/api.js +9 -3
- package/dist/commands/shell/db.d.ts +3 -0
- package/dist/commands/shell/db.js +9 -3
- package/dist/commands/shell/redis.d.ts +3 -0
- package/dist/commands/shell/redis.js +9 -3
- package/dist/commands/shell/web.d.ts +3 -0
- package/dist/commands/shell/web.js +9 -3
- package/dist/commands/{dev → start}/index.d.ts +2 -2
- package/dist/commands/start/index.js +111 -0
- package/dist/commands/status/index.d.ts +3 -0
- package/dist/commands/status/index.js +8 -2
- package/dist/commands/stop/index.d.ts +1 -0
- package/dist/commands/stop/index.js +9 -1
- package/dist/interactive.d.ts +1 -0
- package/dist/interactive.js +171 -0
- package/dist/templates/env +61 -0
- package/dist/utils/docker.js +10 -18
- package/dist/utils/env.js +20 -10
- package/dist/utils/selection.d.ts +7 -0
- package/dist/utils/selection.js +68 -0
- package/dist/utils/template.d.ts +11 -0
- package/dist/utils/template.js +18 -0
- package/oclif.manifest.json +367 -90
- package/package.json +30 -25
- package/dist/commands/db/index.js +0 -11
- package/dist/commands/deploy/index.d.ts +0 -8
- package/dist/commands/deploy/index.js +0 -95
- package/dist/commands/dev/index.js +0 -59
- package/dist/commands/setup/index.d.ts +0 -5
- package/dist/commands/setup/index.js +0 -12
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# NodeBBS CLI
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> NodeBBS 论坛系统专业运维工具
|
|
4
4
|
|
|
5
5
|
[](https://oclif.io)
|
|
6
6
|
[](https://npmjs.org/package/nodebbs)
|
|
@@ -11,7 +11,8 @@ NodeBBS CLI 是一个专为全栈开发者设计的命令行工具,用于简
|
|
|
11
11
|
|
|
12
12
|
### 特性
|
|
13
13
|
|
|
14
|
-
-
|
|
14
|
+
- �️ **交互式菜单** - 支持键盘导航的可视化命令选择(新增)
|
|
15
|
+
- �🚀 **快速启动** - 一键启动开发环境
|
|
15
16
|
- 🎯 **全栈友好** - 命令设计贴近开发者思维
|
|
16
17
|
- 🔧 **服务级控制** - 可以单独管理每个服务
|
|
17
18
|
- 📊 **实时日志** - 方便查看各服务日志
|
|
@@ -32,44 +33,36 @@ pnpm add -g nodebbs
|
|
|
32
33
|
yarn global add nodebbs
|
|
33
34
|
```
|
|
34
35
|
|
|
35
|
-
或者直接使用 npx
|
|
36
|
+
或者直接使用 npx(推荐):
|
|
36
37
|
|
|
37
38
|
```bash
|
|
39
|
+
# 进入交互式菜单(推荐)
|
|
40
|
+
npx nodebbs
|
|
41
|
+
|
|
42
|
+
# 运行特定命令
|
|
38
43
|
npx nodebbs [command]
|
|
39
44
|
```
|
|
40
45
|
|
|
41
46
|
## 🚀 快速开始
|
|
42
47
|
|
|
43
|
-
### 1.
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
# 首次使用,初始化项目环境
|
|
47
|
-
npx nodebbs setup
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
这会创建 `.env` 文件,请编辑该文件并修改:
|
|
51
|
-
- `POSTGRES_PASSWORD` - 数据库密码
|
|
52
|
-
- `REDIS_PASSWORD` - Redis 密码
|
|
53
|
-
- `JWT_SECRET` - JWT 密钥
|
|
54
|
-
|
|
55
|
-
### 2. 启动开发环境
|
|
48
|
+
### 1. 启动服务
|
|
56
49
|
|
|
57
50
|
```bash
|
|
58
|
-
#
|
|
59
|
-
npx nodebbs
|
|
51
|
+
# 启动所有服务(生产模式,包含完整检查)
|
|
52
|
+
npx nodebbs start
|
|
60
53
|
|
|
61
|
-
#
|
|
62
|
-
npx nodebbs
|
|
54
|
+
# 重新构建并启动(跳过检查,用于更新)
|
|
55
|
+
npx nodebbs start --build
|
|
63
56
|
```
|
|
64
57
|
|
|
65
|
-
###
|
|
58
|
+
### 2. 查看服务状态
|
|
66
59
|
|
|
67
60
|
```bash
|
|
68
61
|
# 查看所有服务状态
|
|
69
62
|
npx nodebbs status
|
|
70
63
|
```
|
|
71
64
|
|
|
72
|
-
###
|
|
65
|
+
### 3. 访问应用
|
|
73
66
|
|
|
74
67
|
- **Web 前端**: http://localhost:3100
|
|
75
68
|
- **API 文档**: http://localhost:7100/docs
|
|
@@ -77,61 +70,46 @@ npx nodebbs status
|
|
|
77
70
|
|
|
78
71
|
## 📚 命令参考
|
|
79
72
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
#### `nodebbs setup`
|
|
83
|
-
初始化项目(首次使用)
|
|
73
|
+
#### `nodebbs start`
|
|
74
|
+
开始部署
|
|
84
75
|
|
|
85
76
|
```bash
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
#### `nodebbs dev`
|
|
90
|
-
启动开发环境
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
# 启动所有服务
|
|
94
|
-
npx nodebbs dev
|
|
95
|
-
|
|
96
|
-
# 重新构建并启动
|
|
97
|
-
npx nodebbs dev --rebuild
|
|
98
|
-
npx nodebbs dev -r
|
|
77
|
+
# 交互式启动
|
|
78
|
+
npx nodebbs start
|
|
99
79
|
|
|
100
80
|
# 指定环境启动
|
|
101
|
-
npx nodebbs
|
|
102
|
-
|
|
103
|
-
|
|
81
|
+
npx nodebbs start -e production
|
|
82
|
+
|
|
83
|
+
# 重新构建并启动(跳过检查)
|
|
84
|
+
npx nodebbs start --build
|
|
104
85
|
```
|
|
105
86
|
|
|
106
87
|
**参数**:
|
|
107
88
|
- `-e, --env` - 部署环境(production, lowmem, basic)
|
|
108
|
-
- `-
|
|
109
|
-
|
|
110
|
-
#### `nodebbs deploy`
|
|
111
|
-
部署到生产环境
|
|
89
|
+
- `-b, --build` - 重新构建镜像并启动(跳过健康检查和初始化)
|
|
112
90
|
|
|
113
|
-
|
|
114
|
-
# 完整部署流程
|
|
115
|
-
npx nodebbs deploy
|
|
116
|
-
|
|
117
|
-
# 指定环境部署
|
|
118
|
-
npx nodebbs deploy -e production
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
包含:
|
|
91
|
+
启动流程包含:
|
|
122
92
|
- Docker 环境检查
|
|
123
93
|
- 环境变量验证
|
|
124
94
|
- 镜像构建
|
|
125
95
|
- 服务启动
|
|
126
|
-
-
|
|
127
|
-
-
|
|
96
|
+
- 健康检查(默认开启,使用 `--build` 跳过)
|
|
97
|
+
- 数据库初始化(默认开启,使用 `--build` 跳过)
|
|
98
|
+
|
|
99
|
+
#### `nodebbs restart`
|
|
100
|
+
重启所有服务(不更新镜像,支持环境选择)
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
npx nodebbs restart
|
|
104
|
+
npx nodebbs restart -e production
|
|
105
|
+
```
|
|
128
106
|
|
|
129
107
|
#### `nodebbs stop`
|
|
130
|
-
|
|
108
|
+
停止服务(支持环境选择)
|
|
131
109
|
|
|
132
110
|
```bash
|
|
133
|
-
# 停止所有服务
|
|
134
111
|
npx nodebbs stop
|
|
112
|
+
npx nodebbs stop -e production
|
|
135
113
|
|
|
136
114
|
# 停止服务并删除数据卷(危险!)
|
|
137
115
|
npx nodebbs stop --volumes
|
|
@@ -141,27 +119,25 @@ npx nodebbs stop -v
|
|
|
141
119
|
**参数**:
|
|
142
120
|
- `-v, --volumes` - 同时删除数据卷
|
|
143
121
|
|
|
144
|
-
#### `nodebbs restart`
|
|
145
|
-
重启所有服务
|
|
146
|
-
|
|
147
|
-
```bash
|
|
148
|
-
npx nodebbs restart
|
|
149
|
-
```
|
|
150
|
-
|
|
151
122
|
#### `nodebbs status`
|
|
152
123
|
查看服务状态
|
|
153
124
|
|
|
154
125
|
```bash
|
|
155
126
|
npx nodebbs status
|
|
127
|
+
# 查看指定环境状态
|
|
128
|
+
npx nodebbs status -e production
|
|
156
129
|
```
|
|
157
130
|
|
|
158
131
|
### 日志管理
|
|
159
132
|
|
|
160
133
|
#### `nodebbs logs`
|
|
161
|
-
|
|
134
|
+
查看服务日志
|
|
162
135
|
|
|
163
136
|
```bash
|
|
137
|
+
# 查看所有日志
|
|
164
138
|
npx nodebbs logs
|
|
139
|
+
# 查看指定环境日志
|
|
140
|
+
npx nodebbs logs -e production
|
|
165
141
|
```
|
|
166
142
|
|
|
167
143
|
#### `nodebbs logs:api`
|
|
@@ -194,11 +170,14 @@ npx nodebbs logs:redis
|
|
|
194
170
|
|
|
195
171
|
### Shell 访问
|
|
196
172
|
|
|
197
|
-
#### `nodebbs shell
|
|
198
|
-
|
|
173
|
+
#### `nodebbs shell`
|
|
174
|
+
进入容器 Shell (支持 api, web, db, redis)
|
|
199
175
|
|
|
200
176
|
```bash
|
|
177
|
+
# 进入 API 容器
|
|
201
178
|
npx nodebbs shell:api
|
|
179
|
+
# 进入生产环境 API 容器
|
|
180
|
+
npx nodebbs shell:api -e production
|
|
202
181
|
```
|
|
203
182
|
|
|
204
183
|
#### `nodebbs shell:web`
|
|
@@ -224,12 +203,7 @@ npx nodebbs shell:redis
|
|
|
224
203
|
|
|
225
204
|
### 数据库管理
|
|
226
205
|
|
|
227
|
-
#### `nodebbs db`
|
|
228
|
-
打开数据库管理界面(Drizzle Studio)
|
|
229
206
|
|
|
230
|
-
```bash
|
|
231
|
-
npx nodebbs db
|
|
232
|
-
```
|
|
233
207
|
|
|
234
208
|
#### `nodebbs db:generate`
|
|
235
209
|
生成数据库迁移文件
|
|
@@ -266,30 +240,49 @@ npx nodebbs db:seed
|
|
|
266
240
|
npx nodebbs db:reset
|
|
267
241
|
```
|
|
268
242
|
|
|
243
|
+
#### `nodebbs db:backup`
|
|
244
|
+
备份数据库
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
npx nodebbs db:backup
|
|
248
|
+
npx nodebbs db:backup -o backup.sql
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
#### `nodebbs clean`
|
|
252
|
+
清理 Docker 缓存和残留资源
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
# 交互式选择清理项目
|
|
256
|
+
npx nodebbs clean
|
|
257
|
+
|
|
258
|
+
# 自动清理所有(构建缓存、无用镜像、网络)
|
|
259
|
+
npx nodebbs clean -a
|
|
260
|
+
|
|
261
|
+
# 仅清理构建缓存
|
|
262
|
+
npx nodebbs clean --cache
|
|
263
|
+
```
|
|
264
|
+
|
|
269
265
|
## 🎯 使用场景
|
|
270
266
|
|
|
271
267
|
### 场景 1:新开发者加入项目
|
|
272
268
|
|
|
273
269
|
```bash
|
|
274
|
-
# 1.
|
|
275
|
-
npx nodebbs
|
|
276
|
-
|
|
277
|
-
# 2. 启动开发环境
|
|
278
|
-
npx nodebbs dev
|
|
270
|
+
# 1. 启动服务
|
|
271
|
+
npx nodebbs start
|
|
279
272
|
|
|
280
|
-
#
|
|
273
|
+
# 2. 初始化数据库
|
|
281
274
|
npx nodebbs db:push
|
|
282
275
|
npx nodebbs db:seed
|
|
283
276
|
|
|
284
|
-
#
|
|
277
|
+
# 3. 查看服务状态
|
|
285
278
|
npx nodebbs status
|
|
286
279
|
```
|
|
287
280
|
|
|
288
281
|
### 场景 2:日常开发
|
|
289
282
|
|
|
290
283
|
```bash
|
|
291
|
-
#
|
|
292
|
-
npx nodebbs
|
|
284
|
+
# 启动服务
|
|
285
|
+
npx nodebbs start
|
|
293
286
|
|
|
294
287
|
# 查看 API 日志
|
|
295
288
|
npx nodebbs logs:api
|
|
@@ -297,8 +290,7 @@ npx nodebbs logs:api
|
|
|
297
290
|
# 进入 API 容器调试
|
|
298
291
|
npx nodebbs shell:api
|
|
299
292
|
|
|
300
|
-
|
|
301
|
-
npx nodebbs db
|
|
293
|
+
|
|
302
294
|
|
|
303
295
|
# 重置测试数据
|
|
304
296
|
npx nodebbs db:reset
|
|
@@ -311,7 +303,7 @@ npx nodebbs db:reset
|
|
|
311
303
|
npx nodebbs stop
|
|
312
304
|
|
|
313
305
|
# 重新构建并启动
|
|
314
|
-
npx nodebbs
|
|
306
|
+
npx nodebbs start --build
|
|
315
307
|
|
|
316
308
|
# 查看日志确认启动成功
|
|
317
309
|
npx nodebbs logs
|
|
@@ -321,7 +313,7 @@ npx nodebbs logs
|
|
|
321
313
|
|
|
322
314
|
```bash
|
|
323
315
|
# 部署到生产环境
|
|
324
|
-
npx nodebbs
|
|
316
|
+
npx nodebbs start -e production
|
|
325
317
|
|
|
326
318
|
# 查看服务状态
|
|
327
319
|
npx nodebbs status
|
|
@@ -336,8 +328,7 @@ npx nodebbs db:migrate
|
|
|
336
328
|
### 场景 5:数据库操作
|
|
337
329
|
|
|
338
330
|
```bash
|
|
339
|
-
|
|
340
|
-
npx nodebbs db
|
|
331
|
+
|
|
341
332
|
|
|
342
333
|
# 运行迁移
|
|
343
334
|
npx nodebbs db:migrate
|
|
@@ -355,18 +346,16 @@ npx nodebbs shell:db
|
|
|
355
346
|
|
|
356
347
|
| Makefile 命令 | NodeBBS CLI 命令 |
|
|
357
348
|
|--------------|-----------------|
|
|
358
|
-
| `make
|
|
359
|
-
| `make
|
|
360
|
-
| `make
|
|
361
|
-
| `make rebuild` | `npx nodebbs
|
|
362
|
-
| `ENV=prod make rebuild` | `npx nodebbs deploy -e production` |
|
|
349
|
+
| `make up` | `npx nodebbs start` |
|
|
350
|
+
| `make build` | `npx nodebbs start --build` |
|
|
351
|
+
| `make rebuild` | `npx nodebbs start --build` |
|
|
352
|
+
| `ENV=prod make rebuild` | `npx nodebbs start -e production --build` |
|
|
363
353
|
| `make down` | `npx nodebbs stop` |
|
|
364
|
-
| `make restart` | `npx nodebbs restart` |
|
|
365
354
|
| `make ps` | `npx nodebbs status` |
|
|
366
355
|
| `make logs` | `npx nodebbs logs` |
|
|
367
356
|
| `make logs-api` | `npx nodebbs logs:api` |
|
|
368
357
|
| `make exec-api` | `npx nodebbs shell:api` |
|
|
369
|
-
|
|
358
|
+
|
|
370
359
|
| `make clean-all` | `npx nodebbs stop --volumes` |
|
|
371
360
|
|
|
372
361
|
## ⚙️ 环境配置
|
|
@@ -400,6 +389,18 @@ API_URL=http://localhost:7100
|
|
|
400
389
|
CORS_ORIGIN=*
|
|
401
390
|
```
|
|
402
391
|
|
|
392
|
+
> **提示**:可以使用 `openssl rand -hex 32` 命令生成安全的随机密钥。
|
|
393
|
+
|
|
394
|
+
### 环境持久化
|
|
395
|
+
|
|
396
|
+
CLI 会自动记住您上次启动的环境:
|
|
397
|
+
|
|
398
|
+
1. 当您运行 `nodebbs start` 并选择环境(如 `production`)后,CLI 会在当前目录创建 `.nodebbs-env` 文件记录该选择。
|
|
399
|
+
2. 后续运行 `nodebbs logs`, `nodebbs status` 等命令时,会自动使用该环境,无需再次指定 `-e production`。
|
|
400
|
+
3. 运行 `nodebbs stop` 成功停止服务后,会自动删除 `.nodebbs-env` 文件。
|
|
401
|
+
|
|
402
|
+
**注意**:如果您需要临时操作其他环境,仍然可以使用 `-e` 参数强制指定,例如 `nodebbs logs -e basic`。
|
|
403
|
+
|
|
403
404
|
## 🛠️ 高级用法
|
|
404
405
|
|
|
405
406
|
### 内置模板
|
|
@@ -408,7 +409,7 @@ NodeBBS CLI 内置了 Docker Compose 配置文件,即使项目中没有这些
|
|
|
408
409
|
|
|
409
410
|
```bash
|
|
410
411
|
# 在任何目录运行
|
|
411
|
-
npx nodebbs
|
|
412
|
+
npx nodebbs start
|
|
412
413
|
```
|
|
413
414
|
|
|
414
415
|
CLI 会自动:
|
|
@@ -474,7 +475,7 @@ npx nodebbs stop --volumes
|
|
|
474
475
|
|
|
475
476
|
```bash
|
|
476
477
|
# 克隆仓库
|
|
477
|
-
git clone https://github.com/
|
|
478
|
+
git clone https://github.com/aiprojecthub/nodebbs.git
|
|
478
479
|
cd nodebbs
|
|
479
480
|
|
|
480
481
|
# 安装依赖
|
|
@@ -493,26 +494,17 @@ pnpm build
|
|
|
493
494
|
# 查看帮助
|
|
494
495
|
./bin/run.js --help
|
|
495
496
|
|
|
496
|
-
# 测试
|
|
497
|
-
./bin/run.js
|
|
497
|
+
# 测试 start 命令
|
|
498
|
+
./bin/run.js start --help
|
|
498
499
|
|
|
499
500
|
# 测试 logs 命令
|
|
500
501
|
./bin/run.js logs --help
|
|
501
502
|
```
|
|
502
503
|
|
|
503
|
-
## 🤝 贡献
|
|
504
|
-
|
|
505
|
-
欢迎贡献!请查看 [CONTRIBUTING.md](CONTRIBUTING.md) 了解详情。
|
|
506
|
-
|
|
507
|
-
## 📄 许可证
|
|
508
|
-
|
|
509
|
-
MIT License - 详见 [LICENSE](LICENSE) 文件
|
|
510
|
-
|
|
511
504
|
## 🔗 相关链接
|
|
512
505
|
|
|
513
|
-
- [NodeBBS 项目](https://github.com/
|
|
514
|
-
- [问题反馈](https://github.com/
|
|
515
|
-
- [更新日志](CHANGELOG.md)
|
|
506
|
+
- [NodeBBS 项目](https://github.com/aiprojecthub/nodebbs)
|
|
507
|
+
- [问题反馈](https://github.com/aiprojecthub/nodebbs/issues)
|
|
516
508
|
|
|
517
509
|
## 💡 提示
|
|
518
510
|
|
package/bin/dev.js
CHANGED
|
@@ -2,4 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import {execute} from '@oclif/core'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
if (process.argv.length <= 2) {
|
|
6
|
+
const {runInteractive} = await import('../src/interactive.ts')
|
|
7
|
+
await runInteractive(import.meta.url)
|
|
8
|
+
} else {
|
|
9
|
+
await execute({development: true, dir: import.meta.url})
|
|
10
|
+
}
|
package/bin/run.js
CHANGED
|
@@ -2,4 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import {execute} from '@oclif/core'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
if (process.argv.length <= 2) {
|
|
6
|
+
const {runInteractive} = await import('../dist/interactive.js')
|
|
7
|
+
await runInteractive(import.meta.url)
|
|
8
|
+
} else {
|
|
9
|
+
await execute({dir: import.meta.url})
|
|
10
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Clean extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static flags: {
|
|
5
|
+
all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
6
|
+
cache: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
images: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
};
|
|
10
|
+
run(): Promise<void>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import { checkbox, confirm } from '@inquirer/prompts';
|
|
3
|
+
import { logger } from '../../utils/logger.js';
|
|
4
|
+
import { execa } from 'execa';
|
|
5
|
+
export default class Clean extends Command {
|
|
6
|
+
static description = '清理 Docker 缓存和残留资源';
|
|
7
|
+
static flags = {
|
|
8
|
+
all: Flags.boolean({
|
|
9
|
+
char: 'a',
|
|
10
|
+
description: '清理所有 (构建缓存、无用镜像、网络)',
|
|
11
|
+
default: false,
|
|
12
|
+
}),
|
|
13
|
+
cache: Flags.boolean({
|
|
14
|
+
description: '清理构建缓存',
|
|
15
|
+
default: false,
|
|
16
|
+
}),
|
|
17
|
+
images: Flags.boolean({
|
|
18
|
+
description: '清理无用镜像 (dangling)',
|
|
19
|
+
default: false,
|
|
20
|
+
}),
|
|
21
|
+
force: Flags.boolean({
|
|
22
|
+
char: 'f',
|
|
23
|
+
description: '跳过确认提示',
|
|
24
|
+
default: false,
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
async run() {
|
|
28
|
+
const { flags } = await this.parse(Clean);
|
|
29
|
+
let targets = [];
|
|
30
|
+
// 根据标志确定清理目标
|
|
31
|
+
if (flags.all) {
|
|
32
|
+
targets = ['cache', 'images', 'networks'];
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
if (flags.cache)
|
|
36
|
+
targets.push('cache');
|
|
37
|
+
if (flags.images)
|
|
38
|
+
targets.push('images');
|
|
39
|
+
}
|
|
40
|
+
// 如果未提供标志,进行交互式选择
|
|
41
|
+
if (targets.length === 0) {
|
|
42
|
+
targets = await checkbox({
|
|
43
|
+
message: '请选择要清理的项目:',
|
|
44
|
+
choices: [
|
|
45
|
+
{ name: '构建缓存 (Build Cache)', value: 'cache' },
|
|
46
|
+
{ name: '无用镜像 (Dangling Images)', value: 'images' },
|
|
47
|
+
{ name: '无用网络 (Unused Networks)', value: 'networks' },
|
|
48
|
+
],
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (targets.length === 0) {
|
|
52
|
+
logger.info('未选择任何清理项目。');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
// 确认提示
|
|
56
|
+
if (!flags.force) {
|
|
57
|
+
logger.warning(`即将清理: ${targets.join(', ')}`);
|
|
58
|
+
const confirmed = await confirm({
|
|
59
|
+
message: '确认继续?',
|
|
60
|
+
default: false
|
|
61
|
+
});
|
|
62
|
+
if (!confirmed) {
|
|
63
|
+
logger.info('操作已取消。');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// 执行清理
|
|
68
|
+
try {
|
|
69
|
+
if (targets.includes('cache')) {
|
|
70
|
+
logger.info('正在清理构建缓存...');
|
|
71
|
+
await execa('docker', ['builder', 'prune', '-f'], { stdio: 'inherit' });
|
|
72
|
+
logger.success('构建缓存已清理');
|
|
73
|
+
}
|
|
74
|
+
if (targets.includes('images')) {
|
|
75
|
+
logger.info('正在清理无用镜像...');
|
|
76
|
+
await execa('docker', ['image', 'prune', '-f'], { stdio: 'inherit' });
|
|
77
|
+
logger.success('无用镜像已清理');
|
|
78
|
+
}
|
|
79
|
+
if (targets.includes('networks')) {
|
|
80
|
+
logger.info('正在清理无用网络...');
|
|
81
|
+
await execa('docker', ['network', 'prune', '-f'], { stdio: 'inherit' });
|
|
82
|
+
logger.success('无用网络已清理');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
logger.error('清理过程中发生错误');
|
|
87
|
+
if (error instanceof Error) {
|
|
88
|
+
logger.error(error.message);
|
|
89
|
+
}
|
|
90
|
+
this.exit(1);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class DbBackup extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static flags: {
|
|
5
|
+
env: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
|
+
output: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
};
|
|
8
|
+
run(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import { getComposeFiles } from '../../utils/docker.js';
|
|
3
|
+
import { logger } from '../../utils/logger.js';
|
|
4
|
+
import { execa } from 'execa';
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import dotenv from 'dotenv';
|
|
8
|
+
import { EnvFlag, selectEnvironment } from '../../utils/selection.js';
|
|
9
|
+
export default class DbBackup extends Command {
|
|
10
|
+
static description = '备份数据库 (PostgreSQL)';
|
|
11
|
+
static flags = {
|
|
12
|
+
env: EnvFlag,
|
|
13
|
+
output: Flags.string({
|
|
14
|
+
char: 'o',
|
|
15
|
+
description: '输出文件路径',
|
|
16
|
+
}),
|
|
17
|
+
};
|
|
18
|
+
async run() {
|
|
19
|
+
const { flags } = await this.parse(DbBackup);
|
|
20
|
+
// 1. 选择环境
|
|
21
|
+
const env = await selectEnvironment(flags.env);
|
|
22
|
+
// 2. 确定输出文件
|
|
23
|
+
let outputFile = flags.output;
|
|
24
|
+
if (!outputFile) {
|
|
25
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
26
|
+
outputFile = `backup_${timestamp}.sql`;
|
|
27
|
+
}
|
|
28
|
+
// 确保绝对路径
|
|
29
|
+
const outputPath = path.resolve(process.cwd(), outputFile);
|
|
30
|
+
// 3. 获取 Compose 文件
|
|
31
|
+
const { files, isBuiltIn } = await getComposeFiles(env);
|
|
32
|
+
logger.info(`正在备份数据库到: ${outputPath}`);
|
|
33
|
+
logger.info('环境: ' + env);
|
|
34
|
+
// 加载环境配置
|
|
35
|
+
const envConfig = dotenv.config().parsed || {};
|
|
36
|
+
const dbUser = envConfig.POSTGRES_USER || 'postgres';
|
|
37
|
+
const dbName = envConfig.POSTGRES_DB || 'nodebbs';
|
|
38
|
+
const dbPassword = envConfig.POSTGRES_PASSWORD;
|
|
39
|
+
// 4. 构建 Compose 参数
|
|
40
|
+
const composeArgs = files.flatMap(f => ['-f', f]);
|
|
41
|
+
if (isBuiltIn) {
|
|
42
|
+
composeArgs.push('--project-directory', process.cwd());
|
|
43
|
+
}
|
|
44
|
+
// 5. 运行 pg_dump
|
|
45
|
+
const dumpArgs = [...composeArgs, 'exec', '-T'];
|
|
46
|
+
// 如果有密码则注入
|
|
47
|
+
if (dbPassword) {
|
|
48
|
+
dumpArgs.push('-e', `PGPASSWORD=${dbPassword}`);
|
|
49
|
+
}
|
|
50
|
+
dumpArgs.push('postgres', 'pg_dump', '-U', dbUser, dbName);
|
|
51
|
+
try {
|
|
52
|
+
const subprocess = execa('docker', ['compose', ...dumpArgs]);
|
|
53
|
+
if (subprocess.stdout) {
|
|
54
|
+
subprocess.stdout.pipe(fs.createWriteStream(outputPath));
|
|
55
|
+
}
|
|
56
|
+
await subprocess;
|
|
57
|
+
// 检查文件大小
|
|
58
|
+
const stats = fs.statSync(outputPath);
|
|
59
|
+
if (stats.size === 0) {
|
|
60
|
+
logger.error('备份文件为空,备份可能失败。');
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
logger.success(`数据库备份成功!文件大小: ${(stats.size / 1024 / 1024).toFixed(2)} MB`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
logger.error('数据库备份失败');
|
|
68
|
+
if (error instanceof Error) {
|
|
69
|
+
logger.error(error.message);
|
|
70
|
+
}
|
|
71
|
+
// 如果失败则清理空文件
|
|
72
|
+
if (fs.existsSync(outputPath) && fs.statSync(outputPath).size === 0) {
|
|
73
|
+
fs.unlinkSync(outputPath);
|
|
74
|
+
}
|
|
75
|
+
this.exit(1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
export default class DbGenerate extends Command {
|
|
3
3
|
static description: string;
|
|
4
|
+
static flags: {
|
|
5
|
+
env: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
|
+
};
|
|
4
7
|
run(): Promise<void>;
|
|
5
8
|
}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
import { execCompose, getComposeFiles } from '../../utils/docker.js';
|
|
3
3
|
import { logger } from '../../utils/logger.js';
|
|
4
|
+
import { EnvFlag, selectEnvironment } from '../../utils/selection.js';
|
|
4
5
|
export default class DbGenerate extends Command {
|
|
5
|
-
static description = '
|
|
6
|
+
static description = '生成 Prisma Client (db:generate)';
|
|
7
|
+
static flags = {
|
|
8
|
+
env: EnvFlag,
|
|
9
|
+
};
|
|
6
10
|
async run() {
|
|
7
|
-
const {
|
|
8
|
-
|
|
11
|
+
const { flags } = await this.parse(DbGenerate);
|
|
12
|
+
const env = await selectEnvironment(flags.env);
|
|
13
|
+
const { files, isBuiltIn } = await getComposeFiles(env);
|
|
14
|
+
logger.info('正在生成 Prisma Client...');
|
|
9
15
|
await execCompose(files, 'api', ['npm', 'run', 'db:generate'], isBuiltIn);
|
|
10
16
|
logger.success('数据库迁移文件生成完成');
|
|
11
17
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
export default class DbMigrate extends Command {
|
|
3
3
|
static description: string;
|
|
4
|
+
static flags: {
|
|
5
|
+
env: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
|
+
};
|
|
4
7
|
run(): Promise<void>;
|
|
5
8
|
}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
import { execCompose, getComposeFiles } from '../../utils/docker.js';
|
|
3
3
|
import { logger } from '../../utils/logger.js';
|
|
4
|
+
import { EnvFlag, selectEnvironment } from '../../utils/selection.js';
|
|
4
5
|
export default class DbMigrate extends Command {
|
|
5
|
-
static description = '执行数据库迁移';
|
|
6
|
+
static description = '执行数据库迁移 (db:migrate)';
|
|
7
|
+
static flags = {
|
|
8
|
+
env: EnvFlag,
|
|
9
|
+
};
|
|
6
10
|
async run() {
|
|
7
|
-
const {
|
|
11
|
+
const { flags } = await this.parse(DbMigrate);
|
|
12
|
+
const env = await selectEnvironment(flags.env);
|
|
13
|
+
const { files, isBuiltIn } = await getComposeFiles(env);
|
|
8
14
|
logger.info('正在执行数据库迁移...');
|
|
9
15
|
await execCompose(files, 'api', ['npm', 'run', 'db:migrate'], isBuiltIn);
|
|
10
16
|
logger.success('数据库迁移完成');
|