nodebbs 0.1.0 → 0.3.0-beta.0

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 CHANGED
@@ -7,15 +7,17 @@
7
7
 
8
8
  ## 📖 简介
9
9
 
10
- NodeBBS CLI 是一个专为全栈开发者设计的命令行工具,用于简化 NodeBBS 项目的开发、部署和管理流程。它提供了直观的命令接口,让你可以轻松管理 Docker 容器、查看日志、操作数据库等。
10
+ NodeBBS CLI 是一个专为全栈开发者设计的命令行工具,用于简化 NodeBBS 项目的开发、部署和管理流程。它能够自动识别 **源码项目** 和 **纯净镜像部署** 两种场景,并提供开箱即用的 Docker 容器管理能力。
11
11
 
12
12
  ### 特性
13
13
 
14
- - �️ **交互式菜单** - 支持键盘导航的可视化命令选择
14
+ - **交互式菜单** - 支持键盘导航的可视化命令选择
15
+ - 🧠 **智能识别** - 自动检测源码模式或镜像模式
16
+ - 🔄 **平滑升级** - 一个命令完成更新(自动选择重建或拉取镜像)
15
17
  - 🎯 **全栈友好** - 命令设计贴近开发者思维
16
18
  - 📊 **实时日志** - 方便查看各服务日志
17
19
  - 💾 **数据库管理** - 内置数据库迁移和管理工具
18
- - 🌐 **内置模板** - 无需本地配置文件即可通过远程镜像快速部署
20
+ - 🌐 **内置模板** - 无需本地配置文件即可使用
19
21
 
20
22
  ## 📦 安装
21
23
 
@@ -45,210 +47,63 @@ npx nodebbs [command]
45
47
  ```bash
46
48
  npx nodebbs
47
49
  ```
48
- - **start**: 开始部署(首次使用推荐选择此项)
49
- - **rebuild**: 更新并重启(Pull & Restart,代码/镜像更新后使用)
50
-
51
- ## 📚 命令参考
52
-
53
- #### `nodebbs start`
54
- 开始部署
55
-
56
- ```bash
57
- # 交互式启动
58
- npx nodebbs start
59
-
60
- # 指定环境启动
61
- npx nodebbs start -e production
62
-
63
- **参数**:
64
- - `-e, --env` - 部署环境(production, lowmem)
65
- - `-t, --tag` - 镜像版本 tag (默认: latest)
66
-
67
- 启动流程包含:
68
- - Docker 环境检查
69
- - 环境变量验证
70
- - 拉取最新镜像
71
- - 服务启动
72
- - 健康检查(默认开启)
73
- - 数据库初始化(默认开启)
74
-
75
- #### `nodebbs rebuild`
76
- 拉取最新镜像并重启服务 (Update & Restart)
77
-
78
- ```bash
79
- npx nodebbs rebuild
80
- ```
81
- > 此命令是 `start` 的别名,用于快速更新部署。
82
-
83
- #### `nodebbs restart`
84
- 重启所有服务(不更新镜像,支持环境选择)
85
-
86
- ```bash
87
- npx nodebbs restart
88
- npx nodebbs restart -e production
89
- ```
90
-
91
- #### `nodebbs stop`
92
- 停止服务(支持环境选择)
93
-
94
- ```bash
95
- npx nodebbs stop
96
- npx nodebbs stop -e production
97
-
98
- # 停止服务并删除数据卷(危险!)
99
- npx nodebbs stop --volumes
100
- npx nodebbs stop -v
101
- ```
102
-
103
- **参数**:
104
- - `-v, --volumes` - 同时删除数据卷
105
-
106
- #### `nodebbs status`
107
- 查看服务状态
108
-
109
- ```bash
110
- npx nodebbs status
111
- # 查看指定环境状态
112
- npx nodebbs status -e production
113
- ```
114
-
115
- ### 日志管理
116
-
117
- #### `nodebbs logs`
118
- 查看服务日志
119
-
120
- ```bash
121
- # 查看所有日志
122
- npx nodebbs logs
123
- # 查看指定环境日志
124
- npx nodebbs logs -e production
125
- ```
126
-
127
- #### `nodebbs logs:api`
128
- 查看 API 服务日志
129
-
130
- ```bash
131
- npx nodebbs logs:api
132
- ```
133
50
 
134
- #### `nodebbs logs:web`
135
- 查看 Web 服务日志
136
-
137
- ```bash
138
- npx nodebbs logs:web
139
- ```
140
-
141
- #### `nodebbs logs:db`
142
- 查看数据库日志
143
-
144
- ```bash
145
- npx nodebbs logs:db
146
- ```
147
-
148
- #### `nodebbs logs:redis`
149
- 查看 Redis 日志
150
-
151
- ```bash
152
- npx nodebbs logs:redis
153
- ```
154
-
155
- ### 离线部署
156
-
157
- #### `nodebbs pack`
158
- 生成离线部署包(包含镜像、配置文件和安装脚本)
159
-
160
- ```bash
161
- npx nodebbs pack
162
- npx nodebbs pack -o my-deployment.tar.gz
163
- ```
164
-
165
- **参数**:
166
- - `-o, --output` - 输出文件名 (默认: nodebbs-offline.tar.gz)
167
-
168
-
169
- ### Shell 访问
170
-
171
- #### `nodebbs shell`
172
- 进入容器 Shell (支持 api, web, db, redis)
173
-
174
- ```bash
175
- # 进入 API 容器
176
- npx nodebbs shell:api
177
- # 进入生产环境 API 容器
178
- npx nodebbs shell:api -e production
179
- ```
180
-
181
- #### `nodebbs shell:web`
182
- 进入 Web 容器
183
-
184
- ```bash
185
- npx nodebbs shell:web
186
- ```
187
-
188
- #### `nodebbs shell:db`
189
- 进入数据库(自动启动 psql)
190
-
191
- ```bash
192
- npx nodebbs shell:db
193
- ```
194
-
195
- #### `nodebbs shell:redis`
196
- 进入 Redis(自动启动 redis-cli)
197
-
198
- ```bash
199
- npx nodebbs shell:redis
200
- ```
201
-
202
- ### 数据库管理
203
-
204
- #### `nodebbs db:push`
205
- 推送数据库 schema
206
-
207
- ```bash
208
- npx nodebbs db:push
209
- ```
210
-
211
- #### `nodebbs db:seed`
212
- 初始化数据库数据
213
-
214
- ```bash
215
- npx nodebbs db:seed
216
- ```
217
-
218
- #### `nodebbs db:reset`
219
- 重置并重新初始化数据(危险!)
220
-
221
- ```bash
222
- npx nodebbs db:reset
223
- ```
224
-
225
- #### `nodebbs db:backup`
226
- 备份数据库
227
-
228
- ```bash
229
- npx nodebbs db:backup
230
- npx nodebbs db:backup -o backup.sql
231
- ```
232
-
233
- #### `nodebbs clean`
234
- 清理 Docker 缓存和残留资源
235
-
236
- ```bash
237
- # 交互式选择清理项目
238
- npx nodebbs clean
239
-
240
- # 自动清理所有(构建缓存、无用镜像、网络)
241
- npx nodebbs clean -a
242
-
243
- # 仅清理构建缓存
244
- npx nodebbs clean --cache
245
- ```
246
-
247
- ## 🎯 使用场景
51
+ - **start**: 开始部署(首次使用推荐选择此项)
52
+ - **upgrade**: 升级并重启(自动检测更新策略)
53
+
54
+ ## 📚 部署模式
55
+
56
+ CLI 会自动根据当前目录内容判断部署模式:
57
+
58
+ 1. **源码模式 (Source Mode)**
59
+ - 检测到 `package.json`。
60
+ - 行为:使用本地源码执行 `docker build` 构建镜像。
61
+ - 适用:开发环境、二次开发。
62
+
63
+ 2. **镜像模式 (Image Mode)**
64
+ - 未检测到源码。
65
+ - 行为:直接从远程仓库拉取预构建镜像。
66
+ - 适用:生产环境部署、快速体验。
67
+ - **配置**:首次启动时会提示输入镜像版本(默认为 `latest`),并自动写入 `.env`。
68
+
69
+ ## 🛠️ 常用命令
70
+
71
+ ```bash
72
+ $ nodebbs
73
+ ? 选择命令:
74
+ start 启动服务
75
+ stop 停止服务
76
+ restart 重启服务
77
+ upgrade 升级服务
78
+ status 查看服务状态
79
+ logs 查看服务日志 [+]
80
+ shell 进入容器终端 [+]
81
+ db 数据库操作 (备份, 迁移, 种子数据等) [+]
82
+ pack 生成离线部署包
83
+ clean 清理 Docker 缓存和残留资源
84
+ help 显示帮助信息
85
+ ❌ 退出
86
+ ```
87
+
88
+ #### 命令说明
89
+
90
+ | 命令 | 说明 |
91
+ |------|---------|
92
+ | **start** | 启动服务(根据当前配置环境)|
93
+ | **stop** | 停止所有服务 |
94
+ | **restart** | 重启服务(相当于 `docker compose up --force-recreate`)|
95
+ | **upgrade** | 升级服务(拉取最新 Docker 镜像或重新构建本地镜像)|
96
+ | **status** | 查看所有容器的运行状态和健康检查结果 |
97
+ | **logs** | 查看服务日志(支持选择特定服务 API/Web/DB/Redis)|
98
+ | **shell** | 进入容器终端进行调试(支持选择特定服务)|
99
+ | **db** | 数据库高级操作:<br>• 备份 (backup)<br>• 恢复 (import)<br>• 迁移 (migrate)<br>• 种子数据 (seed) |
100
+ | **pack** | 生成离线部署包(**仅限源码模式**),方便在无网环境部署 |
101
+ | **clean** | 清理工具(删除未使用镜像、容器、卷,释放磁盘空间)|
248
102
 
249
103
  ### 离线服务器部署
250
104
 
251
105
  1. **在开发机打包**:
106
+ > 注意:此命令需要在 **源码模式** 下运行(项目根目录包含 package.json)。
252
107
  ```bash
253
108
  # 生成离线包
254
109
  npx nodebbs pack
@@ -270,129 +125,29 @@ vi .env
270
125
  ./install.sh
271
126
  ```
272
127
 
273
-
274
-
275
128
  ## ⚙️ 环境配置
276
129
 
277
130
  ### 支持的环境
278
131
 
279
- - **lowmem** - 低配环境(1C1G/1C2G)
280
- - **production** - 生产环境(标准配置,推荐)
132
+ - **production** - 生产环境(推荐,启用资源限制与安全配置)
133
+ - **lowmem** - 低配环境(适用于 1C2G 服务器)
134
+ - **basic** - 基础环境(测试用,无资源限制)
281
135
 
282
136
  ### 环境变量
283
137
 
284
- `.env` 文件中配置:
138
+ CLI 首次运行时会引导生成 `.env` 文件。
139
+ 在 **镜像模式** 下,会额外生成:
285
140
 
286
141
  ```env
287
- # 数据库配置
288
- POSTGRES_PASSWORD=your_secure_password
289
- POSTGRES_DB=nodebbs
290
-
291
- # Redis 配置
292
- REDIS_PASSWORD=your_redis_password
293
-
294
- # JWT 配置
295
- JWT_SECRET=your_jwt_secret
296
-
297
- # 应用配置
298
- APP_URL=http://localhost:3100
299
- API_URL=http://localhost:7100
300
-
301
- # CORS 配置
302
- CORS_ORIGIN=*
303
- ```
304
-
305
- > **提示**:可以使用 `openssl rand -hex 32` 命令生成安全的随机密钥。
306
-
307
- ### 环境持久化
308
-
309
- CLI 会自动记住您上次启动的环境:
310
-
311
- 1. 当您运行 `nodebbs start` 并选择环境(如 `production`)后,CLI 会在当前目录创建 `.nodebbs-env` 文件记录该选择。
312
- 2. 后续运行 `nodebbs logs`, `nodebbs status` 等命令时,会自动使用该环境,无需再次指定 `-e production`。
313
- 3. 运行 `nodebbs stop` 成功停止服务后,会自动删除 `.nodebbs-env` 文件。
314
-
315
- **注意**:如果您需要临时操作其他环境,仍然可以使用 `-e` 参数强制指定,例如 `nodebbs logs -e lowmem`。
316
-
317
- ## 🛠️ 高级用法
318
-
319
- ### 内置模板
320
-
321
- NodeBBS CLI 内置了 Docker Compose 配置文件,即使项目中没有这些文件,也可以直接使用:
322
-
323
- ```bash
324
- # 在任何目录运行
325
- npx nodebbs start
326
- ```
327
-
328
- CLI 会自动:
329
- 1. 检查当前目录是否有 `docker-compose.yml`
330
- 2. 如果没有,使用内置模板
331
- 3. 使用当前目录作为项目目录
332
-
333
- ### 自定义配置
334
-
335
- 如果需要自定义配置,在项目根目录创建:
336
- - `docker-compose.yml` - 基础/生产环境配置
337
- - `docker-compose.lowmem.yml` - 低配环境配置 (Override)
338
-
339
- CLI 会优先使用本地配置文件。
340
-
341
- ## 🐛 故障排除
342
-
343
- ### 服务启动失败
344
-
345
- ```bash
346
- # 查看日志
347
- npx nodebbs logs
348
-
349
- # 查看特定服务日志
350
- npx nodebbs logs:api
351
-
352
- # 检查服务状态
353
- npx nodebbs status
354
- ```
355
-
356
- ### 数据库连接问题
357
-
358
- ```bash
359
- # 进入数据库检查
360
- npx nodebbs shell:db
361
-
362
- # 重置数据库
363
- npx nodebbs db:reset
364
- ```
365
-
366
- ### 端口冲突
367
-
368
- 检查以下端口是否被占用:
369
- - 3100 - Web 前端
370
- - 7100 - API 服务
371
- - 5432 - PostgreSQL
372
- - 6379 - Redis
373
-
374
- ### 清理环境
375
-
376
- ```bash
377
- # 停止所有服务
378
- npx nodebbs stop
379
-
380
- # 停止并删除所有数据(危险!)
381
- npx nodebbs stop --volumes
142
+ API_IMAGE=ghcr.io/aiprojecthub/nodebbs-api:latest
143
+ WEB_IMAGE=ghcr.io/aiprojecthub/nodebbs-web:latest
382
144
  ```
383
145
 
146
+ 如需升级至特定版本,修改 `.env` 中的版本号后运行 `nodebbs upgrade` 即可。
384
147
  ## 🔗 相关链接
385
148
 
386
149
  - [NodeBBS 项目](https://github.com/aiprojecthub/nodebbs)
387
150
  - [问题反馈](https://github.com/aiprojecthub/nodebbs/issues)
388
-
389
- ## 💡 提示
390
-
391
- - 使用 `npx nodebbs --help` 查看所有可用命令
392
- - 使用 `npx nodebbs [command] --help` 查看特定命令的帮助
393
- - 所有命令都支持 `--help` 参数
394
- - 建议在生产环境使用前先在测试环境验证
395
-
396
151
  ---
397
152
 
398
153
  Made with ❤️ by NodeBBS Team
@@ -3,8 +3,7 @@ export default class DbImport extends Command {
3
3
  static description: string;
4
4
  static flags: {
5
5
  env: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
- file: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
- yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
6
+ input: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
7
  };
9
8
  run(): Promise<void>;
10
9
  }
@@ -1,5 +1,4 @@
1
1
  import { Command, Flags } from '@oclif/core';
2
- import { confirm } from '@inquirer/prompts';
3
2
  import { getComposeFiles } from '../../utils/docker.js';
4
3
  import { logger } from '../../utils/logger.js';
5
4
  import { execa } from 'execa';
@@ -7,72 +6,101 @@ import fs from 'node:fs';
7
6
  import path from 'node:path';
8
7
  import dotenv from 'dotenv';
9
8
  import { EnvFlag, selectEnvironment } from '../../utils/selection.js';
9
+ import { confirm, select, input } from '@inquirer/prompts';
10
10
  export default class DbImport extends Command {
11
- static description = '导入数据库 (PostgreSQL)';
11
+ static description = '导入数据库 (从 SQL 文件恢复)';
12
12
  static flags = {
13
13
  env: EnvFlag,
14
- file: Flags.string({
15
- char: 'f',
16
- description: 'SQL 备份文件路径',
17
- required: true,
18
- }),
19
- yes: Flags.boolean({
20
- char: 'y',
21
- description: '跳过确认提示',
22
- default: false,
14
+ input: Flags.string({
15
+ char: 'i',
16
+ description: '输入文件路径',
23
17
  }),
24
18
  };
25
19
  async run() {
26
20
  const { flags } = await this.parse(DbImport);
27
21
  // 1. 选择环境
28
22
  const env = await selectEnvironment(flags.env);
29
- // 2. 验证文件
30
- const inputPath = path.resolve(process.cwd(), flags.file);
23
+ // 2. 确定输入文件
24
+ let inputFile = flags.input;
25
+ if (!inputFile) {
26
+ // 尝试自动查找当前目录下的 .sql 文件
27
+ const files = fs.readdirSync(process.cwd())
28
+ .filter(f => f.endsWith('.sql'))
29
+ .sort((a, b) => {
30
+ // 尝试按时间倒序 (虽然文件名可能有时间戳,但用文件修改时间更准)
31
+ const statA = fs.statSync(a);
32
+ const statB = fs.statSync(b);
33
+ return statB.mtime.getTime() - statA.mtime.getTime();
34
+ });
35
+ if (files.length > 0) {
36
+ const choices = files.map(f => ({
37
+ name: `${f} (${(fs.statSync(f).size / 1024 / 1024).toFixed(2)} MB)`,
38
+ value: f
39
+ }));
40
+ choices.push({ name: '手动输入路径', value: '__MANUAL__' });
41
+ const selection = await select({
42
+ message: '请选择导入文件:',
43
+ choices: choices,
44
+ });
45
+ if (selection === '__MANUAL__') {
46
+ inputFile = await input({ message: '请输入文件路径:' });
47
+ }
48
+ else {
49
+ inputFile = selection;
50
+ }
51
+ }
52
+ else {
53
+ inputFile = await input({ message: '未找到 SQL 文件,请输入路径:' });
54
+ }
55
+ }
56
+ // 确保绝对路径并检查是否存在
57
+ const inputPath = path.resolve(process.cwd(), inputFile);
31
58
  if (!fs.existsSync(inputPath)) {
32
- logger.error(`找不到备份文件: ${inputPath}`);
59
+ logger.error(`文件未找到: ${inputPath}`);
33
60
  this.exit(1);
34
61
  }
35
- // 3. 用户确认
36
- if (!flags.yes) {
37
- logger.warning('警告:此操作将覆盖当前数据库中的数据!');
38
- const confirmImport = await confirm({
39
- message: `确认从 ${flags.file} 恢复数据库?`,
40
- default: false
41
- });
42
- if (!confirmImport) {
43
- logger.info('操作已取消。');
44
- this.exit(0);
45
- }
62
+ // 3. 危险操作确认
63
+ logger.warning('警告: 此操作将清空当前数据库的所有数据并从备份恢复!');
64
+ logger.warning(`目标文件: ${inputPath}`);
65
+ const isConfirmed = await confirm({
66
+ message: '确认继续?(数据丢失不可撤销)',
67
+ default: false
68
+ });
69
+ if (!isConfirmed) {
70
+ logger.info('操作已取消');
71
+ this.exit(0);
46
72
  }
47
73
  // 4. 获取 Compose 文件
48
74
  const { files, isBuiltIn } = await getComposeFiles(env);
49
- logger.info(`正在从文件恢复数据库: ${inputPath}`);
50
- logger.info('环境: ' + env);
51
- // 加载环境配置
52
- const envConfig = dotenv.config().parsed || {};
53
- const dbUser = envConfig.POSTGRES_USER || 'postgres';
54
- const dbName = envConfig.POSTGRES_DB || 'nodebbs';
55
- const dbPassword = envConfig.POSTGRES_PASSWORD;
56
- // 5. 构建 Compose 参数
57
75
  const composeArgs = files.flatMap(f => ['-f', f]);
58
76
  if (isBuiltIn) {
59
77
  composeArgs.push('--project-directory', process.cwd());
60
78
  }
61
- // 6. 运行 psql
62
- // const dumpArgs = [...composeArgs, 'exec', '-T'] // backup uses exec
63
- // import should also use exec -T to accept stdin
64
- const importArgs = [...composeArgs, 'exec', '-T'];
65
- // 如果有密码则注入
66
- if (dbPassword) {
67
- importArgs.push('-e', `PGPASSWORD=${dbPassword}`);
68
- }
69
- importArgs.push('postgres', 'psql', '-U', dbUser, '-d', dbName);
79
+ // 加载环境配置获取数据库信息 (用于可能的连接参数,虽然 docker exec 默认用 postgres 用户通常足够)
80
+ const envConfig = dotenv.config().parsed || {};
81
+ const dbUser = envConfig.POSTGRES_USER || 'postgres';
82
+ const dbName = envConfig.POSTGRES_DB || 'nodebbs';
83
+ const dbPassword = envConfig.POSTGRES_PASSWORD;
84
+ // 5. 执行恢复
85
+ logger.info('正在准备恢复数据库...');
70
86
  try {
71
- const subprocess = execa('docker', ['compose', ...importArgs], {
87
+ // 步骤 A: 重置 Schema (Drop & Create public)
88
+ // 这样做比单纯导入更干净,因为 pg_dump 默认可能不包含 DROP TABLE
89
+ logger.info('正在重置数据库 Schema...');
90
+ const resetCmd = [...composeArgs, 'exec', '-T', 'postgres', 'psql', '-U', dbUser, '-d', dbName, '-c', 'DROP SCHEMA public CASCADE; CREATE SCHEMA public;'];
91
+ // 如果有密码
92
+ const envs = process.env;
93
+ if (dbPassword)
94
+ envs.PGPASSWORD = dbPassword;
95
+ await execa('docker', ['compose', ...resetCmd], { env: envs });
96
+ logger.success('数据库 Schema 已重置');
97
+ // 步骤 B: 导入数据
98
+ logger.info(`正在导入数据 from ${path.basename(inputPath)}...`);
99
+ const importCmd = [...composeArgs, 'exec', '-T', 'postgres', 'psql', '-U', dbUser, '-d', dbName];
100
+ const subprocess = execa('docker', ['compose', ...importCmd], {
101
+ env: envs,
72
102
  input: fs.createReadStream(inputPath)
73
103
  });
74
- // subprocess.stdout?.pipe(process.stdout)
75
- // subprocess.stderr?.pipe(process.stderr)
76
104
  await subprocess;
77
105
  logger.success('数据库导入成功!');
78
106
  }