pouch.coffee 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. class PouchDBService
  2. constructor: (options = {}) ->
  3. {
  4. @localDbName = 'braer_color_cache'
  5. @remoteDbUrl = 'http://localhost:5984/braer_color_shop'
  6. @userFilter = { userId: null }
  7. @appVersion = '1.0.0'
  8. } = options
  9. @localDb = null
  10. @remoteDb = null
  11. @initialized = false
  12. @syncHandler = null
  13. # Основная инициализация
  14. init: ->
  15. return Promise.resolve() if @initialized
  16. try
  17. debug.log '🚀 Инициализация PouchDB сервиса...'
  18. # Создание локальной базы
  19. @localDb = new PouchDB(@localDbName)
  20. debug.log '📁 Локальная база создана:', @localDbName
  21. # Создание/подключение удаленной базы
  22. await @ensureRemoteDatabase()
  23. # Загрузка design документов
  24. await @ensureDesignDocs()
  25. # Настройка синхронизации
  26. await @setupSync()
  27. @initialized = true
  28. debug.log '✅ PouchDB сервис инициализирован'
  29. return Promise.resolve()
  30. catch error
  31. console.error '❌ Ошибка инициализации PouchDB:', error
  32. return Promise.reject(error)
  33. # Создание удаленной базы если не существует
  34. ensureRemoteDatabase: ->
  35. try
  36. @remoteDb = new PouchDB(@remoteDbUrl)
  37. # Проверка существования базы
  38. info = await @remoteDb.info()
  39. debug.log '🌐 Удаленная база подключена:', info.db_name
  40. catch error
  41. if error.status == 404
  42. debug.log '📦 Создание новой удаленной базы...'
  43. # В браузере создание БД происходит автоматически при первом обращении
  44. @remoteDb = new PouchDB(@remoteDbUrl)
  45. info = await @remoteDb.info()
  46. debug.log '✅ Удаленная база создана:', info.db_name
  47. else
  48. throw error
  49. # Создание design документов
  50. ensureDesignDocs: ->
  51. try
  52. # Загрузка design документов
  53. adminDesign = require 'app/design/admin.coffee'
  54. siteDesign = require 'app/design/site.coffee'
  55. # Сохранение design документов
  56. await @saveDesignDoc(adminDesign)
  57. await @saveDesignDoc(siteDesign)
  58. debug.log '📝 Design документы загружены'
  59. catch error
  60. console.error 'Ошибка загрузки design документов:', error
  61. throw error
  62. # Сохранение design документа с проверкой версий
  63. saveDesignDoc: (designDoc) ->
  64. try
  65. existingDoc = await @remoteDb.get(designDoc._id)
  66. # Проверка необходимости обновления
  67. if existingDoc.hash != designDoc.hash || existingDoc.version != designDoc.version
  68. designDoc._rev = existingDoc._rev
  69. await @remoteDb.put(designDoc)
  70. debug.log "🔄 Design документ обновлен: #{designDoc._id}"
  71. else
  72. debug.log "✅ Design документ актуален: #{designDoc._id}"
  73. catch error
  74. if error.status == 404
  75. # Документ не существует, создаем новый
  76. await @remoteDb.put(designDoc)
  77. debug.log "✅ Design документ создан: #{designDoc._id}"
  78. else
  79. throw error
  80. # Настройка синхронизации
  81. setupSync: ->
  82. @syncHandler = PouchDB.sync(@remoteDb, @localDb, {
  83. live: true,
  84. retry: true,
  85. filter: (doc) => @shouldSyncDocument(doc)
  86. })
  87. @syncHandler
  88. .on('change', (info) =>
  89. debug.log '🔄 Синхронизация:', info)
  90. .on('paused', (err) =>
  91. debug.log '⏸️ Синхронизация приостановлена')
  92. .on('active', =>
  93. debug.log '🔄 Синхронизация активна')
  94. .on('error', (err) =>
  95. console.error '❌ Ошибка синхронизации:', err)
  96. debug.log '🔁 Синхронизация настроена'
  97. # Проверка необходимости синхронизации документа
  98. shouldSyncDocument: (doc) ->
  99. return false if doc._id?.startsWith('_design/')
  100. # Всегда синхронизируем общие данные
  101. if doc.type in ['product', 'category', 'settings', 'hero_slide', 'blog_article', 'route', 'domain_settings']
  102. return true
  103. # Для пользовательских данных проверяем принадлежность
  104. if doc.type in ['order', 'user_data', 'cart']
  105. return doc.userId == @userFilter?.userId
  106. # Для мультидоменности проверяем принадлежность к домену
  107. if doc.domains
  108. return @userFilter?.currentDomain in doc.domains
  109. return false
  110. # Умное получение документа
  111. getDocument: (docId) ->
  112. @ensureInit()
  113. try
  114. # Сначала пробуем локально
  115. doc = await @localDb.get(docId)
  116. debug.log '📄 Документ получен из локального кэша:', docId
  117. return doc
  118. catch localError
  119. if localError.status == 404
  120. try
  121. # Затем пробуем удаленно
  122. doc = await @remoteDb.get(docId)
  123. # Сохраняем в кэш для будущих запросов
  124. await @localDb.put(doc)
  125. debug.log '📄 Документ получен из удаленной БД и закэширован:', docId
  126. return doc
  127. catch remoteError
  128. console.error '❌ Документ не найден:', docId
  129. throw remoteError
  130. else
  131. throw localError
  132. # Сохранение документа (только в удаленную БД)
  133. saveToRemote: (doc) ->
  134. @ensureInit()
  135. try
  136. # Проверяем существование документа
  137. existingDoc = await @remoteDb.get(doc._id)
  138. doc._rev = existingDoc._rev
  139. result = await @remoteDb.put(doc)
  140. debug.log '💾 Документ обновлен в удаленной БД:', doc._id
  141. return result
  142. catch error
  143. if error.status == 404
  144. # Документ не существует, создаем новый
  145. result = await @remoteDb.put(doc)
  146. debug.log '💾 Документ создан в удаленной БД:', doc._id
  147. return result
  148. else
  149. console.error '❌ Ошибка сохранения документа:', error
  150. throw error
  151. # Пакетное сохранение
  152. bulkDocs: (docs) ->
  153. @ensureInit()
  154. await @remoteDb.bulkDocs(docs)
  155. # Выполнение view запроса
  156. queryView: (designDoc, viewName, options = {}) ->
  157. @ensureInit()
  158. await @remoteDb.query("#{designDoc}/#{viewName}", options)
  159. # Получение вложений
  160. getAttachment: (docId, attachmentName) ->
  161. @ensureInit()
  162. await @remoteDb.getAttachment(docId, attachmentName)
  163. # Сохранение вложения
  164. putAttachment: (docId, attachmentName, attachment, type) ->
  165. @ensureInit()
  166. try
  167. doc = await @remoteDb.get(docId)
  168. await @remoteDb.putAttachment(docId, attachmentName, doc._rev, attachment, type)
  169. debug.log '📎 Вложение сохранено:', "#{docId}/#{attachmentName}"
  170. catch error
  171. if error.status == 404
  172. # Документ не существует, создаем сначала базовый документ
  173. await @remoteDb.put({ _id: docId, type: 'with_attachments' })
  174. await @remoteDb.putAttachment(docId, attachmentName, attachment, type)
  175. else
  176. throw error
  177. # Вспомогательные методы
  178. ensureInit: ->
  179. if !@initialized
  180. throw new Error 'PouchDBService не инициализирован. Вызовите init() сначала.'
  181. destroy: ->
  182. if @syncHandler
  183. @syncHandler.cancel()
  184. @initialized = false
  185. debug.log '🧹 PouchDBService уничтожен'
  186. # Создание и экспорт синглтона
  187. module.exports = new PouchDBService({
  188. localDbName: 'braer_color_cache'
  189. remoteDbUrl: 'https://oleg:631074@couchdb.favt.ru.net/braer_color_shop'
  190. userFilter: { userId: 'current_user_id' }
  191. appVersion: '1.0.0'
  192. })