blockmine 1.0.6 → 1.0.8
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/README.md +1 -1
- package/backend/cli.js +16 -5
- package/backend/package.json +1 -1
- package/backend/prisma/schema.prisma +1 -1
- package/backend/src/core/PluginManager.js +63 -45
- package/backend/src/core/commands/ping.js +1 -1
- package/package.json +1 -1
- package/backend/src/core/commands/warn.js +0 -40
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ BlockMine — это open-source решение для централизова
|
|
|
39
39
|
|
|
40
40
|
## 🚀 Ключевые возможности
|
|
41
41
|
|
|
42
|
-
* **💻 Современный веб-интерфейс**: Адаптивная панель на React и Tailwind CSS для управления ботами, серверами и плагинами
|
|
42
|
+
* **💻 Современный веб-интерфейс**: Адаптивная панель на React и Tailwind CSS для управления ботами, серверами и плагинами
|
|
43
43
|
* **🤖 Комплексное управление ботами**:
|
|
44
44
|
* Запуск, остановка и перезапуск ботов в один клик.
|
|
45
45
|
* Интерактивная консоль для каждого бота.
|
package/backend/cli.js
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
2
|
const fs = require('fs');
|
|
3
|
+
const os = require('os');
|
|
3
4
|
const path = require('path');
|
|
4
5
|
const { execSync } = require('child_process');
|
|
5
6
|
const { startServer } = require('./src/server.js');
|
|
6
7
|
|
|
8
|
+
|
|
9
|
+
const DATA_DIR = path.join(os.homedir(), '.blockmine');
|
|
10
|
+
if (!fs.existsSync(DATA_DIR)) {
|
|
11
|
+
console.log(`[BlockMine] Создание папки для данных: ${DATA_DIR}`);
|
|
12
|
+
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
process.env.DATABASE_URL = `file:${path.join(DATA_DIR, 'blockmine.db')}`;
|
|
16
|
+
|
|
7
17
|
const prismaSchemaPath = path.join(__dirname, 'prisma', 'schema.prisma');
|
|
8
18
|
|
|
19
|
+
|
|
9
20
|
function runCommand(command) {
|
|
10
21
|
try {
|
|
11
22
|
console.log(`> ${command}`);
|
|
@@ -18,12 +29,12 @@ function runCommand(command) {
|
|
|
18
29
|
|
|
19
30
|
async function main() {
|
|
20
31
|
console.log('Запуск панели управления BlockMine...');
|
|
21
|
-
|
|
32
|
+
console.log(`[BlockMine] Хранилище данных: ${DATA_DIR}`);
|
|
22
33
|
|
|
23
34
|
const migrationsPath = path.join(__dirname, 'prisma', 'migrations');
|
|
24
|
-
if (!fs.existsSync(
|
|
25
|
-
console.log('
|
|
26
|
-
runCommand(`npx prisma migrate
|
|
35
|
+
if (!fs.existsSync(path.join(DATA_DIR, 'blockmine.db'))) {
|
|
36
|
+
console.log('База данных не найдена. Создаем и применяем все миграции...');
|
|
37
|
+
runCommand(`npx prisma migrate deploy --schema=${prismaSchemaPath}`);
|
|
27
38
|
console.log('Первоначальная настройка базы данных завершена.');
|
|
28
39
|
} else {
|
|
29
40
|
console.log('Проверка и применение обновлений базы данных...');
|
package/backend/package.json
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
|
|
2
|
-
const { simpleGit } = require('simple-git');
|
|
3
2
|
const path = require('path');
|
|
4
3
|
const fs = require('fs/promises');
|
|
4
|
+
const os = require('os');
|
|
5
5
|
const { PrismaClient } = require('@prisma/client');
|
|
6
|
-
const
|
|
6
|
+
const AdmZip = require('adm-zip');
|
|
7
7
|
const semver = require('semver');
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const prisma = new PrismaClient();
|
|
10
|
+
|
|
11
|
+
const DATA_DIR = path.join(os.homedir(), '.blockmine');
|
|
12
|
+
const PLUGINS_BASE_DIR = path.join(DATA_DIR, 'storage', 'plugins');
|
|
10
13
|
|
|
11
14
|
class PluginManager {
|
|
12
15
|
constructor() {
|
|
13
|
-
this.git = simpleGit();
|
|
14
16
|
this.ensureBaseDirExists();
|
|
15
17
|
}
|
|
16
18
|
|
|
@@ -25,34 +27,61 @@ class PluginManager {
|
|
|
25
27
|
async installFromGithub(botId, repoUrl, prismaClient = prisma) {
|
|
26
28
|
const botPluginsDir = path.join(PLUGINS_BASE_DIR, `bot_${botId}`);
|
|
27
29
|
await fs.mkdir(botPluginsDir, { recursive: true });
|
|
28
|
-
|
|
29
|
-
const localPath = path.join(botPluginsDir, repoName);
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
const existing = await prismaClient.installedPlugin.findFirst({ where: { botId, sourceUri: repoUrl } });
|
|
32
32
|
if (existing) throw new Error(`Плагин из ${repoUrl} уже установлен.`);
|
|
33
33
|
|
|
34
34
|
try {
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
const url = new URL(repoUrl);
|
|
36
|
+
const repoPath = url.pathname.replace(/^\/|\.git$/g, '');
|
|
37
|
+
const archiveUrlMain = `https://github.com/${repoPath}/archive/refs/heads/main.zip`;
|
|
38
|
+
const archiveUrlMaster = `https://github.com/${repoPath}/archive/refs/heads/master.zip`;
|
|
39
|
+
|
|
40
|
+
let response = await fetch(archiveUrlMain);
|
|
41
|
+
if (!response.ok) {
|
|
42
|
+
console.log(`[PluginManager] Ветка 'main' не найдена для ${repoUrl}, пробую 'master'...`);
|
|
43
|
+
response = await fetch(archiveUrlMaster);
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
throw new Error(`Не удалось скачать архив плагина. Статус: ${response.status}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const buffer = await response.arrayBuffer();
|
|
49
|
+
|
|
50
|
+
const zip = new AdmZip(Buffer.from(buffer));
|
|
51
|
+
const zipEntries = zip.getEntries();
|
|
52
|
+
if (zipEntries.length === 0) {
|
|
53
|
+
throw new Error('Скачанный архив плагина пуст.');
|
|
43
54
|
}
|
|
55
|
+
const rootFolderName = zipEntries[0].entryName.split('/')[0];
|
|
56
|
+
const repoName = path.basename(repoPath);
|
|
57
|
+
const localPath = path.join(botPluginsDir, repoName);
|
|
58
|
+
|
|
59
|
+
zip.extractAllTo(botPluginsDir, true);
|
|
60
|
+
|
|
61
|
+
await fs.rename(path.join(botPluginsDir, rootFolderName), localPath);
|
|
62
|
+
|
|
44
63
|
return await this.registerPlugin(botId, localPath, 'GITHUB', repoUrl, prismaClient);
|
|
64
|
+
|
|
45
65
|
} catch (error) {
|
|
46
|
-
|
|
66
|
+
console.error(`[PluginManager] Ошибка установки с GitHub: ${error.message}`);
|
|
67
|
+
if (error.message.includes('fetch')) {
|
|
68
|
+
throw new Error(`Не удалось подключиться к GitHub или репозиторий не найден. Проверьте ссылку и ваше интернет-соединение.`);
|
|
69
|
+
}
|
|
47
70
|
throw error;
|
|
48
71
|
}
|
|
49
72
|
}
|
|
50
73
|
|
|
51
74
|
async registerPlugin(botId, directoryPath, sourceType, sourceUri, prismaClient = prisma) {
|
|
52
75
|
const packageJsonPath = path.join(directoryPath, 'package.json');
|
|
53
|
-
|
|
76
|
+
let packageJson;
|
|
77
|
+
try {
|
|
78
|
+
packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
|
|
79
|
+
} catch (e) {
|
|
80
|
+
throw new Error(`Не удалось прочитать или распарсить package.json в плагине по пути: ${directoryPath}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
54
83
|
if (!packageJson.name || !packageJson.version) {
|
|
55
|
-
throw new Error('package.json не содержит name и version');
|
|
84
|
+
throw new Error('package.json не содержит обязательных полей name и version');
|
|
56
85
|
}
|
|
57
86
|
|
|
58
87
|
const pluginData = {
|
|
@@ -77,6 +106,8 @@ class PluginManager {
|
|
|
77
106
|
const mainFile = manifest.main || 'index.js';
|
|
78
107
|
const entryPointPath = path.join(plugin.path, mainFile);
|
|
79
108
|
|
|
109
|
+
await fs.access(entryPointPath);
|
|
110
|
+
|
|
80
111
|
const pluginModule = require(entryPointPath);
|
|
81
112
|
|
|
82
113
|
if (pluginModule && typeof pluginModule.onUnload === 'function') {
|
|
@@ -89,7 +120,9 @@ class PluginManager {
|
|
|
89
120
|
}
|
|
90
121
|
|
|
91
122
|
if (plugin.sourceType === 'GITHUB' || plugin.sourceType === 'IMPORTED') {
|
|
92
|
-
await fs.rm(plugin.path, { recursive: true, force: true })
|
|
123
|
+
await fs.rm(plugin.path, { recursive: true, force: true }).catch(err => {
|
|
124
|
+
console.error(`Не удалось удалить папку плагина ${plugin.path}:`, err);
|
|
125
|
+
});
|
|
93
126
|
}
|
|
94
127
|
await prisma.installedPlugin.delete({ where: { id: pluginId } });
|
|
95
128
|
}
|
|
@@ -106,8 +139,8 @@ class PluginManager {
|
|
|
106
139
|
const catalogInfo = catalogMap.get(plugin.sourceUri);
|
|
107
140
|
if (!catalogInfo || !catalogInfo.latestTag) continue;
|
|
108
141
|
|
|
109
|
-
const localVersion = plugin.version;
|
|
110
|
-
const recommendedVersion = semver.
|
|
142
|
+
const localVersion = semver.coerce(plugin.version)?.version || plugin.version;
|
|
143
|
+
const recommendedVersion = semver.coerce(catalogInfo.latestTag)?.version || catalogInfo.latestTag;
|
|
111
144
|
|
|
112
145
|
if (semver.gt(recommendedVersion, localVersion)) {
|
|
113
146
|
updatesAvailable.push({
|
|
@@ -131,30 +164,15 @@ class PluginManager {
|
|
|
131
164
|
throw new Error('Плагин не найден или не является GitHub-плагином.');
|
|
132
165
|
}
|
|
133
166
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
const recommendedTag = catalogInfo.latestTag;
|
|
145
|
-
await git.fetch(['--tags', '--force']);
|
|
146
|
-
await git.checkout(recommendedTag, ['-f']);
|
|
147
|
-
|
|
148
|
-
console.log(`[PluginManager] Плагин ${plugin.name} успешно обновлен до версии ${recommendedTag}.`);
|
|
149
|
-
|
|
150
|
-
await prisma.installedPlugin.delete({ where: { id: plugin.id } });
|
|
151
|
-
return await this.registerPlugin(plugin.botId, pluginDirectory, 'GITHUB', plugin.sourceUri);
|
|
152
|
-
|
|
153
|
-
} catch (error) {
|
|
154
|
-
console.error(`[PluginManager] Ошибка обновления плагина ${plugin.name}:`, error.message);
|
|
155
|
-
try { await simpleGit(pluginDirectory).checkout('main', ['-f']); } catch(e) { }
|
|
156
|
-
throw new Error(`Не удалось обновить плагин: ${error.message}`);
|
|
157
|
-
}
|
|
167
|
+
console.log(`[PluginManager] Начало обновления плагина ${plugin.name}...`);
|
|
168
|
+
|
|
169
|
+
const repoUrl = plugin.sourceUri;
|
|
170
|
+
const botId = plugin.botId;
|
|
171
|
+
|
|
172
|
+
await this.deletePlugin(pluginId);
|
|
173
|
+
console.log(`[PluginManager] Старая версия ${plugin.name} удалена, устанавливаем новую...`);
|
|
174
|
+
|
|
175
|
+
return await this.installFromGithub(botId, repoUrl);
|
|
158
176
|
}
|
|
159
177
|
}
|
|
160
178
|
|
|
@@ -27,7 +27,7 @@ class PingCommand extends Command {
|
|
|
27
27
|
if (target) {
|
|
28
28
|
bot.api.sendMessage(typeChat, `Понг, ${user.username}! Пингую игрока ${target}.`, user.username);
|
|
29
29
|
} else {
|
|
30
|
-
bot.api.sendMessage(typeChat,
|
|
30
|
+
bot.api.sendMessage(typeChat, `Понг, ${user.username}!`, user.username);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
}
|
package/package.json
CHANGED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
const Command = require('../system/Command');
|
|
2
|
-
|
|
3
|
-
class WarnCommand extends Command {
|
|
4
|
-
constructor() {
|
|
5
|
-
super({
|
|
6
|
-
name: 'warn',
|
|
7
|
-
description: 'Выдать предупреждение игроку.',
|
|
8
|
-
aliases: ['пред'],
|
|
9
|
-
cooldown: 10,
|
|
10
|
-
|
|
11
|
-
permissions: 'admin.warn',
|
|
12
|
-
|
|
13
|
-
owner: 'system',
|
|
14
|
-
allowedChatTypes: ['chat', 'private'],
|
|
15
|
-
|
|
16
|
-
args: [
|
|
17
|
-
{
|
|
18
|
-
name: 'target',
|
|
19
|
-
type: 'string',
|
|
20
|
-
required: true,
|
|
21
|
-
description: 'Ник игрока'
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
name: 'reason',
|
|
25
|
-
type: 'greedy_string',
|
|
26
|
-
required: true,
|
|
27
|
-
description: 'Причина предупреждения'
|
|
28
|
-
}
|
|
29
|
-
]
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async handler(bot, typeChat, user, { target, reason }) {
|
|
34
|
-
|
|
35
|
-
bot.api.sendMessage(
|
|
36
|
-
typeChat, `${user.username} выдал предупреждение игроку ${target}. Причина: ${reason}`);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
module.exports = WarnCommand;
|