Issues (208)

server/config.js (13 issues)

1
var fs      = require('fs');
2
var extend  = require('extend');
3
var express = require('express');
4
var request = require('request');
5
6
// ------------------------------------------
7
//  CONSTRUCTOR
8
// ------------------------------------------
9
10
var init = function(){
11
  info('Starting ConfigManager ...');
12
  
13
  // Load properties
14
  load();
15
  
16
  // Configure proxy
17
  if (Config.http.proxy){
18
    request.defaults({'proxy': Config.http.proxy})
19
  }
20
  
21
  // Expose properties to global
22
  global.Config = Config;
23
  
24
  return ConfigManager;
25
}
26
27
// ------------------------------------------
28
//  CONFIG
29
// ------------------------------------------
30
var path   = require('path');
31
var ROOT   = path.normalize(__dirname+'/..');
0 ignored issues
show
Consider using the path module for constructing paths since they are otherwise not cross-OS compatible.
Loading history...
32
33
var SERVER = path.normalize(ROOT+'/server/server.prop');
34
var PLUGIN = process.env.PLUGINS_PATH || path.normalize(ROOT+'/../../plugins');
35
var VIEW   = path.normalize(ROOT+'/webapp/views');
36
var CUSTOM = path.normalize(ROOT+'/data/custom.prop');
37
38
info('ROOT', ROOT, '\nSERVER', SERVER, '\nPLUGIN', PLUGIN);
39
40
info('NODE_PATH', module.paths);
41
42
var Config = { 'debug' : false };
43
var load = function(){
44
  try { 
45
    extend(true, Config, loadProperties());
46
    extend(true, Config, loadPlugins());
47
    extend(true, Config, loadCustoms());
48
    Config.bot.version = "4.0";
49
  } 
50
  catch(ex) { error('Error while loading properties: %s', ex.message);  }
51
  return ConfigManager;
52
}
53
54
/**
55
 * Load default properties
56
 */
57
var loadProperties = function(){
58
  if (!fs.existsSync(SERVER)) { return {}; }
59
  info('Loading server properties...', SERVER);
60
  var json = fs.readFileSync(SERVER, 'utf8');
61
  return JSON.parse(json);
62
}
63
64
/**
65
 * Load plugin properties recursively
66
 */
67
var loadPlugins = function(folder, json){ 
68
  var json   = json   || {};
69
  var folder = folder || PLUGIN;
70
71
  if (!fs.existsSync(folder)) { return json; }  
72
  fs.readdirSync(folder).forEach(function(file){
73
    var path = folder+'/'+file;
74
    
75
    // Directory
76
    if (fs.statSync(path).isDirectory()){
77
      loadPlugins(path, json);
78
      return json;
79
    }
80
    
81
    // Ends with .prop
82
    if (file.endsWith('.prop')){
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if file.endsWith(".prop") is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
83
      info('Loading plugin properties... %s', path);
84
      try {
85
        var load   =  fs.readFileSync(path,'utf8');
86
        var plugin = JSON.parse(load);
87
        extend(true, json, plugin);
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
88
      } catch(ex){ error('Error in %s: %s', file, ex.message); }
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
89
    }
90
  });
91
  return json;
92
}
93
94
/**
95
 * Load custom properties
96
 */
97
var loadCustoms = function(){
98
  if (!fs.existsSync(CUSTOM)) { warn("Can't load custom properties !", CUSTOM); return {}; }
99
  info('Loading custom properties...', CUSTOM);
100
  
101
  var load = fs.readFileSync(CUSTOM,'utf8');
102
  var json = {}; 
0 ignored issues
show
The assignment to variable json seems to be never used. Consider removing it.
Loading history...
103
  try { json = JSON.parse(load); } catch (ex){ error('Error in custom.prop: %s', ex.message); }
104
  
105
  json['modules']  = retains(json['modules'],  Config['modules']);
106
  json['phantoms'] = retains(json['phantoms'], Config['phantoms']);
107
  json['cron']     = retains(json['cron'],     Config['cron']);
108
109
  return json;
110
}
111
112
var retains = function(source, target){
113
  if (typeof source != 'object') return source;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
114
  
115
  var clean  = {};
116
  Object.keys(source).forEach(function(attr){
117
    if (attr == 'description' || attr == 'version'){ return false; }
118
    if (target[attr] === undefined
119
        && attr != 'x' && attr != 'y' 
120
        && attr != 'w' && attr != 'h'
121
        && attr != 'c' && attr != 'disabled'){ return warn('Skip config: %s', attr); }
122
    clean[attr] = retains(source[attr], target[attr]);
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
123
  });
124
  
125
  return clean;
126
}
127
128
/**
129
 * Load plugin properties file
130
 */
131
var loadJSON = function(name){
132
  var path = PLUGIN+'/'+name+'/'+name+'.prop';
133
  if (!fs.existsSync(path)){ return {}; }
134
  info('Loading plugin properties... %s', path);
135
  try {
136
    var json = fs.readFileSync(path,'utf8');
137
    return JSON.parse(json);
138
  } catch(ex){ error('Error in %s: %s', name+'.prop', ex.message); }
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
139
}
140
141
// ------------------------------------------
142
//  SAVE
143
// ------------------------------------------
144
145
var setProperty = function(keys, value, json){
146
  var config = json || Config;
147
  var keys = keys.split('.');
148
  for (var k in keys){
0 ignored issues
show
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
149
    var key = keys[k];
150
    if (typeof config[key] === 'object' ){
151
      config = config[key];
152
    } else {
153
      config[key] = value;
154
    }
155
  }
156
}
157
158
var save = function(cfg) {
159
  try {
160
    Config = cfg || Config;
161
    var json = JSON.stringify(Config, undefined, 2);
162
163
    //json = json.replace(/\{/g,"{\n  ").replace(/\}/g,"\n  }").replace(/,/g,",\n  ");
164
    fs.writeFileSync(CUSTOM, json, 'utf8');
165
    info('Properties saved successfully');
166
  } catch(ex) {
167
    error('Error while saving properties: %s', ex.message);
168
  }
169
}
170
171
// ------------------------------------------
172
//  ROUTER
173
// ------------------------------------------
174
175
176
var Router = express.Router();
177
178
Router.get('/portal/config', function(req, res, next) {
0 ignored issues
show
The parameter next is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
179
  res.render('portal/config.ejs');
180
});
181
182
Router.post('/portal/config', function(req, res, next) {
0 ignored issues
show
The parameter next is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
183
  var keys    = Object.keys(req.body);
184
  for(var i   = 0 ; i < keys.length ; i++){
185
    var key   = keys[i];
186
    var value = Helper.parse(req.body[key]);
0 ignored issues
show
The variable Helper seems to be never declared. If this is a global, consider adding a /** global: Helper */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
187
    setProperty(key, value);
188
  }
189
  SARAH.ConfigManager.save();
0 ignored issues
show
The variable SARAH seems to be never declared. If this is a global, consider adding a /** global: SARAH */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
190
  res.redirect('/portal/config');
191
});
192
193
// ------------------------------------------
194
//  PUBLIC
195
// ------------------------------------------
196
197
var ConfigManager = {
198
  'init'   : init,
199
  'load'   : load,
200
  'save'   : save,
201
  
202
  'loadJSON' : loadJSON,
203
  'getConfig': function(){ warn('getConfig is deprecated for SARAH 4.x, use global Config'); return Config; },
204
  'Router' : Router,
205
  'Config' : Config,
206
  'PLUGIN' : PLUGIN,
207
  'ROOT'   : ROOT,
208
  'VIEW'   : VIEW
209
}
210
211
// Exports Manager
212
exports.init = ConfigManager.init;