@myassis/gateway 1.0.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/README.md +194 -0
- package/dist/.env +6 -0
- package/dist/api/index.js +182 -0
- package/dist/config/index.js +41 -0
- package/dist/index.js +183 -0
- package/dist/middleware/auth.js +53 -0
- package/dist/middleware/errorHandler.js +20 -0
- package/dist/routes/agent.js +513 -0
- package/dist/routes/auth.js +172 -0
- package/dist/routes/chat.js +45 -0
- package/dist/routes/config.js +21 -0
- package/dist/routes/models.js +123 -0
- package/dist/routes/service.js +240 -0
- package/dist/routes/settings.js +101 -0
- package/dist/routes/skillHub.js +126 -0
- package/dist/routes/skills.js +159 -0
- package/dist/routes/tasks.js +149 -0
- package/dist/routes/upload.js +129 -0
- package/dist/routes/version.js +66 -0
- package/dist/services/HMSPushService.js +24 -0
- package/dist/services/LocalTaskService.js +223 -0
- package/dist/services/NotificationService.js +242 -0
- package/dist/services/ServiceManager.js +348 -0
- package/dist/services/TaskSchedulerService.js +195 -0
- package/dist/services/TaskService.js +240 -0
- package/dist/services/WebSocketService.js +236 -0
- package/dist/services/agent/Agent.js +120 -0
- package/dist/services/agent/AgentManager.js +265 -0
- package/dist/services/agent/AgentStore.js +73 -0
- package/dist/services/dataService.js +293 -0
- package/dist/services/index.js +15 -0
- package/dist/services/llm/LLMClient.js +724 -0
- package/dist/services/memory/MemoryManager.js +117 -0
- package/dist/services/model/ModelCapabilities.js +141 -0
- package/dist/services/model/index.js +4 -0
- package/dist/services/models.js +16 -0
- package/dist/services/session/MigrationManager.js +176 -0
- package/dist/services/session/Session.js +733 -0
- package/dist/services/session/SessionManager.js +255 -0
- package/dist/services/session/SessionStore.js +186 -0
- package/dist/services/session/index.js +3 -0
- package/dist/services/skills.js +34 -0
- package/dist/services/systemPrompt.js +150 -0
- package/dist/services/task/PushTokenStore.js +124 -0
- package/dist/services/task/TaskStore.js +143 -0
- package/dist/services/tools/calculator.js +27 -0
- package/dist/services/tools/edit.js +318 -0
- package/dist/services/tools/exec.js +119 -0
- package/dist/services/tools/fetch.js +155 -0
- package/dist/services/tools/file.js +315 -0
- package/dist/services/tools/index.js +48 -0
- package/dist/services/tools/keyboard.js +145 -0
- package/dist/services/tools/model.js +86 -0
- package/dist/services/tools/mouse.js +55 -0
- package/dist/services/tools/screenshot.js +19 -0
- package/dist/services/tools/search.js +53 -0
- package/dist/services/tools/skill.js +108 -0
- package/dist/services/tools/task.js +110 -0
- package/dist/services/tools/types.js +1 -0
- package/dist/services/tools/webFetch.js +34 -0
- package/dist/stores/authStore.js +178 -0
- package/dist/stores/index.js +6 -0
- package/dist/stores/memoryStore.js +191 -0
- package/dist/stores/persistStore.js +317 -0
- package/package.json +94 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { getLogger } from '@pocketclaw/shared';
|
|
3
|
+
const logger = getLogger('ChatRoutes');
|
|
4
|
+
const router = Router();
|
|
5
|
+
// Chat completion proxy - forwards to cloud API
|
|
6
|
+
router.post('/completion', async (req, res) => {
|
|
7
|
+
try {
|
|
8
|
+
const { model, messages, stream = false } = req.body;
|
|
9
|
+
// TODO: Forward to actual cloud API based on model selection
|
|
10
|
+
// This is a placeholder implementation
|
|
11
|
+
res.json({
|
|
12
|
+
id: `chat-${Date.now()}`,
|
|
13
|
+
model,
|
|
14
|
+
choices: [{
|
|
15
|
+
message: {
|
|
16
|
+
role: 'assistant',
|
|
17
|
+
content: 'Gateway proxy ready. Configure cloud API credentials to use AI capabilities.'
|
|
18
|
+
}
|
|
19
|
+
}]
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
logger.error('Chat completion error:', error);
|
|
24
|
+
res.status(500).json({ error: 'Chat completion failed' });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
// SSE stream proxy
|
|
28
|
+
router.post('/stream', async (req, res) => {
|
|
29
|
+
try {
|
|
30
|
+
const { model, messages } = req.body;
|
|
31
|
+
// TODO: Forward to actual cloud API with SSE streaming
|
|
32
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
33
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
34
|
+
res.setHeader('Connection', 'keep-alive');
|
|
35
|
+
// Placeholder response
|
|
36
|
+
res.write(`data: ${JSON.stringify({ content: 'Gateway stream ready. Configure cloud API credentials.' })}\n\n`);
|
|
37
|
+
res.write('data: [DONE]\n\n');
|
|
38
|
+
res.end();
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
logger.error('Stream error:', error);
|
|
42
|
+
res.status(500).json({ error: 'Streaming failed' });
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
export default router;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
const router = Router();
|
|
3
|
+
// Get app configuration
|
|
4
|
+
router.get('/', (req, res) => {
|
|
5
|
+
res.json({
|
|
6
|
+
version: '2.0.0',
|
|
7
|
+
environment: process.env.NODE_ENV || 'development',
|
|
8
|
+
features: {
|
|
9
|
+
localModels: true,
|
|
10
|
+
cloudModels: true,
|
|
11
|
+
streaming: true,
|
|
12
|
+
fileSystem: true
|
|
13
|
+
},
|
|
14
|
+
limits: {
|
|
15
|
+
maxFileSize: 10 * 1024 * 1024, // 10MB
|
|
16
|
+
maxModels: 10,
|
|
17
|
+
maxSkills: 20
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
export default router;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { getLogger, detectModelCapabilities } from '@pocketclaw/shared';
|
|
3
|
+
import { modelsService } from '../services/index.js';
|
|
4
|
+
import { persistStore } from '../stores/persistStore.js';
|
|
5
|
+
import { requireAuth } from '../middleware/auth.js';
|
|
6
|
+
const logger = getLogger('models');
|
|
7
|
+
const router = express.Router();
|
|
8
|
+
/**
|
|
9
|
+
* 获取所有模型配置
|
|
10
|
+
* GET /api/v1/models
|
|
11
|
+
*/
|
|
12
|
+
router.get('/', async (req, res) => {
|
|
13
|
+
try {
|
|
14
|
+
const result = await modelsService.list();
|
|
15
|
+
res.json(result);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
logger.error('获取模型列表失败:', error?.message);
|
|
19
|
+
res.status(500).json({ success: false, error: '获取模型列表失败' });
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
/**
|
|
23
|
+
* 获取单个模型配置
|
|
24
|
+
* GET /api/v1/models/:id
|
|
25
|
+
*/
|
|
26
|
+
router.get('/:id', async (req, res) => {
|
|
27
|
+
try {
|
|
28
|
+
const result = await modelsService.get(req.params.id);
|
|
29
|
+
res.json(result);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
logger.error('获取模型详情失败:', error?.message);
|
|
33
|
+
res.status(500).json({ success: false, error: '获取模型详情失败' });
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
/**
|
|
37
|
+
* 创建模型配置
|
|
38
|
+
* POST /api/v1/models
|
|
39
|
+
*/
|
|
40
|
+
router.post('/', requireAuth, async (req, res) => {
|
|
41
|
+
try {
|
|
42
|
+
const userId = req.userId;
|
|
43
|
+
const modelData = req.body;
|
|
44
|
+
const apiKey = modelData.apiKey;
|
|
45
|
+
// 自动检测模型能力,设置 supportsToolCall
|
|
46
|
+
const modelCapabilities = detectModelCapabilities(modelData.modelId || '');
|
|
47
|
+
const dataToSave = {
|
|
48
|
+
...modelData,
|
|
49
|
+
// 数据库 is_primary, is_active, supports_tool_call 都是 INTEGER 类型,转换为 0/1
|
|
50
|
+
isPrimary: modelData.isPrimary ? 1 : 0,
|
|
51
|
+
isActive: modelData.isActive ? 1 : 0,
|
|
52
|
+
supportsToolCall: modelCapabilities.supportsToolCall ? 1 : 0,
|
|
53
|
+
};
|
|
54
|
+
// 调用服务端,不包含 apiKey
|
|
55
|
+
const result = await modelsService.create(dataToSave);
|
|
56
|
+
// 如果提供了 API Key,持久化存储
|
|
57
|
+
if (apiKey && result.success && result.data?.id) {
|
|
58
|
+
persistStore.setModelApiKey(result.data.id, apiKey);
|
|
59
|
+
}
|
|
60
|
+
res.json(result);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
logger.error('创建模型失败:', error?.message);
|
|
64
|
+
res.status(500).json({ success: false, error: '创建模型失败' });
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
/**
|
|
68
|
+
* 更新模型配置
|
|
69
|
+
* PUT /api/v1/models/:id
|
|
70
|
+
*/
|
|
71
|
+
router.put('/:id', requireAuth, async (req, res) => {
|
|
72
|
+
try {
|
|
73
|
+
const userId = req.userId;
|
|
74
|
+
const modelData = req.body;
|
|
75
|
+
const apiKey = modelData.apiKey;
|
|
76
|
+
// API Key 单独存储到本地,不传给服务端
|
|
77
|
+
if (apiKey) {
|
|
78
|
+
persistStore.setModelApiKey(req.params.id, apiKey);
|
|
79
|
+
}
|
|
80
|
+
// 自动检测模型能力,设置 supportsToolCall
|
|
81
|
+
const modelCapabilities = detectModelCapabilities(modelData.modelId || '');
|
|
82
|
+
const dataToSave = {
|
|
83
|
+
...modelData,
|
|
84
|
+
supportsToolCall: modelCapabilities.supportsToolCall ? 1 : 0,
|
|
85
|
+
};
|
|
86
|
+
// 调用服务端,不包含 apiKey
|
|
87
|
+
const result = await modelsService.update(req.params.id, dataToSave);
|
|
88
|
+
res.json(result);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
logger.error('更新模型失败:', error?.message);
|
|
92
|
+
res.status(500).json({ success: false, error: '更新模型失败' });
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
/**
|
|
96
|
+
* 删除模型配置
|
|
97
|
+
* DELETE /api/v1/models/:id
|
|
98
|
+
*/
|
|
99
|
+
router.delete('/:id', async (req, res) => {
|
|
100
|
+
try {
|
|
101
|
+
const result = await modelsService.delete(req.params.id);
|
|
102
|
+
res.json(result);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
logger.error('删除模型失败:', error?.message);
|
|
106
|
+
res.status(500).json({ success: false, error: '删除模型失败' });
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
/**
|
|
110
|
+
* 设置主模型
|
|
111
|
+
* PUT /api/v1/models/:id/primary
|
|
112
|
+
*/
|
|
113
|
+
router.post('/:id/primary', async (req, res) => {
|
|
114
|
+
try {
|
|
115
|
+
const result = await modelsService.setPrimary(req.params.id);
|
|
116
|
+
res.json(result);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
logger.error('设置主模型失败:', error?.message);
|
|
120
|
+
res.status(500).json({ success: false, error: '设置主模型失败' });
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
export default router;
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { getLogger } from '@pocketclaw/shared';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { getServiceInfo, installService, uninstallService, startService, stopService, restartService, checkForUpdates, updateService, } from '../services/ServiceManager.js';
|
|
5
|
+
import { webSocketService } from '../services/WebSocketService.js';
|
|
6
|
+
import { requireAuth } from '../middleware/auth.js';
|
|
7
|
+
const router = Router();
|
|
8
|
+
// 所有 service 路由需要认证
|
|
9
|
+
router.use(requireAuth);
|
|
10
|
+
const logger = getLogger('service');
|
|
11
|
+
/**
|
|
12
|
+
* 获取服务状态
|
|
13
|
+
* GET /api/v1/service
|
|
14
|
+
*/
|
|
15
|
+
router.get('/', async (_req, res) => {
|
|
16
|
+
try {
|
|
17
|
+
const info = await getServiceInfo();
|
|
18
|
+
res.json({ success: true, data: info });
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
logger.error('获取服务状态失败:', err?.message);
|
|
22
|
+
res.status(500).json({ success: false, error: err?.message });
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
/**
|
|
26
|
+
* 安装并启动服务
|
|
27
|
+
* POST /api/v1/service/install
|
|
28
|
+
*/
|
|
29
|
+
router.post('/install', async (_req, res) => {
|
|
30
|
+
try {
|
|
31
|
+
const result = await installService();
|
|
32
|
+
if (result.success) {
|
|
33
|
+
res.json(result);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
res.status(400).json(result);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
logger.error('安装服务失败:', err?.message);
|
|
41
|
+
res.status(500).json({ success: false, message: err?.message });
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* 卸载服务
|
|
46
|
+
* DELETE /api/v1/service
|
|
47
|
+
*/
|
|
48
|
+
router.delete('/', async (_req, res) => {
|
|
49
|
+
try {
|
|
50
|
+
const result = await uninstallService();
|
|
51
|
+
if (result.success) {
|
|
52
|
+
res.json(result);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
res.status(400).json(result);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
logger.error('卸载服务失败:', err?.message);
|
|
60
|
+
res.status(500).json({ success: false, message: err?.message });
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
/**
|
|
64
|
+
* 启动服务
|
|
65
|
+
* POST /api/v1/service/start
|
|
66
|
+
*/
|
|
67
|
+
router.post('/start', async (_req, res) => {
|
|
68
|
+
try {
|
|
69
|
+
const result = await startService();
|
|
70
|
+
if (result.success) {
|
|
71
|
+
res.json(result);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
res.status(400).json(result);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
logger.error('启动服务失败:', err?.message);
|
|
79
|
+
res.status(500).json({ success: false, message: err?.message });
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
/**
|
|
83
|
+
* 停止服务
|
|
84
|
+
* POST /api/v1/service/stop
|
|
85
|
+
*/
|
|
86
|
+
router.post('/stop', async (_req, res) => {
|
|
87
|
+
try {
|
|
88
|
+
const result = await stopService();
|
|
89
|
+
if (result.success) {
|
|
90
|
+
res.json(result);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
res.status(400).json(result);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
logger.error('停止服务失败:', err?.message);
|
|
98
|
+
res.status(500).json({ success: false, message: err?.message });
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
/**
|
|
102
|
+
* 重启服务
|
|
103
|
+
* POST /api/v1/service/restart
|
|
104
|
+
*/
|
|
105
|
+
router.post('/restart', async (_req, res) => {
|
|
106
|
+
try {
|
|
107
|
+
const result = await restartService();
|
|
108
|
+
if (result.success) {
|
|
109
|
+
res.json(result);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
res.status(400).json(result);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
logger.error('重启服务失败:', err?.message);
|
|
117
|
+
res.status(500).json({ success: false, message: err?.message });
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
/**
|
|
121
|
+
* 检查是否有可用更新
|
|
122
|
+
* GET /api/v1/service/check-update
|
|
123
|
+
*/
|
|
124
|
+
router.get('/check-update', async (_req, res) => {
|
|
125
|
+
try {
|
|
126
|
+
const result = await checkForUpdates();
|
|
127
|
+
res.json({ success: true, data: result });
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
logger.error('检查更新失败:', err?.message);
|
|
131
|
+
res.status(500).json({ success: false, message: err?.message });
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
/**
|
|
135
|
+
* 检查并更新服务
|
|
136
|
+
* POST /api/v1/service/update
|
|
137
|
+
*/
|
|
138
|
+
router.post('/update', async (_req, res) => {
|
|
139
|
+
try {
|
|
140
|
+
// 先检查版本
|
|
141
|
+
const check = await checkForUpdates();
|
|
142
|
+
if (check.error) {
|
|
143
|
+
res.status(500).json({ success: false, message: `检查更新失败: ${check.error}` });
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (!check.hasUpdate) {
|
|
147
|
+
res.json({ success: true, message: `已是最新版本 ${check.currentVersion}` });
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
// 尝试自动更新(有 nssm 时会成功)
|
|
151
|
+
const result = await updateService();
|
|
152
|
+
if (result.success) {
|
|
153
|
+
res.json(result);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
// 自动更新失败,推送手动更新提示给 Desktop
|
|
157
|
+
const userId = 'default';
|
|
158
|
+
const pushed = webSocketService.sendUpdateRequired(userId, result.message);
|
|
159
|
+
if (pushed) {
|
|
160
|
+
res.json({ success: true, message: '已推送手动更新提示到桌面端', manual: true });
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
// Desktop 不在线时直接返回错误消息
|
|
164
|
+
res.status(400).json({ success: false, message: result.message, manual: true });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
logger.error('更新服务失败:', err?.message);
|
|
169
|
+
res.status(500).json({ success: false, message: err?.message });
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
/**
|
|
173
|
+
* 获取最新 Gateway EXE 下载地址
|
|
174
|
+
* GET /api/v1/service/latest-exe-url
|
|
175
|
+
*/
|
|
176
|
+
router.get('/latest-exe-url', async (_req, res) => {
|
|
177
|
+
try {
|
|
178
|
+
const check = await checkForUpdates();
|
|
179
|
+
if (check.error) {
|
|
180
|
+
res.status(500).json({ success: false, message: check.error });
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (!check.hasUpdate) {
|
|
184
|
+
res.json({ success: true, hasUpdate: false, message: `已是最新版本 ${check.currentVersion}` });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
// TODO: 从 GitHub Releases 或自有 CDN 获取下载地址
|
|
188
|
+
// 示例: https://github.com/pocketclaw/gateway/releases/download/v${check.latestVersion}/gateway.exe
|
|
189
|
+
const downloadUrl = `https://pocket-claw-1251246038.cos.ap-chengdu.myqcloud.com/pocket-claw-1251246038/installs/v${check.latestVersion}/gateway-installer_${check.latestVersion}.exe`;
|
|
190
|
+
res.json({
|
|
191
|
+
success: true,
|
|
192
|
+
hasUpdate: true,
|
|
193
|
+
currentVersion: check.currentVersion,
|
|
194
|
+
latestVersion: check.latestVersion,
|
|
195
|
+
downloadUrl,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
logger.error('获取下载地址失败:', err?.message);
|
|
200
|
+
res.status(500).json({ success: false, message: err?.message });
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
/**
|
|
204
|
+
* EXE 方式更新 Gateway
|
|
205
|
+
* POST /api/v1/service/update-exe
|
|
206
|
+
* 由 Desktop 调用,Gateway 下载最新 exe 并替换自己
|
|
207
|
+
*/
|
|
208
|
+
router.post('/update-exe', async (_req, res) => {
|
|
209
|
+
try {
|
|
210
|
+
// 检查是否是 exe 运行方式
|
|
211
|
+
if (!process.pkg) {
|
|
212
|
+
res.status(400).json({ success: false, message: '当前不是 EXE 运行方式,无法使用此接口' });
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
// 获取最新版本信息
|
|
216
|
+
const check = await checkForUpdates();
|
|
217
|
+
if (check.error) {
|
|
218
|
+
res.status(500).json({ success: false, message: `检查更新失败: ${check.error}` });
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (!check.hasUpdate) {
|
|
222
|
+
res.json({ success: true, message: `已是最新版本 ${check.currentVersion}` });
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
// 获取当前 exe 路径
|
|
226
|
+
const currentExePath = process.execPath;
|
|
227
|
+
const exeDir = path.dirname(currentExePath);
|
|
228
|
+
const exeName = path.basename(currentExePath);
|
|
229
|
+
const backupPath = path.join(exeDir, `${exeName}.backup`);
|
|
230
|
+
const downloadPath = path.join(exeDir, `${exeName}.new`);
|
|
231
|
+
// TODO: 从 GitHub Releases 或自有 CDN 下载最新 exe
|
|
232
|
+
// 这里需要实现下载逻辑
|
|
233
|
+
res.status(501).json({ success: false, message: 'EXE 更新功能尚未实现' });
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
logger.error('EXE 更新失败:', err?.message);
|
|
237
|
+
res.status(500).json({ success: false, message: err?.message });
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
export default router;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { getLogger } from '@pocketclaw/shared';
|
|
3
|
+
import { settingsService } from '../services/index.js';
|
|
4
|
+
import { authStore, persistStore } from '../stores/index.js';
|
|
5
|
+
import { authApi } from '../api/index.js';
|
|
6
|
+
const logger = getLogger('settings');
|
|
7
|
+
const router = express.Router();
|
|
8
|
+
/**
|
|
9
|
+
* 获取用户设置
|
|
10
|
+
* GET /api/v1/settings
|
|
11
|
+
*/
|
|
12
|
+
router.get('/', async (req, res) => {
|
|
13
|
+
try {
|
|
14
|
+
const result = await settingsService.get();
|
|
15
|
+
res.json(result);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
logger.error('获取用户设置失败:', error?.message);
|
|
19
|
+
res.status(500).json({ success: false, error: '获取用户设置失败' });
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
/**
|
|
23
|
+
* 更新用户设置
|
|
24
|
+
* PUT /api/v1/settings
|
|
25
|
+
*/
|
|
26
|
+
router.put('/', async (req, res) => {
|
|
27
|
+
try {
|
|
28
|
+
const result = await settingsService.update(req.body);
|
|
29
|
+
res.json(result);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
logger.error('更新用户设置失败:', error?.message);
|
|
33
|
+
res.status(500).json({ success: false, error: '更新用户设置失败' });
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
/**
|
|
37
|
+
* 修改密码
|
|
38
|
+
* PUT /api/v1/settings/password
|
|
39
|
+
*/
|
|
40
|
+
router.put('/password', async (req, res) => {
|
|
41
|
+
try {
|
|
42
|
+
const { oldPassword, newPassword } = req.body;
|
|
43
|
+
if (!oldPassword || !newPassword) {
|
|
44
|
+
res.status(400).json({ success: false, error: 'Missing required fields' });
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const result = await authApi.changePassword({ oldPassword, newPassword });
|
|
48
|
+
res.json(result);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
logger.error('修改密码失败:', error?.message);
|
|
52
|
+
res.status(500).json({ success: false, error: '修改密码失败' });
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
/**
|
|
56
|
+
* 获取本地用户设置(不上传服务端)
|
|
57
|
+
* GET /api/v1/settings/local
|
|
58
|
+
*/
|
|
59
|
+
router.get('/local', async (req, res) => {
|
|
60
|
+
try {
|
|
61
|
+
const settings = persistStore.getAllLocalUserSettings();
|
|
62
|
+
res.json({ success: true, data: settings });
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
logger.error('获取本地用户设置失败:', error?.message);
|
|
66
|
+
res.status(500).json({ success: false, error: '获取本地用户设置失败' });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
/**
|
|
70
|
+
* 更新本地用户设置(不上传服务端)
|
|
71
|
+
* PUT /api/v1/settings/local
|
|
72
|
+
*/
|
|
73
|
+
router.put('/local', async (req, res) => {
|
|
74
|
+
try {
|
|
75
|
+
persistStore.setLocalUserSettings(req.body);
|
|
76
|
+
const settings = persistStore.getAllLocalUserSettings();
|
|
77
|
+
res.json({ success: true, data: settings });
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
logger.error('更新本地用户设置失败:', error?.message);
|
|
81
|
+
res.status(500).json({ success: false, error: '更新本地用户设置失败' });
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
/**
|
|
85
|
+
* 删除账号
|
|
86
|
+
* DELETE /api/v1/settings/account
|
|
87
|
+
*/
|
|
88
|
+
router.delete('/account', async (req, res) => {
|
|
89
|
+
try {
|
|
90
|
+
const result = await authApi.deleteAccount({ password: req.params.password });
|
|
91
|
+
if (result.success) {
|
|
92
|
+
authStore.clear();
|
|
93
|
+
}
|
|
94
|
+
res.json(result);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
logger.error('删除账号失败:', error?.message);
|
|
98
|
+
res.status(500).json({ success: false, error: '删除账号失败' });
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
export default router;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway SkillHub 路由
|
|
3
|
+
* 技能库(可安装的技能)
|
|
4
|
+
*/
|
|
5
|
+
import express from 'express';
|
|
6
|
+
import { skillHubService } from '../services/index.js';
|
|
7
|
+
import { requireAuth } from '../middleware/auth.js';
|
|
8
|
+
import { getLogger } from '@pocketclaw/shared';
|
|
9
|
+
const logger = getLogger('skillHub');
|
|
10
|
+
const router = express.Router();
|
|
11
|
+
/**
|
|
12
|
+
* 获取技能库列表
|
|
13
|
+
* GET /api/v1/skill-hub?page=1&pageSize=20&keyword=xxx&category=xxx
|
|
14
|
+
*/
|
|
15
|
+
router.get('/', async (req, res) => {
|
|
16
|
+
try {
|
|
17
|
+
const { page, pageSize, search, category } = req.query;
|
|
18
|
+
const params = {};
|
|
19
|
+
if (page)
|
|
20
|
+
params.page = parseInt(page);
|
|
21
|
+
if (pageSize)
|
|
22
|
+
params.pageSize = parseInt(pageSize);
|
|
23
|
+
if (search)
|
|
24
|
+
params.keyword = search;
|
|
25
|
+
if (category)
|
|
26
|
+
params.category = category;
|
|
27
|
+
const result = await skillHubService.list(params);
|
|
28
|
+
res.json(result);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
logger.debug('获取技能库列表失败:', error?.message);
|
|
32
|
+
res.status(500).json({ success: false, error: error?.message });
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* 获取技能库详情
|
|
37
|
+
* GET /api/v1/skill-hub/:id
|
|
38
|
+
*/
|
|
39
|
+
router.get('/:id', async (req, res) => {
|
|
40
|
+
try {
|
|
41
|
+
const hubId = req.params.id;
|
|
42
|
+
if (!hubId) {
|
|
43
|
+
return res.status(400).json({ success: false, error: '无效的技能库ID' });
|
|
44
|
+
}
|
|
45
|
+
const result = await skillHubService.get(hubId);
|
|
46
|
+
res.json(result);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
logger.error('获取技能库详情失败:', error?.message);
|
|
50
|
+
res.status(500).json({ success: false, error: '获取技能库详情失败' });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* 获取用户是否使用过技能
|
|
55
|
+
* GET /api/v1/skill-hub/:id/used
|
|
56
|
+
*/
|
|
57
|
+
router.get('/:id/used', async (req, res) => {
|
|
58
|
+
try {
|
|
59
|
+
const hubId = req.params.id;
|
|
60
|
+
if (!hubId) {
|
|
61
|
+
return res.status(400).json({ success: false, error: '无效的技能库ID' });
|
|
62
|
+
}
|
|
63
|
+
const result = await skillHubService.checkUsed(hubId);
|
|
64
|
+
res.json(result);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
logger.error('检查技能使用状态失败:', error?.message);
|
|
68
|
+
res.status(500).json({ success: false, error: error?.message });
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
/**
|
|
72
|
+
* 获取用户评分
|
|
73
|
+
* GET /api/v1/skill-hub/:id/user-rating
|
|
74
|
+
*/
|
|
75
|
+
router.get('/:id/user-rating', async (req, res) => {
|
|
76
|
+
try {
|
|
77
|
+
const hubId = req.params.id;
|
|
78
|
+
if (!hubId) {
|
|
79
|
+
return res.status(400).json({ success: false, error: '无效的技能库ID' });
|
|
80
|
+
}
|
|
81
|
+
const result = await skillHubService.userRating(hubId);
|
|
82
|
+
res.json(result);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
logger.error('获取用户评分失败:', error?.message);
|
|
86
|
+
res.status(500).json({ success: false, error: error?.message });
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
/**
|
|
90
|
+
* 获取技能分类
|
|
91
|
+
* GET /api/v1/skill-hub/categories
|
|
92
|
+
*/
|
|
93
|
+
router.get('/categories/list', async (req, res) => {
|
|
94
|
+
try {
|
|
95
|
+
const result = await skillHubService.categories();
|
|
96
|
+
res.json(result);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
logger.error('获取技能分类失败:', error?.message);
|
|
100
|
+
res.status(500).json({ success: false, error: error?.message });
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
/**
|
|
104
|
+
* 技能库评分
|
|
105
|
+
* POST /api/v1/skill-hub/:id/rate
|
|
106
|
+
*/
|
|
107
|
+
router.post('/:id/rate', requireAuth, async (req, res) => {
|
|
108
|
+
try {
|
|
109
|
+
const hubId = req.params.id;
|
|
110
|
+
const { rating } = req.body;
|
|
111
|
+
const userId = req.userId;
|
|
112
|
+
if (!hubId) {
|
|
113
|
+
return res.status(400).json({ success: false, error: '无效的技能库ID' });
|
|
114
|
+
}
|
|
115
|
+
if (rating === undefined || rating < 1 || rating > 5) {
|
|
116
|
+
return res.status(400).json({ success: false, error: '评分必须是1-5的数字' });
|
|
117
|
+
}
|
|
118
|
+
const result = await skillHubService.rate(hubId, rating);
|
|
119
|
+
res.json(result);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
logger.error('技能库评分失败:', error?.message);
|
|
123
|
+
res.status(500).json({ success: false, error: error?.message });
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
export default router;
|