diff --git a/src/shadowbox/server/manager_metrics.ts b/src/shadowbox/server/manager_metrics.ts index 437727073..dd0e923aa 100644 --- a/src/shadowbox/server/manager_metrics.ts +++ b/src/shadowbox/server/manager_metrics.ts @@ -17,11 +17,8 @@ import {DataUsageByUser, DataUsageTimeframe} from '../model/metrics'; export type TunnelTimeDimension = 'access_key' | 'country' | 'asn'; -interface TunneTimeRequest { - params: { - sinceUnixTimestamp: number; - dimensions?: TunnelTimeDimension[]; - }; +interface TunnelTimeRequest { + hours: number; } interface TunnelTimeResponse { @@ -35,7 +32,7 @@ interface TunnelTimeResponse { export interface ManagerMetrics { getOutboundByteTransfer(timeframe: DataUsageTimeframe): Promise; - getTunnelTime(request: TunneTimeRequest): Promise; + getTunnelTimeByLocation(request: TunnelTimeRequest): Promise; } // Reads manager metrics from a Prometheus instance. @@ -60,19 +57,14 @@ export class PrometheusManagerMetrics implements ManagerMetrics { return {bytesTransferredByUserId: usage}; } - async getTunnelTime({ - params: {dimensions, sinceUnixTimestamp}, - }: TunneTimeRequest): Promise { - const timeExpression = `[${Math.round(Date.now() / 1000) - sinceUnixTimestamp}s]`; - const dimensionsExpression = - dimensions && dimensions.length ? ` by (${dimensions.join()})` : ''; - const prometheusQuery = `sum(increase(shadowsocks_tunnel_time_seconds${timeExpression}))${dimensionsExpression}`; - const {result} = await this.prometheusClient.query(prometheusQuery); + async getTunnelTimeByLocation({hours}: TunnelTimeRequest): Promise { + const {result} = await this.prometheusClient.query( + `sum(increase(shadowsocks_tunnel_time_seconds_per_location[${hours}h])) by (location, asn, asorg)` + ); return result.map((entry) => ({ - access_key: entry.metric['access_key'], - country: entry.metric['country'], - asn: entry.metric['asn'] ? parseInt(entry.metric['asn']) : undefined, + location: entry.metric['location'], + asn: entry.metric['asn'] ? parseInt(entry.metric['asn'], 10) : undefined, tunnel_time: {hours: Math.round(parseFloat(entry.value[1]) / 60 / 60)}, })); } diff --git a/src/shadowbox/server/manager_service.ts b/src/shadowbox/server/manager_service.ts index 2d793a007..329f99aa7 100644 --- a/src/shadowbox/server/manager_service.ts +++ b/src/shadowbox/server/manager_service.ts @@ -163,7 +163,10 @@ export function bindService( ); apiServer.get(`${apiPrefix}/metrics/transfer`, service.getDataUsage.bind(service)); - apiServer.get(`${apiPrefix}/metrics/tunnel`, service.getTunnelTime.bind(service)); + apiServer.get( + `${apiPrefix}/metrics/tunnel/location`, + service.getTunnelTimeByLocation.bind(service) + ); apiServer.get(`${apiPrefix}/metrics/enabled`, service.getShareMetrics.bind(service)); apiServer.put(`${apiPrefix}/metrics/enabled`, service.setShareMetrics.bind(service)); @@ -607,46 +610,10 @@ export class ShadowsocksManagerService { } } - async getTunnelTime(req: RequestType, res: ResponseType, next: restify.Next) { + async getTunnelTimeByLocation(req: RequestType, res: ResponseType, next: restify.Next) { try { logging.debug(`getTunnelTime request ${JSON.stringify(req.query)}`); - const sinceUnixTimestamp = parseInt(req.query.since as string, 10); - if ( - typeof sinceUnixTimestamp !== 'number' || - sinceUnixTimestamp <= 0 || - isNaN(sinceUnixTimestamp) || - sinceUnixTimestamp > Date.now() / 1000 - ) { - return next( - new restifyErrors.InvalidArgumentError( - {statusCode: 400}, - 'Parameter `since` must be a unix timestamp in the past. Got: ' + req.query.since - ) - ); - } - const dimensions = (req.query.dimensions as string)?.split(','); - if (!Array.isArray(dimensions)) { - return next( - new restifyErrors.InvalidArgumentError( - {statusCode: 400}, - 'Parameter `dimensions` must be an array. Got: ' + req.query.dimensions - ) - ); - } - - if (dimensions.some((d) => d !== 'access_key' && d !== 'country' && d !== 'asn')) { - return next( - new restifyErrors.InvalidArgumentError( - {statusCode: 400}, - 'Parameter `dimensions` must be an array containing only "access_key", "country" and "asn". Got: ' + - req.query.dimensions - ) - ); - } - - const response = await this.managerMetrics.getTunnelTime({ - params: {sinceUnixTimestamp, dimensions: dimensions as TunnelTimeDimension[]}, - }); + const response = await this.managerMetrics.getTunnelTimeByLocation({hours: 30 * 24}); res.send(HttpSuccess.OK, response); logging.debug(`getTunnelTime response ${JSON.stringify(response)}`); return next();