nodebbs 0.0.1 → 0.0.3
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 +83 -99
- 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/index.d.ts +3 -0
- package/dist/commands/db/index.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/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 +110 -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 +7 -1
- package/dist/templates/env +61 -0
- package/dist/utils/docker.js +3 -11
- package/dist/utils/env.js +19 -9
- package/dist/utils/selection.d.ts +5 -0
- package/dist/utils/selection.js +24 -0
- package/dist/utils/template.d.ts +11 -0
- package/dist/utils/template.js +18 -0
- package/oclif.manifest.json +380 -89
- package/package.json +21 -21
- package/dist/commands/deploy/index.d.ts +0 -8
- package/dist/commands/deploy/index.js +0 -89
- package/dist/commands/dev/index.js +0 -53
- package/dist/commands/setup/index.d.ts +0 -5
- package/dist/commands/setup/index.js +0 -12
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
export default class DbSeed 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
|
}
|
package/dist/commands/db/seed.js
CHANGED
|
@@ -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 DbSeed extends Command {
|
|
5
|
-
static description = '
|
|
6
|
+
static description = '填充种子数据 (db:seed)';
|
|
7
|
+
static flags = {
|
|
8
|
+
env: EnvFlag,
|
|
9
|
+
};
|
|
6
10
|
async run() {
|
|
7
|
-
const {
|
|
8
|
-
|
|
11
|
+
const { flags } = await this.parse(DbSeed);
|
|
12
|
+
const env = await selectEnvironment(flags.env);
|
|
13
|
+
const { files, isBuiltIn } = await getComposeFiles(env);
|
|
14
|
+
logger.info('正在填充种子数据...');
|
|
9
15
|
await execCompose(files, 'api', ['npm', 'run', 'seed'], isBuiltIn);
|
|
10
16
|
logger.success('数据初始化完成');
|
|
11
17
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
export default class LogsApi 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 { runCompose, 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 LogsApi extends Command {
|
|
5
6
|
static description = '查看 API 服务日志';
|
|
7
|
+
static flags = {
|
|
8
|
+
env: EnvFlag,
|
|
9
|
+
};
|
|
6
10
|
async run() {
|
|
7
|
-
const {
|
|
8
|
-
|
|
11
|
+
const { flags } = await this.parse(LogsApi);
|
|
12
|
+
const env = await selectEnvironment(flags.env);
|
|
13
|
+
const { files, isBuiltIn } = await getComposeFiles(env);
|
|
14
|
+
logger.info('正在获取 API 服务日志...');
|
|
9
15
|
await runCompose(files, ['logs', '-f', 'api'], isBuiltIn);
|
|
10
16
|
}
|
|
11
17
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
export default class LogsDb 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
|
}
|
package/dist/commands/logs/db.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
import { runCompose, 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 LogsDb extends Command {
|
|
5
6
|
static description = '查看数据库日志';
|
|
7
|
+
static flags = {
|
|
8
|
+
env: EnvFlag,
|
|
9
|
+
};
|
|
6
10
|
async run() {
|
|
7
|
-
const {
|
|
8
|
-
|
|
11
|
+
const { flags } = await this.parse(LogsDb);
|
|
12
|
+
const env = await selectEnvironment(flags.env);
|
|
13
|
+
const { files, isBuiltIn } = await getComposeFiles(env);
|
|
14
|
+
logger.info('正在获取数据库日志...');
|
|
9
15
|
await runCompose(files, ['logs', '-f', 'postgres'], isBuiltIn);
|
|
10
16
|
}
|
|
11
17
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
export default class Logs 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 { runCompose, 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 Logs extends Command {
|
|
5
6
|
static description = '查看所有服务日志';
|
|
7
|
+
static flags = {
|
|
8
|
+
env: EnvFlag,
|
|
9
|
+
};
|
|
6
10
|
async run() {
|
|
7
|
-
const {
|
|
8
|
-
|
|
11
|
+
const { flags } = await this.parse(Logs);
|
|
12
|
+
const env = await selectEnvironment(flags.env);
|
|
13
|
+
const { files, isBuiltIn } = await getComposeFiles(env);
|
|
14
|
+
logger.info('正在获取所有服务日志 (按 Ctrl+C 退出)...');
|
|
9
15
|
await runCompose(files, ['logs', '-f'], isBuiltIn);
|
|
10
16
|
}
|
|
11
17
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
export default class LogsRedis 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 { runCompose, 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 LogsRedis extends Command {
|
|
5
6
|
static description = '查看 Redis 日志';
|
|
7
|
+
static flags = {
|
|
8
|
+
env: EnvFlag,
|
|
9
|
+
};
|
|
6
10
|
async run() {
|
|
7
|
-
const {
|
|
8
|
-
|
|
11
|
+
const { flags } = await this.parse(LogsRedis);
|
|
12
|
+
const env = await selectEnvironment(flags.env);
|
|
13
|
+
const { files, isBuiltIn } = await getComposeFiles(env);
|
|
14
|
+
logger.info('正在获取 Redis 日志...');
|
|
9
15
|
await runCompose(files, ['logs', '-f', 'redis'], isBuiltIn);
|
|
10
16
|
}
|
|
11
17
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
export default class LogsWeb 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 { runCompose, 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 LogsWeb extends Command {
|
|
5
|
-
static description = '查看 Web
|
|
6
|
+
static description = '查看 Web 前端日志';
|
|
7
|
+
static flags = {
|
|
8
|
+
env: EnvFlag,
|
|
9
|
+
};
|
|
6
10
|
async run() {
|
|
7
|
-
const {
|
|
8
|
-
|
|
11
|
+
const { flags } = await this.parse(LogsWeb);
|
|
12
|
+
const env = await selectEnvironment(flags.env);
|
|
13
|
+
const { files, isBuiltIn } = await getComposeFiles(env);
|
|
14
|
+
logger.info('正在获取 Web 前端日志...');
|
|
9
15
|
await runCompose(files, ['logs', '-f', 'web'], isBuiltIn);
|
|
10
16
|
}
|
|
11
17
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
export default class Restart 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,19 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
import { runCompose, 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 Restart extends Command {
|
|
5
|
-
static description = '重启所有服务';
|
|
6
|
+
static description = '重启所有服务 (docker compose restart)';
|
|
7
|
+
static flags = {
|
|
8
|
+
env: EnvFlag,
|
|
9
|
+
};
|
|
6
10
|
async run() {
|
|
7
|
-
const {
|
|
11
|
+
const { flags } = await this.parse(Restart);
|
|
12
|
+
// 1. Select Environment
|
|
13
|
+
const env = await selectEnvironment(flags.env);
|
|
14
|
+
const { files, isBuiltIn } = await getComposeFiles(env);
|
|
8
15
|
logger.info('正在重启服务...');
|
|
16
|
+
logger.info('环境: ' + env);
|
|
9
17
|
await runCompose(files, ['restart'], isBuiltIn);
|
|
10
18
|
logger.success('服务已重启');
|
|
11
19
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
export default class ShellApi 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 { runCompose, 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 ShellApi extends Command {
|
|
5
|
-
static description = '进入 API
|
|
6
|
+
static description = '进入 API 服务 shell';
|
|
7
|
+
static flags = {
|
|
8
|
+
env: EnvFlag,
|
|
9
|
+
};
|
|
6
10
|
async run() {
|
|
7
|
-
const {
|
|
8
|
-
|
|
11
|
+
const { flags } = await this.parse(ShellApi);
|
|
12
|
+
const env = await selectEnvironment(flags.env);
|
|
13
|
+
const { files, isBuiltIn } = await getComposeFiles(env);
|
|
14
|
+
logger.info('正在进入 API 服务 shell...');
|
|
9
15
|
await runCompose(files, ['exec', 'api', 'sh'], isBuiltIn);
|
|
10
16
|
}
|
|
11
17
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
export default class ShellDb 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 { runCompose, 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 ShellDb extends Command {
|
|
5
|
-
static description = '进入数据库';
|
|
6
|
+
static description = '进入数据库 shell (psql)';
|
|
7
|
+
static flags = {
|
|
8
|
+
env: EnvFlag,
|
|
9
|
+
};
|
|
6
10
|
async run() {
|
|
7
|
-
const {
|
|
8
|
-
|
|
11
|
+
const { flags } = await this.parse(ShellDb);
|
|
12
|
+
const env = await selectEnvironment(flags.env);
|
|
13
|
+
const { files, isBuiltIn } = await getComposeFiles(env);
|
|
14
|
+
logger.info('正在进入数据库 shell...');
|
|
9
15
|
await runCompose(files, ['exec', 'postgres', 'psql', '-U', 'postgres', '-d', 'nodebbs'], isBuiltIn);
|
|
10
16
|
}
|
|
11
17
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
export default class ShellRedis 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 { runCompose, 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 ShellRedis extends Command {
|
|
5
|
-
static description = '进入 Redis';
|
|
6
|
+
static description = '进入 Redis shell (redis-cli)';
|
|
7
|
+
static flags = {
|
|
8
|
+
env: EnvFlag,
|
|
9
|
+
};
|
|
6
10
|
async run() {
|
|
7
|
-
const {
|
|
8
|
-
|
|
11
|
+
const { flags } = await this.parse(ShellRedis);
|
|
12
|
+
const env = await selectEnvironment(flags.env);
|
|
13
|
+
const { files, isBuiltIn } = await getComposeFiles(env);
|
|
14
|
+
logger.info('正在进入 Redis shell...');
|
|
9
15
|
await runCompose(files, ['exec', 'redis', 'redis-cli'], isBuiltIn);
|
|
10
16
|
}
|
|
11
17
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
export default class ShellWeb 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 { runCompose, 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 ShellWeb extends Command {
|
|
5
|
-
static description = '进入 Web
|
|
6
|
+
static description = '进入 Web 前端 shell';
|
|
7
|
+
static flags = {
|
|
8
|
+
env: EnvFlag,
|
|
9
|
+
};
|
|
6
10
|
async run() {
|
|
7
|
-
const {
|
|
8
|
-
|
|
11
|
+
const { flags } = await this.parse(ShellWeb);
|
|
12
|
+
const env = await selectEnvironment(flags.env);
|
|
13
|
+
const { files, isBuiltIn } = await getComposeFiles(env);
|
|
14
|
+
logger.info('正在进入 Web 前端 shell...');
|
|
9
15
|
await runCompose(files, ['exec', 'web', 'sh'], isBuiltIn);
|
|
10
16
|
}
|
|
11
17
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
-
export default class
|
|
2
|
+
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
|
-
|
|
6
|
+
build: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
7
|
};
|
|
8
8
|
run(): Promise<void>;
|
|
9
9
|
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import { confirm } from '@inquirer/prompts';
|
|
3
|
+
import { checkDocker, runCompose, waitForHealth, execCompose, getComposeFiles } from '../../utils/docker.js';
|
|
4
|
+
import { initEnv, checkEnv } from '../../utils/env.js';
|
|
5
|
+
import { logger } from '../../utils/logger.js';
|
|
6
|
+
import dotenv from 'dotenv';
|
|
7
|
+
import { EnvFlag, selectEnvironment } from '../../utils/selection.js';
|
|
8
|
+
export default class Start extends Command {
|
|
9
|
+
static description = '启动 NodeBBS (部署模式)';
|
|
10
|
+
static flags = {
|
|
11
|
+
env: EnvFlag,
|
|
12
|
+
build: Flags.boolean({
|
|
13
|
+
char: 'b',
|
|
14
|
+
description: '重新构建并启动服务 (跳过健康检查和数据初始化)',
|
|
15
|
+
default: false,
|
|
16
|
+
}),
|
|
17
|
+
};
|
|
18
|
+
async run() {
|
|
19
|
+
const { flags } = await this.parse(Start);
|
|
20
|
+
if (flags.build) {
|
|
21
|
+
logger.header('NodeBBS 重新构建启动');
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
logger.header('NodeBBS Docker 部署');
|
|
25
|
+
}
|
|
26
|
+
// 1. Select Environment
|
|
27
|
+
const env = await selectEnvironment(flags.env);
|
|
28
|
+
// 2. Get Compose Files
|
|
29
|
+
const { files: composeFiles, isBuiltIn } = await getComposeFiles(env);
|
|
30
|
+
if (isBuiltIn) {
|
|
31
|
+
logger.info('使用内置 Docker Compose 模板...');
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
logger.info('使用项目 Docker Compose 文件...');
|
|
35
|
+
}
|
|
36
|
+
if (env === 'production') {
|
|
37
|
+
logger.success('已选择:标准生产环境');
|
|
38
|
+
}
|
|
39
|
+
else if (env === 'lowmem') {
|
|
40
|
+
logger.success('已选择:低配环境');
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
logger.success('已选择:基础环境');
|
|
44
|
+
if (!flags.build) {
|
|
45
|
+
logger.warning('注意:无资源限制,不推荐用于生产环境。');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// 3. Check Docker & Env
|
|
49
|
+
await checkDocker();
|
|
50
|
+
// initEnv guarantees .env exists (or exits)
|
|
51
|
+
await initEnv();
|
|
52
|
+
await checkEnv(env);
|
|
53
|
+
if (!flags.build) {
|
|
54
|
+
const continueDeploy = await confirm({
|
|
55
|
+
message: '是否继续启动?',
|
|
56
|
+
default: true
|
|
57
|
+
});
|
|
58
|
+
if (!continueDeploy) {
|
|
59
|
+
logger.info('操作已取消。');
|
|
60
|
+
this.exit(0);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// 4. Build & Start Services
|
|
64
|
+
if (flags.build) {
|
|
65
|
+
logger.info('正在重新构建并启动服务...');
|
|
66
|
+
await runCompose(composeFiles, ['up', '-d', '--build'], isBuiltIn);
|
|
67
|
+
logger.success('服务已重新构建并启动');
|
|
68
|
+
}
|
|
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('服务启动指令已发送');
|
|
76
|
+
}
|
|
77
|
+
// 5. Post-Start Actions (Full Deploy only)
|
|
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 启动成功!');
|
|
100
|
+
// 6. Show Info
|
|
101
|
+
logger.info(`环境: ${env}`);
|
|
102
|
+
const envConfig = dotenv.config().parsed || {};
|
|
103
|
+
const webPort = envConfig.WEB_PORT || '3100';
|
|
104
|
+
const apiPort = envConfig.API_PORT || '7100';
|
|
105
|
+
console.log(` Web 前端: http://localhost:${webPort}`);
|
|
106
|
+
console.log(` API 服务: http://localhost:${apiPort}`);
|
|
107
|
+
console.log(` API 文档: http://localhost:${apiPort}/docs`);
|
|
108
|
+
console.log(` 健康检查: http://localhost:${apiPort}/api`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
export default class Status 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 { runCompose, 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 Status extends Command {
|
|
5
6
|
static description = '查看服务状态';
|
|
7
|
+
static flags = {
|
|
8
|
+
env: EnvFlag,
|
|
9
|
+
};
|
|
6
10
|
async run() {
|
|
7
|
-
const {
|
|
8
|
-
|
|
11
|
+
const { flags } = await this.parse(Status);
|
|
12
|
+
const env = await selectEnvironment(flags.env);
|
|
13
|
+
const { files, isBuiltIn } = await getComposeFiles(env);
|
|
14
|
+
logger.info('正在获取服务状态...');
|
|
9
15
|
await runCompose(files, ['ps'], isBuiltIn);
|
|
10
16
|
}
|
|
11
17
|
}
|
|
@@ -3,6 +3,7 @@ export default class Stop extends Command {
|
|
|
3
3
|
static description: string;
|
|
4
4
|
static flags: {
|
|
5
5
|
volumes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
6
|
+
env: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
7
|
};
|
|
7
8
|
run(): Promise<void>;
|
|
8
9
|
}
|
|
@@ -2,6 +2,7 @@ import { Command, Flags } from '@oclif/core';
|
|
|
2
2
|
import { runCompose, getComposeFiles } from '../../utils/docker.js';
|
|
3
3
|
import { logger } from '../../utils/logger.js';
|
|
4
4
|
import { confirm } from '@inquirer/prompts';
|
|
5
|
+
import { EnvFlag, selectEnvironment } from '../../utils/selection.js';
|
|
5
6
|
export default class Stop extends Command {
|
|
6
7
|
static description = '停止服务';
|
|
7
8
|
static flags = {
|
|
@@ -10,10 +11,15 @@ export default class Stop extends Command {
|
|
|
10
11
|
description: '同时删除数据卷(危险!)',
|
|
11
12
|
default: false,
|
|
12
13
|
}),
|
|
14
|
+
env: EnvFlag,
|
|
13
15
|
};
|
|
14
16
|
async run() {
|
|
15
17
|
const { flags } = await this.parse(Stop);
|
|
16
|
-
|
|
18
|
+
// 1. Select Environment
|
|
19
|
+
const env = await selectEnvironment(flags.env, {
|
|
20
|
+
prompt: '请选择运行环境(停止服务需匹配启动环境):'
|
|
21
|
+
});
|
|
22
|
+
const { files, isBuiltIn } = await getComposeFiles(env);
|
|
17
23
|
if (flags.volumes) {
|
|
18
24
|
logger.warning('警告:这将删除所有数据!');
|
|
19
25
|
const confirmDelete = await confirm({
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# ========================================
|
|
2
|
+
# NodeBBS Docker Compose 环境变量配置
|
|
3
|
+
# ========================================
|
|
4
|
+
|
|
5
|
+
# 应用名称
|
|
6
|
+
APP_NAME=nodebbs
|
|
7
|
+
|
|
8
|
+
# ========================================
|
|
9
|
+
# 数据库配置
|
|
10
|
+
# ========================================
|
|
11
|
+
POSTGRES_PASSWORD=your_secure_postgres_password_here
|
|
12
|
+
POSTGRES_DB=nodebbs
|
|
13
|
+
POSTGRES_PORT=5432
|
|
14
|
+
|
|
15
|
+
# ========================================
|
|
16
|
+
# Redis 配置
|
|
17
|
+
# ========================================
|
|
18
|
+
REDIS_PASSWORD=your_secure_redis_password_here
|
|
19
|
+
REDIS_PORT=6379
|
|
20
|
+
|
|
21
|
+
# ========================================
|
|
22
|
+
# API 服务配置
|
|
23
|
+
# ========================================
|
|
24
|
+
API_PORT=7100
|
|
25
|
+
|
|
26
|
+
# 用户缓存 TTL(秒)
|
|
27
|
+
# 开发环境: 30-60, 生产环境: 120-300
|
|
28
|
+
USER_CACHE_TTL=120
|
|
29
|
+
|
|
30
|
+
# JWT 配置
|
|
31
|
+
# 使用 `openssl rand -base64 32` 生成安全的密钥
|
|
32
|
+
JWT_SECRET=change-this-to-a-secure-random-string-in-production
|
|
33
|
+
JWT_ACCESS_TOKEN_EXPIRES_IN=1y
|
|
34
|
+
|
|
35
|
+
# CORS 配置
|
|
36
|
+
# 生产环境建议设置为具体的域名,例如: https://yourdomain.com
|
|
37
|
+
CORS_ORIGIN=*
|
|
38
|
+
|
|
39
|
+
# 应用 URL(OAuth 回调使用)
|
|
40
|
+
APP_URL=http://localhost:3100
|
|
41
|
+
|
|
42
|
+
# ========================================
|
|
43
|
+
# Web 前端配置
|
|
44
|
+
# ========================================
|
|
45
|
+
WEB_PORT=3100
|
|
46
|
+
|
|
47
|
+
# API 地址(公网访问地址)
|
|
48
|
+
# 使用 docker 部署必须指定 IP 或域名(避免SSR跨容器通信问题)
|
|
49
|
+
# 开发环境: http://192.168.0.100:7100
|
|
50
|
+
# 生产环境: https://api.yourdomain.com
|
|
51
|
+
NEXT_PUBLIC_API_URL=http://192.168.0.100:7100
|
|
52
|
+
|
|
53
|
+
# 应用 URL(公网访问地址)
|
|
54
|
+
# 开发环境: http://localhost:3100
|
|
55
|
+
# 生产环境: https://yourdomain.com
|
|
56
|
+
NEXT_PUBLIC_APP_URL=http://localhost:3100
|
|
57
|
+
|
|
58
|
+
# ========================================
|
|
59
|
+
# 时区配置
|
|
60
|
+
# ========================================
|
|
61
|
+
TZ=Asia/Shanghai
|
package/dist/utils/docker.js
CHANGED
|
@@ -3,9 +3,8 @@ import { logger } from './logger.js';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { exists } from 'node:fs';
|
|
5
5
|
import { promisify } from 'node:util';
|
|
6
|
-
import {
|
|
6
|
+
import { getTemplateDir, getTemplatePath } from './template.js';
|
|
7
7
|
const fileExists = promisify(exists);
|
|
8
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
8
|
export async function getComposeFiles(env) {
|
|
10
9
|
const workDir = process.cwd();
|
|
11
10
|
let isBuiltIn = false;
|
|
@@ -15,15 +14,8 @@ export async function getComposeFiles(env) {
|
|
|
15
14
|
if (!await fileExists(baseFile)) {
|
|
16
15
|
// Use built-in templates
|
|
17
16
|
isBuiltIn = true;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
templateDir = path.join(__dirname, '..', 'templates');
|
|
21
|
-
baseFile = path.join(templateDir, 'docker-compose.yml');
|
|
22
|
-
// Verify the built-in template exists
|
|
23
|
-
if (!await fileExists(baseFile)) {
|
|
24
|
-
logger.error('内置模板未找到,请确保 CLI 正确安装。');
|
|
25
|
-
throw new Error('Built-in templates not found');
|
|
26
|
-
}
|
|
17
|
+
templateDir = getTemplateDir();
|
|
18
|
+
baseFile = getTemplatePath('docker-compose.yml');
|
|
27
19
|
}
|
|
28
20
|
const files = [baseFile];
|
|
29
21
|
if (env === 'production') {
|