Browse Source

Merge remote-tracking branch 'upstream/main' into feature/access_edit

pull/5566/head
Germán Martín 1 month ago
parent
commit
2b5dcd5b3f
  1. 7
      CHANGELOG.md
  2. 1
      apps/api/src/app/endpoints/sitemap/sitemap.controller.ts
  3. 138
      apps/api/src/app/endpoints/sitemap/sitemap.service.ts
  4. 1
      apps/api/src/assets/sitemap.xml
  5. 4
      apps/api/src/middlewares/html-template.middleware.ts
  6. 10
      apps/client/src/app/components/admin-jobs/admin-jobs.component.ts
  7. 58
      apps/client/src/app/components/admin-jobs/admin-jobs.html
  8. 17
      apps/client/src/app/pages/blog/2025/09/hacktoberfest-2025/hacktoberfest-2025-page.component.ts
  9. 201
      apps/client/src/app/pages/blog/2025/09/hacktoberfest-2025/hacktoberfest-2025-page.html
  10. 9
      apps/client/src/app/pages/blog/blog-page-routing.module.ts
  11. 24
      apps/client/src/app/pages/blog/blog-page.html
  12. 10
      apps/client/src/app/pages/portfolio/activities/activities-page.component.ts
  13. 84
      apps/client/src/app/services/data.service.ts
  14. BIN
      apps/client/src/assets/images/blog/hacktoberfest-2025.png
  15. 12
      package-lock.json
  16. 4
      package.json

7
CHANGELOG.md

@ -5,7 +5,12 @@ 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.203.0 - 2025-09-27
### Added
- Added support for column sorting to the queue jobs table in the admin control panel
- Added a blog post: _Hacktoberfest 2025_
### Changed

1
apps/api/src/app/endpoints/sitemap/sitemap.controller.ts

@ -37,6 +37,7 @@ export class SitemapController {
response.setHeader('content-type', 'application/xml');
response.send(
interpolate(this.sitemapXml, {
blogPosts: this.sitemapService.getBlogPosts({ currentDate }),
personalFinanceTools: this.configurationService.get(
'ENABLE_FEATURE_SUBSCRIPTION'
)

138
apps/api/src/app/endpoints/sitemap/sitemap.service.ts

@ -17,6 +17,121 @@ export class SitemapService {
private readonly i18nService: I18nService
) {}
public getBlogPosts({ currentDate }: { currentDate: string }) {
const rootUrl = this.configurationService.get('ROOT_URL');
return [
{
languageCode: 'de',
routerLink: ['2021', '07', 'hallo-ghostfolio']
},
{
languageCode: 'en',
routerLink: ['2021', '07', 'hello-ghostfolio']
},
{
languageCode: 'en',
routerLink: ['2022', '01', 'ghostfolio-first-months-in-open-source']
},
{
languageCode: 'en',
routerLink: ['2022', '07', 'ghostfolio-meets-internet-identity']
},
{
languageCode: 'en',
routerLink: ['2022', '07', 'how-do-i-get-my-finances-in-order']
},
{
languageCode: 'en',
routerLink: ['2022', '08', '500-stars-on-github']
},
{
languageCode: 'en',
routerLink: ['2022', '10', 'hacktoberfest-2022']
},
{
languageCode: 'en',
routerLink: ['2022', '11', 'black-friday-2022']
},
{
languageCode: 'en',
routerLink: [
'2022',
'12',
'the-importance-of-tracking-your-personal-finances'
]
},
{
languageCode: 'de',
routerLink: ['2023', '01', 'ghostfolio-auf-sackgeld-vorgestellt']
},
{
languageCode: 'en',
routerLink: ['2023', '02', 'ghostfolio-meets-umbrel']
},
{
languageCode: 'en',
routerLink: ['2023', '03', 'ghostfolio-reaches-1000-stars-on-github']
},
{
languageCode: 'en',
routerLink: [
'2023',
'05',
'unlock-your-financial-potential-with-ghostfolio'
]
},
{
languageCode: 'en',
routerLink: ['2023', '07', 'exploring-the-path-to-fire']
},
{
languageCode: 'en',
routerLink: ['2023', '08', 'ghostfolio-joins-oss-friends']
},
{
languageCode: 'en',
routerLink: ['2023', '09', 'ghostfolio-2']
},
{
languageCode: 'en',
routerLink: ['2023', '09', 'hacktoberfest-2023']
},
{
languageCode: 'en',
routerLink: ['2023', '11', 'black-week-2023']
},
{
languageCode: 'en',
routerLink: ['2023', '11', 'hacktoberfest-2023-debriefing']
},
{
languageCode: 'en',
routerLink: ['2024', '09', 'hacktoberfest-2024']
},
{
languageCode: 'en',
routerLink: ['2024', '11', 'black-weeks-2024']
},
{
languageCode: 'en',
routerLink: ['2025', '09', 'hacktoberfest-2025']
}
]
.map(({ languageCode, routerLink }) => {
return this.createRouteSitemapUrl({
currentDate,
languageCode,
rootUrl,
route: {
routerLink: [publicRoutes.blog.path, ...routerLink],
path: undefined
}
});
})
.join('\n');
}
public getPersonalFinanceTools({ currentDate }: { currentDate: string }) {
const rootUrl = this.configurationService.get('ROOT_URL');
@ -43,20 +158,21 @@ export class SitemapService {
});
return personalFinanceTools.map(({ alias, key }) => {
const location = [
rootUrl,
languageCode,
const routerLink = [
resourcesPath,
personalFinanceToolsPath,
`${productPath}-${alias ?? key}`
].join('/');
return [
' <url>',
` <loc>${location}</loc>`,
` <lastmod>${currentDate}T00:00:00+00:00</lastmod>`,
' </url>'
].join('\n');
];
return this.createRouteSitemapUrl({
currentDate,
languageCode,
rootUrl,
route: {
routerLink,
path: undefined
}
});
});
}).join('\n');
}

1
apps/api/src/assets/sitemap.xml

@ -5,5 +5,6 @@
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
${publicRoutes}
${blogPosts}
${personalFinanceTools}
</urlset>

4
apps/api/src/middlewares/html-template.middleware.ts

@ -75,6 +75,10 @@ const locales = {
'/en/blog/2024/11/black-weeks-2024': {
featureGraphicPath: 'assets/images/blog/black-weeks-2024.jpg',
title: `Black Weeks 2024 - ${title}`
},
'/en/blog/2025/09/hacktoberfest-2025': {
featureGraphicPath: 'assets/images/blog/hacktoberfest-2025.png',
title: `Hacktoberfest 2025 - ${title}`
}
};

10
apps/client/src/app/components/admin-jobs/admin-jobs.component.ts

@ -16,7 +16,8 @@ import {
ChangeDetectorRef,
Component,
OnDestroy,
OnInit
OnInit,
ViewChild
} from '@angular/core';
import {
FormBuilder,
@ -27,6 +28,7 @@ import {
import { MatButtonModule } from '@angular/material/button';
import { MatMenuModule } from '@angular/material/menu';
import { MatSelectModule } from '@angular/material/select';
import { MatSort, MatSortModule } from '@angular/material/sort';
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { IonIcon } from '@ionic/angular/standalone';
import { JobStatus } from 'bull';
@ -44,6 +46,7 @@ import {
removeCircleOutline,
timeOutline
} from 'ionicons/icons';
import { get } from 'lodash';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@ -57,6 +60,7 @@ import { takeUntil } from 'rxjs/operators';
MatButtonModule,
MatMenuModule,
MatSelectModule,
MatSortModule,
MatTableModule,
NgxSkeletonLoaderModule,
ReactiveFormsModule
@ -66,6 +70,8 @@ import { takeUntil } from 'rxjs/operators';
templateUrl: './admin-jobs.html'
})
export class GfAdminJobsComponent implements OnDestroy, OnInit {
@ViewChild(MatSort) sort: MatSort;
public DATA_GATHERING_QUEUE_PRIORITY_LOW = DATA_GATHERING_QUEUE_PRIORITY_LOW;
public DATA_GATHERING_QUEUE_PRIORITY_HIGH =
DATA_GATHERING_QUEUE_PRIORITY_HIGH;
@ -196,6 +202,8 @@ export class GfAdminJobsComponent implements OnDestroy, OnInit {
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ jobs }) => {
this.dataSource = new MatTableDataSource(jobs);
this.dataSource.sort = this.sort;
this.dataSource.sortingDataAccessor = get;
this.isLoading = false;

58
apps/client/src/app/components/admin-jobs/admin-jobs.html

@ -16,9 +16,21 @@
</mat-select>
</mat-form-field>
</form>
<table class="gf-table w-100" mat-table [dataSource]="dataSource">
<table
class="gf-table w-100"
mat-table
matSort
matSortActive="created"
matSortDirection="desc"
[dataSource]="dataSource"
>
<ng-container matColumnDef="index">
<th *matHeaderCellDef class="px-1 py-2 text-right" mat-header-cell>
<th
*matHeaderCellDef
class="px-1 py-2 text-right"
mat-header-cell
mat-sort-header="id"
>
<ng-container i18n>Job ID</ng-container>
</th>
<td *matCellDef="let element" class="px-1 py-2 text-right" mat-cell>
@ -27,7 +39,12 @@
</ng-container>
<ng-container matColumnDef="type">
<th *matHeaderCellDef class="px-1 py-2" mat-header-cell>
<th
*matHeaderCellDef
class="px-1 py-2"
mat-header-cell
mat-sort-header="name"
>
<ng-container i18n>Type</ng-container>
</th>
<td *matCellDef="let element" class="px-1 py-2" mat-cell>
@ -42,7 +59,12 @@
</ng-container>
<ng-container matColumnDef="symbol">
<th *matHeaderCellDef class="px-1 py-2" mat-header-cell>
<th
*matHeaderCellDef
class="px-1 py-2"
mat-header-cell
mat-sort-header="data.symbol"
>
<ng-container i18n>Symbol</ng-container>
</th>
<td *matCellDef="let element" class="px-1 py-2" mat-cell>
@ -51,7 +73,12 @@
</ng-container>
<ng-container matColumnDef="dataSource">
<th *matHeaderCellDef class="px-1 py-2" mat-header-cell>
<th
*matHeaderCellDef
class="px-1 py-2"
mat-header-cell
mat-sort-header="data.dataSource"
>
<ng-container i18n>Data Source</ng-container>
</th>
<td *matCellDef="let element" class="px-1 py-2" mat-cell>
@ -60,7 +87,12 @@
</ng-container>
<ng-container matColumnDef="priority">
<th *matHeaderCellDef class="px-1 py-2" mat-header-cell>
<th
*matHeaderCellDef
class="px-1 py-2"
mat-header-cell
mat-sort-header="opts.priority"
>
<ng-container i18n>Priority</ng-container>
</th>
<td *matCellDef="let element" class="px-1 py-2" mat-cell>
@ -79,7 +111,12 @@
</ng-container>
<ng-container matColumnDef="attempts">
<th *matHeaderCellDef class="px-1 py-2 text-right" mat-header-cell>
<th
*matHeaderCellDef
class="px-1 py-2 text-right"
mat-header-cell
mat-sort-header="attemptsMade"
>
<ng-container i18n>Attempts</ng-container>
</th>
<td *matCellDef="let element" class="px-1 py-2 text-right" mat-cell>
@ -88,7 +125,12 @@
</ng-container>
<ng-container matColumnDef="created">
<th *matHeaderCellDef class="px-1 py-2" mat-header-cell>
<th
*matHeaderCellDef
class="px-1 py-2"
mat-header-cell
mat-sort-header="timestamp"
>
<ng-container i18n>Created</ng-container>
</th>
<td *matCellDef="let element" class="px-1 py-2" mat-cell>

17
apps/client/src/app/pages/blog/2025/09/hacktoberfest-2025/hacktoberfest-2025-page.component.ts

@ -0,0 +1,17 @@
import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router';
@Component({
host: { class: 'page' },
imports: [MatButtonModule, RouterModule],
selector: 'gf-hacktoberfest-2025-page',
templateUrl: './hacktoberfest-2025-page.html'
})
export class Hacktoberfest2025PageComponent {
public routerLinkAbout = publicRoutes.about.routerLink;
public routerLinkBlog = publicRoutes.blog.routerLink;
public routerLinkOpenStartup = publicRoutes.openStartup.routerLink;
}

201
apps/client/src/app/pages/blog/2025/09/hacktoberfest-2025/hacktoberfest-2025-page.html

@ -0,0 +1,201 @@
<div class="blog container">
<div class="row">
<div class="col-md-8 offset-md-2">
<article>
<div class="mb-4 text-center">
<h1 class="mb-1">Hacktoberfest 2025</h1>
<div class="mb-3 text-muted"><small>2025-09-27</small></div>
<img
alt="Hacktoberfest 2025 with Ghostfolio Teaser"
class="rounded w-100"
src="../assets/images/blog/hacktoberfest-2025.png"
title="Hacktoberfest 2025 with Ghostfolio"
/>
</div>
<section class="mb-4">
<p>
Ghostfolio is joining
<a href="https://hacktoberfest.com">Hacktoberfest</a> for the fourth
time and <a [routerLink]="routerLinkAbout">we</a> are looking
forward to meeting new open-source contributors along the way. Every
year in October, Hacktoberfest celebrates open source by
highlighting projects, maintainers, and contributors from around the
globe. Open source maintainers dedicate extra time to support new
contributors while guiding them through their first pull requests on
<a href="https://github.com/ghostfolio/ghostfolio">GitHub</a>.
</p>
</section>
<section class="mb-4">
<h2 class="h4">
Meet Ghostfolio: a modern Dashboard for Personal Finance
</h2>
<p>
<a href="https://ghostfol.io">Ghostfolio</a> is a web application
that makes it easy to manage your personal finances. It aggregates
your assets and helps you make informed decisions to balance your
portfolio or plan future investments.
</p>
<p>
The software is fully written in
<a href="https://www.typescriptlang.org">TypeScript</a> and
organized as an <a href="https://nx.dev">Nx</a> workspace, utilizing
the latest framework releases. The backend is based on
<a href="https://nestjs.com">NestJS</a> in combination with
<a href="https://www.postgresql.org">PostgreSQL</a> as a database
together with <a href="https://www.prisma.io">Prisma</a> and
<a href="https://redis.io">Redis</a> for caching. The frontend is
developed with <a href="https://angular.dev">Angular</a>.
</p>
<p>
With over 200 contributors, the OSS project is used daily by a
growing global community. Ghostfolio counts more than
<a [routerLink]="routerLinkOpenStartup">6’500 stars on GitHub</a>
and
<a [routerLink]="routerLinkOpenStartup"
>1’600’000+ pulls on Docker Hub</a
>, standing out for its simple and user-friendly experience.
</p>
</section>
<section class="mb-4">
<h2 class="h4">How you can make an impact</h2>
<p>
Every contribution makes a difference. Whether it is implementing
new features, resolving bugs, refactoring code, enhancing
documentation, adding unit tests, or translating content into
another language, you can actively shape our project.
</p>
<p>
New to our codebase? No worries! We have labeled a few
<a
href="https://github.com/ghostfolio/ghostfolio/issues?q=is%3Aissue+is%3Aopen+label%3Ahacktoberfest"
>issues</a
>
with <code>hacktoberfest</code> that are ideal for newcomers.
</p>
<p>
The official Hacktoberfest website provides some valuable
<a
href="https://hacktoberfest.com/participation/#beginner-resources"
>resources for beginners</a
>
to start contributing in open source.
</p>
</section>
<section class="mb-4">
<h2 class="h4">Connect with us</h2>
<p>
If you have further questions or ideas, please join our
<a
href="https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg"
>Slack</a
>
community or get in touch on X
<a href="https://x.com/ghostfolio_">&#64;ghostfolio_</a>.
</p>
<p>
We look forward to collaborating.<br />
Thomas from Ghostfolio
</p>
</section>
<section class="mb-4">
<ul class="list-inline">
<li class="list-inline-item">
<span class="badge badge-light">Angular</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Community</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Dashboard</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Docker</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Finance</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Fintech</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Ghostfolio</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">GitHub</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Hacktoberfest</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Hacktoberfest 2025</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Investment</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">NestJS</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Nx</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">October</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Open Source</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">OSS</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Personal Finance</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Portfolio</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Portfolio Tracker</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Prisma</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Redis</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Software</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">TypeScript</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">UX</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Wealth</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Wealth Management</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Web Application</span>
</li>
</ul>
</section>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a i18n [routerLink]="routerLinkBlog">Blog</a>
</li>
<li
aria-current="page"
class="active breadcrumb-item text-truncate"
>
Hacktoberfest 2025
</li>
</ol>
</nav>
</article>
</div>
</div>
</div>

9
apps/client/src/app/pages/blog/blog-page-routing.module.ts

@ -201,6 +201,15 @@ const routes: Routes = [
(c) => c.BlackWeeks2024PageComponent
),
title: 'Black Weeks 2024'
},
{
canActivate: [AuthGuard],
path: '2025/09/hacktoberfest-2025',
loadComponent: () =>
import(
'./2025/09/hacktoberfest-2025/hacktoberfest-2025-page.component'
).then((c) => c.Hacktoberfest2025PageComponent),
title: 'Hacktoberfest 2025'
}
];

24
apps/client/src/app/pages/blog/blog-page.html

@ -8,6 +8,30 @@
finance</small
>
</h1>
<mat-card appearance="outlined" class="mb-3">
<mat-card-content class="p-0">
<div class="container p-0">
<div class="flex-nowrap no-gutters row">
<a
class="d-flex overflow-hidden p-3 w-100"
href="../en/blog/2025/09/hacktoberfest-2025"
>
<div class="flex-grow-1 overflow-hidden">
<div class="h6 m-0 text-truncate">Hacktoberfest 2025</div>
<div class="d-flex text-muted">2025-09-27</div>
</div>
<div class="align-items-center d-flex">
<ion-icon
class="chevron text-muted"
name="chevron-forward-outline"
size="small"
/>
</div>
</a>
</div>
</div>
</mat-card-content>
</mat-card>
@if (hasPermissionForSubscription) {
<mat-card appearance="outlined" class="mb-3">
<mat-card-content class="p-0">

10
apps/client/src/app/pages/portfolio/activities/activities-page.component.ts

@ -305,10 +305,10 @@ export class GfActivitiesPageComponent implements OnDestroy, OnInit {
});
}
public openUpdateActivityDialog(activity: Activity) {
public openUpdateActivityDialog(aActivity: Activity) {
const dialogRef = this.dialog.open(GfCreateOrUpdateActivityDialog, {
data: {
activity,
activity: aActivity,
accounts: this.user?.accounts,
user: this.user
},
@ -319,10 +319,10 @@ export class GfActivitiesPageComponent implements OnDestroy, OnInit {
dialogRef
.afterClosed()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((transaction: UpdateOrderDto | null) => {
if (transaction) {
.subscribe((activity: UpdateOrderDto) => {
if (activity) {
this.dataService
.putOrder(transaction)
.putOrder(activity)
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe({
next: () => {

84
apps/client/src/app/services/data.service.ts

@ -518,48 +518,6 @@ export class DataService {
);
}
public fetchSymbolItem({
dataSource,
includeHistoricalData,
symbol
}: {
dataSource: DataSource | string;
includeHistoricalData?: number;
symbol: string;
}) {
let params = new HttpParams();
if (includeHistoricalData) {
params = params.append('includeHistoricalData', includeHistoricalData);
}
return this.http.get<SymbolItem>(`/api/v1/symbol/${dataSource}/${symbol}`, {
params
});
}
public fetchSymbols({
includeIndices = false,
query
}: {
includeIndices?: boolean;
query: string;
}) {
let params = new HttpParams().set('query', query);
if (includeIndices) {
params = params.append('includeIndices', includeIndices);
}
return this.http
.get<LookupResponse>('/api/v1/symbol/lookup', { params })
.pipe(
map(({ items }) => {
return items;
})
);
}
public fetchPortfolioDetails({
filters,
withMarkets = false
@ -731,6 +689,48 @@ export class DataService {
);
}
public fetchSymbolItem({
dataSource,
includeHistoricalData,
symbol
}: {
dataSource: DataSource | string;
includeHistoricalData?: number;
symbol: string;
}) {
let params = new HttpParams();
if (includeHistoricalData) {
params = params.append('includeHistoricalData', includeHistoricalData);
}
return this.http.get<SymbolItem>(`/api/v1/symbol/${dataSource}/${symbol}`, {
params
});
}
public fetchSymbols({
includeIndices = false,
query
}: {
includeIndices?: boolean;
query: string;
}) {
let params = new HttpParams().set('query', query);
if (includeIndices) {
params = params.append('includeIndices', includeIndices);
}
return this.http
.get<LookupResponse>('/api/v1/symbol/lookup', { params })
.pipe(
map(({ items }) => {
return items;
})
);
}
public fetchTags() {
return this.http.get<Tag[]>('/api/v1/tags');
}

BIN
apps/client/src/assets/images/blog/hacktoberfest-2025.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

12
package-lock.json

@ -1,12 +1,12 @@
{
"name": "ghostfolio",
"version": "2.202.0",
"version": "2.203.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ghostfolio",
"version": "2.202.0",
"version": "2.203.0",
"hasInstallScript": true,
"license": "AGPL-3.0",
"dependencies": {
@ -130,7 +130,7 @@
"@types/big.js": "6.2.2",
"@types/google-spreadsheet": "3.1.5",
"@types/jest": "29.5.13",
"@types/lodash": "4.17.17",
"@types/lodash": "4.17.20",
"@types/node": "22.15.17",
"@types/papaparse": "5.3.7",
"@types/passport-google-oauth20": "2.0.16",
@ -14435,9 +14435,9 @@
}
},
"node_modules/@types/lodash": {
"version": "4.17.17",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.17.tgz",
"integrity": "sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ==",
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==",
"dev": true,
"license": "MIT"
},

4
package.json

@ -1,6 +1,6 @@
{
"name": "ghostfolio",
"version": "2.202.0",
"version": "2.203.0",
"homepage": "https://ghostfol.io",
"license": "AGPL-3.0",
"repository": "https://github.com/ghostfolio/ghostfolio",
@ -176,7 +176,7 @@
"@types/big.js": "6.2.2",
"@types/google-spreadsheet": "3.1.5",
"@types/jest": "29.5.13",
"@types/lodash": "4.17.17",
"@types/lodash": "4.17.20",
"@types/node": "22.15.17",
"@types/papaparse": "5.3.7",
"@types/passport-google-oauth20": "2.0.16",

Loading…
Cancel
Save