Browse Source

Task/guard system tags in admin control panel (#7532)

* Guard system tags against deletion and renaming

* Update changelog
pull/7543/head
Thomas Kaul 3 days ago
committed by GitHub
parent
commit
b812bc7421
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      CHANGELOG.md
  2. 5
      apps/api/src/app/endpoints/tags/tags.controller.ts
  3. 12
      apps/client/src/app/components/admin-tag/admin-tag.component.html
  4. 9
      apps/client/src/app/components/admin-tag/admin-tag.component.ts
  5. 8
      libs/common/src/lib/config.ts
  6. 9
      libs/common/src/lib/helper.ts

4
CHANGELOG.md

@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added the platform logo to the account selector in the create or update activity dialog
### Changed
- Guarded the system tags against deletion and renaming in the tag management of the admin control panel
## 3.42.0 - 2026-08-04
### Changed

5
apps/api/src/app/endpoints/tags/tags.controller.ts

@ -2,6 +2,7 @@ import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorat
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { TagService } from '@ghostfolio/api/services/tag/tag.service';
import { CreateTagDto, UpdateTagDto } from '@ghostfolio/common/dtos';
import { isSystemTag } from '@ghostfolio/common/helper';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { RequestWithUser } from '@ghostfolio/common/types';
@ -69,7 +70,7 @@ export class TagsController {
id
});
if (!originalTag) {
if (!originalTag || isSystemTag(originalTag)) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
@ -94,7 +95,7 @@ export class TagsController {
id
});
if (!originalTag) {
if (!originalTag || isSystemTag(originalTag)) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN

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

@ -83,7 +83,11 @@
<ion-icon name="ellipsis-horizontal" />
</button>
<mat-menu #tagMenu="matMenu" xPosition="before">
<button mat-menu-item (click)="onUpdateTag(element)">
<button
mat-menu-item
[disabled]="isSystemTag(element)"
(click)="onUpdateTag(element)"
>
<span class="align-items-center d-flex">
<ion-icon class="mr-2" name="create-outline" />
<span><ng-container i18n>Edit</ng-container>...</span>
@ -92,7 +96,11 @@
<hr class="m-0" />
<button
mat-menu-item
[disabled]="element.accountCount > 0 || element.activityCount > 0"
[disabled]="
element.accountCount > 0 ||
element.activityCount > 0 ||
isSystemTag(element)
"
(click)="onDeleteTag(element.id)"
>
<span class="align-items-center d-flex">

9
apps/client/src/app/components/admin-tag/admin-tag.component.ts

@ -2,7 +2,11 @@ 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';
import {
getLocale,
getLowercase,
isSystemTag
} from '@ghostfolio/common/helper';
import { translate } from '@ghostfolio/ui/i18n';
import { NotificationService } from '@ghostfolio/ui/notifications';
import { DataService } from '@ghostfolio/ui/services';
@ -67,6 +71,7 @@ export class GfAdminTagComponent implements OnInit {
'activities',
'actions'
];
protected readonly isSystemTag = isSystemTag;
protected readonly pageSize = DEFAULT_PAGE_SIZE;
protected tags: Tag[];
protected readonly translate = translate;
@ -99,7 +104,7 @@ export class GfAdminTagComponent implements OnInit {
return id === params['tagId'];
});
if (tag) {
if (tag && !isSystemTag(tag)) {
this.openUpdateTagDialog(tag);
}
} else {

8
libs/common/src/lib/config.ts

@ -338,10 +338,16 @@ export const SUPPORTED_LANGUAGE_CODES = [
'zh'
] as const;
export const TAG_ID_DEMO = 'efa08cb3-9b9d-4974-ac68-db13a19c4874';
export const TAG_ID_EMERGENCY_FUND = '4452656d-9fa4-4bd0-ba38-70492e31d180';
export const TAG_ID_EXCLUDE_FROM_ANALYSIS =
'f2e868af-8333-459f-b161-cbc6544c24bd';
export const TAG_ID_DEMO = 'efa08cb3-9b9d-4974-ac68-db13a19c4874';
export const TAG_IDS_SYSTEM = [
TAG_ID_DEMO,
TAG_ID_EMERGENCY_FUND,
TAG_ID_EXCLUDE_FROM_ANALYSIS
];
export const THROTTLE_DAILY_KEY = 'daily';
export const THROTTLE_DAILY_TTL = ms('1 day');

9
libs/common/src/lib/helper.ts

@ -43,7 +43,8 @@ import {
ghostfolioFearAndGreedIndexSymbolStocks,
ghostfolioPrefix,
SEARCH_QUERY_MINIMUM_LENGTH,
TAG_ID_EXCLUDE_FROM_ANALYSIS
TAG_ID_EXCLUDE_FROM_ANALYSIS,
TAG_IDS_SYSTEM
} from './config';
import {
AssetProfileIdentifier,
@ -557,6 +558,12 @@ export function isSplitRatio({
);
}
export function isSystemTag(tag?: { id: string }) {
return TAG_IDS_SYSTEM.some((id) => {
return id === tag?.id;
});
}
export function isValidCustomAssetProfileSymbol(aSymbol: string) {
return hasGhostfolioPrefix(aSymbol) || isUUID(aSymbol);
}

Loading…
Cancel
Save