@samanhappy/mcphub 0.12.1 → 0.12.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/README.fr.md +4 -0
- package/README.md +4 -0
- package/README.zh.md +4 -0
- package/dist/betterAuth.js +63 -4
- package/dist/betterAuth.js.map +1 -1
- package/dist/controllers/configController.js +1 -1
- package/dist/controllers/dxtController.js +1 -137
- package/dist/controllers/dxtController.js.map +1 -1
- package/dist/controllers/mcpbController.js +138 -0
- package/dist/controllers/mcpbController.js.map +1 -0
- package/dist/controllers/serverController.js +76 -12
- package/dist/controllers/serverController.js.map +1 -1
- package/dist/dao/DaoFactory.js +3 -4
- package/dist/dao/DaoFactory.js.map +1 -1
- package/dist/dao/examples.js +1 -1
- package/dist/dao/examples.js.map +1 -1
- package/dist/db/connection.js +146 -1
- package/dist/db/connection.js.map +1 -1
- package/dist/db/repositories/BaseRepository.js +33 -9
- package/dist/db/repositories/BaseRepository.js.map +1 -1
- package/dist/db/repositories/BearerKeyRepository.js +41 -16
- package/dist/db/repositories/BearerKeyRepository.js.map +1 -1
- package/dist/db/repositories/OAuthTokenRepository.js +107 -68
- package/dist/db/repositories/OAuthTokenRepository.js.map +1 -1
- package/dist/db/repositories/UserRepository.js +45 -18
- package/dist/db/repositories/UserRepository.js.map +1 -1
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -1
- package/dist/routes/index.js +3 -3
- package/dist/routes/index.js.map +1 -1
- package/dist/server.js +39 -2
- package/dist/server.js.map +1 -1
- package/dist/services/mcpService.js +19 -1
- package/dist/services/mcpService.js.map +1 -1
- package/dist/services/proxy.js +0 -2
- package/dist/services/proxy.js.map +1 -1
- package/dist/services/sseService.js +25 -9
- package/dist/services/sseService.js.map +1 -1
- package/dist/services/vectorSearchService.js +74 -8
- package/dist/services/vectorSearchService.js.map +1 -1
- package/dist/utils/dbRetry.js +199 -0
- package/dist/utils/dbRetry.js.map +1 -0
- package/dist/utils/smartRouting.js +13 -0
- package/dist/utils/smartRouting.js.map +1 -1
- package/frontend/dist/assets/index-AkdLybCq.js +294 -0
- package/frontend/dist/assets/index-AkdLybCq.js.map +1 -0
- package/frontend/dist/assets/index-DkQ2UJF_.css +1 -0
- package/frontend/dist/index.html +2 -2
- package/package.json +13 -9
- package/frontend/dist/assets/index-7Q7UOsR3.js +0 -289
- package/frontend/dist/assets/index-7Q7UOsR3.js.map +0 -1
- package/frontend/dist/assets/index-GOMK9Sm1.css +0 -1
|
@@ -1,60 +1,85 @@
|
|
|
1
1
|
import { BearerKey } from '../entities/BearerKey.js';
|
|
2
2
|
import { getAppDataSource } from '../connection.js';
|
|
3
|
+
import { withDbRetry } from '../../utils/dbRetry.js';
|
|
4
|
+
// Default retry options
|
|
5
|
+
const DEFAULT_RETRY_OPTIONS = {
|
|
6
|
+
maxRetries: 3,
|
|
7
|
+
initialDelayMs: 500,
|
|
8
|
+
maxDelayMs: 10000,
|
|
9
|
+
backoffMultiplier: 2,
|
|
10
|
+
jitter: true,
|
|
11
|
+
};
|
|
3
12
|
/**
|
|
4
|
-
* Repository for BearerKey entity
|
|
13
|
+
* Repository for BearerKey entity with automatic retry logic
|
|
5
14
|
*/
|
|
6
15
|
export class BearerKeyRepository {
|
|
7
|
-
constructor() {
|
|
16
|
+
constructor(retryOptions) {
|
|
8
17
|
this.repository = getAppDataSource().getRepository(BearerKey);
|
|
18
|
+
this.retryOptions = { ...DEFAULT_RETRY_OPTIONS, ...retryOptions };
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Execute an operation with retry logic
|
|
22
|
+
*/
|
|
23
|
+
async withRetry(operation, operationName) {
|
|
24
|
+
return withDbRetry(operation, {
|
|
25
|
+
...this.retryOptions,
|
|
26
|
+
operationName: `BearerKeyRepository.${operationName}`,
|
|
27
|
+
});
|
|
9
28
|
}
|
|
10
29
|
/**
|
|
11
30
|
* Find all bearer keys
|
|
12
31
|
*/
|
|
13
32
|
async findAll() {
|
|
14
|
-
return
|
|
33
|
+
return this.withRetry(() => this.repository.find({ order: { createdAt: 'ASC' } }), 'findAll');
|
|
15
34
|
}
|
|
16
35
|
/**
|
|
17
36
|
* Count bearer keys
|
|
18
37
|
*/
|
|
19
38
|
async count() {
|
|
20
|
-
return
|
|
39
|
+
return this.withRetry(() => this.repository.count(), 'count');
|
|
21
40
|
}
|
|
22
41
|
/**
|
|
23
42
|
* Find bearer key by id
|
|
24
43
|
*/
|
|
25
44
|
async findById(id) {
|
|
26
|
-
return
|
|
45
|
+
return this.withRetry(() => this.repository.findOne({ where: { id } }), 'findById');
|
|
27
46
|
}
|
|
28
47
|
/**
|
|
29
48
|
* Find bearer key by token value
|
|
30
49
|
*/
|
|
31
50
|
async findByToken(token) {
|
|
32
|
-
return
|
|
51
|
+
return this.withRetry(() => this.repository.findOne({ where: { token } }), 'findByToken');
|
|
33
52
|
}
|
|
34
53
|
/**
|
|
35
54
|
* Create a new bearer key
|
|
36
55
|
*/
|
|
37
56
|
async create(data) {
|
|
38
|
-
|
|
39
|
-
|
|
57
|
+
return this.withRetry(async () => {
|
|
58
|
+
const entity = this.repository.create(data);
|
|
59
|
+
return await this.repository.save(entity);
|
|
60
|
+
}, 'create');
|
|
40
61
|
}
|
|
41
62
|
/**
|
|
42
63
|
* Update an existing bearer key
|
|
43
64
|
*/
|
|
44
65
|
async update(id, updates) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
66
|
+
return this.withRetry(async () => {
|
|
67
|
+
const existing = await this.findById(id);
|
|
68
|
+
if (!existing) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const merged = this.repository.merge(existing, updates);
|
|
72
|
+
return await this.repository.save(merged);
|
|
73
|
+
}, 'update');
|
|
51
74
|
}
|
|
52
75
|
/**
|
|
53
76
|
* Delete a bearer key
|
|
54
77
|
*/
|
|
55
78
|
async delete(id) {
|
|
56
|
-
|
|
57
|
-
|
|
79
|
+
return this.withRetry(async () => {
|
|
80
|
+
const result = await this.repository.delete({ id });
|
|
81
|
+
return (result.affected ?? 0) > 0;
|
|
82
|
+
}, 'delete');
|
|
58
83
|
}
|
|
59
84
|
}
|
|
60
85
|
export default BearerKeyRepository;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BearerKeyRepository.js","sourceRoot":"","sources":["../../../src/db/repositories/BearerKeyRepository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"BearerKeyRepository.js","sourceRoot":"","sources":["../../../src/db/repositories/BearerKeyRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,158 +1,197 @@
|
|
|
1
1
|
import { MoreThan } from 'typeorm';
|
|
2
2
|
import { OAuthToken } from '../entities/OAuthToken.js';
|
|
3
3
|
import { getAppDataSource } from '../connection.js';
|
|
4
|
+
import { withDbRetry } from '../../utils/dbRetry.js';
|
|
5
|
+
// Default retry options
|
|
6
|
+
const DEFAULT_RETRY_OPTIONS = {
|
|
7
|
+
maxRetries: 3,
|
|
8
|
+
initialDelayMs: 500,
|
|
9
|
+
maxDelayMs: 10000,
|
|
10
|
+
backoffMultiplier: 2,
|
|
11
|
+
jitter: true,
|
|
12
|
+
};
|
|
4
13
|
/**
|
|
5
|
-
* Repository for OAuthToken entity
|
|
14
|
+
* Repository for OAuthToken entity with automatic retry logic
|
|
6
15
|
*/
|
|
7
16
|
export class OAuthTokenRepository {
|
|
8
|
-
constructor() {
|
|
17
|
+
constructor(retryOptions) {
|
|
9
18
|
this.repository = getAppDataSource().getRepository(OAuthToken);
|
|
19
|
+
this.retryOptions = { ...DEFAULT_RETRY_OPTIONS, ...retryOptions };
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Execute an operation with retry logic
|
|
23
|
+
*/
|
|
24
|
+
async withRetry(operation, operationName) {
|
|
25
|
+
return withDbRetry(operation, {
|
|
26
|
+
...this.retryOptions,
|
|
27
|
+
operationName: `OAuthTokenRepository.${operationName}`,
|
|
28
|
+
});
|
|
10
29
|
}
|
|
11
30
|
/**
|
|
12
31
|
* Find all OAuth tokens
|
|
13
32
|
*/
|
|
14
33
|
async findAll() {
|
|
15
|
-
return
|
|
34
|
+
return this.withRetry(() => this.repository.find(), 'findAll');
|
|
16
35
|
}
|
|
17
36
|
/**
|
|
18
37
|
* Find OAuth token by access token
|
|
19
38
|
*/
|
|
20
39
|
async findByAccessToken(accessToken) {
|
|
21
|
-
return
|
|
40
|
+
return this.withRetry(() => this.repository.findOne({ where: { accessToken } }), 'findByAccessToken');
|
|
22
41
|
}
|
|
23
42
|
/**
|
|
24
43
|
* Find OAuth token by refresh token
|
|
25
44
|
*/
|
|
26
45
|
async findByRefreshToken(refreshToken) {
|
|
27
|
-
return
|
|
46
|
+
return this.withRetry(() => this.repository.findOne({ where: { refreshToken } }), 'findByRefreshToken');
|
|
28
47
|
}
|
|
29
48
|
/**
|
|
30
49
|
* Find OAuth tokens by client ID
|
|
31
50
|
*/
|
|
32
51
|
async findByClientId(clientId) {
|
|
33
|
-
return
|
|
52
|
+
return this.withRetry(() => this.repository.find({ where: { clientId } }), 'findByClientId');
|
|
34
53
|
}
|
|
35
54
|
/**
|
|
36
55
|
* Find OAuth tokens by username
|
|
37
56
|
*/
|
|
38
57
|
async findByUsername(username) {
|
|
39
|
-
return
|
|
58
|
+
return this.withRetry(() => this.repository.find({ where: { username } }), 'findByUsername');
|
|
40
59
|
}
|
|
41
60
|
/**
|
|
42
61
|
* Create a new OAuth token
|
|
43
62
|
*/
|
|
44
63
|
async create(token) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
return this.withRetry(async () => {
|
|
65
|
+
// Remove any existing tokens with the same access token or refresh token
|
|
66
|
+
if (token.accessToken) {
|
|
67
|
+
await this.repository.delete({ accessToken: token.accessToken });
|
|
68
|
+
}
|
|
69
|
+
if (token.refreshToken) {
|
|
70
|
+
await this.repository.delete({ refreshToken: token.refreshToken });
|
|
71
|
+
}
|
|
72
|
+
const newToken = this.repository.create(token);
|
|
73
|
+
return await this.repository.save(newToken);
|
|
74
|
+
}, 'create');
|
|
54
75
|
}
|
|
55
76
|
/**
|
|
56
77
|
* Update an existing OAuth token
|
|
57
78
|
*/
|
|
58
79
|
async update(accessToken, tokenData) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
80
|
+
return this.withRetry(async () => {
|
|
81
|
+
const token = await this.repository.findOne({ where: { accessToken } });
|
|
82
|
+
if (!token) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
const updated = this.repository.merge(token, tokenData);
|
|
86
|
+
return await this.repository.save(updated);
|
|
87
|
+
}, 'update');
|
|
65
88
|
}
|
|
66
89
|
/**
|
|
67
90
|
* Delete an OAuth token by access token
|
|
68
91
|
*/
|
|
69
92
|
async delete(accessToken) {
|
|
70
|
-
|
|
71
|
-
|
|
93
|
+
return this.withRetry(async () => {
|
|
94
|
+
const result = await this.repository.delete({ accessToken });
|
|
95
|
+
return (result.affected ?? 0) > 0;
|
|
96
|
+
}, 'delete');
|
|
72
97
|
}
|
|
73
98
|
/**
|
|
74
99
|
* Check if OAuth token exists by access token
|
|
75
100
|
*/
|
|
76
101
|
async exists(accessToken) {
|
|
77
|
-
|
|
78
|
-
|
|
102
|
+
return this.withRetry(async () => {
|
|
103
|
+
const count = await this.repository.count({ where: { accessToken } });
|
|
104
|
+
return count > 0;
|
|
105
|
+
}, 'exists');
|
|
79
106
|
}
|
|
80
107
|
/**
|
|
81
108
|
* Count total OAuth tokens
|
|
82
109
|
*/
|
|
83
110
|
async count() {
|
|
84
|
-
return
|
|
111
|
+
return this.withRetry(() => this.repository.count(), 'count');
|
|
85
112
|
}
|
|
86
113
|
/**
|
|
87
114
|
* Revoke token by access token or refresh token
|
|
88
115
|
*/
|
|
89
116
|
async revokeToken(token) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
117
|
+
return this.withRetry(async () => {
|
|
118
|
+
// Try to find by access token first
|
|
119
|
+
let tokenEntity = await this.repository.findOne({ where: { accessToken: token } });
|
|
120
|
+
if (!tokenEntity) {
|
|
121
|
+
// Try to find by refresh token
|
|
122
|
+
tokenEntity = await this.repository.findOne({ where: { refreshToken: token } });
|
|
123
|
+
}
|
|
124
|
+
if (!tokenEntity) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
const result = await this.repository.delete({ id: tokenEntity.id });
|
|
128
|
+
return (result.affected ?? 0) > 0;
|
|
129
|
+
}, 'revokeToken');
|
|
101
130
|
}
|
|
102
131
|
/**
|
|
103
132
|
* Revoke all tokens for a user
|
|
104
133
|
*/
|
|
105
134
|
async revokeUserTokens(username) {
|
|
106
|
-
|
|
107
|
-
|
|
135
|
+
return this.withRetry(async () => {
|
|
136
|
+
const result = await this.repository.delete({ username });
|
|
137
|
+
return result.affected ?? 0;
|
|
138
|
+
}, 'revokeUserTokens');
|
|
108
139
|
}
|
|
109
140
|
/**
|
|
110
141
|
* Revoke all tokens for a client
|
|
111
142
|
*/
|
|
112
143
|
async revokeClientTokens(clientId) {
|
|
113
|
-
|
|
114
|
-
|
|
144
|
+
return this.withRetry(async () => {
|
|
145
|
+
const result = await this.repository.delete({ clientId });
|
|
146
|
+
return result.affected ?? 0;
|
|
147
|
+
}, 'revokeClientTokens');
|
|
115
148
|
}
|
|
116
149
|
/**
|
|
117
150
|
* Clean up expired tokens
|
|
118
151
|
*/
|
|
119
152
|
async cleanupExpired() {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
153
|
+
return this.withRetry(async () => {
|
|
154
|
+
const now = new Date();
|
|
155
|
+
// Delete tokens where both access token and refresh token are expired
|
|
156
|
+
// (or refresh token doesn't exist)
|
|
157
|
+
const result = await this.repository
|
|
158
|
+
.createQueryBuilder()
|
|
159
|
+
.delete()
|
|
160
|
+
.from(OAuthToken)
|
|
161
|
+
.where('access_token_expires_at < :now', { now })
|
|
162
|
+
.andWhere('(refresh_token_expires_at IS NULL OR refresh_token_expires_at < :now)', { now })
|
|
163
|
+
.execute();
|
|
164
|
+
return result.affected ?? 0;
|
|
165
|
+
}, 'cleanupExpired');
|
|
131
166
|
}
|
|
132
167
|
/**
|
|
133
168
|
* Check if access token is valid (exists and not expired)
|
|
134
169
|
*/
|
|
135
170
|
async isAccessTokenValid(accessToken) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
171
|
+
return this.withRetry(async () => {
|
|
172
|
+
const count = await this.repository.count({
|
|
173
|
+
where: {
|
|
174
|
+
accessToken,
|
|
175
|
+
accessTokenExpiresAt: MoreThan(new Date()),
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
return count > 0;
|
|
179
|
+
}, 'isAccessTokenValid');
|
|
143
180
|
}
|
|
144
181
|
/**
|
|
145
182
|
* Check if refresh token is valid (exists and not expired)
|
|
146
183
|
*/
|
|
147
184
|
async isRefreshTokenValid(refreshToken) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
185
|
+
return this.withRetry(async () => {
|
|
186
|
+
const token = await this.repository.findOne({ where: { refreshToken } });
|
|
187
|
+
if (!token) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
if (!token.refreshTokenExpiresAt) {
|
|
191
|
+
return true; // No expiration means always valid
|
|
192
|
+
}
|
|
193
|
+
return token.refreshTokenExpiresAt > new Date();
|
|
194
|
+
}, 'isRefreshTokenValid');
|
|
156
195
|
}
|
|
157
196
|
}
|
|
158
197
|
export default OAuthTokenRepository;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OAuthTokenRepository.js","sourceRoot":"","sources":["../../../src/db/repositories/OAuthTokenRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"OAuthTokenRepository.js","sourceRoot":"","sources":["../../../src/db/repositories/OAuthTokenRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,67 +1,94 @@
|
|
|
1
1
|
import { User } from '../entities/User.js';
|
|
2
2
|
import { getAppDataSource } from '../connection.js';
|
|
3
|
+
import { withDbRetry } from '../../utils/dbRetry.js';
|
|
4
|
+
// Default retry options
|
|
5
|
+
const DEFAULT_RETRY_OPTIONS = {
|
|
6
|
+
maxRetries: 3,
|
|
7
|
+
initialDelayMs: 500,
|
|
8
|
+
maxDelayMs: 10000,
|
|
9
|
+
backoffMultiplier: 2,
|
|
10
|
+
jitter: true,
|
|
11
|
+
};
|
|
3
12
|
/**
|
|
4
|
-
* Repository for User entity
|
|
13
|
+
* Repository for User entity with automatic retry logic
|
|
5
14
|
*/
|
|
6
15
|
export class UserRepository {
|
|
7
|
-
constructor() {
|
|
16
|
+
constructor(retryOptions) {
|
|
8
17
|
this.repository = getAppDataSource().getRepository(User);
|
|
18
|
+
this.retryOptions = { ...DEFAULT_RETRY_OPTIONS, ...retryOptions };
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Execute an operation with retry logic
|
|
22
|
+
*/
|
|
23
|
+
async withRetry(operation, operationName) {
|
|
24
|
+
return withDbRetry(operation, {
|
|
25
|
+
...this.retryOptions,
|
|
26
|
+
operationName: `UserRepository.${operationName}`,
|
|
27
|
+
});
|
|
9
28
|
}
|
|
10
29
|
/**
|
|
11
30
|
* Find all users
|
|
12
31
|
*/
|
|
13
32
|
async findAll() {
|
|
14
|
-
return
|
|
33
|
+
return this.withRetry(() => this.repository.find({ order: { createdAt: 'ASC' } }), 'findAll');
|
|
15
34
|
}
|
|
16
35
|
/**
|
|
17
36
|
* Find user by username
|
|
18
37
|
*/
|
|
19
38
|
async findByUsername(username) {
|
|
20
|
-
return
|
|
39
|
+
return this.withRetry(() => this.repository.findOne({ where: { username } }), 'findByUsername');
|
|
21
40
|
}
|
|
22
41
|
/**
|
|
23
42
|
* Create a new user
|
|
24
43
|
*/
|
|
25
44
|
async create(user) {
|
|
26
|
-
|
|
27
|
-
|
|
45
|
+
return this.withRetry(async () => {
|
|
46
|
+
const newUser = this.repository.create(user);
|
|
47
|
+
return await this.repository.save(newUser);
|
|
48
|
+
}, 'create');
|
|
28
49
|
}
|
|
29
50
|
/**
|
|
30
51
|
* Update an existing user
|
|
31
52
|
*/
|
|
32
53
|
async update(username, userData) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
54
|
+
return this.withRetry(async () => {
|
|
55
|
+
const user = await this.repository.findOne({ where: { username } });
|
|
56
|
+
if (!user) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const updated = this.repository.merge(user, userData);
|
|
60
|
+
return await this.repository.save(updated);
|
|
61
|
+
}, 'update');
|
|
39
62
|
}
|
|
40
63
|
/**
|
|
41
64
|
* Delete a user
|
|
42
65
|
*/
|
|
43
66
|
async delete(username) {
|
|
44
|
-
|
|
45
|
-
|
|
67
|
+
return this.withRetry(async () => {
|
|
68
|
+
const result = await this.repository.delete({ username });
|
|
69
|
+
return (result.affected ?? 0) > 0;
|
|
70
|
+
}, 'delete');
|
|
46
71
|
}
|
|
47
72
|
/**
|
|
48
73
|
* Check if user exists
|
|
49
74
|
*/
|
|
50
75
|
async exists(username) {
|
|
51
|
-
|
|
52
|
-
|
|
76
|
+
return this.withRetry(async () => {
|
|
77
|
+
const count = await this.repository.count({ where: { username } });
|
|
78
|
+
return count > 0;
|
|
79
|
+
}, 'exists');
|
|
53
80
|
}
|
|
54
81
|
/**
|
|
55
82
|
* Count total users
|
|
56
83
|
*/
|
|
57
84
|
async count() {
|
|
58
|
-
return
|
|
85
|
+
return this.withRetry(() => this.repository.count(), 'count');
|
|
59
86
|
}
|
|
60
87
|
/**
|
|
61
88
|
* Find all admin users
|
|
62
89
|
*/
|
|
63
90
|
async findAdmins() {
|
|
64
|
-
return
|
|
91
|
+
return this.withRetry(() => this.repository.find({ where: { isAdmin: true }, order: { createdAt: 'ASC' } }), 'findAdmins');
|
|
65
92
|
}
|
|
66
93
|
}
|
|
67
94
|
export default UserRepository;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserRepository.js","sourceRoot":"","sources":["../../../src/db/repositories/UserRepository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"UserRepository.js","sourceRoot":"","sources":["../../../src/db/repositories/UserRepository.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,83 @@ import 'reflect-metadata';
|
|
|
2
2
|
import AppServer from './server.js';
|
|
3
3
|
import { initializeDatabaseMode } from './utils/migration.js';
|
|
4
4
|
import { createFetchWithProxy, getProxyConfigFromEnv } from './services/proxy.js';
|
|
5
|
+
import { isRetryableDbError } from './utils/dbRetry.js';
|
|
5
6
|
const appServer = new AppServer();
|
|
7
|
+
// Track consecutive fatal errors for circuit breaker pattern
|
|
8
|
+
let consecutiveFatalErrors = 0;
|
|
9
|
+
const MAX_CONSECUTIVE_FATAL_ERRORS = 5;
|
|
10
|
+
const FATAL_ERROR_WINDOW_MS = 60000; // 1 minute
|
|
11
|
+
let lastFatalErrorTime = 0;
|
|
12
|
+
/**
|
|
13
|
+
* Handle uncaught exceptions - log and determine if recovery is possible
|
|
14
|
+
*/
|
|
15
|
+
const handleUncaughtException = (error) => {
|
|
16
|
+
console.error('[FATAL] Uncaught exception:', error);
|
|
17
|
+
// Check if this is a retryable database error
|
|
18
|
+
if (isRetryableDbError(error)) {
|
|
19
|
+
console.warn('[RECOVERY] Database connection error detected, attempting to continue...');
|
|
20
|
+
// For database errors, we don't crash - the retry logic should handle it
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
// Track consecutive fatal errors
|
|
24
|
+
const now = Date.now();
|
|
25
|
+
if (now - lastFatalErrorTime < FATAL_ERROR_WINDOW_MS) {
|
|
26
|
+
consecutiveFatalErrors++;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
consecutiveFatalErrors = 1;
|
|
30
|
+
}
|
|
31
|
+
lastFatalErrorTime = now;
|
|
32
|
+
// Circuit breaker: if too many consecutive errors, exit
|
|
33
|
+
if (consecutiveFatalErrors >= MAX_CONSECUTIVE_FATAL_ERRORS) {
|
|
34
|
+
console.error(`[FATAL] Too many consecutive fatal errors (${consecutiveFatalErrors}), exiting...`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
console.warn(`[RECOVERY] Non-fatal error, continuing... (error count: ${consecutiveFatalErrors})`);
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Handle unhandled promise rejections - log and determine if recovery is possible
|
|
41
|
+
*/
|
|
42
|
+
const handleUnhandledRejection = (reason, promise) => {
|
|
43
|
+
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
44
|
+
console.error('[FATAL] Unhandled promise rejection:', error);
|
|
45
|
+
console.error('[FATAL] Promise:', promise);
|
|
46
|
+
// Check if this is a retryable database error
|
|
47
|
+
if (isRetryableDbError(error)) {
|
|
48
|
+
console.warn('[RECOVERY] Database connection error detected in promise, attempting to continue...');
|
|
49
|
+
// For database errors, we don't crash - the retry logic should handle it
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// Track consecutive fatal errors
|
|
53
|
+
const now = Date.now();
|
|
54
|
+
if (now - lastFatalErrorTime < FATAL_ERROR_WINDOW_MS) {
|
|
55
|
+
consecutiveFatalErrors++;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
consecutiveFatalErrors = 1;
|
|
59
|
+
}
|
|
60
|
+
lastFatalErrorTime = now;
|
|
61
|
+
// Circuit breaker: if too many consecutive errors, exit
|
|
62
|
+
if (consecutiveFatalErrors >= MAX_CONSECUTIVE_FATAL_ERRORS) {
|
|
63
|
+
console.error(`[FATAL] Too many consecutive fatal errors (${consecutiveFatalErrors}), exiting...`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
console.warn(`[RECOVERY] Non-fatal error, continuing... (error count: ${consecutiveFatalErrors})`);
|
|
67
|
+
};
|
|
68
|
+
// Set up global error handlers
|
|
69
|
+
process.on('uncaughtException', handleUncaughtException);
|
|
70
|
+
process.on('unhandledRejection', handleUnhandledRejection);
|
|
71
|
+
// Handle graceful shutdown
|
|
72
|
+
process.on('SIGTERM', async () => {
|
|
73
|
+
console.log('[SHUTDOWN] Received SIGTERM, shutting down gracefully...');
|
|
74
|
+
await appServer.shutdown();
|
|
75
|
+
process.exit(0);
|
|
76
|
+
});
|
|
77
|
+
process.on('SIGINT', async () => {
|
|
78
|
+
console.log('[SHUTDOWN] Received SIGINT, shutting down gracefully...');
|
|
79
|
+
await appServer.shutdown();
|
|
80
|
+
process.exit(0);
|
|
81
|
+
});
|
|
6
82
|
const maskProxyUrl = (value) => {
|
|
7
83
|
if (!value) {
|
|
8
84
|
return undefined;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,SAAS,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|