@ozdao/martyrs 0.2.563 → 0.2.565

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 (207) hide show
  1. package/dist/abac-BPl9Bmf9.js +1527 -0
  2. package/dist/builder.js +51 -39
  3. package/dist/{common.schema-GFSlNJo7.js → common.schema-DswiUXKB.js} +1 -1
  4. package/dist/community.server.js +48 -9
  5. package/dist/core.server.js +6 -4
  6. package/dist/{crud-C7FSTUes.js → crud-q1ye5IhV.js} +7 -7
  7. package/dist/events.server.js +3 -3
  8. package/dist/gallery.server.js +2 -2
  9. package/dist/inventory.server.js +4 -6
  10. package/dist/{main-CmjWiDVF.js → main-B9o1iBAZ.js} +1279 -1287
  11. package/dist/marketplace.server.js +1 -1
  12. package/dist/martyrs/src/components/Button/Button.vue2.js +33 -42
  13. package/dist/martyrs/src/components/Button/Button.vue2.js.map +1 -1
  14. package/dist/martyrs/src/components/EditImages/{EditImages.vue.js → EditImages.vue2.js} +2 -2
  15. package/dist/martyrs/src/components/EditImages/EditImages.vue2.js.map +1 -0
  16. package/dist/martyrs/src/components/Feed/Feed.vue.js +1 -1
  17. package/dist/martyrs/src/components/FieldPhone/FieldPhone.vue.js +1 -1
  18. package/dist/martyrs/src/components/FieldPhone/FieldPhone.vue.js.map +1 -1
  19. package/dist/martyrs/src/components/Loader/Loader.vue.js +1 -2
  20. package/dist/martyrs/src/components/Loader/Loader.vue.js.map +1 -1
  21. package/dist/martyrs/src/components/Menu/{Menu.vue.js → Menu.vue2.js} +2 -2
  22. package/dist/martyrs/src/components/Menu/Menu.vue2.js.map +1 -0
  23. package/dist/martyrs/src/components/Tab/{Tab.vue.js → Tab.vue2.js} +2 -2
  24. package/dist/martyrs/src/components/Tab/Tab.vue2.js.map +1 -0
  25. package/dist/martyrs/src/components/Tree/Tree.vue.js +6 -3
  26. package/dist/martyrs/src/components/Tree/Tree.vue.js.map +1 -1
  27. package/dist/martyrs/src/modules/auth/auth.client.js +10 -7
  28. package/dist/martyrs/src/modules/auth/auth.client.js.map +1 -1
  29. package/dist/martyrs/src/modules/auth/views/components/pages/EnterPassword.vue.js +1 -1
  30. package/dist/martyrs/src/modules/auth/views/components/pages/Invite.vue.js +1 -1
  31. package/dist/martyrs/src/modules/auth/views/components/pages/Profile.vue.js +1 -1
  32. package/dist/martyrs/src/modules/auth/views/components/pages/ProfileBlogposts.vue.js +1 -1
  33. package/dist/martyrs/src/modules/auth/views/components/pages/ResetPassword.vue.js +1 -1
  34. package/dist/martyrs/src/modules/auth/views/components/pages/SignIn.vue.js +12 -12
  35. package/dist/martyrs/src/modules/auth/views/components/pages/SignIn.vue.js.map +1 -1
  36. package/dist/martyrs/src/modules/auth/views/components/pages/SignUp.vue.js +1 -1
  37. package/dist/martyrs/src/modules/auth/views/router/auth.router.js +116 -0
  38. package/dist/martyrs/src/modules/auth/views/router/auth.router.js.map +1 -0
  39. package/dist/martyrs/src/modules/auth/views/router/users.router.js +180 -0
  40. package/dist/martyrs/src/modules/auth/views/router/users.router.js.map +1 -0
  41. package/dist/martyrs/src/modules/backoffice/components/partials/Sidebar.vue.js +3 -3
  42. package/dist/martyrs/src/modules/backoffice/components/partials/Sidebar.vue.js.map +1 -1
  43. package/dist/martyrs/src/modules/core/locales/en.js +45 -0
  44. package/dist/martyrs/src/modules/core/locales/en.js.map +1 -1
  45. package/dist/martyrs/src/modules/core/locales/ru.js +45 -0
  46. package/dist/martyrs/src/modules/core/locales/ru.js.map +1 -1
  47. package/dist/martyrs/src/modules/core/views/classes/i18n.manager.js +9 -0
  48. package/dist/martyrs/src/modules/core/views/classes/i18n.manager.js.map +1 -1
  49. package/dist/martyrs/src/modules/core/views/components/sections/{Filters.vue.js → Filters.vue2.js} +2 -2
  50. package/dist/martyrs/src/modules/core/views/components/sections/Filters.vue2.js.map +1 -0
  51. package/dist/martyrs/src/modules/core/views/components/sections/SectionPageTitle.vue.js +1 -1
  52. package/dist/martyrs/src/modules/core/views/mixins/mixins.js +1 -2
  53. package/dist/martyrs/src/modules/core/views/mixins/mixins.js.map +1 -1
  54. package/dist/martyrs/src/modules/core/views/router/addRoutes.js +6 -1
  55. package/dist/martyrs/src/modules/core/views/router/addRoutes.js.map +1 -1
  56. package/dist/martyrs/src/modules/events/components/pages/EditEvent.vue.js +1 -1
  57. package/dist/martyrs/src/modules/events/components/pages/Event.vue.js +1 -1
  58. package/dist/martyrs/src/modules/events/components/pages/EventsBackoffice.vue.js +1 -1
  59. package/dist/martyrs/src/modules/gallery/components/sections/BackofficeGallery.vue.js +1 -1
  60. package/dist/martyrs/src/modules/inventory/components/pages/InventoryEdit.vue.js +2 -2
  61. package/dist/martyrs/src/modules/inventory/components/pages/InventoryEdit.vue.js.map +1 -1
  62. package/dist/martyrs/src/modules/marketplace/views/components/pages/Marketplace.vue.js +1 -1
  63. package/dist/martyrs/src/modules/marketplace/views/store/marketplace.js +0 -16
  64. package/dist/martyrs/src/modules/marketplace/views/store/marketplace.js.map +1 -1
  65. package/dist/martyrs/src/modules/notifications/components/elements/NotificationBadge.vue.js +4 -4
  66. package/dist/martyrs/src/modules/notifications/components/elements/NotificationBadge.vue.js.map +1 -1
  67. package/dist/martyrs/src/modules/notifications/components/pages/Notifications.vue.js +1 -1
  68. package/dist/martyrs/src/modules/orders/components/elements/FieldSubscribeNewsletter.vue.js +3 -0
  69. package/dist/martyrs/src/modules/orders/components/elements/FieldSubscribeNewsletter.vue.js.map +1 -1
  70. package/dist/martyrs/src/modules/orders/components/pages/OrderCreateBackoffice.vue.js +1 -1
  71. package/dist/martyrs/src/modules/orders/components/pages/Orders.vue.js +1 -1
  72. package/dist/martyrs/src/modules/orders/components/sections/FormDelivery.vue.js +1 -1
  73. package/dist/martyrs/src/modules/organizations/components/blocks/CardOrganization.vue.js +1 -1
  74. package/dist/martyrs/src/modules/organizations/components/blocks/CardOrganization.vue.js.map +1 -1
  75. package/dist/martyrs/src/modules/organizations/components/pages/Organization.vue.js +1 -1
  76. package/dist/martyrs/src/modules/organizations/components/pages/OrganizationBackoffice.vue.js +1 -1
  77. package/dist/martyrs/src/modules/organizations/components/pages/OrganizationEdit.vue.js +2 -2
  78. package/dist/martyrs/src/modules/organizations/components/pages/OrganizationEdit.vue.js.map +1 -1
  79. package/dist/martyrs/src/modules/organizations/components/pages/Organizations.vue.js +1 -1
  80. package/dist/martyrs/src/modules/organizations/components/sections/Organizations.vue.js +1 -1
  81. package/dist/martyrs/src/modules/products/components/blocks/CardCategory.vue.js +1 -1
  82. package/dist/martyrs/src/modules/products/components/blocks/CardCategory.vue.js.map +1 -1
  83. package/dist/martyrs/src/modules/products/components/blocks/CardProduct.vue.js +15 -2
  84. package/dist/martyrs/src/modules/products/components/blocks/CardProduct.vue.js.map +1 -1
  85. package/dist/martyrs/src/modules/products/components/pages/Categories.vue.js +9 -6
  86. package/dist/martyrs/src/modules/products/components/pages/Categories.vue.js.map +1 -1
  87. package/dist/martyrs/src/modules/products/components/pages/CategoryEdit.vue.js +4 -3
  88. package/dist/martyrs/src/modules/products/components/pages/CategoryEdit.vue.js.map +1 -1
  89. package/dist/martyrs/src/modules/products/components/pages/Product.vue.js +11 -2
  90. package/dist/martyrs/src/modules/products/components/pages/Product.vue.js.map +1 -1
  91. package/dist/martyrs/src/modules/products/components/pages/ProductEdit.vue.js +2 -2
  92. package/dist/martyrs/src/modules/products/components/pages/Products.vue.js +2 -2
  93. package/dist/martyrs/src/modules/products/components/sections/EditVariants.vue.js +1 -1
  94. package/dist/martyrs/src/modules/products/components/sections/SectionProduct.vue.js +11 -8
  95. package/dist/martyrs/src/modules/products/components/sections/SectionProduct.vue.js.map +1 -1
  96. package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttToolbar.vue.js +1 -1
  97. package/dist/martyrs/src/modules/rents/views/components/pages/Rents.vue.js +1 -1
  98. package/dist/martyrs/src/modules/rents/views/components/pages/RentsEdit.vue.js +210 -60
  99. package/dist/martyrs/src/modules/rents/views/components/pages/RentsEdit.vue.js.map +1 -1
  100. package/dist/martyrs/src/modules/spots/components/pages/Map.vue.js +3 -3
  101. package/dist/martyrs/src/modules/spots/components/pages/Map.vue.js.map +1 -1
  102. package/dist/martyrs/src/modules/spots/components/pages/SpotEdit.vue.js +1 -1
  103. package/dist/martyrs.css +1 -1
  104. package/dist/martyrs.es.js +1 -1
  105. package/dist/music.server.js +11 -12
  106. package/dist/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/core/utils.js +1 -1
  107. package/dist/node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/renderer/utils.js +1 -1
  108. package/dist/notifications.server.js +0 -3
  109. package/dist/orders.server.js +5 -6
  110. package/dist/organizations.server.js +9 -10
  111. package/dist/products.server.js +27 -26
  112. package/dist/{queryProcessor-CBQgZycY.js → queryProcessor-C_5Iipam.js} +4 -1
  113. package/dist/rents.server.js +2 -3
  114. package/dist/spots.server.js +1 -1
  115. package/dist/style.css +38 -23
  116. package/dist/{web-cNKIl_cL.js → web-BF3ijvEr.js} +1 -1
  117. package/package.json +1 -1
  118. package/src/builder/modes/ssr.rspack.dev.js +4 -3
  119. package/src/builder/rspack/rspack.config.api.js +15 -4
  120. package/src/builder/rspack/rspack.config.base.js +3 -3
  121. package/src/builder/rspack/rspack.config.ssr.client.js +28 -28
  122. package/src/builder/templates/page.js +2 -2
  123. package/src/components/Button/Button.vue +50 -37
  124. package/src/components/FieldPhone/FieldPhone.vue +1 -1
  125. package/src/components/Loader/Loader.vue +1 -1
  126. package/src/components/Tree/Tree.vue +6 -3
  127. package/src/modules/PROCESS.md +0 -0
  128. package/src/modules/TASKS.MD +17 -0
  129. package/src/modules/auth/auth.client.js +11 -7
  130. package/src/modules/auth/views/components/pages/SignIn.vue +1 -1
  131. package/src/modules/auth/views/router/auth.router.js +94 -0
  132. package/src/modules/auth/views/router/users.router.js +153 -0
  133. package/src/modules/backoffice/components/partials/Sidebar.vue +7 -7
  134. package/src/modules/community/community.server.js +8 -0
  135. package/src/modules/community/policies/blog.policies.js +55 -0
  136. package/src/modules/community/routes/blog.routes.js +1 -1
  137. package/src/modules/community/routes/comments.routes.js +1 -1
  138. package/src/modules/community/routes/reactions.routes.js +1 -4
  139. package/src/modules/core/controllers/classes/abac/abac.adapter.express.js +206 -124
  140. package/src/modules/core/controllers/classes/abac/abac.adapter.ws.js +203 -50
  141. package/src/modules/core/controllers/classes/abac/abac.core.js +127 -36
  142. package/src/modules/core/controllers/classes/abac/abac.fields.js +144 -179
  143. package/src/modules/core/controllers/classes/abac/abac.js +201 -10
  144. package/src/modules/core/controllers/classes/abac/abac.policies.js +147 -57
  145. package/src/modules/core/controllers/classes/crud/crud.policies.js +5 -5
  146. package/src/modules/core/controllers/policies/core.policies.js +5 -2
  147. package/src/modules/core/controllers/utils/queryProcessor.js +4 -1
  148. package/src/modules/core/core.server.js +1 -0
  149. package/src/modules/core/locales/en.js +45 -0
  150. package/src/modules/core/locales/ru.js +45 -0
  151. package/src/modules/core/models/schemas/common.schema.js +1 -1
  152. package/src/modules/core/views/classes/i18n.manager.js +13 -0
  153. package/src/modules/core/views/components/sections/filters/FilterPrice.vue +81 -0
  154. package/src/modules/core/views/mixins/mixins.js +1 -2
  155. package/src/modules/core/views/router/addRoutes.js +6 -1
  156. package/src/modules/events/routes/events.routes.js +1 -1
  157. package/src/modules/inventory/components/pages/InventoryEdit.vue +3 -3
  158. package/src/modules/inventory/policies/inventory.policies.js +1 -1
  159. package/src/modules/inventory/routes/inventory.routes.js +1 -1
  160. package/src/modules/marketplace/marketplace.router.js +66 -0
  161. package/src/modules/marketplace/views/components/layouts/Marketplace.vue +363 -0
  162. package/src/modules/marketplace/views/components/pages/Catalog.vue +73 -0
  163. package/src/modules/marketplace/views/store/marketplace.js +0 -16
  164. package/src/modules/music/controllers/stream.controller.js +1 -1
  165. package/src/modules/music/music.server.js +1 -1
  166. package/src/modules/music/policies/music.policies.js +3 -2
  167. package/src/modules/music/router/library.router.js +26 -0
  168. package/src/modules/music/router/music.router.js +176 -0
  169. package/src/modules/notifications/components/elements/NotificationBadge.vue +5 -6
  170. package/src/modules/notifications/notifications.server.js +1 -3
  171. package/src/modules/orders/components/elements/FieldSubscribeNewsletter.vue +5 -0
  172. package/src/modules/orders/orders.server.js +0 -1
  173. package/src/modules/organizations/components/blocks/CardOrganization.vue +2 -2
  174. package/src/modules/organizations/components/pages/DepartmentEdit.vue +2 -2
  175. package/src/modules/organizations/components/pages/OrganizationEdit.vue +2 -2
  176. package/src/modules/organizations/policies/organizations.policies.js +12 -6
  177. package/src/modules/organizations/routes/organizations.routes.js +1 -3
  178. package/src/modules/products/components/blocks/CardCategory.vue +1 -1
  179. package/src/modules/products/components/blocks/CardProduct.vue +16 -2
  180. package/src/modules/products/components/pages/Categories.vue +9 -6
  181. package/src/modules/products/components/pages/CategoryEdit.vue +8 -4
  182. package/src/modules/products/components/pages/Product.vue +11 -5
  183. package/src/modules/products/components/sections/SectionProduct.vue +11 -7
  184. package/src/modules/products/controllers/categories.controller.js +32 -27
  185. package/src/modules/products/routes/categories.routes.js +1 -1
  186. package/src/modules/rents/controllers/routes/rents.routes.js +1 -1
  187. package/src/modules/rents/views/components/pages/RentsEdit.vue +208 -49
  188. package/src/modules/spots/components/pages/Map.vue +2 -2
  189. package/dist/abac-DYoheWuc.js +0 -1031
  190. package/dist/core.abac-DUPBnlk6.js +0 -298
  191. package/dist/core.logger-C3q8A9dl.js +0 -51
  192. package/dist/martyrs/src/components/EditImages/EditImages.vue.js.map +0 -1
  193. package/dist/martyrs/src/components/Menu/Menu.vue.js.map +0 -1
  194. package/dist/martyrs/src/components/Tab/Tab.vue.js.map +0 -1
  195. package/dist/martyrs/src/modules/auth/auth.router.js +0 -342
  196. package/dist/martyrs/src/modules/auth/auth.router.js.map +0 -1
  197. package/dist/martyrs/src/modules/core/views/components/sections/Filters.vue.js.map +0 -1
  198. package/src/modules/auth/auth.router.js +0 -262
  199. package/src/modules/core/controllers/classes/abac/v2/abac-core-fixed.js +0 -313
  200. package/src/modules/core/controllers/classes/abac/v2/abac-express-fixed.js +0 -276
  201. package/src/modules/core/controllers/classes/abac/v2/abac-fields-fixed.js +0 -425
  202. package/src/modules/core/controllers/classes/abac/v2/abac-main-fixed.js +0 -295
  203. package/src/modules/core/controllers/classes/abac/v2/abac-policies-fixed.js +0 -316
  204. package/src/modules/core/controllers/classes/abac/v2/abac-ws-fixed.js +0 -237
  205. package/src/modules/core/controllers/classes/core.abac.js +0 -310
  206. package/src/modules/core/controllers/classes/core.crud.js +0 -89
  207. package/src/modules/governance/reactcode/eslint.config.js +0 -28
@@ -1,9 +1,14 @@
1
1
  // @martyrs/src/modules/core/controllers/classes/abac/abac.fields.js
2
+ import set from 'lodash/set.js';
3
+ import get from 'lodash/get.js';
4
+ import unset from 'lodash/unset.js';
5
+ import cloneDeep from 'lodash/cloneDeep.js';
2
6
 
3
7
  export default class ABACFields {
4
8
  constructor(abac) {
5
9
  this.abac = abac;
6
10
  this.configs = new Map();
11
+ this.compiledPatterns = new Map(); // Кэш скомпилированных паттернов
7
12
  }
8
13
 
9
14
  /**
@@ -13,10 +18,8 @@ export default class ABACFields {
13
18
  const normalized = {};
14
19
 
15
20
  for (const [pattern, conf] of Object.entries(config)) {
16
- // Извлекаем базовые настройки (все кроме actions)
17
21
  const { actions, ...baseSettings } = conf;
18
22
 
19
- // Нормализуем базовые настройки
20
23
  const base = {
21
24
  actions: baseSettings.actions || '*',
22
25
  access: baseSettings.access || 'allow',
@@ -27,7 +30,6 @@ export default class ABACFields {
27
30
  pattern
28
31
  };
29
32
 
30
- // Если есть переопределения для действий
31
33
  if (actions && typeof actions === 'object') {
32
34
  normalized[pattern] = {
33
35
  base,
@@ -42,6 +44,8 @@ export default class ABACFields {
42
44
  }
43
45
 
44
46
  this.configs.set(resourceName, normalized);
47
+ // Очищаем кэш паттернов при изменении конфига
48
+ this.compiledPatterns.delete(resourceName);
45
49
  return this;
46
50
  }
47
51
 
@@ -49,7 +53,6 @@ export default class ABACFields {
49
53
  * Проверка доступа к полям
50
54
  */
51
55
  async checkFields(context, data, action = null) {
52
- // Используем нормализованный контекст из core
53
56
  const normalizedContext = this.abac.core.normalizeContext(context);
54
57
 
55
58
  if (normalizedContext.skipFieldPolicies) {
@@ -69,38 +72,32 @@ export default class ABACFields {
69
72
  return { allowed: data, denied: [], errors: [], transformed: data };
70
73
  }
71
74
 
72
- // Применяем расширения к контексту (если есть)
73
- if (this.abac.policies && this.abac.policies.priorities.extensions.length > 0) {
74
- for (const [name, extensionFn] of this.abac.policies.priorities.extensions) {
75
- try {
76
- await extensionFn(normalizedContext);
77
- } catch (error) {
78
- console.error(`Extension ${name} error:`, error);
79
- }
80
- }
81
- }
75
+ // Применяем расширения
76
+ await this._applyExtensions(normalizedContext);
82
77
 
78
+ // Используем structuredClone или lodash для безопасного клонирования
83
79
  const result = {
84
- allowed: JSON.parse(JSON.stringify(data)),
80
+ allowed: this._deepClone(data),
85
81
  denied: [],
86
82
  errors: [],
87
83
  transformed: null
88
84
  };
89
85
 
90
86
  // Собираем правила с учетом force
91
- const rules = this._collectRules(data, config, fieldAction);
87
+ const rules = this._collectRulesOptimized(data, config, fieldAction, resource);
92
88
  const forced = rules.filter(r => r.rule.force);
93
89
  const regular = rules.filter(r => !r.rule.force);
90
+
91
+ // Set для отслеживания обработанных путей (БЫЛ БАГ!)
92
+ const processed = new Set();
94
93
 
95
94
  // Применяем правила (сначала forced, потом обычные)
96
95
  for (const { path, value, rule } of [...forced, ...regular]) {
97
- const processed = new Set();
98
-
99
- // Пропускаем уже обработанные пути (для force)
96
+ // Пропускаем уже обработанные пути
100
97
  if (processed.has(path)) continue;
101
98
  processed.add(path);
102
99
 
103
- // Проверка доступа - передаем поле и значение как параметры
100
+ // Проверка доступа
104
101
  const hasAccess = await this._checkFieldAccess(
105
102
  rule.access,
106
103
  normalizedContext,
@@ -134,7 +131,7 @@ export default class ABACFields {
134
131
  }
135
132
 
136
133
  // Трансформация
137
- result.transformed = JSON.parse(JSON.stringify(result.allowed));
134
+ result.transformed = this._deepClone(result.allowed);
138
135
 
139
136
  for (const { path, value, rule } of [...forced, ...regular]) {
140
137
  if (!rule.transform) continue;
@@ -149,102 +146,94 @@ export default class ABACFields {
149
146
  path,
150
147
  result.transformed
151
148
  );
152
- this._setValue(result.transformed, path, transformed);
149
+ set(result.transformed, path, transformed);
153
150
  }
154
151
 
155
152
  return result;
156
153
  }
157
154
 
158
155
  /**
159
- * Проверка доступа к полю
156
+ * Применение расширений
160
157
  * @private
161
158
  */
162
- async _checkFieldAccess(access, context, fieldPath, fieldValue) {
163
- if (access === 'allow') return true;
164
- if (access === 'deny') return false;
165
- if (access === 'optional') return true;
166
-
167
- if (typeof access === 'function') {
159
+ async _applyExtensions(context) {
160
+ if (!this.abac.policies || !this.abac.policies.priorities.extensions.length) {
161
+ return;
162
+ }
163
+
164
+ for (const [name, extensionFn] of this.abac.policies.priorities.extensions) {
168
165
  try {
169
- // Передаем поле и значение как параметры функции
170
- return !!(await access(context, fieldPath, fieldValue));
171
- } catch (e) {
172
- console.error('Field access check error:', e);
173
- return false;
166
+ await extensionFn(context);
167
+ } catch (error) {
168
+ this.abac.logger?.error('Extension error', { name, error });
174
169
  }
175
170
  }
176
-
177
- return true;
178
171
  }
179
172
 
180
173
  /**
181
- * Валидация поля
174
+ * Безопасное клонирование
182
175
  * @private
183
176
  */
184
- async _validateField(validator, value, context, fieldPath) {
185
- if (typeof validator === 'function') {
177
+ _deepClone(obj) {
178
+ // Используем structuredClone если доступен (Node 17+)
179
+ if (typeof structuredClone === 'function') {
186
180
  try {
187
- // Передаем контекст и путь как параметры
188
- const result = await validator(value, context, fieldPath);
189
- if (typeof result === 'boolean') {
190
- return { isValid: result, errors: result ? [] : ['Validation failed'] };
191
- }
192
- return result;
181
+ return structuredClone(obj);
193
182
  } catch (e) {
194
- return { isValid: false, errors: [e.message] };
183
+ // Fallback для циклических ссылок
195
184
  }
196
185
  }
197
-
198
- if (validator && validator.validate) {
199
- return validator.validate(value);
200
- }
201
-
202
- return { isValid: true, errors: [] };
203
- }
204
-
205
- /**
206
- * Трансформация поля
207
- * @private
208
- */
209
- async _transformField(transform, value, context, fieldPath, currentData) {
210
- try {
211
- // Передаем все необходимые данные как параметры
212
- return await transform(value, context, fieldPath, currentData);
213
- } catch (error) {
214
- console.error('Field transform error:', error);
215
- return value;
216
- }
186
+ // Fallback на lodash
187
+ return cloneDeep(obj);
217
188
  }
218
189
 
219
190
  /**
220
- * Сбор применимых правил
191
+ * Оптимизированный сбор правил
221
192
  * @private
222
193
  */
223
- _collectRules(data, config, action) {
194
+ _collectRulesOptimized(data, config, action, resource) {
224
195
  const rules = [];
225
- const patterns = Object.keys(config);
196
+
197
+ // Компилируем паттерны если нужно
198
+ const compiledPatterns = this._getCompiledPatterns(resource, config);
199
+
200
+ // Проверяем есть ли паттерн "*" - если да, применяем его ко всем полям
201
+ const hasWildcard = compiledPatterns.has('*');
226
202
 
227
203
  // Оптимизация: сначала собираем все пути
228
- const dataPaths = this._extractPaths(data);
204
+ const dataPaths = this._extractPathsOptimized(data);
205
+
206
+ // Если есть wildcard, сразу добавляем правило для всех путей
207
+ if (hasWildcard) {
208
+ const wildcardConfig = config['*'];
209
+ const rule = this._getRuleForAction(wildcardConfig, action);
210
+
211
+ if (this._matchesAction(rule.actions, action)) {
212
+ for (const path of dataPaths) {
213
+ rules.push({
214
+ path,
215
+ value: get(data, path),
216
+ rule
217
+ });
218
+ }
219
+ // Если применили wildcard, можем не проверять другие паттерны
220
+ return rules;
221
+ }
222
+ }
229
223
 
224
+ // Проверяем остальные паттерны
230
225
  for (const path of dataPaths) {
231
- for (const pattern of patterns) {
232
- if (this._matchesPattern(path, pattern)) {
226
+ for (const [pattern, matcher] of compiledPatterns) {
227
+ if (pattern === '*') continue; // Уже обработали
228
+
229
+ if (matcher(path)) {
233
230
  const fieldConfig = config[pattern];
231
+ const rule = this._getRuleForAction(fieldConfig, action);
234
232
 
235
- // Получаем правило для конкретного действия
236
- let rule;
237
- if (fieldConfig.actions && fieldConfig.actions[action]) {
238
- rule = fieldConfig.actions[action];
239
- } else {
240
- rule = fieldConfig.base;
241
- }
242
-
243
- // Проверяем, подходит ли правило для действия
244
233
  if (this._matchesAction(rule.actions, action)) {
245
234
  rules.push({
246
235
  path,
247
- value: this._getValue(data, path),
236
+ value: get(data, path),
248
237
  rule
249
238
  });
250
239
  break; // Берем первое совпадение
@@ -257,43 +246,54 @@ export default class ABACFields {
257
246
  }
258
247
 
259
248
  /**
260
- * Проверка паттерна
249
+ * Получение скомпилированных паттернов
261
250
  * @private
262
251
  */
263
- _matchesPattern(path, pattern) {
264
- if (pattern === path) return true;
265
- if (pattern === '*') return true;
266
-
267
- const pathParts = path.split('.');
268
- const patternParts = pattern.split('.');
269
-
270
- for (let i = 0; i < patternParts.length; i++) {
271
- const pp = patternParts[i];
272
- const pathPart = pathParts[i];
252
+ _getCompiledPatterns(resource, config) {
253
+ if (!this.compiledPatterns.has(resource)) {
254
+ const compiled = new Map();
273
255
 
274
- if (pp === '**') return true;
275
- if (pp === '*' && pathPart !== undefined) continue;
276
- if (pp === '[*]' && /^\[\d+\]$/.test(pathPart)) continue;
277
- if (pp !== pathPart) return false;
256
+ for (const pattern of Object.keys(config)) {
257
+ compiled.set(pattern, this._compilePattern(pattern));
258
+ }
259
+
260
+ this.compiledPatterns.set(resource, compiled);
278
261
  }
279
262
 
280
- return pathParts.length === patternParts.length;
263
+ return this.compiledPatterns.get(resource);
281
264
  }
282
265
 
283
266
  /**
284
- * Извлечение путей
267
+ * Компиляция паттерна в функцию проверки
285
268
  * @private
286
269
  */
287
- _extractPaths(obj, prefix = '') {
288
- const paths = [];
270
+ _compilePattern(pattern) {
271
+ if (pattern === '*') return () => true;
272
+
273
+ // Преобразуем паттерн в регулярку
274
+ const escaped = pattern
275
+ .replace(/\./g, '\\.')
276
+ .replace(/\*\*/g, '.*')
277
+ .replace(/\*/g, '[^.]+')
278
+ .replace(/\[\*\]/g, '\\[\\d+\\]');
289
279
 
280
+ const regex = new RegExp(`^${escaped}$`);
281
+
282
+ return (path) => regex.test(path);
283
+ }
284
+
285
+ /**
286
+ * Оптимизированное извлечение путей
287
+ * @private
288
+ */
289
+ _extractPathsOptimized(obj, prefix = '', paths = []) {
290
290
  if (!obj || typeof obj !== 'object') return paths;
291
291
 
292
292
  if (Array.isArray(obj)) {
293
293
  obj.forEach((item, i) => {
294
294
  const path = prefix ? `${prefix}[${i}]` : `[${i}]`;
295
295
  paths.push(path);
296
- paths.push(...this._extractPaths(item, path));
296
+ this._extractPathsOptimized(item, path, paths);
297
297
  });
298
298
  } else {
299
299
  for (const key in obj) {
@@ -302,7 +302,7 @@ export default class ABACFields {
302
302
  paths.push(path);
303
303
 
304
304
  if (obj[key] && typeof obj[key] === 'object') {
305
- paths.push(...this._extractPaths(obj[key], path));
305
+ this._extractPathsOptimized(obj[key], path, paths);
306
306
  }
307
307
  }
308
308
  }
@@ -312,19 +312,33 @@ export default class ABACFields {
312
312
  }
313
313
 
314
314
  /**
315
- * Проверка доступа
315
+ * Получение правила для действия
316
+ * @private
317
+ */
318
+ _getRuleForAction(fieldConfig, action) {
319
+ if (fieldConfig.actions && fieldConfig.actions[action]) {
320
+ return fieldConfig.actions[action];
321
+ }
322
+ return fieldConfig.base;
323
+ }
324
+
325
+ /**
326
+ * Проверка доступа к полю
316
327
  * @private
317
328
  */
318
- async _checkAccess(access, ctx) {
329
+ async _checkFieldAccess(access, context, fieldPath, fieldValue) {
319
330
  if (access === 'allow') return true;
320
331
  if (access === 'deny') return false;
321
332
  if (access === 'optional') return true;
322
333
 
323
334
  if (typeof access === 'function') {
324
335
  try {
325
- return !!(await access(ctx));
336
+ return !!(await access(context, fieldPath, fieldValue));
326
337
  } catch (e) {
327
- console.error('Access check error:', e);
338
+ this.abac.logger?.error('Field access check error', {
339
+ field: fieldPath,
340
+ error: e.message
341
+ });
328
342
  return false;
329
343
  }
330
344
  }
@@ -333,13 +347,13 @@ export default class ABACFields {
333
347
  }
334
348
 
335
349
  /**
336
- * Валидация
350
+ * Валидация поля
337
351
  * @private
338
352
  */
339
- async _validate(validator, value, ctx) {
353
+ async _validateField(validator, value, context, fieldPath) {
340
354
  if (typeof validator === 'function') {
341
355
  try {
342
- const result = await validator(value, ctx);
356
+ const result = await validator(value, context, fieldPath);
343
357
  if (typeof result === 'boolean') {
344
358
  return { isValid: result, errors: result ? [] : ['Validation failed'] };
345
359
  }
@@ -356,15 +370,31 @@ export default class ABACFields {
356
370
  return { isValid: true, errors: [] };
357
371
  }
358
372
 
373
+ /**
374
+ * Трансформация поля
375
+ * @private
376
+ */
377
+ async _transformField(transform, value, context, fieldPath, currentData) {
378
+ try {
379
+ return await transform(value, context, fieldPath, currentData);
380
+ } catch (error) {
381
+ this.abac.logger?.error('Field transform error', {
382
+ field: fieldPath,
383
+ error: error.message
384
+ });
385
+ return value;
386
+ }
387
+ }
388
+
359
389
  /**
360
390
  * Обработка отказа
361
391
  * @private
362
392
  */
363
- async _handleDenied(result, path, rule, ctx) {
393
+ async _handleDenied(result, path, rule) {
364
394
  result.denied.push({ path, reason: rule });
365
395
 
366
396
  if (rule === 'remove') {
367
- this._removePath(result.allowed, path);
397
+ unset(result.allowed, path);
368
398
  } else if (rule === 'error') {
369
399
  throw new Error(`Access denied: ${path}`);
370
400
  }
@@ -379,73 +409,7 @@ export default class ABACFields {
379
409
  return Array.isArray(actions) ? actions.includes(action) : actions === action;
380
410
  }
381
411
 
382
- /**
383
- * Получение значения
384
- * @private
385
- */
386
- _getValue(obj, path) {
387
- const parts = path.split(/\.|\[|\]/).filter(Boolean);
388
- let current = obj;
389
-
390
- for (const part of parts) {
391
- if (!current) return undefined;
392
- current = /^\d+$/.test(part) ? current[parseInt(part)] : current[part];
393
- }
394
-
395
- return current;
396
- }
397
-
398
- /**
399
- * Установка значения
400
- * @private
401
- */
402
- _setValue(obj, path, value) {
403
- const parts = path.split(/\.|\[|\]/).filter(Boolean);
404
- let current = obj;
405
-
406
- for (let i = 0; i < parts.length - 1; i++) {
407
- const part = parts[i];
408
- const next = parts[i + 1];
409
- const isArray = /^\d+$/.test(next);
410
-
411
- if (!current[part]) {
412
- current[part] = isArray ? [] : {};
413
- }
414
- current = current[part];
415
- }
416
-
417
- const last = parts[parts.length - 1];
418
- if (/^\d+$/.test(last)) {
419
- current[parseInt(last)] = value;
420
- } else {
421
- current[last] = value;
422
- }
423
- }
424
-
425
- /**
426
- * Удаление пути
427
- * @private
428
- */
429
- _removePath(obj, path) {
430
- const parts = path.split(/\.|\[|\]/).filter(Boolean);
431
- let current = obj;
432
-
433
- // Доходим до родителя
434
- for (let i = 0; i < parts.length - 1; i++) {
435
- const part = /^\d+$/.test(parts[i]) ? parseInt(parts[i]) : parts[i];
436
- if (!current[part]) return;
437
- current = current[part];
438
- }
439
-
440
- // Удаляем элемент
441
- const last = parts[parts.length - 1];
442
- if (/^\d+$/.test(last)) {
443
- current.splice(parseInt(last), 1);
444
- } else {
445
- delete current[last];
446
- }
447
- }
448
-
412
+ // Публичные методы для управления конфигами
449
413
  getConfig(resourceName) {
450
414
  return this.configs.get(resourceName);
451
415
  }
@@ -455,6 +419,7 @@ export default class ABACFields {
455
419
  }
456
420
 
457
421
  removeConfig(resourceName) {
422
+ this.compiledPatterns.delete(resourceName);
458
423
  return this.configs.delete(resourceName);
459
424
  }
460
425
  }