blockmine 1.2.0 → 1.3.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/backend/cli.js +17 -15
- package/backend/nodemon.json +1 -1
- package/backend/package-lock.json +2539 -2539
- package/backend/package.json +36 -33
- package/backend/prisma/generated/client/default.d.ts +1 -0
- package/backend/prisma/generated/client/default.js +1 -0
- package/backend/prisma/generated/client/edge.d.ts +1 -0
- package/backend/prisma/generated/client/edge.js +280 -0
- package/backend/prisma/generated/client/index-browser.js +272 -0
- package/backend/prisma/generated/client/index.d.ts +16892 -0
- package/backend/prisma/generated/client/index.js +301 -0
- package/backend/prisma/generated/client/package.json +97 -0
- package/backend/prisma/generated/client/query_engine-windows.dll.node +0 -0
- package/backend/prisma/generated/client/runtime/edge-esm.js +31 -0
- package/backend/prisma/generated/client/runtime/edge.js +31 -0
- package/backend/prisma/generated/client/runtime/index-browser.d.ts +365 -0
- package/backend/prisma/generated/client/runtime/index-browser.js +13 -0
- package/backend/prisma/generated/client/runtime/library.d.ts +3403 -0
- package/backend/prisma/generated/client/runtime/library.js +143 -0
- package/backend/prisma/generated/client/runtime/react-native.js +80 -0
- package/backend/prisma/generated/client/runtime/wasm.js +32 -0
- package/backend/prisma/generated/client/schema.prisma +148 -0
- package/backend/prisma/generated/client/wasm.d.ts +1 -0
- package/backend/prisma/generated/client/wasm.js +272 -0
- package/backend/prisma/migrations/20250615232848_add_scheduled_tasks/migration.sql +13 -0
- package/backend/prisma/schema.prisma +150 -137
- package/backend/src/api/routes/tasks.js +95 -0
- package/backend/src/core/BotManager.js +8 -0
- package/backend/src/core/PluginManager.js +63 -17
- package/backend/src/core/TaskScheduler.js +117 -0
- package/backend/src/real-time/socketHandler.js +30 -30
- package/backend/src/server.js +22 -20
- package/frontend/dist/assets/index-4S5VJ11r.js +8179 -0
- package/frontend/dist/assets/index-B5_cke-P.css +1 -0
- package/frontend/dist/favicon.svg +2 -2
- package/frontend/dist/index.html +20 -20
- package/frontend/dist/logo.svg +178 -178
- package/frontend/dist/site.webmanifest +20 -20
- package/frontend/package.json +66 -65
- package/package.json +38 -38
- package/frontend/dist/assets/index-8hxjI7oG.css +0 -1
- package/frontend/dist/assets/index-Brxc-96r.js +0 -8179
|
@@ -28,6 +28,9 @@ const DATA_DIR = path.join(os.homedir(), '.blockmine');
|
|
|
28
28
|
if (TELEMETRY_ENABLED && STATS_SERVER_URL) {
|
|
29
29
|
const idPath = path.join(DATA_DIR, '.instance_id');
|
|
30
30
|
try {
|
|
31
|
+
if (!fs.existsSync(DATA_DIR)) {
|
|
32
|
+
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
33
|
+
}
|
|
31
34
|
instanceId = fs.readFileSync(idPath, 'utf-8');
|
|
32
35
|
} catch (e) {
|
|
33
36
|
instanceId = uuidv4();
|
|
@@ -348,6 +351,11 @@ class BotManager {
|
|
|
348
351
|
|
|
349
352
|
const allowedTypes = JSON.parse(dbCommand.allowedChatTypes || '[]');
|
|
350
353
|
if (!allowedTypes.includes(typeChat) && !user.isOwner) {
|
|
354
|
+
|
|
355
|
+
if (typeChat === 'global') {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
351
359
|
this._sendThrottledWarning(
|
|
352
360
|
botConfig.id,
|
|
353
361
|
username,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
|
-
const
|
|
2
|
+
const fse = require('fs-extra');
|
|
3
3
|
const os = require('os');
|
|
4
4
|
const { PrismaClient } = require('@prisma/client');
|
|
5
5
|
const AdmZip = require('adm-zip');
|
|
@@ -42,13 +42,13 @@ class PluginManager {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
async ensureBaseDirExists() {
|
|
45
|
-
await
|
|
45
|
+
await fse.mkdir(PLUGINS_BASE_DIR, { recursive: true }).catch(console.error);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
async installFromLocalPath(botId, directoryPath) {
|
|
49
49
|
const newPlugin = await this.registerPlugin(botId, directoryPath, 'LOCAL', directoryPath);
|
|
50
50
|
try {
|
|
51
|
-
const packageJson = JSON.parse(await
|
|
51
|
+
const packageJson = JSON.parse(await fse.readFile(path.join(directoryPath, 'package.json'), 'utf-8'));
|
|
52
52
|
reportPluginDownload(packageJson.name);
|
|
53
53
|
} catch(e) {
|
|
54
54
|
console.error('Не удалось прочитать package.json для отправки статистики локального плагина');
|
|
@@ -58,7 +58,7 @@ class PluginManager {
|
|
|
58
58
|
|
|
59
59
|
async installFromGithub(botId, repoUrl, prismaClient = prisma) {
|
|
60
60
|
const botPluginsDir = path.join(PLUGINS_BASE_DIR, `bot_${botId}`);
|
|
61
|
-
await
|
|
61
|
+
await fse.mkdir(botPluginsDir, { recursive: true });
|
|
62
62
|
|
|
63
63
|
const existing = await prismaClient.installedPlugin.findFirst({ where: { botId, sourceUri: repoUrl } });
|
|
64
64
|
if (existing) throw new Error(`Плагин из ${repoUrl} уже установлен.`);
|
|
@@ -88,13 +88,21 @@ class PluginManager {
|
|
|
88
88
|
const repoName = path.basename(repoPath);
|
|
89
89
|
const localPath = path.join(botPluginsDir, repoName);
|
|
90
90
|
|
|
91
|
+
if (await fse.pathExists(localPath)) {
|
|
92
|
+
await fse.remove(localPath);
|
|
93
|
+
}
|
|
94
|
+
const tempExtractPath = path.join(botPluginsDir, rootFolderName);
|
|
95
|
+
if (await fse.pathExists(tempExtractPath)) {
|
|
96
|
+
await fse.remove(tempExtractPath);
|
|
97
|
+
}
|
|
98
|
+
|
|
91
99
|
zip.extractAllTo(botPluginsDir, true);
|
|
92
100
|
|
|
93
|
-
await
|
|
101
|
+
await fse.move(tempExtractPath, localPath, { overwrite: true });
|
|
94
102
|
|
|
95
103
|
const newPlugin = await this.registerPlugin(botId, localPath, 'GITHUB', repoUrl, prismaClient);
|
|
96
104
|
|
|
97
|
-
const packageJson = JSON.parse(await
|
|
105
|
+
const packageJson = JSON.parse(await fse.readFile(path.join(localPath, 'package.json'), 'utf-8'));
|
|
98
106
|
reportPluginDownload(packageJson.name);
|
|
99
107
|
|
|
100
108
|
return newPlugin;
|
|
@@ -112,7 +120,7 @@ class PluginManager {
|
|
|
112
120
|
const packageJsonPath = path.join(directoryPath, 'package.json');
|
|
113
121
|
let packageJson;
|
|
114
122
|
try {
|
|
115
|
-
packageJson = JSON.parse(await
|
|
123
|
+
packageJson = JSON.parse(await fse.readFile(packageJsonPath, 'utf-8'));
|
|
116
124
|
} catch (e) {
|
|
117
125
|
throw new Error(`Не удалось прочитать или распарсить package.json в плагине по пути: ${directoryPath}`);
|
|
118
126
|
}
|
|
@@ -138,29 +146,67 @@ class PluginManager {
|
|
|
138
146
|
const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
|
|
139
147
|
if (!plugin) throw new Error('Плагин не найден');
|
|
140
148
|
|
|
149
|
+
const pluginOwnerId = `plugin:${plugin.name}`;
|
|
150
|
+
console.log(`[PluginManager] Начало удаления плагина ${plugin.name} (ID: ${plugin.id}) и его ресурсов.`);
|
|
151
|
+
console.log(`[PluginManager] Идентификатор владельца для очистки: ${pluginOwnerId}`);
|
|
152
|
+
|
|
141
153
|
try {
|
|
142
154
|
const manifest = plugin.manifest ? JSON.parse(plugin.manifest) : {};
|
|
143
155
|
const mainFile = manifest.main || 'index.js';
|
|
144
156
|
const entryPointPath = path.join(plugin.path, mainFile);
|
|
145
157
|
|
|
146
|
-
await
|
|
147
|
-
|
|
158
|
+
if (await fse.pathExists(entryPointPath)) {
|
|
159
|
+
if (require.cache[require.resolve(entryPointPath)]) {
|
|
160
|
+
delete require.cache[require.resolve(entryPointPath)];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const pluginModule = require(entryPointPath);
|
|
148
164
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
165
|
+
if (pluginModule && typeof pluginModule.onUnload === 'function') {
|
|
166
|
+
console.log(`[PluginManager] Вызов хука onUnload для плагина ${plugin.name}...`);
|
|
167
|
+
await pluginModule.onUnload({ botId: plugin.botId, prisma });
|
|
168
|
+
console.log(`[PluginManager] Хук onUnload для ${plugin.name} успешно выполнен.`);
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
console.warn(`[PluginManager] Главный файл плагина ${entryPointPath} не найден. Хук onUnload пропущен.`);
|
|
153
172
|
}
|
|
154
173
|
} catch (error) {
|
|
155
174
|
console.error(`[PluginManager] Ошибка при выполнении хука onUnload для плагина ${plugin.name}:`, error);
|
|
156
175
|
}
|
|
157
176
|
|
|
158
|
-
|
|
159
|
-
await
|
|
160
|
-
|
|
177
|
+
try {
|
|
178
|
+
await prisma.$transaction(async (tx) => {
|
|
179
|
+
const deletedCommands = await tx.command.deleteMany({
|
|
180
|
+
where: { botId: plugin.botId, owner: pluginOwnerId },
|
|
181
|
+
});
|
|
182
|
+
if (deletedCommands.count > 0) console.log(`[DB Cleanup] Удалено команд: ${deletedCommands.count}`);
|
|
183
|
+
|
|
184
|
+
const deletedPermissions = await tx.permission.deleteMany({
|
|
185
|
+
where: { botId: plugin.botId, owner: pluginOwnerId },
|
|
186
|
+
});
|
|
187
|
+
if (deletedPermissions.count > 0) console.log(`[DB Cleanup] Удалено прав: ${deletedPermissions.count}`);
|
|
188
|
+
|
|
189
|
+
const deletedGroups = await tx.group.deleteMany({
|
|
190
|
+
where: { botId: plugin.botId, owner: pluginOwnerId },
|
|
191
|
+
});
|
|
192
|
+
if (deletedGroups.count > 0) console.log(`[DB Cleanup] Удалено групп: ${deletedGroups.count}`);
|
|
193
|
+
|
|
194
|
+
await tx.installedPlugin.delete({ where: { id: pluginId } });
|
|
195
|
+
console.log(`[DB Cleanup] Запись о плагине ${plugin.name} удалена.`);
|
|
161
196
|
});
|
|
197
|
+
} catch (dbError) {
|
|
198
|
+
console.error(`[PluginManager] Ошибка при очистке БД для плагина ${plugin.name}:`, dbError);
|
|
199
|
+
throw new Error('Ошибка при удалении данных плагина из БД. Файлы не были удалены.');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (plugin.sourceType === 'GITHUB' || plugin.sourceType === 'IMPORTED') {
|
|
203
|
+
try {
|
|
204
|
+
await fse.remove(plugin.path);
|
|
205
|
+
console.log(`[PluginManager] Папка плагина ${plugin.path} успешно удалена.`);
|
|
206
|
+
} catch (fileError) {
|
|
207
|
+
console.error(`Не удалось удалить папку плагина ${plugin.path}:`, fileError);
|
|
208
|
+
}
|
|
162
209
|
}
|
|
163
|
-
await prisma.installedPlugin.delete({ where: { id: pluginId } });
|
|
164
210
|
}
|
|
165
211
|
|
|
166
212
|
async checkForUpdates(botId, catalog) {
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
const cron = require('node-cron');
|
|
2
|
+
const { PrismaClient } = require('@prisma/client');
|
|
3
|
+
const BotManager = require('./BotManager');
|
|
4
|
+
|
|
5
|
+
const prisma = new PrismaClient();
|
|
6
|
+
|
|
7
|
+
class TaskScheduler {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.scheduledJobs = new Map();
|
|
10
|
+
console.log('[TaskScheduler] Сервис планировщика инициализирован.');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async initialize() {
|
|
14
|
+
console.log('[TaskScheduler] Загрузка и планирование активных задач из БД...');
|
|
15
|
+
const tasks = await prisma.scheduledTask.findMany({ where: { isEnabled: true } });
|
|
16
|
+
|
|
17
|
+
tasks.forEach(task => {
|
|
18
|
+
this.scheduleTask(task);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
console.log(`[TaskScheduler] Запланировано ${this.scheduledJobs.size} задач.`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async executeTask(task) {
|
|
25
|
+
console.log(`[TaskScheduler] Выполнение задачи: "${task.name}" (ID: ${task.id})`);
|
|
26
|
+
let botIds = [];
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const targetIds = JSON.parse(task.targetBotIds);
|
|
30
|
+
if (Array.isArray(targetIds) && targetIds[0] === 'ALL') {
|
|
31
|
+
const allBots = await prisma.bot.findMany({ select: { id: true } });
|
|
32
|
+
botIds = allBots.map(b => b.id);
|
|
33
|
+
} else {
|
|
34
|
+
botIds = targetIds.map(id => parseInt(id, 10));
|
|
35
|
+
}
|
|
36
|
+
} catch (e) {
|
|
37
|
+
console.error(`[TaskScheduler] Ошибка парсинга targetBotIds для задачи ${task.id}:`, e.message);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
for (const botId of botIds) {
|
|
42
|
+
try {
|
|
43
|
+
const botConfig = await prisma.bot.findUnique({ where: { id: botId }, include: { server: true } });
|
|
44
|
+
if (!botConfig) continue;
|
|
45
|
+
|
|
46
|
+
switch (task.action) {
|
|
47
|
+
case 'START_BOT':
|
|
48
|
+
console.log(` -> Запуск бота ${botConfig.username}`);
|
|
49
|
+
if (!BotManager.bots.has(botId)) await BotManager.startBot(botConfig);
|
|
50
|
+
break;
|
|
51
|
+
case 'STOP_BOT':
|
|
52
|
+
console.log(` -> Остановка бота ${botConfig.username}`);
|
|
53
|
+
if (BotManager.bots.has(botId)) BotManager.stopBot(botId);
|
|
54
|
+
break;
|
|
55
|
+
case 'RESTART_BOT':
|
|
56
|
+
console.log(` -> Перезапуск бота ${botConfig.username}`);
|
|
57
|
+
if (BotManager.bots.has(botId)) {
|
|
58
|
+
BotManager.stopBot(botId);
|
|
59
|
+
setTimeout(() => BotManager.startBot(botConfig), 5000);
|
|
60
|
+
} else {
|
|
61
|
+
await BotManager.startBot(botConfig);
|
|
62
|
+
}
|
|
63
|
+
break;
|
|
64
|
+
case 'SEND_COMMAND':
|
|
65
|
+
if (BotManager.bots.has(botId)) {
|
|
66
|
+
const payload = JSON.parse(task.payload || '{}');
|
|
67
|
+
if (payload.command) {
|
|
68
|
+
console.log(` -> Отправка команды "${payload.command}" боту ${botConfig.username}`);
|
|
69
|
+
BotManager.sendMessageToBot(botId, payload.command);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error(`[TaskScheduler] Ошибка выполнения действия для бота ID ${botId}:`, error);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
await prisma.scheduledTask.update({ where: { id: task.id }, data: { lastRun: new Date() } });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
scheduleTask(task) {
|
|
82
|
+
if (this.scheduledJobs.has(task.id)) {
|
|
83
|
+
this.unscheduleTask(task.id);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!cron.validate(task.cronPattern)) {
|
|
87
|
+
console.error(`[TaskScheduler] Неверный cron-паттерн для задачи ID ${task.id}: ${task.cronPattern}`);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const job = cron.schedule(task.cronPattern, () => this.executeTask(task), {
|
|
92
|
+
scheduled: true,
|
|
93
|
+
timezone: "Europe/Moscow"
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
this.scheduledJobs.set(task.id, job);
|
|
97
|
+
console.log(`[TaskScheduler] Задача "${task.name}" запланирована с паттерном: ${task.cronPattern}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
unscheduleTask(taskId) {
|
|
101
|
+
const job = this.scheduledJobs.get(taskId);
|
|
102
|
+
if (job) {
|
|
103
|
+
job.stop();
|
|
104
|
+
this.scheduledJobs.delete(taskId);
|
|
105
|
+
console.log(`[TaskScheduler] Задача ID ${taskId} снята с планирования.`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async updateTask(updatedTask) {
|
|
110
|
+
this.unscheduleTask(updatedTask.id);
|
|
111
|
+
if (updatedTask.isEnabled) {
|
|
112
|
+
this.scheduleTask(updatedTask);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = new TaskScheduler();
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
const { Server } = require('socket.io');
|
|
2
|
-
|
|
3
|
-
let io;
|
|
4
|
-
|
|
5
|
-
function initializeSocket(httpServer) {
|
|
6
|
-
io = new Server(httpServer, {
|
|
7
|
-
cors: {
|
|
8
|
-
origin: "http://localhost:5173",
|
|
9
|
-
methods: ["GET", "POST"]
|
|
10
|
-
}
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
io.on('connection', (socket) => {
|
|
14
|
-
console.log('A user connected to Socket.IO');
|
|
15
|
-
socket.on('disconnect', () => {
|
|
16
|
-
console.log('User disconnected');
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
console.log('Socket.IO initialized');
|
|
21
|
-
return io;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function getIO() {
|
|
25
|
-
if (!io) {
|
|
26
|
-
throw new Error("Socket.IO not initialized!");
|
|
27
|
-
}
|
|
28
|
-
return io;
|
|
29
|
-
}
|
|
30
|
-
|
|
1
|
+
const { Server } = require('socket.io');
|
|
2
|
+
|
|
3
|
+
let io;
|
|
4
|
+
|
|
5
|
+
function initializeSocket(httpServer) {
|
|
6
|
+
io = new Server(httpServer, {
|
|
7
|
+
cors: {
|
|
8
|
+
origin: "http://localhost:5173",
|
|
9
|
+
methods: ["GET", "POST"]
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
io.on('connection', (socket) => {
|
|
14
|
+
console.log('A user connected to Socket.IO');
|
|
15
|
+
socket.on('disconnect', () => {
|
|
16
|
+
console.log('User disconnected');
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
console.log('Socket.IO initialized');
|
|
21
|
+
return io;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getIO() {
|
|
25
|
+
if (!io) {
|
|
26
|
+
throw new Error("Socket.IO not initialized!");
|
|
27
|
+
}
|
|
28
|
+
return io;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
31
|
module.exports = { initializeSocket, getIO };
|
package/backend/src/server.js
CHANGED
|
@@ -2,22 +2,15 @@ const express = require('express');
|
|
|
2
2
|
const http = require('http');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const fs = require('fs');
|
|
5
|
-
const os = require('os');
|
|
6
|
-
|
|
7
|
-
const DATA_DIR = path.join(os.homedir(), '.blockmine');
|
|
8
|
-
if (!fs.existsSync(DATA_DIR)) {
|
|
9
|
-
console.log(`[Server] Создание папки для данных: ${DATA_DIR}`);
|
|
10
|
-
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
process.env.DATABASE_URL = `file:${path.join(DATA_DIR, 'blockmine.db')}`;
|
|
14
5
|
|
|
15
6
|
const { initializeSocket } = require('./real-time/socketHandler');
|
|
16
7
|
const botRoutes = require('./api/routes/bots');
|
|
17
8
|
const pluginRoutes = require('./api/routes/plugins');
|
|
18
9
|
const serverRoutes = require('./api/routes/servers');
|
|
19
10
|
const permissionsRoutes = require('./api/routes/permissions');
|
|
11
|
+
const taskRoutes = require('./api/routes/tasks');
|
|
20
12
|
const BotManager = require('./core/BotManager');
|
|
13
|
+
const TaskScheduler = require('./core/TaskScheduler');
|
|
21
14
|
|
|
22
15
|
const app = express();
|
|
23
16
|
const server = http.createServer(app);
|
|
@@ -28,8 +21,10 @@ const PORT = process.env.PORT || 3001;
|
|
|
28
21
|
|
|
29
22
|
app.use(express.json());
|
|
30
23
|
|
|
31
|
-
|
|
32
|
-
const
|
|
24
|
+
|
|
25
|
+
const frontendPath = path.resolve(__dirname, '..', '..', 'frontend', 'dist');
|
|
26
|
+
const rootPath = path.resolve(__dirname, '..', '..');
|
|
27
|
+
|
|
33
28
|
|
|
34
29
|
app.get('/api/version', async (req, res) => {
|
|
35
30
|
try {
|
|
@@ -43,27 +38,35 @@ app.get('/api/version', async (req, res) => {
|
|
|
43
38
|
}
|
|
44
39
|
});
|
|
45
40
|
|
|
41
|
+
app.use('/api/tasks', taskRoutes);
|
|
46
42
|
app.use('/api/bots', botRoutes);
|
|
47
43
|
app.use('/api/plugins', pluginRoutes);
|
|
48
44
|
app.use('/api/servers', serverRoutes);
|
|
49
45
|
app.use('/api/permissions', permissionsRoutes);
|
|
50
46
|
|
|
47
|
+
|
|
48
|
+
|
|
51
49
|
app.use(express.static(frontendPath));
|
|
52
50
|
|
|
53
51
|
app.get(/^(?!\/api).*/, (req, res) => {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
52
|
+
const indexPath = path.join(frontendPath, 'index.html');
|
|
53
|
+
|
|
54
|
+
if (fs.existsSync(indexPath)) {
|
|
55
|
+
res.sendFile(indexPath);
|
|
56
|
+
} else {
|
|
57
|
+
console.error(`Критическая ошибка: файл index.html не найден по пути ${indexPath}`);
|
|
58
|
+
res.status(404).send(
|
|
59
|
+
'<h1>Файлы фронтенда не найдены!1!!!!111</h1>'
|
|
60
|
+
);
|
|
61
|
+
}
|
|
60
62
|
});
|
|
61
63
|
|
|
62
64
|
async function startServer() {
|
|
63
65
|
return new Promise((resolve) => {
|
|
64
|
-
server.listen(PORT, () => {
|
|
66
|
+
server.listen(PORT, async () => {
|
|
65
67
|
console.log(`Backend сервер успешно запущен на http://localhost:${PORT}`);
|
|
66
68
|
console.log(`Панель управления доступна по адресу: http://localhost:${PORT}`);
|
|
69
|
+
await TaskScheduler.initialize();
|
|
67
70
|
resolve(server);
|
|
68
71
|
});
|
|
69
72
|
});
|
|
@@ -101,5 +104,4 @@ module.exports = { startServer, app, server };
|
|
|
101
104
|
|
|
102
105
|
if (require.main === module) {
|
|
103
106
|
startServer();
|
|
104
|
-
}
|
|
105
|
-
|
|
107
|
+
}
|