coding-tool-x 3.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/CHANGELOG.md +599 -0
- package/LICENSE +21 -0
- package/README.md +439 -0
- package/bin/ctx.js +8 -0
- package/dist/web/assets/Analytics-DN_YsnkW.js +39 -0
- package/dist/web/assets/Analytics-DuYvId7u.css +1 -0
- package/dist/web/assets/ConfigTemplates-Bidwfdf2.css +1 -0
- package/dist/web/assets/ConfigTemplates-DpXIMy0p.js +1 -0
- package/dist/web/assets/Home-38JTUlYt.js +1 -0
- package/dist/web/assets/Home-CjupSEWE.css +1 -0
- package/dist/web/assets/PluginManager-CX2tgq2H.js +1 -0
- package/dist/web/assets/PluginManager-ROyoZ-6m.css +1 -0
- package/dist/web/assets/ProjectList-C1lDcsn6.js +1 -0
- package/dist/web/assets/ProjectList-oJIyIRkP.css +1 -0
- package/dist/web/assets/SessionList-C55tjV7i.css +1 -0
- package/dist/web/assets/SessionList-CZ7T6rVx.js +1 -0
- package/dist/web/assets/SkillManager-D7pd-d_P.css +1 -0
- package/dist/web/assets/SkillManager-DLN9f79y.js +1 -0
- package/dist/web/assets/WorkspaceManager-CrwgQgmP.css +1 -0
- package/dist/web/assets/WorkspaceManager-DxlHZkpZ.js +1 -0
- package/dist/web/assets/icons-DRrXwWZi.js +1 -0
- package/dist/web/assets/index-CetESrXw.css +1 -0
- package/dist/web/assets/index-Cfvn-2Gb.js +2 -0
- package/dist/web/assets/markdown-BfC0goYb.css +10 -0
- package/dist/web/assets/markdown-C9MYpaSi.js +1 -0
- package/dist/web/assets/naive-ui-DlpKk-8M.js +1 -0
- package/dist/web/assets/vendors-DMjSfzlv.js +7 -0
- package/dist/web/assets/vue-vendor-DET08QYg.js +45 -0
- package/dist/web/favicon.ico +0 -0
- package/dist/web/index.html +20 -0
- package/dist/web/logo.png +0 -0
- package/docs/bannel.png +0 -0
- package/docs/home.png +0 -0
- package/docs/logo.png +0 -0
- package/docs/model-redirection.md +251 -0
- package/docs/multi-channel-load-balancing.md +249 -0
- package/package.json +80 -0
- package/src/commands/channels.js +551 -0
- package/src/commands/cli-type.js +101 -0
- package/src/commands/daemon.js +365 -0
- package/src/commands/doctor.js +333 -0
- package/src/commands/export-config.js +205 -0
- package/src/commands/list.js +222 -0
- package/src/commands/logs.js +261 -0
- package/src/commands/plugin.js +585 -0
- package/src/commands/port-config.js +135 -0
- package/src/commands/proxy-control.js +264 -0
- package/src/commands/proxy.js +152 -0
- package/src/commands/resume.js +137 -0
- package/src/commands/search.js +190 -0
- package/src/commands/security.js +37 -0
- package/src/commands/stats.js +398 -0
- package/src/commands/switch.js +48 -0
- package/src/commands/toggle-proxy.js +247 -0
- package/src/commands/ui.js +99 -0
- package/src/commands/update.js +97 -0
- package/src/commands/workspace.js +454 -0
- package/src/config/default.js +69 -0
- package/src/config/loader.js +149 -0
- package/src/config/model-metadata.js +167 -0
- package/src/config/model-metadata.json +125 -0
- package/src/config/model-pricing.js +35 -0
- package/src/config/paths.js +190 -0
- package/src/index.js +680 -0
- package/src/plugins/constants.js +15 -0
- package/src/plugins/event-bus.js +54 -0
- package/src/plugins/manifest-validator.js +129 -0
- package/src/plugins/plugin-api.js +128 -0
- package/src/plugins/plugin-installer.js +601 -0
- package/src/plugins/plugin-loader.js +229 -0
- package/src/plugins/plugin-manager.js +170 -0
- package/src/plugins/registry.js +152 -0
- package/src/plugins/schema/plugin-manifest.json +115 -0
- package/src/reset-config.js +94 -0
- package/src/server/api/agents.js +826 -0
- package/src/server/api/aliases.js +36 -0
- package/src/server/api/channels.js +368 -0
- package/src/server/api/claude-hooks.js +480 -0
- package/src/server/api/codex-channels.js +417 -0
- package/src/server/api/codex-projects.js +104 -0
- package/src/server/api/codex-proxy.js +195 -0
- package/src/server/api/codex-sessions.js +483 -0
- package/src/server/api/codex-statistics.js +57 -0
- package/src/server/api/commands.js +482 -0
- package/src/server/api/config-export.js +212 -0
- package/src/server/api/config-registry.js +357 -0
- package/src/server/api/config-sync.js +155 -0
- package/src/server/api/config-templates.js +248 -0
- package/src/server/api/config.js +521 -0
- package/src/server/api/convert.js +260 -0
- package/src/server/api/dashboard.js +142 -0
- package/src/server/api/env.js +144 -0
- package/src/server/api/favorites.js +77 -0
- package/src/server/api/gemini-channels.js +366 -0
- package/src/server/api/gemini-projects.js +91 -0
- package/src/server/api/gemini-proxy.js +173 -0
- package/src/server/api/gemini-sessions.js +376 -0
- package/src/server/api/gemini-statistics.js +57 -0
- package/src/server/api/health-check.js +31 -0
- package/src/server/api/mcp.js +399 -0
- package/src/server/api/opencode-channels.js +419 -0
- package/src/server/api/opencode-projects.js +99 -0
- package/src/server/api/opencode-proxy.js +207 -0
- package/src/server/api/opencode-sessions.js +327 -0
- package/src/server/api/opencode-statistics.js +57 -0
- package/src/server/api/plugins.js +463 -0
- package/src/server/api/pm2-autostart.js +269 -0
- package/src/server/api/projects.js +124 -0
- package/src/server/api/prompts.js +279 -0
- package/src/server/api/proxy.js +306 -0
- package/src/server/api/security.js +53 -0
- package/src/server/api/sessions.js +514 -0
- package/src/server/api/settings.js +142 -0
- package/src/server/api/skills.js +570 -0
- package/src/server/api/statistics.js +238 -0
- package/src/server/api/ui-config.js +64 -0
- package/src/server/api/workspaces.js +456 -0
- package/src/server/codex-proxy-server.js +681 -0
- package/src/server/dev-server.js +26 -0
- package/src/server/gemini-proxy-server.js +610 -0
- package/src/server/index.js +422 -0
- package/src/server/opencode-proxy-server.js +4771 -0
- package/src/server/proxy-server.js +669 -0
- package/src/server/services/agents-service.js +1137 -0
- package/src/server/services/alias.js +71 -0
- package/src/server/services/channel-health.js +234 -0
- package/src/server/services/channel-scheduler.js +240 -0
- package/src/server/services/channels.js +447 -0
- package/src/server/services/codex-channels.js +705 -0
- package/src/server/services/codex-config.js +90 -0
- package/src/server/services/codex-parser.js +322 -0
- package/src/server/services/codex-sessions.js +936 -0
- package/src/server/services/codex-settings-manager.js +619 -0
- package/src/server/services/codex-speed-test-template.json +24 -0
- package/src/server/services/codex-statistics-service.js +161 -0
- package/src/server/services/commands-service.js +574 -0
- package/src/server/services/config-export-service.js +1165 -0
- package/src/server/services/config-registry-service.js +828 -0
- package/src/server/services/config-sync-manager.js +941 -0
- package/src/server/services/config-sync-service.js +504 -0
- package/src/server/services/config-templates-service.js +913 -0
- package/src/server/services/enhanced-cache.js +196 -0
- package/src/server/services/env-checker.js +409 -0
- package/src/server/services/env-manager.js +436 -0
- package/src/server/services/favorites.js +165 -0
- package/src/server/services/format-converter.js +620 -0
- package/src/server/services/gemini-channels.js +459 -0
- package/src/server/services/gemini-config.js +73 -0
- package/src/server/services/gemini-sessions.js +689 -0
- package/src/server/services/gemini-settings-manager.js +263 -0
- package/src/server/services/gemini-statistics-service.js +157 -0
- package/src/server/services/health-check.js +85 -0
- package/src/server/services/mcp-client.js +790 -0
- package/src/server/services/mcp-service.js +1732 -0
- package/src/server/services/model-detector.js +1245 -0
- package/src/server/services/network-access.js +80 -0
- package/src/server/services/opencode-channels.js +366 -0
- package/src/server/services/opencode-gateway-adapters.js +1168 -0
- package/src/server/services/opencode-gateway-converter.js +639 -0
- package/src/server/services/opencode-sessions.js +931 -0
- package/src/server/services/opencode-settings-manager.js +478 -0
- package/src/server/services/opencode-statistics-service.js +161 -0
- package/src/server/services/plugins-service.js +1268 -0
- package/src/server/services/prompts-service.js +534 -0
- package/src/server/services/proxy-runtime.js +79 -0
- package/src/server/services/repo-scanner-base.js +708 -0
- package/src/server/services/request-logger.js +130 -0
- package/src/server/services/response-decoder.js +21 -0
- package/src/server/services/security-config.js +131 -0
- package/src/server/services/session-cache.js +127 -0
- package/src/server/services/session-converter.js +577 -0
- package/src/server/services/sessions.js +900 -0
- package/src/server/services/settings-manager.js +163 -0
- package/src/server/services/skill-service.js +1482 -0
- package/src/server/services/speed-test.js +1146 -0
- package/src/server/services/statistics-service.js +1043 -0
- package/src/server/services/ui-config.js +132 -0
- package/src/server/services/workspace-service.js +830 -0
- package/src/server/utils/pricing.js +73 -0
- package/src/server/websocket-server.js +513 -0
- package/src/ui/menu.js +139 -0
- package/src/ui/prompts.js +100 -0
- package/src/utils/format.js +43 -0
- package/src/utils/port-helper.js +108 -0
- package/src/utils/session.js +240 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const { getStatistics, getDailyStatistics, getTodayStatistics, getTrendStatistics, getAvailableFilters } = require('../services/statistics-service');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 获取总体统计数据
|
|
7
|
+
* GET /api/statistics/summary
|
|
8
|
+
*/
|
|
9
|
+
router.get('/summary', (req, res) => {
|
|
10
|
+
try {
|
|
11
|
+
const stats = getStatistics();
|
|
12
|
+
res.json(stats);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
console.error('Failed to get statistics:', error);
|
|
15
|
+
res.status(500).json({ error: 'Failed to get statistics' });
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 获取今日统计数据
|
|
21
|
+
* GET /api/statistics/today
|
|
22
|
+
*/
|
|
23
|
+
router.get('/today', (req, res) => {
|
|
24
|
+
try {
|
|
25
|
+
const stats = getTodayStatistics();
|
|
26
|
+
res.json(stats);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error('Failed to get today statistics:', error);
|
|
29
|
+
res.status(500).json({ error: 'Failed to get today statistics' });
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 获取指定日期的统计数据
|
|
35
|
+
* GET /api/statistics/daily/:date
|
|
36
|
+
*
|
|
37
|
+
* @param {string} date - 日期,格式:YYYY-MM-DD
|
|
38
|
+
*/
|
|
39
|
+
router.get('/daily/:date', (req, res) => {
|
|
40
|
+
try {
|
|
41
|
+
const { date } = req.params;
|
|
42
|
+
|
|
43
|
+
// 验证日期格式
|
|
44
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
|
45
|
+
return res.status(400).json({ error: 'Invalid date format. Expected YYYY-MM-DD' });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const stats = getDailyStatistics(date);
|
|
49
|
+
res.json(stats);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error('Failed to get daily statistics:', error);
|
|
52
|
+
res.status(500).json({ error: 'Failed to get daily statistics' });
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 获取最近N天的统计数据
|
|
58
|
+
* GET /api/statistics/recent?days=7
|
|
59
|
+
*
|
|
60
|
+
* @query {number} days - 天数,默认7天
|
|
61
|
+
*/
|
|
62
|
+
router.get('/recent', (req, res) => {
|
|
63
|
+
try {
|
|
64
|
+
const days = parseInt(req.query.days) || 7;
|
|
65
|
+
|
|
66
|
+
if (days < 1 || days > 90) {
|
|
67
|
+
return res.status(400).json({ error: 'Days must be between 1 and 90' });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const result = [];
|
|
71
|
+
const today = new Date();
|
|
72
|
+
|
|
73
|
+
for (let i = 0; i < days; i++) {
|
|
74
|
+
const date = new Date(today);
|
|
75
|
+
date.setDate(date.getDate() - i);
|
|
76
|
+
const dateStr = date.toISOString().split('T')[0];
|
|
77
|
+
const stats = getDailyStatistics(dateStr);
|
|
78
|
+
result.push({
|
|
79
|
+
date: dateStr,
|
|
80
|
+
...stats
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
res.json(result);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error('Failed to get recent statistics:', error);
|
|
87
|
+
res.status(500).json({ error: 'Failed to get recent statistics' });
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 获取可用的过滤器选项
|
|
93
|
+
* GET /api/statistics/filters?startDate=&endDate=
|
|
94
|
+
*/
|
|
95
|
+
router.get('/filters', (req, res) => {
|
|
96
|
+
try {
|
|
97
|
+
const { startDate, endDate } = req.query;
|
|
98
|
+
if (!startDate || !endDate) {
|
|
99
|
+
return res.status(400).json({ error: 'startDate and endDate are required' });
|
|
100
|
+
}
|
|
101
|
+
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
|
102
|
+
if (!dateRegex.test(startDate) || !dateRegex.test(endDate)) {
|
|
103
|
+
return res.status(400).json({ error: 'Invalid date format. Expected YYYY-MM-DD' });
|
|
104
|
+
}
|
|
105
|
+
const result = getAvailableFilters(startDate, endDate);
|
|
106
|
+
res.json(result);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.error('Failed to get available filters:', error);
|
|
109
|
+
res.status(500).json({ error: 'Failed to get available filters' });
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 获取趋势统计数据
|
|
115
|
+
* GET /api/statistics/trend
|
|
116
|
+
*
|
|
117
|
+
* @query {string} startDate - YYYY-MM-DD (required)
|
|
118
|
+
* @query {string} endDate - YYYY-MM-DD (required)
|
|
119
|
+
* @query {string} granularity - 'day' | 'hour' (default: 'day')
|
|
120
|
+
* @query {string} groupBy - 'model' | 'channel' | 'toolType' (default: 'model')
|
|
121
|
+
* @query {string} metric - 'tokens' | 'cost' | 'requests' (default: 'tokens')
|
|
122
|
+
* @query {string} filterToolType - optional filter
|
|
123
|
+
* @query {string} filterChannel - optional filter
|
|
124
|
+
* @query {string} filterModel - optional filter
|
|
125
|
+
*/
|
|
126
|
+
router.get('/trend', async (req, res) => {
|
|
127
|
+
try {
|
|
128
|
+
const {
|
|
129
|
+
startDate, endDate, granularity = 'day', step = 1, groupBy = 'model', metric = 'tokens',
|
|
130
|
+
filterToolType, filterChannel, filterModel
|
|
131
|
+
} = req.query;
|
|
132
|
+
|
|
133
|
+
if (!startDate || !endDate) {
|
|
134
|
+
return res.status(400).json({ error: 'startDate and endDate are required' });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
|
138
|
+
if (!dateRegex.test(startDate) || !dateRegex.test(endDate)) {
|
|
139
|
+
return res.status(400).json({ error: 'Invalid date format. Expected YYYY-MM-DD' });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const start = new Date(startDate);
|
|
143
|
+
const end = new Date(endDate);
|
|
144
|
+
const diffDays = Math.round((end - start) / (1000 * 60 * 60 * 24)) + 1;
|
|
145
|
+
|
|
146
|
+
if (diffDays < 1) {
|
|
147
|
+
return res.status(400).json({ error: 'endDate must be >= startDate' });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (granularity === 'hour' && diffDays > 7) {
|
|
151
|
+
return res.status(400).json({ error: 'Hour granularity is limited to 7 days' });
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (diffDays > 90) {
|
|
155
|
+
return res.status(400).json({ error: 'Date range cannot exceed 90 days' });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const filters = {
|
|
159
|
+
toolType: filterToolType || null,
|
|
160
|
+
channel: filterChannel || null,
|
|
161
|
+
model: filterModel || null
|
|
162
|
+
};
|
|
163
|
+
const result = await getTrendStatistics({ startDate, endDate, granularity, step, groupBy, metric, filters });
|
|
164
|
+
res.json({ success: true, ...result });
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.error('Failed to get trend statistics:', error);
|
|
167
|
+
res.status(500).json({ error: 'Failed to get trend statistics' });
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* 导出趋势统计数据
|
|
173
|
+
* GET /api/statistics/trend/export
|
|
174
|
+
*
|
|
175
|
+
* @query {string} startDate - YYYY-MM-DD (required)
|
|
176
|
+
* @query {string} endDate - YYYY-MM-DD (required)
|
|
177
|
+
* @query {string} granularity - 'day' | 'hour' (default: 'day')
|
|
178
|
+
* @query {string} groupBy - 'model' | 'channel' | 'toolType' (default: 'model')
|
|
179
|
+
* @query {string} metric - 'tokens' | 'cost' | 'requests' (default: 'tokens')
|
|
180
|
+
* @query {string} format - 'csv' | 'json' (default: 'csv')
|
|
181
|
+
*/
|
|
182
|
+
router.get('/trend/export', async (req, res) => {
|
|
183
|
+
try {
|
|
184
|
+
const { startDate, endDate, granularity = 'day', step = 1, groupBy = 'model', metric = 'tokens', format = 'csv' } = req.query;
|
|
185
|
+
|
|
186
|
+
if (!startDate || !endDate) {
|
|
187
|
+
return res.status(400).json({ error: 'startDate and endDate are required' });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
|
191
|
+
if (!dateRegex.test(startDate) || !dateRegex.test(endDate)) {
|
|
192
|
+
return res.status(400).json({ error: 'Invalid date format. Expected YYYY-MM-DD' });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const start = new Date(startDate);
|
|
196
|
+
const end = new Date(endDate);
|
|
197
|
+
const diffDays = Math.round((end - start) / (1000 * 60 * 60 * 24)) + 1;
|
|
198
|
+
|
|
199
|
+
if (diffDays < 1) {
|
|
200
|
+
return res.status(400).json({ error: 'endDate must be >= startDate' });
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (granularity === 'hour' && diffDays > 7) {
|
|
204
|
+
return res.status(400).json({ error: 'Hour granularity is limited to 7 days' });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (diffDays > 90) {
|
|
208
|
+
return res.status(400).json({ error: 'Date range cannot exceed 90 days' });
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const result = await getTrendStatistics({ startDate, endDate, granularity, step, groupBy, metric });
|
|
212
|
+
|
|
213
|
+
const filename = `analytics-${startDate}-${endDate}-${groupBy}-${metric}.${format}`;
|
|
214
|
+
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
|
215
|
+
|
|
216
|
+
if (format === 'json') {
|
|
217
|
+
res.setHeader('Content-Type', 'application/json');
|
|
218
|
+
return res.json(result);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// CSV format
|
|
222
|
+
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
|
|
223
|
+
const seriesNames = result.series.map(s => s.name);
|
|
224
|
+
const header = ['Date/Time', ...seriesNames, 'Total'].join(',');
|
|
225
|
+
const rows = result.labels.map((label, i) => {
|
|
226
|
+
const values = result.series.map(s => s.data[i] || 0);
|
|
227
|
+
const total = values.reduce((a, b) => a + b, 0);
|
|
228
|
+
return [label, ...values, total].join(',');
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
res.send([header, ...rows].join('\n'));
|
|
232
|
+
} catch (error) {
|
|
233
|
+
console.error('Failed to export trend statistics:', error);
|
|
234
|
+
res.status(500).json({ error: 'Failed to export trend statistics' });
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
module.exports = router;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const {
|
|
4
|
+
loadUIConfig,
|
|
5
|
+
saveUIConfig,
|
|
6
|
+
updateUIConfig,
|
|
7
|
+
updateNestedUIConfig
|
|
8
|
+
} = require('../services/ui-config');
|
|
9
|
+
|
|
10
|
+
// Get all UI config
|
|
11
|
+
router.get('/', (req, res) => {
|
|
12
|
+
try {
|
|
13
|
+
const config = loadUIConfig();
|
|
14
|
+
res.json({ success: true, config });
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error('Error getting UI config:', error);
|
|
17
|
+
res.status(500).json({ error: error.message });
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Update entire UI config
|
|
22
|
+
router.post('/', (req, res) => {
|
|
23
|
+
try {
|
|
24
|
+
const { config } = req.body;
|
|
25
|
+
if (!config) {
|
|
26
|
+
return res.status(400).json({ error: 'Missing config' });
|
|
27
|
+
}
|
|
28
|
+
saveUIConfig(config);
|
|
29
|
+
res.json({ success: true, config });
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error('Error saving UI config:', error);
|
|
32
|
+
res.status(500).json({ error: error.message });
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Update specific config key
|
|
37
|
+
router.put('/:key', (req, res) => {
|
|
38
|
+
try {
|
|
39
|
+
const { key } = req.params;
|
|
40
|
+
const { value } = req.body;
|
|
41
|
+
|
|
42
|
+
const config = updateUIConfig(key, value);
|
|
43
|
+
res.json({ success: true, config });
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error('Error updating UI config:', error);
|
|
46
|
+
res.status(500).json({ error: error.message });
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Update nested config
|
|
51
|
+
router.put('/:parentKey/:childKey', (req, res) => {
|
|
52
|
+
try {
|
|
53
|
+
const { parentKey, childKey } = req.params;
|
|
54
|
+
const { value } = req.body;
|
|
55
|
+
|
|
56
|
+
const config = updateNestedUIConfig(parentKey, childKey, value);
|
|
57
|
+
res.json({ success: true, config });
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error('Error updating nested UI config:', error);
|
|
60
|
+
res.status(500).json({ error: error.message });
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
module.exports = router;
|