@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.
Files changed (199) hide show
  1. package/frigg-cli/.eslintrc.js +141 -0
  2. package/frigg-cli/__tests__/jest.config.js +102 -0
  3. package/frigg-cli/__tests__/unit/commands/build.test.js +483 -0
  4. package/frigg-cli/__tests__/unit/commands/install.test.js +418 -0
  5. package/frigg-cli/__tests__/unit/commands/ui.test.js +592 -0
  6. package/frigg-cli/__tests__/utils/command-tester.js +170 -0
  7. package/frigg-cli/__tests__/utils/mock-factory.js +270 -0
  8. package/frigg-cli/__tests__/utils/test-fixtures.js +463 -0
  9. package/frigg-cli/__tests__/utils/test-setup.js +286 -0
  10. package/frigg-cli/build-command/index.js +54 -0
  11. package/frigg-cli/deploy-command/index.js +36 -0
  12. package/frigg-cli/generate-command/__tests__/generate-command.test.js +312 -0
  13. package/frigg-cli/generate-command/azure-generator.js +43 -0
  14. package/frigg-cli/generate-command/gcp-generator.js +47 -0
  15. package/frigg-cli/generate-command/index.js +332 -0
  16. package/frigg-cli/generate-command/terraform-generator.js +555 -0
  17. package/frigg-cli/generate-iam-command.js +115 -0
  18. package/frigg-cli/index.js +47 -1
  19. package/frigg-cli/index.test.js +1 -4
  20. package/frigg-cli/init-command/backend-first-handler.js +756 -0
  21. package/frigg-cli/init-command/index.js +93 -0
  22. package/frigg-cli/init-command/template-handler.js +143 -0
  23. package/frigg-cli/install-command/index.js +1 -4
  24. package/frigg-cli/package.json +51 -0
  25. package/frigg-cli/start-command/index.js +24 -4
  26. package/frigg-cli/test/init-command.test.js +180 -0
  27. package/frigg-cli/test/npm-registry.test.js +319 -0
  28. package/frigg-cli/ui-command/index.js +154 -0
  29. package/frigg-cli/utils/app-resolver.js +319 -0
  30. package/frigg-cli/utils/backend-path.js +16 -17
  31. package/frigg-cli/utils/npm-registry.js +167 -0
  32. package/frigg-cli/utils/process-manager.js +199 -0
  33. package/frigg-cli/utils/repo-detection.js +405 -0
  34. package/infrastructure/AWS-DISCOVERY-TROUBLESHOOTING.md +245 -0
  35. package/infrastructure/AWS-IAM-CREDENTIAL-NEEDS.md +596 -0
  36. package/infrastructure/DEPLOYMENT-INSTRUCTIONS.md +268 -0
  37. package/infrastructure/GENERATE-IAM-DOCS.md +253 -0
  38. package/infrastructure/IAM-POLICY-TEMPLATES.md +176 -0
  39. package/infrastructure/README-TESTING.md +332 -0
  40. package/infrastructure/README.md +421 -0
  41. package/infrastructure/WEBSOCKET-CONFIGURATION.md +105 -0
  42. package/infrastructure/__tests__/fixtures/mock-aws-resources.js +391 -0
  43. package/infrastructure/__tests__/helpers/test-utils.js +277 -0
  44. package/infrastructure/aws-discovery.js +568 -0
  45. package/infrastructure/aws-discovery.test.js +373 -0
  46. package/infrastructure/build-time-discovery.js +206 -0
  47. package/infrastructure/build-time-discovery.test.js +375 -0
  48. package/infrastructure/create-frigg-infrastructure.js +3 -5
  49. package/infrastructure/frigg-deployment-iam-stack.yaml +379 -0
  50. package/infrastructure/iam-generator.js +687 -0
  51. package/infrastructure/iam-generator.test.js +169 -0
  52. package/infrastructure/iam-policy-basic.json +212 -0
  53. package/infrastructure/iam-policy-full.json +282 -0
  54. package/infrastructure/integration.test.js +383 -0
  55. package/infrastructure/run-discovery.js +110 -0
  56. package/infrastructure/serverless-template.js +923 -113
  57. package/infrastructure/serverless-template.test.js +541 -0
  58. package/management-ui/.eslintrc.js +22 -0
  59. package/management-ui/README.md +203 -0
  60. package/management-ui/components.json +21 -0
  61. package/management-ui/docs/phase2-integration-guide.md +320 -0
  62. package/management-ui/index.html +13 -0
  63. package/management-ui/package-lock.json +16517 -0
  64. package/management-ui/package.json +76 -0
  65. package/management-ui/packages/devtools/frigg-cli/ui-command/index.js +302 -0
  66. package/management-ui/postcss.config.js +6 -0
  67. package/management-ui/server/api/backend.js +256 -0
  68. package/management-ui/server/api/cli.js +315 -0
  69. package/management-ui/server/api/codegen.js +663 -0
  70. package/management-ui/server/api/connections.js +857 -0
  71. package/management-ui/server/api/discovery.js +185 -0
  72. package/management-ui/server/api/environment/index.js +1 -0
  73. package/management-ui/server/api/environment/router.js +378 -0
  74. package/management-ui/server/api/environment.js +328 -0
  75. package/management-ui/server/api/integrations.js +876 -0
  76. package/management-ui/server/api/logs.js +248 -0
  77. package/management-ui/server/api/monitoring.js +282 -0
  78. package/management-ui/server/api/open-ide.js +31 -0
  79. package/management-ui/server/api/project.js +1029 -0
  80. package/management-ui/server/api/users/sessions.js +371 -0
  81. package/management-ui/server/api/users/simulation.js +254 -0
  82. package/management-ui/server/api/users.js +362 -0
  83. package/management-ui/server/api-contract.md +275 -0
  84. package/management-ui/server/index.js +873 -0
  85. package/management-ui/server/middleware/errorHandler.js +93 -0
  86. package/management-ui/server/middleware/security.js +32 -0
  87. package/management-ui/server/processManager.js +296 -0
  88. package/management-ui/server/server.js +346 -0
  89. package/management-ui/server/services/aws-monitor.js +413 -0
  90. package/management-ui/server/services/npm-registry.js +347 -0
  91. package/management-ui/server/services/template-engine.js +538 -0
  92. package/management-ui/server/utils/cliIntegration.js +220 -0
  93. package/management-ui/server/utils/environment/auditLogger.js +471 -0
  94. package/management-ui/server/utils/environment/awsParameterStore.js +264 -0
  95. package/management-ui/server/utils/environment/encryption.js +278 -0
  96. package/management-ui/server/utils/environment/envFileManager.js +286 -0
  97. package/management-ui/server/utils/import-commonjs.js +28 -0
  98. package/management-ui/server/utils/response.js +83 -0
  99. package/management-ui/server/websocket/handler.js +325 -0
  100. package/management-ui/src/App.jsx +109 -0
  101. package/management-ui/src/assets/FriggLogo.svg +1 -0
  102. package/management-ui/src/components/AppRouter.jsx +65 -0
  103. package/management-ui/src/components/Button.jsx +70 -0
  104. package/management-ui/src/components/Card.jsx +97 -0
  105. package/management-ui/src/components/EnvironmentCompare.jsx +400 -0
  106. package/management-ui/src/components/EnvironmentEditor.jsx +372 -0
  107. package/management-ui/src/components/EnvironmentImportExport.jsx +469 -0
  108. package/management-ui/src/components/EnvironmentSchema.jsx +491 -0
  109. package/management-ui/src/components/EnvironmentSecurity.jsx +463 -0
  110. package/management-ui/src/components/ErrorBoundary.jsx +73 -0
  111. package/management-ui/src/components/IntegrationCard.jsx +481 -0
  112. package/management-ui/src/components/IntegrationCardEnhanced.jsx +770 -0
  113. package/management-ui/src/components/IntegrationExplorer.jsx +379 -0
  114. package/management-ui/src/components/IntegrationStatus.jsx +336 -0
  115. package/management-ui/src/components/Layout.jsx +716 -0
  116. package/management-ui/src/components/LoadingSpinner.jsx +113 -0
  117. package/management-ui/src/components/RepositoryPicker.jsx +248 -0
  118. package/management-ui/src/components/SessionMonitor.jsx +350 -0
  119. package/management-ui/src/components/StatusBadge.jsx +208 -0
  120. package/management-ui/src/components/UserContextSwitcher.jsx +212 -0
  121. package/management-ui/src/components/UserSimulation.jsx +327 -0
  122. package/management-ui/src/components/Welcome.jsx +434 -0
  123. package/management-ui/src/components/codegen/APIEndpointGenerator.jsx +637 -0
  124. package/management-ui/src/components/codegen/APIModuleSelector.jsx +227 -0
  125. package/management-ui/src/components/codegen/CodeGenerationWizard.jsx +247 -0
  126. package/management-ui/src/components/codegen/CodePreviewEditor.jsx +316 -0
  127. package/management-ui/src/components/codegen/DynamicModuleForm.jsx +271 -0
  128. package/management-ui/src/components/codegen/FormBuilder.jsx +737 -0
  129. package/management-ui/src/components/codegen/IntegrationGenerator.jsx +855 -0
  130. package/management-ui/src/components/codegen/ProjectScaffoldWizard.jsx +797 -0
  131. package/management-ui/src/components/codegen/SchemaBuilder.jsx +303 -0
  132. package/management-ui/src/components/codegen/TemplateSelector.jsx +586 -0
  133. package/management-ui/src/components/codegen/index.js +10 -0
  134. package/management-ui/src/components/connections/ConnectionConfigForm.jsx +362 -0
  135. package/management-ui/src/components/connections/ConnectionHealthMonitor.jsx +182 -0
  136. package/management-ui/src/components/connections/ConnectionTester.jsx +200 -0
  137. package/management-ui/src/components/connections/EntityRelationshipMapper.jsx +292 -0
  138. package/management-ui/src/components/connections/OAuthFlow.jsx +204 -0
  139. package/management-ui/src/components/connections/index.js +5 -0
  140. package/management-ui/src/components/index.js +21 -0
  141. package/management-ui/src/components/monitoring/APIGatewayMetrics.jsx +222 -0
  142. package/management-ui/src/components/monitoring/LambdaMetrics.jsx +169 -0
  143. package/management-ui/src/components/monitoring/MetricsChart.jsx +197 -0
  144. package/management-ui/src/components/monitoring/MonitoringDashboard.jsx +393 -0
  145. package/management-ui/src/components/monitoring/SQSMetrics.jsx +246 -0
  146. package/management-ui/src/components/monitoring/index.js +6 -0
  147. package/management-ui/src/components/monitoring/monitoring.css +218 -0
  148. package/management-ui/src/components/theme-provider.jsx +52 -0
  149. package/management-ui/src/components/theme-toggle.jsx +39 -0
  150. package/management-ui/src/components/ui/badge.tsx +36 -0
  151. package/management-ui/src/components/ui/button.test.jsx +56 -0
  152. package/management-ui/src/components/ui/button.tsx +57 -0
  153. package/management-ui/src/components/ui/card.tsx +76 -0
  154. package/management-ui/src/components/ui/dropdown-menu.tsx +199 -0
  155. package/management-ui/src/components/ui/select.tsx +157 -0
  156. package/management-ui/src/components/ui/skeleton.jsx +15 -0
  157. package/management-ui/src/hooks/useFrigg.jsx +601 -0
  158. package/management-ui/src/hooks/useSocket.jsx +58 -0
  159. package/management-ui/src/index.css +193 -0
  160. package/management-ui/src/lib/utils.ts +6 -0
  161. package/management-ui/src/main.jsx +10 -0
  162. package/management-ui/src/pages/CodeGeneration.jsx +14 -0
  163. package/management-ui/src/pages/Connections.jsx +252 -0
  164. package/management-ui/src/pages/ConnectionsEnhanced.jsx +633 -0
  165. package/management-ui/src/pages/Dashboard.jsx +311 -0
  166. package/management-ui/src/pages/Environment.jsx +314 -0
  167. package/management-ui/src/pages/IntegrationConfigure.jsx +669 -0
  168. package/management-ui/src/pages/IntegrationDiscovery.jsx +567 -0
  169. package/management-ui/src/pages/IntegrationTest.jsx +742 -0
  170. package/management-ui/src/pages/Integrations.jsx +253 -0
  171. package/management-ui/src/pages/Monitoring.jsx +17 -0
  172. package/management-ui/src/pages/Simulation.jsx +155 -0
  173. package/management-ui/src/pages/Users.jsx +492 -0
  174. package/management-ui/src/services/api.js +41 -0
  175. package/management-ui/src/services/apiModuleService.js +193 -0
  176. package/management-ui/src/services/websocket-handlers.js +120 -0
  177. package/management-ui/src/test/api/project.test.js +273 -0
  178. package/management-ui/src/test/components/Welcome.test.jsx +378 -0
  179. package/management-ui/src/test/mocks/server.js +178 -0
  180. package/management-ui/src/test/setup.js +61 -0
  181. package/management-ui/src/test/utils/test-utils.jsx +134 -0
  182. package/management-ui/src/utils/repository.js +98 -0
  183. package/management-ui/src/utils/repository.test.js +118 -0
  184. package/management-ui/src/workflows/phase2-integration-workflows.js +884 -0
  185. package/management-ui/tailwind.config.js +63 -0
  186. package/management-ui/tsconfig.json +37 -0
  187. package/management-ui/tsconfig.node.json +10 -0
  188. package/management-ui/vite.config.js +26 -0
  189. package/management-ui/vitest.config.js +38 -0
  190. package/package.json +16 -9
  191. package/infrastructure/app-handler-helpers.js +0 -57
  192. package/infrastructure/backend-utils.js +0 -90
  193. package/infrastructure/routers/auth.js +0 -26
  194. package/infrastructure/routers/integration-defined-routers.js +0 -37
  195. package/infrastructure/routers/middleware/loadUser.js +0 -15
  196. package/infrastructure/routers/middleware/requireLoggedInUser.js +0 -12
  197. package/infrastructure/routers/user.js +0 -41
  198. package/infrastructure/routers/websocket.js +0 -55
  199. package/infrastructure/workers/integration-defined-workers.js +0 -24
@@ -0,0 +1,463 @@
1
+ import React, { useState, useEffect } from 'react'
2
+ import api from '../services/api'
3
+
4
+ const EnvironmentSecurity = ({ environment = 'local' }) => {
5
+ const [securitySettings, setSecuritySettings] = useState({
6
+ maskingEnabled: true,
7
+ encryptionEnabled: false,
8
+ auditLoggingEnabled: true,
9
+ accessControl: {
10
+ enabled: false,
11
+ defaultPermission: 'read',
12
+ rules: []
13
+ }
14
+ })
15
+ const [maskingPatterns, setMaskingPatterns] = useState([
16
+ { pattern: 'PASSWORD', enabled: true },
17
+ { pattern: 'SECRET', enabled: true },
18
+ { pattern: 'KEY', enabled: true },
19
+ { pattern: 'TOKEN', enabled: true },
20
+ { pattern: 'PRIVATE', enabled: true },
21
+ { pattern: 'CREDENTIAL', enabled: true },
22
+ { pattern: 'AUTH', enabled: true },
23
+ { pattern: 'API_KEY', enabled: true }
24
+ ])
25
+ const [customPattern, setCustomPattern] = useState('')
26
+ const [auditLogs, setAuditLogs] = useState([])
27
+ const [showAuditLogs, setShowAuditLogs] = useState(false)
28
+ const [accessRules, setAccessRules] = useState([])
29
+ const [newRule, setNewRule] = useState({
30
+ user: '',
31
+ pattern: '',
32
+ permission: 'read'
33
+ })
34
+
35
+ // Load security settings
36
+ useEffect(() => {
37
+ loadSecuritySettings()
38
+ loadAuditLogs()
39
+ }, [environment])
40
+
41
+ const loadSecuritySettings = () => {
42
+ // Load from localStorage for now
43
+ const saved = localStorage.getItem(`security-settings-${environment}`)
44
+ if (saved) {
45
+ const parsed = JSON.parse(saved)
46
+ setSecuritySettings(parsed.settings || securitySettings)
47
+ setMaskingPatterns(parsed.patterns || maskingPatterns)
48
+ setAccessRules(parsed.rules || [])
49
+ }
50
+ }
51
+
52
+ const saveSecuritySettings = () => {
53
+ const toSave = {
54
+ settings: securitySettings,
55
+ patterns: maskingPatterns,
56
+ rules: accessRules
57
+ }
58
+ localStorage.setItem(`security-settings-${environment}`, JSON.stringify(toSave))
59
+ }
60
+
61
+ const loadAuditLogs = async () => {
62
+ // Simulate loading audit logs
63
+ const mockLogs = [
64
+ {
65
+ id: 1,
66
+ timestamp: new Date().toISOString(),
67
+ user: 'admin@example.com',
68
+ action: 'update',
69
+ variable: 'DATABASE_URL',
70
+ environment,
71
+ details: 'Updated variable value'
72
+ },
73
+ {
74
+ id: 2,
75
+ timestamp: new Date(Date.now() - 3600000).toISOString(),
76
+ user: 'dev@example.com',
77
+ action: 'create',
78
+ variable: 'API_KEY',
79
+ environment,
80
+ details: 'Created new variable'
81
+ },
82
+ {
83
+ id: 3,
84
+ timestamp: new Date(Date.now() - 7200000).toISOString(),
85
+ user: 'admin@example.com',
86
+ action: 'delete',
87
+ variable: 'OLD_SECRET',
88
+ environment,
89
+ details: 'Deleted variable'
90
+ }
91
+ ]
92
+ setAuditLogs(mockLogs)
93
+ }
94
+
95
+ const toggleSetting = (setting) => {
96
+ const updated = { ...securitySettings, [setting]: !securitySettings[setting] }
97
+ setSecuritySettings(updated)
98
+ saveSecuritySettings()
99
+ }
100
+
101
+ const togglePattern = (index) => {
102
+ const updated = [...maskingPatterns]
103
+ updated[index].enabled = !updated[index].enabled
104
+ setMaskingPatterns(updated)
105
+ saveSecuritySettings()
106
+ }
107
+
108
+ const addCustomPattern = () => {
109
+ if (customPattern && !maskingPatterns.find(p => p.pattern === customPattern)) {
110
+ const updated = [...maskingPatterns, { pattern: customPattern.toUpperCase(), enabled: true }]
111
+ setMaskingPatterns(updated)
112
+ setCustomPattern('')
113
+ saveSecuritySettings()
114
+ }
115
+ }
116
+
117
+ const removePattern = (index) => {
118
+ const updated = maskingPatterns.filter((_, i) => i !== index)
119
+ setMaskingPatterns(updated)
120
+ saveSecuritySettings()
121
+ }
122
+
123
+ const addAccessRule = () => {
124
+ if (newRule.user && newRule.pattern) {
125
+ const updated = [...accessRules, { ...newRule, id: Date.now() }]
126
+ setAccessRules(updated)
127
+ setNewRule({ user: '', pattern: '', permission: 'read' })
128
+ saveSecuritySettings()
129
+ }
130
+ }
131
+
132
+ const removeAccessRule = (id) => {
133
+ const updated = accessRules.filter(rule => rule.id !== id)
134
+ setAccessRules(updated)
135
+ saveSecuritySettings()
136
+ }
137
+
138
+ const exportAuditLogs = () => {
139
+ const csv = [
140
+ ['Timestamp', 'User', 'Action', 'Variable', 'Environment', 'Details'].join(','),
141
+ ...auditLogs.map(log => [
142
+ log.timestamp,
143
+ log.user,
144
+ log.action,
145
+ log.variable,
146
+ log.environment,
147
+ log.details
148
+ ].join(','))
149
+ ].join('\n')
150
+
151
+ const blob = new Blob([csv], { type: 'text/csv' })
152
+ const url = URL.createObjectURL(blob)
153
+ const a = document.createElement('a')
154
+ a.href = url
155
+ a.download = `audit-logs-${environment}-${Date.now()}.csv`
156
+ a.click()
157
+ URL.revokeObjectURL(url)
158
+ }
159
+
160
+ return (
161
+ <div className="environment-security space-y-6">
162
+ {/* Security Overview */}
163
+ <div className="bg-white rounded-lg shadow p-6">
164
+ <h3 className="text-lg font-medium text-gray-900 mb-4">Security Settings</h3>
165
+
166
+ <div className="space-y-4">
167
+ {/* Masking Toggle */}
168
+ <div className="flex items-center justify-between">
169
+ <div>
170
+ <h4 className="text-sm font-medium text-gray-900">Variable Masking</h4>
171
+ <p className="text-sm text-gray-500">Hide sensitive variable values in the UI</p>
172
+ </div>
173
+ <button
174
+ onClick={() => toggleSetting('maskingEnabled')}
175
+ className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
176
+ securitySettings.maskingEnabled ? 'bg-blue-600' : 'bg-gray-200'
177
+ }`}
178
+ >
179
+ <span
180
+ className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
181
+ securitySettings.maskingEnabled ? 'translate-x-6' : 'translate-x-1'
182
+ }`}
183
+ />
184
+ </button>
185
+ </div>
186
+
187
+ {/* Encryption Toggle */}
188
+ <div className="flex items-center justify-between">
189
+ <div>
190
+ <h4 className="text-sm font-medium text-gray-900">Value Encryption</h4>
191
+ <p className="text-sm text-gray-500">Encrypt sensitive values at rest</p>
192
+ </div>
193
+ <button
194
+ onClick={() => toggleSetting('encryptionEnabled')}
195
+ className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
196
+ securitySettings.encryptionEnabled ? 'bg-blue-600' : 'bg-gray-200'
197
+ }`}
198
+ >
199
+ <span
200
+ className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
201
+ securitySettings.encryptionEnabled ? 'translate-x-6' : 'translate-x-1'
202
+ }`}
203
+ />
204
+ </button>
205
+ </div>
206
+
207
+ {/* Audit Logging Toggle */}
208
+ <div className="flex items-center justify-between">
209
+ <div>
210
+ <h4 className="text-sm font-medium text-gray-900">Audit Logging</h4>
211
+ <p className="text-sm text-gray-500">Track all changes to environment variables</p>
212
+ </div>
213
+ <button
214
+ onClick={() => toggleSetting('auditLoggingEnabled')}
215
+ className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
216
+ securitySettings.auditLoggingEnabled ? 'bg-blue-600' : 'bg-gray-200'
217
+ }`}
218
+ >
219
+ <span
220
+ className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
221
+ securitySettings.auditLoggingEnabled ? 'translate-x-6' : 'translate-x-1'
222
+ }`}
223
+ />
224
+ </button>
225
+ </div>
226
+
227
+ {/* Access Control Toggle */}
228
+ <div className="flex items-center justify-between">
229
+ <div>
230
+ <h4 className="text-sm font-medium text-gray-900">Access Control</h4>
231
+ <p className="text-sm text-gray-500">Enable role-based access control</p>
232
+ </div>
233
+ <button
234
+ onClick={() => {
235
+ const updated = { ...securitySettings }
236
+ updated.accessControl.enabled = !updated.accessControl.enabled
237
+ setSecuritySettings(updated)
238
+ saveSecuritySettings()
239
+ }}
240
+ className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
241
+ securitySettings.accessControl.enabled ? 'bg-blue-600' : 'bg-gray-200'
242
+ }`}
243
+ >
244
+ <span
245
+ className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
246
+ securitySettings.accessControl.enabled ? 'translate-x-6' : 'translate-x-1'
247
+ }`}
248
+ />
249
+ </button>
250
+ </div>
251
+ </div>
252
+ </div>
253
+
254
+ {/* Masking Patterns */}
255
+ {securitySettings.maskingEnabled && (
256
+ <div className="bg-white rounded-lg shadow p-6">
257
+ <h3 className="text-lg font-medium text-gray-900 mb-4">Masking Patterns</h3>
258
+ <p className="text-sm text-gray-500 mb-4">
259
+ Variables containing these patterns will be automatically masked
260
+ </p>
261
+
262
+ <div className="space-y-2 mb-4">
263
+ {maskingPatterns.map((pattern, index) => (
264
+ <div key={index} className="flex items-center justify-between py-2">
265
+ <div className="flex items-center">
266
+ <input
267
+ type="checkbox"
268
+ checked={pattern.enabled}
269
+ onChange={() => togglePattern(index)}
270
+ className="mr-3"
271
+ />
272
+ <code className="text-sm font-mono text-gray-700">{pattern.pattern}</code>
273
+ </div>
274
+ {index >= 8 && ( // Allow removing custom patterns only
275
+ <button
276
+ onClick={() => removePattern(index)}
277
+ className="text-red-600 hover:text-red-800"
278
+ >
279
+ Remove
280
+ </button>
281
+ )}
282
+ </div>
283
+ ))}
284
+ </div>
285
+
286
+ <div className="flex items-center space-x-2">
287
+ <input
288
+ type="text"
289
+ value={customPattern}
290
+ onChange={(e) => setCustomPattern(e.target.value.toUpperCase())}
291
+ placeholder="Add custom pattern"
292
+ className="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-blue-500"
293
+ />
294
+ <button
295
+ onClick={addCustomPattern}
296
+ className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"
297
+ >
298
+ Add Pattern
299
+ </button>
300
+ </div>
301
+ </div>
302
+ )}
303
+
304
+ {/* Access Control Rules */}
305
+ {securitySettings.accessControl.enabled && (
306
+ <div className="bg-white rounded-lg shadow p-6">
307
+ <h3 className="text-lg font-medium text-gray-900 mb-4">Access Control Rules</h3>
308
+
309
+ <div className="mb-4">
310
+ <label className="block text-sm font-medium text-gray-700 mb-2">
311
+ Default Permission
312
+ </label>
313
+ <select
314
+ value={securitySettings.accessControl.defaultPermission}
315
+ onChange={(e) => {
316
+ const updated = { ...securitySettings }
317
+ updated.accessControl.defaultPermission = e.target.value
318
+ setSecuritySettings(updated)
319
+ saveSecuritySettings()
320
+ }}
321
+ className="px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-blue-500"
322
+ >
323
+ <option value="none">No Access</option>
324
+ <option value="read">Read Only</option>
325
+ <option value="write">Read & Write</option>
326
+ </select>
327
+ </div>
328
+
329
+ {/* Access Rules Table */}
330
+ {accessRules.length > 0 && (
331
+ <div className="mb-4">
332
+ <table className="min-w-full divide-y divide-gray-200">
333
+ <thead className="bg-gray-50">
334
+ <tr>
335
+ <th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">User/Role</th>
336
+ <th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Variable Pattern</th>
337
+ <th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Permission</th>
338
+ <th className="px-4 py-2"></th>
339
+ </tr>
340
+ </thead>
341
+ <tbody className="bg-white divide-y divide-gray-200">
342
+ {accessRules.map(rule => (
343
+ <tr key={rule.id}>
344
+ <td className="px-4 py-2 text-sm">{rule.user}</td>
345
+ <td className="px-4 py-2 text-sm font-mono">{rule.pattern}</td>
346
+ <td className="px-4 py-2 text-sm">{rule.permission}</td>
347
+ <td className="px-4 py-2 text-sm text-right">
348
+ <button
349
+ onClick={() => removeAccessRule(rule.id)}
350
+ className="text-red-600 hover:text-red-800"
351
+ >
352
+ Remove
353
+ </button>
354
+ </td>
355
+ </tr>
356
+ ))}
357
+ </tbody>
358
+ </table>
359
+ </div>
360
+ )}
361
+
362
+ {/* Add Rule Form */}
363
+ <div className="grid grid-cols-3 gap-2">
364
+ <input
365
+ type="text"
366
+ value={newRule.user}
367
+ onChange={(e) => setNewRule({ ...newRule, user: e.target.value })}
368
+ placeholder="User or role"
369
+ className="px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-blue-500"
370
+ />
371
+ <input
372
+ type="text"
373
+ value={newRule.pattern}
374
+ onChange={(e) => setNewRule({ ...newRule, pattern: e.target.value })}
375
+ placeholder="Variable pattern (e.g., API_*)"
376
+ className="px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-blue-500"
377
+ />
378
+ <div className="flex space-x-2">
379
+ <select
380
+ value={newRule.permission}
381
+ onChange={(e) => setNewRule({ ...newRule, permission: e.target.value })}
382
+ className="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-blue-500"
383
+ >
384
+ <option value="none">No Access</option>
385
+ <option value="read">Read Only</option>
386
+ <option value="write">Read & Write</option>
387
+ </select>
388
+ <button
389
+ onClick={addAccessRule}
390
+ className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"
391
+ >
392
+ Add
393
+ </button>
394
+ </div>
395
+ </div>
396
+ </div>
397
+ )}
398
+
399
+ {/* Audit Logs */}
400
+ {securitySettings.auditLoggingEnabled && (
401
+ <div className="bg-white rounded-lg shadow p-6">
402
+ <div className="flex items-center justify-between mb-4">
403
+ <h3 className="text-lg font-medium text-gray-900">Audit Logs</h3>
404
+ <div className="space-x-2">
405
+ <button
406
+ onClick={() => setShowAuditLogs(!showAuditLogs)}
407
+ className="px-3 py-1 text-sm bg-gray-200 text-gray-700 rounded hover:bg-gray-300"
408
+ >
409
+ {showAuditLogs ? 'Hide' : 'Show'} Logs
410
+ </button>
411
+ <button
412
+ onClick={exportAuditLogs}
413
+ className="px-3 py-1 text-sm bg-gray-200 text-gray-700 rounded hover:bg-gray-300"
414
+ >
415
+ Export CSV
416
+ </button>
417
+ </div>
418
+ </div>
419
+
420
+ {showAuditLogs && (
421
+ <div className="overflow-x-auto">
422
+ <table className="min-w-full divide-y divide-gray-200">
423
+ <thead className="bg-gray-50">
424
+ <tr>
425
+ <th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Timestamp</th>
426
+ <th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">User</th>
427
+ <th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Action</th>
428
+ <th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Variable</th>
429
+ <th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Details</th>
430
+ </tr>
431
+ </thead>
432
+ <tbody className="bg-white divide-y divide-gray-200">
433
+ {auditLogs.map(log => (
434
+ <tr key={log.id}>
435
+ <td className="px-4 py-2 text-sm text-gray-500">
436
+ {new Date(log.timestamp).toLocaleString()}
437
+ </td>
438
+ <td className="px-4 py-2 text-sm">{log.user}</td>
439
+ <td className="px-4 py-2 text-sm">
440
+ <span className={`px-2 py-1 text-xs rounded ${
441
+ log.action === 'create' ? 'bg-green-100 text-green-800' :
442
+ log.action === 'update' ? 'bg-blue-100 text-blue-800' :
443
+ log.action === 'delete' ? 'bg-red-100 text-red-800' :
444
+ 'bg-gray-100 text-gray-800'
445
+ }`}>
446
+ {log.action}
447
+ </span>
448
+ </td>
449
+ <td className="px-4 py-2 text-sm font-mono">{log.variable}</td>
450
+ <td className="px-4 py-2 text-sm text-gray-500">{log.details}</td>
451
+ </tr>
452
+ ))}
453
+ </tbody>
454
+ </table>
455
+ </div>
456
+ )}
457
+ </div>
458
+ )}
459
+ </div>
460
+ )
461
+ }
462
+
463
+ export default EnvironmentSecurity
@@ -0,0 +1,73 @@
1
+ import React from 'react'
2
+
3
+ class ErrorBoundary extends React.Component {
4
+ constructor(props) {
5
+ super(props)
6
+ this.state = { hasError: false, error: null, errorInfo: null }
7
+ }
8
+
9
+ static getDerivedStateFromError(error) {
10
+ // Update state so the next render will show the fallback UI
11
+ return { hasError: true }
12
+ }
13
+
14
+ componentDidCatch(error, errorInfo) {
15
+ // Log the error to console or error reporting service
16
+ console.error('ErrorBoundary caught an error:', error, errorInfo)
17
+ this.setState({
18
+ error: error,
19
+ errorInfo: errorInfo
20
+ })
21
+ }
22
+
23
+ render() {
24
+ if (this.state.hasError) {
25
+ if (this.props.fallback) {
26
+ return this.props.fallback
27
+ }
28
+
29
+ return (
30
+ <div className="min-h-screen bg-gray-50 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
31
+ <div className="sm:mx-auto sm:w-full sm:max-w-md">
32
+ <div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
33
+ <div className="text-center">
34
+ <div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100">
35
+ <svg className="h-6 w-6 text-red-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
36
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L3.34 16.5c-.77.833.192 2.5 1.732 2.5z" />
37
+ </svg>
38
+ </div>
39
+ <h3 className="mt-4 text-lg font-medium text-gray-900">Something went wrong</h3>
40
+ <p className="mt-2 text-sm text-gray-500">
41
+ An unexpected error occurred. Please refresh the page and try again.
42
+ </p>
43
+ {process.env.NODE_ENV === 'development' && this.state.error && (
44
+ <details className="mt-4 text-left">
45
+ <summary className="cursor-pointer text-sm font-medium text-gray-700 hover:text-gray-900">
46
+ Error details (development only)
47
+ </summary>
48
+ <pre className="mt-2 text-xs text-red-600 whitespace-pre-wrap">
49
+ {this.state.error.toString()}
50
+ {this.state.errorInfo.componentStack}
51
+ </pre>
52
+ </details>
53
+ )}
54
+ <div className="mt-6">
55
+ <button
56
+ onClick={() => window.location.reload()}
57
+ className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
58
+ >
59
+ Reload page
60
+ </button>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ </div>
65
+ </div>
66
+ )
67
+ }
68
+
69
+ return this.props.children
70
+ }
71
+ }
72
+
73
+ export default ErrorBoundary