@friggframework/devtools 2.0.0-next.3 → 2.0.0-next.30
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/frigg-cli/.eslintrc.js +141 -0
- package/frigg-cli/__tests__/jest.config.js +102 -0
- package/frigg-cli/__tests__/unit/commands/build.test.js +483 -0
- package/frigg-cli/__tests__/unit/commands/install.test.js +418 -0
- package/frigg-cli/__tests__/unit/commands/ui.test.js +592 -0
- package/frigg-cli/__tests__/utils/command-tester.js +170 -0
- package/frigg-cli/__tests__/utils/mock-factory.js +270 -0
- package/frigg-cli/__tests__/utils/test-fixtures.js +463 -0
- package/frigg-cli/__tests__/utils/test-setup.js +286 -0
- package/frigg-cli/build-command/index.js +54 -0
- package/frigg-cli/deploy-command/index.js +36 -0
- package/frigg-cli/generate-command/__tests__/generate-command.test.js +312 -0
- package/frigg-cli/generate-command/azure-generator.js +43 -0
- package/frigg-cli/generate-command/gcp-generator.js +47 -0
- package/frigg-cli/generate-command/index.js +332 -0
- package/frigg-cli/generate-command/terraform-generator.js +555 -0
- package/frigg-cli/generate-iam-command.js +115 -0
- package/frigg-cli/index.js +47 -1
- package/frigg-cli/index.test.js +1 -4
- package/frigg-cli/init-command/backend-first-handler.js +756 -0
- package/frigg-cli/init-command/index.js +93 -0
- package/frigg-cli/init-command/template-handler.js +143 -0
- package/frigg-cli/install-command/index.js +1 -4
- package/frigg-cli/package.json +51 -0
- package/frigg-cli/start-command/index.js +24 -4
- package/frigg-cli/test/init-command.test.js +180 -0
- package/frigg-cli/test/npm-registry.test.js +319 -0
- package/frigg-cli/ui-command/index.js +154 -0
- package/frigg-cli/utils/app-resolver.js +319 -0
- package/frigg-cli/utils/backend-path.js +16 -17
- package/frigg-cli/utils/npm-registry.js +167 -0
- package/frigg-cli/utils/process-manager.js +199 -0
- package/frigg-cli/utils/repo-detection.js +405 -0
- package/infrastructure/AWS-DISCOVERY-TROUBLESHOOTING.md +245 -0
- package/infrastructure/AWS-IAM-CREDENTIAL-NEEDS.md +596 -0
- package/infrastructure/DEPLOYMENT-INSTRUCTIONS.md +268 -0
- package/infrastructure/GENERATE-IAM-DOCS.md +253 -0
- package/infrastructure/IAM-POLICY-TEMPLATES.md +176 -0
- package/infrastructure/README-TESTING.md +332 -0
- package/infrastructure/README.md +421 -0
- package/infrastructure/WEBSOCKET-CONFIGURATION.md +105 -0
- package/infrastructure/__tests__/fixtures/mock-aws-resources.js +391 -0
- package/infrastructure/__tests__/helpers/test-utils.js +277 -0
- package/infrastructure/aws-discovery.js +568 -0
- package/infrastructure/aws-discovery.test.js +373 -0
- package/infrastructure/build-time-discovery.js +206 -0
- package/infrastructure/build-time-discovery.test.js +375 -0
- package/infrastructure/create-frigg-infrastructure.js +3 -5
- package/infrastructure/frigg-deployment-iam-stack.yaml +379 -0
- package/infrastructure/iam-generator.js +687 -0
- package/infrastructure/iam-generator.test.js +169 -0
- package/infrastructure/iam-policy-basic.json +212 -0
- package/infrastructure/iam-policy-full.json +282 -0
- package/infrastructure/integration.test.js +383 -0
- package/infrastructure/run-discovery.js +110 -0
- package/infrastructure/serverless-template.js +923 -113
- package/infrastructure/serverless-template.test.js +541 -0
- package/management-ui/.eslintrc.js +22 -0
- package/management-ui/README.md +203 -0
- package/management-ui/components.json +21 -0
- package/management-ui/docs/phase2-integration-guide.md +320 -0
- package/management-ui/index.html +13 -0
- package/management-ui/package-lock.json +16517 -0
- package/management-ui/package.json +76 -0
- package/management-ui/packages/devtools/frigg-cli/ui-command/index.js +302 -0
- package/management-ui/postcss.config.js +6 -0
- package/management-ui/server/api/backend.js +256 -0
- package/management-ui/server/api/cli.js +315 -0
- package/management-ui/server/api/codegen.js +663 -0
- package/management-ui/server/api/connections.js +857 -0
- package/management-ui/server/api/discovery.js +185 -0
- package/management-ui/server/api/environment/index.js +1 -0
- package/management-ui/server/api/environment/router.js +378 -0
- package/management-ui/server/api/environment.js +328 -0
- package/management-ui/server/api/integrations.js +876 -0
- package/management-ui/server/api/logs.js +248 -0
- package/management-ui/server/api/monitoring.js +282 -0
- package/management-ui/server/api/open-ide.js +31 -0
- package/management-ui/server/api/project.js +1029 -0
- package/management-ui/server/api/users/sessions.js +371 -0
- package/management-ui/server/api/users/simulation.js +254 -0
- package/management-ui/server/api/users.js +362 -0
- package/management-ui/server/api-contract.md +275 -0
- package/management-ui/server/index.js +873 -0
- package/management-ui/server/middleware/errorHandler.js +93 -0
- package/management-ui/server/middleware/security.js +32 -0
- package/management-ui/server/processManager.js +296 -0
- package/management-ui/server/server.js +346 -0
- package/management-ui/server/services/aws-monitor.js +413 -0
- package/management-ui/server/services/npm-registry.js +347 -0
- package/management-ui/server/services/template-engine.js +538 -0
- package/management-ui/server/utils/cliIntegration.js +220 -0
- package/management-ui/server/utils/environment/auditLogger.js +471 -0
- package/management-ui/server/utils/environment/awsParameterStore.js +264 -0
- package/management-ui/server/utils/environment/encryption.js +278 -0
- package/management-ui/server/utils/environment/envFileManager.js +286 -0
- package/management-ui/server/utils/import-commonjs.js +28 -0
- package/management-ui/server/utils/response.js +83 -0
- package/management-ui/server/websocket/handler.js +325 -0
- package/management-ui/src/App.jsx +109 -0
- package/management-ui/src/assets/FriggLogo.svg +1 -0
- package/management-ui/src/components/AppRouter.jsx +65 -0
- package/management-ui/src/components/Button.jsx +70 -0
- package/management-ui/src/components/Card.jsx +97 -0
- package/management-ui/src/components/EnvironmentCompare.jsx +400 -0
- package/management-ui/src/components/EnvironmentEditor.jsx +372 -0
- package/management-ui/src/components/EnvironmentImportExport.jsx +469 -0
- package/management-ui/src/components/EnvironmentSchema.jsx +491 -0
- package/management-ui/src/components/EnvironmentSecurity.jsx +463 -0
- package/management-ui/src/components/ErrorBoundary.jsx +73 -0
- package/management-ui/src/components/IntegrationCard.jsx +481 -0
- package/management-ui/src/components/IntegrationCardEnhanced.jsx +770 -0
- package/management-ui/src/components/IntegrationExplorer.jsx +379 -0
- package/management-ui/src/components/IntegrationStatus.jsx +336 -0
- package/management-ui/src/components/Layout.jsx +716 -0
- package/management-ui/src/components/LoadingSpinner.jsx +113 -0
- package/management-ui/src/components/RepositoryPicker.jsx +248 -0
- package/management-ui/src/components/SessionMonitor.jsx +350 -0
- package/management-ui/src/components/StatusBadge.jsx +208 -0
- package/management-ui/src/components/UserContextSwitcher.jsx +212 -0
- package/management-ui/src/components/UserSimulation.jsx +327 -0
- package/management-ui/src/components/Welcome.jsx +434 -0
- package/management-ui/src/components/codegen/APIEndpointGenerator.jsx +637 -0
- package/management-ui/src/components/codegen/APIModuleSelector.jsx +227 -0
- package/management-ui/src/components/codegen/CodeGenerationWizard.jsx +247 -0
- package/management-ui/src/components/codegen/CodePreviewEditor.jsx +316 -0
- package/management-ui/src/components/codegen/DynamicModuleForm.jsx +271 -0
- package/management-ui/src/components/codegen/FormBuilder.jsx +737 -0
- package/management-ui/src/components/codegen/IntegrationGenerator.jsx +855 -0
- package/management-ui/src/components/codegen/ProjectScaffoldWizard.jsx +797 -0
- package/management-ui/src/components/codegen/SchemaBuilder.jsx +303 -0
- package/management-ui/src/components/codegen/TemplateSelector.jsx +586 -0
- package/management-ui/src/components/codegen/index.js +10 -0
- package/management-ui/src/components/connections/ConnectionConfigForm.jsx +362 -0
- package/management-ui/src/components/connections/ConnectionHealthMonitor.jsx +182 -0
- package/management-ui/src/components/connections/ConnectionTester.jsx +200 -0
- package/management-ui/src/components/connections/EntityRelationshipMapper.jsx +292 -0
- package/management-ui/src/components/connections/OAuthFlow.jsx +204 -0
- package/management-ui/src/components/connections/index.js +5 -0
- package/management-ui/src/components/index.js +21 -0
- package/management-ui/src/components/monitoring/APIGatewayMetrics.jsx +222 -0
- package/management-ui/src/components/monitoring/LambdaMetrics.jsx +169 -0
- package/management-ui/src/components/monitoring/MetricsChart.jsx +197 -0
- package/management-ui/src/components/monitoring/MonitoringDashboard.jsx +393 -0
- package/management-ui/src/components/monitoring/SQSMetrics.jsx +246 -0
- package/management-ui/src/components/monitoring/index.js +6 -0
- package/management-ui/src/components/monitoring/monitoring.css +218 -0
- package/management-ui/src/components/theme-provider.jsx +52 -0
- package/management-ui/src/components/theme-toggle.jsx +39 -0
- package/management-ui/src/components/ui/badge.tsx +36 -0
- package/management-ui/src/components/ui/button.test.jsx +56 -0
- package/management-ui/src/components/ui/button.tsx +57 -0
- package/management-ui/src/components/ui/card.tsx +76 -0
- package/management-ui/src/components/ui/dropdown-menu.tsx +199 -0
- package/management-ui/src/components/ui/select.tsx +157 -0
- package/management-ui/src/components/ui/skeleton.jsx +15 -0
- package/management-ui/src/hooks/useFrigg.jsx +601 -0
- package/management-ui/src/hooks/useSocket.jsx +58 -0
- package/management-ui/src/index.css +193 -0
- package/management-ui/src/lib/utils.ts +6 -0
- package/management-ui/src/main.jsx +10 -0
- package/management-ui/src/pages/CodeGeneration.jsx +14 -0
- package/management-ui/src/pages/Connections.jsx +252 -0
- package/management-ui/src/pages/ConnectionsEnhanced.jsx +633 -0
- package/management-ui/src/pages/Dashboard.jsx +311 -0
- package/management-ui/src/pages/Environment.jsx +314 -0
- package/management-ui/src/pages/IntegrationConfigure.jsx +669 -0
- package/management-ui/src/pages/IntegrationDiscovery.jsx +567 -0
- package/management-ui/src/pages/IntegrationTest.jsx +742 -0
- package/management-ui/src/pages/Integrations.jsx +253 -0
- package/management-ui/src/pages/Monitoring.jsx +17 -0
- package/management-ui/src/pages/Simulation.jsx +155 -0
- package/management-ui/src/pages/Users.jsx +492 -0
- package/management-ui/src/services/api.js +41 -0
- package/management-ui/src/services/apiModuleService.js +193 -0
- package/management-ui/src/services/websocket-handlers.js +120 -0
- package/management-ui/src/test/api/project.test.js +273 -0
- package/management-ui/src/test/components/Welcome.test.jsx +378 -0
- package/management-ui/src/test/mocks/server.js +178 -0
- package/management-ui/src/test/setup.js +61 -0
- package/management-ui/src/test/utils/test-utils.jsx +134 -0
- package/management-ui/src/utils/repository.js +98 -0
- package/management-ui/src/utils/repository.test.js +118 -0
- package/management-ui/src/workflows/phase2-integration-workflows.js +884 -0
- package/management-ui/tailwind.config.js +63 -0
- package/management-ui/tsconfig.json +37 -0
- package/management-ui/tsconfig.node.json +10 -0
- package/management-ui/vite.config.js +26 -0
- package/management-ui/vitest.config.js +38 -0
- package/package.json +16 -9
- package/infrastructure/app-handler-helpers.js +0 -57
- package/infrastructure/backend-utils.js +0 -90
- package/infrastructure/routers/auth.js +0 -26
- package/infrastructure/routers/integration-defined-routers.js +0 -37
- package/infrastructure/routers/middleware/loadUser.js +0 -15
- package/infrastructure/routers/middleware/requireLoggedInUser.js +0 -12
- package/infrastructure/routers/user.js +0 -41
- package/infrastructure/routers/websocket.js +0 -55
- package/infrastructure/workers/integration-defined-workers.js +0 -24
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import express from 'express'
|
|
2
|
+
import crypto from 'crypto'
|
|
3
|
+
import { wsHandler } from '../../websocket/handler.js'
|
|
4
|
+
|
|
5
|
+
const router = express.Router()
|
|
6
|
+
|
|
7
|
+
// In-memory session store (for development only)
|
|
8
|
+
const sessions = new Map()
|
|
9
|
+
const sessionsByUser = new Map()
|
|
10
|
+
|
|
11
|
+
// Session configuration
|
|
12
|
+
const SESSION_DURATION = 3600000 // 1 hour
|
|
13
|
+
const MAX_SESSIONS_PER_USER = 5
|
|
14
|
+
|
|
15
|
+
// Create a new session
|
|
16
|
+
router.post('/create', async (req, res) => {
|
|
17
|
+
const { userId, metadata = {} } = req.body
|
|
18
|
+
|
|
19
|
+
if (!userId) {
|
|
20
|
+
return res.status(400).json({
|
|
21
|
+
error: 'userId is required'
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
// Clean up expired sessions for this user
|
|
27
|
+
cleanupUserSessions(userId)
|
|
28
|
+
|
|
29
|
+
// Check session limit
|
|
30
|
+
const userSessions = sessionsByUser.get(userId) || []
|
|
31
|
+
if (userSessions.length >= MAX_SESSIONS_PER_USER) {
|
|
32
|
+
// Remove oldest session
|
|
33
|
+
const oldestSession = userSessions[0]
|
|
34
|
+
removeSession(oldestSession)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Create new session
|
|
38
|
+
const sessionId = `sess_${crypto.randomBytes(16).toString('hex')}`
|
|
39
|
+
const sessionToken = `token_${crypto.randomBytes(32).toString('hex')}`
|
|
40
|
+
|
|
41
|
+
const session = {
|
|
42
|
+
id: sessionId,
|
|
43
|
+
userId,
|
|
44
|
+
token: sessionToken,
|
|
45
|
+
createdAt: new Date().toISOString(),
|
|
46
|
+
expiresAt: new Date(Date.now() + SESSION_DURATION).toISOString(),
|
|
47
|
+
lastActivity: new Date().toISOString(),
|
|
48
|
+
metadata,
|
|
49
|
+
active: true,
|
|
50
|
+
activities: []
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Store session
|
|
54
|
+
sessions.set(sessionId, session)
|
|
55
|
+
|
|
56
|
+
// Update user sessions
|
|
57
|
+
const updatedUserSessions = [...(sessionsByUser.get(userId) || []), sessionId]
|
|
58
|
+
sessionsByUser.set(userId, updatedUserSessions)
|
|
59
|
+
|
|
60
|
+
// Broadcast session creation
|
|
61
|
+
wsHandler.broadcast('session:created', {
|
|
62
|
+
sessionId,
|
|
63
|
+
userId,
|
|
64
|
+
timestamp: new Date().toISOString()
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
res.json({
|
|
68
|
+
status: 'success',
|
|
69
|
+
session: {
|
|
70
|
+
id: session.id,
|
|
71
|
+
token: session.token,
|
|
72
|
+
expiresAt: session.expiresAt
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
} catch (error) {
|
|
76
|
+
res.status(500).json({
|
|
77
|
+
error: error.message,
|
|
78
|
+
details: 'Failed to create session'
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
// Get session details
|
|
84
|
+
router.get('/:sessionId', async (req, res) => {
|
|
85
|
+
const { sessionId } = req.params
|
|
86
|
+
const session = sessions.get(sessionId)
|
|
87
|
+
|
|
88
|
+
if (!session) {
|
|
89
|
+
return res.status(404).json({
|
|
90
|
+
error: 'Session not found'
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Check if expired
|
|
95
|
+
if (new Date(session.expiresAt) < new Date()) {
|
|
96
|
+
removeSession(sessionId)
|
|
97
|
+
return res.status(410).json({
|
|
98
|
+
error: 'Session expired'
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
res.json({
|
|
103
|
+
session: {
|
|
104
|
+
id: session.id,
|
|
105
|
+
userId: session.userId,
|
|
106
|
+
createdAt: session.createdAt,
|
|
107
|
+
expiresAt: session.expiresAt,
|
|
108
|
+
lastActivity: session.lastActivity,
|
|
109
|
+
active: session.active,
|
|
110
|
+
metadata: session.metadata
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
// Get all sessions for a user
|
|
116
|
+
router.get('/user/:userId', async (req, res) => {
|
|
117
|
+
const { userId } = req.params
|
|
118
|
+
|
|
119
|
+
// Clean up expired sessions
|
|
120
|
+
cleanupUserSessions(userId)
|
|
121
|
+
|
|
122
|
+
const userSessionIds = sessionsByUser.get(userId) || []
|
|
123
|
+
const userSessions = userSessionIds
|
|
124
|
+
.map(id => sessions.get(id))
|
|
125
|
+
.filter(session => session && new Date(session.expiresAt) > new Date())
|
|
126
|
+
.map(session => ({
|
|
127
|
+
id: session.id,
|
|
128
|
+
createdAt: session.createdAt,
|
|
129
|
+
expiresAt: session.expiresAt,
|
|
130
|
+
lastActivity: session.lastActivity,
|
|
131
|
+
active: session.active,
|
|
132
|
+
metadata: session.metadata
|
|
133
|
+
}))
|
|
134
|
+
|
|
135
|
+
res.json({
|
|
136
|
+
sessions: userSessions,
|
|
137
|
+
total: userSessions.length
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
// Track activity in a session
|
|
142
|
+
router.post('/:sessionId/activity', async (req, res) => {
|
|
143
|
+
const { sessionId } = req.params
|
|
144
|
+
const { action, data = {} } = req.body
|
|
145
|
+
|
|
146
|
+
const session = sessions.get(sessionId)
|
|
147
|
+
|
|
148
|
+
if (!session) {
|
|
149
|
+
return res.status(404).json({
|
|
150
|
+
error: 'Session not found'
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Check if expired
|
|
155
|
+
if (new Date(session.expiresAt) < new Date()) {
|
|
156
|
+
removeSession(sessionId)
|
|
157
|
+
return res.status(410).json({
|
|
158
|
+
error: 'Session expired'
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
// Update last activity
|
|
164
|
+
session.lastActivity = new Date().toISOString()
|
|
165
|
+
|
|
166
|
+
// Add activity to log
|
|
167
|
+
const activity = {
|
|
168
|
+
id: `act_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
169
|
+
action,
|
|
170
|
+
data,
|
|
171
|
+
timestamp: new Date().toISOString()
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
session.activities.push(activity)
|
|
175
|
+
|
|
176
|
+
// Keep only last 100 activities
|
|
177
|
+
if (session.activities.length > 100) {
|
|
178
|
+
session.activities = session.activities.slice(-100)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Broadcast activity
|
|
182
|
+
wsHandler.broadcast('session:activity', {
|
|
183
|
+
sessionId,
|
|
184
|
+
userId: session.userId,
|
|
185
|
+
activity,
|
|
186
|
+
timestamp: new Date().toISOString()
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
res.json({
|
|
190
|
+
status: 'success',
|
|
191
|
+
activity
|
|
192
|
+
})
|
|
193
|
+
} catch (error) {
|
|
194
|
+
res.status(500).json({
|
|
195
|
+
error: error.message,
|
|
196
|
+
details: 'Failed to track activity'
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
// Refresh session (extend expiry)
|
|
202
|
+
router.post('/:sessionId/refresh', async (req, res) => {
|
|
203
|
+
const { sessionId } = req.params
|
|
204
|
+
const session = sessions.get(sessionId)
|
|
205
|
+
|
|
206
|
+
if (!session) {
|
|
207
|
+
return res.status(404).json({
|
|
208
|
+
error: 'Session not found'
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Check if expired
|
|
213
|
+
if (new Date(session.expiresAt) < new Date()) {
|
|
214
|
+
removeSession(sessionId)
|
|
215
|
+
return res.status(410).json({
|
|
216
|
+
error: 'Session expired'
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
// Extend expiry
|
|
222
|
+
session.expiresAt = new Date(Date.now() + SESSION_DURATION).toISOString()
|
|
223
|
+
session.lastActivity = new Date().toISOString()
|
|
224
|
+
|
|
225
|
+
// Generate new token
|
|
226
|
+
session.token = `token_${crypto.randomBytes(32).toString('hex')}`
|
|
227
|
+
|
|
228
|
+
res.json({
|
|
229
|
+
status: 'success',
|
|
230
|
+
session: {
|
|
231
|
+
id: session.id,
|
|
232
|
+
token: session.token,
|
|
233
|
+
expiresAt: session.expiresAt
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
} catch (error) {
|
|
237
|
+
res.status(500).json({
|
|
238
|
+
error: error.message,
|
|
239
|
+
details: 'Failed to refresh session'
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
// End session
|
|
245
|
+
router.delete('/:sessionId', async (req, res) => {
|
|
246
|
+
const { sessionId } = req.params
|
|
247
|
+
const session = sessions.get(sessionId)
|
|
248
|
+
|
|
249
|
+
if (!session) {
|
|
250
|
+
return res.status(404).json({
|
|
251
|
+
error: 'Session not found'
|
|
252
|
+
})
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
removeSession(sessionId)
|
|
257
|
+
|
|
258
|
+
// Broadcast session end
|
|
259
|
+
wsHandler.broadcast('session:ended', {
|
|
260
|
+
sessionId,
|
|
261
|
+
userId: session.userId,
|
|
262
|
+
timestamp: new Date().toISOString()
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
res.json({
|
|
266
|
+
status: 'success',
|
|
267
|
+
message: 'Session ended'
|
|
268
|
+
})
|
|
269
|
+
} catch (error) {
|
|
270
|
+
res.status(500).json({
|
|
271
|
+
error: error.message,
|
|
272
|
+
details: 'Failed to end session'
|
|
273
|
+
})
|
|
274
|
+
}
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
// Get all active sessions
|
|
278
|
+
router.get('/', async (req, res) => {
|
|
279
|
+
try {
|
|
280
|
+
// Clean up all expired sessions
|
|
281
|
+
cleanupAllSessions()
|
|
282
|
+
|
|
283
|
+
const activeSessions = Array.from(sessions.values())
|
|
284
|
+
.filter(session => new Date(session.expiresAt) > new Date())
|
|
285
|
+
.map(session => ({
|
|
286
|
+
id: session.id,
|
|
287
|
+
userId: session.userId,
|
|
288
|
+
createdAt: session.createdAt,
|
|
289
|
+
expiresAt: session.expiresAt,
|
|
290
|
+
lastActivity: session.lastActivity,
|
|
291
|
+
active: session.active,
|
|
292
|
+
metadata: session.metadata
|
|
293
|
+
}))
|
|
294
|
+
|
|
295
|
+
res.json({
|
|
296
|
+
sessions: activeSessions,
|
|
297
|
+
total: activeSessions.length,
|
|
298
|
+
byUser: getSessionCountByUser()
|
|
299
|
+
})
|
|
300
|
+
} catch (error) {
|
|
301
|
+
res.status(500).json({
|
|
302
|
+
error: error.message,
|
|
303
|
+
details: 'Failed to fetch sessions'
|
|
304
|
+
})
|
|
305
|
+
}
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
// Helper functions
|
|
309
|
+
function removeSession(sessionId) {
|
|
310
|
+
const session = sessions.get(sessionId)
|
|
311
|
+
if (!session) return
|
|
312
|
+
|
|
313
|
+
// Remove from sessions map
|
|
314
|
+
sessions.delete(sessionId)
|
|
315
|
+
|
|
316
|
+
// Remove from user sessions
|
|
317
|
+
const userSessions = sessionsByUser.get(session.userId) || []
|
|
318
|
+
const filtered = userSessions.filter(id => id !== sessionId)
|
|
319
|
+
if (filtered.length === 0) {
|
|
320
|
+
sessionsByUser.delete(session.userId)
|
|
321
|
+
} else {
|
|
322
|
+
sessionsByUser.set(session.userId, filtered)
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function cleanupUserSessions(userId) {
|
|
327
|
+
const userSessionIds = sessionsByUser.get(userId) || []
|
|
328
|
+
const now = new Date()
|
|
329
|
+
|
|
330
|
+
userSessionIds.forEach(sessionId => {
|
|
331
|
+
const session = sessions.get(sessionId)
|
|
332
|
+
if (!session || new Date(session.expiresAt) < now) {
|
|
333
|
+
removeSession(sessionId)
|
|
334
|
+
}
|
|
335
|
+
})
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function cleanupAllSessions() {
|
|
339
|
+
const now = new Date()
|
|
340
|
+
|
|
341
|
+
Array.from(sessions.keys()).forEach(sessionId => {
|
|
342
|
+
const session = sessions.get(sessionId)
|
|
343
|
+
if (new Date(session.expiresAt) < now) {
|
|
344
|
+
removeSession(sessionId)
|
|
345
|
+
}
|
|
346
|
+
})
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function getSessionCountByUser() {
|
|
350
|
+
const counts = {}
|
|
351
|
+
|
|
352
|
+
sessionsByUser.forEach((sessionIds, userId) => {
|
|
353
|
+
const activeSessions = sessionIds.filter(id => {
|
|
354
|
+
const session = sessions.get(id)
|
|
355
|
+
return session && new Date(session.expiresAt) > new Date()
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
if (activeSessions.length > 0) {
|
|
359
|
+
counts[userId] = activeSessions.length
|
|
360
|
+
}
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
return counts
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Clean up expired sessions periodically
|
|
367
|
+
setInterval(() => {
|
|
368
|
+
cleanupAllSessions()
|
|
369
|
+
}, 300000) // Every 5 minutes
|
|
370
|
+
|
|
371
|
+
export default router
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import express from 'express'
|
|
2
|
+
import { wsHandler } from '../../websocket/handler.js'
|
|
3
|
+
|
|
4
|
+
const router = express.Router()
|
|
5
|
+
|
|
6
|
+
// Store active simulation sessions
|
|
7
|
+
const simulationSessions = new Map()
|
|
8
|
+
|
|
9
|
+
// Simulate user authentication for integration testing
|
|
10
|
+
router.post('/authenticate', async (req, res) => {
|
|
11
|
+
const { userId, integrationId } = req.body
|
|
12
|
+
|
|
13
|
+
if (!userId || !integrationId) {
|
|
14
|
+
return res.status(400).json({
|
|
15
|
+
error: 'userId and integrationId are required'
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
// Create a simulated auth token
|
|
21
|
+
const sessionId = `sim_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
|
22
|
+
const authToken = `sim_token_${userId}_${integrationId}_${Date.now()}`
|
|
23
|
+
|
|
24
|
+
const session = {
|
|
25
|
+
sessionId,
|
|
26
|
+
userId,
|
|
27
|
+
integrationId,
|
|
28
|
+
authToken,
|
|
29
|
+
createdAt: new Date().toISOString(),
|
|
30
|
+
expiresAt: new Date(Date.now() + 3600000).toISOString(), // 1 hour
|
|
31
|
+
status: 'active'
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
simulationSessions.set(sessionId, session)
|
|
35
|
+
|
|
36
|
+
// Broadcast simulation event
|
|
37
|
+
wsHandler.broadcast('simulation:auth', {
|
|
38
|
+
action: 'authenticated',
|
|
39
|
+
session,
|
|
40
|
+
timestamp: new Date().toISOString()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
res.json({
|
|
44
|
+
status: 'success',
|
|
45
|
+
session
|
|
46
|
+
})
|
|
47
|
+
} catch (error) {
|
|
48
|
+
res.status(500).json({
|
|
49
|
+
error: error.message,
|
|
50
|
+
details: 'Failed to simulate authentication'
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
// Simulate user actions within an integration
|
|
56
|
+
router.post('/action', async (req, res) => {
|
|
57
|
+
const { sessionId, action, payload } = req.body
|
|
58
|
+
|
|
59
|
+
if (!sessionId || !action) {
|
|
60
|
+
return res.status(400).json({
|
|
61
|
+
error: 'sessionId and action are required'
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const session = simulationSessions.get(sessionId)
|
|
66
|
+
if (!session) {
|
|
67
|
+
return res.status(404).json({
|
|
68
|
+
error: 'Session not found'
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const actionResult = {
|
|
74
|
+
actionId: `action_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
75
|
+
sessionId,
|
|
76
|
+
userId: session.userId,
|
|
77
|
+
integrationId: session.integrationId,
|
|
78
|
+
action,
|
|
79
|
+
payload,
|
|
80
|
+
timestamp: new Date().toISOString(),
|
|
81
|
+
result: 'success',
|
|
82
|
+
response: generateMockResponse(action, payload)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Broadcast action event
|
|
86
|
+
wsHandler.broadcast('simulation:action', {
|
|
87
|
+
action: 'performed',
|
|
88
|
+
actionResult,
|
|
89
|
+
timestamp: new Date().toISOString()
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
res.json({
|
|
93
|
+
status: 'success',
|
|
94
|
+
actionResult
|
|
95
|
+
})
|
|
96
|
+
} catch (error) {
|
|
97
|
+
res.status(500).json({
|
|
98
|
+
error: error.message,
|
|
99
|
+
details: 'Failed to simulate action'
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
// Get active simulation sessions
|
|
105
|
+
router.get('/sessions', async (req, res) => {
|
|
106
|
+
try {
|
|
107
|
+
const sessions = Array.from(simulationSessions.values())
|
|
108
|
+
.filter(session => new Date(session.expiresAt) > new Date())
|
|
109
|
+
|
|
110
|
+
res.json({
|
|
111
|
+
sessions,
|
|
112
|
+
total: sessions.length
|
|
113
|
+
})
|
|
114
|
+
} catch (error) {
|
|
115
|
+
res.status(500).json({
|
|
116
|
+
error: error.message,
|
|
117
|
+
details: 'Failed to fetch sessions'
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
// End a simulation session
|
|
123
|
+
router.delete('/sessions/:sessionId', async (req, res) => {
|
|
124
|
+
const { sessionId } = req.params
|
|
125
|
+
|
|
126
|
+
if (!simulationSessions.has(sessionId)) {
|
|
127
|
+
return res.status(404).json({
|
|
128
|
+
error: 'Session not found'
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
const session = simulationSessions.get(sessionId)
|
|
134
|
+
simulationSessions.delete(sessionId)
|
|
135
|
+
|
|
136
|
+
// Broadcast session end
|
|
137
|
+
wsHandler.broadcast('simulation:session', {
|
|
138
|
+
action: 'ended',
|
|
139
|
+
sessionId,
|
|
140
|
+
userId: session.userId,
|
|
141
|
+
timestamp: new Date().toISOString()
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
res.json({
|
|
145
|
+
status: 'success',
|
|
146
|
+
message: 'Session ended'
|
|
147
|
+
})
|
|
148
|
+
} catch (error) {
|
|
149
|
+
res.status(500).json({
|
|
150
|
+
error: error.message,
|
|
151
|
+
details: 'Failed to end session'
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
// Simulate integration webhook events
|
|
157
|
+
router.post('/webhook', async (req, res) => {
|
|
158
|
+
const { userId, integrationId, event, data } = req.body
|
|
159
|
+
|
|
160
|
+
if (!userId || !integrationId || !event) {
|
|
161
|
+
return res.status(400).json({
|
|
162
|
+
error: 'userId, integrationId, and event are required'
|
|
163
|
+
})
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const webhookEvent = {
|
|
168
|
+
eventId: `webhook_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
169
|
+
userId,
|
|
170
|
+
integrationId,
|
|
171
|
+
event,
|
|
172
|
+
data,
|
|
173
|
+
timestamp: new Date().toISOString(),
|
|
174
|
+
processed: false
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Broadcast webhook event
|
|
178
|
+
wsHandler.broadcast('simulation:webhook', {
|
|
179
|
+
action: 'received',
|
|
180
|
+
webhookEvent,
|
|
181
|
+
timestamp: new Date().toISOString()
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
// Simulate processing delay
|
|
185
|
+
setTimeout(() => {
|
|
186
|
+
webhookEvent.processed = true
|
|
187
|
+
wsHandler.broadcast('simulation:webhook', {
|
|
188
|
+
action: 'processed',
|
|
189
|
+
webhookEvent,
|
|
190
|
+
timestamp: new Date().toISOString()
|
|
191
|
+
})
|
|
192
|
+
}, 1000)
|
|
193
|
+
|
|
194
|
+
res.json({
|
|
195
|
+
status: 'success',
|
|
196
|
+
webhookEvent
|
|
197
|
+
})
|
|
198
|
+
} catch (error) {
|
|
199
|
+
res.status(500).json({
|
|
200
|
+
error: error.message,
|
|
201
|
+
details: 'Failed to simulate webhook'
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
// Helper function to generate mock responses
|
|
207
|
+
function generateMockResponse(action, payload) {
|
|
208
|
+
const mockResponses = {
|
|
209
|
+
'list': {
|
|
210
|
+
items: [
|
|
211
|
+
{ id: '1', name: 'Item 1', created: new Date().toISOString() },
|
|
212
|
+
{ id: '2', name: 'Item 2', created: new Date().toISOString() }
|
|
213
|
+
],
|
|
214
|
+
total: 2
|
|
215
|
+
},
|
|
216
|
+
'create': {
|
|
217
|
+
id: `item_${Date.now()}`,
|
|
218
|
+
...payload,
|
|
219
|
+
created: new Date().toISOString()
|
|
220
|
+
},
|
|
221
|
+
'update': {
|
|
222
|
+
id: payload?.id || `item_${Date.now()}`,
|
|
223
|
+
...payload,
|
|
224
|
+
updated: new Date().toISOString()
|
|
225
|
+
},
|
|
226
|
+
'delete': {
|
|
227
|
+
success: true,
|
|
228
|
+
deleted: new Date().toISOString()
|
|
229
|
+
},
|
|
230
|
+
'sync': {
|
|
231
|
+
synced: Math.floor(Math.random() * 100),
|
|
232
|
+
failed: Math.floor(Math.random() * 10),
|
|
233
|
+
timestamp: new Date().toISOString()
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return mockResponses[action] || {
|
|
238
|
+
success: true,
|
|
239
|
+
action,
|
|
240
|
+
timestamp: new Date().toISOString()
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Clean up expired sessions periodically
|
|
245
|
+
setInterval(() => {
|
|
246
|
+
const now = new Date()
|
|
247
|
+
for (const [sessionId, session] of simulationSessions.entries()) {
|
|
248
|
+
if (new Date(session.expiresAt) < now) {
|
|
249
|
+
simulationSessions.delete(sessionId)
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}, 60000) // Every minute
|
|
253
|
+
|
|
254
|
+
export default router
|