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 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
- #!/usr/bin/env node
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(migrationsPath)) {
25
- console.log('Папка с миграциями не найдена. Создаем первоначальную миграцию...');
26
- runCommand(`npx prisma migrate dev --name init --schema=${prismaSchemaPath}`);
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('Проверка и применение обновлений базы данных...');
@@ -22,8 +22,8 @@
22
22
  "mineflayer": "^4.20.1",
23
23
  "multer": "^2.0.1",
24
24
  "pidusage": "^3.0.2",
25
+ "prisma": "^5.14.0",
25
26
  "semver": "^7.6.2",
26
- "simple-git": "^3.25.0",
27
27
  "socket.io": "^4.7.5",
28
28
  "socks": "^2.8.5"
29
29
  },
@@ -6,7 +6,7 @@ generator client {
6
6
 
7
7
  datasource db {
8
8
  provider = "sqlite"
9
- url = "file:./dev.db"
9
+ url = env("DATABASE_URL")
10
10
  }
11
11
 
12
12
  model Server {
@@ -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 prisma = new PrismaClient();
6
+ const AdmZip = require('adm-zip');
7
7
  const semver = require('semver');
8
8
 
9
- const PLUGINS_BASE_DIR = path.resolve(__dirname, '../../storage/plugins');
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
- const repoName = path.basename(repoUrl, '.git');
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
- await this.git.clone(repoUrl, localPath);
36
- const git = simpleGit(localPath);
37
- const response = await fetch("https://raw.githubusercontent.com/blockmineJS/official-plugins-list/main/index.json");
38
- const catalog = await response.json();
39
- const catalogInfo = catalog.find(p => p.repoUrl === repoUrl);
40
- if (catalogInfo && catalogInfo.latestTag) {
41
- await git.fetch(['--tags', '--force']);
42
- await git.checkout(catalogInfo.latestTag, ['-f']);
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
- await fs.rm(localPath, { recursive: true, force: true }).catch(() => {});
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
- const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
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.clean(catalogInfo.latestTag);
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
- const pluginDirectory = plugin.path;
135
- try {
136
- const git = simpleGit(pluginDirectory);
137
- const response = await fetch("https://raw.githubusercontent.com/blockmineJS/official-plugins-list/main/index.json");
138
- const catalog = await response.json();
139
- const catalogInfo = catalog.find(p => p.repoUrl === plugin.sourceUri);
140
-
141
- if (!catalogInfo || !catalogInfo.latestTag) {
142
- throw new Error('Не найдена информация о проверенной версии в каталоге.');
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, `В`, user.username);
30
+ bot.api.sendMessage(typeChat, `Понг, ${user.username}!`, user.username);
31
31
  }
32
32
  }
33
33
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  {
3
3
  "name": "blockmine",
4
- "version": "1.0.6",
4
+ "version": "1.0.8",
5
5
  "description": "Мощная панель управления ботами для Майнкрафта.",
6
6
  "author": "merka",
7
7
  "license": "MIT",
@@ -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;