@intranefr/superbackend 1.5.2 → 1.5.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/index.js +2 -0
- package/manage.js +745 -0
- package/package.json +4 -2
- package/src/controllers/admin.controller.js +11 -5
- package/src/controllers/adminAgents.controller.js +37 -0
- package/src/controllers/adminLlm.controller.js +19 -0
- package/src/controllers/adminMarkdowns.controller.js +157 -0
- package/src/controllers/adminScripts.controller.js +138 -0
- package/src/controllers/adminTelegram.controller.js +72 -0
- package/src/controllers/markdowns.controller.js +42 -0
- package/src/helpers/mongooseHelper.js +6 -6
- package/src/helpers/scriptBase.js +2 -2
- package/src/middleware.js +136 -29
- package/src/models/Agent.js +105 -0
- package/src/models/AgentMessage.js +82 -0
- package/src/models/Markdown.js +75 -0
- package/src/models/ScriptRun.js +8 -0
- package/src/models/TelegramBot.js +42 -0
- package/src/routes/adminAgents.routes.js +13 -0
- package/src/routes/adminLlm.routes.js +1 -0
- package/src/routes/adminMarkdowns.routes.js +16 -0
- package/src/routes/adminScripts.routes.js +4 -1
- package/src/routes/adminTelegram.routes.js +14 -0
- package/src/routes/markdowns.routes.js +16 -0
- 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/jsonConfigs.service.js +22 -10
- package/src/services/llm.service.js +219 -6
- package/src/services/markdowns.service.js +522 -0
- package/src/services/scriptsRunner.service.js +328 -37
- package/src/services/telegram.service.js +130 -0
- package/views/admin-agents.ejs +273 -0
- package/views/admin-coolify-deploy.ejs +8 -8
- package/views/admin-dashboard.ejs +36 -5
- package/views/admin-experiments.ejs +1 -1
- package/views/admin-markdowns.ejs +905 -0
- package/views/admin-scripts.ejs +221 -4
- package/views/admin-telegram.ejs +269 -0
- package/views/partials/dashboard/nav-items.ejs +3 -0
- package/analysis-only.skill +0 -0
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intranefr/superbackend",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.3",
|
|
4
4
|
"description": "Node.js middleware that gives your project backend superpowers",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"start": "node server.js",
|
|
8
|
-
"dev": "nodemon --verbose --ignore uploads --ignore '*.log' server.js",
|
|
8
|
+
"dev": "nodemon --verbose --ignore uploads --ignore stdout.log --ignore '*.log' server.js",
|
|
9
9
|
"start:minio": "docker compose -f compose.standalone.yml --profile minio-only up -d minio",
|
|
10
10
|
"minio:envs": "node -e \"console.log(['S3_ENDPOINT=http://localhost:9000','S3_REGION=us-east-1','S3_ACCESS_KEY_ID=minioadmin','S3_SECRET_ACCESS_KEY=minioadmin','S3_BUCKET=saasbackend','S3_FORCE_PATH_STYLE=true'].join('\\n'))\"",
|
|
11
11
|
"build:sdk:error-tracking:browser": "esbuild sdk/error-tracking/browser/src/embed.js --bundle --format=iife --global-name=saasbackendErrorTrackingEmbed --outfile=sdk/error-tracking/browser/dist/embed.iife.js",
|
|
@@ -47,11 +47,13 @@
|
|
|
47
47
|
"mysql2": "^3.16.1",
|
|
48
48
|
"node-cron": "^4.2.1",
|
|
49
49
|
"node-pty": "^1.1.0",
|
|
50
|
+
"node-telegram-bot-api": "^0.67.0",
|
|
50
51
|
"openai": "^4.0.0",
|
|
51
52
|
"redis": "^4.7.1",
|
|
52
53
|
"resend": "^6.4.0",
|
|
53
54
|
"ssh2-sftp-client": "^12.0.1",
|
|
54
55
|
"stripe": "^14.0.0",
|
|
56
|
+
"terminal-kit": "^3.1.2",
|
|
55
57
|
"vm2": "^3.10.0",
|
|
56
58
|
"ws": "^8.18.0"
|
|
57
59
|
},
|
|
@@ -352,7 +352,7 @@ const getWebhookStats = asyncHandler(async (req, res) => {
|
|
|
352
352
|
const provisionCoolifyDeploy = asyncHandler(async (req, res) => {
|
|
353
353
|
try {
|
|
354
354
|
const { overwrite } = req.body;
|
|
355
|
-
const managePath = path.join(process.cwd(), "manage.
|
|
355
|
+
const managePath = path.join(process.cwd(), "manage.js");
|
|
356
356
|
const exists = fs.existsSync(managePath);
|
|
357
357
|
|
|
358
358
|
if (exists && !overwrite) {
|
|
@@ -363,13 +363,19 @@ const provisionCoolifyDeploy = asyncHandler(async (req, res) => {
|
|
|
363
363
|
});
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
-
//
|
|
367
|
-
|
|
366
|
+
// Copy the improved manage.js from polybot
|
|
367
|
+
const sourceManageJs = path.join(__dirname, "../../../manage.js");
|
|
368
|
+
if (fs.existsSync(sourceManageJs)) {
|
|
369
|
+
fs.copyFileSync(sourceManageJs, managePath);
|
|
370
|
+
// Make it executable
|
|
371
|
+
fs.chmodSync(managePath, '755');
|
|
372
|
+
}
|
|
373
|
+
|
|
368
374
|
res.json({
|
|
369
375
|
success: true,
|
|
370
376
|
message: exists
|
|
371
|
-
? "Coolify Headless Deploy script (manage.
|
|
372
|
-
: "Coolify Headless Deploy script (manage.
|
|
377
|
+
? "Coolify Headless Deploy script (manage.js) was updated."
|
|
378
|
+
: "Coolify Headless Deploy script (manage.js) is ready in the root directory.",
|
|
373
379
|
path: managePath,
|
|
374
380
|
});
|
|
375
381
|
} catch (error) {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const Agent = require('../models/Agent');
|
|
2
|
+
|
|
3
|
+
exports.listAgents = async (req, res) => {
|
|
4
|
+
try {
|
|
5
|
+
const agents = await Agent.find().lean();
|
|
6
|
+
return res.json({ items: agents });
|
|
7
|
+
} catch (error) {
|
|
8
|
+
return res.status(500).json({ error: error.message });
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
exports.createAgent = async (req, res) => {
|
|
13
|
+
try {
|
|
14
|
+
const agent = await Agent.create(req.body);
|
|
15
|
+
return res.json(agent);
|
|
16
|
+
} catch (error) {
|
|
17
|
+
return res.status(500).json({ error: error.message });
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
exports.updateAgent = async (req, res) => {
|
|
22
|
+
try {
|
|
23
|
+
const agent = await Agent.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
|
24
|
+
return res.json(agent);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
return res.status(500).json({ error: error.message });
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
exports.deleteAgent = async (req, res) => {
|
|
31
|
+
try {
|
|
32
|
+
await Agent.findByIdAndDelete(req.params.id);
|
|
33
|
+
return res.json({ success: true });
|
|
34
|
+
} catch (error) {
|
|
35
|
+
return res.status(500).json({ error: error.message });
|
|
36
|
+
}
|
|
37
|
+
};
|
|
@@ -387,6 +387,24 @@ async function listCosts(req, res) {
|
|
|
387
387
|
}
|
|
388
388
|
}
|
|
389
389
|
|
|
390
|
+
async function listProviders(req, res) {
|
|
391
|
+
try {
|
|
392
|
+
const providers = await getJsonSetting(PROVIDERS_KEY, {});
|
|
393
|
+
const safeProviders = {};
|
|
394
|
+
if (providers && typeof providers === "object") {
|
|
395
|
+
for (const [key, value] of Object.entries(providers)) {
|
|
396
|
+
if (!value || typeof value !== "object") continue;
|
|
397
|
+
const { apiKey, ...rest } = value;
|
|
398
|
+
safeProviders[key] = rest;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
res.json({ providers: safeProviders });
|
|
402
|
+
} catch (error) {
|
|
403
|
+
console.error("[adminLlm] listProviders error", error);
|
|
404
|
+
res.status(500).json({ error: "Failed to load providers" });
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
390
408
|
module.exports = {
|
|
391
409
|
getConfig,
|
|
392
410
|
saveConfig,
|
|
@@ -394,4 +412,5 @@ module.exports = {
|
|
|
394
412
|
listAudit,
|
|
395
413
|
listCosts,
|
|
396
414
|
listOpenRouterModels,
|
|
415
|
+
listProviders,
|
|
397
416
|
};
|
|
@@ -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
|
+
};
|
|
@@ -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
|
+
};
|
|
@@ -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
|
+
};
|
|
@@ -83,7 +83,7 @@ class MongooseHelper {
|
|
|
83
83
|
const uri = this.getMongoUri();
|
|
84
84
|
const options = this.getConnectionOptions();
|
|
85
85
|
|
|
86
|
-
console.log(`[MongooseHelper] Connecting to MongoDB...`);
|
|
86
|
+
if (!process.env.TUI_MODE) console.log(`[MongooseHelper] Connecting to MongoDB...`);
|
|
87
87
|
|
|
88
88
|
// Clear any existing connection
|
|
89
89
|
if (mongoose.connection.readyState !== 0) {
|
|
@@ -94,7 +94,7 @@ class MongooseHelper {
|
|
|
94
94
|
|
|
95
95
|
this.isConnected = true;
|
|
96
96
|
|
|
97
|
-
console.log(`[MongooseHelper] ✅ Connected to MongoDB`);
|
|
97
|
+
if (!process.env.TUI_MODE) console.log(`[MongooseHelper] ✅ Connected to MongoDB`);
|
|
98
98
|
|
|
99
99
|
// Setup connection error handling
|
|
100
100
|
mongoose.connection.on('error', (error) => {
|
|
@@ -104,13 +104,13 @@ class MongooseHelper {
|
|
|
104
104
|
});
|
|
105
105
|
|
|
106
106
|
mongoose.connection.on('disconnected', () => {
|
|
107
|
-
console.log('[MongooseHelper] Disconnected from MongoDB');
|
|
107
|
+
if (!process.env.TUI_MODE) console.log('[MongooseHelper] Disconnected from MongoDB');
|
|
108
108
|
this.isConnected = false;
|
|
109
109
|
this.connectionPromise = null;
|
|
110
110
|
});
|
|
111
111
|
|
|
112
112
|
mongoose.connection.on('reconnected', () => {
|
|
113
|
-
console.log('[MongooseHelper] Reconnected to MongoDB');
|
|
113
|
+
if (!process.env.TUI_MODE) console.log('[MongooseHelper] Reconnected to MongoDB');
|
|
114
114
|
this.isConnected = true;
|
|
115
115
|
});
|
|
116
116
|
|
|
@@ -136,7 +136,7 @@ class MongooseHelper {
|
|
|
136
136
|
if (this.connectionCount <= 0) {
|
|
137
137
|
try {
|
|
138
138
|
await mongoose.disconnect();
|
|
139
|
-
console.log('[MongooseHelper] ✅ Disconnected from MongoDB');
|
|
139
|
+
if (!process.env.TUI_MODE) console.log('[MongooseHelper] ✅ Disconnected from MongoDB');
|
|
140
140
|
} catch (error) {
|
|
141
141
|
console.error('[MongooseHelper] Disconnect error:', error);
|
|
142
142
|
} finally {
|
|
@@ -155,7 +155,7 @@ class MongooseHelper {
|
|
|
155
155
|
try {
|
|
156
156
|
if (mongoose.connection.readyState !== 0) {
|
|
157
157
|
await mongoose.disconnect();
|
|
158
|
-
console.log('[MongooseHelper] ✅ Force disconnected from MongoDB');
|
|
158
|
+
if (!process.env.TUI_MODE) console.log('[MongooseHelper] ✅ Force disconnected from MongoDB');
|
|
159
159
|
}
|
|
160
160
|
} catch (error) {
|
|
161
161
|
console.error('[MongooseHelper] Force disconnect error:', error);
|
|
@@ -55,13 +55,13 @@ class ScriptBase {
|
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
try {
|
|
58
|
-
console.log(`[${this.name}] Starting script execution...`);
|
|
58
|
+
if (!process.env.TUI_MODE) console.log(`[${this.name}] Starting script execution...`);
|
|
59
59
|
|
|
60
60
|
const executionPromise = this._executeWithConnection();
|
|
61
61
|
const result = await Promise.race([executionPromise, timeoutPromise]);
|
|
62
62
|
|
|
63
63
|
const duration = Date.now() - this.startTime;
|
|
64
|
-
console.log(`[${this.name}] ✅ Completed in ${duration}ms`);
|
|
64
|
+
if (!process.env.TUI_MODE) console.log(`[${this.name}] ✅ Completed in ${duration}ms`);
|
|
65
65
|
|
|
66
66
|
return result;
|
|
67
67
|
} catch (error) {
|