@fecommunity/reactpress 4.0.0-beta.10 → 4.0.0-beta.12
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 +21 -26
- package/out/bin/reactpress.js +30 -5
- package/out/core/services/theme-bootstrap.js +3 -2
- package/out/lib/doctor.js +9 -1
- package/out/lib/i18n/strings.js +42 -0
- package/out/lib/lifecycle.js +5 -0
- package/out/lib/project-logs.js +197 -0
- package/package.json +1 -1
- package/server/bin/reactpress-server.js +19 -0
- package/server/dist/app.module.js +5 -5
- package/server/dist/main.js +2 -0
- package/server/dist/modules/extension/extension.module.js +3 -1
- package/server/dist/modules/extension/preview-draft.store.js +1 -1
- package/server/dist/modules/extension/theme-registry.bridge.js +23 -1
- package/server/dist/modules/extension/theme.service.js +19 -19
- package/server/dist/modules/setting/setting.constant.js +3 -3
- package/server/dist/modules/setting/setting.service.js +9 -9
- package/server/package-lock.json +11 -3
- package/server/package.json +1 -1
- package/toolkit/package.json +13 -1
package/README.md
CHANGED
|
@@ -1,47 +1,42 @@
|
|
|
1
|
-
# @fecommunity/reactpress
|
|
1
|
+
# @fecommunity/reactpress
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
ReactPress 4 CLI — zero-dependency publishing platform. Initialize a local SQLite site and bundled API with a single command.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Monorepo docs: [github.com/fecommunity/reactpress](https://github.com/fecommunity/reactpress) · Chinese overview: [README-zh_CN.md](../README-zh_CN.md)
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Install
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install -g @fecommunity/reactpress
|
|
10
|
+
npm install -g @fecommunity/reactpress
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Requires Node.js 18+. On first install, `postinstall` downloads bundled server runtime dependencies (~1–2 minutes).
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
## 快速开始
|
|
15
|
+
## Quick start
|
|
18
16
|
|
|
19
17
|
```bash
|
|
20
|
-
mkdir my-
|
|
21
|
-
reactpress
|
|
22
|
-
reactpress-cli start
|
|
18
|
+
mkdir my-site && cd my-site
|
|
19
|
+
reactpress init
|
|
23
20
|
```
|
|
24
21
|
|
|
25
|
-
|
|
22
|
+
- Site: `http://localhost:3001`
|
|
23
|
+
- Admin: `http://localhost:3001/admin/` (`admin` / `admin`)
|
|
24
|
+
- API: `http://127.0.0.1:3002/api`
|
|
26
25
|
|
|
27
|
-
##
|
|
26
|
+
## Commands
|
|
28
27
|
|
|
29
|
-
|
|
|
30
|
-
|
|
31
|
-
| `reactpress
|
|
32
|
-
| `reactpress
|
|
33
|
-
| `reactpress
|
|
34
|
-
| `reactpress-cli restart` | 重启服务 |
|
|
35
|
-
| `reactpress-cli status` | 查看状态 |
|
|
36
|
-
| `reactpress-cli config [key] [value]` | 查看/修改配置 |
|
|
37
|
-
| `reactpress-cli config server.port 3003 --apply` | 改端口并重启 |
|
|
28
|
+
| Command | Description |
|
|
29
|
+
|---------|-------------|
|
|
30
|
+
| `reactpress init [dir]` | Initialize project (default command) |
|
|
31
|
+
| `reactpress doctor [dir]` | Diagnose Node.js, ports, database, and services |
|
|
32
|
+
| `reactpress doctor logs [dir]` | Tail API logs (error / request / response) for debugging |
|
|
38
33
|
|
|
39
|
-
##
|
|
34
|
+
## Requirements
|
|
40
35
|
|
|
41
36
|
- Node.js 18+
|
|
42
37
|
- macOS / Linux / Windows
|
|
43
|
-
-
|
|
38
|
+
- No Docker, nginx, or MySQL required (embedded SQLite + bundled API)
|
|
44
39
|
|
|
45
|
-
##
|
|
40
|
+
## License
|
|
46
41
|
|
|
47
42
|
MIT © FECommunity
|
package/out/bin/reactpress.js
CHANGED
|
@@ -20,6 +20,7 @@ const { t } = require('../lib/i18n');
|
|
|
20
20
|
const { describeProject } = require('../lib/project-type');
|
|
21
21
|
const { refreshBannerWithStartup } = require('../ui/banner-startup');
|
|
22
22
|
const { runDoctor } = require('../lib/doctor');
|
|
23
|
+
const { runDoctorLogs } = require('../lib/project-logs');
|
|
23
24
|
const LEGACY_COMMANDS = new Set([
|
|
24
25
|
'start',
|
|
25
26
|
'dev',
|
|
@@ -65,6 +66,8 @@ async function startServices(projectRoot) {
|
|
|
65
66
|
const code = await runLifecycleCommand('start', projectRoot);
|
|
66
67
|
if (code !== 0)
|
|
67
68
|
process.exit(code);
|
|
69
|
+
const { ensureDefaultTheme } = require('../core/services/theme-bootstrap');
|
|
70
|
+
await ensureDefaultTheme(projectRoot);
|
|
68
71
|
printRunningPanel(projectRoot);
|
|
69
72
|
try {
|
|
70
73
|
const themeBin = getThemeBin(projectRoot);
|
|
@@ -108,20 +111,42 @@ program
|
|
|
108
111
|
.action(async (directory, options) => {
|
|
109
112
|
await runInit(directory || '.', options);
|
|
110
113
|
});
|
|
111
|
-
program
|
|
112
|
-
.command('doctor
|
|
114
|
+
const doctorCommand = program
|
|
115
|
+
.command('doctor')
|
|
116
|
+
.description(t('cli.doctor.description'));
|
|
117
|
+
doctorCommand
|
|
118
|
+
.command('logs [directory]')
|
|
119
|
+
.description(t('cli.doctor.logs.description'))
|
|
120
|
+
.option('--tail <n>', t('cli.doctor.logs.tailOption'), '50')
|
|
121
|
+
.option('--source <name>', t('cli.doctor.logs.sourceOption'), 'error')
|
|
122
|
+
.option('--grep <pattern>', t('cli.doctor.logs.grepOption'))
|
|
123
|
+
.option('--list', t('cli.doctor.logs.listOption'))
|
|
124
|
+
.action(async (directory, options) => {
|
|
125
|
+
const projectRoot = path.resolve(directory || '.');
|
|
126
|
+
process.env.REACTPRESS_ORIGINAL_CWD = projectRoot;
|
|
127
|
+
const code = await runDoctorLogs(projectRoot, options);
|
|
128
|
+
process.exit(code);
|
|
129
|
+
});
|
|
130
|
+
doctorCommand
|
|
131
|
+
.command('check [directory]', { isDefault: true })
|
|
113
132
|
.description(t('cli.doctor.description'))
|
|
114
|
-
.
|
|
133
|
+
.option('--show-logs', t('cli.doctor.showLogs'))
|
|
134
|
+
.action(async (directory, options) => {
|
|
115
135
|
const projectRoot = path.resolve(directory || '.');
|
|
116
136
|
process.env.REACTPRESS_ORIGINAL_CWD = projectRoot;
|
|
117
|
-
const code = await runDoctor(projectRoot);
|
|
137
|
+
const code = await runDoctor(projectRoot, options);
|
|
118
138
|
process.exit(code);
|
|
119
139
|
});
|
|
120
140
|
program.on('--help', () => {
|
|
121
141
|
console.log('');
|
|
122
142
|
console.log(brand.bold(t('cli.help.examples')));
|
|
123
143
|
console.log(divider(40));
|
|
124
|
-
for (const line of [
|
|
144
|
+
for (const line of [
|
|
145
|
+
t('cli.help.init'),
|
|
146
|
+
t('cli.help.doctor'),
|
|
147
|
+
t('cli.help.doctorLogs'),
|
|
148
|
+
t('cli.help.zeroDep'),
|
|
149
|
+
]) {
|
|
125
150
|
console.log(brand.dim(line));
|
|
126
151
|
}
|
|
127
152
|
console.log('');
|
|
@@ -18,7 +18,7 @@ function writeActiveThemeManifest(projectRoot, themeId, themeDir) {
|
|
|
18
18
|
}
|
|
19
19
|
/** Install catalog featured theme when no resolvable theme exists (npm standalone init). */
|
|
20
20
|
async function ensureDefaultTheme(projectRoot) {
|
|
21
|
-
const { hasResolvableActiveTheme
|
|
21
|
+
const { hasResolvableActiveTheme } = require('../../lib/theme-runtime');
|
|
22
22
|
const { installThemeFromNpm } = require('../../lib/theme-install');
|
|
23
23
|
const { readThemeCatalog } = require('../../lib/theme-registry');
|
|
24
24
|
const { t } = require('../../lib/i18n');
|
|
@@ -27,7 +27,8 @@ async function ensureDefaultTheme(projectRoot) {
|
|
|
27
27
|
return { installed: false, skipped: true };
|
|
28
28
|
}
|
|
29
29
|
const catalog = readThemeCatalog(root);
|
|
30
|
-
const featured = catalog.themes.find((entry) => entry.featured && entry.npm) ??
|
|
30
|
+
const featured = catalog.themes.find((entry) => entry.featured && entry.npm) ??
|
|
31
|
+
catalog.themes.find((entry) => entry.npm);
|
|
31
32
|
if (!featured?.npm) {
|
|
32
33
|
return { installed: false, error: 'NO_CATALOG_THEME' };
|
|
33
34
|
}
|
package/out/lib/doctor.js
CHANGED
|
@@ -13,6 +13,7 @@ const { checkNginx } = require('./nginx');
|
|
|
13
13
|
const { envFileStatus } = require('./status');
|
|
14
14
|
const { describeProject } = require('./project-type');
|
|
15
15
|
const { t } = require('./i18n');
|
|
16
|
+
const { runDoctorLogs } = require('./project-logs');
|
|
16
17
|
function checkNodeVersion() {
|
|
17
18
|
const major = parseInt(process.versions.node.split('.')[0], 10);
|
|
18
19
|
if (major >= 18) {
|
|
@@ -301,7 +302,7 @@ async function runCheckWithSpinner(name, run) {
|
|
|
301
302
|
}
|
|
302
303
|
return result;
|
|
303
304
|
}
|
|
304
|
-
async function runDoctor(projectRoot) {
|
|
305
|
+
async function runDoctor(projectRoot, options = {}) {
|
|
305
306
|
const profile = await resolveProjectProfile(projectRoot);
|
|
306
307
|
applyProjectRuntimeEnv(profile);
|
|
307
308
|
const env = envFileStatus(projectRoot);
|
|
@@ -366,6 +367,13 @@ async function runDoctor(projectRoot) {
|
|
|
366
367
|
console.log(` ${brand.primary('→')} ${brand.dim(name)} ${brand.warn(fix)}`);
|
|
367
368
|
}
|
|
368
369
|
}
|
|
370
|
+
if (!options.showLogs) {
|
|
371
|
+
console.log('');
|
|
372
|
+
console.log(` ${brand.dim(t('doctor.logsHint'))}`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (options.showLogs) {
|
|
376
|
+
await runDoctorLogs(projectRoot, { tail: 40, source: 'error' });
|
|
369
377
|
}
|
|
370
378
|
console.log('');
|
|
371
379
|
return failed === 0 ? 0 : 1;
|
package/out/lib/i18n/strings.js
CHANGED
|
@@ -70,6 +70,12 @@ const STRINGS = {
|
|
|
70
70
|
'cli.help.nginx': ' reactpress nginx up Start reverse proxy (:80)',
|
|
71
71
|
'cli.status.description': 'Project, API, frontend, and Docker status',
|
|
72
72
|
'cli.doctor.description': 'Diagnose Node, Docker, ports, database, and API health',
|
|
73
|
+
'cli.doctor.showLogs': 'Show recent API error logs after diagnostics',
|
|
74
|
+
'cli.doctor.logs.description': 'View recent API logs (error, request, response)',
|
|
75
|
+
'cli.doctor.logs.tailOption': 'Number of lines to show per log file',
|
|
76
|
+
'cli.doctor.logs.sourceOption': 'Log source: error, request, response, or all',
|
|
77
|
+
'cli.doctor.logs.grepOption': 'Filter log lines by regex',
|
|
78
|
+
'cli.doctor.logs.listOption': 'List available log files only',
|
|
73
79
|
'cli.db.description': 'Database operations',
|
|
74
80
|
'cli.db.backup': 'Backup current project database with mysqldump',
|
|
75
81
|
'cli.db.backup.output': 'Output SQL file path',
|
|
@@ -80,6 +86,7 @@ const STRINGS = {
|
|
|
80
86
|
'cli.help.examples': 'Usage:',
|
|
81
87
|
'cli.help.init': ' reactpress init Initialize (default)',
|
|
82
88
|
'cli.help.doctor': ' reactpress doctor Diagnose environment issues',
|
|
89
|
+
'cli.help.doctorLogs': ' reactpress doctor logs Tail API logs for quick debugging',
|
|
83
90
|
'cli.help.zeroDep': ' Zero deps: Node.js 18+ only — no Docker, nginx, or MySQL',
|
|
84
91
|
'cli.plugin.description': 'Manage ReactPress plugins',
|
|
85
92
|
'cli.plugin.install.description': 'Install a local plugin into .reactpress/plugins',
|
|
@@ -408,6 +415,20 @@ const STRINGS = {
|
|
|
408
415
|
'doctor.checking': 'Checking {name}…',
|
|
409
416
|
'doctor.summary': '{passed} passed · {failed} failed · {total} total',
|
|
410
417
|
'doctor.fixesHeader': 'Suggested fixes',
|
|
418
|
+
'doctor.logsHint': 'Tip: run reactpress doctor logs --tail 100 to inspect API error logs',
|
|
419
|
+
'doctor.logs.title': 'ReactPress Doctor · Logs',
|
|
420
|
+
'doctor.logs.subtitle': 'API log viewer',
|
|
421
|
+
'doctor.logs.dir': 'Log dir {path}',
|
|
422
|
+
'doctor.logs.empty': 'No API log files found yet.',
|
|
423
|
+
'doctor.logs.emptyHint': 'Start the API with reactpress init — logs are written to .reactpress/logs/server/',
|
|
424
|
+
'doctor.logs.listHeader': 'Available log files',
|
|
425
|
+
'doctor.logs.fileHeader': '{file} (showing {count} line(s))',
|
|
426
|
+
'doctor.logs.noMatchingLines': 'No matching log lines.',
|
|
427
|
+
'doctor.logs.badPattern': 'Invalid grep pattern: {pattern}',
|
|
428
|
+
'doctor.logs.noPid': 'No API pid file at {path}',
|
|
429
|
+
'doctor.logs.pidRunning': 'API pid {pid} (recorded in {path})',
|
|
430
|
+
'doctor.logs.pidStale': 'Stale pid {pid} in {path} — process not running',
|
|
431
|
+
'doctor.logs.moreHint': 'Use --tail, --source, --grep, or --list for more control (see reactpress doctor logs --help)',
|
|
411
432
|
'status.title': 'ReactPress project status',
|
|
412
433
|
'status.dir': 'Project {path}',
|
|
413
434
|
'status.apiSource': 'API source {source}',
|
|
@@ -703,6 +724,12 @@ const STRINGS = {
|
|
|
703
724
|
'cli.help.nginx': ' reactpress nginx up 启动反向代理 (:80)',
|
|
704
725
|
'cli.status.description': '查看项目、API、前端、Docker 综合状态',
|
|
705
726
|
'cli.doctor.description': '诊断环境:Node、Docker、端口、数据库、API 健康',
|
|
727
|
+
'cli.doctor.showLogs': '诊断结束后显示最近的 API 错误日志',
|
|
728
|
+
'cli.doctor.logs.description': '查看 API 日志(error / request / response)',
|
|
729
|
+
'cli.doctor.logs.tailOption': '每个日志文件显示的行数',
|
|
730
|
+
'cli.doctor.logs.sourceOption': '日志类型:error、request、response 或 all',
|
|
731
|
+
'cli.doctor.logs.grepOption': '用正则过滤日志行',
|
|
732
|
+
'cli.doctor.logs.listOption': '仅列出可用日志文件',
|
|
706
733
|
'cli.db.description': '数据库运维',
|
|
707
734
|
'cli.db.backup': '使用 mysqldump 备份当前项目数据库',
|
|
708
735
|
'cli.db.backup.output': '输出 SQL 文件路径',
|
|
@@ -713,6 +740,7 @@ const STRINGS = {
|
|
|
713
740
|
'cli.help.examples': '用法:',
|
|
714
741
|
'cli.help.init': ' reactpress init 初始化(默认命令)',
|
|
715
742
|
'cli.help.doctor': ' reactpress doctor 诊断环境问题',
|
|
743
|
+
'cli.help.doctorLogs': ' reactpress doctor logs 查看 API 日志,快速定位问题',
|
|
716
744
|
'cli.help.zeroDep': ' 零依赖:仅需 Node.js 18+,无需 Docker、nginx 或 MySQL',
|
|
717
745
|
'cli.plugin.description': '管理 ReactPress 插件',
|
|
718
746
|
'cli.plugin.install.description': '安装本地插件到 .reactpress/plugins',
|
|
@@ -1040,6 +1068,20 @@ const STRINGS = {
|
|
|
1040
1068
|
'doctor.checking': '正在检查 {name}…',
|
|
1041
1069
|
'doctor.summary': '通过 {passed} · 失败 {failed} · 共 {total} 项',
|
|
1042
1070
|
'doctor.fixesHeader': '修复建议',
|
|
1071
|
+
'doctor.logsHint': '提示:运行 reactpress doctor logs --tail 100 查看 API 错误日志',
|
|
1072
|
+
'doctor.logs.title': 'ReactPress Doctor · 日志',
|
|
1073
|
+
'doctor.logs.subtitle': 'API 日志查看',
|
|
1074
|
+
'doctor.logs.dir': '日志目录 {path}',
|
|
1075
|
+
'doctor.logs.empty': '尚未找到 API 日志文件。',
|
|
1076
|
+
'doctor.logs.emptyHint': '运行 reactpress init 启动 API 后,日志会写入 .reactpress/logs/server/',
|
|
1077
|
+
'doctor.logs.listHeader': '可用日志文件',
|
|
1078
|
+
'doctor.logs.fileHeader': '{file}(显示 {count} 行)',
|
|
1079
|
+
'doctor.logs.noMatchingLines': '没有匹配的日志行。',
|
|
1080
|
+
'doctor.logs.badPattern': 'grep 正则无效:{pattern}',
|
|
1081
|
+
'doctor.logs.noPid': '未找到 API pid 文件:{path}',
|
|
1082
|
+
'doctor.logs.pidRunning': 'API 进程 pid {pid}(记录在 {path})',
|
|
1083
|
+
'doctor.logs.pidStale': 'pid 文件 {path} 中的 {pid} 已失效(进程未运行)',
|
|
1084
|
+
'doctor.logs.moreHint': '可用 --tail、--source、--grep、--list 进一步筛选(见 reactpress doctor logs --help)',
|
|
1043
1085
|
'status.title': 'ReactPress 项目状态',
|
|
1044
1086
|
'status.dir': '项目目录 {path}',
|
|
1045
1087
|
'status.apiSource': 'API 来源 {source}',
|
package/out/lib/lifecycle.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
// @ts-nocheck
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
4
6
|
const { spawn } = require('child_process');
|
|
5
7
|
const ora = require('ora');
|
|
6
8
|
const { ensureProjectEnvironment } = require('./bootstrap');
|
|
@@ -92,6 +94,8 @@ async function startApi(projectRoot, { wait = true } = {}) {
|
|
|
92
94
|
else {
|
|
93
95
|
console.log(t('lifecycle.startingBundledApi'));
|
|
94
96
|
}
|
|
97
|
+
const serverLogDir = path.join(projectRoot, '.reactpress', 'logs', 'server');
|
|
98
|
+
fs.mkdirSync(serverLogDir, { recursive: true });
|
|
95
99
|
const child = spawn(process.execPath, [getServerBin(projectRoot)], {
|
|
96
100
|
cwd: getServerDir(projectRoot),
|
|
97
101
|
detached: true,
|
|
@@ -99,6 +103,7 @@ async function startApi(projectRoot, { wait = true } = {}) {
|
|
|
99
103
|
env: {
|
|
100
104
|
...process.env,
|
|
101
105
|
REACTPRESS_ORIGINAL_CWD: projectRoot,
|
|
106
|
+
REACTPRESS_SERVER_LOG_DIR: serverLogDir,
|
|
102
107
|
},
|
|
103
108
|
});
|
|
104
109
|
child.unref();
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
// @ts-nocheck
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { brand, divider, gradientText, palette, sectionHeader } = require('../ui/theme');
|
|
7
|
+
const { getServerDir, getPidFile } = require('./paths');
|
|
8
|
+
const { readPid, isProcessRunning } = require('./process');
|
|
9
|
+
const { t } = require('./i18n');
|
|
10
|
+
const LOG_SOURCES = ['error', 'request', 'response'];
|
|
11
|
+
function getProjectServerLogDir(projectRoot) {
|
|
12
|
+
const preferred = path.join(projectRoot, '.reactpress', 'logs', 'server');
|
|
13
|
+
if (fs.existsSync(preferred)) {
|
|
14
|
+
return preferred;
|
|
15
|
+
}
|
|
16
|
+
const bundled = path.join(getServerDir(projectRoot), 'logs');
|
|
17
|
+
if (fs.existsSync(bundled)) {
|
|
18
|
+
return bundled;
|
|
19
|
+
}
|
|
20
|
+
return preferred;
|
|
21
|
+
}
|
|
22
|
+
function normalizeSource(source) {
|
|
23
|
+
const value = String(source || 'error').toLowerCase();
|
|
24
|
+
if (value === 'all')
|
|
25
|
+
return 'all';
|
|
26
|
+
if (LOG_SOURCES.includes(value))
|
|
27
|
+
return value;
|
|
28
|
+
return 'error';
|
|
29
|
+
}
|
|
30
|
+
function listLogFiles(logDir, source = 'error') {
|
|
31
|
+
const normalized = normalizeSource(source);
|
|
32
|
+
const sources = normalized === 'all' ? LOG_SOURCES : [normalized];
|
|
33
|
+
const files = [];
|
|
34
|
+
for (const name of sources) {
|
|
35
|
+
const dir = path.join(logDir, name);
|
|
36
|
+
if (!fs.existsSync(dir))
|
|
37
|
+
continue;
|
|
38
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
39
|
+
if (!entry.endsWith('.log'))
|
|
40
|
+
continue;
|
|
41
|
+
const fullPath = path.join(dir, entry);
|
|
42
|
+
let stat;
|
|
43
|
+
try {
|
|
44
|
+
stat = fs.statSync(fullPath);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (!stat.isFile())
|
|
50
|
+
continue;
|
|
51
|
+
files.push({
|
|
52
|
+
source: name,
|
|
53
|
+
path: fullPath,
|
|
54
|
+
name: entry,
|
|
55
|
+
mtimeMs: stat.mtimeMs,
|
|
56
|
+
size: stat.size,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
files.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
61
|
+
return files;
|
|
62
|
+
}
|
|
63
|
+
function readTailLines(filePath, lineCount = 50) {
|
|
64
|
+
const maxLines = Math.max(1, Math.min(Number(lineCount) || 50, 5000));
|
|
65
|
+
let content;
|
|
66
|
+
try {
|
|
67
|
+
content = fs.readFileSync(filePath, 'utf8');
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
if (!content)
|
|
73
|
+
return [];
|
|
74
|
+
const lines = content.split(/\r?\n/);
|
|
75
|
+
if (lines.length && lines[lines.length - 1] === '') {
|
|
76
|
+
lines.pop();
|
|
77
|
+
}
|
|
78
|
+
if (lines.length <= maxLines)
|
|
79
|
+
return lines;
|
|
80
|
+
return lines.slice(-maxLines);
|
|
81
|
+
}
|
|
82
|
+
function filterLines(lines, pattern) {
|
|
83
|
+
if (!pattern)
|
|
84
|
+
return lines;
|
|
85
|
+
let re;
|
|
86
|
+
try {
|
|
87
|
+
re = new RegExp(pattern, 'i');
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
const err = new Error(t('doctor.logs.badPattern', { pattern }));
|
|
91
|
+
err.code = 'REACTPRESS_LOG_BAD_PATTERN';
|
|
92
|
+
throw err;
|
|
93
|
+
}
|
|
94
|
+
return lines.filter((line) => re.test(line));
|
|
95
|
+
}
|
|
96
|
+
function formatBytes(size) {
|
|
97
|
+
if (size < 1024)
|
|
98
|
+
return `${size} B`;
|
|
99
|
+
if (size < 1024 * 1024)
|
|
100
|
+
return `${(size / 1024).toFixed(1)} KB`;
|
|
101
|
+
return `${(size / (1024 * 1024)).toFixed(1)} MB`;
|
|
102
|
+
}
|
|
103
|
+
function formatTimestamp(ms) {
|
|
104
|
+
return new Date(ms).toISOString().replace('T', ' ').slice(0, 19);
|
|
105
|
+
}
|
|
106
|
+
function printProcessHint(projectRoot) {
|
|
107
|
+
const pid = readPid(projectRoot);
|
|
108
|
+
const pidFile = getPidFile(projectRoot);
|
|
109
|
+
if (!pid) {
|
|
110
|
+
console.log(` ${brand.dim(t('doctor.logs.noPid', { path: pidFile }))}`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const alive = isProcessRunning(pid);
|
|
114
|
+
console.log(` ${brand.dim(alive
|
|
115
|
+
? t('doctor.logs.pidRunning', { pid, path: pidFile })
|
|
116
|
+
: t('doctor.logs.pidStale', { pid, path: pidFile }))}`);
|
|
117
|
+
}
|
|
118
|
+
async function runDoctorLogs(projectRoot, options = {}) {
|
|
119
|
+
const tail = Math.max(1, Math.min(parseInt(String(options.tail ?? '50'), 10) || 50, 5000));
|
|
120
|
+
const source = normalizeSource(options.source);
|
|
121
|
+
const logDir = getProjectServerLogDir(projectRoot);
|
|
122
|
+
const files = listLogFiles(logDir, source);
|
|
123
|
+
const w = 56;
|
|
124
|
+
console.log('');
|
|
125
|
+
console.log(` ${gradientText(t('doctor.logs.title'), [palette.primary, palette.accent], { bold: true })} ${brand.dim(t('doctor.logs.subtitle'))}`);
|
|
126
|
+
console.log(` ${brand.dim(t('doctor.project', { path: projectRoot }))}`);
|
|
127
|
+
console.log(` ${brand.dim(t('doctor.logs.dir', { path: logDir }))}`);
|
|
128
|
+
printProcessHint(projectRoot);
|
|
129
|
+
console.log(` ${divider(w)}`);
|
|
130
|
+
if (options.list) {
|
|
131
|
+
if (!files.length) {
|
|
132
|
+
console.log(` ${brand.warn(t('doctor.logs.empty'))}`);
|
|
133
|
+
console.log(` ${brand.dim(t('doctor.logs.emptyHint'))}`);
|
|
134
|
+
console.log('');
|
|
135
|
+
return 1;
|
|
136
|
+
}
|
|
137
|
+
console.log(sectionHeader(t('doctor.logs.listHeader')));
|
|
138
|
+
for (const file of files) {
|
|
139
|
+
const rel = path.join(file.source, file.name);
|
|
140
|
+
console.log(` ${brand.primary(rel)} ${brand.dim(`${formatBytes(file.size)} ${formatTimestamp(file.mtimeMs)}`)}`);
|
|
141
|
+
}
|
|
142
|
+
console.log('');
|
|
143
|
+
return 0;
|
|
144
|
+
}
|
|
145
|
+
if (!files.length) {
|
|
146
|
+
console.log(` ${brand.warn(t('doctor.logs.empty'))}`);
|
|
147
|
+
console.log(` ${brand.dim(t('doctor.logs.emptyHint'))}`);
|
|
148
|
+
console.log('');
|
|
149
|
+
return 1;
|
|
150
|
+
}
|
|
151
|
+
const grouped = new Map();
|
|
152
|
+
for (const file of files) {
|
|
153
|
+
if (!grouped.has(file.source))
|
|
154
|
+
grouped.set(file.source, file);
|
|
155
|
+
}
|
|
156
|
+
let printed = 0;
|
|
157
|
+
for (const name of source === 'all' ? LOG_SOURCES : [source]) {
|
|
158
|
+
const file = grouped.get(name);
|
|
159
|
+
if (!file)
|
|
160
|
+
continue;
|
|
161
|
+
let lines = readTailLines(file.path, tail);
|
|
162
|
+
lines = filterLines(lines, options.grep);
|
|
163
|
+
const rel = path.join(file.source, file.name);
|
|
164
|
+
console.log('');
|
|
165
|
+
console.log(sectionHeader(t('doctor.logs.fileHeader', {
|
|
166
|
+
file: rel,
|
|
167
|
+
count: lines.length,
|
|
168
|
+
})));
|
|
169
|
+
if (!lines.length) {
|
|
170
|
+
console.log(` ${brand.dim(t('doctor.logs.noMatchingLines'))}`);
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
for (const line of lines) {
|
|
174
|
+
const isError = /\[ERROR\]/i.test(line) || name === 'error';
|
|
175
|
+
console.log(` ${isError ? brand.warn(line) : brand.dim(line)}`);
|
|
176
|
+
}
|
|
177
|
+
printed += lines.length;
|
|
178
|
+
}
|
|
179
|
+
if (!printed) {
|
|
180
|
+
console.log(` ${brand.warn(t('doctor.logs.noMatchingLines'))}`);
|
|
181
|
+
console.log('');
|
|
182
|
+
return 1;
|
|
183
|
+
}
|
|
184
|
+
console.log('');
|
|
185
|
+
console.log(` ${brand.dim(t('doctor.logs.moreHint'))}`);
|
|
186
|
+
console.log('');
|
|
187
|
+
return 0;
|
|
188
|
+
}
|
|
189
|
+
module.exports = {
|
|
190
|
+
LOG_SOURCES,
|
|
191
|
+
getProjectServerLogDir,
|
|
192
|
+
listLogFiles,
|
|
193
|
+
readTailLines,
|
|
194
|
+
filterLines,
|
|
195
|
+
runDoctorLogs,
|
|
196
|
+
};
|
|
197
|
+
//# sourceMappingURL=project-logs.js.map
|
package/package.json
CHANGED
|
@@ -154,6 +154,22 @@ function startWithPM2() {
|
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
// Function to prepend bundled runtime paths so sibling toolkit/ can resolve server/node_modules.
|
|
158
|
+
function prependNodePath(...dirs) {
|
|
159
|
+
const Module = require('module');
|
|
160
|
+
const sep = path.delimiter;
|
|
161
|
+
const parts = (process.env.NODE_PATH || '').split(sep).filter(Boolean);
|
|
162
|
+
for (const dir of dirs) {
|
|
163
|
+
if (fs.existsSync(dir) && !parts.includes(dir)) {
|
|
164
|
+
parts.unshift(dir);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (parts.length) {
|
|
168
|
+
process.env.NODE_PATH = parts.join(sep);
|
|
169
|
+
Module._initPaths();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
157
173
|
// Function to start with regular Node.js
|
|
158
174
|
function startWithNode() {
|
|
159
175
|
// Check if the server is built
|
|
@@ -186,6 +202,9 @@ function startWithNode() {
|
|
|
186
202
|
// Set environment variables
|
|
187
203
|
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
|
188
204
|
|
|
205
|
+
// Bundled CLI layout: toolkit/ is a sibling of server/; expose server deps to toolkit requires.
|
|
206
|
+
prependNodePath(path.join(serverDir, 'node_modules'));
|
|
207
|
+
|
|
189
208
|
// Import and run the server (require() alone does not invoke main — see dist/main.js)
|
|
190
209
|
try {
|
|
191
210
|
const mainModule = require(distPath);
|
|
@@ -7,9 +7,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
};
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.AppModule = void 0;
|
|
10
|
-
const
|
|
10
|
+
const config_1 = require("../../toolkit/dist/config");
|
|
11
11
|
const common_1 = require("@nestjs/common");
|
|
12
|
-
const
|
|
12
|
+
const config_2 = require("@nestjs/config");
|
|
13
13
|
const typeorm_1 = require("@nestjs/typeorm");
|
|
14
14
|
const typeorm_options_1 = require("./database/typeorm-options");
|
|
15
15
|
const api_key_module_1 = require("./modules/api-key/api-key.module");
|
|
@@ -35,10 +35,10 @@ let AppModule = class AppModule {
|
|
|
35
35
|
AppModule = __decorate([
|
|
36
36
|
(0, common_1.Module)({
|
|
37
37
|
imports: [
|
|
38
|
-
|
|
38
|
+
config_2.ConfigModule.forRoot({ isGlobal: true, envFilePath: [config_1.config.file] }),
|
|
39
39
|
typeorm_1.TypeOrmModule.forRootAsync({
|
|
40
|
-
imports: [
|
|
41
|
-
inject: [
|
|
40
|
+
imports: [config_2.ConfigModule],
|
|
41
|
+
inject: [config_2.ConfigService],
|
|
42
42
|
useFactory: async (configService) => (0, typeorm_options_1.createTypeOrmOptions)(configService),
|
|
43
43
|
}),
|
|
44
44
|
user_module_1.UserModule,
|
package/server/dist/main.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.main = void 0;
|
|
3
4
|
require("./node-polyfills");
|
|
4
5
|
const bodyParser = require("body-parser");
|
|
5
6
|
const express = require("express");
|
|
@@ -141,6 +142,7 @@ const main = async () => {
|
|
|
141
142
|
process.exit(1);
|
|
142
143
|
}
|
|
143
144
|
};
|
|
145
|
+
exports.main = main;
|
|
144
146
|
const getProjectRoot = () => {
|
|
145
147
|
if (process.env.REACTPRESS_ORIGINAL_CWD) {
|
|
146
148
|
if (!(0, local_api_quiet_util_1.isLocalApiQuiet)()) {
|
|
@@ -27,7 +27,9 @@ let ExtensionModule = class ExtensionModule {
|
|
|
27
27
|
this.pluginLoader = pluginLoader;
|
|
28
28
|
}
|
|
29
29
|
onModuleInit() {
|
|
30
|
-
void this.themeService.ensureDefaultTheme()
|
|
30
|
+
void this.themeService.ensureDefaultTheme().catch((err) => {
|
|
31
|
+
console.warn('[ThemeService] ensureDefaultTheme skipped:', err instanceof Error ? err.message : err);
|
|
32
|
+
});
|
|
31
33
|
void this.pluginLoader.loadActivePlugins();
|
|
32
34
|
}
|
|
33
35
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getPreviewDraft = exports.putPreviewDraft = void 0;
|
|
4
|
-
const
|
|
4
|
+
const extension_1 = require("../../../../toolkit/dist/theme/extension");
|
|
5
5
|
const crypto_1 = require("crypto");
|
|
6
6
|
const TTL_MS = 30 * 60 * 1000;
|
|
7
7
|
const store = new Map();
|
|
@@ -5,11 +5,33 @@ const fs = require("fs");
|
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const project_root_util_1 = require("../../utils/project-root.util");
|
|
7
7
|
let cached = null;
|
|
8
|
+
function resolveBundledCliPackageRoot() {
|
|
9
|
+
const serverRoot = path.join(__dirname, '..', '..', '..');
|
|
10
|
+
const cliRoot = path.dirname(serverRoot);
|
|
11
|
+
const registryPath = path.join(cliRoot, 'out', 'lib', 'theme-registry.js');
|
|
12
|
+
if (fs.existsSync(registryPath)) {
|
|
13
|
+
return cliRoot;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const pkgPath = path.join(cliRoot, 'package.json');
|
|
17
|
+
if (!fs.existsSync(pkgPath))
|
|
18
|
+
return null;
|
|
19
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
20
|
+
if (pkg.name === '@fecommunity/reactpress') {
|
|
21
|
+
return cliRoot;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch (_a) {
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
8
28
|
function resolveThemeRegistryPath(monorepoRoot) {
|
|
29
|
+
const bundledCliRoot = resolveBundledCliPackageRoot();
|
|
9
30
|
const candidates = [
|
|
31
|
+
bundledCliRoot ? path.join(bundledCliRoot, 'out', 'lib', 'theme-registry.js') : null,
|
|
10
32
|
path.join(monorepoRoot, 'cli', 'out', 'lib', 'theme-registry.js'),
|
|
11
33
|
path.join(monorepoRoot, 'cli', 'lib', 'theme-registry.js'),
|
|
12
|
-
];
|
|
34
|
+
].filter(Boolean);
|
|
13
35
|
for (const candidate of candidates) {
|
|
14
36
|
if (fs.existsSync(candidate))
|
|
15
37
|
return candidate;
|
|
@@ -14,8 +14,8 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
14
14
|
var ThemeService_1;
|
|
15
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
16
|
exports.ThemeService = void 0;
|
|
17
|
-
const
|
|
18
|
-
const
|
|
17
|
+
const extension_1 = require("../../../../toolkit/dist/theme/extension");
|
|
18
|
+
const extension_2 = require("../../../../toolkit/dist/theme/extension");
|
|
19
19
|
const node_1 = require("../../../../toolkit/dist/theme/node");
|
|
20
20
|
const common_1 = require("@nestjs/common");
|
|
21
21
|
const typeorm_1 = require("@nestjs/typeorm");
|
|
@@ -26,7 +26,7 @@ const typeorm_2 = require("typeorm");
|
|
|
26
26
|
const project_root_util_1 = require("../../utils/project-root.util");
|
|
27
27
|
const setting_service_1 = require("../setting/setting.service");
|
|
28
28
|
const setting_entity_1 = require("../setting/setting.entity");
|
|
29
|
-
const
|
|
29
|
+
const theme_placeholder_cover_1 = require("../../utils/theme-placeholder-cover");
|
|
30
30
|
const theme_registry_bridge_1 = require("./theme-registry.bridge");
|
|
31
31
|
const preview_draft_store_1 = require("./preview-draft.store");
|
|
32
32
|
function projectRoot() {
|
|
@@ -119,14 +119,14 @@ let ThemeService = ThemeService_1 = class ThemeService {
|
|
|
119
119
|
return null;
|
|
120
120
|
try {
|
|
121
121
|
const raw = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
122
|
-
const parsed = (0,
|
|
122
|
+
const parsed = (0, extension_2.parseThemeManifest)(raw);
|
|
123
123
|
if (parsed)
|
|
124
124
|
return parsed;
|
|
125
125
|
if (raw && typeof raw.id === 'string') {
|
|
126
|
-
return (0,
|
|
126
|
+
return (0, extension_2.parseThemeManifest)(Object.assign(Object.assign({}, raw), { id: raw.id }));
|
|
127
127
|
}
|
|
128
128
|
const id = path.basename(themeDir);
|
|
129
|
-
return (0,
|
|
129
|
+
return (0, extension_2.parseThemeManifest)(Object.assign({ id, name: id, version: '1.0.0' }, raw));
|
|
130
130
|
}
|
|
131
131
|
catch (_a) {
|
|
132
132
|
return null;
|
|
@@ -222,11 +222,11 @@ let ThemeService = ThemeService_1 = class ThemeService {
|
|
|
222
222
|
}
|
|
223
223
|
async getThemeState() {
|
|
224
224
|
const row = await this.getSettingRow();
|
|
225
|
-
return (0,
|
|
225
|
+
return (0, extension_2.getThemeStateFromGlobalSetting)(this.parseGlobalSetting(row));
|
|
226
226
|
}
|
|
227
227
|
async saveThemeState(patch) {
|
|
228
228
|
const row = await this.getSettingRow();
|
|
229
|
-
const merged = (0,
|
|
229
|
+
const merged = (0, extension_2.mergeThemeStateIntoGlobalSetting)(this.parseGlobalSetting(row), patch);
|
|
230
230
|
row.globalSetting = JSON.stringify(merged);
|
|
231
231
|
await this.settingRepository.save(row);
|
|
232
232
|
return merged.theme;
|
|
@@ -367,7 +367,7 @@ let ThemeService = ThemeService_1 = class ThemeService {
|
|
|
367
367
|
const manifest = this.getThemeManifestForConfig(themeId);
|
|
368
368
|
if (!manifest)
|
|
369
369
|
throw new common_1.NotFoundException(`Theme "${themeId}" not found`);
|
|
370
|
-
const schema = (0,
|
|
370
|
+
const schema = (0, extension_2.getConfigurationSchemaFromManifest)(manifest, themeId);
|
|
371
371
|
return { themeId, schema };
|
|
372
372
|
}
|
|
373
373
|
async getThemeConfiguration(themeId) {
|
|
@@ -378,7 +378,7 @@ let ThemeService = ThemeService_1 = class ThemeService {
|
|
|
378
378
|
if (!manifest)
|
|
379
379
|
throw new common_1.NotFoundException(`Theme "${themeId}" not found`);
|
|
380
380
|
const row = await this.getSettingRow();
|
|
381
|
-
const configuration = (0,
|
|
381
|
+
const configuration = (0, extension_2.getMergedThemeConfiguration)(this.parseGlobalSetting(row), themeId, {
|
|
382
382
|
manifest,
|
|
383
383
|
});
|
|
384
384
|
return { themeId, configuration };
|
|
@@ -391,7 +391,7 @@ let ThemeService = ThemeService_1 = class ThemeService {
|
|
|
391
391
|
const manifest = this.getThemeManifestForConfig(themeId);
|
|
392
392
|
if (!manifest)
|
|
393
393
|
throw new common_1.NotFoundException(`Theme "${themeId}" not found`);
|
|
394
|
-
const schema = (0,
|
|
394
|
+
const schema = (0, extension_2.getConfigurationSchemaFromManifest)(manifest, themeId);
|
|
395
395
|
if (!schema) {
|
|
396
396
|
throw new common_1.BadRequestException(`Theme "${themeId}" does not declare options schema`);
|
|
397
397
|
}
|
|
@@ -399,21 +399,21 @@ let ThemeService = ThemeService_1 = class ThemeService {
|
|
|
399
399
|
const globalRaw = this.parseGlobalSetting(row);
|
|
400
400
|
const locale = (_a = options === null || options === void 0 ? void 0 : options.locale) !== null && _a !== void 0 ? _a : 'zh';
|
|
401
401
|
if (options === null || options === void 0 ? void 0 : options.replace) {
|
|
402
|
-
const seed = (_b = (0,
|
|
403
|
-
const validation = (0,
|
|
402
|
+
const seed = (_b = (0, extension_2.getThemeConfigurationSeed)(themeId, locale)) !== null && _b !== void 0 ? _b : (0, extension_2.getMergedThemeConfiguration)(globalRaw, themeId, { locale, manifest });
|
|
403
|
+
const validation = (0, extension_2.validateThemeConfiguration)(schema, seed);
|
|
404
404
|
if (!validation.valid) {
|
|
405
405
|
throw new common_1.BadRequestException((_c = validation.message) !== null && _c !== void 0 ? _c : 'Invalid configuration');
|
|
406
406
|
}
|
|
407
|
-
const mergedGlobal = (0,
|
|
407
|
+
const mergedGlobal = (0, extension_2.mergeConfigIntoGlobalSetting)(globalRaw, themeId, seed);
|
|
408
408
|
row.globalSetting = JSON.stringify(mergedGlobal);
|
|
409
409
|
await this.settingRepository.save(row);
|
|
410
410
|
return { themeId, configuration: seed };
|
|
411
411
|
}
|
|
412
|
-
const result = (0,
|
|
412
|
+
const result = (0, extension_2.validateAndMergeThemeConfiguration)(themeId, globalRaw, patch, manifest);
|
|
413
413
|
if (!result.ok) {
|
|
414
414
|
throw new common_1.BadRequestException('message' in result ? result.message : 'Invalid configuration');
|
|
415
415
|
}
|
|
416
|
-
const mergedGlobal = (0,
|
|
416
|
+
const mergedGlobal = (0, extension_2.mergeConfigIntoGlobalSetting)(globalRaw, themeId, result.config);
|
|
417
417
|
row.globalSetting = JSON.stringify(mergedGlobal);
|
|
418
418
|
await this.settingRepository.save(row);
|
|
419
419
|
return { themeId, configuration: result.config };
|
|
@@ -798,7 +798,7 @@ let ThemeService = ThemeService_1 = class ThemeService {
|
|
|
798
798
|
var _a;
|
|
799
799
|
const manifest = this.getThemeManifestFromSources(id);
|
|
800
800
|
const defaults = this.themeAppearanceDefaults(manifest);
|
|
801
|
-
return (0,
|
|
801
|
+
return (0, theme_placeholder_cover_1.buildThemePlaceholderCoverSvg)({
|
|
802
802
|
id,
|
|
803
803
|
name: (_a = manifest === null || manifest === void 0 ? void 0 : manifest.name) !== null && _a !== void 0 ? _a : id,
|
|
804
804
|
primary: defaults.primaryColor,
|
|
@@ -916,7 +916,7 @@ let ThemeService = ThemeService_1 = class ThemeService {
|
|
|
916
916
|
state.activeTheme,
|
|
917
917
|
'hello-world',
|
|
918
918
|
...state.installedThemes,
|
|
919
|
-
|
|
919
|
+
extension_2.defaultSiteThemeState.activeTheme,
|
|
920
920
|
];
|
|
921
921
|
const seen = new Set();
|
|
922
922
|
for (const id of candidates) {
|
|
@@ -948,7 +948,7 @@ let ThemeService = ThemeService_1 = class ThemeService {
|
|
|
948
948
|
activeTheme = saved.activeTheme;
|
|
949
949
|
}
|
|
950
950
|
else if (!activeTheme) {
|
|
951
|
-
const saved = await this.saveThemeState(
|
|
951
|
+
const saved = await this.saveThemeState(extension_2.defaultSiteThemeState);
|
|
952
952
|
activeTheme = saved.activeTheme;
|
|
953
953
|
}
|
|
954
954
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.UNPROTECTED_KEYS = exports.settings = exports.i18n = void 0;
|
|
4
|
-
const
|
|
4
|
+
const extension_1 = require("../../../../toolkit/dist/theme/extension");
|
|
5
5
|
var config_1 = require("../../../../toolkit/dist/config");
|
|
6
6
|
Object.defineProperty(exports, "i18n", { enumerable: true, get: function () { return config_1.messages; } });
|
|
7
|
-
exports.settings =
|
|
8
|
-
exports.UNPROTECTED_KEYS = [...
|
|
7
|
+
exports.settings = extension_1.systemGlobalSettingDefaults;
|
|
8
|
+
exports.UNPROTECTED_KEYS = [...extension_1.PUBLIC_SETTING_KEYS];
|
|
9
9
|
//# sourceMappingURL=setting.constant.js.map
|
|
@@ -13,7 +13,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.SettingService = void 0;
|
|
16
|
-
const
|
|
16
|
+
const extension_1 = require("../../../../toolkit/dist/theme/extension");
|
|
17
17
|
const common_1 = require("@nestjs/common");
|
|
18
18
|
const config_1 = require("@nestjs/config");
|
|
19
19
|
const typeorm_1 = require("@nestjs/typeorm");
|
|
@@ -121,8 +121,8 @@ let SettingService = class SettingService {
|
|
|
121
121
|
catch (_b) {
|
|
122
122
|
data = {};
|
|
123
123
|
}
|
|
124
|
-
const themeState = (0,
|
|
125
|
-
const defaultTheme = (0,
|
|
124
|
+
const themeState = (0, extension_1.getThemeStateFromGlobalSetting)(data);
|
|
125
|
+
const defaultTheme = (0, extension_1.getThemeStateFromGlobalSetting)(setting_constant_1.settings);
|
|
126
126
|
const mergedTheme = Object.assign(Object.assign(Object.assign({}, defaultTheme), themeState), { mods: Object.assign(Object.assign({}, defaultTheme.mods), themeState.mods), installedThemes: ((_a = themeState.installedThemes) === null || _a === void 0 ? void 0 : _a.length) > 0 ? themeState.installedThemes : defaultTheme.installedThemes });
|
|
127
127
|
const mergedSettings = Object.assign(Object.assign({}, data), { theme: mergedTheme });
|
|
128
128
|
const mergedSettingsString = JSON.stringify(mergedSettings);
|
|
@@ -166,7 +166,7 @@ let SettingService = class SettingService {
|
|
|
166
166
|
catch (_a) {
|
|
167
167
|
gs = {};
|
|
168
168
|
}
|
|
169
|
-
const { global, changed } = (0,
|
|
169
|
+
const { global, changed } = (0, extension_1.migrateLegacyAppearanceToThemeMods)(gs, target);
|
|
170
170
|
if (changed) {
|
|
171
171
|
target.globalSetting = JSON.stringify(global);
|
|
172
172
|
await this.settingRepository.save(target);
|
|
@@ -184,14 +184,14 @@ let SettingService = class SettingService {
|
|
|
184
184
|
catch (_a) {
|
|
185
185
|
gs = {};
|
|
186
186
|
}
|
|
187
|
-
const themeState = (0,
|
|
187
|
+
const themeState = (0, extension_1.getThemeStateFromGlobalSetting)(gs);
|
|
188
188
|
const activeTheme = themeState.activeTheme || 'hello-world';
|
|
189
189
|
const config = gs.config && typeof gs.config === 'object'
|
|
190
190
|
? Object.assign({}, gs.config)
|
|
191
191
|
: {};
|
|
192
192
|
let changed = false;
|
|
193
193
|
for (const themeId of THEMES_WITH_CONFIGURATION) {
|
|
194
|
-
const seed = (0,
|
|
194
|
+
const seed = (0, extension_1.getThemeConfigurationSeed)(themeId, 'zh');
|
|
195
195
|
if (!seed)
|
|
196
196
|
continue;
|
|
197
197
|
const current = config[themeId];
|
|
@@ -221,16 +221,16 @@ let SettingService = class SettingService {
|
|
|
221
221
|
if (isAdmin && !innerInvoke) {
|
|
222
222
|
return res;
|
|
223
223
|
}
|
|
224
|
-
const effective = (0,
|
|
224
|
+
const effective = (0, extension_1.resolveEffectiveSettingRow)(res);
|
|
225
225
|
if (innerInvoke) {
|
|
226
226
|
return effective;
|
|
227
227
|
}
|
|
228
|
-
return (0,
|
|
228
|
+
return (0, extension_1.buildPublicSettingView)(effective);
|
|
229
229
|
}
|
|
230
230
|
async update(setting) {
|
|
231
231
|
var _a;
|
|
232
232
|
const old = await this.ensureSettingRow();
|
|
233
|
-
const sanitized = (0,
|
|
233
|
+
const sanitized = (0, extension_1.pickSystemSettingPatch)(setting);
|
|
234
234
|
if ('smtpPass' in sanitized && !String((_a = sanitized.smtpPass) !== null && _a !== void 0 ? _a : '').trim()) {
|
|
235
235
|
delete sanitized.smtpPass;
|
|
236
236
|
}
|
package/server/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fecommunity/reactpress-server",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.12",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@fecommunity/reactpress-server",
|
|
9
|
-
"version": "4.0.0-beta.
|
|
9
|
+
"version": "4.0.0-beta.12",
|
|
10
10
|
"deprecated": "Use the built-in API in @fecommunity/reactpress (CLI). Standalone server publishing stopped from 3.1.",
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"dependencies": {
|
|
@@ -91,7 +91,15 @@
|
|
|
91
91
|
},
|
|
92
92
|
"../toolkit": {
|
|
93
93
|
"name": "@fecommunity/reactpress-toolkit",
|
|
94
|
-
"version": "4.0.0-beta.
|
|
94
|
+
"version": "4.0.0-beta.12",
|
|
95
|
+
"dependencies": {
|
|
96
|
+
"ajv": "^8.17.1",
|
|
97
|
+
"axios": "^1.12.2",
|
|
98
|
+
"dotenv": "^8.6.0",
|
|
99
|
+
"fs-extra": "^10.0.0",
|
|
100
|
+
"lodash": "^4.17.21",
|
|
101
|
+
"swagger-typescript-api": "^12.0.4"
|
|
102
|
+
}
|
|
95
103
|
},
|
|
96
104
|
"node_modules/@angular-devkit/core": {
|
|
97
105
|
"version": "7.3.8",
|
package/server/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fecommunity/reactpress-server",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.12",
|
|
4
4
|
"deprecated": "Use the built-in API in @fecommunity/reactpress (CLI). Standalone server publishing stopped from 3.1.",
|
|
5
5
|
"description": "ReactPress NestJS CMS API — headless REST for the publishing platform (MySQL or SQLite)",
|
|
6
6
|
"author": "fecommunity",
|
package/toolkit/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fecommunity/reactpress-toolkit",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.12",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -44,6 +44,10 @@
|
|
|
44
44
|
"types": "./dist/theme/node.d.ts",
|
|
45
45
|
"default": "./dist/theme/node.js"
|
|
46
46
|
},
|
|
47
|
+
"./theme/extension": {
|
|
48
|
+
"types": "./dist/theme/extension/index.d.ts",
|
|
49
|
+
"default": "./dist/theme/extension/index.js"
|
|
50
|
+
},
|
|
47
51
|
"./theme/providers": {
|
|
48
52
|
"types": "./dist/theme/providers/index.d.ts",
|
|
49
53
|
"default": "./dist/theme/providers/index.js"
|
|
@@ -92,5 +96,13 @@
|
|
|
92
96
|
"types": "./dist/plugin/node.d.ts",
|
|
93
97
|
"default": "./dist/plugin/node.js"
|
|
94
98
|
}
|
|
99
|
+
},
|
|
100
|
+
"dependencies": {
|
|
101
|
+
"ajv": "^8.17.1",
|
|
102
|
+
"axios": "^1.12.2",
|
|
103
|
+
"dotenv": "^8.6.0",
|
|
104
|
+
"fs-extra": "^10.0.0",
|
|
105
|
+
"lodash": "^4.17.21",
|
|
106
|
+
"swagger-typescript-api": "^12.0.4"
|
|
95
107
|
}
|
|
96
108
|
}
|