@intranefr/superbackend 1.5.2 → 1.6.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.
Files changed (134) hide show
  1. package/cookies.txt +6 -0
  2. package/cookies1.txt +6 -0
  3. package/cookies2.txt +6 -0
  4. package/cookies3.txt +6 -0
  5. package/cookies4.txt +5 -0
  6. package/cookies_old.txt +5 -0
  7. package/cookies_old_test.txt +6 -0
  8. package/cookies_super.txt +5 -0
  9. package/cookies_super_test.txt +6 -0
  10. package/cookies_test.txt +6 -0
  11. package/index.js +9 -0
  12. package/manage.js +745 -0
  13. package/package.json +6 -2
  14. package/plugins/core-waiting-list-migration/README.md +118 -0
  15. package/plugins/core-waiting-list-migration/index.js +438 -0
  16. package/plugins/global-settings-presets/index.js +20 -0
  17. package/plugins/hello-cli/index.js +17 -0
  18. package/plugins/ui-components-seeder/components/suiAlert.js +212 -0
  19. package/plugins/ui-components-seeder/components/suiToast.js +186 -0
  20. package/plugins/ui-components-seeder/index.js +31 -0
  21. package/public/js/admin-ui-components-preview.js +281 -0
  22. package/public/js/admin-ui-components.js +408 -0
  23. package/public/js/llm-provider-model-picker.js +193 -0
  24. package/public/test-iframe-fix.html +63 -0
  25. package/public/test-iframe.html +14 -0
  26. package/src/admin/endpointRegistry.js +68 -0
  27. package/src/controllers/admin.controller.js +36 -10
  28. package/src/controllers/adminAgents.controller.js +37 -0
  29. package/src/controllers/adminDataCleanup.controller.js +45 -0
  30. package/src/controllers/adminLlm.controller.js +19 -8
  31. package/src/controllers/adminLogin.controller.js +269 -0
  32. package/src/controllers/adminMarkdowns.controller.js +157 -0
  33. package/src/controllers/adminPlugins.controller.js +55 -0
  34. package/src/controllers/adminRegistry.controller.js +106 -0
  35. package/src/controllers/adminScripts.controller.js +138 -0
  36. package/src/controllers/adminStats.controller.js +4 -4
  37. package/src/controllers/adminTelegram.controller.js +72 -0
  38. package/src/controllers/markdowns.controller.js +42 -0
  39. package/src/controllers/registry.controller.js +32 -0
  40. package/src/controllers/waitingList.controller.js +52 -74
  41. package/src/helpers/mongooseHelper.js +6 -6
  42. package/src/helpers/scriptBase.js +2 -2
  43. package/src/middleware/auth.js +71 -1
  44. package/src/middleware/rbac.js +62 -0
  45. package/src/middleware.js +584 -176
  46. package/src/models/Agent.js +105 -0
  47. package/src/models/AgentMessage.js +82 -0
  48. package/src/models/GlobalSetting.js +11 -1
  49. package/src/models/Markdown.js +75 -0
  50. package/src/models/ScriptRun.js +8 -0
  51. package/src/models/TelegramBot.js +42 -0
  52. package/src/models/UiComponent.js +2 -0
  53. package/src/models/User.js +1 -1
  54. package/src/routes/admin.routes.js +3 -3
  55. package/src/routes/adminAgents.routes.js +13 -0
  56. package/src/routes/adminAssets.routes.js +11 -11
  57. package/src/routes/adminBlog.routes.js +2 -2
  58. package/src/routes/adminBlogAi.routes.js +2 -2
  59. package/src/routes/adminBlogAutomation.routes.js +2 -2
  60. package/src/routes/adminCache.routes.js +2 -2
  61. package/src/routes/adminConsoleManager.routes.js +2 -2
  62. package/src/routes/adminCrons.routes.js +2 -2
  63. package/src/routes/adminDataCleanup.routes.js +26 -0
  64. package/src/routes/adminDbBrowser.routes.js +2 -2
  65. package/src/routes/adminEjsVirtual.routes.js +2 -2
  66. package/src/routes/adminFeatureFlags.routes.js +6 -6
  67. package/src/routes/adminHeadless.routes.js +2 -2
  68. package/src/routes/adminHealthChecks.routes.js +2 -2
  69. package/src/routes/adminI18n.routes.js +2 -2
  70. package/src/routes/adminJsonConfigs.routes.js +8 -8
  71. package/src/routes/adminLlm.routes.js +8 -7
  72. package/src/routes/adminLogin.routes.js +23 -0
  73. package/src/routes/adminMarkdowns.routes.js +10 -0
  74. package/src/routes/adminMigration.routes.js +12 -12
  75. package/src/routes/adminPages.routes.js +2 -2
  76. package/src/routes/adminPlugins.routes.js +15 -0
  77. package/src/routes/adminProxy.routes.js +2 -2
  78. package/src/routes/adminRateLimits.routes.js +8 -8
  79. package/src/routes/adminRbac.routes.js +2 -2
  80. package/src/routes/adminRegistry.routes.js +24 -0
  81. package/src/routes/adminScripts.routes.js +6 -3
  82. package/src/routes/adminSeoConfig.routes.js +10 -10
  83. package/src/routes/adminTelegram.routes.js +14 -0
  84. package/src/routes/adminTerminals.routes.js +2 -2
  85. package/src/routes/adminUiComponents.routes.js +2 -2
  86. package/src/routes/adminUploadNamespaces.routes.js +7 -7
  87. package/src/routes/blogInternal.routes.js +2 -2
  88. package/src/routes/experiments.routes.js +2 -2
  89. package/src/routes/formsAdmin.routes.js +6 -6
  90. package/src/routes/globalSettings.routes.js +8 -8
  91. package/src/routes/internalExperiments.routes.js +2 -2
  92. package/src/routes/markdowns.routes.js +16 -0
  93. package/src/routes/notificationAdmin.routes.js +7 -7
  94. package/src/routes/orgAdmin.routes.js +16 -16
  95. package/src/routes/pages.routes.js +3 -3
  96. package/src/routes/registry.routes.js +11 -0
  97. package/src/routes/stripeAdmin.routes.js +12 -12
  98. package/src/routes/userAdmin.routes.js +7 -7
  99. package/src/routes/waitingListAdmin.routes.js +2 -2
  100. package/src/routes/workflows.routes.js +3 -3
  101. package/src/services/agent.service.js +546 -0
  102. package/src/services/agentHistory.service.js +345 -0
  103. package/src/services/agentTools.service.js +578 -0
  104. package/src/services/dataCleanup.service.js +286 -0
  105. package/src/services/jsonConfigs.service.js +284 -10
  106. package/src/services/llm.service.js +219 -6
  107. package/src/services/markdowns.service.js +522 -0
  108. package/src/services/plugins.service.js +348 -0
  109. package/src/services/registry.service.js +452 -0
  110. package/src/services/scriptsRunner.service.js +328 -37
  111. package/src/services/telegram.service.js +130 -0
  112. package/src/services/uiComponents.service.js +180 -0
  113. package/src/services/waitingListJson.service.js +401 -0
  114. package/src/utils/rbac/rightsRegistry.js +118 -0
  115. package/test-access.js +63 -0
  116. package/test-iframe-fix.html +63 -0
  117. package/test-iframe.html +14 -0
  118. package/views/admin-403.ejs +92 -0
  119. package/views/admin-agents.ejs +273 -0
  120. package/views/admin-coolify-deploy.ejs +8 -8
  121. package/views/admin-dashboard-home.ejs +52 -2
  122. package/views/admin-dashboard.ejs +179 -7
  123. package/views/admin-data-cleanup.ejs +357 -0
  124. package/views/admin-experiments.ejs +1 -1
  125. package/views/admin-login.ejs +286 -0
  126. package/views/admin-markdowns.ejs +905 -0
  127. package/views/admin-plugins-system.ejs +223 -0
  128. package/views/admin-scripts.ejs +221 -4
  129. package/views/admin-telegram.ejs +269 -0
  130. package/views/admin-ui-components.ejs +82 -402
  131. package/views/admin-users.ejs +207 -11
  132. package/views/partials/dashboard/nav-items.ejs +5 -0
  133. package/views/partials/llm-provider-model-picker.ejs +0 -161
  134. package/analysis-only.skill +0 -0
@@ -0,0 +1,157 @@
1
+ const {
2
+ ERROR_CODES,
3
+ listMarkdowns,
4
+ getMarkdownById,
5
+ createMarkdown,
6
+ updateMarkdown,
7
+ deleteMarkdown,
8
+ getFolderContents,
9
+ getUniqueGroupCodes,
10
+ validatePathUniqueness,
11
+ } = require('../services/markdowns.service');
12
+
13
+ function handleServiceError(res, error) {
14
+ const msg = error?.message || 'Operation failed';
15
+ const code = error?.code;
16
+
17
+ if (code === 'VALIDATION' || code === 'INVALID_MARKDOWN' || code === 'INVALID_GROUP_CODE') {
18
+ return res.status(400).json({ error: msg });
19
+ }
20
+ if (code === 'NOT_FOUND') {
21
+ return res.status(404).json({ error: msg });
22
+ }
23
+ if (code === 'PATH_NOT_UNIQUE') {
24
+ return res.status(409).json({ error: msg });
25
+ }
26
+
27
+ return res.status(500).json({ error: msg });
28
+ }
29
+
30
+ function parseJsonMaybe(value) {
31
+ if (value === undefined || value === null) return null;
32
+ if (typeof value !== 'string') return value;
33
+ const trimmed = value.trim();
34
+ if (!trimmed) return null;
35
+ try {
36
+ return JSON.parse(trimmed);
37
+ } catch (_) {
38
+ return null;
39
+ }
40
+ }
41
+
42
+ exports.list = async (req, res) => {
43
+ try {
44
+ const filters = {
45
+ category: req.query.category,
46
+ group_code: req.query.group_code,
47
+ status: req.query.status,
48
+ ownerUserId: req.query.ownerUserId,
49
+ orgId: req.query.orgId,
50
+ search: req.query.search,
51
+ };
52
+
53
+ const pagination = {
54
+ page: Number(req.query.page) || 1,
55
+ limit: Number(req.query.limit) || 50,
56
+ sort: parseJsonMaybe(req.query.sort) || { updatedAt: -1 },
57
+ };
58
+
59
+ const result = await listMarkdowns(filters, pagination, { isAdmin: true });
60
+ return res.json(result);
61
+ } catch (error) {
62
+ console.error('Error listing markdowns:', error);
63
+ return handleServiceError(res, error);
64
+ }
65
+ };
66
+
67
+ exports.get = async (req, res) => {
68
+ try {
69
+ const item = await getMarkdownById(req.params.id);
70
+ if (!item) return res.status(404).json({ error: 'Markdown not found' });
71
+ return res.json({ item });
72
+ } catch (error) {
73
+ console.error('Error fetching markdown:', error);
74
+ return handleServiceError(res, error);
75
+ }
76
+ };
77
+
78
+ exports.create = async (req, res) => {
79
+ try {
80
+ const item = await createMarkdown(req.body || {});
81
+ return res.status(201).json({ item });
82
+ } catch (error) {
83
+ console.error('Error creating markdown:', error);
84
+ return handleServiceError(res, error);
85
+ }
86
+ };
87
+
88
+ exports.update = async (req, res) => {
89
+ try {
90
+ const item = await updateMarkdown(req.params.id, req.body || {});
91
+ return res.json({ item });
92
+ } catch (error) {
93
+ console.error('Error updating markdown:', error);
94
+ return handleServiceError(res, error);
95
+ }
96
+ };
97
+
98
+ exports.remove = async (req, res) => {
99
+ try {
100
+ const result = await deleteMarkdown(req.params.id);
101
+ return res.json(result);
102
+ } catch (error) {
103
+ console.error('Error deleting markdown:', error);
104
+ return handleServiceError(res, error);
105
+ }
106
+ };
107
+
108
+ exports.getFolderContents = async (req, res) => {
109
+ try {
110
+ const { category } = req.params;
111
+ const { group_code } = req.params;
112
+
113
+ const pagination = {
114
+ page: Number(req.query.page) || 1,
115
+ limit: Number(req.query.limit) || 100,
116
+ sort: parseJsonMaybe(req.query.sort) || { title: 1 },
117
+ };
118
+
119
+ const result = await getFolderContents(category, group_code, pagination, { isAdmin: true });
120
+ return res.json(result);
121
+ } catch (error) {
122
+ console.error('Error getting folder contents:', error);
123
+ return handleServiceError(res, error);
124
+ }
125
+ };
126
+
127
+ exports.validatePath = async (req, res) => {
128
+ try {
129
+ const { category, group_code, slug, excludeId } = req.body;
130
+
131
+ if (!category || !slug) {
132
+ return res.status(400).json({ error: 'category and slug are required' });
133
+ }
134
+
135
+ const isUnique = await validatePathUniqueness(category, group_code, slug, excludeId);
136
+ return res.json({ unique: isUnique });
137
+ } catch (error) {
138
+ console.error('Error validating path:', error);
139
+ return handleServiceError(res, error);
140
+ }
141
+ };
142
+
143
+ exports.getGroupCodes = async (req, res) => {
144
+ try {
145
+ const { category } = req.params;
146
+
147
+ if (!category) {
148
+ return res.status(400).json({ error: 'category is required' });
149
+ }
150
+
151
+ const groupCodes = await getUniqueGroupCodes(category, { isAdmin: true });
152
+ return res.json(groupCodes);
153
+ } catch (error) {
154
+ console.error('Error getting group codes:', error);
155
+ return handleServiceError(res, error);
156
+ }
157
+ };
@@ -0,0 +1,55 @@
1
+ const pluginsService = require('../services/plugins.service');
2
+
3
+ function handleError(res, error) {
4
+ const code = error?.code;
5
+ const message = error?.message || 'Operation failed';
6
+
7
+ if (code === 'NOT_FOUND') return res.status(404).json({ error: message });
8
+ if (code === 'VALIDATION') return res.status(400).json({ error: message });
9
+ return res.status(500).json({ error: message });
10
+ }
11
+
12
+ function runtimeContext(req) {
13
+ const superbackend = globalThis.superbackend || globalThis.saasbackend || {};
14
+ return {
15
+ services: superbackend.services || {},
16
+ helpers: superbackend.helpers || {},
17
+ request: req,
18
+ };
19
+ }
20
+
21
+ exports.list = async (req, res) => {
22
+ try {
23
+ const items = await pluginsService.listPlugins();
24
+ return res.json({ items });
25
+ } catch (error) {
26
+ return handleError(res, error);
27
+ }
28
+ };
29
+
30
+ exports.enable = async (req, res) => {
31
+ try {
32
+ const result = await pluginsService.enablePlugin(req.params.id, { context: runtimeContext(req) });
33
+ return res.json(result);
34
+ } catch (error) {
35
+ return handleError(res, error);
36
+ }
37
+ };
38
+
39
+ exports.disable = async (req, res) => {
40
+ try {
41
+ const result = await pluginsService.disablePlugin(req.params.id);
42
+ return res.json(result);
43
+ } catch (error) {
44
+ return handleError(res, error);
45
+ }
46
+ };
47
+
48
+ exports.install = async (req, res) => {
49
+ try {
50
+ const result = await pluginsService.installPlugin(req.params.id, { context: runtimeContext(req) });
51
+ return res.json(result);
52
+ } catch (error) {
53
+ return handleError(res, error);
54
+ }
55
+ };
@@ -0,0 +1,106 @@
1
+ const registryService = require('../services/registry.service');
2
+
3
+ function handleError(res, error) {
4
+ const code = error?.code;
5
+ const message = error?.message || 'Operation failed';
6
+
7
+ if (code === 'VALIDATION') return res.status(400).json({ error: message });
8
+ if (code === 'NOT_FOUND') return res.status(404).json({ error: message });
9
+ if (code === 'CONFLICT') return res.status(409).json({ error: message });
10
+ return res.status(500).json({ error: message });
11
+ }
12
+
13
+ exports.listRegistries = async (req, res) => {
14
+ try {
15
+ const items = await registryService.listRegistries();
16
+ return res.json({ items });
17
+ } catch (error) {
18
+ return handleError(res, error);
19
+ }
20
+ };
21
+
22
+ exports.createRegistry = async (req, res) => {
23
+ try {
24
+ const item = await registryService.createRegistry(req.body || {});
25
+ return res.status(201).json({ item });
26
+ } catch (error) {
27
+ return handleError(res, error);
28
+ }
29
+ };
30
+
31
+ exports.getRegistry = async (req, res) => {
32
+ try {
33
+ const item = await registryService.getRegistry(req.params.id);
34
+ if (!item) return res.status(404).json({ error: 'registry not found' });
35
+ return res.json({ item });
36
+ } catch (error) {
37
+ return handleError(res, error);
38
+ }
39
+ };
40
+
41
+ exports.updateRegistry = async (req, res) => {
42
+ try {
43
+ const item = await registryService.updateRegistry(req.params.id, req.body || {});
44
+ return res.json({ item });
45
+ } catch (error) {
46
+ return handleError(res, error);
47
+ }
48
+ };
49
+
50
+ exports.deleteRegistry = async (req, res) => {
51
+ try {
52
+ const result = await registryService.deleteRegistry(req.params.id);
53
+ return res.json(result);
54
+ } catch (error) {
55
+ return handleError(res, error);
56
+ }
57
+ };
58
+
59
+ exports.createToken = async (req, res) => {
60
+ try {
61
+ const result = await registryService.createToken(req.params.id, req.body || {});
62
+ return res.status(201).json(result);
63
+ } catch (error) {
64
+ return handleError(res, error);
65
+ }
66
+ };
67
+
68
+ exports.deleteToken = async (req, res) => {
69
+ try {
70
+ const result = await registryService.deleteToken(req.params.id, req.params.tokenId);
71
+ return res.json(result);
72
+ } catch (error) {
73
+ return handleError(res, error);
74
+ }
75
+ };
76
+
77
+ exports.listItems = async (req, res) => {
78
+ try {
79
+ const result = await registryService.listItemsForRegistry(
80
+ req.params.id,
81
+ req.query,
82
+ req.headers.authorization,
83
+ );
84
+ return res.json(result);
85
+ } catch (error) {
86
+ return handleError(res, error);
87
+ }
88
+ };
89
+
90
+ exports.upsertItem = async (req, res) => {
91
+ try {
92
+ const item = await registryService.upsertItem(req.params.id, req.body || {});
93
+ return res.json({ item });
94
+ } catch (error) {
95
+ return handleError(res, error);
96
+ }
97
+ };
98
+
99
+ exports.deleteItem = async (req, res) => {
100
+ try {
101
+ const result = await registryService.deleteItem(req.params.id, req.params.itemId);
102
+ return res.json(result);
103
+ } catch (error) {
104
+ return handleError(res, error);
105
+ }
106
+ };
@@ -349,3 +349,141 @@ exports.streamRun = async (req, res) => {
349
349
  return res.end();
350
350
  }
351
351
  };
352
+
353
+ // Get programmatic output (clean result for API consumption)
354
+ async function getProgrammaticOutput(req, res) {
355
+ try {
356
+ const { runId } = req.params;
357
+ if (!runId) {
358
+ return res.status(400).json({ error: 'runId is required' });
359
+ }
360
+
361
+ const run = await ScriptRun.findById(runId).lean();
362
+ if (!run) {
363
+ return res.status(404).json({ error: 'Script run not found' });
364
+ }
365
+
366
+ // Parse programmatic output if it's JSON
367
+ let parsedResult = null;
368
+ let isJson = false;
369
+
370
+ if (run.programmaticOutput) {
371
+ try {
372
+ parsedResult = JSON.parse(run.programmaticOutput);
373
+ isJson = true;
374
+ } catch {
375
+ // Not JSON, keep as string
376
+ parsedResult = null;
377
+ isJson = false;
378
+ }
379
+ }
380
+
381
+ res.json({
382
+ runId: run._id,
383
+ status: run.status,
384
+ exitCode: run.exitCode,
385
+ programmaticOutput: run.programmaticOutput || 'No output',
386
+ outputType: run.outputType || 'none',
387
+ isJson: isJson,
388
+ parsedResult: parsedResult,
389
+ returnResult: run.returnResult,
390
+ lastConsoleLog: run.lastConsoleLog,
391
+ createdAt: run.createdAt,
392
+ updatedAt: run.updatedAt,
393
+ startedAt: run.startedAt,
394
+ finishedAt: run.finishedAt
395
+ });
396
+ } catch (err) {
397
+ console.error('Error getting programmatic output:', err);
398
+ res.status(500).json({ error: err?.message || 'Internal server error' });
399
+ }
400
+ }
401
+
402
+ // Get full script output
403
+ async function getFullOutput(req, res) {
404
+ try {
405
+ const { runId } = req.params;
406
+ if (!runId) {
407
+ return res.status(400).json({ error: 'runId is required' });
408
+ }
409
+
410
+ const run = await ScriptRun.findById(runId).lean();
411
+ if (!run) {
412
+ return res.status(404).json({ error: 'Script run not found' });
413
+ }
414
+
415
+ res.json({
416
+ runId: run._id,
417
+ status: run.status,
418
+ exitCode: run.exitCode,
419
+ fullOutput: run.fullOutput || '',
420
+ outputSize: run.outputSize || 0,
421
+ lineCount: run.lineCount || 0,
422
+ lastOutputUpdate: run.lastOutputUpdate,
423
+ createdAt: run.createdAt,
424
+ updatedAt: run.updatedAt,
425
+ startedAt: run.startedAt,
426
+ finishedAt: run.finishedAt
427
+ });
428
+ } catch (err) {
429
+ console.error('Error getting full output:', err);
430
+ res.status(500).json({ error: err?.message || 'Internal server error' });
431
+ }
432
+ }
433
+
434
+ // Download script output as file
435
+ async function downloadOutput(req, res) {
436
+ try {
437
+ const { runId } = req.params;
438
+ if (!runId) {
439
+ return res.status(400).json({ error: 'runId is required' });
440
+ }
441
+
442
+ const run = await ScriptRun.findById(runId).lean();
443
+ if (!run) {
444
+ return res.status(404).json({ error: 'Script run not found' });
445
+ }
446
+
447
+ const filename = `script-output-${runId}-${run.createdAt.toISOString().slice(0, 19).replace(/:/g, '-')}.txt`;
448
+
449
+ res.setHeader('Content-Type', 'text/plain');
450
+ res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
451
+
452
+ // Include metadata at the top
453
+ const metadata = [
454
+ `Script Run ID: ${runId}`,
455
+ `Status: ${run.status}`,
456
+ `Exit Code: ${run.exitCode || 'N/A'}`,
457
+ `Started: ${run.startedAt || 'N/A'}`,
458
+ `Finished: ${run.finishedAt || 'N/A'}`,
459
+ `Output Size: ${run.outputSize || 0} characters`,
460
+ `Line Count: ${run.lineCount || 0}`,
461
+ `Created: ${run.createdAt}`,
462
+ '=' .repeat(50),
463
+ ''
464
+ ].join('\n');
465
+
466
+ res.send(metadata + (run.fullOutput || run.outputTail || 'No output available'));
467
+ } catch (err) {
468
+ console.error('Error downloading output:', err);
469
+ res.status(500).json({ error: err?.message || 'Internal server error' });
470
+ }
471
+ }
472
+
473
+ exports.getFullOutput = getFullOutput;
474
+ exports.downloadOutput = downloadOutput;
475
+
476
+ module.exports = {
477
+ listScripts: exports.listScripts,
478
+ getScript: exports.getScript,
479
+ createScript: exports.createScript,
480
+ updateScript: exports.updateScript,
481
+ deleteScript: exports.deleteScript,
482
+ runScript: exports.runScript,
483
+ listRuns: exports.listRuns,
484
+ getRun: exports.getRun,
485
+ streamRunLogs: exports.streamRun,
486
+ getProgrammaticOutput: getProgrammaticOutput,
487
+ getFullOutput: exports.getFullOutput,
488
+ downloadOutput: exports.downloadOutput,
489
+ };
@@ -4,7 +4,7 @@ const AuditEvent = require('../models/AuditEvent');
4
4
  const ErrorAggregate = require('../models/ErrorAggregate');
5
5
  const Asset = require('../models/Asset');
6
6
  const FormSubmission = require('../models/FormSubmission');
7
- const WaitingList = require('../models/WaitingList');
7
+ const waitingListService = require('../services/waitingListJson.service');
8
8
  const EmailLog = require('../models/EmailLog');
9
9
  const VirtualEjsFile = require('../models/VirtualEjsFile');
10
10
  const JsonConfig = require('../models/JsonConfig');
@@ -35,7 +35,7 @@ exports.getOverviewStats = async (req, res) => {
35
35
  totalJsonConfigs,
36
36
  // SaaS & Billing
37
37
  totalForms,
38
- totalWaiting,
38
+ waitingListStats,
39
39
  totalPlans,
40
40
  totalWorkflows
41
41
  ] = await Promise.all([
@@ -51,7 +51,7 @@ exports.getOverviewStats = async (req, res) => {
51
51
  VirtualEjsFile.countDocuments(),
52
52
  JsonConfig.countDocuments(),
53
53
  FormSubmission.countDocuments(),
54
- WaitingList.countDocuments(),
54
+ waitingListService.getWaitingListStats().catch(() => ({ totalSubscribers: 0 })), // Fallback to 0 if service fails
55
55
  StripeCatalogItem.countDocuments({ active: true }),
56
56
  Workflow.countDocuments()
57
57
  ]);
@@ -106,7 +106,7 @@ exports.getOverviewStats = async (req, res) => {
106
106
  },
107
107
  saas: {
108
108
  forms: totalForms,
109
- waiting: totalWaiting,
109
+ waiting: waitingListStats.totalSubscribers || 0,
110
110
  plans: totalPlans,
111
111
  workflows: totalWorkflows
112
112
  }
@@ -0,0 +1,72 @@
1
+ const TelegramBot = require('../models/TelegramBot');
2
+ const Agent = require('../models/Agent');
3
+ const telegramService = require('../services/telegram.service');
4
+
5
+ exports.listBots = async (req, res) => {
6
+ try {
7
+ const bots = await TelegramBot.find().populate('defaultAgentId', 'name').lean();
8
+ return res.json({ items: bots });
9
+ } catch (error) {
10
+ return res.status(500).json({ error: error.message });
11
+ }
12
+ };
13
+
14
+ exports.createBot = async (req, res) => {
15
+ try {
16
+ const data = { ...req.body };
17
+ if (data.defaultAgentId === '') delete data.defaultAgentId;
18
+
19
+ const bot = await TelegramBot.create(data);
20
+ if (bot.isActive) {
21
+ await telegramService.startBot(bot._id);
22
+ }
23
+ return res.json(bot);
24
+ } catch (error) {
25
+ return res.status(500).json({ error: error.message });
26
+ }
27
+ };
28
+
29
+ exports.updateBot = async (req, res) => {
30
+ try {
31
+ const data = { ...req.body };
32
+ if (data.defaultAgentId === '') data.defaultAgentId = null;
33
+
34
+ const bot = await TelegramBot.findByIdAndUpdate(req.params.id, data, { new: true });
35
+ if (bot.isActive) {
36
+ await telegramService.startBot(bot._id);
37
+ } else {
38
+ await telegramService.stopBot(bot._id);
39
+ }
40
+ return res.json(bot);
41
+ } catch (error) {
42
+ return res.status(500).json({ error: error.message });
43
+ }
44
+ };
45
+
46
+ exports.deleteBot = async (req, res) => {
47
+ try {
48
+ await telegramService.stopBot(req.params.id);
49
+ await TelegramBot.findByIdAndDelete(req.params.id);
50
+ return res.json({ success: true });
51
+ } catch (error) {
52
+ return res.status(500).json({ error: error.message });
53
+ }
54
+ };
55
+
56
+ exports.toggleBot = async (req, res) => {
57
+ try {
58
+ const bot = await TelegramBot.findById(req.params.id);
59
+ bot.isActive = !bot.isActive;
60
+ await bot.save();
61
+
62
+ if (bot.isActive) {
63
+ await telegramService.startBot(bot._id);
64
+ } else {
65
+ await telegramService.stopBot(bot._id);
66
+ }
67
+
68
+ return res.json(bot);
69
+ } catch (error) {
70
+ return res.status(500).json({ error: error.message });
71
+ }
72
+ };
@@ -0,0 +1,42 @@
1
+ const { getMarkdownByPath, searchMarkdowns } = require('../services/markdowns.service');
2
+
3
+ exports.getByPath = async (req, res) => {
4
+ try {
5
+ const { category, group_code, slug } = req.params;
6
+ // Check if JSON is requested via query or if we are on a .json route
7
+ const isJson = req.query?.json === 'true' || req.query?.json === '1' || req.path.endsWith('/json');
8
+
9
+ const doc = await getMarkdownByPath(category, group_code, slug);
10
+
11
+ if (isJson) {
12
+ return res.json({ item: doc });
13
+ }
14
+
15
+ // Serve raw markdown with correct MIME type
16
+ return res.type('text/markdown').send(doc.markdownRaw);
17
+ } catch (error) {
18
+ const code = error?.code;
19
+ if (code === 'NOT_FOUND') {
20
+ return res.status(404).json({ error: 'Markdown not found' });
21
+ }
22
+
23
+ console.error('Error fetching markdown:', error);
24
+ return res.status(500).json({ error: error?.message || 'Failed to fetch markdown' });
25
+ }
26
+ };
27
+
28
+ exports.search = async (req, res) => {
29
+ try {
30
+ const { q: query, category, group_code, limit = 50 } = req.query;
31
+
32
+ if (!query) {
33
+ return res.status(400).json({ error: 'Search query (q) is required' });
34
+ }
35
+
36
+ const results = await searchMarkdowns(query, { category, group_code, limit: Number(limit) });
37
+ return res.json({ results });
38
+ } catch (error) {
39
+ console.error('Error searching markdowns:', error);
40
+ return res.status(500).json({ error: error?.message || 'Failed to search markdowns' });
41
+ }
42
+ };
@@ -0,0 +1,32 @@
1
+ const registryService = require('../services/registry.service');
2
+
3
+ function handleError(res, error) {
4
+ const code = error?.code;
5
+ const message = error?.message || 'Operation failed';
6
+
7
+ if (code === 'VALIDATION') return res.status(400).json({ error: { code: 'INVALID_REQUEST', message } });
8
+ if (code === 'NOT_FOUND') return res.status(404).json({ error: { code: 'NOT_FOUND', message } });
9
+ return res.status(500).json({ error: { code: 'INTERNAL_ERROR', message } });
10
+ }
11
+
12
+ exports.auth = async (req, res) => {
13
+ try {
14
+ const payload = await registryService.getAuthStatus(req.params.id, req.headers.authorization);
15
+ return res.json(payload);
16
+ } catch (error) {
17
+ return handleError(res, error);
18
+ }
19
+ };
20
+
21
+ exports.list = async (req, res) => {
22
+ try {
23
+ const payload = await registryService.listItemsForRegistry(
24
+ req.params.id,
25
+ req.query,
26
+ req.headers.authorization,
27
+ );
28
+ return res.json(payload);
29
+ } catch (error) {
30
+ return handleError(res, error);
31
+ }
32
+ };