diff --git a/libs/ui/src/lib/premium-indicator/premium-indicator.component.html b/libs/ui/src/lib/premium-indicator/premium-indicator.component.html
index 71baae6cb..635346ffb 100644
--- a/libs/ui/src/lib/premium-indicator/premium-indicator.component.html
+++ b/libs/ui/src/lib/premium-indicator/premium-indicator.component.html
@@ -1,5 +1,6 @@
Allocation
- %
+ %
|
From 7f00c2881197ecc120cb2bb4517abea331a8f7c1 Mon Sep 17 00:00:00 2001
From: Kenrick Tandrian <60643640+KenTandrian@users.noreply.github.com>
Date: Sat, 4 Jul 2026 13:12:48 +0700
Subject: [PATCH 2/4] Task/improve type safety in accounts page component
(#7195)
* feat(client): resolve type errors
* feat(client): enforce encapsulation
* feat(client): replace constructor based DI with inject functions
* feat(client): replace deprecated getDeviceInfo
---
.../pages/accounts/accounts-page.component.ts | 140 +++++++++---------
.../interfaces/interfaces.ts | 4 +-
2 files changed, 76 insertions(+), 68 deletions(-)
diff --git a/apps/client/src/app/pages/accounts/accounts-page.component.ts b/apps/client/src/app/pages/accounts/accounts-page.component.ts
index cca1eda03..c99fc40ac 100644
--- a/apps/client/src/app/pages/accounts/accounts-page.component.ts
+++ b/apps/client/src/app/pages/accounts/accounts-page.component.ts
@@ -17,7 +17,9 @@ import { DataService } from '@ghostfolio/ui/services';
import {
ChangeDetectorRef,
Component,
+ computed,
DestroyRef,
+ inject,
OnInit
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@@ -25,7 +27,7 @@ import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
import { Account as AccountModel } from '@prisma/client';
import { DeviceDetectorService } from 'ngx-device-detector';
-import { EMPTY, Subscription } from 'rxjs';
+import { EMPTY } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { GfCreateOrUpdateAccountDialogComponent } from './create-or-update-account-dialog/create-or-update-account-dialog.component';
@@ -41,29 +43,33 @@ import { GfTransferBalanceDialogComponent } from './transfer-balance/transfer-ba
templateUrl: './accounts-page.html'
})
export class GfAccountsPageComponent implements OnInit {
- public accounts: AccountModel[];
- public activitiesCount = 0;
- public deviceType: string;
- public hasImpersonationId: boolean;
- public hasPermissionToCreateAccount: boolean;
- public hasPermissionToUpdateAccount: boolean;
- public routeQueryParams: Subscription;
- public totalBalanceInBaseCurrency = 0;
- public totalValueInBaseCurrency = 0;
- public user: User;
-
- public constructor(
- private changeDetectorRef: ChangeDetectorRef,
- private dataService: DataService,
- private destroyRef: DestroyRef,
- private deviceDetectorService: DeviceDetectorService,
- private dialog: MatDialog,
- private impersonationStorageService: ImpersonationStorageService,
- private notificationService: NotificationService,
- private route: ActivatedRoute,
- private router: Router,
- private userService: UserService
- ) {
+ protected accounts: AccountModel[];
+ protected activitiesCount = 0;
+ protected hasImpersonationId: boolean;
+ protected hasPermissionToCreateAccount: boolean;
+ protected hasPermissionToUpdateAccount: boolean;
+ protected totalBalanceInBaseCurrency = 0;
+ protected totalValueInBaseCurrency = 0;
+ protected user: User;
+
+ private readonly deviceType = computed(
+ () => this.deviceDetectorService.deviceInfo().deviceType
+ );
+
+ private readonly changeDetectorRef = inject(ChangeDetectorRef);
+ private readonly dataService = inject(DataService);
+ private readonly destroyRef = inject(DestroyRef);
+ private readonly deviceDetectorService = inject(DeviceDetectorService);
+ private readonly dialog = inject(MatDialog);
+ private readonly impersonationStorageService = inject(
+ ImpersonationStorageService
+ );
+ private readonly notificationService = inject(NotificationService);
+ private readonly route = inject(ActivatedRoute);
+ private readonly router = inject(Router);
+ private readonly userService = inject(UserService);
+
+ public constructor() {
this.route.queryParams
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((params) => {
@@ -80,7 +86,9 @@ export class GfAccountsPageComponent implements OnInit {
return id === params['accountId'];
});
- this.openUpdateAccountDialog(account);
+ if (account) {
+ this.openUpdateAccountDialog(account);
+ }
} else {
this.router.navigate(['.'], { relativeTo: this.route });
}
@@ -91,8 +99,6 @@ export class GfAccountsPageComponent implements OnInit {
}
public ngOnInit() {
- this.deviceType = this.deviceDetectorService.getDeviceInfo().deviceType;
-
this.impersonationStorageService
.onChangeHasImpersonation()
.pipe(takeUntilDestroyed(this.destroyRef))
@@ -122,32 +128,7 @@ export class GfAccountsPageComponent implements OnInit {
this.fetchAccounts();
}
- public fetchAccounts() {
- this.dataService
- .fetchAccounts()
- .pipe(takeUntilDestroyed(this.destroyRef))
- .subscribe(
- ({
- accounts,
- activitiesCount,
- totalBalanceInBaseCurrency,
- totalValueInBaseCurrency
- }) => {
- this.accounts = accounts;
- this.activitiesCount = activitiesCount;
- this.totalBalanceInBaseCurrency = totalBalanceInBaseCurrency;
- this.totalValueInBaseCurrency = totalValueInBaseCurrency;
-
- if (this.accounts?.length <= 0) {
- this.router.navigate([], { queryParams: { createDialog: true } });
- }
-
- this.changeDetectorRef.markForCheck();
- }
- );
- }
-
- public onDeleteAccount(aId: string) {
+ protected onDeleteAccount(aId: string) {
this.reset();
this.dataService
@@ -163,19 +144,44 @@ export class GfAccountsPageComponent implements OnInit {
});
}
- public onTransferBalance() {
+ protected onTransferBalance() {
this.router.navigate([], {
queryParams: { transferBalanceDialog: true }
});
}
- public onUpdateAccount(aAccount: AccountModel) {
+ protected onUpdateAccount(aAccount: AccountModel) {
this.router.navigate([], {
queryParams: { accountId: aAccount.id, editDialog: true }
});
}
- public openUpdateAccountDialog({
+ private fetchAccounts() {
+ this.dataService
+ .fetchAccounts()
+ .pipe(takeUntilDestroyed(this.destroyRef))
+ .subscribe(
+ ({
+ accounts,
+ activitiesCount,
+ totalBalanceInBaseCurrency,
+ totalValueInBaseCurrency
+ }) => {
+ this.accounts = accounts;
+ this.activitiesCount = activitiesCount;
+ this.totalBalanceInBaseCurrency = totalBalanceInBaseCurrency;
+ this.totalValueInBaseCurrency = totalValueInBaseCurrency;
+
+ if (this.accounts?.length <= 0) {
+ this.router.navigate([], { queryParams: { createDialog: true } });
+ }
+
+ this.changeDetectorRef.markForCheck();
+ }
+ );
+ }
+
+ private openUpdateAccountDialog({
balance,
comment,
currency,
@@ -199,8 +205,8 @@ export class GfAccountsPageComponent implements OnInit {
platformId
}
},
- height: this.deviceType === 'mobile' ? '98vh' : '80vh',
- width: this.deviceType === 'mobile' ? '100vw' : '50rem'
+ height: this.deviceType() === 'mobile' ? '98vh' : '80vh',
+ width: this.deviceType() === 'mobile' ? '100vw' : '50rem'
});
dialogRef
@@ -237,15 +243,15 @@ export class GfAccountsPageComponent implements OnInit {
autoFocus: false,
data: {
accountId: aAccountId,
- deviceType: this.deviceType,
+ deviceType: this.deviceType(),
hasImpersonationId: this.hasImpersonationId,
hasPermissionToCreateActivity:
!this.hasImpersonationId &&
hasPermission(this.user?.permissions, permissions.createActivity) &&
!this.user?.settings?.isRestrictedView
},
- height: this.deviceType === 'mobile' ? '98vh' : '80vh',
- width: this.deviceType === 'mobile' ? '100vw' : '50rem'
+ height: this.deviceType() === 'mobile' ? '98vh' : '80vh',
+ width: this.deviceType() === 'mobile' ? '100vw' : '50rem'
});
dialogRef
@@ -267,15 +273,15 @@ export class GfAccountsPageComponent implements OnInit {
account: {
balance: 0,
comment: null,
- currency: this.user?.settings?.baseCurrency,
+ currency: this.user?.settings?.baseCurrency ?? null,
id: null,
isExcluded: false,
name: null,
platformId: null
}
- },
- height: this.deviceType === 'mobile' ? '98vh' : '80vh',
- width: this.deviceType === 'mobile' ? '100vw' : '50rem'
+ } satisfies CreateOrUpdateAccountDialogParams,
+ height: this.deviceType() === 'mobile' ? '98vh' : '80vh',
+ width: this.deviceType() === 'mobile' ? '100vw' : '50rem'
});
dialogRef
@@ -312,7 +318,7 @@ export class GfAccountsPageComponent implements OnInit {
data: {
accounts: this.accounts
},
- width: this.deviceType === 'mobile' ? '100vw' : '50rem'
+ width: this.deviceType() === 'mobile' ? '100vw' : '50rem'
});
dialogRef
@@ -353,7 +359,7 @@ export class GfAccountsPageComponent implements OnInit {
}
private reset() {
- this.accounts = undefined;
+ this.accounts = [];
this.activitiesCount = 0;
this.totalBalanceInBaseCurrency = 0;
this.totalValueInBaseCurrency = 0;
diff --git a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/interfaces/interfaces.ts b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/interfaces/interfaces.ts
index a3e6272f8..469ebc844 100644
--- a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/interfaces/interfaces.ts
+++ b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/interfaces/interfaces.ts
@@ -1,5 +1,7 @@
import { Account } from '@prisma/client';
export interface CreateOrUpdateAccountDialogParams {
- account: Omit ;
+ account: Omit & {
+ id: string | null;
+ };
}
From 680a5ce82995121adf1410a1d4d2f12bab2fa28d Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Sat, 4 Jul 2026 08:15:34 +0200
Subject: [PATCH 3/4] Task/upgrade yahoo-finance2 to version 3.15.4 (#7204)
* Update yahoo-finance2 to version 3.15.4
* Update changelog
---
CHANGELOG.md | 1 +
package-lock.json | 8 ++++----
package.json | 2 +-
3 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9c6a4fc79..9cb127833 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Improved the language localization by translating various tooltips across the application
+- Upgraded `yahoo-finance2` from version `3.14.3` to `3.15.4`
## 3.19.1 - 2026-07-03
diff --git a/package-lock.json b/package-lock.json
index 8370a5765..ac172b504 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -95,7 +95,7 @@
"tablemark": "4.1.0",
"twitter-api-v2": "1.29.0",
"undici": "8.5.0",
- "yahoo-finance2": "3.15.3",
+ "yahoo-finance2": "3.15.4",
"zod": "4.4.3",
"zone.js": "0.16.1"
},
@@ -35734,9 +35734,9 @@
}
},
"node_modules/yahoo-finance2": {
- "version": "3.15.3",
- "resolved": "https://registry.npmjs.org/yahoo-finance2/-/yahoo-finance2-3.15.3.tgz",
- "integrity": "sha512-AFmVZ4ACg3QFT2a/hyJv4Scp2J45gm/6xetVgvUvXzZypqsqbgreXJad4i+qm0onj6yeCf2fPhvow7Eh0CwwzA==",
+ "version": "3.15.4",
+ "resolved": "https://registry.npmjs.org/yahoo-finance2/-/yahoo-finance2-3.15.4.tgz",
+ "integrity": "sha512-90eOw76iqS//ksQGL4d/VcchbysnpWzFXVuiBtG7uuImlBDdxBA0BtccxCuTvVZDDu1aXm1TqBGc+MPr6wNkyQ==",
"license": "MIT",
"dependencies": {
"@deno/shim-deno": "~0.18.0",
diff --git a/package.json b/package.json
index 4585d34ca..b9d5be234 100644
--- a/package.json
+++ b/package.json
@@ -139,7 +139,7 @@
"tablemark": "4.1.0",
"twitter-api-v2": "1.29.0",
"undici": "8.5.0",
- "yahoo-finance2": "3.15.3",
+ "yahoo-finance2": "3.15.4",
"zod": "4.4.3",
"zone.js": "0.16.1"
},
From fc8c85c5e8e7d431aa0cda63a8e095469040325b Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Sat, 4 Jul 2026 08:15:52 +0200
Subject: [PATCH 4/4] Task/harden endpoint of public access for portfolio
sharing (#7205)
* Harden endpoint by restricting to public accesses
* Update changelog
---
CHANGELOG.md | 1 +
apps/api/src/app/endpoints/public/public.controller.ts | 5 ++++-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9cb127833..bb5f41685 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
+- Hardened the endpoint of the public access for portfolio sharing by restricting it to public accesses
- Improved the language localization by translating various tooltips across the application
- Upgraded `yahoo-finance2` from version `3.14.3` to `3.15.4`
diff --git a/apps/api/src/app/endpoints/public/public.controller.ts b/apps/api/src/app/endpoints/public/public.controller.ts
index d06e3b5fe..f43e51f7b 100644
--- a/apps/api/src/app/endpoints/public/public.controller.ts
+++ b/apps/api/src/app/endpoints/public/public.controller.ts
@@ -46,7 +46,10 @@ export class PublicController {
public async getPublicPortfolio(
@Param('accessId') accessId: string
): Promise {
- const access = await this.accessService.access({ id: accessId });
+ const access = await this.accessService.access({
+ granteeUserId: null,
+ id: accessId
+ });
if (!access) {
throw new HttpException(
|