blockmine 1.18.3 → 1.19.0
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/CHANGELOG.md +144 -107
- package/backend/cli.js +59 -57
- package/backend/prisma/migrations/20250701190321_add/migration.sql +2 -2
- package/backend/prisma/migrations/20250709150611_add_run_on_startup_to_tasks/migration.sql +21 -21
- package/backend/prisma/migrations/20250709151124_make_cron_pattern_optional/migration.sql +21 -21
- package/backend/prisma/migrations/20250718181335_add_plugin_data_store/migration.sql +14 -14
- package/backend/prisma/migrations/20250719115906_add_plugin_owner_to_graphs/migration.sql +45 -45
- package/backend/prisma/migrations/20250723160648_add_bot_sort_order/migration.sql +2 -2
- package/backend/prisma/migrations/20250816083216_add_panel_user_bot_access/migration.sql +30 -0
- package/backend/prisma/migrations/migration_lock.toml +2 -2
- package/backend/prisma/schema.prisma +244 -229
- package/backend/src/api/middleware/botAccess.js +35 -0
- package/backend/src/api/routes/auth.js +633 -595
- package/backend/src/api/routes/bots.js +292 -68
- package/backend/src/api/routes/eventGraphs.js +459 -459
- package/backend/src/api/routes/servers.js +27 -0
- package/backend/src/core/GraphExecutionEngine.js +917 -917
- package/backend/src/core/PluginLoader.js +208 -86
- package/backend/src/core/PluginManager.js +465 -427
- package/backend/src/core/commands/dev.js +6 -1
- package/backend/src/real-time/presence.js +74 -0
- package/backend/src/real-time/socketHandler.js +2 -0
- package/backend/src/server.js +193 -186
- package/frontend/dist/assets/{index-BqqUSU9S.js → index-5m_JZxJ-.js} +1693 -1688
- package/frontend/dist/assets/index-BFd7YoAj.css +1 -0
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/index-THQP1_d3.css +0 -1
|
@@ -52,7 +52,12 @@ class DevCommand extends Command {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
const enabledPluginsCount = bot.config.plugins.length;
|
|
55
|
-
|
|
55
|
+
|
|
56
|
+
const uniqueCommands = new Set();
|
|
57
|
+
for (const command of bot.commands.values()) {
|
|
58
|
+
uniqueCommands.add(command.name);
|
|
59
|
+
}
|
|
60
|
+
const totalCommandsCount = uniqueCommands.size;
|
|
56
61
|
|
|
57
62
|
bot.api.sendMessage(typeChat, `Бот создан с помощью - BlockMine. Версия: v${appVersion}. Активных плагинов: ${enabledPluginsCount}. Всего команд: ${totalCommandsCount}`, user.username);
|
|
58
63
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const jwt = require('jsonwebtoken');
|
|
2
|
+
const config = require('../config');
|
|
3
|
+
|
|
4
|
+
const JWT_SECRET = config.security.jwtSecret;
|
|
5
|
+
|
|
6
|
+
const presenceMap = new Map();
|
|
7
|
+
|
|
8
|
+
const HEARTBEAT_TTL_MS = 60 * 1000;
|
|
9
|
+
|
|
10
|
+
function verifyToken(token) {
|
|
11
|
+
try {
|
|
12
|
+
return jwt.verify(token, JWT_SECRET, { algorithms: ['HS256'] });
|
|
13
|
+
} catch (e) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function broadcast(io) {
|
|
19
|
+
const now = Date.now();
|
|
20
|
+
for (const [userId, info] of presenceMap.entries()) {
|
|
21
|
+
if (now - info.lastSeen > HEARTBEAT_TTL_MS) {
|
|
22
|
+
presenceMap.delete(userId);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const list = Array.from(presenceMap.entries()).map(([userId, info]) => ({
|
|
26
|
+
userId: Number(userId),
|
|
27
|
+
username: info.username,
|
|
28
|
+
path: info.path || '/',
|
|
29
|
+
lastSeen: info.lastSeen,
|
|
30
|
+
}));
|
|
31
|
+
io.emit('presence:list', list);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function handleConnection(io, socket) {
|
|
35
|
+
const token = socket.handshake?.auth?.token;
|
|
36
|
+
const decoded = verifyToken(token);
|
|
37
|
+
if (!decoded) {
|
|
38
|
+
return socket.disconnect(true);
|
|
39
|
+
}
|
|
40
|
+
const { userId, username } = decoded;
|
|
41
|
+
presenceMap.set(userId, { username, socketId: socket.id, lastSeen: Date.now(), path: '/' });
|
|
42
|
+
broadcast(io);
|
|
43
|
+
|
|
44
|
+
socket.on('presence:heartbeat', () => {
|
|
45
|
+
const info = presenceMap.get(userId);
|
|
46
|
+
if (info) {
|
|
47
|
+
info.lastSeen = Date.now();
|
|
48
|
+
presenceMap.set(userId, info);
|
|
49
|
+
broadcast(io);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
socket.on('presence:update', ({ path }) => {
|
|
54
|
+
const info = presenceMap.get(userId) || { username, socketId: socket.id };
|
|
55
|
+
info.lastSeen = Date.now();
|
|
56
|
+
info.path = typeof path === 'string' ? path : '/';
|
|
57
|
+
presenceMap.set(userId, info);
|
|
58
|
+
broadcast(io);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
socket.on('disconnect', () => {
|
|
62
|
+
for (const [uid, info] of presenceMap.entries()) {
|
|
63
|
+
if (info.socketId === socket.id) {
|
|
64
|
+
presenceMap.delete(uid);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
broadcast(io);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = {
|
|
72
|
+
handleConnection,
|
|
73
|
+
broadcast,
|
|
74
|
+
};
|
|
@@ -2,6 +2,7 @@ const { Server } = require('socket.io');
|
|
|
2
2
|
const config = require('../config');
|
|
3
3
|
|
|
4
4
|
const { botManager } = require('../core/services');
|
|
5
|
+
const presence = require('./presence');
|
|
5
6
|
|
|
6
7
|
let io;
|
|
7
8
|
|
|
@@ -18,6 +19,7 @@ function initializeSocket(httpServer) {
|
|
|
18
19
|
});
|
|
19
20
|
|
|
20
21
|
io.on('connection', (socket) => {
|
|
22
|
+
presence.handleConnection(io, socket);
|
|
21
23
|
|
|
22
24
|
socket.on('disconnect', () => {
|
|
23
25
|
botManager.handleSocketDisconnect(socket);
|
package/backend/src/server.js
CHANGED
|
@@ -1,187 +1,194 @@
|
|
|
1
|
-
const express = require('express');
|
|
2
|
-
const http = require('http');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const os = require('os');
|
|
6
|
-
const prisma = require('./lib/prisma');
|
|
7
|
-
|
|
8
|
-
const config = require('./config');
|
|
9
|
-
const { initializeSocket } = require('./real-time/socketHandler');
|
|
10
|
-
const { botManager, pluginManager } = require('./core/services');
|
|
11
|
-
|
|
12
|
-
const botRoutes = require('./api/routes/bots');
|
|
13
|
-
const pluginRoutes = require('./api/routes/plugins');
|
|
14
|
-
const serverRoutes = require('./api/routes/servers');
|
|
15
|
-
const permissionsRoutes = require('./api/routes/permissions');
|
|
16
|
-
const taskRoutes = require('./api/routes/tasks');
|
|
17
|
-
const { router: authRoutes, ALL_PERMISSIONS } = require('./api/routes/auth');
|
|
18
|
-
const searchRoutes = require('./api/routes/search');
|
|
19
|
-
const eventGraphsRouter = require('./api/routes/eventGraphs');
|
|
20
|
-
const TaskScheduler = require('./core/TaskScheduler');
|
|
21
|
-
const panelRoutes = require('./api/routes/panel');
|
|
22
|
-
const changelogRoutes = require('./api/routes/changelog');
|
|
23
|
-
const logsRoutes = require('./api/routes/logs');
|
|
24
|
-
|
|
25
|
-
const app = express();
|
|
26
|
-
const server = http.createServer(app);
|
|
27
|
-
|
|
28
|
-
initializeSocket(server);
|
|
29
|
-
|
|
30
|
-
app.set('botManager', botManager);
|
|
31
|
-
app.set('pluginManager', pluginManager);
|
|
32
|
-
|
|
33
|
-
const PORT = config.server.port;
|
|
34
|
-
const HOST = config.server.host;
|
|
35
|
-
|
|
36
|
-
app.use(express.json({ limit: '50mb' }));
|
|
37
|
-
app.use(express.urlencoded({ limit: '50mb', extended: true }));
|
|
38
|
-
|
|
39
|
-
const frontendPath = path.resolve(__dirname, '..', '..', 'frontend', 'dist');
|
|
40
|
-
const rootPath = path.resolve(__dirname, '..', '..');
|
|
41
|
-
|
|
42
|
-
app.use('/api/auth', authRoutes);
|
|
43
|
-
|
|
44
|
-
app.use('/api/version', (req, res, next) => {
|
|
45
|
-
async function getVersion() {
|
|
46
|
-
try {
|
|
47
|
-
const packageJsonPath = path.join(rootPath, 'package.json');
|
|
48
|
-
const packageJsonData = await fs.promises.readFile(packageJsonPath, 'utf-8');
|
|
49
|
-
const { version } = JSON.parse(packageJsonData);
|
|
50
|
-
res.json({ version });
|
|
51
|
-
} catch (error) {
|
|
52
|
-
console.error("Failed to read app version:", error);
|
|
53
|
-
res.status(500).json({ error: 'Could not retrieve app version.' });
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
getVersion();
|
|
57
|
-
});
|
|
58
|
-
app.use('/api/tasks', taskRoutes);
|
|
59
|
-
app.use('/api/bots', botRoutes);
|
|
60
|
-
app.use('/api/plugins', pluginRoutes);
|
|
61
|
-
app.use('/api/servers', serverRoutes);
|
|
62
|
-
app.use('/api/permissions', permissionsRoutes);
|
|
63
|
-
app.use('/api/search', searchRoutes);
|
|
64
|
-
app.use('/api/panel', panelRoutes);
|
|
65
|
-
app.use('/api/changelog', changelogRoutes);
|
|
66
|
-
app.use('/api/logs', logsRoutes);
|
|
67
|
-
|
|
68
|
-
app.use(express.static(frontendPath));
|
|
69
|
-
|
|
70
|
-
app.get(/^(?!\/api).*/, (req, res) => {
|
|
71
|
-
const indexPath = path.join(frontendPath, 'index.html');
|
|
72
|
-
|
|
73
|
-
if (fs.existsSync(indexPath)) {
|
|
74
|
-
res.sendFile(indexPath);
|
|
75
|
-
} else {
|
|
76
|
-
console.error(`Критическая ошибка: файл index.html не найден по пути ${indexPath}`);
|
|
77
|
-
res.status(404).send(
|
|
78
|
-
'<h1>Файлы фронтенда не найдены! Соберите фронтенд командой "npm run build --workspace=frontend"</h1>'
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
async function runStartupMigrations() {
|
|
84
|
-
try {
|
|
85
|
-
const adminRole = await prisma.panelRole.findUnique({ where: { name: 'Admin' } });
|
|
86
|
-
if (adminRole) {
|
|
87
|
-
const permissions = JSON.parse(adminRole.permissions);
|
|
88
|
-
if (permissions.includes('*')) {
|
|
89
|
-
const newPermissions = ALL_PERMISSIONS
|
|
90
|
-
.map(p => p.id)
|
|
91
|
-
.filter(id => id !== '*');
|
|
92
|
-
|
|
93
|
-
await prisma.panelRole.update({
|
|
94
|
-
where: { id: adminRole.id },
|
|
95
|
-
data: { permissions: JSON.stringify(newPermissions) }
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const
|
|
161
|
-
if (
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
console.log('[Shutdown]
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
process.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const http = require('http');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const prisma = require('./lib/prisma');
|
|
7
|
+
|
|
8
|
+
const config = require('./config');
|
|
9
|
+
const { initializeSocket } = require('./real-time/socketHandler');
|
|
10
|
+
const { botManager, pluginManager } = require('./core/services');
|
|
11
|
+
|
|
12
|
+
const botRoutes = require('./api/routes/bots');
|
|
13
|
+
const pluginRoutes = require('./api/routes/plugins');
|
|
14
|
+
const serverRoutes = require('./api/routes/servers');
|
|
15
|
+
const permissionsRoutes = require('./api/routes/permissions');
|
|
16
|
+
const taskRoutes = require('./api/routes/tasks');
|
|
17
|
+
const { router: authRoutes, ALL_PERMISSIONS, VIEWER_PERMISSIONS } = require('./api/routes/auth');
|
|
18
|
+
const searchRoutes = require('./api/routes/search');
|
|
19
|
+
const eventGraphsRouter = require('./api/routes/eventGraphs');
|
|
20
|
+
const TaskScheduler = require('./core/TaskScheduler');
|
|
21
|
+
const panelRoutes = require('./api/routes/panel');
|
|
22
|
+
const changelogRoutes = require('./api/routes/changelog');
|
|
23
|
+
const logsRoutes = require('./api/routes/logs');
|
|
24
|
+
|
|
25
|
+
const app = express();
|
|
26
|
+
const server = http.createServer(app);
|
|
27
|
+
|
|
28
|
+
initializeSocket(server);
|
|
29
|
+
|
|
30
|
+
app.set('botManager', botManager);
|
|
31
|
+
app.set('pluginManager', pluginManager);
|
|
32
|
+
|
|
33
|
+
const PORT = config.server.port;
|
|
34
|
+
const HOST = config.server.host;
|
|
35
|
+
|
|
36
|
+
app.use(express.json({ limit: '50mb' }));
|
|
37
|
+
app.use(express.urlencoded({ limit: '50mb', extended: true }));
|
|
38
|
+
|
|
39
|
+
const frontendPath = path.resolve(__dirname, '..', '..', 'frontend', 'dist');
|
|
40
|
+
const rootPath = path.resolve(__dirname, '..', '..');
|
|
41
|
+
|
|
42
|
+
app.use('/api/auth', authRoutes);
|
|
43
|
+
|
|
44
|
+
app.use('/api/version', (req, res, next) => {
|
|
45
|
+
async function getVersion() {
|
|
46
|
+
try {
|
|
47
|
+
const packageJsonPath = path.join(rootPath, 'package.json');
|
|
48
|
+
const packageJsonData = await fs.promises.readFile(packageJsonPath, 'utf-8');
|
|
49
|
+
const { version } = JSON.parse(packageJsonData);
|
|
50
|
+
res.json({ version });
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error("Failed to read app version:", error);
|
|
53
|
+
res.status(500).json({ error: 'Could not retrieve app version.' });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
getVersion();
|
|
57
|
+
});
|
|
58
|
+
app.use('/api/tasks', taskRoutes);
|
|
59
|
+
app.use('/api/bots', botRoutes);
|
|
60
|
+
app.use('/api/plugins', pluginRoutes);
|
|
61
|
+
app.use('/api/servers', serverRoutes);
|
|
62
|
+
app.use('/api/permissions', permissionsRoutes);
|
|
63
|
+
app.use('/api/search', searchRoutes);
|
|
64
|
+
app.use('/api/panel', panelRoutes);
|
|
65
|
+
app.use('/api/changelog', changelogRoutes);
|
|
66
|
+
app.use('/api/logs', logsRoutes);
|
|
67
|
+
|
|
68
|
+
app.use(express.static(frontendPath));
|
|
69
|
+
|
|
70
|
+
app.get(/^(?!\/api).*/, (req, res) => {
|
|
71
|
+
const indexPath = path.join(frontendPath, 'index.html');
|
|
72
|
+
|
|
73
|
+
if (fs.existsSync(indexPath)) {
|
|
74
|
+
res.sendFile(indexPath);
|
|
75
|
+
} else {
|
|
76
|
+
console.error(`Критическая ошибка: файл index.html не найден по пути ${indexPath}`);
|
|
77
|
+
res.status(404).send(
|
|
78
|
+
'<h1>Файлы фронтенда не найдены! Соберите фронтенд командой "npm run build --workspace=frontend"</h1>'
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
async function runStartupMigrations() {
|
|
84
|
+
try {
|
|
85
|
+
const adminRole = await prisma.panelRole.findUnique({ where: { name: 'Admin' } });
|
|
86
|
+
if (adminRole) {
|
|
87
|
+
const permissions = JSON.parse(adminRole.permissions);
|
|
88
|
+
if (permissions.includes('*')) {
|
|
89
|
+
const newPermissions = ALL_PERMISSIONS
|
|
90
|
+
.map(p => p.id)
|
|
91
|
+
.filter(id => id !== '*');
|
|
92
|
+
|
|
93
|
+
await prisma.panelRole.update({
|
|
94
|
+
where: { id: adminRole.id },
|
|
95
|
+
data: { permissions: JSON.stringify(newPermissions) }
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Создаем/обновляем роль Viewer
|
|
101
|
+
const viewerRole = await prisma.panelRole.upsert({
|
|
102
|
+
where: { name: 'Viewer' },
|
|
103
|
+
update: { permissions: JSON.stringify(VIEWER_PERMISSIONS) },
|
|
104
|
+
create: { name: 'Viewer', permissions: JSON.stringify(VIEWER_PERMISSIONS) }
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const rootUser = await prisma.panelUser.findUnique({ where: { id: 1 }, include: { role: true } });
|
|
108
|
+
if (rootUser && rootUser.role) {
|
|
109
|
+
const allPermissions = ALL_PERMISSIONS.map(p => p.id).filter(id => id !== '*');
|
|
110
|
+
const currentPermissions = JSON.parse(rootUser.role.permissions);
|
|
111
|
+
|
|
112
|
+
if (JSON.stringify(allPermissions.sort()) !== JSON.stringify(currentPermissions.sort())) {
|
|
113
|
+
await prisma.panelRole.update({
|
|
114
|
+
where: { id: rootUser.role.id },
|
|
115
|
+
data: { permissions: JSON.stringify(allPermissions) }
|
|
116
|
+
});
|
|
117
|
+
console.log(`[Migration] Права для root-пользователя "${rootUser.username}" (ID: 1) были синхронизированы.`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error('[Migration] Ошибка во время миграции прав:', error);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function startServer() {
|
|
126
|
+
await runStartupMigrations();
|
|
127
|
+
return new Promise((resolve) => {
|
|
128
|
+
server.listen(PORT, HOST, async () => {
|
|
129
|
+
console.log(`\nBackend сервер успешно запущен на http://${HOST}:${PORT}`);
|
|
130
|
+
|
|
131
|
+
if (HOST === '0.0.0.0') {
|
|
132
|
+
const networkInterfaces = os.networkInterfaces();
|
|
133
|
+
console.log('Панель управления доступна по следующим адресам:');
|
|
134
|
+
console.log(` - Локально: http://localhost:${PORT}`);
|
|
135
|
+
Object.keys(networkInterfaces).forEach(ifaceName => {
|
|
136
|
+
networkInterfaces[ifaceName].forEach(iface => {
|
|
137
|
+
if (iface.family === 'IPv4' && !iface.internal) {
|
|
138
|
+
console.log(` - В сети: http://${iface.address}:${PORT}`);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
console.log(' - А также по вашему внешнему IP адресу.');
|
|
143
|
+
|
|
144
|
+
} else {
|
|
145
|
+
console.log(`Панель управления доступна по адресу: http://localhost:${PORT}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
await TaskScheduler.initialize();
|
|
149
|
+
resolve(server);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
const gracefulShutdown = async (signal) => {
|
|
156
|
+
console.log(`[Shutdown] Получен сигнал ${signal}. Начинаем корректное завершение...`);
|
|
157
|
+
|
|
158
|
+
TaskScheduler.shutdown();
|
|
159
|
+
|
|
160
|
+
const botIds = Array.from(botManager.bots.keys());
|
|
161
|
+
if (botIds.length > 0) {
|
|
162
|
+
console.log(`[Shutdown] Остановка ${botIds.length} активных ботов...`);
|
|
163
|
+
await Promise.all(botIds.map(botId => botManager.stopBot(botId)));
|
|
164
|
+
console.log('[Shutdown] Все боты остановлены.');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const io = require('./real-time/socketHandler').getIO();
|
|
168
|
+
if (io) {
|
|
169
|
+
io.close(async () => {
|
|
170
|
+
console.log('[Shutdown] WebSocket сервер закрыт.');
|
|
171
|
+
|
|
172
|
+
await new Promise(resolve => server.close(resolve));
|
|
173
|
+
console.log('[Shutdown] HTTP сервер закрыт.');
|
|
174
|
+
|
|
175
|
+
const prisma = require('./lib/prisma');
|
|
176
|
+
await prisma.$disconnect();
|
|
177
|
+
console.log('[Shutdown] Соединение с БД закрыто.');
|
|
178
|
+
|
|
179
|
+
console.log('[Shutdown] Корректное завершение выполнено.');
|
|
180
|
+
process.exit(0);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
process.on('SIGUSR2', () => gracefulShutdown('SIGUSR2 (nodemon)'));
|
|
186
|
+
process.on('SIGINT', () => gracefulShutdown('SIGINT (Ctrl+C)'));
|
|
187
|
+
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
module.exports = { startServer, app, server };
|
|
191
|
+
|
|
192
|
+
if (require.main === module) {
|
|
193
|
+
startServer();
|
|
187
194
|
}
|