Работа над проектом
This commit is contained in:
@@ -72,6 +72,8 @@ class WebSocketController {
|
|||||||
await this.getBookList(req, ws); break;
|
await this.getBookList(req, ws); break;
|
||||||
case 'get-genre-tree':
|
case 'get-genre-tree':
|
||||||
await this.getGenreTree(req, ws); break;
|
await this.getGenreTree(req, ws); break;
|
||||||
|
case 'get-book-link':
|
||||||
|
await this.getBookLink(req, ws); break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Action not found: ${req.action}`);
|
throw new Error(`Action not found: ${req.action}`);
|
||||||
@@ -141,6 +143,15 @@ class WebSocketController {
|
|||||||
|
|
||||||
this.send(result, req, ws);
|
this.send(result, req, ws);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getBookLink(req, ws) {
|
||||||
|
if (!utils.hasProp(req, 'bookPath'))
|
||||||
|
throw new Error(`bookPath is empty`);
|
||||||
|
|
||||||
|
const result = await this.webWorker.getBookLink(req.bookPath);
|
||||||
|
|
||||||
|
this.send(result, req, ws);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = WebSocketController;
|
module.exports = WebSocketController;
|
||||||
|
|||||||
@@ -442,6 +442,9 @@ class DbCreator {
|
|||||||
await db.create({table: 'query_cache'});
|
await db.create({table: 'query_cache'});
|
||||||
await db.create({table: 'query_time'});
|
await db.create({table: 'query_time'});
|
||||||
|
|
||||||
|
//кэш-таблица имен файлов и их хешей
|
||||||
|
await db.create({table: 'file_hash'});
|
||||||
|
|
||||||
callback({job: 'done', jobMessage: ''});
|
callback({job: 'done', jobMessage: ''});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
const os = require('os');
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|
||||||
|
const ZipReader = require('./ZipReader');
|
||||||
const WorkerState = require('./WorkerState');
|
const WorkerState = require('./WorkerState');
|
||||||
const { JembaDbThread } = require('jembadb');
|
const { JembaDbThread } = require('jembadb');
|
||||||
const DbCreator = require('./DbCreator');
|
const DbCreator = require('./DbCreator');
|
||||||
@@ -263,6 +265,95 @@ class WebWorker {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async extractBook(bookPath) {
|
||||||
|
const tempDir = this.config.tempDir;
|
||||||
|
const outFile = `${tempDir}/${utils.randomHexString(30)}`;
|
||||||
|
|
||||||
|
const folder = path.dirname(bookPath);
|
||||||
|
const file = path.basename(bookPath);
|
||||||
|
|
||||||
|
const zipReader = new ZipReader();
|
||||||
|
await zipReader.open(folder);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await zipReader.extractToFile(file, outFile);
|
||||||
|
return outFile;
|
||||||
|
} finally {
|
||||||
|
zipReader.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async restoreBook(bookPath) {
|
||||||
|
const db = this.db;
|
||||||
|
const publicDir = this.config.publicDir;
|
||||||
|
|
||||||
|
const extractedFile = await this.extractBook(bookPath);
|
||||||
|
|
||||||
|
const hash = await utils.getFileHash(extractedFile, 'sha256', 'hex');
|
||||||
|
const link = `/files/${hash}`;
|
||||||
|
const publicPath = `${publicDir}${link}`;
|
||||||
|
|
||||||
|
if (!await fs.pathExists(publicPath)) {
|
||||||
|
await fs.move(extractedFile, publicPath);
|
||||||
|
} else {
|
||||||
|
await fs.remove(extractedFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.insert({
|
||||||
|
table: 'file_hash',
|
||||||
|
replace: true,
|
||||||
|
rows: [
|
||||||
|
{id: bookPath, hash},
|
||||||
|
{id: hash, bookPath}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
return link;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getBookLink(bookPath) {
|
||||||
|
this.checkMyState();
|
||||||
|
|
||||||
|
const db = this.db;
|
||||||
|
const publicDir = this.config.publicDir;
|
||||||
|
let link = '';
|
||||||
|
|
||||||
|
//найдем хеш
|
||||||
|
const rows = await db.select({table: 'file_hash', where: `@@id(${db.esc(bookPath)})`});
|
||||||
|
if (rows.length) {//хеш найден по bookPath
|
||||||
|
const hash = rows[0].hash;
|
||||||
|
link = `/files/${hash}`;
|
||||||
|
const publicPath = `${publicDir}${link}`;
|
||||||
|
|
||||||
|
if (!await fs.pathExists(publicPath)) {
|
||||||
|
link = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!link) {
|
||||||
|
link = await this.restoreBook(bookPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!link)
|
||||||
|
throw new Error('404 Файл не найден');
|
||||||
|
|
||||||
|
return {link};
|
||||||
|
}
|
||||||
|
|
||||||
|
async restoreBookFile(publicPath) {
|
||||||
|
const db = this.db;
|
||||||
|
const hash = path.basename(publicPath);
|
||||||
|
|
||||||
|
//найдем bookPath
|
||||||
|
const rows = await db.select({table: 'file_hash', where: `@@id(${db.esc(hash)})`});
|
||||||
|
if (rows.length) {//bookPath найден по хешу
|
||||||
|
const bookPath = rows[0].bookPath;
|
||||||
|
await this.restoreBook(bookPath);
|
||||||
|
} else {//bookPath не найден
|
||||||
|
throw new Error('404 Файл не найден');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async logServerStats() {
|
async logServerStats() {
|
||||||
while (1) {// eslint-disable-line
|
while (1) {// eslint-disable-line
|
||||||
try {
|
try {
|
||||||
@@ -274,7 +365,7 @@ class WebWorker {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(LM_ERR, e.message);
|
log(LM_ERR, e.message);
|
||||||
}
|
}
|
||||||
await utils.sleep(5000);
|
await utils.sleep(5*1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,10 @@ function intersectSet(arrSet) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function randomHexString(len) {
|
||||||
|
return crypto.randomBytes(len).toString('hex')
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
sleep,
|
sleep,
|
||||||
versionText,
|
versionText,
|
||||||
@@ -99,4 +103,5 @@ module.exports = {
|
|||||||
getFileHash,
|
getFileHash,
|
||||||
getBufHash,
|
getBufHash,
|
||||||
intersectSet,
|
intersectSet,
|
||||||
|
randomHexString,
|
||||||
};
|
};
|
||||||
@@ -149,19 +149,42 @@ async function main() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function initStatic(app, config) {// eslint-disable-line
|
function initStatic(app, config) {
|
||||||
//загрузка файлов в /files
|
const WebWorker = require('./core/WebWorker');//singleton
|
||||||
//TODO
|
const webWorker = new WebWorker(config);
|
||||||
|
|
||||||
|
//загрузка или восстановление файлов в /files, при необходимости
|
||||||
|
app.use(async(req, res, next) => {
|
||||||
|
if ((req.method !== 'GET' && req.method !== 'HEAD') ||
|
||||||
|
!(req.path.indexOf('/files/') === 0)
|
||||||
|
) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
const publicPath = `${config.publicDir}${req.path}`;
|
||||||
|
|
||||||
|
//восстановим
|
||||||
|
try {
|
||||||
|
if (!await fs.pathExists(publicPath)) {
|
||||||
|
await webWorker.restoreBookFile(publicPath);
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
log(LM_ERR, `static::restoreBookFile ${req.path} > ${e.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
});
|
||||||
|
|
||||||
|
//заголовки при отдаче
|
||||||
|
const filesDir = `${config.publicDir}/files`;
|
||||||
app.use(express.static(config.publicDir, {
|
app.use(express.static(config.publicDir, {
|
||||||
maxAge: '30d',
|
maxAge: '30d',
|
||||||
|
|
||||||
/*setHeaders: (res, filePath) => {
|
setHeaders: (res, filePath) => {
|
||||||
if (path.dirname(filePath) == filesDir) {
|
if (path.dirname(filePath) == filesDir) {
|
||||||
res.set('Content-Type', 'application/xml');
|
|
||||||
res.set('Content-Encoding', 'gzip');
|
res.set('Content-Encoding', 'gzip');
|
||||||
}
|
}
|
||||||
},*/
|
},
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user