site.coffee 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. # app/design/site.coffee
  2. class SiteDesignDocuments
  3. constructor: ->
  4. @designDocs = {
  5. products: @getProductsDesignDoc()
  6. categories: @getCategoriesDesignDoc()
  7. orders: @getOrdersDesignDoc()
  8. validation: @getValidationDesignDoc()
  9. domains: @getDomainsDesignDoc()
  10. }
  11. getProductsDesignDoc: ->
  12. {
  13. _id: '_design/products'
  14. views:
  15. # Все активные товары
  16. all_active:
  17. map: """
  18. function(doc) {
  19. if (doc.type === 'product' && doc.active !== false) {
  20. emit(doc._id, null);
  21. }
  22. }
  23. """
  24. # Товары по категориям
  25. by_category:
  26. map: """
  27. function(doc) {
  28. if (doc.type === 'product' && doc.active !== false && doc.category) {
  29. emit([doc.category, doc.name], {
  30. name: doc.name,
  31. price: doc.price,
  32. brand: doc.brand,
  33. inStock: doc.inStock,
  34. images: doc.images
  35. });
  36. }
  37. }
  38. """
  39. # Товары по брендам
  40. by_brand:
  41. map: """
  42. function(doc) {
  43. if (doc.type === 'product' && doc.active !== false && doc.brand) {
  44. emit([doc.brand, doc.name], null);
  45. }
  46. }
  47. """
  48. # Поиск по SKU
  49. by_sku:
  50. map: """
  51. function(doc) {
  52. if (doc.type === 'product' && doc.sku) {
  53. emit(doc.sku, null);
  54. }
  55. }
  56. """
  57. # Товары по доменам
  58. by_domain:
  59. map: """
  60. function(doc) {
  61. if (doc.type === 'product' && doc.active !== false && doc.domains) {
  62. doc.domains.forEach(function(domain) {
  63. emit(domain, null);
  64. });
  65. }
  66. }
  67. """
  68. language: "javascript"
  69. }
  70. getCategoriesDesignDoc: ->
  71. {
  72. _id: '_design/categories'
  73. views:
  74. # Все активные категории
  75. all_active:
  76. map: """
  77. function(doc) {
  78. if (doc.type === 'category' && doc.active !== false) {
  79. emit(doc._id, null);
  80. }
  81. }
  82. """
  83. # Категории по slug
  84. by_slug:
  85. map: """
  86. function(doc) {
  87. if (doc.type === 'category' && doc.slug) {
  88. emit(doc.slug, null);
  89. }
  90. }
  91. """
  92. # Иерархия категорий
  93. hierarchical:
  94. map: """
  95. function(doc) {
  96. if (doc.type === 'category' && doc.active !== false) {
  97. var path = doc.parent ? [doc.parent, doc._id] : [doc._id];
  98. emit(path, {
  99. name: doc.name,
  100. parent: doc.parent,
  101. order: doc.order
  102. });
  103. }
  104. }
  105. """
  106. # Категории по доменам
  107. by_domain:
  108. map: """
  109. function(doc) {
  110. if (doc.type === 'category' && doc.active !== false && doc.domains) {
  111. doc.domains.forEach(function(domain) {
  112. emit(domain, null);
  113. });
  114. }
  115. }
  116. """
  117. language: "javascript"
  118. }
  119. getOrdersDesignDoc: ->
  120. {
  121. _id: '_design/orders'
  122. views:
  123. # Заказы по пользователю
  124. by_user:
  125. map: """
  126. function(doc) {
  127. if (doc.type === 'order' && doc.userId) {
  128. emit([doc.userId, doc.createdAt], null);
  129. }
  130. }
  131. """
  132. # Заказы по статусу
  133. by_status:
  134. map: """
  135. function(doc) {
  136. if (doc.type === 'order' && doc.status) {
  137. emit([doc.status, doc.createdAt], null);
  138. }
  139. }
  140. """
  141. language: "javascript"
  142. }
  143. getDomainsDesignDoc: ->
  144. {
  145. _id: '_design/domains'
  146. views:
  147. # Настройки по доменам
  148. settings_by_domain:
  149. map: """
  150. function(doc) {
  151. if (doc.type === 'domain_settings' && doc.domain) {
  152. emit(doc.domain, null);
  153. }
  154. }
  155. """
  156. language: "javascript"
  157. }
  158. getValidationDesignDoc: ->
  159. {
  160. _id: '_design/validation'
  161. validate_doc_update: """
  162. function(newDoc, oldDoc, userCtx, secObj) {
  163. // Базовые проверки для всех документов
  164. if (!newDoc.type) {
  165. throw({forbidden: 'Document must have a type'});
  166. }
  167. // Разрешенные типы документов
  168. var validTypes = [
  169. 'product', 'category', 'order', 'user',
  170. 'domain_settings', 'hero_slide', 'blog_article'
  171. ];
  172. if (validTypes.indexOf(newDoc.type) === -1) {
  173. throw({forbidden: 'Invalid document type: ' + newDoc.type});
  174. }
  175. // Проверка товаров
  176. if (newDoc.type === 'product') {
  177. if (!newDoc.name || newDoc.name.trim() === '') {
  178. throw({forbidden: 'Product must have a name'});
  179. }
  180. if (!newDoc.sku || newDoc.sku.trim() === '') {
  181. throw({forbidden: 'Product must have SKU'});
  182. }
  183. if (typeof newDoc.price !== 'number' || newDoc.price < 0) {
  184. throw({forbidden: 'Product must have valid price'});
  185. }
  186. }
  187. // Проверка категорий
  188. if (newDoc.type === 'category') {
  189. if (!newDoc.name || newDoc.name.trim() === '') {
  190. throw({forbidden: 'Category must have a name'});
  191. }
  192. if (!newDoc.slug || newDoc.slug.trim() === '') {
  193. throw({forbidden: 'Category must have a slug'});
  194. }
  195. }
  196. // Проверка заказов
  197. if (newDoc.type === 'order') {
  198. if (!newDoc.userId) {
  199. throw({forbidden: 'Order must have user ID'});
  200. }
  201. if (!Array.isArray(newDoc.items) || newDoc.items.length === 0) {
  202. throw({forbidden: 'Order must have items'});
  203. }
  204. }
  205. // Запрет изменения типа документа
  206. if (oldDoc && oldDoc.type !== newDoc.type) {
  207. throw({forbidden: 'Document type cannot be changed'});
  208. }
  209. }
  210. """
  211. language: "javascript"
  212. }
  213. module.exports = new SiteDesignDocuments()