Commit 9da15d4d32b6bad76641d7d3f5b2d8b8fadc728e

Authored by Adhidarma Hadiwinoto
1 parent 3cb69228e8
Exists in master

pause, resume, terminate

Showing 4 changed files with 109 additions and 2 deletions Inline Diff

1 var express = require('express'); 1 var express = require('express');
2 var app = express(); 2 var app = express();
3 var nunjucks = require('nunjucks'); 3 var nunjucks = require('nunjucks');
4 var passport = require('passport'); 4 var passport = require('passport');
5 var LocalStrategy = require('passport-local').Strategy; 5 var LocalStrategy = require('passport-local').Strategy;
6 var crypto = require('crypto'); 6 var crypto = require('crypto');
7 //var sha256sum = crypto.createHash('sha256'); 7 //var sha256sum = crypto.createHash('sha256');
8 var fsextra = require('fs-extra'); 8 var fsextra = require('fs-extra');
9 var strftime = require('strftime'); 9 var strftime = require('strftime');
10 var fs = require('fs'); 10 var fs = require('fs');
11 var ini = require('ini'); 11 var ini = require('ini');
12 var os = require('os'); 12 var os = require('os');
13 13
14 var config; 14 var config;
15 var aaa; 15 var aaa;
16 var matrix; 16 var matrix;
17 var view_path = __dirname + '/views'; 17 var view_path = __dirname + '/views';
18 18
19 function loggedIn(req, res, next) { 19 function loggedIn(req, res, next) {
20 if (req.user) { 20 if (req.user) {
21 next(); 21 next();
22 } else { 22 } else {
23 res.redirect('/login'); 23 res.redirect('/login');
24 } 24 }
25 } 25 }
26 26
27 function matchedPasswordAndHash(password, hash) { 27 function matchedPasswordAndHash(password, hash) {
28 var sha256sum = crypto.createHash('sha256'); 28 var sha256sum = crypto.createHash('sha256');
29 sha256sum.update(password); 29 sha256sum.update(password);
30 return (hash == sha256sum.digest('hex')) 30 return (hash == sha256sum.digest('hex'))
31 } 31 }
32 32
33 function renderDashboard(req, res) { 33 function renderDashboard(req, res) {
34 res.render('dashboard.html', { 34 res.render('dashboard.html', {
35 gateway_name: config.globals.gateway_name, 35 gateway_name: config.globals.gateway_name,
36 title: 'Dashboard', 36 title: 'Dashboard',
37 config: config, 37 config: config,
38 }); 38 });
39 } 39 }
40 40
41 function renderConfigIndex(req, res) { 41 function renderConfigIndex(req, res) {
42 42
43 masked = ['config.expresso.password']; 43 masked = ['config.expresso.password'];
44 44
45 res.render('config.index.html', { 45 res.render('config.index.html', {
46 gateway_name: config.globals.gateway_name, 46 gateway_name: config.globals.gateway_name,
47 title: 'CONFIG', configs: config, 47 title: 'CONFIG', configs: config,
48 config_dirty: matrix.config_dirty, 48 config_dirty: matrix.config_dirty,
49 isMask: function(scope, keyword) { 49 isMask: function(scope, keyword) {
50 return masked.indexOf('config.' + scope + '.' + keyword) >= 0; 50 return masked.indexOf('config.' + scope + '.' + keyword) >= 0;
51 }, 51 },
52 }); 52 });
53 } 53 }
54 54
55 function renderConfigEdit(req, res) { 55 function renderConfigEdit(req, res) {
56 var template = "config.edit.html"; 56 var template = "config.edit.html";
57 57
58 if (req.scope == 'expresso' && req.keyword == 'password') { 58 if (req.scope == 'expresso' && req.keyword == 'password') {
59 template = "config.edit.expresso.password.html"; 59 template = "config.edit.expresso.password.html";
60 } 60 }
61 61
62 res.render(template, { 62 res.render(template, {
63 gateway_name: config.globals.gateway_name, 63 gateway_name: config.globals.gateway_name,
64 title: 'Edit Konfigurasi', 64 title: 'Edit Konfigurasi',
65 scope: req.scope, 65 scope: req.scope,
66 keyword: req.keyword, 66 keyword: req.keyword,
67 old_value: config[req.scope][req.keyword] 67 old_value: config[req.scope][req.keyword]
68 }); 68 });
69 } 69 }
70 70
71 function renderConfigAdd(req, res){ 71 function renderConfigAdd(req, res){
72 72
73 var template = 'config.add.html'; 73 var template = 'config.add.html';
74 if (req.scope == 'products') { 74 if (req.scope == 'products') {
75 template = 'config.add.products.html'; 75 template = 'config.add.products.html';
76 } 76 }
77 77
78 res.render(template, { 78 res.render(template, {
79 gateway_name: config.globals.gateway_name, 79 gateway_name: config.globals.gateway_name,
80 title: 'Tambah Item Konfigurasi', 80 title: 'Tambah Item Konfigurasi',
81 scope: req.scope 81 scope: req.scope
82 }); 82 });
83 } 83 }
84 84
85 function submitPause(req, res) { 85 function submitPause(req, res) {
86 matrix.pause = 1; 86 matrix.pause = 1;
87 res.redirect('/'); 87 res.redirect('/');
88 return; 88 return;
89 } 89 }
90 90
91 function submitResume(req, res) { 91 function submitResume(req, res) {
92 matrix.pause = 0; 92 matrix.pause = 0;
93 res.redirect('/'); 93 res.redirect('/');
94 return; 94 return;
95 } 95 }
96 96
97 function submitTerminate(req, res) { 97 function submitTerminate(req, res) {
98 res.end('Terminating....'); 98 res.end('Terminating....', function() {
99 process.exit(); 99 setTimeout(process.exit, 2 * 1000);
100 });
100 } 101 }
101 102
102 function submitConfigEdit(req, res) { 103 function submitConfigEdit(req, res) {
103 104
104 if (req.scope == 'expresso' && req.keyword == 'password') { 105 if (req.scope == 'expresso' && req.keyword == 'password') {
105 if (req.body.newValue != req.body.newValue2) { 106 if (req.body.newValue != req.body.newValue2) {
106 res.redirect('/config/edit/' + req.scope + '/' + req.keyword); 107 res.redirect('/config/edit/' + req.scope + '/' + req.keyword);
107 return; 108 return;
108 } 109 }
109 110
110 var sha256sum = crypto.createHash('sha256'); 111 var sha256sum = crypto.createHash('sha256');
111 sha256sum.update(req.body.newValue); 112 sha256sum.update(req.body.newValue);
112 req.body.newValue = sha256sum.digest('hex'); 113 req.body.newValue = sha256sum.digest('hex');
113 114
114 } else if (req.scope == 'globals' && req.keyword == 'products' && req.body.newValue.trim()) { 115 } else if (req.scope == 'globals' && req.keyword == 'products' && req.body.newValue.trim()) {
115 116
116 try { 117 try {
117 var unsortedString = req.body.newValue.toUpperCase().replace(/ /g, '').trim(); 118 var unsortedString = req.body.newValue.toUpperCase().replace(/ /g, '').trim();
118 var unsortedProducts = unsortedString.split(','); 119 var unsortedProducts = unsortedString.split(',');
119 var sortedProducts = aaa.sortProductsArray(unsortedProducts); 120 var sortedProducts = aaa.sortProductsArray(unsortedProducts);
120 req.body.newValue = sortedProducts.join(','); 121 req.body.newValue = sortedProducts.join(',');
121 } 122 }
122 catch(e) { 123 catch(e) {
123 console.log('Error sorting products'); 124 console.log('Error sorting products');
124 } 125 }
125 } 126 }
126 127
127 if (config[req.body.scope][req.body.keyword] != req.body.newValue.trim()) { 128 if (config[req.body.scope][req.body.keyword] != req.body.newValue.trim()) {
128 config[req.body.scope][req.body.keyword] = req.body.newValue.trim(); 129 config[req.body.scope][req.body.keyword] = req.body.newValue.trim();
129 matrix.config_dirty = 1; 130 matrix.config_dirty = 1;
130 } 131 }
131 132
132 res.redirect('/config'); 133 res.redirect('/config');
133 } 134 }
134 135
135 function submitConfigAdd(req, res) { 136 function submitConfigAdd(req, res) {
136 137
137 if (!req.body.newKeyword.trim()) { 138 if (!req.body.newKeyword.trim()) {
138 res.redirect('/config'); 139 res.redirect('/config');
139 return; 140 return;
140 } 141 }
141 142
142 if (config[req.body.scope] === undefined) { 143 if (config[req.body.scope] === undefined) {
143 config[req.body.scope] = {}; 144 config[req.body.scope] = {};
144 } 145 }
145 146
146 config[req.body.scope][req.body.newKeyword.trim()] = req.body.newValue.trim(); 147 config[req.body.scope][req.body.newKeyword.trim()] = req.body.newValue.trim();
147 matrix.config_dirty = 1; 148 matrix.config_dirty = 1;
148 149
149 res.redirect('/config#config.' + req.body.scope + '.' + req.body.newKeyword); 150 res.redirect('/config#config.' + req.body.scope + '.' + req.body.newKeyword);
150 } 151 }
151 152
152 function submitConfigDelete(req, res) { 153 function submitConfigDelete(req, res) {
153 154
154 matrix.config_dirty = 1; 155 matrix.config_dirty = 1;
155 delete config[req.scope][req.keyword]; 156 delete config[req.scope][req.keyword];
156 157
157 res.redirect('/config'); 158 res.redirect('/config');
158 } 159 }
159 160
160 function submitConfigSave(req, res) { 161 function submitConfigSave(req, res) {
161 fsextra.copy('config.ini', 'config.ini.backup_' + strftime('%F_%H%M%S', new Date()), function(err) { 162 fsextra.copy('config.ini', 'config.ini.backup_' + strftime('%F_%H%M%S', new Date()), function(err) {
162 fs.writeFileSync('./config.ini', ini.stringify(config)); 163 fs.writeFileSync('./config.ini', ini.stringify(config));
163 matrix.config_dirty = 0; 164 matrix.config_dirty = 0;
164 res.redirect('/config'); 165 res.redirect('/config');
165 }); 166 });
166 } 167 }
167 168
168 function renderLoginPage(req, res) { 169 function renderLoginPage(req, res) {
169 res.render('signin.html', {title: config.globals.gateway_name}); 170 res.render('signin.html', {title: config.globals.gateway_name});
170 } 171 }
171 172
172 function renderConfigAskDelete(req, res) { 173 function renderConfigAskDelete(req, res) {
173 res.render('config.ask.delete.html', { 174 res.render('config.ask.delete.html', {
174 gateway_name: config.globals.gateway_name, 175 gateway_name: config.globals.gateway_name,
175 title: 'Konfirmasi Penghapusan', 176 title: 'Konfirmasi Penghapusan',
176 scope: req.scope, 177 scope: req.scope,
177 keyword: req.keyword, 178 keyword: req.keyword,
178 value: config[req.scope][req.keyword], 179 value: config[req.scope][req.keyword],
179 }); 180 });
180 } 181 }
181 182
183 function renderAskTerminate(req, res) {
184 res.render('ask.terminate.html', {
185 gateway_name: config.globals.gateway_name,
186 title: 'Konfirmasi Terminasi'
187 });
188 }
189
190
182 function renderRuntimeInfo(req, res) { 191 function renderRuntimeInfo(req, res) {
183 var template = "runtime-info.html"; 192 var template = "runtime-info.html";
184 193
185 var os_info = { 194 var os_info = {
186 uptime: os.uptime(), 195 uptime: os.uptime(),
187 loadavg: os.loadavg(), 196 loadavg: os.loadavg(),
188 hostname: os.hostname(), 197 hostname: os.hostname(),
189 type: os.type(), 198 type: os.type(),
190 platform: os.platform(), 199 platform: os.platform(),
191 arch: os.arch(), 200 arch: os.arch(),
192 release: os.release(), 201 release: os.release(),
193 totalmem: os.totalmem(), 202 totalmem: os.totalmem(),
194 } 203 }
195 204
196 var pendings; 205 var pendings;
197 try { 206 try {
198 pendings = JSON.stringify(aaa.getPendingList(), null, 2); 207 pendings = JSON.stringify(aaa.getPendingList(), null, 2);
199 } 208 }
200 catch(err) { 209 catch(err) {
201 pendings = "{}"; 210 pendings = "{}";
202 } 211 }
203 212
204 res.render(template, { 213 res.render(template, {
205 gateway_name: config.globals.gateway_name, 214 gateway_name: config.globals.gateway_name,
206 title: 'Runtime Info', 215 title: 'Runtime Info',
207 matrix: JSON.stringify(matrix, null, 2), 216 matrix: JSON.stringify(matrix, null, 2),
208 config: JSON.stringify(config, null, 2), 217 config: JSON.stringify(config, null, 2),
209 nodejs_versions: JSON.stringify(process.versions, null, 2), 218 nodejs_versions: JSON.stringify(process.versions, null, 2),
210 pendings: pendings, 219 pendings: pendings,
211 memory_usage: JSON.stringify(process.memoryUsage(), null, 2), 220 memory_usage: JSON.stringify(process.memoryUsage(), null, 2),
212 uptime: process.uptime(), 221 uptime: process.uptime(),
213 os_info: JSON.stringify(os_info, null, 2), 222 os_info: JSON.stringify(os_info, null, 2),
214 net_ifaces: JSON.stringify(os.networkInterfaces(), null, 2), 223 net_ifaces: JSON.stringify(os.networkInterfaces(), null, 2),
215 }); 224 });
216 } 225 }
217 226
218 function isNoTemplateCache() { 227 function isNoTemplateCache() {
219 var retval = false; 228 var retval = false;
220 try { 229 try {
221 retval = config.expresso.no_template_cache.toUpperCase() == 'YES'; 230 retval = config.expresso.no_template_cache.toUpperCase() == 'YES';
222 } 231 }
223 catch(err) { 232 catch(err) {
224 return retval; 233 return retval;
225 } 234 }
226 235
227 return retval; 236 return retval;
228 } 237 }
229 238
230 function createServer() { 239 function createServer() {
231 if (!config.expresso || !config.expresso.listen_port) { 240 if (!config.expresso || !config.expresso.listen_port) {
232 console.log('Not starting expresso admin UI'); 241 console.log('Not starting expresso admin UI');
233 return; 242 return;
234 } 243 }
235 244
236 nunjucks.configure(view_path, { 245 nunjucks.configure(view_path, {
237 autoescape: true, 246 autoescape: true,
238 noCache: isNoTemplateCache(), 247 noCache: isNoTemplateCache(),
239 express: app 248 express: app
240 }); 249 });
241 250
242 app.use(express.static(__dirname + '/public')); 251 app.use(express.static(__dirname + '/public'));
243 app.use(require('cookie-parser')()); 252 app.use(require('cookie-parser')());
244 app.use(require('body-parser').urlencoded({ extended: true })); 253 app.use(require('body-parser').urlencoded({ extended: true }));
245 254
246 var express_session_opts = { 255 var express_session_opts = {
247 secret: 'keyboard cat', 256 secret: 'keyboard cat',
248 resave: false, 257 resave: false,
249 saveUninitialized: false 258 saveUninitialized: false
250 }; 259 };
251 260
252 if (config.expresso && config.expresso.session_name) { 261 if (config.expresso && config.expresso.session_name) {
253 express_session_opts.name = config.expresso.session_name; 262 express_session_opts.name = config.expresso.session_name;
254 } 263 }
255 264
256 app.use(require('express-session')(express_session_opts)); 265 app.use(require('express-session')(express_session_opts));
257 app.use(passport.initialize()); 266 app.use(passport.initialize());
258 app.use(passport.session()); 267 app.use(passport.session());
259 268
260 passport.use(new LocalStrategy( 269 passport.use(new LocalStrategy(
261 function(username, password, done) { 270 function(username, password, done) {
262 271
263 if (username == 'admin' && matchedPasswordAndHash(password, config.expresso.password)) { 272 if (username == 'admin' && matchedPasswordAndHash(password, config.expresso.password)) {
264 var user = { 273 var user = {
265 username: username, 274 username: username,
266 last_login: Date.now() / 1000 | 0 275 last_login: Date.now() / 1000 | 0
267 } 276 }
268 277
269 return done(null, user); 278 return done(null, user);
270 } 279 }
271 280
272 return done(null, false, { message: 'Incorrect password.' }); 281 return done(null, false, { message: 'Incorrect password.' });
273 } 282 }
274 )); 283 ));
275 284
276 passport.serializeUser(function(user, done) { 285 passport.serializeUser(function(user, done) {
277 done(null, user.username); 286 done(null, user.username);
278 }); 287 });
279 288
280 passport.deserializeUser(function(username, done) { 289 passport.deserializeUser(function(username, done) {
281 var user = { 290 var user = {
282 username: username 291 username: username
283 }; 292 };
284 done(null, user); 293 done(null, user);
285 }); 294 });
286 295
287 app.get('/', loggedIn, function(req, res) { res.redirect('/runtime-info'); }); 296 app.get('/', loggedIn, function(req, res) { res.redirect('/runtime-info'); });
288 app.get('/dashboard', loggedIn, renderDashboard); 297 app.get('/dashboard', loggedIn, renderDashboard);
289 app.get('/config', loggedIn, renderConfigIndex); 298 app.get('/config', loggedIn, renderConfigIndex);
290 app.get('/login', renderLoginPage); 299 app.get('/login', renderLoginPage);
291 app.post('/login', passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' })); 300 app.post('/login', passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' }));
292 app.get('/logout', function(req, res){ 301 app.get('/logout', function(req, res){
293 req.logout(); 302 req.logout();
294 res.redirect('/login'); 303 res.redirect('/login');
295 }); 304 });
296 305
297 app.param('scope', function(req, res, next, value) { 306 app.param('scope', function(req, res, next, value) {
298 req.scope = value; 307 req.scope = value;
299 next(); 308 next();
300 }); 309 });
301 310
302 app.param('keyword', function(req, res, next, value) { 311 app.param('keyword', function(req, res, next, value) {
303 req.keyword = value; 312 req.keyword = value;
304 next(); 313 next();
305 }); 314 });
306 315
307 app.get('/config/edit/:scope/:keyword', loggedIn, renderConfigEdit); 316 app.get('/config/edit/:scope/:keyword', loggedIn, renderConfigEdit);
308 app.post('/config/edit/:scope/:keyword', loggedIn, submitConfigEdit); 317 app.post('/config/edit/:scope/:keyword', loggedIn, submitConfigEdit);
309 318
310 app.get('/config/ask-delete/:scope/:keyword', loggedIn, renderConfigAskDelete); 319 app.get('/config/ask-delete/:scope/:keyword', loggedIn, renderConfigAskDelete);
311 app.get('/config/delete/:scope/:keyword', loggedIn, submitConfigDelete); 320 app.get('/config/delete/:scope/:keyword', loggedIn, submitConfigDelete);
312 321
313 app.get('/config/add/:scope', loggedIn, renderConfigAdd); 322 app.get('/config/add/:scope', loggedIn, renderConfigAdd);
314 app.post('/config/add/:scope', loggedIn, submitConfigAdd); 323 app.post('/config/add/:scope', loggedIn, submitConfigAdd);
315 324
316 app.get('/config/save', loggedIn, submitConfigSave); 325 app.get('/config/save', loggedIn, submitConfigSave);
317 326
318 app.get('/pause', loggedIn, submitPause); 327 app.get('/pause', loggedIn, submitPause);
319 app.get('/resume', loggedIn, submitResume); 328 app.get('/resume', loggedIn, submitResume);
320 app.get('/terminate', loggedIn, submitTerminate); 329 app.get('/terminate', loggedIn, submitTerminate);
330 app.get('/ask-terminate', loggedIn, renderAskTerminate);
321 331
322 app.get('/runtime-info', loggedIn, renderRuntimeInfo); 332 app.get('/runtime-info', loggedIn, renderRuntimeInfo);
323 333
324 var server = app.listen(config.expresso.listen_port, function () { 334 var server = app.listen(config.expresso.listen_port, function () {
325 var host = server.address().address; 335 var host = server.address().address;
326 var port = server.address().port; 336 var port = server.address().port;
327 337
328 console.log('Expresso admin UI listening at http://%s:%s', host, port); 338 console.log('Expresso admin UI listening at http://%s:%s', host, port);
329 }); 339 });
330 } 340 }
331 341
332 function sanitizeMatrix(matrix) { 342 function sanitizeMatrix(matrix) {
333 if (matrix === undefined) { 343 if (matrix === undefined) {
334 matrix = {}; 344 matrix = {};
335 } 345 }
336 if (matrix.config_dirty === undefined) { 346 if (matrix.config_dirty === undefined) {
337 matrix.config_dirty = 0; 347 matrix.config_dirty = 0;
338 } 348 }
339 } 349 }
340 350
341 function start(options) { 351 function start(options) {
342 if (options['config']) { 352 if (options['config']) {
343 config = options['config']; 353 config = options['config'];
344 } 354 }
345 355
346 if (options['matrix']) { 356 if (options['matrix']) {
347 matrix = options['matrix']; 357 matrix = options['matrix'];
348 } 358 }
349 sanitizeMatrix(matrix); 359 sanitizeMatrix(matrix);
350 360
351 if (options['aaa']) { 361 if (options['aaa']) {
352 aaa = options['aaa']; 362 aaa = options['aaa'];
353 } 363 }
354 364
355 if (options['expresso_views']) { 365 if (options['expresso_views']) {
356 view_path = options['expresso_views']; 366 view_path = options['expresso_views'];
357 } 367 }
358 368
359 createServer(); 369 createServer();
360 } 370 }
361 371
362 exports.start = start; 372 exports.start = start;
363 373
views/ask.terminate.html
File was created 1 {% extends "layout-with-topbar.html" %}
2
3 {% block content %}
4
5 Yakin ingin terminasi gateway?
6
7 <br/><br/>
8
9 <a class="btn btn-danger" href="/terminate" role="button">
10 Terminasi</a>
11
12 <a class="btn btn-default" href="/dashboard" role="button">
13 Batal</a>
14
15 {% endblock %}
16
views/dashboard.html
File was created 1 {% extends "layout-with-topbar.html" %}
2
3 {% block content %}
4
5 <div id="paused-or-running" class="panel panel-default">
6
7 <div class="panel-heading">
8 <h3>Pause and Resume</h3>
9 </div>
10
11 <div class="panel-body">
12 {% if config.globals.pause %}
13
14
15 <p> System paused. Click the button to resume it.</p>
16
17 <p>
18 <a href="/resume" class="btn btn-primary">Resume</a>
19 </p>
20
21
22 {% else %}
23
24 <p> System is running. Click the button to pause it</p>
25
26 <div class="pull-right">
27 <a href="/pause" class="btn btn-primary">Pause</a>
28 </div>
29
30 {% endif %}
31 </div>
32
33 </div>
34
35 <div id="terminate" class="panel panel-default">
36
37 <div class="panel-heading">
38 <h3>Process Termination</h3>
39 </div>
40
41 <div class="panel-body">
42 <p>Click the button to terminate (or restart) the gateway.</p>
43
44 <div class="pull-right">
45 <a href="/ask-terminate" class="btn btn-danger">Terminate / Restart</a>
46 </div>
47 </div>
48
49 </div>
50
51 {% endblock %}
52
views/runtime-info.html
File was created 1 {% extends "layout-with-topbar.html" %}
2
3 {% block content %}
4
5 <p>
6 Process uptime: {{ uptime }} seconds
7 </p>
8
9 <h2 id="matrix">The Matrix</h2>
10 <pre>{{ matrix }}</pre>
11
12 <h2 id="pendings">Pending Transactions</h2>
13 <pre>{{ pendings }}</pre>
14
15
16 <h2 id="config">Config</h2>
17 <pre>{{ config }}</pre>
18
19 <h2 id="memory">Memory Usage</h2>
20 <pre>{{ memory_usage }}</pre>
21
22 <h2 id="nodejs_versions">Node.js Versions</h2>
23 <pre>{{ nodejs_versions }}</pre>
24
25 <h2 id="os_info">OS</h2>
26 <pre>{{ os_info }}</pre>
27
28 <h2 id="net_iface">Network Interfaces</h2>
29 <pre>{{ net_ifaces }}</pre>
30
31 {% endblock %}
32