Выделение файлов приложения в рабочую директорию

This commit is contained in:
Book Pauk
2022-12-11 18:30:33 +07:00
parent 55ee303fc5
commit 5d5ad40f4e
8 changed files with 160 additions and 119 deletions

8
.gitignore vendored
View File

@@ -1,5 +1,3 @@
/node_modules /node_modules
/server/data /server/.liberama*
/server/public /dist
/server/ipfs
/dist

View File

@@ -1,5 +1,5 @@
{ {
"name": "Liberama", "name": "liberama",
"version": "0.12.2", "version": "0.12.2",
"author": "Book Pauk <bookpauk@gmail.com>", "author": "Book Pauk <bookpauk@gmail.com>",
"license": "CC0-1.0", "license": "CC0-1.0",
@@ -8,7 +8,7 @@
"node": ">=16.16.0" "node": ">=16.16.0"
}, },
"scripts": { "scripts": {
"dev": "nodemon --inspect --ignore server/public --ignore server/data --ignore client --exec 'node server'", "dev": "nodemon --inspect --ignore server/.liberama --ignore client --exec 'node server'",
"build:client": "webpack --config build/webpack.prod.config.js", "build:client": "webpack --config build/webpack.prod.config.js",
"build:linux": "npm run build:client && node build/linux && pkg -t node16-linux-x64 -C GZip -o dist/linux/liberama .", "build:linux": "npm run build:client && node build/linux && pkg -t node16-linux-x64 -C GZip -o dist/linux/liberama .",
"build:win": "npm run build:client && node build/win && pkg -t node16-win-x64 -C GZip -o dist/win/liberama .", "build:win": "npm run build:client && node build/win && pkg -t node16-win-x64 -C GZip -o dist/win/liberama .",

View File

@@ -2,18 +2,14 @@ const path = require('path');
const pckg = require('../../package.json'); const pckg = require('../../package.json');
const execDir = path.resolve(__dirname, '..'); const execDir = path.resolve(__dirname, '..');
const dataDir = `${execDir}/data`;
module.exports = { module.exports = {
branch: 'unknown', branch: 'unknown',
version: pckg.version, version: pckg.version,
name: pckg.name, name: pckg.name,
dataDir: dataDir, execDir,
tempDir: `${dataDir}/tmp`,
logDir: `${dataDir}/log`,
publicDir: `${execDir}/public`,
uploadDir: `${execDir}/public/upload`,
loggingEnabled: true, loggingEnabled: true,
maxUploadFileSize: 50*1024*1024,//50Мб maxUploadFileSize: 50*1024*1024,//50Мб
@@ -48,13 +44,13 @@ module.exports = {
servers: [ servers: [
{ {
serverName: '1', serverName: '1',
mode: 'normal', //'none', 'normal', 'site', 'reader', 'omnireader', 'liberama.top', 'book_update_checker' mode: 'reader', //'reader', 'omnireader', 'liberama.top', 'book_update_checker'
ip: '0.0.0.0', ip: '0.0.0.0',
port: '33080', port: '33080',
}, },
/*{ /*{
serverName: '2', serverName: '2',
mode: 'book_update_checker', //'none', 'normal', 'site', 'reader', 'omnireader', 'liberama.top', 'book_update_checker' mode: 'book_update_checker',
isHttps: true, isHttps: true,
keysFile: 'server', keysFile: 'server',
ip: '0.0.0.0', ip: '0.0.0.0',

View File

@@ -1,4 +1,5 @@
const _ = require('lodash'); const _ = require('lodash');
const path = require('path');
const fs = require('fs-extra'); const fs = require('fs-extra');
const branchFilename = __dirname + '/application_env'; const branchFilename = __dirname + '/application_env';
@@ -29,7 +30,7 @@ class ConfigManager {
return instance; return instance;
} }
async init() { async init(dataDir) {
if (this.inited) if (this.inited)
throw new Error('already inited'); throw new Error('already inited');
@@ -44,10 +45,17 @@ class ConfigManager {
process.env.NODE_ENV = this.branch; process.env.NODE_ENV = this.branch;
this.branchConfigFile = __dirname + `/${this.branch}.js`; this.branchConfigFile = __dirname + `/${this.branch}.js`;
this._config = require(this.branchConfigFile); const config = require(this.branchConfigFile);
await fs.ensureDir(this._config.dataDir); if (dataDir) {
this._userConfigFile = `${this._config.dataDir}/config.json`; config.dataDir = path.resolve(dataDir);
} else {
config.dataDir = `${config.execDir}/.${config.name}`;
}
await fs.ensureDir(config.dataDir);
this._userConfigFile = `${config.dataDir}/config.json`;
this._config = config;
this.inited = true; this.inited = true;
} }
@@ -72,15 +80,28 @@ class ConfigManager {
} }
async load() { async load() {
if (!this.inited) try {
throw new Error('not inited'); if (!this.inited)
if (!await fs.pathExists(this.userConfigFile)) { throw new Error('not inited');
await this.save();
return;
}
const data = await fs.readFile(this.userConfigFile, 'utf8'); if (await fs.pathExists(this.userConfigFile)) {
this.config = JSON.parse(data); const data = JSON.parse(await fs.readFile(this.userConfigFile, 'utf8'));
const config = _.pick(data, propsToSave);
this.config = config;
//сохраним конфиг, если не все атрибуты присутствуют в файле конфига
for (const prop of propsToSave)
if (!Object.prototype.hasOwnProperty.call(config, prop)) {
await this.save();
break;
}
} else {
await this.save();
}
} catch(e) {
throw new Error(`Error while loading "${this.userConfigFile}": ${e.message}`);
}
} }
async save() { async save() {

View File

@@ -2,15 +2,11 @@ const path = require('path');
const base = require('./base'); const base = require('./base');
const execDir = path.dirname(process.execPath); const execDir = path.dirname(process.execPath);
const dataDir = `${execDir}/data`;
module.exports = Object.assign({}, base, { module.exports = Object.assign({}, base, {
branch: 'production', branch: 'production',
dataDir: dataDir,
tempDir: `${dataDir}/tmp`, execDir,
logDir: `${dataDir}/log`,
publicDir: `${execDir}/public`,
uploadDir: `${execDir}/public/upload`,
servers: [ servers: [
{ {

View File

@@ -29,9 +29,6 @@ class ReaderWorker {
this.config.tempDownloadDir = `${config.tempDir}/download`; this.config.tempDownloadDir = `${config.tempDir}/download`;
fs.ensureDirSync(this.config.tempDownloadDir); fs.ensureDirSync(this.config.tempDownloadDir);
this.config.tempPublicDir = `${config.publicDir}/tmp`;
fs.ensureDirSync(this.config.tempPublicDir);
this.workerState = new WorkerState(); this.workerState = new WorkerState();
this.down = new FileDownloader(config.maxUploadFileSize); this.down = new FileDownloader(config.maxUploadFileSize);
this.decomp = new FileDecompressor(3*config.maxUploadFileSize); this.decomp = new FileDecompressor(3*config.maxUploadFileSize);
@@ -55,7 +52,7 @@ class ReaderWorker {
moveToRemote: true, moveToRemote: true,
}, },
{ {
dir: this.config.uploadDir, dir: this.config.uploadPublicDir,
remoteDir: '/upload', remoteDir: '/upload',
maxSize: this.config.maxUploadPublicDirSize, maxSize: this.config.maxUploadPublicDirSize,
moveToRemote: true, moveToRemote: true,
@@ -119,7 +116,7 @@ class ReaderWorker {
await fs.writeFile(downloadedFilename, downdata); await fs.writeFile(downloadedFilename, downdata);
} else {//uploaded file } else {//uploaded file
const fileHash = url.substr(7); const fileHash = url.substr(7);
downloadedFilename = `${this.config.uploadDir}/${fileHash}`; downloadedFilename = `${this.config.uploadPublicDir}/${fileHash}`;
if (!await fs.pathExists(downloadedFilename)) { if (!await fs.pathExists(downloadedFilename)) {
//если удалено из upload, попробуем восстановить из удаленного хранилища //если удалено из upload, попробуем восстановить из удаленного хранилища
try { try {
@@ -227,7 +224,7 @@ class ReaderWorker {
async saveFile(file) { async saveFile(file) {
const hash = await utils.getFileHash(file.path, 'sha256', 'hex'); const hash = await utils.getFileHash(file.path, 'sha256', 'hex');
const outFilename = `${this.config.uploadDir}/${hash}`; const outFilename = `${this.config.uploadPublicDir}/${hash}`;
if (!await fs.pathExists(outFilename)) { if (!await fs.pathExists(outFilename)) {
await fs.move(file.path, outFilename); await fs.move(file.path, outFilename);
@@ -242,7 +239,7 @@ class ReaderWorker {
async saveFileBuf(buf) { async saveFileBuf(buf) {
const hash = await utils.getBufHash(buf, 'sha256', 'hex'); const hash = await utils.getBufHash(buf, 'sha256', 'hex');
const outFilename = `${this.config.uploadDir}/${hash}`; const outFilename = `${this.config.uploadPublicDir}/${hash}`;
if (!await fs.pathExists(outFilename)) { if (!await fs.pathExists(outFilename)) {
await fs.writeFile(outFilename, buf); await fs.writeFile(outFilename, buf);
@@ -255,7 +252,7 @@ class ReaderWorker {
} }
async uploadFileTouch(url) { async uploadFileTouch(url) {
const outFilename = `${this.config.uploadDir}/${url.replace('disk://', '')}`; const outFilename = `${this.config.uploadPublicDir}/${url.replace('disk://', '')}`;
await utils.touchFile(outFilename); await utils.touchFile(outFilename);

View File

@@ -1,7 +1,6 @@
require('tls').DEFAULT_MIN_VERSION = 'TLSv1'; require('tls').DEFAULT_MIN_VERSION = 'TLSv1';
const fs = require('fs-extra'); const fs = require('fs-extra');
const argv = require('minimist')(process.argv.slice(2));
const express = require('express'); const express = require('express');
const compression = require('compression'); const compression = require('compression');
const http = require('http'); const http = require('http');
@@ -11,42 +10,84 @@ const WebSocket = require ('ws');
const ayncExit = new (require('./core/AsyncExit'))(); const ayncExit = new (require('./core/AsyncExit'))();
let log = null; let log = null;
let config;
let argv;
let branch = '';
const argvStrings = ['host', 'port', 'app-dir', 'lib-dir', 'inpx'];
const maxPayloadSize = 50;//in MB const maxPayloadSize = 50;//in MB
function versionText(config) {
return `${config.name} v${config.version}, Node.js ${process.version}`;
}
function showHelp(defaultConfig) {
console.log(versionText(defaultConfig));
console.log(
`Usage: ${defaultConfig.name} [options]
Options:
--help Print ${defaultConfig.name} command line options
--app-dir=<dirpath> Set application working directory, default: <execDir>/.${defaultConfig.name}
--auto-repair Force auto repairing of corrupted database on start
`
);
}
async function init() { async function init() {
argv = require('minimist')(process.argv.slice(2), {string: argvStrings});
const dataDir = argv['app-dir'];
//config //config
const configManager = new (require('./config'))();//singleton const configManager = new (require('./config'))();//singleton
await configManager.init(); await configManager.init(dataDir);
configManager.userConfigFile = argv.config; const defaultConfig = configManager.config;
await configManager.load(); await configManager.load();
const config = configManager.config; config = configManager.config;
branch = config.branch;
//dirs
config.tempDir = `${config.dataDir}/tmp`;
config.logDir = `${config.dataDir}/log`;
config.publicDir = `${config.dataDir}/public`;
config.publicFilesDir = `${config.dataDir}/public-files`;
config.tempPublicDir = `${config.publicFilesDir}/tmp`;
config.uploadPublicDir = `${config.publicFilesDir}/upload`;
configManager.config = config;///!!!
await fs.ensureDir(config.dataDir);
await fs.ensureDir(config.publicDir);
await fs.ensureDir(config.tempPublicDir);
await fs.ensureDir(config.uploadPublicDir);
await fs.ensureDir(config.tempDir);
await fs.emptyDir(config.tempDir);
//logger //logger
const appLogger = new (require('./core/AppLogger'))();//singleton const appLogger = new (require('./core/AppLogger'))();//singleton
await appLogger.init(config); await appLogger.init(config);
log = appLogger.log; log = appLogger.log;
//dirs //cli
log(`${config.name} v${config.version}, Node.js ${process.version}`); if (argv.help) {
log('Initializing'); showHelp(defaultConfig);
ayncExit.exit(0);
await fs.ensureDir(config.dataDir); } else {
await fs.ensureDir(config.uploadDir); log(versionText(config));
log('Initializing');
await fs.ensureDir(config.tempDir);
await fs.emptyDir(config.tempDir);
const appDir = `${config.publicDir}/app`;
const appNewDir = `${config.publicDir}/app_new`;
if (await fs.pathExists(appNewDir)) {
await fs.remove(appDir);
await fs.move(appNewDir, appDir);
} }
//connections //connections
const jembaConnManager = new (require('./db/JembaConnManager'))();//singleton const jembaConnManager = new (require('./db/JembaConnManager'))();//singleton
await jembaConnManager.init(config, argv['auto-repair']); await jembaConnManager.init(config, argv['auto-repair']);
//web app
if (branch !== 'development') {
//const createWebApp = require('./createWebApp');
//await createWebApp(config);
}
} }
async function main() { async function main() {

View File

@@ -1,5 +1,4 @@
const fs = require('fs-extra'); const fs = require('fs-extra');
const path = require('path');
const express = require('express'); const express = require('express');
const multer = require('multer'); const multer = require('multer');
@@ -19,15 +18,9 @@ function initRoutes(app, wss, config) {
initStatic(app, config); initStatic(app, config);
const misc = new c.MiscController(config);
const reader = new c.ReaderController(config); const reader = new c.ReaderController(config);
const worker = new c.WorkerController(config);
new c.WebSocketController(wss, config); new c.WebSocketController(wss, config);
//access
const [aAll, aNormal, aSite, aReader, aOmnireader] = // eslint-disable-line no-unused-vars
[config.mode, 'normal', 'site', 'reader', 'omnireader'];
//multer //multer
const storage = multer.diskStorage({ const storage = multer.diskStorage({
destination: (req, file, cb) => { destination: (req, file, cb) => {
@@ -41,42 +34,29 @@ function initRoutes(app, wss, config) {
//routes //routes
const routes = [ const routes = [
['POST', '/api/config', misc.getConfig.bind(misc), [aAll], {}], ['POST', '/api/reader/upload-file', [upload.single('file'), reader.uploadFile.bind(reader)]],
['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], {}],
]; ];
//to app //to app
for (let route of routes) { for (let route of routes) {
let callbacks = []; let callbacks = [];
let [httpMethod, path, controllers, access, options] = route; let [httpMethod, path, controllers] = route;
let controller = controllers; let controller = controllers;
if (Array.isArray(controllers)) { if (Array.isArray(controllers)) {
controller = controllers[controllers.length - 1]; controller = controllers[controllers.length - 1];
callbacks = controllers.slice(0, -1); callbacks = controllers.slice(0, -1);
} }
access = new Set(access); const callback = async function(req, res) {
try {
const result = await controller(req, res);
let callback = () => {}; if (result !== false)
if (access.has(config.mode)) {//allowed res.send(result);
callback = async function(req, res) { } catch (e) {
try { res.status(500).send({error: e.message});
const result = await controller(req, res, options); }
};
if (result !== false)
res.send(result);
} catch (e) {
res.status(500).send({error: e.message});
}
};
} else {//forbidden
callback = async function(req, res) {
res.status(403);
};
}
callbacks.push(callback); callbacks.push(callback);
switch (httpMethod) { switch (httpMethod) {
@@ -96,42 +76,54 @@ function initStatic(app, config) {
const readerWorker = new ReaderWorker(config); const readerWorker = new ReaderWorker(config);
//восстановление файлов в /tmp и /upload из webdav-storage, при необходимости //восстановление файлов в /tmp и /upload из webdav-storage, при необходимости
app.use(async(req, res, next) => { app.use('/tmp',
if ((req.method !== 'GET' && req.method !== 'HEAD') || async(req, res, next) => {
!(req.path.indexOf('/tmp/') === 0 || req.path.indexOf('/upload/') === 0) if (req.method !== 'GET' && req.method !== 'HEAD') {
) { return next();
return next();
}
const filePath = `${config.publicDir}${req.path}`;
//восстановим
try {
if (!await fs.pathExists(filePath)) {
if (req.path.indexOf('/tmp/') === 0) {
await readerWorker.restoreRemoteFile(req.path, '/tmp');
} else if (req.path.indexOf('/upload/') === 0) {
await readerWorker.restoreRemoteFile(req.path, '/upload');
}
} }
} catch(e) {
log(LM_ERR, `static::restoreRemoteFile ${req.path} > ${e.message}`);
}
return next(); const filePath = `${config.tempPublicDir}${req.path}`;
});
const tmpDir = `${config.publicDir}/tmp`; //восстановим
app.use(express.static(config.publicDir, { try {
maxAge: '30d', if (!await fs.pathExists(filePath))
await readerWorker.restoreRemoteFile(req.path, '/tmp');
} catch(e) {
log(LM_ERR, `static::restoreRemoteFile ${filePath} > ${e.message}`);
}
setHeaders: (res, filePath) => { return next();
if (path.dirname(filePath) == tmpDir) { },
express.static(config.tempPublicDir, {
setHeaders: (res) => {
res.set('Content-Type', 'application/xml'); res.set('Content-Type', 'application/xml');
res.set('Content-Encoding', 'gzip'); res.set('Content-Encoding', 'gzip');
},
})
);
app.use('/upload',
async(req, res, next) => {
if (req.method !== 'GET' && req.method !== 'HEAD') {
return next();
} }
const filePath = `${config.uploadPublicDir}${req.path}`;
//восстановим
try {
if (!await fs.pathExists(filePath))
await readerWorker.restoreRemoteFile(req.path, '/upload');
} catch(e) {
log(LM_ERR, `static::restoreRemoteFile ${filePath} > ${e.message}`);
}
return next();
}, },
})); express.static(config.uploadPublicDir)
);
app.use(express.static(config.publicDir));
} }
module.exports = { module.exports = {