@intranefr/superbackend 1.5.3 → 1.6.4
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/cookies.txt +6 -0
- package/cookies1.txt +6 -0
- package/cookies2.txt +6 -0
- package/cookies3.txt +6 -0
- package/cookies4.txt +5 -0
- package/cookies_old.txt +5 -0
- package/cookies_old_test.txt +6 -0
- package/cookies_super.txt +5 -0
- package/cookies_super_test.txt +6 -0
- package/cookies_test.txt +6 -0
- package/index.js +7 -0
- package/package.json +3 -1
- package/plugins/core-waiting-list-migration/README.md +118 -0
- package/plugins/core-waiting-list-migration/index.js +438 -0
- package/plugins/global-settings-presets/index.js +20 -0
- package/plugins/hello-cli/index.js +17 -0
- package/plugins/ui-components-seeder/components/suiAlert.js +212 -0
- package/plugins/ui-components-seeder/components/suiToast.js +186 -0
- package/plugins/ui-components-seeder/index.js +31 -0
- package/public/js/admin-ui-components-preview.js +281 -0
- package/public/js/admin-ui-components.js +408 -0
- package/public/js/llm-provider-model-picker.js +193 -0
- package/public/test-iframe-fix.html +63 -0
- package/public/test-iframe.html +14 -0
- package/src/admin/endpointRegistry.js +68 -0
- package/src/controllers/admin.controller.js +25 -5
- package/src/controllers/adminDataCleanup.controller.js +45 -0
- package/src/controllers/adminLlm.controller.js +0 -8
- package/src/controllers/adminLogin.controller.js +269 -0
- package/src/controllers/adminPlugins.controller.js +55 -0
- package/src/controllers/adminRegistry.controller.js +106 -0
- package/src/controllers/adminStats.controller.js +4 -4
- package/src/controllers/registry.controller.js +32 -0
- package/src/controllers/waitingList.controller.js +52 -74
- package/src/middleware/auth.js +71 -1
- package/src/middleware/rbac.js +62 -0
- package/src/middleware.js +480 -156
- package/src/models/GlobalSetting.js +11 -1
- package/src/models/UiComponent.js +2 -0
- package/src/models/User.js +1 -1
- package/src/routes/admin.routes.js +3 -3
- package/src/routes/adminAgents.routes.js +2 -2
- package/src/routes/adminAssets.routes.js +11 -11
- package/src/routes/adminBlog.routes.js +2 -2
- package/src/routes/adminBlogAi.routes.js +2 -2
- package/src/routes/adminBlogAutomation.routes.js +2 -2
- package/src/routes/adminCache.routes.js +2 -2
- package/src/routes/adminConsoleManager.routes.js +2 -2
- package/src/routes/adminCrons.routes.js +2 -2
- package/src/routes/adminDataCleanup.routes.js +26 -0
- package/src/routes/adminDbBrowser.routes.js +2 -2
- package/src/routes/adminEjsVirtual.routes.js +2 -2
- package/src/routes/adminFeatureFlags.routes.js +6 -6
- package/src/routes/adminHeadless.routes.js +2 -2
- package/src/routes/adminHealthChecks.routes.js +2 -2
- package/src/routes/adminI18n.routes.js +2 -2
- package/src/routes/adminJsonConfigs.routes.js +8 -8
- package/src/routes/adminLlm.routes.js +8 -8
- package/src/routes/adminLogin.routes.js +23 -0
- package/src/routes/adminMarkdowns.routes.js +3 -9
- package/src/routes/adminMigration.routes.js +12 -12
- package/src/routes/adminPages.routes.js +2 -2
- package/src/routes/adminPlugins.routes.js +15 -0
- package/src/routes/adminProxy.routes.js +2 -2
- package/src/routes/adminRateLimits.routes.js +8 -8
- package/src/routes/adminRbac.routes.js +2 -2
- package/src/routes/adminRegistry.routes.js +24 -0
- package/src/routes/adminScripts.routes.js +2 -2
- package/src/routes/adminSeoConfig.routes.js +10 -10
- package/src/routes/adminTelegram.routes.js +2 -2
- package/src/routes/adminTerminals.routes.js +2 -2
- package/src/routes/adminUiComponents.routes.js +2 -2
- package/src/routes/adminUploadNamespaces.routes.js +7 -7
- package/src/routes/blogInternal.routes.js +2 -2
- package/src/routes/experiments.routes.js +2 -2
- package/src/routes/formsAdmin.routes.js +6 -6
- package/src/routes/globalSettings.routes.js +8 -8
- package/src/routes/internalExperiments.routes.js +2 -2
- package/src/routes/notificationAdmin.routes.js +7 -7
- package/src/routes/orgAdmin.routes.js +16 -16
- package/src/routes/pages.routes.js +3 -3
- package/src/routes/registry.routes.js +11 -0
- package/src/routes/stripeAdmin.routes.js +12 -12
- package/src/routes/userAdmin.routes.js +7 -7
- package/src/routes/waitingListAdmin.routes.js +2 -2
- package/src/routes/workflows.routes.js +3 -3
- package/src/services/dataCleanup.service.js +286 -0
- package/src/services/jsonConfigs.service.js +262 -0
- package/src/services/plugins.service.js +348 -0
- package/src/services/registry.service.js +452 -0
- package/src/services/uiComponents.service.js +180 -0
- package/src/services/waitingListJson.service.js +401 -0
- package/src/utils/rbac/rightsRegistry.js +118 -0
- package/test-access.js +63 -0
- package/test-iframe-fix.html +63 -0
- package/test-iframe.html +14 -0
- package/views/admin-403.ejs +92 -0
- package/views/admin-dashboard-home.ejs +52 -2
- package/views/admin-dashboard.ejs +143 -2
- package/views/admin-data-cleanup.ejs +357 -0
- package/views/admin-login.ejs +286 -0
- package/views/admin-plugins-system.ejs +223 -0
- package/views/admin-ui-components.ejs +82 -402
- package/views/admin-users.ejs +207 -11
- package/views/partials/dashboard/nav-items.ejs +2 -0
- package/views/partials/llm-provider-model-picker.ejs +0 -161
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Access Denied - SuperBackend Admin</title>
|
|
7
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
8
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@latest/dist/tabler-icons.min.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body class="bg-gray-50 min-h-screen flex items-center justify-center">
|
|
11
|
+
<div class="max-w-md w-full bg-white rounded-lg shadow-lg p-8">
|
|
12
|
+
<div class="text-center">
|
|
13
|
+
<!-- Error Icon -->
|
|
14
|
+
<div class="mx-auto w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mb-4">
|
|
15
|
+
<i class="ti ti-lock text-red-600 text-2xl"></i>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<!-- Error Title -->
|
|
19
|
+
<h1 class="text-2xl font-bold text-gray-900 mb-2">Access Denied</h1>
|
|
20
|
+
|
|
21
|
+
<!-- Error Message -->
|
|
22
|
+
<p class="text-gray-600 mb-6">
|
|
23
|
+
You don't have permission to access this module.
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
<!-- Permission Details -->
|
|
27
|
+
<div class="bg-gray-50 rounded-lg p-4 mb-6 text-left">
|
|
28
|
+
<h3 class="text-sm font-semibold text-gray-700 mb-2">Permission Details:</h3>
|
|
29
|
+
<div class="space-y-1 text-xs">
|
|
30
|
+
<div class="flex justify-between">
|
|
31
|
+
<span class="text-gray-600">Module:</span>
|
|
32
|
+
<span class="font-mono text-gray-800"><%= moduleId %></span>
|
|
33
|
+
</div>
|
|
34
|
+
<div class="flex justify-between">
|
|
35
|
+
<span class="text-gray-600">Action:</span>
|
|
36
|
+
<span class="font-mono text-gray-800"><%= action %></span>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="flex justify-between">
|
|
39
|
+
<span class="text-gray-600">Required:</span>
|
|
40
|
+
<span class="font-mono text-gray-800 break-all"><%= required %></span>
|
|
41
|
+
</div>
|
|
42
|
+
<% if (reason) { %>
|
|
43
|
+
<div class="flex justify-between">
|
|
44
|
+
<span class="text-gray-600">Reason:</span>
|
|
45
|
+
<span class="font-mono text-gray-800"><%= reason %></span>
|
|
46
|
+
</div>
|
|
47
|
+
<% } %>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<!-- User Info -->
|
|
52
|
+
<% if (user) { %>
|
|
53
|
+
<div class="bg-blue-50 rounded-lg p-4 mb-6 text-left">
|
|
54
|
+
<h3 class="text-sm font-semibold text-blue-700 mb-2">Current User:</h3>
|
|
55
|
+
<div class="space-y-1 text-xs">
|
|
56
|
+
<div class="flex justify-between">
|
|
57
|
+
<span class="text-blue-600">Name:</span>
|
|
58
|
+
<span class="font-mono text-blue-800"><%= user.name || 'N/A' %></span>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="flex justify-between">
|
|
61
|
+
<span class="text-blue-600">Email:</span>
|
|
62
|
+
<span class="font-mono text-blue-800 break-all"><%= user.email %></span>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="flex justify-between">
|
|
65
|
+
<span class="text-blue-600">Role:</span>
|
|
66
|
+
<span class="font-mono text-blue-800"><%= user.role || 'N/A' %></span>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
<% } %>
|
|
71
|
+
|
|
72
|
+
<!-- Action Buttons -->
|
|
73
|
+
<div class="space-y-3">
|
|
74
|
+
<a href="<%= adminPath %>" class="w-full bg-blue-600 text-white py-2 px-4 rounded-lg hover:bg-blue-700 transition-colors flex items-center justify-center">
|
|
75
|
+
<i class="ti ti-layout-dashboard mr-2"></i>
|
|
76
|
+
Return to Dashboard
|
|
77
|
+
</a>
|
|
78
|
+
|
|
79
|
+
<button onclick="history.back()" class="w-full bg-gray-200 text-gray-700 py-2 px-4 rounded-lg hover:bg-gray-300 transition-colors flex items-center justify-center">
|
|
80
|
+
<i class="ti ti-arrow-left mr-2"></i>
|
|
81
|
+
Go Back
|
|
82
|
+
</button>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<!-- Help Text -->
|
|
86
|
+
<p class="text-xs text-gray-500 mt-6">
|
|
87
|
+
If you believe this is an error, please contact your system administrator.
|
|
88
|
+
</p>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
</body>
|
|
92
|
+
</html>
|
|
@@ -149,14 +149,64 @@
|
|
|
149
149
|
|
|
150
150
|
<script>
|
|
151
151
|
let growthChart, emailChart;
|
|
152
|
+
const isIframe = <%= typeof isIframe !== 'undefined' && isIframe %>;
|
|
152
153
|
|
|
153
154
|
async function fetchStats() {
|
|
154
155
|
try {
|
|
155
|
-
|
|
156
|
-
|
|
156
|
+
let data;
|
|
157
|
+
|
|
158
|
+
if (isIframe) {
|
|
159
|
+
// In iframe mode, request data from parent window
|
|
160
|
+
data = await new Promise((resolve, reject) => {
|
|
161
|
+
const messageId = 'stats-request-' + Date.now();
|
|
162
|
+
|
|
163
|
+
const handleMessage = (event) => {
|
|
164
|
+
if (event.data.type === 'stats-response' && event.data.messageId === messageId) {
|
|
165
|
+
window.removeEventListener('message', handleMessage);
|
|
166
|
+
if (event.data.error) {
|
|
167
|
+
reject(new Error(event.data.error));
|
|
168
|
+
} else {
|
|
169
|
+
resolve(event.data.data);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
window.addEventListener('message', handleMessage);
|
|
175
|
+
|
|
176
|
+
// Request data from parent
|
|
177
|
+
window.parent.postMessage({
|
|
178
|
+
type: 'stats-request',
|
|
179
|
+
messageId: messageId,
|
|
180
|
+
endpoint: '<%= baseUrl %>/api/admin/stats/overview'
|
|
181
|
+
}, '*');
|
|
182
|
+
|
|
183
|
+
// Timeout after 5 seconds
|
|
184
|
+
setTimeout(() => {
|
|
185
|
+
window.removeEventListener('message', handleMessage);
|
|
186
|
+
reject(new Error('Timeout waiting for parent response'));
|
|
187
|
+
}, 5000);
|
|
188
|
+
});
|
|
189
|
+
} else {
|
|
190
|
+
// Normal mode - direct fetch
|
|
191
|
+
const res = await fetch('<%= baseUrl %>/api/admin/stats/overview');
|
|
192
|
+
data = await res.json();
|
|
193
|
+
}
|
|
194
|
+
|
|
157
195
|
updateUI(data);
|
|
158
196
|
} catch (err) {
|
|
159
197
|
console.error('Failed to load stats:', err);
|
|
198
|
+
// Show error state
|
|
199
|
+
const app = document.getElementById('app');
|
|
200
|
+
if (app) {
|
|
201
|
+
app.innerHTML = `
|
|
202
|
+
<div class="text-center py-12">
|
|
203
|
+
<i class="ti ti-alert-triangle text-4xl text-yellow-500 mb-4"></i>
|
|
204
|
+
<h3 class="text-lg font-semibold text-gray-900 mb-2">Unable to load stats</h3>
|
|
205
|
+
<p class="text-gray-500">${err.message}</p>
|
|
206
|
+
${isIframe ? '<p class="text-sm text-gray-400 mt-2">Iframe mode: Parent window communication failed</p>' : ''}
|
|
207
|
+
</div>
|
|
208
|
+
`;
|
|
209
|
+
}
|
|
160
210
|
}
|
|
161
211
|
}
|
|
162
212
|
|
|
@@ -30,6 +30,35 @@
|
|
|
30
30
|
<span class="ml-1">to search</span>
|
|
31
31
|
</div>
|
|
32
32
|
<div class="flex items-center gap-4">
|
|
33
|
+
<!-- User Info & Logout -->
|
|
34
|
+
<div class="flex items-center gap-3">
|
|
35
|
+
<div class="text-right">
|
|
36
|
+
<div class="text-sm font-medium text-gray-700">{{ currentUser.name || currentUser.username || 'Admin User' }}</div>
|
|
37
|
+
<div class="text-xs text-gray-500">{{ currentUser.email || currentUser.authType + ' authentication' }}</div>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="relative">
|
|
40
|
+
<button
|
|
41
|
+
@click="showUserMenu = !showUserMenu"
|
|
42
|
+
class="flex items-center justify-center w-8 h-8 bg-blue-600 text-white rounded-full hover:bg-blue-700 transition-colors">
|
|
43
|
+
<i class="ti ti-user text-sm"></i>
|
|
44
|
+
</button>
|
|
45
|
+
|
|
46
|
+
<!-- User Dropdown Menu -->
|
|
47
|
+
<div v-if="showUserMenu" class="absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg border border-gray-200 py-1 z-50">
|
|
48
|
+
<div class="px-4 py-2 border-b border-gray-100">
|
|
49
|
+
<div class="text-sm font-medium text-gray-900">{{ currentUser.name || currentUser.username || 'Admin User' }}</div>
|
|
50
|
+
<div class="text-xs text-gray-500">{{ currentUser.role }}</div>
|
|
51
|
+
</div>
|
|
52
|
+
<button
|
|
53
|
+
@click="handleLogout"
|
|
54
|
+
class="w-full text-left px-4 py-2 text-sm text-red-600 hover:bg-red-50 transition-colors flex items-center gap-2">
|
|
55
|
+
<i class="ti ti-logout"></i>
|
|
56
|
+
Logout
|
|
57
|
+
</button>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
33
62
|
<button @click="globalZenMode = !globalZenMode" class="text-gray-500 hover:text-blue-600 p-1.5 rounded-lg border border-gray-200 bg-white" title="Toggle Global Zen Mode (ESC ESC to exit)">
|
|
34
63
|
<i class="ti ti-maximize"></i>
|
|
35
64
|
</button>
|
|
@@ -56,7 +85,7 @@
|
|
|
56
85
|
v-for="tab in tabs"
|
|
57
86
|
v-show="activeTabId === tab.id"
|
|
58
87
|
:key="tab.id"
|
|
59
|
-
:src="
|
|
88
|
+
:src="getIframeSrc(tab.path)"
|
|
60
89
|
class="absolute inset-0 w-full h-full border-none"
|
|
61
90
|
:id="'frame-' + tab.id"
|
|
62
91
|
></iframe>
|
|
@@ -97,6 +126,10 @@
|
|
|
97
126
|
const tabs = ref([]);
|
|
98
127
|
const activeTabId = ref(null);
|
|
99
128
|
const globalZenMode = ref(false);
|
|
129
|
+
|
|
130
|
+
// User authentication state
|
|
131
|
+
const currentUser = ref({});
|
|
132
|
+
const showUserMenu = ref(false);
|
|
100
133
|
|
|
101
134
|
// ESC ESC to exit Zen Mode
|
|
102
135
|
let escCount = 0;
|
|
@@ -385,9 +418,59 @@
|
|
|
385
418
|
}
|
|
386
419
|
};
|
|
387
420
|
|
|
421
|
+
// User authentication functions
|
|
422
|
+
const fetchCurrentUser = async () => {
|
|
423
|
+
try {
|
|
424
|
+
const response = await fetch(`${adminBase}/auth-status`);
|
|
425
|
+
if (response.ok) {
|
|
426
|
+
const userData = await response.json();
|
|
427
|
+
if (userData.authenticated) {
|
|
428
|
+
currentUser.value = userData.user || {};
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
} catch (error) {
|
|
432
|
+
console.error('Failed to fetch user data:', error);
|
|
433
|
+
// Set default user data on error
|
|
434
|
+
currentUser.value = { name: 'Admin User', authType: 'unknown' };
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
const handleLogout = async () => {
|
|
439
|
+
try {
|
|
440
|
+
const response = await fetch(`${adminBase}/logout`, {
|
|
441
|
+
method: 'POST',
|
|
442
|
+
headers: {
|
|
443
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
if (response.redirected) {
|
|
448
|
+
window.location.href = response.url;
|
|
449
|
+
} else {
|
|
450
|
+
// Fallback redirect
|
|
451
|
+
window.location.href = `${adminBase}/login`;
|
|
452
|
+
}
|
|
453
|
+
} catch (error) {
|
|
454
|
+
console.error('Logout error:', error);
|
|
455
|
+
// Fallback redirect
|
|
456
|
+
window.location.href = `${adminBase}/login`;
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
// Close user menu when clicking outside
|
|
461
|
+
const handleClickOutside = (event) => {
|
|
462
|
+
if (showUserMenu.value && !event.target.closest('.relative')) {
|
|
463
|
+
showUserMenu.value = false;
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
|
|
388
467
|
onMounted(() => {
|
|
389
468
|
window.addEventListener('keydown', handleKeydown);
|
|
390
469
|
window.addEventListener('message', handleMessage);
|
|
470
|
+
window.addEventListener('click', handleClickOutside);
|
|
471
|
+
|
|
472
|
+
// Fetch current user data
|
|
473
|
+
fetchCurrentUser();
|
|
391
474
|
|
|
392
475
|
// Load saved tabs from localStorage
|
|
393
476
|
const savedState = loadTabsFromStorage();
|
|
@@ -411,8 +494,17 @@
|
|
|
411
494
|
onUnmounted(() => {
|
|
412
495
|
window.removeEventListener('keydown', handleKeydown);
|
|
413
496
|
window.removeEventListener('message', handleMessage);
|
|
497
|
+
window.removeEventListener('click', handleClickOutside);
|
|
414
498
|
});
|
|
415
499
|
|
|
500
|
+
const getIframeSrc = (path) => {
|
|
501
|
+
// Ensure path starts with /
|
|
502
|
+
const cleanPath = path.startsWith('/') ? path : ('/' + path);
|
|
503
|
+
// Add iframe token for authentication
|
|
504
|
+
const separator = cleanPath.includes('?') ? '&' : '?';
|
|
505
|
+
return cleanPath + separator + 'iframe_token=authenticated';
|
|
506
|
+
};
|
|
507
|
+
|
|
416
508
|
return {
|
|
417
509
|
baseUrl,
|
|
418
510
|
adminBase,
|
|
@@ -420,6 +512,8 @@
|
|
|
420
512
|
tabs,
|
|
421
513
|
activeTabId,
|
|
422
514
|
globalZenMode,
|
|
515
|
+
currentUser,
|
|
516
|
+
showUserMenu,
|
|
423
517
|
openTab,
|
|
424
518
|
closeTab,
|
|
425
519
|
showPalette,
|
|
@@ -431,12 +525,59 @@
|
|
|
431
525
|
closePalette,
|
|
432
526
|
navigatePalette,
|
|
433
527
|
selectPaletteItem,
|
|
434
|
-
selectModule
|
|
528
|
+
selectModule,
|
|
529
|
+
handleLogout,
|
|
530
|
+
getIframeSrc
|
|
435
531
|
};
|
|
436
532
|
}
|
|
437
533
|
}).mount('#app');
|
|
438
534
|
</script>
|
|
439
535
|
<script>
|
|
536
|
+
// Handle iframe communication for API requests
|
|
537
|
+
window.addEventListener('message', async (event) => {
|
|
538
|
+
if (event.data.type === 'stats-request') {
|
|
539
|
+
try {
|
|
540
|
+
const response = await fetch(event.data.endpoint);
|
|
541
|
+
const data = await response.json();
|
|
542
|
+
|
|
543
|
+
// Send response back to iframe
|
|
544
|
+
event.source.postMessage({
|
|
545
|
+
type: 'stats-response',
|
|
546
|
+
messageId: event.data.messageId,
|
|
547
|
+
data: data
|
|
548
|
+
}, '*');
|
|
549
|
+
} catch (error) {
|
|
550
|
+
// Send error back to iframe
|
|
551
|
+
event.source.postMessage({
|
|
552
|
+
type: 'stats-response',
|
|
553
|
+
messageId: event.data.messageId,
|
|
554
|
+
error: error.message
|
|
555
|
+
}, '*');
|
|
556
|
+
}
|
|
557
|
+
} else if (event.data.type === 'api-request') {
|
|
558
|
+
try {
|
|
559
|
+
const response = await fetch(event.data.url, event.data.options);
|
|
560
|
+
const data = await response.json();
|
|
561
|
+
|
|
562
|
+
// Send response back to iframe
|
|
563
|
+
event.source.postMessage({
|
|
564
|
+
type: 'api-response',
|
|
565
|
+
messageId: event.data.messageId,
|
|
566
|
+
ok: response.ok,
|
|
567
|
+
data: data
|
|
568
|
+
}, '*');
|
|
569
|
+
} catch (error) {
|
|
570
|
+
// Send error back to iframe
|
|
571
|
+
event.source.postMessage({
|
|
572
|
+
type: 'api-response',
|
|
573
|
+
messageId: event.data.messageId,
|
|
574
|
+
ok: false,
|
|
575
|
+
error: error.message
|
|
576
|
+
}, '*');
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
|
|
440
581
|
window.addEventListener("keydown", (e) => {
|
|
441
582
|
if ((e.ctrlKey || e.metaKey) && e.key === "k") {
|
|
442
583
|
e.preventDefault();
|