Skip to content

Commit 0b3783c

Browse files
committed
chore: enhance bandwidth stats endpoints with new user usage commands and DTOs
1 parent add1c8a commit 0b3783c

17 files changed

+547
-6
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { z } from 'zod';
2+
3+
import { BANDWIDTH_STATS_ROUTES, REST_API } from '../../../api';
4+
import { getEndpointDetails } from '../../../constants';
5+
6+
export namespace GetStatsNodeUsersUsageCommand {
7+
export const url = REST_API.BANDWIDTH_STATS.NODES.GET_USERS;
8+
export const TSQ_url = url(':uuid');
9+
10+
export const endpointDetails = getEndpointDetails(
11+
BANDWIDTH_STATS_ROUTES.NODES.GET_USERS(':uuid'),
12+
'get',
13+
'Get Node Users Usage by Node UUID',
14+
);
15+
16+
export const RequestQuerySchema = z.object({
17+
start: z.string().date(),
18+
end: z.string().date(),
19+
topUsersLimit: z.coerce.number().min(1).default(100),
20+
});
21+
22+
export type RequestQuery = z.infer<typeof RequestQuerySchema>;
23+
24+
export const RequestSchema = z.object({
25+
uuid: z.string().uuid(),
26+
});
27+
28+
export type Request = z.infer<typeof RequestSchema>;
29+
30+
export const ResponseSchema = z.object({
31+
response: z.object({
32+
categories: z.array(z.string()),
33+
sparklineData: z.array(z.number()),
34+
topUsers: z.array(
35+
z.object({
36+
color: z.string(),
37+
username: z.string(),
38+
total: z.number(),
39+
}),
40+
),
41+
}),
42+
});
43+
44+
export type Response = z.infer<typeof ResponseSchema>;
45+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
export * from './get-stats-node-users-usage.command';
12
export * from './get-stats-nodes-realtime-usage.command';
23
export * from './get-stats-nodes-usage.command';

libs/contract/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@remnawave/backend-contract",
3-
"version": "2.3.69",
3+
"version": "2.3.73",
44
"public": true,
55
"license": "AGPL-3.0-only",
66
"description": "A contract library for Remnawave Backend. It can be used in backend and frontend.",

src/common/utils/get-date-range-array.util.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ export function getDateRangeArrayUtil(
88
endDate: Date;
99
dates: string[];
1010
} {
11-
const startDate = dayjs(start).utc().startOf('day');
12-
const endDate = dayjs(end).utc().startOf('day');
11+
const startDate = dayjs.utc(start).startOf('day');
12+
const endDate = dayjs.utc(end).endOf('day');
1313
const days = endDate.diff(startDate, 'day') + 1;
1414

1515
return {

src/modules/nodes-user-usage-history/bandwidth-stats-nodes.controller.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { RolesGuard } from '@common/guards/roles';
1010
import {
1111
GetLegacyStatsNodeUserUsageCommand,
1212
GetStatsNodesRealtimeUsageCommand,
13+
GetStatsNodeUsersUsageCommand,
1314
} from '@libs/contracts/commands';
1415
import { BANDWIDTH_STATS_NODES_CONTROLLER, CONTROLLERS_INFO } from '@libs/contracts/api';
1516
import { ROLE } from '@libs/contracts/constants';
@@ -19,6 +20,9 @@ import {
1920
GetLegacyStatsNodesUsersUsageRequestQueryDto,
2021
GetLegacyStatsNodesUsersUsageResponseDto,
2122
GetStatsNodesRealtimeUsageResponseDto,
23+
GetStatsNodeUsersUsageRequestDto,
24+
GetStatsNodeUsersUsageRequestQueryDto,
25+
GetStatsNodeUsersUsageResponseDto,
2226
} from './dtos';
2327
import {
2428
GetLegacyStatsNodesUsersUsageResponseModel,
@@ -88,4 +92,47 @@ export class BandwidthStatsNodesController {
8892
response: data.map((item) => new GetStatsNodesRealtimeUsageResponseModel(item)),
8993
};
9094
}
95+
96+
@ApiOkResponse({
97+
type: GetStatsNodeUsersUsageResponseDto,
98+
description: 'Stats node users usage fetched successfully',
99+
})
100+
@ApiParam({ name: 'uuid', type: String, description: 'UUID of the node', required: true })
101+
@ApiQuery({
102+
name: 'end',
103+
type: Date,
104+
description: 'End date',
105+
required: true,
106+
})
107+
@ApiQuery({
108+
name: 'start',
109+
type: Date,
110+
description: 'Start date',
111+
required: true,
112+
})
113+
@ApiQuery({
114+
name: 'topUsersLimit',
115+
type: Number,
116+
description: 'Limit of top users to return',
117+
required: true,
118+
})
119+
@Endpoint({
120+
command: GetStatsNodeUsersUsageCommand,
121+
httpCode: HttpStatus.OK,
122+
})
123+
async getStatsNodeUsersUsage(
124+
@Query() query: GetStatsNodeUsersUsageRequestQueryDto,
125+
@Param() paramData: GetStatsNodeUsersUsageRequestDto,
126+
): Promise<GetStatsNodeUsersUsageResponseDto> {
127+
const result = await this.nodesUserUsageHistoryService.getStatsNodesUsersUsage(
128+
paramData.uuid,
129+
query.start,
130+
query.end,
131+
query.topUsersLimit,
132+
);
133+
const data = errorHandler(result);
134+
return {
135+
response: data,
136+
};
137+
}
91138
}

src/modules/nodes-user-usage-history/bandwidth-stats-users.controller.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,20 @@ import { errorHandler } from '@common/helpers/error-handler.helper';
1414
import { Endpoint } from '@common/decorators/base-endpoint';
1515
import { Roles } from '@common/decorators/roles/roles';
1616
import { RolesGuard } from '@common/guards/roles';
17+
import { GetLegacyStatsUserUsageCommand, GetStatsUserUsageCommand } from '@libs/contracts/commands';
1718
import { BANDWIDTH_STATS_USERS_CONTROLLER, CONTROLLERS_INFO } from '@libs/contracts/api';
18-
import { GetLegacyStatsUserUsageCommand } from '@libs/contracts/commands';
1919
import { ROLE } from '@libs/contracts/constants';
2020

2121
import {
2222
GetLegacyStatsUserUsageRequestDto,
2323
GetLegacyStatsUserUsageRequestQueryDto,
2424
GetLegacyStatsUserUsageResponseDto,
2525
} from './dtos/get-legacy-stats-users-usage.dto';
26+
import {
27+
GetStatsUserUsageRequestDto,
28+
GetStatsUserUsageRequestQueryDto,
29+
GetStatsUserUsageResponseDto,
30+
} from './dtos';
2631
import { NodesUserUsageHistoryService } from './nodes-user-usage-history.service';
2732
import { GetLegacyStatsUserUsageResponseModel } from './models';
2833

@@ -74,4 +79,41 @@ export class BandwidthStatsUsersController {
7479
response: data.map((item) => new GetLegacyStatsUserUsageResponseModel(item)),
7580
};
7681
}
82+
83+
@ApiOkResponse({
84+
type: GetStatsUserUsageResponseDto,
85+
description: 'Stats user usage fetched successfully',
86+
})
87+
@ApiParam({ name: 'uuid', type: String, description: 'UUID of the user', required: true })
88+
@ApiQuery({
89+
name: 'end',
90+
type: Date,
91+
description: 'End date',
92+
required: true,
93+
})
94+
@ApiQuery({
95+
name: 'start',
96+
type: Date,
97+
description: 'Start date',
98+
required: true,
99+
})
100+
@Endpoint({
101+
command: GetStatsUserUsageCommand,
102+
httpCode: HttpStatus.OK,
103+
})
104+
async getStatsNodesUsage(
105+
@Query() query: GetStatsUserUsageRequestQueryDto,
106+
@Param() paramData: GetStatsUserUsageRequestDto,
107+
): Promise<GetStatsUserUsageResponseDto> {
108+
const result = await this.nodesUserUsageHistoryService.getStatsUserUsage(
109+
paramData.uuid,
110+
query.start,
111+
query.end,
112+
);
113+
114+
const data = errorHandler(result);
115+
return {
116+
response: data,
117+
};
118+
}
77119
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { createZodDto } from 'nestjs-zod';
2+
3+
import { GetStatsNodeUsersUsageCommand } from '@contract/commands';
4+
5+
export class GetStatsNodeUsersUsageRequestQueryDto extends createZodDto(
6+
GetStatsNodeUsersUsageCommand.RequestQuerySchema,
7+
) {}
8+
9+
export class GetStatsNodeUsersUsageRequestDto extends createZodDto(
10+
GetStatsNodeUsersUsageCommand.RequestSchema,
11+
) {}
12+
13+
export class GetStatsNodeUsersUsageResponseDto extends createZodDto(
14+
GetStatsNodeUsersUsageCommand.ResponseSchema,
15+
) {}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { createZodDto } from 'nestjs-zod';
2+
3+
import { GetStatsUserUsageCommand } from '@contract/commands';
4+
5+
export class GetStatsUserUsageRequestQueryDto extends createZodDto(
6+
GetStatsUserUsageCommand.RequestQuerySchema,
7+
) {}
8+
9+
export class GetStatsUserUsageRequestDto extends createZodDto(
10+
GetStatsUserUsageCommand.RequestSchema,
11+
) {}
12+
13+
export class GetStatsUserUsageResponseDto extends createZodDto(
14+
GetStatsUserUsageCommand.ResponseSchema,
15+
) {}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
export * from './get-legacy-stats-nodes-users-usage.dto';
2+
export * from './get-stats-node-users-usage.dto';
23
export * from './get-stats-nodes-realtime-usage.dto';
4+
export * from './get-stats-user-usage.dto';
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
export interface IGetUniversalSeries {
2+
uuid: string;
3+
name: string;
4+
countryCode: string;
5+
total: bigint;
6+
data: bigint[];
7+
}
8+
9+
export interface IGetUniversalTopNode {
10+
uuid: string;
11+
name: string;
12+
countryCode: string;
13+
total: bigint;
14+
}
15+
16+
export interface IGetUniversalSeriesConverted {
17+
uuid: string;
18+
name: string;
19+
color: string;
20+
countryCode: string;
21+
total: number;
22+
data: number[];
23+
}
24+
25+
export interface IGetUniversalTopNodeConverted {
26+
uuid: string;
27+
color: string;
28+
name: string;
29+
countryCode: string;
30+
total: number;
31+
}

0 commit comments

Comments
 (0)