Эх сурвалжийг харах

Добавлен метод api /reader/storage и класс ReaderStorage

Book Pauk 6 жил өмнө
parent
commit
05128b12a8

+ 21 - 4
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 = '';

+ 92 - 0
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;

+ 1 - 1
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;

+ 2 - 1
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);