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

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

4
.gitignore vendored
View File

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

View File

@@ -1,5 +1,5 @@
{
"name": "Liberama",
"name": "liberama",
"version": "0.12.2",
"author": "Book Pauk <bookpauk@gmail.com>",
"license": "CC0-1.0",
@@ -8,7 +8,7 @@
"node": ">=16.16.0"
},
"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: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 .",

View File

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

View File

@@ -1,4 +1,5 @@
const _ = require('lodash');
const path = require('path');
const fs = require('fs-extra');
const branchFilename = __dirname + '/application_env';
@@ -29,7 +30,7 @@ class ConfigManager {
return instance;
}
async init() {
async init(dataDir) {
if (this.inited)
throw new Error('already inited');
@@ -44,10 +45,17 @@ class ConfigManager {
process.env.NODE_ENV = this.branch;
this.branchConfigFile = __dirname + `/${this.branch}.js`;
this._config = require(this.branchConfigFile);
const config = require(this.branchConfigFile);
await fs.ensureDir(this._config.dataDir);
this._userConfigFile = `${this._config.dataDir}/config.json`;
if (dataDir) {
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;
}
@@ -72,15 +80,28 @@ class ConfigManager {
}
async load() {
if (!this.inited)
throw new Error('not inited');
if (!await fs.pathExists(this.userConfigFile)) {
await this.save();
return;
}
try {
if (!this.inited)
throw new Error('not inited');
const data = await fs.readFile(this.userConfigFile, 'utf8');
this.config = JSON.parse(data);
if (await fs.pathExists(this.userConfigFile)) {
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() {

View File

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

View File

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

View File

@@ -1,7 +1,6 @@
require('tls').DEFAULT_MIN_VERSION = 'TLSv1';
const fs = require('fs-extra');
const argv = require('minimist')(process.argv.slice(2));
const express = require('express');
const compression = require('compression');
const http = require('http');
@@ -11,42 +10,84 @@ const WebSocket = require ('ws');
const ayncExit = new (require('./core/AsyncExit'))();
let log = null;
let config;
let argv;
let branch = '';
const argvStrings = ['host', 'port', 'app-dir', 'lib-dir', 'inpx'];
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() {
argv = require('minimist')(process.argv.slice(2), {string: argvStrings});
const dataDir = argv['app-dir'];
//config
const configManager = new (require('./config'))();//singleton
await configManager.init();
configManager.userConfigFile = argv.config;
await configManager.init(dataDir);
const defaultConfig = configManager.config;
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
const appLogger = new (require('./core/AppLogger'))();//singleton
await appLogger.init(config);
log = appLogger.log;
//dirs
log(`${config.name} v${config.version}, Node.js ${process.version}`);
log('Initializing');
await fs.ensureDir(config.dataDir);
await fs.ensureDir(config.uploadDir);
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);
//cli
if (argv.help) {
showHelp(defaultConfig);
ayncExit.exit(0);
} else {
log(versionText(config));
log('Initializing');
}
//connections
const jembaConnManager = new (require('./db/JembaConnManager'))();//singleton
await jembaConnManager.init(config, argv['auto-repair']);
//web app
if (branch !== 'development') {
//const createWebApp = require('./createWebApp');
//await createWebApp(config);
}
}
async function main() {

View File

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