@intranefr/superbackend 1.4.4 → 1.5.1
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 +5 -0
- package/README.md +11 -0
- package/index.js +39 -1
- package/package.json +11 -3
- package/public/sdk/ui-components.iife.js +191 -0
- package/sdk/ui-components/browser/src/index.js +228 -0
- package/src/admin/endpointRegistry.js +120 -0
- package/src/controllers/admin.controller.js +111 -5
- 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/adminHeadless.controller.js +91 -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 +320 -0
- package/src/controllers/adminSeoConfig.controller.js +71 -48
- package/src/controllers/adminTerminals.controller.js +39 -0
- package/src/controllers/adminUiComponents.controller.js +315 -0
- package/src/controllers/adminUiComponentsAi.controller.js +34 -0
- 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/fileManager.controller.js +190 -0
- package/src/controllers/fileManagerStoragePolicy.controller.js +23 -0
- package/src/controllers/healthChecksPublic.controller.js +196 -0
- package/src/controllers/metrics.controller.js +64 -4
- package/src/controllers/orgAdmin.controller.js +366 -0
- package/src/controllers/uiComponentsPublic.controller.js +118 -0
- package/src/middleware/auth.js +7 -0
- package/src/middleware/internalCronAuth.js +29 -0
- package/src/middleware/rbac.js +62 -0
- package/src/middleware.js +879 -56
- 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/ExternalDbConnection.js +49 -0
- package/src/models/FileEntry.js +22 -0
- package/src/models/HeadlessModelDefinition.js +10 -0
- 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 +42 -0
- package/src/models/ScriptRun.js +22 -0
- package/src/models/UiComponent.js +29 -0
- package/src/models/UiComponentProject.js +26 -0
- package/src/models/UiComponentProjectComponent.js +18 -0
- package/src/routes/admin.routes.js +1 -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/adminHeadless.routes.js +8 -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/adminScripts.routes.js +21 -0
- package/src/routes/adminSeoConfig.routes.js +5 -4
- package/src/routes/adminTerminals.routes.js +13 -0
- package/src/routes/adminUiComponents.routes.js +30 -0
- package/src/routes/blogInternal.routes.js +14 -0
- package/src/routes/blogPublic.routes.js +9 -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/log.routes.js +43 -60
- package/src/routes/metrics.routes.js +4 -2
- package/src/routes/orgAdmin.routes.js +6 -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/uiComponentsPublic.routes.js +9 -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 +184 -0
- package/src/services/blogPublishing.service.js +58 -0
- package/src/services/cacheLayer.service.js +696 -0
- package/src/services/consoleManager.service.js +700 -0
- package/src/services/consoleOverride.service.js +6 -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/fileManager.service.js +475 -0
- package/src/services/fileManagerStoragePolicy.service.js +285 -0
- package/src/services/headlessExternalModels.service.js +292 -0
- package/src/services/headlessModels.service.js +26 -6
- 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/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 +259 -0
- package/src/services/terminals.service.js +152 -0
- package/src/services/terminalsWs.service.js +100 -0
- package/src/services/uiComponentsAi.service.js +299 -0
- package/src/services/uiComponentsCrypto.service.js +39 -0
- 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 +29 -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-db-browser.ejs +445 -0
- package/views/admin-ejs-virtual.ejs +16 -10
- package/views/admin-file-manager.ejs +942 -0
- package/views/admin-headless.ejs +294 -24
- 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 +528 -10
- 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 +497 -0
- package/views/admin-seo-config.ejs +61 -7
- package/views/admin-terminals.ejs +328 -0
- package/views/admin-ui-components.ejs +741 -0
- package/views/admin-users.ejs +261 -4
- 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 +14 -0
- package/views/partials/llm-provider-model-picker.ejs +183 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
|
|
4
|
+
const { basicAuth } = require('../middleware/auth');
|
|
5
|
+
const controller = require('../controllers/adminRbac.controller');
|
|
6
|
+
|
|
7
|
+
router.use(basicAuth);
|
|
8
|
+
|
|
9
|
+
router.get('/rights', controller.listRights);
|
|
10
|
+
router.get('/users', controller.searchUsers);
|
|
11
|
+
router.get('/users/:userId/orgs', controller.getUserOrgs);
|
|
12
|
+
router.post('/test', controller.testRight);
|
|
13
|
+
|
|
14
|
+
router.get('/roles', controller.listRoles);
|
|
15
|
+
router.post('/roles', controller.createRole);
|
|
16
|
+
router.patch('/roles/:id', controller.updateRole);
|
|
17
|
+
|
|
18
|
+
router.get('/groups', controller.listGroups);
|
|
19
|
+
router.post('/groups', controller.createGroup);
|
|
20
|
+
router.patch('/groups/:id', controller.updateGroup);
|
|
21
|
+
router.get('/groups/:id/members', controller.listGroupMembers);
|
|
22
|
+
router.post('/groups/:id/members', controller.addGroupMember);
|
|
23
|
+
router.post('/groups/:id/members/bulk', controller.addGroupMembersBulk);
|
|
24
|
+
router.delete('/groups/:id/members/:memberId', controller.removeGroupMember);
|
|
25
|
+
router.post('/groups/:id/members/bulk-remove', controller.removeGroupMembersBulk);
|
|
26
|
+
router.get('/groups/:id/roles', controller.listGroupRoles);
|
|
27
|
+
router.post('/groups/:id/roles', controller.addGroupRole);
|
|
28
|
+
router.delete('/groups/:id/roles/:groupRoleId', controller.removeGroupRole);
|
|
29
|
+
|
|
30
|
+
router.get('/grants', controller.listGrants);
|
|
31
|
+
router.post('/grants', controller.createGrant);
|
|
32
|
+
router.delete('/grants/:id', controller.deleteGrant);
|
|
33
|
+
|
|
34
|
+
router.get('/users/:userId/roles', controller.listUserRoles);
|
|
35
|
+
router.post('/users/:userId/roles', controller.addUserRole);
|
|
36
|
+
router.delete('/users/:userId/roles/:userRoleId', controller.removeUserRole);
|
|
37
|
+
|
|
38
|
+
module.exports = router;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
|
|
4
|
+
const { basicAuth } = require('../middleware/auth');
|
|
5
|
+
const controller = require('../controllers/adminScripts.controller');
|
|
6
|
+
|
|
7
|
+
router.use(basicAuth);
|
|
8
|
+
|
|
9
|
+
router.get('/', controller.listScripts);
|
|
10
|
+
router.post('/', controller.createScript);
|
|
11
|
+
router.get('/runs', controller.listRuns);
|
|
12
|
+
router.get('/runs/:runId', controller.getRun);
|
|
13
|
+
router.get('/runs/:runId/stream', controller.streamRun);
|
|
14
|
+
|
|
15
|
+
router.get('/:id', controller.getScript);
|
|
16
|
+
router.put('/:id', controller.updateScript);
|
|
17
|
+
router.delete('/:id', controller.deleteScript);
|
|
18
|
+
|
|
19
|
+
router.post('/:id/run', controller.runScript);
|
|
20
|
+
|
|
21
|
+
module.exports = router;
|
|
@@ -3,18 +3,19 @@ const router = express.Router();
|
|
|
3
3
|
|
|
4
4
|
const { basicAuth } = require('../middleware/auth');
|
|
5
5
|
const adminSeoConfigController = require('../controllers/adminSeoConfig.controller');
|
|
6
|
+
const rateLimiter = require('../services/rateLimiter.service');
|
|
6
7
|
|
|
7
8
|
router.get('/', basicAuth, adminSeoConfigController.get);
|
|
8
9
|
router.put('/', basicAuth, adminSeoConfigController.update);
|
|
9
10
|
|
|
10
11
|
// SEO Config helpers
|
|
11
12
|
router.get('/ai/views', basicAuth, adminSeoConfigController.seoConfigAiListViews);
|
|
12
|
-
router.post('/ai/generate-entry', basicAuth, adminSeoConfigController.seoConfigAiGenerateEntry);
|
|
13
|
-
router.post('/ai/improve-entry', basicAuth, adminSeoConfigController.seoConfigAiImproveEntry);
|
|
13
|
+
router.post('/ai/generate-entry', basicAuth, rateLimiter.limit('seoAiLimiter'), adminSeoConfigController.seoConfigAiGenerateEntry);
|
|
14
|
+
router.post('/ai/improve-entry', basicAuth, rateLimiter.limit('seoAiLimiter'), adminSeoConfigController.seoConfigAiImproveEntry);
|
|
14
15
|
router.post('/pages/apply-entry', basicAuth, adminSeoConfigController.seoConfigApplyEntry);
|
|
15
16
|
|
|
16
17
|
router.put('/og/svg', basicAuth, adminSeoConfigController.updateOgSvg);
|
|
17
|
-
router.post('/og/generate-png', basicAuth, adminSeoConfigController.generateOgPng);
|
|
18
|
-
router.post('/ai/edit-svg', basicAuth, adminSeoConfigController.aiEditSvg);
|
|
18
|
+
router.post('/og/generate-png', basicAuth, rateLimiter.limit('seoAiLimiter'), adminSeoConfigController.generateOgPng);
|
|
19
|
+
router.post('/ai/edit-svg', basicAuth, rateLimiter.limit('seoAiLimiter'), adminSeoConfigController.aiEditSvg);
|
|
19
20
|
|
|
20
21
|
module.exports = router;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
|
|
4
|
+
const { basicAuth } = require('../middleware/auth');
|
|
5
|
+
const controller = require('../controllers/adminTerminals.controller');
|
|
6
|
+
|
|
7
|
+
router.use(basicAuth);
|
|
8
|
+
|
|
9
|
+
router.post('/sessions', controller.createSession);
|
|
10
|
+
router.get('/sessions', controller.listSessions);
|
|
11
|
+
router.delete('/sessions/:sessionId', controller.killSession);
|
|
12
|
+
|
|
13
|
+
module.exports = router;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
|
|
4
|
+
const { basicAuth } = require('../middleware/auth');
|
|
5
|
+
const adminUiComponentsController = require('../controllers/adminUiComponents.controller');
|
|
6
|
+
const adminUiComponentsAiController = require('../controllers/adminUiComponentsAi.controller');
|
|
7
|
+
const rateLimiter = require('../services/rateLimiter.service');
|
|
8
|
+
|
|
9
|
+
router.use(basicAuth);
|
|
10
|
+
|
|
11
|
+
router.get('/projects', adminUiComponentsController.listProjects);
|
|
12
|
+
router.post('/projects', adminUiComponentsController.createProject);
|
|
13
|
+
router.get('/projects/:projectId', adminUiComponentsController.getProject);
|
|
14
|
+
router.put('/projects/:projectId', adminUiComponentsController.updateProject);
|
|
15
|
+
router.delete('/projects/:projectId', adminUiComponentsController.deleteProject);
|
|
16
|
+
router.post('/projects/:projectId/rotate-key', adminUiComponentsController.rotateProjectKey);
|
|
17
|
+
|
|
18
|
+
router.get('/components', adminUiComponentsController.listComponents);
|
|
19
|
+
router.post('/components', adminUiComponentsController.createComponent);
|
|
20
|
+
router.get('/components/:code', adminUiComponentsController.getComponent);
|
|
21
|
+
router.put('/components/:code', adminUiComponentsController.updateComponent);
|
|
22
|
+
router.delete('/components/:code', adminUiComponentsController.deleteComponent);
|
|
23
|
+
|
|
24
|
+
router.get('/projects/:projectId/components', adminUiComponentsController.listProjectAssignments);
|
|
25
|
+
router.post('/projects/:projectId/components/:code', adminUiComponentsController.setAssignment);
|
|
26
|
+
router.delete('/projects/:projectId/components/:code', adminUiComponentsController.deleteAssignment);
|
|
27
|
+
|
|
28
|
+
router.post('/ai/components/:code/propose', rateLimiter.limit('aiOperationsLimiter'), adminUiComponentsAiController.propose);
|
|
29
|
+
|
|
30
|
+
module.exports = router;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
|
|
4
|
+
const controller = require('../controllers/blogInternal.controller');
|
|
5
|
+
const { requireInternalCronToken } = require('../middleware/internalCronAuth');
|
|
6
|
+
const rateLimiter = require('../services/rateLimiter.service');
|
|
7
|
+
|
|
8
|
+
router.use(express.json({ limit: '1mb' }));
|
|
9
|
+
router.use(requireInternalCronToken);
|
|
10
|
+
|
|
11
|
+
router.post('/blog/automation/run', rateLimiter.limit('blogAiLimiter'), controller.runAutomation);
|
|
12
|
+
router.post('/blog/publish-scheduled/run', controller.publishScheduled);
|
|
13
|
+
|
|
14
|
+
module.exports = router;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
|
|
4
|
+
const controller = require('../controllers/blogPublic.controller');
|
|
5
|
+
|
|
6
|
+
router.get('/blog-posts', controller.listPublished);
|
|
7
|
+
router.get('/blog-posts/:slug', controller.getPublishedBySlug);
|
|
8
|
+
|
|
9
|
+
module.exports = router;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const multerModule = require('multer');
|
|
4
|
+
// `multer` is normally a callable function. In some test environments it can be mocked as a plain object.
|
|
5
|
+
const multerFactory = typeof multerModule === 'function' ? multerModule : () => multerModule;
|
|
6
|
+
const memoryStorage =
|
|
7
|
+
typeof multerModule?.memoryStorage === 'function'
|
|
8
|
+
? multerModule.memoryStorage
|
|
9
|
+
: () => ({});
|
|
10
|
+
|
|
11
|
+
const { authenticate } = require('../middleware/auth');
|
|
12
|
+
const { requireRight } = require('../middleware/rbac');
|
|
13
|
+
const controller = require('../controllers/fileManager.controller');
|
|
14
|
+
|
|
15
|
+
const fileManagerStoragePolicyService = require('../services/fileManagerStoragePolicy.service');
|
|
16
|
+
const storagePolicyRoutes = require('./fileManagerStoragePolicy.routes');
|
|
17
|
+
|
|
18
|
+
const dynamicUploadSingle = (fieldName) => async (req, res, next) => {
|
|
19
|
+
try {
|
|
20
|
+
const orgId = req.query.orgId || req.body?.orgId || req.headers['x-org-id'] || null;
|
|
21
|
+
const driveType = req.query.driveType || req.body?.driveType;
|
|
22
|
+
const driveId = req.query.driveId || req.body?.driveId;
|
|
23
|
+
|
|
24
|
+
const { maxUploadBytes } = await fileManagerStoragePolicyService.resolveEffectiveLimits({
|
|
25
|
+
userId: req.user._id,
|
|
26
|
+
orgId,
|
|
27
|
+
driveType,
|
|
28
|
+
driveId,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const fileSize = Number.isFinite(maxUploadBytes) && maxUploadBytes > 0 ? maxUploadBytes : 1073741824;
|
|
32
|
+
|
|
33
|
+
const upload = multerFactory({
|
|
34
|
+
storage: memoryStorage(),
|
|
35
|
+
limits: { fileSize },
|
|
36
|
+
}).single(fieldName);
|
|
37
|
+
|
|
38
|
+
upload(req, res, next);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
next(error);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
router.use(authenticate);
|
|
45
|
+
|
|
46
|
+
router.use(storagePolicyRoutes);
|
|
47
|
+
|
|
48
|
+
router.get('/drives', requireRight('file_manager:drives:read'), controller.listDrives);
|
|
49
|
+
router.get('/folders', requireRight('file_manager:files:read'), controller.listFolder);
|
|
50
|
+
|
|
51
|
+
router.post(
|
|
52
|
+
'/files/upload',
|
|
53
|
+
requireRight('file_manager:files:upload'),
|
|
54
|
+
dynamicUploadSingle('file'),
|
|
55
|
+
controller.upload
|
|
56
|
+
);
|
|
57
|
+
router.get('/files/:id/download', requireRight('file_manager:files:download'), controller.download);
|
|
58
|
+
router.patch('/files/:id', requireRight('file_manager:files:update'), controller.update);
|
|
59
|
+
router.delete('/files/:id', requireRight('file_manager:files:delete'), controller.delete);
|
|
60
|
+
router.post('/files/:id/share', requireRight('file_manager:files:share'), controller.setShare);
|
|
61
|
+
|
|
62
|
+
module.exports = router;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
|
|
4
|
+
const { requireRight } = require('../middleware/rbac');
|
|
5
|
+
const controller = require('../controllers/fileManagerStoragePolicy.controller');
|
|
6
|
+
|
|
7
|
+
router.get('/storage-policy', requireRight('file_manager:access'), controller.getStoragePolicy);
|
|
8
|
+
|
|
9
|
+
module.exports = router;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
|
|
4
|
+
const controller = require('../controllers/healthChecksPublic.controller');
|
|
5
|
+
|
|
6
|
+
router.get('/status', controller.getStatus);
|
|
7
|
+
router.get('/status/json', controller.getStatusJson);
|
|
8
|
+
|
|
9
|
+
module.exports = router;
|
package/src/routes/log.routes.js
CHANGED
|
@@ -1,66 +1,35 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
2
|
const router = express.Router();
|
|
3
3
|
const { logError, getConfig } = require('../services/errorLogger');
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
const rateLimitStore = new Map();
|
|
7
|
-
const CLEANUP_INTERVAL = 60000;
|
|
8
|
-
|
|
9
|
-
setInterval(() => {
|
|
10
|
-
const now = Date.now();
|
|
11
|
-
for (const [key, data] of rateLimitStore) {
|
|
12
|
-
if (now - data.windowStart > 60000) {
|
|
13
|
-
rateLimitStore.delete(key);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}, CLEANUP_INTERVAL);
|
|
17
|
-
|
|
18
|
-
async function checkRateLimit(ip, isAuthenticated) {
|
|
19
|
-
const config = await getConfig();
|
|
20
|
-
const limit = isAuthenticated ? config.errorRateLimitPerMinute : config.errorRateLimitAnonPerMinute;
|
|
21
|
-
const key = `${ip}:${isAuthenticated ? 'auth' : 'anon'}`;
|
|
22
|
-
const now = Date.now();
|
|
23
|
-
|
|
24
|
-
let data = rateLimitStore.get(key);
|
|
25
|
-
if (!data || now - data.windowStart > 60000) {
|
|
26
|
-
data = { windowStart: now, count: 0 };
|
|
27
|
-
rateLimitStore.set(key, data);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
data.count++;
|
|
31
|
-
|
|
32
|
-
if (data.count > limit) {
|
|
33
|
-
return { allowed: false, remaining: 0, limit };
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return { allowed: true, remaining: limit - data.count, limit };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function extractUserFromToken(req) {
|
|
40
|
-
try {
|
|
41
|
-
const authHeader = req.headers.authorization;
|
|
42
|
-
if (!authHeader || !authHeader.startsWith('Bearer ')) return null;
|
|
43
|
-
const token = authHeader.slice(7).trim();
|
|
44
|
-
if (!token) return null;
|
|
45
|
-
const decoded = verifyAccessToken(token);
|
|
46
|
-
return { userId: decoded.userId, role: decoded.role };
|
|
47
|
-
} catch (e) {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
4
|
+
const rateLimiter = require('../services/rateLimiter.service');
|
|
51
5
|
|
|
52
6
|
router.post('/error', async (req, res) => {
|
|
53
7
|
try {
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
const isAuthenticated =
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
8
|
+
// Determine if user is authenticated
|
|
9
|
+
const authHeader = req.headers.authorization;
|
|
10
|
+
const isAuthenticated = authHeader && authHeader.startsWith('Bearer ');
|
|
11
|
+
|
|
12
|
+
// Choose appropriate limiter based on authentication status
|
|
13
|
+
const limiterId = isAuthenticated ? 'errorReportingAuthLimiter' : 'errorReportingAnonLimiter';
|
|
14
|
+
|
|
15
|
+
// Perform rate limit check
|
|
16
|
+
let rateCheck;
|
|
17
|
+
try {
|
|
18
|
+
rateCheck = await rateLimiter.check(limiterId, { req });
|
|
19
|
+
|
|
20
|
+
// Set rate limit headers to match current behavior
|
|
21
|
+
res.setHeader('X-RateLimit-Limit', rateCheck.limit.max);
|
|
22
|
+
res.setHeader('X-RateLimit-Remaining', Math.max(0, rateCheck.remaining));
|
|
23
|
+
|
|
24
|
+
if (!rateCheck.allowed) {
|
|
25
|
+
return res.status(429).json({
|
|
26
|
+
error: 'Too many error reports. Please try again later.'
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
} catch (rateLimitError) {
|
|
30
|
+
// If rate limiter fails, allow the request (fail open behavior)
|
|
31
|
+
console.error('[RateLimiter] Error checking rate limit:', rateLimitError);
|
|
32
|
+
// Continue with error logging without rate limiting
|
|
64
33
|
}
|
|
65
34
|
|
|
66
35
|
const config = await getConfig();
|
|
@@ -69,6 +38,20 @@ router.post('/error', async (req, res) => {
|
|
|
69
38
|
}
|
|
70
39
|
|
|
71
40
|
const body = req.body || {};
|
|
41
|
+
|
|
42
|
+
// Extract user info for attribution (same as before)
|
|
43
|
+
let user = null;
|
|
44
|
+
if (isAuthenticated) {
|
|
45
|
+
try {
|
|
46
|
+
const token = authHeader.slice(7).trim();
|
|
47
|
+
const { verifyAccessToken } = require('../utils/jwt');
|
|
48
|
+
const decoded = verifyAccessToken(token);
|
|
49
|
+
user = { userId: decoded.userId, role: decoded.role };
|
|
50
|
+
} catch (e) {
|
|
51
|
+
// Invalid token, proceed as anonymous
|
|
52
|
+
console.error('[LogRoutes] Invalid token:', e.message);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
72
55
|
|
|
73
56
|
const event = {
|
|
74
57
|
source: 'frontend',
|
|
@@ -78,9 +61,9 @@ router.post('/error', async (req, res) => {
|
|
|
78
61
|
message: String(body.message || '').slice(0, 2000),
|
|
79
62
|
stack: String(body.stack || '').slice(0, 5000),
|
|
80
63
|
actor: {
|
|
81
|
-
userId: user?.userId,
|
|
82
|
-
role: user?.role,
|
|
83
|
-
ip,
|
|
64
|
+
userId: user?.userId || null,
|
|
65
|
+
role: user?.role || null,
|
|
66
|
+
ip: req.ip || req.headers['x-forwarded-for']?.split(',')[0]?.trim() || 'unknown',
|
|
84
67
|
userAgent: req.headers['user-agent'],
|
|
85
68
|
},
|
|
86
69
|
request: {
|
|
@@ -2,8 +2,10 @@ const express = require('express');
|
|
|
2
2
|
const router = express.Router();
|
|
3
3
|
const metricsController = require('../controllers/metrics.controller');
|
|
4
4
|
const asyncHandler = require('../utils/asyncHandler');
|
|
5
|
+
const rateLimiter = require('../services/rateLimiter.service');
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
router.
|
|
7
|
+
// Add rate limiting to prevent abuse
|
|
8
|
+
router.post('/track', rateLimiter.limit('metricsTrackLimiter'), asyncHandler(metricsController.track));
|
|
9
|
+
router.get('/impact', rateLimiter.limit('metricsImpactLimiter'), asyncHandler(metricsController.getImpact));
|
|
8
10
|
|
|
9
11
|
module.exports = router;
|
|
@@ -6,9 +6,15 @@ const orgAdminController = require('../controllers/orgAdmin.controller');
|
|
|
6
6
|
const asyncHandler = require('../utils/asyncHandler');
|
|
7
7
|
|
|
8
8
|
router.get('/', basicAuth, asyncHandler(orgAdminController.listOrgs));
|
|
9
|
+
router.post('/', basicAuth, asyncHandler(orgAdminController.createOrganization));
|
|
9
10
|
router.get('/:orgId', basicAuth, asyncHandler(orgAdminController.getOrg));
|
|
11
|
+
router.put('/:orgId', basicAuth, asyncHandler(orgAdminController.updateOrganization));
|
|
12
|
+
router.patch('/:orgId/disable', basicAuth, asyncHandler(orgAdminController.disableOrganization));
|
|
13
|
+
router.patch('/:orgId/enable', basicAuth, asyncHandler(orgAdminController.enableOrganization));
|
|
14
|
+
router.delete('/:orgId', basicAuth, asyncHandler(orgAdminController.deleteOrganization));
|
|
10
15
|
|
|
11
16
|
router.get('/:orgId/members', basicAuth, asyncHandler(orgAdminController.listMembers));
|
|
17
|
+
router.post('/:orgId/members', basicAuth, asyncHandler(orgAdminController.addMember));
|
|
12
18
|
router.patch('/:orgId/members/:memberId', basicAuth, asyncHandler(orgAdminController.updateMember));
|
|
13
19
|
router.delete('/:orgId/members/:memberId', basicAuth, asyncHandler(orgAdminController.removeMember));
|
|
14
20
|
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const pagesService = require('../services/pages.service');
|
|
4
|
+
const { basicAuth } = require('../middleware/auth');
|
|
5
|
+
|
|
6
|
+
router.get('*', async (req, res, next) => {
|
|
7
|
+
try {
|
|
8
|
+
const pagesPrefix = req.app.get('pagesPrefix') || '/';
|
|
9
|
+
const adminPath = req.app.get('adminPath') || '/admin';
|
|
10
|
+
const routePath = req.path;
|
|
11
|
+
|
|
12
|
+
const draft = req.query?.draft === '1' || req.query?.draft === 'true';
|
|
13
|
+
const statuses = draft ? ['published', 'draft'] : ['published'];
|
|
14
|
+
|
|
15
|
+
if (draft) {
|
|
16
|
+
let nextCalled = false;
|
|
17
|
+
basicAuth(req, res, () => {
|
|
18
|
+
nextCalled = true;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// If basicAuth did not call next(), it likely ended the response.
|
|
22
|
+
if (!nextCalled) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const firstSegment = routePath.replace(/^\//, '').split('/')[0];
|
|
28
|
+
if (pagesService.isReservedSegment(firstSegment, adminPath)) {
|
|
29
|
+
return next();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const page = await pagesService.findPageByRoutePath(routePath, {
|
|
33
|
+
pagesPrefix,
|
|
34
|
+
tenantId: null,
|
|
35
|
+
includeGlobal: true,
|
|
36
|
+
statuses,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
if (!page) {
|
|
40
|
+
return next();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const viewsRoot = req.app.get('views') || undefined;
|
|
44
|
+
const html = await pagesService.renderPage(page, { viewsRoot, req, res });
|
|
45
|
+
|
|
46
|
+
if (!html || html.trim() === '') {
|
|
47
|
+
throw new Error('Page rendered as empty content');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
res.send(html);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
if (err.code === 'NOT_FOUND') {
|
|
53
|
+
return next();
|
|
54
|
+
}
|
|
55
|
+
console.error(`[pages] render error for path ${req.path}:`, err);
|
|
56
|
+
|
|
57
|
+
// Render a friendly error page if possible, otherwise send a simple message
|
|
58
|
+
try {
|
|
59
|
+
const errorHtml = `
|
|
60
|
+
<!DOCTYPE html>
|
|
61
|
+
<html>
|
|
62
|
+
<head>
|
|
63
|
+
<title>Page Rendering Error</title>
|
|
64
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
65
|
+
</head>
|
|
66
|
+
<body class="bg-gray-50 flex items-center justify-center min-h-screen p-4 font-sans">
|
|
67
|
+
<div class="max-w-2xl w-full bg-white shadow-xl rounded-xl p-8 border-t-8 border-red-500">
|
|
68
|
+
<div class="flex items-center gap-4 mb-6">
|
|
69
|
+
<div class="bg-red-100 p-3 rounded-full">
|
|
70
|
+
<svg class="w-8 h-8 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
71
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
|
|
72
|
+
</svg>
|
|
73
|
+
</div>
|
|
74
|
+
<h1 class="text-3xl font-extrabold text-gray-900">Rendering Error</h1>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<p class="text-lg text-gray-700 mb-6">
|
|
78
|
+
We encountered a technical issue while trying to display this page.
|
|
79
|
+
This usually happens when a template or a content block is missing or contains an error.
|
|
80
|
+
</p>
|
|
81
|
+
|
|
82
|
+
<div class="bg-red-50 border-l-4 border-red-400 p-4 mb-8">
|
|
83
|
+
<div class="flex">
|
|
84
|
+
<div class="flex-shrink-0">
|
|
85
|
+
<svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
|
|
86
|
+
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
|
|
87
|
+
</svg>
|
|
88
|
+
</div>
|
|
89
|
+
<div class="ml-3">
|
|
90
|
+
<h3 class="text-sm font-medium text-red-800">Error Details</h3>
|
|
91
|
+
<div class="mt-2 text-sm text-red-700">
|
|
92
|
+
<p class="font-mono break-all">${err.message}</p>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<div class="flex flex-col sm:flex-row gap-4">
|
|
99
|
+
<a href="/" class="inline-flex items-center justify-center px-5 py-3 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 transition">
|
|
100
|
+
Go to Homepage
|
|
101
|
+
</a>
|
|
102
|
+
<button onclick="window.location.reload()" class="inline-flex items-center justify-center px-5 py-3 border border-gray-300 text-base font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 transition">
|
|
103
|
+
Try Again
|
|
104
|
+
</button>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<div class="mt-10 pt-6 border-t border-gray-100">
|
|
108
|
+
<p class="text-sm text-gray-500">
|
|
109
|
+
If you are the administrator, please check the server logs for more information.
|
|
110
|
+
</p>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</body>
|
|
114
|
+
</html>
|
|
115
|
+
`;
|
|
116
|
+
res.status(500).send(errorHtml);
|
|
117
|
+
} catch (e) {
|
|
118
|
+
res.status(500).send('Error rendering page and error page');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
module.exports = router;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
|
|
4
|
+
const { proxyRequest } = require('../services/proxy.service');
|
|
5
|
+
|
|
6
|
+
function extractTargetUrl(req) {
|
|
7
|
+
const originalUrl = String(req.originalUrl || '');
|
|
8
|
+
const baseUrl = String(req.baseUrl || '');
|
|
9
|
+
|
|
10
|
+
const prefix = `${baseUrl}/`;
|
|
11
|
+
let remainder = originalUrl;
|
|
12
|
+
if (prefix && remainder.startsWith(prefix)) {
|
|
13
|
+
remainder = remainder.slice(prefix.length);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// remove any leading slash
|
|
17
|
+
remainder = remainder.replace(/^\//, '');
|
|
18
|
+
|
|
19
|
+
// remainder begins with the encoded/decoded target URL
|
|
20
|
+
return remainder;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
router.all('/*', async (req, res) => {
|
|
24
|
+
try {
|
|
25
|
+
const targetUrl = extractTargetUrl(req);
|
|
26
|
+
req.proxyTargetUrl = targetUrl;
|
|
27
|
+
|
|
28
|
+
const result = await proxyRequest(req);
|
|
29
|
+
|
|
30
|
+
res.status(result.status);
|
|
31
|
+
const headers = result.headers && typeof result.headers === 'object' ? result.headers : {};
|
|
32
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
33
|
+
if (v === undefined || v === null) continue;
|
|
34
|
+
try {
|
|
35
|
+
res.setHeader(k, v);
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return res.send(result.body);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
return res.status(500).json({ error: 'Proxy failed' });
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
module.exports = router;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
|
|
4
|
+
const { authenticate } = require('../middleware/auth');
|
|
5
|
+
const rbacService = require('../services/rbac.service');
|
|
6
|
+
|
|
7
|
+
router.get('/my-orgs', authenticate, async (req, res) => {
|
|
8
|
+
const orgIds = await rbacService.getUserOrgIds(req.user._id);
|
|
9
|
+
return res.json({ orgIds });
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
router.get('/my-rights', authenticate, async (req, res) => {
|
|
13
|
+
const { orgId } = req.query;
|
|
14
|
+
if (!orgId) {
|
|
15
|
+
return res.status(400).json({ error: 'orgId is required' });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const { grants, explain, orgMember } = await rbacService.getEffectiveGrants({ userId: req.user._id, orgId });
|
|
19
|
+
if (!orgMember) {
|
|
20
|
+
return res.status(403).json({ error: 'You are not a member of this organization' });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return res.json({
|
|
24
|
+
grants: grants.map((g) => ({
|
|
25
|
+
id: String(g._id),
|
|
26
|
+
right: g.right,
|
|
27
|
+
effect: g.effect,
|
|
28
|
+
subjectType: g.subjectType,
|
|
29
|
+
subjectId: String(g.subjectId),
|
|
30
|
+
scopeType: g.scopeType,
|
|
31
|
+
scopeId: g.scopeId ? String(g.scopeId) : null,
|
|
32
|
+
})),
|
|
33
|
+
explain,
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
router.post('/check', authenticate, async (req, res) => {
|
|
38
|
+
const { orgId, right } = req.body || {};
|
|
39
|
+
if (!orgId || !right) {
|
|
40
|
+
return res.status(400).json({ error: 'orgId and right are required' });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const result = await rbacService.checkRight({ userId: req.user._id, orgId, right });
|
|
44
|
+
return res.json({ allowed: result.allowed, reason: result.reason, decisionLayer: result.decisionLayer || null });
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
module.exports = router;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
|
|
4
|
+
const uiComponentsPublicController = require('../controllers/uiComponentsPublic.controller');
|
|
5
|
+
|
|
6
|
+
router.get('/projects/:projectId/manifest', uiComponentsPublicController.getManifest);
|
|
7
|
+
router.get('/projects/:projectId/components/:code', uiComponentsPublicController.getComponent);
|
|
8
|
+
|
|
9
|
+
module.exports = router;
|
|
@@ -3,6 +3,7 @@ const router = express.Router();
|
|
|
3
3
|
const webhookController = require('../controllers/webhook.controller');
|
|
4
4
|
const authMiddleware = require('../middleware/auth');
|
|
5
5
|
const orgMiddleware = require('../middleware/org');
|
|
6
|
+
const rateLimiter = require('../services/rateLimiter.service');
|
|
6
7
|
|
|
7
8
|
// Webhook routes support both User (JWT) and SuperAdmin (Basic Auth)
|
|
8
9
|
router.use((req, res, next) => {
|
|
@@ -27,6 +28,6 @@ router.post('/', webhookController.create);
|
|
|
27
28
|
router.patch('/:id', webhookController.update);
|
|
28
29
|
router.get('/:id/history', webhookController.getHistory);
|
|
29
30
|
router.delete('/:id', webhookController.delete);
|
|
30
|
-
router.post('/:id/test', webhookController.test);
|
|
31
|
+
router.post('/:id/test', rateLimiter.limit('webhookTestLimiter'), webhookController.test);
|
|
31
32
|
|
|
32
33
|
module.exports = router;
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
2
|
const router = express.Router();
|
|
3
|
+
const { basicAuth } = require('../middleware/auth');
|
|
3
4
|
const Workflow = require('../models/Workflow');
|
|
4
5
|
const WorkflowExecution = require('../models/WorkflowExecution');
|
|
5
6
|
const workflowService = require('../services/workflow.service');
|
|
6
7
|
|
|
8
|
+
// Apply basic authentication to all workflows endpoints
|
|
9
|
+
router.use(basicAuth);
|
|
10
|
+
|
|
7
11
|
// List workflows
|
|
8
12
|
router.get('/', async (req, res) => {
|
|
9
13
|
const query = req.currentOrganization ? { organizationId: req.currentOrganization.id } : {};
|