From a4805e3e833c5f1a687cd682b79bbe534e97703c Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 30 Apr 2025 17:49:45 +0200 Subject: [PATCH] Improve watchlist for impersonation mode --- .../watchlist/watchlist.controller.ts | 13 ++++++-- .../endpoints/watchlist/watchlist.module.ts | 2 ++ .../home-watchlist.component.ts | 30 ++++++++++++++----- .../home-watchlist/home-watchlist.html | 2 +- 4 files changed, 36 insertions(+), 11 deletions(-) diff --git a/apps/api/src/app/endpoints/watchlist/watchlist.controller.ts b/apps/api/src/app/endpoints/watchlist/watchlist.controller.ts index c9e41d5d3..8d9d322a8 100644 --- a/apps/api/src/app/endpoints/watchlist/watchlist.controller.ts +++ b/apps/api/src/app/endpoints/watchlist/watchlist.controller.ts @@ -2,6 +2,8 @@ import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorat import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor'; import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor'; +import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service'; +import { HEADER_KEY_IMPERSONATION } from '@ghostfolio/common/config'; import { WatchlistResponse } from '@ghostfolio/common/interfaces'; import { permissions } from '@ghostfolio/common/permissions'; import { RequestWithUser } from '@ghostfolio/common/types'; @@ -11,6 +13,7 @@ import { Controller, Delete, Get, + Headers, HttpException, Inject, Param, @@ -29,6 +32,7 @@ import { WatchlistService } from './watchlist.service'; @Controller('watchlist') export class WatchlistController { public constructor( + private readonly impersonationService: ImpersonationService, @Inject(REQUEST) private readonly request: RequestWithUser, private readonly watchlistService: WatchlistService ) {} @@ -79,9 +83,14 @@ export class WatchlistController { @HasPermission(permissions.readWatchlist) @UseGuards(AuthGuard('jwt'), HasPermissionGuard) @UseInterceptors(TransformDataSourceInResponseInterceptor) - public async getWatchlistItems(): Promise { + public async getWatchlistItems( + @Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string + ): Promise { + const impersonationUserId = + await this.impersonationService.validateImpersonationId(impersonationId); + const watchlist = await this.watchlistService.getWatchlistItems( - this.request.user.id + impersonationUserId || this.request.user.id ); return { diff --git a/apps/api/src/app/endpoints/watchlist/watchlist.module.ts b/apps/api/src/app/endpoints/watchlist/watchlist.module.ts index 2addd2de0..462001aef 100644 --- a/apps/api/src/app/endpoints/watchlist/watchlist.module.ts +++ b/apps/api/src/app/endpoints/watchlist/watchlist.module.ts @@ -1,6 +1,7 @@ import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; +import { ImpersonationModule } from '@ghostfolio/api/services/impersonation/impersonation.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { DataGatheringModule } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; @@ -15,6 +16,7 @@ import { WatchlistService } from './watchlist.service'; imports: [ DataGatheringModule, DataProviderModule, + ImpersonationModule, PrismaModule, SymbolProfileModule, TransformDataSourceInRequestModule, diff --git a/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts b/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts index daf512b34..61df6311d 100644 --- a/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts +++ b/apps/client/src/app/components/home-watchlist/home-watchlist.component.ts @@ -1,4 +1,5 @@ import { DataService } from '@ghostfolio/client/services/data.service'; +import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; import { AssetProfileIdentifier, @@ -44,6 +45,7 @@ import { CreateWatchlistItemDialogParams } from './create-watchlist-item-dialog/ }) export class HomeWatchlistComponent implements OnDestroy, OnInit { public deviceType: string; + public hasImpersonationId: boolean; public hasPermissionToCreateWatchlistItem: boolean; public hasPermissionToDeleteWatchlistItem: boolean; public user: User; @@ -56,12 +58,20 @@ export class HomeWatchlistComponent implements OnDestroy, OnInit { private dataService: DataService, private deviceService: DeviceDetectorService, private dialog: MatDialog, + private impersonationStorageService: ImpersonationStorageService, private route: ActivatedRoute, private router: Router, private userService: UserService ) { this.deviceType = this.deviceService.getDeviceInfo().deviceType; + this.impersonationStorageService + .onChangeHasImpersonation() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe((impersonationId) => { + this.hasImpersonationId = !!impersonationId; + }); + this.route.queryParams .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((params) => { @@ -76,14 +86,18 @@ export class HomeWatchlistComponent implements OnDestroy, OnInit { if (state?.user) { this.user = state.user; - this.hasPermissionToCreateWatchlistItem = hasPermission( - this.user.permissions, - permissions.createWatchlistItem - ); - this.hasPermissionToDeleteWatchlistItem = hasPermission( - this.user.permissions, - permissions.deleteWatchlistItem - ); + this.hasPermissionToCreateWatchlistItem = + !this.hasImpersonationId && + hasPermission( + this.user.permissions, + permissions.createWatchlistItem + ); + this.hasPermissionToDeleteWatchlistItem = + !this.hasImpersonationId && + hasPermission( + this.user.permissions, + permissions.deleteWatchlistItem + ); this.changeDetectorRef.markForCheck(); } diff --git a/apps/client/src/app/components/home-watchlist/home-watchlist.html b/apps/client/src/app/components/home-watchlist/home-watchlist.html index ef073d331..bfbe36b99 100644 --- a/apps/client/src/app/components/home-watchlist/home-watchlist.html +++ b/apps/client/src/app/components/home-watchlist/home-watchlist.html @@ -20,7 +20,7 @@ -@if (hasPermissionToCreateWatchlistItem) { +@if (!hasImpersonationId && hasPermissionToCreateWatchlistItem) {