From 020c982e73c706c7ec0a81fabe75ef0c73dad7fe Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:05:11 +0200 Subject: [PATCH 1/5] Bugfix/hourly market data updates not refreshing prices for asset profiles with manual data source (#7122) * Fix hourly market data updates not refreshing prices for MANUAL asset profiles * Update changelog --- CHANGELOG.md | 6 +++ .../data-provider/data-provider.service.ts | 6 ++- .../interfaces/data-provider.interface.ts | 1 + .../data-provider/manual/manual.service.ts | 45 ++++++++++--------- 4 files changed, 35 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a903122d4..4ec1539da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Fixed + +- Fixed an issue with hourly market data updates not refreshing prices for asset profiles with `MANUAL` data source + ## 3.15.1 - 2026-06-23 ### Changed diff --git a/apps/api/src/services/data-provider/data-provider.service.ts b/apps/api/src/services/data-provider/data-provider.service.ts index 5d49848fa..10b0e6fd8 100644 --- a/apps/api/src/services/data-provider/data-provider.service.ts +++ b/apps/api/src/services/data-provider/data-provider.service.ts @@ -662,7 +662,11 @@ export class DataProviderService implements OnModuleInit { ); const promise = Promise.resolve( - dataProvider.getQuotes({ requestTimeout, symbols: symbolsChunk }) + dataProvider.getQuotes({ + requestTimeout, + useCache, + symbols: symbolsChunk + }) ); promises.push( diff --git a/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts b/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts index a55c9f328..5002fa87e 100644 --- a/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts +++ b/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts @@ -75,6 +75,7 @@ export interface GetHistoricalParams { export interface GetQuotesParams { requestTimeout?: number; symbols: string[]; + useCache?: boolean; } export interface GetSearchParams { diff --git a/apps/api/src/services/data-provider/manual/manual.service.ts b/apps/api/src/services/data-provider/manual/manual.service.ts index 87e116dda..66571c239 100644 --- a/apps/api/src/services/data-provider/manual/manual.service.ts +++ b/apps/api/src/services/data-provider/manual/manual.service.ts @@ -136,7 +136,8 @@ export class ManualService implements DataProviderInterface { } public async getQuotes({ - symbols + symbols, + useCache = true }: GetQuotesParams): Promise<{ [symbol: string]: DataProviderResponse }> { const response: { [symbol: string]: DataProviderResponse } = {}; @@ -164,32 +165,32 @@ export class ManualService implements DataProviderInterface { } }); - const symbolProfilesWithScraperConfigurationAndInstantMode = - symbolProfiles.filter(({ scraperConfiguration }) => { + const symbolProfilesToScrape = symbolProfiles.filter( + ({ scraperConfiguration }) => { return ( - scraperConfiguration?.mode === 'instant' && + (scraperConfiguration?.mode === 'instant' || !useCache) && scraperConfiguration?.selector && scraperConfiguration?.url ); - }); - - const scraperResultPromises = - symbolProfilesWithScraperConfigurationAndInstantMode.map( - async ({ scraperConfiguration, symbol }) => { - try { - const marketPrice = await this.scrape({ - scraperConfiguration, - symbol - }); - return { marketPrice, symbol }; - } catch (error) { - this.logger.error( - `Could not get quote for ${symbol} (${this.getName()}): [${error.name}] ${error.message}` - ); - return { symbol, marketPrice: undefined }; - } + } + ); + + const scraperResultPromises = symbolProfilesToScrape.map( + async ({ scraperConfiguration, symbol }) => { + try { + const marketPrice = await this.scrape({ + scraperConfiguration, + symbol + }); + return { marketPrice, symbol }; + } catch (error) { + this.logger.error( + `Could not get quote for ${symbol} (${this.getName()}): [${error.name}] ${error.message}` + ); + return { symbol, marketPrice: undefined }; } - ); + } + ); // Wait for all scraping requests to complete concurrently const scraperResults = await Promise.all(scraperResultPromises); From 11f37f8f7ddae1eedd91c361ea8d5d7f697249ba Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:06:26 +0200 Subject: [PATCH 2/5] Task/improve style of Ghostfolio Premium card in admin settings (#7127) Improve style --- .../admin-settings.component.scss | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/apps/client/src/app/components/admin-settings/admin-settings.component.scss b/apps/client/src/app/components/admin-settings/admin-settings.component.scss index 1c0a17624..29340ea0d 100644 --- a/apps/client/src/app/components/admin-settings/admin-settings.component.scss +++ b/apps/client/src/app/components/admin-settings/admin-settings.component.scss @@ -10,7 +10,33 @@ } .mat-mdc-card { - --mat-card-outlined-container-color: whitesmoke; + --gradient-opacity: 50%; + --mat-card-outlined-outline-color: rgb(193, 219, 254); + + background-color: white; + background-image: linear-gradient( + 109.6deg, + color-mix(in srgb, rgba(255, 255, 255, 1) 100%, transparent) 11.2%, + color-mix( + in srgb, + rgba(221, 108, 241, 0.26) var(--gradient-opacity), + transparent + ) + 42%, + color-mix( + in srgb, + rgba(229, 106, 253, 0.71) var(--gradient-opacity), + transparent + ) + 71.5%, + color-mix( + in srgb, + rgba(123, 183, 253, 1) var(--gradient-opacity), + transparent + ) + 100% + ); + color: rgb(var(--dark-primary-text)); .mat-mdc-card-actions { min-height: 0; @@ -32,6 +58,6 @@ :host-context(.theme-dark) { .mat-mdc-card { - --mat-card-outlined-container-color: #222222; + --mat-card-outlined-outline-color: white; } } From a2dbfcf8e301cd8b4ed5a70c13ba019d536820e9 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:07:04 +0200 Subject: [PATCH 3/5] Task/refactor timeouts to use ms() (#7123) Refactor to use ms() --- libs/common/src/lib/config.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/common/src/lib/config.ts b/libs/common/src/lib/config.ts index f96a934e7..03600088f 100644 --- a/libs/common/src/lib/config.ts +++ b/libs/common/src/lib/config.ts @@ -90,10 +90,12 @@ export const DEFAULT_PAGE_SIZE = 50; export const DEFAULT_PORT = 3333; export const DEFAULT_PROCESSOR_GATHER_ASSET_PROFILE_CONCURRENCY = 1; export const DEFAULT_PROCESSOR_GATHER_HISTORICAL_MARKET_DATA_CONCURRENCY = 1; -export const DEFAULT_PROCESSOR_GATHER_HISTORICAL_MARKET_DATA_TIMEOUT = 60000; +export const DEFAULT_PROCESSOR_GATHER_HISTORICAL_MARKET_DATA_TIMEOUT = + ms('1 minute'); export const DEFAULT_PROCESSOR_GATHER_STATISTICS_CONCURRENCY = 1; export const DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_CONCURRENCY = 1; -export const DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_TIMEOUT = 30000; +export const DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_TIMEOUT = + ms('30 seconds'); export const DEFAULT_REDACTED_PATHS = [ 'accounts[*].balance', From f64edb6705aa86cedb968567603fc669f1cb9371 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:16:54 +0200 Subject: [PATCH 4/5] Feature/add pagination to platform and tag management of admin control panel (#7126) * Add pagination * Update changelog --- CHANGELOG.md | 5 +++++ .../components/admin-platform/admin-platform.component.html | 6 ++++++ .../components/admin-platform/admin-platform.component.ts | 6 ++++++ .../src/app/components/admin-tag/admin-tag.component.html | 6 ++++++ .../src/app/components/admin-tag/admin-tag.component.ts | 6 ++++++ 5 files changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ec1539da..47c43d38f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added pagination to the platform management of the admin control panel +- Added pagination to the tag management of the admin control panel + ### Fixed - Fixed an issue with hourly market data updates not refreshing prices for asset profiles with `MANUAL` data source diff --git a/apps/client/src/app/components/admin-platform/admin-platform.component.html b/apps/client/src/app/components/admin-platform/admin-platform.component.html index 44f5a6eab..19682bdc0 100644 --- a/apps/client/src/app/components/admin-platform/admin-platform.component.html +++ b/apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -96,3 +96,9 @@ + + diff --git a/apps/client/src/app/components/admin-platform/admin-platform.component.ts b/apps/client/src/app/components/admin-platform/admin-platform.component.ts index 26e6c2b1e..727585478 100644 --- a/apps/client/src/app/components/admin-platform/admin-platform.component.ts +++ b/apps/client/src/app/components/admin-platform/admin-platform.component.ts @@ -1,4 +1,5 @@ import { UserService } from '@ghostfolio/client/services/user/user.service'; +import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config'; import { CreatePlatformDto, UpdatePlatformDto } from '@ghostfolio/common/dtos'; import { ConfirmationDialogType } from '@ghostfolio/common/enums'; import { getLocale, getLowercase } from '@ghostfolio/common/helper'; @@ -22,6 +23,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { MatButtonModule } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; import { MatMenuModule } from '@angular/material/menu'; +import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator'; import { MatSort, MatSortModule } from '@angular/material/sort'; import { MatTableDataSource, MatTableModule } from '@angular/material/table'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; @@ -46,6 +48,7 @@ import { CreateOrUpdatePlatformDialogParams } from './create-or-update-platform- IonIcon, MatButtonModule, MatMenuModule, + MatPaginatorModule, MatSortModule, MatTableModule, RouterModule @@ -59,11 +62,13 @@ export class GfAdminPlatformComponent implements OnInit { protected dataSource = new MatTableDataSource(); protected readonly displayedColumns = ['name', 'url', 'accounts', 'actions']; + protected readonly pageSize = DEFAULT_PAGE_SIZE; protected platforms: Platform[]; private readonly deviceType = computed( () => this.deviceDetectorService.deviceInfo().deviceType ); + private readonly paginator = viewChild.required(MatPaginator); private readonly sort = viewChild.required(MatSort); private readonly adminService = inject(AdminService); @@ -145,6 +150,7 @@ export class GfAdminPlatformComponent implements OnInit { this.platforms = platforms; this.dataSource = new MatTableDataSource(platforms); + this.dataSource.paginator = this.paginator(); this.dataSource.sort = this.sort(); this.dataSource.sortingDataAccessor = getLowercase; diff --git a/apps/client/src/app/components/admin-tag/admin-tag.component.html b/apps/client/src/app/components/admin-tag/admin-tag.component.html index 3c125d5c0..a84cbb283 100644 --- a/apps/client/src/app/components/admin-tag/admin-tag.component.html +++ b/apps/client/src/app/components/admin-tag/admin-tag.component.html @@ -89,3 +89,9 @@ + + diff --git a/apps/client/src/app/components/admin-tag/admin-tag.component.ts b/apps/client/src/app/components/admin-tag/admin-tag.component.ts index fcb901707..7bd69c964 100644 --- a/apps/client/src/app/components/admin-tag/admin-tag.component.ts +++ b/apps/client/src/app/components/admin-tag/admin-tag.component.ts @@ -1,4 +1,5 @@ import { UserService } from '@ghostfolio/client/services/user/user.service'; +import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config'; import { CreateTagDto, UpdateTagDto } from '@ghostfolio/common/dtos'; import { ConfirmationDialogType } from '@ghostfolio/common/enums'; import { getLocale, getLowercase } from '@ghostfolio/common/helper'; @@ -21,6 +22,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { MatButtonModule } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; import { MatMenuModule } from '@angular/material/menu'; +import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator'; import { MatSort, MatSortModule } from '@angular/material/sort'; import { MatTableDataSource, MatTableModule } from '@angular/material/table'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; @@ -44,6 +46,7 @@ import { CreateOrUpdateTagDialogParams } from './create-or-update-tag-dialog/int IonIcon, MatButtonModule, MatMenuModule, + MatPaginatorModule, MatSortModule, MatTableModule, RouterModule @@ -62,11 +65,13 @@ export class GfAdminTagComponent implements OnInit { 'activities', 'actions' ]; + protected readonly pageSize = DEFAULT_PAGE_SIZE; protected tags: Tag[]; private readonly deviceType = computed( () => this.deviceDetectorService.deviceInfo().deviceType ); + private readonly paginator = viewChild.required(MatPaginator); private readonly sort = viewChild.required(MatSort); private readonly changeDetectorRef = inject(ChangeDetectorRef); @@ -147,6 +152,7 @@ export class GfAdminTagComponent implements OnInit { this.tags = tags; this.dataSource = new MatTableDataSource(this.tags); + this.dataSource.paginator = this.paginator(); this.dataSource.sort = this.sort(); this.dataSource.sortingDataAccessor = getLowercase; From 0d5c20622647fcdecabee2680b3b6a71a494b41b Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:19:33 +0200 Subject: [PATCH 5/5] Bugfix/log context formatting in performance logging service (#7121) * Fix log context formatting * Update changelog --- CHANGELOG.md | 1 + .../performance-logging/performance-logging.service.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47c43d38f..4f019b7f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed an issue with hourly market data updates not refreshing prices for asset profiles with `MANUAL` data source +- Fixed an issue with the log context formatting in the performance logging service ## 3.15.1 - 2026-06-23 diff --git a/apps/api/src/interceptors/performance-logging/performance-logging.service.ts b/apps/api/src/interceptors/performance-logging/performance-logging.service.ts index a07783cd9..b2ea03c37 100644 --- a/apps/api/src/interceptors/performance-logging/performance-logging.service.ts +++ b/apps/api/src/interceptors/performance-logging/performance-logging.service.ts @@ -2,7 +2,7 @@ import { Injectable, Logger } from '@nestjs/common'; @Injectable() export class PerformanceLoggingService { - private readonly logger = new Logger(PerformanceLoggingService.name); + private readonly logger = new Logger(); public logPerformance({ className,