nodebbs 0.4.1 → 0.4.2

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.
Files changed (40) hide show
  1. package/README.md +13 -11
  2. package/dist/commands/backup/all.d.ts +1 -1
  3. package/dist/commands/backup/all.js +9 -9
  4. package/dist/commands/backup/db.js +5 -5
  5. package/dist/commands/backup/uploads.js +8 -9
  6. package/dist/commands/clean/index.d.ts +1 -1
  7. package/dist/commands/clean/index.js +12 -12
  8. package/dist/commands/db/reset.js +4 -4
  9. package/dist/commands/import/all.js +10 -15
  10. package/dist/commands/import/db.js +11 -16
  11. package/dist/commands/import/uploads.js +13 -18
  12. package/dist/commands/logs/all.js +2 -2
  13. package/dist/commands/logs/api.js +2 -2
  14. package/dist/commands/logs/db.js +1 -1
  15. package/dist/commands/logs/redis.js +1 -1
  16. package/dist/commands/logs/web.js +1 -1
  17. package/dist/commands/pack/index.js +30 -27
  18. package/dist/commands/restart/index.js +6 -8
  19. package/dist/commands/shell/api.js +1 -1
  20. package/dist/commands/shell/db.js +2 -2
  21. package/dist/commands/shell/redis.js +2 -2
  22. package/dist/commands/shell/web.js +1 -1
  23. package/dist/commands/start/index.d.ts +1 -1
  24. package/dist/commands/start/index.js +12 -22
  25. package/dist/commands/status/index.js +2 -2
  26. package/dist/commands/stop/index.d.ts +1 -1
  27. package/dist/commands/stop/index.js +7 -7
  28. package/dist/commands/upgrade/index.js +5 -5
  29. package/dist/interactive.js +26 -28
  30. package/dist/templates/docker-compose.yml +2 -8
  31. package/dist/templates/env +2 -0
  32. package/dist/utils/docker.js +22 -20
  33. package/dist/utils/env.d.ts +1 -1
  34. package/dist/utils/env.js +40 -42
  35. package/dist/utils/logger.d.ts +2 -2
  36. package/dist/utils/logger.js +8 -8
  37. package/dist/utils/selection.d.ts +2 -2
  38. package/dist/utils/selection.js +7 -10
  39. package/oclif.manifest.json +42 -42
  40. package/package.json +2 -2
package/README.md CHANGED
@@ -71,31 +71,33 @@ CLI 会自动根据当前目录内容判断部署模式:
71
71
  ```bash
72
72
  $ nodebbs
73
73
  ? 选择命令:
74
- ❯ start 启动服务
75
- stop 停止服务
76
- restart 重启服务
77
- upgrade 升级服务
78
- status 查看服务状态
79
- logs 查看服务日志 [+]
74
+ ❯ start 启动
75
+ stop 停止
76
+ restart 重启
77
+ upgrade 升级
78
+ status 查看状态
79
+ logs 查看日志 [+]
80
80
  db 数据库操作 (种子数据, 重置等) [+]
81
81
  backup 备份数据 (数据库, 上传文件等) [+]
82
82
  import 导入/恢复数据 [+]
83
83
  clean 清理 Docker 缓存和残留资源
84
84
  shell 进入容器终端 [+]
85
85
  pack 生成离线部署包
86
- 退出
86
+ ──────────────
87
+ ❯ 返回
88
+ 退出
87
89
  ```
88
90
 
89
91
  #### 命令说明
90
92
 
91
93
  | 命令 | 说明 |
92
94
  |------|---------|
93
- | **start** | 启动服务(根据当前配置环境)|
95
+ | **start** | 启动(根据当前配置环境)|
94
96
  | **stop** | 停止所有服务 |
95
- | **restart** | 重启服务(相当于 `docker compose up --force-recreate`)|
96
- | **upgrade** | 升级服务(拉取最新 Docker 镜像或重新构建本地镜像)|
97
+ | **restart** | 重启(相当于 `docker compose up --force-recreate`)|
98
+ | **upgrade** | 升级(拉取最新 Docker 镜像或重新构建本地镜像)|
97
99
  | **status** | 查看所有容器的运行状态和健康检查结果 |
98
- | **logs** | 查看服务日志(支持选择特定服务 API/Web/DB/Redis)|
100
+ | **logs** | 查看日志(支持选择特定服务 API/Web/DB/Redis)|
99
101
  | **db** | 数据库操作:种子数据 (seed)、重置 (reset) 等 |
100
102
  | **backup** | 备份数据:<br>• 数据库 (db)<br>• 上传文件 (uploads)<br>• 一键全部备份 (all) |
101
103
  | **import** | 恢复数据:<br>• 数据库 (db)<br>• 上传文件 (uploads)<br>• 一键全部恢复 (all) |
@@ -2,8 +2,8 @@ import { Command } from '@oclif/core';
2
2
  export default class BackupAll extends Command {
3
3
  static description: string;
4
4
  static flags: {
5
- output: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
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
7
  };
8
8
  run(): Promise<void>;
9
9
  }
@@ -1,26 +1,26 @@
1
1
  import { Command, Flags } from '@oclif/core';
2
- import { logger } from '../../utils/logger.js';
3
2
  import fs from 'node:fs';
4
3
  import path from 'node:path';
4
+ import { logger } from '../../utils/logger.js';
5
5
  import BackupDb from './db.js';
6
6
  import BackupUploads from './uploads.js';
7
7
  export default class BackupAll extends Command {
8
- static description = '一键备份全部数据 (数据库 + 上传文件)';
8
+ static description = '一键备份全部数据';
9
9
  static flags = {
10
- output: Flags.string({
11
- char: 'o',
12
- description: '输出目录路径',
13
- }),
14
10
  env: Flags.string({
15
11
  char: 'e',
16
12
  description: '运行环境 (production, lowmem, default)',
17
13
  options: ['production', 'lowmem', 'default'],
18
14
  }),
15
+ output: Flags.string({
16
+ char: 'o',
17
+ description: '输出目录路径',
18
+ }),
19
19
  };
20
20
  async run() {
21
21
  const { flags } = await this.parse(BackupAll);
22
22
  // 生成备份目录
23
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
23
+ const timestamp = new Date().toISOString().replaceAll(/[:.]/g, '-').slice(0, 19);
24
24
  const backupDirName = flags.output || `backup_${timestamp}`;
25
25
  const backupDir = path.resolve(process.cwd(), backupDirName);
26
26
  // 创建备份目录
@@ -40,7 +40,7 @@ export default class BackupAll extends Command {
40
40
  }
41
41
  await BackupDb.run(dbArgs);
42
42
  }
43
- catch (error) {
43
+ catch {
44
44
  logger.error('数据库备份失败,终止操作');
45
45
  this.exit(1);
46
46
  }
@@ -49,7 +49,7 @@ export default class BackupAll extends Command {
49
49
  try {
50
50
  await BackupUploads.run(['-o', uploadsBackupFile]);
51
51
  }
52
- catch (error) {
52
+ catch {
53
53
  logger.warning('上传文件备份失败 (可能卷不存在)');
54
54
  // 不终止,因为数据库已备份
55
55
  }
@@ -1,13 +1,13 @@
1
1
  import { Command, Flags } from '@oclif/core';
2
- import { getComposeFiles } from '../../utils/docker.js';
3
- import { logger } from '../../utils/logger.js';
2
+ import dotenv from 'dotenv';
4
3
  import { execa } from 'execa';
5
4
  import fs from 'node:fs';
6
5
  import path from 'node:path';
7
- import dotenv from 'dotenv';
6
+ import { getComposeFiles } from '../../utils/docker.js';
7
+ import { logger } from '../../utils/logger.js';
8
8
  import { EnvFlag, selectEnvironment } from '../../utils/selection.js';
9
9
  export default class BackupDb extends Command {
10
- static description = '备份数据库 (PostgreSQL)';
10
+ static description = '备份数据库';
11
11
  static flags = {
12
12
  env: EnvFlag,
13
13
  output: Flags.string({
@@ -22,7 +22,7 @@ export default class BackupDb extends Command {
22
22
  // 2. 确定输出文件
23
23
  let outputFile = flags.output;
24
24
  if (!outputFile) {
25
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
25
+ const timestamp = new Date().toISOString().replaceAll(/[:.]/g, '-').slice(0, 19);
26
26
  outputFile = `backup_db_${timestamp}.sql`;
27
27
  }
28
28
  // 确保绝对路径
@@ -1,11 +1,11 @@
1
1
  import { Command, Flags } from '@oclif/core';
2
- import { logger } from '../../utils/logger.js';
2
+ import dotenv from 'dotenv';
3
3
  import { execa } from 'execa';
4
4
  import fs from 'node:fs';
5
5
  import path from 'node:path';
6
- import dotenv from 'dotenv';
6
+ import { logger } from '../../utils/logger.js';
7
7
  export default class BackupUploads extends Command {
8
- static description = '备份上传文件 (用户头像、附件等)';
8
+ static description = '备份上传文件';
9
9
  static flags = {
10
10
  output: Flags.string({
11
11
  char: 'o',
@@ -23,16 +23,14 @@ export default class BackupUploads extends Command {
23
23
  // 确定输出文件
24
24
  let outputFile = flags.output;
25
25
  if (!outputFile) {
26
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
26
+ const timestamp = new Date().toISOString().replaceAll(/[:.]/g, '-').slice(0, 19);
27
27
  outputFile = `backup_uploads_${timestamp}.tar.gz`;
28
28
  }
29
29
  // 确保绝对路径
30
30
  const outputPath = path.resolve(process.cwd(), outputFile);
31
31
  const outputDir = path.dirname(outputPath);
32
32
  const outputFileName = path.basename(outputPath);
33
- logger.info(`正在备份上传文件...`);
34
- logger.info(`数据卷: ${volumeName}`);
35
- logger.info(`输出文件: ${outputPath}`);
33
+ logger.info(`正在备份上传文件 → ${outputPath}`);
36
34
  try {
37
35
  // 使用 alpine 容器备份卷内容
38
36
  await execa('docker', [
@@ -101,10 +99,11 @@ export default class BackupUploads extends Command {
101
99
  if (volumes.length === 1) {
102
100
  return volumes[0];
103
101
  }
104
- else if (volumes.length > 1) {
102
+ if (volumes.length > 1) {
105
103
  // 多个匹配,提示用户
106
104
  logger.warning('找到多个可能的上传卷:');
107
- volumes.forEach(v => logger.info(` - ${v}`));
105
+ for (const v of volumes)
106
+ logger.info(` - ${v}`);
108
107
  logger.info('请在 .env 中设置 COMPOSE_PROJECT_NAME 以指定正确的卷');
109
108
  return volumes[0]; // 默认使用第一个
110
109
  }
@@ -4,9 +4,9 @@ export default class Clean extends Command {
4
4
  static flags: {
5
5
  all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
6
6
  cache: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
- images: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
7
  env: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
8
  force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ images: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
10
  };
11
11
  run(): Promise<void>;
12
12
  }
@@ -1,32 +1,32 @@
1
- import { Command, Flags } from '@oclif/core';
2
1
  import { checkbox, confirm } from '@inquirer/prompts';
3
- import { logger } from '../../utils/logger.js';
2
+ import { Command, Flags } from '@oclif/core';
4
3
  import { execa } from 'execa';
4
+ import { logger } from '../../utils/logger.js';
5
5
  import { clearStoredEnv } from '../../utils/selection.js';
6
6
  export default class Clean extends Command {
7
7
  static description = '清理 Docker 缓存和残留资源';
8
8
  static flags = {
9
9
  all: Flags.boolean({
10
10
  char: 'a',
11
- description: '清理所有 (构建缓存、无用镜像、网络、环境锁定)',
12
11
  default: false,
12
+ description: '清理所有 (构建缓存、无用镜像、网络、环境锁定)',
13
13
  }),
14
14
  cache: Flags.boolean({
15
- description: '清理构建缓存',
16
- default: false,
17
- }),
18
- images: Flags.boolean({
19
- description: '清理无用镜像 (dangling)',
20
15
  default: false,
16
+ description: '清理构建缓存',
21
17
  }),
22
18
  env: Flags.boolean({
23
- description: '清理环境锁定 (Environment Lock)',
24
19
  default: false,
20
+ description: '清理环境锁定 (Environment Lock)',
25
21
  }),
26
22
  force: Flags.boolean({
27
23
  char: 'f',
24
+ default: false,
28
25
  description: '跳过确认提示',
26
+ }),
27
+ images: Flags.boolean({
29
28
  default: false,
29
+ description: '清理无用镜像 (dangling)',
30
30
  }),
31
31
  };
32
32
  async run() {
@@ -47,13 +47,13 @@ export default class Clean extends Command {
47
47
  // 如果未提供标志,进行交互式选择
48
48
  if (targets.length === 0) {
49
49
  targets = await checkbox({
50
- message: '请选择要清理的项目:',
51
50
  choices: [
52
51
  { name: '构建缓存 (Build Cache)', value: 'cache' },
53
52
  { name: '无用镜像 (Dangling Images)', value: 'images' },
54
53
  { name: '无用网络 (Unused Networks)', value: 'networks' },
55
54
  { name: '环境锁定 (Environment Lock)', value: 'env' },
56
55
  ],
56
+ message: '请选择要清理的项目:',
57
57
  });
58
58
  }
59
59
  if (targets.length === 0) {
@@ -64,8 +64,8 @@ export default class Clean extends Command {
64
64
  if (!flags.force) {
65
65
  logger.warning(`即将清理: ${targets.join(', ')}`);
66
66
  const confirmed = await confirm({
67
- message: '确认继续?',
68
- default: false
67
+ default: false,
68
+ message: '确认继续?'
69
69
  });
70
70
  if (!confirmed) {
71
71
  logger.info('操作已取消。');
@@ -1,5 +1,5 @@
1
- import { Command } from '@oclif/core';
2
1
  import { confirm } from '@inquirer/prompts';
2
+ import { Command } from '@oclif/core';
3
3
  import { execCompose, getComposeFiles } from '../../utils/docker.js';
4
4
  import { logger } from '../../utils/logger.js';
5
5
  import { EnvFlag, selectEnvironment } from '../../utils/selection.js';
@@ -12,10 +12,10 @@ export default class DbReset extends Command {
12
12
  const { flags } = await this.parse(DbReset);
13
13
  const env = await selectEnvironment(flags.env);
14
14
  const { files, isBuiltIn } = await getComposeFiles(env);
15
- logger.warning('警告:这将清除所有数据!');
15
+ logger.warning('这将清除所有数据!');
16
16
  const confirmReset = await confirm({
17
- message: '确认继续?',
18
- default: false
17
+ default: false,
18
+ message: '确认继续?'
19
19
  });
20
20
  if (!confirmReset) {
21
21
  logger.info('操作已取消。');
@@ -1,12 +1,12 @@
1
+ import { confirm, input, select } from '@inquirer/prompts';
1
2
  import { Command, Flags } from '@oclif/core';
2
- import { logger } from '../../utils/logger.js';
3
3
  import fs from 'node:fs';
4
4
  import path from 'node:path';
5
- import { select, input, confirm } from '@inquirer/prompts';
5
+ import { logger } from '../../utils/logger.js';
6
6
  import ImportDb from './db.js';
7
7
  import ImportUploads from './uploads.js';
8
8
  export default class ImportAll extends Command {
9
- static description = '一键恢复全部数据 (数据库 + 上传文件)';
9
+ static description = '一键恢复全部数据';
10
10
  static flags = {
11
11
  dir: Flags.string({
12
12
  char: 'd',
@@ -34,15 +34,10 @@ export default class ImportAll extends Command {
34
34
  const choices = dirs.map(d => ({ name: d, value: d }));
35
35
  choices.push({ name: '手动输入路径', value: '__MANUAL__' });
36
36
  const selection = await select({
37
+ choices,
37
38
  message: '请选择备份目录:',
38
- choices: choices,
39
39
  });
40
- if (selection === '__MANUAL__') {
41
- backupDir = await input({ message: '请输入备份目录路径:' });
42
- }
43
- else {
44
- backupDir = selection;
45
- }
40
+ backupDir = selection === '__MANUAL__' ? (await input({ message: '请输入备份目录路径:' })) : selection;
46
41
  }
47
42
  else {
48
43
  backupDir = await input({ message: '未找到备份目录,请输入路径:' });
@@ -69,10 +64,10 @@ export default class ImportAll extends Command {
69
64
  if (uploadsFile)
70
65
  logger.info(` - 上传文件: ${uploadsFile}`);
71
66
  // 最终确认
72
- logger.warning('\n警告: 此操作将清空当前所有数据并从备份恢复!');
67
+ logger.warning('此操作将清空当前所有数据并从备份恢复!');
73
68
  const isConfirmed = await confirm({
74
- message: '确认继续完整恢复?(数据丢失不可撤销)',
75
- default: false
69
+ default: false,
70
+ message: '确认继续完整恢复?(数据丢失不可撤销)'
76
71
  });
77
72
  if (!isConfirmed) {
78
73
  logger.info('操作已取消');
@@ -92,7 +87,7 @@ export default class ImportAll extends Command {
92
87
  process.env.NODEBBS_SKIP_CONFIRM = 'true';
93
88
  await ImportDb.run(dbArgs);
94
89
  }
95
- catch (error) {
90
+ catch {
96
91
  logger.error('数据库恢复失败');
97
92
  this.exit(1);
98
93
  }
@@ -110,7 +105,7 @@ export default class ImportAll extends Command {
110
105
  process.env.NODEBBS_SKIP_CONFIRM = 'true';
111
106
  await ImportUploads.run(['-i', path.join(backupPath, uploadsFile)]);
112
107
  }
113
- catch (error) {
108
+ catch {
114
109
  logger.warning('上传文件恢复失败');
115
110
  }
116
111
  finally {
@@ -1,14 +1,14 @@
1
+ import { confirm, input, select } from '@inquirer/prompts';
1
2
  import { Command, Flags } from '@oclif/core';
2
- import { getComposeFiles } from '../../utils/docker.js';
3
- import { logger } from '../../utils/logger.js';
3
+ import dotenv from 'dotenv';
4
4
  import { execa } from 'execa';
5
5
  import fs from 'node:fs';
6
6
  import path from 'node:path';
7
- import dotenv from 'dotenv';
7
+ import { getComposeFiles } from '../../utils/docker.js';
8
+ import { logger } from '../../utils/logger.js';
8
9
  import { EnvFlag, selectEnvironment } from '../../utils/selection.js';
9
- import { confirm, select, input } from '@inquirer/prompts';
10
10
  export default class ImportDb extends Command {
11
- static description = '导入数据库 (从 SQL 文件恢复)';
11
+ static description = '导入数据库';
12
12
  static flags = {
13
13
  env: EnvFlag,
14
14
  input: Flags.string({
@@ -39,15 +39,10 @@ export default class ImportDb extends Command {
39
39
  }));
40
40
  choices.push({ name: '手动输入路径', value: '__MANUAL__' });
41
41
  const selection = await select({
42
+ choices,
42
43
  message: '请选择导入文件:',
43
- choices: choices,
44
44
  });
45
- if (selection === '__MANUAL__') {
46
- inputFile = await input({ message: '请输入文件路径:' });
47
- }
48
- else {
49
- inputFile = selection;
50
- }
45
+ inputFile = selection === '__MANUAL__' ? (await input({ message: '请输入文件路径:' })) : selection;
51
46
  }
52
47
  else {
53
48
  inputFile = await input({ message: '未找到 SQL 文件,请输入路径:' });
@@ -60,11 +55,11 @@ export default class ImportDb extends Command {
60
55
  this.exit(1);
61
56
  }
62
57
  // 3. 危险操作确认
63
- logger.warning('警告: 此操作将清空当前数据库的所有数据并从备份恢复!');
58
+ logger.warning('此操作将清空当前数据库的所有数据并从备份恢复!');
64
59
  logger.warning(`目标文件: ${inputPath}`);
65
60
  const isConfirmed = await confirm({
66
- message: '确认继续?(数据丢失不可撤销)',
67
- default: false
61
+ default: false,
62
+ message: '确认继续?(数据丢失不可撤销)'
68
63
  });
69
64
  if (!isConfirmed) {
70
65
  logger.info('操作已取消');
@@ -94,7 +89,7 @@ export default class ImportDb extends Command {
94
89
  await execa('docker', ['compose', ...resetCmd], { env: envs });
95
90
  logger.success('数据库 Schema 已重置');
96
91
  // 步骤 B: 导入数据
97
- logger.info(`正在导入数据 from ${path.basename(inputPath)}...`);
92
+ logger.info(`正在从 ${path.basename(inputPath)} 导入数据...`);
98
93
  const importCmd = [...composeArgs, 'exec', '-T', 'postgres', 'psql', '-U', dbUser, '-d', dbName];
99
94
  const subprocess = execa('docker', ['compose', ...importCmd], {
100
95
  env: envs,
@@ -1,12 +1,12 @@
1
+ import { confirm, input, select } from '@inquirer/prompts';
1
2
  import { Command, Flags } from '@oclif/core';
2
- import { logger } from '../../utils/logger.js';
3
+ import dotenv from 'dotenv';
3
4
  import { execa } from 'execa';
4
5
  import fs from 'node:fs';
5
6
  import path from 'node:path';
6
- import dotenv from 'dotenv';
7
- import { confirm, select, input } from '@inquirer/prompts';
7
+ import { logger } from '../../utils/logger.js';
8
8
  export default class ImportUploads extends Command {
9
- static description = '恢复上传文件 (从 tar.gz 备份恢复)';
9
+ static description = '恢复上传文件';
10
10
  static flags = {
11
11
  input: Flags.string({
12
12
  char: 'i',
@@ -33,15 +33,10 @@ export default class ImportUploads extends Command {
33
33
  }));
34
34
  choices.push({ name: '手动输入路径', value: '__MANUAL__' });
35
35
  const selection = await select({
36
+ choices,
36
37
  message: '请选择上传文件备份:',
37
- choices: choices,
38
38
  });
39
- if (selection === '__MANUAL__') {
40
- inputFile = await input({ message: '请输入文件路径:' });
41
- }
42
- else {
43
- inputFile = selection;
44
- }
39
+ inputFile = selection === '__MANUAL__' ? (await input({ message: '请输入文件路径:' })) : selection;
45
40
  }
46
41
  else {
47
42
  inputFile = await input({ message: '未找到上传备份文件 (.tar.gz),请输入路径:' });
@@ -57,20 +52,19 @@ export default class ImportUploads extends Command {
57
52
  const volumeName = await this.getOrCreateUploadsVolume();
58
53
  // 危险操作确认
59
54
  if (process.env.NODEBBS_SKIP_CONFIRM !== 'true') {
60
- logger.warning('警告: 此操作将清空当前上传目录的所有文件并从备份恢复!');
55
+ logger.warning('此操作将清空当前上传目录的所有文件并从备份恢复!');
61
56
  logger.warning(`目标文件: ${inputPath}`);
62
57
  logger.warning(`数据卷: ${volumeName}`);
63
58
  const isConfirmed = await confirm({
64
- message: '确认继续?(文件丢失不可撤销)',
65
- default: false
59
+ default: false,
60
+ message: '确认继续?(文件丢失不可撤销)'
66
61
  });
67
62
  if (!isConfirmed) {
68
63
  logger.info('操作已取消');
69
64
  this.exit(0);
70
65
  }
71
66
  }
72
- logger.info('正在恢复上传文件...');
73
- logger.info(`数据卷: ${volumeName}`);
67
+ logger.info(`正在恢复上传文件 (卷: ${volumeName})...`);
74
68
  const inputDir = path.dirname(inputPath);
75
69
  const inputFileName = path.basename(inputPath);
76
70
  try {
@@ -128,9 +122,10 @@ export default class ImportUploads extends Command {
128
122
  if (volumes.length === 1) {
129
123
  return volumes[0];
130
124
  }
131
- else if (volumes.length > 1) {
125
+ if (volumes.length > 1) {
132
126
  logger.warning('找到多个可能的上传卷:');
133
- volumes.forEach(v => logger.info(` - ${v}`));
127
+ for (const v of volumes)
128
+ logger.info(` - ${v}`);
134
129
  logger.info('请在 .env 中设置 COMPOSE_PROJECT_NAME 以指定正确的卷');
135
130
  return volumes[0];
136
131
  }
@@ -1,9 +1,9 @@
1
1
  import { Command } from '@oclif/core';
2
- import { runCompose, getComposeFiles } from '../../utils/docker.js';
2
+ import { getComposeFiles, runCompose } from '../../utils/docker.js';
3
3
  import { logger } from '../../utils/logger.js';
4
4
  import { EnvFlag, selectEnvironment } from '../../utils/selection.js';
5
5
  export default class Logs extends Command {
6
- static description = '查看所有服务日志';
6
+ static description = '查看日志';
7
7
  static flags = {
8
8
  env: EnvFlag,
9
9
  };
@@ -1,9 +1,9 @@
1
1
  import { Command } from '@oclif/core';
2
- import { runCompose, getComposeFiles } from '../../utils/docker.js';
2
+ import { getComposeFiles, runCompose } from '../../utils/docker.js';
3
3
  import { logger } from '../../utils/logger.js';
4
4
  import { EnvFlag, selectEnvironment } from '../../utils/selection.js';
5
5
  export default class LogsApi extends Command {
6
- static description = '查看 API 服务日志';
6
+ static description = '查看 API 日志';
7
7
  static flags = {
8
8
  env: EnvFlag,
9
9
  };
@@ -1,5 +1,5 @@
1
1
  import { Command } from '@oclif/core';
2
- import { runCompose, getComposeFiles } from '../../utils/docker.js';
2
+ import { getComposeFiles, runCompose } from '../../utils/docker.js';
3
3
  import { logger } from '../../utils/logger.js';
4
4
  import { EnvFlag, selectEnvironment } from '../../utils/selection.js';
5
5
  export default class LogsDb extends Command {
@@ -1,5 +1,5 @@
1
1
  import { Command } from '@oclif/core';
2
- import { runCompose, getComposeFiles } from '../../utils/docker.js';
2
+ import { getComposeFiles, runCompose } from '../../utils/docker.js';
3
3
  import { logger } from '../../utils/logger.js';
4
4
  import { EnvFlag, selectEnvironment } from '../../utils/selection.js';
5
5
  export default class LogsRedis extends Command {
@@ -1,5 +1,5 @@
1
1
  import { Command } from '@oclif/core';
2
- import { runCompose, getComposeFiles } from '../../utils/docker.js';
2
+ import { getComposeFiles, runCompose } from '../../utils/docker.js';
3
3
  import { logger } from '../../utils/logger.js';
4
4
  import { EnvFlag, selectEnvironment } from '../../utils/selection.js';
5
5
  export default class LogsWeb extends Command {