mirror of https://github.com/requarks/wiki
parent
99a07d342c
commit
6ea243e8d4
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,52 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
var loki = require('lokijs'),
|
||||
fs = require("fs"),
|
||||
path = require("path"),
|
||||
Promise = require('bluebird'),
|
||||
_ = require('lodash');
|
||||
|
||||
var cols = ['User', 'Entry'];
|
||||
|
||||
/**
|
||||
* Loki.js module
|
||||
*
|
||||
* @param {Object} appconfig Application config
|
||||
* @return {Object} LokiJS instance
|
||||
*/
|
||||
module.exports = function(appconfig) {
|
||||
|
||||
let dbReadyResolve;
|
||||
let dbReady = new Promise((resolve, reject) => {
|
||||
dbReadyResolve = resolve;
|
||||
});
|
||||
|
||||
// Initialize Loki.js
|
||||
|
||||
let dbModel = {
|
||||
Store: new loki(path.join(appconfig.datadir.db, 'app.db'), {
|
||||
env: 'NODEJS',
|
||||
autosave: true,
|
||||
autosaveInterval: 5000
|
||||
}),
|
||||
onReady: dbReady
|
||||
};
|
||||
|
||||
// Load Models
|
||||
|
||||
dbModel.Store.loadDatabase({}, () => {
|
||||
|
||||
_.forEach(cols, (col) => {
|
||||
dbModel[col] = dbModel.Store.getCollection(col);
|
||||
if(!dbModel[col]) {
|
||||
dbModel[col] = dbModel.Store.addCollection(col);
|
||||
}
|
||||
});
|
||||
|
||||
dbReadyResolve();
|
||||
|
||||
});
|
||||
|
||||
return dbModel;
|
||||
|
||||
};
|
@ -0,0 +1,54 @@
|
||||
"use strict";
|
||||
|
||||
const modb = require('mongoose'),
|
||||
Promise = require('bluebird'),
|
||||
_ = require('lodash');
|
||||
|
||||
/**
|
||||
* Entry schema
|
||||
*
|
||||
* @type {<Mongoose.Schema>}
|
||||
*/
|
||||
var entrySchema = modb.Schema({
|
||||
|
||||
_id: String,
|
||||
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
minlength: 2
|
||||
},
|
||||
subtitle: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
parent: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
content: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
|
||||
},
|
||||
{
|
||||
timestamps: {}
|
||||
});
|
||||
|
||||
entrySchema.index({
|
||||
_id: 'text',
|
||||
title: 'text',
|
||||
subtitle: 'text',
|
||||
content: 'text'
|
||||
}, {
|
||||
weights: {
|
||||
_id: 3,
|
||||
title: 10,
|
||||
subtitle: 5,
|
||||
content: 1
|
||||
},
|
||||
name: 'EntriesTextIndex'
|
||||
});
|
||||
|
||||
module.exports = modb.model('Entry', entrySchema);
|
@ -0,0 +1,51 @@
|
||||
"use strict";
|
||||
|
||||
const modb = require('mongoose'),
|
||||
Promise = require('bluebird'),
|
||||
_ = require('lodash');
|
||||
|
||||
/**
|
||||
* Upload File schema
|
||||
*
|
||||
* @type {<Mongoose.Schema>}
|
||||
*/
|
||||
var uplFileSchema = modb.Schema({
|
||||
|
||||
_id: String,
|
||||
|
||||
category: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: 'binary'
|
||||
},
|
||||
mime: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: 'application/octet-stream'
|
||||
},
|
||||
extra: {
|
||||
type: Object
|
||||
},
|
||||
folder: {
|
||||
type: String,
|
||||
ref: 'UplFolder'
|
||||
},
|
||||
filename: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
basename: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
filesize: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
|
||||
},
|
||||
{
|
||||
timestamps: {}
|
||||
});
|
||||
|
||||
module.exports = modb.model('UplFile', uplFileSchema);
|
@ -0,0 +1,23 @@
|
||||
"use strict";
|
||||
|
||||
const modb = require('mongoose'),
|
||||
Promise = require('bluebird'),
|
||||
_ = require('lodash');
|
||||
|
||||
/**
|
||||
* Upload Folder schema
|
||||
*
|
||||
* @type {<Mongoose.Schema>}
|
||||
*/
|
||||
var uplFolderSchema = modb.Schema({
|
||||
|
||||
name: {
|
||||
type: String
|
||||
}
|
||||
|
||||
},
|
||||
{
|
||||
timestamps: {}
|
||||
});
|
||||
|
||||
module.exports = modb.model('UplFolder', uplFolderSchema);
|
@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
|
||||
const modb = require('mongoose'),
|
||||
Promise = require('bluebird'),
|
||||
_ = require('lodash');
|
||||
|
||||
/**
|
||||
* Region schema
|
||||
*
|
||||
* @type {<Mongoose.Schema>}
|
||||
*/
|
||||
var userSchema = modb.Schema({
|
||||
|
||||
email: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
|
||||
},
|
||||
{
|
||||
timestamps: {}
|
||||
});
|
||||
|
||||
module.exports = modb.model('User', userSchema);
|
@ -1,335 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
var path = require('path'),
|
||||
loki = require('lokijs'),
|
||||
Promise = require('bluebird'),
|
||||
fs = Promise.promisifyAll(require('fs-extra')),
|
||||
multer = require('multer'),
|
||||
_ = require('lodash');
|
||||
|
||||
var regFolderName = new RegExp("^[a-z0-9][a-z0-9\-]*[a-z0-9]$");
|
||||
|
||||
/**
|
||||
* Local Data Storage
|
||||
*
|
||||
* @param {Object} appconfig The application configuration
|
||||
*/
|
||||
module.exports = {
|
||||
|
||||
_uploadsPath: './repo/uploads',
|
||||
_uploadsThumbsPath: './data/thumbs',
|
||||
_uploadsFolders: [],
|
||||
_uploadsDb: null,
|
||||
|
||||
uploadImgHandler: null,
|
||||
|
||||
/**
|
||||
* Initialize Local Data Storage model
|
||||
*
|
||||
* @param {Object} appconfig The application config
|
||||
* @return {Object} Local Data Storage model instance
|
||||
*/
|
||||
init(appconfig, mode = 'server') {
|
||||
|
||||
let self = this;
|
||||
|
||||
self._uploadsPath = path.resolve(ROOTPATH, appconfig.datadir.repo, 'uploads');
|
||||
self._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.datadir.db, 'thumbs');
|
||||
|
||||
// Finish initialization tasks
|
||||
|
||||
switch(mode) {
|
||||
case 'agent':
|
||||
//todo
|
||||
break;
|
||||
case 'server':
|
||||
self.createBaseDirectories(appconfig);
|
||||
self.initMulter(appconfig);
|
||||
break;
|
||||
case 'ws':
|
||||
self.initDb(appconfig);
|
||||
break;
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize Uploads DB
|
||||
*
|
||||
* @param {Object} appconfig The application config
|
||||
* @return {boolean} Void
|
||||
*/
|
||||
initDb(appconfig) {
|
||||
|
||||
let self = this;
|
||||
|
||||
let dbReadyResolve;
|
||||
let dbReady = new Promise((resolve, reject) => {
|
||||
dbReadyResolve = resolve;
|
||||
});
|
||||
|
||||
// Initialize Loki.js
|
||||
|
||||
let dbModel = {
|
||||
Store: new loki(path.join(appconfig.datadir.db, 'uploads.db'), {
|
||||
env: 'NODEJS',
|
||||
autosave: true,
|
||||
autosaveInterval: 15000
|
||||
}),
|
||||
onReady: dbReady
|
||||
};
|
||||
|
||||
// Load Models
|
||||
|
||||
dbModel.Store.loadDatabase({}, () => {
|
||||
|
||||
dbModel.Files = dbModel.Store.getCollection('Files');
|
||||
if(!dbModel.Files) {
|
||||
dbModel.Files = dbModel.Store.addCollection('Files', {
|
||||
indices: ['category', 'folder']
|
||||
});
|
||||
}
|
||||
|
||||
dbReadyResolve();
|
||||
|
||||
});
|
||||
|
||||
self._uploadsDb = dbModel;
|
||||
|
||||
return true;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Init Multer upload handlers
|
||||
*
|
||||
* @param {Object} appconfig The application config
|
||||
* @return {boolean} Void
|
||||
*/
|
||||
initMulter(appconfig) {
|
||||
|
||||
this.uploadImgHandler = multer({
|
||||
storage: multer.diskStorage({
|
||||
destination: (req, f, cb) => {
|
||||
cb(null, path.resolve(ROOTPATH, appconfig.datadir.db, 'temp-upload'))
|
||||
}
|
||||
}),
|
||||
fileFilter: (req, f, cb) => {
|
||||
|
||||
//-> Check filesize (3 MB max)
|
||||
|
||||
if(f.size > 3145728) {
|
||||
return cb(null, false);
|
||||
}
|
||||
|
||||
//-> Check MIME type (quick check only)
|
||||
|
||||
if(!_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], f.mimetype)) {
|
||||
return cb(null, false);
|
||||
}
|
||||
|
||||
cb(null, true);
|
||||
}
|
||||
}).array('imgfile', 20);
|
||||
|
||||
return true;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the thumbnails folder path.
|
||||
*
|
||||
* @return {String} The thumbs path.
|
||||
*/
|
||||
getThumbsPath() {
|
||||
return this._uploadsThumbsPath;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a base directories (Synchronous).
|
||||
*
|
||||
* @param {Object} appconfig The application config
|
||||
* @return {Void} Void
|
||||
*/
|
||||
createBaseDirectories(appconfig) {
|
||||
|
||||
winston.info('[SERVER] Checking data directories...');
|
||||
|
||||
try {
|
||||
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.datadir.db));
|
||||
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.datadir.db, './cache'));
|
||||
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.datadir.db, './thumbs'));
|
||||
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.datadir.db, './temp-upload'));
|
||||
|
||||
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.datadir.repo));
|
||||
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.datadir.repo, './uploads'));
|
||||
} catch (err) {
|
||||
winston.error(err);
|
||||
}
|
||||
|
||||
winston.info('[SERVER] Data and Repository directories are OK.');
|
||||
|
||||
return;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the uploads folders.
|
||||
*
|
||||
* @param {Array<String>} arrFolders The arr folders
|
||||
* @return {Void} Void
|
||||
*/
|
||||
setUploadsFolders(arrFolders) {
|
||||
|
||||
this._uploadsFolders = arrFolders;
|
||||
return;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the uploads folders.
|
||||
*
|
||||
* @return {Array<String>} The uploads folders.
|
||||
*/
|
||||
getUploadsFolders() {
|
||||
return this._uploadsFolders;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates an uploads folder.
|
||||
*
|
||||
* @param {String} folderName The folder name
|
||||
* @return {Promise} Promise of the operation
|
||||
*/
|
||||
createUploadsFolder(folderName) {
|
||||
|
||||
let self = this;
|
||||
|
||||
folderName = _.kebabCase(_.trim(folderName));
|
||||
|
||||
if(_.isEmpty(folderName) || !regFolderName.test(folderName)) {
|
||||
return Promise.resolve(self.getUploadsFolders());
|
||||
}
|
||||
|
||||
return fs.ensureDirAsync(path.join(self._uploadsPath, folderName)).then(() => {
|
||||
if(!_.includes(self._uploadsFolders, folderName)) {
|
||||
self._uploadsFolders.push(folderName);
|
||||
self._uploadsFolders = _.sortBy(self._uploadsFolders);
|
||||
}
|
||||
return self.getUploadsFolders();
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if folder is valid and exists
|
||||
*
|
||||
* @param {String} folderName The folder name
|
||||
* @return {Boolean} True if valid
|
||||
*/
|
||||
validateUploadsFolder(folderName) {
|
||||
|
||||
folderName = (_.includes(this._uploadsFolders, folderName)) ? folderName : '';
|
||||
return path.resolve(this._uploadsPath, folderName);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if filename is valid and unique
|
||||
*
|
||||
* @param {String} f The filename
|
||||
* @param {String} fld The containing folder
|
||||
* @return {Promise<String>} Promise of the accepted filename
|
||||
*/
|
||||
validateUploadsFilename(f, fld) {
|
||||
|
||||
let fObj = path.parse(f);
|
||||
let fname = _.chain(fObj.name).trim().toLower().kebabCase().value().replace(/[^a-z0-9\-]+/g, '');
|
||||
let fext = _.toLower(fObj.ext);
|
||||
|
||||
if(!_.includes(['.jpg', '.jpeg', '.png', '.gif', '.webp'], fext)) {
|
||||
fext = '.png';
|
||||
}
|
||||
|
||||
f = fname + fext;
|
||||
let fpath = path.resolve(this._uploadsPath, fld, f);
|
||||
|
||||
return fs.statAsync(fpath).then((s) => {
|
||||
throw new Error('File ' + f + ' already exists.');
|
||||
}).catch((err) => {
|
||||
if(err.code === 'ENOENT') {
|
||||
return f;
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse relative Uploads path
|
||||
*
|
||||
* @param {String} f Relative Uploads path
|
||||
* @return {Object} Parsed path (folder and filename)
|
||||
*/
|
||||
parseUploadsRelPath(f) {
|
||||
|
||||
let fObj = path.parse(f);
|
||||
return {
|
||||
folder: fObj.dir,
|
||||
filename: fObj.base
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the uploads files.
|
||||
*
|
||||
* @param {Array<Object>} arrFiles The uploads files
|
||||
* @return {Void} Void
|
||||
*/
|
||||
setUploadsFiles(arrFiles) {
|
||||
|
||||
let self = this;
|
||||
|
||||
if(_.isArray(arrFiles) && arrFiles.length > 0) {
|
||||
self._uploadsDb.Files.clear();
|
||||
self._uploadsDb.Files.insert(arrFiles);
|
||||
self._uploadsDb.Files.ensureIndex('category', true);
|
||||
self._uploadsDb.Files.ensureIndex('folder', true);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds one or more uploads files.
|
||||
*
|
||||
* @param {Array<Object>} arrFiles The uploads files
|
||||
* @return {Void} Void
|
||||
*/
|
||||
addUploadsFiles(arrFiles) {
|
||||
if(_.isArray(arrFiles) || _.isPlainObject(arrFiles)) {
|
||||
this._uploadsDb.Files.insert(arrFiles);
|
||||
}
|
||||
return;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the uploads files.
|
||||
*
|
||||
* @param {String} cat Category type
|
||||
* @param {String} fld Folder
|
||||
* @return {Array<Object>} The files matching the query
|
||||
*/
|
||||
getUploadsFiles(cat, fld) {
|
||||
|
||||
return this._uploadsDb.Files.chain().find({
|
||||
'$and': [{ 'category' : cat },{ 'folder' : fld }]
|
||||
}).simplesort('filename').data();
|
||||
|
||||
}
|
||||
|
||||
};
|
@ -0,0 +1,64 @@
|
||||
"use strict";
|
||||
|
||||
const modb = require('mongoose'),
|
||||
fs = require("fs"),
|
||||
path = require("path"),
|
||||
_ = require('lodash');
|
||||
|
||||
/**
|
||||
* MongoDB module
|
||||
*
|
||||
* @param {Object} appconfig Application config
|
||||
* @return {Object} MongoDB wrapper instance
|
||||
*/
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
* Initialize DB
|
||||
*
|
||||
* @param {Object} appconfig The application config
|
||||
* @return {Object} DB instance
|
||||
*/
|
||||
init(appconfig) {
|
||||
|
||||
let self = this;
|
||||
|
||||
let dbModelsPath = path.resolve(ROOTPATH, 'models', 'db');
|
||||
|
||||
modb.Promise = require('bluebird');
|
||||
|
||||
// Event handlers
|
||||
|
||||
modb.connection.on('error', (err) => {
|
||||
winston.error('[' + PROCNAME + '] Failed to connect to MongoDB instance.');
|
||||
});
|
||||
modb.connection.once('open', function() {
|
||||
winston.log('[' + PROCNAME + '] Connected to MongoDB instance.');
|
||||
});
|
||||
|
||||
// Store connection handle
|
||||
|
||||
self.connection = modb.connection;
|
||||
self.ObjectId = modb.Types.ObjectId;
|
||||
|
||||
// Load DB Models
|
||||
|
||||
fs
|
||||
.readdirSync(dbModelsPath)
|
||||
.filter(function(file) {
|
||||
return (file.indexOf(".") !== 0);
|
||||
})
|
||||
.forEach(function(file) {
|
||||
let modelName = _.upperFirst(_.camelCase(_.split(file,'.')[0]));
|
||||
self[modelName] = require(path.join(dbModelsPath, file));
|
||||
});
|
||||
|
||||
// Connect
|
||||
|
||||
self.onReady = modb.connect(appconfig.db);
|
||||
|
||||
return self;
|
||||
|
||||
}
|
||||
|
||||
};
|
@ -1,198 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
var Promise = require('bluebird'),
|
||||
_ = require('lodash'),
|
||||
path = require('path'),
|
||||
searchIndex = require('search-index'),
|
||||
stopWord = require('stopword');
|
||||
|
||||
/**
|
||||
* Search Model
|
||||
*/
|
||||
module.exports = {
|
||||
|
||||
_si: null,
|
||||
|
||||
/**
|
||||
* Initialize Search model
|
||||
*
|
||||
* @param {Object} appconfig The application config
|
||||
* @return {Object} Search model instance
|
||||
*/
|
||||
init(appconfig) {
|
||||
|
||||
let self = this;
|
||||
let dbPath = path.resolve(ROOTPATH, appconfig.datadir.db, 'search');
|
||||
|
||||
searchIndex({
|
||||
deletable: true,
|
||||
fieldedSearch: true,
|
||||
indexPath: dbPath,
|
||||
logLevel: 'error',
|
||||
stopwords: stopWord.getStopwords(appconfig.lang).sort()
|
||||
}, (err, si) => {
|
||||
if(err) {
|
||||
winston.error('Failed to initialize search-index.', err);
|
||||
} else {
|
||||
self._si = Promise.promisifyAll(si);
|
||||
}
|
||||
});
|
||||
|
||||
return self;
|
||||
|
||||
},
|
||||
|
||||
find(terms) {
|
||||
|
||||
let self = this;
|
||||
terms = _.chain(terms)
|
||||
.deburr()
|
||||
.toLower()
|
||||
.trim()
|
||||
.replace(/[^a-z0-9 ]/g, '')
|
||||
.value();
|
||||
|
||||
let arrTerms = _.chain(terms)
|
||||
.split(' ')
|
||||
.filter((f) => { return !_.isEmpty(f); })
|
||||
.value();
|
||||
|
||||
|
||||
return self._si.searchAsync({
|
||||
query: {
|
||||
AND: [{ '*': arrTerms }]
|
||||
},
|
||||
pageSize: 10
|
||||
}).get('hits').then((hits) => {
|
||||
|
||||
if(hits.length < 5) {
|
||||
return self._si.matchAsync({
|
||||
beginsWith: terms,
|
||||
threshold: 3,
|
||||
limit: 5,
|
||||
type: 'simple'
|
||||
}).then((matches) => {
|
||||
|
||||
return {
|
||||
match: hits,
|
||||
suggest: matches
|
||||
};
|
||||
|
||||
});
|
||||
} else {
|
||||
return {
|
||||
match: hits,
|
||||
suggest: []
|
||||
};
|
||||
}
|
||||
|
||||
}).catch((err) => {
|
||||
|
||||
if(err.type === 'NotFoundError') {
|
||||
return {
|
||||
match: [],
|
||||
suggest: []
|
||||
};
|
||||
} else {
|
||||
winston.error(err);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a document to the index
|
||||
*
|
||||
* @param {Object} content Document content
|
||||
* @return {Promise} Promise of the add operation
|
||||
*/
|
||||
add(content) {
|
||||
|
||||
let self = this;
|
||||
|
||||
return self.delete(content.entryPath).then(() => {
|
||||
|
||||
return self._si.addAsync({
|
||||
entryPath: content.entryPath,
|
||||
title: content.meta.title,
|
||||
subtitle: content.meta.subtitle || '',
|
||||
parent: content.parent.title || '',
|
||||
content: content.text || ''
|
||||
}, {
|
||||
fieldOptions: [{
|
||||
fieldName: 'entryPath',
|
||||
searchable: true,
|
||||
weight: 2
|
||||
},
|
||||
{
|
||||
fieldName: 'title',
|
||||
nGramLength: [1, 2],
|
||||
searchable: true,
|
||||
weight: 3
|
||||
},
|
||||
{
|
||||
fieldName: 'subtitle',
|
||||
searchable: true,
|
||||
weight: 1,
|
||||
store: false
|
||||
},
|
||||
{
|
||||
fieldName: 'parent',
|
||||
searchable: false,
|
||||
},
|
||||
{
|
||||
fieldName: 'content',
|
||||
searchable: true,
|
||||
weight: 0,
|
||||
store: false
|
||||
}]
|
||||
}).then(() => {
|
||||
winston.info('Entry ' + content.entryPath + ' added/updated to index.');
|
||||
return true;
|
||||
}).catch((err) => {
|
||||
winston.error(err);
|
||||
});
|
||||
|
||||
}).catch((err) => {
|
||||
winston.error(err);
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete an entry from the index
|
||||
*
|
||||
* @param {String} The entry path
|
||||
* @return {Promise} Promise of the operation
|
||||
*/
|
||||
delete(entryPath) {
|
||||
|
||||
let self = this;
|
||||
|
||||
return self._si.searchAsync({
|
||||
query: {
|
||||
AND: [{ 'entryPath': [entryPath] }]
|
||||
}
|
||||
}).then((results) => {
|
||||
|
||||
if(results.totalHits > 0) {
|
||||
let delIds = _.map(results.hits, 'id');
|
||||
return self._si.delAsync(delIds);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
}).catch((err) => {
|
||||
|
||||
if(err.type === 'NotFoundError') {
|
||||
return true;
|
||||
} else {
|
||||
winston.error(err);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
};
|
@ -0,0 +1,152 @@
|
||||
"use strict";
|
||||
|
||||
var path = require('path'),
|
||||
Promise = require('bluebird'),
|
||||
fs = Promise.promisifyAll(require('fs-extra')),
|
||||
multer = require('multer'),
|
||||
_ = require('lodash');
|
||||
|
||||
/**
|
||||
* Local Data Storage
|
||||
*
|
||||
* @param {Object} appconfig The application configuration
|
||||
*/
|
||||
module.exports = {
|
||||
|
||||
_uploadsPath: './repo/uploads',
|
||||
_uploadsThumbsPath: './data/thumbs',
|
||||
|
||||
uploadImgHandler: null,
|
||||
|
||||
/**
|
||||
* Initialize Local Data Storage model
|
||||
*
|
||||
* @param {Object} appconfig The application config
|
||||
* @return {Object} Local Data Storage model instance
|
||||
*/
|
||||
init(appconfig) {
|
||||
|
||||
this._uploadsPath = path.resolve(ROOTPATH, appconfig.paths.repo, 'uploads');
|
||||
this._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.paths.data, 'thumbs');
|
||||
|
||||
this.createBaseDirectories(appconfig);
|
||||
this.initMulter(appconfig);
|
||||
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Init Multer upload handlers
|
||||
*
|
||||
* @param {Object} appconfig The application config
|
||||
* @return {boolean} Void
|
||||
*/
|
||||
initMulter(appconfig) {
|
||||
|
||||
this.uploadImgHandler = multer({
|
||||
storage: multer.diskStorage({
|
||||
destination: (req, f, cb) => {
|
||||
cb(null, path.resolve(ROOTPATH, appconfig.paths.data, 'temp-upload'))
|
||||
}
|
||||
}),
|
||||
fileFilter: (req, f, cb) => {
|
||||
|
||||
//-> Check filesize (3 MB max)
|
||||
|
||||
if(f.size > 3145728) {
|
||||
return cb(null, false);
|
||||
}
|
||||
|
||||
//-> Check MIME type (quick check only)
|
||||
|
||||
if(!_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], f.mimetype)) {
|
||||
return cb(null, false);
|
||||
}
|
||||
|
||||
cb(null, true);
|
||||
}
|
||||
}).array('imgfile', 20);
|
||||
|
||||
return true;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a base directories (Synchronous).
|
||||
*
|
||||
* @param {Object} appconfig The application config
|
||||
* @return {Void} Void
|
||||
*/
|
||||
createBaseDirectories(appconfig) {
|
||||
|
||||
winston.info('[SERVER] Checking data directories...');
|
||||
|
||||
try {
|
||||
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data));
|
||||
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './cache'));
|
||||
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './thumbs'));
|
||||
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './temp-upload'));
|
||||
|
||||
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.repo));
|
||||
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.repo, './uploads'));
|
||||
} catch (err) {
|
||||
winston.error(err);
|
||||
}
|
||||
|
||||
winston.info('[SERVER] Data and Repository directories are OK.');
|
||||
|
||||
return;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the uploads path.
|
||||
*
|
||||
* @return {String} The uploads path.
|
||||
*/
|
||||
getUploadsPath() {
|
||||
return this._uploadsPath;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the thumbnails folder path.
|
||||
*
|
||||
* @return {String} The thumbs path.
|
||||
*/
|
||||
getThumbsPath() {
|
||||
return this._uploadsThumbsPath;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if filename is valid and unique
|
||||
*
|
||||
* @param {String} f The filename
|
||||
* @param {String} fld The containing folder
|
||||
* @return {Promise<String>} Promise of the accepted filename
|
||||
*/
|
||||
validateUploadsFilename(f, fld) {
|
||||
|
||||
let fObj = path.parse(f);
|
||||
let fname = _.chain(fObj.name).trim().toLower().kebabCase().value().replace(/[^a-z0-9\-]+/g, '');
|
||||
let fext = _.toLower(fObj.ext);
|
||||
|
||||
if(!_.includes(['.jpg', '.jpeg', '.png', '.gif', '.webp'], fext)) {
|
||||
fext = '.png';
|
||||
}
|
||||
|
||||
f = fname + fext;
|
||||
let fpath = path.resolve(this._uploadsPath, fld, f);
|
||||
|
||||
return fs.statAsync(fpath).then((s) => {
|
||||
throw new Error('File ' + f + ' already exists.');
|
||||
}).catch((err) => {
|
||||
if(err.code === 'ENOENT') {
|
||||
return f;
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
};
|
@ -0,0 +1,133 @@
|
||||
"use strict";
|
||||
|
||||
const Promise = require('bluebird'),
|
||||
_ = require('lodash'),
|
||||
path = require('path');
|
||||
|
||||
/**
|
||||
* Search Model
|
||||
*/
|
||||
module.exports = {
|
||||
|
||||
_si: null,
|
||||
|
||||
/**
|
||||
* Initialize Search model
|
||||
*
|
||||
* @param {Object} appconfig The application config
|
||||
* @return {Object} Search model instance
|
||||
*/
|
||||
init(appconfig) {
|
||||
|
||||
let self = this;
|
||||
|
||||
return self;
|
||||
|
||||
},
|
||||
|
||||
find(terms) {
|
||||
|
||||
let self = this;
|
||||
terms = _.chain(terms)
|
||||
.deburr()
|
||||
.toLower()
|
||||
.trim()
|
||||
.replace(/[^a-z0-9 ]/g, '')
|
||||
.split(' ')
|
||||
.filter((f) => { return !_.isEmpty(f); })
|
||||
.join(' ')
|
||||
.value();
|
||||
|
||||
return db.Entry.find(
|
||||
{ $text: { $search: terms } },
|
||||
{ score: { $meta: "textScore" }, title: 1 }
|
||||
)
|
||||
.sort({ score: { $meta: "textScore" } })
|
||||
.limit(10)
|
||||
.exec()
|
||||
.then((hits) => {
|
||||
|
||||
/*if(hits.length < 5) {
|
||||
return self._si.matchAsync({
|
||||
beginsWith: terms,
|
||||
threshold: 3,
|
||||
limit: 5,
|
||||
type: 'simple'
|
||||
}).then((matches) => {
|
||||
|
||||
return {
|
||||
match: hits,
|
||||
suggest: matches
|
||||
};
|
||||
|
||||
});
|
||||
} else {*/
|
||||
return {
|
||||
match: hits,
|
||||
suggest: []
|
||||
};
|
||||
//}
|
||||
|
||||
}).catch((err) => {
|
||||
|
||||
if(err.type === 'NotFoundError') {
|
||||
return {
|
||||
match: [],
|
||||
suggest: []
|
||||
};
|
||||
} else {
|
||||
winston.error(err);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete an entry from the index
|
||||
*
|
||||
* @param {String} The entry path
|
||||
* @return {Promise} Promise of the operation
|
||||
*/
|
||||
delete(entryPath) {
|
||||
|
||||
let self = this;
|
||||
/*let hasResults = false;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
self._si.search({
|
||||
query: {
|
||||
AND: { 'entryPath': [entryPath] }
|
||||
}
|
||||
}).on('data', (results) => {
|
||||
|
||||
hasResults = true;
|
||||
|
||||
if(results.totalHits > 0) {
|
||||
let delIds = _.map(results.hits, 'id');
|
||||
self._si.del(delIds).on('end', () => { return resolve(true); });
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
|
||||
}).on('error', (err) => {
|
||||
|
||||
if(err.type === 'NotFoundError') {
|
||||
resolve(true);
|
||||
} else {
|
||||
winston.error(err);
|
||||
reject(err);
|
||||
}
|
||||
|
||||
}).on('end', () => {
|
||||
if(!hasResults) {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
|
||||
});*/
|
||||
|
||||
}
|
||||
|
||||
};
|
@ -0,0 +1,160 @@
|
||||
"use strict";
|
||||
|
||||
var path = require('path'),
|
||||
Promise = require('bluebird'),
|
||||
fs = Promise.promisifyAll(require('fs-extra')),
|
||||
multer = require('multer'),
|
||||
_ = require('lodash');
|
||||
|
||||
var regFolderName = new RegExp("^[a-z0-9][a-z0-9\-]*[a-z0-9]$");
|
||||
|
||||
/**
|
||||
* Uploads
|
||||
*/
|
||||
module.exports = {
|
||||
|
||||
_uploadsPath: './repo/uploads',
|
||||
_uploadsThumbsPath: './data/thumbs',
|
||||
|
||||
/**
|
||||
* Initialize Local Data Storage model
|
||||
*
|
||||
* @param {Object} appconfig The application config
|
||||
* @return {Object} Uploads model instance
|
||||
*/
|
||||
init(appconfig) {
|
||||
|
||||
this._uploadsPath = path.resolve(ROOTPATH, appconfig.paths.repo, 'uploads');
|
||||
this._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.paths.data, 'thumbs');
|
||||
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the thumbnails folder path.
|
||||
*
|
||||
* @return {String} The thumbs path.
|
||||
*/
|
||||
getThumbsPath() {
|
||||
return this._uploadsThumbsPath;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the uploads folders.
|
||||
*
|
||||
* @param {Array<String>} arrFolders The arr folders
|
||||
* @return {Void} Void
|
||||
*/
|
||||
setUploadsFolders(arrFolders) {
|
||||
|
||||
this._uploadsFolders = arrFolders;
|
||||
return;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the uploads folders.
|
||||
*
|
||||
* @return {Array<String>} The uploads folders.
|
||||
*/
|
||||
getUploadsFolders() {
|
||||
return this._uploadsFolders;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates an uploads folder.
|
||||
*
|
||||
* @param {String} folderName The folder name
|
||||
* @return {Promise} Promise of the operation
|
||||
*/
|
||||
createUploadsFolder(folderName) {
|
||||
|
||||
let self = this;
|
||||
|
||||
folderName = _.kebabCase(_.trim(folderName));
|
||||
|
||||
if(_.isEmpty(folderName) || !regFolderName.test(folderName)) {
|
||||
return Promise.resolve(self.getUploadsFolders());
|
||||
}
|
||||
|
||||
return fs.ensureDirAsync(path.join(self._uploadsPath, folderName)).then(() => {
|
||||
if(!_.includes(self._uploadsFolders, folderName)) {
|
||||
self._uploadsFolders.push(folderName);
|
||||
self._uploadsFolders = _.sortBy(self._uploadsFolders);
|
||||
}
|
||||
return self.getUploadsFolders();
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if folder is valid and exists
|
||||
*
|
||||
* @param {String} folderName The folder name
|
||||
* @return {Boolean} True if valid
|
||||
*/
|
||||
validateUploadsFolder(folderName) {
|
||||
|
||||
if(_.includes(this._uploadsFolders, folderName)) {
|
||||
return path.resolve(this._uploadsPath, folderName);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the uploads files.
|
||||
*
|
||||
* @param {Array<Object>} arrFiles The uploads files
|
||||
* @return {Void} Void
|
||||
*/
|
||||
setUploadsFiles(arrFiles) {
|
||||
|
||||
let self = this;
|
||||
|
||||
/*if(_.isArray(arrFiles) && arrFiles.length > 0) {
|
||||
self._uploadsDb.Files.clear();
|
||||
self._uploadsDb.Files.insert(arrFiles);
|
||||
self._uploadsDb.Files.ensureIndex('category', true);
|
||||
self._uploadsDb.Files.ensureIndex('folder', true);
|
||||
}*/
|
||||
|
||||
return;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds one or more uploads files.
|
||||
*
|
||||
* @param {Array<Object>} arrFiles The uploads files
|
||||
* @return {Void} Void
|
||||
*/
|
||||
addUploadsFiles(arrFiles) {
|
||||
if(_.isArray(arrFiles) || _.isPlainObject(arrFiles)) {
|
||||
//this._uploadsDb.Files.insert(arrFiles);
|
||||
}
|
||||
return;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the uploads files.
|
||||
*
|
||||
* @param {String} cat Category type
|
||||
* @param {String} fld Folder
|
||||
* @return {Array<Object>} The files matching the query
|
||||
*/
|
||||
getUploadsFiles(cat, fld) {
|
||||
|
||||
return /*this._uploadsDb.Files.chain().find({
|
||||
'$and': [{ 'category' : cat },{ 'folder' : fld }]
|
||||
}).simplesort('filename').data()*/;
|
||||
|
||||
},
|
||||
|
||||
deleteUploadsFile(fldName, f) {
|
||||
|
||||
}
|
||||
|
||||
};
|
Loading…
Reference in new issue