nodebbs 0.1.0 → 0.2.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
@@ -15,7 +15,7 @@ NodeBBS CLI 是一个专为全栈开发者设计的命令行工具,用于简
15
15
  - 🎯 **全栈友好** - 命令设计贴近开发者思维
16
16
  - 📊 **实时日志** - 方便查看各服务日志
17
17
  - 💾 **数据库管理** - 内置数据库迁移和管理工具
18
- - 🌐 **内置模板** - 无需本地配置文件即可通过远程镜像快速部署
18
+ - 🌐 **内置模板** - 无需本地配置文件即可使用
19
19
 
20
20
  ## 📦 安装
21
21
 
@@ -46,7 +46,7 @@ npx nodebbs [command]
46
46
  npx nodebbs
47
47
  ```
48
48
  - **start**: 开始部署(首次使用推荐选择此项)
49
- - **rebuild**: 更新并重启(Pull & Restart,代码/镜像更新后使用)
49
+ - **rebuild**: 重新构建并启动(跳过检查,通常在代码更新后选择此项)
50
50
 
51
51
  ## 📚 命令参考
52
52
 
@@ -60,25 +60,21 @@ npx nodebbs start
60
60
  # 指定环境启动
61
61
  npx nodebbs start -e production
62
62
 
63
+ # 重新构建并启动(跳过检查)
64
+ npx nodebbs start --build
65
+ ```
66
+
63
67
  **参数**:
64
- - `-e, --env` - 部署环境(production, lowmem)
65
- - `-t, --tag` - 镜像版本 tag (默认: latest)
68
+ - `-e, --env` - 部署环境(production, lowmem, basic
69
+ - `-b, --build` - 重新构建镜像并启动(跳过健康检查和初始化)
66
70
 
67
71
  启动流程包含:
68
72
  - Docker 环境检查
69
73
  - 环境变量验证
70
- - 拉取最新镜像
74
+ - 镜像构建
71
75
  - 服务启动
72
- - 健康检查(默认开启)
73
- - 数据库初始化(默认开启)
74
-
75
- #### `nodebbs rebuild`
76
- 拉取最新镜像并重启服务 (Update & Restart)
77
-
78
- ```bash
79
- npx nodebbs rebuild
80
- ```
81
- > 此命令是 `start` 的别名,用于快速更新部署。
76
+ - 健康检查(默认开启,使用 `--build` 跳过)
77
+ - 数据库初始化(默认开启,使用 `--build` 跳过)
82
78
 
83
79
  #### `nodebbs restart`
84
80
  重启所有服务(不更新镜像,支持环境选择)
@@ -276,8 +272,9 @@ vi .env
276
272
 
277
273
  ### 支持的环境
278
274
 
275
+ - **basic** - 基础环境(仅用于测试)
279
276
  - **lowmem** - 低配环境(1C1G/1C2G)
280
- - **production** - 生产环境(标准配置,推荐)
277
+ - **production** - 生产环境(2C4G+,推荐)
281
278
 
282
279
  ### 环境变量
283
280
 
@@ -312,7 +309,7 @@ CLI 会自动记住您上次启动的环境:
312
309
  2. 后续运行 `nodebbs logs`, `nodebbs status` 等命令时,会自动使用该环境,无需再次指定 `-e production`。
313
310
  3. 运行 `nodebbs stop` 成功停止服务后,会自动删除 `.nodebbs-env` 文件。
314
311
 
315
- **注意**:如果您需要临时操作其他环境,仍然可以使用 `-e` 参数强制指定,例如 `nodebbs logs -e lowmem`。
312
+ **注意**:如果您需要临时操作其他环境,仍然可以使用 `-e` 参数强制指定,例如 `nodebbs logs -e basic`。
316
313
 
317
314
  ## 🛠️ 高级用法
318
315
 
@@ -333,8 +330,9 @@ CLI 会自动:
333
330
  ### 自定义配置
334
331
 
335
332
  如果需要自定义配置,在项目根目录创建:
336
- - `docker-compose.yml` - 基础/生产环境配置
337
- - `docker-compose.lowmem.yml` - 低配环境配置 (Override)
333
+ - `docker-compose.yml` - 基础配置
334
+ - `docker-compose.prod.yml` - 生产环境配置
335
+ - `docker-compose.lowmem.yml` - 低配环境配置
338
336
 
339
337
  CLI 会优先使用本地配置文件。
340
338
 
@@ -74,6 +74,9 @@ export default class Pack extends Command {
74
74
  return line;
75
75
  });
76
76
  fs.writeFileSync(path.join(tmpDir, 'docker-compose.yml'), cleanedLines.join('\n'));
77
+ if (fs.existsSync('docker-compose.prod.yml')) {
78
+ fs.copyFileSync('docker-compose.prod.yml', path.join(tmpDir, 'docker-compose.prod.yml'));
79
+ }
77
80
  if (fs.existsSync('.env')) {
78
81
  this.warn('检测到 .env 文件,出于安全考虑,不会默认打包 .env 文件。请在部署时手动配置环境变量。');
79
82
  }
@@ -114,9 +117,7 @@ services:
114
117
  image: nodebbs-web:latest
115
118
  EOF
116
119
 
117
- COMPOSE_FILES="-f docker-compose.yml -f docker-compose.override.yml"
118
-
119
- docker compose \$COMPOSE_FILES up -d
120
+ docker compose -f docker-compose.yml -f docker-compose.prod.yml -f docker-compose.override.yml up -d
120
121
 
121
122
  echo "部署完成!"
122
123
  `;
@@ -1,11 +1,11 @@
1
1
  import { Command } from '@oclif/core';
2
2
  import Start from '../start/index.js';
3
3
  export default class Rebuild extends Command {
4
- static description = '拉取最新镜像并重启服务 (Update & Restart)';
4
+ static description = '重新构建并启动服务 (start --build)';
5
5
  // Allow passing flags like -e to the underlying start command
6
6
  static strict = false;
7
7
  async run() {
8
- // Invoke Start command to pull latest images and restart
9
- await Start.run(this.argv);
8
+ // Invoke Start command with --build flag and any other arguments
9
+ await Start.run(['--build', ...this.argv]);
10
10
  }
11
11
  }
@@ -3,7 +3,7 @@ export default class Start 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
- tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
+ build: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
7
  };
8
8
  run(): Promise<void>;
9
9
  }
@@ -4,20 +4,25 @@ import { checkDocker, runCompose, waitForHealth, execCompose, getComposeFiles }
4
4
  import { initEnv, checkEnv } from '../../utils/env.js';
5
5
  import { logger } from '../../utils/logger.js';
6
6
  import dotenv from 'dotenv';
7
- import { input } from '@inquirer/prompts';
8
7
  import { EnvFlag, selectEnvironment } from '../../utils/selection.js';
9
8
  export default class Start extends Command {
10
9
  static description = '开始部署';
11
10
  static flags = {
12
11
  env: EnvFlag,
13
- tag: Flags.string({
14
- char: 't',
15
- description: 'Image version tag (e.g. latest, v0.1.0)',
12
+ build: Flags.boolean({
13
+ char: 'b',
14
+ description: '重新构建并启动服务 (跳过健康检查和数据初始化)',
15
+ default: false,
16
16
  }),
17
17
  };
18
18
  async run() {
19
19
  const { flags } = await this.parse(Start);
20
- logger.header('NodeBBS Docker 部署');
20
+ if (flags.build) {
21
+ logger.header('NodeBBS 重新构建启动');
22
+ }
23
+ else {
24
+ logger.header('NodeBBS Docker 部署');
25
+ }
21
26
  // 1. 选择环境
22
27
  const env = await selectEnvironment(flags.env);
23
28
  // 2. 获取 Compose 文件
@@ -34,83 +39,69 @@ export default class Start extends Command {
34
39
  else if (env === 'lowmem') {
35
40
  logger.success('已选择:低配环境');
36
41
  }
42
+ else {
43
+ logger.success('已选择:基础环境');
44
+ if (!flags.build) {
45
+ logger.warning('注意:无资源限制,不推荐用于生产环境。');
46
+ }
47
+ }
37
48
  // 3. 检查 Docker 和环境变量
38
49
  await checkDocker();
39
50
  // initEnv 保证 .env 存在(或退出)
40
51
  await initEnv();
41
52
  await checkEnv(env);
42
- // 如果不是本地构建,则询问镜像版本
43
- // 先加载环境变量以获取当前的 IMAGE 配置(如果有)
44
- const envConfig = dotenv.config().parsed || {};
45
- let tag = flags.tag;
46
- if (!tag) {
47
- tag = await input({
48
- message: '请选择要部署的镜像版本:',
49
- default: 'latest'
53
+ if (!flags.build) {
54
+ const continueDeploy = await confirm({
55
+ message: '是否继续启动?',
56
+ default: true
50
57
  });
51
- }
52
- // 构造新的镜像名称
53
- // 逻辑:如果 .env 里定义了 API_IMAGE,则解析出 registry/name 部分,然后拼接 tag
54
- // 否则使用默认 registry
55
- const defaultApiImage = 'ghcr.io/aiprojecthub/nodebbs-api';
56
- const defaultWebImage = 'ghcr.io/aiprojecthub/nodebbs-web';
57
- let currentApiImage = envConfig.API_IMAGE || defaultApiImage;
58
- let currentWebImage = envConfig.WEB_IMAGE || defaultWebImage;
59
- const replaceTag = (image, newTag) => {
60
- const parts = image.split(':');
61
- if (parts.length > 1 && !parts[parts.length - 1].includes('/')) {
62
- // 最后一个部分不包含 '/',认为是 tag
63
- parts.pop();
58
+ if (!continueDeploy) {
59
+ logger.info('操作已取消。');
60
+ this.exit(0);
64
61
  }
65
- return `${parts.join(':')}:${newTag}`;
66
- };
67
- process.env.API_IMAGE = replaceTag(currentApiImage, tag);
68
- process.env.WEB_IMAGE = replaceTag(currentWebImage, tag);
69
- logger.info(`将部署版本: ${tag}`);
70
- logger.info(`API Image: ${process.env.API_IMAGE}`);
71
- logger.info(`Web Image: ${process.env.WEB_IMAGE}`);
72
- const continueDeploy = await confirm({
73
- message: '是否继续启动?',
74
- default: true
75
- });
76
- if (!continueDeploy) {
77
- logger.info('操作已取消。');
78
- this.exit(0);
79
62
  }
80
- // 4. 拉取并启动服务
81
- logger.info('正在拉取最新的 Docker 镜像...');
82
- await runCompose(composeFiles, ['pull'], isBuiltIn);
83
- logger.success('镜像拉取完成');
84
- logger.info('正在启动服务...');
85
- await runCompose(composeFiles, ['up', '-d'], isBuiltIn);
86
- logger.success('服务启动指令已发送');
87
- // 5. 启动后操作
88
- await waitForHealth(composeFiles, isBuiltIn);
89
- const pushDb = await confirm({
90
- message: '是否推送数据库 schema?',
91
- default: false
92
- });
93
- if (pushDb) {
94
- logger.info('正在推送数据库 schema...');
95
- await execCompose(composeFiles, 'api', ['npm', 'run', 'db:push'], isBuiltIn);
96
- logger.success('数据库 schema 推送完成');
63
+ // 4. 构建并启动服务
64
+ if (flags.build) {
65
+ logger.info('正在重新构建并启动服务...');
66
+ await runCompose(composeFiles, ['up', '-d', '--build'], isBuiltIn);
67
+ logger.success('服务已重新构建并启动');
97
68
  }
98
- const seedDb = await confirm({
99
- message: '是否初始化种子数据?',
100
- default: false
101
- });
102
- if (seedDb) {
103
- logger.info('正在初始化数据...');
104
- await execCompose(composeFiles, 'api', ['npm', 'run', 'seed'], isBuiltIn);
105
- logger.success('数据初始化完成');
69
+ else {
70
+ logger.info('正在构建 Docker 镜像...');
71
+ await runCompose(composeFiles, ['build', '--no-cache'], isBuiltIn);
72
+ logger.success('镜像构建完成');
73
+ logger.info('正在启动服务...');
74
+ await runCompose(composeFiles, ['up', '-d'], isBuiltIn);
75
+ logger.success('服务启动指令已发送');
106
76
  }
107
- logger.header('NodeBBS 启动成功!');
77
+ // 5. 启动后操作 (仅完整部署)
78
+ if (!flags.build) {
79
+ await waitForHealth(composeFiles, isBuiltIn);
80
+ const pushDb = await confirm({
81
+ message: '是否推送数据库 schema?',
82
+ default: false
83
+ });
84
+ if (pushDb) {
85
+ logger.info('正在推送数据库 schema...');
86
+ await execCompose(composeFiles, 'api', ['npm', 'run', 'db:push'], isBuiltIn);
87
+ logger.success('数据库 schema 推送完成');
88
+ }
89
+ const seedDb = await confirm({
90
+ message: '是否初始化种子数据?',
91
+ default: false
92
+ });
93
+ if (seedDb) {
94
+ logger.info('正在初始化数据...');
95
+ await execCompose(composeFiles, 'api', ['npm', 'run', 'seed'], isBuiltIn);
96
+ logger.success('数据初始化完成');
97
+ }
98
+ }
99
+ logger.header(flags.build ? '启动成功!' : 'NodeBBS 启动成功!');
108
100
  // 6. 显示信息
109
101
  logger.info(`环境: ${env}`);
110
- // 复用之前的 envConfig 或重新读取 (这里直接复用即可,或者为了保险重新读取但不要 redeclare)
111
- const finalEnvConfig = dotenv.config().parsed || {};
112
- const webPort = finalEnvConfig.WEB_PORT || '3100';
113
- const apiPort = finalEnvConfig.API_PORT || '7100';
102
+ const envConfig = dotenv.config().parsed || {};
103
+ const webPort = envConfig.WEB_PORT || '3100';
104
+ const apiPort = envConfig.API_PORT || '7100';
114
105
  console.log(` Web 前端: http://localhost:${webPort}`);
115
106
  console.log(` API 服务: http://localhost:${apiPort}`);
116
107
  console.log(` API 文档: http://localhost:${apiPort}/docs`);
@@ -1,6 +1,19 @@
1
+ # 低内存环境 Docker Compose 覆盖配置
2
+ # 适用于 1C1G 或 1C2G 的低配服务器
3
+ # 使用方式: docker compose -f docker-compose.yml -f docker-compose.lowmem.yml up -d
4
+
1
5
  services:
2
6
  # PostgreSQL 数据库 - 低内存优化
3
7
  postgres:
8
+ restart: always
9
+ ports: [] # 不暴露端口到主机,只在内部网络访问
10
+ environment:
11
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
12
+ healthcheck:
13
+ interval: 30s
14
+ timeout: 10s
15
+ retries: 5
16
+ start_period: 40s
4
17
  deploy:
5
18
  resources:
6
19
  limits:
@@ -10,13 +23,14 @@ services:
10
23
  cpus: '0.1'
11
24
  memory: 128M
12
25
  logging:
26
+ driver: "json-file"
13
27
  options:
14
28
  max-size: "5m"
15
29
  max-file: "2"
16
30
 
17
31
  # Redis 缓存 - 低内存优化
18
32
  redis:
19
- # 降低内存限制
33
+ restart: always
20
34
  command: >
21
35
  redis-server
22
36
  --requirepass ${REDIS_PASSWORD}
@@ -26,6 +40,12 @@ services:
26
40
  --maxmemory-policy allkeys-lru
27
41
  --save 900 1
28
42
  --save 300 10
43
+ ports: [] # 不暴露端口到主机
44
+ healthcheck:
45
+ interval: 30s
46
+ timeout: 10s
47
+ retries: 5
48
+ start_period: 20s
29
49
  deploy:
30
50
  resources:
31
51
  limits:
@@ -35,15 +55,23 @@ services:
35
55
  cpus: '0.1'
36
56
  memory: 64M
37
57
  logging:
58
+ driver: "json-file"
38
59
  options:
39
60
  max-size: "5m"
40
61
  max-file: "2"
41
62
 
42
63
  # API 服务 - 低内存优化
43
64
  api:
65
+ restart: always
44
66
  environment:
45
- # 降低 Node.js 内存限制
67
+ USER_CACHE_TTL: ${USER_CACHE_TTL:-300}
68
+ JWT_SECRET: ${JWT_SECRET}
69
+ CORS_ORIGIN: ${CORS_ORIGIN}
70
+ APP_URL: ${APP_URL}
71
+ # Node.js 内存限制
46
72
  NODE_OPTIONS: "--max-old-space-size=384"
73
+ volumes:
74
+ - api_uploads:/app/apps/api/uploads
47
75
  healthcheck:
48
76
  start_period: 90s # 低内存环境启动更慢
49
77
  interval: 60s
@@ -56,13 +84,22 @@ services:
56
84
  cpus: '0.2'
57
85
  memory: 256M
58
86
  logging:
87
+ driver: "json-file"
59
88
  options:
60
89
  max-size: "10m"
90
+ max-file: "3"
61
91
 
62
92
  # Web 前端服务 - 低内存优化
63
93
  web:
94
+ restart: always
95
+ build:
96
+ args:
97
+ NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL}
98
+ NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL}
64
99
  environment:
65
- # 降低 Node.js 内存限制
100
+ NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL}
101
+ NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL}
102
+ # Node.js 内存限制
66
103
  NODE_OPTIONS: "--max-old-space-size=384"
67
104
  healthcheck:
68
105
  start_period: 90s # 低内存环境启动更慢
@@ -76,5 +113,14 @@ services:
76
113
  cpus: '0.2'
77
114
  memory: 256M
78
115
  logging:
116
+ driver: "json-file"
79
117
  options:
80
118
  max-size: "10m"
119
+ max-file: "3"
120
+
121
+ # 网络配置
122
+ networks:
123
+ nodebbs-network:
124
+ ipam:
125
+ config:
126
+ - subnet: 172.28.0.0/16
@@ -0,0 +1,120 @@
1
+ # 生产环境 Docker Compose 覆盖配置
2
+ # 使用方式: docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
3
+ # 本文件只包含与 docker-compose.yml 的差异部分
4
+
5
+ services:
6
+ # PostgreSQL 数据库 - 生产环境优化
7
+ postgres:
8
+ restart: always
9
+ ports: [] # 生产环境不暴露端口到主机,只在内部网络访问
10
+ environment:
11
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} # 生产环境必须设置强密码
12
+ healthcheck:
13
+ interval: 30s
14
+ timeout: 10s
15
+ retries: 5
16
+ start_period: 40s
17
+ deploy:
18
+ resources:
19
+ limits:
20
+ cpus: '1'
21
+ memory: 512M
22
+ reservations:
23
+ cpus: '0.25'
24
+ memory: 256M
25
+ logging:
26
+ driver: "json-file"
27
+ options:
28
+ max-size: "10m"
29
+ max-file: "3"
30
+
31
+ # Redis 缓存 - 生产环境优化
32
+ redis:
33
+ restart: always
34
+ command: >
35
+ redis-server
36
+ --requirepass ${REDIS_PASSWORD}
37
+ --appendonly yes
38
+ --appendfsync everysec
39
+ --maxmemory 256mb
40
+ --maxmemory-policy allkeys-lru
41
+ ports: [] # 生产环境不暴露端口到主机
42
+ healthcheck:
43
+ interval: 30s
44
+ timeout: 10s
45
+ retries: 5
46
+ start_period: 20s
47
+ deploy:
48
+ resources:
49
+ limits:
50
+ cpus: '0.5'
51
+ memory: 256M
52
+ reservations:
53
+ cpus: '0.1'
54
+ memory: 128M
55
+ logging:
56
+ driver: "json-file"
57
+ options:
58
+ max-size: "10m"
59
+ max-file: "3"
60
+
61
+ # API 服务 - 生产环境优化
62
+ api:
63
+ restart: always
64
+ environment:
65
+ USER_CACHE_TTL: ${USER_CACHE_TTL:-300} # 生产环境缓存时间更长
66
+ JWT_SECRET: ${JWT_SECRET} # 生产环境必须设置
67
+ CORS_ORIGIN: ${CORS_ORIGIN} # 生产环境必须明确设置
68
+ APP_URL: ${APP_URL} # 生产环境实际域名
69
+ NODE_OPTIONS: "--max-old-space-size=512" # 限制 Node.js 内存使用
70
+ volumes:
71
+ - api_uploads:/app/apps/api/uploads # 生产环境不挂载源代码
72
+ healthcheck:
73
+ start_period: 60s
74
+ deploy:
75
+ resources:
76
+ limits:
77
+ cpus: '1'
78
+ memory: 768M
79
+ reservations:
80
+ cpus: '0.3'
81
+ memory: 384M
82
+ logging:
83
+ driver: "json-file"
84
+ options:
85
+ max-size: "20m"
86
+ max-file: "5"
87
+
88
+ # Web 前端服务 - 生产环境优化
89
+ web:
90
+ restart: always
91
+ build:
92
+ args:
93
+ NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL}
94
+ NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL}
95
+ environment:
96
+ NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL}
97
+ NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL}
98
+ NODE_OPTIONS: "--max-old-space-size=512" # 限制 Node.js 内存使用
99
+ healthcheck:
100
+ start_period: 60s
101
+ deploy:
102
+ resources:
103
+ limits:
104
+ cpus: '1'
105
+ memory: 768M
106
+ reservations:
107
+ cpus: '0.3'
108
+ memory: 384M
109
+ logging:
110
+ driver: "json-file"
111
+ options:
112
+ max-size: "20m"
113
+ max-file: "5"
114
+
115
+ # 网络 - 生产环境使用固定子网
116
+ networks:
117
+ nodebbs-network:
118
+ ipam:
119
+ config:
120
+ - subnet: 172.28.0.0/16