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.
Files changed (28) hide show
  1. package/CHANGELOG.md +144 -107
  2. package/backend/cli.js +59 -57
  3. package/backend/prisma/migrations/20250701190321_add/migration.sql +2 -2
  4. package/backend/prisma/migrations/20250709150611_add_run_on_startup_to_tasks/migration.sql +21 -21
  5. package/backend/prisma/migrations/20250709151124_make_cron_pattern_optional/migration.sql +21 -21
  6. package/backend/prisma/migrations/20250718181335_add_plugin_data_store/migration.sql +14 -14
  7. package/backend/prisma/migrations/20250719115906_add_plugin_owner_to_graphs/migration.sql +45 -45
  8. package/backend/prisma/migrations/20250723160648_add_bot_sort_order/migration.sql +2 -2
  9. package/backend/prisma/migrations/20250816083216_add_panel_user_bot_access/migration.sql +30 -0
  10. package/backend/prisma/migrations/migration_lock.toml +2 -2
  11. package/backend/prisma/schema.prisma +244 -229
  12. package/backend/src/api/middleware/botAccess.js +35 -0
  13. package/backend/src/api/routes/auth.js +633 -595
  14. package/backend/src/api/routes/bots.js +292 -68
  15. package/backend/src/api/routes/eventGraphs.js +459 -459
  16. package/backend/src/api/routes/servers.js +27 -0
  17. package/backend/src/core/GraphExecutionEngine.js +917 -917
  18. package/backend/src/core/PluginLoader.js +208 -86
  19. package/backend/src/core/PluginManager.js +465 -427
  20. package/backend/src/core/commands/dev.js +6 -1
  21. package/backend/src/real-time/presence.js +74 -0
  22. package/backend/src/real-time/socketHandler.js +2 -0
  23. package/backend/src/server.js +193 -186
  24. package/frontend/dist/assets/{index-BqqUSU9S.js → index-5m_JZxJ-.js} +1693 -1688
  25. package/frontend/dist/assets/index-BFd7YoAj.css +1 -0
  26. package/frontend/dist/index.html +2 -2
  27. package/package.json +1 -1
  28. package/frontend/dist/assets/index-THQP1_d3.css +0 -1
@@ -0,0 +1,30 @@
1
+ -- CreateTable
2
+ CREATE TABLE "PanelUserBotAccess" (
3
+ "userId" INTEGER NOT NULL,
4
+ "botId" INTEGER NOT NULL,
5
+
6
+ PRIMARY KEY ("userId", "botId"),
7
+ CONSTRAINT "PanelUserBotAccess_userId_fkey" FOREIGN KEY ("userId") REFERENCES "PanelUser" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
8
+ CONSTRAINT "PanelUserBotAccess_botId_fkey" FOREIGN KEY ("botId") REFERENCES "Bot" ("id") ON DELETE CASCADE ON UPDATE CASCADE
9
+ );
10
+
11
+ -- RedefineTables
12
+ PRAGMA defer_foreign_keys=ON;
13
+ PRAGMA foreign_keys=OFF;
14
+ CREATE TABLE "new_PanelUser" (
15
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
16
+ "uuid" TEXT NOT NULL,
17
+ "username" TEXT NOT NULL,
18
+ "passwordHash" TEXT NOT NULL,
19
+ "roleId" INTEGER NOT NULL,
20
+ "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
21
+ "allBots" BOOLEAN NOT NULL DEFAULT true,
22
+ CONSTRAINT "PanelUser_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "PanelRole" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
23
+ );
24
+ INSERT INTO "new_PanelUser" ("createdAt", "id", "passwordHash", "roleId", "username", "uuid") SELECT "createdAt", "id", "passwordHash", "roleId", "username", "uuid" FROM "PanelUser";
25
+ DROP TABLE "PanelUser";
26
+ ALTER TABLE "new_PanelUser" RENAME TO "PanelUser";
27
+ CREATE UNIQUE INDEX "PanelUser_uuid_key" ON "PanelUser"("uuid");
28
+ CREATE UNIQUE INDEX "PanelUser_username_key" ON "PanelUser"("username");
29
+ PRAGMA foreign_keys=ON;
30
+ PRAGMA defer_foreign_keys=OFF;
@@ -1,3 +1,3 @@
1
- # Please do not edit this file manually
2
- # It should be added in your version-control system (i.e. Git)
1
+ # Please do not edit this file manually
2
+ # It should be added in your version-control system (i.e. Git)
3
3
  provider = "sqlite"
@@ -1,229 +1,244 @@
1
- generator client {
2
- provider = "prisma-client-js"
3
- }
4
-
5
- datasource db {
6
- provider = "sqlite"
7
- url = env("DATABASE_URL")
8
- }
9
-
10
- model Server {
11
- id Int @id @default(autoincrement())
12
- name String @unique
13
- host String
14
- port Int @default(25565)
15
- version String
16
- bots Bot[]
17
- }
18
-
19
- model Bot {
20
- id Int @id @default(autoincrement())
21
- username String @unique
22
- password String?
23
- prefix String? @default("@")
24
- note String?
25
- owners String? @default("")
26
- sortOrder Int?
27
-
28
- server Server @relation(fields: [serverId], references: [id])
29
- serverId Int
30
-
31
- proxyHost String?
32
- proxyPort Int?
33
- proxyUsername String?
34
- proxyPassword String?
35
-
36
- installedPlugins InstalledPlugin[]
37
- users User[]
38
- groups Group[]
39
- permissions Permission[]
40
- commands Command[]
41
- eventGraphs EventGraph[]
42
- pluginData PluginDataStore[]
43
-
44
- createdAt DateTime @default(now())
45
- updatedAt DateTime @updatedAt
46
- }
47
-
48
-
49
- model InstalledPlugin {
50
- id Int @id @default(autoincrement())
51
- botId Int
52
- bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
53
- name String
54
- version String
55
- description String?
56
- sourceType String
57
- sourceUri String?
58
- path String
59
- isEnabled Boolean @default(true)
60
-
61
- manifest String?
62
- settings String? @default("{}")
63
-
64
- createdAt DateTime @default(now())
65
-
66
- commands Command[]
67
- eventGraphs EventGraph[]
68
-
69
- @@unique([botId, name])
70
- }
71
-
72
- model Command {
73
- id Int @id @default(autoincrement())
74
- botId Int
75
- bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
76
- name String
77
- isEnabled Boolean @default(true)
78
- cooldown Int @default(0)
79
- aliases String @default("[]")
80
- description String?
81
- owner String?
82
- permissionId Int?
83
- permission Permission? @relation(fields: [permissionId], references: [id], onDelete: SetNull)
84
- allowedChatTypes String @default("[\"chat\", \"private\"]")
85
-
86
- isVisual Boolean @default(false)
87
- argumentsJson String? @default("[]")
88
- graphJson String? @default("null")
89
-
90
- pluginOwnerId Int?
91
- pluginOwner InstalledPlugin? @relation(fields: [pluginOwnerId], references: [id], onDelete: SetNull)
92
-
93
- @@unique([botId, name])
94
- }
95
-
96
- model EventGraph {
97
- id Int @id @default(autoincrement())
98
- botId Int
99
- bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
100
- name String
101
- isEnabled Boolean @default(true)
102
- graphJson String? @default("null")
103
- variables String? @default("[]")
104
-
105
- triggers EventTrigger[]
106
-
107
- createdAt DateTime @default(now())
108
- updatedAt DateTime @updatedAt
109
-
110
- pluginOwnerId Int?
111
- pluginOwner InstalledPlugin? @relation(fields: [pluginOwnerId], references: [id], onDelete: SetNull)
112
-
113
- @@unique([botId, name])
114
- }
115
-
116
- model EventTrigger {
117
- id Int @id @default(autoincrement())
118
- graphId Int
119
- graph EventGraph @relation(fields: [graphId], references: [id], onDelete: Cascade)
120
- eventType String // e.g., "entitySpawn", "chat"
121
-
122
- @@unique([graphId, eventType])
123
- }
124
-
125
- model User {
126
- id Int @id @default(autoincrement())
127
- username String
128
- isBlacklisted Boolean @default(false)
129
- botId Int
130
- bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
131
- groups UserGroup[]
132
-
133
- @@unique([botId, username])
134
- }
135
-
136
- model Group {
137
- id Int @id @default(autoincrement())
138
- name String
139
- owner String @default("system")
140
- botId Int
141
- bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
142
- permissions GroupPermission[]
143
- users UserGroup[]
144
-
145
- @@unique([botId, name])
146
- }
147
-
148
- model Permission {
149
- id Int @id @default(autoincrement())
150
- name String
151
- description String?
152
- owner String @default("system")
153
- botId Int
154
- bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
155
- groups GroupPermission[]
156
- commands Command[]
157
-
158
- @@unique([botId, name])
159
- }
160
-
161
-
162
- model UserGroup {
163
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
164
- userId Int
165
- group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
166
- groupId Int
167
-
168
- @@id([userId, groupId])
169
- }
170
-
171
- model GroupPermission {
172
- group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
173
- groupId Int
174
- permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade)
175
- permissionId Int
176
-
177
- @@id([groupId, permissionId])
178
- }
179
-
180
-
181
-
182
- model ScheduledTask {
183
- id Int @id @default(autoincrement())
184
- name String
185
- cronPattern String?
186
- action String
187
- targetBotIds String // джсон массив ID ботов или "ALL"
188
- payload String? @default("{}") // джсон для доп данных, например, команды
189
- isEnabled Boolean @default(true)
190
- runOnStartup Boolean @default(false)
191
- lastRun DateTime?
192
- createdAt DateTime @default(now())
193
- updatedAt DateTime @updatedAt
194
- }
195
-
196
- model PanelUser {
197
- id Int @id @default(autoincrement())
198
- uuid String @unique @default(uuid())
199
- username String @unique
200
- passwordHash String
201
- role PanelRole @relation(fields: [roleId], references: [id])
202
- roleId Int
203
- createdAt DateTime @default(now())
204
- }
205
-
206
- // Роли пользователей (Admin, Moderator, Viewer)
207
- model PanelRole {
208
- id Int @id @default(autoincrement())
209
- name String @unique
210
-
211
- // Храним права как джсон строку. SQLite не поддерживает массивы.
212
- // Пример: '[\"bot:create\", \"bot:delete\", \"user:manage\"]'
213
- permissions String @default("[]")
214
- users PanelUser[]
215
- }
216
-
217
- model PluginDataStore {
218
- id Int @id @default(autoincrement())
219
- pluginName String
220
- botId Int
221
- key String
222
- value String // в виде json
223
- createdAt DateTime @default(now())
224
- updatedAt DateTime @updatedAt
225
-
226
- bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
227
-
228
- @@unique([pluginName, botId, key])
229
- }
1
+ generator client {
2
+ provider = "prisma-client-js"
3
+ }
4
+
5
+ datasource db {
6
+ provider = "sqlite"
7
+ url = env("DATABASE_URL")
8
+ }
9
+
10
+ model Server {
11
+ id Int @id @default(autoincrement())
12
+ name String @unique
13
+ host String
14
+ port Int @default(25565)
15
+ version String
16
+ bots Bot[]
17
+ }
18
+
19
+ model Bot {
20
+ id Int @id @default(autoincrement())
21
+ username String @unique
22
+ password String?
23
+ prefix String? @default("@")
24
+ note String?
25
+ owners String? @default("")
26
+ sortOrder Int?
27
+
28
+ server Server @relation(fields: [serverId], references: [id])
29
+ serverId Int
30
+
31
+ proxyHost String?
32
+ proxyPort Int?
33
+ proxyUsername String?
34
+ proxyPassword String?
35
+
36
+ installedPlugins InstalledPlugin[]
37
+ users User[]
38
+ groups Group[]
39
+ permissions Permission[]
40
+ commands Command[]
41
+ eventGraphs EventGraph[]
42
+ pluginData PluginDataStore[]
43
+
44
+ panelUserAccess PanelUserBotAccess[]
45
+
46
+ createdAt DateTime @default(now())
47
+ updatedAt DateTime @updatedAt
48
+ }
49
+
50
+
51
+ model InstalledPlugin {
52
+ id Int @id @default(autoincrement())
53
+ botId Int
54
+ bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
55
+ name String
56
+ version String
57
+ description String?
58
+ sourceType String
59
+ sourceUri String?
60
+ path String
61
+ isEnabled Boolean @default(true)
62
+
63
+ manifest String?
64
+ settings String? @default("{}")
65
+
66
+ createdAt DateTime @default(now())
67
+
68
+ commands Command[]
69
+ eventGraphs EventGraph[]
70
+
71
+ @@unique([botId, name])
72
+ }
73
+
74
+ model Command {
75
+ id Int @id @default(autoincrement())
76
+ botId Int
77
+ bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
78
+ name String
79
+ isEnabled Boolean @default(true)
80
+ cooldown Int @default(0)
81
+ aliases String @default("[]")
82
+ description String?
83
+ owner String?
84
+ permissionId Int?
85
+ permission Permission? @relation(fields: [permissionId], references: [id], onDelete: SetNull)
86
+ allowedChatTypes String @default("[\"chat\", \"private\"]")
87
+
88
+ isVisual Boolean @default(false)
89
+ argumentsJson String? @default("[]")
90
+ graphJson String? @default("null")
91
+
92
+ pluginOwnerId Int?
93
+ pluginOwner InstalledPlugin? @relation(fields: [pluginOwnerId], references: [id], onDelete: SetNull)
94
+
95
+ @@unique([botId, name])
96
+ }
97
+
98
+ model EventGraph {
99
+ id Int @id @default(autoincrement())
100
+ botId Int
101
+ bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
102
+ name String
103
+ isEnabled Boolean @default(true)
104
+ graphJson String? @default("null")
105
+ variables String? @default("[]")
106
+
107
+ triggers EventTrigger[]
108
+
109
+ createdAt DateTime @default(now())
110
+ updatedAt DateTime @updatedAt
111
+
112
+ pluginOwnerId Int?
113
+ pluginOwner InstalledPlugin? @relation(fields: [pluginOwnerId], references: [id], onDelete: SetNull)
114
+
115
+ @@unique([botId, name])
116
+ }
117
+
118
+ model EventTrigger {
119
+ id Int @id @default(autoincrement())
120
+ graphId Int
121
+ graph EventGraph @relation(fields: [graphId], references: [id], onDelete: Cascade)
122
+ eventType String // e.g., "entitySpawn", "chat"
123
+
124
+ @@unique([graphId, eventType])
125
+ }
126
+
127
+ model User {
128
+ id Int @id @default(autoincrement())
129
+ username String
130
+ isBlacklisted Boolean @default(false)
131
+ botId Int
132
+ bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
133
+ groups UserGroup[]
134
+
135
+ @@unique([botId, username])
136
+ }
137
+
138
+ model Group {
139
+ id Int @id @default(autoincrement())
140
+ name String
141
+ owner String @default("system")
142
+ botId Int
143
+ bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
144
+ permissions GroupPermission[]
145
+ users UserGroup[]
146
+
147
+ @@unique([botId, name])
148
+ }
149
+
150
+ model Permission {
151
+ id Int @id @default(autoincrement())
152
+ name String
153
+ description String?
154
+ owner String @default("system")
155
+ botId Int
156
+ bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
157
+ groups GroupPermission[]
158
+ commands Command[]
159
+
160
+ @@unique([botId, name])
161
+ }
162
+
163
+
164
+ model UserGroup {
165
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
166
+ userId Int
167
+ group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
168
+ groupId Int
169
+
170
+ @@id([userId, groupId])
171
+ }
172
+
173
+ model GroupPermission {
174
+ group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
175
+ groupId Int
176
+ permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade)
177
+ permissionId Int
178
+
179
+ @@id([groupId, permissionId])
180
+ }
181
+
182
+
183
+
184
+ model ScheduledTask {
185
+ id Int @id @default(autoincrement())
186
+ name String
187
+ cronPattern String?
188
+ action String
189
+ targetBotIds String // джсон массив ID ботов или "ALL"
190
+ payload String? @default("{}") // джсон для доп данных, например, команды
191
+ isEnabled Boolean @default(true)
192
+ runOnStartup Boolean @default(false)
193
+ lastRun DateTime?
194
+ createdAt DateTime @default(now())
195
+ updatedAt DateTime @updatedAt
196
+ }
197
+
198
+ model PanelUser {
199
+ id Int @id @default(autoincrement())
200
+ uuid String @unique @default(uuid())
201
+ username String @unique
202
+ passwordHash String
203
+ role PanelRole @relation(fields: [roleId], references: [id])
204
+ roleId Int
205
+ createdAt DateTime @default(now())
206
+
207
+ allBots Boolean @default(true)
208
+
209
+ botAccess PanelUserBotAccess[]
210
+ }
211
+
212
+ // Роли пользователей (Admin, Moderator, Viewer)
213
+ model PanelRole {
214
+ id Int @id @default(autoincrement())
215
+ name String @unique
216
+
217
+ // Храним права как джсон строку. SQLite не поддерживает массивы.
218
+ // Пример: '[\"bot:create\", \"bot:delete\", \"user:manage\"]'
219
+ permissions String @default("[]")
220
+ users PanelUser[]
221
+ }
222
+
223
+ model PanelUserBotAccess {
224
+ user PanelUser @relation(fields: [userId], references: [id], onDelete: Cascade)
225
+ userId Int
226
+ bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
227
+ botId Int
228
+
229
+ @@id([userId, botId])
230
+ }
231
+
232
+ model PluginDataStore {
233
+ id Int @id @default(autoincrement())
234
+ pluginName String
235
+ botId Int
236
+ key String
237
+ value String // в виде json
238
+ createdAt DateTime @default(now())
239
+ updatedAt DateTime @updatedAt
240
+
241
+ bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
242
+
243
+ @@unique([pluginName, botId, key])
244
+ }
@@ -0,0 +1,35 @@
1
+ const { PrismaClient } = require('@prisma/client');
2
+ const prisma = new PrismaClient();
3
+
4
+ /**
5
+ * Проверяет доступ текущего panelUser (из req.user.userId) к botId из params
6
+ * Если у пользователя allBots=true (или поля нет), доступ разрешен.
7
+ * Иначе проверяется наличие записи в PanelUserBotAccess.
8
+ */
9
+ async function checkBotAccess(req, res, next) {
10
+ try {
11
+ const userId = req.user?.userId;
12
+ if (!userId) return res.status(401).json({ error: 'Неавторизовано' });
13
+
14
+ const botIdRaw = req.params.botId ?? req.params.id;
15
+ const botId = parseInt(botIdRaw, 10);
16
+ if (isNaN(botId)) return res.status(400).json({ error: 'Некорректный botId' });
17
+
18
+ const user = await prisma.panelUser.findUnique({
19
+ where: { id: userId },
20
+ include: { botAccess: { select: { botId: true } } }
21
+ });
22
+ if (!user) return res.status(401).json({ error: 'Пользователь не найден' });
23
+
24
+ // Если поле allBots отсутствует (старый клиент) — считаем true
25
+ if (user.allBots !== false) return next();
26
+ const allowed = user.botAccess.some((a) => a.botId === botId);
27
+ if (!allowed) return res.status(403).json({ error: 'Доступ к боту запрещен' });
28
+ return next();
29
+ } catch (e) {
30
+ console.error('[checkBotAccess] error', e);
31
+ return res.status(500).json({ error: 'Ошибка проверки доступа' });
32
+ }
33
+ }
34
+
35
+ module.exports = { checkBotAccess };