blockmine 1.21.0 → 1.23.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 (492) hide show
  1. package/.claude/agents/README.md +469 -0
  2. package/.claude/agents/auth-route-debugger.md +118 -0
  3. package/.claude/agents/auth-route-tester.md +93 -0
  4. package/.claude/agents/auto-error-resolver.md +97 -0
  5. package/.claude/agents/build-optimizer.md +236 -0
  6. package/.claude/agents/code-architecture-reviewer.md +83 -0
  7. package/.claude/agents/code-refactor-master.md +94 -0
  8. package/.claude/agents/cost-optimizer.md +134 -0
  9. package/.claude/agents/deployment-orchestrator.md +113 -0
  10. package/.claude/agents/documentation-architect.md +82 -0
  11. package/.claude/agents/frontend-error-fixer.md +77 -0
  12. package/.claude/agents/iac-code-generator.md +71 -0
  13. package/.claude/agents/incident-responder.md +346 -0
  14. package/.claude/agents/infrastructure-architect.md +31 -0
  15. package/.claude/agents/kubernetes-specialist.md +56 -0
  16. package/.claude/agents/migration-planner.md +181 -0
  17. package/.claude/agents/network-architect.md +196 -0
  18. package/.claude/agents/plan-reviewer.md +52 -0
  19. package/.claude/agents/refactor-planner.md +63 -0
  20. package/.claude/agents/security-scanner.md +102 -0
  21. package/.claude/agents/web-research-specialist.md +78 -0
  22. package/.claude/commands/cost-analysis.md +315 -0
  23. package/.claude/commands/dev-docs-update.md +55 -0
  24. package/.claude/commands/dev-docs.md +51 -0
  25. package/.claude/commands/incident-debug.md +247 -0
  26. package/.claude/commands/infra-plan.md +81 -0
  27. package/.claude/commands/migration-plan.md +478 -0
  28. package/.claude/commands/route-research-for-testing.md +37 -0
  29. package/.claude/commands/security-review.md +66 -0
  30. package/.claude/hooks/CONFIG.md +448 -0
  31. package/.claude/hooks/README.md +163 -0
  32. package/.claude/hooks/SKILL_ACTIVATION_COMPLETE.md +226 -0
  33. package/.claude/hooks/WINDOWS_HOOKS_README.md +151 -0
  34. package/.claude/hooks/add-skill-activation-banners.ts +132 -0
  35. package/.claude/hooks/comprehensive-skill-test.ts +1315 -0
  36. package/.claude/hooks/error-handling-reminder.sh +12 -0
  37. package/.claude/hooks/error-handling-reminder.ts +222 -0
  38. package/.claude/hooks/k8s-manifest-validator.sh +56 -0
  39. package/.claude/hooks/package-lock.json +556 -0
  40. package/.claude/hooks/package.json +16 -0
  41. package/.claude/hooks/post-tool-use-tracker.ps1 +174 -0
  42. package/.claude/hooks/post-tool-use-tracker.sh +183 -0
  43. package/.claude/hooks/security-policy-check.sh +247 -0
  44. package/.claude/hooks/skill-activation-prompt.ps1 +10 -0
  45. package/.claude/hooks/skill-activation-prompt.sh +10 -0
  46. package/.claude/hooks/skill-activation-prompt.ts +141 -0
  47. package/.claude/hooks/stop-build-check-enhanced.sh +130 -0
  48. package/.claude/hooks/terraform-validator.sh +53 -0
  49. package/.claude/hooks/test-input.json +7 -0
  50. package/.claude/hooks/test-skill-activation.ts +427 -0
  51. package/.claude/hooks/trigger-build-resolver.sh +79 -0
  52. package/.claude/hooks/tsc-check.sh +173 -0
  53. package/.claude/hooks/tsconfig.json +19 -0
  54. package/.claude/settings.json +59 -0
  55. package/.claude/settings.local.json +36 -14
  56. package/.claude/skills/README.md +507 -0
  57. package/.claude/skills/api-engineering/SKILL.md +63 -0
  58. package/.claude/skills/api-engineering/resources/api-versioning.md +88 -0
  59. package/.claude/skills/api-engineering/resources/graphql-patterns.md +106 -0
  60. package/.claude/skills/api-engineering/resources/rate-limiting.md +118 -0
  61. package/.claude/skills/api-engineering/resources/rest-api-design.md +105 -0
  62. package/.claude/skills/backend-dev-guidelines/SKILL.md +306 -0
  63. package/.claude/skills/backend-dev-guidelines/resources/architecture-overview.md +451 -0
  64. package/.claude/skills/backend-dev-guidelines/resources/async-and-errors.md +307 -0
  65. package/.claude/skills/backend-dev-guidelines/resources/complete-examples.md +638 -0
  66. package/.claude/skills/backend-dev-guidelines/resources/configuration.md +275 -0
  67. package/.claude/skills/backend-dev-guidelines/resources/database-patterns.md +224 -0
  68. package/.claude/skills/backend-dev-guidelines/resources/middleware-guide.md +213 -0
  69. package/.claude/skills/backend-dev-guidelines/resources/routing-and-controllers.md +756 -0
  70. package/.claude/skills/backend-dev-guidelines/resources/sentry-and-monitoring.md +336 -0
  71. package/.claude/skills/backend-dev-guidelines/resources/services-and-repositories.md +789 -0
  72. package/.claude/skills/backend-dev-guidelines/resources/testing-guide.md +235 -0
  73. package/.claude/skills/backend-dev-guidelines/resources/validation-patterns.md +754 -0
  74. package/.claude/skills/budget-and-cost-management/SKILL.md +850 -0
  75. package/.claude/skills/build-engineering/SKILL.md +431 -0
  76. package/.claude/skills/build-engineering/resources/artifact-repositories.md +72 -0
  77. package/.claude/skills/build-engineering/resources/build-caching.md +96 -0
  78. package/.claude/skills/build-engineering/resources/build-pipelines.md +105 -0
  79. package/.claude/skills/build-engineering/resources/build-security.md +95 -0
  80. package/.claude/skills/build-engineering/resources/build-systems.md +389 -0
  81. package/.claude/skills/build-engineering/resources/compilation-optimization.md +201 -0
  82. package/.claude/skills/build-engineering/resources/dependency-management.md +73 -0
  83. package/.claude/skills/build-engineering/resources/monorepo-builds.md +110 -0
  84. package/.claude/skills/build-engineering/resources/performance-optimization.md +113 -0
  85. package/.claude/skills/build-engineering/resources/reproducible-builds.md +82 -0
  86. package/.claude/skills/cloud-engineering/SKILL.md +675 -0
  87. package/.claude/skills/cloud-engineering/resources/aws-patterns.md +742 -0
  88. package/.claude/skills/cloud-engineering/resources/azure-patterns.md +714 -0
  89. package/.claude/skills/cloud-engineering/resources/cleared-cloud-environments.md +987 -0
  90. package/.claude/skills/cloud-engineering/resources/cloud-cost-optimization.md +757 -0
  91. package/.claude/skills/cloud-engineering/resources/cloud-networking.md +1058 -0
  92. package/.claude/skills/cloud-engineering/resources/cloud-security-tools.md +1530 -0
  93. package/.claude/skills/cloud-engineering/resources/cloud-security.md +990 -0
  94. package/.claude/skills/cloud-engineering/resources/gcp-patterns.md +758 -0
  95. package/.claude/skills/cloud-engineering/resources/migration-strategies.md +820 -0
  96. package/.claude/skills/cloud-engineering/resources/multi-cloud-strategies.md +670 -0
  97. package/.claude/skills/cloud-engineering/resources/oci-patterns.md +1198 -0
  98. package/.claude/skills/cloud-engineering/resources/serverless-patterns.md +795 -0
  99. package/.claude/skills/cloud-engineering/resources/well-architected-frameworks.md +966 -0
  100. package/.claude/skills/cybersecurity/SKILL.md +409 -0
  101. package/.claude/skills/cybersecurity/resources/security-architecture.md +266 -0
  102. package/.claude/skills/database-engineering/SKILL.md +61 -0
  103. package/.claude/skills/database-engineering/resources/backup-and-recovery.md +72 -0
  104. package/.claude/skills/database-engineering/resources/database-replication.md +63 -0
  105. package/.claude/skills/database-engineering/resources/postgresql-fundamentals.md +70 -0
  106. package/.claude/skills/database-engineering/resources/query-optimization.md +68 -0
  107. package/.claude/skills/devsecops/SKILL.md +374 -0
  108. package/.claude/skills/devsecops/resources/ci-cd-security.md +204 -0
  109. package/.claude/skills/devsecops/resources/compliance-automation.md +530 -0
  110. package/.claude/skills/devsecops/resources/compliance-frameworks.md +2322 -0
  111. package/.claude/skills/devsecops/resources/container-security.md +915 -0
  112. package/.claude/skills/devsecops/resources/cspm-integration.md +1440 -0
  113. package/.claude/skills/devsecops/resources/policy-enforcement.md +619 -0
  114. package/.claude/skills/devsecops/resources/secrets-management.md +755 -0
  115. package/.claude/skills/devsecops/resources/security-monitoring.md +146 -0
  116. package/.claude/skills/devsecops/resources/security-scanning.md +887 -0
  117. package/.claude/skills/devsecops/resources/security-testing.md +203 -0
  118. package/.claude/skills/devsecops/resources/supply-chain-security.md +518 -0
  119. package/.claude/skills/devsecops/resources/vulnerability-management.md +481 -0
  120. package/.claude/skills/devsecops/resources/zero-trust-architecture.md +177 -0
  121. package/.claude/skills/documentation-as-code/SKILL.md +323 -0
  122. package/.claude/skills/documentation-as-code/resources/api-documentation.md +90 -0
  123. package/.claude/skills/documentation-as-code/resources/changelog-management.md +79 -0
  124. package/.claude/skills/documentation-as-code/resources/diagram-generation.md +44 -0
  125. package/.claude/skills/documentation-as-code/resources/docs-as-code-workflow.md +99 -0
  126. package/.claude/skills/documentation-as-code/resources/documentation-automation.md +68 -0
  127. package/.claude/skills/documentation-as-code/resources/documentation-sites.md +79 -0
  128. package/.claude/skills/documentation-as-code/resources/markdown-best-practices.md +162 -0
  129. package/.claude/skills/documentation-as-code/resources/openapi-specification.md +77 -0
  130. package/.claude/skills/documentation-as-code/resources/readme-engineering.md +60 -0
  131. package/.claude/skills/documentation-as-code/resources/technical-writing-guide.md +202 -0
  132. package/.claude/skills/engineering-management/SKILL.md +356 -0
  133. package/.claude/skills/engineering-management/resources/career-ladders.md +609 -0
  134. package/.claude/skills/engineering-management/resources/hiring-and-assessment.md +555 -0
  135. package/.claude/skills/engineering-management/resources/one-on-one-guides.md +609 -0
  136. package/.claude/skills/engineering-management/resources/resource-planning.md +557 -0
  137. package/.claude/skills/engineering-management/resources/team-organization-patterns.md +491 -0
  138. package/.claude/skills/engineering-management/resources/technical-interviews.md +474 -0
  139. package/.claude/skills/engineering-operations-management/SKILL.md +817 -0
  140. package/.claude/skills/error-tracking/SKILL.md +379 -0
  141. package/.claude/skills/frontend-dev-guidelines/SKILL.md +403 -0
  142. package/.claude/skills/frontend-dev-guidelines/resources/common-patterns.md +331 -0
  143. package/.claude/skills/frontend-dev-guidelines/resources/complete-examples.md +872 -0
  144. package/.claude/skills/frontend-dev-guidelines/resources/component-patterns.md +502 -0
  145. package/.claude/skills/frontend-dev-guidelines/resources/data-fetching.md +767 -0
  146. package/.claude/skills/frontend-dev-guidelines/resources/file-organization.md +502 -0
  147. package/.claude/skills/frontend-dev-guidelines/resources/loading-and-error-states.md +501 -0
  148. package/.claude/skills/frontend-dev-guidelines/resources/performance.md +406 -0
  149. package/.claude/skills/frontend-dev-guidelines/resources/routing-guide.md +364 -0
  150. package/.claude/skills/frontend-dev-guidelines/resources/styling-guide.md +428 -0
  151. package/.claude/skills/frontend-dev-guidelines/resources/typescript-standards.md +418 -0
  152. package/.claude/skills/general-it-engineering/SKILL.md +393 -0
  153. package/.claude/skills/general-it-engineering/resources/asset-management.md +712 -0
  154. package/.claude/skills/general-it-engineering/resources/automation-orchestration.md +817 -0
  155. package/.claude/skills/general-it-engineering/resources/business-continuity.md +786 -0
  156. package/.claude/skills/general-it-engineering/resources/change-management.md +715 -0
  157. package/.claude/skills/general-it-engineering/resources/enterprise-monitoring.md +729 -0
  158. package/.claude/skills/general-it-engineering/resources/help-desk-operations.md +738 -0
  159. package/.claude/skills/general-it-engineering/resources/incident-service-management.md +834 -0
  160. package/.claude/skills/general-it-engineering/resources/it-governance.md +753 -0
  161. package/.claude/skills/general-it-engineering/resources/itil-framework.md +503 -0
  162. package/.claude/skills/general-it-engineering/resources/service-management.md +669 -0
  163. package/.claude/skills/infrastructure-architecture/SKILL.md +328 -0
  164. package/.claude/skills/infrastructure-architecture/resources/architecture-decision-records.md +505 -0
  165. package/.claude/skills/infrastructure-architecture/resources/architecture-patterns.md +528 -0
  166. package/.claude/skills/infrastructure-architecture/resources/capacity-planning.md +453 -0
  167. package/.claude/skills/infrastructure-architecture/resources/cleared-environment-architecture.md +773 -0
  168. package/.claude/skills/infrastructure-architecture/resources/cost-architecture.md +499 -0
  169. package/.claude/skills/infrastructure-architecture/resources/data-architecture.md +501 -0
  170. package/.claude/skills/infrastructure-architecture/resources/disaster-recovery.md +535 -0
  171. package/.claude/skills/infrastructure-architecture/resources/migration-architecture.md +512 -0
  172. package/.claude/skills/infrastructure-architecture/resources/multi-region-design.md +608 -0
  173. package/.claude/skills/infrastructure-architecture/resources/reference-architectures.md +562 -0
  174. package/.claude/skills/infrastructure-architecture/resources/security-architecture.md +538 -0
  175. package/.claude/skills/infrastructure-architecture/resources/system-design-principles.md +489 -0
  176. package/.claude/skills/infrastructure-architecture/resources/workload-classification.md +1000 -0
  177. package/.claude/skills/infrastructure-strategy/SKILL.md +924 -0
  178. package/.claude/skills/network-engineering/SKILL.md +385 -0
  179. package/.claude/skills/network-engineering/resources/dns-management.md +738 -0
  180. package/.claude/skills/network-engineering/resources/load-balancing.md +820 -0
  181. package/.claude/skills/network-engineering/resources/network-architecture.md +546 -0
  182. package/.claude/skills/network-engineering/resources/network-security.md +921 -0
  183. package/.claude/skills/network-engineering/resources/network-troubleshooting.md +749 -0
  184. package/.claude/skills/network-engineering/resources/routing-switching.md +373 -0
  185. package/.claude/skills/network-engineering/resources/sdn-networking.md +695 -0
  186. package/.claude/skills/network-engineering/resources/service-mesh-networking.md +777 -0
  187. package/.claude/skills/network-engineering/resources/tcp-ip-protocols.md +444 -0
  188. package/.claude/skills/network-engineering/resources/vpn-connectivity.md +672 -0
  189. package/.claude/skills/observability-engineering/SKILL.md +101 -0
  190. package/.claude/skills/observability-engineering/resources/apm-tools.md +97 -0
  191. package/.claude/skills/observability-engineering/resources/correlation-strategies.md +87 -0
  192. package/.claude/skills/observability-engineering/resources/distributed-tracing.md +98 -0
  193. package/.claude/skills/observability-engineering/resources/logs-aggregation.md +118 -0
  194. package/.claude/skills/observability-engineering/resources/observability-cost-optimization.md +141 -0
  195. package/.claude/skills/observability-engineering/resources/opentelemetry.md +110 -0
  196. package/.claude/skills/platform-engineering/SKILL.md +555 -0
  197. package/.claude/skills/platform-engineering/resources/architecture-overview.md +600 -0
  198. package/.claude/skills/platform-engineering/resources/container-orchestration.md +916 -0
  199. package/.claude/skills/platform-engineering/resources/cost-optimization.md +634 -0
  200. package/.claude/skills/platform-engineering/resources/developer-platforms.md +670 -0
  201. package/.claude/skills/platform-engineering/resources/gitops-automation.md +650 -0
  202. package/.claude/skills/platform-engineering/resources/infrastructure-as-code.md +778 -0
  203. package/.claude/skills/platform-engineering/resources/infrastructure-standards.md +708 -0
  204. package/.claude/skills/platform-engineering/resources/multi-tenancy.md +602 -0
  205. package/.claude/skills/platform-engineering/resources/platform-security.md +711 -0
  206. package/.claude/skills/platform-engineering/resources/resource-management.md +592 -0
  207. package/.claude/skills/platform-engineering/resources/service-mesh.md +628 -0
  208. package/.claude/skills/release-engineering/SKILL.md +393 -0
  209. package/.claude/skills/release-engineering/resources/artifact-management.md +108 -0
  210. package/.claude/skills/release-engineering/resources/build-optimization.md +84 -0
  211. package/.claude/skills/release-engineering/resources/ci-cd-pipelines.md +411 -0
  212. package/.claude/skills/release-engineering/resources/deployment-strategies.md +197 -0
  213. package/.claude/skills/release-engineering/resources/pipeline-security.md +62 -0
  214. package/.claude/skills/release-engineering/resources/progressive-delivery.md +83 -0
  215. package/.claude/skills/release-engineering/resources/release-automation.md +68 -0
  216. package/.claude/skills/release-engineering/resources/release-orchestration.md +77 -0
  217. package/.claude/skills/release-engineering/resources/rollback-strategies.md +66 -0
  218. package/.claude/skills/release-engineering/resources/versioning-strategies.md +59 -0
  219. package/.claude/skills/route-tester/SKILL.md +392 -0
  220. package/.claude/skills/skill-developer/ADVANCED.md +197 -0
  221. package/.claude/skills/skill-developer/HOOK_MECHANISMS.md +306 -0
  222. package/.claude/skills/skill-developer/PATTERNS_LIBRARY.md +152 -0
  223. package/.claude/skills/skill-developer/SKILL.md +430 -0
  224. package/.claude/skills/skill-developer/SKILL_RULES_REFERENCE.md +315 -0
  225. package/.claude/skills/skill-developer/TRIGGER_TYPES.md +305 -0
  226. package/.claude/skills/skill-developer/TROUBLESHOOTING.md +514 -0
  227. package/.claude/skills/skill-rules.json +2940 -0
  228. package/.claude/skills/sre/SKILL.md +464 -0
  229. package/.claude/skills/sre/resources/alerting-best-practices.md +282 -0
  230. package/.claude/skills/sre/resources/capacity-planning.md +226 -0
  231. package/.claude/skills/sre/resources/chaos-engineering.md +193 -0
  232. package/.claude/skills/sre/resources/disaster-recovery.md +232 -0
  233. package/.claude/skills/sre/resources/incident-management.md +436 -0
  234. package/.claude/skills/sre/resources/observability-stack.md +240 -0
  235. package/.claude/skills/sre/resources/on-call-runbooks.md +167 -0
  236. package/.claude/skills/sre/resources/performance-optimization.md +108 -0
  237. package/.claude/skills/sre/resources/reliability-patterns.md +183 -0
  238. package/.claude/skills/sre/resources/slo-sli-sla.md +464 -0
  239. package/.claude/skills/sre/resources/toil-reduction.md +145 -0
  240. package/.claude/skills/systems-engineering/SKILL.md +648 -0
  241. package/.claude/skills/systems-engineering/resources/automation-patterns.md +771 -0
  242. package/.claude/skills/systems-engineering/resources/configuration-management.md +998 -0
  243. package/.claude/skills/systems-engineering/resources/linux-administration.md +672 -0
  244. package/.claude/skills/systems-engineering/resources/networking-fundamentals.md +982 -0
  245. package/.claude/skills/systems-engineering/resources/performance-tuning.md +871 -0
  246. package/.claude/skills/systems-engineering/resources/powershell-scripting.md +482 -0
  247. package/.claude/skills/systems-engineering/resources/security-hardening.md +739 -0
  248. package/.claude/skills/systems-engineering/resources/shell-scripting.md +915 -0
  249. package/.claude/skills/systems-engineering/resources/storage-management.md +628 -0
  250. package/.claude/skills/systems-engineering/resources/system-monitoring.md +787 -0
  251. package/.claude/skills/systems-engineering/resources/troubleshooting-guide.md +753 -0
  252. package/.claude/skills/systems-engineering/resources/windows-administration.md +738 -0
  253. package/.claude/skills/technical-leadership/SKILL.md +728 -0
  254. package/CHANGELOG.md +102 -42
  255. package/CLAUDE.md +284 -0
  256. package/README.md +315 -71
  257. package/backend/docs/SECRETS_DOCUMENTATION.md +327 -0
  258. package/backend/jest.config.js +59 -0
  259. package/backend/package-lock.json +6801 -0
  260. package/backend/package.json +24 -4
  261. package/backend/prisma/migrations/20251026104609_add_websocket_api/migration.sql +33 -0
  262. package/backend/prisma/migrations/20251116111851_add_execution_trace/migration.sql +22 -0
  263. package/backend/prisma/migrations/20251120154914_add_panel_api_keys/migration.sql +21 -0
  264. package/backend/prisma/migrations/20251121110241_add_proxy_table/migration.sql +45 -0
  265. package/backend/prisma/migrations/migration_lock.toml +2 -2
  266. package/backend/prisma/schema.prisma +103 -1
  267. package/backend/src/__tests__/core/DependencyService.test.js +336 -0
  268. package/backend/src/__tests__/core/UserService.test.js +875 -0
  269. package/backend/src/__tests__/repositories/BaseRepository.test.js +146 -0
  270. package/backend/src/__tests__/repositories/BotRepository.test.js +118 -0
  271. package/backend/src/__tests__/repositories/CommandRepository.test.js +132 -0
  272. package/backend/src/__tests__/repositories/EventGraphRepository.test.js +93 -0
  273. package/backend/src/__tests__/repositories/GroupRepository.test.js +155 -0
  274. package/backend/src/__tests__/repositories/PermissionRepository.test.js +130 -0
  275. package/backend/src/__tests__/repositories/PluginRepository.test.js +107 -0
  276. package/backend/src/__tests__/repositories/ServerRepository.test.js +80 -0
  277. package/backend/src/__tests__/repositories/UserRepository.test.js +128 -0
  278. package/backend/src/__tests__/secretsFilter.test.js +425 -0
  279. package/backend/src/__tests__/services/BotLifecycleService.test.js +416 -0
  280. package/backend/src/__tests__/services/BotProcessManager.test.js +285 -0
  281. package/backend/src/__tests__/services/CacheManager.test.js +125 -0
  282. package/backend/src/__tests__/services/CommandExecutionService.test.js +460 -0
  283. package/backend/src/__tests__/services/ResourceMonitorService.test.js +207 -0
  284. package/backend/src/__tests__/services/TelemetryService.test.js +291 -0
  285. package/backend/src/__tests__/setup.js +25 -0
  286. package/backend/src/ai/plugin-assistant-system-prompt.md +788 -0
  287. package/backend/src/api/middleware/auth.js +27 -0
  288. package/backend/src/api/middleware/botAccess.js +7 -3
  289. package/backend/src/api/middleware/panelApiAuth.js +135 -0
  290. package/backend/src/api/routes/aiAssistant.js +995 -0
  291. package/backend/src/api/routes/apiKeys.js +181 -0
  292. package/backend/src/api/routes/auth.js +669 -633
  293. package/backend/src/api/routes/botCommands.js +107 -0
  294. package/backend/src/api/routes/botGroups.js +165 -0
  295. package/backend/src/api/routes/botHistory.js +108 -0
  296. package/backend/src/api/routes/botPermissions.js +99 -0
  297. package/backend/src/api/routes/botStatus.js +36 -0
  298. package/backend/src/api/routes/botUsers.js +162 -0
  299. package/backend/src/api/routes/bots.js +2451 -2360
  300. package/backend/src/api/routes/eventGraphs.js +4 -1
  301. package/backend/src/api/routes/logs.js +13 -3
  302. package/backend/src/api/routes/panel.js +66 -66
  303. package/backend/src/api/routes/panelApiKeys.js +179 -0
  304. package/backend/src/api/routes/pluginIde.js +1715 -135
  305. package/backend/src/api/routes/plugins.js +376 -218
  306. package/backend/src/api/routes/proxies.js +130 -0
  307. package/backend/src/api/routes/search.js +4 -0
  308. package/backend/src/api/routes/servers.js +20 -3
  309. package/backend/src/api/routes/settings.js +5 -0
  310. package/backend/src/api/routes/system.js +174 -0
  311. package/backend/src/api/routes/traces.js +131 -0
  312. package/backend/src/config/debug.config.js +36 -0
  313. package/backend/src/container.js +82 -0
  314. package/backend/src/core/BotHistoryStore.js +180 -0
  315. package/backend/src/core/BotManager.js +149 -868
  316. package/backend/src/core/BotManager.old.js +1093 -0
  317. package/backend/src/core/BotProcess.js +850 -191
  318. package/backend/src/core/EventGraphManager.js +194 -198
  319. package/backend/src/core/GraphExecutionEngine.js +709 -57
  320. package/backend/src/core/MessageQueue.js +39 -12
  321. package/backend/src/core/NodeRegistry.js +37 -1134
  322. package/backend/src/core/PluginLoader.js +99 -5
  323. package/backend/src/core/PluginManager.js +126 -15
  324. package/backend/src/core/PrismaService.js +32 -0
  325. package/backend/src/core/TaskScheduler.js +1 -1
  326. package/backend/src/core/UserService.js +3 -3
  327. package/backend/src/core/__tests__/PrismaService.test.js +24 -0
  328. package/backend/src/core/commands/README.md +305 -0
  329. package/backend/src/core/commands/dev.js +13 -7
  330. package/backend/src/core/commands/ping.js +10 -4
  331. package/backend/src/core/commands/whois.js +63 -0
  332. package/backend/src/core/config/validation.js +27 -0
  333. package/backend/src/core/constants/graphTypes.js +21 -0
  334. package/backend/src/core/node-registries/actions.js +202 -0
  335. package/backend/src/core/node-registries/arrays.js +155 -0
  336. package/backend/src/core/node-registries/bot.js +23 -0
  337. package/backend/src/core/node-registries/data.js +290 -0
  338. package/backend/src/core/node-registries/debug.js +26 -0
  339. package/backend/src/core/node-registries/events.js +201 -0
  340. package/backend/src/core/node-registries/flow.js +139 -0
  341. package/backend/src/core/node-registries/logic.js +62 -0
  342. package/backend/src/core/node-registries/math.js +42 -0
  343. package/backend/src/core/node-registries/objects.js +98 -0
  344. package/backend/src/core/node-registries/strings.js +187 -0
  345. package/backend/src/core/node-registries/time.js +113 -0
  346. package/backend/src/core/node-registries/type.js +25 -0
  347. package/backend/src/core/node-registries/users.js +79 -0
  348. package/backend/src/core/nodes/{action_bot_look_at.js → actions/bot_look_at.js} +36 -36
  349. package/backend/src/core/nodes/{action_bot_set_variable.js → actions/bot_set_variable.js} +32 -32
  350. package/backend/src/core/nodes/actions/create_command.js +189 -0
  351. package/backend/src/core/nodes/actions/delete_command.js +92 -0
  352. package/backend/src/core/nodes/{action_send_log.js → actions/send_log.js} +28 -23
  353. package/backend/src/core/nodes/{action_send_message.js → actions/send_message.js} +32 -32
  354. package/backend/src/core/nodes/actions/send_websocket_response.js +33 -0
  355. package/backend/src/core/nodes/actions/update_command.js +133 -0
  356. package/backend/src/core/nodes/arrays/get_next.js +35 -0
  357. package/backend/src/core/nodes/arrays/join.js +28 -0
  358. package/backend/src/core/nodes/{data_cast.js → data/cast.js} +10 -1
  359. package/backend/src/core/nodes/data/datetime_literal.js +27 -0
  360. package/backend/src/core/nodes/data/entity_info.js +69 -0
  361. package/backend/src/core/nodes/data/get_nearby_entities.js +32 -0
  362. package/backend/src/core/nodes/data/get_nearby_players.js +64 -0
  363. package/backend/src/core/nodes/{data_get_user_field.js → data/get_user_field.js} +1 -1
  364. package/backend/src/core/nodes/data/type_check.js +53 -0
  365. package/backend/src/core/nodes/{debug_log.js → debug/log.js} +16 -16
  366. package/backend/src/core/nodes/{flow_branch.js → flow/branch.js} +15 -15
  367. package/backend/src/core/nodes/{flow_break.js → flow/break.js} +14 -14
  368. package/backend/src/core/nodes/flow/delay.js +43 -0
  369. package/backend/src/core/nodes/{flow_for_each.js → flow/for_each.js} +39 -39
  370. package/backend/src/core/nodes/{flow_sequence.js → flow/sequence.js} +16 -16
  371. package/backend/src/core/nodes/{flow_switch.js → flow/switch.js} +47 -47
  372. package/backend/src/core/nodes/{flow_while.js → flow/while.js} +1 -1
  373. package/backend/src/core/nodes/logic/__tests__/compare.test.js +83 -0
  374. package/backend/src/core/nodes/logic/not.js +22 -0
  375. package/backend/src/core/nodes/math/__tests__/operation.test.js +65 -0
  376. package/backend/src/core/nodes/strings/__tests__/concat.test.js +89 -0
  377. package/backend/src/core/nodes/{string_starts_with.js → strings/starts_with.js} +1 -1
  378. package/backend/src/core/nodes/strings/to_lower.js +22 -0
  379. package/backend/src/core/nodes/strings/to_upper.js +22 -0
  380. package/backend/src/core/nodes/time/__tests__/now.test.js +24 -0
  381. package/backend/src/core/nodes/time/add.js +33 -0
  382. package/backend/src/core/nodes/time/compare.js +35 -0
  383. package/backend/src/core/nodes/time/diff.js +29 -0
  384. package/backend/src/core/nodes/time/format.js +32 -0
  385. package/backend/src/core/nodes/time/now.js +18 -0
  386. package/backend/src/core/nodes/type/to_string.js +32 -0
  387. package/backend/src/core/nodes/{user_check_blacklist.js → users/check_blacklist.js} +37 -37
  388. package/backend/src/core/nodes/{user_get_groups.js → users/get_groups.js} +36 -36
  389. package/backend/src/core/nodes/{user_get_permissions.js → users/get_permissions.js} +36 -36
  390. package/backend/src/core/nodes/{user_set_blacklist.js → users/set_blacklist.js} +37 -37
  391. package/backend/src/core/services/BotLifecycleService.js +835 -0
  392. package/backend/src/core/services/BotProcessManager.js +163 -0
  393. package/backend/src/core/services/CacheManager.js +111 -0
  394. package/backend/src/core/services/CommandExecutionService.js +430 -0
  395. package/backend/src/core/services/DebugSessionManager.js +347 -0
  396. package/backend/src/core/services/GraphCollaborationManager.js +501 -0
  397. package/backend/src/core/services/MinecraftBotManager.js +259 -0
  398. package/backend/src/core/services/MinecraftViewerService.js +216 -0
  399. package/backend/src/core/services/ResourceMonitorService.js +90 -0
  400. package/backend/src/core/services/TelemetryService.js +124 -0
  401. package/backend/src/core/services/TraceCollectorService.js +545 -0
  402. package/backend/src/core/services/ValidationService.js +132 -0
  403. package/backend/src/core/services/__tests__/ValidationService.test.js +148 -0
  404. package/backend/src/core/services.js +20 -5
  405. package/backend/src/core/system/CommandContext.js +84 -0
  406. package/backend/src/core/system/RuntimeCommandRegistry.js +116 -0
  407. package/backend/src/core/system/Transport.js +74 -0
  408. package/backend/src/core/utils/__tests__/jsonParser.test.js +44 -0
  409. package/backend/src/core/utils/jsonParser.js +18 -0
  410. package/backend/src/core/utils/secretsFilter.js +262 -0
  411. package/backend/src/core/utils/variableParser.js +89 -0
  412. package/backend/src/core/validation/__tests__/nodeSchemas.test.js +175 -0
  413. package/backend/src/core/validation/nodeSchemas.js +112 -0
  414. package/backend/src/lib/prisma.js +2 -4
  415. package/backend/src/real-time/botApi/handlers/commandHandlers.js +28 -0
  416. package/backend/src/real-time/botApi/handlers/graphHandlers.js +99 -0
  417. package/backend/src/real-time/botApi/handlers/graphWebSocketHandlers.js +147 -0
  418. package/backend/src/real-time/botApi/handlers/index.js +43 -0
  419. package/backend/src/real-time/botApi/handlers/messageHandlers.js +66 -0
  420. package/backend/src/real-time/botApi/handlers/statusHandlers.js +17 -0
  421. package/backend/src/real-time/botApi/handlers/userHandlers.js +141 -0
  422. package/backend/src/real-time/botApi/index.js +40 -0
  423. package/backend/src/real-time/botApi/middleware.js +79 -0
  424. package/backend/src/real-time/botApi/utils.js +65 -0
  425. package/backend/src/real-time/panelNamespace.js +387 -0
  426. package/backend/src/real-time/presence.js +7 -2
  427. package/backend/src/real-time/socketHandler.js +400 -5
  428. package/backend/src/repositories/BaseRepository.js +43 -0
  429. package/backend/src/repositories/BotRepository.js +42 -0
  430. package/backend/src/repositories/CommandRepository.js +53 -0
  431. package/backend/src/repositories/EventGraphRepository.js +40 -0
  432. package/backend/src/repositories/GroupRepository.js +69 -0
  433. package/backend/src/repositories/PermissionRepository.js +48 -0
  434. package/backend/src/repositories/PluginRepository.js +42 -0
  435. package/backend/src/repositories/ServerRepository.js +27 -0
  436. package/backend/src/repositories/UserRepository.js +48 -0
  437. package/backend/src/server.js +21 -0
  438. package/backend/src/test-refactor.js +85 -0
  439. package/frontend/dist/assets/index-B1serztM.js +11210 -0
  440. package/frontend/dist/assets/index-t6K1u4OV.css +32 -0
  441. package/frontend/dist/index.html +2 -2
  442. package/frontend/package-lock.json +9437 -0
  443. package/frontend/package.json +8 -5
  444. package/package.json +3 -2
  445. package/screen/console.png +0 -0
  446. package/screen/dashboard.png +0 -0
  447. package/screen/graph_collabe.png +0 -0
  448. package/screen/graph_live_debug.png +0 -0
  449. package/screen/management_command.png +0 -0
  450. package/screen/node_debug_trace.png +0 -0
  451. package/screen/plugin_/320/276/320/261/320/267/320/276/321/200.png +0 -0
  452. package/screen/websocket.png +0 -0
  453. package/screen//320/275/320/260/321/201/321/202/321/200/320/276/320/271/320/272/320/270_/320/276/321/202/320/264/320/265/320/273/321/214/320/275/321/213/321/205_/320/272/320/276/320/274/320/260/320/275/320/264_/320/272/320/260/320/266/320/264/321/203_/320/272/320/276/320/274/320/260/320/275/320/273/320/264/321/203_/320/274/320/276/320/266/320/275/320/276_/320/275/320/260/321/201/321/202/321/200/320/260/320/270/320/262/320/260/321/202/321/214.png +0 -0
  454. package/screen//320/277/320/273/320/260/320/275/320/270/321/200/320/276/320/262/321/211/320/270/320/272_/320/274/320/276/320/266/320/275/320/276_/320/267/320/260/320/264/320/260/320/262/320/260/321/202/321/214_/320/264/320/265/320/271/321/201/321/202/320/262/320/270/321/217_/320/277/320/276_/320/262/321/200/320/265/320/274/320/265/320/275/320/270.png +0 -0
  455. package/frontend/dist/assets/index-B9GedHEa.js +0 -8352
  456. package/frontend/dist/assets/index-zLiy9MDx.css +0 -1
  457. package/nul +0 -0
  458. /package/backend/src/core/nodes/{action_http_request.js → actions/http_request.js} +0 -0
  459. /package/backend/src/core/nodes/{array_add_element.js → arrays/add_element.js} +0 -0
  460. /package/backend/src/core/nodes/{array_contains.js → arrays/contains.js} +0 -0
  461. /package/backend/src/core/nodes/{array_find_index.js → arrays/find_index.js} +0 -0
  462. /package/backend/src/core/nodes/{array_get_by_index.js → arrays/get_by_index.js} +0 -0
  463. /package/backend/src/core/nodes/{array_get_random_element.js → arrays/get_random_element.js} +0 -0
  464. /package/backend/src/core/nodes/{array_remove_by_index.js → arrays/remove_by_index.js} +0 -0
  465. /package/backend/src/core/nodes/{bot_get_position.js → bot/get_position.js} +0 -0
  466. /package/backend/src/core/nodes/{data_array_literal.js → data/array_literal.js} +0 -0
  467. /package/backend/src/core/nodes/{data_boolean_literal.js → data/boolean_literal.js} +0 -0
  468. /package/backend/src/core/nodes/{data_get_argument.js → data/get_argument.js} +0 -0
  469. /package/backend/src/core/nodes/{data_get_bot_look.js → data/get_bot_look.js} +0 -0
  470. /package/backend/src/core/nodes/{data_get_entity_field.js → data/get_entity_field.js} +0 -0
  471. /package/backend/src/core/nodes/{data_get_server_players.js → data/get_server_players.js} +0 -0
  472. /package/backend/src/core/nodes/{data_get_variable.js → data/get_variable.js} +0 -0
  473. /package/backend/src/core/nodes/{data_length.js → data/length.js} +0 -0
  474. /package/backend/src/core/nodes/{data_make_object.js → data/make_object.js} +0 -0
  475. /package/backend/src/core/nodes/{data_number_literal.js → data/number_literal.js} +0 -0
  476. /package/backend/src/core/nodes/{data_string_literal.js → data/string_literal.js} +0 -0
  477. /package/backend/src/core/nodes/{logic_compare.js → logic/compare.js} +0 -0
  478. /package/backend/src/core/nodes/{logic_operation.js → logic/operation.js} +0 -0
  479. /package/backend/src/core/nodes/{math_operation.js → math/operation.js} +0 -0
  480. /package/backend/src/core/nodes/{math_random_number.js → math/random_number.js} +0 -0
  481. /package/backend/src/core/nodes/{object_create.js → objects/create.js} +0 -0
  482. /package/backend/src/core/nodes/{object_delete.js → objects/delete.js} +0 -0
  483. /package/backend/src/core/nodes/{object_get.js → objects/get.js} +0 -0
  484. /package/backend/src/core/nodes/{object_has_key.js → objects/has_key.js} +0 -0
  485. /package/backend/src/core/nodes/{object_set.js → objects/set.js} +0 -0
  486. /package/backend/src/core/nodes/{string_concat.js → strings/concat.js} +0 -0
  487. /package/backend/src/core/nodes/{string_contains.js → strings/contains.js} +0 -0
  488. /package/backend/src/core/nodes/{string_ends_with.js → strings/ends_with.js} +0 -0
  489. /package/backend/src/core/nodes/{string_equals.js → strings/equals.js} +0 -0
  490. /package/backend/src/core/nodes/{string_length.js → strings/length.js} +0 -0
  491. /package/backend/src/core/nodes/{string_matches.js → strings/matches.js} +0 -0
  492. /package/backend/src/core/nodes/{string_split.js → strings/split.js} +0 -0
@@ -1,634 +1,670 @@
1
- const express = require('express');
2
- const bcrypt = require('bcryptjs');
3
- const jwt = require('jsonwebtoken');
4
- const crypto = require('crypto');
5
- const { PrismaClient } = require('@prisma/client');
6
- const config = require('../../config');
7
- const { authenticate, authorize } = require('../middleware/auth');
8
- const path = require('path');
9
- const os = require('os');
10
-
11
- const router = express.Router();
12
- const prisma = new PrismaClient();
13
-
14
- const JWT_SECRET = config.security.jwtSecret;
15
- const JWT_EXPIRES_IN = '7d';
16
-
17
- const activeResetTokens = new Map();
18
-
19
- function ownerOnly(req, res, next) {
20
- if (req.user && req.user.userId === 1) return next();
21
- return res.status(403).json({ error: 'Только владелец может изменять права пользователей и роли.' });
22
- }
23
-
24
- /**
25
- * @route GET /api/auth/status
26
- * @desc Проверяет, была ли произведена первоначальная настройка (создан админ)
27
- * @access Public
28
- */
29
- router.get('/status', async (req, res) => {
30
- try {
31
- const userCount = await prisma.panelUser.count();
32
- res.json({ needsSetup: userCount === 0 });
33
- } catch (error) {
34
- console.error('[Auth Status Error]', error);
35
- res.status(500).json({ error: 'Не удалось проверить статус сервера' });
36
- }
37
- });
38
-
39
- /**
40
- * @route GET /api/auth/config-path
41
- * @desc Получить путь к конфигурационному файлу
42
- * @access Public
43
- */
44
- router.get('/config-path', (req, res) => {
45
- const configPath = path.join(os.homedir(), '.blockmine', 'config.json');
46
- res.json({ configPath });
47
- });
48
-
49
- /**
50
- * @route POST /api/auth/setup
51
- * @desc Создает первого пользователя с ролью администратора
52
- * @access Public (только если нет других пользователей)
53
- */
54
- router.post('/setup', async (req, res) => {
55
- try {
56
- const userCount = await prisma.panelUser.count();
57
- if (userCount > 0) {
58
- return res.status(403).json({ error: 'Настройка уже произведена.' });
59
- }
60
-
61
- const { username, password } = req.body;
62
- if (!username || !password || password.length < 4) {
63
- return res.status(400).json({ error: 'Имя пользователя и пароль (минимум 4 символа) обязательны.' });
64
- }
65
-
66
- const hashedPassword = await bcrypt.hash(password, 12);
67
-
68
- let newUser;
69
-
70
- await prisma.$transaction(async (tx) => {
71
- const adminPermissions = ALL_PERMISSIONS
72
- .map(p => p.id)
73
- .filter(id => id !== '*' && id !== 'plugin:develop');
74
-
75
- const adminRole = await tx.panelRole.upsert({
76
- where: { name: 'Admin' },
77
- update: {},
78
- create: {
79
- name: 'Admin',
80
- permissions: JSON.stringify(adminPermissions)
81
- },
82
- });
83
-
84
- newUser = await tx.panelUser.create({
85
- data: {
86
- username,
87
- passwordHash: hashedPassword,
88
- roleId: adminRole.id,
89
- },
90
- include: { role: true }
91
- });
92
- });
93
-
94
-
95
- const permissions = JSON.parse(newUser.role.permissions || '[]');
96
-
97
- const payload = {
98
- userId: newUser.id,
99
- username: newUser.username,
100
- permissions: permissions,
101
- };
102
-
103
- const token = jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
104
-
105
- res.status(201).json({
106
- token,
107
- user: {
108
- id: newUser.id,
109
- username: newUser.username,
110
- permissions: permissions,
111
- }
112
- });
113
-
114
- } catch (error) {
115
- if (error.code === 'P2002') {
116
- return res.status(409).json({ error: 'Пользователь с таким именем уже существует.' });
117
- }
118
- console.error('[Setup Error]', error);
119
- res.status(500).json({ error: 'Не удалось создать администратора' });
120
- }
121
- });
122
-
123
- /**
124
- * @route POST /api/auth/recovery/verify
125
- * @desc Проверка кода восстановления
126
- * @access Public
127
- */
128
- router.post('/recovery/verify', async (req, res) => {
129
- try {
130
- const { recoveryCode } = req.body;
131
-
132
- if (!recoveryCode) {
133
- return res.status(400).json({ error: 'Код восстановления обязателен' });
134
- }
135
-
136
- if (recoveryCode !== config.security.adminRecoveryCode) {
137
- await new Promise(resolve => setTimeout(resolve, 1000));
138
- return res.status(401).json({ error: 'Неверный код восстановления' });
139
- }
140
-
141
- const rootUser = await prisma.panelUser.findFirst({
142
- orderBy: { id: 'asc' },
143
- include: { role: true }
144
- });
145
-
146
- if (!rootUser) {
147
- return res.status(404).json({ error: 'В системе нет ни одного пользователя. Выполните первоначальную настройку.' });
148
- }
149
-
150
- const tokenId = crypto.randomBytes(16).toString('hex');
151
- const resetToken = jwt.sign(
152
- {
153
- userId: rootUser.id,
154
- type: 'password-reset',
155
- tokenId: tokenId,
156
- timestamp: Date.now()
157
- },
158
- JWT_SECRET,
159
- { expiresIn: '5m' }
160
- );
161
-
162
- activeResetTokens.set(tokenId, {
163
- userId: rootUser.id,
164
- createdAt: Date.now(),
165
- used: false
166
- });
167
-
168
- setTimeout(() => {
169
- activeResetTokens.delete(tokenId);
170
- }, 5 * 60 * 1000);
171
-
172
- res.json({
173
- success: true,
174
- username: rootUser.username,
175
- resetToken
176
- });
177
-
178
- } catch (error) {
179
- console.error('[Recovery Verify Error]', error);
180
- res.status(500).json({ error: 'Ошибка при проверке кода восстановления' });
181
- }
182
- });
183
-
184
- /**
185
- * @route POST /api/auth/recovery/reset
186
- * @desc Сброс пароля с использованием токена
187
- * @access Public (с валидным токеном сброса)
188
- */
189
- router.post('/recovery/reset', async (req, res) => {
190
- try {
191
- const { resetToken, newPassword } = req.body;
192
-
193
- if (!resetToken || !newPassword) {
194
- return res.status(400).json({ error: 'Токен и новый пароль обязательны' });
195
- }
196
-
197
- if (newPassword.length < 4) {
198
- return res.status(400).json({ error: 'Пароль должен быть не менее 4 символов' });
199
- }
200
-
201
- let decoded;
202
- try {
203
- decoded = jwt.verify(resetToken, JWT_SECRET);
204
- if (decoded.type !== 'password-reset') {
205
- throw new Error('Invalid token type');
206
- }
207
-
208
- const tokenInfo = activeResetTokens.get(decoded.tokenId);
209
- if (!tokenInfo) {
210
- throw new Error('Token not found in active tokens');
211
- }
212
-
213
- if (tokenInfo.used) {
214
- throw new Error('Token already used');
215
- }
216
-
217
- if (tokenInfo.userId !== decoded.userId) {
218
- throw new Error('Token userId mismatch');
219
- }
220
-
221
- } catch (err) {
222
- return res.status(401).json({ error: 'Недействительный или истекший токен' });
223
- }
224
-
225
- const hashedPassword = await bcrypt.hash(newPassword, 12);
226
- const updatedUser = await prisma.panelUser.update({
227
- where: { id: decoded.userId },
228
- data: { passwordHash: hashedPassword },
229
- select: { username: true }
230
- });
231
-
232
- const tokenInfo = activeResetTokens.get(decoded.tokenId);
233
- tokenInfo.used = true;
234
-
235
- activeResetTokens.delete(decoded.tokenId);
236
-
237
- res.json({
238
- message: 'Пароль успешно сброшен',
239
- username: updatedUser.username
240
- });
241
-
242
- } catch (error) {
243
- console.error('[Recovery Reset Error]', error);
244
- res.status(500).json({ error: 'Ошибка при сбросе пароля' });
245
- }
246
- });
247
-
248
- /**
249
- * @route POST /api/auth/login
250
- * @desc Аутентифицирует пользователя и возвращает токен
251
- * @access Public
252
- */
253
- router.post('/login', async (req, res) => {
254
- try {
255
- const { username, password } = req.body;
256
- if (!username || !password) {
257
- return res.status(400).json({ error: 'Имя пользователя и пароль обязательны' });
258
- }
259
-
260
- const user = await prisma.panelUser.findUnique({
261
- where: { username },
262
- include: { role: true },
263
- });
264
-
265
- if (!user) {
266
- return res.status(401).json({ error: 'Неверные учетные данные' });
267
- }
268
-
269
- const isMatch = await bcrypt.compare(password, user.passwordHash);
270
- if (!isMatch) {
271
- return res.status(401).json({ error: 'Неверные учетные данные' });
272
- }
273
-
274
- const permissions = JSON.parse(user.role.permissions || '[]');
275
-
276
- const payload = {
277
- userId: user.id,
278
- username: user.username,
279
- permissions: permissions,
280
- };
281
-
282
- const token = jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
283
-
284
- res.json({
285
- token,
286
- user: {
287
- id: user.id,
288
- username: user.username,
289
- permissions: permissions,
290
- }
291
- });
292
-
293
- } catch (error) {
294
- console.error('[Login Error]', error);
295
- res.status(500).json({ error: 'Ошибка входа в систему' });
296
- }
297
- });
298
-
299
-
300
- /**
301
- * @route GET /api/auth/me
302
- * @desc Получить данные текущего пользователя по токену
303
- * @access Private
304
- */
305
- router.get('/me', authenticate, async (req, res) => {
306
- try {
307
- const user = await prisma.panelUser.findUnique({
308
- where: { id: req.user.userId },
309
- select: { id: true, username: true, role: { select: { permissions: true } } }
310
- });
311
-
312
- if (!user) {
313
- return res.status(404).json({ error: "Пользователь не найден." });
314
- }
315
-
316
- res.json({
317
- id: user.id,
318
- username: user.username,
319
- permissions: JSON.parse(user.role.permissions || '[]')
320
- });
321
- } catch (error) {
322
- res.status(500).json({ error: "Ошибка сервера" });
323
- }
324
- });
325
-
326
-
327
- /**
328
- * @route GET /api/auth/users
329
- * @desc Получить всех пользователей и их роли
330
- * @access Private (Admin only)
331
- */
332
- router.get('/users', authenticate, authorize('panel:user:list'), async (req, res) => {
333
- try {
334
- const users = await prisma.panelUser.findMany({
335
- include: { role: true, botAccess: { include: { bot: { select: { id: true, username: true } } } } },
336
- orderBy: { username: 'asc' }
337
- });
338
- res.json(users.map(u => ({
339
- ...u,
340
- botAccess: u.botAccess.map(a => ({ botId: a.botId, bot: a.bot }))
341
- })));
342
- } catch (error) {
343
- res.status(500).json({ error: 'Не удалось получить пользователей' });
344
- }
345
- });
346
-
347
- /**
348
- * @route POST /api/auth/users
349
- * @desc Создать нового пользователя
350
- * @access Private (Admin only)
351
- */
352
- router.post('/users', authenticate, ownerOnly, authorize('panel:user:create'), async (req, res) => {
353
- const { username, password, roleId, allBots = true, botIds = [] } = req.body;
354
- if (!username || !password || !roleId) {
355
- return res.status(400).json({ error: 'Имя, пароль и роль обязательны' });
356
- }
357
- if (password.length < 4) {
358
- return res.status(400).json({ error: 'Пароль должен быть не менее 4 символов' });
359
- }
360
-
361
- try {
362
- const hashedPassword = await bcrypt.hash(password, 12);
363
- const newUser = await prisma.panelUser.create({
364
- data: {
365
- username,
366
- passwordHash: hashedPassword,
367
- roleId: parseInt(roleId, 10),
368
- allBots: !!allBots,
369
- botAccess: allBots ? undefined : {
370
- create: (Array.isArray(botIds) ? botIds : []).map(id => ({ bot: { connect: { id: Number(id) } } }))
371
- }
372
- },
373
- include: { role: true, botAccess: true }
374
- });
375
- const { passwordHash, ...userToReturn } = newUser;
376
- res.status(201).json(userToReturn);
377
- } catch (error) {
378
- if (error.code === 'P2002') {
379
- return res.status(409).json({ error: 'Пользователь с таким именем уже существует' });
380
- }
381
- res.status(500).json({ error: 'Не удалось создать пользователя' });
382
- }
383
- });
384
-
385
-
386
- /**
387
- * @route GET /api/auth/roles
388
- * @desc Получить все роли
389
- * @access Private (Admin only)
390
- */
391
- router.get('/roles', authenticate, authorize('panel:role:list'), async (req, res) => {
392
- try {
393
- const roles = await prisma.panelRole.findMany();
394
- const rolesWithParsedPermissions = roles.map(role => ({
395
- ...role,
396
- permissions: JSON.parse(role.permissions || '[]')
397
- }));
398
- res.json(rolesWithParsedPermissions);
399
- } catch (error) {
400
- res.status(500).json({ error: 'Не удалось получить роли' });
401
- }
402
- });
403
-
404
- const ALL_PERMISSIONS = [
405
- { id: '*', label: 'Все права (Администратор)' },
406
- { id: 'bot:list', label: 'Просмотр ботов' },
407
- { id: 'bot:create', label: 'Создание ботов' },
408
- { id: 'bot:update', label: 'Редактирование ботов' },
409
- { id: 'bot:delete', label: 'Удаление ботов' },
410
- { id: 'bot:start_stop', label: 'Запуск/остановка ботов' },
411
- { id: 'bot:interact', label: 'Взаимодействие с ботом (консоль)' },
412
- { id: 'bot:export', label: 'Экспорт ботов' },
413
- { id: 'bot:import', label: 'Импорт ботов' },
414
- { id: 'management:view', label: 'Просмотр вкладки "Управление" у бота' },
415
- { id: 'management:edit', label: 'Редактирование на вкладке "Управление" у бота' },
416
- { id: 'plugin:list', label: 'Просмотр плагинов' },
417
- { id: 'plugin:install', label: 'Установка плагинов' },
418
- { id: 'plugin:delete', label: 'Удаление плагинов' },
419
- { id: 'plugin:update', label: 'Обновление плагинов' },
420
- { id: 'plugin:settings:view', label: 'Просмотр настроек плагинов' },
421
- { id: 'plugin:settings:edit', label: 'Редактирование настроек плагинов' },
422
- { id: 'plugin:browse', label: 'Просмотр каталога плагинов' },
423
- { id: 'plugin:develop', label: 'Разработка и редактирование плагинов (IDE)' },
424
- { id: 'server:list', label: 'Просмотр серверов' },
425
- { id: 'server:create', label: 'Создание серверов' },
426
- { id: 'server:delete', label: 'Удаление серверов' },
427
- { id: 'task:list', label: 'Просмотр задач' },
428
- { id: 'task:create', label: 'Создание задач' },
429
- { id: 'task:edit', label: 'Редактирование задач' },
430
- { id: 'task:delete', label: 'Удаление задач' },
431
- { id: 'panel:user:list', label: 'Просмотр пользователей панели' },
432
- { id: 'panel:user:create', label: 'Создание пользователей панели' },
433
- { id: 'panel:user:edit', label: 'Редактирование пользователей панели' },
434
- { id: 'panel:user:delete', label: 'Удаление пользователей панели' },
435
- { id: 'panel:role:list', label: 'Просмотр ролей панели' },
436
- { id: 'panel:role:create', label: 'Создание ролей панели' },
437
- { id: 'panel:role:edit', label: 'Редактирование ролей панели' },
438
- { id: 'panel:role:delete', label: 'Удаление ролей панели' },
439
- { id: 'panel:settings:view', label: 'Просмотр глобальных настроек' },
440
- { id: 'panel:settings:edit', label: 'Редактирование глобальных настроек' },
441
- { id: 'graph:read', label: 'Просмотр магазина графов' },
442
- { id: 'graph:download', label: 'Скачивание графов из магазина' },
443
- { id: 'graph:like', label: 'Лайки графов в магазине' },
444
- { id: 'graph:publish', label: 'Публикация графов в магазин' },
445
- ];
446
-
447
- const VIEWER_PERMISSIONS = [
448
- 'bot:list',
449
- 'plugin:list',
450
- 'plugin:settings:view',
451
- 'management:view',
452
- 'server:list',
453
- 'task:list',
454
- 'graph:read',
455
- ];
456
-
457
- /**
458
- * @route GET /api/auth/permissions
459
- * @desc Получить список всех возможных прав в системе
460
- * @access Private
461
- */
462
- router.get('/permissions', authenticate, (req, res) => {
463
- res.json(ALL_PERMISSIONS);
464
- });
465
-
466
-
467
- /**
468
- * @route PUT /api/auth/users/:id
469
- * @desc Обновить пользователя (роль, пароль)
470
- * @access Private (Admin only)
471
- */
472
- router.put('/users/:id', authenticate, ownerOnly, authorize('panel:user:edit'), async (req, res) => {
473
- const userId = parseInt(req.params.id, 10);
474
- const { password, roleId, allBots, botIds } = req.body;
475
-
476
- try {
477
- const owner = await prisma.panelUser.findFirst({ orderBy: { id: 'asc' } });
478
- const isOwner = owner && owner.id === userId;
479
-
480
- const updateData = {};
481
- if (password) {
482
- if (password.length < 4) return res.status(400).json({ error: 'Пароль должен быть не менее 4 символов' });
483
- updateData.passwordHash = await bcrypt.hash(password, 12);
484
- }
485
- if (roleId) {
486
- updateData.roleId = parseInt(roleId, 10);
487
- }
488
- if (!isOwner && typeof allBots === 'boolean') {
489
- updateData.allBots = allBots;
490
- }
491
-
492
- const updatedUser = await prisma.panelUser.update({
493
- where: { id: userId },
494
- data: updateData,
495
- include: { role: true }
496
- });
497
-
498
- if (Array.isArray(botIds)) {
499
- await prisma.panelUserBotAccess.deleteMany({ where: { userId } });
500
- if (!isOwner && !updatedUser.allBots && botIds.length > 0) {
501
- await prisma.panelUserBotAccess.createMany({
502
- data: botIds.map(id => ({ userId, botId: Number(id) }))
503
- });
504
- }
505
- }
506
-
507
- const { passwordHash, ...userToReturn } = updatedUser;
508
- res.json(userToReturn);
509
-
510
- } catch (error) {
511
- res.status(500).json({ error: 'Не удалось обновить пользователя' });
512
- }
513
- });
514
-
515
- /**
516
- * @route DELETE /api/auth/users/:id
517
- * @desc Удалить пользователя
518
- * @access Private (Admin only)
519
- */
520
- router.delete('/users/:id', authenticate, ownerOnly, authorize('panel:user:delete'), async (req, res) => {
521
- const userId = parseInt(req.params.id, 10);
522
-
523
- if (req.user.userId === userId) {
524
- return res.status(403).json({ error: 'Вы не можете удалить свою собственную учетную запись.' });
525
- }
526
-
527
- try {
528
- const owner = await prisma.panelUser.findFirst({ orderBy: { id: 'asc' } });
529
- if (owner && owner.id === userId) {
530
- return res.status(403).json({ error: 'Нельзя удалить владельца системы.' });
531
- }
532
- await prisma.panelUser.delete({ where: { id: userId } });
533
- res.status(204).send();
534
- } catch (error) {
535
- if (error.code === 'P2025') {
536
- return res.status(404).json({ error: 'Пользователь не найден' });
537
- }
538
- res.status(500).json({ error: 'Не удалось удалить пользователя' });
539
- }
540
- });
541
-
542
- /**
543
- * @route POST /api/auth/roles
544
- * @desc Создать новую роль
545
- * @access Private (Admin only)
546
- */
547
- router.post('/roles', authenticate, ownerOnly, authorize('panel:role:create'), async (req, res) => {
548
- const { name, permissions } = req.body;
549
- if (!name || !Array.isArray(permissions)) {
550
- return res.status(400).json({ error: 'Имя и массив прав обязательны' });
551
- }
552
- try {
553
- const newRole = await prisma.panelRole.create({
554
- data: {
555
- name,
556
- permissions: JSON.stringify(permissions)
557
- }
558
- });
559
- res.status(201).json({
560
- ...newRole,
561
- permissions: JSON.parse(newRole.permissions)
562
- });
563
- } catch (error) {
564
- if (error.code === 'P2002') {
565
- return res.status(409).json({ error: 'Роль с таким именем уже существует' });
566
- }
567
- res.status(500).json({ error: 'Не удалось создать роль' });
568
- }
569
- });
570
-
571
- /**
572
- * @route PUT /api/auth/roles/:id
573
- * @desc Обновить роль (имя и права)
574
- * @access Private (Admin only)
575
- */
576
- router.put('/roles/:id', authenticate, ownerOnly, authorize('panel:role:edit'), async (req, res) => {
577
- const roleId = parseInt(req.params.id, 10);
578
- const { name, permissions } = req.body;
579
- if (!name || !Array.isArray(permissions)) {
580
- return res.status(400).json({ error: 'Имя и массив прав обязательны' });
581
- }
582
- try {
583
- const role = await prisma.panelRole.findUnique({ where: { id: roleId } });
584
- if (role && role.name === 'Admin') {
585
- return res.status(403).json({ error: 'Редактирование роли "Admin" запрещено.' });
586
- }
587
-
588
- const updatedRole = await prisma.panelRole.update({
589
- where: { id: roleId },
590
- data: {
591
- name,
592
- permissions: JSON.stringify(permissions)
593
- }
594
- });
595
- res.json({
596
- ...updatedRole,
597
- permissions: JSON.parse(updatedRole.permissions)
598
- });
599
- } catch (error) {
600
- if (error.code === 'P2002') {
601
- return res.status(409).json({ error: 'Роль с таким именем уже существует' });
602
- }
603
- res.status(500).json({ error: 'Не удалось обновить роль' });
604
- }
605
- });
606
-
607
- /**
608
- * @route DELETE /api/auth/roles/:id
609
- * @desc Удалить роль
610
- * @access Private (Admin only)
611
- */
612
- router.delete('/roles/:id', authenticate, ownerOnly, authorize('panel:role:delete'), async (req, res) => {
613
- const roleId = parseInt(req.params.id, 10);
614
- try {
615
- const role = await prisma.panelRole.findUnique({ where: { id: roleId }, include: { users: true } });
616
- if (!role) return res.status(404).json({ error: 'Роль не найдена' });
617
- if (role.name === 'Admin') {
618
- return res.status(403).json({ error: 'Удаление роли "Admin" запрещено.' });
619
- }
620
- if (role.users.length > 0) {
621
- return res.status(400).json({ error: 'Нельзя удалить роль, которая назначена пользователям.' });
622
- }
623
- await prisma.panelRole.delete({ where: { id: roleId } });
624
- res.status(204).send();
625
- } catch (error) {
626
- res.status(500).json({ error: 'Не удалось удалить роль' });
627
- }
628
- });
629
-
630
- module.exports = {
631
- router,
632
- ALL_PERMISSIONS,
633
- VIEWER_PERMISSIONS,
1
+ const express = require('express');
2
+ const bcrypt = require('bcryptjs');
3
+ const jwt = require('jsonwebtoken');
4
+ const crypto = require('crypto');
5
+ const { PrismaClient } = require('@prisma/client');
6
+ const config = require('../../config');
7
+ const { authenticate, authorize } = require('../middleware/auth');
8
+ const { authenticatePanelApiKey } = require('../middleware/panelApiAuth');
9
+ const path = require('path');
10
+ const os = require('os');
11
+
12
+ const router = express.Router();
13
+ const prisma = new PrismaClient();
14
+
15
+ const JWT_SECRET = config.security.jwtSecret;
16
+ const JWT_EXPIRES_IN = '7d';
17
+
18
+ const activeResetTokens = new Map();
19
+
20
+ const ALL_PERMISSIONS = [
21
+ { id: '*', label: 'Все права (Администратор)' },
22
+ { id: 'bot:list', label: 'Просмотр ботов' },
23
+ { id: 'bot:create', label: 'Создание ботов' },
24
+ { id: 'bot:update', label: 'Редактирование ботов' },
25
+ { id: 'bot:delete', label: 'Удаление ботов' },
26
+ { id: 'bot:start_stop', label: 'Запуск/остановка ботов' },
27
+ { id: 'bot:interact', label: 'Взаимодействие с ботом (консоль)' },
28
+ { id: 'bot:export', label: 'Экспорт ботов' },
29
+ { id: 'bot:import', label: 'Импорт ботов' },
30
+ { id: 'bot:history:view', label: 'Просмотр истории чата и команд' },
31
+ { id: 'bot:history:manage', label: 'Управление историей (очистка)' },
32
+ { id: 'management:view', label: 'Просмотр вкладки "Управление" у бота' },
33
+ { id: 'management:edit', label: 'Редактирование на вкладке "Управление" у бота' },
34
+ { id: 'plugin:list', label: 'Просмотр плагинов' },
35
+ { id: 'plugin:install', label: 'Установка плагинов' },
36
+ { id: 'plugin:delete', label: 'Удаление плагинов' },
37
+ { id: 'plugin:update', label: 'Обновление плагинов' },
38
+ { id: 'plugin:settings:view', label: 'Просмотр настроек плагинов' },
39
+ { id: 'plugin:settings:edit', label: 'Редактирование настроек плагинов' },
40
+ { id: 'plugin:browse', label: 'Просмотр каталога плагинов' },
41
+ { id: 'plugin:develop', label: 'Разработка и редактирование плагинов (IDE)' },
42
+ { id: 'server:list', label: 'Просмотр серверов' },
43
+ { id: 'server:create', label: 'Создание серверов' },
44
+ { id: 'server:delete', label: 'Удаление серверов' },
45
+ { id: 'proxy:list', label: 'Просмотр прокси' },
46
+ { id: 'proxy:create', label: 'Создание и редактирование прокси' },
47
+ { id: 'proxy:delete', label: 'Удаление прокси' },
48
+ { id: 'task:list', label: 'Просмотр задач' },
49
+ { id: 'task:create', label: 'Создание задач' },
50
+ { id: 'task:edit', label: 'Редактирование задач' },
51
+ { id: 'task:delete', label: 'Удаление задач' },
52
+ { id: 'panel:user:list', label: 'Просмотр пользователей панели' },
53
+ { id: 'panel:user:create', label: 'Создание пользователей панели' },
54
+ { id: 'panel:user:edit', label: 'Редактирование пользователей панели' },
55
+ { id: 'panel:user:delete', label: 'Удаление пользователей панели' },
56
+ { id: 'panel:role:list', label: 'Просмотр ролей панели' },
57
+ { id: 'panel:role:create', label: 'Создание ролей панели' },
58
+ { id: 'panel:role:edit', label: 'Редактирование ролей панели' },
59
+ { id: 'panel:role:delete', label: 'Удаление ролей панели' },
60
+ { id: 'panel:settings:view', label: 'Просмотр глобальных настроек' },
61
+ { id: 'panel:settings:edit', label: 'Редактирование глобальных настроек' },
62
+ { id: 'graph:read', label: 'Просмотр магазина графов' },
63
+ { id: 'graph:download', label: 'Скачивание графов из магазина' },
64
+ { id: 'graph:like', label: 'Лайки графов в магазине' },
65
+ { id: 'graph:publish', label: 'Публикация графов в магазин' },
66
+ ];
67
+
68
+ const VIEWER_PERMISSIONS = [
69
+ 'bot:list',
70
+ 'bot:history:view',
71
+ 'plugin:list',
72
+ 'plugin:settings:view',
73
+ 'management:view',
74
+ 'server:list',
75
+ 'proxy:list',
76
+ 'task:list',
77
+ 'graph:read',
78
+ ];
79
+
80
+ function ownerOnly(req, res, next) {
81
+ if (req.user && req.user.userId === 1) return next();
82
+ return res.status(403).json({ error: 'Только владелец может изменять права пользователей и роли.' });
83
+ }
84
+
85
+ /**
86
+ * @route GET /api/auth/status
87
+ * @desc Проверяет, была ли произведена первоначальная настройка (создан админ)
88
+ * @access Public
89
+ */
90
+ router.get('/status', async (req, res) => {
91
+ try {
92
+ const userCount = await prisma.panelUser.count();
93
+ res.json({ needsSetup: userCount === 0 });
94
+ } catch (error) {
95
+ console.error('[Auth Status Error]', error);
96
+ res.status(500).json({ error: 'Не удалось проверить статус сервера' });
97
+ }
98
+ });
99
+
100
+ /**
101
+ * @route GET /api/auth/config-path
102
+ * @desc Получить путь к конфигурационному файлу
103
+ * @access Public
104
+ */
105
+ router.get('/config-path', (req, res) => {
106
+ const configPath = path.join(os.homedir(), '.blockmine', 'config.json');
107
+ res.json({ configPath });
108
+ });
109
+
110
+ /**
111
+ * @route POST /api/auth/setup
112
+ * @desc Создает первого пользователя с ролью администратора
113
+ * @access Public (только если нет других пользователей)
114
+ */
115
+ router.post('/setup', async (req, res) => {
116
+ try {
117
+ const userCount = await prisma.panelUser.count();
118
+ if (userCount > 0) {
119
+ return res.status(403).json({ error: 'Настройка уже произведена.' });
120
+ }
121
+
122
+ const { username, password } = req.body;
123
+ if (!username || !password || password.length < 4) {
124
+ return res.status(400).json({ error: 'Имя пользователя и пароль (минимум 4 символа) обязательны.' });
125
+ }
126
+
127
+ const hashedPassword = await bcrypt.hash(password, 12);
128
+
129
+ let newUser;
130
+
131
+ await prisma.$transaction(async (tx) => {
132
+ const adminPermissions = ALL_PERMISSIONS
133
+ .map(p => p.id)
134
+ .filter(id => id !== '*' && id !== 'plugin:develop');
135
+
136
+ const adminRole = await tx.panelRole.upsert({
137
+ where: { name: 'Admin' },
138
+ update: {},
139
+ create: {
140
+ name: 'Admin',
141
+ permissions: JSON.stringify(adminPermissions)
142
+ },
143
+ });
144
+
145
+ newUser = await tx.panelUser.create({
146
+ data: {
147
+ username,
148
+ passwordHash: hashedPassword,
149
+ roleId: adminRole.id,
150
+ },
151
+ include: { role: true }
152
+ });
153
+ });
154
+
155
+
156
+ const permissions = JSON.parse(newUser.role.permissions || '[]');
157
+
158
+ const payload = {
159
+ userId: newUser.id,
160
+ username: newUser.username,
161
+ permissions: permissions,
162
+ };
163
+
164
+ const token = jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
165
+
166
+ res.status(201).json({
167
+ token,
168
+ user: {
169
+ id: newUser.id,
170
+ username: newUser.username,
171
+ permissions: permissions,
172
+ }
173
+ });
174
+
175
+ } catch (error) {
176
+ if (error.code === 'P2002') {
177
+ return res.status(409).json({ error: 'Пользователь с таким именем уже существует.' });
178
+ }
179
+ console.error('[Setup Error]', error);
180
+ res.status(500).json({ error: 'Не удалось создать администратора' });
181
+ }
182
+ });
183
+
184
+ /**
185
+ * @route POST /api/auth/recovery/verify
186
+ * @desc Проверка кода восстановления
187
+ * @access Public
188
+ */
189
+ router.post('/recovery/verify', async (req, res) => {
190
+ try {
191
+ const { recoveryCode } = req.body;
192
+
193
+ if (!recoveryCode) {
194
+ return res.status(400).json({ error: 'Код восстановления обязателен' });
195
+ }
196
+
197
+ if (recoveryCode !== config.security.adminRecoveryCode) {
198
+ await new Promise(resolve => setTimeout(resolve, 1000));
199
+ return res.status(401).json({ error: 'Неверный код восстановления' });
200
+ }
201
+
202
+ const rootUser = await prisma.panelUser.findFirst({
203
+ orderBy: { id: 'asc' },
204
+ include: { role: true }
205
+ });
206
+
207
+ if (!rootUser) {
208
+ return res.status(404).json({ error: 'В системе нет ни одного пользователя. Выполните первоначальную настройку.' });
209
+ }
210
+
211
+ const tokenId = crypto.randomBytes(16).toString('hex');
212
+ const resetToken = jwt.sign(
213
+ {
214
+ userId: rootUser.id,
215
+ type: 'password-reset',
216
+ tokenId: tokenId,
217
+ timestamp: Date.now()
218
+ },
219
+ JWT_SECRET,
220
+ { expiresIn: '5m' }
221
+ );
222
+
223
+ activeResetTokens.set(tokenId, {
224
+ userId: rootUser.id,
225
+ createdAt: Date.now(),
226
+ used: false
227
+ });
228
+
229
+ setTimeout(() => {
230
+ activeResetTokens.delete(tokenId);
231
+ }, 5 * 60 * 1000);
232
+
233
+ res.json({
234
+ success: true,
235
+ username: rootUser.username,
236
+ resetToken
237
+ });
238
+
239
+ } catch (error) {
240
+ console.error('[Recovery Verify Error]', error);
241
+ res.status(500).json({ error: 'Ошибка при проверке кода восстановления' });
242
+ }
243
+ });
244
+
245
+ /**
246
+ * @route POST /api/auth/recovery/reset
247
+ * @desc Сброс пароля с использованием токена
248
+ * @access Public (с валидным токеном сброса)
249
+ */
250
+ router.post('/recovery/reset', async (req, res) => {
251
+ try {
252
+ const { resetToken, newPassword } = req.body;
253
+
254
+ if (!resetToken || !newPassword) {
255
+ return res.status(400).json({ error: 'Токен и новый пароль обязательны' });
256
+ }
257
+
258
+ if (newPassword.length < 4) {
259
+ return res.status(400).json({ error: 'Пароль должен быть не менее 4 символов' });
260
+ }
261
+
262
+ let decoded;
263
+ try {
264
+ decoded = jwt.verify(resetToken, JWT_SECRET);
265
+ if (decoded.type !== 'password-reset') {
266
+ throw new Error('Invalid token type');
267
+ }
268
+
269
+ const tokenInfo = activeResetTokens.get(decoded.tokenId);
270
+ if (!tokenInfo) {
271
+ throw new Error('Token not found in active tokens');
272
+ }
273
+
274
+ if (tokenInfo.used) {
275
+ throw new Error('Token already used');
276
+ }
277
+
278
+ if (tokenInfo.userId !== decoded.userId) {
279
+ throw new Error('Token userId mismatch');
280
+ }
281
+
282
+ } catch (err) {
283
+ return res.status(401).json({ error: 'Недействительный или истекший токен' });
284
+ }
285
+
286
+ const hashedPassword = await bcrypt.hash(newPassword, 12);
287
+ const updatedUser = await prisma.panelUser.update({
288
+ where: { id: decoded.userId },
289
+ data: { passwordHash: hashedPassword },
290
+ select: { username: true }
291
+ });
292
+
293
+ const tokenInfo = activeResetTokens.get(decoded.tokenId);
294
+ tokenInfo.used = true;
295
+
296
+ activeResetTokens.delete(decoded.tokenId);
297
+
298
+ res.json({
299
+ message: 'Пароль успешно сброшен',
300
+ username: updatedUser.username
301
+ });
302
+
303
+ } catch (error) {
304
+ console.error('[Recovery Reset Error]', error);
305
+ res.status(500).json({ error: 'Ошибка при сбросе пароля' });
306
+ }
307
+ });
308
+
309
+ /**
310
+ * @route POST /api/auth/login
311
+ * @desc Аутентифицирует пользователя и возвращает токен
312
+ * @access Public
313
+ */
314
+ router.post('/login', async (req, res) => {
315
+ try {
316
+ const { username, password } = req.body;
317
+ if (!username || !password) {
318
+ return res.status(400).json({ error: 'Имя пользователя и пароль обязательны' });
319
+ }
320
+
321
+ const user = await prisma.panelUser.findUnique({
322
+ where: { username },
323
+ include: { role: true },
324
+ });
325
+
326
+ if (!user) {
327
+ return res.status(401).json({ error: 'Неверные учетные данные' });
328
+ }
329
+
330
+ const isMatch = await bcrypt.compare(password, user.passwordHash);
331
+ if (!isMatch) {
332
+ return res.status(401).json({ error: 'Неверные учетные данные' });
333
+ }
334
+
335
+ let permissions = JSON.parse(user.role.permissions || '[]');
336
+
337
+ // Владелец (первый пользователь) всегда получает все актуальные права
338
+ if (user.id === 1) {
339
+ permissions = ALL_PERMISSIONS.map(p => p.id).filter(id => id !== '*');
340
+ }
341
+
342
+ const payload = {
343
+ userId: user.id,
344
+ username: user.username,
345
+ permissions: permissions,
346
+ };
347
+
348
+ const token = jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
349
+
350
+ res.json({
351
+ token,
352
+ user: {
353
+ id: user.id,
354
+ username: user.username,
355
+ permissions: permissions,
356
+ }
357
+ });
358
+
359
+ } catch (error) {
360
+ console.error('[Login Error]', error);
361
+ res.status(500).json({ error: 'Ошибка входа в систему' });
362
+ }
363
+ });
364
+
365
+
366
+ /**
367
+ * @route GET /api/auth/validate
368
+ * @desc Validate Panel API key
369
+ * @access Public (requires API key)
370
+ */
371
+ router.get('/validate', authenticatePanelApiKey, async (req, res) => {
372
+ res.json({
373
+ valid: true,
374
+ user: {
375
+ id: req.user.id,
376
+ username: req.user.username,
377
+ role: req.user.roleName,
378
+ permissions: req.user.permissions
379
+ },
380
+ apiKey: {
381
+ id: req.apiKey.id,
382
+ name: req.apiKey.name,
383
+ prefix: req.apiKey.prefix
384
+ }
385
+ });
386
+ });
387
+
388
+ /**
389
+ * @route GET /api/auth/me
390
+ * @desc Получить данные текущего пользователя по токену
391
+ * @access Private
392
+ */
393
+ router.get('/me', authenticate, async (req, res) => {
394
+ try {
395
+ const user = await prisma.panelUser.findUnique({
396
+ where: { id: req.user.userId },
397
+ select: { id: true, username: true, role: { select: { permissions: true } } }
398
+ });
399
+
400
+ if (!user) {
401
+ return res.status(404).json({ error: "Пользователь не найден." });
402
+ }
403
+
404
+ res.json({
405
+ id: user.id,
406
+ username: user.username,
407
+ permissions: JSON.parse(user.role.permissions || '[]')
408
+ });
409
+ } catch (error) {
410
+ res.status(500).json({ error: "Ошибка сервера" });
411
+ }
412
+ });
413
+
414
+
415
+ /**
416
+ * @route GET /api/auth/users
417
+ * @desc Получить всех пользователей и их роли
418
+ * @access Private (Admin only)
419
+ */
420
+ router.get('/users', authenticate, authorize('panel:user:list'), async (req, res) => {
421
+ try {
422
+ const users = await prisma.panelUser.findMany({
423
+ include: { role: true, botAccess: { include: { bot: { select: { id: true, username: true } } } } },
424
+ orderBy: { username: 'asc' }
425
+ });
426
+ res.json(users.map(u => ({
427
+ ...u,
428
+ botAccess: u.botAccess.map(a => ({ botId: a.botId, bot: a.bot }))
429
+ })));
430
+ } catch (error) {
431
+ res.status(500).json({ error: 'Не удалось получить пользователей' });
432
+ }
433
+ });
434
+
435
+ /**
436
+ * @route POST /api/auth/users
437
+ * @desc Создать нового пользователя
438
+ * @access Private (Admin only)
439
+ */
440
+ router.post('/users', authenticate, ownerOnly, authorize('panel:user:create'), async (req, res) => {
441
+ const { username, password, roleId, allBots = true, botIds = [] } = req.body;
442
+ if (!username || !password || !roleId) {
443
+ return res.status(400).json({ error: 'Имя, пароль и роль обязательны' });
444
+ }
445
+ if (password.length < 4) {
446
+ return res.status(400).json({ error: 'Пароль должен быть не менее 4 символов' });
447
+ }
448
+
449
+ try {
450
+ const hashedPassword = await bcrypt.hash(password, 12);
451
+ const newUser = await prisma.panelUser.create({
452
+ data: {
453
+ username,
454
+ passwordHash: hashedPassword,
455
+ roleId: parseInt(roleId, 10),
456
+ allBots: !!allBots,
457
+ botAccess: allBots ? undefined : {
458
+ create: (Array.isArray(botIds) ? botIds : []).map(id => ({ bot: { connect: { id: Number(id) } } }))
459
+ }
460
+ },
461
+ include: { role: true, botAccess: true }
462
+ });
463
+ const { passwordHash, ...userToReturn } = newUser;
464
+ res.status(201).json(userToReturn);
465
+ } catch (error) {
466
+ if (error.code === 'P2002') {
467
+ return res.status(409).json({ error: 'Пользователь с таким именем уже существует' });
468
+ }
469
+ res.status(500).json({ error: 'Не удалось создать пользователя' });
470
+ }
471
+ });
472
+
473
+
474
+ /**
475
+ * @route GET /api/auth/roles
476
+ * @desc Получить все роли
477
+ * @access Private (Admin only)
478
+ */
479
+ router.get('/roles', authenticate, authorize('panel:role:list'), async (req, res) => {
480
+ try {
481
+ const roles = await prisma.panelRole.findMany();
482
+ const rolesWithParsedPermissions = roles.map(role => ({
483
+ ...role,
484
+ permissions: JSON.parse(role.permissions || '[]')
485
+ }));
486
+ res.json(rolesWithParsedPermissions);
487
+ } catch (error) {
488
+ res.status(500).json({ error: 'Не удалось получить роли' });
489
+ }
490
+ });
491
+
492
+
493
+ /**
494
+ * @route GET /api/auth/permissions
495
+ * @desc Получить список всех возможных прав в системе
496
+ * @access Private
497
+ */
498
+ router.get('/permissions', authenticate, (req, res) => {
499
+ res.json(ALL_PERMISSIONS);
500
+ });
501
+
502
+
503
+ /**
504
+ * @route PUT /api/auth/users/:id
505
+ * @desc Обновить пользователя (роль, пароль)
506
+ * @access Private (Admin only)
507
+ */
508
+ router.put('/users/:id', authenticate, ownerOnly, authorize('panel:user:edit'), async (req, res) => {
509
+ const userId = parseInt(req.params.id, 10);
510
+ const { password, roleId, allBots, botIds } = req.body;
511
+
512
+ try {
513
+ const owner = await prisma.panelUser.findFirst({ orderBy: { id: 'asc' } });
514
+ const isOwner = owner && owner.id === userId;
515
+
516
+ const updateData = {};
517
+ if (password) {
518
+ if (password.length < 4) return res.status(400).json({ error: 'Пароль должен быть не менее 4 символов' });
519
+ updateData.passwordHash = await bcrypt.hash(password, 12);
520
+ }
521
+ if (roleId) {
522
+ updateData.roleId = parseInt(roleId, 10);
523
+ }
524
+ if (!isOwner && typeof allBots === 'boolean') {
525
+ updateData.allBots = allBots;
526
+ }
527
+
528
+ const updatedUser = await prisma.panelUser.update({
529
+ where: { id: userId },
530
+ data: updateData,
531
+ include: { role: true }
532
+ });
533
+
534
+ if (Array.isArray(botIds)) {
535
+ await prisma.panelUserBotAccess.deleteMany({ where: { userId } });
536
+ if (!isOwner && !updatedUser.allBots && botIds.length > 0) {
537
+ await prisma.panelUserBotAccess.createMany({
538
+ data: botIds.map(id => ({ userId, botId: Number(id) }))
539
+ });
540
+ }
541
+ }
542
+
543
+ const { passwordHash, ...userToReturn } = updatedUser;
544
+ res.json(userToReturn);
545
+
546
+ } catch (error) {
547
+ res.status(500).json({ error: 'Не удалось обновить пользователя' });
548
+ }
549
+ });
550
+
551
+ /**
552
+ * @route DELETE /api/auth/users/:id
553
+ * @desc Удалить пользователя
554
+ * @access Private (Admin only)
555
+ */
556
+ router.delete('/users/:id', authenticate, ownerOnly, authorize('panel:user:delete'), async (req, res) => {
557
+ const userId = parseInt(req.params.id, 10);
558
+
559
+ if (req.user.userId === userId) {
560
+ return res.status(403).json({ error: 'Вы не можете удалить свою собственную учетную запись.' });
561
+ }
562
+
563
+ try {
564
+ const owner = await prisma.panelUser.findFirst({ orderBy: { id: 'asc' } });
565
+ if (owner && owner.id === userId) {
566
+ return res.status(403).json({ error: 'Нельзя удалить владельца системы.' });
567
+ }
568
+ await prisma.panelUser.delete({ where: { id: userId } });
569
+ res.status(204).send();
570
+ } catch (error) {
571
+ if (error.code === 'P2025') {
572
+ return res.status(404).json({ error: 'Пользователь не найден' });
573
+ }
574
+ res.status(500).json({ error: 'Не удалось удалить пользователя' });
575
+ }
576
+ });
577
+
578
+ /**
579
+ * @route POST /api/auth/roles
580
+ * @desc Создать новую роль
581
+ * @access Private (Admin only)
582
+ */
583
+ router.post('/roles', authenticate, ownerOnly, authorize('panel:role:create'), async (req, res) => {
584
+ const { name, permissions } = req.body;
585
+ if (!name || !Array.isArray(permissions)) {
586
+ return res.status(400).json({ error: 'Имя и массив прав обязательны' });
587
+ }
588
+ try {
589
+ const newRole = await prisma.panelRole.create({
590
+ data: {
591
+ name,
592
+ permissions: JSON.stringify(permissions)
593
+ }
594
+ });
595
+ res.status(201).json({
596
+ ...newRole,
597
+ permissions: JSON.parse(newRole.permissions)
598
+ });
599
+ } catch (error) {
600
+ if (error.code === 'P2002') {
601
+ return res.status(409).json({ error: 'Роль с таким именем уже существует' });
602
+ }
603
+ res.status(500).json({ error: 'Не удалось создать роль' });
604
+ }
605
+ });
606
+
607
+ /**
608
+ * @route PUT /api/auth/roles/:id
609
+ * @desc Обновить роль (имя и права)
610
+ * @access Private (Admin only)
611
+ */
612
+ router.put('/roles/:id', authenticate, ownerOnly, authorize('panel:role:edit'), async (req, res) => {
613
+ const roleId = parseInt(req.params.id, 10);
614
+ const { name, permissions } = req.body;
615
+ if (!name || !Array.isArray(permissions)) {
616
+ return res.status(400).json({ error: 'Имя и массив прав обязательны' });
617
+ }
618
+ try {
619
+ const role = await prisma.panelRole.findUnique({ where: { id: roleId } });
620
+ if (role && role.name === 'Admin') {
621
+ return res.status(403).json({ error: 'Редактирование роли "Admin" запрещено.' });
622
+ }
623
+
624
+ const updatedRole = await prisma.panelRole.update({
625
+ where: { id: roleId },
626
+ data: {
627
+ name,
628
+ permissions: JSON.stringify(permissions)
629
+ }
630
+ });
631
+ res.json({
632
+ ...updatedRole,
633
+ permissions: JSON.parse(updatedRole.permissions)
634
+ });
635
+ } catch (error) {
636
+ if (error.code === 'P2002') {
637
+ return res.status(409).json({ error: 'Роль с таким именем уже существует' });
638
+ }
639
+ res.status(500).json({ error: 'Не удалось обновить роль' });
640
+ }
641
+ });
642
+
643
+ /**
644
+ * @route DELETE /api/auth/roles/:id
645
+ * @desc Удалить роль
646
+ * @access Private (Admin only)
647
+ */
648
+ router.delete('/roles/:id', authenticate, ownerOnly, authorize('panel:role:delete'), async (req, res) => {
649
+ const roleId = parseInt(req.params.id, 10);
650
+ try {
651
+ const role = await prisma.panelRole.findUnique({ where: { id: roleId }, include: { users: true } });
652
+ if (!role) return res.status(404).json({ error: 'Роль не найдена' });
653
+ if (role.name === 'Admin') {
654
+ return res.status(403).json({ error: 'Удаление роли "Admin" запрещено.' });
655
+ }
656
+ if (role.users.length > 0) {
657
+ return res.status(400).json({ error: 'Нельзя удалить роль, которая назначена пользователям.' });
658
+ }
659
+ await prisma.panelRole.delete({ where: { id: roleId } });
660
+ res.status(204).send();
661
+ } catch (error) {
662
+ res.status(500).json({ error: 'Не удалось удалить роль' });
663
+ }
664
+ });
665
+
666
+ module.exports = {
667
+ router,
668
+ ALL_PERMISSIONS,
669
+ VIEWER_PERMISSIONS,
634
670
  };