@fecommunity/reactpress-cli 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/bin/reactpress.js +246 -0
- package/lib/api-dev-runner.js +6 -0
- package/lib/api-dev.js +84 -0
- package/lib/bootstrap.js +109 -0
- package/lib/build.js +23 -0
- package/lib/dev.js +109 -0
- package/lib/docker.js +156 -0
- package/lib/http.js +80 -0
- package/lib/lifecycle.js +138 -0
- package/lib/paths.js +75 -0
- package/lib/pm2.js +31 -0
- package/lib/process.js +45 -0
- package/lib/publish.js +947 -0
- package/lib/root.js +31 -0
- package/lib/spawn.js +85 -0
- package/lib/status.js +54 -0
- package/package.json +32 -52
- package/ui/banner.js +16 -0
- package/ui/interactive.js +158 -0
- package/ui/theme.js +25 -0
- package/toolkit/dist/api/Article.d.ts +0 -123
- package/toolkit/dist/api/Article.js +0 -188
- package/toolkit/dist/api/Auth.d.ts +0 -32
- package/toolkit/dist/api/Auth.js +0 -58
- package/toolkit/dist/api/Category.d.ts +0 -51
- package/toolkit/dist/api/Category.js +0 -84
- package/toolkit/dist/api/Comment.d.ts +0 -60
- package/toolkit/dist/api/Comment.js +0 -97
- package/toolkit/dist/api/File.d.ts +0 -42
- package/toolkit/dist/api/File.js +0 -71
- package/toolkit/dist/api/HttpClient.d.ts +0 -41
- package/toolkit/dist/api/HttpClient.js +0 -96
- package/toolkit/dist/api/Knowledge.d.ts +0 -78
- package/toolkit/dist/api/Knowledge.js +0 -123
- package/toolkit/dist/api/Page.d.ts +0 -60
- package/toolkit/dist/api/Page.js +0 -97
- package/toolkit/dist/api/Search.d.ts +0 -32
- package/toolkit/dist/api/Search.js +0 -58
- package/toolkit/dist/api/Setting.d.ts +0 -24
- package/toolkit/dist/api/Setting.js +0 -45
- package/toolkit/dist/api/Smtp.d.ts +0 -33
- package/toolkit/dist/api/Smtp.js +0 -58
- package/toolkit/dist/api/Tag.d.ts +0 -60
- package/toolkit/dist/api/Tag.js +0 -97
- package/toolkit/dist/api/User.d.ts +0 -43
- package/toolkit/dist/api/User.js +0 -72
- package/toolkit/dist/api/View.d.ts +0 -51
- package/toolkit/dist/api/View.js +0 -84
- package/toolkit/dist/api/index.d.ts +0 -13
- package/toolkit/dist/api/index.js +0 -30
- package/toolkit/dist/api/instance.d.ts +0 -47
- package/toolkit/dist/api/instance.js +0 -71
- package/toolkit/dist/config/env.d.ts +0 -1
- package/toolkit/dist/config/env.js +0 -89
- package/toolkit/dist/config/global.d.ts +0 -34
- package/toolkit/dist/config/global.js +0 -382
- package/toolkit/dist/config/i18n.d.ts +0 -4
- package/toolkit/dist/config/i18n.js +0 -43
- package/toolkit/dist/config/index.d.ts +0 -15
- package/toolkit/dist/config/index.js +0 -25
- package/toolkit/dist/index.d.ts +0 -11
- package/toolkit/dist/index.js +0 -38
- package/toolkit/dist/locales/en.json +0 -97
- package/toolkit/dist/locales/zh.json +0 -97
- package/toolkit/dist/types/index.d.ts +0 -245
- package/toolkit/dist/types/index.js +0 -12
- package/toolkit/dist/utils/index.d.ts +0 -33
- package/toolkit/dist/utils/index.js +0 -59
- package/toolkit/package.json +0 -19
package/lib/root.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/** Monorepo root (parent of cli/) */
|
|
5
|
+
function getMonorepoRoot() {
|
|
6
|
+
return path.resolve(__dirname, '../..');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function getProjectRoot() {
|
|
10
|
+
return path.resolve(process.env.REACTPRESS_ORIGINAL_CWD || process.cwd());
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function ensureOriginalCwd() {
|
|
14
|
+
const root = getProjectRoot();
|
|
15
|
+
process.env.REACTPRESS_ORIGINAL_CWD = root;
|
|
16
|
+
return root;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isMonorepoCheckout(cwd = getMonorepoRoot()) {
|
|
20
|
+
return (
|
|
21
|
+
fs.existsSync(path.join(cwd, 'pnpm-workspace.yaml')) ||
|
|
22
|
+
fs.existsSync(path.join(cwd, 'server', 'src', 'main.ts'))
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = {
|
|
27
|
+
getMonorepoRoot,
|
|
28
|
+
getProjectRoot,
|
|
29
|
+
ensureOriginalCwd,
|
|
30
|
+
isMonorepoCheckout,
|
|
31
|
+
};
|
package/lib/spawn.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const { spawn, spawnSync } = require('child_process');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const { getMonorepoRoot } = require('./root');
|
|
5
|
+
const { getCliPackageRoot } = require('./paths');
|
|
6
|
+
|
|
7
|
+
function runSync(command, args, options = {}) {
|
|
8
|
+
const result = spawnSync(command, args, {
|
|
9
|
+
cwd: options.cwd || getMonorepoRoot(),
|
|
10
|
+
stdio: 'inherit',
|
|
11
|
+
env: {
|
|
12
|
+
...process.env,
|
|
13
|
+
REACTPRESS_ORIGINAL_CWD:
|
|
14
|
+
options.cwd || process.env.REACTPRESS_ORIGINAL_CWD || process.cwd(),
|
|
15
|
+
},
|
|
16
|
+
shell: options.shell ?? false,
|
|
17
|
+
});
|
|
18
|
+
if (result.status !== 0) {
|
|
19
|
+
const err = new Error(`命令失败 (${command}): 退出码 ${result.status ?? 1}`);
|
|
20
|
+
err.exitCode = result.status ?? 1;
|
|
21
|
+
throw err;
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function runNodeScript(scriptPath, args = [], options = {}) {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const child = spawn(process.execPath, [scriptPath, ...args], {
|
|
29
|
+
stdio: 'inherit',
|
|
30
|
+
cwd: options.cwd || getMonorepoRoot(),
|
|
31
|
+
env: {
|
|
32
|
+
...process.env,
|
|
33
|
+
REACTPRESS_ORIGINAL_CWD:
|
|
34
|
+
options.cwd || process.env.REACTPRESS_ORIGINAL_CWD || process.cwd(),
|
|
35
|
+
...options.env,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
child.on('error', (error) => {
|
|
40
|
+
console.error(chalk.red('[ReactPress]'), error.message || error);
|
|
41
|
+
reject(error);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
child.on('close', (code) => {
|
|
45
|
+
if (code !== 0) {
|
|
46
|
+
reject(Object.assign(new Error(`退出码 ${code}`), { exitCode: code }));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
resolve(code);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function spawnDetached(scriptPath, args = [], options = {}) {
|
|
55
|
+
const child = spawn(process.execPath, [scriptPath, ...args], {
|
|
56
|
+
stdio: options.stdio ?? 'ignore',
|
|
57
|
+
detached: true,
|
|
58
|
+
cwd: options.cwd,
|
|
59
|
+
env: {
|
|
60
|
+
...process.env,
|
|
61
|
+
REACTPRESS_ORIGINAL_CWD:
|
|
62
|
+
options.cwd || process.env.REACTPRESS_ORIGINAL_CWD || process.cwd(),
|
|
63
|
+
...options.env,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
child.unref();
|
|
67
|
+
return child;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function runReactpressCli(args, options = {}) {
|
|
71
|
+
const cliBin = path.join(getCliPackageRoot(), 'dist', 'index.js');
|
|
72
|
+
return runSync(process.execPath, [cliBin, ...args], options);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function resolveCliScript(relativePath) {
|
|
76
|
+
return path.join(__dirname, '..', relativePath);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = {
|
|
80
|
+
runSync,
|
|
81
|
+
runNodeScript,
|
|
82
|
+
spawnDetached,
|
|
83
|
+
runReactpressCli,
|
|
84
|
+
resolveCliScript,
|
|
85
|
+
};
|
package/lib/status.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const { loadServerSiteUrl, loadClientSiteUrl, isHttpResponding } = require('./http');
|
|
5
|
+
const { isUsingMonorepoServer } = require('./paths');
|
|
6
|
+
const { readPid, isProcessRunning } = require('./process');
|
|
7
|
+
const { isDockerRunning } = require('./docker');
|
|
8
|
+
const { ensureOriginalCwd } = require('./root');
|
|
9
|
+
|
|
10
|
+
function envFileStatus(projectRoot) {
|
|
11
|
+
const envPath = path.join(projectRoot, '.env');
|
|
12
|
+
const configPath = path.join(projectRoot, '.reactpress', 'config.json');
|
|
13
|
+
return {
|
|
14
|
+
env: fs.existsSync(envPath),
|
|
15
|
+
config: fs.existsSync(configPath),
|
|
16
|
+
envPath,
|
|
17
|
+
configPath,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function printUnifiedStatus(projectRoot = ensureOriginalCwd()) {
|
|
22
|
+
const env = envFileStatus(projectRoot);
|
|
23
|
+
const apiUrl = loadServerSiteUrl(projectRoot);
|
|
24
|
+
const clientUrl = loadClientSiteUrl(projectRoot);
|
|
25
|
+
const pid = readPid(projectRoot);
|
|
26
|
+
const [apiHttp, clientHttp] = await Promise.all([
|
|
27
|
+
isHttpResponding(apiUrl),
|
|
28
|
+
isHttpResponding(clientUrl),
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
console.log('');
|
|
32
|
+
console.log(chalk.bold.cyan(' ReactPress 项目状态'));
|
|
33
|
+
console.log(chalk.gray(' ─────────────────────────────────────'));
|
|
34
|
+
console.log(` 项目目录 ${chalk.white(projectRoot)}`);
|
|
35
|
+
console.log(` API 来源 ${chalk.white(isUsingMonorepoServer() ? 'monorepo server/' : 'reactpress-cli')}`);
|
|
36
|
+
console.log(
|
|
37
|
+
` 配置文件 ${env.config ? chalk.green('.reactpress/config.json ✓') : chalk.yellow('未初始化')}`
|
|
38
|
+
);
|
|
39
|
+
console.log(` 环境变量 ${env.env ? chalk.green('.env ✓') : chalk.yellow('缺少 .env')}`);
|
|
40
|
+
console.log(chalk.gray(' ─────────────────────────────────────'));
|
|
41
|
+
console.log(chalk.bold(' API'));
|
|
42
|
+
console.log(` URL ${apiUrl}`);
|
|
43
|
+
console.log(` HTTP ${apiHttp ? chalk.green('在线') : chalk.red('离线')}`);
|
|
44
|
+
console.log(` PID ${pid ?? '—'} ${pid && isProcessRunning(pid) ? chalk.green('(运行中)') : ''}`);
|
|
45
|
+
console.log(chalk.bold(' 前端'));
|
|
46
|
+
console.log(` URL ${clientUrl}`);
|
|
47
|
+
console.log(` HTTP ${clientHttp ? chalk.green('在线') : chalk.gray('离线')}`);
|
|
48
|
+
console.log(chalk.gray(' ─────────────────────────────────────'));
|
|
49
|
+
console.log(chalk.bold(' Docker'));
|
|
50
|
+
console.log(` 引擎 ${isDockerRunning() ? chalk.green('可用') : chalk.red('未运行')}`);
|
|
51
|
+
console.log('');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = { printUnifiedStatus, envFileStatus };
|
package/package.json
CHANGED
|
@@ -1,75 +1,55 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fecommunity/reactpress-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "ReactPress CLI — init, dev, build, deploy, publish",
|
|
5
|
+
"author": "fecommunity",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/fecommunity/reactpress.git",
|
|
10
|
+
"directory": "cli"
|
|
11
|
+
},
|
|
5
12
|
"keywords": [
|
|
6
13
|
"reactpress",
|
|
7
14
|
"cms",
|
|
8
15
|
"blog",
|
|
9
16
|
"cli"
|
|
10
17
|
],
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
"homepage": "https://github.com/fecommunity/reactpress-cli#readme",
|
|
14
|
-
"bugs": {
|
|
15
|
-
"url": "https://github.com/fecommunity/reactpress-cli/issues"
|
|
16
|
-
},
|
|
17
|
-
"repository": {
|
|
18
|
-
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/fecommunity/reactpress-cli.git"
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=18"
|
|
20
20
|
},
|
|
21
|
-
"type": "module",
|
|
22
21
|
"bin": {
|
|
22
|
+
"reactpress": "./bin/reactpress.js",
|
|
23
23
|
"reactpress-cli": "./dist/index.js"
|
|
24
24
|
},
|
|
25
|
+
"main": "./bin/reactpress.js",
|
|
25
26
|
"files": [
|
|
26
|
-
"
|
|
27
|
-
"
|
|
27
|
+
"bin",
|
|
28
|
+
"lib",
|
|
29
|
+
"ui",
|
|
28
30
|
"dist",
|
|
29
|
-
"
|
|
31
|
+
"server",
|
|
30
32
|
"templates",
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"server/package.json",
|
|
35
|
-
"server/ecosystem.config.js",
|
|
36
|
-
"toolkit/dist",
|
|
37
|
-
"toolkit/package.json"
|
|
33
|
+
"scripts/install-bundled-runtime.mjs",
|
|
34
|
+
"README.md",
|
|
35
|
+
"LICENSE"
|
|
38
36
|
],
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
"dependencies": {
|
|
43
|
-
"chalk": "^5.4.1",
|
|
44
|
-
"commander": "^13.1.0",
|
|
45
|
-
"cross-spawn": "^7.0.6",
|
|
46
|
-
"dotenv": "^16.4.7",
|
|
47
|
-
"fs-extra": "^11.3.0",
|
|
48
|
-
"mysql2": "^3.14.0",
|
|
49
|
-
"ora": "^8.2.0"
|
|
37
|
+
"scripts": {
|
|
38
|
+
"prepare": "node scripts/sync-bundled-core.mjs",
|
|
39
|
+
"prepack": "node scripts/sync-bundled-core.mjs"
|
|
50
40
|
},
|
|
51
41
|
"publishConfig": {
|
|
52
42
|
"access": "public",
|
|
53
43
|
"registry": "https://registry.npmjs.org/"
|
|
54
44
|
},
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"typescript": "^5.8.2",
|
|
62
|
-
"vitest": "^3.0.9"
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"chalk": "^4.1.2",
|
|
47
|
+
"commander": "^9.4.1",
|
|
48
|
+
"concurrently": "^7.0.0",
|
|
49
|
+
"inquirer": "^8.2.4",
|
|
50
|
+
"open": "^8.2.1"
|
|
63
51
|
},
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"cli": "node dist/index.js",
|
|
67
|
-
"dev": "tsx src/index.ts",
|
|
68
|
-
"test": "vitest run",
|
|
69
|
-
"test:watch": "vitest",
|
|
70
|
-
"test:coverage": "vitest run --coverage",
|
|
71
|
-
"lint": "tsc --noEmit",
|
|
72
|
-
"pack:local": "npm pack",
|
|
73
|
-
"postinstall": "node scripts/install-bundled-runtime.mjs"
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@fecommunity/reactpress-cli-core": "npm:@fecommunity/reactpress-cli@0.1.0"
|
|
74
54
|
}
|
|
75
|
-
}
|
|
55
|
+
}
|
package/ui/banner.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const { brand } = require('./theme');
|
|
3
|
+
const { getMonorepoRoot } = require('../lib/root');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
function printBanner() {
|
|
7
|
+
const version = require(path.join(getMonorepoRoot(), 'package.json')).version;
|
|
8
|
+
console.log('');
|
|
9
|
+
console.log(brand.primary(' ╭─────────────────────────────────────────╮'));
|
|
10
|
+
console.log(brand.primary(' │') + chalk.bold.white(' ReactPress') + brand.muted(' · 全栈发布平台 CLI ') + brand.primary('│'));
|
|
11
|
+
console.log(brand.primary(' ╰─────────────────────────────────────────╯'));
|
|
12
|
+
console.log(brand.muted(` v${version} · init · dev · build · deploy · publish`));
|
|
13
|
+
console.log('');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = { printBanner };
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
const inquirer = require('inquirer');
|
|
2
|
+
const open = require('open');
|
|
3
|
+
const { printBanner } = require('./banner');
|
|
4
|
+
const { brand, label } = require('./theme');
|
|
5
|
+
const { ensureOriginalCwd } = require('../lib/root');
|
|
6
|
+
const { ensureProjectEnvironment } = require('../lib/bootstrap');
|
|
7
|
+
const { runDev } = require('../lib/dev');
|
|
8
|
+
const { runApiDev } = require('../lib/api-dev');
|
|
9
|
+
const { runLifecycleCommand } = require('../lib/lifecycle');
|
|
10
|
+
const { runDockerCommand } = require('../lib/docker');
|
|
11
|
+
const { printUnifiedStatus } = require('../lib/status');
|
|
12
|
+
const { runBuild } = require('../lib/build');
|
|
13
|
+
const { runNodeScript } = require('../lib/spawn');
|
|
14
|
+
const { getClientBin } = require('../lib/paths');
|
|
15
|
+
const { loadClientSiteUrl } = require('../lib/http');
|
|
16
|
+
|
|
17
|
+
const MENU_ACTIONS = [
|
|
18
|
+
{ name: '零配置开发 (env + DB + API + 前端)', value: 'dev' },
|
|
19
|
+
{ name: '初始化项目 (.reactpress + .env + 数据库)', value: 'init' },
|
|
20
|
+
{ name: '查看项目状态', value: 'status' },
|
|
21
|
+
new inquirer.Separator(),
|
|
22
|
+
{ name: '仅启动 API (开发 watch)', value: 'dev:api' },
|
|
23
|
+
{ name: '仅启动前端', value: 'dev:client' },
|
|
24
|
+
{ name: '启动 API (后台生产)', value: 'server:start' },
|
|
25
|
+
{ name: '停止 API', value: 'server:stop' },
|
|
26
|
+
{ name: '重启 API', value: 'server:restart' },
|
|
27
|
+
new inquirer.Separator(),
|
|
28
|
+
{ name: '构建 (toolkit → server → client)', value: 'build' },
|
|
29
|
+
{ name: 'Docker 开发环境 (DB + nginx + 全栈)', value: 'docker:start' },
|
|
30
|
+
{ name: 'Docker 仅启动数据库', value: 'docker:up' },
|
|
31
|
+
{ name: '停止 Docker 服务', value: 'docker:stop' },
|
|
32
|
+
new inquirer.Separator(),
|
|
33
|
+
{ name: '在浏览器打开管理后台', value: 'open:admin' },
|
|
34
|
+
{ name: '发布 npm 包 (交互式)', value: 'publish' },
|
|
35
|
+
{ name: '退出', value: 'exit' },
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
async function runMenuAction(action, projectRoot) {
|
|
39
|
+
switch (action) {
|
|
40
|
+
case 'dev':
|
|
41
|
+
console.log(label('启动全栈开发…'));
|
|
42
|
+
await runDev(projectRoot);
|
|
43
|
+
return false;
|
|
44
|
+
case 'init': {
|
|
45
|
+
console.log(label('初始化项目…'));
|
|
46
|
+
const result = await ensureProjectEnvironment(projectRoot);
|
|
47
|
+
console.log(brand.success(result.message || '完成'));
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
case 'status':
|
|
51
|
+
await printUnifiedStatus(projectRoot);
|
|
52
|
+
return true;
|
|
53
|
+
case 'dev:api':
|
|
54
|
+
await runApiDev(projectRoot);
|
|
55
|
+
return false;
|
|
56
|
+
case 'dev:client':
|
|
57
|
+
await runNodeScript(getClientBin(), [], { cwd: projectRoot });
|
|
58
|
+
return false;
|
|
59
|
+
case 'server:start': {
|
|
60
|
+
const code = await runLifecycleCommand('start', projectRoot);
|
|
61
|
+
if (code !== 0) process.exit(code);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
case 'server:stop':
|
|
65
|
+
await runLifecycleCommand('stop', projectRoot);
|
|
66
|
+
return true;
|
|
67
|
+
case 'server:restart': {
|
|
68
|
+
const code = await runLifecycleCommand('restart', projectRoot);
|
|
69
|
+
if (code !== 0) process.exit(code);
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
case 'build':
|
|
73
|
+
await runBuild('all', projectRoot);
|
|
74
|
+
return true;
|
|
75
|
+
case 'docker:start':
|
|
76
|
+
await runDockerCommand('start', projectRoot);
|
|
77
|
+
return false;
|
|
78
|
+
case 'docker:up':
|
|
79
|
+
await runDockerCommand('up', projectRoot);
|
|
80
|
+
return true;
|
|
81
|
+
case 'docker:stop':
|
|
82
|
+
await runDockerCommand('down', projectRoot);
|
|
83
|
+
return true;
|
|
84
|
+
case 'open:admin': {
|
|
85
|
+
const url = loadClientSiteUrl(projectRoot);
|
|
86
|
+
console.log(label(`打开 ${url}`));
|
|
87
|
+
await open(url);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
case 'publish': {
|
|
91
|
+
const prev = process.argv.slice();
|
|
92
|
+
process.argv = [process.argv[0], process.argv[1], '--publish'];
|
|
93
|
+
await require('../lib/publish').main();
|
|
94
|
+
process.argv = prev;
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
case 'exit':
|
|
98
|
+
console.log(brand.muted(' 再见。'));
|
|
99
|
+
return false;
|
|
100
|
+
default:
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function runInteractiveLoop() {
|
|
106
|
+
const projectRoot = ensureOriginalCwd();
|
|
107
|
+
printBanner();
|
|
108
|
+
|
|
109
|
+
let loop = true;
|
|
110
|
+
while (loop) {
|
|
111
|
+
const { action } = await inquirer.prompt([
|
|
112
|
+
{
|
|
113
|
+
type: 'list',
|
|
114
|
+
name: 'action',
|
|
115
|
+
message: '选择操作',
|
|
116
|
+
pageSize: 14,
|
|
117
|
+
choices: MENU_ACTIONS,
|
|
118
|
+
},
|
|
119
|
+
]);
|
|
120
|
+
|
|
121
|
+
if (action === 'exit') {
|
|
122
|
+
console.log(brand.muted(' 再见。'));
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
const stay = await runMenuAction(action, projectRoot);
|
|
128
|
+
if (!stay) {
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (action !== 'status') {
|
|
133
|
+
const { again } = await inquirer.prompt([
|
|
134
|
+
{
|
|
135
|
+
type: 'confirm',
|
|
136
|
+
name: 'again',
|
|
137
|
+
message: '返回主菜单?',
|
|
138
|
+
default: true,
|
|
139
|
+
},
|
|
140
|
+
]);
|
|
141
|
+
loop = again;
|
|
142
|
+
}
|
|
143
|
+
} catch (err) {
|
|
144
|
+
console.error(brand.error(` ${err.message || err}`));
|
|
145
|
+
const { retry } = await inquirer.prompt([
|
|
146
|
+
{
|
|
147
|
+
type: 'confirm',
|
|
148
|
+
name: 'retry',
|
|
149
|
+
message: '返回主菜单重试?',
|
|
150
|
+
default: true,
|
|
151
|
+
},
|
|
152
|
+
]);
|
|
153
|
+
loop = retry;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
module.exports = { runInteractiveLoop, runMenuAction };
|
package/ui/theme.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
|
|
3
|
+
const brand = {
|
|
4
|
+
primary: chalk.hex('#6366f1'),
|
|
5
|
+
accent: chalk.hex('#22d3ee'),
|
|
6
|
+
success: chalk.green,
|
|
7
|
+
warn: chalk.yellow,
|
|
8
|
+
error: chalk.red,
|
|
9
|
+
muted: chalk.gray,
|
|
10
|
+
bold: chalk.bold,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function label(text) {
|
|
14
|
+
return brand.primary(`› ${text}`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function ok(text) {
|
|
18
|
+
return brand.success(`✓ ${text}`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function fail(text) {
|
|
22
|
+
return brand.error(`✗ ${text}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = { brand, label, ok, fail };
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { IArticleControllerCreateData } from '../types';
|
|
2
|
-
import { HttpClient, RequestParams } from './HttpClient';
|
|
3
|
-
export declare class Article<SecurityDataType = unknown> {
|
|
4
|
-
http: HttpClient<SecurityDataType>;
|
|
5
|
-
constructor(http: HttpClient<SecurityDataType>);
|
|
6
|
-
/**
|
|
7
|
-
* No description
|
|
8
|
-
*
|
|
9
|
-
* @tags Article
|
|
10
|
-
* @name ArticleControllerCreate
|
|
11
|
-
* @request POST:/article
|
|
12
|
-
* @response `200` `IArticleControllerCreateData` 创建文章
|
|
13
|
-
*/
|
|
14
|
-
create: (params?: RequestParams) => Promise<IArticleControllerCreateData>;
|
|
15
|
-
/**
|
|
16
|
-
* No description
|
|
17
|
-
*
|
|
18
|
-
* @tags Article
|
|
19
|
-
* @name ArticleControllerFindAll
|
|
20
|
-
* @request GET:/article
|
|
21
|
-
* @response `200` `IArticleControllerFindAllData`
|
|
22
|
-
*/
|
|
23
|
-
findAll: (params?: RequestParams) => Promise<any>;
|
|
24
|
-
/**
|
|
25
|
-
* No description
|
|
26
|
-
*
|
|
27
|
-
* @tags Article
|
|
28
|
-
* @name ArticleControllerFindArticlesByCategory
|
|
29
|
-
* @request GET:/article/category/{id}
|
|
30
|
-
* @response `200` `IArticleControllerFindArticlesByCategoryData`
|
|
31
|
-
*/
|
|
32
|
-
findArticlesByCategory: (id: string, params?: RequestParams) => Promise<any>;
|
|
33
|
-
/**
|
|
34
|
-
* No description
|
|
35
|
-
*
|
|
36
|
-
* @tags Article
|
|
37
|
-
* @name ArticleControllerFindArticlesByTag
|
|
38
|
-
* @request GET:/article/tag/{id}
|
|
39
|
-
* @response `200` `IArticleControllerFindArticlesByTagData`
|
|
40
|
-
*/
|
|
41
|
-
findArticlesByTag: (id: string, params?: RequestParams) => Promise<any>;
|
|
42
|
-
/**
|
|
43
|
-
* No description
|
|
44
|
-
*
|
|
45
|
-
* @tags Article
|
|
46
|
-
* @name ArticleControllerGetRecommendArticles
|
|
47
|
-
* @request GET:/article/all/recommend
|
|
48
|
-
* @response `200` `IArticleControllerGetRecommendArticlesData`
|
|
49
|
-
*/
|
|
50
|
-
getRecommendArticles: (params?: RequestParams) => Promise<any>;
|
|
51
|
-
/**
|
|
52
|
-
* No description
|
|
53
|
-
*
|
|
54
|
-
* @tags Article
|
|
55
|
-
* @name ArticleControllerGetArchives
|
|
56
|
-
* @request GET:/article/archives
|
|
57
|
-
* @response `200` `IArticleControllerGetArchivesData`
|
|
58
|
-
*/
|
|
59
|
-
getArchives: (params?: RequestParams) => Promise<any>;
|
|
60
|
-
/**
|
|
61
|
-
* No description
|
|
62
|
-
*
|
|
63
|
-
* @tags Article
|
|
64
|
-
* @name ArticleControllerRecommend
|
|
65
|
-
* @request GET:/article/recommend
|
|
66
|
-
* @response `200` `IArticleControllerRecommendData`
|
|
67
|
-
*/
|
|
68
|
-
recommend: (params?: RequestParams) => Promise<any>;
|
|
69
|
-
/**
|
|
70
|
-
* No description
|
|
71
|
-
*
|
|
72
|
-
* @tags Article
|
|
73
|
-
* @name ArticleControllerFindById
|
|
74
|
-
* @request GET:/article/{id}
|
|
75
|
-
* @response `200` `IArticleControllerFindByIdData`
|
|
76
|
-
*/
|
|
77
|
-
findById: (id: string, params?: RequestParams) => Promise<any>;
|
|
78
|
-
/**
|
|
79
|
-
* No description
|
|
80
|
-
*
|
|
81
|
-
* @tags Article
|
|
82
|
-
* @name ArticleControllerUpdateById
|
|
83
|
-
* @request PATCH:/article/{id}
|
|
84
|
-
* @response `200` `IArticleControllerUpdateByIdData`
|
|
85
|
-
*/
|
|
86
|
-
updateById: (id: string, params?: RequestParams) => Promise<any>;
|
|
87
|
-
/**
|
|
88
|
-
* No description
|
|
89
|
-
*
|
|
90
|
-
* @tags Article
|
|
91
|
-
* @name ArticleControllerDeleteById
|
|
92
|
-
* @request DELETE:/article/{id}
|
|
93
|
-
* @response `200` `IArticleControllerDeleteByIdData`
|
|
94
|
-
*/
|
|
95
|
-
deleteById: (id: string, params?: RequestParams) => Promise<any>;
|
|
96
|
-
/**
|
|
97
|
-
* No description
|
|
98
|
-
*
|
|
99
|
-
* @tags Article
|
|
100
|
-
* @name ArticleControllerCheckPassword
|
|
101
|
-
* @request POST:/article/{id}/checkPassword
|
|
102
|
-
* @response `200` `IArticleControllerCheckPasswordData`
|
|
103
|
-
*/
|
|
104
|
-
checkPassword: (id: string, params?: RequestParams) => Promise<any>;
|
|
105
|
-
/**
|
|
106
|
-
* No description
|
|
107
|
-
*
|
|
108
|
-
* @tags Article
|
|
109
|
-
* @name ArticleControllerUpdateViewsById
|
|
110
|
-
* @request POST:/article/{id}/views
|
|
111
|
-
* @response `200` `IArticleControllerUpdateViewsByIdData`
|
|
112
|
-
*/
|
|
113
|
-
updateViewsById: (id: string, params?: RequestParams) => Promise<any>;
|
|
114
|
-
/**
|
|
115
|
-
* No description
|
|
116
|
-
*
|
|
117
|
-
* @tags Article
|
|
118
|
-
* @name ArticleControllerUpdateLikesById
|
|
119
|
-
* @request POST:/article/{id}/likes
|
|
120
|
-
* @response `200` `IArticleControllerUpdateLikesByIdData`
|
|
121
|
-
*/
|
|
122
|
-
updateLikesById: (id: string, params?: RequestParams) => Promise<any>;
|
|
123
|
-
}
|