From bd43ea9b6cbb96379d1ef81e90fb47bb6999efbd Mon Sep 17 00:00:00 2001 From: csehatt741 <77381875+csehatt741@users.noreply.github.com> Date: Sun, 29 Jun 2025 17:46:50 +0200 Subject: [PATCH] Feature/dynamically compose public routes in sitemap (#5035) * Dynamically compose public routes in sitemap * Update changelog --- CHANGELOG.md | 1 + .../endpoints/sitemap/sitemap.controller.ts | 6 +- .../app/endpoints/sitemap/sitemap.service.ts | 125 +++- apps/api/src/assets/sitemap.xml | 581 +----------------- ...erfaces.ts => internal-route.interface.ts} | 4 +- .../interfaces/public-route.interface.ts | 7 + libs/common/src/lib/routes/routes.ts | 25 +- .../src/lib/assistant/assistant.component.ts | 4 +- 8 files changed, 134 insertions(+), 619 deletions(-) rename libs/common/src/lib/routes/interfaces/{interfaces.ts => internal-route.interface.ts} (69%) create mode 100644 libs/common/src/lib/routes/interfaces/public-route.interface.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d6f10399f..08a7ad772 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 - Introduced fuzzy search for the quick links of the assistant - Improved the search results of the assistant to only display categories with content +- Enhanced the sitemap to dynamically compose public routes - Renamed `Account` to `account` in the `Order` database schema - Improved the language localization for German (`de`) diff --git a/apps/api/src/app/endpoints/sitemap/sitemap.controller.ts b/apps/api/src/app/endpoints/sitemap/sitemap.controller.ts index 4d6dfb5ea..f10f0bfd8 100644 --- a/apps/api/src/app/endpoints/sitemap/sitemap.controller.ts +++ b/apps/api/src/app/endpoints/sitemap/sitemap.controller.ts @@ -37,12 +37,14 @@ export class SitemapController { response.setHeader('content-type', 'application/xml'); response.send( interpolate(this.sitemapXml, { - currentDate, personalFinanceTools: this.configurationService.get( 'ENABLE_FEATURE_SUBSCRIPTION' ) ? this.sitemapService.getPersonalFinanceTools({ currentDate }) - : '' + : '', + publicRoutes: this.sitemapService.getPublicRoutes({ + currentDate + }) }) ); } diff --git a/apps/api/src/app/endpoints/sitemap/sitemap.service.ts b/apps/api/src/app/endpoints/sitemap/sitemap.service.ts index ea8a7c8c2..d18fe884a 100644 --- a/apps/api/src/app/endpoints/sitemap/sitemap.service.ts +++ b/apps/api/src/app/endpoints/sitemap/sitemap.service.ts @@ -2,11 +2,16 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/con import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; import { SUPPORTED_LANGUAGE_CODES } from '@ghostfolio/common/config'; import { personalFinanceTools } from '@ghostfolio/common/personal-finance-tools'; +import { PublicRoute } from '@ghostfolio/common/routes/interfaces/public-route.interface'; +import { publicRoutes } from '@ghostfolio/common/routes/routes'; import { Injectable } from '@nestjs/common'; @Injectable() export class SitemapService { + private static readonly TRANSLATION_TAGGED_MESSAGE_REGEX = + /:.*@@(?[a-zA-Z0-9.]+):(?.+)/; + public constructor( private readonly configurationService: ConfigurationService, private readonly i18nService: I18nService @@ -15,33 +20,97 @@ export class SitemapService { public getPersonalFinanceTools({ currentDate }: { currentDate: string }) { const rootUrl = this.configurationService.get('ROOT_URL'); - return personalFinanceTools - .map(({ alias, key }) => { - return SUPPORTED_LANGUAGE_CODES.map((languageCode) => { - const resourcesPath = this.i18nService.getTranslation({ - languageCode, - id: 'routes.resources' - }); - - const personalFinanceToolsPath = this.i18nService.getTranslation({ - languageCode, - id: 'routes.resources.personalFinanceTools' - }); - - const openSourceAlternativeToPath = this.i18nService.getTranslation({ - languageCode, - id: 'routes.resources.personalFinanceTools.openSourceAlternativeTo' - }); - - return [ - ' ', - ` ${rootUrl}/${languageCode}/${resourcesPath}/${personalFinanceToolsPath}/${openSourceAlternativeToPath}-${alias ?? key}`, - ` ${currentDate}T00:00:00+00:00`, - ' ' - ].join('\n'); - }); - }) - .flat() - .join('\n'); + return SUPPORTED_LANGUAGE_CODES.flatMap((languageCode) => { + return personalFinanceTools.map(({ alias, key }) => { + const route = + publicRoutes.resources.subRoutes.personalFinanceTools.subRoutes + .product; + const params = { + currentDate, + languageCode, + rootUrl, + urlPostfix: alias ?? key + }; + + return this.createRouteSitemapUrl({ ...params, route }); + }); + }).join('\n'); + } + + public getPublicRoutes({ currentDate }: { currentDate: string }) { + const rootUrl = this.configurationService.get('ROOT_URL'); + + return SUPPORTED_LANGUAGE_CODES.flatMap((languageCode) => { + const params = { + currentDate, + languageCode, + rootUrl + }; + + return [ + this.createRouteSitemapUrl(params), + ...this.createSitemapUrls(params, publicRoutes) + ]; + }).join('\n'); + } + + private createRouteSitemapUrl({ + currentDate, + languageCode, + rootUrl, + route, + urlPostfix + }: { + currentDate: string; + languageCode: string; + rootUrl: string; + route?: PublicRoute; + urlPostfix?: string; + }): string { + const segments = + route?.routerLink.map((link) => { + const match = link.match( + SitemapService.TRANSLATION_TAGGED_MESSAGE_REGEX + ); + + const segment = match + ? (this.i18nService.getTranslation({ + languageCode, + id: match.groups.id + }) ?? match.groups.message) + : link; + + return segment.replace(/^\/+|\/+$/, ''); + }) ?? []; + + const location = + [rootUrl, languageCode, ...segments].join('/') + + (urlPostfix ? `-${urlPostfix}` : ''); + + return [ + ' ', + ` ${location}`, + ` ${currentDate}T00:00:00+00:00`, + ' ' + ].join('\n'); + } + + private createSitemapUrls( + params: { currentDate: string; languageCode: string; rootUrl: string }, + routes: Record + ): string[] { + return Object.values(routes).flatMap((route) => { + if (route.excludeFromSitemap) { + return []; + } + + const urls = [this.createRouteSitemapUrl({ ...params, route })]; + + if (route.subRoutes) { + urls.push(...this.createSitemapUrls(params, route.subRoutes)); + } + + return urls; + }); } } diff --git a/apps/api/src/assets/sitemap.xml b/apps/api/src/assets/sitemap.xml index f1ec2d95d..fb2a5403e 100644 --- a/apps/api/src/assets/sitemap.xml +++ b/apps/api/src/assets/sitemap.xml @@ -4,585 +4,6 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"> - - https://ghostfol.io/ca - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de/blog - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de/blog/2021/07/hallo-ghostfolio - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de/blog/2023/01/ghostfolio-auf-sackgeld-vorgestellt - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de/features - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de/haeufig-gestellte-fragen - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de/maerkte - daily - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de/open - daily - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de/preise - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de/registrierung - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de/ressourcen - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de/ressourcen/lexikon - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de/ressourcen/maerkte - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de/ressourcen/personal-finance-tools - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de/ressourcen/ratgeber - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de/ueber-uns - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de/ueber-uns/changelog - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de/ueber-uns/datenschutzbestimmungen - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de/ueber-uns/lizenz - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/de/ueber-uns/oss-friends - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/development/storybook - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/about - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/about/changelog - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/about/license - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/about/oss-friends - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog/2021/07/hello-ghostfolio - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog/2022/01/ghostfolio-first-months-in-open-source - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog/2022/07/ghostfolio-meets-internet-identity - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog/2022/07/how-do-i-get-my-finances-in-order - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog/2022/08/500-stars-on-github - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog/2022/10/hacktoberfest-2022 - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog/2022/11/black-friday-2022 - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog/2022/12/the-importance-of-tracking-your-personal-finances - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog/2023/02/ghostfolio-meets-umbrel - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog/2023/03/ghostfolio-reaches-1000-stars-on-github - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog/2023/05/unlock-your-financial-potential-with-ghostfolio - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog/2023/07/exploring-the-path-to-fire - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog/2023/08/ghostfolio-joins-oss-friends - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog/2023/09/ghostfolio-2 - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog/2023/09/hacktoberfest-2023 - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog/2023/11/black-week-2023 - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog/2023/11/hacktoberfest-2023-debriefing - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog/2024/09/hacktoberfest-2024 - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/blog/2024/11/black-weeks-2024 - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/faq - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/saas - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/self-hosting - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/features - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/markets - daily - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/open - daily - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/pricing - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/register - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/resources - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/resources/glossary - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/resources/guides - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/resources/markets - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/en/resources/personal-finance-tools - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/es - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/es/funcionalidades - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/es/mercados - daily - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/es/open - daily - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/es/precios - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/es/preguntas-mas-frecuentes - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/es/recursos - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/es/recursos/personal-finance-tools - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/es/registro - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/es/sobre - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/es/sobre/changelog - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/es/sobre/licencia - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/es/sobre/oss-friends - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/es/sobre/politica-de-privacidad - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/fr - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/fr/a-propos - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/fr/a-propos/journal-des-modifications - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/fr/a-propos/licence - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/fr/a-propos/oss-friends - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/fr/a-propos/politique-de-confidentialite - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/fr/enregistrement - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/fr/fonctionnalites - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/fr/foire-aux-questions - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/fr/marches - daily - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/fr/open - daily - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/fr/prix - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/fr/ressources - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/fr/ressources/personal-finance-tools - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/it - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/it/domande-piu-frequenti - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/it/funzionalita - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/it/informazioni-su - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/it/informazioni-su/registro-delle-modifiche - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/it/informazioni-su/informativa-sulla-privacy - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/it/informazioni-su/licenza - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/it/informazioni-su/oss-friends - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/it/iscrizione - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/it/mercati - daily - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/it/open - daily - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/it/prezzi - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/it/risorse - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/it/risorse/personal-finance-tools - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/nl - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/nl/bronnen - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/nl/bronnen/personal-finance-tools - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/nl/functionaliteiten - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/nl/markten - daily - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/nl/open - daily - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/nl/over - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/nl/over/licentie - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/nl/over/oss-friends - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/nl/over/privacybeleid - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/nl/over/wijzigingslogboek - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/nl/prijzen - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/nl/registratie - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/nl/veelgestelde-vragen - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pl - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pl/blog - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pl/cennik - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pl/funkcje - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pl/o-ghostfolio - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pl/open - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pl/rynki - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pl/zarejestruj - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pt - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pt/blog - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pt/funcionalidades - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pt/mercados - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pt/open - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pt/perguntas-mais-frequentes - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pt/precos - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pt/recursos - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pt/recursos/personal-finance-tools - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pt/registo - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pt/sobre - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pt/sobre/changelog - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pt/sobre/licenca - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pt/sobre/oss-friends - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/pt/sobre/politica-de-privacidade - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/tr - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/uk - ${currentDate}T00:00:00+00:00 - - - https://ghostfol.io/zh - ${currentDate}T00:00:00+00:00 - + ${publicRoutes} ${personalFinanceTools} diff --git a/libs/common/src/lib/routes/interfaces/interfaces.ts b/libs/common/src/lib/routes/interfaces/internal-route.interface.ts similarity index 69% rename from libs/common/src/lib/routes/interfaces/interfaces.ts rename to libs/common/src/lib/routes/interfaces/internal-route.interface.ts index 5acbb2350..02f205979 100644 --- a/libs/common/src/lib/routes/interfaces/interfaces.ts +++ b/libs/common/src/lib/routes/interfaces/internal-route.interface.ts @@ -1,9 +1,9 @@ import { User } from '@ghostfolio/common/interfaces'; -export interface IRoute { +export interface InternalRoute { excludeFromAssistant?: boolean | ((aUser: User) => boolean); path: string; routerLink: string[]; - subRoutes?: Record; + subRoutes?: Record; title: string; } diff --git a/libs/common/src/lib/routes/interfaces/public-route.interface.ts b/libs/common/src/lib/routes/interfaces/public-route.interface.ts new file mode 100644 index 000000000..ad133200d --- /dev/null +++ b/libs/common/src/lib/routes/interfaces/public-route.interface.ts @@ -0,0 +1,7 @@ +export interface PublicRoute { + excludeFromSitemap?: boolean; + path: string; + routerLink: string[]; + subRoutes?: Record; + title?: string; +} diff --git a/libs/common/src/lib/routes/routes.ts b/libs/common/src/lib/routes/routes.ts index ee58d12a7..9c6461a75 100644 --- a/libs/common/src/lib/routes/routes.ts +++ b/libs/common/src/lib/routes/routes.ts @@ -1,11 +1,21 @@ import { User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; -import '@angular/localize/init'; +import { InternalRoute } from './interfaces/internal-route.interface'; +import { PublicRoute } from './interfaces/public-route.interface'; -import { IRoute } from './interfaces/interfaces'; +if (typeof window !== 'undefined') { + import('@angular/localize/init'); +} else { + (global as any).$localize = ( + messageParts: TemplateStringsArray, + ...expressions: any[] + ) => { + return String.raw({ raw: messageParts }, ...expressions); + }; +} -export const internalRoutes: Record = { +export const internalRoutes: Record = { account: { path: 'account', routerLink: ['/account'], @@ -156,7 +166,7 @@ export const internalRoutes: Record = { } }; -export const publicRoutes = { +export const publicRoutes: Record = { about: { path: $localize`:kebab-case@@routes.about:about`, routerLink: ['/' + $localize`:kebab-case@@routes.about:about`], @@ -300,9 +310,14 @@ export const publicRoutes = { $localize`:kebab-case@@routes.resources.personalFinanceTools:personal-finance-tools` ], subRoutes: { - excludeFromSitemap: true, product: { + excludeFromSitemap: true, path: $localize`:kebab-case@@routes.resources.personalFinanceTools.openSourceAlternativeTo:open-source-alternative-to`, + routerLink: [ + '/' + $localize`:kebab-case@@routes.resources:resources`, + $localize`:kebab-case@@routes.resources.personalFinanceTools:personal-finance-tools`, + $localize`:kebab-case@@routes.resources.personalFinanceTools.openSourceAlternativeTo:open-source-alternative-to` + ], title: $localize`Open Source Alternative to` } }, diff --git a/libs/ui/src/lib/assistant/assistant.component.ts b/libs/ui/src/lib/assistant/assistant.component.ts index 75e770ca7..de1733cf2 100644 --- a/libs/ui/src/lib/assistant/assistant.component.ts +++ b/libs/ui/src/lib/assistant/assistant.component.ts @@ -3,7 +3,7 @@ import { AdminService } from '@ghostfolio/client/services/admin.service'; import { DataService } from '@ghostfolio/client/services/data.service'; import { getAssetProfileIdentifier } from '@ghostfolio/common/helper'; import { Filter, PortfolioPosition, User } from '@ghostfolio/common/interfaces'; -import { IRoute } from '@ghostfolio/common/routes/interfaces/interfaces'; +import { InternalRoute } from '@ghostfolio/common/routes/interfaces/internal-route.interface'; import { internalRoutes } from '@ghostfolio/common/routes/routes'; import { DateRange } from '@ghostfolio/common/types'; import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo'; @@ -641,7 +641,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { acc.push(...Object.values(route.subRoutes)); } return acc; - }, [] as IRoute[]); + }, [] as InternalRoute[]); const fuse = new Fuse(allRoutes, { keys: ['title'],