diff --git a/server/controllers/ReaderController.js b/server/controllers/ReaderController.js index 6e58e2fa..25050aaf 100644 --- a/server/controllers/ReaderController.js +++ b/server/controllers/ReaderController.js @@ -1,8 +1,7 @@ const BaseController = require('./BaseController'); -const ReaderWorker = require('../core/ReaderWorker'); -const workerState = require('../core/workerState'); -//const log = require('../core/getLogger').getLog(); -//const _ = require('lodash'); +const ReaderWorker = require('../core/ReaderWorker'); +const readerStorage = require('../core/readerStorage'); +const workerState = require('../core/workerState'); class ReaderController extends BaseController { constructor(config) { @@ -27,6 +26,24 @@ class ReaderController extends BaseController { return false; } + async storage(req, res) { + const request = req.body; + let error = ''; + try { + if (!request.action) + throw new Error(`key 'action' is empty`); + if (!request.items || Array.isArray(request.data)) + throw new Error(`key 'items' is empty`); + + return await readerStorage.doAction(request); + } catch (e) { + error = e.message; + } + //bad request + res.status(400).send({error}); + return false; + } + async uploadFile(req, res) { const file = req.file; let error = ''; diff --git a/server/core/readerStorage.js b/server/core/readerStorage.js new file mode 100644 index 00000000..9180ce9d --- /dev/null +++ b/server/core/readerStorage.js @@ -0,0 +1,92 @@ +const SQL = require('sql-template-strings'); +const _ = require('lodash'); + +const connManager = require('../db/connManager'); + +class ReaderStorage { + constructor() { + this.storagePool = connManager.pool.readerStorage; + } + + async doAction(act) { + let result = {}; + switch (act.action) { + case 'check': + result = await this.checkItems(act.items); + break; + case 'get': + result = await this.getItems(act.items); + break; + case 'set': + result = await this.setItems(act.items, act.force); + break; + default: + throw new Error('Unknown action'); + } + + return result; + } + + async checkItems(items) { + let result = {state: 'success', items: {}}; + + const dbh = await this.storagePool.get(); + try { + for (const id of Object.keys(items)) { + const rows = await dbh.all(SQL`SELECT rev FROM storage WHERE id = ${id}`); + const rev = (rows.length && rows[0].rev ? rows[0].rev : 0); + result.items[id] = {rev}; + } + } finally { + dbh.ret(); + } + + return result; + } + + async getItems(items) { + let result = {state: 'success', items: {}}; + + const dbh = await this.storagePool.get(); + try { + for (const id of Object.keys(items)) { + const rows = await dbh.all(SQL`SELECT rev, data FROM storage WHERE id = ${id}`); + const rev = (rows.length && rows[0].rev ? rows[0].rev : 0); + const data = (rows.length && rows[0].data ? rows[0].data : ''); + result.items[id] = {rev, data}; + } + } finally { + dbh.ret(); + } + + return result; + } + + async setItems(items, force) { + let check = await this.checkItems(items); + + //сначала проверим совпадение ревизий + for (const id of Object.keys(items)) { + if (!_.isString(items[id].data)) + throw new Error('items.data is not a string'); + + if (!force && check.items[id].rev + 1 !== items[id].rev) + return {state: 'reject', items: check.items}; + } + + const dbh = await this.storagePool.get(); + try { + for (const id of Object.keys(items)) { + await dbh.run(SQL`INSERT OR REPLACE INTO storage (id, rev, data) VALUES (${id}, ${items[id].rev}, ${items[id].data})`); + } + } finally { + dbh.ret(); + } + + return {state: 'success'}; + } +} + +const readerStorage = new ReaderStorage(); + +module.exports = readerStorage; \ No newline at end of file diff --git a/server/db/migrations/readerStorage/001-create.js b/server/db/migrations/readerStorage/001-create.js index 41d205cf..0549ec5b 100644 --- a/server/db/migrations/readerStorage/001-create.js +++ b/server/db/migrations/readerStorage/001-create.js @@ -1,6 +1,6 @@ module.exports = ` -- Up -CREATE TABLE storage (id2 INTEGER PRIMARY KEY, name TEXT); +CREATE TABLE storage (id TEXT PRIMARY KEY, rev INTEGER, data TEXT); -- Down DROP TABLE storage; diff --git a/server/routes.js b/server/routes.js index 8fb0fc67..ffdd92c8 100644 --- a/server/routes.js +++ b/server/routes.js @@ -26,6 +26,7 @@ function initRoutes(app, config) { const routes = [ ['POST', '/api/config', misc.getConfig.bind(misc), [aAll], {}], ['POST', '/api/reader/load-book', reader.loadBook.bind(reader), [aAll], {}], + ['POST', '/api/reader/storage', reader.storage.bind(reader), [aAll], {}], ['POST', '/api/reader/upload-file', [upload.single('file'), reader.uploadFile.bind(reader)], [aAll], {}], ['POST', '/api/worker/get-state', worker.getState.bind(worker), [aAll], {}], ]; @@ -60,7 +61,7 @@ function initRoutes(app, config) { }; } callbacks.push(callback); - + switch (httpMethod) { case 'GET' : app.get(path, ...callbacks);