|
@@ -0,0 +1,233 @@
|
|
|
|
|
+class PouchDBService
|
|
|
|
|
+ constructor: (options = {}) ->
|
|
|
|
|
+ {
|
|
|
|
|
+ @localDbName = 'braer_color_cache'
|
|
|
|
|
+ @remoteDbUrl = 'http://localhost:5984/braer_color_shop'
|
|
|
|
|
+ @userFilter = { userId: null }
|
|
|
|
|
+ @appVersion = '1.0.0'
|
|
|
|
|
+ } = options
|
|
|
|
|
+
|
|
|
|
|
+ @localDb = null
|
|
|
|
|
+ @remoteDb = null
|
|
|
|
|
+ @initialized = false
|
|
|
|
|
+ @syncHandler = null
|
|
|
|
|
+
|
|
|
|
|
+ # Основная инициализация
|
|
|
|
|
+ init: ->
|
|
|
|
|
+ return Promise.resolve() if @initialized
|
|
|
|
|
+
|
|
|
|
|
+ try
|
|
|
|
|
+ debug.log '🚀 Инициализация PouchDB сервиса...'
|
|
|
|
|
+
|
|
|
|
|
+ # Создание локальной базы
|
|
|
|
|
+ @localDb = new PouchDB(@localDbName)
|
|
|
|
|
+ debug.log '📁 Локальная база создана:', @localDbName
|
|
|
|
|
+
|
|
|
|
|
+ # Создание/подключение удаленной базы
|
|
|
|
|
+ await @ensureRemoteDatabase()
|
|
|
|
|
+
|
|
|
|
|
+ # Загрузка design документов
|
|
|
|
|
+ await @ensureDesignDocs()
|
|
|
|
|
+
|
|
|
|
|
+ # Настройка синхронизации
|
|
|
|
|
+ await @setupSync()
|
|
|
|
|
+
|
|
|
|
|
+ @initialized = true
|
|
|
|
|
+ debug.log '✅ PouchDB сервис инициализирован'
|
|
|
|
|
+ return Promise.resolve()
|
|
|
|
|
+
|
|
|
|
|
+ catch error
|
|
|
|
|
+ console.error '❌ Ошибка инициализации PouchDB:', error
|
|
|
|
|
+ return Promise.reject(error)
|
|
|
|
|
+
|
|
|
|
|
+ # Создание удаленной базы если не существует
|
|
|
|
|
+ ensureRemoteDatabase: ->
|
|
|
|
|
+ try
|
|
|
|
|
+ @remoteDb = new PouchDB(@remoteDbUrl)
|
|
|
|
|
+
|
|
|
|
|
+ # Проверка существования базы
|
|
|
|
|
+ info = await @remoteDb.info()
|
|
|
|
|
+ debug.log '🌐 Удаленная база подключена:', info.db_name
|
|
|
|
|
+
|
|
|
|
|
+ catch error
|
|
|
|
|
+ if error.status == 404
|
|
|
|
|
+ debug.log '📦 Создание новой удаленной базы...'
|
|
|
|
|
+ # В браузере создание БД происходит автоматически при первом обращении
|
|
|
|
|
+ @remoteDb = new PouchDB(@remoteDbUrl)
|
|
|
|
|
+ info = await @remoteDb.info()
|
|
|
|
|
+ debug.log '✅ Удаленная база создана:', info.db_name
|
|
|
|
|
+ else
|
|
|
|
|
+ throw error
|
|
|
|
|
+
|
|
|
|
|
+ # Создание design документов
|
|
|
|
|
+ ensureDesignDocs: ->
|
|
|
|
|
+ try
|
|
|
|
|
+ # Загрузка design документов
|
|
|
|
|
+ adminDesign = require 'app/design/admin.coffee'
|
|
|
|
|
+ siteDesign = require 'app/design/site.coffee'
|
|
|
|
|
+
|
|
|
|
|
+ # Сохранение design документов
|
|
|
|
|
+ await @saveDesignDoc(adminDesign)
|
|
|
|
|
+ await @saveDesignDoc(siteDesign)
|
|
|
|
|
+
|
|
|
|
|
+ debug.log '📝 Design документы загружены'
|
|
|
|
|
+
|
|
|
|
|
+ catch error
|
|
|
|
|
+ console.error 'Ошибка загрузки design документов:', error
|
|
|
|
|
+ throw error
|
|
|
|
|
+
|
|
|
|
|
+ # Сохранение design документа с проверкой версий
|
|
|
|
|
+ saveDesignDoc: (designDoc) ->
|
|
|
|
|
+ try
|
|
|
|
|
+ existingDoc = await @remoteDb.get(designDoc._id)
|
|
|
|
|
+
|
|
|
|
|
+ # Проверка необходимости обновления
|
|
|
|
|
+ if existingDoc.hash != designDoc.hash || existingDoc.version != designDoc.version
|
|
|
|
|
+ designDoc._rev = existingDoc._rev
|
|
|
|
|
+ await @remoteDb.put(designDoc)
|
|
|
|
|
+ debug.log "🔄 Design документ обновлен: #{designDoc._id}"
|
|
|
|
|
+ else
|
|
|
|
|
+ debug.log "✅ Design документ актуален: #{designDoc._id}"
|
|
|
|
|
+
|
|
|
|
|
+ catch error
|
|
|
|
|
+ if error.status == 404
|
|
|
|
|
+ # Документ не существует, создаем новый
|
|
|
|
|
+ await @remoteDb.put(designDoc)
|
|
|
|
|
+ debug.log "✅ Design документ создан: #{designDoc._id}"
|
|
|
|
|
+ else
|
|
|
|
|
+ throw error
|
|
|
|
|
+
|
|
|
|
|
+ # Настройка синхронизации
|
|
|
|
|
+ setupSync: ->
|
|
|
|
|
+ @syncHandler = PouchDB.sync(@remoteDb, @localDb, {
|
|
|
|
|
+ live: true,
|
|
|
|
|
+ retry: true,
|
|
|
|
|
+ filter: (doc) => @shouldSyncDocument(doc)
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ @syncHandler
|
|
|
|
|
+ .on('change', (info) =>
|
|
|
|
|
+ debug.log '🔄 Синхронизация:', info)
|
|
|
|
|
+ .on('paused', (err) =>
|
|
|
|
|
+ debug.log '⏸️ Синхронизация приостановлена')
|
|
|
|
|
+ .on('active', =>
|
|
|
|
|
+ debug.log '🔄 Синхронизация активна')
|
|
|
|
|
+ .on('error', (err) =>
|
|
|
|
|
+ console.error '❌ Ошибка синхронизации:', err)
|
|
|
|
|
+
|
|
|
|
|
+ debug.log '🔁 Синхронизация настроена'
|
|
|
|
|
+
|
|
|
|
|
+ # Проверка необходимости синхронизации документа
|
|
|
|
|
+ shouldSyncDocument: (doc) ->
|
|
|
|
|
+ return false if doc._id?.startsWith('_design/')
|
|
|
|
|
+
|
|
|
|
|
+ # Всегда синхронизируем общие данные
|
|
|
|
|
+ if doc.type in ['product', 'category', 'settings', 'hero_slide', 'blog_article', 'route', 'domain_settings']
|
|
|
|
|
+ return true
|
|
|
|
|
+
|
|
|
|
|
+ # Для пользовательских данных проверяем принадлежность
|
|
|
|
|
+ if doc.type in ['order', 'user_data', 'cart']
|
|
|
|
|
+ return doc.userId == @userFilter?.userId
|
|
|
|
|
+
|
|
|
|
|
+ # Для мультидоменности проверяем принадлежность к домену
|
|
|
|
|
+ if doc.domains
|
|
|
|
|
+ return @userFilter?.currentDomain in doc.domains
|
|
|
|
|
+
|
|
|
|
|
+ return false
|
|
|
|
|
+
|
|
|
|
|
+ # Умное получение документа
|
|
|
|
|
+ getDocument: (docId) ->
|
|
|
|
|
+ @ensureInit()
|
|
|
|
|
+
|
|
|
|
|
+ try
|
|
|
|
|
+ # Сначала пробуем локально
|
|
|
|
|
+ doc = await @localDb.get(docId)
|
|
|
|
|
+ debug.log '📄 Документ получен из локального кэша:', docId
|
|
|
|
|
+ return doc
|
|
|
|
|
+
|
|
|
|
|
+ catch localError
|
|
|
|
|
+ if localError.status == 404
|
|
|
|
|
+ try
|
|
|
|
|
+ # Затем пробуем удаленно
|
|
|
|
|
+ doc = await @remoteDb.get(docId)
|
|
|
|
|
+ # Сохраняем в кэш для будущих запросов
|
|
|
|
|
+ await @localDb.put(doc)
|
|
|
|
|
+ debug.log '📄 Документ получен из удаленной БД и закэширован:', docId
|
|
|
|
|
+ return doc
|
|
|
|
|
+ catch remoteError
|
|
|
|
|
+ console.error '❌ Документ не найден:', docId
|
|
|
|
|
+ throw remoteError
|
|
|
|
|
+ else
|
|
|
|
|
+ throw localError
|
|
|
|
|
+
|
|
|
|
|
+ # Сохранение документа (только в удаленную БД)
|
|
|
|
|
+ saveToRemote: (doc) ->
|
|
|
|
|
+ @ensureInit()
|
|
|
|
|
+
|
|
|
|
|
+ try
|
|
|
|
|
+ # Проверяем существование документа
|
|
|
|
|
+ existingDoc = await @remoteDb.get(doc._id)
|
|
|
|
|
+ doc._rev = existingDoc._rev
|
|
|
|
|
+ result = await @remoteDb.put(doc)
|
|
|
|
|
+ debug.log '💾 Документ обновлен в удаленной БД:', doc._id
|
|
|
|
|
+ return result
|
|
|
|
|
+
|
|
|
|
|
+ catch error
|
|
|
|
|
+ if error.status == 404
|
|
|
|
|
+ # Документ не существует, создаем новый
|
|
|
|
|
+ result = await @remoteDb.put(doc)
|
|
|
|
|
+ debug.log '💾 Документ создан в удаленной БД:', doc._id
|
|
|
|
|
+ return result
|
|
|
|
|
+ else
|
|
|
|
|
+ console.error '❌ Ошибка сохранения документа:', error
|
|
|
|
|
+ throw error
|
|
|
|
|
+
|
|
|
|
|
+ # Пакетное сохранение
|
|
|
|
|
+ bulkDocs: (docs) ->
|
|
|
|
|
+ @ensureInit()
|
|
|
|
|
+ await @remoteDb.bulkDocs(docs)
|
|
|
|
|
+
|
|
|
|
|
+ # Выполнение view запроса
|
|
|
|
|
+ queryView: (designDoc, viewName, options = {}) ->
|
|
|
|
|
+ @ensureInit()
|
|
|
|
|
+ await @remoteDb.query("#{designDoc}/#{viewName}", options)
|
|
|
|
|
+
|
|
|
|
|
+ # Получение вложений
|
|
|
|
|
+ getAttachment: (docId, attachmentName) ->
|
|
|
|
|
+ @ensureInit()
|
|
|
|
|
+ await @remoteDb.getAttachment(docId, attachmentName)
|
|
|
|
|
+
|
|
|
|
|
+ # Сохранение вложения
|
|
|
|
|
+ putAttachment: (docId, attachmentName, attachment, type) ->
|
|
|
|
|
+ @ensureInit()
|
|
|
|
|
+
|
|
|
|
|
+ try
|
|
|
|
|
+ doc = await @remoteDb.get(docId)
|
|
|
|
|
+ await @remoteDb.putAttachment(docId, attachmentName, doc._rev, attachment, type)
|
|
|
|
|
+ debug.log '📎 Вложение сохранено:', "#{docId}/#{attachmentName}"
|
|
|
|
|
+ catch error
|
|
|
|
|
+ if error.status == 404
|
|
|
|
|
+ # Документ не существует, создаем сначала базовый документ
|
|
|
|
|
+ await @remoteDb.put({ _id: docId, type: 'with_attachments' })
|
|
|
|
|
+ await @remoteDb.putAttachment(docId, attachmentName, attachment, type)
|
|
|
|
|
+ else
|
|
|
|
|
+ throw error
|
|
|
|
|
+
|
|
|
|
|
+ # Вспомогательные методы
|
|
|
|
|
+ ensureInit: ->
|
|
|
|
|
+ if !@initialized
|
|
|
|
|
+ throw new Error 'PouchDBService не инициализирован. Вызовите init() сначала.'
|
|
|
|
|
+
|
|
|
|
|
+ destroy: ->
|
|
|
|
|
+ if @syncHandler
|
|
|
|
|
+ @syncHandler.cancel()
|
|
|
|
|
+ @initialized = false
|
|
|
|
|
+ debug.log '🧹 PouchDBService уничтожен'
|
|
|
|
|
+
|
|
|
|
|
+# Создание и экспорт синглтона
|
|
|
|
|
+module.exports = new PouchDBService({
|
|
|
|
|
+ localDbName: 'braer_color_cache'
|
|
|
|
|
+ remoteDbUrl: 'https://oleg:631074@couchdb.favt.ru.net/braer_color_shop'
|
|
|
|
|
+ userFilter: { userId: 'current_user_id' }
|
|
|
|
|
+ appVersion: '1.0.0'
|
|
|
|
|
+})
|