Выделение файлов приложения в рабочую директорию
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,5 +1,3 @@
|
||||
/node_modules
|
||||
/server/data
|
||||
/server/public
|
||||
/server/ipfs
|
||||
/server/.liberama*
|
||||
/dist
|
||||
|
||||
@@ -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 .",
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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: [
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
110
server/routes.js
110
server/routes.js
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user