Browse Source

Feature/add endpoint to get user by id (#5910)

* Add endpoint to get user by id

* Update changelog

---------

Co-authored-by: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
pull/5909/head^2
Arghya Das 3 weeks ago
committed by GitHub
parent
commit
697ecfe9bd
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      CHANGELOG.md
  2. 8
      apps/api/src/app/admin/admin.controller.ts
  3. 38
      apps/api/src/app/admin/admin.service.ts
  4. 13
      libs/common/src/lib/interfaces/admin-user.interface.ts
  5. 4
      libs/common/src/lib/interfaces/index.ts
  6. 3
      libs/common/src/lib/interfaces/responses/admin-user-response.interface.ts
  7. 14
      libs/common/src/lib/interfaces/responses/admin-users-response.interface.ts

4
CHANGELOG.md

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## 2.215.0-beta.1 - 2025-11-05
### Added
- Added the endpoint `GET /api/v1/admin/user/:id`
### Changed
- Improved the _Self-Hosting_ section content for the _Compare with..._ concept on the Frequently Asked Questions (FAQ) page

8
apps/api/src/app/admin/admin.controller.ts

@ -17,6 +17,7 @@ import { getAssetProfileIdentifier } from '@ghostfolio/common/helper';
import {
AdminData,
AdminMarketData,
AdminUserResponse,
AdminUsersResponse,
EnhancedSymbolProfile,
ScraperConfiguration
@ -321,4 +322,11 @@ export class AdminController {
take: isNaN(take) ? undefined : take
});
}
@Get('user/:id')
@HasPermission(permissions.accessAdminControl)
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async getUser(@Param('id') id: string): Promise<AdminUserResponse> {
return this.adminService.getUser(id);
}
}

38
apps/api/src/app/admin/admin.service.ts

@ -23,6 +23,7 @@ import {
AdminMarketData,
AdminMarketDataDetails,
AdminMarketDataItem,
AdminUserResponse,
AdminUsersResponse,
AssetProfileIdentifier,
EnhancedSymbolProfile,
@ -35,7 +36,8 @@ import {
BadRequestException,
HttpException,
Injectable,
Logger
Logger,
NotFoundException
} from '@nestjs/common';
import {
AssetClass,
@ -507,6 +509,18 @@ export class AdminService {
};
}
public async getUser(id: string): Promise<AdminUserResponse> {
const [user] = await this.getUsersWithAnalytics({
where: { id }
});
if (!user) {
throw new NotFoundException(`User with ID ${id} not found`);
}
return user;
}
public async getUsers({
skip,
take = Number.MAX_SAFE_INTEGER
@ -516,7 +530,15 @@ export class AdminService {
}): Promise<AdminUsersResponse> {
const [count, users] = await Promise.all([
this.countUsersWithAnalytics(),
this.getUsersWithAnalytics({ skip, take })
this.getUsersWithAnalytics({
skip,
take,
where: {
NOT: {
analytics: null
}
}
})
]);
return { count, users };
@ -814,17 +836,17 @@ export class AdminService {
private async getUsersWithAnalytics({
skip,
take
take,
where
}: {
skip?: number;
take?: number;
where?: Prisma.UserWhereInput;
}): Promise<AdminUsersResponse['users']> {
let orderBy: Prisma.Enumerable<Prisma.UserOrderByWithRelationInput> = [
{ createdAt: 'desc' }
];
let where: Prisma.UserWhereInput;
if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) {
orderBy = [
{
@ -833,12 +855,6 @@ export class AdminService {
}
}
];
where = {
NOT: {
analytics: null
}
};
}
const usersWithAnalytics = await this.prismaService.user.findMany({

13
libs/common/src/lib/interfaces/admin-user.interface.ts

@ -0,0 +1,13 @@
import { Role } from '@prisma/client';
export interface AdminUser {
accountCount: number;
activityCount: number;
country: string;
createdAt: Date;
dailyApiRequests: number;
engagement: number;
id: string;
lastActivity: Date;
role: Role;
}

4
libs/common/src/lib/interfaces/index.ts

@ -7,6 +7,7 @@ import type {
AdminMarketData,
AdminMarketDataItem
} from './admin-market-data.interface';
import type { AdminUser } from './admin-user.interface';
import type { AssetClassSelectorOption } from './asset-class-selector-option.interface';
import type { AssetProfileIdentifier } from './asset-profile-identifier.interface';
import type { BenchmarkProperty } from './benchmark-property.interface';
@ -38,6 +39,7 @@ import type { AccountBalancesResponse } from './responses/account-balances-respo
import type { AccountsResponse } from './responses/accounts-response.interface';
import type { ActivitiesResponse } from './responses/activities-response.interface';
import type { ActivityResponse } from './responses/activity-response.interface';
import type { AdminUserResponse } from './responses/admin-user-response.interface';
import type { AdminUsersResponse } from './responses/admin-users-response.interface';
import type { AiPromptResponse } from './responses/ai-prompt-response.interface';
import type { ApiKeyResponse } from './responses/api-key-response.interface';
@ -92,6 +94,8 @@ export {
AdminMarketData,
AdminMarketDataDetails,
AdminMarketDataItem,
AdminUser,
AdminUserResponse,
AdminUsersResponse,
AiPromptResponse,
ApiKeyResponse,

3
libs/common/src/lib/interfaces/responses/admin-user-response.interface.ts

@ -0,0 +1,3 @@
import { AdminUser } from '../admin-user.interface';
export interface AdminUserResponse extends AdminUser {}

14
libs/common/src/lib/interfaces/responses/admin-users-response.interface.ts

@ -1,16 +1,6 @@
import { Role } from '@prisma/client';
import { AdminUser } from '../admin-user.interface';
export interface AdminUsersResponse {
count: number;
users: {
accountCount: number;
activityCount: number;
country: string;
createdAt: Date;
dailyApiRequests: number;
engagement: number;
id: string;
lastActivity: Date;
role: Role;
}[];
users: AdminUser[];
}

Loading…
Cancel
Save