profoundjs-swagger-stats 1.1.4 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -0
- package/dashboards/prometheus/swagger-stats dashboard v3.json +1269 -0
- package/examples/authtest/authtest.js +5 -10
- package/examples/fastify/fasifytest.js +24 -5
- package/examples/hapijstest/hapijstest.js +17 -2
- package/examples/restify/restifytest.js +16 -1
- package/examples/spectest/spectest.js +7 -9
- package/examples/testapp/petstore.json +1699 -0
- package/examples/testapp/testapp.js +6 -62
- package/lib/sws-api-swagger.yaml +1 -1
- package/lib/swsAPIStats.js +6 -1
- package/lib/swsAuth.js +140 -0
- package/lib/swsElasticEmitter.js +103 -45
- package/lib/swsHapi.js +29 -34
- package/lib/swsInterface.js +40 -189
- package/lib/swsProcessor.js +6 -1
- package/lib/swsReqResStats.js +1 -1
- package/lib/swsUtil.js +7 -21
- package/lib/swssettings.js +8 -2
- package/package.json +90 -91
- package/schema/elasticsearch/api_index_template.json +1 -1
- package/schema/elasticsearch/api_index_template_7x.json +185 -0
- package/scripts/elasticsearch/elastic7/docker-compose.yml +6 -17
- package/scripts/elasticsearch/elastic8/docker-compose.yml +34 -0
- package/ux/css/app.dc22c4da.css +7 -0
- package/ux/css/chunk-vendors.a6da6d7a.css +10 -0
- package/ux/fonts/KFOkCnqEu92Fr1MmgVxIIzQ.4aa2e698.woff +0 -0
- package/ux/fonts/KFOlCnqEu92Fr1MmEU9fBBc-.40bcb2b8.woff +0 -0
- package/ux/fonts/KFOlCnqEu92Fr1MmSU5fBBc-.ea60988b.woff +0 -0
- package/ux/fonts/KFOlCnqEu92Fr1MmWUlfBBc-.0774a8b7.woff +0 -0
- package/ux/fonts/KFOlCnqEu92Fr1MmYUtfBBc-.bcb7c7e2.woff +0 -0
- package/ux/fonts/KFOmCnqEu92Fr1Mu4mxM.d3907d0c.woff +0 -0
- package/ux/fonts/fa-brands-400.1a575a41.woff +0 -0
- package/ux/fonts/fa-brands-400.ed311c7a.woff2 +0 -0
- package/ux/fonts/fa-regular-400.b91d376b.woff2 +0 -0
- package/ux/fonts/fa-regular-400.d1d7e3b4.woff +0 -0
- package/ux/fonts/fa-solid-900.d745348d.woff +0 -0
- package/ux/fonts/fa-solid-900.d824df7e.woff2 +0 -0
- package/ux/fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNa.3e1afe59.woff +0 -0
- package/ux/fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.a4160421.woff2 +0 -0
- package/ux/index.html +1 -1
- package/ux/js/app.d095d0d6.js +1 -0
- package/ux/js/chunk-2d0b90b4.afe783bc.js +8 -0
- package/ux/js/chunk-2d0daf1e.a05fda31.js +11 -0
- package/ux/js/chunk-461883cd.a2aa4876.js +82 -0
- package/ux/js/chunk-vendors.4f167dc9.js +39 -0
- package/ux/logo.png +0 -0
- package/.editorconfig +0 -21
- package/.travis.yml +0 -23
- package/dashboards/prometheus/swagger-stats dashboard.json +0 -1118
- package/dist/css/sws.min.css +0 -18
- package/dist/fonts/FontAwesome.otf +0 -0
- package/dist/fonts/fontawesome-webfont.eot +0 -0
- package/dist/fonts/fontawesome-webfont.svg +0 -2671
- package/dist/fonts/fontawesome-webfont.ttf +0 -0
- package/dist/fonts/fontawesome-webfont.woff +0 -0
- package/dist/fonts/fontawesome-webfont.woff2 +0 -0
- package/dist/fonts/glyphicons-halflings-regular.eot +0 -0
- package/dist/fonts/glyphicons-halflings-regular.svg +0 -288
- package/dist/fonts/glyphicons-halflings-regular.ttf +0 -0
- package/dist/fonts/glyphicons-halflings-regular.woff +0 -0
- package/dist/fonts/glyphicons-halflings-regular.woff2 +0 -0
- package/dist/images/favicon.png +0 -0
- package/dist/js/sws.min.js +0 -1
- package/gulpfile.js +0 -103
- package/karma.conf.js +0 -153
- package/ux/css/app.71245b0e.css +0 -7
- package/ux/css/chunk-vendors.809c52d1.css +0 -10
- package/ux/fonts/KFOkCnqEu92Fr1MmgVxIIzQ.5cb7edfc.woff +0 -0
- package/ux/fonts/KFOlCnqEu92Fr1MmEU9fBBc-.87284894.woff +0 -0
- package/ux/fonts/KFOlCnqEu92Fr1MmSU5fBBc-.b00849e0.woff +0 -0
- package/ux/fonts/KFOlCnqEu92Fr1MmWUlfBBc-.adcde98f.woff +0 -0
- package/ux/fonts/KFOlCnqEu92Fr1MmYUtfBBc-.bb1e4dc6.woff +0 -0
- package/ux/fonts/KFOmCnqEu92Fr1Mu4mxM.60fa3c06.woff +0 -0
- package/ux/fonts/fa-brands-400.c5e0f14f.woff +0 -0
- package/ux/fonts/fa-brands-400.cccc9d29.woff2 +0 -0
- package/ux/fonts/fa-regular-400.c4f508e7.woff +0 -0
- package/ux/fonts/fa-regular-400.f5f2566b.woff2 +0 -0
- package/ux/fonts/fa-solid-900.333bae20.woff +0 -0
- package/ux/fonts/fa-solid-900.44d537ab.woff2 +0 -0
- package/ux/fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNa.29b882f0.woff +0 -0
- package/ux/fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.0509ab09.woff2 +0 -0
- package/ux/js/app.0159ef6a.js +0 -1
- package/ux/js/chunk-2d0b90b4.92f1ea49.js +0 -8
- package/ux/js/chunk-461883cd.442efb25.js +0 -76
- package/ux/js/chunk-vendors.31d3ce30.js +0 -33
- /package/ux/js/{chunk-2d0de2f2.553bf976.js → chunk-2d0de2f2.2848649c.js} +0 -0
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
var http = require('http');
|
|
4
2
|
var path = require('path');
|
|
5
3
|
var debug = require('debug')('sws:testapp');
|
|
@@ -10,11 +8,8 @@ var server = null;
|
|
|
10
8
|
// Express and middlewares
|
|
11
9
|
var express = require('express');
|
|
12
10
|
var expressBodyParser = require('body-parser');
|
|
13
|
-
var expressFavicon = require('serve-favicon');
|
|
14
|
-
var expressStatic = require('serve-static');
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
var swaggerParser = require('swagger-parser');
|
|
12
|
+
const swaggerParser = require('swagger-parser');
|
|
18
13
|
|
|
19
14
|
var swStats = require('../../lib'); // require('swagger-stats');
|
|
20
15
|
|
|
@@ -22,11 +17,6 @@ var swStats = require('../../lib'); // require('swagger-stats');
|
|
|
22
17
|
var API = require('./api');
|
|
23
18
|
|
|
24
19
|
var app = module.exports = express();
|
|
25
|
-
app.use(expressFavicon(path.join(__dirname, '../../ui/favicon.png')));
|
|
26
|
-
app.use('/ui',expressStatic(path.join(__dirname, '../../ui')));
|
|
27
|
-
app.use('/ui/dist',expressStatic(path.join(__dirname, '../../dist')));
|
|
28
|
-
app.use('/node_modules',expressStatic(path.join(__dirname, '../../node_modules')));
|
|
29
|
-
app.use('/node_modules',expressStatic(path.join(__dirname, '../../node_modules')));
|
|
30
20
|
app.use(expressBodyParser.json()); // for parsing application/json
|
|
31
21
|
app.use(expressBodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
|
|
32
22
|
|
|
@@ -37,7 +27,7 @@ app.set('port', process.env.PORT || 3040);
|
|
|
37
27
|
app.disable('etag');
|
|
38
28
|
|
|
39
29
|
app.get('/', function(req,res) {
|
|
40
|
-
res.redirect('/
|
|
30
|
+
res.redirect('/swagger-stats/');
|
|
41
31
|
});
|
|
42
32
|
|
|
43
33
|
app.get('/apidoc.json', function(req,res){
|
|
@@ -50,66 +40,20 @@ if( process.env.SWS_TEST_TIMEBUCKET ){
|
|
|
50
40
|
tlBucket = parseInt(process.env.SWS_TEST_TIMEBUCKET);
|
|
51
41
|
}
|
|
52
42
|
|
|
53
|
-
|
|
54
|
-
var apifile = path.join(__dirname,'api.js');
|
|
55
|
-
debug('initializing swagger spec from api file %s',apifile);
|
|
56
|
-
var swOptions = {
|
|
57
|
-
swaggerDefinition: {
|
|
58
|
-
"info": {
|
|
59
|
-
"description": "This is a Petstore API implementation for swagger-stats sample app",
|
|
60
|
-
"version": "1.0.0",
|
|
61
|
-
"title": "Swagger-Stats Petstore ",
|
|
62
|
-
"contact": {
|
|
63
|
-
"email": "sv2@slanatech.com"
|
|
64
|
-
},
|
|
65
|
-
"license": {
|
|
66
|
-
"name": "MIT"
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
"host": "localhost",
|
|
70
|
-
"basePath": "/api/v1",
|
|
71
|
-
"tags": [
|
|
72
|
-
{
|
|
73
|
-
"name": "pet",
|
|
74
|
-
"description": "Everything about your Pets",
|
|
75
|
-
"externalDocs": {
|
|
76
|
-
"description": "Find out more",
|
|
77
|
-
"url": "http://swaggerstats.io"
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
"name": "store",
|
|
82
|
-
"description": "Access to Petstore orders"
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
"name": "user",
|
|
86
|
-
"description": "Operations about user",
|
|
87
|
-
"externalDocs": {
|
|
88
|
-
"description": "Find out more about our store",
|
|
89
|
-
"url": "http://swaggerstats.io"
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
],
|
|
93
|
-
"schemes": ["http"]
|
|
94
|
-
},
|
|
95
|
-
apis: [apifile] // Path to the API files with swagger docs in comments
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
// Initialize swagger-jsdoc -> returns validated swagger spec in json format
|
|
99
|
-
var swaggerSpec = swaggerJSDoc(swOptions);
|
|
43
|
+
const swaggerSpec = require('./petstore.json');
|
|
100
44
|
|
|
101
45
|
// Testing validation of 3rd-party API spec
|
|
102
|
-
|
|
46
|
+
const parser = new swaggerParser();
|
|
103
47
|
|
|
104
48
|
parser.validate(swaggerSpec,function(err, api) {
|
|
105
49
|
if (!err) {
|
|
106
50
|
debug('Success validating swagger file!');
|
|
107
|
-
swaggerSpec = api;
|
|
51
|
+
//swaggerSpec = api;
|
|
108
52
|
|
|
109
53
|
// Enable swagger-stats middleware
|
|
110
54
|
app.use(swStats.getMiddleware({
|
|
111
55
|
name: 'swagger-stats-testapp',
|
|
112
|
-
version: '0.
|
|
56
|
+
version: '0.99.7',
|
|
113
57
|
timelineBucketDuration: tlBucket,
|
|
114
58
|
uriPath: '/swagger-stats',
|
|
115
59
|
swaggerSpec:swaggerSpec,
|
package/lib/sws-api-swagger.yaml
CHANGED
package/lib/swsAPIStats.js
CHANGED
|
@@ -185,6 +185,7 @@ swsAPIStats.prototype.addPathSpecs = function (pathSpecs) {
|
|
|
185
185
|
if( fullExpressPath.indexOf('{') !== -1 ) {
|
|
186
186
|
fullExpressPath = fullExpressPath.replace(/\{/g, ':');
|
|
187
187
|
fullExpressPath = fullExpressPath.replace(/\}/g, '');
|
|
188
|
+
fullExpressPath = fullExpressPath.replace(/\?(\w+=)/g, '\\?$1');
|
|
188
189
|
re = pathToRegexp(fullExpressPath, keys);
|
|
189
190
|
}
|
|
190
191
|
|
|
@@ -332,6 +333,10 @@ swsAPIStats.prototype.extractPathParams = function (matchResult,keys) {
|
|
|
332
333
|
swsAPIStats.prototype.matchRequest = function (req) {
|
|
333
334
|
|
|
334
335
|
var url = req.sws.originalUrl;
|
|
336
|
+
// Handle "/pets" and "/pets/" the same way - #105
|
|
337
|
+
if( url.endsWith('/') ) {
|
|
338
|
+
url = url.slice(0,-1);
|
|
339
|
+
}
|
|
335
340
|
|
|
336
341
|
req.sws.match = false; // No match by default
|
|
337
342
|
|
|
@@ -351,7 +356,7 @@ swsAPIStats.prototype.matchRequest = function (req) {
|
|
|
351
356
|
matchEntry = this.apiMatchIndex[url];
|
|
352
357
|
apiPath = url;
|
|
353
358
|
debug('SWS:MATCH: %s exact match', url);
|
|
354
|
-
}else{
|
|
359
|
+
} else {
|
|
355
360
|
// if not, search by regex matching
|
|
356
361
|
for(var swPath in this.apiMatchIndex) {
|
|
357
362
|
if( this.apiMatchIndex[swPath].re !== null) {
|
package/lib/swsAuth.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
const swsSettings = require('./swssettings');
|
|
2
|
+
const debug = require('debug')('sws:auth');
|
|
3
|
+
const basicAuth = require("basic-auth");
|
|
4
|
+
const Cookies = require('cookies');
|
|
5
|
+
const { v1: uuidv1 } = require('uuid');
|
|
6
|
+
|
|
7
|
+
/* Authentication */
|
|
8
|
+
class SwsAuth {
|
|
9
|
+
|
|
10
|
+
constructor() {
|
|
11
|
+
this.sessionIDs = {};
|
|
12
|
+
this.expireIntervalId = null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
storeSessionID(sid){
|
|
16
|
+
// PJS - Create sid value if not passed
|
|
17
|
+
sid = sid || uuidv1();
|
|
18
|
+
|
|
19
|
+
let tsSec = Date.now() + swsSettings.sessionMaxAge*1000;
|
|
20
|
+
this.sessionIDs[sid] = tsSec;
|
|
21
|
+
//debug('Session ID updated: %s=%d', sid,tssec);
|
|
22
|
+
if( !this.expireIntervalId ){
|
|
23
|
+
this.expireIntervalId = setInterval(() => {this.expireSessionIDs();},500);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// PJS - Send back sid value
|
|
27
|
+
return sid;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
removeSessionID(sid){
|
|
31
|
+
delete this.sessionIDs[sid];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// If authentication is enabled, executed periodically and expires old session IDs
|
|
35
|
+
expireSessionIDs(){
|
|
36
|
+
let tssec = Date.now();
|
|
37
|
+
let expired = [];
|
|
38
|
+
for(let sid in this.sessionIDs){
|
|
39
|
+
if(this.sessionIDs[sid] < (tssec + 500)){
|
|
40
|
+
expired.push(sid);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
for(let i=0;i<expired.length;i++){
|
|
44
|
+
delete this.sessionIDs[expired[i]];
|
|
45
|
+
debug('Session ID expired: %s', expired[i]);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async processAuth(req,res) {
|
|
50
|
+
|
|
51
|
+
if( !swsSettings.authentication ){
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if( swsSettings.customAuth ){
|
|
56
|
+
// return swsSettings.customAuth(req);
|
|
57
|
+
// PJS Custom
|
|
58
|
+
return swsSettings.customAuth(req, res);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let cookies = new Cookies( req, res );
|
|
62
|
+
|
|
63
|
+
// Check session cookie
|
|
64
|
+
let sessionIdCookie = cookies.get('sws-session-id');
|
|
65
|
+
if( (sessionIdCookie !== undefined) && (sessionIdCookie !== null) ){
|
|
66
|
+
|
|
67
|
+
if( sessionIdCookie in this.sessionIDs ){
|
|
68
|
+
// renew it
|
|
69
|
+
//sessionIDs[sessionIdCookie] = Date.now();
|
|
70
|
+
this.storeSessionID(sessionIdCookie);
|
|
71
|
+
cookies.set('sws-session-id',sessionIdCookie,{path:swsSettings.basePath+swsSettings.uriPath,maxAge:swsSettings.sessionMaxAge*1000});
|
|
72
|
+
// Ok
|
|
73
|
+
req['sws-auth'] = true;
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let authInfo = basicAuth(req);
|
|
79
|
+
|
|
80
|
+
let authenticated = false;
|
|
81
|
+
let msg = 'Authentication required';
|
|
82
|
+
|
|
83
|
+
if( (authInfo !== undefined) && (authInfo!==null) && ('name' in authInfo) && ('pass' in authInfo)){
|
|
84
|
+
if(typeof swsSettings.onAuthenticate === 'function'){
|
|
85
|
+
|
|
86
|
+
let onAuthResult = null;
|
|
87
|
+
try{
|
|
88
|
+
onAuthResult = await swsSettings.onAuthenticate(req, authInfo.name, authInfo.pass);
|
|
89
|
+
}catch (e){
|
|
90
|
+
msg = `Authentication error: ${e.message}`;
|
|
91
|
+
res.statusCode = 403;
|
|
92
|
+
res.end(msg);
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if( onAuthResult ){
|
|
96
|
+
authenticated = true;
|
|
97
|
+
// Session is only for stats requests
|
|
98
|
+
if(req.url.startsWith(swsSettings.pathStats)){
|
|
99
|
+
// Generate session id
|
|
100
|
+
let sessid = uuidv1();
|
|
101
|
+
this.storeSessionID(sessid);
|
|
102
|
+
// Set session cookie with expiration in 15 min
|
|
103
|
+
cookies.set('sws-session-id',sessid,{path:swsSettings.basePath+swsSettings.uriPath,maxAge:swsSettings.sessionMaxAge*1000});
|
|
104
|
+
}
|
|
105
|
+
req['sws-auth'] = true;
|
|
106
|
+
return true;
|
|
107
|
+
} else {
|
|
108
|
+
res.statusCode = 403;
|
|
109
|
+
res.end(msg);
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}else{
|
|
113
|
+
res.statusCode = 403;
|
|
114
|
+
res.end(msg);
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}else{
|
|
118
|
+
res.statusCode = 403;
|
|
119
|
+
res.end(msg);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
processLogout(req,res){
|
|
125
|
+
let cookies = new Cookies( req, res );
|
|
126
|
+
let sessionIdCookie = cookies.get('sws-session-id');
|
|
127
|
+
if( (sessionIdCookie !== undefined) && (sessionIdCookie !== null) ){
|
|
128
|
+
if( sessionIdCookie in this.sessionIDs ){
|
|
129
|
+
this.removeSessionID(sessionIdCookie);
|
|
130
|
+
cookies.set('sws-session-id'); // deletes cookie
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
res.statusCode = 200;
|
|
134
|
+
res.end('Logged out');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let swsAuth = new SwsAuth();
|
|
140
|
+
module.exports = swsAuth;
|
package/lib/swsElasticEmitter.js
CHANGED
|
@@ -4,17 +4,29 @@
|
|
|
4
4
|
|
|
5
5
|
'use strict';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
7
|
+
const os = require('os');
|
|
8
|
+
const util = require('util');
|
|
9
|
+
const http = require('http');
|
|
10
|
+
const url = require('url');
|
|
11
|
+
const https = require('https');
|
|
12
|
+
//const axios = require('axios');
|
|
13
|
+
let axios = null;
|
|
14
|
+
let axiosPromise = new Promise(async (resolve) => {
|
|
15
|
+
axios = (await import('axios')).default;
|
|
16
|
+
resolve(axios);
|
|
17
|
+
});
|
|
18
|
+
/*
|
|
19
|
+
(async () => {
|
|
20
|
+
axios = (await import('axios')).default;
|
|
21
|
+
})();
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const debug = require('debug')('sws:elastic');
|
|
25
|
+
const swsUtil = require('./swsUtil');
|
|
26
|
+
const moment = require('moment');
|
|
27
|
+
|
|
28
|
+
const indexTemplate = require('../schema/elasticsearch/api_index_template.json');
|
|
29
|
+
const indexTemplate7X = require('../schema/elasticsearch/api_index_template_7x.json');
|
|
18
30
|
|
|
19
31
|
|
|
20
32
|
const ES_MAX_BUFF = 50;
|
|
@@ -25,6 +37,9 @@ function swsElasticEmitter() {
|
|
|
25
37
|
// Options
|
|
26
38
|
this.options = null;
|
|
27
39
|
|
|
40
|
+
this.es7 = false;
|
|
41
|
+
this.es8 = true;
|
|
42
|
+
|
|
28
43
|
this.indexBuffer = '';
|
|
29
44
|
this.bufferCount = 0;
|
|
30
45
|
this.lastFlush = 0;
|
|
@@ -38,6 +53,9 @@ function swsElasticEmitter() {
|
|
|
38
53
|
this.elasticUsername = null;
|
|
39
54
|
this.elasticPassword = null;
|
|
40
55
|
|
|
56
|
+
this.elasticsearchCert = null;
|
|
57
|
+
this.elasticsearchKey = null
|
|
58
|
+
|
|
41
59
|
this.indexPrefix = "api-";
|
|
42
60
|
|
|
43
61
|
this.enabled = false;
|
|
@@ -78,11 +96,26 @@ swsElasticEmitter.prototype.initialize = function (swsOptions) {
|
|
|
78
96
|
this.elasticPassword = swsOptions[swsUtil.supportedOptions.elasticsearchPassword];
|
|
79
97
|
}
|
|
80
98
|
|
|
81
|
-
|
|
82
|
-
|
|
99
|
+
if(swsUtil.supportedOptions.elasticsearchCert in swsOptions) {
|
|
100
|
+
this.elasticsearchCert = swsOptions[swsUtil.supportedOptions.elasticsearchCert]
|
|
101
|
+
}
|
|
83
102
|
|
|
84
|
-
|
|
103
|
+
if( swsUtil.supportedOptions.elasticsearchKey in swsOptions) {
|
|
104
|
+
this.elasticsearchKey = swsOptions[swsUtil.supportedOptions.elasticsearchKey]
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if(this.elasticsearchKey && this.elasticsearchCert){
|
|
108
|
+
axios.defaults.httpsAgent = new https.Agent({
|
|
109
|
+
rejectUnauthorized: false, // (NOTE: this will disable client verification)
|
|
110
|
+
cert: this.elasticsearchCert,
|
|
111
|
+
key: this.elasticsearchKey,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
85
114
|
|
|
115
|
+
// Check / Initialize schema
|
|
116
|
+
Promise.resolve(axiosPromise).then( () => {
|
|
117
|
+
this.initTemplate();
|
|
118
|
+
});
|
|
86
119
|
};
|
|
87
120
|
|
|
88
121
|
// initialize index template
|
|
@@ -90,50 +123,70 @@ swsElasticEmitter.prototype.initTemplate = function(rrr) {
|
|
|
90
123
|
|
|
91
124
|
var that = this;
|
|
92
125
|
|
|
93
|
-
var requiredTemplateVersion =
|
|
126
|
+
var requiredTemplateVersion = indexTemplate7X.version;
|
|
94
127
|
|
|
95
128
|
// Check if there is a template
|
|
96
129
|
var templateURL = this.elasticURL+'/_template/template_api';
|
|
97
|
-
var
|
|
98
|
-
var
|
|
130
|
+
var getOptionsVersion = {method:'get',url:this.elasticURL};
|
|
131
|
+
var getOptions = {method:'get',url:templateURL};
|
|
132
|
+
let putOptions = {method:'put',url:templateURL, data:indexTemplate7X};
|
|
99
133
|
|
|
100
134
|
if (this.elasticUsername && this.elasticPassword) {
|
|
101
135
|
var auth = {
|
|
102
136
|
username: this.elasticUsername,
|
|
103
137
|
password: this.elasticPassword,
|
|
104
138
|
}
|
|
139
|
+
getOptionsVersion.auth = auth;
|
|
105
140
|
getOptions.auth = auth;
|
|
106
141
|
putOptions.auth = auth;
|
|
107
142
|
}
|
|
108
143
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
144
|
+
axios(getOptionsVersion).then( (response) => {
|
|
145
|
+
const body = response.data || {};
|
|
146
|
+
if(body && ('version' in body) && ('number' in body.version)){
|
|
147
|
+
that.es7 = body.version.number.startsWith('7');
|
|
148
|
+
that.es8 = body.version.number.startsWith('8');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if( !that.es7 && !that.es8){
|
|
152
|
+
putOptions.json = indexTemplate;
|
|
153
|
+
}
|
|
113
154
|
|
|
114
|
-
|
|
155
|
+
let initializeNeeded = false;
|
|
115
156
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if(
|
|
120
|
-
|
|
121
|
-
initializeNeeded = true;
|
|
122
|
-
}
|
|
157
|
+
axios(getOptions).then( (response) => {
|
|
158
|
+
const body = response.data || {};
|
|
159
|
+
if( 'template_api' in body ) {
|
|
160
|
+
if (!('version' in body.template_api) || (body.template_api.version < requiredTemplateVersion)) {
|
|
161
|
+
initializeNeeded = true;
|
|
123
162
|
}
|
|
124
163
|
}
|
|
125
|
-
|
|
164
|
+
}).catch((error)=>{
|
|
165
|
+
if(error.response && error.response.status === 404) {
|
|
166
|
+
initializeNeeded = true;
|
|
167
|
+
} else {
|
|
168
|
+
debug(`Error querying template: ${error.message}`);
|
|
169
|
+
that.enabled = false;
|
|
170
|
+
}
|
|
171
|
+
}).finally(()=> {
|
|
126
172
|
if(initializeNeeded){
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
173
|
+
axios(putOptions).then((response) =>{
|
|
174
|
+
debug("Elasticsearch template updated");
|
|
175
|
+
that.enabled = true;
|
|
176
|
+
}).catch((error)=>{
|
|
177
|
+
debug(`Failed to update template: ${error.message}`);
|
|
178
|
+
that.enabled = false;
|
|
131
179
|
});
|
|
180
|
+
}else{
|
|
181
|
+
that.enabled = true;
|
|
132
182
|
}
|
|
183
|
+
});
|
|
133
184
|
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
185
|
|
|
186
|
+
}).catch((error)=>{
|
|
187
|
+
debug(`Error getting version: ${error.message}`);
|
|
188
|
+
that.enabled = false;
|
|
189
|
+
});
|
|
137
190
|
};
|
|
138
191
|
|
|
139
192
|
// Update timeline and stats per tick
|
|
@@ -180,7 +233,11 @@ swsElasticEmitter.prototype.processRecord = function(rrr){
|
|
|
180
233
|
|
|
181
234
|
// Create metadata
|
|
182
235
|
var indexName = this.indexPrefix+moment(rrr['@timestamp']).utc().format('YYYY.MM.DD');
|
|
183
|
-
|
|
236
|
+
|
|
237
|
+
let meta = {index:{_index:indexName,_id:rrr.id}};
|
|
238
|
+
if(!this.es7 && !this.es8){
|
|
239
|
+
meta = {index:{_index:indexName,_type:'api',_id:rrr.id}};
|
|
240
|
+
}
|
|
184
241
|
|
|
185
242
|
// Add to buffer
|
|
186
243
|
this.indexBuffer += JSON.stringify(meta) + '\n';
|
|
@@ -201,12 +258,13 @@ swsElasticEmitter.prototype.flush = function(){
|
|
|
201
258
|
|
|
202
259
|
this.lastFlush = Date.now();
|
|
203
260
|
|
|
204
|
-
|
|
261
|
+
let options = {
|
|
262
|
+
method: 'post',
|
|
205
263
|
url: this.elasticURLBulk,
|
|
206
264
|
headers: {
|
|
207
265
|
'Content-Type': 'application/x-ndjson'
|
|
208
266
|
},
|
|
209
|
-
|
|
267
|
+
data: this.indexBuffer,
|
|
210
268
|
};
|
|
211
269
|
|
|
212
270
|
if (this.elasticUsername && this.elasticPassword) {
|
|
@@ -216,13 +274,13 @@ swsElasticEmitter.prototype.flush = function(){
|
|
|
216
274
|
}
|
|
217
275
|
}
|
|
218
276
|
|
|
219
|
-
|
|
220
|
-
if (
|
|
221
|
-
debug(
|
|
222
|
-
}
|
|
223
|
-
if (response && ('statusCode' in response) && (response.statusCode !== 200)) {
|
|
224
|
-
debug('Indexing Error: %d %s',response.statusCode, response.message);
|
|
277
|
+
axios(options).then((response)=> {
|
|
278
|
+
if (response && ('status' in response) && (response.status !== 200)) {
|
|
279
|
+
debug('Indexing Error: %d %s',response.status, response.message);
|
|
225
280
|
}
|
|
281
|
+
}).catch((error)=>{
|
|
282
|
+
debug(`Indexing Error: ${error.message}`);
|
|
283
|
+
that.enabled = false;
|
|
226
284
|
});
|
|
227
285
|
|
|
228
286
|
this.indexBuffer = '';
|
package/lib/swsHapi.js
CHANGED
|
@@ -3,6 +3,7 @@ const path = require('path');
|
|
|
3
3
|
const promClient = require("prom-client");
|
|
4
4
|
const swsSettings = require('./swssettings');
|
|
5
5
|
const swsProcessor = require('./swsProcessor');
|
|
6
|
+
const swsAuth = require('./swsAuth');
|
|
6
7
|
const swsUtil = require('./swsUtil');
|
|
7
8
|
const debug = require('debug')('sws:hapi');
|
|
8
9
|
const url = require('url');
|
|
@@ -33,7 +34,7 @@ class SwsHapi {
|
|
|
33
34
|
debug("processResponse:ERROR: " + e);
|
|
34
35
|
}
|
|
35
36
|
});
|
|
36
|
-
await server.ext('onRequest', function (request, h) {
|
|
37
|
+
await server.ext('onRequest', async function (request, h) {
|
|
37
38
|
let nodeReq = request.raw.req;
|
|
38
39
|
let nodeRes = request.raw.res;
|
|
39
40
|
nodeRes._swsReq = nodeReq;
|
|
@@ -43,6 +44,14 @@ class SwsHapi {
|
|
|
43
44
|
if(reqUrl.startsWith(swsSettings.uriPath)){
|
|
44
45
|
// Don't track sws requests
|
|
45
46
|
nodeReq.sws.track = false;
|
|
47
|
+
/*
|
|
48
|
+
if(reqUrl.startsWith(swsSettings.pathStats)){
|
|
49
|
+
let authResult = await swsAuth.processAuth(request.raw.req,request.raw.res);
|
|
50
|
+
if( authResult ){
|
|
51
|
+
//return h.continue;
|
|
52
|
+
return processor.getStats(request.raw.req.sws.query);
|
|
53
|
+
}
|
|
54
|
+
}*/
|
|
46
55
|
return h.continue;
|
|
47
56
|
}
|
|
48
57
|
try {
|
|
@@ -56,7 +65,14 @@ class SwsHapi {
|
|
|
56
65
|
server.route({
|
|
57
66
|
method: 'GET',
|
|
58
67
|
path: swsSettings.pathStats,
|
|
59
|
-
handler: function (request, h) {
|
|
68
|
+
handler: async function (request, h) {
|
|
69
|
+
let authResult = await swsAuth.processAuth(request.raw.req,request.raw.res);
|
|
70
|
+
if( !authResult ){
|
|
71
|
+
return h.abandon;
|
|
72
|
+
}
|
|
73
|
+
if(('sws-auth' in request.raw.req) && request.raw.req['sws-auth']){
|
|
74
|
+
request.raw.res.setHeader('x-sws-authenticated','true');
|
|
75
|
+
}
|
|
60
76
|
return processor.getStats(request.raw.req.sws.query);
|
|
61
77
|
},
|
|
62
78
|
options: options.routeOptions
|
|
@@ -65,45 +81,24 @@ class SwsHapi {
|
|
|
65
81
|
server.route({
|
|
66
82
|
method: 'GET',
|
|
67
83
|
path: swsSettings.pathMetrics,
|
|
68
|
-
handler: function (request, h) {
|
|
69
|
-
|
|
84
|
+
handler: async function (request, h) {
|
|
85
|
+
let authResult = await swsAuth.processAuth(request.raw.req,request.raw.res);
|
|
86
|
+
if( !authResult ){
|
|
87
|
+
return h.abandon;
|
|
88
|
+
}
|
|
89
|
+
const response = h.response(await promClient.register.metrics());
|
|
70
90
|
response.code(200);
|
|
71
91
|
response.header('Content-Type', 'text/plain');
|
|
72
92
|
return response;
|
|
73
93
|
},
|
|
74
94
|
options: options.routeOptions
|
|
75
95
|
});
|
|
76
|
-
//
|
|
77
|
-
server.route({
|
|
78
|
-
method: 'GET',
|
|
79
|
-
path: swsSettings.uriPath,
|
|
80
|
-
handler: function (request, h) {
|
|
81
|
-
return h.redirect(swsSettings.pathUI);
|
|
82
|
-
},
|
|
83
|
-
options: options.routeOptions
|
|
84
|
-
});
|
|
85
|
-
// Return UI
|
|
96
|
+
// Logout
|
|
86
97
|
server.route({
|
|
87
98
|
method: 'GET',
|
|
88
|
-
path: swsSettings.
|
|
99
|
+
path: swsSettings.pathLogout,
|
|
89
100
|
handler: function (request, h) {
|
|
90
|
-
|
|
91
|
-
},
|
|
92
|
-
options: options.routeOptions
|
|
93
|
-
});
|
|
94
|
-
// Return Dist
|
|
95
|
-
server.route({
|
|
96
|
-
method: 'GET',
|
|
97
|
-
path: swsSettings.pathDist+'/{file*}',
|
|
98
|
-
handler: function (request, h) {
|
|
99
|
-
let fileName = request.params.file;
|
|
100
|
-
var options = {
|
|
101
|
-
root: path.join(__dirname,'..','dist'),
|
|
102
|
-
dotfiles: 'deny'
|
|
103
|
-
// TODO Caching
|
|
104
|
-
};
|
|
105
|
-
request.raw.res.setHeader('Content-Type', send.mime.lookup(path.basename(fileName)));
|
|
106
|
-
send(request.raw.req, fileName, options).pipe(request.raw.res);
|
|
101
|
+
swsAuth.processLogout(request.raw.req,request.raw.res);
|
|
107
102
|
return h.abandon;
|
|
108
103
|
},
|
|
109
104
|
options: options.routeOptions
|
|
@@ -111,13 +106,13 @@ class SwsHapi {
|
|
|
111
106
|
// Return UX
|
|
112
107
|
server.route({
|
|
113
108
|
method: 'GET',
|
|
114
|
-
path: swsSettings.pathUX+'
|
|
109
|
+
path: swsSettings.pathUX+'{file*}',
|
|
115
110
|
handler: function (request, h) {
|
|
116
111
|
let fileName = request.params.file;
|
|
117
112
|
if(!fileName){
|
|
118
113
|
fileName = 'index.html';
|
|
119
114
|
}
|
|
120
|
-
|
|
115
|
+
let options = {
|
|
121
116
|
root: path.join(__dirname,'..','ux'),
|
|
122
117
|
dotfiles: 'deny'
|
|
123
118
|
// TODO Caching
|