@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.
- package/cookies.txt +6 -0
- package/cookies1.txt +6 -0
- package/cookies2.txt +6 -0
- package/cookies3.txt +6 -0
- package/cookies4.txt +5 -0
- package/cookies_old.txt +5 -0
- package/cookies_old_test.txt +6 -0
- package/cookies_super.txt +5 -0
- package/cookies_super_test.txt +6 -0
- package/cookies_test.txt +6 -0
- package/index.js +9 -0
- package/manage.js +745 -0
- package/package.json +6 -2
- package/plugins/core-waiting-list-migration/README.md +118 -0
- package/plugins/core-waiting-list-migration/index.js +438 -0
- package/plugins/global-settings-presets/index.js +20 -0
- package/plugins/hello-cli/index.js +17 -0
- package/plugins/ui-components-seeder/components/suiAlert.js +212 -0
- package/plugins/ui-components-seeder/components/suiToast.js +186 -0
- package/plugins/ui-components-seeder/index.js +31 -0
- package/public/js/admin-ui-components-preview.js +281 -0
- package/public/js/admin-ui-components.js +408 -0
- package/public/js/llm-provider-model-picker.js +193 -0
- package/public/test-iframe-fix.html +63 -0
- package/public/test-iframe.html +14 -0
- package/src/admin/endpointRegistry.js +68 -0
- package/src/controllers/admin.controller.js +36 -10
- package/src/controllers/adminAgents.controller.js +37 -0
- package/src/controllers/adminDataCleanup.controller.js +45 -0
- package/src/controllers/adminLlm.controller.js +19 -8
- package/src/controllers/adminLogin.controller.js +269 -0
- package/src/controllers/adminMarkdowns.controller.js +157 -0
- package/src/controllers/adminPlugins.controller.js +55 -0
- package/src/controllers/adminRegistry.controller.js +106 -0
- package/src/controllers/adminScripts.controller.js +138 -0
- package/src/controllers/adminStats.controller.js +4 -4
- package/src/controllers/adminTelegram.controller.js +72 -0
- package/src/controllers/markdowns.controller.js +42 -0
- package/src/controllers/registry.controller.js +32 -0
- package/src/controllers/waitingList.controller.js +52 -74
- package/src/helpers/mongooseHelper.js +6 -6
- package/src/helpers/scriptBase.js +2 -2
- package/src/middleware/auth.js +71 -1
- package/src/middleware/rbac.js +62 -0
- package/src/middleware.js +584 -176
- package/src/models/Agent.js +105 -0
- package/src/models/AgentMessage.js +82 -0
- package/src/models/GlobalSetting.js +11 -1
- package/src/models/Markdown.js +75 -0
- package/src/models/ScriptRun.js +8 -0
- package/src/models/TelegramBot.js +42 -0
- package/src/models/UiComponent.js +2 -0
- package/src/models/User.js +1 -1
- package/src/routes/admin.routes.js +3 -3
- package/src/routes/adminAgents.routes.js +13 -0
- package/src/routes/adminAssets.routes.js +11 -11
- package/src/routes/adminBlog.routes.js +2 -2
- package/src/routes/adminBlogAi.routes.js +2 -2
- package/src/routes/adminBlogAutomation.routes.js +2 -2
- package/src/routes/adminCache.routes.js +2 -2
- package/src/routes/adminConsoleManager.routes.js +2 -2
- package/src/routes/adminCrons.routes.js +2 -2
- package/src/routes/adminDataCleanup.routes.js +26 -0
- package/src/routes/adminDbBrowser.routes.js +2 -2
- package/src/routes/adminEjsVirtual.routes.js +2 -2
- package/src/routes/adminFeatureFlags.routes.js +6 -6
- package/src/routes/adminHeadless.routes.js +2 -2
- package/src/routes/adminHealthChecks.routes.js +2 -2
- package/src/routes/adminI18n.routes.js +2 -2
- package/src/routes/adminJsonConfigs.routes.js +8 -8
- package/src/routes/adminLlm.routes.js +8 -7
- package/src/routes/adminLogin.routes.js +23 -0
- package/src/routes/adminMarkdowns.routes.js +10 -0
- package/src/routes/adminMigration.routes.js +12 -12
- package/src/routes/adminPages.routes.js +2 -2
- package/src/routes/adminPlugins.routes.js +15 -0
- package/src/routes/adminProxy.routes.js +2 -2
- package/src/routes/adminRateLimits.routes.js +8 -8
- package/src/routes/adminRbac.routes.js +2 -2
- package/src/routes/adminRegistry.routes.js +24 -0
- package/src/routes/adminScripts.routes.js +6 -3
- package/src/routes/adminSeoConfig.routes.js +10 -10
- package/src/routes/adminTelegram.routes.js +14 -0
- package/src/routes/adminTerminals.routes.js +2 -2
- package/src/routes/adminUiComponents.routes.js +2 -2
- package/src/routes/adminUploadNamespaces.routes.js +7 -7
- package/src/routes/blogInternal.routes.js +2 -2
- package/src/routes/experiments.routes.js +2 -2
- package/src/routes/formsAdmin.routes.js +6 -6
- package/src/routes/globalSettings.routes.js +8 -8
- package/src/routes/internalExperiments.routes.js +2 -2
- package/src/routes/markdowns.routes.js +16 -0
- package/src/routes/notificationAdmin.routes.js +7 -7
- package/src/routes/orgAdmin.routes.js +16 -16
- package/src/routes/pages.routes.js +3 -3
- package/src/routes/registry.routes.js +11 -0
- package/src/routes/stripeAdmin.routes.js +12 -12
- package/src/routes/userAdmin.routes.js +7 -7
- package/src/routes/waitingListAdmin.routes.js +2 -2
- package/src/routes/workflows.routes.js +3 -3
- package/src/services/agent.service.js +546 -0
- package/src/services/agentHistory.service.js +345 -0
- package/src/services/agentTools.service.js +578 -0
- package/src/services/dataCleanup.service.js +286 -0
- package/src/services/jsonConfigs.service.js +284 -10
- package/src/services/llm.service.js +219 -6
- package/src/services/markdowns.service.js +522 -0
- package/src/services/plugins.service.js +348 -0
- package/src/services/registry.service.js +452 -0
- package/src/services/scriptsRunner.service.js +328 -37
- package/src/services/telegram.service.js +130 -0
- package/src/services/uiComponents.service.js +180 -0
- package/src/services/waitingListJson.service.js +401 -0
- package/src/utils/rbac/rightsRegistry.js +118 -0
- package/test-access.js +63 -0
- package/test-iframe-fix.html +63 -0
- package/test-iframe.html +14 -0
- package/views/admin-403.ejs +92 -0
- package/views/admin-agents.ejs +273 -0
- package/views/admin-coolify-deploy.ejs +8 -8
- package/views/admin-dashboard-home.ejs +52 -2
- package/views/admin-dashboard.ejs +179 -7
- package/views/admin-data-cleanup.ejs +357 -0
- package/views/admin-experiments.ejs +1 -1
- package/views/admin-login.ejs +286 -0
- package/views/admin-markdowns.ejs +905 -0
- package/views/admin-plugins-system.ejs +223 -0
- package/views/admin-scripts.ejs +221 -4
- package/views/admin-telegram.ejs +269 -0
- package/views/admin-ui-components.ejs +82 -402
- package/views/admin-users.ejs +207 -11
- package/views/partials/dashboard/nav-items.ejs +5 -0
- package/views/partials/llm-provider-model-picker.ejs +0 -161
- 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
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
+
};
|