diff --git a/CHANGELOG.md b/CHANGELOG.md index a574f70a8..5c1511116 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,20 +5,34 @@ 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 +## 2.225.0 - 2025-12-31 + +### Added + +- Added a new endpoint to get all platforms (`GET api/v1/platforms`) +- Added the session url to the endpoint response of the _Stripe_ checkout ### Changed +- Improved the routing of the user detail dialog in the users section of the admin control panel - Lifted the asset profile identifier editing restriction for `MANUAL` data sources in the asset profile details dialog of the admin control panel -- Upgraded `angular` from version `20.2.4` to `20.3.9` +- Deprecated the public _Stripe_ key +- Improved the language localization for German (`de`) +- Eliminated `ngx-stripe` +- Upgraded `angular` from version `20.2.4` to `21.0.6` +- Upgraded `marked` from version `15.0.4` to `17.0.1` +- Upgraded `ngx-device-detector` from version `10.1.0` to `11.0.0` - Upgraded `ng-extract-i18n-merge` from `3.1.0` to `3.2.1` -- Upgraded `Nx` from version `21.5.1` to `22.1.3` +- Upgraded `ngx-markdown` from version `20.0.0` to `21.0.1` +- Upgraded `Nx` from version `21.5.1` to `22.3.3` - Upgraded `shx` from version `0.3.4` to `0.4.0` - Upgraded `storybook` from version `9.1.5` to `10.1.10` +- Upgraded `zone.js` from version `0.15.1` to `0.16.0` ### Fixed - Added the missing currency suffix to the cash balance field in the create or update account dialog +- Fixed the time in market display of the portfolio summary tab on the home page for the impersonation mode - Fixed the delete button in the asset profile details dialog of the admin control panel by providing the missing `watchedByCount` parameter ## 2.224.2 - 2025-12-20 diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index 5ec148558..89f52e1ea 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -37,6 +37,7 @@ import { AssetsModule } from './endpoints/assets/assets.module'; import { BenchmarksModule } from './endpoints/benchmarks/benchmarks.module'; import { GhostfolioModule } from './endpoints/data-providers/ghostfolio/ghostfolio.module'; import { MarketDataModule } from './endpoints/market-data/market-data.module'; +import { PlatformsModule } from './endpoints/platforms/platforms.module'; import { PublicModule } from './endpoints/public/public.module'; import { SitemapModule } from './endpoints/sitemap/sitemap.module'; import { TagsModule } from './endpoints/tags/tags.module'; @@ -95,6 +96,7 @@ import { UserModule } from './user/user.module'; MarketDataModule, OrderModule, PlatformModule, + PlatformsModule, PortfolioModule, PortfolioSnapshotQueueModule, PrismaModule, diff --git a/apps/api/src/app/endpoints/platforms/platforms.controller.ts b/apps/api/src/app/endpoints/platforms/platforms.controller.ts new file mode 100644 index 000000000..46303a3f8 --- /dev/null +++ b/apps/api/src/app/endpoints/platforms/platforms.controller.ts @@ -0,0 +1,22 @@ +import { PlatformService } from '@ghostfolio/api/app/platform/platform.service'; +import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; +import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; +import { PlatformsResponse } from '@ghostfolio/common/interfaces'; +import { permissions } from '@ghostfolio/common/permissions'; + +import { Controller, Get, UseGuards } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; + +@Controller('platforms') +export class PlatformsController { + public constructor(private readonly platformService: PlatformService) {} + + @Get() + @HasPermission(permissions.readPlatforms) + @UseGuards(AuthGuard('jwt'), HasPermissionGuard) + public async getPlatforms(): Promise { + const platforms = await this.platformService.getPlatforms(); + + return { platforms }; + } +} diff --git a/apps/api/src/app/endpoints/platforms/platforms.module.ts b/apps/api/src/app/endpoints/platforms/platforms.module.ts new file mode 100644 index 000000000..21d0edf69 --- /dev/null +++ b/apps/api/src/app/endpoints/platforms/platforms.module.ts @@ -0,0 +1,11 @@ +import { PlatformModule } from '@ghostfolio/api/app/platform/platform.module'; + +import { Module } from '@nestjs/common'; + +import { PlatformsController } from './platforms.controller'; + +@Module({ + controllers: [PlatformsController], + imports: [PlatformModule] +}) +export class PlatformsModule {} diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 084c8f4ed..faabee79b 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -1954,6 +1954,7 @@ export class PortfolioService { }).length, committedFunds: committedFunds.toNumber(), currentValueInBaseCurrency: currentValueInBaseCurrency.toNumber(), + dateOfFirstActivity: firstOrderDate, dividendInBaseCurrency: dividendInBaseCurrency.toNumber(), emergencyFund: { assets: emergencyFundHoldingsValueInBaseCurrency, diff --git a/apps/api/src/app/subscription/subscription.service.ts b/apps/api/src/app/subscription/subscription.service.ts index 0fad8c8ac..8dc7d27f1 100644 --- a/apps/api/src/app/subscription/subscription.service.ts +++ b/apps/api/src/app/subscription/subscription.service.ts @@ -100,7 +100,8 @@ export class SubscriptionService { ); return { - sessionId: session.id + sessionId: session.id, + sessionUrl: session.url }; } diff --git a/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts b/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts index 3cf935b1e..6030e62d4 100644 --- a/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts +++ b/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts @@ -18,7 +18,7 @@ import { import { Injectable } from '@nestjs/common'; import { DataSource, SymbolProfile } from '@prisma/client'; -import * as Alphavantage from 'alphavantage'; +import Alphavantage from 'alphavantage'; import { format, isAfter, isBefore, parse } from 'date-fns'; import { AlphaVantageHistoricalResponse } from './interfaces/interfaces'; diff --git a/apps/api/tsconfig.app.json b/apps/api/tsconfig.app.json index 655120714..d38ef826f 100644 --- a/apps/api/tsconfig.app.json +++ b/apps/api/tsconfig.app.json @@ -4,6 +4,7 @@ "outDir": "../../dist/out-tsc", "types": ["node"], "emitDecoratorMetadata": true, + "moduleResolution": "node10", "target": "es2021", "module": "commonjs" }, diff --git a/apps/api/tsconfig.spec.json b/apps/api/tsconfig.spec.json index 148da8555..934e28503 100644 --- a/apps/api/tsconfig.spec.json +++ b/apps/api/tsconfig.spec.json @@ -3,6 +3,7 @@ "compilerOptions": { "outDir": "../../dist/out-tsc", "module": "commonjs", + "moduleResolution": "node10", "types": ["jest", "node"] }, "include": ["**/*.spec.ts", "**/*.test.ts", "**/*.d.ts", "jest.config.ts"] diff --git a/apps/client/project.json b/apps/client/project.json index 0d3571cdf..09968d23f 100644 --- a/apps/client/project.json +++ b/apps/client/project.json @@ -75,7 +75,6 @@ "ngswConfigPath": "apps/client/ngsw-config.json", "optimization": false, "polyfills": "apps/client/src/polyfills.ts", - "scripts": ["node_modules/marked/marked.min.js"], "serviceWorker": true, "sourceMap": true, "styles": [ diff --git a/apps/client/src/app/components/access-table/access-table.component.ts b/apps/client/src/app/components/access-table/access-table.component.ts index fe2c81199..76548c45d 100644 --- a/apps/client/src/app/components/access-table/access-table.component.ts +++ b/apps/client/src/app/components/access-table/access-table.component.ts @@ -4,7 +4,6 @@ import { publicRoutes } from '@ghostfolio/common/routes/routes'; import { NotificationService } from '@ghostfolio/ui/notifications'; import { Clipboard, ClipboardModule } from '@angular/cdk/clipboard'; -import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, @@ -36,7 +35,6 @@ import ms from 'ms'; changeDetection: ChangeDetectionStrategy.OnPush, imports: [ ClipboardModule, - CommonModule, IonIcon, MatButtonModule, MatMenuModule, diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts index 57ee57f19..abea236b8 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts @@ -29,7 +29,6 @@ import { GfSymbolAutocompleteComponent } from '@ghostfolio/ui/symbol-autocomplet import { GfValueComponent } from '@ghostfolio/ui/value'; import { TextFieldModule } from '@angular/cdk/text-field'; -import { CommonModule } from '@angular/common'; import { HttpErrorResponse } from '@angular/common/http'; import { ChangeDetectionStrategy, @@ -95,7 +94,6 @@ import { AssetProfileDialogParams } from './interfaces/interfaces'; changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'd-flex flex-column h-100' }, imports: [ - CommonModule, FormsModule, GfCurrencySelectorComponent, GfEntityLogoComponent, diff --git a/apps/client/src/app/components/admin-users/admin-users.component.ts b/apps/client/src/app/components/admin-users/admin-users.component.ts index 99fbe7901..1722b498f 100644 --- a/apps/client/src/app/components/admin-users/admin-users.component.ts +++ b/apps/client/src/app/components/admin-users/admin-users.component.ts @@ -18,6 +18,7 @@ import { User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; +import { internalRoutes } from '@ghostfolio/common/routes/routes'; import { NotificationService } from '@ghostfolio/ui/notifications'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; import { GfValueComponent } from '@ghostfolio/ui/value'; @@ -39,7 +40,7 @@ import { PageEvent } from '@angular/material/paginator'; import { MatTableDataSource, MatTableModule } from '@angular/material/table'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import { IonIcon } from '@ionic/angular/standalone'; import { differenceInSeconds, @@ -69,7 +70,8 @@ import { takeUntil } from 'rxjs/operators'; MatMenuModule, MatPaginatorModule, MatTableModule, - NgxSkeletonLoaderModule + NgxSkeletonLoaderModule, + RouterModule ], selector: 'gf-admin-users', styleUrls: ['./admin-users.scss'], @@ -88,6 +90,8 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { public info: InfoItem; public isLoading = false; public pageSize = DEFAULT_PAGE_SIZE; + public routerLinkAdminControlUsers = + internalRoutes.adminControl.subRoutes.users.routerLink; public totalItems = 0; public user: User; @@ -136,11 +140,13 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { ]; } - this.route.queryParams + this.route.paramMap .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((params) => { - if (params['userDetailDialog'] && params['userId']) { - this.openUserDetailDialog(params['userId']); + const userId = params.get('userId'); + + if (userId) { + this.openUserDetailDialog(userId); } }); @@ -248,9 +254,9 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { } public onOpenUserDetailDialog(userId: string) { - this.router.navigate([], { - queryParams: { userId, userDetailDialog: true } - }); + this.router.navigate( + internalRoutes.adminControl.subRoutes.users.routerLink.concat(userId) + ); } public ngOnDestroy() { @@ -301,7 +307,9 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { .afterClosed() .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(() => { - this.router.navigate(['.'], { relativeTo: this.route }); + this.router.navigate( + internalRoutes.adminControl.subRoutes.users.routerLink + ); }); } } diff --git a/apps/client/src/app/components/admin-users/admin-users.html b/apps/client/src/app/components/admin-users/admin-users.html index ebcdc6f5f..0f9789feb 100644 --- a/apps/client/src/app/components/admin-users/admin-users.html +++ b/apps/client/src/app/components/admin-users/admin-users.html @@ -215,9 +215,9 @@ - + @if (hasPermissionToImpersonateAllUsers) {