mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2025-12-20 16:50:39 +00:00
update time-series and parse-url
This commit is contained in:
@@ -28,7 +28,7 @@ class HttpRequestor extends BaseRequestor {
|
||||
assert(['GET', 'POST'].includes(this.method));
|
||||
|
||||
const u = this._parsedUrl = parseUrl(this.url);
|
||||
this._baseUrl = `${u.protocol}://${u.resource}`;
|
||||
this._baseUrl = `${u.protocol}://${u.resource}:${u.port || 80}`;
|
||||
this._resource = u.resource;
|
||||
this._protocol = u.protocol;
|
||||
this._search = u.search;
|
||||
@@ -49,7 +49,7 @@ class HttpRequestor extends BaseRequestor {
|
||||
this.logger.debug(`HttpRequestor:created pool for ${this._baseUrl}`);
|
||||
}
|
||||
}
|
||||
else this.client = new Client(`${u.protocol}://${u.resource}`);
|
||||
else this.client = new Client(`${u.protocol}://${u.resource}:${u.port || 80}`);
|
||||
}
|
||||
|
||||
get baseUrl() {
|
||||
@@ -101,7 +101,7 @@ class HttpRequestor extends BaseRequestor {
|
||||
query = u.query;
|
||||
}
|
||||
else {
|
||||
client = newClient = new Client(`${u.protocol}://${u.resource}`);
|
||||
client = newClient = new Client(`${u.protocol}://${u.resource}:${u.port || 80}`);
|
||||
path = u.pathname;
|
||||
query = u.query;
|
||||
}
|
||||
|
||||
@@ -1,42 +1,7 @@
|
||||
const bent = require('bent');
|
||||
const parseUrl = require('parse-url');
|
||||
const assert = require('assert');
|
||||
const snakeCaseKeys = require('./snakecase-keys');
|
||||
const crypto = require('crypto');
|
||||
const timeSeries = require('@jambonz/time-series');
|
||||
let alerter ;
|
||||
|
||||
const toBase64 = (str) => Buffer.from(str || '', 'utf8').toString('base64');
|
||||
|
||||
function computeSignature(payload, timestamp, secret) {
|
||||
assert(secret);
|
||||
const data = `${timestamp}.${JSON.stringify(payload)}`;
|
||||
return crypto
|
||||
.createHmac('sha256', secret)
|
||||
.update(data, 'utf8')
|
||||
.digest('hex');
|
||||
}
|
||||
|
||||
function generateSigHeader(payload, secret) {
|
||||
const timestamp = Math.floor(Date.now() / 1000);
|
||||
const signature = computeSignature(payload, timestamp, secret);
|
||||
const scheme = 'v1';
|
||||
return {
|
||||
'Jambonz-Signature': `t=${timestamp},${scheme}=${signature}`
|
||||
};
|
||||
}
|
||||
|
||||
function basicAuth(username, password) {
|
||||
if (!username || !password) return {};
|
||||
const creds = `${username}:${password || ''}`;
|
||||
const header = `Basic ${toBase64(creds)}`;
|
||||
return {Authorization: header};
|
||||
}
|
||||
|
||||
function isRelativeUrl(u) {
|
||||
return typeof u === 'string' && u.startsWith('/');
|
||||
}
|
||||
|
||||
function isAbsoluteUrl(u) {
|
||||
return typeof u === 'string' &&
|
||||
u.startsWith('https://') || u.startsWith('http://');
|
||||
@@ -49,14 +14,6 @@ class Requestor {
|
||||
this.logger = logger;
|
||||
this.url = hook.url;
|
||||
this.method = hook.method || 'POST';
|
||||
this.authHeader = basicAuth(hook.username, hook.password);
|
||||
|
||||
const u = parseUrl(this.url);
|
||||
const myPort = u.port ? `:${u.port}` : '';
|
||||
const baseUrl = this._baseUrl = `${u.protocol}://${u.resource}${myPort}`;
|
||||
|
||||
this.get = bent(baseUrl, 'GET', 'buffer', 200, 201);
|
||||
this.post = bent(baseUrl, 'POST', 'buffer', 200, 201);
|
||||
|
||||
this.username = hook.username;
|
||||
this.password = hook.password;
|
||||
@@ -78,72 +35,15 @@ class Requestor {
|
||||
}
|
||||
}
|
||||
|
||||
get baseUrl() {
|
||||
return this._baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an HTTP request.
|
||||
* All requests use json bodies.
|
||||
* All requests expect a 200 statusCode on success
|
||||
* @param {object|string} hook - may be a absolute or relative url, or an object
|
||||
* @param {string} [hook.url] - an absolute or relative url
|
||||
* @param {string} [hook.method] - 'GET' or 'POST'
|
||||
* @param {string} [hook.username] - if basic auth is protecting the endpoint
|
||||
* @param {string} [hook.password] - if basic auth is protecting the endpoint
|
||||
* @param {object} [params] - request parameters
|
||||
*/
|
||||
async request(hook, params) {
|
||||
const payload = params ? snakeCaseKeys(params, ['customerData', 'sip']) : null;
|
||||
const url = hook.url || hook;
|
||||
const method = hook.method || 'POST';
|
||||
|
||||
assert.ok(url, 'Requestor:request url was not provided');
|
||||
assert.ok, (['GET', 'POST'].includes(method), `Requestor:request method must be 'GET' or 'POST' not ${method}`);
|
||||
const {url: urlInfo = hook, method: methodInfo = 'POST'} = hook; // mask user/pass
|
||||
this.logger.debug({url: urlInfo, method: methodInfo, payload}, `Requestor:request ${method} ${url}`);
|
||||
const startAt = process.hrtime();
|
||||
|
||||
let buf;
|
||||
try {
|
||||
const sigHeader = generateSigHeader(payload, this.secret);
|
||||
const headers = {...sigHeader, ...this.authHeader};
|
||||
//this.logger.info({url, headers}, 'send webhook');
|
||||
buf = isRelativeUrl(url) ?
|
||||
await this.post(url, payload, headers) :
|
||||
await bent(method, 'buffer', 200, 201, 202)(url, payload, headers);
|
||||
} catch (err) {
|
||||
this.logger.error({err, secret: this.secret, baseUrl: this.baseUrl, url, statusCode: err.statusCode},
|
||||
`web callback returned unexpected error code ${err.statusCode}`);
|
||||
let opts = {account_sid: this.account_sid};
|
||||
if (err.code === 'ECONNREFUSED') {
|
||||
opts = {...opts, alert_type: alerter.AlertType.WEBHOOK_CONNECTION_FAILURE, url};
|
||||
}
|
||||
else if (err.name === 'StatusError') {
|
||||
opts = {...opts, alert_type: alerter.AlertType.WEBHOOK_STATUS_FAILURE, url, status: err.statusCode};
|
||||
}
|
||||
else {
|
||||
opts = {...opts, alert_type: alerter.AlertType.WEBHOOK_CONNECTION_FAILURE, url, detail: err.message};
|
||||
}
|
||||
alerter.writeAlerts(opts).catch((err) => this.logger.info({err, opts}, 'Error writing alert'));
|
||||
|
||||
throw err;
|
||||
}
|
||||
const diff = process.hrtime(startAt);
|
||||
const time = diff[0] * 1e3 + diff[1] * 1e-6;
|
||||
const rtt = time.toFixed(0);
|
||||
if (buf) this.stats.histogram('app.hook.response_time', rtt, ['hook_type:app']);
|
||||
|
||||
if (buf && buf.toString().length > 0) {
|
||||
try {
|
||||
const json = JSON.parse(buf.toString());
|
||||
this.logger.info({response: json}, `Requestor:request ${method} ${url} succeeded in ${rtt}ms`);
|
||||
return json;
|
||||
}
|
||||
catch (err) {
|
||||
//this.logger.debug({err, url, method}, `Requestor:request returned non-JSON content: '${buf.toString()}'`);
|
||||
}
|
||||
get Alerter() {
|
||||
if (!alerter) {
|
||||
alerter = timeSeries(this.logger, {
|
||||
host: process.env.JAMBONES_TIME_SERIES_HOST,
|
||||
commitSize: 50,
|
||||
commitInterval: 'test' === process.env.NODE_ENV ? 7 : 20
|
||||
});
|
||||
}
|
||||
return alerter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
100
package-lock.json
generated
100
package-lock.json
generated
@@ -13,7 +13,7 @@
|
||||
"@jambonz/http-health-check": "^0.0.1",
|
||||
"@jambonz/realtimedb-helpers": "^0.4.30",
|
||||
"@jambonz/stats-collector": "^0.1.6",
|
||||
"@jambonz/time-series": "^0.1.12",
|
||||
"@jambonz/time-series": "^0.2.1",
|
||||
"@opentelemetry/api": "^1.1.0",
|
||||
"@opentelemetry/exporter-jaeger": "^1.3.1",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.27.0",
|
||||
@@ -33,14 +33,14 @@
|
||||
"helmet": "^5.1.0",
|
||||
"ip": "^1.1.8",
|
||||
"moment": "^2.29.4",
|
||||
"parse-url": "^7.0.2",
|
||||
"parse-url": "^8.1.0",
|
||||
"pino": "^6.14.0",
|
||||
"sdp-transform": "^2.14.1",
|
||||
"short-uuid": "^4.2.0",
|
||||
"to-snake-case": "^1.0.0",
|
||||
"undici": "^5.8.2",
|
||||
"uuid": "^8.3.2",
|
||||
"verify-aws-sns-signature": "^0.0.7",
|
||||
"verify-aws-sns-signature": "^0.1.0",
|
||||
"ws": "^8.8.0",
|
||||
"xml2js": "^0.4.23"
|
||||
},
|
||||
@@ -566,9 +566,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jambonz/time-series": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/time-series/-/time-series-0.1.12.tgz",
|
||||
"integrity": "sha512-TmCG4jcI8oK3NXOc4/PbdRhhMVLEr5FOyG4IIWpNlwB0vbjAGLY3K+O5PF4fXK+UcNYnIrUcrd2C0J9z3+YBxw==",
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/time-series/-/time-series-0.2.1.tgz",
|
||||
"integrity": "sha512-uAoeZ3ibS7kEOGdT+vaY8BB8hOV4q38eEaF+d5OvLQaHCrPonNiwB8tWhhXDwtYdDompfqVRUy/plNA9fyS7Vw==",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.1",
|
||||
"influx": "^5.9.3"
|
||||
@@ -3614,14 +3614,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-ssh": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz",
|
||||
"integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==",
|
||||
"dependencies": {
|
||||
"protocols": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/is-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
|
||||
@@ -4436,17 +4428,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-url": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
|
||||
"integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/nyc": {
|
||||
"version": "15.1.0",
|
||||
"resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz",
|
||||
@@ -4672,22 +4653,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/parse-path": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-path/-/parse-path-5.0.0.tgz",
|
||||
"integrity": "sha512-qOpH55/+ZJ4jUu/oLO+ifUKjFPNZGfnPJtzvGzKN/4oLMil5m9OH4VpOj6++9/ytJcfks4kzH2hhi87GL/OU9A==",
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.0.0.tgz",
|
||||
"integrity": "sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==",
|
||||
"dependencies": {
|
||||
"protocols": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-url": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/parse-url/-/parse-url-7.0.2.tgz",
|
||||
"integrity": "sha512-PqO4Z0eCiQ08Wj6QQmrmp5YTTxpYfONdOEamrtvK63AmzXpcavIVQubGHxOEwiIoDZFb8uDOoQFS0NCcjqIYQg==",
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-url/-/parse-url-8.1.0.tgz",
|
||||
"integrity": "sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==",
|
||||
"dependencies": {
|
||||
"is-ssh": "^1.4.0",
|
||||
"normalize-url": "^6.1.0",
|
||||
"parse-path": "^5.0.0",
|
||||
"protocols": "^2.0.1"
|
||||
"parse-path": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
@@ -5993,12 +5971,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/verify-aws-sns-signature": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/verify-aws-sns-signature/-/verify-aws-sns-signature-0.0.7.tgz",
|
||||
"integrity": "sha512-j/yePIQvLqRGshOwuEs9VT7jGh++1hBoOjjt+Rl4aAffJTu+22GwTPfAD9fLY9VqRrR4Cuiid3eNHOkmxE3TNg==",
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/verify-aws-sns-signature/-/verify-aws-sns-signature-0.1.0.tgz",
|
||||
"integrity": "sha512-giPj4dIrhounlQA+AAy0CZmqARrT2o4WjIcv1GKcnQiKBDmDpJyGIaHu/ESwOGVcZf68aLHFPrEzhudXSp4Krw==",
|
||||
"dependencies": {
|
||||
"bent": "^7.3.12",
|
||||
"parse-url": "^7.0.2"
|
||||
"parse-url": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/verror": {
|
||||
@@ -6714,9 +6692,9 @@
|
||||
}
|
||||
},
|
||||
"@jambonz/time-series": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/time-series/-/time-series-0.1.12.tgz",
|
||||
"integrity": "sha512-TmCG4jcI8oK3NXOc4/PbdRhhMVLEr5FOyG4IIWpNlwB0vbjAGLY3K+O5PF4fXK+UcNYnIrUcrd2C0J9z3+YBxw==",
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/time-series/-/time-series-0.2.1.tgz",
|
||||
"integrity": "sha512-uAoeZ3ibS7kEOGdT+vaY8BB8hOV4q38eEaF+d5OvLQaHCrPonNiwB8tWhhXDwtYdDompfqVRUy/plNA9fyS7Vw==",
|
||||
"requires": {
|
||||
"debug": "^4.3.1",
|
||||
"influx": "^5.9.3"
|
||||
@@ -9030,14 +9008,6 @@
|
||||
"call-bind": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"is-ssh": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz",
|
||||
"integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==",
|
||||
"requires": {
|
||||
"protocols": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"is-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
|
||||
@@ -9665,11 +9635,6 @@
|
||||
"process-on-spawn": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"normalize-url": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
|
||||
"integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="
|
||||
},
|
||||
"nyc": {
|
||||
"version": "15.1.0",
|
||||
"resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz",
|
||||
@@ -9838,22 +9803,19 @@
|
||||
}
|
||||
},
|
||||
"parse-path": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-path/-/parse-path-5.0.0.tgz",
|
||||
"integrity": "sha512-qOpH55/+ZJ4jUu/oLO+ifUKjFPNZGfnPJtzvGzKN/4oLMil5m9OH4VpOj6++9/ytJcfks4kzH2hhi87GL/OU9A==",
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.0.0.tgz",
|
||||
"integrity": "sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==",
|
||||
"requires": {
|
||||
"protocols": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"parse-url": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/parse-url/-/parse-url-7.0.2.tgz",
|
||||
"integrity": "sha512-PqO4Z0eCiQ08Wj6QQmrmp5YTTxpYfONdOEamrtvK63AmzXpcavIVQubGHxOEwiIoDZFb8uDOoQFS0NCcjqIYQg==",
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-url/-/parse-url-8.1.0.tgz",
|
||||
"integrity": "sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==",
|
||||
"requires": {
|
||||
"is-ssh": "^1.4.0",
|
||||
"normalize-url": "^6.1.0",
|
||||
"parse-path": "^5.0.0",
|
||||
"protocols": "^2.0.1"
|
||||
"parse-path": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"parseurl": {
|
||||
@@ -10869,12 +10831,12 @@
|
||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
|
||||
},
|
||||
"verify-aws-sns-signature": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/verify-aws-sns-signature/-/verify-aws-sns-signature-0.0.7.tgz",
|
||||
"integrity": "sha512-j/yePIQvLqRGshOwuEs9VT7jGh++1hBoOjjt+Rl4aAffJTu+22GwTPfAD9fLY9VqRrR4Cuiid3eNHOkmxE3TNg==",
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/verify-aws-sns-signature/-/verify-aws-sns-signature-0.1.0.tgz",
|
||||
"integrity": "sha512-giPj4dIrhounlQA+AAy0CZmqARrT2o4WjIcv1GKcnQiKBDmDpJyGIaHu/ESwOGVcZf68aLHFPrEzhudXSp4Krw==",
|
||||
"requires": {
|
||||
"bent": "^7.3.12",
|
||||
"parse-url": "^7.0.2"
|
||||
"parse-url": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"verror": {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
"url": "https://github.com/jambonz/jambonz-feature-server.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/jambonz/jambonz-feature-server/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node app",
|
||||
@@ -30,7 +29,7 @@
|
||||
"@jambonz/http-health-check": "^0.0.1",
|
||||
"@jambonz/realtimedb-helpers": "^0.4.30",
|
||||
"@jambonz/stats-collector": "^0.1.6",
|
||||
"@jambonz/time-series": "^0.1.12",
|
||||
"@jambonz/time-series": "^0.2.1",
|
||||
"@opentelemetry/api": "^1.1.0",
|
||||
"@opentelemetry/exporter-jaeger": "^1.3.1",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.27.0",
|
||||
@@ -50,14 +49,14 @@
|
||||
"helmet": "^5.1.0",
|
||||
"ip": "^1.1.8",
|
||||
"moment": "^2.29.4",
|
||||
"parse-url": "^7.0.2",
|
||||
"parse-url": "^8.1.0",
|
||||
"pino": "^6.14.0",
|
||||
"sdp-transform": "^2.14.1",
|
||||
"short-uuid": "^4.2.0",
|
||||
"to-snake-case": "^1.0.0",
|
||||
"undici": "^5.8.2",
|
||||
"uuid": "^8.3.2",
|
||||
"verify-aws-sns-signature": "^0.0.7",
|
||||
"verify-aws-sns-signature": "^0.1.0",
|
||||
"ws": "^8.8.0",
|
||||
"xml2js": "^0.4.23"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user