@intranefr/superbackend 1.4.3
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/.commiat +4 -0
- package/.env.example +47 -0
- package/README.md +110 -0
- package/index.js +94 -0
- package/package.json +67 -0
- package/public/css/styles.css +139 -0
- package/public/js/animations.js +41 -0
- package/sdk/error-tracking/browser/package.json +16 -0
- package/sdk/error-tracking/browser/src/core.js +270 -0
- package/sdk/error-tracking/browser/src/embed.js +18 -0
- package/sdk/error-tracking/browser/src/index.js +1 -0
- package/server.js +5 -0
- package/src/admin/endpointRegistry.js +300 -0
- package/src/controllers/admin.controller.js +321 -0
- package/src/controllers/adminAssets.controller.js +530 -0
- package/src/controllers/adminAssetsStorage.controller.js +260 -0
- package/src/controllers/adminEjsVirtual.controller.js +354 -0
- package/src/controllers/adminFeatureFlags.controller.js +155 -0
- package/src/controllers/adminHeadless.controller.js +1071 -0
- package/src/controllers/adminI18n.controller.js +604 -0
- package/src/controllers/adminJsonConfigs.controller.js +97 -0
- package/src/controllers/adminLlm.controller.js +273 -0
- package/src/controllers/adminMigration.controller.js +257 -0
- package/src/controllers/adminSeoConfig.controller.js +515 -0
- package/src/controllers/adminStats.controller.js +121 -0
- package/src/controllers/adminUploadNamespaces.controller.js +208 -0
- package/src/controllers/assets.controller.js +248 -0
- package/src/controllers/auth.controller.js +93 -0
- package/src/controllers/billing.controller.js +223 -0
- package/src/controllers/featureFlags.controller.js +35 -0
- package/src/controllers/forms.controller.js +217 -0
- package/src/controllers/globalSettings.controller.js +252 -0
- package/src/controllers/headlessCrud.controller.js +126 -0
- package/src/controllers/i18n.controller.js +12 -0
- package/src/controllers/invite.controller.js +249 -0
- package/src/controllers/jsonConfigs.controller.js +19 -0
- package/src/controllers/metrics.controller.js +149 -0
- package/src/controllers/notificationAdmin.controller.js +264 -0
- package/src/controllers/notifications.controller.js +131 -0
- package/src/controllers/org.controller.js +357 -0
- package/src/controllers/orgAdmin.controller.js +491 -0
- package/src/controllers/stripeAdmin.controller.js +410 -0
- package/src/controllers/user.controller.js +361 -0
- package/src/controllers/userAdmin.controller.js +277 -0
- package/src/controllers/waitingList.controller.js +167 -0
- package/src/controllers/webhook.controller.js +200 -0
- package/src/middleware/auth.js +66 -0
- package/src/middleware/errorCapture.js +170 -0
- package/src/middleware/headlessApiTokenAuth.js +57 -0
- package/src/middleware/org.js +108 -0
- package/src/middleware.js +901 -0
- package/src/models/ActionEvent.js +31 -0
- package/src/models/ActivityLog.js +41 -0
- package/src/models/Asset.js +84 -0
- package/src/models/AuditEvent.js +93 -0
- package/src/models/EmailLog.js +28 -0
- package/src/models/ErrorAggregate.js +72 -0
- package/src/models/FormSubmission.js +41 -0
- package/src/models/GlobalSetting.js +38 -0
- package/src/models/HeadlessApiToken.js +24 -0
- package/src/models/HeadlessModelDefinition.js +41 -0
- package/src/models/I18nEntry.js +77 -0
- package/src/models/I18nLocale.js +33 -0
- package/src/models/Invite.js +70 -0
- package/src/models/JsonConfig.js +46 -0
- package/src/models/Notification.js +60 -0
- package/src/models/Organization.js +57 -0
- package/src/models/OrganizationMember.js +43 -0
- package/src/models/StripeCatalogItem.js +77 -0
- package/src/models/StripeWebhookEvent.js +57 -0
- package/src/models/User.js +89 -0
- package/src/models/VirtualEjsFile.js +60 -0
- package/src/models/VirtualEjsFileVersion.js +43 -0
- package/src/models/VirtualEjsGroupChange.js +32 -0
- package/src/models/WaitingList.js +41 -0
- package/src/models/Webhook.js +63 -0
- package/src/models/Workflow.js +29 -0
- package/src/models/WorkflowExecution.js +12 -0
- package/src/routes/admin.routes.js +26 -0
- package/src/routes/adminAssets.routes.js +28 -0
- package/src/routes/adminAssetsStorage.routes.js +13 -0
- package/src/routes/adminAudit.routes.js +196 -0
- package/src/routes/adminEjsVirtual.routes.js +17 -0
- package/src/routes/adminErrors.routes.js +164 -0
- package/src/routes/adminFeatureFlags.routes.js +12 -0
- package/src/routes/adminHeadless.routes.js +38 -0
- package/src/routes/adminI18n.routes.js +22 -0
- package/src/routes/adminJsonConfigs.routes.js +15 -0
- package/src/routes/adminLlm.routes.js +12 -0
- package/src/routes/adminMigration.routes.js +81 -0
- package/src/routes/adminSeoConfig.routes.js +20 -0
- package/src/routes/adminUploadNamespaces.routes.js +13 -0
- package/src/routes/assets.routes.js +21 -0
- package/src/routes/auth.routes.js +12 -0
- package/src/routes/billing.routes.js +11 -0
- package/src/routes/errorTracking.routes.js +31 -0
- package/src/routes/featureFlags.routes.js +9 -0
- package/src/routes/forms.routes.js +9 -0
- package/src/routes/formsAdmin.routes.js +13 -0
- package/src/routes/globalSettings.routes.js +18 -0
- package/src/routes/headless.routes.js +15 -0
- package/src/routes/i18n.routes.js +8 -0
- package/src/routes/invite.routes.js +9 -0
- package/src/routes/jsonConfigs.routes.js +8 -0
- package/src/routes/log.routes.js +111 -0
- package/src/routes/metrics.routes.js +9 -0
- package/src/routes/notificationAdmin.routes.js +15 -0
- package/src/routes/notifications.routes.js +12 -0
- package/src/routes/org.routes.js +31 -0
- package/src/routes/orgAdmin.routes.js +20 -0
- package/src/routes/publicAssets.routes.js +7 -0
- package/src/routes/stripeAdmin.routes.js +20 -0
- package/src/routes/user.routes.js +22 -0
- package/src/routes/userAdmin.routes.js +15 -0
- package/src/routes/waitingList.routes.js +13 -0
- package/src/routes/waitingListAdmin.routes.js +9 -0
- package/src/routes/webhook.routes.js +32 -0
- package/src/routes/workflowWebhook.routes.js +54 -0
- package/src/routes/workflows.routes.js +110 -0
- package/src/services/assets.service.js +110 -0
- package/src/services/audit.service.js +62 -0
- package/src/services/auditLogger.js +165 -0
- package/src/services/ejsVirtual.service.js +614 -0
- package/src/services/email.service.js +351 -0
- package/src/services/errorLogger.js +221 -0
- package/src/services/featureFlags.service.js +202 -0
- package/src/services/forms.service.js +214 -0
- package/src/services/globalSettings.service.js +49 -0
- package/src/services/headlessApiTokens.service.js +158 -0
- package/src/services/headlessCrypto.service.js +31 -0
- package/src/services/headlessModels.service.js +356 -0
- package/src/services/i18n.service.js +314 -0
- package/src/services/i18nInferredKeys.service.js +337 -0
- package/src/services/jsonConfigs.service.js +392 -0
- package/src/services/llm.service.js +749 -0
- package/src/services/migration.service.js +581 -0
- package/src/services/migrationAssets/fsLocal.js +58 -0
- package/src/services/migrationAssets/index.js +134 -0
- package/src/services/migrationAssets/s3.js +75 -0
- package/src/services/migrationAssets/sftp.js +92 -0
- package/src/services/notification.service.js +212 -0
- package/src/services/objectStorage.service.js +514 -0
- package/src/services/seoConfig.service.js +402 -0
- package/src/services/storage.js +150 -0
- package/src/services/stripe.service.js +185 -0
- package/src/services/stripeHelper.service.js +264 -0
- package/src/services/uploadNamespaces.service.js +326 -0
- package/src/services/webhook.service.js +157 -0
- package/src/services/workflow.service.js +271 -0
- package/src/utils/asyncHandler.js +5 -0
- package/src/utils/encryption.js +80 -0
- package/src/utils/jwt.js +40 -0
- package/src/utils/orgRoles.js +156 -0
- package/src/utils/validation.js +26 -0
- package/src/utils/webhookRetry.js +93 -0
- package/views/admin-assets.ejs +444 -0
- package/views/admin-audit.ejs +283 -0
- package/views/admin-coolify-deploy.ejs +207 -0
- package/views/admin-dashboard-home.ejs +291 -0
- package/views/admin-dashboard.ejs +397 -0
- package/views/admin-ejs-virtual.ejs +280 -0
- package/views/admin-errors.ejs +368 -0
- package/views/admin-feature-flags.ejs +390 -0
- package/views/admin-forms.ejs +526 -0
- package/views/admin-global-settings.ejs +436 -0
- package/views/admin-headless.ejs +2020 -0
- package/views/admin-i18n-locales.ejs +221 -0
- package/views/admin-i18n.ejs +728 -0
- package/views/admin-json-configs.ejs +410 -0
- package/views/admin-llm.ejs +884 -0
- package/views/admin-metrics.ejs +274 -0
- package/views/admin-migration.ejs +814 -0
- package/views/admin-notifications.ejs +430 -0
- package/views/admin-organizations.ejs +984 -0
- package/views/admin-seo-config.ejs +673 -0
- package/views/admin-stripe-pricing.ejs +558 -0
- package/views/admin-test.ejs +342 -0
- package/views/admin-users.ejs +452 -0
- package/views/admin-waiting-list.ejs +547 -0
- package/views/admin-webhooks.ejs +329 -0
- package/views/admin-workflows.ejs +310 -0
- package/views/partials/admin-assets-script.ejs +2022 -0
- package/views/partials/admin-test-sidebar.ejs +14 -0
- package/views/partials/dashboard/nav-items.ejs +66 -0
- package/views/partials/dashboard/palette.ejs +63 -0
- package/views/partials/dashboard/sidebar.ejs +21 -0
- package/views/partials/dashboard/tab-bar.ejs +26 -0
- package/views/partials/footer.ejs +3 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const { basicAuth } = require('../middleware/auth');
|
|
4
|
+
const adminUploadNamespacesController = require('../controllers/adminUploadNamespaces.controller');
|
|
5
|
+
|
|
6
|
+
router.get('/', basicAuth, adminUploadNamespacesController.listNamespaces);
|
|
7
|
+
router.get('/summary', basicAuth, adminUploadNamespacesController.getNamespacesSummary);
|
|
8
|
+
router.get('/:key', basicAuth, adminUploadNamespacesController.getNamespace);
|
|
9
|
+
router.post('/', basicAuth, adminUploadNamespacesController.createNamespace);
|
|
10
|
+
router.put('/:key', basicAuth, adminUploadNamespacesController.updateNamespace);
|
|
11
|
+
router.delete('/:key', basicAuth, adminUploadNamespacesController.deleteNamespace);
|
|
12
|
+
|
|
13
|
+
module.exports = router;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const multer = require('multer');
|
|
4
|
+
const { authenticate } = require('../middleware/auth');
|
|
5
|
+
const assetsController = require('../controllers/assets.controller');
|
|
6
|
+
const { auditMiddleware } = require('../services/auditLogger');
|
|
7
|
+
|
|
8
|
+
const upload = multer({
|
|
9
|
+
storage: multer.memoryStorage(),
|
|
10
|
+
limits: {
|
|
11
|
+
fileSize: parseInt(process.env.MULTER_FILE_SIZE_LIMIT || '1073741824', 10)
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
router.post('/upload', authenticate, auditMiddleware('user.asset.upload', { entityType: 'Asset' }), upload.single('file'), assetsController.upload);
|
|
16
|
+
router.get('/', authenticate, assetsController.list);
|
|
17
|
+
router.get('/:id', authenticate, assetsController.get);
|
|
18
|
+
router.get('/:id/download', authenticate, assetsController.download);
|
|
19
|
+
router.delete('/:id', authenticate, auditMiddleware('user.asset.delete', { entityType: 'Asset', getEntityId: (req) => req.params.id }), assetsController.delete);
|
|
20
|
+
|
|
21
|
+
module.exports = router;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const authController = require('../controllers/auth.controller');
|
|
4
|
+
const { authenticate } = require('../middleware/auth');
|
|
5
|
+
const { auditMiddleware } = require('../services/auditLogger');
|
|
6
|
+
|
|
7
|
+
router.post('/register', auditMiddleware('public.auth.register', { entityType: 'User' }), authController.register);
|
|
8
|
+
router.post('/login', auditMiddleware('public.auth.login', { entityType: 'User' }), authController.login);
|
|
9
|
+
router.post('/refresh-token', auditMiddleware('public.auth.refresh', { entityType: 'User' }), authController.refresh);
|
|
10
|
+
router.get('/me', authenticate, authController.me);
|
|
11
|
+
|
|
12
|
+
module.exports = router;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const billingController = require('../controllers/billing.controller');
|
|
4
|
+
const { authenticate } = require('../middleware/auth');
|
|
5
|
+
const { auditMiddleware } = require('../services/auditLogger');
|
|
6
|
+
|
|
7
|
+
router.post('/create-checkout-session', authenticate, auditMiddleware('user.billing.checkout_session.create', { entityType: 'StripeCheckoutSession' }), billingController.createCheckoutSession);
|
|
8
|
+
router.post('/create-portal-session', authenticate, auditMiddleware('user.billing.portal_session.create', { entityType: 'StripePortalSession' }), billingController.createPortalSession);
|
|
9
|
+
router.post('/reconcile-subscription', authenticate, auditMiddleware('user.billing.subscription.reconcile', { entityType: 'Subscription' }), billingController.reconcileSubscription);
|
|
10
|
+
|
|
11
|
+
module.exports = router;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
const router = express.Router();
|
|
6
|
+
|
|
7
|
+
router.get('/browser-sdk', (req, res) => {
|
|
8
|
+
const filePath = path.join(
|
|
9
|
+
__dirname,
|
|
10
|
+
'..',
|
|
11
|
+
'..',
|
|
12
|
+
'sdk',
|
|
13
|
+
'error-tracking',
|
|
14
|
+
'browser',
|
|
15
|
+
'dist',
|
|
16
|
+
'embed.iife.js',
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
fs.readFile(filePath, 'utf8', (err, contents) => {
|
|
20
|
+
if (err) {
|
|
21
|
+
res.status(404).type('text/plain').send('Browser SDK not found');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
26
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
27
|
+
res.send(contents);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
module.exports = router;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const { authenticate } = require('../middleware/auth');
|
|
4
|
+
const featureFlagsController = require('../controllers/featureFlags.controller');
|
|
5
|
+
|
|
6
|
+
router.get('/public', featureFlagsController.getPublicFlags);
|
|
7
|
+
router.get('/', authenticate, featureFlagsController.getEvaluatedFlags);
|
|
8
|
+
|
|
9
|
+
module.exports = router;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const formsController = require('../controllers/forms.controller');
|
|
4
|
+
const asyncHandler = require('../utils/asyncHandler');
|
|
5
|
+
const { auditMiddleware } = require('../services/auditLogger');
|
|
6
|
+
|
|
7
|
+
router.post('/submit/:formId', auditMiddleware('public.form.submit', { entityType: 'FormSubmission' }), asyncHandler(formsController.submit));
|
|
8
|
+
|
|
9
|
+
module.exports = router;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const { basicAuth } = require('../middleware/auth');
|
|
4
|
+
const formsController = require('../controllers/forms.controller');
|
|
5
|
+
const asyncHandler = require('../utils/asyncHandler');
|
|
6
|
+
|
|
7
|
+
router.get('/', basicAuth, asyncHandler(formsController.adminList));
|
|
8
|
+
router.delete('/:id', basicAuth, asyncHandler(formsController.deleteSubmission));
|
|
9
|
+
router.get('/definitions', basicAuth, asyncHandler(formsController.getForms));
|
|
10
|
+
router.post('/definitions', basicAuth, asyncHandler(formsController.saveForm));
|
|
11
|
+
router.delete('/definitions/:id', basicAuth, asyncHandler(formsController.deleteForm));
|
|
12
|
+
|
|
13
|
+
module.exports = router;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const { basicAuth } = require('../middleware/auth');
|
|
4
|
+
const globalSettingsController = require('../controllers/globalSettings.controller');
|
|
5
|
+
|
|
6
|
+
// Public route (no auth)
|
|
7
|
+
router.get('/public', globalSettingsController.getPublicSettings);
|
|
8
|
+
|
|
9
|
+
// Protected routes (Basic Auth)
|
|
10
|
+
router.get('/', basicAuth, globalSettingsController.getAllSettings);
|
|
11
|
+
// more specific path before :key catch-all
|
|
12
|
+
router.get('/:key/reveal', basicAuth, globalSettingsController.revealSetting);
|
|
13
|
+
router.get('/:key', basicAuth, globalSettingsController.getSetting);
|
|
14
|
+
router.put('/:key', basicAuth, globalSettingsController.updateSetting);
|
|
15
|
+
router.post('/', basicAuth, globalSettingsController.createSetting);
|
|
16
|
+
router.delete('/:key', basicAuth, globalSettingsController.deleteSetting);
|
|
17
|
+
|
|
18
|
+
module.exports = router;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
|
|
4
|
+
const headlessCrudController = require('../controllers/headlessCrud.controller');
|
|
5
|
+
const { headlessApiTokenAuth, requireHeadlessPermission } = require('../middleware/headlessApiTokenAuth');
|
|
6
|
+
|
|
7
|
+
router.use(headlessApiTokenAuth());
|
|
8
|
+
|
|
9
|
+
router.get('/:modelCode', requireHeadlessPermission(), headlessCrudController.list);
|
|
10
|
+
router.post('/:modelCode', requireHeadlessPermission(), headlessCrudController.create);
|
|
11
|
+
router.get('/:modelCode/:id', requireHeadlessPermission(), headlessCrudController.get);
|
|
12
|
+
router.put('/:modelCode/:id', requireHeadlessPermission(), headlessCrudController.update);
|
|
13
|
+
router.delete('/:modelCode/:id', requireHeadlessPermission(), headlessCrudController.remove);
|
|
14
|
+
|
|
15
|
+
module.exports = router;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const inviteController = require('../controllers/invite.controller');
|
|
4
|
+
const { auditMiddleware } = require('../services/auditLogger');
|
|
5
|
+
|
|
6
|
+
router.get('/info', inviteController.getInviteInfo);
|
|
7
|
+
router.post('/accept', auditMiddleware('user.invite.accept', { entityType: 'Invite' }), inviteController.acceptInvite);
|
|
8
|
+
|
|
9
|
+
module.exports = router;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const { logError, getConfig } = require('../services/errorLogger');
|
|
4
|
+
const { verifyAccessToken } = require('../utils/jwt');
|
|
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
|
+
}
|
|
51
|
+
|
|
52
|
+
router.post('/error', async (req, res) => {
|
|
53
|
+
try {
|
|
54
|
+
const ip = req.ip || req.headers['x-forwarded-for']?.split(',')[0]?.trim() || 'unknown';
|
|
55
|
+
const user = extractUserFromToken(req);
|
|
56
|
+
const isAuthenticated = !!user;
|
|
57
|
+
|
|
58
|
+
const rateCheck = await checkRateLimit(ip, isAuthenticated);
|
|
59
|
+
res.setHeader('X-RateLimit-Limit', rateCheck.limit);
|
|
60
|
+
res.setHeader('X-RateLimit-Remaining', Math.max(0, rateCheck.remaining));
|
|
61
|
+
|
|
62
|
+
if (!rateCheck.allowed) {
|
|
63
|
+
return res.status(429).json({ error: 'Too many error reports. Please try again later.' });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const config = await getConfig();
|
|
67
|
+
if (!config.errorTrackingEnabled) {
|
|
68
|
+
return res.status(200).json({ ok: true, tracked: false });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const body = req.body || {};
|
|
72
|
+
|
|
73
|
+
const event = {
|
|
74
|
+
source: 'frontend',
|
|
75
|
+
severity: body.severity || 'error',
|
|
76
|
+
errorName: body.errorName || body.name || 'Error',
|
|
77
|
+
errorCode: body.errorCode || body.code,
|
|
78
|
+
message: String(body.message || '').slice(0, 2000),
|
|
79
|
+
stack: String(body.stack || '').slice(0, 5000),
|
|
80
|
+
actor: {
|
|
81
|
+
userId: user?.userId,
|
|
82
|
+
role: user?.role,
|
|
83
|
+
ip,
|
|
84
|
+
userAgent: req.headers['user-agent'],
|
|
85
|
+
},
|
|
86
|
+
request: {
|
|
87
|
+
method: body.request?.method,
|
|
88
|
+
path: body.request?.path || body.url,
|
|
89
|
+
statusCode: body.request?.statusCode || body.statusCode,
|
|
90
|
+
requestId: body.request?.requestId || req.headers['x-request-id'],
|
|
91
|
+
},
|
|
92
|
+
runtime: {
|
|
93
|
+
url: String(body.url || body.runtime?.url || '').slice(0, 500),
|
|
94
|
+
referrer: String(body.referrer || body.runtime?.referrer || '').slice(0, 500),
|
|
95
|
+
viewport: body.runtime?.viewport,
|
|
96
|
+
locale: body.runtime?.locale,
|
|
97
|
+
appVersion: body.runtime?.appVersion,
|
|
98
|
+
},
|
|
99
|
+
extra: body.extra,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
await logError(event);
|
|
103
|
+
|
|
104
|
+
return res.status(200).json({ ok: true, tracked: true });
|
|
105
|
+
} catch (err) {
|
|
106
|
+
console.log('[LogRoutes] Error logging frontend error:', err.message);
|
|
107
|
+
return res.status(500).json({ error: 'Failed to log error' });
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
module.exports = router;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const metricsController = require('../controllers/metrics.controller');
|
|
4
|
+
const asyncHandler = require('../utils/asyncHandler');
|
|
5
|
+
|
|
6
|
+
router.post('/track', asyncHandler(metricsController.track));
|
|
7
|
+
router.get('/impact', asyncHandler(metricsController.getImpact));
|
|
8
|
+
|
|
9
|
+
module.exports = router;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
|
|
4
|
+
const { basicAuth } = require('../middleware/auth');
|
|
5
|
+
const notificationAdminController = require('../controllers/notificationAdmin.controller');
|
|
6
|
+
const asyncHandler = require('../utils/asyncHandler');
|
|
7
|
+
|
|
8
|
+
router.get('/stats', basicAuth, asyncHandler(notificationAdminController.getNotificationStats));
|
|
9
|
+
router.get('/', basicAuth, asyncHandler(notificationAdminController.listNotifications));
|
|
10
|
+
router.post('/send', basicAuth, asyncHandler(notificationAdminController.sendNotification));
|
|
11
|
+
router.post('/broadcast', basicAuth, asyncHandler(notificationAdminController.broadcastNotification));
|
|
12
|
+
router.delete('/:id', basicAuth, asyncHandler(notificationAdminController.deleteNotification));
|
|
13
|
+
router.post('/:id/retry-email', basicAuth, asyncHandler(notificationAdminController.retryEmailNotification));
|
|
14
|
+
|
|
15
|
+
module.exports = router;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const { authenticate } = require('../middleware/auth');
|
|
4
|
+
const notificationsController = require('../controllers/notifications.controller');
|
|
5
|
+
const { auditMiddleware } = require('../services/auditLogger');
|
|
6
|
+
|
|
7
|
+
router.get('/notifications', authenticate, notificationsController.getNotifications);
|
|
8
|
+
router.put('/notifications/:id/read', authenticate, auditMiddleware('user.notification.read', { entityType: 'Notification', getEntityId: (req) => req.params.id }), notificationsController.markNotificationAsRead);
|
|
9
|
+
router.get('/activity-log', authenticate, notificationsController.getActivityLog);
|
|
10
|
+
router.post('/activity-log', authenticate, auditMiddleware('user.activity_log.create', { entityType: 'ActivityLog' }), notificationsController.createActivityLog);
|
|
11
|
+
|
|
12
|
+
module.exports = router;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const { authenticate } = require('../middleware/auth');
|
|
4
|
+
const { loadOrgContext, requireOrgMember, requireOrgRoleAtLeast, requireOrgRole } = require('../middleware/org');
|
|
5
|
+
const orgController = require('../controllers/org.controller');
|
|
6
|
+
const inviteController = require('../controllers/invite.controller');
|
|
7
|
+
const { auditMiddleware } = require('../services/auditLogger');
|
|
8
|
+
|
|
9
|
+
router.get('/public', orgController.listPublicOrgs);
|
|
10
|
+
|
|
11
|
+
router.get('/', authenticate, auditMiddleware('user.org.list', { entityType: 'Org' }), orgController.listOrgs);
|
|
12
|
+
router.post('/', authenticate, auditMiddleware('user.org.create', { entityType: 'Org' }), orgController.createOrg);
|
|
13
|
+
|
|
14
|
+
router.get('/:orgId/public', orgController.getOrgPublic);
|
|
15
|
+
|
|
16
|
+
router.get('/:orgId', authenticate, loadOrgContext, requireOrgMember, auditMiddleware('user.org.get', { entityType: 'Org', getEntityId: (req) => req.params.orgId }), orgController.getOrg);
|
|
17
|
+
router.put('/:orgId', authenticate, loadOrgContext, requireOrgRoleAtLeast('admin'), auditMiddleware('user.org.update', { entityType: 'Org', getEntityId: (req) => req.params.orgId }), orgController.updateOrg);
|
|
18
|
+
router.delete('/:orgId', authenticate, loadOrgContext, requireOrgRole('owner'), auditMiddleware('user.org.delete', { entityType: 'Org', getEntityId: (req) => req.params.orgId }), orgController.deleteOrg);
|
|
19
|
+
|
|
20
|
+
router.post('/:orgId/join', authenticate, loadOrgContext, auditMiddleware('user.org.join', { entityType: 'Org', getEntityId: (req) => req.params.orgId }), orgController.joinOrg);
|
|
21
|
+
|
|
22
|
+
router.get('/:orgId/members', authenticate, loadOrgContext, requireOrgMember, auditMiddleware('user.org.members.list', { entityType: 'Org', getEntityId: (req) => req.params.orgId }), orgController.listMembers);
|
|
23
|
+
router.post('/:orgId/members', authenticate, loadOrgContext, requireOrgRoleAtLeast('admin'), auditMiddleware('user.org.member.add', { entityType: 'Org', getEntityId: (req) => req.params.orgId }), orgController.addMember);
|
|
24
|
+
router.put('/:orgId/members/:userId/role', authenticate, loadOrgContext, requireOrgRoleAtLeast('admin'), auditMiddleware('user.org.member.role.update', { entityType: 'Org', getEntityId: (req) => req.params.orgId }), orgController.updateMemberRole);
|
|
25
|
+
router.delete('/:orgId/members/:userId', authenticate, loadOrgContext, requireOrgRoleAtLeast('admin'), auditMiddleware('user.org.member.remove', { entityType: 'Org', getEntityId: (req) => req.params.orgId }), orgController.removeMember);
|
|
26
|
+
|
|
27
|
+
router.get('/:orgId/invites', authenticate, loadOrgContext, requireOrgRoleAtLeast('admin'), auditMiddleware('user.org.invites.list', { entityType: 'Org', getEntityId: (req) => req.params.orgId }), inviteController.listInvites);
|
|
28
|
+
router.post('/:orgId/invites', authenticate, loadOrgContext, requireOrgRoleAtLeast('admin'), auditMiddleware('user.org.invite.create', { entityType: 'OrgInvite', getEntityId: (req) => req.params.inviteId || req.params.orgId }), inviteController.createInvite);
|
|
29
|
+
router.delete('/:orgId/invites/:inviteId', authenticate, loadOrgContext, requireOrgRoleAtLeast('admin'), auditMiddleware('user.org.invite.revoke', { entityType: 'OrgInvite', getEntityId: (req) => req.params.inviteId }), inviteController.revokeInvite);
|
|
30
|
+
|
|
31
|
+
module.exports = router;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
|
|
4
|
+
const { basicAuth } = require('../middleware/auth');
|
|
5
|
+
const orgAdminController = require('../controllers/orgAdmin.controller');
|
|
6
|
+
const asyncHandler = require('../utils/asyncHandler');
|
|
7
|
+
|
|
8
|
+
router.get('/', basicAuth, asyncHandler(orgAdminController.listOrgs));
|
|
9
|
+
router.get('/:orgId', basicAuth, asyncHandler(orgAdminController.getOrg));
|
|
10
|
+
|
|
11
|
+
router.get('/:orgId/members', basicAuth, asyncHandler(orgAdminController.listMembers));
|
|
12
|
+
router.patch('/:orgId/members/:memberId', basicAuth, asyncHandler(orgAdminController.updateMember));
|
|
13
|
+
router.delete('/:orgId/members/:memberId', basicAuth, asyncHandler(orgAdminController.removeMember));
|
|
14
|
+
|
|
15
|
+
router.get('/:orgId/invites', basicAuth, asyncHandler(orgAdminController.listInvites));
|
|
16
|
+
router.post('/:orgId/invites', basicAuth, asyncHandler(orgAdminController.createInvite));
|
|
17
|
+
router.delete('/:orgId/invites/:inviteId', basicAuth, asyncHandler(orgAdminController.revokeInvite));
|
|
18
|
+
router.post('/:orgId/invites/:inviteId/resend', basicAuth, asyncHandler(orgAdminController.resendInvite));
|
|
19
|
+
|
|
20
|
+
module.exports = router;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
|
|
4
|
+
const { basicAuth } = require('../middleware/auth');
|
|
5
|
+
const stripeAdminController = require('../controllers/stripeAdmin.controller');
|
|
6
|
+
const asyncHandler = require('../utils/asyncHandler');
|
|
7
|
+
|
|
8
|
+
router.get('/status', basicAuth, asyncHandler(stripeAdminController.getStripeStatus));
|
|
9
|
+
router.get('/catalog', basicAuth, asyncHandler(stripeAdminController.listCatalog));
|
|
10
|
+
router.get('/catalog/:id', basicAuth, asyncHandler(stripeAdminController.getCatalogItem));
|
|
11
|
+
router.post('/catalog/upsert', basicAuth, asyncHandler(stripeAdminController.upsertCatalogItem));
|
|
12
|
+
router.post('/catalog/import', basicAuth, asyncHandler(stripeAdminController.importStripePrice));
|
|
13
|
+
router.post('/catalog/:id/deactivate', basicAuth, asyncHandler(stripeAdminController.deactivateCatalogItem));
|
|
14
|
+
router.post('/catalog/:id/activate', basicAuth, asyncHandler(stripeAdminController.activateCatalogItem));
|
|
15
|
+
router.delete('/catalog/:id', basicAuth, asyncHandler(stripeAdminController.deleteCatalogItem));
|
|
16
|
+
router.get('/products', basicAuth, asyncHandler(stripeAdminController.listStripeProducts));
|
|
17
|
+
router.get('/prices', basicAuth, asyncHandler(stripeAdminController.listStripePrices));
|
|
18
|
+
router.post('/env/sync', basicAuth, asyncHandler(stripeAdminController.syncEnvFromCatalog));
|
|
19
|
+
|
|
20
|
+
module.exports = router;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const { authenticate } = require('../middleware/auth');
|
|
4
|
+
const userController = require('../controllers/user.controller');
|
|
5
|
+
const { auditMiddleware } = require('../services/auditLogger');
|
|
6
|
+
|
|
7
|
+
// Profile management
|
|
8
|
+
router.put('/profile', authenticate, auditMiddleware('user.profile.update', { entityType: 'User' }), userController.updateProfile);
|
|
9
|
+
|
|
10
|
+
// Password management
|
|
11
|
+
router.put('/password', authenticate, auditMiddleware('user.password.change', { entityType: 'User' }), userController.changePassword);
|
|
12
|
+
router.post('/password-reset-request', auditMiddleware('user.password_reset.request', { entityType: 'User' }), userController.requestPasswordReset);
|
|
13
|
+
router.post('/password-reset-confirm', auditMiddleware('user.password_reset.confirm', { entityType: 'User' }), userController.confirmPasswordReset);
|
|
14
|
+
|
|
15
|
+
// Account deletion
|
|
16
|
+
router.delete('/account', authenticate, auditMiddleware('user.account.delete', { entityType: 'User' }), userController.deleteAccount);
|
|
17
|
+
|
|
18
|
+
// Settings
|
|
19
|
+
router.get('/settings', authenticate, auditMiddleware('user.settings.get', { entityType: 'User' }), userController.getSettings);
|
|
20
|
+
router.put('/settings', authenticate, auditMiddleware('user.settings.update', { entityType: 'User' }), userController.updateSettings);
|
|
21
|
+
|
|
22
|
+
module.exports = router;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
|
|
4
|
+
const { basicAuth } = require('../middleware/auth');
|
|
5
|
+
const userAdminController = require('../controllers/userAdmin.controller');
|
|
6
|
+
const asyncHandler = require('../utils/asyncHandler');
|
|
7
|
+
|
|
8
|
+
router.get('/stats', basicAuth, asyncHandler(userAdminController.getUserStats));
|
|
9
|
+
router.get('/', basicAuth, asyncHandler(userAdminController.listUsers));
|
|
10
|
+
router.get('/:id', basicAuth, asyncHandler(userAdminController.getUser));
|
|
11
|
+
router.patch('/:id', basicAuth, asyncHandler(userAdminController.updateUser));
|
|
12
|
+
router.post('/:id/disable', basicAuth, asyncHandler(userAdminController.disableUser));
|
|
13
|
+
router.post('/:id/enable', basicAuth, asyncHandler(userAdminController.enableUser));
|
|
14
|
+
|
|
15
|
+
module.exports = router;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const waitingListController = require('../controllers/waitingList.controller');
|
|
4
|
+
const asyncHandler = require('../utils/asyncHandler');
|
|
5
|
+
const { auditMiddleware } = require('../services/auditLogger');
|
|
6
|
+
|
|
7
|
+
// POST /api/waiting-list/subscribe - Subscribe to waiting list
|
|
8
|
+
router.post('/subscribe', auditMiddleware('public.waiting_list.subscribe', { entityType: 'WaitingList' }), asyncHandler(waitingListController.subscribe));
|
|
9
|
+
|
|
10
|
+
// GET /api/waiting-list/stats - Get waiting list statistics (public)
|
|
11
|
+
router.get('/stats', asyncHandler(waitingListController.getStats));
|
|
12
|
+
|
|
13
|
+
module.exports = router;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const { basicAuth } = require('../middleware/auth');
|
|
4
|
+
const waitingListController = require('../controllers/waitingList.controller');
|
|
5
|
+
const asyncHandler = require('../utils/asyncHandler');
|
|
6
|
+
|
|
7
|
+
router.get('/', basicAuth, asyncHandler(waitingListController.adminList));
|
|
8
|
+
|
|
9
|
+
module.exports = router;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const webhookController = require('../controllers/webhook.controller');
|
|
4
|
+
const authMiddleware = require('../middleware/auth');
|
|
5
|
+
const orgMiddleware = require('../middleware/org');
|
|
6
|
+
|
|
7
|
+
// Webhook routes support both User (JWT) and SuperAdmin (Basic Auth)
|
|
8
|
+
router.use((req, res, next) => {
|
|
9
|
+
// If already authenticated via Basic Auth (SuperAdmin), skip JWT check
|
|
10
|
+
const authHeader = req.headers.authorization;
|
|
11
|
+
if (authHeader && authHeader.startsWith('Basic ')) {
|
|
12
|
+
return authMiddleware.basicAuth(req, res, next);
|
|
13
|
+
}
|
|
14
|
+
// Otherwise require JWT
|
|
15
|
+
authMiddleware.authenticate(req, res, next);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Optional organization context: required for non-admins
|
|
19
|
+
router.use((req, res, next) => {
|
|
20
|
+
const isBasicAuth = req.headers.authorization?.startsWith('Basic ');
|
|
21
|
+
if (isBasicAuth) return next();
|
|
22
|
+
orgMiddleware.loadOrgContext(req, res, next);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
router.get('/', webhookController.getAll);
|
|
26
|
+
router.post('/', webhookController.create);
|
|
27
|
+
router.patch('/:id', webhookController.update);
|
|
28
|
+
router.get('/:id/history', webhookController.getHistory);
|
|
29
|
+
router.delete('/:id', webhookController.delete);
|
|
30
|
+
router.post('/:id/test', webhookController.test);
|
|
31
|
+
|
|
32
|
+
module.exports = router;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const Workflow = require('../models/Workflow');
|
|
4
|
+
const workflowService = require('../services/workflow.service');
|
|
5
|
+
|
|
6
|
+
// Public Webhook Entrypoint
|
|
7
|
+
router.all('/:webhookId', async (req, res) => {
|
|
8
|
+
const workflow = await Workflow.findOne({
|
|
9
|
+
$or: [{ _id: req.params.webhookId }, { webhookSlug: req.params.webhookId }]
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
if (!workflow) return res.status(404).json({ error: 'Workflow not found' });
|
|
13
|
+
|
|
14
|
+
// Method Restriction
|
|
15
|
+
if (workflow.entrypoint?.allowedMethods?.length > 0 && !workflow.entrypoint.allowedMethods.includes(req.method)) {
|
|
16
|
+
return res.status(405).json({ error: `Method ${req.method} not allowed` });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Auth Checks
|
|
20
|
+
if (workflow.entrypoint?.auth?.type === 'header') {
|
|
21
|
+
const val = req.headers[workflow.entrypoint.auth.headerName?.toLowerCase()];
|
|
22
|
+
if (val !== workflow.entrypoint.auth.headerValue) return res.status(401).json({ error: 'Unauthorized' });
|
|
23
|
+
} else if (workflow.entrypoint?.auth?.type === 'bearer') {
|
|
24
|
+
const authHeader = req.headers['authorization'];
|
|
25
|
+
if (!authHeader || !authHeader.startsWith('Bearer ') || authHeader.split(' ')[1] !== workflow.entrypoint.auth.headerValue) {
|
|
26
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (workflow.status !== 'active' && !workflow.entrypoint?.testMode) {
|
|
31
|
+
return res.status(403).json({ error: 'Workflow is inactive' });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const initialContext = {
|
|
35
|
+
body: req.body,
|
|
36
|
+
query: req.query,
|
|
37
|
+
headers: req.headers,
|
|
38
|
+
method: req.method
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
if (workflow.entrypoint?.awaitResponse) {
|
|
42
|
+
try {
|
|
43
|
+
const service = await workflowService.execute(workflow._id, initialContext);
|
|
44
|
+
res.status(200).json(service.context.lastNode || { success: true });
|
|
45
|
+
} catch (err) {
|
|
46
|
+
res.status(500).json({ error: 'Execution failed', message: err.message });
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
workflowService.execute(workflow._id, initialContext).catch(console.error);
|
|
50
|
+
res.status(201).json({ message: 'Triggered', executionId: Date.now().toString() });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
module.exports = router;
|