Add logging & prom middlewares to Price Service (#191)
This commit is contained in:
parent
e9807ade29
commit
5b13be3bbf
|
@ -15,12 +15,17 @@
|
|||
"@pythnetwork/pyth-sdk-js": "^0.1.0",
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/morgan": "^1.9.3",
|
||||
"@types/response-time": "^2.3.5",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^10.0.0",
|
||||
"ethers": "^5.4.4",
|
||||
"express": "^4.17.2",
|
||||
"express-validation": "^4.0.1",
|
||||
"http-status-codes": "^2.2.0",
|
||||
"morgan": "^1.10.0",
|
||||
"prom-client": "^14.0.1",
|
||||
"response-time": "^2.3.2",
|
||||
"winston": "^3.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -1067,6 +1072,19 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@hapi/hoek": {
|
||||
"version": "9.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz",
|
||||
"integrity": "sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw=="
|
||||
},
|
||||
"node_modules/@hapi/topo": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz",
|
||||
"integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==",
|
||||
"dependencies": {
|
||||
"@hapi/hoek": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@improbable-eng/grpc-web": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@improbable-eng/grpc-web/-/grpc-web-0.14.1.tgz",
|
||||
|
@ -1137,6 +1155,24 @@
|
|||
"resolved": "https://registry.npmjs.org/@pythnetwork/pyth-sdk-js/-/pyth-sdk-js-0.1.0.tgz",
|
||||
"integrity": "sha512-fsGx2vkXncoIpsrcjx6WY7JN0R74YG/lX2UA1Wz/m6MPgJrde1LHkmikOSdMZUU3KkpWGHT1ZdYoW+Ikv7Nv9g=="
|
||||
},
|
||||
"node_modules/@sideway/address": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz",
|
||||
"integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==",
|
||||
"dependencies": {
|
||||
"@hapi/hoek": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@sideway/formula": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz",
|
||||
"integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg=="
|
||||
},
|
||||
"node_modules/@sideway/pinpoint": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
|
||||
"integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="
|
||||
},
|
||||
"node_modules/@solana/buffer-layout": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.0.tgz",
|
||||
|
@ -1484,6 +1520,11 @@
|
|||
"@types/range-parser": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/hapi__joi": {
|
||||
"version": "16.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/hapi__joi/-/hapi__joi-16.0.12.tgz",
|
||||
"integrity": "sha512-xJYifuz59jXdWY5JMS15uvA3ycS3nQYOGqoIIE0+fwQ0qI3/4CxBc6RHsOTp6wk9M0NWEdpcTl02lOQOKMifbQ=="
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.14.182",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
|
||||
|
@ -1499,6 +1540,14 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
||||
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw=="
|
||||
},
|
||||
"node_modules/@types/morgan": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.3.tgz",
|
||||
"integrity": "sha512-BiLcfVqGBZCyNCnCH3F4o2GmDLrpy0HeBVnNlyZG4fo88ZiE9SoiBe3C+2ezuwbjlEyT+PDZ17//TAlRxAn75Q==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "16.11.27",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.27.tgz",
|
||||
|
@ -1514,6 +1563,15 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
|
||||
"integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw=="
|
||||
},
|
||||
"node_modules/@types/response-time": {
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/response-time/-/response-time-2.3.5.tgz",
|
||||
"integrity": "sha512-4ANzp+I3K7sztFFAGPALWBvSl4ayaDSKzI2Bok+WNz+en2eB2Pvk6VCjR47PBXBWOkEg2r4uWpZOlXA5DNINOQ==",
|
||||
"dependencies": {
|
||||
"@types/express": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/serve-static": {
|
||||
"version": "1.13.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz",
|
||||
|
@ -1803,6 +1861,22 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"node_modules/basic-auth": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "5.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/basic-auth/node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"node_modules/bech32": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz",
|
||||
|
@ -2440,6 +2514,16 @@
|
|||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express-validation": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/express-validation/-/express-validation-4.0.1.tgz",
|
||||
"integrity": "sha512-DA8Nkl82ISo2Vi+eYjcokISz0eom1mcKW8t44Ns4MvsSEeg0WU0Ofoc0uWSZ0tv0J2Z5u7+jJ5nVmXN/tYRpfg==",
|
||||
"dependencies": {
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/hapi__joi": "16.x.x",
|
||||
"joi": "^17.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eyes": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
|
||||
|
@ -2801,6 +2885,18 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.48.tgz",
|
||||
"integrity": "sha512-4kxzqkrpwYtn6okJUcb2lfUu9ilnb3yhUOH6qX3nug8D2DupZ2drIkff2yJzYcNJVl3begnlcaBJ7tqiTTzjnQ=="
|
||||
},
|
||||
"node_modules/joi": {
|
||||
"version": "17.6.0",
|
||||
"resolved": "https://registry.npmjs.org/joi/-/joi-17.6.0.tgz",
|
||||
"integrity": "sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw==",
|
||||
"dependencies": {
|
||||
"@hapi/hoek": "^9.0.0",
|
||||
"@hapi/topo": "^5.0.0",
|
||||
"@sideway/address": "^4.1.3",
|
||||
"@sideway/formula": "^3.0.0",
|
||||
"@sideway/pinpoint": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/js-base64": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.2.tgz",
|
||||
|
@ -3034,6 +3130,29 @@
|
|||
"resolved": "https://registry.npmjs.org/mobile-detect/-/mobile-detect-1.4.5.tgz",
|
||||
"integrity": "sha512-yc0LhH6tItlvfLBugVUEtgawwFU2sIe+cSdmRJJCTMZ5GEJyLxNyC/NIOAOGk67Fa8GNpOttO3Xz/1bHpXFD/g=="
|
||||
},
|
||||
"node_modules/morgan": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
|
||||
"integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
|
||||
"dependencies": {
|
||||
"basic-auth": "~2.0.1",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~2.0.0",
|
||||
"on-finished": "~2.3.0",
|
||||
"on-headers": "~1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/morgan/node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
|
@ -3105,6 +3224,14 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/on-headers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
|
@ -3407,6 +3534,18 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/response-time": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/response-time/-/response-time-2.3.2.tgz",
|
||||
"integrity": "sha1-/6cbq5UtYvfB1Jt0NDVfvGjf/Fo=",
|
||||
"dependencies": {
|
||||
"depd": "~1.1.0",
|
||||
"on-headers": "~1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
|
@ -4794,6 +4933,19 @@
|
|||
"yargs": "^16.2.0"
|
||||
}
|
||||
},
|
||||
"@hapi/hoek": {
|
||||
"version": "9.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz",
|
||||
"integrity": "sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw=="
|
||||
},
|
||||
"@hapi/topo": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz",
|
||||
"integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==",
|
||||
"requires": {
|
||||
"@hapi/hoek": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"@improbable-eng/grpc-web": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@improbable-eng/grpc-web/-/grpc-web-0.14.1.tgz",
|
||||
|
@ -4861,6 +5013,24 @@
|
|||
"resolved": "https://registry.npmjs.org/@pythnetwork/pyth-sdk-js/-/pyth-sdk-js-0.1.0.tgz",
|
||||
"integrity": "sha512-fsGx2vkXncoIpsrcjx6WY7JN0R74YG/lX2UA1Wz/m6MPgJrde1LHkmikOSdMZUU3KkpWGHT1ZdYoW+Ikv7Nv9g=="
|
||||
},
|
||||
"@sideway/address": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz",
|
||||
"integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==",
|
||||
"requires": {
|
||||
"@hapi/hoek": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"@sideway/formula": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz",
|
||||
"integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg=="
|
||||
},
|
||||
"@sideway/pinpoint": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
|
||||
"integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="
|
||||
},
|
||||
"@solana/buffer-layout": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.0.tgz",
|
||||
|
@ -5112,6 +5282,11 @@
|
|||
"@types/range-parser": "*"
|
||||
}
|
||||
},
|
||||
"@types/hapi__joi": {
|
||||
"version": "16.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/hapi__joi/-/hapi__joi-16.0.12.tgz",
|
||||
"integrity": "sha512-xJYifuz59jXdWY5JMS15uvA3ycS3nQYOGqoIIE0+fwQ0qI3/4CxBc6RHsOTp6wk9M0NWEdpcTl02lOQOKMifbQ=="
|
||||
},
|
||||
"@types/lodash": {
|
||||
"version": "4.14.182",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
|
||||
|
@ -5127,6 +5302,14 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
||||
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw=="
|
||||
},
|
||||
"@types/morgan": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.3.tgz",
|
||||
"integrity": "sha512-BiLcfVqGBZCyNCnCH3F4o2GmDLrpy0HeBVnNlyZG4fo88ZiE9SoiBe3C+2ezuwbjlEyT+PDZ17//TAlRxAn75Q==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "16.11.27",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.27.tgz",
|
||||
|
@ -5142,6 +5325,15 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
|
||||
"integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw=="
|
||||
},
|
||||
"@types/response-time": {
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/response-time/-/response-time-2.3.5.tgz",
|
||||
"integrity": "sha512-4ANzp+I3K7sztFFAGPALWBvSl4ayaDSKzI2Bok+WNz+en2eB2Pvk6VCjR47PBXBWOkEg2r4uWpZOlXA5DNINOQ==",
|
||||
"requires": {
|
||||
"@types/express": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/serve-static": {
|
||||
"version": "1.13.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz",
|
||||
|
@ -5395,6 +5587,21 @@
|
|||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
|
||||
},
|
||||
"basic-auth": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"bech32": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz",
|
||||
|
@ -5936,6 +6143,16 @@
|
|||
"vary": "~1.1.2"
|
||||
}
|
||||
},
|
||||
"express-validation": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/express-validation/-/express-validation-4.0.1.tgz",
|
||||
"integrity": "sha512-DA8Nkl82ISo2Vi+eYjcokISz0eom1mcKW8t44Ns4MvsSEeg0WU0Ofoc0uWSZ0tv0J2Z5u7+jJ5nVmXN/tYRpfg==",
|
||||
"requires": {
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/hapi__joi": "16.x.x",
|
||||
"joi": "^17.6.0"
|
||||
}
|
||||
},
|
||||
"eyes": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
|
||||
|
@ -6208,6 +6425,18 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"joi": {
|
||||
"version": "17.6.0",
|
||||
"resolved": "https://registry.npmjs.org/joi/-/joi-17.6.0.tgz",
|
||||
"integrity": "sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw==",
|
||||
"requires": {
|
||||
"@hapi/hoek": "^9.0.0",
|
||||
"@hapi/topo": "^5.0.0",
|
||||
"@sideway/address": "^4.1.3",
|
||||
"@sideway/formula": "^3.0.0",
|
||||
"@sideway/pinpoint": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"js-base64": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.2.tgz",
|
||||
|
@ -6395,6 +6624,25 @@
|
|||
"resolved": "https://registry.npmjs.org/mobile-detect/-/mobile-detect-1.4.5.tgz",
|
||||
"integrity": "sha512-yc0LhH6tItlvfLBugVUEtgawwFU2sIe+cSdmRJJCTMZ5GEJyLxNyC/NIOAOGk67Fa8GNpOttO3Xz/1bHpXFD/g=="
|
||||
},
|
||||
"morgan": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
|
||||
"integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
|
||||
"requires": {
|
||||
"basic-auth": "~2.0.1",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~2.0.0",
|
||||
"on-finished": "~2.3.0",
|
||||
"on-headers": "~1.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
|
@ -6441,6 +6689,11 @@
|
|||
"ee-first": "1.1.1"
|
||||
}
|
||||
},
|
||||
"on-headers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
|
@ -6671,6 +6924,15 @@
|
|||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"response-time": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/response-time/-/response-time-2.3.2.tgz",
|
||||
"integrity": "sha1-/6cbq5UtYvfB1Jt0NDVfvGjf/Fo=",
|
||||
"requires": {
|
||||
"depd": "~1.1.0",
|
||||
"on-headers": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
|
|
|
@ -24,12 +24,17 @@
|
|||
"@pythnetwork/pyth-sdk-js": "^0.1.0",
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/morgan": "^1.9.3",
|
||||
"@types/response-time": "^2.3.5",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^10.0.0",
|
||||
"ethers": "^5.4.4",
|
||||
"express": "^4.17.2",
|
||||
"express-validation": "^4.0.1",
|
||||
"http-status-codes": "^2.2.0",
|
||||
"morgan": "^1.10.0",
|
||||
"prom-client": "^14.0.1",
|
||||
"response-time": "^2.3.2",
|
||||
"winston": "^3.3.3"
|
||||
},
|
||||
"directories": {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Time in seconds
|
||||
export type TimestampInSec = number;
|
||||
export type DurationInSec = number;
|
||||
export type DurationInMs = number;
|
||||
|
||||
export function sleep(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { stat } from "fs";
|
||||
import http = require("http");
|
||||
import client = require("prom-client");
|
||||
import { DurationInSec } from "./helpers";
|
||||
import { DurationInMs, DurationInSec } from "./helpers";
|
||||
import { logger } from "./logging";
|
||||
|
||||
// NOTE: To create a new metric:
|
||||
|
@ -8,7 +9,7 @@ import { logger } from "./logging";
|
|||
// 2) Create a method to set the metric to a value (such as `incIncoming` function below)
|
||||
// 3) Register the metric using `register.registerMetric` function.
|
||||
|
||||
const SERVICE_PREFIX = "price_service__";
|
||||
const SERVICE_PREFIX = "pyth__price_service__";
|
||||
|
||||
export class PromClient {
|
||||
private register = new client.Registry();
|
||||
|
@ -19,41 +20,17 @@ export class PromClient {
|
|||
name: `${SERVICE_PREFIX}vaas_received`,
|
||||
help: "number of Pyth VAAs received",
|
||||
});
|
||||
private apiLatestVaaRequestsCounter = new client.Counter({
|
||||
name: `${SERVICE_PREFIX}api_latest_vaa_requests_received`,
|
||||
help: "Number of requests for latest vaa of a price feed"
|
||||
private apiResponseTimeSummary = new client.Summary({
|
||||
name: `${SERVICE_PREFIX}api_response_time_ms`,
|
||||
help: "Response time of a VAA",
|
||||
labelNames: ["path", "status"]
|
||||
});
|
||||
private apiLatestVaaNotFoundResponseCounter = new client.Counter({
|
||||
name: `${SERVICE_PREFIX}api_latest_vaa_not_found_response`,
|
||||
help: "Number of not found responses for latest vaa of a price feed request"
|
||||
});
|
||||
private apiLatestVaaSuccessResponseCounter = new client.Counter({
|
||||
name: `${SERVICE_PREFIX}api_latest_vaa_success_response`,
|
||||
help: "Number of successful responses for latest vaa of a price feed request"
|
||||
});
|
||||
private apiLatestVaaFreshnessHistogram = new client.Histogram({
|
||||
name: `${SERVICE_PREFIX}api_latest_vaa_freshness`,
|
||||
private apiRequestsPriceFreshnessHistogram = new client.Histogram({
|
||||
name: `${SERVICE_PREFIX}api_requests_price_freshness_seconds`,
|
||||
help: "Freshness time of Vaa (time difference of Vaa and request time)",
|
||||
buckets: [1, 5, 10, 15, 30, 60, 120, 180]
|
||||
buckets: [1, 5, 10, 15, 30, 60, 120, 180],
|
||||
labelNames: ["path", "price_id"]
|
||||
});
|
||||
private apiLatestPriceFeedRequestsCounter = new client.Counter({
|
||||
name: `${SERVICE_PREFIX}api_latest_price_feed_requests_received`,
|
||||
help: "Number of requests for latest Price Feed of a price feed id"
|
||||
});
|
||||
private apiLatestPriceFeedNotFoundResponseCounter = new client.Counter({
|
||||
name: `${SERVICE_PREFIX}api_latest_price_feed_not_found_response`,
|
||||
help: "Number of not found responses for latest Price Feed of a price feed id"
|
||||
});
|
||||
private apiLatestPriceFeedSuccessResponseCounter = new client.Counter({
|
||||
name: `${SERVICE_PREFIX}api_latest_price_feed_success_response`,
|
||||
help: "Number of successful responses for latest vaa of a price feed id"
|
||||
});
|
||||
private apiLatestPriceFeedFreshnessHistogram = new client.Histogram({
|
||||
name: `${SERVICE_PREFIX}api_latest_price_feed_freshness`,
|
||||
help: "Freshness time of Vaa (time difference of retrieval time and request time)",
|
||||
buckets: [1, 5, 10, 15, 30, 60, 120, 180]
|
||||
});
|
||||
|
||||
// End metrics
|
||||
|
||||
private server = http.createServer(async (req, res) => {
|
||||
|
@ -72,16 +49,8 @@ export class PromClient {
|
|||
this.collectDefaultMetrics({ register: this.register, prefix: SERVICE_PREFIX });
|
||||
// Register each metric
|
||||
this.register.registerMetric(this.receivedVaaCounter);
|
||||
|
||||
this.register.registerMetric(this.apiLatestVaaRequestsCounter);
|
||||
this.register.registerMetric(this.apiLatestVaaNotFoundResponseCounter);
|
||||
this.register.registerMetric(this.apiLatestVaaSuccessResponseCounter);
|
||||
this.register.registerMetric(this.apiLatestVaaFreshnessHistogram);
|
||||
|
||||
this.register.registerMetric(this.apiLatestPriceFeedRequestsCounter);
|
||||
this.register.registerMetric(this.apiLatestPriceFeedNotFoundResponseCounter);
|
||||
this.register.registerMetric(this.apiLatestPriceFeedSuccessResponseCounter);
|
||||
this.register.registerMetric(this.apiLatestPriceFeedFreshnessHistogram);
|
||||
this.register.registerMetric(this.apiResponseTimeSummary)
|
||||
this.register.registerMetric(this.apiRequestsPriceFreshnessHistogram);
|
||||
// End registering metric
|
||||
|
||||
logger.info("prometheus client listening on port " + config.port);
|
||||
|
@ -92,35 +61,17 @@ export class PromClient {
|
|||
this.receivedVaaCounter.inc();
|
||||
}
|
||||
|
||||
incApiLatestVaaRequests() {
|
||||
this.apiLatestVaaRequestsCounter.inc();
|
||||
addResponseTime(path: string, status: number, duration: DurationInMs) {
|
||||
this.apiResponseTimeSummary.observe({
|
||||
path: path,
|
||||
status: status
|
||||
}, duration);
|
||||
}
|
||||
|
||||
incApiLatestVaaNotFoundResponse() {
|
||||
this.apiLatestVaaNotFoundResponseCounter.inc();
|
||||
}
|
||||
|
||||
incApiLatestVaaSuccessResponse() {
|
||||
this.apiLatestVaaSuccessResponseCounter.inc();
|
||||
}
|
||||
|
||||
addApiLatestVaaFreshness(duration: DurationInSec) {
|
||||
this.apiLatestVaaFreshnessHistogram.observe(duration);
|
||||
}
|
||||
|
||||
incApiLatestPriceFeedRequests() {
|
||||
this.apiLatestPriceFeedRequestsCounter.inc();
|
||||
}
|
||||
|
||||
incApiLatestPriceFeedNotFoundResponse() {
|
||||
this.apiLatestPriceFeedNotFoundResponseCounter.inc();
|
||||
}
|
||||
|
||||
incApiLatestPriceFeedSuccessResponse() {
|
||||
this.apiLatestPriceFeedSuccessResponseCounter.inc();
|
||||
}
|
||||
|
||||
addApiLatestPriceFeedFreshness(duration: DurationInSec) {
|
||||
this.apiLatestPriceFeedFreshnessHistogram.observe(duration);
|
||||
addApiRequestsPriceFreshness(path: string, priceId: string, duration: DurationInSec) {
|
||||
this.apiRequestsPriceFreshnessHistogram.observe({
|
||||
path: path,
|
||||
price_id: priceId,
|
||||
}, duration);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
import express from "express";
|
||||
import cors from "cors";
|
||||
import { Request, Response } from "express";
|
||||
import morgan from "morgan";
|
||||
import responseTime from "response-time";
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { PriceFeedPriceInfo } from "./listen";
|
||||
import { logger } from "./logging";
|
||||
import { PromClient } from "./promClient";
|
||||
import { DurationInSec } from "./helpers";
|
||||
import { DurationInMs, DurationInSec } from "./helpers";
|
||||
import { StatusCodes } from "http-status-codes";
|
||||
import { validate, ValidationError, Joi, schema } from "express-validation";
|
||||
|
||||
const MORGAN_LOG_FORMAT = ':remote-addr - :remote-user ":method :url HTTP/:http-version"' +
|
||||
' :status :res[content-length] :response-time ms ":referrer" ":user-agent"';
|
||||
|
||||
export class RestAPI {
|
||||
private port: number;
|
||||
|
@ -13,7 +19,7 @@ export class RestAPI {
|
|||
private isReady: (() => boolean) | undefined;
|
||||
private promClient: PromClient | undefined;
|
||||
|
||||
constructor(config: { port: number; },
|
||||
constructor(config: { port: number; },
|
||||
priceFeedVaaInfo: PriceFeedPriceInfo,
|
||||
isReady?: () => boolean,
|
||||
promClient?: PromClient) {
|
||||
|
@ -28,82 +34,75 @@ export class RestAPI {
|
|||
const app = express();
|
||||
app.use(cors());
|
||||
|
||||
const winstonStream = {
|
||||
write: (text: string) => {
|
||||
logger.info(text);
|
||||
}
|
||||
};
|
||||
|
||||
app.use(morgan(MORGAN_LOG_FORMAT, { stream: winstonStream }));
|
||||
|
||||
app.use(responseTime((req: Request, res: Response, time: DurationInMs) => {
|
||||
if (res.statusCode !== StatusCodes.NOT_FOUND) {
|
||||
this.promClient?.addResponseTime(req.path, res.statusCode, time);
|
||||
}
|
||||
}))
|
||||
|
||||
app.listen(this.port, () =>
|
||||
logger.debug("listening on REST port " + this.port)
|
||||
);
|
||||
|
||||
let endpoints: string[] = [];
|
||||
|
||||
const latestVaaBytesInputSchema: schema = {
|
||||
query: Joi.object({
|
||||
id: Joi.string().regex(/^[a-f0-9]{64}$/)
|
||||
})
|
||||
}
|
||||
app.get("/latest_vaa_bytes", validate(latestVaaBytesInputSchema), (req: Request, res: Response) => {
|
||||
let priceId = req.query.id as string;
|
||||
|
||||
app.get("/latest_vaa_bytes/:price_feed_id", (req: Request, res: Response) => {
|
||||
this.promClient?.incApiLatestVaaRequests();
|
||||
logger.info(`Received latest_vaa_bytes request for ${req.params.price_feed_id}`)
|
||||
|
||||
let latestPriceInfo = this.priceFeedVaaInfo.getLatestPriceInfo(req.params.price_feed_id);
|
||||
let latestPriceInfo = this.priceFeedVaaInfo.getLatestPriceInfo(priceId);
|
||||
|
||||
if (latestPriceInfo === undefined) {
|
||||
this.promClient?.incApiLatestVaaNotFoundResponse();
|
||||
res.sendStatus(StatusCodes.NOT_FOUND);
|
||||
res.sendStatus(StatusCodes.BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
this.promClient?.incApiLatestVaaSuccessResponse();
|
||||
|
||||
const freshness: DurationInSec = (new Date).getTime()/1000 - latestPriceInfo.receiveTime;
|
||||
this.promClient?.addApiLatestVaaFreshness(freshness);
|
||||
const freshness: DurationInSec = (new Date).getTime() / 1000 - latestPriceInfo.receiveTime;
|
||||
this.promClient?.addApiRequestsPriceFreshness(req.path, priceId, freshness);
|
||||
|
||||
res.send(latestPriceInfo.vaaBytes);
|
||||
});
|
||||
endpoints.push("latest_vaa_bytes/<price_feed_id>");
|
||||
endpoints.push("latest_vaa_bytes?id=<price_feed_id>");
|
||||
|
||||
// It will be called with query param `id` such as: `/latest_price_feed?id=xyz&id=abc
|
||||
app.get("/latest_price_feed", (req: Request, res: Response) => {
|
||||
this.promClient?.incApiLatestPriceFeedRequests();
|
||||
logger.info(`Received latest_price_feed request for query: ${req.query}`);
|
||||
const latestPriceFeedInputSchema: schema = {
|
||||
query: Joi.object({
|
||||
id: Joi.array().items(Joi.string().regex(/^[a-f0-9]{64}$/))
|
||||
})
|
||||
}
|
||||
app.get("/latest_price_feed", validate(latestPriceFeedInputSchema), (req: Request, res: Response) => {
|
||||
let priceIds = req.query.id as string[];
|
||||
|
||||
if (req.query.id === undefined) {
|
||||
res.status(StatusCodes.BAD_REQUEST).send("No id is provided");
|
||||
return;
|
||||
}
|
||||
|
||||
let priceIds: string[] = [];
|
||||
if (typeof(req.query.id) === "string") {
|
||||
priceIds.push(req.query.id);
|
||||
} else if (Array.isArray(req.query.id)) {
|
||||
for (let entry of req.query.id) {
|
||||
if (typeof(entry) === "string") {
|
||||
priceIds.push(entry);
|
||||
} else {
|
||||
res.status(StatusCodes.BAD_REQUEST).send("id is expected to be a hex string or an array of hex strings");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res.status(StatusCodes.BAD_REQUEST).send("id is expected to be a hex string or an array of hex strings");
|
||||
return;
|
||||
}
|
||||
|
||||
let responseJson = []
|
||||
let responseJson = [];
|
||||
|
||||
for (let id of priceIds) {
|
||||
let latestPriceInfo = this.priceFeedVaaInfo.getLatestPriceInfo(id);
|
||||
|
||||
if (latestPriceInfo === undefined) {
|
||||
this.promClient?.incApiLatestPriceFeedNotFoundResponse();
|
||||
res.status(StatusCodes.NOT_FOUND).send(`Price Feed with id ${id} not found`);
|
||||
res.status(StatusCodes.BAD_REQUEST).send(`Price Feed with id ${id} not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
const freshness: DurationInSec = (new Date).getTime()/1000 - latestPriceInfo.receiveTime;
|
||||
this.promClient?.addApiLatestPriceFeedFreshness(freshness);
|
||||
|
||||
|
||||
const freshness: DurationInSec = (new Date).getTime() / 1000 - latestPriceInfo.receiveTime;
|
||||
this.promClient?.addApiRequestsPriceFreshness(req.path, id, freshness);
|
||||
|
||||
responseJson.push(latestPriceInfo.priceFeed.toJson());
|
||||
}
|
||||
|
||||
this.promClient?.incApiLatestPriceFeedSuccessResponse();
|
||||
|
||||
res.json(responseJson);
|
||||
});
|
||||
endpoints.push("latest_price_feed/<price_feed_id>");
|
||||
endpoints.push("latest_price_feed?id[]=<price_feed_id>&id[]=<price_feed_id_2>&..");
|
||||
|
||||
|
||||
app.get("/ready", (_, res: Response) => {
|
||||
|
@ -124,5 +123,13 @@ export class RestAPI {
|
|||
app.get("/", (_, res: Response) =>
|
||||
res.json(endpoints)
|
||||
);
|
||||
|
||||
app.use(function(err: any, _: Request, res: Response, next: NextFunction) {
|
||||
if (err instanceof ValidationError) {
|
||||
return res.status(err.statusCode).json(err);
|
||||
}
|
||||
|
||||
return next(err);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue