@friggframework/devtools 2.0.0--canary.398.dd443c7.0 → 2.0.0--canary.402.d2f4ae6.0
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/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/index.js +19 -1
- 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/package.json +51 -0
- 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 +25 -0
- 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/serverless-template.js +177 -292
- 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/{dist/index.html → index.html} +1 -2
- 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/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 +5 -5
- package/management-ui/dist/assets/index-BA21WgFa.js +0 -1221
- package/management-ui/dist/assets/index-CbM64Oba.js +0 -1221
- package/management-ui/dist/assets/index-CkvseXTC.css +0 -1
- /package/management-ui/{dist/assets/FriggLogo-B7Xx8ZW1.svg → src/assets/FriggLogo.svg} +0 -0
|
@@ -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
|