@myassis/gateway 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/index.js +69 -59
- package/dist/config/index.js +2 -2
- package/dist/index.js +85 -11
- package/dist/middleware/auth.js +9 -25
- package/dist/middleware/errorHandler.js +1 -1
- package/dist/routes/agent.js +30 -19
- package/dist/routes/auth.js +35 -26
- package/dist/routes/chat.js +1 -1
- package/dist/routes/models.js +11 -14
- package/dist/routes/service.js +3 -6
- package/dist/routes/settings.js +11 -9
- package/dist/routes/skillHub.js +10 -6
- package/dist/routes/skills.js +13 -10
- package/dist/routes/tasks.js +6 -3
- package/dist/routes/upload.js +5 -2
- package/dist/routes/version.js +4 -4
- package/dist/services/LocalTaskService.js +2 -2
- package/dist/services/NotificationService.js +4 -4
- package/dist/services/ServiceManager.js +12 -12
- package/dist/services/TaskSchedulerService.js +21 -12
- package/dist/services/TaskService.js +25 -22
- package/dist/services/WebSocketService.js +16 -6
- package/dist/services/agent/Agent.js +7 -7
- package/dist/services/agent/AgentManager.js +26 -18
- package/dist/services/agent/AgentStore.js +1 -1
- package/dist/services/dataService.js +63 -73
- package/dist/services/index.js +9 -9
- package/dist/services/llm/LLMClient.js +11 -9
- package/dist/services/memory/MemoryManager.js +180 -17
- package/dist/services/model/index.js +1 -1
- package/dist/services/session/MigrationManager.js +1 -1
- package/dist/services/session/Session.js +74 -50
- package/dist/services/session/SessionManager.js +14 -7
- package/dist/services/session/SessionStore.js +2 -28
- package/dist/services/session/index.js +2 -2
- package/dist/services/systemPrompt.js +9 -5
- package/dist/services/task/PushTokenStore.js +2 -20
- package/dist/services/task/TaskStore.js +2 -21
- package/dist/services/tools/edit.js +122 -148
- package/dist/services/tools/exec.js +1 -1
- package/dist/services/tools/fetch.js +1 -1
- package/dist/services/tools/file.js +4 -9
- package/dist/services/tools/index.js +18 -17
- package/dist/services/tools/model.js +9 -7
- package/dist/services/tools/sessionsSpawn.js +54 -0
- package/dist/services/tools/skill.js +14 -13
- package/dist/services/tools/task.js +3 -5
- package/dist/stores/authStore.js +52 -66
- package/dist/stores/index.js +3 -3
- package/dist/stores/persistStore.js +37 -3
- package/package.json +7 -3
- package/scripts/fix-dist-imports.js +95 -0
- package/scripts/fix-imports.js +90 -0
- package/scripts/postbuild.js +39 -0
package/dist/api/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Gateway 统一 API 调用模块
|
|
3
3
|
* 负责与后端 Server 通信
|
|
4
|
-
*
|
|
4
|
+
* 多用户模式:每个请求显式传 token(替代 authStore)
|
|
5
5
|
*
|
|
6
6
|
* 服务端路由结构:
|
|
7
7
|
* - /api/v1/auth/* - 认证路由
|
|
@@ -9,7 +9,12 @@
|
|
|
9
9
|
* - /api/v1/skill-hubs/* - 技能库路由
|
|
10
10
|
*/
|
|
11
11
|
import crypto from 'crypto';
|
|
12
|
-
import {
|
|
12
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
13
|
+
// 多用户模式请求上下文:存储当前请求的 token
|
|
14
|
+
const requestContext = new AsyncLocalStorage();
|
|
15
|
+
export function runWithToken(token, fn) {
|
|
16
|
+
return requestContext.run({ token }, fn);
|
|
17
|
+
}
|
|
13
18
|
// Server 服务地址(从环境变量读取)
|
|
14
19
|
const SERVER_BASE_URL = process.env.SERVER_BASE_URL || 'http://localhost:3000';
|
|
15
20
|
// 签名密钥(用于请求签名)
|
|
@@ -35,16 +40,15 @@ function generateSignature(params, timestamp) {
|
|
|
35
40
|
return crypto.createHash('sha256').update(signString).digest('hex');
|
|
36
41
|
}
|
|
37
42
|
// 基础请求方法工厂
|
|
38
|
-
// prefix: 路由前缀,如 '/api/v1/auth', '/api/v1/data', '/api/v1/skill-hubs'
|
|
39
43
|
function createRequest(prefix) {
|
|
40
44
|
return async function (path, options = {}) {
|
|
41
|
-
const { method = 'GET', body, headers = {}, params, skipAuth = false } = options;
|
|
45
|
+
const { method = 'GET', body, headers = {}, params, skipAuth = false, token, } = options;
|
|
42
46
|
// 构建 URL
|
|
43
47
|
let url = `${SERVER_BASE_URL}${prefix}${path}`;
|
|
44
48
|
// 添加查询参数
|
|
45
49
|
if (params) {
|
|
46
50
|
const queryString = Object.entries(params)
|
|
47
|
-
.filter(([
|
|
51
|
+
.filter(([, v]) => v !== undefined && v !== null)
|
|
48
52
|
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
|
|
49
53
|
.join('&');
|
|
50
54
|
if (queryString) {
|
|
@@ -63,10 +67,12 @@ function createRequest(prefix) {
|
|
|
63
67
|
...headers
|
|
64
68
|
};
|
|
65
69
|
// 自动注入 Token(除非明确跳过)
|
|
70
|
+
// 优先级:显式参数 > AsyncLocalStorage 上下文 > authStore(降级兼容)
|
|
66
71
|
if (!skipAuth) {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
72
|
+
const ctx = requestContext.getStore();
|
|
73
|
+
const authToken = token || ctx?.token;
|
|
74
|
+
if (authToken) {
|
|
75
|
+
requestHeaders['Authorization'] = `Bearer ${authToken}`;
|
|
70
76
|
}
|
|
71
77
|
}
|
|
72
78
|
try {
|
|
@@ -90,40 +96,44 @@ function createRequest(prefix) {
|
|
|
90
96
|
};
|
|
91
97
|
}
|
|
92
98
|
// 创建不同前缀的请求方法
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
const
|
|
99
|
+
const _authRequest = createRequest('/api/v1/auth');
|
|
100
|
+
const _dataRequest = createRequest('/api/v1/data');
|
|
101
|
+
const _skillHubRequest = createRequest('/api/v1/skill-hubs');
|
|
102
|
+
function authRequest(path, options) {
|
|
103
|
+
return _authRequest(path, options || {});
|
|
104
|
+
}
|
|
105
|
+
function dataRequest(path, options) {
|
|
106
|
+
return _dataRequest(path, options || {});
|
|
107
|
+
}
|
|
108
|
+
function skillHubRequest(path, options) {
|
|
109
|
+
return _skillHubRequest(path, options || {});
|
|
110
|
+
}
|
|
96
111
|
// ============ 认证 API ============
|
|
97
112
|
export const authApi = {
|
|
98
113
|
login: (data) => authRequest('/login', { method: 'POST', body: data, skipAuth: true }),
|
|
99
114
|
register: (data) => authRequest('/register', { method: 'POST', body: data, skipAuth: true }),
|
|
100
|
-
logout: () => authRequest('/logout', { method: 'POST', body: {} }),
|
|
115
|
+
logout: (token) => authRequest('/logout', { method: 'POST', body: {}, token }),
|
|
101
116
|
refresh: (refreshToken) => authRequest('/refresh', { method: 'POST', body: { refreshToken }, skipAuth: true }),
|
|
102
|
-
me: () => authRequest('/me'),
|
|
103
|
-
updateProfile: (data) => authRequest('/profile', { method: 'PUT', body: data }),
|
|
104
|
-
changePassword: (data) => authRequest('/password', { method: 'PUT', body: data }),
|
|
105
|
-
deleteAccount: (data, token) => {
|
|
106
|
-
const headers = {};
|
|
107
|
-
if (token)
|
|
108
|
-
headers['Authorization'] = `Bearer ${token}`;
|
|
109
|
-
return authRequest('/account/delete', { method: 'POST', body: data, headers });
|
|
110
|
-
},
|
|
117
|
+
me: (token) => authRequest('/me', { token }),
|
|
118
|
+
updateProfile: (data, token) => authRequest('/profile', { method: 'PUT', body: data, token }),
|
|
119
|
+
changePassword: (data, token) => authRequest('/password', { method: 'PUT', body: data, token }),
|
|
120
|
+
deleteAccount: (data, token) => authRequest('/account/delete', { method: 'POST', body: data, token }),
|
|
111
121
|
};
|
|
112
122
|
// ============ 技能 API (data) ============
|
|
113
123
|
export const skillsApi = {
|
|
114
|
-
list: () => dataRequest('/skills?brief=true'),
|
|
115
|
-
get: (skillId) => dataRequest(`/skills/${skillId}
|
|
116
|
-
install: (data) => dataRequest('/skills', { method: 'POST', body: data }),
|
|
117
|
-
create: (data) => dataRequest('/skills', { method: 'POST', body: data }),
|
|
118
|
-
update: (skillId, data) => dataRequest(`/skills/${skillId}`, { method: 'PUT', body: data }),
|
|
119
|
-
uninstall: (skillId) => dataRequest(`/skills/${skillId}`, { method: 'DELETE' }),
|
|
120
|
-
setApiKey: (skillId, data) => dataRequest(`/skills/${skillId}/api-key`, { method: 'POST', body: data }),
|
|
121
|
-
rate: (skillId, data) => dataRequest(`/skills/${skillId}/rating`, { method: 'POST', body: data }),
|
|
122
|
-
parse: (data) => dataRequest('/skills/parse', { method: 'POST', body: data }),
|
|
124
|
+
list: (token) => dataRequest('/skills?brief=true', { token }),
|
|
125
|
+
get: (skillId, token) => dataRequest(`/skills/${skillId}`, { token }),
|
|
126
|
+
install: (data, token) => dataRequest('/skills', { method: 'POST', body: data, token }),
|
|
127
|
+
create: (data, token) => dataRequest('/skills', { method: 'POST', body: data, token }),
|
|
128
|
+
update: (skillId, data, token) => dataRequest(`/skills/${skillId}`, { method: 'PUT', body: data, token }),
|
|
129
|
+
uninstall: (skillId, token) => dataRequest(`/skills/${skillId}`, { method: 'DELETE', token }),
|
|
130
|
+
setApiKey: (skillId, data, token) => dataRequest(`/skills/${skillId}/api-key`, { method: 'POST', body: data, token }),
|
|
131
|
+
rate: (skillId, data, token) => dataRequest(`/skills/${skillId}/rating`, { method: 'POST', body: data, token }),
|
|
132
|
+
parse: (data, token) => dataRequest('/skills/parse', { method: 'POST', body: data, token }),
|
|
123
133
|
};
|
|
124
134
|
// ============ 技能库 API (skill-hubs) ============
|
|
125
135
|
export const skillHubApi = {
|
|
126
|
-
list: (params) => {
|
|
136
|
+
list: (params, token) => {
|
|
127
137
|
const queryParams = {};
|
|
128
138
|
if (params?.page)
|
|
129
139
|
queryParams.page = String(params.page);
|
|
@@ -133,44 +143,44 @@ export const skillHubApi = {
|
|
|
133
143
|
queryParams.keyword = params.keyword;
|
|
134
144
|
if (params?.category)
|
|
135
145
|
queryParams.category = params.category;
|
|
136
|
-
return skillHubRequest('/', { params: queryParams });
|
|
146
|
+
return skillHubRequest('/', { params: queryParams, token });
|
|
137
147
|
},
|
|
138
|
-
get: (hubId) => skillHubRequest(`/${hubId}
|
|
139
|
-
categories: () => skillHubRequest('/categories'),
|
|
140
|
-
preinstalled: () => skillHubRequest('/preinstalled'),
|
|
141
|
-
user: () => skillHubRequest(`/user
|
|
142
|
-
checkUsed: (hubId) => skillHubRequest(`/${hubId}/used
|
|
143
|
-
rate: (hubId, rating) => skillHubRequest(`/${hubId}/rate`, { method: 'POST', body: { rating } }),
|
|
144
|
-
userRating: (hubId) => skillHubRequest(`/${hubId}/user-rating
|
|
145
|
-
install: (hubId) => skillHubRequest(`/${hubId}/install`, { method: 'POST', body: {} }),
|
|
146
|
-
use: (hubId) => skillHubRequest(`/${hubId}/use`, { method: 'POST', body: {} }),
|
|
148
|
+
get: (hubId, token) => skillHubRequest(`/${hubId}`, { token }),
|
|
149
|
+
categories: (token) => skillHubRequest('/categories', { token }),
|
|
150
|
+
preinstalled: (token) => skillHubRequest('/preinstalled', { token }),
|
|
151
|
+
user: (token) => skillHubRequest(`/user`, { token }),
|
|
152
|
+
checkUsed: (hubId, token) => skillHubRequest(`/${hubId}/used`, { token }),
|
|
153
|
+
rate: (hubId, rating, token) => skillHubRequest(`/${hubId}/rate`, { method: 'POST', body: { rating }, token }),
|
|
154
|
+
userRating: (hubId, token) => skillHubRequest(`/${hubId}/user-rating`, { token }),
|
|
155
|
+
install: (hubId, token) => skillHubRequest(`/${hubId}/install`, { method: 'POST', body: {}, token }),
|
|
156
|
+
use: (hubId, token) => skillHubRequest(`/${hubId}/use`, { method: 'POST', body: {}, token }),
|
|
147
157
|
};
|
|
148
158
|
// ============ 模型 API (data) ============
|
|
149
159
|
export const modelsApi = {
|
|
150
|
-
list: () => dataRequest('/models'),
|
|
151
|
-
get: (modelId) => dataRequest(`/models/${modelId}
|
|
152
|
-
create: (data) => dataRequest('/models', { method: 'POST', body: data }),
|
|
153
|
-
update: (modelId, data) => dataRequest(`/models/${modelId}`, { method: 'PUT', body: data }),
|
|
154
|
-
delete: (modelId) => dataRequest(`/models/${modelId}`, { method: 'DELETE' }),
|
|
155
|
-
setPrimary: (modelId) => dataRequest(`/models/${modelId}/primary`, { method: 'POST', body: {} }),
|
|
160
|
+
list: (token) => dataRequest('/models', { token }),
|
|
161
|
+
get: (modelId, token) => dataRequest(`/models/${modelId}`, { token }),
|
|
162
|
+
create: (data, token) => dataRequest('/models', { method: 'POST', body: data, token }),
|
|
163
|
+
update: (modelId, data, token) => dataRequest(`/models/${modelId}`, { method: 'PUT', body: data, token }),
|
|
164
|
+
delete: (modelId, token) => dataRequest(`/models/${modelId}`, { method: 'DELETE', token }),
|
|
165
|
+
setPrimary: (modelId, token) => dataRequest(`/models/${modelId}/primary`, { method: 'POST', body: {}, token }),
|
|
156
166
|
};
|
|
157
167
|
// ============ 任务 API (data) ============
|
|
158
168
|
export const tasksApi = {
|
|
159
|
-
list: (params) => dataRequest('/tasks', { params }),
|
|
160
|
-
get: (taskId) => dataRequest(`/tasks/${taskId}
|
|
161
|
-
create: (data) => dataRequest('/tasks', { method: 'POST', body: data }),
|
|
162
|
-
update: (taskId, data) => dataRequest(`/tasks/${taskId}`, { method: 'PUT', body: data }),
|
|
163
|
-
delete: (taskId) => dataRequest(`/tasks/${taskId}`, { method: 'DELETE' }),
|
|
164
|
-
complete: (taskId) => dataRequest(`/tasks/${taskId}/complete`, { method: 'POST', body: {} }),
|
|
165
|
-
cancel: (taskId) => dataRequest(`/tasks/${taskId}/cancel`, { method: 'POST', body: {} }),
|
|
166
|
-
updateStatus: (taskId, data) => dataRequest(`/tasks/${taskId}/status`, { method: 'POST', body: data }),
|
|
167
|
-
notifying: () => dataRequest('/tasks/notifying'),
|
|
168
|
-
resetSession: () => dataRequest('/reset-session', { method: 'POST', body: {} }),
|
|
169
|
+
list: (params, token) => dataRequest('/tasks', { params, token }),
|
|
170
|
+
get: (taskId, token) => dataRequest(`/tasks/${taskId}`, { token }),
|
|
171
|
+
create: (data, token) => dataRequest('/tasks', { method: 'POST', body: data, token }),
|
|
172
|
+
update: (taskId, data, token) => dataRequest(`/tasks/${taskId}`, { method: 'PUT', body: data, token }),
|
|
173
|
+
delete: (taskId, token) => dataRequest(`/tasks/${taskId}`, { method: 'DELETE', token }),
|
|
174
|
+
complete: (taskId, token) => dataRequest(`/tasks/${taskId}/complete`, { method: 'POST', body: {}, token }),
|
|
175
|
+
cancel: (taskId, token) => dataRequest(`/tasks/${taskId}/cancel`, { method: 'POST', body: {}, token }),
|
|
176
|
+
updateStatus: (taskId, data, token) => dataRequest(`/tasks/${taskId}/status`, { method: 'POST', body: data, token }),
|
|
177
|
+
notifying: (token) => dataRequest('/tasks/notifying', { token }),
|
|
178
|
+
resetSession: (token) => dataRequest('/reset-session', { method: 'POST', body: {}, token }),
|
|
169
179
|
};
|
|
170
180
|
// ============ 设置 API (data) ============
|
|
171
181
|
export const settingsApi = {
|
|
172
|
-
get: () => dataRequest('/settings'),
|
|
173
|
-
update: (data) => dataRequest('/settings', { method: 'PUT', body: data }),
|
|
182
|
+
get: (token) => dataRequest('/settings', { token }),
|
|
183
|
+
update: (data, token) => dataRequest('/settings', { method: 'PUT', body: data, token }),
|
|
174
184
|
};
|
|
175
185
|
export default {
|
|
176
186
|
authApi,
|
package/dist/config/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getLogger } from '@
|
|
1
|
+
import { getLogger } from '@myassis/shared';
|
|
2
2
|
const logger = getLogger('GatewayConfig');
|
|
3
3
|
import { config as dotenvConfig } from 'dotenv';
|
|
4
4
|
import * as path from 'path';
|
|
@@ -36,6 +36,6 @@ export const appConfig = {
|
|
|
36
36
|
clientUrl: process.env.CLIENT_URL || 'http://localhost:3000',
|
|
37
37
|
nodeEnv: process.env.NODE_ENV || defaultEnv,
|
|
38
38
|
messageKeep: 4,
|
|
39
|
-
appName: '
|
|
39
|
+
appName: '我的助手'
|
|
40
40
|
};
|
|
41
41
|
export default appConfig;
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
import express from 'express';
|
|
2
3
|
import cors from 'cors';
|
|
3
4
|
import helmet from 'helmet';
|
|
4
5
|
import compression from 'compression';
|
|
5
6
|
import http from 'http';
|
|
6
7
|
import { appConfig } from './config/index.js';
|
|
7
|
-
import { getLogger } from '@
|
|
8
|
+
import { getLogger } from '@myassis/shared';
|
|
8
9
|
import authRoutes from './routes/auth.js';
|
|
9
10
|
import agentRoutes from './routes/agent.js';
|
|
10
11
|
import modelsRoutes from './routes/models.js';
|
|
@@ -19,9 +20,10 @@ import uploadRoutes from './routes/upload.js';
|
|
|
19
20
|
import versionRoutes from './routes/version.js';
|
|
20
21
|
import { errorHandler } from './middleware/errorHandler.js';
|
|
21
22
|
import { authStore } from './stores/index.js';
|
|
23
|
+
import { persistStore } from './stores/persistStore.js';
|
|
22
24
|
import { webSocketService } from './services/WebSocketService.js';
|
|
23
25
|
import { taskSchedulerService } from './services/TaskSchedulerService.js';
|
|
24
|
-
import { getServiceInfo, installService, uninstallService, startService, stopService, updateService, } from './services/ServiceManager.js';
|
|
26
|
+
import { getServiceInfo, installService, uninstallService, startService, stopService, restartService, updateService, } from './services/ServiceManager.js';
|
|
25
27
|
const logger = getLogger('index');
|
|
26
28
|
// ─── CLI 模式 ─────────────────────────────────────────
|
|
27
29
|
// gateway install | start | stop | uninstall | status | update
|
|
@@ -39,17 +41,87 @@ if (cliCommand) {
|
|
|
39
41
|
if (!info.canManage)
|
|
40
42
|
console.log(`(当前平台不支持服务管理)`);
|
|
41
43
|
}
|
|
44
|
+
else if (cliCommand === 'addCors') {
|
|
45
|
+
const domain = process.argv[3];
|
|
46
|
+
if (!domain) {
|
|
47
|
+
console.log('用法: gateway addCors <域名>');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
const added = persistStore.addCorsDomain(domain);
|
|
51
|
+
if (added) {
|
|
52
|
+
console.log(`✅ 已添加 CORS 域名: ${domain}`);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
console.log(`⚠️ 域名已存在: ${domain}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else if (cliCommand === 'removeCors') {
|
|
59
|
+
const domain = process.argv[3];
|
|
60
|
+
if (!domain) {
|
|
61
|
+
console.log('用法: gateway removeCors <域名>');
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
const removed = persistStore.removeCorsDomain(domain);
|
|
65
|
+
if (removed) {
|
|
66
|
+
console.log(`✅ 已移除 CORS 域名: ${domain}`);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.log(`⚠️ 域名不存在: ${domain}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else if (cliCommand === 'listCors') {
|
|
73
|
+
const domains = persistStore.getCorsDomains();
|
|
74
|
+
if (domains.length === 0) {
|
|
75
|
+
console.log('暂无自定义 CORS 域名(仅使用内置内网规则)');
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
console.log(`自定义 CORS 域名 (${domains.length}):`);
|
|
79
|
+
domains.forEach(d => console.log(` - ${d}`));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (cliCommand === '--help' || cliCommand === '-h') {
|
|
83
|
+
console.log(`我的助手 Gateway CLI
|
|
84
|
+
|
|
85
|
+
用法: gateway <命令>
|
|
86
|
+
|
|
87
|
+
服务管理命令:
|
|
88
|
+
install 安装 Gateway 服务(后台运行)
|
|
89
|
+
uninstall 卸载 Gateway 服务
|
|
90
|
+
start 启动 Gateway 服务
|
|
91
|
+
stop 停止 Gateway 服务
|
|
92
|
+
restart 重启 Gateway 服务
|
|
93
|
+
update 更新 Gateway(需重新安装)
|
|
94
|
+
status 查看服务状态
|
|
95
|
+
|
|
96
|
+
CORS 管理命令:
|
|
97
|
+
addCors <域名> 添加允许的跨域域名
|
|
98
|
+
removeCors <域名> 移除允许的跨域域名
|
|
99
|
+
listCors 列出所有自定义跨域域名
|
|
100
|
+
|
|
101
|
+
其他:
|
|
102
|
+
--help, -h 显示本帮助信息
|
|
103
|
+
`);
|
|
104
|
+
const domains = persistStore.getCorsDomains();
|
|
105
|
+
if (domains.length === 0) {
|
|
106
|
+
console.log('暂无自定义 CORS 域名(仅使用内置内网规则)');
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
console.log(`自定义 CORS 域名 (${domains.length}):`);
|
|
110
|
+
domains.forEach(d => console.log(` - ${d}`));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
42
113
|
else {
|
|
43
114
|
const fnMap = {
|
|
44
115
|
install: installService,
|
|
45
116
|
uninstall: uninstallService,
|
|
46
117
|
start: startService,
|
|
47
118
|
stop: stopService,
|
|
119
|
+
restart: restartService,
|
|
48
120
|
update: updateService,
|
|
49
121
|
};
|
|
50
122
|
const handler = fnMap[cliCommand];
|
|
51
123
|
if (!handler) {
|
|
52
|
-
console.log(`未知命令: ${cliCommand}
|
|
124
|
+
console.log(`未知命令: ${cliCommand}。输入 "gateway --help" 查看帮助。`);
|
|
53
125
|
process.exit(1);
|
|
54
126
|
}
|
|
55
127
|
const result = await handler();
|
|
@@ -93,6 +165,15 @@ else {
|
|
|
93
165
|
if (lanPatterns.some(pattern => pattern.test(origin))) {
|
|
94
166
|
return callback(null, true);
|
|
95
167
|
}
|
|
168
|
+
// 检查自定义域名(支持子域名匹配)
|
|
169
|
+
const customDomains = persistStore.getCorsDomains();
|
|
170
|
+
const originLower = origin.toLowerCase();
|
|
171
|
+
if (customDomains.some(d => {
|
|
172
|
+
const dLower = d.toLowerCase();
|
|
173
|
+
return originLower === dLower || originLower.endsWith('.' + dLower);
|
|
174
|
+
})) {
|
|
175
|
+
return callback(null, true);
|
|
176
|
+
}
|
|
96
177
|
callback(new Error('Not allowed by CORS'));
|
|
97
178
|
},
|
|
98
179
|
credentials: true,
|
|
@@ -134,14 +215,7 @@ else {
|
|
|
134
215
|
if (port !== appConfig.port) {
|
|
135
216
|
logger.info(`Port ${appConfig.port} is in use, fallback to port ${port}`);
|
|
136
217
|
}
|
|
137
|
-
logger.info(
|
|
138
|
-
if (authStore.isAuthenticated()) {
|
|
139
|
-
const user = authStore.getUser();
|
|
140
|
-
logger.info(`User logged in: ${user?.nickname || user?.email} (ID: ${user?.id})`);
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
logger.info(`No user logged in`);
|
|
144
|
-
}
|
|
218
|
+
logger.info(`我的助手 Gateway Service running on port ${port}`);
|
|
145
219
|
}).on('error', (err) => {
|
|
146
220
|
if (err.code === 'EADDRINUSE') {
|
|
147
221
|
const nextPort = port + 1;
|
package/dist/middleware/auth.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 认证中间件
|
|
3
|
+
* 从请求头 Authorization: Bearer <token> 解码 JWT,提取 userId
|
|
4
|
+
* 同时保留原始 token 供路由转发给 Server
|
|
5
|
+
*/
|
|
6
|
+
import { authStore } from '@/stores';
|
|
7
|
+
import { getLogger } from '@myassis/shared';
|
|
8
|
+
const logger = getLogger('middleware/auth');
|
|
2
9
|
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
|
|
3
10
|
/**
|
|
4
11
|
* 从请求头提取 Bearer token
|
|
@@ -10,18 +17,6 @@ function extractToken(req) {
|
|
|
10
17
|
}
|
|
11
18
|
return undefined;
|
|
12
19
|
}
|
|
13
|
-
/**
|
|
14
|
-
* 解码 JWT,提取 userId
|
|
15
|
-
*/
|
|
16
|
-
function decodeUserId(token) {
|
|
17
|
-
try {
|
|
18
|
-
const payload = jwt.verify(token, JWT_SECRET);
|
|
19
|
-
return payload.userId;
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
return undefined;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
20
|
/**
|
|
26
21
|
* 需要认证:token 无效返回 401
|
|
27
22
|
*/
|
|
@@ -31,7 +26,7 @@ export function requireAuth(req, res, next) {
|
|
|
31
26
|
res.status(401).json({ success: false, error: 'Unauthorized' });
|
|
32
27
|
return;
|
|
33
28
|
}
|
|
34
|
-
const userId =
|
|
29
|
+
const userId = authStore.getUserId(token);
|
|
35
30
|
if (!userId) {
|
|
36
31
|
res.status(401).json({ success: false, error: 'Unauthorized' });
|
|
37
32
|
return;
|
|
@@ -40,14 +35,3 @@ export function requireAuth(req, res, next) {
|
|
|
40
35
|
req.token = token;
|
|
41
36
|
next();
|
|
42
37
|
}
|
|
43
|
-
/**
|
|
44
|
-
* 可选认证:token 存在则解码,不存在也放行
|
|
45
|
-
*/
|
|
46
|
-
export function optionalAuth(req, _res, next) {
|
|
47
|
-
const token = extractToken(req);
|
|
48
|
-
if (token) {
|
|
49
|
-
req.userId = decodeUserId(token);
|
|
50
|
-
req.token = token;
|
|
51
|
-
}
|
|
52
|
-
next();
|
|
53
|
-
}
|
package/dist/routes/agent.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
2
|
import { requireAuth } from '@/middleware/auth.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { runWithToken } from '@/api/index.js';
|
|
4
|
+
import { getSessionManager } from '@/services/session';
|
|
5
|
+
import { initAgentManager, agentManagerMap } from '@/services/agent/AgentManager';
|
|
5
6
|
import { AgentStore } from '@/services/agent/AgentStore';
|
|
6
|
-
import { getLogger } from '@
|
|
7
|
+
import { getLogger } from '@myassis/shared';
|
|
7
8
|
const logger = getLogger('AgentRoutes');
|
|
8
9
|
import { sessionStore } from '@/services/session/SessionStore';
|
|
9
10
|
const router = Router();
|
|
10
11
|
// 所有路由需要认证
|
|
11
|
-
router.use(requireAuth)
|
|
12
|
+
router.use(requireAuth, (req, _res, next) => {
|
|
13
|
+
void runWithToken(req.token, async () => { next(); });
|
|
14
|
+
});
|
|
12
15
|
// Middleware: ensure agentManager is initialized
|
|
13
16
|
function ensureAgentManager(req, res, next) {
|
|
14
17
|
try {
|
|
15
18
|
// Get or create AgentStore using SessionStore's db instance
|
|
16
19
|
const db = sessionStore.getDb();
|
|
17
20
|
const agentStore = new AgentStore(db);
|
|
18
|
-
|
|
19
|
-
if (!agentManager) {
|
|
20
|
-
initAgentManager(agentStore);
|
|
21
|
-
}
|
|
21
|
+
const agentManager = initAgentManager(agentStore, req.userId);
|
|
22
22
|
agentManager.initialize();
|
|
23
|
-
req.agentManager =
|
|
23
|
+
req.agentManager = agentManagerMap;
|
|
24
24
|
next();
|
|
25
25
|
}
|
|
26
26
|
catch (error) {
|
|
@@ -36,6 +36,7 @@ function ensureAgentManager(req, res, next) {
|
|
|
36
36
|
router.get('/list', ensureAgentManager, async (req, res) => {
|
|
37
37
|
try {
|
|
38
38
|
const userId = req.userId;
|
|
39
|
+
const agentManager = agentManagerMap.get(userId);
|
|
39
40
|
const agentList = agentManager.getAgentList();
|
|
40
41
|
res.json({ success: true, data: agentList });
|
|
41
42
|
}
|
|
@@ -51,6 +52,7 @@ router.get('/list', ensureAgentManager, async (req, res) => {
|
|
|
51
52
|
router.get('/:id', ensureAgentManager, async (req, res) => {
|
|
52
53
|
try {
|
|
53
54
|
const userId = req.userId;
|
|
55
|
+
const agentManager = agentManagerMap.get(userId);
|
|
54
56
|
const agent = agentManager.getAgent(req.params.id);
|
|
55
57
|
if (!agent) {
|
|
56
58
|
return res.status(404).json({ success: false, error: 'Agent not found' });
|
|
@@ -85,6 +87,7 @@ router.post('/create', ensureAgentManager, async (req, res) => {
|
|
|
85
87
|
if (!name) {
|
|
86
88
|
return res.status(400).json({ success: false, error: 'Name is required' });
|
|
87
89
|
}
|
|
90
|
+
const agentManager = agentManagerMap.get(userId);
|
|
88
91
|
const agent = agentManager.createAgent({
|
|
89
92
|
name,
|
|
90
93
|
description,
|
|
@@ -120,6 +123,7 @@ router.put('/:id', ensureAgentManager, async (req, res) => {
|
|
|
120
123
|
const userId = req.userId;
|
|
121
124
|
logger.debug('update agent', req.body);
|
|
122
125
|
const { name, description, systemPrompt, avatar, config } = req.body;
|
|
126
|
+
const agentManager = agentManagerMap.get(userId);
|
|
123
127
|
const agent = agentManager.updateAgent(req.params.id, {
|
|
124
128
|
name,
|
|
125
129
|
description,
|
|
@@ -155,6 +159,7 @@ router.put('/:id', ensureAgentManager, async (req, res) => {
|
|
|
155
159
|
router.delete('/:id', ensureAgentManager, async (req, res) => {
|
|
156
160
|
try {
|
|
157
161
|
const userId = req.userId;
|
|
162
|
+
const agentManager = agentManagerMap.get(userId);
|
|
158
163
|
const deleted = agentManager.deleteAgent(req.params.id);
|
|
159
164
|
if (!deleted) {
|
|
160
165
|
return res.status(404).json({ success: false, error: 'Agent not found' });
|
|
@@ -175,6 +180,7 @@ router.delete('/:id', ensureAgentManager, async (req, res) => {
|
|
|
175
180
|
router.get('/current', ensureAgentManager, async (req, res) => {
|
|
176
181
|
try {
|
|
177
182
|
const userId = req.userId;
|
|
183
|
+
const agentManager = agentManagerMap.get(userId);
|
|
178
184
|
const current = agentManager.getCurrentAgent();
|
|
179
185
|
res.json({
|
|
180
186
|
success: true, data: current ? {
|
|
@@ -203,6 +209,7 @@ router.post('/set-current', ensureAgentManager, async (req, res) => {
|
|
|
203
209
|
try {
|
|
204
210
|
const userId = req.userId;
|
|
205
211
|
const { agentId } = req.body;
|
|
212
|
+
const agentManager = agentManagerMap.get(userId);
|
|
206
213
|
agentManager.setCurrentAgent(agentId || null);
|
|
207
214
|
res.json({ success: true });
|
|
208
215
|
}
|
|
@@ -219,6 +226,7 @@ router.post('/set-current', ensureAgentManager, async (req, res) => {
|
|
|
219
226
|
router.get('/:id/sessions', ensureAgentManager, async (req, res) => {
|
|
220
227
|
try {
|
|
221
228
|
const userId = req.userId;
|
|
229
|
+
const agentManager = agentManagerMap.get(userId);
|
|
222
230
|
const agent = agentManager.getAgent(req.params.id);
|
|
223
231
|
if (!agent) {
|
|
224
232
|
return res.status(404).json({ success: false, error: 'Agent not found' });
|
|
@@ -238,6 +246,7 @@ router.get('/:id/sessions', ensureAgentManager, async (req, res) => {
|
|
|
238
246
|
router.post('/:id/sessions', ensureAgentManager, async (req, res) => {
|
|
239
247
|
try {
|
|
240
248
|
const userId = req.userId;
|
|
249
|
+
const agentManager = agentManagerMap.get(userId);
|
|
241
250
|
const agent = agentManager.getAgent(req.params.id);
|
|
242
251
|
if (!agent) {
|
|
243
252
|
return res.status(404).json({ success: false, error: 'Agent not found' });
|
|
@@ -270,6 +279,7 @@ router.post('/:id/sessions', ensureAgentManager, async (req, res) => {
|
|
|
270
279
|
router.put('/:id/sessions/:sessionId', ensureAgentManager, async (req, res) => {
|
|
271
280
|
try {
|
|
272
281
|
const userId = req.userId;
|
|
282
|
+
const agentManager = agentManagerMap.get(userId);
|
|
273
283
|
const agent = agentManager.getAgent(req.params.id);
|
|
274
284
|
if (!agent) {
|
|
275
285
|
return res.status(404).json({ success: false, error: 'Agent not found' });
|
|
@@ -299,6 +309,7 @@ router.put('/:id/sessions/:sessionId', ensureAgentManager, async (req, res) => {
|
|
|
299
309
|
router.delete('/:id/sessions/:sessionId', ensureAgentManager, async (req, res) => {
|
|
300
310
|
try {
|
|
301
311
|
const userId = req.userId;
|
|
312
|
+
const agentManager = agentManagerMap.get(userId);
|
|
302
313
|
const agent = agentManager.getAgent(req.params.id);
|
|
303
314
|
if (!agent) {
|
|
304
315
|
return res.status(404).json({ success: false, error: 'Agent not found' });
|
|
@@ -323,7 +334,7 @@ router.get('/sessions/:sessionId/messages', ensureAgentManager, async (req, res)
|
|
|
323
334
|
const page = parseInt(req.query.page) || 1;
|
|
324
335
|
const pageSize = parseInt(req.query.pageSize) || 20;
|
|
325
336
|
// Find session directly
|
|
326
|
-
const session =
|
|
337
|
+
const session = getSessionManager(userId).getSession(sessionId);
|
|
327
338
|
if (session) {
|
|
328
339
|
session.loadMessages(); // Ensure messages are loaded
|
|
329
340
|
const messages = session.getMessagesByPage(page, pageSize);
|
|
@@ -354,7 +365,7 @@ router.delete('/sessions/:sessionId/messages/:messageId', ensureAgentManager, as
|
|
|
354
365
|
const userId = req.userId;
|
|
355
366
|
const { sessionId, messageId } = req.params;
|
|
356
367
|
// Find session directly
|
|
357
|
-
const session =
|
|
368
|
+
const session = getSessionManager(userId).getSession(sessionId);
|
|
358
369
|
if (session) {
|
|
359
370
|
session.deleteMessage(messageId);
|
|
360
371
|
return res.json({ success: true });
|
|
@@ -378,7 +389,7 @@ router.put('/sessions/:sessionId/messages/:messageId/feedback', ensureAgentManag
|
|
|
378
389
|
if (feedback !== null && feedback !== 'like' && feedback !== 'dislike') {
|
|
379
390
|
return res.status(400).json({ success: false, error: 'Invalid feedback value. Must be "like", "dislike", or null' });
|
|
380
391
|
}
|
|
381
|
-
const session =
|
|
392
|
+
const session = getSessionManager(userId).getSession(sessionId);
|
|
382
393
|
if (session) {
|
|
383
394
|
session.updateMessageFeedback(messageId, feedback);
|
|
384
395
|
return res.json({ success: true, data: { messageId, feedback } });
|
|
@@ -399,7 +410,7 @@ router.delete('/sessions/:sessionId/messages', ensureAgentManager, async (req, r
|
|
|
399
410
|
const userId = req.userId;
|
|
400
411
|
const { sessionId } = req.params;
|
|
401
412
|
// Find session directly
|
|
402
|
-
const session =
|
|
413
|
+
const session = getSessionManager(userId).getSession(sessionId);
|
|
403
414
|
if (session) {
|
|
404
415
|
session.clearMessages();
|
|
405
416
|
return res.json({ success: true });
|
|
@@ -419,12 +430,12 @@ router.post('/sessions/:sessionId/stream', ensureAgentManager, async (req, res)
|
|
|
419
430
|
try {
|
|
420
431
|
const userId = req.userId;
|
|
421
432
|
const { sessionId } = req.params;
|
|
422
|
-
const { content, attachments, userMessageId, assistantMessageId
|
|
433
|
+
const { content, attachments, userMessageId, assistantMessageId } = req.body;
|
|
423
434
|
if (!content) {
|
|
424
435
|
return res.status(400).json({ success: false, error: 'Content is required' });
|
|
425
436
|
}
|
|
426
437
|
// Find session directly
|
|
427
|
-
const session =
|
|
438
|
+
const session = getSessionManager(userId).getSession(sessionId);
|
|
428
439
|
if (session) {
|
|
429
440
|
// Set SSE headers
|
|
430
441
|
res.setHeader('Content-Type', 'text/event-stream');
|
|
@@ -464,7 +475,7 @@ router.post('/sessions/:sessionId/reset', async (req, res) => {
|
|
|
464
475
|
try {
|
|
465
476
|
const userId = req.userId;
|
|
466
477
|
const { sessionId } = req.params;
|
|
467
|
-
const stopped =
|
|
478
|
+
const stopped = getSessionManager(userId).stopSession(sessionId);
|
|
468
479
|
res.json({ success: true, data: { stopped } });
|
|
469
480
|
}
|
|
470
481
|
catch (error) {
|
|
@@ -481,9 +492,9 @@ router.post('/sessions/:sessionId/set-current', async (req, res) => {
|
|
|
481
492
|
try {
|
|
482
493
|
const userId = req.userId;
|
|
483
494
|
const { sessionId } = req.params;
|
|
484
|
-
|
|
495
|
+
getSessionManager(userId).setCurrentSession(sessionId);
|
|
485
496
|
// 同步清除 session 的未读数
|
|
486
|
-
const session =
|
|
497
|
+
const session = getSessionManager(userId).getSession(sessionId);
|
|
487
498
|
if (session) {
|
|
488
499
|
session.clearUnreadCount();
|
|
489
500
|
}
|
|
@@ -502,7 +513,7 @@ router.post('/sessions/:sessionId/set-current', async (req, res) => {
|
|
|
502
513
|
router.get('/sessions/unread-count', async (req, res) => {
|
|
503
514
|
try {
|
|
504
515
|
const userId = req.userId;
|
|
505
|
-
const total =
|
|
516
|
+
const total = getSessionManager(userId).getTotalUnreadCount();
|
|
506
517
|
res.json({ success: true, data: { total } });
|
|
507
518
|
}
|
|
508
519
|
catch (error) {
|