@intranefr/superbackend 1.5.0 → 1.5.2
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/.env.example +15 -0
- package/README.md +11 -0
- package/analysis-only.skill +0 -0
- package/index.js +23 -0
- package/package.json +8 -2
- package/src/admin/endpointRegistry.js +120 -0
- package/src/controllers/admin.controller.js +90 -6
- package/src/controllers/adminBlockDefinitions.controller.js +127 -0
- package/src/controllers/adminBlockDefinitionsAi.controller.js +54 -0
- package/src/controllers/adminCache.controller.js +342 -0
- package/src/controllers/adminContextBlockDefinitions.controller.js +141 -0
- package/src/controllers/adminCrons.controller.js +388 -0
- package/src/controllers/adminDbBrowser.controller.js +124 -0
- package/src/controllers/adminEjsVirtual.controller.js +13 -3
- package/src/controllers/adminExperiments.controller.js +200 -0
- package/src/controllers/adminHeadless.controller.js +9 -2
- package/src/controllers/adminHealthChecks.controller.js +570 -0
- package/src/controllers/adminI18n.controller.js +51 -29
- package/src/controllers/adminLlm.controller.js +126 -2
- package/src/controllers/adminPages.controller.js +720 -0
- package/src/controllers/adminPagesContextBlocksAi.controller.js +54 -0
- package/src/controllers/adminProxy.controller.js +113 -0
- package/src/controllers/adminRateLimits.controller.js +138 -0
- package/src/controllers/adminRbac.controller.js +803 -0
- package/src/controllers/adminScripts.controller.js +126 -4
- package/src/controllers/adminSeoConfig.controller.js +71 -48
- package/src/controllers/blogAdmin.controller.js +279 -0
- package/src/controllers/blogAiAdmin.controller.js +224 -0
- package/src/controllers/blogAutomationAdmin.controller.js +141 -0
- package/src/controllers/blogInternal.controller.js +26 -0
- package/src/controllers/blogPublic.controller.js +89 -0
- package/src/controllers/experiments.controller.js +85 -0
- package/src/controllers/fileManager.controller.js +190 -0
- package/src/controllers/fileManagerStoragePolicy.controller.js +23 -0
- package/src/controllers/healthChecksPublic.controller.js +196 -0
- package/src/controllers/internalExperiments.controller.js +17 -0
- package/src/controllers/metrics.controller.js +64 -4
- package/src/controllers/orgAdmin.controller.js +80 -0
- package/src/helpers/mongooseHelper.js +258 -0
- package/src/helpers/scriptBase.js +230 -0
- package/src/helpers/scriptRunner.js +335 -0
- package/src/middleware/rbac.js +62 -0
- package/src/middleware.js +810 -48
- package/src/models/BlockDefinition.js +27 -0
- package/src/models/BlogAutomationLock.js +14 -0
- package/src/models/BlogAutomationRun.js +39 -0
- package/src/models/BlogPost.js +42 -0
- package/src/models/CacheEntry.js +26 -0
- package/src/models/ConsoleEntry.js +32 -0
- package/src/models/ConsoleLog.js +23 -0
- package/src/models/ContextBlockDefinition.js +33 -0
- package/src/models/CronExecution.js +47 -0
- package/src/models/CronJob.js +70 -0
- package/src/models/Experiment.js +75 -0
- package/src/models/ExperimentAssignment.js +23 -0
- package/src/models/ExperimentEvent.js +26 -0
- package/src/models/ExperimentMetricBucket.js +30 -0
- package/src/models/ExternalDbConnection.js +49 -0
- package/src/models/FileEntry.js +22 -0
- package/src/models/GlobalSetting.js +1 -2
- package/src/models/HealthAutoHealAttempt.js +57 -0
- package/src/models/HealthCheck.js +132 -0
- package/src/models/HealthCheckRun.js +51 -0
- package/src/models/HealthIncident.js +49 -0
- package/src/models/Page.js +95 -0
- package/src/models/PageCollection.js +42 -0
- package/src/models/ProxyEntry.js +66 -0
- package/src/models/RateLimitCounter.js +19 -0
- package/src/models/RateLimitMetricBucket.js +20 -0
- package/src/models/RbacGrant.js +25 -0
- package/src/models/RbacGroup.js +16 -0
- package/src/models/RbacGroupMember.js +13 -0
- package/src/models/RbacGroupRole.js +13 -0
- package/src/models/RbacRole.js +25 -0
- package/src/models/RbacUserRole.js +13 -0
- package/src/models/ScriptDefinition.js +1 -0
- package/src/models/Webhook.js +2 -0
- package/src/routes/admin.routes.js +2 -0
- package/src/routes/adminBlog.routes.js +21 -0
- package/src/routes/adminBlogAi.routes.js +16 -0
- package/src/routes/adminBlogAutomation.routes.js +27 -0
- package/src/routes/adminCache.routes.js +20 -0
- package/src/routes/adminConsoleManager.routes.js +302 -0
- package/src/routes/adminCrons.routes.js +25 -0
- package/src/routes/adminDbBrowser.routes.js +65 -0
- package/src/routes/adminEjsVirtual.routes.js +2 -1
- package/src/routes/adminExperiments.routes.js +29 -0
- package/src/routes/adminHeadless.routes.js +2 -1
- package/src/routes/adminHealthChecks.routes.js +28 -0
- package/src/routes/adminI18n.routes.js +4 -3
- package/src/routes/adminLlm.routes.js +4 -2
- package/src/routes/adminPages.routes.js +55 -0
- package/src/routes/adminProxy.routes.js +15 -0
- package/src/routes/adminRateLimits.routes.js +17 -0
- package/src/routes/adminRbac.routes.js +38 -0
- package/src/routes/adminSeoConfig.routes.js +5 -4
- package/src/routes/adminUiComponents.routes.js +2 -1
- package/src/routes/blogInternal.routes.js +14 -0
- package/src/routes/blogPublic.routes.js +9 -0
- package/src/routes/experiments.routes.js +30 -0
- package/src/routes/fileManager.routes.js +62 -0
- package/src/routes/fileManagerStoragePolicy.routes.js +9 -0
- package/src/routes/healthChecksPublic.routes.js +9 -0
- package/src/routes/internalExperiments.routes.js +15 -0
- package/src/routes/log.routes.js +43 -60
- package/src/routes/metrics.routes.js +4 -2
- package/src/routes/orgAdmin.routes.js +1 -0
- package/src/routes/pages.routes.js +123 -0
- package/src/routes/proxy.routes.js +46 -0
- package/src/routes/rbac.routes.js +47 -0
- package/src/routes/webhook.routes.js +2 -1
- package/src/routes/workflows.routes.js +4 -0
- package/src/services/blockDefinitionsAi.service.js +247 -0
- package/src/services/blog.service.js +99 -0
- package/src/services/blogAutomation.service.js +978 -0
- package/src/services/blogCronsBootstrap.service.js +185 -0
- package/src/services/blogPublishing.service.js +58 -0
- package/src/services/cacheLayer.service.js +696 -0
- package/src/services/consoleManager.service.js +738 -0
- package/src/services/consoleOverride.service.js +7 -1
- package/src/services/cronScheduler.service.js +350 -0
- package/src/services/dbBrowser.service.js +536 -0
- package/src/services/ejsVirtual.service.js +102 -32
- package/src/services/experiments.service.js +273 -0
- package/src/services/experimentsAggregation.service.js +308 -0
- package/src/services/experimentsCronsBootstrap.service.js +118 -0
- package/src/services/experimentsRetention.service.js +43 -0
- package/src/services/experimentsWs.service.js +134 -0
- package/src/services/fileManager.service.js +475 -0
- package/src/services/fileManagerStoragePolicy.service.js +285 -0
- package/src/services/globalSettings.service.js +15 -0
- package/src/services/healthChecks.service.js +650 -0
- package/src/services/healthChecksBootstrap.service.js +109 -0
- package/src/services/healthChecksScheduler.service.js +106 -0
- package/src/services/jsonConfigs.service.js +2 -2
- package/src/services/llmDefaults.service.js +190 -0
- package/src/services/migrationAssets/s3.js +2 -2
- package/src/services/pages.service.js +602 -0
- package/src/services/pagesContext.service.js +331 -0
- package/src/services/pagesContextBlocksAi.service.js +349 -0
- package/src/services/proxy.service.js +535 -0
- package/src/services/rateLimiter.service.js +623 -0
- package/src/services/rbac.service.js +212 -0
- package/src/services/scriptsRunner.service.js +215 -15
- package/src/services/uiComponentsAi.service.js +6 -19
- package/src/services/workflow.service.js +23 -8
- package/src/utils/orgRoles.js +14 -0
- package/src/utils/rbac/engine.js +60 -0
- package/src/utils/rbac/rightsRegistry.js +33 -0
- package/views/admin-blog-automation.ejs +877 -0
- package/views/admin-blog-edit.ejs +542 -0
- package/views/admin-blog.ejs +399 -0
- package/views/admin-cache.ejs +681 -0
- package/views/admin-console-manager.ejs +680 -0
- package/views/admin-crons.ejs +645 -0
- package/views/admin-dashboard.ejs +28 -8
- package/views/admin-db-browser.ejs +445 -0
- package/views/admin-ejs-virtual.ejs +16 -10
- package/views/admin-experiments.ejs +91 -0
- package/views/admin-file-manager.ejs +942 -0
- package/views/admin-health-checks.ejs +725 -0
- package/views/admin-i18n.ejs +59 -5
- package/views/admin-llm.ejs +99 -1
- package/views/admin-organizations.ejs +163 -1
- package/views/admin-pages.ejs +2424 -0
- package/views/admin-proxy.ejs +491 -0
- package/views/admin-rate-limiter.ejs +625 -0
- package/views/admin-rbac.ejs +1331 -0
- package/views/admin-scripts.ejs +597 -3
- package/views/admin-seo-config.ejs +61 -7
- package/views/admin-ui-components.ejs +57 -25
- package/views/admin-workflows.ejs +7 -7
- package/views/file-manager.ejs +866 -0
- package/views/pages/blocks/contact.ejs +27 -0
- package/views/pages/blocks/cta.ejs +18 -0
- package/views/pages/blocks/faq.ejs +20 -0
- package/views/pages/blocks/features.ejs +19 -0
- package/views/pages/blocks/hero.ejs +13 -0
- package/views/pages/blocks/html.ejs +5 -0
- package/views/pages/blocks/image.ejs +14 -0
- package/views/pages/blocks/testimonials.ejs +26 -0
- package/views/pages/blocks/text.ejs +10 -0
- package/views/pages/layouts/default.ejs +51 -0
- package/views/pages/layouts/minimal.ejs +42 -0
- package/views/pages/layouts/sidebar.ejs +54 -0
- package/views/pages/partials/footer.ejs +13 -0
- package/views/pages/partials/header.ejs +12 -0
- package/views/pages/partials/sidebar.ejs +8 -0
- package/views/pages/runtime/page.ejs +10 -0
- package/views/pages/templates/article.ejs +20 -0
- package/views/pages/templates/default.ejs +12 -0
- package/views/pages/templates/landing.ejs +14 -0
- package/views/pages/templates/listing.ejs +15 -0
- package/views/partials/admin-image-upload-modal.ejs +221 -0
- package/views/partials/dashboard/nav-items.ejs +12 -0
- package/views/partials/dashboard/palette.ejs +5 -3
- package/views/partials/llm-provider-model-picker.ejs +183 -0
- package/src/routes/llmUi.routes.js +0 -26
|
@@ -102,6 +102,15 @@
|
|
|
102
102
|
<label class="block text-sm font-medium">Route path</label>
|
|
103
103
|
<input id="seo-ai-route" class="mt-1 w-full border rounded px-3 py-2 text-sm" placeholder="/marketplace" />
|
|
104
104
|
</div>
|
|
105
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-2">
|
|
106
|
+
<%- include('partials/llm-provider-model-picker', {
|
|
107
|
+
providerInputId: 'seoAiProviderKey',
|
|
108
|
+
modelInputId: 'seoAiModel',
|
|
109
|
+
providerLabel: 'Provider',
|
|
110
|
+
modelLabel: 'Model',
|
|
111
|
+
showOpenRouterFetch: true,
|
|
112
|
+
}) %>
|
|
113
|
+
</div>
|
|
105
114
|
<div class="flex gap-2">
|
|
106
115
|
<button class="px-3 py-2 bg-purple-600 text-white rounded hover:bg-purple-700 text-sm" onclick="seoAiGenerateFromView()">Generate entry</button>
|
|
107
116
|
<button class="px-3 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 text-sm" onclick="seoAiApplyProposed()">Apply</button>
|
|
@@ -124,6 +133,15 @@
|
|
|
124
133
|
<label class="block text-sm font-medium">Instruction</label>
|
|
125
134
|
<input id="seo-ai-instruction" class="mt-1 w-full border rounded px-3 py-2 text-sm" placeholder="Improve description including desktop apps" />
|
|
126
135
|
</div>
|
|
136
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-2">
|
|
137
|
+
<%- include('partials/llm-provider-model-picker', {
|
|
138
|
+
providerInputId: 'seoAiImproveProviderKey',
|
|
139
|
+
modelInputId: 'seoAiImproveModel',
|
|
140
|
+
providerLabel: 'Provider',
|
|
141
|
+
modelLabel: 'Model',
|
|
142
|
+
showOpenRouterFetch: true,
|
|
143
|
+
}) %>
|
|
144
|
+
</div>
|
|
127
145
|
<div class="flex gap-2">
|
|
128
146
|
<label class="inline-flex items-center gap-2 text-sm text-gray-700">
|
|
129
147
|
<input id="seo-ai-robots-noindex" type="checkbox" class="rounded border-gray-300" />
|
|
@@ -219,9 +237,16 @@
|
|
|
219
237
|
</div>
|
|
220
238
|
|
|
221
239
|
<div class="mb-3">
|
|
222
|
-
<
|
|
223
|
-
|
|
224
|
-
|
|
240
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-2">
|
|
241
|
+
<%- include('partials/llm-provider-model-picker', {
|
|
242
|
+
providerInputId: 'ogAiProviderKey',
|
|
243
|
+
modelInputId: 'ai-model',
|
|
244
|
+
providerLabel: 'Provider',
|
|
245
|
+
modelLabel: 'Model (optional override)',
|
|
246
|
+
showOpenRouterFetch: true,
|
|
247
|
+
}) %>
|
|
248
|
+
</div>
|
|
249
|
+
<div class="mt-1 text-xs text-gray-600">Legacy OpenRouter keys (<code>seoconfig.ai.openrouter.apiKey</code> / <code>ai.openrouter.apiKey</code>) are still supported when provider is <code>openrouter</code>.</div>
|
|
225
250
|
</div>
|
|
226
251
|
|
|
227
252
|
<div class="flex justify-end gap-2">
|
|
@@ -233,7 +258,15 @@
|
|
|
233
258
|
</div>
|
|
234
259
|
|
|
235
260
|
<script>
|
|
236
|
-
const API_BASE = window.location.origin + "<%= baseUrl %>";
|
|
261
|
+
const API_BASE = window.location.origin + "<%= baseUrl || '' %>";
|
|
262
|
+
|
|
263
|
+
function initLlmPickers() {
|
|
264
|
+
if (!window.__llmProviderModelPicker || !window.__llmProviderModelPicker.init) return;
|
|
265
|
+
|
|
266
|
+
window.__llmProviderModelPicker.init({ apiBase: API_BASE, providerInputId: 'seoAiProviderKey', modelInputId: 'seoAiModel' });
|
|
267
|
+
window.__llmProviderModelPicker.init({ apiBase: API_BASE, providerInputId: 'seoAiImproveProviderKey', modelInputId: 'seoAiImproveModel' });
|
|
268
|
+
window.__llmProviderModelPicker.init({ apiBase: API_BASE, providerInputId: 'ogAiProviderKey', modelInputId: 'ai-model' });
|
|
269
|
+
}
|
|
237
270
|
|
|
238
271
|
function showToast(message, type = 'success') {
|
|
239
272
|
const container = document.getElementById('toast-container');
|
|
@@ -409,10 +442,17 @@
|
|
|
409
442
|
try {
|
|
410
443
|
const viewPath = document.getElementById('seo-ai-view').value;
|
|
411
444
|
const routePath = document.getElementById('seo-ai-route').value;
|
|
445
|
+
const providerKey = document.getElementById('seoAiProviderKey')?.value;
|
|
446
|
+
const model = document.getElementById('seoAiModel')?.value;
|
|
412
447
|
const res = await fetch(`${API_BASE}/api/admin/seo-config/ai/generate-entry`, {
|
|
413
448
|
method: 'POST',
|
|
414
449
|
headers: { 'Content-Type': 'application/json' },
|
|
415
|
-
body: JSON.stringify({
|
|
450
|
+
body: JSON.stringify({
|
|
451
|
+
viewPath,
|
|
452
|
+
routePath,
|
|
453
|
+
providerKey: providerKey ? String(providerKey).trim() : undefined,
|
|
454
|
+
model: model ? String(model).trim() : undefined,
|
|
455
|
+
}),
|
|
416
456
|
});
|
|
417
457
|
const data = await res.json().catch(() => ({}));
|
|
418
458
|
if (!res.ok) throw new Error(data?.error || 'Failed to generate entry');
|
|
@@ -432,10 +472,17 @@
|
|
|
432
472
|
try {
|
|
433
473
|
const routePath = document.getElementById('seo-ai-existing-route').value;
|
|
434
474
|
const instruction = document.getElementById('seo-ai-instruction').value;
|
|
475
|
+
const providerKey = document.getElementById('seoAiImproveProviderKey')?.value;
|
|
476
|
+
const model = document.getElementById('seoAiImproveModel')?.value;
|
|
435
477
|
const res = await fetch(`${API_BASE}/api/admin/seo-config/ai/improve-entry`, {
|
|
436
478
|
method: 'POST',
|
|
437
479
|
headers: { 'Content-Type': 'application/json' },
|
|
438
|
-
body: JSON.stringify({
|
|
480
|
+
body: JSON.stringify({
|
|
481
|
+
routePath,
|
|
482
|
+
instruction,
|
|
483
|
+
providerKey: providerKey ? String(providerKey).trim() : undefined,
|
|
484
|
+
model: model ? String(model).trim() : undefined,
|
|
485
|
+
}),
|
|
439
486
|
});
|
|
440
487
|
const data = await res.json().catch(() => ({}));
|
|
441
488
|
if (!res.ok) throw new Error(data?.error || 'Failed to improve entry');
|
|
@@ -617,6 +664,7 @@
|
|
|
617
664
|
document.getElementById('ai-instruction').value = '';
|
|
618
665
|
document.getElementById('ai-model').value = '';
|
|
619
666
|
document.getElementById('ai-modal').classList.remove('hidden');
|
|
667
|
+
initLlmPickers();
|
|
620
668
|
}
|
|
621
669
|
|
|
622
670
|
function closeAiModal() {
|
|
@@ -634,11 +682,17 @@
|
|
|
634
682
|
const svgRaw = document.getElementById('og-svg-raw').value;
|
|
635
683
|
const instruction = document.getElementById('ai-instruction').value;
|
|
636
684
|
const model = document.getElementById('ai-model').value;
|
|
685
|
+
const providerKey = document.getElementById('ogAiProviderKey')?.value;
|
|
637
686
|
|
|
638
687
|
const res = await fetch(`${API_BASE}/api/admin/seo-config/ai/edit-svg`, {
|
|
639
688
|
method: 'POST',
|
|
640
689
|
headers: { 'Content-Type': 'application/json' },
|
|
641
|
-
body: JSON.stringify({
|
|
690
|
+
body: JSON.stringify({
|
|
691
|
+
svgRaw,
|
|
692
|
+
instruction,
|
|
693
|
+
providerKey: providerKey ? String(providerKey).trim() : undefined,
|
|
694
|
+
model: model || undefined,
|
|
695
|
+
}),
|
|
642
696
|
});
|
|
643
697
|
const data = await res.json().catch(() => ({}));
|
|
644
698
|
if (!res.ok) throw new Error(data?.error || 'AI failed');
|
|
@@ -207,17 +207,13 @@ curl "{{ baseUrl }}/api/ui-components/manifest/prj_yourproject" \
|
|
|
207
207
|
</div>
|
|
208
208
|
|
|
209
209
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-2">
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
<div>
|
|
218
|
-
<label class="block text-xs font-medium text-gray-600 mb-1">Model</label>
|
|
219
|
-
<input v-model="ai.model" class="w-full border rounded px-2 py-2 text-sm" placeholder="(default)" />
|
|
220
|
-
</div>
|
|
210
|
+
<%- include('partials/llm-provider-model-picker', {
|
|
211
|
+
providerInputId: 'uiComponentsAiProviderKey',
|
|
212
|
+
modelInputId: 'uiComponentsAiModel',
|
|
213
|
+
providerLabel: 'Provider',
|
|
214
|
+
modelLabel: 'Model',
|
|
215
|
+
showOpenRouterFetch: true,
|
|
216
|
+
}) %>
|
|
221
217
|
</div>
|
|
222
218
|
|
|
223
219
|
<div>
|
|
@@ -346,6 +342,7 @@ curl "{{ baseUrl }}/api/ui-components/manifest/prj_yourproject" \
|
|
|
346
342
|
setup() {
|
|
347
343
|
const baseUrl = '<%= baseUrl %>';
|
|
348
344
|
const adminPath = '<%= adminPath %>';
|
|
345
|
+
const API_BASE = window.location.origin + baseUrl;
|
|
349
346
|
|
|
350
347
|
const STORAGE_KEYS = {
|
|
351
348
|
providerKey: 'uiComponents.ai.providerKey',
|
|
@@ -393,7 +390,6 @@ curl "{{ baseUrl }}/api/ui-components/manifest/prj_yourproject" \
|
|
|
393
390
|
const assignments = ref([]);
|
|
394
391
|
const lastGeneratedKey = ref('');
|
|
395
392
|
|
|
396
|
-
const llmProviderKeys = ref([]);
|
|
397
393
|
const ai = ref({
|
|
398
394
|
providerKey: localStorage.getItem(STORAGE_KEYS.providerKey) || '',
|
|
399
395
|
model: localStorage.getItem(STORAGE_KEYS.model) || '',
|
|
@@ -428,11 +424,56 @@ curl "{{ baseUrl }}/api/ui-components/manifest/prj_yourproject" \
|
|
|
428
424
|
persistHelpState();
|
|
429
425
|
}
|
|
430
426
|
|
|
427
|
+
function syncAiPickerToVue() {
|
|
428
|
+
const providerEl = document.getElementById('uiComponentsAiProviderKey');
|
|
429
|
+
const modelEl = document.getElementById('uiComponentsAiModel');
|
|
430
|
+
if (providerEl) providerEl.value = String(ai.value.providerKey || '');
|
|
431
|
+
if (modelEl) modelEl.value = String(ai.value.model || '');
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function wireAiPickerListeners() {
|
|
435
|
+
const providerEl = document.getElementById('uiComponentsAiProviderKey');
|
|
436
|
+
const modelEl = document.getElementById('uiComponentsAiModel');
|
|
437
|
+
if (!providerEl || !modelEl) return;
|
|
438
|
+
|
|
439
|
+
if (providerEl.dataset.wired === '1') return;
|
|
440
|
+
providerEl.dataset.wired = '1';
|
|
441
|
+
modelEl.dataset.wired = '1';
|
|
442
|
+
|
|
443
|
+
providerEl.addEventListener('input', () => {
|
|
444
|
+
ai.value.providerKey = String(providerEl.value || '');
|
|
445
|
+
persistAiSettings();
|
|
446
|
+
});
|
|
447
|
+
providerEl.addEventListener('change', () => {
|
|
448
|
+
ai.value.providerKey = String(providerEl.value || '');
|
|
449
|
+
persistAiSettings();
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
modelEl.addEventListener('input', () => {
|
|
453
|
+
ai.value.model = String(modelEl.value || '');
|
|
454
|
+
persistAiSettings();
|
|
455
|
+
});
|
|
456
|
+
modelEl.addEventListener('change', () => {
|
|
457
|
+
ai.value.model = String(modelEl.value || '');
|
|
458
|
+
persistAiSettings();
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
async function initAiPicker() {
|
|
463
|
+
if (!window.__llmProviderModelPicker || !window.__llmProviderModelPicker.init) return;
|
|
464
|
+
await window.__llmProviderModelPicker.init({
|
|
465
|
+
apiBase: API_BASE,
|
|
466
|
+
providerInputId: 'uiComponentsAiProviderKey',
|
|
467
|
+
modelInputId: 'uiComponentsAiModel',
|
|
468
|
+
});
|
|
469
|
+
syncAiPickerToVue();
|
|
470
|
+
wireAiPickerListeners();
|
|
471
|
+
}
|
|
472
|
+
|
|
431
473
|
async function loadLlmConfig() {
|
|
432
474
|
try {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
llmProviderKeys.value = Object.keys(providers || {}).sort();
|
|
475
|
+
await initAiPicker();
|
|
476
|
+
showToast('LLM config reloaded', 'success');
|
|
436
477
|
} catch (e) {
|
|
437
478
|
showToast(e.message, 'error');
|
|
438
479
|
}
|
|
@@ -658,13 +699,8 @@ curl "{{ baseUrl }}/api/ui-components/manifest/prj_yourproject" \
|
|
|
658
699
|
onMounted(async () => {
|
|
659
700
|
try {
|
|
660
701
|
loadHelpState();
|
|
661
|
-
await loadLlmConfig();
|
|
662
702
|
await refreshAll();
|
|
663
|
-
|
|
664
|
-
const snippetEl = document.querySelector('.autoInjectSnippet code');
|
|
665
|
-
if (snippetEl) {
|
|
666
|
-
snippetEl.innerHTML = snippetEl.innerHTML.split('__ORIGIN__').join(window.location.origin);
|
|
667
|
-
}
|
|
703
|
+
await initAiPicker();
|
|
668
704
|
} catch (e) {
|
|
669
705
|
showToast(e.message, 'error');
|
|
670
706
|
}
|
|
@@ -683,14 +719,10 @@ curl "{{ baseUrl }}/api/ui-components/manifest/prj_yourproject" \
|
|
|
683
719
|
newProject,
|
|
684
720
|
componentEditor,
|
|
685
721
|
toggleHelp,
|
|
686
|
-
llmProviderKeys,
|
|
687
722
|
ai,
|
|
688
723
|
aiLoading,
|
|
689
724
|
aiProposal,
|
|
690
725
|
aiWarnings,
|
|
691
|
-
loadLlmConfig,
|
|
692
|
-
aiPropose,
|
|
693
|
-
aiApply,
|
|
694
726
|
refreshAll,
|
|
695
727
|
createProject,
|
|
696
728
|
selectProject,
|
|
@@ -220,11 +220,11 @@
|
|
|
220
220
|
this.loadWorkflows().then(() => {
|
|
221
221
|
if (workflowIdFromUrl !== 'new' && workflowIdFromUrl.length > 5) {
|
|
222
222
|
const wf = this.workflows.find(w => w._id === workflowIdFromUrl);
|
|
223
|
-
if (wf) this.editWorkflow(wf); else fetch(`/saas/api/workflows/${workflowIdFromUrl}`).then(res => res.ok && res.json().then(data => this.editWorkflow(data)));
|
|
223
|
+
if (wf) this.editWorkflow(wf); else fetch(`/saas/api/admin/workflows/${workflowIdFromUrl}`).then(res => res.ok && res.json().then(data => this.editWorkflow(data)));
|
|
224
224
|
} else if (workflowIdFromUrl === 'new') this.createNew();
|
|
225
225
|
});
|
|
226
226
|
},
|
|
227
|
-
async loadWorkflows() { const res = await fetch('/saas/api/workflows'); this.workflows = await res.json(); },
|
|
227
|
+
async loadWorkflows() { const res = await fetch('/saas/api/admin/workflows'); this.workflows = await res.json(); },
|
|
228
228
|
createNew() { this.workflow = { name: 'New Workflow', status: 'inactive', entrypoint: { awaitResponse: false, allowedMethods: ['POST', 'GET'], auth: { type: 'none' } }, testDataset: { method: 'POST', body: { message: 'Hello' }, query: {}, headers: {} }, nodes: [] }; this.view = 'editor'; },
|
|
229
229
|
editWorkflow(wf) {
|
|
230
230
|
const currentView = this.view; this.workflow = JSON.parse(JSON.stringify(wf)); this.showRuns = false; this.runs = []; this.inspectedRun = null;
|
|
@@ -233,7 +233,7 @@
|
|
|
233
233
|
this.view = (currentView === 'list') ? 'editor' : currentView;
|
|
234
234
|
},
|
|
235
235
|
toggleMethod(m) { this.workflow.entrypoint.allowedMethods = this.workflow.entrypoint.allowedMethods || []; const idx = this.workflow.entrypoint.allowedMethods.indexOf(m); if (idx > -1) this.workflow.entrypoint.allowedMethods.splice(idx, 1); else this.workflow.entrypoint.allowedMethods.push(m); },
|
|
236
|
-
async deleteWorkflow(id) { if (confirm('Are you sure?')) { await fetch(`/saas/api/workflows/${id}`, { method: 'DELETE' }); await this.loadWorkflows(); } },
|
|
236
|
+
async deleteWorkflow(id) { if (confirm('Are you sure?')) { await fetch(`/saas/api/admin/workflows/${id}`, { method: 'DELETE' }); await this.loadWorkflows(); } },
|
|
237
237
|
getWebhookUrl() { return window.location.origin + '/saas/w/' + (this.workflow._id || 'NEW'); },
|
|
238
238
|
copyWebhookUrl() { navigator.clipboard.writeText(this.getWebhookUrl()); this.showToast('Copied!'); },
|
|
239
239
|
addNode(type) { this.workflow.nodes.push({ id: 'node_' + Date.now(), type, name: 'node_' + (this.workflow.nodes.length + 1) }); },
|
|
@@ -247,7 +247,7 @@
|
|
|
247
247
|
getNodeIcon(type) { return { llm: 'ti-brain', if: 'ti-git-branch', http: 'ti-world', exit: 'ti-door-exit', parallel: 'ti-git-merge' }[type] || 'ti-circle'; },
|
|
248
248
|
getNodeColor(type) { return { llm: 'bg-indigo-500', if: 'bg-yellow-500', http: 'bg-blue-500', exit: 'bg-red-500', parallel: 'bg-purple-500' }[type]; },
|
|
249
249
|
async saveWorkflow(silent = false) {
|
|
250
|
-
const res = await fetch(this.workflow._id ? `/saas/api/workflows/${this.workflow._id}` : '/saas/api/workflows', { method: this.workflow._id ? 'PUT' : 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(this.workflow) });
|
|
250
|
+
const res = await fetch(this.workflow._id ? `/saas/api/admin/workflows/${this.workflow._id}` : '/saas/api/admin/workflows', { method: this.workflow._id ? 'PUT' : 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(this.workflow) });
|
|
251
251
|
if (res.ok) { const saved = await res.json(); if (!silent) this.showToast('Saved!'); await this.loadWorkflows(); this.editWorkflow(saved); }
|
|
252
252
|
},
|
|
253
253
|
async runFullTest(entrypointOnly = false) {
|
|
@@ -255,7 +255,7 @@
|
|
|
255
255
|
try {
|
|
256
256
|
const payload = { method: this.testConfig.method, body: JSON.parse(this.testConfig.body), query: JSON.parse(this.testConfig.query), headers: JSON.parse(this.testConfig.headers) };
|
|
257
257
|
if (entrypointOnly) { this.workflow.testDataset = payload; await this.saveWorkflow(true); return this.showToast('Dataset set.'); }
|
|
258
|
-
this.testing = true; const res = await fetch(`/saas/api/workflows/${this.workflow._id}/test`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
|
|
258
|
+
this.testing = true; const res = await fetch(`/saas/api/admin/workflows/${this.workflow._id}/test`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
|
|
259
259
|
this.testResult = await res.json(); if (this.showRuns) this.loadRuns(); this.showToast('Ran.');
|
|
260
260
|
} catch (e) { this.showToast('Failed', 'error'); } finally { this.testing = false; }
|
|
261
261
|
},
|
|
@@ -263,11 +263,11 @@
|
|
|
263
263
|
async testIsolatedNode(node) {
|
|
264
264
|
const context = { entrypoint: this.workflow.testDataset, payload: this.workflow.testDataset, lastNode: this.workflow.testDataset };
|
|
265
265
|
try {
|
|
266
|
-
const res = await fetch(`/saas/api/workflows/${this.workflow._id}/nodes/${node.id}/test`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ context, node }) });
|
|
266
|
+
const res = await fetch(`/saas/api/admin/workflows/${this.workflow._id}/nodes/${node.id}/test`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ context, node }) });
|
|
267
267
|
const data = await res.json(); node.testOutput = data.result; node.testResult = data.result; this.showToast('Tested');
|
|
268
268
|
} catch (e) { this.showToast('Failed', 'error'); }
|
|
269
269
|
},
|
|
270
|
-
async loadRuns() { if (!this.workflow._id) return; const res = await fetch(`/saas/api/workflows/${this.workflow._id}/runs`); this.runs = await res.json(); },
|
|
270
|
+
async loadRuns() { if (!this.workflow._id) return; const res = await fetch(`/saas/api/admin/workflows/${this.workflow._id}/runs`); this.runs = await res.json(); },
|
|
271
271
|
inspectRun(run) { this.inspectedRun = run; }, testWorkflow() { this.showTestPanel = true; },
|
|
272
272
|
showToast(message, type = 'success') {
|
|
273
273
|
const id = Date.now(); this.toasts.push({ id, message, type, show: true });
|