mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	Turns out we've been trying to return API token data in instance stats for a while, but that the serialization has failed. Serializing a JS map just yields an empty object. This PR fixes that serialization and also adds API tokens to the instance stats schema (it wasn't before, but we did return it). Adding it to the schema is also part of making resource usage visible as part of the soft limits project.
		
			
				
	
	
		
			171 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			171 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { Parser } from 'json2csv';
 | |
| import type { Response } from 'express';
 | |
| import type { AuthedRequest } from '../../types/core';
 | |
| import type { IUnleashServices } from '../../types/services';
 | |
| import type { IUnleashConfig } from '../../types/option';
 | |
| import Controller from '../controller';
 | |
| import { NONE } from '../../types/permissions';
 | |
| import type {
 | |
|     InstanceStatsService,
 | |
|     InstanceStatsSigned,
 | |
| } from '../../features/instance-stats/instance-stats-service';
 | |
| import type { OpenApiService } from '../../services/openapi-service';
 | |
| import {
 | |
|     createCsvResponseSchema,
 | |
|     createResponseSchema,
 | |
| } from '../../openapi/util/create-response-schema';
 | |
| import type { InstanceAdminStatsSchema } from '../../openapi';
 | |
| import { serializeDates } from '../../types';
 | |
| 
 | |
| class InstanceAdminController extends Controller {
 | |
|     private instanceStatsService: InstanceStatsService;
 | |
| 
 | |
|     private openApiService: OpenApiService;
 | |
| 
 | |
|     private jsonCsvParser: Parser;
 | |
| 
 | |
|     constructor(
 | |
|         config: IUnleashConfig,
 | |
|         {
 | |
|             instanceStatsService,
 | |
|             openApiService,
 | |
|         }: Pick<IUnleashServices, 'instanceStatsService' | 'openApiService'>,
 | |
|     ) {
 | |
|         super(config);
 | |
|         this.jsonCsvParser = new Parser();
 | |
|         this.openApiService = openApiService;
 | |
|         this.instanceStatsService = instanceStatsService;
 | |
| 
 | |
|         this.route({
 | |
|             method: 'get',
 | |
|             path: '/statistics/csv',
 | |
|             handler: this.getStatisticsCSV,
 | |
|             permission: NONE,
 | |
|             middleware: [
 | |
|                 openApiService.validPath({
 | |
|                     tags: ['Instance Admin'],
 | |
|                     summary: 'Instance usage statistics',
 | |
|                     description:
 | |
|                         'Provides statistics about various features of Unleash to allow for reporting of usage for self-hosted customers. The response contains data such as the number of users, groups, features, strategies, versions, etc.',
 | |
|                     operationId: 'getInstanceAdminStatsCsv',
 | |
|                     responses: {
 | |
|                         200: createCsvResponseSchema(
 | |
|                             'instanceAdminStatsSchemaCsv',
 | |
|                             this.jsonCsvParser.parse(
 | |
|                                 this.instanceStatsExample(),
 | |
|                             ),
 | |
|                         ),
 | |
|                     },
 | |
|                 }),
 | |
|             ],
 | |
|         });
 | |
| 
 | |
|         this.route({
 | |
|             method: 'get',
 | |
|             path: '/statistics',
 | |
|             handler: this.getStatistics,
 | |
|             permission: NONE,
 | |
|             middleware: [
 | |
|                 openApiService.validPath({
 | |
|                     tags: ['Instance Admin'],
 | |
|                     operationId: 'getInstanceAdminStats',
 | |
|                     summary: 'Instance usage statistics',
 | |
|                     description:
 | |
|                         'Provides statistics about various features of Unleash to allow for reporting of usage for self-hosted customers. The response contains data such as the number of users, groups, features, strategies, versions, etc.',
 | |
|                     responses: {
 | |
|                         200: createResponseSchema('instanceAdminStatsSchema'),
 | |
|                     },
 | |
|                     deprecated: true,
 | |
|                 }),
 | |
|             ],
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     instanceStatsExample(): InstanceStatsSigned {
 | |
|         return {
 | |
|             OIDCenabled: true,
 | |
|             SAMLenabled: false,
 | |
|             clientApps: [
 | |
|                 { range: 'allTime', count: 15 },
 | |
|                 { range: '30d', count: 9 },
 | |
|                 { range: '7d', count: 5 },
 | |
|             ],
 | |
|             contextFields: 6,
 | |
|             environments: 2,
 | |
|             featureExports: 0,
 | |
|             featureImports: 0,
 | |
|             featureToggles: 29,
 | |
|             archivedFeatureToggles: 10,
 | |
|             groups: 3,
 | |
|             instanceId: 'ed3861ae-78f9-4e8c-8e57-b57efc15f82b',
 | |
|             projects: 4,
 | |
|             roles: 5,
 | |
|             customRootRoles: 2,
 | |
|             customRootRolesInUse: 1,
 | |
|             segments: 2,
 | |
|             strategies: 8,
 | |
|             sum: 'some-sha256-hash',
 | |
|             timestamp: new Date(2023, 6, 12, 10, 0, 0, 0),
 | |
|             users: 10,
 | |
|             serviceAccounts: 2,
 | |
|             apiTokens: new Map([]),
 | |
|             versionEnterprise: '5.1.7',
 | |
|             versionOSS: '5.1.7',
 | |
|             activeUsers: {
 | |
|                 last90: 15,
 | |
|                 last60: 12,
 | |
|                 last30: 10,
 | |
|                 last7: 5,
 | |
|             },
 | |
|             productionChanges: {
 | |
|                 last30: 100,
 | |
|                 last60: 200,
 | |
|                 last90: 200,
 | |
|             },
 | |
|             previousDayMetricsBucketsCount: {
 | |
|                 variantCount: 100,
 | |
|                 enabledCount: 200,
 | |
|             },
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     private serializeStats(
 | |
|         instanceStats: InstanceStatsSigned,
 | |
|     ): InstanceAdminStatsSchema {
 | |
|         const apiTokensObj = Object.fromEntries(
 | |
|             instanceStats.apiTokens.entries(),
 | |
|         );
 | |
|         return serializeDates({
 | |
|             ...instanceStats,
 | |
|             apiTokens: apiTokensObj,
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     async getStatistics(
 | |
|         _: AuthedRequest,
 | |
|         res: Response<InstanceAdminStatsSchema>,
 | |
|     ): Promise<void> {
 | |
|         const instanceStats = await this.instanceStatsService.getSignedStats();
 | |
|         res.json(this.serializeStats(instanceStats));
 | |
|     }
 | |
| 
 | |
|     async getStatisticsCSV(
 | |
|         _: AuthedRequest,
 | |
|         res: Response<InstanceAdminStatsSchema>,
 | |
|     ): Promise<void> {
 | |
|         const instanceStats = await this.instanceStatsService.getSignedStats();
 | |
|         const fileName = `unleash-${
 | |
|             instanceStats.instanceId
 | |
|         }-${Date.now()}.csv`;
 | |
| 
 | |
|         const json2csvParser = new Parser();
 | |
|         const csv = json2csvParser.parse(this.serializeStats(instanceStats));
 | |
| 
 | |
|         res.contentType('csv');
 | |
|         res.attachment(fileName);
 | |
|         res.send(csv);
 | |
|     }
 | |
| }
 | |
| 
 | |
| export default InstanceAdminController;
 |