|
@@ -1,5 +1,6 @@
|
|
|
const fs = require('fs-extra');
|
|
|
const path = require('path');
|
|
|
+const JSZip = require('jszip');
|
|
|
|
|
|
const express = require('express');
|
|
|
const utils = require('./core/utils');
|
|
@@ -7,68 +8,95 @@ const webAppDir = require('../build/appdir');
|
|
|
|
|
|
const log = new (require('./core/AppLogger'))().log;//singleton
|
|
|
|
|
|
+function generateZip(zipFile, dataFile, data) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const zip = new JSZip();
|
|
|
+ zip.file(dataFile, data)
|
|
|
+ .generateNodeStream({
|
|
|
+ streamFiles: true,
|
|
|
+ compression: 'DEFLATE',
|
|
|
+ compressionOptions: {level: 6},
|
|
|
+ }).on('error', reject)
|
|
|
+ .pipe(fs.createWriteStream(zipFile)).on('error', reject)
|
|
|
+ .on('finish', (err) => {
|
|
|
+ if (err) reject(err);
|
|
|
+ else resolve();
|
|
|
+ }
|
|
|
+ );
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
module.exports = (app, config) => {
|
|
|
/*
|
|
|
config.bookPathStatic = `${config.rootPathStatic}/book`;
|
|
|
config.bookDir = `${config.publicFilesDir}/book`;
|
|
|
*/
|
|
|
//загрузка или восстановление файлов в /public-files, при необходимости
|
|
|
- app.use(config.bookPathStatic, async(req, res, next) => {
|
|
|
+ app.use([`${config.bookPathStatic}/:fileName/:fileType`, `${config.bookPathStatic}/:fileName`], async(req, res, next) => {
|
|
|
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
|
|
return next();
|
|
|
}
|
|
|
|
|
|
- if (path.extname(req.path) == '') {
|
|
|
- const bookFile = `${config.bookDir}${req.path}`;
|
|
|
- const bookFileDesc = `${bookFile}.d.json`;
|
|
|
+ try {
|
|
|
+ const fileName = req.params.fileName;
|
|
|
+ const fileType = req.params.fileType;
|
|
|
|
|
|
- let downFileName = '';
|
|
|
- //восстановим из json-файла описания
|
|
|
- try {
|
|
|
+ if (path.extname(fileName) === '') {//восстановление файлов {hash}.raw, {hash}.zip
|
|
|
+ let bookFile = `${config.bookDir}/${fileName}`;
|
|
|
+ const bookFileDesc = `${bookFile}.d.json`;
|
|
|
+
|
|
|
+ //восстановим из json-файла описания
|
|
|
if (await fs.pathExists(bookFile) && await fs.pathExists(bookFileDesc)) {
|
|
|
await utils.touchFile(bookFile);
|
|
|
await utils.touchFile(bookFileDesc);
|
|
|
|
|
|
let desc = await fs.readFile(bookFileDesc, 'utf8');
|
|
|
- desc = JSON.parse(desc);
|
|
|
- downFileName = desc.downFileName;
|
|
|
+ let downFileName = (JSON.parse(desc)).downFileName;
|
|
|
+ let gzipped = true;
|
|
|
+
|
|
|
+ if (!req.acceptsEncodings('gzip') || fileType) {
|
|
|
+ const rawFile = `${bookFile}.raw`;
|
|
|
+ //не принимает gzip, тогда распакуем
|
|
|
+ if (!await fs.pathExists(rawFile))
|
|
|
+ await utils.gunzipFile(bookFile, rawFile);
|
|
|
+
|
|
|
+ gzipped = false;
|
|
|
+
|
|
|
+ if (fileType === undefined || fileType === 'raw') {
|
|
|
+ bookFile = rawFile;
|
|
|
+ } else if (fileType === 'zip') {
|
|
|
+ //создаем zip-файл
|
|
|
+ bookFile += '.zip';
|
|
|
+ if (!await fs.pathExists(bookFile)) {
|
|
|
+ const data = await fs.readFile(rawFile);
|
|
|
+ await generateZip(bookFile, downFileName, data);
|
|
|
+ }
|
|
|
+ downFileName += '.zip';
|
|
|
+ } else {
|
|
|
+ throw new Error(`Unsupported file type: ${fileType}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //отдача файла
|
|
|
+ if (gzipped)
|
|
|
+ res.set('Content-Encoding', 'gzip');
|
|
|
+ res.set('Content-Disposition', `inline; filename*=UTF-8''${encodeURIComponent(downFileName)}`);
|
|
|
+ res.sendFile(bookFile);
|
|
|
+ return;
|
|
|
} else {
|
|
|
await fs.remove(bookFile);
|
|
|
await fs.remove(bookFileDesc);
|
|
|
}
|
|
|
- } catch(e) {
|
|
|
- log(LM_ERR, e.message);
|
|
|
- }
|
|
|
-
|
|
|
- if (downFileName) {
|
|
|
- res.downFileName = downFileName;
|
|
|
-
|
|
|
- if (!req.acceptsEncodings('gzip')) {
|
|
|
- //не принимает gzip, тогда распакуем
|
|
|
- const rawFile = `${bookFile}.raw`;
|
|
|
- if (!await fs.pathExists(rawFile))
|
|
|
- await utils.gunzipFile(bookFile, rawFile);
|
|
|
-
|
|
|
- req.url += '.raw';
|
|
|
- res.rawFile = true;
|
|
|
- }
|
|
|
}
|
|
|
+ } catch(e) {
|
|
|
+ log(LM_ERR, e.message);
|
|
|
}
|
|
|
|
|
|
return next();
|
|
|
});
|
|
|
|
|
|
- //заголовки при отдаче
|
|
|
- app.use(config.bookPathStatic, express.static(config.bookDir, {
|
|
|
- setHeaders: (res) => {
|
|
|
- if (res.downFileName) {
|
|
|
- if (!res.rawFile)
|
|
|
- res.set('Content-Encoding', 'gzip');
|
|
|
-
|
|
|
- res.set('Content-Disposition', `inline; filename*=UTF-8''${encodeURIComponent(res.downFileName)}`);
|
|
|
- }
|
|
|
- },
|
|
|
- }));
|
|
|
+ //иначе просто отдаем запрошенный файл из /public-files
|
|
|
+ app.use(config.bookPathStatic, express.static(config.bookDir));
|
|
|
|
|
|
if (config.rootPathStatic) {
|
|
|
//подмена rootPath в файлах статики WebApp при необходимости
|
|
@@ -77,18 +105,22 @@ module.exports = (app, config) => {
|
|
|
return next();
|
|
|
}
|
|
|
|
|
|
- const reqPath = (req.path == '/' ? '/index.html' : req.path);
|
|
|
- const ext = path.extname(reqPath);
|
|
|
- if (ext == '.html' || ext == '.js' || ext == '.css') {
|
|
|
- const reqFile = `${config.publicDir}${reqPath}`;
|
|
|
- const flagFile = `${reqFile}.replaced`;
|
|
|
-
|
|
|
- if (!await fs.pathExists(flagFile) && await fs.pathExists(reqFile)) {
|
|
|
- const content = await fs.readFile(reqFile, 'utf8');
|
|
|
- const re = new RegExp(`/${webAppDir}`, 'g');
|
|
|
- await fs.writeFile(reqFile, content.replace(re, `${config.rootPathStatic}/${webAppDir}`));
|
|
|
- await fs.writeFile(flagFile, '');
|
|
|
+ try {
|
|
|
+ const reqPath = (req.path == '/' ? '/index.html' : req.path);
|
|
|
+ const ext = path.extname(reqPath);
|
|
|
+ if (ext == '.html' || ext == '.js' || ext == '.css') {
|
|
|
+ const reqFile = `${config.publicDir}${reqPath}`;
|
|
|
+ const flagFile = `${reqFile}.replaced`;
|
|
|
+
|
|
|
+ if (!await fs.pathExists(flagFile) && await fs.pathExists(reqFile)) {
|
|
|
+ const content = await fs.readFile(reqFile, 'utf8');
|
|
|
+ const re = new RegExp(`/${webAppDir}`, 'g');
|
|
|
+ await fs.writeFile(reqFile, content.replace(re, `${config.rootPathStatic}/${webAppDir}`));
|
|
|
+ await fs.writeFile(flagFile, '');
|
|
|
+ }
|
|
|
}
|
|
|
+ } catch(e) {
|
|
|
+ log(LM_ERR, e.message);
|
|
|
}
|
|
|
|
|
|
return next();
|