Browse Source

Merge branch 'main' into task/upgrade-simplewebauthn-to-version-13.3

pull/7115/head
Thomas Kaul 3 weeks ago
committed by GitHub
parent
commit
d53c39269c
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 10
      CHANGELOG.md
  2. 2
      apps/api/src/interceptors/performance-logging/performance-logging.service.ts
  3. 6
      apps/api/src/services/data-provider/data-provider.service.ts
  4. 1
      apps/api/src/services/data-provider/interfaces/data-provider.interface.ts
  5. 45
      apps/api/src/services/data-provider/manual/manual.service.ts
  6. 6
      apps/client/src/app/components/admin-platform/admin-platform.component.html
  7. 6
      apps/client/src/app/components/admin-platform/admin-platform.component.ts
  8. 30
      apps/client/src/app/components/admin-settings/admin-settings.component.scss
  9. 6
      apps/client/src/app/components/admin-tag/admin-tag.component.html
  10. 6
      apps/client/src/app/components/admin-tag/admin-tag.component.ts
  11. 6
      libs/common/src/lib/config.ts

10
CHANGELOG.md

@ -7,10 +7,20 @@ 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
### Changed
- Upgraded `@simplewebauthn/browser` and `@simplewebauthn/server` from version `13.2.2` to `13.3`
### 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
### Changed

2
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,

6
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(

1
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 {

45
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);

6
apps/client/src/app/components/admin-platform/admin-platform.component.html

@ -96,3 +96,9 @@
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr>
<tr *matRowDef="let row; columns: displayedColumns" mat-row></tr>
</table>
<mat-paginator
[class.d-none]="dataSource.data.length <= pageSize"
[hidePageSize]="true"
[pageSize]="pageSize"
/>

6
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<Platform>();
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;

30
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;
}
}

6
apps/client/src/app/components/admin-tag/admin-tag.component.html

@ -89,3 +89,9 @@
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr>
<tr *matRowDef="let row; columns: displayedColumns" mat-row></tr>
</table>
<mat-paginator
[class.d-none]="dataSource.data.length <= pageSize"
[hidePageSize]="true"
[pageSize]="pageSize"
/>

6
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;

6
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',

Loading…
Cancel
Save