@mcp-shark/mcp-shark 1.5.4 → 1.5.6

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.
Files changed (188) hide show
  1. package/README.md +32 -96
  2. package/bin/mcp-shark.js +1 -1
  3. package/core/configs/codex.js +68 -0
  4. package/core/configs/environment.js +51 -0
  5. package/{lib/common → core}/configs/index.js +16 -1
  6. package/core/constants/Defaults.js +15 -0
  7. package/core/constants/HttpStatus.js +14 -0
  8. package/core/constants/Server.js +20 -0
  9. package/core/constants/StatusCodes.js +25 -0
  10. package/core/constants/index.js +7 -0
  11. package/core/container/DependencyContainer.js +179 -0
  12. package/core/db/init.js +33 -0
  13. package/core/index.js +10 -0
  14. package/{mcp-server/lib/common/error.js → core/libraries/ErrorLibrary.js} +4 -0
  15. package/core/libraries/LoggerLibrary.js +91 -0
  16. package/core/libraries/SerializationLibrary.js +32 -0
  17. package/core/libraries/bootstrap-logger.js +19 -0
  18. package/core/libraries/errors/ApplicationError.js +97 -0
  19. package/core/libraries/index.js +17 -0
  20. package/{mcp-server/lib → core/mcp-server}/auditor/audit.js +77 -53
  21. package/core/mcp-server/index.js +192 -0
  22. package/{mcp-server/lib → core/mcp-server}/server/external/all.js +1 -1
  23. package/core/mcp-server/server/external/config.js +75 -0
  24. package/{mcp-server/lib → core/mcp-server}/server/external/single/client.js +1 -1
  25. package/{mcp-server/lib → core/mcp-server}/server/external/single/request.js +1 -1
  26. package/{mcp-server/lib → core/mcp-server}/server/external/single/run.js +20 -11
  27. package/{mcp-server/lib → core/mcp-server}/server/external/single/transport.js +1 -1
  28. package/{mcp-server/lib → core/mcp-server}/server/internal/handlers/error.js +1 -1
  29. package/core/mcp-server/server/internal/handlers/prompts-get.js +28 -0
  30. package/core/mcp-server/server/internal/handlers/prompts-list.js +21 -0
  31. package/core/mcp-server/server/internal/handlers/resources-list.js +21 -0
  32. package/core/mcp-server/server/internal/handlers/resources-read.js +28 -0
  33. package/core/mcp-server/server/internal/handlers/tools-call.js +44 -0
  34. package/core/mcp-server/server/internal/handlers/tools-list.js +23 -0
  35. package/core/mcp-server/server/internal/run.js +53 -0
  36. package/{mcp-server/lib → core/mcp-server}/server/internal/server.js +11 -1
  37. package/core/models/ConversationFilters.js +31 -0
  38. package/core/models/ExportFormat.js +8 -0
  39. package/core/models/RequestFilters.js +43 -0
  40. package/core/models/SessionFilters.js +23 -0
  41. package/core/models/index.js +8 -0
  42. package/core/repositories/AuditRepository.js +233 -0
  43. package/core/repositories/ConversationRepository.js +182 -0
  44. package/core/repositories/PacketRepository.js +237 -0
  45. package/core/repositories/SchemaRepository.js +107 -0
  46. package/core/repositories/SessionRepository.js +59 -0
  47. package/core/repositories/StatisticsRepository.js +54 -0
  48. package/core/repositories/index.js +10 -0
  49. package/core/services/AuditService.js +144 -0
  50. package/core/services/BackupService.js +222 -0
  51. package/core/services/ConfigDetectionService.js +89 -0
  52. package/core/services/ConfigFileService.js +210 -0
  53. package/core/services/ConfigPatchingService.js +137 -0
  54. package/core/services/ConfigService.js +250 -0
  55. package/core/services/ConfigTransformService.js +178 -0
  56. package/core/services/ConversationService.js +19 -0
  57. package/core/services/ExportService.js +117 -0
  58. package/core/services/LogService.js +64 -0
  59. package/core/services/McpClientService.js +235 -0
  60. package/core/services/McpDiscoveryService.js +107 -0
  61. package/core/services/RequestService.js +56 -0
  62. package/core/services/ScanCacheService.js +242 -0
  63. package/core/services/ScanService.js +167 -0
  64. package/core/services/ServerManagementService.js +206 -0
  65. package/core/services/SessionService.js +34 -0
  66. package/core/services/SettingsService.js +163 -0
  67. package/core/services/StatisticsService.js +64 -0
  68. package/core/services/TokenService.js +94 -0
  69. package/core/services/index.js +25 -0
  70. package/core/services/parsers/ConfigParserFactory.js +113 -0
  71. package/core/services/parsers/JsonConfigParser.js +66 -0
  72. package/core/services/parsers/LegacyJsonConfigParser.js +71 -0
  73. package/core/services/parsers/TomlConfigParser.js +87 -0
  74. package/core/services/parsers/index.js +4 -0
  75. package/{ui/server → core}/utils/scan-cache/directory.js +1 -1
  76. package/core/utils/validation.js +77 -0
  77. package/package.json +14 -11
  78. package/ui/dist/assets/index-CArYxKxS.js +35 -0
  79. package/ui/dist/index.html +1 -1
  80. package/ui/server/controllers/BackupController.js +129 -0
  81. package/ui/server/controllers/ConfigController.js +92 -0
  82. package/ui/server/controllers/ConversationController.js +41 -0
  83. package/ui/server/controllers/LogController.js +44 -0
  84. package/ui/server/controllers/McpClientController.js +60 -0
  85. package/ui/server/controllers/McpDiscoveryController.js +44 -0
  86. package/ui/server/controllers/RequestController.js +129 -0
  87. package/ui/server/controllers/ScanController.js +122 -0
  88. package/ui/server/controllers/ServerManagementController.js +154 -0
  89. package/ui/server/controllers/SessionController.js +57 -0
  90. package/ui/server/controllers/SettingsController.js +24 -0
  91. package/ui/server/controllers/StatisticsController.js +54 -0
  92. package/ui/server/controllers/TokenController.js +58 -0
  93. package/ui/server/controllers/index.js +17 -0
  94. package/ui/server/routes/backups/index.js +15 -9
  95. package/ui/server/routes/composite/index.js +63 -32
  96. package/ui/server/routes/composite/servers.js +20 -15
  97. package/ui/server/routes/config.js +13 -172
  98. package/ui/server/routes/conversations.js +9 -19
  99. package/ui/server/routes/help.js +4 -3
  100. package/ui/server/routes/logs.js +14 -26
  101. package/ui/server/routes/playground.js +11 -174
  102. package/ui/server/routes/requests.js +12 -232
  103. package/ui/server/routes/sessions.js +10 -21
  104. package/ui/server/routes/settings.js +10 -192
  105. package/ui/server/routes/smartscan.js +26 -15
  106. package/ui/server/routes/statistics.js +8 -79
  107. package/ui/server/setup.js +163 -0
  108. package/ui/server/swagger/paths/backups.js +151 -0
  109. package/ui/server/swagger/paths/components.js +76 -0
  110. package/ui/server/swagger/paths/config.js +117 -0
  111. package/ui/server/swagger/paths/conversations.js +29 -0
  112. package/ui/server/swagger/paths/help.js +82 -0
  113. package/ui/server/swagger/paths/logs.js +87 -0
  114. package/ui/server/swagger/paths/playground.js +49 -0
  115. package/ui/server/swagger/paths/requests.js +178 -0
  116. package/ui/server/swagger/paths/serverManagement.js +205 -0
  117. package/ui/server/swagger/paths/sessions.js +61 -0
  118. package/ui/server/swagger/paths/settings.js +31 -0
  119. package/ui/server/swagger/paths/smartScan/discovery.js +97 -0
  120. package/ui/server/swagger/paths/smartScan/index.js +13 -0
  121. package/ui/server/swagger/paths/smartScan/scans.js +151 -0
  122. package/ui/server/swagger/paths/smartScan/token.js +71 -0
  123. package/ui/server/swagger/paths/statistics.js +40 -0
  124. package/ui/server/swagger/paths.js +38 -0
  125. package/ui/server/swagger/swagger.js +37 -0
  126. package/ui/server/utils/cleanup.js +99 -0
  127. package/ui/server/utils/config.js +18 -96
  128. package/ui/server/utils/errorHandler.js +43 -0
  129. package/ui/server/utils/logger.js +2 -2
  130. package/ui/server/utils/paths.js +27 -30
  131. package/ui/server/utils/port.js +21 -21
  132. package/ui/server/utils/process.js +18 -10
  133. package/ui/server/utils/processState.js +17 -0
  134. package/ui/server/utils/signals.js +34 -0
  135. package/ui/server/websocket/broadcast.js +33 -0
  136. package/ui/server/websocket/handler.js +52 -0
  137. package/ui/server.js +51 -230
  138. package/ui/src/App.jsx +2 -0
  139. package/ui/src/CompositeSetup.jsx +23 -9
  140. package/ui/src/PacketFilters.jsx +17 -3
  141. package/ui/src/components/AlertModal.jsx +116 -0
  142. package/ui/src/components/App/ApiDocsButton.jsx +57 -0
  143. package/ui/src/components/App/useAppState.js +43 -1
  144. package/ui/src/components/BackupList.jsx +27 -3
  145. package/ui/src/utils/requestPairing.js +35 -36
  146. package/ui/src/utils/requestUtils.js +1 -0
  147. package/lib/common/db/init.js +0 -132
  148. package/lib/common/db/logger.js +0 -349
  149. package/lib/common/db/query.js +0 -403
  150. package/lib/common/logger.js +0 -90
  151. package/mcp-server/index.js +0 -138
  152. package/mcp-server/lib/server/external/config.js +0 -57
  153. package/mcp-server/lib/server/internal/handlers/prompts-get.js +0 -20
  154. package/mcp-server/lib/server/internal/handlers/prompts-list.js +0 -13
  155. package/mcp-server/lib/server/internal/handlers/resources-list.js +0 -13
  156. package/mcp-server/lib/server/internal/handlers/resources-read.js +0 -20
  157. package/mcp-server/lib/server/internal/handlers/tools-call.js +0 -35
  158. package/mcp-server/lib/server/internal/handlers/tools-list.js +0 -15
  159. package/mcp-server/lib/server/internal/run.js +0 -37
  160. package/mcp-server/mcp-shark.js +0 -22
  161. package/ui/dist/assets/index-CFHeMNwd.js +0 -35
  162. package/ui/server/routes/backups/deleteBackup.js +0 -54
  163. package/ui/server/routes/backups/listBackups.js +0 -75
  164. package/ui/server/routes/backups/restoreBackup.js +0 -83
  165. package/ui/server/routes/backups/viewBackup.js +0 -47
  166. package/ui/server/routes/composite/setup.js +0 -129
  167. package/ui/server/routes/composite/status.js +0 -7
  168. package/ui/server/routes/composite/stop.js +0 -39
  169. package/ui/server/routes/composite/utils.js +0 -45
  170. package/ui/server/routes/smartscan/discover.js +0 -118
  171. package/ui/server/routes/smartscan/scans/clearCache.js +0 -23
  172. package/ui/server/routes/smartscan/scans/createBatchScans.js +0 -124
  173. package/ui/server/routes/smartscan/scans/createScan.js +0 -43
  174. package/ui/server/routes/smartscan/scans/getCachedResults.js +0 -52
  175. package/ui/server/routes/smartscan/scans/getScan.js +0 -42
  176. package/ui/server/routes/smartscan/scans/listScans.js +0 -25
  177. package/ui/server/routes/smartscan/scans.js +0 -13
  178. package/ui/server/routes/smartscan/token.js +0 -57
  179. package/ui/server/utils/config-update.js +0 -240
  180. package/ui/server/utils/scan-cache/all-results.js +0 -197
  181. package/ui/server/utils/scan-cache/file-operations.js +0 -107
  182. package/ui/server/utils/scan-cache/hash.js +0 -47
  183. package/ui/server/utils/scan-cache/server-operations.js +0 -85
  184. package/ui/server/utils/scan-cache.js +0 -12
  185. package/ui/server/utils/smartscan-token.js +0 -43
  186. /package/{mcp-server/lib → core/mcp-server}/server/external/kv.js +0 -0
  187. /package/{mcp-server/lib → core/mcp-server}/server/internal/handlers/common.js +0 -0
  188. /package/{mcp-server/lib → core/mcp-server}/server/internal/session.js +0 -0
@@ -1,239 +1,19 @@
1
- import { queryRequests } from '#common/db/query';
2
- import logger from '../utils/logger.js';
3
- import { serializeBigInt } from '../utils/serialization.js';
1
+ import { RequestController } from '../controllers/RequestController.js';
4
2
 
5
- const sanitizeSearch = (value) => {
6
- if (value !== undefined && value !== null) {
7
- const trimmed = String(value).trim();
8
- return trimmed.length > 0 ? trimmed : null;
9
- }
10
- return null;
11
- };
3
+ export function createRequestsRoutes(container) {
4
+ const requestService = container.getService('request');
5
+ const exportService = container.getService('export');
6
+ const serializationLib = container.getLibrary('serialization');
7
+ const logger = container.getLibrary('logger');
12
8
 
13
- export function createRequestsRoutes(db) {
14
- const router = {};
15
-
16
- router.getRequests = (req, res) => {
17
- try {
18
- const limit = Number.parseInt(req.query.limit) || 1000;
19
- const offset = Number.parseInt(req.query.offset) || 0;
20
-
21
- // Sanitize search parameter - convert empty strings to null
22
- const search = sanitizeSearch(req.query.search);
23
-
24
- // Build filters object, ensuring all values are properly typed
25
- const filters = {
26
- sessionId: (req.query.sessionId && String(req.query.sessionId).trim()) || null,
27
- direction: (req.query.direction && String(req.query.direction).trim()) || null,
28
- method: (req.query.method && String(req.query.method).trim()) || null,
29
- jsonrpcMethod: (req.query.jsonrpcMethod && String(req.query.jsonrpcMethod).trim()) || null,
30
- statusCode: req.query.statusCode ? Number.parseInt(req.query.statusCode) : null,
31
- jsonrpcId: (req.query.jsonrpcId && String(req.query.jsonrpcId).trim()) || null,
32
- search: search,
33
- serverName: (req.query.serverName && String(req.query.serverName).trim()) || null,
34
- startTime: req.query.startTime ? BigInt(req.query.startTime) : null,
35
- endTime: req.query.endTime ? BigInt(req.query.endTime) : null,
36
- limit,
37
- offset,
38
- };
39
-
40
- // Remove undefined values to avoid issues
41
- Object.keys(filters).forEach((key) => {
42
- if (filters[key] === undefined) {
43
- filters[key] = null;
44
- }
45
- });
46
-
47
- const requests = queryRequests(db, filters);
48
- res.json(serializeBigInt(requests));
49
- } catch (error) {
50
- logger.error({ error: error.message }, 'Error in getRequests');
51
- res.status(500).json({ error: 'Failed to query requests', details: error.message });
52
- }
53
- };
54
-
55
- router.getRequest = (req, res) => {
56
- const stmt = db.prepare('SELECT * FROM packets WHERE frame_number = ?');
57
- const request = stmt.get(Number.parseInt(req.params.frameNumber));
58
- if (!request) {
59
- return res.status(404).json({ error: 'Request not found' });
60
- }
61
- res.json(serializeBigInt(request));
62
- };
63
-
64
- router.clearRequests = (_req, res) => {
65
- try {
66
- // Disable foreign key constraints temporarily to avoid constraint violations
67
- db.exec('PRAGMA foreign_keys = OFF');
68
-
69
- // Get list of all tables in the database
70
- const tablesResult = db
71
- .prepare(
72
- `
73
- SELECT name FROM sqlite_master
74
- WHERE type='table' AND name NOT LIKE 'sqlite_%'
75
- `
76
- )
77
- .all();
78
-
79
- const existingTables = tablesResult.map((row) => row.name);
80
-
81
- // Based on lib/common/db schema, these are the traffic-related tables:
82
- // - packets: Individual HTTP request/response packets
83
- // - conversations: Correlated request/response pairs
84
- // - sessions: Session tracking
85
- // Clear in order to respect any foreign key dependencies
86
- const trafficTables = [
87
- 'conversations', // Clear conversations first (may reference packets)
88
- 'packets', // Clear packets (main traffic data)
89
- 'sessions', // Clear sessions (may reference packets)
90
- ];
9
+ const controller = new RequestController(requestService, exportService, serializationLib, logger);
91
10
 
92
- // Delete from each table that exists
93
- const clearResults = trafficTables.reduce(
94
- (acc, table) => {
95
- if (existingTables.includes(table)) {
96
- try {
97
- db.exec(`DELETE FROM ${table}`);
98
- return {
99
- count: acc.count + 1,
100
- tables: [...acc.tables, table],
101
- };
102
- } catch (err) {
103
- logger.warn({ table, error: err.message }, 'Error clearing table');
104
- }
105
- }
106
- return acc;
107
- },
108
- { count: 0, tables: [] }
109
- );
110
- const clearedCount = clearResults.count;
111
- const clearedTables = clearResults.tables;
112
-
113
- // Re-enable foreign key constraints
114
- db.exec('PRAGMA foreign_keys = ON');
115
-
116
- res.json({
117
- success: true,
118
- message: `Cleared ${clearedCount} table(s): ${clearedTables.join(', ')}. All captured traffic has been cleared.`,
119
- });
120
- } catch (error) {
121
- // Make sure to re-enable foreign keys even if there's an error
122
- try {
123
- db.exec('PRAGMA foreign_keys = ON');
124
- } catch (_e) {
125
- // Ignore
126
- }
127
- logger.error({ error: error.message }, 'Error clearing requests');
128
- res.status(500).json({ error: 'Failed to clear traffic', details: error.message });
129
- }
130
- };
131
-
132
- router.exportRequests = (req, res) => {
133
- try {
134
- // Sanitize search parameter - convert empty strings to null
135
- const search = sanitizeSearch(req.query.search);
136
-
137
- const filters = {
138
- sessionId: req.query.sessionId || null,
139
- direction: req.query.direction || null,
140
- method: req.query.method || null,
141
- jsonrpcMethod: req.query.jsonrpcMethod || null,
142
- statusCode: req.query.statusCode ? Number.parseInt(req.query.statusCode) : null,
143
- jsonrpcId: req.query.jsonrpcId || null,
144
- search: search,
145
- serverName: req.query.serverName || null,
146
- startTime: req.query.startTime ? BigInt(req.query.startTime) : null,
147
- endTime: req.query.endTime ? BigInt(req.query.endTime) : null,
148
- limit: 100000,
149
- offset: 0,
150
- };
151
-
152
- const requests = queryRequests(db, filters);
153
- const format = req.query.format || 'json';
154
-
155
- const formatExport = (requests, format) => {
156
- if (format === 'csv') {
157
- const headers = [
158
- 'Frame',
159
- 'Time',
160
- 'Source',
161
- 'Destination',
162
- 'Protocol',
163
- 'Length',
164
- 'Method',
165
- 'Status',
166
- 'JSON-RPC Method',
167
- 'Session ID',
168
- 'Server Name',
169
- ];
170
- const rows = requests.map((req) => [
171
- req.frame_number || '',
172
- req.timestamp_iso || '',
173
- req.request?.host || '',
174
- req.request?.host || '',
175
- 'HTTP',
176
- req.length || '',
177
- req.request?.method || '',
178
- req.response?.status_code || '',
179
- req.jsonrpc_method || '',
180
- req.session_id || '',
181
- req.server_name || '',
182
- ]);
183
-
184
- const content = [
185
- headers.join(','),
186
- ...rows.map((row) =>
187
- row.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(',')
188
- ),
189
- ].join('\n');
190
- return { content, contentType: 'text/csv', extension: 'csv' };
191
- }
192
-
193
- if (format === 'txt') {
194
- const content = requests
195
- .map((req, idx) => {
196
- const lines = [
197
- `=== Request/Response #${idx + 1} (Frame ${req.frame_number || 'N/A'}) ===`,
198
- `Time: ${req.timestamp_iso || 'N/A'}`,
199
- `Session ID: ${req.session_id || 'N/A'}`,
200
- `Server: ${req.server_name || 'N/A'}`,
201
- `Direction: ${req.direction || 'N/A'}`,
202
- `Method: ${req.request?.method || 'N/A'}`,
203
- `Status: ${req.response?.status_code || 'N/A'}`,
204
- `JSON-RPC Method: ${req.jsonrpc_method || 'N/A'}`,
205
- `JSON-RPC ID: ${req.jsonrpc_id || 'N/A'}`,
206
- `Length: ${req.length || 0} bytes`,
207
- '',
208
- 'Request:',
209
- JSON.stringify(req.request || {}, null, 2),
210
- '',
211
- 'Response:',
212
- JSON.stringify(req.response || {}, null, 2),
213
- '',
214
- '---',
215
- '',
216
- ];
217
- return lines.join('\n');
218
- })
219
- .join('\n');
220
- return { content, contentType: 'text/plain', extension: 'txt' };
221
- }
222
-
223
- const content = JSON.stringify(serializeBigInt(requests), null, 2);
224
- return { content, contentType: 'application/json', extension: 'json' };
225
- };
226
-
227
- const { content, contentType, extension } = formatExport(requests, format);
11
+ const router = {};
228
12
 
229
- const filename = `mcp-shark-traffic-${new Date().toISOString().replace(/[:.]/g, '-')}.${extension}`;
230
- res.setHeader('Content-Type', contentType);
231
- res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
232
- res.send(content);
233
- } catch (error) {
234
- res.status(500).json({ error: 'Failed to export traffic', details: error.message });
235
- }
236
- };
13
+ router.getRequests = (req, res) => controller.getRequests(req, res);
14
+ router.getRequest = (req, res) => controller.getRequest(req, res);
15
+ router.clearRequests = (req, res) => controller.clearRequests(req, res);
16
+ router.exportRequests = (req, res) => controller.exportRequests(req, res);
237
17
 
238
18
  return router;
239
19
  }
@@ -1,27 +1,16 @@
1
- import { getSessionRequests, getSessions } from '#common/db/query';
2
- import { serializeBigInt } from '../utils/serialization.js';
1
+ import { SessionController } from '../controllers/SessionController.js';
3
2
 
4
- export function createSessionsRoutes(db) {
5
- const router = {};
3
+ export function createSessionsRoutes(container) {
4
+ const sessionService = container.getService('session');
5
+ const serializationLib = container.getLibrary('serialization');
6
+ const logger = container.getLibrary('logger');
7
+
8
+ const controller = new SessionController(sessionService, serializationLib, logger);
6
9
 
7
- router.getSessions = (req, res) => {
8
- const limit = Number.parseInt(req.query.limit) || 1000;
9
- const offset = Number.parseInt(req.query.offset) || 0;
10
- const filters = {
11
- startTime: req.query.startTime ? BigInt(req.query.startTime) : null,
12
- endTime: req.query.endTime ? BigInt(req.query.endTime) : null,
13
- limit,
14
- offset,
15
- };
16
- const sessions = getSessions(db, filters);
17
- res.json(serializeBigInt(sessions));
18
- };
10
+ const router = {};
19
11
 
20
- router.getSessionRequests = (req, res) => {
21
- const limit = Number.parseInt(req.query.limit) || 10000;
22
- const requests = getSessionRequests(db, req.params.sessionId, limit);
23
- res.json(serializeBigInt(requests));
24
- };
12
+ router.getSessions = (req, res) => controller.getSessions(req, res);
13
+ router.getSessionRequests = (req, res) => controller.getSessionRequests(req, res);
25
14
 
26
15
  return router;
27
16
  }
@@ -1,199 +1,17 @@
1
- import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
2
- import { homedir } from 'node:os';
3
- import { join } from 'node:path';
4
- import { getDatabaseFile, getWorkingDirectory } from '#common/configs';
5
- import logger from '../utils/logger.js';
6
- import { getScanResultsDirectory } from '../utils/scan-cache/directory.js';
1
+ import { SettingsController } from '#ui/server/controllers/index.js';
7
2
 
8
- const SMART_SCAN_TOKEN_NAME = 'smart-scan-token.json';
3
+ /**
4
+ * Create settings routes
5
+ * Routes delegate to SettingsController which calls SettingsService
6
+ */
7
+ export function createSettingsRoutes(container) {
8
+ const settingsService = container.getService('settings');
9
+ const logger = container.getLibrary('logger');
10
+ const settingsController = new SettingsController(settingsService, logger);
9
11
 
10
- function getSmartScanTokenPath() {
11
- return join(getWorkingDirectory(), SMART_SCAN_TOKEN_NAME);
12
- }
13
-
14
- function getTokenMetadata() {
15
- try {
16
- const tokenPath = getSmartScanTokenPath();
17
- if (existsSync(tokenPath)) {
18
- const content = readFileSync(tokenPath, 'utf8');
19
- const data = JSON.parse(content);
20
- const stats = statSync(tokenPath);
21
- return {
22
- token: data.token || null,
23
- updatedAt: data.updatedAt || stats.mtime.toISOString(),
24
- path: tokenPath,
25
- exists: true,
26
- };
27
- }
28
- return {
29
- token: null,
30
- updatedAt: null,
31
- path: tokenPath,
32
- exists: false,
33
- };
34
- } catch (error) {
35
- logger.error({ error: error.message }, 'Error reading Smart Scan token metadata');
36
- return {
37
- token: null,
38
- updatedAt: null,
39
- path: getSmartScanTokenPath(),
40
- exists: false,
41
- };
42
- }
43
- }
44
-
45
- function getBackupCount() {
46
- try {
47
- const homeDir = homedir();
48
- const backupDirs = [join(homeDir, '.cursor'), join(homeDir, '.codeium', 'windsurf')];
49
-
50
- const newFormatCount = backupDirs.reduce((count, dir) => {
51
- if (existsSync(dir)) {
52
- const files = readdirSync(dir);
53
- const matchingFiles = files.filter((file) => {
54
- return /^\.(.+)-mcpshark\.\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}\.json$/.test(file);
55
- });
56
- return count + matchingFiles.length;
57
- }
58
- return count;
59
- }, 0);
60
-
61
- // Also count old .backup format
62
- const commonPaths = [
63
- join(homeDir, '.cursor', 'mcp.json'),
64
- join(homeDir, '.codeium', 'windsurf', 'mcp_config.json'),
65
- ];
66
- const oldFormatCount = commonPaths.reduce((count, configPath) => {
67
- if (existsSync(`${configPath}.backup`)) {
68
- return count + 1;
69
- }
70
- return count;
71
- }, 0);
72
-
73
- return newFormatCount + oldFormatCount;
74
- } catch (error) {
75
- logger.error({ error: error.message }, 'Error counting backups');
76
- return 0;
77
- }
78
- }
79
-
80
- function toDisplayPath(absolutePath) {
81
- const homeDir = homedir();
82
- return absolutePath.replace(homeDir, '~');
83
- }
84
-
85
- export function createSettingsRoutes() {
86
12
  const router = {};
87
13
 
88
- router.getSettings = (_req, res) => {
89
- try {
90
- const homeDir = homedir();
91
- const workingDir = getWorkingDirectory();
92
- const databasePath = getDatabaseFile();
93
- const scanResultsDir = getScanResultsDirectory();
94
- const tokenPath = getSmartScanTokenPath();
95
- const tokenMetadata = getTokenMetadata();
96
-
97
- const cursorConfigPath = join(homeDir, '.cursor', 'mcp.json');
98
- const windsurfConfigPath = join(homeDir, '.codeium', 'windsurf', 'mcp_config.json');
99
-
100
- const cursorBackupDir = join(homeDir, '.cursor');
101
- const windsurfBackupDir = join(homeDir, '.codeium', 'windsurf');
102
-
103
- const settings = {
104
- paths: {
105
- workingDirectory: {
106
- absolute: workingDir,
107
- display: toDisplayPath(workingDir),
108
- exists: existsSync(workingDir),
109
- },
110
- database: {
111
- absolute: databasePath,
112
- display: toDisplayPath(databasePath),
113
- exists: existsSync(databasePath),
114
- },
115
- smartScanResults: {
116
- absolute: scanResultsDir,
117
- display: toDisplayPath(scanResultsDir),
118
- exists: existsSync(scanResultsDir),
119
- },
120
- smartScanToken: {
121
- absolute: tokenPath,
122
- display: toDisplayPath(tokenPath),
123
- exists: tokenMetadata.exists,
124
- },
125
- backupDirectories: {
126
- cursor: {
127
- absolute: cursorBackupDir,
128
- display: toDisplayPath(cursorBackupDir),
129
- exists: existsSync(cursorBackupDir),
130
- },
131
- windsurf: {
132
- absolute: windsurfBackupDir,
133
- display: toDisplayPath(windsurfBackupDir),
134
- exists: existsSync(windsurfBackupDir),
135
- },
136
- },
137
- configFiles: {
138
- cursor: {
139
- absolute: cursorConfigPath,
140
- display: toDisplayPath(cursorConfigPath),
141
- exists: existsSync(cursorConfigPath),
142
- },
143
- windsurf: {
144
- absolute: windsurfConfigPath,
145
- display: toDisplayPath(windsurfConfigPath),
146
- exists: existsSync(windsurfConfigPath),
147
- },
148
- },
149
- },
150
- smartScan: {
151
- token: tokenMetadata.token,
152
- tokenPath: {
153
- absolute: tokenMetadata.path,
154
- display: toDisplayPath(tokenMetadata.path),
155
- },
156
- tokenUpdatedAt: tokenMetadata.updatedAt,
157
- tokenExists: tokenMetadata.exists,
158
- },
159
- database: {
160
- path: {
161
- absolute: databasePath,
162
- display: toDisplayPath(databasePath),
163
- },
164
- exists: existsSync(databasePath),
165
- },
166
- system: {
167
- platform: process.platform,
168
- homeDirectory: {
169
- absolute: homeDir,
170
- display: '~',
171
- },
172
- },
173
- backups: {
174
- directories: [
175
- {
176
- absolute: cursorBackupDir,
177
- display: toDisplayPath(cursorBackupDir),
178
- },
179
- {
180
- absolute: windsurfBackupDir,
181
- display: toDisplayPath(windsurfBackupDir),
182
- },
183
- ],
184
- count: getBackupCount(),
185
- },
186
- };
187
-
188
- res.json(settings);
189
- } catch (error) {
190
- logger.error({ error: error.message }, 'Error getting settings');
191
- res.status(500).json({
192
- error: 'Failed to get settings',
193
- details: error.message,
194
- });
195
- }
196
- };
14
+ router.getSettings = settingsController.getSettings;
197
15
 
198
16
  return router;
199
17
  }
@@ -1,24 +1,35 @@
1
+ import {
2
+ McpDiscoveryController,
3
+ ScanController,
4
+ TokenController,
5
+ } from '#ui/server/controllers/index.js';
6
+
1
7
  /**
2
- * Smart Scan API proxy routes
3
- * Proxies requests to the Smart Scan API to avoid CORS issues
8
+ * Create Smart Scan routes
9
+ * Routes delegate to controllers which call services
4
10
  */
11
+ export function createSmartScanRoutes(container) {
12
+ const scanService = container.getService('scan');
13
+ const scanCacheService = container.getService('scanCache');
14
+ const mcpDiscoveryService = container.getService('mcpDiscovery');
15
+ const tokenService = container.getService('token');
16
+ const logger = container.getLibrary('logger');
5
17
 
6
- import * as discoverRoutes from './smartscan/discover.js';
7
- import * as scanRoutes from './smartscan/scans.js';
8
- import * as tokenRoutes from './smartscan/token.js';
18
+ const scanController = new ScanController(scanService, scanCacheService, logger);
19
+ const mcpDiscoveryController = new McpDiscoveryController(mcpDiscoveryService, logger);
20
+ const tokenController = new TokenController(tokenService, logger);
9
21
 
10
- export function createSmartScanRoutes() {
11
22
  const router = {};
12
23
 
13
- router.getToken = tokenRoutes.getToken;
14
- router.saveToken = tokenRoutes.saveToken;
15
- router.discoverServers = discoverRoutes.discoverServers;
16
- router.getCachedResults = scanRoutes.getCachedResults;
17
- router.createScan = scanRoutes.createScan;
18
- router.getScan = scanRoutes.getScan;
19
- router.listScans = scanRoutes.listScans;
20
- router.createBatchScans = scanRoutes.createBatchScans;
21
- router.clearCache = scanRoutes.clearCache;
24
+ router.getToken = tokenController.getToken;
25
+ router.saveToken = tokenController.saveToken;
26
+ router.discoverServers = mcpDiscoveryController.discoverServers;
27
+ router.getCachedResults = scanController.getCachedResults;
28
+ router.createScan = scanController.createScan;
29
+ router.getScan = scanController.getScan;
30
+ router.listScans = scanController.listScans;
31
+ router.createBatchScans = scanController.createBatchScans;
32
+ router.clearCache = scanController.clearCache;
22
33
 
23
34
  return router;
24
35
  }
@@ -1,86 +1,15 @@
1
- import { queryRequests } from '#common/db/query';
2
- import logger from '../utils/logger.js';
3
- import { serializeBigInt } from '../utils/serialization.js';
1
+ import { StatisticsController } from '../controllers/StatisticsController.js';
4
2
 
5
- const sanitizeSearch = (value) => {
6
- if (value !== undefined && value !== null) {
7
- const trimmed = String(value).trim();
8
- return trimmed.length > 0 ? trimmed : null;
9
- }
10
- return null;
11
- };
3
+ export function createStatisticsRoutes(container) {
4
+ const statisticsService = container.getService('statistics');
5
+ const serializationLib = container.getLibrary('serialization');
6
+ const logger = container.getLibrary('logger');
12
7
 
13
- export function createStatisticsRoutes(db) {
14
- const router = {};
15
-
16
- router.getStatistics = (req, res) => {
17
- try {
18
- // Sanitize search parameter - convert empty strings to null
19
- const search = sanitizeSearch(req.query.search);
20
-
21
- // Build filters object matching the requests route
22
- const filters = {
23
- sessionId: (req.query.sessionId && String(req.query.sessionId).trim()) || null,
24
- direction: (req.query.direction && String(req.query.direction).trim()) || null,
25
- method: (req.query.method && String(req.query.method).trim()) || null,
26
- jsonrpcMethod: (req.query.jsonrpcMethod && String(req.query.jsonrpcMethod).trim()) || null,
27
- statusCode: req.query.statusCode ? Number.parseInt(req.query.statusCode) : null,
28
- jsonrpcId: (req.query.jsonrpcId && String(req.query.jsonrpcId).trim()) || null,
29
- search: search,
30
- serverName: (req.query.serverName && String(req.query.serverName).trim()) || null,
31
- startTime: req.query.startTime ? BigInt(req.query.startTime) : null,
32
- endTime: req.query.endTime ? BigInt(req.query.endTime) : null,
33
- limit: 1000000, // Large limit for accurate statistics
34
- offset: 0,
35
- };
36
-
37
- // Remove undefined values to avoid issues
38
- Object.keys(filters).forEach((key) => {
39
- if (filters[key] === undefined) {
40
- filters[key] = null;
41
- }
42
- });
8
+ const controller = new StatisticsController(statisticsService, serializationLib, logger);
43
9
 
44
- // Get all filtered requests (no limit for accurate statistics)
45
- const allRequests = queryRequests(db, filters);
46
-
47
- // Calculate statistics from filtered requests
48
- const totalPackets = allRequests.length;
49
- const totalRequests = allRequests.filter((r) => r.direction === 'request').length;
50
- const totalResponses = allRequests.filter((r) => r.direction === 'response').length;
51
- const totalErrors = allRequests.filter((r) => {
52
- if (r.direction === 'response') {
53
- const statusCode = r.status_code || r.status;
54
- return (
55
- statusCode >= 400 ||
56
- (r.body_json && typeof r.body_json === 'object' && r.body_json.error)
57
- );
58
- }
59
- return false;
60
- }).length;
61
-
62
- // Get unique sessions
63
- const uniqueSessions = new Set();
64
- allRequests.forEach((r) => {
65
- if (r.session_id) {
66
- uniqueSessions.add(r.session_id);
67
- }
68
- });
69
-
70
- const stats = {
71
- total_packets: totalPackets,
72
- total_requests: totalRequests,
73
- total_responses: totalResponses,
74
- total_errors: totalErrors,
75
- unique_sessions: uniqueSessions.size,
76
- };
10
+ const router = {};
77
11
 
78
- res.json(serializeBigInt(stats));
79
- } catch (error) {
80
- logger.error({ error: error.message }, 'Error in getStatistics');
81
- res.status(500).json({ error: 'Failed to get statistics', details: error.message });
82
- }
83
- };
12
+ router.getStatistics = (req, res) => controller.getStatistics(req, res);
84
13
 
85
14
  return router;
86
15
  }